diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 5f524612ed..f76ebba8e9 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -49,7 +49,7 @@ jobs: with: python-version: "3.10" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.10.0 + uses: docker/setup-buildx-action@v3.11.1 - name: Set TAG run: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 22bdeca429..ca6d1b0aac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -296,7 +296,7 @@ jobs: name: Run script/clang-tidy for ZEPHYR options: --environment nrf52-tidy --grep USE_ZEPHYR pio_cache_key: tidy-zephyr - ignore_errors: true + ignore_errors: false steps: - name: Check out code from GitHub diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eae01fe0b3..b4518b27b5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -99,7 +99,7 @@ jobs: python-version: "3.10" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.10.0 + uses: docker/setup-buildx-action@v3.11.1 - name: Log in to docker hub uses: docker/login-action@v3.4.0 @@ -178,7 +178,7 @@ jobs: merge-multiple: true - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.10.0 + uses: docker/setup-buildx-action@v3.11.1 - name: Log in to docker hub if: matrix.registry == 'dockerhub' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d55c00eea7..634c474571 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.11.10 + rev: v0.12.0 hooks: # Run the linter. - id: ruff diff --git a/CODEOWNERS b/CODEOWNERS index dade427a45..ebbc8732ea 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -150,6 +150,7 @@ esphome/components/esp32_improv/* @jesserockz esphome/components/esp32_rmt/* @jesserockz esphome/components/esp32_rmt_led_strip/* @jesserockz esphome/components/esp8266/* @esphome/core +esphome/components/esp_ldo/* @clydebarrow esphome/components/ethernet_info/* @gtjadsonsantos esphome/components/event/* @nohat esphome/components/event_emitter/* @Rapsssito @@ -235,6 +236,7 @@ esphome/components/kamstrup_kmp/* @cfeenstra1024 esphome/components/key_collector/* @ssieb esphome/components/key_provider/* @ssieb esphome/components/kuntze/* @ssieb +esphome/components/lc709203f/* @ilikecake esphome/components/lcd_menu/* @numo68 esphome/components/ld2410/* @regevbr @sebcaps esphome/components/ld2420/* @descipher @@ -320,6 +322,7 @@ esphome/components/number/* @esphome/core esphome/components/one_wire/* @ssieb esphome/components/online_image/* @clydebarrow @guillempages esphome/components/opentherm/* @olegtarasov +esphome/components/openthread/* @mrene esphome/components/ota/* @esphome/core esphome/components/output/* @esphome/core esphome/components/packet_transport/* @clydebarrow @@ -517,6 +520,7 @@ esphome/components/xiaomi_lywsd03mmc/* @ahpohl esphome/components/xiaomi_mhoc303/* @drug123 esphome/components/xiaomi_mhoc401/* @vevsvevs esphome/components/xiaomi_rtcgq02lm/* @jesserockz +esphome/components/xiaomi_xmwsdj04mmc/* @medusalix esphome/components/xl9535/* @mreditor97 esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68 esphome/components/xxtea/* @clydebarrow diff --git a/Doxyfile b/Doxyfile index f807ba5c4e..03d432b924 100644 --- a/Doxyfile +++ b/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 = 2025.6.0-dev +PROJECT_NUMBER = 2025.7.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 diff --git a/esphome/__main__.py b/esphome/__main__.py index adada75163..2dbdfeb1ff 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -134,6 +134,7 @@ def get_port_type(port): def run_miniterm(config, port, args): + from aioesphomeapi import LogParser import serial from esphome import platformio_api @@ -158,6 +159,7 @@ def run_miniterm(config, port, args): ser.dtr = False ser.rts = False + parser = LogParser() tries = 0 while tries < 5: try: @@ -174,8 +176,7 @@ def run_miniterm(config, port, args): .decode("utf8", "backslashreplace") ) time_str = datetime.now().time().strftime("[%H:%M:%S]") - message = time_str + line - safe_print(message) + safe_print(parser.parse_line(line, time_str)) backtrace_state = platformio_api.process_stacktrace( config, line, backtrace_state=backtrace_state diff --git a/esphome/codegen.py b/esphome/codegen.py index bfa1683ce7..8e02ec1164 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -22,6 +22,7 @@ from esphome.cpp_generator import ( # noqa: F401 TemplateArguments, add, add_build_flag, + add_build_unflag, add_define, add_global, add_library, @@ -34,6 +35,7 @@ from esphome.cpp_generator import ( # noqa: F401 process_lambda, progmem_array, safe_exp, + set_cpp_standard, statement, static_const_array, templatable, diff --git a/esphome/components/absolute_humidity/absolute_humidity.cpp b/esphome/components/absolute_humidity/absolute_humidity.cpp index 877e6dbe22..c3cb159aed 100644 --- a/esphome/components/absolute_humidity/absolute_humidity.cpp +++ b/esphome/components/absolute_humidity/absolute_humidity.cpp @@ -40,9 +40,11 @@ void AbsoluteHumidityComponent::dump_config() { break; } - ESP_LOGCONFIG(TAG, "Sources"); - ESP_LOGCONFIG(TAG, " Temperature: '%s'", this->temperature_sensor_->get_name().c_str()); - ESP_LOGCONFIG(TAG, " Relative Humidity: '%s'", this->humidity_sensor_->get_name().c_str()); + ESP_LOGCONFIG(TAG, + "Sources\n" + " Temperature: '%s'\n" + " Relative Humidity: '%s'", + this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str()); } float AbsoluteHumidityComponent::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/ac_dimmer/ac_dimmer.cpp b/esphome/components/ac_dimmer/ac_dimmer.cpp index 4901719b32..276adeebb0 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.cpp +++ b/esphome/components/ac_dimmer/ac_dimmer.cpp @@ -193,14 +193,13 @@ void AcDimmer::setup() { setTimer1Callback(&timer_interrupt); #endif #ifdef USE_ESP32 - // 80 Divider -> 1 count=1µs - dimmer_timer = timerBegin(0, 80, true); - timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true); + // timer frequency of 1mhz + dimmer_timer = timerBegin(1000000); + timerAttachInterrupt(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. - timerAlarmWrite(dimmer_timer, 50, true); - timerAlarmEnable(dimmer_timer); + timerAlarm(dimmer_timer, 50, true, 0); #endif } void AcDimmer::write_state(float state) { @@ -214,8 +213,10 @@ void AcDimmer::dump_config() { ESP_LOGCONFIG(TAG, "AcDimmer:"); LOG_PIN(" Output Pin: ", this->gate_pin_); LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_); - ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->store_.min_power / 10.0f); - ESP_LOGCONFIG(TAG, " Init with half cycle: %s", YESNO(this->init_with_half_cycle_)); + ESP_LOGCONFIG(TAG, + " Min Power: %.1f%%\n" + " Init with half cycle: %s", + this->store_.min_power / 10.0f, YESNO(this->init_with_half_cycle_)); if (method_ == DIM_METHOD_LEADING_PULSE) { ESP_LOGCONFIG(TAG, " Method: leading pulse"); } else if (method_ == DIM_METHOD_LEADING) { diff --git a/esphome/components/adc/adc_sensor_esp32.cpp b/esphome/components/adc/adc_sensor_esp32.cpp index efd3bafb83..d6cf6e893b 100644 --- a/esphome/components/adc/adc_sensor_esp32.cpp +++ b/esphome/components/adc/adc_sensor_esp32.cpp @@ -22,7 +22,7 @@ static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; void ADCSensor::setup() { - ESP_LOGCONFIG(TAG, "Running setup for '%s'...", this->get_name().c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); if (this->channel1_ != ADC1_CHANNEL_MAX) { adc1_config_width(ADC_WIDTH_MAX_SOC_BITS); @@ -77,8 +77,10 @@ void ADCSensor::dump_config() { break; } } - ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); - ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); + ESP_LOGCONFIG(TAG, + " Samples: %i\n" + " Sampling mode: %s", + this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/adc/adc_sensor_esp8266.cpp b/esphome/components/adc/adc_sensor_esp8266.cpp index 14837562aa..67128fb1f3 100644 --- a/esphome/components/adc/adc_sensor_esp8266.cpp +++ b/esphome/components/adc/adc_sensor_esp8266.cpp @@ -17,7 +17,7 @@ namespace adc { static const char *const TAG = "adc.esp8266"; void ADCSensor::setup() { - ESP_LOGCONFIG(TAG, "Running setup for '%s'...", this->get_name().c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); #ifndef USE_ADC_SENSOR_VCC this->pin_->setup(); #endif @@ -30,8 +30,10 @@ void ADCSensor::dump_config() { #else LOG_PIN(" Pin: ", this->pin_); #endif // USE_ADC_SENSOR_VCC - ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); - ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); + ESP_LOGCONFIG(TAG, + " Samples: %i\n" + " Sampling mode: %s", + this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/adc/adc_sensor_libretiny.cpp b/esphome/components/adc/adc_sensor_libretiny.cpp index 5676857dc9..f7c7e669ec 100644 --- a/esphome/components/adc/adc_sensor_libretiny.cpp +++ b/esphome/components/adc/adc_sensor_libretiny.cpp @@ -9,7 +9,7 @@ namespace adc { static const char *const TAG = "adc.libretiny"; void ADCSensor::setup() { - ESP_LOGCONFIG(TAG, "Running setup for '%s'...", this->get_name().c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); #ifndef USE_ADC_SENSOR_VCC this->pin_->setup(); #endif // !USE_ADC_SENSOR_VCC @@ -22,8 +22,10 @@ void ADCSensor::dump_config() { #else // USE_ADC_SENSOR_VCC LOG_PIN(" Pin: ", this->pin_); #endif // USE_ADC_SENSOR_VCC - ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); - ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); + ESP_LOGCONFIG(TAG, + " Samples: %i\n" + " Sampling mode: %s", + this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/adc/adc_sensor_rp2040.cpp b/esphome/components/adc/adc_sensor_rp2040.cpp index 8c6afb8186..91d331270b 100644 --- a/esphome/components/adc/adc_sensor_rp2040.cpp +++ b/esphome/components/adc/adc_sensor_rp2040.cpp @@ -14,7 +14,7 @@ namespace adc { static const char *const TAG = "adc.rp2040"; void ADCSensor::setup() { - ESP_LOGCONFIG(TAG, "Running setup for '%s'...", this->get_name().c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); static bool initialized = false; if (!initialized) { adc_init(); @@ -33,8 +33,10 @@ void ADCSensor::dump_config() { LOG_PIN(" Pin: ", this->pin_); #endif // USE_ADC_SENSOR_VCC } - ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); - ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); + ESP_LOGCONFIG(TAG, + " Samples: %i\n" + " Sampling mode: %s", + this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/ade7880/ade7880.cpp b/esphome/components/ade7880/ade7880.cpp index 4a45b3b321..55f834bf86 100644 --- a/esphome/components/ade7880/ade7880.cpp +++ b/esphome/components/ade7880/ade7880.cpp @@ -177,11 +177,14 @@ void ADE7880::dump_config() { LOG_SENSOR(" ", "Power Factor", this->channel_a_->power_factor); LOG_SENSOR(" ", "Forward Active Energy", this->channel_a_->forward_active_energy); LOG_SENSOR(" ", "Reverse Active Energy", this->channel_a_->reverse_active_energy); - ESP_LOGCONFIG(TAG, " Calibration:"); - ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_a_->current_gain_calibration); - ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_a_->voltage_gain_calibration); - ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_a_->power_gain_calibration); - ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_a_->phase_angle_calibration); + ESP_LOGCONFIG(TAG, + " Calibration:\n" + " Current: %" PRId32 "\n" + " Voltage: %" PRId32 "\n" + " Power: %" PRId32 "\n" + " Phase Angle: %u", + this->channel_a_->current_gain_calibration, this->channel_a_->voltage_gain_calibration, + this->channel_a_->power_gain_calibration, this->channel_a_->phase_angle_calibration); } if (this->channel_b_ != nullptr) { @@ -193,11 +196,14 @@ void ADE7880::dump_config() { LOG_SENSOR(" ", "Power Factor", this->channel_b_->power_factor); LOG_SENSOR(" ", "Forward Active Energy", this->channel_b_->forward_active_energy); LOG_SENSOR(" ", "Reverse Active Energy", this->channel_b_->reverse_active_energy); - ESP_LOGCONFIG(TAG, " Calibration:"); - ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_b_->current_gain_calibration); - ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_b_->voltage_gain_calibration); - ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_b_->power_gain_calibration); - ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_b_->phase_angle_calibration); + ESP_LOGCONFIG(TAG, + " Calibration:\n" + " Current: %" PRId32 "\n" + " Voltage: %" PRId32 "\n" + " Power: %" PRId32 "\n" + " Phase Angle: %u", + this->channel_b_->current_gain_calibration, this->channel_b_->voltage_gain_calibration, + this->channel_b_->power_gain_calibration, this->channel_b_->phase_angle_calibration); } if (this->channel_c_ != nullptr) { @@ -209,18 +215,23 @@ void ADE7880::dump_config() { LOG_SENSOR(" ", "Power Factor", this->channel_c_->power_factor); LOG_SENSOR(" ", "Forward Active Energy", this->channel_c_->forward_active_energy); LOG_SENSOR(" ", "Reverse Active Energy", this->channel_c_->reverse_active_energy); - ESP_LOGCONFIG(TAG, " Calibration:"); - ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_c_->current_gain_calibration); - ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_c_->voltage_gain_calibration); - ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_c_->power_gain_calibration); - ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_c_->phase_angle_calibration); + ESP_LOGCONFIG(TAG, + " Calibration:\n" + " Current: %" PRId32 "\n" + " Voltage: %" PRId32 "\n" + " Power: %" PRId32 "\n" + " Phase Angle: %u", + this->channel_c_->current_gain_calibration, this->channel_c_->voltage_gain_calibration, + this->channel_c_->power_gain_calibration, this->channel_c_->phase_angle_calibration); } if (this->channel_n_ != nullptr) { ESP_LOGCONFIG(TAG, " Neutral:"); LOG_SENSOR(" ", "Current", this->channel_n_->current); - ESP_LOGCONFIG(TAG, " Calibration:"); - ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_n_->current_gain_calibration); + ESP_LOGCONFIG(TAG, + " Calibration:\n" + " Current: %" PRId32, + this->channel_n_->current_gain_calibration); } LOG_I2C_DEVICE(this); diff --git a/esphome/components/ade7953_base/ade7953_base.cpp b/esphome/components/ade7953_base/ade7953_base.cpp index 2511b4e04c..5f5fdd27ee 100644 --- a/esphome/components/ade7953_base/ade7953_base.cpp +++ b/esphome/components/ade7953_base/ade7953_base.cpp @@ -58,15 +58,18 @@ void ADE7953::dump_config() { LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_); LOG_SENSOR(" ", "Rective Power A Sensor", this->reactive_power_a_sensor_); LOG_SENSOR(" ", "Reactive Power B Sensor", this->reactive_power_b_sensor_); - ESP_LOGCONFIG(TAG, " USE_ACC_ENERGY_REGS: %d", this->use_acc_energy_regs_); - ESP_LOGCONFIG(TAG, " PGA_V_8: 0x%X", pga_v_); - ESP_LOGCONFIG(TAG, " PGA_IA_8: 0x%X", pga_ia_); - ESP_LOGCONFIG(TAG, " PGA_IB_8: 0x%X", pga_ib_); - ESP_LOGCONFIG(TAG, " VGAIN_32: 0x%08jX", (uintmax_t) vgain_); - ESP_LOGCONFIG(TAG, " AIGAIN_32: 0x%08jX", (uintmax_t) aigain_); - ESP_LOGCONFIG(TAG, " BIGAIN_32: 0x%08jX", (uintmax_t) bigain_); - ESP_LOGCONFIG(TAG, " AWGAIN_32: 0x%08jX", (uintmax_t) awgain_); - ESP_LOGCONFIG(TAG, " BWGAIN_32: 0x%08jX", (uintmax_t) bwgain_); + ESP_LOGCONFIG(TAG, + " USE_ACC_ENERGY_REGS: %d\n" + " PGA_V_8: 0x%X\n" + " PGA_IA_8: 0x%X\n" + " PGA_IB_8: 0x%X\n" + " VGAIN_32: 0x%08jX\n" + " AIGAIN_32: 0x%08jX\n" + " BIGAIN_32: 0x%08jX\n" + " AWGAIN_32: 0x%08jX\n" + " BWGAIN_32: 0x%08jX", + this->use_acc_energy_regs_, pga_v_, pga_ia_, pga_ib_, (uintmax_t) vgain_, (uintmax_t) aigain_, + (uintmax_t) bigain_, (uintmax_t) awgain_, (uintmax_t) bwgain_); } #define ADE_PUBLISH_(name, val, factor) \ diff --git a/esphome/components/ade7953_i2c/ade7953_i2c.cpp b/esphome/components/ade7953_i2c/ade7953_i2c.cpp index ae381824db..59c2254d44 100644 --- a/esphome/components/ade7953_i2c/ade7953_i2c.cpp +++ b/esphome/components/ade7953_i2c/ade7953_i2c.cpp @@ -1,6 +1,6 @@ #include "ade7953_i2c.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ade7953_i2c { diff --git a/esphome/components/ade7953_spi/ade7953_spi.cpp b/esphome/components/ade7953_spi/ade7953_spi.cpp index 77a2a8adc7..6b16d933a2 100644 --- a/esphome/components/ade7953_spi/ade7953_spi.cpp +++ b/esphome/components/ade7953_spi/ade7953_spi.cpp @@ -1,6 +1,6 @@ #include "ade7953_spi.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ade7953_spi { diff --git a/esphome/components/ads1118/ads1118.cpp b/esphome/components/ads1118/ads1118.cpp index 976b599a6a..1daa8fdfd4 100644 --- a/esphome/components/ads1118/ads1118.cpp +++ b/esphome/components/ads1118/ads1118.cpp @@ -1,4 +1,5 @@ #include "ads1118.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/ags10/ags10.cpp b/esphome/components/ags10/ags10.cpp index 16228708ed..797a07afa5 100644 --- a/esphome/components/ags10/ags10.cpp +++ b/esphome/components/ags10/ags10.cpp @@ -1,4 +1,5 @@ #include "ags10.h" +#include "esphome/core/helpers.h" #include diff --git a/esphome/components/aht10/aht10.cpp b/esphome/components/aht10/aht10.cpp index de7fd04be9..7f17e1c0d6 100644 --- a/esphome/components/aht10/aht10.cpp +++ b/esphome/components/aht10/aht10.cpp @@ -13,9 +13,9 @@ // results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time. #include "aht10.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace aht10 { @@ -115,7 +115,7 @@ void AHT10Component::read_data_() { if (this->humidity_sensor_ == nullptr) { ESP_LOGV(TAG, "Invalid humidity (reading not required)"); } else { - ESP_LOGD(TAG, "Invalid humidity, retrying..."); + ESP_LOGD(TAG, "Invalid humidity, retrying"); if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) { this->status_set_warning(ESP_LOG_MSG_COMM_FAIL); } diff --git a/esphome/components/alarm_control_panel/__init__.py b/esphome/components/alarm_control_panel/__init__.py index 1bcb83bce7..e88050132a 100644 --- a/esphome/components/alarm_control_panel/__init__.py +++ b/esphome/components/alarm_control_panel/__init__.py @@ -235,6 +235,7 @@ async def register_alarm_control_panel(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_alarm_control_panel(var)) + CORE.register_platform_component("alarm_control_panel", var) await setup_alarm_control_panel_core_(var, config) diff --git a/esphome/components/am43/am43_base.h b/esphome/components/am43/am43_base.h index e817f161fe..35354af9ed 100644 --- a/esphome/components/am43/am43_base.h +++ b/esphome/components/am43/am43_base.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace am43 { diff --git a/esphome/components/am43/cover/am43_cover.cpp b/esphome/components/am43/cover/am43_cover.cpp index 93c77ea364..0d49439095 100644 --- a/esphome/components/am43/cover/am43_cover.cpp +++ b/esphome/components/am43/cover/am43_cover.cpp @@ -12,8 +12,10 @@ using namespace esphome::cover; void Am43Component::dump_config() { LOG_COVER("", "AM43 Cover", this); - ESP_LOGCONFIG(TAG, " Device Pin: %d", this->pin_); - ESP_LOGCONFIG(TAG, " Invert Position: %d", (int) this->invert_position_); + ESP_LOGCONFIG(TAG, + " Device Pin: %d\n" + " Invert Position: %d", + this->pin_, (int) this->invert_position_); } void Am43Component::setup() { diff --git a/esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp b/esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp index 8dcbb2ac4b..f83f2aff08 100644 --- a/esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +++ b/esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp @@ -34,8 +34,10 @@ void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) { void AnalogThresholdBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this); LOG_SENSOR(" ", "Sensor", this->sensor_); - ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_.value()); - ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_.value()); + ESP_LOGCONFIG(TAG, + " Upper threshold: %.11f\n" + " Lower threshold: %.11f", + this->upper_threshold_.value(), this->lower_threshold_.value()); } } // namespace analog_threshold diff --git a/esphome/components/anova/anova.cpp b/esphome/components/anova/anova.cpp index ebf6c1d037..d0e8f6827f 100644 --- a/esphome/components/anova/anova.cpp +++ b/esphome/components/anova/anova.cpp @@ -17,7 +17,11 @@ void Anova::setup() { this->current_request_ = 0; } -void Anova::loop() {} +void Anova::loop() { + // Parent BLEClientNode has a loop() method, but this component uses + // polling via update() and BLE callbacks so loop isn't needed + this->disable_loop(); +} void Anova::control(const ClimateCall &call) { if (call.get_mode().has_value()) { diff --git a/esphome/components/apds9306/apds9306.cpp b/esphome/components/apds9306/apds9306.cpp index b785f61c57..9799f54d3d 100644 --- a/esphome/components/apds9306/apds9306.cpp +++ b/esphome/components/apds9306/apds9306.cpp @@ -108,9 +108,12 @@ void APDS9306::dump_config() { } } - ESP_LOGCONFIG(TAG, " Gain: %u", AMBIENT_LIGHT_GAIN_VALUES[this->gain_]); - ESP_LOGCONFIG(TAG, " Measurement rate: %u", MEASUREMENT_RATE_VALUES[this->measurement_rate_]); - ESP_LOGCONFIG(TAG, " Measurement Resolution/Bit width: %d", MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]); + ESP_LOGCONFIG(TAG, + " Gain: %u\n" + " Measurement rate: %u\n" + " Measurement Resolution/Bit width: %d", + AMBIENT_LIGHT_GAIN_VALUES[this->gain_], MEASUREMENT_RATE_VALUES[this->measurement_rate_], + MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 4b63c76fba..bd131ef8de 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -49,6 +49,7 @@ SERVICE_ARG_NATIVE_TYPES = { "string[]": cg.std_vector.template(cg.std_string), } CONF_ENCRYPTION = "encryption" +CONF_BATCH_DELAY = "batch_delay" def validate_encryption_key(value): @@ -109,6 +110,9 @@ CONFIG_SCHEMA = cv.All( ): ACTIONS_SCHEMA, cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA, cv.Optional(CONF_ENCRYPTION): _encryption_schema, + cv.Optional( + CONF_BATCH_DELAY, default="100ms" + ): cv.positive_time_period_milliseconds, cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation( single=True ), @@ -129,6 +133,7 @@ async def to_code(config): cg.add(var.set_port(config[CONF_PORT])) cg.add(var.set_password(config[CONF_PASSWORD])) cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) + cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY])) for conf in config.get(CONF_ACTIONS, []): template_args = [] diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 8dc8255c05..9603694ae8 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -274,6 +274,7 @@ enum EntityCategory { // ==================== BINARY SENSOR ==================== message ListEntitiesBinarySensorResponse { option (id) = 12; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_BINARY_SENSOR"; @@ -291,6 +292,7 @@ message ListEntitiesBinarySensorResponse { } message BinarySensorStateResponse { option (id) = 21; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_BINARY_SENSOR"; option (no_delay) = true; @@ -305,6 +307,7 @@ message BinarySensorStateResponse { // ==================== COVER ==================== message ListEntitiesCoverResponse { option (id) = 13; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_COVER"; @@ -335,6 +338,7 @@ enum CoverOperation { } message CoverStateResponse { option (id) = 22; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_COVER"; option (no_delay) = true; @@ -377,6 +381,7 @@ message CoverCommandRequest { // ==================== FAN ==================== message ListEntitiesFanResponse { option (id) = 14; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_FAN"; @@ -406,6 +411,7 @@ enum FanDirection { } message FanStateResponse { option (id) = 23; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_FAN"; option (no_delay) = true; @@ -455,6 +461,7 @@ enum ColorMode { } message ListEntitiesLightResponse { option (id) = 15; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_LIGHT"; @@ -479,6 +486,7 @@ message ListEntitiesLightResponse { } message LightStateResponse { option (id) = 24; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_LIGHT"; option (no_delay) = true; @@ -548,6 +556,7 @@ enum SensorLastResetType { message ListEntitiesSensorResponse { option (id) = 16; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SENSOR"; @@ -570,6 +579,7 @@ message ListEntitiesSensorResponse { } message SensorStateResponse { option (id) = 25; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SENSOR"; option (no_delay) = true; @@ -584,6 +594,7 @@ message SensorStateResponse { // ==================== SWITCH ==================== message ListEntitiesSwitchResponse { option (id) = 17; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SWITCH"; @@ -601,6 +612,7 @@ message ListEntitiesSwitchResponse { } message SwitchStateResponse { option (id) = 26; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SWITCH"; option (no_delay) = true; @@ -621,6 +633,7 @@ message SwitchCommandRequest { // ==================== TEXT SENSOR ==================== message ListEntitiesTextSensorResponse { option (id) = 18; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_TEXT_SENSOR"; @@ -637,6 +650,7 @@ message ListEntitiesTextSensorResponse { } message TextSensorStateResponse { option (id) = 27; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_TEXT_SENSOR"; option (no_delay) = true; @@ -804,6 +818,7 @@ message ExecuteServiceRequest { // ==================== CAMERA ==================== message ListEntitiesCameraResponse { option (id) = 43; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_ESP32_CAMERA"; @@ -885,6 +900,7 @@ enum ClimatePreset { } message ListEntitiesClimateResponse { option (id) = 46; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_CLIMATE"; @@ -920,6 +936,7 @@ message ListEntitiesClimateResponse { } message ClimateStateResponse { option (id) = 47; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_CLIMATE"; option (no_delay) = true; @@ -981,6 +998,7 @@ enum NumberMode { } message ListEntitiesNumberResponse { option (id) = 49; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_NUMBER"; @@ -1002,6 +1020,7 @@ message ListEntitiesNumberResponse { } message NumberStateResponse { option (id) = 50; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_NUMBER"; option (no_delay) = true; @@ -1025,6 +1044,7 @@ message NumberCommandRequest { // ==================== SELECT ==================== message ListEntitiesSelectResponse { option (id) = 52; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SELECT"; @@ -1041,6 +1061,7 @@ message ListEntitiesSelectResponse { } message SelectStateResponse { option (id) = 53; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SELECT"; option (no_delay) = true; @@ -1064,6 +1085,7 @@ message SelectCommandRequest { // ==================== SIREN ==================== message ListEntitiesSirenResponse { option (id) = 55; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SIREN"; @@ -1081,6 +1103,7 @@ message ListEntitiesSirenResponse { } message SirenStateResponse { option (id) = 56; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SIREN"; option (no_delay) = true; @@ -1121,6 +1144,7 @@ enum LockCommand { } message ListEntitiesLockResponse { option (id) = 58; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_LOCK"; @@ -1143,6 +1167,7 @@ message ListEntitiesLockResponse { } message LockStateResponse { option (id) = 59; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_LOCK"; option (no_delay) = true; @@ -1165,6 +1190,7 @@ message LockCommandRequest { // ==================== BUTTON ==================== message ListEntitiesButtonResponse { option (id) = 61; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_BUTTON"; @@ -1217,6 +1243,7 @@ message MediaPlayerSupportedFormat { } message ListEntitiesMediaPlayerResponse { option (id) = 63; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_MEDIA_PLAYER"; @@ -1237,6 +1264,7 @@ message ListEntitiesMediaPlayerResponse { } message MediaPlayerStateResponse { option (id) = 64; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_MEDIA_PLAYER"; option (no_delay) = true; @@ -1638,6 +1666,7 @@ enum VoiceAssistantEvent { VOICE_ASSISTANT_STT_VAD_END = 12; VOICE_ASSISTANT_TTS_STREAM_START = 98; VOICE_ASSISTANT_TTS_STREAM_END = 99; + VOICE_ASSISTANT_INTENT_PROGRESS = 100; } message VoiceAssistantEventData { @@ -1758,6 +1787,7 @@ enum AlarmControlPanelStateCommand { message ListEntitiesAlarmControlPanelResponse { option (id) = 94; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_ALARM_CONTROL_PANEL"; @@ -1776,6 +1806,7 @@ message ListEntitiesAlarmControlPanelResponse { message AlarmControlPanelStateResponse { option (id) = 95; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_ALARM_CONTROL_PANEL"; option (no_delay) = true; @@ -1800,6 +1831,7 @@ enum TextMode { } message ListEntitiesTextResponse { option (id) = 97; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_TEXT"; @@ -1819,6 +1851,7 @@ message ListEntitiesTextResponse { } message TextStateResponse { option (id) = 98; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_TEXT"; option (no_delay) = true; @@ -1843,6 +1876,7 @@ message TextCommandRequest { // ==================== DATETIME DATE ==================== message ListEntitiesDateResponse { option (id) = 100; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_DATE"; @@ -1858,6 +1892,7 @@ message ListEntitiesDateResponse { } message DateStateResponse { option (id) = 101; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_DATE"; option (no_delay) = true; @@ -1885,6 +1920,7 @@ message DateCommandRequest { // ==================== DATETIME TIME ==================== message ListEntitiesTimeResponse { option (id) = 103; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_TIME"; @@ -1900,6 +1936,7 @@ message ListEntitiesTimeResponse { } message TimeStateResponse { option (id) = 104; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_TIME"; option (no_delay) = true; @@ -1927,6 +1964,7 @@ message TimeCommandRequest { // ==================== EVENT ==================== message ListEntitiesEventResponse { option (id) = 107; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_EVENT"; @@ -1945,6 +1983,7 @@ message ListEntitiesEventResponse { } message EventResponse { option (id) = 108; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_EVENT"; @@ -1955,6 +1994,7 @@ message EventResponse { // ==================== VALVE ==================== message ListEntitiesValveResponse { option (id) = 109; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_VALVE"; @@ -1981,6 +2021,7 @@ enum ValveOperation { } message ValveStateResponse { option (id) = 110; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_VALVE"; option (no_delay) = true; @@ -2005,6 +2046,7 @@ message ValveCommandRequest { // ==================== DATETIME DATETIME ==================== message ListEntitiesDateTimeResponse { option (id) = 112; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_DATETIME"; @@ -2020,6 +2062,7 @@ message ListEntitiesDateTimeResponse { } message DateTimeStateResponse { option (id) = 113; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_DATETIME"; option (no_delay) = true; @@ -2043,6 +2086,7 @@ message DateTimeCommandRequest { // ==================== UPDATE ==================== message ListEntitiesUpdateResponse { option (id) = 116; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_UPDATE"; @@ -2059,6 +2103,7 @@ message ListEntitiesUpdateResponse { } message UpdateStateResponse { option (id) = 117; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_UPDATE"; option (no_delay) = true; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index bf95a2813d..0288419405 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "esphome/components/network/util.h" #include "esphome/core/application.h" #include "esphome/core/entity_base.h" @@ -29,40 +31,8 @@ namespace api { static const char *const TAG = "api.connection"; static const int ESP32_CAMERA_STOP_STREAM = 5000; -// helper for allowing only unique entries in the queue -void DeferredMessageQueue::dmq_push_back_with_dedup_(void *source, send_message_t send_message) { - DeferredMessage item(source, send_message); - - auto iter = std::find_if(this->deferred_queue_.begin(), this->deferred_queue_.end(), - [&item](const DeferredMessage &test) -> bool { return test == item; }); - - if (iter != this->deferred_queue_.end()) { - (*iter) = item; - } else { - this->deferred_queue_.push_back(item); - } -} - -void DeferredMessageQueue::process_queue() { - while (!deferred_queue_.empty()) { - DeferredMessage &de = deferred_queue_.front(); - if ((this->api_connection_->*(de.send_message_))(de.source_)) { - // O(n) but memory efficiency is more important than speed here which is why std::vector was chosen - deferred_queue_.erase(deferred_queue_.begin()); - } else { - break; - } - } -} - -void DeferredMessageQueue::defer(void *source, send_message_t send_message) { - this->dmq_push_back_with_dedup_(source, send_message); -} - APIConnection::APIConnection(std::unique_ptr sock, APIServer *parent) - : parent_(parent), deferred_message_queue_(this), initial_state_iterator_(this), list_entities_iterator_(this) { - this->proto_write_buffer_.reserve(64); - + : parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) { #if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE) auto noise_ctx = parent->get_noise_ctx(); if (noise_ctx->has_psk()) { @@ -78,6 +48,9 @@ APIConnection::APIConnection(std::unique_ptr sock, APIServer *pa #error "No frame helper defined" #endif } + +uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); } + void APIConnection::start() { this->last_traffic_ = App.get_loop_component_start_time(); @@ -88,8 +61,8 @@ void APIConnection::start() { APIError err = this->helper_->init(); if (err != APIError::OK) { on_fatal_error(); - ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err), - errno); + ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->get_client_combined_info().c_str(), + api_error_to_str(err), errno); return; } this->client_info_ = helper_->getpeername(); @@ -118,7 +91,7 @@ void APIConnection::loop() { // when network is disconnected force disconnect immediately // don't wait for timeout this->on_fatal_error(); - ESP_LOGW(TAG, "%s: Network unavailable, disconnecting", this->client_combined_info_.c_str()); + ESP_LOGW(TAG, "%s: Network unavailable; disconnecting", this->get_client_combined_info().c_str()); return; } if (this->next_close_) { @@ -131,7 +104,7 @@ void APIConnection::loop() { APIError err = this->helper_->loop(); if (err != APIError::OK) { on_fatal_error(); - ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(), + ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(), api_error_to_str(err), errno); return; } @@ -145,12 +118,12 @@ void APIConnection::loop() { } else if (err != APIError::OK) { on_fatal_error(); if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) { - ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str()); + ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str()); } else if (err == APIError::CONNECTION_CLOSED) { - ESP_LOGW(TAG, "%s: Connection closed", this->client_combined_info_.c_str()); + ESP_LOGW(TAG, "%s: Connection closed", this->get_client_combined_info().c_str()); } else { - ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err), - errno); + ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->get_client_combined_info().c_str(), + api_error_to_str(err), errno); } return; } else { @@ -166,8 +139,10 @@ void APIConnection::loop() { } } - if (!this->deferred_message_queue_.empty() && this->helper_->can_write_without_blocking()) { - this->deferred_message_queue_.process_queue(); + // Process deferred batch if scheduled + if (this->deferred_batch_.batch_scheduled && + App.get_loop_component_start_time() - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) { + this->process_batch_(); } if (!this->list_entities_iterator_.completed()) @@ -182,42 +157,30 @@ void APIConnection::loop() { // Disconnect if not responded within 2.5*keepalive if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) { on_fatal_error(); - ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_combined_info_.c_str()); + ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->get_client_combined_info().c_str()); } } else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && now > this->next_ping_retry_) { - ESP_LOGVV(TAG, "Sending keepalive PING..."); - this->sent_ping_ = this->send_ping_request(PingRequest()); + ESP_LOGVV(TAG, "Sending keepalive PING"); + this->sent_ping_ = this->send_message(PingRequest()); if (!this->sent_ping_) { this->next_ping_retry_ = now + ping_retry_interval; this->ping_retries_++; + std::string warn_str = str_sprintf("%s: Sending keepalive failed %u time(s);", + this->get_client_combined_info().c_str(), this->ping_retries_); if (this->ping_retries_ >= max_ping_retries) { on_fatal_error(); - ESP_LOGE(TAG, "%s: Sending keepalive failed %d time(s). Disconnecting...", this->client_combined_info_.c_str(), - this->ping_retries_); + ESP_LOGE(TAG, "%s disconnecting", warn_str.c_str()); } else if (this->ping_retries_ >= 10) { - ESP_LOGW(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms", - this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval); + ESP_LOGW(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval); } else { - ESP_LOGD(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms", - this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval); + ESP_LOGD(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval); } } } #ifdef USE_ESP32_CAMERA if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) { - // Message will use 8 more bytes than the minimum size, and typical - // MTU is 1500. Sometimes users will see as low as 1460 MTU. - // If its IPv6 the header is 40 bytes, and if its IPv4 - // the header is 20 bytes. So we have 1460 - 40 = 1420 bytes - // available for the payload. But we also need to add the size of - // the protobuf overhead, which is 8 bytes. - // - // To be safe we pick 1390 bytes as the maximum size - // to send in one go. This is the maximum size of a single packet - // that can be sent over the network. - // This is to avoid fragmentation of the packet. - uint32_t to_send = std::min((size_t) 1390, this->image_reader_.available()); + uint32_t to_send = std::min((size_t) MAX_PACKET_SIZE, this->image_reader_.available()); bool done = this->image_reader_.available() == to_send; uint32_t msg_size = 0; ProtoSize::add_fixed_field<4>(msg_size, 1, true); @@ -255,7 +218,7 @@ void APIConnection::loop() { resp.entity_id = it.entity_id; resp.attribute = it.attribute.value(); resp.once = it.once; - if (this->send_subscribe_home_assistant_state_response(resp)) { + if (this->send_message(resp)) { state_subs_at_++; } } @@ -270,55 +233,101 @@ DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) { // remote initiated disconnect_client // don't close yet, we still need to send the disconnect response // close will happen on next loop - ESP_LOGD(TAG, "%s requested disconnected", this->client_combined_info_.c_str()); + ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str()); this->next_close_ = true; DisconnectResponse resp; return resp; } void APIConnection::on_disconnect_response(const DisconnectResponse &value) { - // pass + this->helper_->close(); + this->remove_ = true; +} + +// Encodes a message to the buffer and returns the total number of bytes used, +// including header and footer overhead. Returns 0 if the message doesn't fit. +uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn, + uint32_t remaining_size, bool is_single) { + // Calculate size + uint32_t calculated_size = 0; + msg.calculate_size(calculated_size); + + // Cache frame sizes to avoid repeated virtual calls + const uint8_t header_padding = conn->helper_->frame_header_padding(); + const uint8_t footer_size = conn->helper_->frame_footer_size(); + + // Calculate total size with padding for buffer allocation + size_t total_calculated_size = calculated_size + header_padding + footer_size; + + // Check if it fits + if (total_calculated_size > remaining_size) { + return 0; // Doesn't fit + } + + // Allocate buffer space - pass payload size, allocation functions add header/footer space + ProtoWriteBuffer buffer = is_single ? conn->allocate_single_message_buffer(calculated_size) + : conn->allocate_batch_message_buffer(calculated_size); + + // Get buffer size after allocation (which includes header padding) + std::vector &shared_buf = conn->parent_->get_shared_buffer_ref(); + size_t size_before_encode = shared_buf.size(); + + // Encode directly into buffer + 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(actual_total_size); } #ifdef USE_BINARY_SENSOR -bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) { - return this->send_state_with_value_(binary_sensor, &APIConnection::try_send_binary_sensor_state_, - &APIConnection::try_send_binary_sensor_state_, state); +bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor) { + return this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_state, + BinarySensorStateResponse::MESSAGE_TYPE); } void APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) { - this->send_info_(static_cast(binary_sensor), - reinterpret_cast(&APIConnection::try_send_binary_sensor_info_)); + this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_info, + ListEntitiesBinarySensorResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor) { - return this->try_send_binary_sensor_state_(binary_sensor, binary_sensor->state); + +uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *binary_sensor = static_cast(entity); + BinarySensorStateResponse resp; + resp.state = binary_sensor->state; + resp.missing_state = !binary_sensor->has_state(); + fill_entity_state_base(binary_sensor, resp); + return encode_message_to_buffer(resp, BinarySensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor, bool state) { - BinarySensorStateResponse msg; - msg.state = state; - msg.missing_state = !binary_sensor->has_state(); - msg.key = binary_sensor->get_object_id_hash(); - return this->send_binary_sensor_state_response(msg); -} -bool APIConnection::try_send_binary_sensor_info_(binary_sensor::BinarySensor *binary_sensor) { + +uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *binary_sensor = static_cast(entity); ListEntitiesBinarySensorResponse msg; msg.device_class = binary_sensor->get_device_class(); msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor(); msg.device_uid = binary_sensor->get_device_uid(); msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor); - return this->try_send_entity_info_(static_cast(binary_sensor), msg, - &APIConnection::send_list_entities_binary_sensor_response); + fill_entity_info_base(binary_sensor, msg); + return encode_message_to_buffer(msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } #endif #ifdef USE_COVER bool APIConnection::send_cover_state(cover::Cover *cover) { - return this->send_state_(static_cast(cover), - reinterpret_cast(&APIConnection::try_send_cover_state_)); + return this->schedule_message_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE); } void APIConnection::send_cover_info(cover::Cover *cover) { - this->send_info_(static_cast(cover), - reinterpret_cast(&APIConnection::try_send_cover_info_)); + this->schedule_message_(cover, &APIConnection::try_send_cover_info, ListEntitiesCoverResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_cover_state_(cover::Cover *cover) { +uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *cover = static_cast(entity); CoverStateResponse msg; auto traits = cover->get_traits(); msg.legacy_state = @@ -327,10 +336,12 @@ bool APIConnection::try_send_cover_state_(cover::Cover *cover) { if (traits.get_supports_tilt()) msg.tilt = cover->tilt; msg.current_operation = static_cast(cover->current_operation); - msg.key = cover->get_object_id_hash(); - return this->send_cover_state_response(msg); + fill_entity_state_base(cover, msg); + return encode_message_to_buffer(msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_cover_info_(cover::Cover *cover) { +uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *cover = static_cast(entity); ListEntitiesCoverResponse msg; auto traits = cover->get_traits(); msg.assumed_state = traits.get_is_assumed_state(); @@ -340,8 +351,8 @@ bool APIConnection::try_send_cover_info_(cover::Cover *cover) { msg.device_class = cover->get_device_class(); msg.device_uid = cover->get_device_uid(); msg.unique_id = get_default_unique_id("cover", cover); - return this->try_send_entity_info_(static_cast(cover), msg, - &APIConnection::send_list_entities_cover_response); + fill_entity_info_base(cover, msg); + return encode_message_to_buffer(msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::cover_command(const CoverCommandRequest &msg) { cover::Cover *cover = App.get_cover_by_key(msg.key); @@ -374,14 +385,14 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) { #ifdef USE_FAN bool APIConnection::send_fan_state(fan::Fan *fan) { - return this->send_state_(static_cast(fan), - reinterpret_cast(&APIConnection::try_send_fan_state_)); + return this->schedule_message_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE); } void APIConnection::send_fan_info(fan::Fan *fan) { - this->send_info_(static_cast(fan), - reinterpret_cast(&APIConnection::try_send_fan_info_)); + this->schedule_message_(fan, &APIConnection::try_send_fan_info, ListEntitiesFanResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_fan_state_(fan::Fan *fan) { +uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *fan = static_cast(entity); FanStateResponse msg; auto traits = fan->get_traits(); msg.state = fan->state; @@ -394,10 +405,12 @@ bool APIConnection::try_send_fan_state_(fan::Fan *fan) { msg.direction = static_cast(fan->direction); if (traits.supports_preset_modes()) msg.preset_mode = fan->preset_mode; - msg.key = fan->get_object_id_hash(); - return this->send_fan_state_response(msg); + fill_entity_state_base(fan, msg); + return encode_message_to_buffer(msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_fan_info_(fan::Fan *fan) { +uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *fan = static_cast(entity); ListEntitiesFanResponse msg; auto traits = fan->get_traits(); msg.supports_oscillation = traits.supports_oscillation(); @@ -408,8 +421,8 @@ bool APIConnection::try_send_fan_info_(fan::Fan *fan) { msg.supported_preset_modes.push_back(preset); msg.device_uid = fan->get_device_uid(); msg.unique_id = get_default_unique_id("fan", fan); - return this->try_send_entity_info_(static_cast(fan), msg, - &APIConnection::send_list_entities_fan_response); + fill_entity_info_base(fan, msg); + return encode_message_to_buffer(msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::fan_command(const FanCommandRequest &msg) { fan::Fan *fan = App.get_fan_by_key(msg.key); @@ -435,14 +448,14 @@ void APIConnection::fan_command(const FanCommandRequest &msg) { #ifdef USE_LIGHT bool APIConnection::send_light_state(light::LightState *light) { - return this->send_state_(static_cast(light), - reinterpret_cast(&APIConnection::try_send_light_state_)); + return this->schedule_message_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE); } void APIConnection::send_light_info(light::LightState *light) { - this->send_info_(static_cast(light), - reinterpret_cast(&APIConnection::try_send_light_info_)); + this->schedule_message_(light, &APIConnection::try_send_light_info, ListEntitiesLightResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_light_state_(light::LightState *light) { +uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *light = static_cast(entity); LightStateResponse resp; auto traits = light->get_traits(); auto values = light->remote_values; @@ -460,10 +473,12 @@ bool APIConnection::try_send_light_state_(light::LightState *light) { resp.warm_white = values.get_warm_white(); if (light->supports_effects()) resp.effect = light->get_effect_name(); - resp.key = light->get_object_id_hash(); - return this->send_light_state_response(resp); + fill_entity_state_base(light, resp); + return encode_message_to_buffer(resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_light_info_(light::LightState *light) { +uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *light = static_cast(entity); ListEntitiesLightResponse msg; auto traits = light->get_traits(); for (auto mode : traits.get_supported_color_modes()) @@ -487,8 +502,8 @@ bool APIConnection::try_send_light_info_(light::LightState *light) { } msg.device_uid = light->get_device_uid(); msg.unique_id = get_default_unique_id("light", light); - return this->try_send_entity_info_(static_cast(light), msg, - &APIConnection::send_list_entities_light_response); + fill_entity_info_base(light, msg); + return encode_message_to_buffer(msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::light_command(const LightCommandRequest &msg) { light::LightState *light = App.get_light_by_key(msg.key); @@ -528,26 +543,26 @@ void APIConnection::light_command(const LightCommandRequest &msg) { #endif #ifdef USE_SENSOR -bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) { - return this->send_state_with_value_(sensor, &APIConnection::try_send_sensor_state_, - &APIConnection::try_send_sensor_state_, state); +bool APIConnection::send_sensor_state(sensor::Sensor *sensor) { + return this->schedule_message_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE); } void APIConnection::send_sensor_info(sensor::Sensor *sensor) { - this->send_info_(static_cast(sensor), - reinterpret_cast(&APIConnection::try_send_sensor_info_)); + this->schedule_message_(sensor, &APIConnection::try_send_sensor_info, ListEntitiesSensorResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor) { - return this->try_send_sensor_state_(sensor, sensor->state); -} -bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor, float state) { - SensorStateResponse resp; - resp.state = state; - resp.missing_state = !sensor->has_state(); - resp.key = sensor->get_object_id_hash(); - return this->send_sensor_state_response(resp); +uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *sensor = static_cast(entity); + SensorStateResponse resp; + resp.state = sensor->state; + resp.missing_state = !sensor->has_state(); + fill_entity_state_base(sensor, resp); + return encode_message_to_buffer(resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_sensor_info_(sensor::Sensor *sensor) { + +uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *sensor = static_cast(entity); ListEntitiesSensorResponse msg; msg.unit_of_measurement = sensor->get_unit_of_measurement(); msg.accuracy_decimals = sensor->get_accuracy_decimals(); @@ -558,38 +573,38 @@ bool APIConnection::try_send_sensor_info_(sensor::Sensor *sensor) { msg.unique_id = sensor->unique_id(); if (msg.unique_id.empty()) msg.unique_id = get_default_unique_id("sensor", sensor); - return this->try_send_entity_info_(static_cast(sensor), msg, - &APIConnection::send_list_entities_sensor_response); + fill_entity_info_base(sensor, msg); + return encode_message_to_buffer(msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } #endif #ifdef USE_SWITCH -bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) { - return this->send_state_with_value_(a_switch, &APIConnection::try_send_switch_state_, - &APIConnection::try_send_switch_state_, state); +bool APIConnection::send_switch_state(switch_::Switch *a_switch) { + return this->schedule_message_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE); } void APIConnection::send_switch_info(switch_::Switch *a_switch) { - this->send_info_(static_cast(a_switch), - reinterpret_cast(&APIConnection::try_send_switch_info_)); + this->schedule_message_(a_switch, &APIConnection::try_send_switch_info, ListEntitiesSwitchResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch) { - return this->try_send_switch_state_(a_switch, a_switch->state); -} -bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch, bool state) { - SwitchStateResponse resp; - resp.state = state; - resp.key = a_switch->get_object_id_hash(); - return this->send_switch_state_response(resp); +uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *a_switch = static_cast(entity); + SwitchStateResponse resp; + resp.state = a_switch->state; + fill_entity_state_base(a_switch, resp); + return encode_message_to_buffer(resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_switch_info_(switch_::Switch *a_switch) { + +uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *a_switch = static_cast(entity); ListEntitiesSwitchResponse msg; msg.assumed_state = a_switch->assumed_state(); msg.device_class = a_switch->get_device_class(); msg.device_uid = a_switch->get_device_uid(); msg.unique_id = get_default_unique_id("switch", a_switch); - return this->try_send_entity_info_(static_cast(a_switch), msg, - &APIConnection::send_list_entities_switch_response); + fill_entity_info_base(a_switch, msg); + return encode_message_to_buffer(msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::switch_command(const SwitchCommandRequest &msg) { switch_::Switch *a_switch = App.get_switch_by_key(msg.key); @@ -605,49 +620,47 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) { #endif #ifdef USE_TEXT_SENSOR -bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state) { - return this->send_state_with_value_(text_sensor, &APIConnection::try_send_text_sensor_state_, - &APIConnection::try_send_text_sensor_state_, std::move(state)); +bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor) { + return this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_state, + TextSensorStateResponse::MESSAGE_TYPE); } void APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) { - this->send_info_(static_cast(text_sensor), - reinterpret_cast(&APIConnection::try_send_text_sensor_info_)); + this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_info, + ListEntitiesTextSensorResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor) { - return this->try_send_text_sensor_state_(text_sensor, text_sensor->state); -} -bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor, std::string state) { - TextSensorStateResponse resp; - resp.state = std::move(state); - resp.missing_state = !text_sensor->has_state(); - resp.key = text_sensor->get_object_id_hash(); - return this->send_text_sensor_state_response(resp); +uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *text_sensor = static_cast(entity); + TextSensorStateResponse resp; + resp.state = text_sensor->state; + resp.missing_state = !text_sensor->has_state(); + fill_entity_state_base(text_sensor, resp); + return encode_message_to_buffer(resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_text_sensor_info_(text_sensor::TextSensor *text_sensor) { +uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *text_sensor = static_cast(entity); ListEntitiesTextSensorResponse msg; msg.device_class = text_sensor->get_device_class(); msg.unique_id = text_sensor->unique_id(); msg.device_uid = text_sensor->get_device_uid(); if (msg.unique_id.empty()) msg.unique_id = get_default_unique_id("text_sensor", text_sensor); - return this->try_send_entity_info_(static_cast(text_sensor), msg, - &APIConnection::send_list_entities_text_sensor_response); + fill_entity_info_base(text_sensor, msg); + return encode_message_to_buffer(msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } #endif #ifdef USE_CLIMATE bool APIConnection::send_climate_state(climate::Climate *climate) { - return this->send_state_(static_cast(climate), - reinterpret_cast(&APIConnection::try_send_climate_state_)); + return this->schedule_message_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE); } -void APIConnection::send_climate_info(climate::Climate *climate) { - this->send_info_(static_cast(climate), - reinterpret_cast(&APIConnection::try_send_climate_info_)); -} -bool APIConnection::try_send_climate_state_(climate::Climate *climate) { +uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *climate = static_cast(entity); ClimateStateResponse resp; - resp.key = climate->get_object_id_hash(); + fill_entity_state_base(climate, resp); auto traits = climate->get_traits(); resp.mode = static_cast(climate->mode); resp.action = static_cast(climate->action); @@ -674,9 +687,14 @@ bool APIConnection::try_send_climate_state_(climate::Climate *climate) { resp.current_humidity = climate->current_humidity; if (traits.get_supports_target_humidity()) resp.target_humidity = climate->target_humidity; - return this->send_climate_state_response(resp); + return encode_message_to_buffer(resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_climate_info_(climate::Climate *climate) { +void APIConnection::send_climate_info(climate::Climate *climate) { + this->schedule_message_(climate, &APIConnection::try_send_climate_info, ListEntitiesClimateResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *climate = static_cast(entity); ListEntitiesClimateResponse msg; auto traits = climate->get_traits(); msg.supports_current_temperature = traits.get_supports_current_temperature(); @@ -705,8 +723,8 @@ bool APIConnection::try_send_climate_info_(climate::Climate *climate) { msg.supported_swing_modes.push_back(static_cast(swing_mode)); msg.device_uid = climate->get_device_uid(); msg.unique_id = get_default_unique_id("climate", climate); - return this->try_send_entity_info_(static_cast(climate), msg, - &APIConnection::send_list_entities_climate_response); + fill_entity_info_base(climate, msg); + return encode_message_to_buffer(msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::climate_command(const ClimateCommandRequest &msg) { climate::Climate *climate = App.get_climate_by_key(msg.key); @@ -739,26 +757,26 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) { #endif #ifdef USE_NUMBER -bool APIConnection::send_number_state(number::Number *number, float state) { - return this->send_state_with_value_(number, &APIConnection::try_send_number_state_, - &APIConnection::try_send_number_state_, state); +bool APIConnection::send_number_state(number::Number *number) { + return this->schedule_message_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE); } void APIConnection::send_number_info(number::Number *number) { - this->send_info_(static_cast(number), - reinterpret_cast(&APIConnection::try_send_number_info_)); + this->schedule_message_(number, &APIConnection::try_send_number_info, ListEntitiesNumberResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_number_state_(number::Number *number) { - return this->try_send_number_state_(number, number->state); -} -bool APIConnection::try_send_number_state_(number::Number *number, float state) { - NumberStateResponse resp; - resp.state = state; - resp.missing_state = !number->has_state(); - resp.key = number->get_object_id_hash(); - return this->send_number_state_response(resp); +uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *number = static_cast(entity); + NumberStateResponse resp; + resp.state = number->state; + resp.missing_state = !number->has_state(); + fill_entity_state_base(number, resp); + return encode_message_to_buffer(resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_number_info_(number::Number *number) { + +uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *number = static_cast(entity); ListEntitiesNumberResponse msg; msg.unit_of_measurement = number->traits.get_unit_of_measurement(); msg.mode = static_cast(number->traits.get_mode()); @@ -768,8 +786,8 @@ bool APIConnection::try_send_number_info_(number::Number *number) { msg.step = number->traits.get_step(); msg.device_uid = number->get_device_uid(); msg.unique_id = get_default_unique_id("number", number); - return this->try_send_entity_info_(static_cast(number), msg, - &APIConnection::send_list_entities_number_response); + fill_entity_info_base(number, msg); + return encode_message_to_buffer(msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::number_command(const NumberCommandRequest &msg) { number::Number *number = App.get_number_by_key(msg.key); @@ -784,29 +802,30 @@ void APIConnection::number_command(const NumberCommandRequest &msg) { #ifdef USE_DATETIME_DATE bool APIConnection::send_date_state(datetime::DateEntity *date) { - return this->send_state_(static_cast(date), - reinterpret_cast(&APIConnection::try_send_date_state_)); + return this->schedule_message_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE); } -void APIConnection::send_date_info(datetime::DateEntity *date) { - this->send_info_(static_cast(date), - reinterpret_cast(&APIConnection::try_send_date_info_)); -} -bool APIConnection::try_send_date_state_(datetime::DateEntity *date) { +uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *date = static_cast(entity); DateStateResponse resp; resp.missing_state = !date->has_state(); resp.year = date->year; resp.month = date->month; resp.day = date->day; - - resp.key = date->get_object_id_hash(); - return this->send_date_state_response(resp); + fill_entity_state_base(date, resp); + return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_date_info_(datetime::DateEntity *date) { +void APIConnection::send_date_info(datetime::DateEntity *date) { + this->schedule_message_(date, &APIConnection::try_send_date_info, ListEntitiesDateResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *date = static_cast(entity); ListEntitiesDateResponse msg; msg.device_uid = date->get_device_uid(); msg.unique_id = get_default_unique_id("date", date); - return this->try_send_entity_info_(static_cast(date), msg, - &APIConnection::send_list_entities_date_response); + fill_entity_info_base(date, msg); + return encode_message_to_buffer(msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::date_command(const DateCommandRequest &msg) { datetime::DateEntity *date = App.get_date_by_key(msg.key); @@ -821,29 +840,30 @@ void APIConnection::date_command(const DateCommandRequest &msg) { #ifdef USE_DATETIME_TIME bool APIConnection::send_time_state(datetime::TimeEntity *time) { - return this->send_state_(static_cast(time), - reinterpret_cast(&APIConnection::try_send_time_state_)); + return this->schedule_message_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE); } -void APIConnection::send_time_info(datetime::TimeEntity *time) { - this->send_info_(static_cast(time), - reinterpret_cast(&APIConnection::try_send_time_info_)); -} -bool APIConnection::try_send_time_state_(datetime::TimeEntity *time) { +uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *time = static_cast(entity); TimeStateResponse resp; resp.missing_state = !time->has_state(); resp.hour = time->hour; resp.minute = time->minute; resp.second = time->second; - - resp.key = time->get_object_id_hash(); - return this->send_time_state_response(resp); + fill_entity_state_base(time, resp); + return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_time_info_(datetime::TimeEntity *time) { +void APIConnection::send_time_info(datetime::TimeEntity *time) { + this->schedule_message_(time, &APIConnection::try_send_time_info, ListEntitiesTimeResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *time = static_cast(entity); ListEntitiesTimeResponse msg; msg.device_uid = time->get_device_uid(); msg.unique_id = get_default_unique_id("time", time); - return this->try_send_entity_info_(static_cast(time), msg, - &APIConnection::send_list_entities_time_response); + fill_entity_info_base(time, msg); + return encode_message_to_buffer(msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::time_command(const TimeCommandRequest &msg) { datetime::TimeEntity *time = App.get_time_by_key(msg.key); @@ -858,30 +878,32 @@ void APIConnection::time_command(const TimeCommandRequest &msg) { #ifdef USE_DATETIME_DATETIME bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) { - return this->send_state_(static_cast(datetime), - reinterpret_cast(&APIConnection::try_send_datetime_state_)); + return this->schedule_message_(datetime, &APIConnection::try_send_datetime_state, + DateTimeStateResponse::MESSAGE_TYPE); } -void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) { - this->send_info_(static_cast(datetime), - reinterpret_cast(&APIConnection::try_send_datetime_info_)); -} -bool APIConnection::try_send_datetime_state_(datetime::DateTimeEntity *datetime) { +uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *datetime = static_cast(entity); DateTimeStateResponse resp; resp.missing_state = !datetime->has_state(); if (datetime->has_state()) { ESPTime state = datetime->state_as_esptime(); resp.epoch_seconds = state.timestamp; } - - resp.key = datetime->get_object_id_hash(); - return this->send_date_time_state_response(resp); + fill_entity_state_base(datetime, resp); + return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_datetime_info_(datetime::DateTimeEntity *datetime) { +void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) { + this->schedule_message_(datetime, &APIConnection::try_send_datetime_info, ListEntitiesDateTimeResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *datetime = static_cast(entity); ListEntitiesDateTimeResponse msg; msg.device_uid = datetime->get_device_uid(); msg.unique_id = get_default_unique_id("datetime", datetime); - return this->try_send_entity_info_(static_cast(datetime), msg, - &APIConnection::send_list_entities_date_time_response); + fill_entity_info_base(datetime, msg); + return encode_message_to_buffer(msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::datetime_command(const DateTimeCommandRequest &msg) { datetime::DateTimeEntity *datetime = App.get_datetime_by_key(msg.key); @@ -895,24 +917,26 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) { #endif #ifdef USE_TEXT -bool APIConnection::send_text_state(text::Text *text, std::string state) { - return this->send_state_with_value_(text, &APIConnection::try_send_text_state_, &APIConnection::try_send_text_state_, - std::move(state)); +bool APIConnection::send_text_state(text::Text *text) { + return this->schedule_message_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE); } void APIConnection::send_text_info(text::Text *text) { - this->send_info_(static_cast(text), - reinterpret_cast(&APIConnection::try_send_text_info_)); + this->schedule_message_(text, &APIConnection::try_send_text_info, ListEntitiesTextResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_text_state_(text::Text *text) { return this->try_send_text_state_(text, text->state); } -bool APIConnection::try_send_text_state_(text::Text *text, std::string state) { - TextStateResponse resp; - resp.state = std::move(state); - resp.missing_state = !text->has_state(); - resp.key = text->get_object_id_hash(); - return this->send_text_state_response(resp); +uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *text = static_cast(entity); + TextStateResponse resp; + resp.state = text->state; + resp.missing_state = !text->has_state(); + fill_entity_state_base(text, resp); + return encode_message_to_buffer(resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_text_info_(text::Text *text) { + +uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *text = static_cast(entity); ListEntitiesTextResponse msg; msg.mode = static_cast(text->traits.get_mode()); msg.min_length = text->traits.get_min_length(); @@ -920,8 +944,8 @@ bool APIConnection::try_send_text_info_(text::Text *text) { msg.pattern = text->traits.get_pattern(); msg.device_uid = text->get_device_uid(); msg.unique_id = get_default_unique_id("text", text); - return this->try_send_entity_info_(static_cast(text), msg, - &APIConnection::send_list_entities_text_response); + fill_entity_info_base(text, msg); + return encode_message_to_buffer(msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::text_command(const TextCommandRequest &msg) { text::Text *text = App.get_text_by_key(msg.key); @@ -935,33 +959,33 @@ void APIConnection::text_command(const TextCommandRequest &msg) { #endif #ifdef USE_SELECT -bool APIConnection::send_select_state(select::Select *select, std::string state) { - return this->send_state_with_value_(select, &APIConnection::try_send_select_state_, - &APIConnection::try_send_select_state_, std::move(state)); +bool APIConnection::send_select_state(select::Select *select) { + return this->schedule_message_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE); } void APIConnection::send_select_info(select::Select *select) { - this->send_info_(static_cast(select), - reinterpret_cast(&APIConnection::try_send_select_info_)); + this->schedule_message_(select, &APIConnection::try_send_select_info, ListEntitiesSelectResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_select_state_(select::Select *select) { - return this->try_send_select_state_(select, select->state); -} -bool APIConnection::try_send_select_state_(select::Select *select, std::string state) { - SelectStateResponse resp; - resp.state = std::move(state); - resp.missing_state = !select->has_state(); - resp.key = select->get_object_id_hash(); - return this->send_select_state_response(resp); +uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *select = static_cast(entity); + SelectStateResponse resp; + resp.state = select->state; + resp.missing_state = !select->has_state(); + fill_entity_state_base(select, resp); + return encode_message_to_buffer(resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_select_info_(select::Select *select) { + +uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *select = static_cast(entity); ListEntitiesSelectResponse msg; for (const auto &option : select->traits.get_options()) msg.options.push_back(option); msg.device_uid = select->get_device_uid(); msg.unique_id = get_default_unique_id("select", select); - return this->try_send_entity_info_(static_cast(select), msg, - &APIConnection::send_list_entities_select_response); + fill_entity_info_base(select, msg); + return encode_message_to_buffer(msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::select_command(const SelectCommandRequest &msg) { select::Select *select = App.get_select_by_key(msg.key); @@ -976,16 +1000,17 @@ void APIConnection::select_command(const SelectCommandRequest &msg) { #ifdef USE_BUTTON void esphome::api::APIConnection::send_button_info(button::Button *button) { - this->send_info_(static_cast(button), - reinterpret_cast(&APIConnection::try_send_button_info_)); + this->schedule_message_(button, &APIConnection::try_send_button_info, ListEntitiesButtonResponse::MESSAGE_TYPE); } -bool esphome::api::APIConnection::try_send_button_info_(button::Button *button) { +uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *button = static_cast(entity); ListEntitiesButtonResponse msg; msg.device_class = button->get_device_class(); msg.device_uid = button->get_device_uid(); msg.unique_id = get_default_unique_id("button", button); - return this->try_send_entity_info_(static_cast(button), msg, - &APIConnection::send_list_entities_button_response); + fill_entity_info_base(button, msg); + return encode_message_to_buffer(msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) { button::Button *button = App.get_button_by_key(msg.key); @@ -997,33 +1022,33 @@ void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg #endif #ifdef USE_LOCK -bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) { - return this->send_state_with_value_(a_lock, &APIConnection::try_send_lock_state_, - &APIConnection::try_send_lock_state_, state); +bool APIConnection::send_lock_state(lock::Lock *a_lock) { + return this->schedule_message_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE); } void APIConnection::send_lock_info(lock::Lock *a_lock) { - this->send_info_(static_cast(a_lock), - reinterpret_cast(&APIConnection::try_send_lock_info_)); + this->schedule_message_(a_lock, &APIConnection::try_send_lock_info, ListEntitiesLockResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_lock_state_(lock::Lock *a_lock) { - return this->try_send_lock_state_(a_lock, a_lock->state); -} -bool APIConnection::try_send_lock_state_(lock::Lock *a_lock, lock::LockState state) { - LockStateResponse resp; - resp.state = static_cast(state); - resp.key = a_lock->get_object_id_hash(); - return this->send_lock_state_response(resp); +uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *a_lock = static_cast(entity); + LockStateResponse resp; + resp.state = static_cast(a_lock->state); + fill_entity_state_base(a_lock, resp); + return encode_message_to_buffer(resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_lock_info_(lock::Lock *a_lock) { + +uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *a_lock = static_cast(entity); ListEntitiesLockResponse msg; msg.assumed_state = a_lock->traits.get_assumed_state(); msg.supports_open = a_lock->traits.get_supports_open(); msg.requires_code = a_lock->traits.get_requires_code(); msg.device_uid = a_lock->get_device_uid(); msg.unique_id = get_default_unique_id("lock", a_lock); - return this->try_send_entity_info_(static_cast(a_lock), msg, - &APIConnection::send_list_entities_lock_response); + fill_entity_info_base(a_lock, msg); + return encode_message_to_buffer(msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::lock_command(const LockCommandRequest &msg) { lock::Lock *a_lock = App.get_lock_by_key(msg.key); @@ -1046,22 +1071,23 @@ void APIConnection::lock_command(const LockCommandRequest &msg) { #ifdef USE_VALVE bool APIConnection::send_valve_state(valve::Valve *valve) { - return this->send_state_(static_cast(valve), - reinterpret_cast(&APIConnection::try_send_valve_state_)); + return this->schedule_message_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE); } -void APIConnection::send_valve_info(valve::Valve *valve) { - this->send_info_(static_cast(valve), - reinterpret_cast(&APIConnection::try_send_valve_info_)); -} -bool APIConnection::try_send_valve_state_(valve::Valve *valve) { +uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *valve = static_cast(entity); ValveStateResponse resp; resp.position = valve->position; resp.current_operation = static_cast(valve->current_operation); - - resp.key = valve->get_object_id_hash(); - return this->send_valve_state_response(resp); + fill_entity_state_base(valve, resp); + return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_valve_info_(valve::Valve *valve) { +void APIConnection::send_valve_info(valve::Valve *valve) { + this->schedule_message_(valve, &APIConnection::try_send_valve_info, ListEntitiesValveResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *valve = static_cast(entity); ListEntitiesValveResponse msg; auto traits = valve->get_traits(); msg.device_class = valve->get_device_class(); @@ -1070,8 +1096,8 @@ bool APIConnection::try_send_valve_info_(valve::Valve *valve) { msg.supports_stop = traits.get_supports_stop(); msg.device_uid = valve->get_device_uid(); msg.unique_id = get_default_unique_id("valve", valve); - return this->try_send_entity_info_(static_cast(valve), msg, - &APIConnection::send_list_entities_valve_response); + fill_entity_info_base(valve, msg); + return encode_message_to_buffer(msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::valve_command(const ValveCommandRequest &msg) { valve::Valve *valve = App.get_valve_by_key(msg.key); @@ -1089,14 +1115,12 @@ void APIConnection::valve_command(const ValveCommandRequest &msg) { #ifdef USE_MEDIA_PLAYER bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) { - return this->send_state_(static_cast(media_player), - reinterpret_cast(&APIConnection::try_send_media_player_state_)); + return this->schedule_message_(media_player, &APIConnection::try_send_media_player_state, + MediaPlayerStateResponse::MESSAGE_TYPE); } -void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) { - this->send_info_(static_cast(media_player), - reinterpret_cast(&APIConnection::try_send_media_player_info_)); -} -bool APIConnection::try_send_media_player_state_(media_player::MediaPlayer *media_player) { +uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *media_player = static_cast(entity); MediaPlayerStateResponse resp; media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING ? media_player::MEDIA_PLAYER_STATE_PLAYING @@ -1104,11 +1128,16 @@ bool APIConnection::try_send_media_player_state_(media_player::MediaPlayer *medi resp.state = static_cast(report_state); resp.volume = media_player->volume; resp.muted = media_player->is_muted(); - - resp.key = media_player->get_object_id_hash(); - return this->send_media_player_state_response(resp); + fill_entity_state_base(media_player, resp); + return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_media_player_info_(media_player::MediaPlayer *media_player) { +void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) { + this->schedule_message_(media_player, &APIConnection::try_send_media_player_info, + ListEntitiesMediaPlayerResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *media_player = static_cast(entity); ListEntitiesMediaPlayerResponse msg; auto traits = media_player->get_traits(); msg.supports_pause = traits.get_supports_pause(); @@ -1123,8 +1152,8 @@ bool APIConnection::try_send_media_player_info_(media_player::MediaPlayer *media } msg.device_uid = media_player->get_device_uid(); msg.unique_id = get_default_unique_id("media_player", media_player); - return this->try_send_entity_info_(static_cast(media_player), msg, - &APIConnection::send_list_entities_media_player_response); + fill_entity_info_base(media_player, msg); + return encode_message_to_buffer(msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) { media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key); @@ -1159,15 +1188,16 @@ void APIConnection::set_camera_state(std::shared_ptr this->image_reader_.set_image(std::move(image)); } void APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { - this->send_info_(static_cast(camera), - reinterpret_cast(&APIConnection::try_send_camera_info_)); + this->schedule_message_(camera, &APIConnection::try_send_camera_info, ListEntitiesCameraResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_camera_info_(esp32_camera::ESP32Camera *camera) { +uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *camera = static_cast(entity); ListEntitiesCameraResponse msg; msg.device_uid = camera->get_device_uid(); msg.unique_id = get_default_unique_id("camera", camera); - return this->try_send_entity_info_(static_cast(camera), msg, - &APIConnection::send_list_entities_camera_response); + fill_entity_info_base(camera, msg); + return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::camera_image(const CameraImageRequest &msg) { if (esp32_camera::global_esp32_camera == nullptr) @@ -1210,9 +1240,9 @@ bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertiseme manufacturer_data.legacy_data.assign(manufacturer_data.data.begin(), manufacturer_data.data.end()); manufacturer_data.data.clear(); } - return this->send_bluetooth_le_advertisement_response(resp); + return this->send_message(resp); } - return this->send_bluetooth_le_advertisement_response(msg); + return this->send_message(msg); } void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) { bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg); @@ -1356,29 +1386,33 @@ void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetCon #ifdef USE_ALARM_CONTROL_PANEL bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { - return this->send_state_(static_cast(a_alarm_control_panel), - reinterpret_cast(&APIConnection::try_send_alarm_control_panel_state_)); + return this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_state, + AlarmControlPanelStateResponse::MESSAGE_TYPE); } -void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { - this->send_info_(static_cast(a_alarm_control_panel), - reinterpret_cast(&APIConnection::try_send_alarm_control_panel_info_)); -} -bool APIConnection::try_send_alarm_control_panel_state_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { +uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, + uint32_t remaining_size, bool is_single) { + auto *a_alarm_control_panel = static_cast(entity); AlarmControlPanelStateResponse resp; resp.state = static_cast(a_alarm_control_panel->get_state()); - - resp.key = a_alarm_control_panel->get_object_id_hash(); - return this->send_alarm_control_panel_state_response(resp); + fill_entity_state_base(a_alarm_control_panel, resp); + return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_alarm_control_panel_info_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { +void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { + this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_info, + ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn, + uint32_t remaining_size, bool is_single) { + auto *a_alarm_control_panel = static_cast(entity); ListEntitiesAlarmControlPanelResponse msg; msg.supported_features = a_alarm_control_panel->get_supported_features(); msg.requires_code = a_alarm_control_panel->get_requires_code(); msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm(); msg.device_uid = a_alarm_control_panel->get_device_uid(); msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel); - return this->try_send_entity_info_(static_cast(a_alarm_control_panel), msg, - &APIConnection::send_list_entities_alarm_control_panel_response); + fill_entity_info_base(a_alarm_control_panel, msg); + return encode_message_to_buffer(msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE, conn, remaining_size, + is_single); } void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) { alarm_control_panel::AlarmControlPanel *a_alarm_control_panel = App.get_alarm_control_panel_by_key(msg.key); @@ -1415,46 +1449,41 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe #endif #ifdef USE_EVENT -void APIConnection::send_event(event::Event *event, std::string event_type) { - this->send_state_with_value_(event, &APIConnection::try_send_event_, &APIConnection::try_send_event_, - std::move(event_type)); +void APIConnection::send_event(event::Event *event, const std::string &event_type) { + this->schedule_message_(event, MessageCreator(event_type, EventResponse::MESSAGE_TYPE), EventResponse::MESSAGE_TYPE); } void APIConnection::send_event_info(event::Event *event) { - this->send_info_(static_cast(event), - reinterpret_cast(&APIConnection::try_send_event_info_)); + this->schedule_message_(event, &APIConnection::try_send_event_info, ListEntitiesEventResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_event_(event::Event *event) { - return this->try_send_event_(event, *(event->last_event_type)); -} -bool APIConnection::try_send_event_(event::Event *event, std::string event_type) { +uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn, + uint32_t remaining_size, bool is_single) { EventResponse resp; - resp.event_type = std::move(event_type); - - resp.key = event->get_object_id_hash(); - return this->send_event_response(resp); + resp.event_type = event_type; + fill_entity_state_base(event, resp); + return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_event_info_(event::Event *event) { + +uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *event = static_cast(entity); ListEntitiesEventResponse msg; msg.device_class = event->get_device_class(); for (const auto &event_type : event->get_event_types()) msg.event_types.push_back(event_type); msg.device_uid = event->get_device_uid(); msg.unique_id = get_default_unique_id("event", event); - return this->try_send_entity_info_(static_cast(event), msg, - &APIConnection::send_list_entities_event_response); + fill_entity_info_base(event, msg); + return encode_message_to_buffer(msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } #endif #ifdef USE_UPDATE bool APIConnection::send_update_state(update::UpdateEntity *update) { - return this->send_state_(static_cast(update), - reinterpret_cast(&APIConnection::try_send_update_state_)); + return this->schedule_message_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE); } -void APIConnection::send_update_info(update::UpdateEntity *update) { - this->send_info_(static_cast(update), - reinterpret_cast(&APIConnection::try_send_update_info_)); -} -bool APIConnection::try_send_update_state_(update::UpdateEntity *update) { +uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *update = static_cast(entity); UpdateStateResponse resp; resp.missing_state = !update->has_state(); if (update->has_state()) { @@ -1469,17 +1498,21 @@ bool APIConnection::try_send_update_state_(update::UpdateEntity *update) { resp.release_summary = update->update_info.summary; resp.release_url = update->update_info.release_url; } - - resp.key = update->get_object_id_hash(); - return this->send_update_state_response(resp); + fill_entity_state_base(update, resp); + return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_update_info_(update::UpdateEntity *update) { +void APIConnection::send_update_info(update::UpdateEntity *update) { + this->schedule_message_(update, &APIConnection::try_send_update_info, ListEntitiesUpdateResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *update = static_cast(entity); ListEntitiesUpdateResponse msg; msg.device_class = update->get_device_class(); msg.device_uid = update->get_device_uid(); msg.unique_id = get_default_unique_id("update", update); - return this->try_send_entity_info_(static_cast(update), msg, - &APIConnection::send_list_entities_update_response); + fill_entity_info_base(update, msg); + return encode_message_to_buffer(msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::update_command(const UpdateCommandRequest &msg) { update::UpdateEntity *update = App.get_update_by_key(msg.key); @@ -1494,7 +1527,7 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) { update->check(); break; case enums::UPDATE_COMMAND_NONE: - ESP_LOGE(TAG, "UPDATE_COMMAND_NONE not handled. Check client is sending the correct command"); + ESP_LOGE(TAG, "UPDATE_COMMAND_NONE not handled; confirm command is correct"); break; default: ESP_LOGW(TAG, "Unknown update command: %" PRIu32, msg.command); @@ -1527,14 +1560,13 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char buffer.encode_string(3, line, line_length); // string message = 3 // SubscribeLogsResponse - 29 - return this->send_buffer(buffer, 29); + return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE); } HelloResponse APIConnection::hello(const HelloRequest &msg) { this->client_info_ = msg.client_info; this->client_peername_ = this->helper_->getpeername(); - this->client_combined_info_ = this->client_info_ + " (" + this->client_peername_ + ")"; - this->helper_->set_log_info(this->client_combined_info_); + this->helper_->set_log_info(this->get_client_combined_info()); this->client_api_version_major_ = msg.api_version_major; this->client_api_version_minor_ = msg.api_version_minor; ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(), @@ -1556,7 +1588,7 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) { // bool invalid_password = 1; resp.invalid_password = !correct; if (correct) { - ESP_LOGD(TAG, "%s: Connected successfully", this->client_combined_info_.c_str()); + ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str()); this->connection_state_ = ConnectionState::AUTHENTICATED; this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_); #ifdef USE_HOMEASSISTANT_TIME @@ -1636,7 +1668,7 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) { } } if (!found) { - ESP_LOGV(TAG, "Could not find matching service!"); + ESP_LOGV(TAG, "Could not find service"); } } #ifdef USE_API_NOISE @@ -1671,7 +1703,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { APIError err = this->helper_->loop(); if (err != APIError::OK) { on_fatal_error(); - ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(), + ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(), api_error_to_str(err), errno); return false; } @@ -1682,8 +1714,8 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { } return false; } -bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) { - if (!this->try_to_clear_buffer(message_type != 29)) { // SubscribeLogsResponse +bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) { + if (!this->try_to_clear_buffer(message_type != SubscribeLogsResponse::MESSAGE_TYPE)) { // SubscribeLogsResponse return false; } @@ -1693,10 +1725,10 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) if (err != APIError::OK) { on_fatal_error(); if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) { - ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str()); + ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str()); } else { - ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err), - errno); + ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->get_client_combined_info().c_str(), + api_error_to_str(err), errno); } return false; } @@ -1705,17 +1737,354 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) } void APIConnection::on_unauthenticated_access() { this->on_fatal_error(); - ESP_LOGD(TAG, "%s: tried to access without authentication.", this->client_combined_info_.c_str()); + ESP_LOGD(TAG, "%s requested access without authentication", this->get_client_combined_info().c_str()); } void APIConnection::on_no_setup_connection() { this->on_fatal_error(); - ESP_LOGD(TAG, "%s: tried to access without full connection.", this->client_combined_info_.c_str()); + ESP_LOGD(TAG, "%s requested access without full connection", this->get_client_combined_info().c_str()); } void APIConnection::on_fatal_error() { this->helper_->close(); this->remove_ = true; } +void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type) { + // Check if we already have a message of this type for this entity + // This provides deduplication per entity/message_type combination + // O(n) but optimized for RAM and not performance. + for (auto &item : items) { + if (item.entity == entity && item.message_type == message_type) { + // Update the existing item with the new creator + item.creator = std::move(creator); + return; + } + } + + // No existing item found, add new one + items.emplace_back(entity, std::move(creator), message_type); +} + +bool APIConnection::schedule_batch_() { + if (!this->deferred_batch_.batch_scheduled) { + this->deferred_batch_.batch_scheduled = true; + this->deferred_batch_.batch_start_time = App.get_loop_component_start_time(); + } + return true; +} + +ProtoWriteBuffer APIConnection::allocate_single_message_buffer(uint16_t size) { return this->create_buffer(size); } + +ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint16_t size) { + ProtoWriteBuffer result = this->prepare_message_buffer(size, this->batch_first_message_); + this->batch_first_message_ = false; + return result; +} + +void APIConnection::process_batch_() { + if (this->deferred_batch_.empty()) { + this->deferred_batch_.batch_scheduled = false; + return; + } + + // Try to clear buffer first + if (!this->try_to_clear_buffer(true)) { + // Can't write now, we'll try again later + return; + } + + size_t num_items = this->deferred_batch_.items.size(); + + // Fast path for single message - allocate exact size needed + if (num_items == 1) { + const auto &item = this->deferred_batch_.items[0]; + + // Let the creator calculate size and encode if it fits + uint16_t payload_size = item.creator(item.entity, this, std::numeric_limits::max(), true); + + if (payload_size > 0 && + this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, item.message_type)) { + this->deferred_batch_.clear(); + } else if (payload_size == 0) { + // Message too large + ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type); + this->deferred_batch_.clear(); + } + return; + } + + // Pre-allocate storage for packet info + std::vector packet_info; + packet_info.reserve(num_items); + + // Cache these values to avoid repeated virtual calls + const uint8_t header_padding = this->helper_->frame_header_padding(); + const uint8_t footer_size = this->helper_->frame_footer_size(); + + // Initialize buffer and tracking variables + this->parent_->get_shared_buffer_ref().clear(); + + // Pre-calculate exact buffer size needed based on message types + uint32_t total_estimated_size = 0; + for (const auto &item : this->deferred_batch_.items) { + total_estimated_size += get_estimated_message_size(item.message_type); + } + + // Calculate total overhead for all messages + uint32_t total_overhead = (header_padding + footer_size) * num_items; + + // Reserve based on estimated size (much more accurate than 24-byte worst-case) + this->parent_->get_shared_buffer_ref().reserve(total_estimated_size + total_overhead); + this->batch_first_message_ = true; + + size_t items_processed = 0; + uint16_t remaining_size = std::numeric_limits::max(); + + // Track where each message's header padding begins in the buffer + // For plaintext: this is where the 6-byte header padding starts + // For noise: this is where the 7-byte header padding starts + // The actual message data follows after the header padding + uint32_t current_offset = 0; + + // Process items and encode directly to buffer + for (const auto &item : this->deferred_batch_.items) { + // Try to encode message + // The creator will calculate overhead to determine if the message fits + uint16_t payload_size = item.creator(item.entity, this, remaining_size, false); + + if (payload_size == 0) { + // Message won't fit, stop processing + break; + } + + // 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; + packet_info.emplace_back(item.message_type, current_offset, proto_payload_size); + + // Update tracking variables + items_processed++; + // After first message, set remaining size to MAX_PACKET_SIZE to avoid fragmentation + if (items_processed == 1) { + remaining_size = MAX_PACKET_SIZE; + } + remaining_size -= payload_size; + // Calculate where the next message's header padding will start + // Current buffer size + footer space (that prepare_message_buffer will add for this message) + current_offset = this->parent_->get_shared_buffer_ref().size() + footer_size; + } + + if (items_processed == 0) { + this->deferred_batch_.clear(); + return; + } + + // Add footer space for the last message (for Noise protocol MAC) + if (footer_size > 0) { + auto &shared_buf = this->parent_->get_shared_buffer_ref(); + shared_buf.resize(shared_buf.size() + footer_size); + } + + // Send all collected packets + APIError err = + this->helper_->write_protobuf_packets(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, packet_info); + if (err != APIError::OK && err != APIError::WOULD_BLOCK) { + on_fatal_error(); + if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) { + ESP_LOGW(TAG, "%s: Connection reset during batch write", this->get_client_combined_info().c_str()); + } else { + ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->get_client_combined_info().c_str(), + api_error_to_str(err), errno); + } + } + + // Handle remaining items more efficiently + if (items_processed < this->deferred_batch_.items.size()) { + // Remove processed items from the beginning + this->deferred_batch_.items.erase(this->deferred_batch_.items.begin(), + this->deferred_batch_.items.begin() + items_processed); + + // Reschedule for remaining items + this->schedule_batch_(); + } else { + // All items processed + this->deferred_batch_.clear(); + } +} + +uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) const { + switch (message_type_) { + case 0: // Function pointer + return data_.ptr(entity, conn, remaining_size, is_single); + +#ifdef USE_EVENT + case EventResponse::MESSAGE_TYPE: { + auto *e = static_cast(entity); + return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single); + } +#endif + + default: + // Should not happen, return 0 to indicate no message + return 0; + } +} + +uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + ListEntitiesDoneResponse resp; + return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size, is_single); +} + +uint16_t APIConnection::try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + DisconnectRequest req; + return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size, is_single); +} + +uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) { + // Use generated ESTIMATED_SIZE constants from each message type + switch (message_type) { +#ifdef USE_BINARY_SENSOR + case BinarySensorStateResponse::MESSAGE_TYPE: + return BinarySensorStateResponse::ESTIMATED_SIZE; + case ListEntitiesBinarySensorResponse::MESSAGE_TYPE: + return ListEntitiesBinarySensorResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_SENSOR + case SensorStateResponse::MESSAGE_TYPE: + return SensorStateResponse::ESTIMATED_SIZE; + case ListEntitiesSensorResponse::MESSAGE_TYPE: + return ListEntitiesSensorResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_SWITCH + case SwitchStateResponse::MESSAGE_TYPE: + return SwitchStateResponse::ESTIMATED_SIZE; + case ListEntitiesSwitchResponse::MESSAGE_TYPE: + return ListEntitiesSwitchResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_TEXT_SENSOR + case TextSensorStateResponse::MESSAGE_TYPE: + return TextSensorStateResponse::ESTIMATED_SIZE; + case ListEntitiesTextSensorResponse::MESSAGE_TYPE: + return ListEntitiesTextSensorResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_NUMBER + case NumberStateResponse::MESSAGE_TYPE: + return NumberStateResponse::ESTIMATED_SIZE; + case ListEntitiesNumberResponse::MESSAGE_TYPE: + return ListEntitiesNumberResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_TEXT + case TextStateResponse::MESSAGE_TYPE: + return TextStateResponse::ESTIMATED_SIZE; + case ListEntitiesTextResponse::MESSAGE_TYPE: + return ListEntitiesTextResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_SELECT + case SelectStateResponse::MESSAGE_TYPE: + return SelectStateResponse::ESTIMATED_SIZE; + case ListEntitiesSelectResponse::MESSAGE_TYPE: + return ListEntitiesSelectResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_LOCK + case LockStateResponse::MESSAGE_TYPE: + return LockStateResponse::ESTIMATED_SIZE; + case ListEntitiesLockResponse::MESSAGE_TYPE: + return ListEntitiesLockResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_EVENT + case EventResponse::MESSAGE_TYPE: + return EventResponse::ESTIMATED_SIZE; + case ListEntitiesEventResponse::MESSAGE_TYPE: + return ListEntitiesEventResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_COVER + case CoverStateResponse::MESSAGE_TYPE: + return CoverStateResponse::ESTIMATED_SIZE; + case ListEntitiesCoverResponse::MESSAGE_TYPE: + return ListEntitiesCoverResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_FAN + case FanStateResponse::MESSAGE_TYPE: + return FanStateResponse::ESTIMATED_SIZE; + case ListEntitiesFanResponse::MESSAGE_TYPE: + return ListEntitiesFanResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_LIGHT + case LightStateResponse::MESSAGE_TYPE: + return LightStateResponse::ESTIMATED_SIZE; + case ListEntitiesLightResponse::MESSAGE_TYPE: + return ListEntitiesLightResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_CLIMATE + case ClimateStateResponse::MESSAGE_TYPE: + return ClimateStateResponse::ESTIMATED_SIZE; + case ListEntitiesClimateResponse::MESSAGE_TYPE: + return ListEntitiesClimateResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_ESP32_CAMERA + case ListEntitiesCameraResponse::MESSAGE_TYPE: + return ListEntitiesCameraResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_BUTTON + case ListEntitiesButtonResponse::MESSAGE_TYPE: + return ListEntitiesButtonResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_MEDIA_PLAYER + case MediaPlayerStateResponse::MESSAGE_TYPE: + return MediaPlayerStateResponse::ESTIMATED_SIZE; + case ListEntitiesMediaPlayerResponse::MESSAGE_TYPE: + return ListEntitiesMediaPlayerResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_ALARM_CONTROL_PANEL + case AlarmControlPanelStateResponse::MESSAGE_TYPE: + return AlarmControlPanelStateResponse::ESTIMATED_SIZE; + case ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE: + return ListEntitiesAlarmControlPanelResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_DATETIME_DATE + case DateStateResponse::MESSAGE_TYPE: + return DateStateResponse::ESTIMATED_SIZE; + case ListEntitiesDateResponse::MESSAGE_TYPE: + return ListEntitiesDateResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_DATETIME_TIME + case TimeStateResponse::MESSAGE_TYPE: + return TimeStateResponse::ESTIMATED_SIZE; + case ListEntitiesTimeResponse::MESSAGE_TYPE: + return ListEntitiesTimeResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_DATETIME_DATETIME + case DateTimeStateResponse::MESSAGE_TYPE: + return DateTimeStateResponse::ESTIMATED_SIZE; + case ListEntitiesDateTimeResponse::MESSAGE_TYPE: + return ListEntitiesDateTimeResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_VALVE + case ValveStateResponse::MESSAGE_TYPE: + return ValveStateResponse::ESTIMATED_SIZE; + case ListEntitiesValveResponse::MESSAGE_TYPE: + return ListEntitiesValveResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_UPDATE + case UpdateStateResponse::MESSAGE_TYPE: + return UpdateStateResponse::ESTIMATED_SIZE; + case ListEntitiesUpdateResponse::MESSAGE_TYPE: + return ListEntitiesUpdateResponse::ESTIMATED_SIZE; +#endif + case ListEntitiesServicesResponse::MESSAGE_TYPE: + return ListEntitiesServicesResponse::ESTIMATED_SIZE; + case ListEntitiesDoneResponse::MESSAGE_TYPE: + return ListEntitiesDoneResponse::ESTIMATED_SIZE; + case DisconnectRequest::MESSAGE_TYPE: + return DisconnectRequest::ESTIMATED_SIZE; + default: + // Fallback for unknown message types + return 24; + } +} + } // namespace api } // namespace esphome #endif diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index f965a9e795..66b7ce38a7 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -11,6 +11,7 @@ #include "esphome/core/entity_base.h" #include +#include namespace esphome { namespace api { @@ -18,49 +19,9 @@ namespace api { // Keepalive timeout in milliseconds static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000; -using send_message_t = bool (APIConnection::*)(void *); - -/* - This class holds a pointer to the source component that wants to publish a message, and a pointer to a function that - will lazily publish that message. The two pointers allow dedup in the deferred queue if multiple publishes for the - same component are backed up, and take up only 8 bytes of memory. The entry in the deferred queue (a std::vector) is - the DeferredMessage instance itself (not a pointer to one elsewhere in heap) so still only 8 bytes per entry. Even - 100 backed up messages (you'd have to have at least 100 sensors publishing because of dedup) would take up only 0.8 - kB. -*/ -class DeferredMessageQueue { - struct DeferredMessage { - friend class DeferredMessageQueue; - - protected: - void *source_; - send_message_t send_message_; - - public: - DeferredMessage(void *source, send_message_t send_message) : source_(source), send_message_(send_message) {} - bool operator==(const DeferredMessage &test) const { - return (source_ == test.source_ && send_message_ == test.send_message_); - } - } __attribute__((packed)); - - protected: - // vector is used very specifically for its zero memory overhead even though items are popped from the front (memory - // footprint is more important than speed here) - std::vector deferred_queue_; - APIConnection *api_connection_; - - // helper for allowing only unique entries in the queue - void dmq_push_back_with_dedup_(void *source, send_message_t send_message); - - public: - DeferredMessageQueue(APIConnection *api_connection) : api_connection_(api_connection) {} - void process_queue(); - void defer(void *source, send_message_t send_message); - bool empty() const { return deferred_queue_.empty(); } -}; - class APIConnection : public APIServerConnection { public: + friend class APIServer; APIConnection(std::unique_ptr socket, APIServer *parent); virtual ~APIConnection(); @@ -68,225 +29,105 @@ class APIConnection : public APIServerConnection { void loop(); bool send_list_info_done() { - ListEntitiesDoneResponse resp; - return this->send_list_entities_done_response(resp); + return this->schedule_message_(nullptr, &APIConnection::try_send_list_info_done, + ListEntitiesDoneResponse::MESSAGE_TYPE); } #ifdef USE_BINARY_SENSOR - bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state); + bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor); void send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor); - - protected: - bool try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor); - bool try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor, bool state); - bool try_send_binary_sensor_info_(binary_sensor::BinarySensor *binary_sensor); - - public: #endif #ifdef USE_COVER bool send_cover_state(cover::Cover *cover); void send_cover_info(cover::Cover *cover); void cover_command(const CoverCommandRequest &msg) override; - - protected: - bool try_send_cover_state_(cover::Cover *cover); - bool try_send_cover_info_(cover::Cover *cover); - - public: #endif #ifdef USE_FAN bool send_fan_state(fan::Fan *fan); void send_fan_info(fan::Fan *fan); void fan_command(const FanCommandRequest &msg) override; - - protected: - bool try_send_fan_state_(fan::Fan *fan); - bool try_send_fan_info_(fan::Fan *fan); - - public: #endif #ifdef USE_LIGHT bool send_light_state(light::LightState *light); void send_light_info(light::LightState *light); void light_command(const LightCommandRequest &msg) override; - - protected: - bool try_send_light_state_(light::LightState *light); - bool try_send_light_info_(light::LightState *light); - - public: #endif #ifdef USE_SENSOR - bool send_sensor_state(sensor::Sensor *sensor, float state); + bool send_sensor_state(sensor::Sensor *sensor); void send_sensor_info(sensor::Sensor *sensor); - - protected: - bool try_send_sensor_state_(sensor::Sensor *sensor); - bool try_send_sensor_state_(sensor::Sensor *sensor, float state); - bool try_send_sensor_info_(sensor::Sensor *sensor); - - public: #endif #ifdef USE_SWITCH - bool send_switch_state(switch_::Switch *a_switch, bool state); + bool send_switch_state(switch_::Switch *a_switch); void send_switch_info(switch_::Switch *a_switch); void switch_command(const SwitchCommandRequest &msg) override; - - protected: - bool try_send_switch_state_(switch_::Switch *a_switch); - bool try_send_switch_state_(switch_::Switch *a_switch, bool state); - bool try_send_switch_info_(switch_::Switch *a_switch); - - public: #endif #ifdef USE_TEXT_SENSOR - bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state); + bool send_text_sensor_state(text_sensor::TextSensor *text_sensor); void send_text_sensor_info(text_sensor::TextSensor *text_sensor); - - protected: - bool try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor); - bool try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor, std::string state); - bool try_send_text_sensor_info_(text_sensor::TextSensor *text_sensor); - - public: #endif #ifdef USE_ESP32_CAMERA void set_camera_state(std::shared_ptr image); void send_camera_info(esp32_camera::ESP32Camera *camera); void camera_image(const CameraImageRequest &msg) override; - - protected: - bool try_send_camera_info_(esp32_camera::ESP32Camera *camera); - - public: #endif #ifdef USE_CLIMATE bool send_climate_state(climate::Climate *climate); void send_climate_info(climate::Climate *climate); void climate_command(const ClimateCommandRequest &msg) override; - - protected: - bool try_send_climate_state_(climate::Climate *climate); - bool try_send_climate_info_(climate::Climate *climate); - - public: #endif #ifdef USE_NUMBER - bool send_number_state(number::Number *number, float state); + bool send_number_state(number::Number *number); void send_number_info(number::Number *number); void number_command(const NumberCommandRequest &msg) override; - - protected: - bool try_send_number_state_(number::Number *number); - bool try_send_number_state_(number::Number *number, float state); - bool try_send_number_info_(number::Number *number); - - public: #endif #ifdef USE_DATETIME_DATE bool send_date_state(datetime::DateEntity *date); void send_date_info(datetime::DateEntity *date); void date_command(const DateCommandRequest &msg) override; - - protected: - bool try_send_date_state_(datetime::DateEntity *date); - bool try_send_date_info_(datetime::DateEntity *date); - - public: #endif #ifdef USE_DATETIME_TIME bool send_time_state(datetime::TimeEntity *time); void send_time_info(datetime::TimeEntity *time); void time_command(const TimeCommandRequest &msg) override; - - protected: - bool try_send_time_state_(datetime::TimeEntity *time); - bool try_send_time_info_(datetime::TimeEntity *time); - - public: #endif #ifdef USE_DATETIME_DATETIME bool send_datetime_state(datetime::DateTimeEntity *datetime); void send_datetime_info(datetime::DateTimeEntity *datetime); void datetime_command(const DateTimeCommandRequest &msg) override; - - protected: - bool try_send_datetime_state_(datetime::DateTimeEntity *datetime); - bool try_send_datetime_info_(datetime::DateTimeEntity *datetime); - - public: #endif #ifdef USE_TEXT - bool send_text_state(text::Text *text, std::string state); + bool send_text_state(text::Text *text); void send_text_info(text::Text *text); void text_command(const TextCommandRequest &msg) override; - - protected: - bool try_send_text_state_(text::Text *text); - bool try_send_text_state_(text::Text *text, std::string state); - bool try_send_text_info_(text::Text *text); - - public: #endif #ifdef USE_SELECT - bool send_select_state(select::Select *select, std::string state); + bool send_select_state(select::Select *select); void send_select_info(select::Select *select); void select_command(const SelectCommandRequest &msg) override; - - protected: - bool try_send_select_state_(select::Select *select); - bool try_send_select_state_(select::Select *select, std::string state); - bool try_send_select_info_(select::Select *select); - - public: #endif #ifdef USE_BUTTON void send_button_info(button::Button *button); void button_command(const ButtonCommandRequest &msg) override; - - protected: - bool try_send_button_info_(button::Button *button); - - public: #endif #ifdef USE_LOCK - bool send_lock_state(lock::Lock *a_lock, lock::LockState state); + bool send_lock_state(lock::Lock *a_lock); void send_lock_info(lock::Lock *a_lock); void lock_command(const LockCommandRequest &msg) override; - - protected: - bool try_send_lock_state_(lock::Lock *a_lock); - bool try_send_lock_state_(lock::Lock *a_lock, lock::LockState state); - bool try_send_lock_info_(lock::Lock *a_lock); - - public: #endif #ifdef USE_VALVE bool send_valve_state(valve::Valve *valve); void send_valve_info(valve::Valve *valve); void valve_command(const ValveCommandRequest &msg) override; - - protected: - bool try_send_valve_state_(valve::Valve *valve); - bool try_send_valve_info_(valve::Valve *valve); - - public: #endif #ifdef USE_MEDIA_PLAYER bool send_media_player_state(media_player::MediaPlayer *media_player); void send_media_player_info(media_player::MediaPlayer *media_player); void media_player_command(const MediaPlayerCommandRequest &msg) override; - - protected: - bool try_send_media_player_state_(media_player::MediaPlayer *media_player); - bool try_send_media_player_info_(media_player::MediaPlayer *media_player); - - public: #endif bool try_send_log_message(int level, const char *tag, const char *line); void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { if (!this->service_call_subscription_) return; - this->send_homeassistant_service_response(call); + this->send_message(call); } #ifdef USE_BLUETOOTH_PROXY void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; @@ -308,7 +149,7 @@ class APIConnection : public APIServerConnection { #ifdef USE_HOMEASSISTANT_TIME void send_time_request() { GetTimeRequest req; - this->send_get_time_request(req); + this->send_message(req); } #endif @@ -328,36 +169,17 @@ class APIConnection : public APIServerConnection { bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); void send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override; - - protected: - bool try_send_alarm_control_panel_state_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); - bool try_send_alarm_control_panel_info_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); - - public: #endif #ifdef USE_EVENT - void send_event(event::Event *event, std::string event_type); + void send_event(event::Event *event, const std::string &event_type); void send_event_info(event::Event *event); - - protected: - bool try_send_event_(event::Event *event); - bool try_send_event_(event::Event *event, std::string event_type); - bool try_send_event_info_(event::Event *event); - - public: #endif #ifdef USE_UPDATE bool send_update_state(update::UpdateEntity *update); void send_update_info(update::UpdateEntity *update); void update_command(const UpdateCommandRequest &msg) override; - - protected: - bool try_send_update_state_(update::UpdateEntity *update); - bool try_send_update_info_(update::UpdateEntity *update); - - public: #endif void on_disconnect_response(const DisconnectResponse &value) override; @@ -407,102 +229,67 @@ class APIConnection : public APIServerConnection { void on_no_setup_connection() override; ProtoWriteBuffer create_buffer(uint32_t reserve_size) override { // FIXME: ensure no recursive writes can happen - this->proto_write_buffer_.clear(); + // Get header padding size - used for both reserve and insert uint8_t header_padding = this->helper_->frame_header_padding(); + + // Get shared buffer from parent server + std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); + shared_buf.clear(); // Reserve space for header padding + message + footer // - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext) // - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext) - this->proto_write_buffer_.reserve(reserve_size + header_padding + this->helper_->frame_footer_size()); - // Insert header padding bytes so message encoding starts at the correct position - this->proto_write_buffer_.insert(this->proto_write_buffer_.begin(), header_padding, 0); - return {&this->proto_write_buffer_}; + shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size()); + // Resize to add header padding so message encoding starts at the correct position + shared_buf.resize(header_padding); + return {&shared_buf}; } - bool try_to_clear_buffer(bool log_out_of_space); - bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override; - std::string get_client_combined_info() const { return this->client_combined_info_; } + // Prepare buffer for next message in batch + ProtoWriteBuffer prepare_message_buffer(uint16_t message_size, bool is_first_message) { + // Get reference to shared buffer (it maintains state between batch messages) + std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); + + if (is_first_message) { + shared_buf.clear(); + } + + size_t current_size = shared_buf.size(); + + // Calculate padding to add: + // - First message: just header padding + // - Subsequent messages: footer for previous message + header padding for this message + size_t padding_to_add = is_first_message + ? this->helper_->frame_header_padding() + : this->helper_->frame_header_padding() + this->helper_->frame_footer_size(); + + // Reserve space for padding + message + shared_buf.reserve(current_size + padding_to_add + message_size); + + // Resize to add the padding bytes + shared_buf.resize(current_size + padding_to_add); + + return {&shared_buf}; + } + + bool try_to_clear_buffer(bool log_out_of_space); + bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) override; + + std::string get_client_combined_info() const { + if (this->client_info_ == this->client_peername_) { + // Before Hello message, both are the same (just IP:port) + return this->client_info_; + } + return this->client_info_ + " (" + this->client_peername_ + ")"; + } + + // Buffer allocator methods for batch processing + ProtoWriteBuffer allocate_single_message_buffer(uint16_t size); + ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size); protected: - friend APIServer; - - /** - * Generic send entity state method to reduce code duplication. - * Only attempts to build and send the message if the transmit buffer is available. - * - * This is the base version for entities that use their current state. - * - * @param entity The entity to send state for - * @param try_send_func The function that tries to send the state - * @return True on success or message deferred, false if subscription check failed - */ - bool send_state_(esphome::EntityBase *entity, send_message_t try_send_func) { - if (!this->state_subscription_) - return false; - if (this->try_to_clear_buffer(true) && (this->*try_send_func)(entity)) { - return true; - } - this->deferred_message_queue_.defer(entity, try_send_func); - return true; - } - - /** - * Send entity state method that handles explicit state values. - * Only attempts to build and send the message if the transmit buffer is available. - * - * This method accepts a state parameter to be used instead of the entity's current state. - * It attempts to send the state with the provided value first, and if that fails due to buffer constraints, - * it defers the entity for later processing using the entity-only function. - * - * @tparam EntityT The entity type - * @tparam StateT Type of the state parameter - * @tparam Args Additional argument types (if any) - * @param entity The entity to send state for - * @param try_send_entity_func The function that tries to send the state with entity pointer only - * @param try_send_state_func The function that tries to send the state with entity and state parameters - * @param state The state value to send - * @param args Additional arguments to pass to the try_send_state_func - * @return True on success or message deferred, false if subscription check failed - */ - template - bool send_state_with_value_(EntityT *entity, bool (APIConnection::*try_send_entity_func)(EntityT *), - bool (APIConnection::*try_send_state_func)(EntityT *, StateT, Args...), StateT state, - Args... args) { - if (!this->state_subscription_) - return false; - if (this->try_to_clear_buffer(true) && (this->*try_send_state_func)(entity, state, args...)) { - return true; - } - this->deferred_message_queue_.defer(entity, reinterpret_cast(try_send_entity_func)); - return true; - } - - /** - * Generic send entity info method to reduce code duplication. - * Only attempts to build and send the message if the transmit buffer is available. - * - * @param entity The entity to send info for - * @param try_send_func The function that tries to send the info - */ - void send_info_(esphome::EntityBase *entity, send_message_t try_send_func) { - if (this->try_to_clear_buffer(true) && (this->*try_send_func)(entity)) { - return; - } - this->deferred_message_queue_.defer(entity, try_send_func); - } - - /** - * Generic function for generating entity info response messages. - * This is used to reduce duplication in the try_send_*_info functions. - * - * @param entity The entity to generate info for - * @param response The response object - * @param send_response_func Function pointer to send the response - * @return True if the message was sent successfully - */ - template - bool try_send_entity_info_(esphome::EntityBase *entity, ResponseT &response, - bool (APIServerConnectionBase::*send_response_func)(const ResponseT &)) { + // Helper function to fill common entity info fields + static void fill_entity_info_base(esphome::EntityBase *entity, InfoResponseProtoMessage &response) { // Set common fields that are shared by all entity types response.key = entity->get_object_id_hash(); response.object_id = entity->get_object_id(); @@ -514,48 +301,332 @@ class APIConnection : public APIServerConnection { response.icon = entity->get_icon(); response.disabled_by_default = entity->is_disabled_by_default(); response.entity_category = static_cast(entity->get_entity_category()); - - // Send the response using the provided send method - return (this->*send_response_func)(response); } - bool send_(const void *buf, size_t len, bool force); + // Helper function to fill common entity state fields + static void fill_entity_state_base(esphome::EntityBase *entity, StateResponseProtoMessage &response) { + response.key = entity->get_object_id_hash(); + } - enum class ConnectionState { + // Non-template helper to encode any ProtoMessage + static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn, + uint32_t remaining_size, bool is_single); + +#ifdef USE_BINARY_SENSOR + static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_COVER + static uint16_t try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_FAN + static uint16_t try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); + static uint16_t try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_LIGHT + static uint16_t try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_SENSOR + static uint16_t try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_SWITCH + static uint16_t try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_TEXT_SENSOR + static uint16_t try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_CLIMATE + static uint16_t try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_NUMBER + static uint16_t try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_DATETIME_DATE + static uint16_t try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); + static uint16_t try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_DATETIME_TIME + static uint16_t try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); + static uint16_t try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_DATETIME_DATETIME + static uint16_t try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_TEXT + static uint16_t try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); + static uint16_t try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_SELECT + static uint16_t try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_BUTTON + static uint16_t try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_LOCK + static uint16_t try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); + static uint16_t try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_VALVE + static uint16_t try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_MEDIA_PLAYER + static uint16_t try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_ALARM_CONTROL_PANEL + static uint16_t try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_EVENT + static uint16_t try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn, + uint32_t remaining_size, bool is_single); + static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_UPDATE + static uint16_t try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_ESP32_CAMERA + static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif + + // Method for ListEntitiesDone batching + static uint16_t try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + + // Method for DisconnectRequest batching + static uint16_t try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + + // Helper function to get estimated message size for buffer pre-allocation + static uint16_t get_estimated_message_size(uint16_t message_type); + + // Pointers first (4 bytes each, naturally aligned) + std::unique_ptr helper_; + APIServer *parent_; + + // 4-byte aligned types + uint32_t last_traffic_; + uint32_t next_ping_retry_{0}; + int state_subs_at_ = -1; + + // Strings (12 bytes each on 32-bit) + std::string client_info_; + std::string client_peername_; + + // 2-byte aligned types + uint16_t client_api_version_major_{0}; + uint16_t client_api_version_minor_{0}; + + // Group all 1-byte types together to minimize padding + enum class ConnectionState : uint8_t { WAITING_FOR_HELLO, CONNECTED, AUTHENTICATED, } connection_state_{ConnectionState::WAITING_FOR_HELLO}; - + uint8_t log_subscription_{ESPHOME_LOG_LEVEL_NONE}; bool remove_{false}; + bool state_subscription_{false}; + bool sent_ping_{false}; + bool service_call_subscription_{false}; + bool next_close_ = false; + uint8_t ping_retries_{0}; + // 8 bytes used, no padding needed - // Buffer used to encode proto messages - // Re-use to prevent allocations - std::vector proto_write_buffer_; - std::unique_ptr helper_; - - std::string client_info_; - std::string client_peername_; - std::string client_combined_info_; - uint32_t client_api_version_major_{0}; - uint32_t client_api_version_minor_{0}; + // Larger objects at the end + InitialStateIterator initial_state_iterator_; + ListEntitiesIterator list_entities_iterator_; #ifdef USE_ESP32_CAMERA esp32_camera::CameraImageReader image_reader_; #endif - bool state_subscription_{false}; - int log_subscription_{ESPHOME_LOG_LEVEL_NONE}; - uint32_t last_traffic_; - uint32_t next_ping_retry_{0}; - uint8_t ping_retries_{0}; - bool sent_ping_{false}; - bool service_call_subscription_{false}; - bool next_close_ = false; - APIServer *parent_; - DeferredMessageQueue deferred_message_queue_; - InitialStateIterator initial_state_iterator_; - ListEntitiesIterator list_entities_iterator_; - int state_subs_at_ = -1; + // Function pointer type for message encoding + using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single); + + // Optimized MessageCreator class using union dispatch + class MessageCreator { + public: + // Constructor for function pointer (message_type = 0) + MessageCreator(MessageCreatorPtr ptr) : message_type_(0) { data_.ptr = ptr; } + + // Constructor for string state capture + MessageCreator(const std::string &value, uint16_t msg_type) : message_type_(msg_type) { + data_.string_ptr = new std::string(value); + } + + // Destructor + ~MessageCreator() { + // Clean up string data for string-based message types + if (uses_string_data_()) { + delete data_.string_ptr; + } + } + + // Copy constructor + MessageCreator(const MessageCreator &other) : message_type_(other.message_type_) { + if (message_type_ == 0) { + data_.ptr = other.data_.ptr; + } else if (uses_string_data_()) { + data_.string_ptr = new std::string(*other.data_.string_ptr); + } else { + data_ = other.data_; // For POD types + } + } + + // Move constructor + MessageCreator(MessageCreator &&other) noexcept : data_(other.data_), message_type_(other.message_type_) { + other.message_type_ = 0; // Reset other to function pointer type + other.data_.ptr = nullptr; + } + + // Assignment operators (needed for batch deduplication) + MessageCreator &operator=(const MessageCreator &other) { + if (this != &other) { + // Clean up current string data if needed + if (uses_string_data_()) { + delete data_.string_ptr; + } + // Copy new data + message_type_ = other.message_type_; + if (other.message_type_ == 0) { + data_.ptr = other.data_.ptr; + } else if (other.uses_string_data_()) { + data_.string_ptr = new std::string(*other.data_.string_ptr); + } else { + data_ = other.data_; + } + } + return *this; + } + + MessageCreator &operator=(MessageCreator &&other) noexcept { + if (this != &other) { + // Clean up current string data if needed + if (uses_string_data_()) { + delete data_.string_ptr; + } + // Move data + message_type_ = other.message_type_; + data_ = other.data_; + // Reset other to safe state + other.message_type_ = 0; + other.data_.ptr = nullptr; + } + return *this; + } + + // Call operator + uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) const; + + private: + // Helper to check if this message type uses heap-allocated strings + bool uses_string_data_() const { return message_type_ == EventResponse::MESSAGE_TYPE; } + union CreatorData { + MessageCreatorPtr ptr; // 8 bytes + std::string *string_ptr; // 8 bytes + } data_; // 8 bytes + uint16_t message_type_; // 2 bytes (0 = function ptr, >0 = state capture) + }; + + // Generic batching mechanism for both state updates and entity info + struct DeferredBatch { + struct BatchItem { + EntityBase *entity; // Entity pointer + MessageCreator creator; // Function that creates the message when needed + uint16_t message_type; // Message type for overhead calculation + + // Constructor for creating BatchItem + BatchItem(EntityBase *entity, MessageCreator creator, uint16_t message_type) + : entity(entity), creator(std::move(creator)), message_type(message_type) {} + }; + + std::vector items; + uint32_t batch_start_time{0}; + bool batch_scheduled{false}; + + DeferredBatch() { + // Pre-allocate capacity for typical batch sizes to avoid reallocation + items.reserve(8); + } + + // Add item to the batch + void add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type); + void clear() { + items.clear(); + batch_scheduled = false; + batch_start_time = 0; + } + bool empty() const { return items.empty(); } + }; + + DeferredBatch deferred_batch_; + uint32_t get_batch_delay_ms_() const; + // Message will use 8 more bytes than the minimum size, and typical + // MTU is 1500. Sometimes users will see as low as 1460 MTU. + // If its IPv6 the header is 40 bytes, and if its IPv4 + // the header is 20 bytes. So we have 1460 - 40 = 1420 bytes + // available for the payload. But we also need to add the size of + // the protobuf overhead, which is 8 bytes. + // + // To be safe we pick 1390 bytes as the maximum size + // to send in one go. This is the maximum size of a single packet + // that can be sent over the network. + // This is to avoid fragmentation of the packet. + static constexpr size_t MAX_PACKET_SIZE = 1390; // MTU + + bool schedule_batch_(); + void process_batch_(); + + // State for batch buffer allocation + bool batch_first_message_{false}; + + // Helper function to schedule a deferred message with known message type + bool schedule_message_(EntityBase *entity, MessageCreator creator, uint16_t message_type) { + this->deferred_batch_.add_item(entity, std::move(creator), message_type); + return this->schedule_batch_(); + } + + // Overload for function pointers (for info messages and current state reads) + bool schedule_message_(EntityBase *entity, MessageCreatorPtr function_ptr, uint16_t message_type) { + return schedule_message_(entity, MessageCreator(function_ptr), message_type); + } }; } // namespace api diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 19263f2f2c..e0eb94836d 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -1,9 +1,9 @@ #include "api_frame_helper.h" #ifdef USE_API -#include "esphome/core/log.h" +#include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" -#include "esphome/core/application.h" +#include "esphome/core/log.h" #include "proto.h" #include "api_pb2_size.h" #include @@ -605,9 +605,21 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) { return APIError::OK; } APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) { - int err; - APIError aerr; - aerr = state_action_(); + std::vector *raw_buffer = buffer.get_buffer(); + uint16_t payload_len = static_cast(raw_buffer->size() - frame_header_padding_); + + // Resize to include MAC space (required for Noise encryption) + raw_buffer->resize(raw_buffer->size() + frame_footer_size_); + + // Use write_protobuf_packets with a single packet + std::vector packets; + packets.emplace_back(type, 0, payload_len); + + return write_protobuf_packets(buffer, packets); +} + +APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) { + APIError aerr = state_action_(); if (aerr != APIError::OK) { return aerr; } @@ -616,56 +628,66 @@ APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuf return APIError::WOULD_BLOCK; } - std::vector *raw_buffer = buffer.get_buffer(); - // Message data starts after padding - uint16_t payload_len = raw_buffer->size() - frame_header_padding_; - uint16_t padding = 0; - uint16_t msg_len = 4 + payload_len + padding; - - // We need to resize to include MAC space, but we already reserved it in create_buffer - raw_buffer->resize(raw_buffer->size() + frame_footer_size_); - - // Write the noise header in the padded area - // Buffer layout: - // [0] - 0x01 indicator byte - // [1-2] - Size of encrypted payload (filled after encryption) - // [3-4] - Message type (encrypted) - // [5-6] - Payload length (encrypted) - // [7...] - Actual payload data (encrypted) - uint8_t *buf_start = raw_buffer->data(); - buf_start[0] = 0x01; // indicator - // buf_start[1], buf_start[2] to be set later after encryption - const uint8_t msg_offset = 3; - buf_start[msg_offset + 0] = (uint8_t) (type >> 8); // type high byte - buf_start[msg_offset + 1] = (uint8_t) type; // type low byte - buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len high byte - buf_start[msg_offset + 3] = (uint8_t) payload_len; // data_len low byte - // payload data is already in the buffer starting at position 7 - - NoiseBuffer mbuf; - noise_buffer_init(mbuf); - // The capacity parameter should be msg_len + frame_footer_size_ (MAC length) to allow space for encryption - noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_); - err = noise_cipherstate_encrypt(send_cipher_, &mbuf); - if (err != 0) { - state_ = State::FAILED; - HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str()); - return APIError::CIPHERSTATE_ENCRYPT_FAILED; + if (packets.empty()) { + return APIError::OK; } - uint16_t total_len = 3 + mbuf.size; - buf_start[1] = (uint8_t) (mbuf.size >> 8); - buf_start[2] = (uint8_t) mbuf.size; + std::vector *raw_buffer = buffer.get_buffer(); + this->reusable_iovs_.clear(); + this->reusable_iovs_.reserve(packets.size()); - struct iovec iov; - // Point iov_base to the beginning of the buffer (no unused padding in Noise) - // We send the entire frame: indicator + size + encrypted(type + data_len + payload + MAC) - iov.iov_base = buf_start; - iov.iov_len = total_len; + // We need to encrypt each packet in place + for (const auto &packet : packets) { + uint16_t type = packet.message_type; + uint16_t offset = packet.offset; + uint16_t payload_len = packet.payload_size; + uint16_t msg_len = 4 + payload_len; // type(2) + data_len(2) + payload - // write raw to not have two packets sent if NAGLE disabled - return this->write_raw_(&iov, 1); + // The buffer already has padding at offset + uint8_t *buf_start = raw_buffer->data() + offset; + + // Write noise header + buf_start[0] = 0x01; // indicator + // buf_start[1], buf_start[2] to be set after encryption + + // Write message header (to be encrypted) + const uint8_t msg_offset = 3; + buf_start[msg_offset + 0] = (uint8_t) (type >> 8); // type high byte + buf_start[msg_offset + 1] = (uint8_t) type; // type low byte + buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len high byte + buf_start[msg_offset + 3] = (uint8_t) payload_len; // data_len low byte + // payload data is already in the buffer starting at offset + 7 + + // Make sure we have space for MAC + // The buffer should already have been sized appropriately + + // Encrypt the message in place + NoiseBuffer mbuf; + noise_buffer_init(mbuf); + noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_); + + int err = noise_cipherstate_encrypt(send_cipher_, &mbuf); + if (err != 0) { + state_ = State::FAILED; + HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str()); + return APIError::CIPHERSTATE_ENCRYPT_FAILED; + } + + // Fill in the encrypted size + buf_start[1] = (uint8_t) (mbuf.size >> 8); + buf_start[2] = (uint8_t) mbuf.size; + + // Add iovec for this encrypted packet + struct iovec iov; + iov.iov_base = buf_start; + iov.iov_len = 3 + mbuf.size; // indicator + size + encrypted data + this->reusable_iovs_.push_back(iov); + } + + // Send all encrypted packets in one writev call + return this->write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size()); } + APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) { uint8_t header[3]; header[0] = 0x01; // indicator @@ -780,7 +802,7 @@ extern "C" { // declare how noise generates random bytes (here with a good HWRNG based on the RF system) void noise_rand_bytes(void *output, size_t len) { if (!esphome::random_bytes(reinterpret_cast(output), len)) { - ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!"); + ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting"); arch_restart(); } } @@ -1004,65 +1026,86 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) { return APIError::OK; } APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) { + std::vector *raw_buffer = buffer.get_buffer(); + uint16_t payload_len = static_cast(raw_buffer->size() - frame_header_padding_); + + // Use write_protobuf_packets with a single packet + std::vector packets; + packets.emplace_back(type, 0, payload_len); + + return write_protobuf_packets(buffer, packets); +} + +APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, + const std::vector &packets) { if (state_ != State::DATA) { return APIError::BAD_STATE; } - std::vector *raw_buffer = buffer.get_buffer(); - // Message data starts after padding (frame_header_padding_ = 6) - uint16_t payload_len = static_cast(raw_buffer->size() - frame_header_padding_); - - // Calculate varint sizes for header components - uint8_t size_varint_len = api::ProtoSize::varint(static_cast(payload_len)); - uint8_t type_varint_len = api::ProtoSize::varint(static_cast(type)); - uint8_t total_header_len = 1 + size_varint_len + type_varint_len; - - if (total_header_len > frame_header_padding_) { - // Header is too large to fit in the padding - return APIError::BAD_ARG; + if (packets.empty()) { + return APIError::OK; } - // Calculate where to start writing the header - // The header starts at the latest possible position to minimize unused padding - // - // Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3 - // [0-2] - Unused padding - // [3] - 0x00 indicator byte - // [4] - Payload size varint (1 byte, for sizes 0-127) - // [5] - Message type varint (1 byte, for types 0-127) - // [6...] - Actual payload data - // - // Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2 - // [0-1] - Unused padding - // [2] - 0x00 indicator byte - // [3-4] - Payload size varint (2 bytes, for sizes 128-16383) - // [5] - Message type varint (1 byte, for types 0-127) - // [6...] - Actual payload data - // - // Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0 - // [0] - 0x00 indicator byte - // [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151) - // [4-5] - Message type varint (2 bytes, for types 128-32767) - // [6...] - Actual payload data - uint8_t *buf_start = raw_buffer->data(); - uint8_t header_offset = frame_header_padding_ - total_header_len; + std::vector *raw_buffer = buffer.get_buffer(); + this->reusable_iovs_.clear(); + this->reusable_iovs_.reserve(packets.size()); - // Write the plaintext header - buf_start[header_offset] = 0x00; // indicator + for (const auto &packet : packets) { + uint16_t type = packet.message_type; + uint16_t offset = packet.offset; + uint16_t payload_len = packet.payload_size; - // Encode size varint directly into buffer - ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len); + // Calculate varint sizes for header layout + uint8_t size_varint_len = api::ProtoSize::varint(static_cast(payload_len)); + uint8_t type_varint_len = api::ProtoSize::varint(static_cast(type)); + uint8_t total_header_len = 1 + size_varint_len + type_varint_len; - // Encode type varint directly into buffer - ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len); + // Calculate where to start writing the header + // The header starts at the latest possible position to minimize unused padding + // + // Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3 + // [0-2] - Unused padding + // [3] - 0x00 indicator byte + // [4] - Payload size varint (1 byte, for sizes 0-127) + // [5] - Message type varint (1 byte, for types 0-127) + // [6...] - Actual payload data + // + // Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2 + // [0-1] - Unused padding + // [2] - 0x00 indicator byte + // [3-4] - Payload size varint (2 bytes, for sizes 128-16383) + // [5] - Message type varint (1 byte, for types 0-127) + // [6...] - Actual payload data + // + // Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0 + // [0] - 0x00 indicator byte + // [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151) + // [4-5] - Message type varint (2 bytes, for types 128-32767) + // [6...] - Actual payload data + // + // The message starts at offset + frame_header_padding_ + // So we write the header starting at offset + frame_header_padding_ - total_header_len + uint8_t *buf_start = raw_buffer->data() + offset; + uint32_t header_offset = frame_header_padding_ - total_header_len; - struct iovec iov; - // Point iov_base to the beginning of our header (skip unused padding) - // This ensures we only send the actual header and payload, not the empty padding bytes - iov.iov_base = buf_start + header_offset; - iov.iov_len = total_header_len + payload_len; + // Write the plaintext header + buf_start[header_offset] = 0x00; // indicator - return write_raw_(&iov, 1); + // Encode size varint directly into buffer + ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len); + + // Encode type varint directly into buffer + ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len); + + // Add iovec for this packet (header + payload) + struct iovec iov; + iov.iov_base = buf_start + header_offset; + iov.iov_len = total_header_len + payload_len; + this->reusable_iovs_.push_back(iov); + } + + // Send all packets in one writev call + return write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size()); } #endif // USE_API_PLAINTEXT diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 0799ae0f85..7e90153091 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -27,6 +27,17 @@ struct ReadPacketBuffer { uint16_t data_len; }; +// Packed packet info structure to minimize memory usage +struct PacketInfo { + uint16_t message_type; // 2 bytes + uint16_t offset; // 2 bytes (sufficient for packet size ~1460 bytes) + uint16_t payload_size; // 2 bytes (up to 65535 bytes) + uint16_t padding; // 2 byte (for alignment) + + PacketInfo(uint16_t type, uint16_t off, uint16_t size) + : message_type(type), offset(off), payload_size(size), padding(0) {} +}; + enum class APIError : int { OK = 0, WOULD_BLOCK = 1001, @@ -87,6 +98,10 @@ class APIFrameHelper { // Give this helper a name for logging void set_log_info(std::string info) { info_ = std::move(info); } virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) = 0; + // Write multiple protobuf packets in a single operation + // packets contains (message_type, offset, length) for each message in the buffer + // The buffer contains all messages with appropriate padding before each + virtual APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) = 0; // Get the frame header padding required by this protocol virtual uint8_t frame_header_padding() = 0; // Get the frame footer size required by this protocol @@ -110,38 +125,6 @@ class APIFrameHelper { const uint8_t *current_data() const { return data.data() + offset; } }; - // Queue of data buffers to be sent - std::deque tx_buf_; - - // Common state enum for all frame helpers - // Note: Not all states are used by all implementations - // - INITIALIZE: Used by both Noise and Plaintext - // - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol - // - DATA: Used by both Noise and Plaintext - // - CLOSED: Used by both Noise and Plaintext - // - FAILED: Used by both Noise and Plaintext - // - EXPLICIT_REJECT: Only used by Noise protocol - enum class State { - INITIALIZE = 1, - CLIENT_HELLO = 2, // Noise only - SERVER_HELLO = 3, // Noise only - HANDSHAKE = 4, // Noise only - DATA = 5, - CLOSED = 6, - FAILED = 7, - EXPLICIT_REJECT = 8, // Noise only - }; - - // Current state of the frame helper - State state_{State::INITIALIZE}; - - // Helper name for logging - std::string info_; - - // Socket for communication - socket::Socket *socket_{nullptr}; - std::unique_ptr socket_owned_; - // Common implementation for writing raw data to socket APIError write_raw_(const struct iovec *iov, int iovcnt); @@ -154,12 +137,41 @@ class APIFrameHelper { APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector &tx_buf, const std::string &info, StateEnum &state, StateEnum failed_state); + // Pointers first (4 bytes each) + socket::Socket *socket_{nullptr}; + std::unique_ptr socket_owned_; + + // Common state enum for all frame helpers + // Note: Not all states are used by all implementations + // - INITIALIZE: Used by both Noise and Plaintext + // - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol + // - DATA: Used by both Noise and Plaintext + // - CLOSED: Used by both Noise and Plaintext + // - FAILED: Used by both Noise and Plaintext + // - EXPLICIT_REJECT: Only used by Noise protocol + enum class State : uint8_t { + INITIALIZE = 1, + CLIENT_HELLO = 2, // Noise only + SERVER_HELLO = 3, // Noise only + HANDSHAKE = 4, // Noise only + DATA = 5, + CLOSED = 6, + FAILED = 7, + EXPLICIT_REJECT = 8, // Noise only + }; + + // Containers (size varies, but typically 12+ bytes on 32-bit) + std::deque tx_buf_; + std::string info_; + std::vector reusable_iovs_; + std::vector rx_buf_; + + // Group smaller types together + uint16_t rx_buf_len_ = 0; + State state_{State::INITIALIZE}; uint8_t frame_header_padding_{0}; uint8_t frame_footer_size_{0}; - - // Receive buffer for reading frame data - std::vector rx_buf_; - uint16_t rx_buf_len_ = 0; + // 5 bytes total, 3 bytes padding // Common initialization for both plaintext and noise protocols APIError init_common_(); @@ -182,6 +194,7 @@ class APINoiseFrameHelper : public APIFrameHelper { APIError loop() override; APIError read_packet(ReadPacketBuffer *buffer) override; APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override; + APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) override; // Get the frame header padding required by this protocol uint8_t frame_header_padding() override { return frame_header_padding_; } // Get the frame footer size required by this protocol @@ -194,19 +207,28 @@ class APINoiseFrameHelper : public APIFrameHelper { APIError init_handshake_(); APIError check_handshake_finished_(); void send_explicit_handshake_reject_(const std::string &reason); + + // Pointers first (4 bytes each) + NoiseHandshakeState *handshake_{nullptr}; + NoiseCipherState *send_cipher_{nullptr}; + NoiseCipherState *recv_cipher_{nullptr}; + + // Shared pointer (8 bytes on 32-bit = 4 bytes control block pointer + 4 bytes object pointer) + std::shared_ptr ctx_; + + // Vector (12 bytes on 32-bit) + std::vector prologue_; + + // NoiseProtocolId (size depends on implementation) + NoiseProtocolId nid_; + + // Group small types together // Fixed-size header buffer for noise protocol: // 1 byte for indicator + 2 bytes for message size (16-bit value, not varint) // Note: Maximum message size is UINT16_MAX (65535), with a limit of 128 bytes during handshake phase uint8_t rx_header_buf_[3]; uint8_t rx_header_buf_len_ = 0; - - std::vector prologue_; - - std::shared_ptr ctx_; - NoiseHandshakeState *handshake_{nullptr}; - NoiseCipherState *send_cipher_{nullptr}; - NoiseCipherState *recv_cipher_{nullptr}; - NoiseProtocolId nid_; + // 4 bytes total, no padding }; #endif // USE_API_NOISE @@ -226,12 +248,19 @@ class APIPlaintextFrameHelper : public APIFrameHelper { APIError loop() override; APIError read_packet(ReadPacketBuffer *buffer) override; APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override; + APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) override; uint8_t frame_header_padding() override { return frame_header_padding_; } // Get the frame footer size required by this protocol uint8_t frame_footer_size() override { return frame_footer_size_; } protected: APIError try_read_frame_(ParsedFrame *frame); + + // Group 2-byte aligned types + uint16_t rx_header_parsed_type_ = 0; + uint16_t rx_header_parsed_len_ = 0; + + // Group 1-byte types together // Fixed-size header buffer for plaintext protocol: // We now store the indicator byte + the two varints. // To match noise protocol's maximum message size (UINT16_MAX = 65535), we need: @@ -243,8 +272,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper { uint8_t rx_header_buf_[6]; // 1 byte indicator + 5 bytes for varints (3 for size + 2 for type) uint8_t rx_header_buf_pos_ = 0; bool rx_header_parsed_ = false; - uint16_t rx_header_parsed_type_ = 0; - uint16_t rx_header_parsed_len_ = 0; + // 8 bytes total, no padding needed }; #endif diff --git a/esphome/components/api/api_options.proto b/esphome/components/api/api_options.proto index feaf39ba15..3a547b8688 100644 --- a/esphome/components/api/api_options.proto +++ b/esphome/components/api/api_options.proto @@ -21,4 +21,5 @@ extend google.protobuf.MessageOptions { optional string ifdef = 1038; optional bool log = 1039 [default=true]; optional bool no_delay = 1040 [default=false]; + optional string base_class = 1041; } diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 2674b9c475..682778a881 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -516,6 +516,8 @@ template<> const char *proto_enum_to_string(enums::V return "VOICE_ASSISTANT_TTS_STREAM_START"; case enums::VOICE_ASSISTANT_TTS_STREAM_END: return "VOICE_ASSISTANT_TTS_STREAM_END"; + case enums::VOICE_ASSISTANT_INTENT_PROGRESS: + return "VOICE_ASSISTANT_INTENT_PROGRESS"; default: return "UNKNOWN"; } @@ -628,6 +630,7 @@ template<> const char *proto_enum_to_string(enums::UpdateC } } #endif + bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { @@ -794,28 +797,18 @@ void ConnectResponse::dump_to(std::string &out) const { out.append("}"); } #endif -void DisconnectRequest::encode(ProtoWriteBuffer buffer) const {} -void DisconnectRequest::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void DisconnectRequest::dump_to(std::string &out) const { out.append("DisconnectRequest {}"); } #endif -void DisconnectResponse::encode(ProtoWriteBuffer buffer) const {} -void DisconnectResponse::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void DisconnectResponse::dump_to(std::string &out) const { out.append("DisconnectResponse {}"); } #endif -void PingRequest::encode(ProtoWriteBuffer buffer) const {} -void PingRequest::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void PingRequest::dump_to(std::string &out) const { out.append("PingRequest {}"); } #endif -void PingResponse::encode(ProtoWriteBuffer buffer) const {} -void PingResponse::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {}"); } #endif -void DeviceInfoRequest::encode(ProtoWriteBuffer buffer) const {} -void DeviceInfoRequest::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); } #endif @@ -1103,18 +1096,12 @@ void DeviceInfoResponse::dump_to(std::string &out) const { out.append("}"); } #endif -void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {} -void ListEntitiesRequest::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void ListEntitiesRequest::dump_to(std::string &out) const { out.append("ListEntitiesRequest {}"); } #endif -void ListEntitiesDoneResponse::encode(ProtoWriteBuffer buffer) const {} -void ListEntitiesDoneResponse::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void ListEntitiesDoneResponse::dump_to(std::string &out) const { out.append("ListEntitiesDoneResponse {}"); } #endif -void SubscribeStatesRequest::encode(ProtoWriteBuffer buffer) const {} -void SubscribeStatesRequest::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void SubscribeStatesRequest::dump_to(std::string &out) const { out.append("SubscribeStatesRequest {}"); } #endif @@ -3512,8 +3499,6 @@ void NoiseEncryptionSetKeyResponse::dump_to(std::string &out) const { out.append("}"); } #endif -void SubscribeHomeassistantServicesRequest::encode(ProtoWriteBuffer buffer) const {} -void SubscribeHomeassistantServicesRequest::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const { out.append("SubscribeHomeassistantServicesRequest {}"); @@ -3639,8 +3624,6 @@ void HomeassistantServiceResponse::dump_to(std::string &out) const { out.append("}"); } #endif -void SubscribeHomeAssistantStatesRequest::encode(ProtoWriteBuffer buffer) const {} -void SubscribeHomeAssistantStatesRequest::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const { out.append("SubscribeHomeAssistantStatesRequest {}"); @@ -3744,8 +3727,6 @@ void HomeAssistantStateResponse::dump_to(std::string &out) const { out.append("}"); } #endif -void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {} -void GetTimeRequest::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); } #endif @@ -7717,8 +7698,6 @@ void BluetoothGATTNotifyDataResponse::dump_to(std::string &out) const { out.append("}"); } #endif -void SubscribeBluetoothConnectionsFreeRequest::encode(ProtoWriteBuffer buffer) const {} -void SubscribeBluetoothConnectionsFreeRequest::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void SubscribeBluetoothConnectionsFreeRequest::dump_to(std::string &out) const { out.append("SubscribeBluetoothConnectionsFreeRequest {}"); @@ -8002,8 +7981,6 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const { out.append("}"); } #endif -void UnsubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {} -void UnsubscribeBluetoothLEAdvertisementsRequest::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const { out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}"); @@ -8669,8 +8646,6 @@ void VoiceAssistantWakeWord::dump_to(std::string &out) const { out.append("}"); } #endif -void VoiceAssistantConfigurationRequest::encode(ProtoWriteBuffer buffer) const {} -void VoiceAssistantConfigurationRequest::calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const { out.append("VoiceAssistantConfigurationRequest {}"); diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 114f2c1604..ab30c3a593 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -208,6 +208,7 @@ enum VoiceAssistantEvent : uint32_t { VOICE_ASSISTANT_STT_VAD_END = 12, VOICE_ASSISTANT_TTS_STREAM_START = 98, VOICE_ASSISTANT_TTS_STREAM_END = 99, + VOICE_ASSISTANT_INTENT_PROGRESS = 100, }; enum VoiceAssistantTimerEvent : uint32_t { VOICE_ASSISTANT_TIMER_STARTED = 0, @@ -253,8 +254,34 @@ enum UpdateCommand : uint32_t { } // namespace enums +class InfoResponseProtoMessage : public ProtoMessage { + public: + ~InfoResponseProtoMessage() override = default; + std::string object_id{}; + uint32_t key{0}; + std::string name{}; + std::string unique_id{}; + bool disabled_by_default{false}; + std::string icon{}; + enums::EntityCategory entity_category{}; + + protected: +}; + +class StateResponseProtoMessage : public ProtoMessage { + public: + ~StateResponseProtoMessage() override = default; + uint32_t key{0}; + + protected: +}; class HelloRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 1; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "hello_request"; } +#endif std::string client_info{}; uint32_t api_version_major{0}; uint32_t api_version_minor{0}; @@ -270,6 +297,11 @@ class HelloRequest : public ProtoMessage { }; class HelloResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 2; + static constexpr uint16_t ESTIMATED_SIZE = 26; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "hello_response"; } +#endif uint32_t api_version_major{0}; uint32_t api_version_minor{0}; std::string server_info{}; @@ -286,6 +318,11 @@ class HelloResponse : public ProtoMessage { }; class ConnectRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 3; + static constexpr uint16_t ESTIMATED_SIZE = 9; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "connect_request"; } +#endif std::string password{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -298,6 +335,11 @@ class ConnectRequest : public ProtoMessage { }; class ConnectResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 4; + static constexpr uint16_t ESTIMATED_SIZE = 2; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "connect_response"; } +#endif bool invalid_password{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -310,8 +352,11 @@ class ConnectResponse : public ProtoMessage { }; class DisconnectRequest : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 5; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "disconnect_request"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -320,8 +365,11 @@ class DisconnectRequest : public ProtoMessage { }; class DisconnectResponse : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 6; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "disconnect_response"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -330,8 +378,11 @@ class DisconnectResponse : public ProtoMessage { }; class PingRequest : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 7; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "ping_request"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -340,8 +391,11 @@ class PingRequest : public ProtoMessage { }; class PingResponse : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 8; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "ping_response"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -350,8 +404,11 @@ class PingResponse : public ProtoMessage { }; class DeviceInfoRequest : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 9; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "device_info_request"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -375,6 +432,11 @@ class SubDeviceInfo : public ProtoMessage { }; class DeviceInfoResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 10; + static constexpr uint16_t ESTIMATED_SIZE = 165; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "device_info_response"; } +#endif bool uses_password{false}; std::string name{}; std::string mac_address{}; @@ -407,8 +469,11 @@ class DeviceInfoResponse : public ProtoMessage { }; class ListEntitiesRequest : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 11; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_request"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -417,8 +482,11 @@ class ListEntitiesRequest : public ProtoMessage { }; class ListEntitiesDoneResponse : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 19; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_done_response"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -427,25 +495,26 @@ class ListEntitiesDoneResponse : public ProtoMessage { }; class SubscribeStatesRequest : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 20; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_states_request"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif protected: }; -class ListEntitiesBinarySensorResponse : public ProtoMessage { +class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; + static constexpr uint16_t MESSAGE_TYPE = 12; + static constexpr uint16_t ESTIMATED_SIZE = 60; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_binary_sensor_response"; } +#endif std::string device_class{}; bool is_status_binary_sensor{false}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -458,9 +527,13 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class BinarySensorStateResponse : public ProtoMessage { +class BinarySensorStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 21; + static constexpr uint16_t ESTIMATED_SIZE = 9; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "binary_sensor_state_response"; } +#endif bool state{false}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -473,19 +546,17 @@ class BinarySensorStateResponse : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesCoverResponse : public ProtoMessage { +class ListEntitiesCoverResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; + static constexpr uint16_t MESSAGE_TYPE = 13; + static constexpr uint16_t ESTIMATED_SIZE = 66; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_cover_response"; } +#endif bool assumed_state{false}; bool supports_position{false}; bool supports_tilt{false}; std::string device_class{}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; bool supports_stop{false}; uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -499,9 +570,13 @@ class ListEntitiesCoverResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class CoverStateResponse : public ProtoMessage { +class CoverStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 22; + static constexpr uint16_t ESTIMATED_SIZE = 19; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "cover_state_response"; } +#endif enums::LegacyCoverState legacy_state{}; float position{0.0f}; float tilt{0.0f}; @@ -518,6 +593,11 @@ class CoverStateResponse : public ProtoMessage { }; class CoverCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 30; + static constexpr uint16_t ESTIMATED_SIZE = 25; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "cover_command_request"; } +#endif uint32_t key{0}; bool has_legacy_command{false}; enums::LegacyCoverCommand legacy_command{}; @@ -536,19 +616,17 @@ class CoverCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesFanResponse : public ProtoMessage { +class ListEntitiesFanResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; + static constexpr uint16_t MESSAGE_TYPE = 14; + static constexpr uint16_t ESTIMATED_SIZE = 77; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_fan_response"; } +#endif bool supports_oscillation{false}; bool supports_speed{false}; bool supports_direction{false}; int32_t supported_speed_count{0}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; std::vector supported_preset_modes{}; uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -562,9 +640,13 @@ class ListEntitiesFanResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class FanStateResponse : public ProtoMessage { +class FanStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 23; + static constexpr uint16_t ESTIMATED_SIZE = 26; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "fan_state_response"; } +#endif bool state{false}; bool oscillating{false}; enums::FanSpeed speed{}; @@ -584,6 +666,11 @@ class FanStateResponse : public ProtoMessage { }; class FanCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 31; + static constexpr uint16_t ESTIMATED_SIZE = 38; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "fan_command_request"; } +#endif uint32_t key{0}; bool has_state{false}; bool state{false}; @@ -608,12 +695,13 @@ class FanCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesLightResponse : public ProtoMessage { +class ListEntitiesLightResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; + static constexpr uint16_t MESSAGE_TYPE = 15; + static constexpr uint16_t ESTIMATED_SIZE = 90; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_light_response"; } +#endif std::vector supported_color_modes{}; bool legacy_supports_brightness{false}; bool legacy_supports_rgb{false}; @@ -622,9 +710,6 @@ class ListEntitiesLightResponse : public ProtoMessage { float min_mireds{0.0f}; float max_mireds{0.0f}; std::vector effects{}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -637,9 +722,13 @@ class ListEntitiesLightResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class LightStateResponse : public ProtoMessage { +class LightStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 24; + static constexpr uint16_t ESTIMATED_SIZE = 63; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "light_state_response"; } +#endif bool state{false}; float brightness{0.0f}; enums::ColorMode color_mode{}; @@ -665,6 +754,11 @@ class LightStateResponse : public ProtoMessage { }; class LightCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 32; + static constexpr uint16_t ESTIMATED_SIZE = 107; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "light_command_request"; } +#endif uint32_t key{0}; bool has_state{false}; bool state{false}; @@ -703,21 +797,19 @@ class LightCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesSensorResponse : public ProtoMessage { +class ListEntitiesSensorResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; + static constexpr uint16_t MESSAGE_TYPE = 16; + static constexpr uint16_t ESTIMATED_SIZE = 77; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_sensor_response"; } +#endif std::string unit_of_measurement{}; int32_t accuracy_decimals{0}; bool force_update{false}; std::string device_class{}; enums::SensorStateClass state_class{}; enums::SensorLastResetType legacy_last_reset_type{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -730,9 +822,13 @@ class ListEntitiesSensorResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class SensorStateResponse : public ProtoMessage { +class SensorStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 25; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "sensor_state_response"; } +#endif float state{0.0f}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -745,16 +841,14 @@ class SensorStateResponse : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesSwitchResponse : public ProtoMessage { +class ListEntitiesSwitchResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; + static constexpr uint16_t MESSAGE_TYPE = 17; + static constexpr uint16_t ESTIMATED_SIZE = 60; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_switch_response"; } +#endif bool assumed_state{false}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; std::string device_class{}; uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -768,9 +862,13 @@ class ListEntitiesSwitchResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class SwitchStateResponse : public ProtoMessage { +class SwitchStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 26; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "switch_state_response"; } +#endif bool state{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -784,6 +882,11 @@ class SwitchStateResponse : public ProtoMessage { }; class SwitchCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 33; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "switch_command_request"; } +#endif uint32_t key{0}; bool state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -796,15 +899,13 @@ class SwitchCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesTextSensorResponse : public ProtoMessage { +class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 18; + static constexpr uint16_t ESTIMATED_SIZE = 58; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_text_sensor_response"; } +#endif std::string device_class{}; uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -818,9 +919,13 @@ class ListEntitiesTextSensorResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class TextSensorStateResponse : public ProtoMessage { +class TextSensorStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 27; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "text_sensor_state_response"; } +#endif std::string state{}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -836,6 +941,11 @@ class TextSensorStateResponse : public ProtoMessage { }; class SubscribeLogsRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 28; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_logs_request"; } +#endif enums::LogLevel level{}; bool dump_config{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -849,6 +959,11 @@ class SubscribeLogsRequest : public ProtoMessage { }; class SubscribeLogsResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 29; + static constexpr uint16_t ESTIMATED_SIZE = 13; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_logs_response"; } +#endif enums::LogLevel level{}; std::string message{}; bool send_failed{false}; @@ -864,6 +979,11 @@ class SubscribeLogsResponse : public ProtoMessage { }; class NoiseEncryptionSetKeyRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 124; + static constexpr uint16_t ESTIMATED_SIZE = 9; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "noise_encryption_set_key_request"; } +#endif std::string key{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -876,6 +996,11 @@ class NoiseEncryptionSetKeyRequest : public ProtoMessage { }; class NoiseEncryptionSetKeyResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 125; + static constexpr uint16_t ESTIMATED_SIZE = 2; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "noise_encryption_set_key_response"; } +#endif bool success{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -888,8 +1013,11 @@ class NoiseEncryptionSetKeyResponse : public ProtoMessage { }; class SubscribeHomeassistantServicesRequest : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 34; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_homeassistant_services_request"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -911,6 +1039,11 @@ class HomeassistantServiceMap : public ProtoMessage { }; class HomeassistantServiceResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 35; + static constexpr uint16_t ESTIMATED_SIZE = 113; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "homeassistant_service_response"; } +#endif std::string service{}; std::vector data{}; std::vector data_template{}; @@ -928,8 +1061,11 @@ class HomeassistantServiceResponse : public ProtoMessage { }; class SubscribeHomeAssistantStatesRequest : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 38; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_home_assistant_states_request"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -938,6 +1074,11 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage { }; class SubscribeHomeAssistantStateResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 39; + static constexpr uint16_t ESTIMATED_SIZE = 20; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_home_assistant_state_response"; } +#endif std::string entity_id{}; std::string attribute{}; bool once{false}; @@ -953,6 +1094,11 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage { }; class HomeAssistantStateResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 40; + static constexpr uint16_t ESTIMATED_SIZE = 27; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "home_assistant_state_response"; } +#endif std::string entity_id{}; std::string state{}; std::string attribute{}; @@ -967,8 +1113,11 @@ class HomeAssistantStateResponse : public ProtoMessage { }; class GetTimeRequest : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 36; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "get_time_request"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -977,6 +1126,11 @@ class GetTimeRequest : public ProtoMessage { }; class GetTimeResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 37; + static constexpr uint16_t ESTIMATED_SIZE = 5; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "get_time_response"; } +#endif uint32_t epoch_seconds{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1003,6 +1157,11 @@ class ListEntitiesServicesArgument : public ProtoMessage { }; class ListEntitiesServicesResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 41; + static constexpr uint16_t ESTIMATED_SIZE = 48; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_services_response"; } +#endif std::string name{}; uint32_t key{0}; std::vector args{}; @@ -1040,6 +1199,11 @@ class ExecuteServiceArgument : public ProtoMessage { }; class ExecuteServiceRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 42; + static constexpr uint16_t ESTIMATED_SIZE = 39; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "execute_service_request"; } +#endif uint32_t key{0}; std::vector args{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1052,15 +1216,13 @@ class ExecuteServiceRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; -class ListEntitiesCameraResponse : public ProtoMessage { +class ListEntitiesCameraResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 43; + static constexpr uint16_t ESTIMATED_SIZE = 49; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_camera_response"; } +#endif uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1075,6 +1237,11 @@ class ListEntitiesCameraResponse : public ProtoMessage { }; class CameraImageResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 44; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "camera_image_response"; } +#endif uint32_t key{0}; std::string data{}; bool done{false}; @@ -1091,6 +1258,11 @@ class CameraImageResponse : public ProtoMessage { }; class CameraImageRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 45; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "camera_image_request"; } +#endif bool single{false}; bool stream{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -1102,12 +1274,13 @@ class CameraImageRequest : public ProtoMessage { protected: bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesClimateResponse : public ProtoMessage { +class ListEntitiesClimateResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; + static constexpr uint16_t MESSAGE_TYPE = 46; + static constexpr uint16_t ESTIMATED_SIZE = 156; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_climate_response"; } +#endif bool supports_current_temperature{false}; bool supports_two_point_target_temperature{false}; std::vector supported_modes{}; @@ -1121,9 +1294,6 @@ class ListEntitiesClimateResponse : public ProtoMessage { std::vector supported_custom_fan_modes{}; std::vector supported_presets{}; std::vector supported_custom_presets{}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; float visual_current_temperature_step{0.0f}; bool supports_current_humidity{false}; bool supports_target_humidity{false}; @@ -1141,9 +1311,13 @@ class ListEntitiesClimateResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ClimateStateResponse : public ProtoMessage { +class ClimateStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 47; + static constexpr uint16_t ESTIMATED_SIZE = 65; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "climate_state_response"; } +#endif enums::ClimateMode mode{}; float current_temperature{0.0f}; float target_temperature{0.0f}; @@ -1171,6 +1345,11 @@ class ClimateStateResponse : public ProtoMessage { }; class ClimateCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 48; + static constexpr uint16_t ESTIMATED_SIZE = 83; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "climate_command_request"; } +#endif uint32_t key{0}; bool has_mode{false}; enums::ClimateMode mode{}; @@ -1205,18 +1384,16 @@ class ClimateCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesNumberResponse : public ProtoMessage { +class ListEntitiesNumberResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; + static constexpr uint16_t MESSAGE_TYPE = 49; + static constexpr uint16_t ESTIMATED_SIZE = 84; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_number_response"; } +#endif float min_value{0.0f}; float max_value{0.0f}; float step{0.0f}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; std::string unit_of_measurement{}; enums::NumberMode mode{}; std::string device_class{}; @@ -1232,9 +1409,13 @@ class ListEntitiesNumberResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class NumberStateResponse : public ProtoMessage { +class NumberStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 50; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "number_state_response"; } +#endif float state{0.0f}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -1249,6 +1430,11 @@ class NumberStateResponse : public ProtoMessage { }; class NumberCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 51; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "number_command_request"; } +#endif uint32_t key{0}; float state{0.0f}; void encode(ProtoWriteBuffer buffer) const override; @@ -1260,16 +1446,14 @@ class NumberCommandRequest : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; }; -class ListEntitiesSelectResponse : public ProtoMessage { +class ListEntitiesSelectResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; + static constexpr uint16_t MESSAGE_TYPE = 52; + static constexpr uint16_t ESTIMATED_SIZE = 67; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_select_response"; } +#endif std::vector options{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1282,9 +1466,13 @@ class ListEntitiesSelectResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class SelectStateResponse : public ProtoMessage { +class SelectStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 53; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "select_state_response"; } +#endif std::string state{}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -1300,6 +1488,11 @@ class SelectStateResponse : public ProtoMessage { }; class SelectCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 54; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "select_command_request"; } +#endif uint32_t key{0}; std::string state{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1312,18 +1505,16 @@ class SelectCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; -class ListEntitiesSirenResponse : public ProtoMessage { +class ListEntitiesSirenResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; + static constexpr uint16_t MESSAGE_TYPE = 55; + static constexpr uint16_t ESTIMATED_SIZE = 67; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_siren_response"; } +#endif std::vector tones{}; bool supports_duration{false}; bool supports_volume{false}; - enums::EntityCategory entity_category{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -1335,9 +1526,13 @@ class ListEntitiesSirenResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class SirenStateResponse : public ProtoMessage { +class SirenStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 56; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "siren_state_response"; } +#endif bool state{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1351,6 +1546,11 @@ class SirenStateResponse : public ProtoMessage { }; class SirenCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 57; + static constexpr uint16_t ESTIMATED_SIZE = 33; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "siren_command_request"; } +#endif uint32_t key{0}; bool has_state{false}; bool state{false}; @@ -1371,15 +1571,13 @@ class SirenCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesLockResponse : public ProtoMessage { +class ListEntitiesLockResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 58; + static constexpr uint16_t ESTIMATED_SIZE = 64; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_lock_response"; } +#endif bool assumed_state{false}; bool supports_open{false}; bool requires_code{false}; @@ -1396,9 +1594,13 @@ class ListEntitiesLockResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class LockStateResponse : public ProtoMessage { +class LockStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 59; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "lock_state_response"; } +#endif enums::LockState state{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1412,6 +1614,11 @@ class LockStateResponse : public ProtoMessage { }; class LockCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 60; + static constexpr uint16_t ESTIMATED_SIZE = 18; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "lock_command_request"; } +#endif uint32_t key{0}; enums::LockCommand command{}; bool has_code{false}; @@ -1427,15 +1634,13 @@ class LockCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesButtonResponse : public ProtoMessage { +class ListEntitiesButtonResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 61; + static constexpr uint16_t ESTIMATED_SIZE = 58; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_button_response"; } +#endif std::string device_class{}; uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -1451,6 +1656,11 @@ class ListEntitiesButtonResponse : public ProtoMessage { }; class ButtonCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 62; + static constexpr uint16_t ESTIMATED_SIZE = 5; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "button_command_request"; } +#endif uint32_t key{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1478,15 +1688,13 @@ class MediaPlayerSupportedFormat : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesMediaPlayerResponse : public ProtoMessage { +class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 63; + static constexpr uint16_t ESTIMATED_SIZE = 85; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_media_player_response"; } +#endif bool supports_pause{false}; std::vector supported_formats{}; uint32_t device_uid{0}; @@ -1501,9 +1709,13 @@ class ListEntitiesMediaPlayerResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class MediaPlayerStateResponse : public ProtoMessage { +class MediaPlayerStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 64; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "media_player_state_response"; } +#endif enums::MediaPlayerState state{}; float volume{0.0f}; bool muted{false}; @@ -1519,6 +1731,11 @@ class MediaPlayerStateResponse : public ProtoMessage { }; class MediaPlayerCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 65; + static constexpr uint16_t ESTIMATED_SIZE = 31; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "media_player_command_request"; } +#endif uint32_t key{0}; bool has_command{false}; enums::MediaPlayerCommand command{}; @@ -1541,6 +1758,11 @@ class MediaPlayerCommandRequest : public ProtoMessage { }; class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 66; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_bluetooth_le_advertisements_request"; } +#endif uint32_t flags{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1568,6 +1790,11 @@ class BluetoothServiceData : public ProtoMessage { }; class BluetoothLEAdvertisementResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 67; + static constexpr uint16_t ESTIMATED_SIZE = 107; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_le_advertisement_response"; } +#endif uint64_t address{0}; std::string name{}; int32_t rssi{0}; @@ -1603,6 +1830,11 @@ class BluetoothLERawAdvertisement : public ProtoMessage { }; class BluetoothLERawAdvertisementsResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 93; + static constexpr uint16_t ESTIMATED_SIZE = 34; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_le_raw_advertisements_response"; } +#endif std::vector advertisements{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1615,6 +1847,11 @@ class BluetoothLERawAdvertisementsResponse : public ProtoMessage { }; class BluetoothDeviceRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 68; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_device_request"; } +#endif uint64_t address{0}; enums::BluetoothDeviceRequestType request_type{}; bool has_address_type{false}; @@ -1630,6 +1867,11 @@ class BluetoothDeviceRequest : public ProtoMessage { }; class BluetoothDeviceConnectionResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 69; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_device_connection_response"; } +#endif uint64_t address{0}; bool connected{false}; uint32_t mtu{0}; @@ -1645,6 +1887,11 @@ class BluetoothDeviceConnectionResponse : public ProtoMessage { }; class BluetoothGATTGetServicesRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 70; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_get_services_request"; } +#endif uint64_t address{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1701,6 +1948,11 @@ class BluetoothGATTService : public ProtoMessage { }; class BluetoothGATTGetServicesResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 71; + static constexpr uint16_t ESTIMATED_SIZE = 38; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_get_services_response"; } +#endif uint64_t address{0}; std::vector services{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1715,6 +1967,11 @@ class BluetoothGATTGetServicesResponse : public ProtoMessage { }; class BluetoothGATTGetServicesDoneResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 72; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_get_services_done_response"; } +#endif uint64_t address{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1727,6 +1984,11 @@ class BluetoothGATTGetServicesDoneResponse : public ProtoMessage { }; class BluetoothGATTReadRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 73; + static constexpr uint16_t ESTIMATED_SIZE = 8; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_read_request"; } +#endif uint64_t address{0}; uint32_t handle{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -1740,6 +2002,11 @@ class BluetoothGATTReadRequest : public ProtoMessage { }; class BluetoothGATTReadResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 74; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_read_response"; } +#endif uint64_t address{0}; uint32_t handle{0}; std::string data{}; @@ -1755,6 +2022,11 @@ class BluetoothGATTReadResponse : public ProtoMessage { }; class BluetoothGATTWriteRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 75; + static constexpr uint16_t ESTIMATED_SIZE = 19; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_write_request"; } +#endif uint64_t address{0}; uint32_t handle{0}; bool response{false}; @@ -1771,6 +2043,11 @@ class BluetoothGATTWriteRequest : public ProtoMessage { }; class BluetoothGATTReadDescriptorRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 76; + static constexpr uint16_t ESTIMATED_SIZE = 8; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_read_descriptor_request"; } +#endif uint64_t address{0}; uint32_t handle{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -1784,6 +2061,11 @@ class BluetoothGATTReadDescriptorRequest : public ProtoMessage { }; class BluetoothGATTWriteDescriptorRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 77; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_write_descriptor_request"; } +#endif uint64_t address{0}; uint32_t handle{0}; std::string data{}; @@ -1799,6 +2081,11 @@ class BluetoothGATTWriteDescriptorRequest : public ProtoMessage { }; class BluetoothGATTNotifyRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 78; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_notify_request"; } +#endif uint64_t address{0}; uint32_t handle{0}; bool enable{false}; @@ -1813,6 +2100,11 @@ class BluetoothGATTNotifyRequest : public ProtoMessage { }; class BluetoothGATTNotifyDataResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 79; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_notify_data_response"; } +#endif uint64_t address{0}; uint32_t handle{0}; std::string data{}; @@ -1828,8 +2120,11 @@ class BluetoothGATTNotifyDataResponse : public ProtoMessage { }; class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 80; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_bluetooth_connections_free_request"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1838,6 +2133,11 @@ class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage { }; class BluetoothConnectionsFreeResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 81; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_connections_free_response"; } +#endif uint32_t free{0}; uint32_t limit{0}; std::vector allocated{}; @@ -1852,6 +2152,11 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage { }; class BluetoothGATTErrorResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 82; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_error_response"; } +#endif uint64_t address{0}; uint32_t handle{0}; int32_t error{0}; @@ -1866,6 +2171,11 @@ class BluetoothGATTErrorResponse : public ProtoMessage { }; class BluetoothGATTWriteResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 83; + static constexpr uint16_t ESTIMATED_SIZE = 8; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_write_response"; } +#endif uint64_t address{0}; uint32_t handle{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -1879,6 +2189,11 @@ class BluetoothGATTWriteResponse : public ProtoMessage { }; class BluetoothGATTNotifyResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 84; + static constexpr uint16_t ESTIMATED_SIZE = 8; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_notify_response"; } +#endif uint64_t address{0}; uint32_t handle{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -1892,6 +2207,11 @@ class BluetoothGATTNotifyResponse : public ProtoMessage { }; class BluetoothDevicePairingResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 85; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_device_pairing_response"; } +#endif uint64_t address{0}; bool paired{false}; int32_t error{0}; @@ -1906,6 +2226,11 @@ class BluetoothDevicePairingResponse : public ProtoMessage { }; class BluetoothDeviceUnpairingResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 86; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_device_unpairing_response"; } +#endif uint64_t address{0}; bool success{false}; int32_t error{0}; @@ -1920,8 +2245,11 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage { }; class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 87; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "unsubscribe_bluetooth_le_advertisements_request"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1930,6 +2258,11 @@ class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { }; class BluetoothDeviceClearCacheResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 88; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_device_clear_cache_response"; } +#endif uint64_t address{0}; bool success{false}; int32_t error{0}; @@ -1944,6 +2277,11 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage { }; class BluetoothScannerStateResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 126; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_scanner_state_response"; } +#endif enums::BluetoothScannerState state{}; enums::BluetoothScannerMode mode{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1957,6 +2295,11 @@ class BluetoothScannerStateResponse : public ProtoMessage { }; class BluetoothScannerSetModeRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 127; + static constexpr uint16_t ESTIMATED_SIZE = 2; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_scanner_set_mode_request"; } +#endif enums::BluetoothScannerMode mode{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1969,6 +2312,11 @@ class BluetoothScannerSetModeRequest : public ProtoMessage { }; class SubscribeVoiceAssistantRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 89; + static constexpr uint16_t ESTIMATED_SIZE = 6; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_voice_assistant_request"; } +#endif bool subscribe{false}; uint32_t flags{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -1997,6 +2345,11 @@ class VoiceAssistantAudioSettings : public ProtoMessage { }; class VoiceAssistantRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 90; + static constexpr uint16_t ESTIMATED_SIZE = 41; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_request"; } +#endif bool start{false}; std::string conversation_id{}; uint32_t flags{0}; @@ -2014,6 +2367,11 @@ class VoiceAssistantRequest : public ProtoMessage { }; class VoiceAssistantResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 91; + static constexpr uint16_t ESTIMATED_SIZE = 6; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_response"; } +#endif uint32_t port{0}; bool error{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -2040,6 +2398,11 @@ class VoiceAssistantEventData : public ProtoMessage { }; class VoiceAssistantEventResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 92; + static constexpr uint16_t ESTIMATED_SIZE = 36; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_event_response"; } +#endif enums::VoiceAssistantEvent event_type{}; std::vector data{}; void encode(ProtoWriteBuffer buffer) const override; @@ -2054,6 +2417,11 @@ class VoiceAssistantEventResponse : public ProtoMessage { }; class VoiceAssistantAudio : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 106; + static constexpr uint16_t ESTIMATED_SIZE = 11; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_audio"; } +#endif std::string data{}; bool end{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -2068,6 +2436,11 @@ class VoiceAssistantAudio : public ProtoMessage { }; class VoiceAssistantTimerEventResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 115; + static constexpr uint16_t ESTIMATED_SIZE = 30; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_timer_event_response"; } +#endif enums::VoiceAssistantTimerEvent event_type{}; std::string timer_id{}; std::string name{}; @@ -2086,6 +2459,11 @@ class VoiceAssistantTimerEventResponse : public ProtoMessage { }; class VoiceAssistantAnnounceRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 119; + static constexpr uint16_t ESTIMATED_SIZE = 29; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_announce_request"; } +#endif std::string media_id{}; std::string text{}; std::string preannounce_media_id{}; @@ -2102,6 +2480,11 @@ class VoiceAssistantAnnounceRequest : public ProtoMessage { }; class VoiceAssistantAnnounceFinished : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 120; + static constexpr uint16_t ESTIMATED_SIZE = 2; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_announce_finished"; } +#endif bool success{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2128,8 +2511,11 @@ class VoiceAssistantWakeWord : public ProtoMessage { }; class VoiceAssistantConfigurationRequest : public ProtoMessage { public: - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; + static constexpr uint16_t MESSAGE_TYPE = 121; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_configuration_request"; } +#endif #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2138,6 +2524,11 @@ class VoiceAssistantConfigurationRequest : public ProtoMessage { }; class VoiceAssistantConfigurationResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 122; + static constexpr uint16_t ESTIMATED_SIZE = 56; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_configuration_response"; } +#endif std::vector available_wake_words{}; std::vector active_wake_words{}; uint32_t max_active_wake_words{0}; @@ -2153,6 +2544,11 @@ class VoiceAssistantConfigurationResponse : public ProtoMessage { }; class VoiceAssistantSetConfiguration : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 123; + static constexpr uint16_t ESTIMATED_SIZE = 18; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_set_configuration"; } +#endif std::vector active_wake_words{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2163,15 +2559,13 @@ class VoiceAssistantSetConfiguration : public ProtoMessage { protected: bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; -class ListEntitiesAlarmControlPanelResponse : public ProtoMessage { +class ListEntitiesAlarmControlPanelResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 94; + static constexpr uint16_t ESTIMATED_SIZE = 57; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_alarm_control_panel_response"; } +#endif uint32_t supported_features{0}; bool requires_code{false}; bool requires_code_to_arm{false}; @@ -2187,9 +2581,13 @@ class ListEntitiesAlarmControlPanelResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class AlarmControlPanelStateResponse : public ProtoMessage { +class AlarmControlPanelStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 95; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "alarm_control_panel_state_response"; } +#endif enums::AlarmControlPanelState state{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2203,6 +2601,11 @@ class AlarmControlPanelStateResponse : public ProtoMessage { }; class AlarmControlPanelCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 96; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "alarm_control_panel_command_request"; } +#endif uint32_t key{0}; enums::AlarmControlPanelStateCommand command{}; std::string code{}; @@ -2217,15 +2620,13 @@ class AlarmControlPanelCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesTextResponse : public ProtoMessage { +class ListEntitiesTextResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 97; + static constexpr uint16_t ESTIMATED_SIZE = 68; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_text_response"; } +#endif uint32_t min_length{0}; uint32_t max_length{0}; std::string pattern{}; @@ -2242,9 +2643,13 @@ class ListEntitiesTextResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class TextStateResponse : public ProtoMessage { +class TextStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 98; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "text_state_response"; } +#endif std::string state{}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -2260,6 +2665,11 @@ class TextStateResponse : public ProtoMessage { }; class TextCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 99; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "text_command_request"; } +#endif uint32_t key{0}; std::string state{}; void encode(ProtoWriteBuffer buffer) const override; @@ -2272,15 +2682,13 @@ class TextCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; -class ListEntitiesDateResponse : public ProtoMessage { +class ListEntitiesDateResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 100; + static constexpr uint16_t ESTIMATED_SIZE = 49; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_date_response"; } +#endif uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2293,9 +2701,13 @@ class ListEntitiesDateResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class DateStateResponse : public ProtoMessage { +class DateStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 101; + static constexpr uint16_t ESTIMATED_SIZE = 19; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "date_state_response"; } +#endif bool missing_state{false}; uint32_t year{0}; uint32_t month{0}; @@ -2312,6 +2724,11 @@ class DateStateResponse : public ProtoMessage { }; class DateCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 102; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "date_command_request"; } +#endif uint32_t key{0}; uint32_t year{0}; uint32_t month{0}; @@ -2326,15 +2743,13 @@ class DateCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesTimeResponse : public ProtoMessage { +class ListEntitiesTimeResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 103; + static constexpr uint16_t ESTIMATED_SIZE = 49; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_time_response"; } +#endif uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2347,9 +2762,13 @@ class ListEntitiesTimeResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class TimeStateResponse : public ProtoMessage { +class TimeStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 104; + static constexpr uint16_t ESTIMATED_SIZE = 19; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "time_state_response"; } +#endif bool missing_state{false}; uint32_t hour{0}; uint32_t minute{0}; @@ -2366,6 +2785,11 @@ class TimeStateResponse : public ProtoMessage { }; class TimeCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 105; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "time_command_request"; } +#endif uint32_t key{0}; uint32_t hour{0}; uint32_t minute{0}; @@ -2380,15 +2804,13 @@ class TimeCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesEventResponse : public ProtoMessage { +class ListEntitiesEventResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 107; + static constexpr uint16_t ESTIMATED_SIZE = 76; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_event_response"; } +#endif std::string device_class{}; std::vector event_types{}; uint32_t device_uid{0}; @@ -2403,9 +2825,13 @@ class ListEntitiesEventResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class EventResponse : public ProtoMessage { +class EventResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 108; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "event_response"; } +#endif std::string event_type{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2417,15 +2843,13 @@ class EventResponse : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; -class ListEntitiesValveResponse : public ProtoMessage { +class ListEntitiesValveResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 109; + static constexpr uint16_t ESTIMATED_SIZE = 64; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_valve_response"; } +#endif std::string device_class{}; bool assumed_state{false}; bool supports_position{false}; @@ -2442,9 +2866,13 @@ class ListEntitiesValveResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ValveStateResponse : public ProtoMessage { +class ValveStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 110; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "valve_state_response"; } +#endif float position{0.0f}; enums::ValveOperation current_operation{}; void encode(ProtoWriteBuffer buffer) const override; @@ -2459,6 +2887,11 @@ class ValveStateResponse : public ProtoMessage { }; class ValveCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 111; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "valve_command_request"; } +#endif uint32_t key{0}; bool has_position{false}; float position{0.0f}; @@ -2473,15 +2906,13 @@ class ValveCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesDateTimeResponse : public ProtoMessage { +class ListEntitiesDateTimeResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 112; + static constexpr uint16_t ESTIMATED_SIZE = 49; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_date_time_response"; } +#endif uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2494,9 +2925,13 @@ class ListEntitiesDateTimeResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class DateTimeStateResponse : public ProtoMessage { +class DateTimeStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 113; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "date_time_state_response"; } +#endif bool missing_state{false}; uint32_t epoch_seconds{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -2511,6 +2946,11 @@ class DateTimeStateResponse : public ProtoMessage { }; class DateTimeCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 114; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "date_time_command_request"; } +#endif uint32_t key{0}; uint32_t epoch_seconds{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -2522,15 +2962,13 @@ class DateTimeCommandRequest : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; }; -class ListEntitiesUpdateResponse : public ProtoMessage { +class ListEntitiesUpdateResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 116; + static constexpr uint16_t ESTIMATED_SIZE = 58; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_update_response"; } +#endif std::string device_class{}; uint32_t device_uid{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -2544,9 +2982,13 @@ class ListEntitiesUpdateResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class UpdateStateResponse : public ProtoMessage { +class UpdateStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 117; + static constexpr uint16_t ESTIMATED_SIZE = 61; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "update_state_response"; } +#endif bool missing_state{false}; bool in_progress{false}; bool has_progress{false}; @@ -2569,6 +3011,11 @@ class UpdateStateResponse : public ProtoMessage { }; class UpdateCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 118; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "update_command_request"; } +#endif uint32_t key{0}; enums::UpdateCommand command{}; void encode(ProtoWriteBuffer buffer) const override; diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 5a701aeafa..03017fdfff 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -8,688 +8,12 @@ namespace api { static const char *const TAG = "api.service"; -bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) { #ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 2); -} -bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 4); -} -bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 5); -} -bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 6); -} -bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 7); -} -bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 8); -} -bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 10); -} -bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 19); -} -#ifdef USE_BINARY_SENSOR -bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 12); -} -#endif -#ifdef USE_BINARY_SENSOR -bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 21); -} -#endif -#ifdef USE_COVER -bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 13); -} -#endif -#ifdef USE_COVER -bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 22); -} -#endif -#ifdef USE_COVER -#endif -#ifdef USE_FAN -bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 14); -} -#endif -#ifdef USE_FAN -bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 23); -} -#endif -#ifdef USE_FAN -#endif -#ifdef USE_LIGHT -bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 15); -} -#endif -#ifdef USE_LIGHT -bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 24); -} -#endif -#ifdef USE_LIGHT -#endif -#ifdef USE_SENSOR -bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 16); -} -#endif -#ifdef USE_SENSOR -bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 25); -} -#endif -#ifdef USE_SWITCH -bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 17); -} -#endif -#ifdef USE_SWITCH -bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 26); -} -#endif -#ifdef USE_SWITCH -#endif -#ifdef USE_TEXT_SENSOR -bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 18); -} -#endif -#ifdef USE_TEXT_SENSOR -bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 27); -} -#endif -bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) { - return this->send_message_(msg, 29); -} -#ifdef USE_API_NOISE -#endif -#ifdef USE_API_NOISE -bool APIServerConnectionBase::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_noise_encryption_set_key_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 125); -} -#endif -bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 35); -} -bool APIServerConnectionBase::send_subscribe_home_assistant_state_response( - const SubscribeHomeAssistantStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 39); -} -bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 36); -} -bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 37); -} -bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 41); -} -#ifdef USE_ESP32_CAMERA -bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 43); -} -#endif -#ifdef USE_ESP32_CAMERA -bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 44); -} -#endif -#ifdef USE_ESP32_CAMERA -#endif -#ifdef USE_CLIMATE -bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 46); -} -#endif -#ifdef USE_CLIMATE -bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 47); -} -#endif -#ifdef USE_CLIMATE -#endif -#ifdef USE_NUMBER -bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 49); -} -#endif -#ifdef USE_NUMBER -bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 50); -} -#endif -#ifdef USE_NUMBER -#endif -#ifdef USE_SELECT -bool APIServerConnectionBase::send_list_entities_select_response(const ListEntitiesSelectResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_select_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 52); -} -#endif -#ifdef USE_SELECT -bool APIServerConnectionBase::send_select_state_response(const SelectStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_select_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 53); -} -#endif -#ifdef USE_SELECT -#endif -#ifdef USE_SIREN -bool APIServerConnectionBase::send_list_entities_siren_response(const ListEntitiesSirenResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_siren_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 55); -} -#endif -#ifdef USE_SIREN -bool APIServerConnectionBase::send_siren_state_response(const SirenStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_siren_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 56); -} -#endif -#ifdef USE_SIREN -#endif -#ifdef USE_LOCK -bool APIServerConnectionBase::send_list_entities_lock_response(const ListEntitiesLockResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_lock_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 58); +void APIServerConnectionBase::log_send_message_(const char *name, const std::string &dump) { + ESP_LOGVV(TAG, "send_message %s: %s", name, dump.c_str()); } #endif -#ifdef USE_LOCK -bool APIServerConnectionBase::send_lock_state_response(const LockStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_lock_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 59); -} -#endif -#ifdef USE_LOCK -#endif -#ifdef USE_BUTTON -bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_button_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 61); -} -#endif -#ifdef USE_BUTTON -#endif -#ifdef USE_MEDIA_PLAYER -bool APIServerConnectionBase::send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_media_player_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 63); -} -#endif -#ifdef USE_MEDIA_PLAYER -bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayerStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_media_player_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 64); -} -#endif -#ifdef USE_MEDIA_PLAYER -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 67); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_le_raw_advertisements_response( - const BluetoothLERawAdvertisementsResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_le_raw_advertisements_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 93); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_device_connection_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 69); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 71); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_get_services_done_response( - const BluetoothGATTGetServicesDoneResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_done_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 72); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_read_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 74); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_data_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 79); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_connections_free_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 81); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_error_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 82); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_write_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 83); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 84); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_device_pairing_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 85); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_device_unpairing_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 86); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 88); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_scanner_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 126); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantRequest &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_voice_assistant_request: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 90); -} -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAudio &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_voice_assistant_audio: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 106); -} -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -bool APIServerConnectionBase::send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_voice_assistant_announce_finished: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 120); -} -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -bool APIServerConnectionBase::send_voice_assistant_configuration_response( - const VoiceAssistantConfigurationResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_voice_assistant_configuration_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 122); -} -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_ALARM_CONTROL_PANEL -bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response( - const ListEntitiesAlarmControlPanelResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_alarm_control_panel_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 94); -} -#endif -#ifdef USE_ALARM_CONTROL_PANEL -bool APIServerConnectionBase::send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_alarm_control_panel_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 95); -} -#endif -#ifdef USE_ALARM_CONTROL_PANEL -#endif -#ifdef USE_TEXT -bool APIServerConnectionBase::send_list_entities_text_response(const ListEntitiesTextResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_text_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 97); -} -#endif -#ifdef USE_TEXT -bool APIServerConnectionBase::send_text_state_response(const TextStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_text_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 98); -} -#endif -#ifdef USE_TEXT -#endif -#ifdef USE_DATETIME_DATE -bool APIServerConnectionBase::send_list_entities_date_response(const ListEntitiesDateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_date_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 100); -} -#endif -#ifdef USE_DATETIME_DATE -bool APIServerConnectionBase::send_date_state_response(const DateStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_date_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 101); -} -#endif -#ifdef USE_DATETIME_DATE -#endif -#ifdef USE_DATETIME_TIME -bool APIServerConnectionBase::send_list_entities_time_response(const ListEntitiesTimeResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_time_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 103); -} -#endif -#ifdef USE_DATETIME_TIME -bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_time_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 104); -} -#endif -#ifdef USE_DATETIME_TIME -#endif -#ifdef USE_EVENT -bool APIServerConnectionBase::send_list_entities_event_response(const ListEntitiesEventResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_event_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 107); -} -#endif -#ifdef USE_EVENT -bool APIServerConnectionBase::send_event_response(const EventResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_event_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 108); -} -#endif -#ifdef USE_VALVE -bool APIServerConnectionBase::send_list_entities_valve_response(const ListEntitiesValveResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_valve_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 109); -} -#endif -#ifdef USE_VALVE -bool APIServerConnectionBase::send_valve_state_response(const ValveStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_valve_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 110); -} -#endif -#ifdef USE_VALVE -#endif -#ifdef USE_DATETIME_DATETIME -bool APIServerConnectionBase::send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_date_time_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 112); -} -#endif -#ifdef USE_DATETIME_DATETIME -bool APIServerConnectionBase::send_date_time_state_response(const DateTimeStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_date_time_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 113); -} -#endif -#ifdef USE_DATETIME_DATETIME -#endif -#ifdef USE_UPDATE -bool APIServerConnectionBase::send_list_entities_update_response(const ListEntitiesUpdateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_update_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 116); -} -#endif -#ifdef USE_UPDATE -bool APIServerConnectionBase::send_update_state_response(const UpdateStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_update_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 117); -} -#endif -#ifdef USE_UPDATE -#endif + bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { @@ -1273,567 +597,323 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, void APIServerConnection::on_hello_request(const HelloRequest &msg) { HelloResponse ret = this->hello(msg); - if (!this->send_hello_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } void APIServerConnection::on_connect_request(const ConnectRequest &msg) { ConnectResponse ret = this->connect(msg); - if (!this->send_connect_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) { DisconnectResponse ret = this->disconnect(msg); - if (!this->send_disconnect_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } void APIServerConnection::on_ping_request(const PingRequest &msg) { PingResponse ret = this->ping(msg); - if (!this->send_ping_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; - } - DeviceInfoResponse ret = this->device_info(msg); - if (!this->send_device_info_response(ret)) { - this->on_fatal_error(); + if (this->check_connection_setup_()) { + DeviceInfoResponse ret = this->device_info(msg); + if (!this->send_message(ret)) { + this->on_fatal_error(); + } } } void APIServerConnection::on_list_entities_request(const ListEntitiesRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->list_entities(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->list_entities(msg); } void APIServerConnection::on_subscribe_states_request(const SubscribeStatesRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->subscribe_states(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->subscribe_states(msg); } void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->subscribe_logs(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->subscribe_logs(msg); } void APIServerConnection::on_subscribe_homeassistant_services_request( const SubscribeHomeassistantServicesRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->subscribe_homeassistant_services(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->subscribe_homeassistant_services(msg); } void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->subscribe_home_assistant_states(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->subscribe_home_assistant_states(msg); } void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; - } - GetTimeResponse ret = this->get_time(msg); - if (!this->send_get_time_response(ret)) { - this->on_fatal_error(); + if (this->check_connection_setup_()) { + GetTimeResponse ret = this->get_time(msg); + if (!this->send_message(ret)) { + this->on_fatal_error(); + } } } void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->execute_service(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->execute_service(msg); } #ifdef USE_API_NOISE void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; - } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg); - if (!this->send_noise_encryption_set_key_response(ret)) { - this->on_fatal_error(); + if (this->check_authenticated_()) { + NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg); + if (!this->send_message(ret)) { + this->on_fatal_error(); + } } } #endif #ifdef USE_BUTTON void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->button_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->button_command(msg); } #endif #ifdef USE_ESP32_CAMERA void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->camera_image(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->camera_image(msg); } #endif #ifdef USE_CLIMATE void APIServerConnection::on_climate_command_request(const ClimateCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->climate_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->climate_command(msg); } #endif #ifdef USE_COVER void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->cover_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->cover_command(msg); } #endif #ifdef USE_DATETIME_DATE void APIServerConnection::on_date_command_request(const DateCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->date_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->date_command(msg); } #endif #ifdef USE_DATETIME_DATETIME void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->datetime_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->datetime_command(msg); } #endif #ifdef USE_FAN void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->fan_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->fan_command(msg); } #endif #ifdef USE_LIGHT void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->light_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->light_command(msg); } #endif #ifdef USE_LOCK void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->lock_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->lock_command(msg); } #endif #ifdef USE_MEDIA_PLAYER void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->media_player_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->media_player_command(msg); } #endif #ifdef USE_NUMBER void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->number_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->number_command(msg); } #endif #ifdef USE_SELECT void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->select_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->select_command(msg); } #endif #ifdef USE_SIREN void APIServerConnection::on_siren_command_request(const SirenCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->siren_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->siren_command(msg); } #endif #ifdef USE_SWITCH void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->switch_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->switch_command(msg); } #endif #ifdef USE_TEXT void APIServerConnection::on_text_command_request(const TextCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->text_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->text_command(msg); } #endif #ifdef USE_DATETIME_TIME void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->time_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->time_command(msg); } #endif #ifdef USE_UPDATE void APIServerConnection::on_update_command_request(const UpdateCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->update_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->update_command(msg); } #endif #ifdef USE_VALVE void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->valve_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->valve_command(msg); } #endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request( const SubscribeBluetoothLEAdvertisementsRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->subscribe_bluetooth_le_advertisements(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->subscribe_bluetooth_le_advertisements(msg); } #endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->bluetooth_device_request(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->bluetooth_device_request(msg); } #endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->bluetooth_gatt_get_services(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->bluetooth_gatt_get_services(msg); } #endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->bluetooth_gatt_read(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->bluetooth_gatt_read(msg); } #endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->bluetooth_gatt_write(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->bluetooth_gatt_write(msg); } #endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->bluetooth_gatt_read_descriptor(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->bluetooth_gatt_read_descriptor(msg); } #endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->bluetooth_gatt_write_descriptor(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->bluetooth_gatt_write_descriptor(msg); } #endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->bluetooth_gatt_notify(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->bluetooth_gatt_notify(msg); } #endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_subscribe_bluetooth_connections_free_request( const SubscribeBluetoothConnectionsFreeRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; - } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg); - if (!this->send_bluetooth_connections_free_response(ret)) { - this->on_fatal_error(); + if (this->check_authenticated_()) { + BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg); + if (!this->send_message(ret)) { + this->on_fatal_error(); + } } } #endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request( const UnsubscribeBluetoothLEAdvertisementsRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->unsubscribe_bluetooth_le_advertisements(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->unsubscribe_bluetooth_le_advertisements(msg); } #endif #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->bluetooth_scanner_set_mode(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->bluetooth_scanner_set_mode(msg); } #endif #ifdef USE_VOICE_ASSISTANT void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->subscribe_voice_assistant(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->subscribe_voice_assistant(msg); } #endif #ifdef USE_VOICE_ASSISTANT void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; - } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg); - if (!this->send_voice_assistant_configuration_response(ret)) { - this->on_fatal_error(); + if (this->check_authenticated_()) { + VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg); + if (!this->send_message(ret)) { + this->on_fatal_error(); + } } } #endif #ifdef USE_VOICE_ASSISTANT void APIServerConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->voice_assistant_set_configuration(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->voice_assistant_set_configuration(msg); } #endif #ifdef USE_ALARM_CONTROL_PANEL void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) { - if (!this->is_connection_setup()) { - this->on_no_setup_connection(); - return; + if (this->check_authenticated_()) { + this->alarm_control_panel_command(msg); } - if (!this->is_authenticated()) { - this->on_unauthenticated_access(); - return; - } - this->alarm_control_panel_command(msg); } #endif diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 8ee5c0fcf1..b2be314aaf 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -10,162 +10,94 @@ namespace api { class APIServerConnectionBase : public ProtoService { public: +#ifdef HAS_PROTO_MESSAGE_DUMP + protected: + void log_send_message_(const char *name, const std::string &dump); + + public: +#endif + + template bool send_message(const T &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + this->log_send_message_(T::message_name(), msg.dump()); +#endif + return this->send_message_(msg, T::MESSAGE_TYPE); + } + virtual void on_hello_request(const HelloRequest &value){}; - bool send_hello_response(const HelloResponse &msg); + virtual void on_connect_request(const ConnectRequest &value){}; - bool send_connect_response(const ConnectResponse &msg); - bool send_disconnect_request(const DisconnectRequest &msg); + virtual void on_disconnect_request(const DisconnectRequest &value){}; - bool send_disconnect_response(const DisconnectResponse &msg); virtual void on_disconnect_response(const DisconnectResponse &value){}; - bool send_ping_request(const PingRequest &msg); virtual void on_ping_request(const PingRequest &value){}; - bool send_ping_response(const PingResponse &msg); virtual void on_ping_response(const PingResponse &value){}; virtual void on_device_info_request(const DeviceInfoRequest &value){}; - bool send_device_info_response(const DeviceInfoResponse &msg); + virtual void on_list_entities_request(const ListEntitiesRequest &value){}; - bool send_list_entities_done_response(const ListEntitiesDoneResponse &msg); + virtual void on_subscribe_states_request(const SubscribeStatesRequest &value){}; -#ifdef USE_BINARY_SENSOR - bool send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg); -#endif -#ifdef USE_BINARY_SENSOR - bool send_binary_sensor_state_response(const BinarySensorStateResponse &msg); -#endif -#ifdef USE_COVER - bool send_list_entities_cover_response(const ListEntitiesCoverResponse &msg); -#endif -#ifdef USE_COVER - bool send_cover_state_response(const CoverStateResponse &msg); -#endif + #ifdef USE_COVER virtual void on_cover_command_request(const CoverCommandRequest &value){}; #endif -#ifdef USE_FAN - bool send_list_entities_fan_response(const ListEntitiesFanResponse &msg); -#endif -#ifdef USE_FAN - bool send_fan_state_response(const FanStateResponse &msg); -#endif + #ifdef USE_FAN virtual void on_fan_command_request(const FanCommandRequest &value){}; #endif -#ifdef USE_LIGHT - bool send_list_entities_light_response(const ListEntitiesLightResponse &msg); -#endif -#ifdef USE_LIGHT - bool send_light_state_response(const LightStateResponse &msg); -#endif + #ifdef USE_LIGHT virtual void on_light_command_request(const LightCommandRequest &value){}; #endif -#ifdef USE_SENSOR - bool send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg); -#endif -#ifdef USE_SENSOR - bool send_sensor_state_response(const SensorStateResponse &msg); -#endif -#ifdef USE_SWITCH - bool send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg); -#endif -#ifdef USE_SWITCH - bool send_switch_state_response(const SwitchStateResponse &msg); -#endif + #ifdef USE_SWITCH virtual void on_switch_command_request(const SwitchCommandRequest &value){}; #endif -#ifdef USE_TEXT_SENSOR - bool send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg); -#endif -#ifdef USE_TEXT_SENSOR - bool send_text_sensor_state_response(const TextSensorStateResponse &msg); -#endif + virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){}; - bool send_subscribe_logs_response(const SubscribeLogsResponse &msg); + #ifdef USE_API_NOISE virtual void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &value){}; #endif -#ifdef USE_API_NOISE - bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg); -#endif + virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){}; - bool send_homeassistant_service_response(const HomeassistantServiceResponse &msg); + virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){}; - bool send_subscribe_home_assistant_state_response(const SubscribeHomeAssistantStateResponse &msg); + virtual void on_home_assistant_state_response(const HomeAssistantStateResponse &value){}; - bool send_get_time_request(const GetTimeRequest &msg); virtual void on_get_time_request(const GetTimeRequest &value){}; - bool send_get_time_response(const GetTimeResponse &msg); virtual void on_get_time_response(const GetTimeResponse &value){}; - bool send_list_entities_services_response(const ListEntitiesServicesResponse &msg); + virtual void on_execute_service_request(const ExecuteServiceRequest &value){}; -#ifdef USE_ESP32_CAMERA - bool send_list_entities_camera_response(const ListEntitiesCameraResponse &msg); -#endif -#ifdef USE_ESP32_CAMERA - bool send_camera_image_response(const CameraImageResponse &msg); -#endif + #ifdef USE_ESP32_CAMERA virtual void on_camera_image_request(const CameraImageRequest &value){}; #endif -#ifdef USE_CLIMATE - bool send_list_entities_climate_response(const ListEntitiesClimateResponse &msg); -#endif -#ifdef USE_CLIMATE - bool send_climate_state_response(const ClimateStateResponse &msg); -#endif + #ifdef USE_CLIMATE virtual void on_climate_command_request(const ClimateCommandRequest &value){}; #endif -#ifdef USE_NUMBER - bool send_list_entities_number_response(const ListEntitiesNumberResponse &msg); -#endif -#ifdef USE_NUMBER - bool send_number_state_response(const NumberStateResponse &msg); -#endif + #ifdef USE_NUMBER virtual void on_number_command_request(const NumberCommandRequest &value){}; #endif -#ifdef USE_SELECT - bool send_list_entities_select_response(const ListEntitiesSelectResponse &msg); -#endif -#ifdef USE_SELECT - bool send_select_state_response(const SelectStateResponse &msg); -#endif + #ifdef USE_SELECT virtual void on_select_command_request(const SelectCommandRequest &value){}; #endif -#ifdef USE_SIREN - bool send_list_entities_siren_response(const ListEntitiesSirenResponse &msg); -#endif -#ifdef USE_SIREN - bool send_siren_state_response(const SirenStateResponse &msg); -#endif + #ifdef USE_SIREN virtual void on_siren_command_request(const SirenCommandRequest &value){}; #endif -#ifdef USE_LOCK - bool send_list_entities_lock_response(const ListEntitiesLockResponse &msg); -#endif -#ifdef USE_LOCK - bool send_lock_state_response(const LockStateResponse &msg); -#endif + #ifdef USE_LOCK virtual void on_lock_command_request(const LockCommandRequest &value){}; #endif -#ifdef USE_BUTTON - bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg); -#endif + #ifdef USE_BUTTON virtual void on_button_command_request(const ButtonCommandRequest &value){}; #endif -#ifdef USE_MEDIA_PLAYER - bool send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg); -#endif -#ifdef USE_MEDIA_PLAYER - bool send_media_player_state_response(const MediaPlayerStateResponse &msg); -#endif + #ifdef USE_MEDIA_PLAYER virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){}; #endif @@ -173,33 +105,19 @@ class APIServerConnectionBase : public ProtoService { virtual void on_subscribe_bluetooth_le_advertisements_request( const SubscribeBluetoothLEAdvertisementsRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_le_raw_advertisements_response(const BluetoothLERawAdvertisementsResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_get_services_done_response(const BluetoothGATTGetServicesDoneResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &value){}; #endif @@ -212,49 +130,23 @@ class APIServerConnectionBase : public ProtoService { #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_unsubscribe_bluetooth_le_advertisements_request( const UnsubscribeBluetoothLEAdvertisementsRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &value){}; #endif #ifdef USE_VOICE_ASSISTANT virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){}; #endif -#ifdef USE_VOICE_ASSISTANT - bool send_voice_assistant_request(const VoiceAssistantRequest &msg); -#endif + #ifdef USE_VOICE_ASSISTANT virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){}; #endif @@ -262,7 +154,6 @@ class APIServerConnectionBase : public ProtoService { virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){}; #endif #ifdef USE_VOICE_ASSISTANT - bool send_voice_assistant_audio(const VoiceAssistantAudio &msg); virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){}; #endif #ifdef USE_VOICE_ASSISTANT @@ -271,84 +162,39 @@ class APIServerConnectionBase : public ProtoService { #ifdef USE_VOICE_ASSISTANT virtual void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &value){}; #endif -#ifdef USE_VOICE_ASSISTANT - bool send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg); -#endif + #ifdef USE_VOICE_ASSISTANT virtual void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &value){}; #endif -#ifdef USE_VOICE_ASSISTANT - bool send_voice_assistant_configuration_response(const VoiceAssistantConfigurationResponse &msg); -#endif + #ifdef USE_VOICE_ASSISTANT virtual void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &value){}; #endif -#ifdef USE_ALARM_CONTROL_PANEL - bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg); -#endif -#ifdef USE_ALARM_CONTROL_PANEL - bool send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg); -#endif + #ifdef USE_ALARM_CONTROL_PANEL virtual void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &value){}; #endif -#ifdef USE_TEXT - bool send_list_entities_text_response(const ListEntitiesTextResponse &msg); -#endif -#ifdef USE_TEXT - bool send_text_state_response(const TextStateResponse &msg); -#endif + #ifdef USE_TEXT virtual void on_text_command_request(const TextCommandRequest &value){}; #endif -#ifdef USE_DATETIME_DATE - bool send_list_entities_date_response(const ListEntitiesDateResponse &msg); -#endif -#ifdef USE_DATETIME_DATE - bool send_date_state_response(const DateStateResponse &msg); -#endif + #ifdef USE_DATETIME_DATE virtual void on_date_command_request(const DateCommandRequest &value){}; #endif -#ifdef USE_DATETIME_TIME - bool send_list_entities_time_response(const ListEntitiesTimeResponse &msg); -#endif -#ifdef USE_DATETIME_TIME - bool send_time_state_response(const TimeStateResponse &msg); -#endif + #ifdef USE_DATETIME_TIME virtual void on_time_command_request(const TimeCommandRequest &value){}; #endif -#ifdef USE_EVENT - bool send_list_entities_event_response(const ListEntitiesEventResponse &msg); -#endif -#ifdef USE_EVENT - bool send_event_response(const EventResponse &msg); -#endif -#ifdef USE_VALVE - bool send_list_entities_valve_response(const ListEntitiesValveResponse &msg); -#endif -#ifdef USE_VALVE - bool send_valve_state_response(const ValveStateResponse &msg); -#endif + #ifdef USE_VALVE virtual void on_valve_command_request(const ValveCommandRequest &value){}; #endif -#ifdef USE_DATETIME_DATETIME - bool send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg); -#endif -#ifdef USE_DATETIME_DATETIME - bool send_date_time_state_response(const DateTimeStateResponse &msg); -#endif + #ifdef USE_DATETIME_DATETIME virtual void on_date_time_command_request(const DateTimeCommandRequest &value){}; #endif -#ifdef USE_UPDATE - bool send_list_entities_update_response(const ListEntitiesUpdateResponse &msg); -#endif -#ifdef USE_UPDATE - bool send_update_state_response(const UpdateStateResponse &msg); -#endif + #ifdef USE_UPDATE virtual void on_update_command_request(const UpdateCommandRequest &value){}; #endif diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 2d5c507b9d..740e4259b1 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -24,7 +24,11 @@ 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; } +APIServer::APIServer() { + global_api_server = this; + // Pre-allocate shared write buffer + shared_write_buffer_.reserve(64); +} void APIServer::setup() { ESP_LOGCONFIG(TAG, "Running setup"); @@ -88,6 +92,12 @@ void APIServer::setup() { #ifdef USE_LOGGER if (logger::global_logger != nullptr) { logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) { + if (this->shutting_down_) { + // Don't try to send logs during shutdown + // as it could result in a recursion and + // we would be filling a buffer we are trying to clear + return; + } for (auto &c : this->clients_) { if (!c->remove_) c->try_send_log_message(level, tag, message); @@ -96,7 +106,7 @@ void APIServer::setup() { } #endif - this->last_connected_ = millis(); + this->last_connected_ = App.get_loop_component_start_time(); #ifdef USE_ESP32_CAMERA if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) { @@ -112,8 +122,8 @@ void APIServer::setup() { } void APIServer::loop() { - // Accept new clients only if the socket has incoming connections - if (this->socket_->ready()) { + // 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); @@ -154,7 +164,7 @@ void APIServer::loop() { } if (this->reboot_timeout_ != 0) { - const uint32_t now = millis(); + const uint32_t now = App.get_loop_component_start_time(); if (!this->is_connected()) { if (now - this->last_connected_ > this->reboot_timeout_) { ESP_LOGE(TAG, "No client connected; rebooting"); @@ -169,8 +179,10 @@ void APIServer::loop() { } void APIServer::dump_config() { - ESP_LOGCONFIG(TAG, "API Server:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); + ESP_LOGCONFIG(TAG, + "API Server:\n" + " Address: %s:%u", + network::get_use_address().c_str(), this->port_); #ifdef USE_API_NOISE ESP_LOGCONFIG(TAG, " Using noise encryption: %s", YESNO(this->noise_ctx_->has_psk())); if (!this->noise_ctx_->has_psk()) { @@ -215,11 +227,11 @@ bool APIServer::check_password(const std::string &password) const { void APIServer::handle_disconnect(APIConnection *conn) {} #ifdef USE_BINARY_SENSOR -void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) { +void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_binary_sensor_state(obj, state); + c->send_binary_sensor_state(obj); } #endif @@ -255,7 +267,7 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_sensor_state(obj, state); + c->send_sensor_state(obj); } #endif @@ -264,7 +276,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_switch_state(obj, state); + c->send_switch_state(obj); } #endif @@ -273,7 +285,7 @@ void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_text_sensor_state(obj, state); + c->send_text_sensor_state(obj); } #endif @@ -291,7 +303,7 @@ void APIServer::on_number_update(number::Number *obj, float state) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_number_state(obj, state); + c->send_number_state(obj); } #endif @@ -327,7 +339,7 @@ void APIServer::on_text_update(text::Text *obj, const std::string &state) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_text_state(obj, state); + c->send_text_state(obj); } #endif @@ -336,7 +348,7 @@ void APIServer::on_select_update(select::Select *obj, const std::string &state, if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_select_state(obj, state); + c->send_select_state(obj); } #endif @@ -345,7 +357,7 @@ void APIServer::on_lock_update(lock::Lock *obj) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_lock_state(obj, obj->state); + c->send_lock_state(obj); } #endif @@ -396,6 +408,8 @@ void APIServer::set_port(uint16_t port) { this->port_ = port; } void APIServer::set_password(const std::string &password) { this->password_ = password; } +void APIServer::set_batch_delay(uint32_t batch_delay) { this->batch_delay_ = batch_delay; } + void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) { for (auto &client : this->clients_) { client->send_homeassistant_service_call(call); @@ -454,7 +468,7 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) { ESP_LOGW(TAG, "Disconnecting all clients to reset connections"); this->set_noise_psk(psk); for (auto &c : this->clients_) { - c->send_disconnect_request(DisconnectRequest()); + c->send_message(DisconnectRequest()); } }); } @@ -474,10 +488,36 @@ void APIServer::request_time() { bool APIServer::is_connected() const { return !this->clients_.empty(); } void APIServer::on_shutdown() { - for (auto &c : this->clients_) { - c->send_disconnect_request(DisconnectRequest()); + this->shutting_down_ = true; + + // Close the listening socket to prevent new connections + if (this->socket_) { + this->socket_->close(); + this->socket_ = nullptr; } - delay(10); + + // Change batch delay to 5ms for quick flushing during shutdown + this->batch_delay_ = 5; + + // Send disconnect requests to all connected clients + for (auto &c : this->clients_) { + if (!c->send_message(DisconnectRequest())) { + // If we can't send the disconnect request directly (tx_buffer full), + // schedule it in the batch so it will be sent with the 5ms timer + c->schedule_message_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE); + } + } +} + +bool APIServer::teardown() { + // If network is disconnected, no point trying to flush buffers + if (!network::is_connected()) { + return true; + } + this->loop(); + + // Return true only when all clients have been torn down + return this->clients_.empty(); } } // namespace api diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index a6645b96ce..33412d8a68 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -34,11 +34,17 @@ class APIServer : public Component, public Controller { void loop() override; void dump_config() override; void on_shutdown() override; + bool teardown() override; bool check_password(const std::string &password) const; bool uses_password() const; void set_port(uint16_t port); void set_password(const std::string &password); void set_reboot_timeout(uint32_t reboot_timeout); + void set_batch_delay(uint32_t batch_delay); + uint32_t get_batch_delay() const { return batch_delay_; } + + // Get reference to shared buffer for API connections + std::vector &get_shared_buffer_ref() { return shared_write_buffer_; } #ifdef USE_API_NOISE bool save_noise_psk(psk_t psk, bool make_active = true); @@ -48,7 +54,7 @@ class APIServer : public Component, public Controller { void handle_disconnect(APIConnection *conn); #ifdef USE_BINARY_SENSOR - void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override; + void on_binary_sensor_update(binary_sensor::BinarySensor *obj) override; #endif #ifdef USE_COVER void on_cover_update(cover::Cover *obj) override; @@ -136,17 +142,28 @@ class APIServer : public Component, public Controller { } protected: + // Pointers and pointer-like types first (4 bytes each) std::unique_ptr socket_ = nullptr; - uint16_t port_{6053}; - uint32_t reboot_timeout_{300000}; - uint32_t last_connected_{0}; - std::vector> clients_; - std::string password_; - std::vector state_subs_; - std::vector user_services_; Trigger *client_connected_trigger_ = new Trigger(); Trigger *client_disconnected_trigger_ = new Trigger(); + // 4-byte aligned types + uint32_t reboot_timeout_{300000}; + uint32_t batch_delay_{100}; + uint32_t last_connected_{0}; + + // Vectors and strings (12 bytes each on 32-bit) + std::vector> clients_; + std::string password_; + std::vector shared_write_buffer_; // Shared proto write buffer for all connections + std::vector state_subs_; + std::vector user_services_; + + // Group smaller types together + uint16_t port_{6053}; + bool shutting_down_ = false; + // 3 bytes used, 1 byte padding + #ifdef USE_API_NOISE std::shared_ptr noise_ctx_ = std::make_shared(); ESPPreferenceObject noise_pref_; diff --git a/esphome/components/api/client.py b/esphome/components/api/client.py index c61b8d5ea3..20136ef7b8 100644 --- a/esphome/components/api/client.py +++ b/esphome/components/api/client.py @@ -5,7 +5,7 @@ from datetime import datetime import logging from typing import TYPE_CHECKING, Any -from aioesphomeapi import APIClient +from aioesphomeapi import APIClient, parse_log_message from aioesphomeapi.log_runner import async_run from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__ @@ -46,9 +46,10 @@ async def async_run_logs(config: dict[str, Any], address: str) -> None: time_ = datetime.now() message: bytes = msg.message text = message.decode("utf8", "backslashreplace") - if dashboard: - text = text.replace("\033", "\\033") - print(f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]{text}") + for parsed_msg in parse_log_message( + text, f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]" + ): + print(parsed_msg.replace("\033", "\\033") if dashboard else parsed_msg) stop = await async_run(cli, on_log, name=name) try: diff --git a/esphome/components/api/homeassistant_service.h b/esphome/components/api/homeassistant_service.h index e91756c3c9..32d13b69ae 100644 --- a/esphome/components/api/homeassistant_service.h +++ b/esphome/components/api/homeassistant_service.h @@ -3,8 +3,8 @@ #include "api_server.h" #ifdef USE_API #include "api_pb2.h" -#include "esphome/core/helpers.h" #include "esphome/core/automation.h" +#include "esphome/core/helpers.h" #include namespace esphome { diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index 574aa2525b..ceee3f00b8 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -73,7 +73,7 @@ bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done( ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {} bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) { auto resp = service->encode_list_service_response(); - return this->client_->send_list_entities_services_response(resp); + return this->client_->send_message(resp); } #ifdef USE_ESP32_CAMERA diff --git a/esphome/components/api/proto.cpp b/esphome/components/api/proto.cpp index 7af2e92534..25daf17ccc 100644 --- a/esphome/components/api/proto.cpp +++ b/esphome/components/api/proto.cpp @@ -1,5 +1,6 @@ #include "proto.h" #include +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index fae722f750..e850236db6 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -1,8 +1,8 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include @@ -216,7 +216,7 @@ class ProtoWriteBuffer { this->buffer_->insert(this->buffer_->end(), data, data + len); } void encode_string(uint32_t field_id, const std::string &value, bool force = false) { - this->encode_string(field_id, value.data(), value.size()); + this->encode_string(field_id, value.data(), value.size(), force); } void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) { this->encode_string(field_id, reinterpret_cast(data), len, force); @@ -327,9 +327,11 @@ class ProtoWriteBuffer { class ProtoMessage { public: virtual ~ProtoMessage() = default; - virtual void encode(ProtoWriteBuffer buffer) const = 0; + // Default implementation for messages with no fields + virtual void encode(ProtoWriteBuffer buffer) const {} void decode(const uint8_t *buffer, size_t length); - virtual void calculate_size(uint32_t &total_size) const = 0; + // Default implementation for messages with no fields + virtual void calculate_size(uint32_t &total_size) const {} #ifdef HAS_PROTO_MESSAGE_DUMP std::string dump() const; virtual void dump_to(std::string &out) const = 0; @@ -360,11 +362,11 @@ class ProtoService { * @return A ProtoWriteBuffer object with the reserved size. */ virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0; - virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0; + virtual bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) = 0; virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0; // Optimized method that pre-allocates buffer based on message size - template bool send_message_(const C &msg, uint32_t message_type) { + bool send_message_(const ProtoMessage &msg, uint16_t message_type) { uint32_t msg_size = 0; msg.calculate_size(msg_size); @@ -377,6 +379,26 @@ class ProtoService { // Send the buffer return this->send_buffer(buffer, message_type); } + + // Authentication helper methods + bool check_connection_setup_() { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return false; + } + return true; + } + + bool check_authenticated_() { + if (!this->check_connection_setup_()) { + return false; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return false; + } + return true; + } }; } // namespace api diff --git a/esphome/components/api/subscribe_state.cpp b/esphome/components/api/subscribe_state.cpp index 4b1d5ebc0d..4180435fcc 100644 --- a/esphome/components/api/subscribe_state.cpp +++ b/esphome/components/api/subscribe_state.cpp @@ -8,7 +8,7 @@ namespace api { #ifdef USE_BINARY_SENSOR bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { - return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state); + return this->client_->send_binary_sensor_state(binary_sensor); } #endif #ifdef USE_COVER @@ -21,27 +21,21 @@ bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fa bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); } #endif #ifdef USE_SENSOR -bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { - return this->client_->send_sensor_state(sensor, sensor->state); -} +bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_state(sensor); } #endif #ifdef USE_SWITCH -bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { - return this->client_->send_switch_state(a_switch, a_switch->state); -} +bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_state(a_switch); } #endif #ifdef USE_TEXT_SENSOR bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) { - return this->client_->send_text_sensor_state(text_sensor, text_sensor->state); + return this->client_->send_text_sensor_state(text_sensor); } #endif #ifdef USE_CLIMATE bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); } #endif #ifdef USE_NUMBER -bool InitialStateIterator::on_number(number::Number *number) { - return this->client_->send_number_state(number, number->state); -} +bool InitialStateIterator::on_number(number::Number *number) { return this->client_->send_number_state(number); } #endif #ifdef USE_DATETIME_DATE bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); } @@ -55,15 +49,13 @@ bool InitialStateIterator::on_datetime(datetime::DateTimeEntity *datetime) { } #endif #ifdef USE_TEXT -bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); } +bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text); } #endif #ifdef USE_SELECT -bool InitialStateIterator::on_select(select::Select *select) { - return this->client_->send_select_state(select, select->state); -} +bool InitialStateIterator::on_select(select::Select *select) { return this->client_->send_select_state(select); } #endif #ifdef USE_LOCK -bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); } +bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock); } #endif #ifdef USE_VALVE bool InitialStateIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_state(valve); } diff --git a/esphome/components/as3935/as3935.cpp b/esphome/components/as3935/as3935.cpp index adef45a395..5e6d62b284 100644 --- a/esphome/components/as3935/as3935.cpp +++ b/esphome/components/as3935/as3935.cpp @@ -282,7 +282,7 @@ void AS3935Component::display_oscillator(bool state, uint8_t osc) { // based on the resonance frequency of the antenna and so it should be trimmed // before the calibration is done. bool AS3935Component::calibrate_oscillator() { - ESP_LOGI(TAG, "Starting oscillators calibration..."); + ESP_LOGI(TAG, "Starting oscillators calibration"); this->write_register(CALIB_RCO, WIPE_ALL, DIRECT_COMMAND, 0); // Send command to calibrate the oscillators this->display_oscillator(true, 2); @@ -307,7 +307,7 @@ bool AS3935Component::calibrate_oscillator() { } void AS3935Component::tune_antenna() { - ESP_LOGI(TAG, "Starting antenna tuning..."); + ESP_LOGI(TAG, "Starting antenna tuning"); uint8_t div_ratio = this->read_div_ratio(); uint8_t tune_val = this->read_capacitance(); ESP_LOGI(TAG, "Division Ratio is set to: %d", div_ratio); diff --git a/esphome/components/as5600/as5600.cpp b/esphome/components/as5600/as5600.cpp index 5cfc7e1b9e..ff29ae5cd4 100644 --- a/esphome/components/as5600/as5600.cpp +++ b/esphome/components/as5600/as5600.cpp @@ -95,11 +95,13 @@ void AS5600Component::dump_config() { return; } - ESP_LOGCONFIG(TAG, " Watchdog: %d", this->watchdog_); - ESP_LOGCONFIG(TAG, " Fast Filter: %d", this->fast_filter_); - ESP_LOGCONFIG(TAG, " Slow Filter: %d", this->slow_filter_); - ESP_LOGCONFIG(TAG, " Hysteresis: %d", this->hysteresis_); - ESP_LOGCONFIG(TAG, " Start Position: %d", this->start_position_); + ESP_LOGCONFIG(TAG, + " Watchdog: %d\n" + " Fast Filter: %d\n" + " Slow Filter: %d\n" + " Hysteresis: %d\n" + " Start Position: %d", + this->watchdog_, this->fast_filter_, this->slow_filter_, this->hysteresis_, this->start_position_); if (this->end_mode_ == END_MODE_POSITION) { ESP_LOGCONFIG(TAG, " End Position: %d", this->end_position_); } else { diff --git a/esphome/components/as7341/as7341.cpp b/esphome/components/as7341/as7341.cpp index b01bcf2eed..1e335f43ad 100644 --- a/esphome/components/as7341/as7341.cpp +++ b/esphome/components/as7341/as7341.cpp @@ -41,9 +41,11 @@ void AS7341Component::dump_config() { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " Gain: %u", get_gain()); - ESP_LOGCONFIG(TAG, " ATIME: %u", get_atime()); - ESP_LOGCONFIG(TAG, " ASTEP: %u", get_astep()); + ESP_LOGCONFIG(TAG, + " Gain: %u\n" + " ATIME: %u\n" + " ASTEP: %u", + get_gain(), get_atime(), get_astep()); LOG_SENSOR(" ", "F1", this->f1_); LOG_SENSOR(" ", "F2", this->f2_); diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index 99e250b6fc..eec6a0e327 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -21,8 +21,8 @@ CONFIG_SCHEMA = cv.All( @coroutine_with_priority(200.0) async def to_code(config): if CORE.is_esp32 or CORE.is_libretiny: - # https://github.com/esphome/AsyncTCP/blob/master/library.json - cg.add_library("esphome/AsyncTCP-esphome", "2.1.4") + # https://github.com/ESP32Async/AsyncTCP + cg.add_library("ESP32Async/AsyncTCP", "3.4.4") elif CORE.is_esp8266: - # https://github.com/esphome/ESPAsyncTCP - cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0") + # https://github.com/ESP32Async/ESPAsyncTCP + cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0") diff --git a/esphome/components/at581x/at581x.cpp b/esphome/components/at581x/at581x.cpp index 2171544be2..b4f817b737 100644 --- a/esphome/components/at581x/at581x.cpp +++ b/esphome/components/at581x/at581x.cpp @@ -75,15 +75,18 @@ void AT581XComponent::setup() { ESP_LOGCONFIG(TAG, "Running setup"); } void AT581XComponent::dump_config() { LOG_I2C_DEVICE(this); } #define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) bool AT581XComponent::i2c_write_config() { - ESP_LOGCONFIG(TAG, "Writing new config for AT581X..."); - ESP_LOGCONFIG(TAG, "Frequency: %dMHz", this->freq_); - ESP_LOGCONFIG(TAG, "Sensing distance: %d", this->delta_); - ESP_LOGCONFIG(TAG, "Power: %dµA", this->power_); - ESP_LOGCONFIG(TAG, "Gain: %d", this->gain_); - ESP_LOGCONFIG(TAG, "Trigger base time: %dms", this->trigger_base_time_ms_); - ESP_LOGCONFIG(TAG, "Trigger keep time: %dms", this->trigger_keep_time_ms_); - ESP_LOGCONFIG(TAG, "Protect time: %dms", this->protect_time_ms_); - ESP_LOGCONFIG(TAG, "Self check time: %dms", this->self_check_time_ms_); + 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", + 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_); // Set frequency point if (!this->i2c_write_reg(FREQ_ADDR, GAIN61_VALUE)) { diff --git a/esphome/components/atm90e32/atm90e32.cpp b/esphome/components/atm90e32/atm90e32.cpp index 545997eb14..f05d462845 100644 --- a/esphome/components/atm90e32/atm90e32.cpp +++ b/esphome/components/atm90e32/atm90e32.cpp @@ -686,7 +686,7 @@ void ATM90E32Component::restore_power_offset_calibrations_() { } void ATM90E32Component::clear_gain_calibrations() { - ESP_LOGI(TAG, "[CALIBRATION] Clearing stored gain calibrations and restoring config-defined values..."); + ESP_LOGI(TAG, "[CALIBRATION] Clearing stored gain calibrations and restoring config-defined values"); for (int phase = 0; phase < 3; phase++) { gain_phase_[phase].voltage_gain = this->phase_[phase].voltage_gain_; diff --git a/esphome/components/audio/audio_transfer_buffer.cpp b/esphome/components/audio/audio_transfer_buffer.cpp index 1566884c3d..790cd62db0 100644 --- a/esphome/components/audio/audio_transfer_buffer.cpp +++ b/esphome/components/audio/audio_transfer_buffer.cpp @@ -86,7 +86,7 @@ bool AudioTransferBuffer::reallocate(size_t new_buffer_size) { bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) { this->buffer_size_ = buffer_size; - RAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; this->buffer_ = allocator.allocate(this->buffer_size_); if (this->buffer_ == nullptr) { @@ -101,7 +101,7 @@ bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) { void AudioTransferBuffer::deallocate_buffer_() { if (this->buffer_ != nullptr) { - RAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; allocator.deallocate(this->buffer_, this->buffer_size_); this->buffer_ = nullptr; this->data_start_ = nullptr; diff --git a/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp b/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp index d4e5d7b9c6..e6e049e332 100644 --- a/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +++ b/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp @@ -60,8 +60,10 @@ void AXS15231Touchscreen::dump_config() { LOG_I2C_DEVICE(this); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Width: %d", this->x_raw_max_); - ESP_LOGCONFIG(TAG, " Height: %d", this->y_raw_max_); + ESP_LOGCONFIG(TAG, + " Width: %d\n" + " Height: %d", + this->x_raw_max_, this->y_raw_max_); } } // namespace axs15231 diff --git a/esphome/components/bang_bang/bang_bang_climate.cpp b/esphome/components/bang_bang/bang_bang_climate.cpp index aed97b2890..bb85b49238 100644 --- a/esphome/components/bang_bang/bang_bang_climate.cpp +++ b/esphome/components/bang_bang/bang_bang_climate.cpp @@ -194,11 +194,14 @@ Trigger<> *BangBangClimate::get_heat_trigger() const { return this->heat_trigger void BangBangClimate::set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; } void BangBangClimate::dump_config() { LOG_CLIMATE("", "Bang Bang Climate", this); - ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_)); - ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_)); - ESP_LOGCONFIG(TAG, " Supports AWAY mode: %s", YESNO(this->supports_away_)); - ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.2f°C", this->normal_config_.default_temperature_low); - ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.2f°C", this->normal_config_.default_temperature_high); + ESP_LOGCONFIG(TAG, + " Supports HEAT: %s\n" + " Supports COOL: %s\n" + " Supports AWAY mode: %s\n" + " Default Target Temperature Low: %.2f°C\n" + " Default Target Temperature High: %.2f°C", + YESNO(this->supports_heat_), YESNO(this->supports_cool_), YESNO(this->supports_away_), + this->normal_config_.default_temperature_low, this->normal_config_.default_temperature_high); } BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig() = default; diff --git a/esphome/components/bedjet/bedjet_hub.cpp b/esphome/components/bedjet/bedjet_hub.cpp index fea7080de6..007ca1ca7d 100644 --- a/esphome/components/bedjet/bedjet_hub.cpp +++ b/esphome/components/bedjet/bedjet_hub.cpp @@ -480,13 +480,19 @@ void BedJetHub::set_clock(uint8_t hour, uint8_t minute) { /* Internal */ -void BedJetHub::loop() {} +void BedJetHub::loop() { + // Parent BLEClientNode has a loop() method, but this component uses + // polling via update() and BLE callbacks so loop isn't needed + this->disable_loop(); +} void BedJetHub::update() { this->dispatch_status_(); } void BedJetHub::dump_config() { - ESP_LOGCONFIG(TAG, "BedJet Hub '%s'", this->get_name().c_str()); - ESP_LOGCONFIG(TAG, " ble_client.app_id: %d", this->parent()->app_id); - ESP_LOGCONFIG(TAG, " ble_client.conn_id: %d", this->parent()->get_conn_id()); + ESP_LOGCONFIG(TAG, + "BedJet Hub '%s'\n" + " ble_client.app_id: %d\n" + " ble_client.conn_id: %d", + this->get_name().c_str(), this->parent()->app_id, this->parent()->get_conn_id()); LOG_UPDATE_INTERVAL(this) ESP_LOGCONFIG(TAG, " Child components (%d):", this->children_.size()); for (auto *child : this->children_) { @@ -527,7 +533,7 @@ void BedJetHub::dispatch_status_() { } if (this->timeout_ > 0 && diff > this->timeout_ && this->parent()->enabled) { - ESP_LOGW(TAG, "[%s] Timed out after %" PRId32 " sec. Retrying...", this->get_name().c_str(), this->timeout_); + ESP_LOGW(TAG, "[%s] Timed out after %" PRId32 " sec. Retrying", this->get_name().c_str(), this->timeout_); // set_enabled(false) will only close the connection if state != IDLE. this->parent()->set_state(espbt::ClientState::CONNECTING); this->parent()->set_enabled(false); diff --git a/esphome/components/bedjet/climate/bedjet_climate.cpp b/esphome/components/bedjet/climate/bedjet_climate.cpp index 854129f816..f22d312b5a 100644 --- a/esphome/components/bedjet/climate/bedjet_climate.cpp +++ b/esphome/components/bedjet/climate/bedjet_climate.cpp @@ -83,7 +83,11 @@ void BedJetClimate::reset_state_() { this->publish_state(); } -void BedJetClimate::loop() {} +void BedJetClimate::loop() { + // This component is controlled via the parent BedJetHub + // Empty loop not needed, disable to save CPU cycles + this->disable_loop(); +} void BedJetClimate::control(const ClimateCall &call) { ESP_LOGD(TAG, "Received BedJetClimate::control"); diff --git a/esphome/components/beken_spi_led_strip/led_strip.cpp b/esphome/components/beken_spi_led_strip/led_strip.cpp index 39bf831226..17b2dd1808 100644 --- a/esphome/components/beken_spi_led_strip/led_strip.cpp +++ b/esphome/components/beken_spi_led_strip/led_strip.cpp @@ -7,11 +7,13 @@ extern "C" { #include "rtos_pub.h" -#include "spi.h" +// rtos_pub.h must be included before the rest of the includes + #include "arm_arch.h" #include "general_dma_pub.h" #include "gpio_pub.h" #include "icu_pub.h" +#include "spi.h" #undef SPI_DAT #undef SPI_BASE }; @@ -124,7 +126,7 @@ void BekenSPILEDStripLightOutput::setup() { size_t buffer_size = this->get_buffer_size_(); size_t dma_buffer_size = (buffer_size * 8) + (2 * 64); - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; this->buf_ = allocator.allocate(buffer_size); if (this->buf_ == nullptr) { ESP_LOGE(TAG, "Cannot allocate LED buffer!"); @@ -256,7 +258,7 @@ void BekenSPILEDStripLightOutput::write_state(light::LightState *state) { this->last_refresh_ = now; this->mark_shown_(); - ESP_LOGVV(TAG, "Writing RGB values to bus..."); + ESP_LOGVV(TAG, "Writing RGB values to bus"); if (spi_data == nullptr) { ESP_LOGE(TAG, "SPI not initialized"); @@ -345,8 +347,10 @@ light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index } void BekenSPILEDStripLightOutput::dump_config() { - ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:"); - ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); + ESP_LOGCONFIG(TAG, + "Beken SPI LED Strip:\n" + " Pin: %u", + this->pin_); const char *rgb_order; switch (this->rgb_order_) { case ORDER_RGB: @@ -371,9 +375,11 @@ void BekenSPILEDStripLightOutput::dump_config() { rgb_order = "UNKNOWN"; break; } - ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order); - ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_); - ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_); + ESP_LOGCONFIG(TAG, + " RGB Order: %s\n" + " Max refresh rate: %" PRIu32 "\n" + " Number of LEDs: %u", + rgb_order, *this->max_refresh_rate_, this->num_leds_); } float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; } diff --git a/esphome/components/bh1750/bh1750.cpp b/esphome/components/bh1750/bh1750.cpp index 4b51794907..267a728fdd 100644 --- a/esphome/components/bh1750/bh1750.cpp +++ b/esphome/components/bh1750/bh1750.cpp @@ -50,7 +50,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function< // turn on (after one-shot sensor automatically powers down) uint8_t turn_on = BH1750_COMMAND_POWER_ON; if (this->write(&turn_on, 1) != i2c::ERROR_OK) { - ESP_LOGW(TAG, "Turning on BH1750 failed"); + ESP_LOGW(TAG, "Power on failed"); f(NAN); return; } @@ -60,7 +60,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function< uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111); uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111); if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) { - ESP_LOGW(TAG, "Setting measurement time for BH1750 failed"); + ESP_LOGW(TAG, "Set measurement time failed"); active_mtreg_ = 0; f(NAN); return; @@ -88,7 +88,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function< return; } if (this->write(&cmd, 1) != i2c::ERROR_OK) { - ESP_LOGW(TAG, "Starting measurement for BH1750 failed"); + ESP_LOGW(TAG, "Start measurement failed"); f(NAN); return; } @@ -99,7 +99,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function< this->set_timeout("read", meas_time, [this, mode, mtreg, f]() { uint16_t raw_value; if (this->read(reinterpret_cast(&raw_value), 2) != i2c::ERROR_OK) { - ESP_LOGW(TAG, "Reading BH1750 data failed"); + ESP_LOGW(TAG, "Read data failed"); f(NAN); return; } @@ -156,7 +156,7 @@ void BH1750Sensor::update() { this->publish_state(NAN); return; } - ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), val); + ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), val); this->status_clear_warning(); this->publish_state(val); }); diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 448323da5a..bc26c09622 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -1,7 +1,10 @@ +from logging import getLogger + from esphome import automation, core from esphome.automation import Condition, maybe_simple_id import esphome.codegen as cg from esphome.components import mqtt, web_server +from esphome.components.const import CONF_ON_STATE_CHANGE import esphome.config_validation as cv from esphome.const import ( CONF_DELAY, @@ -98,6 +101,7 @@ IS_PLATFORM_COMPONENT = True CONF_TIME_OFF = "time_off" CONF_TIME_ON = "time_on" +CONF_TRIGGER_ON_INITIAL_STATE = "trigger_on_initial_state" DEFAULT_DELAY = "1s" DEFAULT_TIME_OFF = "100ms" @@ -127,9 +131,17 @@ MultiClickTriggerEvent = binary_sensor_ns.struct("MultiClickTriggerEvent") StateTrigger = binary_sensor_ns.class_( "StateTrigger", automation.Trigger.template(bool) ) +StateChangeTrigger = binary_sensor_ns.class_( + "StateChangeTrigger", + automation.Trigger.template(cg.optional.template(bool), cg.optional.template(bool)), +) + BinarySensorPublishAction = binary_sensor_ns.class_( "BinarySensorPublishAction", automation.Action ) +BinarySensorInvalidateAction = binary_sensor_ns.class_( + "BinarySensorInvalidateAction", automation.Action +) # Condition BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Condition) @@ -144,6 +156,8 @@ AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Compon LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter) SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component) +_LOGGER = getLogger(__name__) + FILTER_REGISTRY = Registry() validate_filters = cv.validate_registry("filter", FILTER_REGISTRY) @@ -386,6 +400,14 @@ def validate_click_timing(value): return value +def validate_publish_initial_state(value): + value = cv.boolean(value) + _LOGGER.warning( + "The 'publish_initial_state' option has been replaced by 'trigger_on_initial_state' and will be removed in a future release" + ) + return value + + _BINARY_SENSOR_SCHEMA = ( cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) .extend(cv.MQTT_COMPONENT_SCHEMA) @@ -395,7 +417,12 @@ _BINARY_SENSOR_SCHEMA = ( cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id( mqtt.MQTTBinarySensorComponent ), - cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean, + cv.Exclusive( + CONF_PUBLISH_INITIAL_STATE, CONF_TRIGGER_ON_INITIAL_STATE + ): validate_publish_initial_state, + cv.Exclusive( + CONF_TRIGGER_ON_INITIAL_STATE, CONF_TRIGGER_ON_INITIAL_STATE + ): cv.boolean, cv.Optional(CONF_DEVICE_CLASS): validate_device_class, cv.Optional(CONF_FILTERS): validate_filters, cv.Optional(CONF_ON_PRESS): automation.validate_automation( @@ -454,6 +481,11 @@ _BINARY_SENSOR_SCHEMA = ( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), } ), + cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateChangeTrigger), + } + ), } ) ) @@ -493,8 +525,10 @@ async def setup_binary_sensor_core_(var, config): if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: cg.add(var.set_device_class(device_class)) - if publish_initial_state := config.get(CONF_PUBLISH_INITIAL_STATE): - cg.add(var.set_publish_initial_state(publish_initial_state)) + trigger = config.get(CONF_TRIGGER_ON_INITIAL_STATE, False) or config.get( + CONF_PUBLISH_INITIAL_STATE, False + ) + cg.add(var.set_trigger_on_initial_state(trigger)) if inverted := config.get(CONF_INVERTED): cg.add(var.set_inverted(inverted)) if filters_config := config.get(CONF_FILTERS): @@ -542,6 +576,17 @@ async def setup_binary_sensor_core_(var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(bool, "x")], conf) + for conf in config.get(CONF_ON_STATE_CHANGE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, + [ + (cg.optional.template(bool), "x_previous"), + (cg.optional.template(bool), "x"), + ], + conf, + ) + if mqtt_id := config.get(CONF_MQTT_ID): mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) @@ -554,6 +599,7 @@ async def register_binary_sensor(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_binary_sensor(var)) + CORE.register_platform_component("binary_sensor", var) await setup_binary_sensor_core_(var, config) @@ -590,3 +636,18 @@ async def binary_sensor_is_off_to_code(config, condition_id, template_arg, args) async def to_code(config): cg.add_define("USE_BINARY_SENSOR") cg.add_global(binary_sensor_ns.using) + + +@automation.register_action( + "binary_sensor.invalidate_state", + BinarySensorInvalidateAction, + cv.maybe_simple_value( + { + cv.Required(CONF_ID): cv.use_id(BinarySensor), + }, + key=CONF_ID, + ), +) +async def binary_sensor_invalidate_state_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + return cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/binary_sensor/automation.cpp b/esphome/components/binary_sensor/automation.cpp index c2e76246aa..64a0d3db8d 100644 --- a/esphome/components/binary_sensor/automation.cpp +++ b/esphome/components/binary_sensor/automation.cpp @@ -68,8 +68,7 @@ void binary_sensor::MultiClickTrigger::on_state_(bool state) { *this->at_index_ = *this->at_index_ + 1; } void binary_sensor::MultiClickTrigger::schedule_cooldown_() { - ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms...", - this->invalid_cooldown_); + ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms", this->invalid_cooldown_); this->is_in_cooldown_ = true; this->set_timeout("cooldown", this->invalid_cooldown_, [this]() { ESP_LOGV(TAG, "Multi Click: Cooldown ended, matching is now enabled again."); diff --git a/esphome/components/binary_sensor/automation.h b/esphome/components/binary_sensor/automation.h index 12b07a05e3..b46436dc41 100644 --- a/esphome/components/binary_sensor/automation.h +++ b/esphome/components/binary_sensor/automation.h @@ -96,7 +96,7 @@ class MultiClickTrigger : public Trigger<>, public Component { : parent_(parent), timing_(std::move(timing)) {} void setup() override { - this->last_state_ = this->parent_->state; + this->last_state_ = this->parent_->get_state_default(false); auto f = std::bind(&MultiClickTrigger::on_state_, this, std::placeholders::_1); this->parent_->add_on_state_callback(f); } @@ -130,6 +130,14 @@ class StateTrigger : public Trigger { } }; +class StateChangeTrigger : public Trigger, optional > { + public: + explicit StateChangeTrigger(BinarySensor *parent) { + parent->add_full_state_callback( + [this](optional old_state, optional state) { this->trigger(old_state, state); }); + } +}; + template class BinarySensorCondition : public Condition { public: BinarySensorCondition(BinarySensor *parent, bool state) : parent_(parent), state_(state) {} @@ -154,5 +162,15 @@ template class BinarySensorPublishAction : public Action BinarySensor *sensor_; }; +template class BinarySensorInvalidateAction : public Action { + public: + explicit BinarySensorInvalidateAction(BinarySensor *sensor) : sensor_(sensor) {} + + void play(Ts... x) override { this->sensor_->invalidate_state(); } + + protected: + BinarySensor *sensor_; +}; + } // namespace binary_sensor } // namespace esphome diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index 20604a0b7e..02b83af552 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -7,42 +7,25 @@ namespace binary_sensor { static const char *const TAG = "binary_sensor"; -void BinarySensor::add_on_state_callback(std::function &&callback) { - this->state_callback_.add(std::move(callback)); -} - -void BinarySensor::publish_state(bool state) { - if (!this->publish_dedup_.next(state)) - return; +void BinarySensor::publish_state(bool new_state) { if (this->filter_list_ == nullptr) { - this->send_state_internal(state, false); + this->send_state_internal(new_state); } else { - this->filter_list_->input(state, false); + this->filter_list_->input(new_state); } } -void BinarySensor::publish_initial_state(bool state) { - if (!this->publish_dedup_.next(state)) - return; - if (this->filter_list_ == nullptr) { - this->send_state_internal(state, true); - } else { - this->filter_list_->input(state, true); +void BinarySensor::publish_initial_state(bool new_state) { + this->invalidate_state(); + this->publish_state(new_state); +} +void BinarySensor::send_state_internal(bool new_state) { + // copy the new state to the visible property for backwards compatibility, before any callbacks + this->state = new_state; + // Note that set_state_ de-dups and will only trigger callbacks if the state has actually changed + if (this->set_state_(new_state)) { + ESP_LOGD(TAG, "'%s': New state is %s", this->get_name().c_str(), ONOFF(new_state)); } } -void BinarySensor::send_state_internal(bool state, bool is_initial) { - if (is_initial) { - ESP_LOGD(TAG, "'%s': Sending initial state %s", this->get_name().c_str(), ONOFF(state)); - } else { - ESP_LOGD(TAG, "'%s': Sending state %s", this->get_name().c_str(), ONOFF(state)); - } - this->has_state_ = true; - this->state = state; - if (!is_initial || this->publish_initial_state_) { - this->state_callback_.call(state); - } -} - -BinarySensor::BinarySensor() : state(false) {} void BinarySensor::add_filter(Filter *filter) { filter->parent_ = this; @@ -60,7 +43,6 @@ void BinarySensor::add_filters(const std::vector &filters) { this->add_filter(filter); } } -bool BinarySensor::has_state() const { return this->has_state_; } bool BinarySensor::is_status_binary_sensor() const { return false; } } // namespace binary_sensor diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 57cae9e2f5..d61be7a49b 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -1,6 +1,5 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" #include "esphome/components/binary_sensor/filter.h" @@ -34,52 +33,39 @@ namespace binary_sensor { * The sub classes should notify the front-end of new states via the publish_state() method which * handles inverted inputs for you. */ -class BinarySensor : public EntityBase, public EntityBase_DeviceClass { +class BinarySensor : public StatefulEntityBase, public EntityBase_DeviceClass { public: - explicit BinarySensor(); - - /** Add a callback to be notified of state changes. - * - * @param callback The void(bool) callback. - */ - void add_on_state_callback(std::function &&callback); + explicit BinarySensor(){}; /** Publish a new state to the front-end. * - * @param state The new state. + * @param new_state The new state. */ - void publish_state(bool state); + void publish_state(bool new_state); /** Publish the initial state, this will not make the callback manager send callbacks * and is meant only for the initial state on boot. * - * @param state The new state. + * @param new_state The new state. */ - void publish_initial_state(bool state); - - /// The current reported state of the binary sensor. - bool state{false}; + void publish_initial_state(bool new_state); void add_filter(Filter *filter); void add_filters(const std::vector &filters); - void set_publish_initial_state(bool publish_initial_state) { this->publish_initial_state_ = publish_initial_state; } - // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) - void send_state_internal(bool state, bool is_initial); + void send_state_internal(bool new_state); /// Return whether this binary sensor has outputted a state. - virtual bool has_state() const; - virtual bool is_status_binary_sensor() const; + // For backward compatibility, provide an accessible property + + bool state{}; + protected: - CallbackManager state_callback_{}; Filter *filter_list_{nullptr}; - bool has_state_{false}; - bool publish_initial_state_{false}; - Deduplicator publish_dedup_; }; class BinarySensorInitiallyOff : public BinarySensor { diff --git a/esphome/components/binary_sensor/filter.cpp b/esphome/components/binary_sensor/filter.cpp index 8f94b108ac..41d0553b35 100644 --- a/esphome/components/binary_sensor/filter.cpp +++ b/esphome/components/binary_sensor/filter.cpp @@ -9,37 +9,36 @@ namespace binary_sensor { static const char *const TAG = "sensor.filter"; -void Filter::output(bool value, bool is_initial) { +void Filter::output(bool value) { + if (this->next_ == nullptr) { + this->parent_->send_state_internal(value); + } else { + this->next_->input(value); + } +} +void Filter::input(bool value) { if (!this->dedup_.next(value)) return; - - if (this->next_ == nullptr) { - this->parent_->send_state_internal(value, is_initial); - } else { - this->next_->input(value, is_initial); - } -} -void Filter::input(bool value, bool is_initial) { - auto b = this->new_value(value, is_initial); + auto b = this->new_value(value); if (b.has_value()) { - this->output(*b, is_initial); + this->output(*b); } } -optional DelayedOnOffFilter::new_value(bool value, bool is_initial) { +optional DelayedOnOffFilter::new_value(bool value) { if (value) { - this->set_timeout("ON_OFF", this->on_delay_.value(), [this, is_initial]() { this->output(true, is_initial); }); + this->set_timeout("ON_OFF", this->on_delay_.value(), [this]() { this->output(true); }); } else { - this->set_timeout("ON_OFF", this->off_delay_.value(), [this, is_initial]() { this->output(false, is_initial); }); + this->set_timeout("ON_OFF", this->off_delay_.value(), [this]() { this->output(false); }); } return {}; } float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; } -optional DelayedOnFilter::new_value(bool value, bool is_initial) { +optional DelayedOnFilter::new_value(bool value) { if (value) { - this->set_timeout("ON", this->delay_.value(), [this, is_initial]() { this->output(true, is_initial); }); + this->set_timeout("ON", this->delay_.value(), [this]() { this->output(true); }); return {}; } else { this->cancel_timeout("ON"); @@ -49,9 +48,9 @@ optional DelayedOnFilter::new_value(bool value, bool is_initial) { float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; } -optional DelayedOffFilter::new_value(bool value, bool is_initial) { +optional DelayedOffFilter::new_value(bool value) { if (!value) { - this->set_timeout("OFF", this->delay_.value(), [this, is_initial]() { this->output(false, is_initial); }); + this->set_timeout("OFF", this->delay_.value(), [this]() { this->output(false); }); return {}; } else { this->cancel_timeout("OFF"); @@ -61,11 +60,11 @@ optional DelayedOffFilter::new_value(bool value, bool is_initial) { float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; } -optional InvertFilter::new_value(bool value, bool is_initial) { return !value; } +optional InvertFilter::new_value(bool value) { return !value; } AutorepeatFilter::AutorepeatFilter(std::vector timings) : timings_(std::move(timings)) {} -optional AutorepeatFilter::new_value(bool value, bool is_initial) { +optional AutorepeatFilter::new_value(bool value) { if (value) { // Ignore if already running if (this->active_timing_ != 0) @@ -101,7 +100,7 @@ void AutorepeatFilter::next_timing_() { void AutorepeatFilter::next_value_(bool val) { const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2]; - this->output(val, false); // This is at least the second one so not initial + this->output(val); // This is at least the second one so not initial this->set_timeout("ON_OFF", val ? timing.time_on : timing.time_off, [this, val]() { this->next_value_(!val); }); } @@ -109,18 +108,18 @@ float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARD LambdaFilter::LambdaFilter(std::function(bool)> f) : f_(std::move(f)) {} -optional LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); } +optional LambdaFilter::new_value(bool value) { return this->f_(value); } -optional SettleFilter::new_value(bool value, bool is_initial) { +optional SettleFilter::new_value(bool value) { if (!this->steady_) { - this->set_timeout("SETTLE", this->delay_.value(), [this, value, is_initial]() { + this->set_timeout("SETTLE", this->delay_.value(), [this, value]() { this->steady_ = true; - this->output(value, is_initial); + this->output(value); }); return {}; } else { this->steady_ = false; - this->output(value, is_initial); + this->output(value); this->set_timeout("SETTLE", this->delay_.value(), [this]() { this->steady_ = true; }); return value; } diff --git a/esphome/components/binary_sensor/filter.h b/esphome/components/binary_sensor/filter.h index f7342db2fb..65838da49d 100644 --- a/esphome/components/binary_sensor/filter.h +++ b/esphome/components/binary_sensor/filter.h @@ -14,11 +14,11 @@ class BinarySensor; class Filter { public: - virtual optional new_value(bool value, bool is_initial) = 0; + virtual optional new_value(bool value) = 0; - void input(bool value, bool is_initial); + void input(bool value); - void output(bool value, bool is_initial); + void output(bool value); protected: friend BinarySensor; @@ -30,7 +30,7 @@ class Filter { class DelayedOnOffFilter : public Filter, public Component { public: - optional new_value(bool value, bool is_initial) override; + optional new_value(bool value) override; float get_setup_priority() const override; @@ -44,7 +44,7 @@ class DelayedOnOffFilter : public Filter, public Component { class DelayedOnFilter : public Filter, public Component { public: - optional new_value(bool value, bool is_initial) override; + optional new_value(bool value) override; float get_setup_priority() const override; @@ -56,7 +56,7 @@ class DelayedOnFilter : public Filter, public Component { class DelayedOffFilter : public Filter, public Component { public: - optional new_value(bool value, bool is_initial) override; + optional new_value(bool value) override; float get_setup_priority() const override; @@ -68,7 +68,7 @@ class DelayedOffFilter : public Filter, public Component { class InvertFilter : public Filter { public: - optional new_value(bool value, bool is_initial) override; + optional new_value(bool value) override; }; struct AutorepeatFilterTiming { @@ -86,7 +86,7 @@ class AutorepeatFilter : public Filter, public Component { public: explicit AutorepeatFilter(std::vector timings); - optional new_value(bool value, bool is_initial) override; + optional new_value(bool value) override; float get_setup_priority() const override; @@ -102,7 +102,7 @@ class LambdaFilter : public Filter { public: explicit LambdaFilter(std::function(bool)> f); - optional new_value(bool value, bool is_initial) override; + optional new_value(bool value) override; protected: std::function(bool)> f_; @@ -110,7 +110,7 @@ class LambdaFilter : public Filter { class SettleFilter : public Filter, public Component { public: - optional new_value(bool value, bool is_initial) override; + optional new_value(bool value) override; float get_setup_priority() const override; diff --git a/esphome/components/bl0906/bl0906.cpp b/esphome/components/bl0906/bl0906.cpp index bddb62ff64..e48715010c 100644 --- a/esphome/components/bl0906/bl0906.cpp +++ b/esphome/components/bl0906/bl0906.cpp @@ -100,7 +100,7 @@ void BL0906::handle_actions_() { for (int i = 0; i < this->action_queue_.size(); i++) { ptr_func = this->action_queue_[i]; if (ptr_func) { - ESP_LOGI(TAG, "HandleActionCallback[%d]...", i); + ESP_LOGI(TAG, "HandleActionCallback[%d]", i); (this->*ptr_func)(); } } diff --git a/esphome/components/bl0942/bl0942.cpp b/esphome/components/bl0942/bl0942.cpp index e6f96c1b19..86eff57147 100644 --- a/esphome/components/bl0942/bl0942.cpp +++ b/esphome/components/bl0942/bl0942.cpp @@ -196,14 +196,17 @@ void BL0942::received_package_(DataPacket *data) { } void BL0942::dump_config() { // NOLINT(readability-function-cognitive-complexity) - ESP_LOGCONFIG(TAG, "BL0942:"); - ESP_LOGCONFIG(TAG, " Reset: %s", TRUEFALSE(this->reset_)); - ESP_LOGCONFIG(TAG, " Address: %d", this->address_); - ESP_LOGCONFIG(TAG, " Nominal line frequency: %d Hz", this->line_freq_); - ESP_LOGCONFIG(TAG, " Current reference: %f", this->current_reference_); - ESP_LOGCONFIG(TAG, " Energy reference: %f", this->energy_reference_); - ESP_LOGCONFIG(TAG, " Power reference: %f", this->power_reference_); - ESP_LOGCONFIG(TAG, " Voltage reference: %f", this->voltage_reference_); + ESP_LOGCONFIG(TAG, + "BL0942:\n" + " Reset: %s\n" + " Address: %d\n" + " Nominal line frequency: %d Hz\n" + " Current reference: %f\n" + " Energy reference: %f\n" + " Power reference: %f\n" + " Voltage reference: %f", + TRUEFALSE(this->reset_), this->address_, this->line_freq_, this->current_reference_, + this->energy_reference_, this->power_reference_, this->voltage_reference_); LOG_SENSOR("", "Voltage", this->voltage_sensor_); LOG_SENSOR("", "Current", this->current_sensor_); LOG_SENSOR("", "Power", this->power_sensor_); diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index 37f8ea32b3..a88172ca87 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -1,7 +1,8 @@ from esphome import automation from esphome.automation import maybe_simple_id import esphome.codegen as cg -from esphome.components import esp32_ble_client, esp32_ble_tracker +from esphome.components import esp32_ble, esp32_ble_client, esp32_ble_tracker +from esphome.components.esp32_ble import BTLoggers import esphome.config_validation as cv from esphome.const import ( CONF_CHARACTERISTIC_UUID, @@ -287,6 +288,9 @@ async def remove_bond_to_code(config, action_id, template_arg, args): async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) await esp32_ble_tracker.register_client(var, config) diff --git a/esphome/components/ble_client/output/ble_binary_output.cpp b/esphome/components/ble_client/output/ble_binary_output.cpp index 3f05bc4b84..ce67193be7 100644 --- a/esphome/components/ble_client/output/ble_binary_output.cpp +++ b/esphome/components/ble_client/output/ble_binary_output.cpp @@ -10,9 +10,12 @@ static const char *const TAG = "ble_binary_output"; void BLEBinaryOutput::dump_config() { ESP_LOGCONFIG(TAG, "BLE Binary Output:"); - ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent_->address_str().c_str()); - ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str()); + ESP_LOGCONFIG(TAG, + " MAC address : %s\n" + " Service UUID : %s\n" + " Characteristic UUID: %s", + this->parent_->address_str().c_str(), this->service_uuid_.to_string().c_str(), + this->char_uuid_.to_string().c_str()); LOG_BINARY_OUTPUT(this); } diff --git a/esphome/components/ble_client/sensor/ble_rssi_sensor.cpp b/esphome/components/ble_client/sensor/ble_rssi_sensor.cpp index 81d244ce6d..663c52ac10 100644 --- a/esphome/components/ble_client/sensor/ble_rssi_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_rssi_sensor.cpp @@ -11,7 +11,11 @@ namespace ble_client { static const char *const TAG = "ble_rssi_sensor"; -void BLEClientRSSISensor::loop() {} +void BLEClientRSSISensor::loop() { + // Parent BLEClientNode has a loop() method, but this component uses + // polling via update() and BLE GAP callbacks so loop isn't needed + this->disable_loop(); +} void BLEClientRSSISensor::dump_config() { LOG_SENSOR("", "BLE Client RSSI Sensor", this); diff --git a/esphome/components/ble_client/sensor/ble_sensor.cpp b/esphome/components/ble_client/sensor/ble_sensor.cpp index 43f61f5304..d0ccfe1f2e 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_sensor.cpp @@ -1,7 +1,7 @@ #include "ble_sensor.h" -#include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #ifdef USE_ESP32 @@ -11,15 +11,22 @@ namespace ble_client { static const char *const TAG = "ble_sensor"; -void BLESensor::loop() {} +void BLESensor::loop() { + // Parent BLEClientNode has a loop() method, but this component uses + // polling via update() and BLE callbacks so loop isn't needed + this->disable_loop(); +} void BLESensor::dump_config() { LOG_SENSOR("", "BLE Sensor", this); - ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str()); - ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_)); + ESP_LOGCONFIG(TAG, + " MAC address : %s\n" + " Service UUID : %s\n" + " Characteristic UUID: %s\n" + " Descriptor UUID : %s\n" + " Notifications : %s", + this->parent()->address_str().c_str(), this->service_uuid_.to_string().c_str(), + this->char_uuid_.to_string().c_str(), this->descr_uuid_.to_string().c_str(), YESNO(this->notify_)); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp index 33938ee7b7..e7da297fa0 100644 --- a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +++ b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp @@ -14,15 +14,22 @@ static const char *const TAG = "ble_text_sensor"; static const std::string EMPTY = ""; -void BLETextSensor::loop() {} +void BLETextSensor::loop() { + // Parent BLEClientNode has a loop() method, but this component uses + // polling via update() and BLE callbacks so loop isn't needed + this->disable_loop(); +} void BLETextSensor::dump_config() { LOG_TEXT_SENSOR("", "BLE Text Sensor", this); - ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str()); - ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_)); + ESP_LOGCONFIG(TAG, + " MAC address : %s\n" + " Service UUID : %s\n" + " Characteristic UUID: %s\n" + " Descriptor UUID : %s\n" + " Notifications : %s", + this->parent()->address_str().c_str(), this->service_uuid_.to_string().c_str(), + this->char_uuid_.to_string().c_str(), this->descr_uuid_.to_string().c_str(), YESNO(this->notify_)); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/bluetooth_proxy/__init__.py b/esphome/components/bluetooth_proxy/__init__.py index 04ac9116c7..5c144cadcc 100644 --- a/esphome/components/bluetooth_proxy/__init__.py +++ b/esphome/components/bluetooth_proxy/__init__.py @@ -1,6 +1,7 @@ import esphome.codegen as cg -from esphome.components import esp32_ble_client, esp32_ble_tracker +from esphome.components import esp32_ble, esp32_ble_client, esp32_ble_tracker from esphome.components.esp32 import add_idf_sdkconfig_option +from esphome.components.esp32_ble import BTLoggers import esphome.config_validation as cv from esphome.const import CONF_ACTIVE, CONF_ID @@ -77,6 +78,9 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.L2CAP, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp index 3c5c2bd438..44d434802c 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp @@ -75,7 +75,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga resp.data.reserve(param->read.value_len); // Use bulk insert instead of individual push_backs resp.data.insert(resp.data.end(), param->read.value, param->read.value + param->read.value_len); - this->proxy_->get_api_connection()->send_bluetooth_gatt_read_response(resp); + this->proxy_->get_api_connection()->send_message(resp); break; } case ESP_GATTC_WRITE_CHAR_EVT: @@ -89,7 +89,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga api::BluetoothGATTWriteResponse resp; resp.address = this->address_; resp.handle = param->write.handle; - this->proxy_->get_api_connection()->send_bluetooth_gatt_write_response(resp); + this->proxy_->get_api_connection()->send_message(resp); break; } case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { @@ -103,7 +103,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga api::BluetoothGATTNotifyResponse resp; resp.address = this->address_; resp.handle = param->unreg_for_notify.handle; - this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp); + this->proxy_->get_api_connection()->send_message(resp); break; } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { @@ -116,7 +116,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga api::BluetoothGATTNotifyResponse resp; resp.address = this->address_; resp.handle = param->reg_for_notify.handle; - this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp); + this->proxy_->get_api_connection()->send_message(resp); break; } case ESP_GATTC_NOTIFY_EVT: { @@ -128,7 +128,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga resp.data.reserve(param->notify.value_len); // Use bulk insert instead of individual push_backs resp.data.insert(resp.data.end(), param->notify.value, param->notify.value + param->notify.value_len); - this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_data_response(resp); + this->proxy_->get_api_connection()->send_message(resp); break; } default: diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.h b/esphome/components/bluetooth_proxy/bluetooth_connection.h index fd83f8dd00..73c034d93b 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.h +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.h @@ -26,10 +26,17 @@ class BluetoothConnection : public esp32_ble_client::BLEClientBase { protected: friend class BluetoothProxy; - bool seen_mtu_or_services_{false}; - int16_t send_service_{-2}; + // Memory optimized layout for 32-bit systems + // Group 1: Pointers (4 bytes each, naturally aligned) BluetoothProxy *proxy_; + + // Group 2: 2-byte types + int16_t send_service_{-2}; // Needs to handle negative values and service count + + // Group 3: 1-byte types + bool seen_mtu_or_services_{false}; + // 1 byte used, 1 byte padding }; } // namespace bluetooth_proxy diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index d8b2111cb0..fbe2a3e67c 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -39,7 +39,7 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta resp.state = static_cast(state); resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE : api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE; - this->api_connection_->send_bluetooth_scanner_state_response(resp); + this->api_connection_->send_message(resp); } bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { @@ -58,7 +58,7 @@ static std::vector &get_batch_buffer() { return batch_buffer; } -bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) { +bool BluetoothProxy::parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) { if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_) return false; @@ -73,7 +73,7 @@ bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_p // Add new advertisements to the batch buffer for (size_t i = 0; i < count; i++) { - auto &result = advertisements[i]; + auto &result = scan_results[i]; uint8_t length = result.adv_data_len + result.scan_rsp_len; batch_buffer.emplace_back(); @@ -103,7 +103,7 @@ void BluetoothProxy::flush_pending_advertisements() { api::BluetoothLERawAdvertisementsResponse resp; resp.advertisements.swap(batch_buffer); - this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp); + this->api_connection_->send_message(resp); } void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) { @@ -141,14 +141,16 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi manufacturer_data.data.assign(data.data.begin(), data.data.end()); } - this->api_connection_->send_bluetooth_le_advertisement(resp); + this->api_connection_->send_message(resp); } void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); - ESP_LOGCONFIG(TAG, " Active: %s", YESNO(this->active_)); - ESP_LOGCONFIG(TAG, " Connections: %d", this->connections_.size()); - ESP_LOGCONFIG(TAG, " Raw advertisements: %s", YESNO(this->raw_advertisements_)); + ESP_LOGCONFIG(TAG, + " Active: %s\n" + " Connections: %d\n" + " Raw advertisements: %s", + YESNO(this->active_), this->connections_.size(), YESNO(this->raw_advertisements_)); } int BluetoothProxy::get_bluetooth_connections_free() { @@ -300,7 +302,7 @@ void BluetoothProxy::loop() { service_resp.characteristics.push_back(std::move(characteristic_resp)); } resp.services.push_back(std::move(service_resp)); - this->api_connection_->send_bluetooth_gatt_get_services_response(resp); + this->api_connection_->send_message(resp); } } } @@ -453,7 +455,7 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest call.success = ret == ESP_OK; call.error = ret; - this->api_connection_->send_bluetooth_device_clear_cache_response(call); + this->api_connection_->send_message(call); break; } @@ -577,7 +579,7 @@ void BluetoothProxy::send_device_connection(uint64_t address, bool connected, ui call.connected = connected; call.mtu = mtu; call.error = error; - this->api_connection_->send_bluetooth_device_connection_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::send_connections_free() { if (this->api_connection_ == nullptr) @@ -590,7 +592,7 @@ void BluetoothProxy::send_connections_free() { call.allocated.push_back(connection->address_); } } - this->api_connection_->send_bluetooth_connections_free_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::send_gatt_services_done(uint64_t address) { @@ -598,7 +600,7 @@ void BluetoothProxy::send_gatt_services_done(uint64_t address) { return; api::BluetoothGATTGetServicesDoneResponse call; call.address = address; - this->api_connection_->send_bluetooth_gatt_get_services_done_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) { @@ -608,7 +610,7 @@ void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_ call.address = address; call.handle = handle; call.error = error; - this->api_connection_->send_bluetooth_gatt_error_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_t error) { @@ -617,7 +619,7 @@ void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_ call.paired = paired; call.error = error; - this->api_connection_->send_bluetooth_device_pairing_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_err_t error) { @@ -626,7 +628,7 @@ void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_e call.success = success; call.error = error; - this->api_connection_->send_bluetooth_device_unpairing_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::bluetooth_scanner_set_mode(bool active) { diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index f75e73e796..f0632350e0 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -52,7 +52,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com public: BluetoothProxy(); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; - bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override; + bool parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) override; void dump_config() override; void setup() override; void loop() override; @@ -134,11 +134,17 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com BluetoothConnection *get_connection_(uint64_t address, bool reserve); - bool active_; - - std::vector connections_{}; + // Memory optimized layout for 32-bit systems + // Group 1: Pointers (4 bytes each, naturally aligned) api::APIConnection *api_connection_{nullptr}; + + // Group 2: Container types (typically 12 bytes on 32-bit) + std::vector connections_{}; + + // Group 3: 1-byte types grouped together + bool active_; bool raw_advertisements_{false}; + // 2 bytes used, 2 bytes padding }; extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/bme280_base/bme280_base.cpp b/esphome/components/bme280_base/bme280_base.cpp index c73b48589d..d2524e5aac 100644 --- a/esphome/components/bme280_base/bme280_base.cpp +++ b/esphome/components/bme280_base/bme280_base.cpp @@ -93,9 +93,8 @@ void BME280Component::setup() { // Mark as not failed before initializing. Some devices will turn off sensors to save on batteries // and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component. - if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) { - this->component_state_ &= ~COMPONENT_STATE_MASK; - this->component_state_ |= COMPONENT_STATE_CONSTRUCTION; + if (this->is_failed()) { + this->reset_to_construction_state(); } if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) { @@ -207,7 +206,7 @@ inline uint8_t oversampling_to_time(BME280Oversampling over_sampling) { return ( void BME280Component::update() { // Enable sensor - ESP_LOGV(TAG, "Sending conversion request..."); + ESP_LOGV(TAG, "Sending conversion request"); uint8_t meas_value = 0; meas_value |= (this->temperature_oversampling_ & 0b111) << 5; meas_value |= (this->pressure_oversampling_ & 0b111) << 2; diff --git a/esphome/components/bme680/sensor.py b/esphome/components/bme680/sensor.py index 5937ac6ad8..f41aefcec3 100644 --- a/esphome/components/bme680/sensor.py +++ b/esphome/components/bme680/sensor.py @@ -12,8 +12,8 @@ from esphome.const import ( CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, + DEVICE_CLASS_ATMOSPHERIC_PRESSURE, DEVICE_CLASS_HUMIDITY, - DEVICE_CLASS_PRESSURE, DEVICE_CLASS_TEMPERATURE, ICON_GAS_CYLINDER, STATE_CLASS_MEASUREMENT, @@ -71,7 +71,7 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_PRESSURE): sensor.sensor_schema( unit_of_measurement=UNIT_HECTOPASCAL, accuracy_decimals=1, - device_class=DEVICE_CLASS_PRESSURE, + device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE, state_class=STATE_CLASS_MEASUREMENT, ).extend( { diff --git a/esphome/components/bme680_bsec/bme680_bsec.cpp b/esphome/components/bme680_bsec/bme680_bsec.cpp index 92642173dc..562d39e7b5 100644 --- a/esphome/components/bme680_bsec/bme680_bsec.cpp +++ b/esphome/components/bme680_bsec/bme680_bsec.cpp @@ -1,6 +1,6 @@ #include "bme680_bsec.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include namespace esphome { @@ -159,11 +159,15 @@ void BME680BSECComponent::dump_config() { this->bme680_status_); } - ESP_LOGCONFIG(TAG, " Temperature Offset: %.2f", this->temperature_offset_); - ESP_LOGCONFIG(TAG, " IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile"); - ESP_LOGCONFIG(TAG, " Supply Voltage: %sV", this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8"); - ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_)); - ESP_LOGCONFIG(TAG, " State Save Interval: %ims", this->state_save_interval_ms_); + ESP_LOGCONFIG(TAG, + " Temperature Offset: %.2f\n" + " IAQ Mode: %s\n" + " Supply Voltage: %sV\n" + " Sample Rate: %s\n" + " State Save Interval: %ims", + this->temperature_offset_, this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile", + this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8", + BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_), this->state_save_interval_ms_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->temperature_sample_rate_)); diff --git a/esphome/components/bme680_bsec/sensor.py b/esphome/components/bme680_bsec/sensor.py index 5107b0bfcd..8d3ae76e3f 100644 --- a/esphome/components/bme680_bsec/sensor.py +++ b/esphome/components/bme680_bsec/sensor.py @@ -15,6 +15,8 @@ from esphome.const import ( DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, ICON_GAS_CYLINDER, ICON_GAUGE, + ICON_THERMOMETER, + ICON_WATER_PERCENT, STATE_CLASS_MEASUREMENT, UNIT_CELSIUS, UNIT_HECTOPASCAL, @@ -27,11 +29,11 @@ from . import CONF_BME680_BSEC_ID, SAMPLE_RATE_OPTIONS, BME680BSECComponent DEPENDENCIES = ["bme680_bsec"] -CONF_IAQ = "iaq" -CONF_CO2_EQUIVALENT = "co2_equivalent" CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent" -UNIT_IAQ = "IAQ" +CONF_CO2_EQUIVALENT = "co2_equivalent" +CONF_IAQ = "iaq" ICON_ACCURACY = "mdi:checkbox-marked-circle-outline" +UNIT_IAQ = "IAQ" TYPES = [ CONF_TEMPERATURE, @@ -49,6 +51,7 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, accuracy_decimals=1, device_class=DEVICE_CLASS_TEMPERATURE, state_class=STATE_CLASS_MEASUREMENT, @@ -65,6 +68,7 @@ CONFIG_SCHEMA = cv.Schema( ), cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( unit_of_measurement=UNIT_PERCENT, + icon=ICON_WATER_PERCENT, accuracy_decimals=1, device_class=DEVICE_CLASS_HUMIDITY, state_class=STATE_CLASS_MEASUREMENT, diff --git a/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp b/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp index 6b3663cdc8..a23711c4ca 100644 --- a/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +++ b/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp @@ -58,13 +58,13 @@ void BME68xBSEC2Component::setup() { } void BME68xBSEC2Component::dump_config() { - ESP_LOGCONFIG(TAG, "BME68X via BSEC2:"); - - ESP_LOGCONFIG(TAG, " BSEC2 version: %d.%d.%d.%d", this->version_.major, this->version_.minor, - this->version_.major_bugfix, this->version_.minor_bugfix); - - ESP_LOGCONFIG(TAG, " BSEC2 configuration blob:"); - ESP_LOGCONFIG(TAG, " Configured: %s", YESNO(this->bsec2_blob_configured_)); + ESP_LOGCONFIG(TAG, + "BME68X via BSEC2:\n" + " BSEC2 version: %d.%d.%d.%d\n" + " BSEC2 configuration blob:\n" + " Configured: %s", + this->version_.major, this->version_.minor, this->version_.major_bugfix, this->version_.minor_bugfix, + YESNO(this->bsec2_blob_configured_)); if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) { ESP_LOGCONFIG(TAG, " Size: %" PRIu32, this->bsec2_configuration_length_); } @@ -77,11 +77,14 @@ void BME68xBSEC2Component::dump_config() { if (this->algorithm_output_ != ALGORITHM_OUTPUT_IAQ) { ESP_LOGCONFIG(TAG, " Algorithm output: %s", BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(this->algorithm_output_)); } - ESP_LOGCONFIG(TAG, " Operating age: %s", BME68X_BSEC2_OPERATING_AGE_LOG(this->operating_age_)); - ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_)); - ESP_LOGCONFIG(TAG, " Voltage: %s", BME68X_BSEC2_VOLTAGE_LOG(this->voltage_)); - ESP_LOGCONFIG(TAG, " State save interval: %ims", this->state_save_interval_ms_); - ESP_LOGCONFIG(TAG, " Temperature offset: %.2f", this->temperature_offset_); + ESP_LOGCONFIG(TAG, + " Operating age: %s\n" + " Sample rate: %s\n" + " Voltage: %s\n" + " State save interval: %ims\n" + " Temperature offset: %.2f", + BME68X_BSEC2_OPERATING_AGE_LOG(this->operating_age_), BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_), + BME68X_BSEC2_VOLTAGE_LOG(this->voltage_), this->state_save_interval_ms_, this->temperature_offset_); #ifdef USE_SENSOR LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); diff --git a/esphome/components/bme68x_bsec2/sensor.py b/esphome/components/bme68x_bsec2/sensor.py index 419f47b248..c7dca437d7 100644 --- a/esphome/components/bme68x_bsec2/sensor.py +++ b/esphome/components/bme68x_bsec2/sensor.py @@ -9,8 +9,10 @@ from esphome.const import ( CONF_SAMPLE_RATE, CONF_TEMPERATURE, DEVICE_CLASS_ATMOSPHERIC_PRESSURE, + DEVICE_CLASS_CARBON_DIOXIDE, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, ICON_GAS_CYLINDER, ICON_GAUGE, ICON_THERMOMETER, @@ -32,7 +34,6 @@ CONF_CO2_EQUIVALENT = "co2_equivalent" CONF_IAQ = "iaq" CONF_IAQ_STATIC = "iaq_static" ICON_ACCURACY = "mdi:checkbox-marked-circle-outline" -ICON_TEST_TUBE = "mdi:test-tube" UNIT_IAQ = "IAQ" TYPES = [ @@ -61,7 +62,6 @@ CONFIG_SCHEMA = cv.Schema( ), cv.Optional(CONF_PRESSURE): sensor.sensor_schema( unit_of_measurement=UNIT_HECTOPASCAL, - icon=ICON_GAUGE, accuracy_decimals=1, device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE, state_class=STATE_CLASS_MEASUREMENT, @@ -102,14 +102,14 @@ CONFIG_SCHEMA = cv.Schema( ), cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema( unit_of_measurement=UNIT_PARTS_PER_MILLION, - icon=ICON_TEST_TUBE, accuracy_decimals=1, + device_class=DEVICE_CLASS_CARBON_DIOXIDE, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema( unit_of_measurement=UNIT_PARTS_PER_MILLION, - icon=ICON_TEST_TUBE, accuracy_decimals=1, + device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, state_class=STATE_CLASS_MEASUREMENT, ), } diff --git a/esphome/components/bmi160/bmi160.cpp b/esphome/components/bmi160/bmi160.cpp index 67106cc20c..aca42f1b52 100644 --- a/esphome/components/bmi160/bmi160.cpp +++ b/esphome/components/bmi160/bmi160.cpp @@ -126,37 +126,37 @@ void BMI160Component::internal_setup_(int stage) { return; } - ESP_LOGV(TAG, " Bringing accelerometer out of sleep..."); + ESP_LOGV(TAG, " Bringing accelerometer out of sleep"); if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::ACCL_SET_PMU_MODE | (uint8_t) AcclPmuMode::NORMAL)) { this->mark_failed(); return; } - ESP_LOGV(TAG, " Waiting for accelerometer to wake up..."); + ESP_LOGV(TAG, " Waiting for accelerometer to wake up"); // need to wait (max delay in datasheet) because we can't send commands while another is in progress // min 5ms, 10ms this->set_timeout(10, [this]() { this->internal_setup_(1); }); break; case 1: - ESP_LOGV(TAG, " Bringing gyroscope out of sleep..."); + ESP_LOGV(TAG, " Bringing gyroscope out of sleep"); if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::GYRO_SET_PMU_MODE | (uint8_t) GyroPmuMode::NORMAL)) { this->mark_failed(); return; } - ESP_LOGV(TAG, " Waiting for gyroscope to wake up..."); + ESP_LOGV(TAG, " Waiting for gyroscope to wake up"); // wait between 51 & 81ms, doing 100 to be safe this->set_timeout(10, [this]() { this->internal_setup_(2); }); break; case 2: - ESP_LOGV(TAG, " Setting up Gyro Config..."); + ESP_LOGV(TAG, " Setting up Gyro Config"); uint8_t gyro_config = (uint8_t) GyroBandwidth::OSR4 | (uint8_t) GyroOuputDataRate::HZ_25; ESP_LOGV(TAG, " Output gyro_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config)); if (!this->write_byte(BMI160_REGISTER_GYRO_CONFIG, gyro_config)) { this->mark_failed(); return; } - ESP_LOGV(TAG, " Setting up Gyro Range..."); + ESP_LOGV(TAG, " Setting up Gyro Range"); uint8_t gyro_range = (uint8_t) GyroRange::RANGE_2000_DPS; ESP_LOGV(TAG, " Output gyro_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_range)); if (!this->write_byte(BMI160_REGISTER_GYRO_RANGE, gyro_range)) { @@ -164,7 +164,7 @@ void BMI160Component::internal_setup_(int stage) { return; } - ESP_LOGV(TAG, " Setting up Accel Config..."); + ESP_LOGV(TAG, " Setting up Accel Config"); uint8_t accel_config = (uint8_t) AcclFilterMode::PERF | (uint8_t) AcclBandwidth::RES_AVG16 | (uint8_t) AccelOutputDataRate::HZ_25; ESP_LOGV(TAG, " Output accel_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config)); @@ -172,7 +172,7 @@ void BMI160Component::internal_setup_(int stage) { this->mark_failed(); return; } - ESP_LOGV(TAG, " Setting up Accel Range..."); + ESP_LOGV(TAG, " Setting up Accel Range"); uint8_t accel_range = (uint8_t) AccelRange::RANGE_16G; ESP_LOGV(TAG, " Output accel_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_range)); if (!this->write_byte(BMI160_REGISTER_ACCEL_RANGE, accel_range)) { @@ -219,7 +219,7 @@ void BMI160Component::update() { return; } - ESP_LOGV(TAG, " Updating BMI160..."); + ESP_LOGV(TAG, " Updating BMI160"); int16_t data[6]; if (this->read_le_int16_(BMI160_REGISTER_DATA_GYRO_X_LSB, data, 6) != i2c::ERROR_OK) { this->status_set_warning(); diff --git a/esphome/components/bmp085/bmp085.cpp b/esphome/components/bmp085/bmp085.cpp index 7f00a915f1..94dc61891b 100644 --- a/esphome/components/bmp085/bmp085.cpp +++ b/esphome/components/bmp085/bmp085.cpp @@ -129,7 +129,7 @@ void BMP085Component::read_pressure_() { this->status_clear_warning(); } bool BMP085Component::set_mode_(uint8_t mode) { - ESP_LOGV(TAG, "Setting mode to 0x%02X...", mode); + ESP_LOGV(TAG, "Setting mode to 0x%02X", mode); return this->write_byte(BMP085_REGISTER_CONTROL, mode); } float BMP085Component::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/bmp280_base/bmp280_base.cpp b/esphome/components/bmp280_base/bmp280_base.cpp index 665707e26c..94b8bd6540 100644 --- a/esphome/components/bmp280_base/bmp280_base.cpp +++ b/esphome/components/bmp280_base/bmp280_base.cpp @@ -155,7 +155,7 @@ inline uint8_t oversampling_to_time(BMP280Oversampling over_sampling) { return ( void BMP280Component::update() { // Enable sensor - ESP_LOGV(TAG, "Sending conversion request..."); + ESP_LOGV(TAG, "Sending conversion request"); uint8_t meas_value = 0; meas_value |= (this->temperature_oversampling_ & 0b111) << 5; meas_value |= (this->pressure_oversampling_ & 0b111) << 2; diff --git a/esphome/components/bmp3xx_base/bmp3xx_base.cpp b/esphome/components/bmp3xx_base/bmp3xx_base.cpp index d35c5fd331..979f354cb2 100644 --- a/esphome/components/bmp3xx_base/bmp3xx_base.cpp +++ b/esphome/components/bmp3xx_base/bmp3xx_base.cpp @@ -73,7 +73,7 @@ void BMP3XXComponent::setup() { ESP_LOGCONFIG(TAG, "Running setup"); // Call the Device base class "initialise" function if (!reset()) { - ESP_LOGE(TAG, "Failed to reset BMP3XX..."); + ESP_LOGE(TAG, "Failed to reset"); this->error_code_ = ERROR_SENSOR_RESET; this->mark_failed(); } @@ -148,8 +148,10 @@ void BMP3XXComponent::setup() { } void BMP3XXComponent::dump_config() { - ESP_LOGCONFIG(TAG, "BMP3XX:"); - ESP_LOGCONFIG(TAG, " Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg); + ESP_LOGCONFIG(TAG, + "BMP3XX:\n" + " Type: %s (0x%X)", + LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg); switch (this->error_code_) { case NONE: break; @@ -157,16 +159,14 @@ void BMP3XXComponent::dump_config() { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case ERROR_WRONG_CHIP_ID: - ESP_LOGE( - TAG, - "BMP3XX has wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390", - this->chip_id_.reg); + ESP_LOGE(TAG, "Wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390", + this->chip_id_.reg); break; case ERROR_SENSOR_RESET: - ESP_LOGE(TAG, "BMP3XX failed to reset"); + ESP_LOGE(TAG, "Failed to reset"); break; default: - ESP_LOGE(TAG, "BMP3XX error code %d", (int) this->error_code_); + ESP_LOGE(TAG, "Error code %d", (int) this->error_code_); break; } ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_filter_))); @@ -186,7 +186,7 @@ inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << u void BMP3XXComponent::update() { // Enable sensor - ESP_LOGV(TAG, "Sending conversion request..."); + ESP_LOGV(TAG, "Sending conversion request"); float meas_time = 1.0f; // Ref: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf 3.9.2 meas_time += 2.02f * oversampling_to_time(this->temperature_oversampling_) + 0.163f; @@ -296,7 +296,7 @@ bool BMP3XXComponent::get_pressure(float &pressure) { bool BMP3XXComponent::get_measurements(float &temperature, float &pressure) { // Check if a measurement is ready if (!data_ready()) { - ESP_LOGD(TAG, "BMP3XX Get measurement - data not ready skipping update"); + ESP_LOGD(TAG, "Get measurement - data not ready skipping update"); return false; } diff --git a/esphome/components/bmp581/bmp581.cpp b/esphome/components/bmp581/bmp581.cpp index 30d5b95a46..2204a6af2e 100644 --- a/esphome/components/bmp581/bmp581.cpp +++ b/esphome/components/bmp581/bmp581.cpp @@ -72,22 +72,22 @@ void BMP581Component::dump_config() { case NONE: break; case ERROR_COMMUNICATION_FAILED: - ESP_LOGE(TAG, " Communication with BMP581 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case ERROR_WRONG_CHIP_ID: - ESP_LOGE(TAG, " BMP581 has wrong chip ID - please verify you are using a BMP 581"); + ESP_LOGE(TAG, "Unknown chip ID"); break; case ERROR_SENSOR_RESET: - ESP_LOGE(TAG, " BMP581 failed to reset"); + ESP_LOGE(TAG, "Reset failed"); break; case ERROR_SENSOR_STATUS: - ESP_LOGE(TAG, " BMP581 sensor status failed, there were NVM problems"); + ESP_LOGE(TAG, "Get status failed"); break; case ERROR_PRIME_IIR_FAILED: - ESP_LOGE(TAG, " BMP581's IIR Filter failed to prime with an initial measurement"); + ESP_LOGE(TAG, "IIR Filter failed to prime with initial measurement"); break; default: - ESP_LOGE(TAG, " BMP581 error code %d", (int) this->error_code_); + ESP_LOGE(TAG, "Error %d", (int) this->error_code_); break; } @@ -98,14 +98,20 @@ void BMP581Component::dump_config() { if (this->temperature_sensor_) { LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); - ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_temperature_level_))); - ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_))); + ESP_LOGCONFIG(TAG, + " IIR Filter: %s\n" + " Oversampling: %s", + LOG_STR_ARG(iir_filter_to_str(this->iir_temperature_level_)), + LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_))); } if (this->pressure_sensor_) { LOG_SENSOR(" ", "Pressure", this->pressure_sensor_); - ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_pressure_level_))); - ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_))); + ESP_LOGCONFIG(TAG, + " IIR Filter: %s\n" + " Oversampling: %s", + LOG_STR_ARG(iir_filter_to_str(this->iir_pressure_level_)), + LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_))); } } @@ -130,7 +136,7 @@ void BMP581Component::setup() { // Power-On-Reboot bit is asserted if sensor successfully reset if (!this->reset_()) { - ESP_LOGE(TAG, "BMP581 failed to reset"); + ESP_LOGE(TAG, "Reset failed"); this->error_code_ = ERROR_SENSOR_RESET; this->mark_failed(); @@ -146,7 +152,7 @@ void BMP581Component::setup() { // read chip id from sensor if (!this->read_byte(BMP581_CHIP_ID, &chip_id)) { - ESP_LOGE(TAG, "Failed to read chip id"); + ESP_LOGE(TAG, "Read chip ID failed"); this->error_code_ = ERROR_COMMUNICATION_FAILED; this->mark_failed(); @@ -156,7 +162,7 @@ void BMP581Component::setup() { // verify id if (chip_id != BMP581_ASIC_ID) { - ESP_LOGE(TAG, "Unknown chip ID, is this a BMP581?"); + ESP_LOGE(TAG, "Unknown chip ID"); this->error_code_ = ERROR_WRONG_CHIP_ID; this->mark_failed(); @@ -179,7 +185,7 @@ void BMP581Component::setup() { // verify status_nvm_rdy bit (it is asserted if boot was successful) if (!(this->status_.bit.status_nvm_rdy)) { - ESP_LOGE(TAG, "NVM not ready after boot"); + ESP_LOGE(TAG, "NVM not ready"); this->error_code_ = ERROR_SENSOR_STATUS; this->mark_failed(); @@ -189,7 +195,7 @@ void BMP581Component::setup() { // verify status_nvm_err bit (it is asserted if an error is detected) if (this->status_.bit.status_nvm_err) { - ESP_LOGE(TAG, "NVM error detected on boot"); + ESP_LOGE(TAG, "NVM error detected"); this->error_code_ = ERROR_SENSOR_STATUS; this->mark_failed(); @@ -254,7 +260,7 @@ void BMP581Component::setup() { } if (!this->prime_iir_filter_()) { - ESP_LOGE(TAG, "Failed to prime the IIR filter with an intiial measurement"); + ESP_LOGE(TAG, "Failed to prime the IIR filter with an initial measurement"); this->error_code_ = ERROR_PRIME_IIR_FAILED; this->mark_failed(); @@ -286,10 +292,10 @@ void BMP581Component::update() { // 1) Request a measurement // ////////////////////////////// - ESP_LOGVV(TAG, "Requesting a measurement from sensor"); + ESP_LOGVV(TAG, "Requesting measurement"); if (!this->start_measurement_()) { - ESP_LOGW(TAG, "Failed to request forced measurement of sensor"); + ESP_LOGW(TAG, "Requesting forced measurement failed"); this->status_set_warning(); return; @@ -299,7 +305,7 @@ void BMP581Component::update() { // 2) Wait for measurement to finish (based on oversampling rates) // ////////////////////////////////////////////////////////////////////// - ESP_LOGVV(TAG, "Measurement is expected to take %d ms to complete", this->conversion_time_); + ESP_LOGVV(TAG, "Measurement should take %d ms", this->conversion_time_); this->set_timeout("measurement", this->conversion_time_, [this]() { float temperature = 0.0; @@ -311,14 +317,14 @@ void BMP581Component::update() { if (this->pressure_sensor_) { if (!this->read_temperature_and_pressure_(temperature, pressure)) { - ESP_LOGW(TAG, "Failed to read temperature and pressure measurements, skipping update"); + ESP_LOGW(TAG, "Failed to read temperature and pressure; skipping update"); this->status_set_warning(); return; } } else { if (!this->read_temperature_(temperature)) { - ESP_LOGW(TAG, "Failed to read temperature measurement, skipping update"); + ESP_LOGW(TAG, "Failed to read temperature; skipping update"); this->status_set_warning(); return; @@ -349,7 +355,7 @@ bool BMP581Component::check_data_readiness_() { // - returns data readiness state if (this->odr_config_.bit.pwr_mode == STANDBY_MODE) { - ESP_LOGD(TAG, "Data is not ready, sensor is in standby mode"); + ESP_LOGD(TAG, "Data not ready, sensor is in standby mode"); return false; } @@ -443,7 +449,7 @@ bool BMP581Component::read_temperature_(float &temperature) { // - the measured temperature (in degrees Celsius) if (!this->check_data_readiness_()) { - ESP_LOGW(TAG, "Data from sensor isn't ready, skipping this update"); + ESP_LOGW(TAG, "Data not ready, skipping this update"); this->status_set_warning(); return false; @@ -451,7 +457,7 @@ bool BMP581Component::read_temperature_(float &temperature) { uint8_t data[3]; if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 3)) { - ESP_LOGW(TAG, "Failed to read sensor's measurement data"); + ESP_LOGW(TAG, "Failed to read measurement"); this->status_set_warning(); return false; @@ -472,7 +478,7 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float & // - the measured pressure (in Pa) if (!this->check_data_readiness_()) { - ESP_LOGW(TAG, "Data from sensor isn't ready, skipping this update"); + ESP_LOGW(TAG, "Data not ready, skipping this update"); this->status_set_warning(); return false; @@ -480,7 +486,7 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float & uint8_t data[6]; if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 6)) { - ESP_LOGW(TAG, "Failed to read sensor's measurement data"); + ESP_LOGW(TAG, "Failed to read measurement"); this->status_set_warning(); return false; diff --git a/esphome/components/bp1658cj/bp1658cj.cpp b/esphome/components/bp1658cj/bp1658cj.cpp index 3a679bd79b..b502a738cd 100644 --- a/esphome/components/bp1658cj/bp1658cj.cpp +++ b/esphome/components/bp1658cj/bp1658cj.cpp @@ -26,8 +26,10 @@ void BP1658CJ::dump_config() { ESP_LOGCONFIG(TAG, "BP1658CJ:"); LOG_PIN(" Data Pin: ", this->data_pin_); LOG_PIN(" Clock Pin: ", this->clock_pin_); - ESP_LOGCONFIG(TAG, " Color Channels Max Power: %u", this->max_power_color_channels_); - ESP_LOGCONFIG(TAG, " White Channels Max Power: %u", this->max_power_white_channels_); + ESP_LOGCONFIG(TAG, + " Color Channels Max Power: %u\n" + " White Channels Max Power: %u", + this->max_power_color_channels_, this->max_power_white_channels_); } void BP1658CJ::loop() { diff --git a/esphome/components/button/__init__.py b/esphome/components/button/__init__.py index b68334dd98..892bf62f3a 100644 --- a/esphome/components/button/__init__.py +++ b/esphome/components/button/__init__.py @@ -108,6 +108,7 @@ async def register_button(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_button(var)) + CORE.register_platform_component("button", var) await setup_button_core_(var, config) diff --git a/esphome/components/canbus/__init__.py b/esphome/components/canbus/__init__.py index 6867177795..cdb57fd481 100644 --- a/esphome/components/canbus/__init__.py +++ b/esphome/components/canbus/__init__.py @@ -1,4 +1,5 @@ import re + from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv diff --git a/esphome/components/cap1188/cap1188.cpp b/esphome/components/cap1188/cap1188.cpp index 2fdcca94c1..af167deb99 100644 --- a/esphome/components/cap1188/cap1188.cpp +++ b/esphome/components/cap1188/cap1188.cpp @@ -52,9 +52,11 @@ void CAP1188Component::dump_config() { ESP_LOGCONFIG(TAG, "CAP1188:"); LOG_I2C_DEVICE(this); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Product ID: 0x%x", this->cap1188_product_id_); - ESP_LOGCONFIG(TAG, " Manufacture ID: 0x%x", this->cap1188_manufacture_id_); - ESP_LOGCONFIG(TAG, " Revision ID: 0x%x", this->cap1188_revision_); + ESP_LOGCONFIG(TAG, + " Product ID: 0x%x\n" + " Manufacture ID: 0x%x\n" + " Revision ID: 0x%x", + this->cap1188_product_id_, this->cap1188_manufacture_id_, this->cap1188_revision_); switch (this->error_code_) { case COMMUNICATION_FAILED: diff --git a/esphome/components/captive_portal/__init__.py b/esphome/components/captive_portal/__init__.py index ea11e733ac..a55887948d 100644 --- a/esphome/components/captive_portal/__init__.py +++ b/esphome/components/captive_portal/__init__.py @@ -41,6 +41,7 @@ async def to_code(config): if CORE.using_arduino: if CORE.is_esp32: + cg.add_library("ESP32 Async UDP", None) cg.add_library("DNSServer", None) cg.add_library("WiFi", None) if CORE.is_esp8266: diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index 31e6c51f0f..51e5cfc8ff 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -37,7 +37,12 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) { request->redirect("/?save"); } -void CaptivePortal::setup() {} +void CaptivePortal::setup() { +#ifndef USE_ARDUINO + // No DNS server needed for non-Arduino frameworks + this->disable_loop(); +#endif +} void CaptivePortal::start() { this->base_->init(); if (!this->initialized_) { @@ -50,6 +55,8 @@ void CaptivePortal::start() { this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError); network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); this->dns_server_->start(53, "*", ip); + // Re-enable loop() when DNS server is started + this->enable_loop(); #endif this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) { @@ -68,7 +75,11 @@ void CaptivePortal::start() { void CaptivePortal::handleRequest(AsyncWebServerRequest *req) { if (req->url() == "/") { +#ifndef USE_ESP8266 + auto *response = req->beginResponse(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ)); +#else auto *response = req->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ)); +#endif response->addHeader("Content-Encoding", "gzip"); req->send(response); return; diff --git a/esphome/components/captive_portal/captive_portal.h b/esphome/components/captive_portal/captive_portal.h index 24d1295e6a..c78fff824a 100644 --- a/esphome/components/captive_portal/captive_portal.h +++ b/esphome/components/captive_portal/captive_portal.h @@ -21,8 +21,11 @@ class CaptivePortal : public AsyncWebHandler, public Component { void dump_config() override; #ifdef USE_ARDUINO void loop() override { - if (this->dns_server_ != nullptr) + if (this->dns_server_ != nullptr) { this->dns_server_->processNextRequest(); + } else { + this->disable_loop(); + } } #endif float get_setup_priority() const override; @@ -37,7 +40,7 @@ class CaptivePortal : public AsyncWebHandler, public Component { #endif } - bool canHandle(AsyncWebServerRequest *request) override { + bool canHandle(AsyncWebServerRequest *request) const override { if (!this->active_) return false; diff --git a/esphome/components/ccs811/ccs811.cpp b/esphome/components/ccs811/ccs811.cpp index 700e1b4df0..cecb92b3df 100644 --- a/esphome/components/ccs811/ccs811.cpp +++ b/esphome/components/ccs811/ccs811.cpp @@ -1,6 +1,7 @@ #include "ccs811.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ccs811 { diff --git a/esphome/components/chsc6x/chsc6x_touchscreen.cpp b/esphome/components/chsc6x/chsc6x_touchscreen.cpp index 7755b1d229..524fa1eb36 100644 --- a/esphome/components/chsc6x/chsc6x_touchscreen.cpp +++ b/esphome/components/chsc6x/chsc6x_touchscreen.cpp @@ -38,9 +38,11 @@ void CHSC6XTouchscreen::dump_config() { ESP_LOGCONFIG(TAG, "CHSC6X Touchscreen:"); LOG_I2C_DEVICE(this); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); - ESP_LOGCONFIG(TAG, " Touch timeout: %d", this->touch_timeout_); - ESP_LOGCONFIG(TAG, " x_raw_max_: %d", this->x_raw_max_); - ESP_LOGCONFIG(TAG, " y_raw_max_: %d", this->y_raw_max_); + ESP_LOGCONFIG(TAG, + " Touch timeout: %d\n" + " x_raw_max_: %d\n" + " y_raw_max_: %d", + this->touch_timeout_, this->x_raw_max_, this->y_raw_max_); } } // namespace chsc6x diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index 7007dc13af..52938a17d0 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -443,6 +443,7 @@ async def register_climate(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_climate(var)) + CORE.register_platform_component("climate", var) await setup_climate_core_(var, config) diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index bc8d932089..edebc0de69 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -569,17 +569,22 @@ bool Climate::set_custom_preset_(const std::string &preset) { void Climate::dump_traits_(const char *tag) { auto traits = this->get_traits(); ESP_LOGCONFIG(tag, "ClimateTraits:"); - ESP_LOGCONFIG(tag, " [x] Visual settings:"); - ESP_LOGCONFIG(tag, " - Min temperature: %.1f", traits.get_visual_min_temperature()); - ESP_LOGCONFIG(tag, " - Max temperature: %.1f", traits.get_visual_max_temperature()); - ESP_LOGCONFIG(tag, " - Temperature step:"); - ESP_LOGCONFIG(tag, " Target: %.1f", traits.get_visual_target_temperature_step()); + ESP_LOGCONFIG(tag, + " [x] Visual settings:\n" + " - Min temperature: %.1f\n" + " - Max temperature: %.1f\n" + " - Temperature step:\n" + " Target: %.1f", + traits.get_visual_min_temperature(), traits.get_visual_max_temperature(), + traits.get_visual_target_temperature_step()); if (traits.get_supports_current_temperature()) { ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step()); } if (traits.get_supports_target_humidity() || traits.get_supports_current_humidity()) { - ESP_LOGCONFIG(tag, " - Min humidity: %.0f", traits.get_visual_min_humidity()); - ESP_LOGCONFIG(tag, " - Max humidity: %.0f", traits.get_visual_max_humidity()); + ESP_LOGCONFIG(tag, + " - Min humidity: %.0f\n" + " - Max humidity: %.0f", + traits.get_visual_min_humidity(), traits.get_visual_max_humidity()); } if (traits.get_supports_two_point_target_temperature()) { ESP_LOGCONFIG(tag, " [x] Supports two-point target temperature"); diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index d81702fb0c..b31a2eedf6 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -3,8 +3,8 @@ #include "esphome/core/component.h" #include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" -#include "esphome/core/preferences.h" #include "esphome/core/log.h" +#include "esphome/core/preferences.h" #include "climate_mode.h" #include "climate_traits.h" diff --git a/esphome/components/climate_ir/climate_ir.cpp b/esphome/components/climate_ir/climate_ir.cpp index 8175383627..dc8117f6ae 100644 --- a/esphome/components/climate_ir/climate_ir.cpp +++ b/esphome/components/climate_ir/climate_ir.cpp @@ -75,10 +75,13 @@ void ClimateIR::control(const climate::ClimateCall &call) { } void ClimateIR::dump_config() { LOG_CLIMATE("", "IR Climate", this); - ESP_LOGCONFIG(TAG, " Min. Temperature: %.1f°C", this->minimum_temperature_); - ESP_LOGCONFIG(TAG, " Max. Temperature: %.1f°C", this->maximum_temperature_); - ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_)); - ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_)); + ESP_LOGCONFIG(TAG, + " Min. Temperature: %.1f°C\n" + " Max. Temperature: %.1f°C\n" + " Supports HEAT: %s\n" + " Supports COOL: %s", + this->minimum_temperature_, this->maximum_temperature_, YESNO(this->supports_heat_), + YESNO(this->supports_cool_)); } } // namespace climate_ir diff --git a/esphome/components/cm1106/cm1106.cpp b/esphome/components/cm1106/cm1106.cpp index aa19c0664e..109524c04a 100644 --- a/esphome/components/cm1106/cm1106.cpp +++ b/esphome/components/cm1106/cm1106.cpp @@ -38,7 +38,7 @@ void CM1106Component::update() { } if (response[0] != 0x16 || response[1] != 0x05 || response[2] != 0x01) { - ESP_LOGW(TAG, "Got wrong UART response from CM1106: %02X %02X %02X %02X...", response[0], response[1], response[2], + ESP_LOGW(TAG, "Got wrong UART response from CM1106: %02X %02X %02X %02X", response[0], response[1], response[2], response[3]); this->status_set_warning(); return; diff --git a/esphome/components/cm1106/sensor.py b/esphome/components/cm1106/sensor.py index 1b8ac14fbe..1d95bcc666 100644 --- a/esphome/components/cm1106/sensor.py +++ b/esphome/components/cm1106/sensor.py @@ -1,10 +1,10 @@ """CM1106 Sensor component for ESPHome.""" -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation from esphome.automation import maybe_simple_id +import esphome.codegen as cg from esphome.components import sensor, uart +import esphome.config_validation as cv from esphome.const import ( CONF_CO2, CONF_ID, diff --git a/esphome/components/const/__init__.py b/esphome/components/const/__init__.py index 6af357f23b..66a5fe5d81 100644 --- a/esphome/components/const/__init__.py +++ b/esphome/components/const/__init__.py @@ -3,3 +3,5 @@ CODEOWNERS = ["@esphome/core"] CONF_DRAW_ROUNDING = "draw_rounding" +CONF_ON_STATE_CHANGE = "on_state_change" +CONF_REQUEST_HEADERS = "request_headers" diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index 13f117c3f0..9fe7593eab 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -189,6 +189,7 @@ async def register_cover(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_cover(var)) + CORE.register_platform_component("cover", var) await setup_cover_core_(var, config) diff --git a/esphome/components/cs5460a/cs5460a.cpp b/esphome/components/cs5460a/cs5460a.cpp index 4e045c17be..e3a5941d94 100644 --- a/esphome/components/cs5460a/cs5460a.cpp +++ b/esphome/components/cs5460a/cs5460a.cpp @@ -319,18 +319,23 @@ bool CS5460AComponent::check_status_() { void CS5460AComponent::dump_config() { uint32_t state = this->get_component_state(); - ESP_LOGCONFIG(TAG, "CS5460A:"); - ESP_LOGCONFIG(TAG, " Init status: %s", + ESP_LOGCONFIG(TAG, + "CS5460A:\n" + " Init status: %s", state == COMPONENT_STATE_LOOP ? "OK" : (state == COMPONENT_STATE_FAILED ? "failed" : "other")); LOG_PIN(" CS Pin: ", cs_); - ESP_LOGCONFIG(TAG, " Samples / cycle: %" PRIu32, samples_); - ESP_LOGCONFIG(TAG, " Phase offset: %i", phase_offset_); - ESP_LOGCONFIG(TAG, " PGA Gain: %s", pga_gain_ == CS5460A_PGA_GAIN_50X ? "50x" : "10x"); - ESP_LOGCONFIG(TAG, " Current gain: %.5f", current_gain_); - ESP_LOGCONFIG(TAG, " Voltage gain: %.5f", voltage_gain_); - ESP_LOGCONFIG(TAG, " Current HPF: %s", current_hpf_ ? "enabled" : "disabled"); - ESP_LOGCONFIG(TAG, " Voltage HPF: %s", voltage_hpf_ ? "enabled" : "disabled"); - ESP_LOGCONFIG(TAG, " Pulse energy: %.2f Wh", pulse_energy_wh_); + ESP_LOGCONFIG(TAG, + " Samples / cycle: %" PRIu32 "\n" + " Phase offset: %i\n" + " PGA Gain: %s\n" + " Current gain: %.5f\n" + " Voltage gain: %.5f\n" + " Current HPF: %s\n" + " Voltage HPF: %s\n" + " Pulse energy: %.2f Wh", + samples_, phase_offset_, pga_gain_ == CS5460A_PGA_GAIN_50X ? "50x" : "10x", current_gain_, + voltage_gain_, current_hpf_ ? "enabled" : "disabled", voltage_hpf_ ? "enabled" : "disabled", + pulse_energy_wh_); LOG_SENSOR(" ", "Voltage", voltage_sensor_); LOG_SENSOR(" ", "Current", current_sensor_); LOG_SENSOR(" ", "Power", power_sensor_); diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index b0876778a3..fe81ae91fe 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -223,11 +223,6 @@ void CSE7766Component::parse_data_() { #endif } -uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) { - return (uint32_t(this->raw_data_[start_index]) << 16) | (uint32_t(this->raw_data_[start_index + 1]) << 8) | - uint32_t(this->raw_data_[start_index + 2]); -} - void CSE7766Component::dump_config() { ESP_LOGCONFIG(TAG, "CSE7766:"); LOG_SENSOR(" ", "Voltage", this->voltage_sensor_); diff --git a/esphome/components/cse7766/cse7766.h b/esphome/components/cse7766/cse7766.h index 5d89b3b75b..8902eafe3c 100644 --- a/esphome/components/cse7766/cse7766.h +++ b/esphome/components/cse7766/cse7766.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/uart/uart.h" @@ -28,7 +29,10 @@ class CSE7766Component : public Component, public uart::UARTDevice { protected: bool check_byte_(); void parse_data_(); - uint32_t get_24_bit_uint_(uint8_t start_index); + uint32_t get_24_bit_uint_(uint8_t start_index) const { + return encode_uint24(this->raw_data_[start_index], this->raw_data_[start_index + 1], + this->raw_data_[start_index + 2]); + } uint8_t raw_data_[24]; uint8_t raw_data_index_{0}; diff --git a/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp b/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp index e76e40dba7..0c5099d4f0 100644 --- a/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +++ b/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp @@ -1,4 +1,5 @@ #include "cst816_touchscreen.h" +#include "esphome/core/helpers.h" namespace esphome { namespace cst816 { @@ -74,8 +75,10 @@ void CST816Touchscreen::dump_config() { LOG_I2C_DEVICE(this); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " X Raw Min: %d, X Raw Max: %d", this->x_raw_min_, this->x_raw_max_); - ESP_LOGCONFIG(TAG, " Y Raw Min: %d, Y Raw Max: %d", this->y_raw_min_, this->y_raw_max_); + ESP_LOGCONFIG(TAG, + " X Raw Min: %d, X Raw Max: %d\n" + " Y Raw Min: %d, Y Raw Max: %d", + this->x_raw_min_, this->x_raw_max_, this->y_raw_min_, this->y_raw_max_); const char *name; switch (this->chip_id_) { case CST820_CHIP_ID: diff --git a/esphome/components/current_based/current_based_cover.cpp b/esphome/components/current_based/current_based_cover.cpp index 8bb27dbeca..895b5515cb 100644 --- a/esphome/components/current_based/current_based_cover.cpp +++ b/esphome/components/current_based/current_based_cover.cpp @@ -151,8 +151,10 @@ void CurrentBasedCover::dump_config() { if (this->max_duration_ != UINT32_MAX) { ESP_LOGCONFIG(TAG, "Maximum duration: %.1fs", this->max_duration_ / 1e3f); } - ESP_LOGCONFIG(TAG, "Start sensing delay: %.1fs", this->start_sensing_delay_ / 1e3f); - ESP_LOGCONFIG(TAG, "Malfunction detection: %s", YESNO(this->malfunction_detection_)); + ESP_LOGCONFIG(TAG, + "Start sensing delay: %.1fs\n" + "Malfunction detection: %s", + this->start_sensing_delay_ / 1e3f, YESNO(this->malfunction_detection_)); } float CurrentBasedCover::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/dac7678/dac7678_output.cpp b/esphome/components/dac7678/dac7678_output.cpp index 31d153beb9..5c10bbc1bc 100644 --- a/esphome/components/dac7678/dac7678_output.cpp +++ b/esphome/components/dac7678/dac7678_output.cpp @@ -1,7 +1,7 @@ #include "dac7678_output.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace dac7678 { @@ -22,7 +22,7 @@ static const uint8_t DAC7678_REG_INTERNAL_REF_1 = 0x90; void DAC7678Output::setup() { ESP_LOGCONFIG(TAG, "Running setup"); - ESP_LOGV(TAG, "Resetting device..."); + ESP_LOGV(TAG, "Resetting device"); // Reset device if (!this->write_byte_16(DAC7678_REG_SOFTWARE_RESET, 0x0000)) { diff --git a/esphome/components/daly_bms/daly_bms.cpp b/esphome/components/daly_bms/daly_bms.cpp index 1dd0520465..2d270cc56e 100644 --- a/esphome/components/daly_bms/daly_bms.cpp +++ b/esphome/components/daly_bms/daly_bms.cpp @@ -1,7 +1,8 @@ #include "daly_bms.h" #include -#include "esphome/core/log.h" #include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace daly_bms { diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index 630bf6962c..24fbf5a1ec 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -158,7 +158,9 @@ async def setup_datetime_core_(var, config): async def register_datetime(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) - cg.add(getattr(cg.App, f"register_{config[CONF_TYPE].lower()}")(var)) + entity_type = config[CONF_TYPE].lower() + cg.add(getattr(cg.App, f"register_{entity_type}")(var)) + CORE.register_platform_component(entity_type, var) await setup_datetime_core_(var, config) cg.add_define(f"USE_DATETIME_{config[CONF_TYPE]}") diff --git a/esphome/components/datetime/date_entity.cpp b/esphome/components/datetime/date_entity.cpp index b5bcef43af..c164a98b2e 100644 --- a/esphome/components/datetime/date_entity.cpp +++ b/esphome/components/datetime/date_entity.cpp @@ -11,25 +11,25 @@ static const char *const TAG = "datetime.date_entity"; void DateEntity::publish_state() { if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) { - this->has_state_ = false; + this->set_has_state(false); return; } if (this->year_ < 1970 || this->year_ > 3000) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Year must be between 1970 and 3000"); return; } if (this->month_ < 1 || this->month_ > 12) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Month must be between 1 and 12"); return; } if (this->day_ > days_in_month(this->month_, this->year_)) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_); return; } - this->has_state_ = true; + this->set_has_state(true); ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_); this->state_callback_.call(); } diff --git a/esphome/components/datetime/datetime_base.h b/esphome/components/datetime/datetime_base.h index dea34e6110..b7645f5539 100644 --- a/esphome/components/datetime/datetime_base.h +++ b/esphome/components/datetime/datetime_base.h @@ -13,9 +13,6 @@ namespace datetime { class DateTimeBase : public EntityBase { public: - /// Return whether this Datetime has gotten a full state yet. - bool has_state() const { return this->has_state_; } - virtual ESPTime state_as_esptime() const = 0; void add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); } @@ -31,8 +28,6 @@ class DateTimeBase : public EntityBase { #ifdef USE_TIME time::RealTimeClock *rtc_; #endif - - bool has_state_{false}; }; #ifdef USE_TIME diff --git a/esphome/components/datetime/datetime_entity.cpp b/esphome/components/datetime/datetime_entity.cpp index 3d92194efa..4e3b051eb3 100644 --- a/esphome/components/datetime/datetime_entity.cpp +++ b/esphome/components/datetime/datetime_entity.cpp @@ -11,40 +11,40 @@ static const char *const TAG = "datetime.datetime_entity"; void DateTimeEntity::publish_state() { if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) { - this->has_state_ = false; + this->set_has_state(false); return; } if (this->year_ < 1970 || this->year_ > 3000) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Year must be between 1970 and 3000"); return; } if (this->month_ < 1 || this->month_ > 12) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Month must be between 1 and 12"); return; } if (this->day_ > days_in_month(this->month_, this->year_)) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_); return; } if (this->hour_ > 23) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Hour must be between 0 and 23"); return; } if (this->minute_ > 59) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Minute must be between 0 and 59"); return; } if (this->second_ > 59) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Second must be between 0 and 59"); return; } - this->has_state_ = true; + this->set_has_state(true); ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_, this->month_, this->day_, this->hour_, this->minute_, this->second_); this->state_callback_.call(); diff --git a/esphome/components/datetime/time_entity.cpp b/esphome/components/datetime/time_entity.cpp index db0094ae01..9b05c2124f 100644 --- a/esphome/components/datetime/time_entity.cpp +++ b/esphome/components/datetime/time_entity.cpp @@ -11,21 +11,21 @@ static const char *const TAG = "datetime.time_entity"; void TimeEntity::publish_state() { if (this->hour_ > 23) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Hour must be between 0 and 23"); return; } if (this->minute_ > 59) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Minute must be between 0 and 59"); return; } if (this->second_ > 59) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Second must be between 0 and 59"); return; } - this->has_state_ = true; + this->set_has_state(true); ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_, this->second_); this->state_callback_.call(); diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index c4de42c7e9..ade0968e08 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -2,9 +2,9 @@ #include #include "esphome/core/application.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/core/version.h" #include #include @@ -15,10 +15,6 @@ namespace debug { static const char *const TAG = "debug"; void DebugComponent::dump_config() { -#ifndef ESPHOME_LOG_HAS_DEBUG - return; // Can't log below if debug logging is disabled -#endif - ESP_LOGCONFIG(TAG, "Debug component:"); #ifdef USE_TEXT_SENSOR LOG_TEXT_SENSOR(" ", "Device info", this->device_info_); diff --git a/esphome/components/debug/debug_component.h b/esphome/components/debug/debug_component.h index 50d26ad2ea..efd0dafab0 100644 --- a/esphome/components/debug/debug_component.h +++ b/esphome/components/debug/debug_component.h @@ -2,8 +2,8 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" -#include "esphome/core/macros.h" #include "esphome/core/helpers.h" +#include "esphome/core/macros.h" #ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index 999cb927b3..e48a4941b3 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -107,8 +107,10 @@ std::string DebugComponent::get_wakeup_cause_() { } void DebugComponent::log_partition_info_() { - ESP_LOGCONFIG(TAG, "Partition table:"); - ESP_LOGCONFIG(TAG, " %-12s %-4s %-8s %-10s %-10s", "Name", "Type", "Subtype", "Address", "Size"); + ESP_LOGCONFIG(TAG, + "Partition table:\n" + " %-12s %-4s %-8s %-10s %-10s", + "Name", "Type", "Subtype", "Address", "Size"); esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); while (it != NULL) { const esp_partition_t *partition = esp_partition_get(it); diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index b53dabc92f..84fc102b66 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -6,6 +6,8 @@ namespace esphome { namespace deep_sleep { static const char *const TAG = "deep_sleep"; +// 5 seconds for deep sleep to ensure clean disconnect from Home Assistant +static const uint32_t TEARDOWN_TIMEOUT_DEEP_SLEEP_MS = 5000; bool global_has_deep_sleep = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) @@ -62,6 +64,10 @@ void DeepSleepComponent::begin_sleep(bool manual) { ESP_LOGI(TAG, "Sleeping for %" PRId64 "us", *this->sleep_duration_); } App.run_safe_shutdown_hooks(); + // It's critical to teardown components cleanly for deep sleep to ensure + // Home Assistant sees a clean disconnect instead of marking the device unavailable + App.teardown_components(TEARDOWN_TIMEOUT_DEEP_SLEEP_MS); + App.run_powerdown_hooks(); this->deep_sleep_(); } diff --git a/esphome/components/deep_sleep/deep_sleep_esp32.cpp b/esphome/components/deep_sleep/deep_sleep_esp32.cpp index 3c5d3382ce..7965ab738a 100644 --- a/esphome/components/deep_sleep/deep_sleep_esp32.cpp +++ b/esphome/components/deep_sleep/deep_sleep_esp32.cpp @@ -46,10 +46,12 @@ void DeepSleepComponent::dump_config_platform_() { LOG_PIN(" Wakeup Pin: ", this->wakeup_pin_); } if (this->wakeup_cause_to_run_duration_.has_value()) { - ESP_LOGCONFIG(TAG, " Default Wakeup Run Duration: %" PRIu32 " ms", - this->wakeup_cause_to_run_duration_->default_cause); - ESP_LOGCONFIG(TAG, " Touch Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->touch_cause); - ESP_LOGCONFIG(TAG, " GPIO Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->gpio_cause); + ESP_LOGCONFIG(TAG, + " Default Wakeup Run Duration: %" PRIu32 " ms\n" + " Touch Wakeup Run Duration: %" PRIu32 " ms\n" + " GPIO Wakeup Run Duration: %" PRIu32 " ms", + this->wakeup_cause_to_run_duration_->default_cause, this->wakeup_cause_to_run_duration_->touch_cause, + this->wakeup_cause_to_run_duration_->gpio_cause); } } diff --git a/esphome/components/dfrobot_sen0395/commands.cpp b/esphome/components/dfrobot_sen0395/commands.cpp index 2c60fb3449..42074c80cf 100644 --- a/esphome/components/dfrobot_sen0395/commands.cpp +++ b/esphome/components/dfrobot_sen0395/commands.cpp @@ -21,7 +21,7 @@ uint8_t Command::execute(DfrobotSen0395Component *parent) { if (this->retries_left_ > 0) { this->retries_left_ -= 1; this->cmd_sent_ = false; - ESP_LOGD(TAG, "Retrying..."); + ESP_LOGD(TAG, "Retrying"); return 0; } else { this->parent_->find_prompt_(); @@ -33,7 +33,7 @@ uint8_t Command::execute(DfrobotSen0395Component *parent) { if (this->retries_left_ > 0) { this->retries_left_ -= 1; this->cmd_sent_ = false; - ESP_LOGD(TAG, "Retrying..."); + ESP_LOGD(TAG, "Retrying"); return 0; } else { this->parent_->find_prompt_(); @@ -51,7 +51,7 @@ uint8_t Command::execute(DfrobotSen0395Component *parent) { if (this->retries_left_ > 0) { this->retries_left_ -= 1; this->cmd_sent_ = false; - ESP_LOGD(TAG, "Retrying..."); + ESP_LOGD(TAG, "Retrying"); } else { return 1; // Command done } diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 27234ed4d1..7248ef624e 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -1,6 +1,6 @@ #include "dht.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace dht { diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index 43da08f4ac..68c1184721 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -182,9 +182,11 @@ using display_writer_t = std::function; #define LOG_DISPLAY(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, prefix type); \ - ESP_LOGCONFIG(TAG, "%s Rotations: %d °", prefix, (obj)->rotation_); \ - ESP_LOGCONFIG(TAG, "%s Dimensions: %dpx x %dpx", prefix, (obj)->get_width(), (obj)->get_height()); \ + ESP_LOGCONFIG(TAG, \ + prefix type "\n" \ + "%s Rotations: %d °\n" \ + "%s Dimensions: %dpx x %dpx", \ + prefix, (obj)->rotation_, prefix, (obj)->get_width(), (obj)->get_height()); \ } /// Turn the pixel OFF. diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index 3af1b63e01..0ecdccc38a 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -11,7 +11,7 @@ namespace display { static const char *const TAG = "display"; void DisplayBuffer::init_internal_(uint32_t buffer_length) { - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; this->buffer_ = allocator.allocate(buffer_length); if (this->buffer_ == nullptr) { ESP_LOGE(TAG, "Could not allocate buffer for display!"); diff --git a/esphome/components/dps310/dps310.cpp b/esphome/components/dps310/dps310.cpp index b1287b6f2d..a7fb7ecd5e 100644 --- a/esphome/components/dps310/dps310.cpp +++ b/esphome/components/dps310/dps310.cpp @@ -86,9 +86,11 @@ void DPS310Component::setup() { } void DPS310Component::dump_config() { - ESP_LOGCONFIG(TAG, "DPS310:"); - ESP_LOGCONFIG(TAG, " Product ID: %u", this->prod_rev_id_ & 0x0F); - ESP_LOGCONFIG(TAG, " Revision ID: %u", (this->prod_rev_id_ >> 4) & 0x0F); + ESP_LOGCONFIG(TAG, + "DPS310:\n" + " Product ID: %u\n" + " Revision ID: %u", + this->prod_rev_id_ & 0x0F, (this->prod_rev_id_ >> 4) & 0x0F); LOG_I2C_DEVICE(this); if (this->is_failed()) { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); diff --git a/esphome/components/dsmr/dsmr.cpp b/esphome/components/dsmr/dsmr.cpp index c0a2883d79..d99cf5e7a9 100644 --- a/esphome/components/dsmr/dsmr.cpp +++ b/esphome/components/dsmr/dsmr.cpp @@ -278,9 +278,11 @@ bool Dsmr::parse_telegram() { } void Dsmr::dump_config() { - ESP_LOGCONFIG(TAG, "DSMR:"); - ESP_LOGCONFIG(TAG, " Max telegram length: %d", this->max_telegram_len_); - ESP_LOGCONFIG(TAG, " Receive timeout: %.1fs", this->receive_timeout_ / 1e3f); + ESP_LOGCONFIG(TAG, + "DSMR:\n" + " Max telegram length: %d\n" + " Receive timeout: %.1fs", + this->max_telegram_len_, this->receive_timeout_ / 1e3f); if (this->request_pin_ != nullptr) { LOG_PIN(" Request Pin: ", this->request_pin_); } diff --git a/esphome/components/duty_cycle/duty_cycle_sensor.cpp b/esphome/components/duty_cycle/duty_cycle_sensor.cpp index 6c24fc7a8b..8939de0ee9 100644 --- a/esphome/components/duty_cycle/duty_cycle_sensor.cpp +++ b/esphome/components/duty_cycle/duty_cycle_sensor.cpp @@ -1,6 +1,6 @@ #include "duty_cycle_sensor.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace duty_cycle { diff --git a/esphome/components/duty_time/duty_time_sensor.cpp b/esphome/components/duty_time/duty_time_sensor.cpp index d4369c89c0..c7319f7c33 100644 --- a/esphome/components/duty_time/duty_time_sensor.cpp +++ b/esphome/components/duty_time/duty_time_sensor.cpp @@ -94,9 +94,11 @@ void DutyTimeSensor::publish_and_save_(const uint32_t sec, const uint32_t ms) { } void DutyTimeSensor::dump_config() { - ESP_LOGCONFIG(TAG, "Duty Time:"); - ESP_LOGCONFIG(TAG, " Update Interval: %" PRId32 "ms", this->get_update_interval()); - ESP_LOGCONFIG(TAG, " Restore: %s", ONOFF(this->restore_)); + ESP_LOGCONFIG(TAG, + "Duty Time:\n" + " Update Interval: %" PRId32 "ms\n" + " Restore: %s", + this->get_update_interval(), ONOFF(this->restore_)); LOG_SENSOR(" ", "Duty Time Sensor:", this); LOG_SENSOR(" ", "Last Duty Time Sensor:", this->last_duty_time_sensor_); } diff --git a/esphome/components/ee895/ee895.cpp b/esphome/components/ee895/ee895.cpp index 4ce29bcfab..bdaa3f3200 100644 --- a/esphome/components/ee895/ee895.cpp +++ b/esphome/components/ee895/ee895.cpp @@ -1,6 +1,6 @@ #include "ee895.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ee895 { diff --git a/esphome/components/emc2101/emc2101.cpp b/esphome/components/emc2101/emc2101.cpp index 6918c6d391..75d324c2bb 100644 --- a/esphome/components/emc2101/emc2101.cpp +++ b/esphome/components/emc2101/emc2101.cpp @@ -100,8 +100,10 @@ void Emc2101Component::dump_config() { if (this->dac_mode_) { ESP_LOGCONFIG(TAG, " DAC Conversion Rate: %X", this->dac_conversion_rate_); } else { - ESP_LOGCONFIG(TAG, " PWM Resolution: %02X", this->pwm_resolution_); - ESP_LOGCONFIG(TAG, " PWM Divider: %02X", this->pwm_divider_); + ESP_LOGCONFIG(TAG, + " PWM Resolution: %02X\n" + " PWM Divider: %02X", + this->pwm_resolution_, this->pwm_divider_); } ESP_LOGCONFIG(TAG, " Inverted: %s", YESNO(this->inverted_)); } diff --git a/esphome/components/es7210/es7210.cpp b/esphome/components/es7210/es7210.cpp index f494bf32f0..bcbaf3d270 100644 --- a/esphome/components/es7210/es7210.cpp +++ b/esphome/components/es7210/es7210.cpp @@ -25,9 +25,11 @@ static const size_t MCLK_DIV_FRE = 256; } void ES7210::dump_config() { - ESP_LOGCONFIG(TAG, "ES7210 audio ADC:"); - ESP_LOGCONFIG(TAG, " Bits Per Sample: %" PRIu8, this->bits_per_sample_); - ESP_LOGCONFIG(TAG, " Sample Rate: %" PRIu32, this->sample_rate_); + ESP_LOGCONFIG(TAG, + "ES7210 audio ADC:\n" + " Bits Per Sample: %" PRIu8 "\n" + " Sample Rate: %" PRIu32, + this->bits_per_sample_, this->sample_rate_); if (this->is_failed()) { ESP_LOGE(TAG, " Failed to initialize"); diff --git a/esphome/components/es8311/es8311.cpp b/esphome/components/es8311/es8311.cpp index 47bd82adb1..0e59ac12d5 100644 --- a/esphome/components/es8311/es8311.cpp +++ b/esphome/components/es8311/es8311.cpp @@ -52,11 +52,13 @@ void ES8311::setup() { } void ES8311::dump_config() { - ESP_LOGCONFIG(TAG, "ES8311 Audio Codec:"); - ESP_LOGCONFIG(TAG, " Use MCLK: %s", YESNO(this->use_mclk_)); - ESP_LOGCONFIG(TAG, " Use Microphone: %s", YESNO(this->use_mic_)); - ESP_LOGCONFIG(TAG, " DAC Bits per Sample: %" PRIu8, this->resolution_out_); - ESP_LOGCONFIG(TAG, " Sample Rate: %" PRIu32, this->sample_frequency_); + ESP_LOGCONFIG(TAG, + "ES8311 Audio Codec:\n" + " Use MCLK: %s\n" + " Use Microphone: %s\n" + " DAC Bits per Sample: %" PRIu8 "\n" + " Sample Rate: %" PRIu32, + YESNO(this->use_mclk_), YESNO(this->use_mic_), this->resolution_out_, this->sample_frequency_); if (this->is_failed()) { ESP_LOGCONFIG(TAG, " Failed to initialize!"); diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index b211015865..f179c315f9 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -71,12 +71,35 @@ from .const import ( # noqa from .gpio import esp32_pin_to_code # noqa _LOGGER = logging.getLogger(__name__) -CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["preferences"] +CODEOWNERS = ["@esphome/core"] IS_TARGET_PLATFORM = True -CONF_RELEASE = "release" +CONF_ASSERTION_LEVEL = "assertion_level" +CONF_COMPILER_OPTIMIZATION = "compiler_optimization" CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES = "enable_idf_experimental_features" +CONF_ENABLE_LWIP_ASSERT = "enable_lwip_assert" +CONF_RELEASE = "release" + +ASSERTION_LEVELS = { + "DISABLE": "CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE", + "ENABLE": "CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE", + "SILENT": "CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT", +} + +COMPILER_OPTIMIZATIONS = { + "DEBUG": "CONFIG_COMPILER_OPTIMIZATION_DEBUG", + "NONE": "CONFIG_COMPILER_OPTIMIZATION_NONE", + "PERF": "CONFIG_COMPILER_OPTIMIZATION_PERF", + "SIZE": "CONFIG_COMPILER_OPTIMIZATION_SIZE", +} + +ARDUINO_ALLOWED_VARIANTS = [ + VARIANT_ESP32, + VARIANT_ESP32C3, + VARIANT_ESP32S2, + VARIANT_ESP32S3, +] def get_cpu_frequencies(*frequencies): @@ -109,6 +132,8 @@ def set_core_data(config): choices = CPU_FREQUENCIES[variant] if "160MHZ" in choices: cpu_frequency = "160MHZ" + elif "360MHZ" in choices: + cpu_frequency = "360MHZ" else: cpu_frequency = choices[-1] config[CONF_CPU_FREQUENCY] = cpu_frequency @@ -127,12 +152,17 @@ def set_core_data(config): CORE.data[KEY_ESP32][KEY_COMPONENTS] = {} elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO: CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" + if variant not in ARDUINO_ALLOWED_VARIANTS: + raise cv.Invalid( + f"ESPHome does not support using the Arduino framework for the {variant}. Please use the ESP-IDF framework instead.", + path=[CONF_FRAMEWORK, CONF_TYPE], + ) CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse( config[CONF_FRAMEWORK][CONF_VERSION] ) CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD] - CORE.data[KEY_ESP32][KEY_VARIANT] = config[CONF_VARIANT] + CORE.data[KEY_ESP32][KEY_VARIANT] = variant CORE.data[KEY_ESP32][KEY_EXTRA_BUILD_FILES] = {} return config @@ -261,11 +291,8 @@ def add_extra_build_file(filename: str, path: str) -> bool: def _format_framework_arduino_version(ver: cv.Version) -> str: # format the given arduino (https://github.com/espressif/arduino-esp32/releases) version to - # a PIO platformio/framework-arduinoespressif32 value - # List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32 - if ver <= cv.Version(1, 0, 3): - return f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" - return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + # a PIO pioarduino/framework-arduinoespressif32 value + return f"pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/{str(ver)}/esp32-{str(ver)}.zip" def _format_framework_espidf_version( @@ -289,12 +316,10 @@ def _format_framework_espidf_version( # The default/recommended arduino framework version # - https://github.com/espressif/arduino-esp32/releases -# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32 -RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 0, 5) -# The platformio/espressif32 version to use for arduino frameworks -# - https://github.com/platformio/platform-espressif32/releases -# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 -ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0) +RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 1, 3) +# The platform-espressif32 version to use for arduino frameworks +# - https://github.com/pioarduino/platform-espressif32/releases +ARDUINO_PLATFORM_VERSION = cv.Version(53, 3, 13) # The default/recommended esp-idf framework version # - https://github.com/espressif/esp-idf/releases @@ -337,8 +362,8 @@ SUPPORTED_PIOARDUINO_ESP_IDF_5X = [ def _arduino_check_versions(value): value = value.copy() lookups = { - "dev": (cv.Version(2, 1, 0), "https://github.com/espressif/arduino-esp32.git"), - "latest": (cv.Version(2, 0, 9), None), + "dev": (cv.Version(3, 1, 3), "https://github.com/espressif/arduino-esp32.git"), + "latest": (cv.Version(3, 1, 3), None), "recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None), } @@ -360,6 +385,10 @@ def _arduino_check_versions(value): CONF_PLATFORM_VERSION, _parse_platform_version(str(ARDUINO_PLATFORM_VERSION)) ) + if value[CONF_SOURCE].startswith("http"): + # prefix is necessary or platformio will complain with a cryptic error + value[CONF_SOURCE] = f"framework-arduinoespressif32@{value[CONF_SOURCE]}" + if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: _LOGGER.warning( "The selected Arduino framework version is not the recommended one. " @@ -451,8 +480,8 @@ def _parse_platform_version(value): if ver.major >= 50: # a pioarduino version if "-" in value: # maybe a release candidate?...definitely not our default, just use it as-is... - return f"https://github.com/pioarduino/platform-espressif32.git#{value}" - return f"https://github.com/pioarduino/platform-espressif32.git#{ver.major}.{ver.minor:02d}.{ver.patch:02d}" + return f"https://github.com/pioarduino/platform-espressif32/releases/download/{value}/platform-espressif32.zip" + return f"https://github.com/pioarduino/platform-espressif32/releases/download/{ver.major}.{ver.minor:02d}.{ver.patch:02d}/platform-espressif32.zip" # if platform version is a valid version constraint, prefix the default package cv.platformio_version_constraint(value) return f"platformio/espressif32@{value}" @@ -542,6 +571,10 @@ ARDUINO_FRAMEWORK_SCHEMA = cv.All( ) CONF_SDKCONFIG_OPTIONS = "sdkconfig_options" +CONF_ENABLE_LWIP_DHCP_SERVER = "enable_lwip_dhcp_server" +CONF_ENABLE_LWIP_MDNS_QUERIES = "enable_lwip_mdns_queries" +CONF_ENABLE_LWIP_BRIDGE_INTERFACE = "enable_lwip_bridge_interface" + ESP_IDF_FRAMEWORK_SCHEMA = cv.All( cv.Schema( { @@ -554,11 +587,30 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All( }, cv.Optional(CONF_ADVANCED, default={}): cv.Schema( { + cv.Optional(CONF_ASSERTION_LEVEL): cv.one_of( + *ASSERTION_LEVELS, upper=True + ), + cv.Optional(CONF_COMPILER_OPTIMIZATION, default="SIZE"): cv.one_of( + *COMPILER_OPTIMIZATIONS, upper=True + ), + cv.Optional(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): cv.boolean, + cv.Optional(CONF_ENABLE_LWIP_ASSERT, default=True): cv.boolean, cv.Optional( CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False ): cv.boolean, cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC): cv.boolean, - cv.Optional(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): cv.boolean, + # DHCP server is needed for WiFi AP mode. When WiFi component is used, + # it will handle disabling DHCP server when AP is not configured. + # Default to false (disabled) when WiFi is not used. + cv.OnlyWithout( + CONF_ENABLE_LWIP_DHCP_SERVER, "wifi", default=False + ): cv.boolean, + cv.Optional( + CONF_ENABLE_LWIP_MDNS_QUERIES, default=False + ): cv.boolean, + cv.Optional( + CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False + ): cv.boolean, } ), cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list( @@ -579,6 +631,21 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All( ) +def _set_default_framework(config): + if CONF_FRAMEWORK not in config: + config = config.copy() + + variant = config[CONF_VARIANT] + if variant in ARDUINO_ALLOWED_VARIANTS: + config[CONF_FRAMEWORK] = ARDUINO_FRAMEWORK_SCHEMA({}) + config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ARDUINO + else: + config[CONF_FRAMEWORK] = ESP_IDF_FRAMEWORK_SCHEMA({}) + config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ESP_IDF + + return config + + FRAMEWORK_ESP_IDF = "esp-idf" FRAMEWORK_ARDUINO = "arduino" FRAMEWORK_SCHEMA = cv.typed_schema( @@ -588,7 +655,6 @@ FRAMEWORK_SCHEMA = cv.typed_schema( }, lower=True, space="-", - default_type=FRAMEWORK_ARDUINO, ) @@ -615,10 +681,11 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional(CONF_PARTITIONS): cv.file_, cv.Optional(CONF_VARIANT): cv.one_of(*VARIANTS, upper=True), - cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA, + cv.Optional(CONF_FRAMEWORK): FRAMEWORK_SCHEMA, } ), _detect_variant, + _set_default_framework, set_core_data, ) @@ -629,6 +696,7 @@ FINAL_VALIDATE_SCHEMA = cv.Schema(final_validate) async def to_code(config): cg.add_platformio_option("board", config[CONF_BOARD]) cg.add_platformio_option("board_upload.flash_size", config[CONF_FLASH_SIZE]) + cg.set_cpp_standard("gnu++17") cg.add_build_flag("-DUSE_ESP32") cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}") @@ -641,7 +709,7 @@ async def to_code(config): conf = config[CONF_FRAMEWORK] cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION]) - if CONF_ADVANCED in conf and conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]: + if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]: cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC") add_extra_script( @@ -672,8 +740,6 @@ async def to_code(config): add_idf_sdkconfig_option( "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME", "partitions.csv" ) - add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False) - add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_SIZE", True) # Increase freertos tick speed from 100Hz to 1kHz so that delay() resolution is 1ms add_idf_sdkconfig_option("CONFIG_FREERTOS_HZ", 1000) @@ -687,16 +753,41 @@ async def to_code(config): # Set default CPU frequency add_idf_sdkconfig_option(f"CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_{freq}", True) + # Apply LWIP optimization settings + advanced = conf[CONF_ADVANCED] + # DHCP server: only disable if explicitly set to false + # WiFi component handles its own optimization when AP mode is not used + if ( + CONF_ENABLE_LWIP_DHCP_SERVER in advanced + and not advanced[CONF_ENABLE_LWIP_DHCP_SERVER] + ): + add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False) + if not advanced.get(CONF_ENABLE_LWIP_MDNS_QUERIES, False): + add_idf_sdkconfig_option("CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES", False) + if not advanced.get(CONF_ENABLE_LWIP_BRIDGE_INTERFACE, False): + add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0) + cg.add_platformio_option("board_build.partitions", "partitions.csv") if CONF_PARTITIONS in config: add_extra_build_file( "partitions.csv", CORE.relative_config_path(config[CONF_PARTITIONS]) ) - for name, value in conf[CONF_SDKCONFIG_OPTIONS].items(): - add_idf_sdkconfig_option(name, RawSdkconfigValue(value)) + if assertion_level := advanced.get(CONF_ASSERTION_LEVEL): + for key, flag in ASSERTION_LEVELS.items(): + add_idf_sdkconfig_option(flag, assertion_level == key) - if conf[CONF_ADVANCED].get(CONF_IGNORE_EFUSE_MAC_CRC): + add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False) + compiler_optimization = advanced.get(CONF_COMPILER_OPTIMIZATION) + for key, flag in COMPILER_OPTIMIZATIONS.items(): + add_idf_sdkconfig_option(flag, compiler_optimization == key) + + add_idf_sdkconfig_option( + "CONFIG_LWIP_ESP_LWIP_ASSERT", + conf[CONF_ADVANCED][CONF_ENABLE_LWIP_ASSERT], + ) + + if advanced.get(CONF_IGNORE_EFUSE_MAC_CRC): add_idf_sdkconfig_option("CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR", True) if (framework_ver.major, framework_ver.minor) >= (4, 4): add_idf_sdkconfig_option( @@ -706,7 +797,7 @@ async def to_code(config): add_idf_sdkconfig_option( "CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE", False ) - if conf[CONF_ADVANCED].get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): + if advanced.get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): _LOGGER.warning( "Using experimental features in ESP-IDF may result in unexpected failures." ) @@ -719,6 +810,9 @@ async def to_code(config): ), ) + for name, value in conf[CONF_SDKCONFIG_OPTIONS].items(): + add_idf_sdkconfig_option(name, RawSdkconfigValue(value)) + for component in conf[CONF_COMPONENTS]: source = component[CONF_SOURCE] if source[CONF_TYPE] == TYPE_GIT: @@ -736,10 +830,7 @@ async def to_code(config): cg.add_platformio_option("framework", "arduino") cg.add_build_flag("-DUSE_ARDUINO") cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO") - cg.add_platformio_option( - "platform_packages", - [f"platformio/framework-arduinoespressif32@{conf[CONF_SOURCE]}"], - ) + cg.add_platformio_option("platform_packages", [conf[CONF_SOURCE]]) if CONF_PARTITIONS in config: cg.add_platformio_option("board_build.partitions", config[CONF_PARTITIONS]) diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index f90b8a4603..e53cdd90d3 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -1,8 +1,8 @@ #ifdef USE_ESP32 -#include "esphome/core/preferences.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/preferences.h" #include #include #include @@ -84,7 +84,7 @@ class ESP32Preferences : public ESPPreferences { if (err == 0) return; - ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS...", esp_err_to_name(err)); + ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS", esp_err_to_name(err)); nvs_flash_deinit(); nvs_flash_erase(); nvs_flash_init(); @@ -111,7 +111,7 @@ class ESP32Preferences : public ESPPreferences { if (s_pending_save.empty()) return true; - ESP_LOGD(TAG, "Saving %d preferences to flash...", s_pending_save.size()); + ESP_LOGV(TAG, "Saving %d items...", s_pending_save.size()); // goal try write all pending saves even if one fails int cached = 0, written = 0, failed = 0; esp_err_t last_err = ESP_OK; @@ -139,10 +139,10 @@ class ESP32Preferences : public ESPPreferences { } s_pending_save.erase(s_pending_save.begin() + i); } - ESP_LOGD(TAG, "Saving %d preferences to flash: %d cached, %d written, %d failed", cached + written + failed, cached, - written, failed); + ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written, + failed); if (failed > 0) { - ESP_LOGE(TAG, "Error saving %d preferences to flash. Last error=%s for key=%s", failed, esp_err_to_name(last_err), + ESP_LOGE(TAG, "Writing %d items failed. Last error=%s for key=%s", failed, esp_err_to_name(last_err), last_key.c_str()); } @@ -173,7 +173,7 @@ class ESP32Preferences : public ESPPreferences { } bool reset() override { - ESP_LOGD(TAG, "Cleaning up preferences in flash..."); + ESP_LOGD(TAG, "Erasing storage"); s_pending_save.clear(); nvs_flash_deinit(); diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 37b4900a03..93bb643596 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -1,3 +1,4 @@ +from enum import Enum import re from esphome import automation @@ -12,9 +13,110 @@ import esphome.final_validate as fv DEPENDENCIES = ["esp32"] CODEOWNERS = ["@jesserockz", "@Rapsssito"] + +class BTLoggers(Enum): + """Bluetooth logger categories available in ESP-IDF. + + Each logger controls debug output for a specific Bluetooth subsystem. + The value is the ESP-IDF sdkconfig option name for controlling the log level. + """ + + # Core Stack Layers + HCI = "CONFIG_BT_LOG_HCI_TRACE_LEVEL" + """Host Controller Interface - Low-level interface between host and controller""" + + BTM = "CONFIG_BT_LOG_BTM_TRACE_LEVEL" + """Bluetooth Manager - Core device control, connections, and security""" + + L2CAP = "CONFIG_BT_LOG_L2CAP_TRACE_LEVEL" + """Logical Link Control and Adaptation Protocol - Connection multiplexing""" + + RFCOMM = "CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL" + """Serial port emulation over Bluetooth (Classic only)""" + + SDP = "CONFIG_BT_LOG_SDP_TRACE_LEVEL" + """Service Discovery Protocol - Service discovery (Classic only)""" + + GAP = "CONFIG_BT_LOG_GAP_TRACE_LEVEL" + """Generic Access Profile - Device discovery and connections""" + + # Network Protocols + BNEP = "CONFIG_BT_LOG_BNEP_TRACE_LEVEL" + """Bluetooth Network Encapsulation Protocol - IP over Bluetooth""" + + PAN = "CONFIG_BT_LOG_PAN_TRACE_LEVEL" + """Personal Area Networking - Ethernet over Bluetooth""" + + # Audio/Video Profiles (Classic Bluetooth) + A2D = "CONFIG_BT_LOG_A2D_TRACE_LEVEL" + """Advanced Audio Distribution - A2DP audio streaming""" + + AVDT = "CONFIG_BT_LOG_AVDT_TRACE_LEVEL" + """Audio/Video Distribution Transport - A2DP transport protocol""" + + AVCT = "CONFIG_BT_LOG_AVCT_TRACE_LEVEL" + """Audio/Video Control Transport - AVRCP transport protocol""" + + AVRC = "CONFIG_BT_LOG_AVRC_TRACE_LEVEL" + """Audio/Video Remote Control - Media playback control""" + + # Security + SMP = "CONFIG_BT_LOG_SMP_TRACE_LEVEL" + """Security Manager Protocol - BLE pairing and encryption""" + + # Application Layer + BTIF = "CONFIG_BT_LOG_BTIF_TRACE_LEVEL" + """Bluetooth Interface - Application interface layer""" + + BTC = "CONFIG_BT_LOG_BTC_TRACE_LEVEL" + """Bluetooth Common - Task handling and coordination""" + + # BLE Specific + BLE_SCAN = "CONFIG_BT_LOG_BLE_SCAN_TRACE_LEVEL" + """BLE scanning operations""" + + GATT = "CONFIG_BT_LOG_GATT_TRACE_LEVEL" + """Generic Attribute Profile - BLE data exchange protocol""" + + # Other Profiles + MCA = "CONFIG_BT_LOG_MCA_TRACE_LEVEL" + """Multi-Channel Adaptation - Health device profile""" + + HID = "CONFIG_BT_LOG_HID_TRACE_LEVEL" + """Human Interface Device - Keyboards, mice, controllers""" + + APPL = "CONFIG_BT_LOG_APPL_TRACE_LEVEL" + """Application layer logging""" + + OSI = "CONFIG_BT_LOG_OSI_TRACE_LEVEL" + """OS abstraction layer - Threading, memory, timers""" + + BLUFI = "CONFIG_BT_LOG_BLUFI_TRACE_LEVEL" + """ESP32 WiFi provisioning over Bluetooth""" + + +# Set to track which loggers are needed by components +_required_loggers: set[BTLoggers] = set() + + +def register_bt_logger(*loggers: BTLoggers) -> None: + """Register Bluetooth logger categories that a component needs. + + Args: + *loggers: One or more BTLoggers enum members + """ + for logger in loggers: + if not isinstance(logger, BTLoggers): + raise TypeError( + f"Logger must be a BTLoggers enum member, got {type(logger)}" + ) + _required_loggers.add(logger) + + CONF_BLE_ID = "ble_id" CONF_IO_CAPABILITY = "io_capability" CONF_ADVERTISING_CYCLE_TIME = "advertising_cycle_time" +CONF_DISABLE_BT_LOGS = "disable_bt_logs" NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] @@ -62,6 +164,9 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional( CONF_ADVERTISING_CYCLE_TIME, default="10s" ): cv.positive_time_period_milliseconds, + cv.SplitDefault(CONF_DISABLE_BT_LOGS, esp32_idf=True): cv.All( + cv.only_with_esp_idf, cv.boolean + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -140,6 +245,16 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True) + # Register the core BLE loggers that are always needed + register_bt_logger(BTLoggers.GAP, BTLoggers.BTM, BTLoggers.HCI) + + # Apply logger settings if log disabling is enabled + if config.get(CONF_DISABLE_BT_LOGS, False): + # Disable all Bluetooth loggers that are not required + for logger in BTLoggers: + if logger not in _required_loggers: + add_idf_sdkconfig_option(f"{logger.value}_NONE", True) + cg.add_define("USE_ESP32_BLE") diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 9d9a264154..cf63ad34d7 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -1,6 +1,7 @@ #ifdef USE_ESP32 #include "ble.h" +#include "ble_event_pool.h" #include "esphome/core/application.h" #include "esphome/core/log.h" @@ -23,9 +24,6 @@ namespace esp32_ble { static const char *const TAG = "esp32_ble"; -static RAMAllocator EVENT_ALLOCATOR( // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - RAMAllocator::ALLOW_FAILURE | RAMAllocator::ALLOC_INTERNAL); - void ESP32BLE::setup() { global_ble = this; ESP_LOGCONFIG(TAG, "Running setup"); @@ -270,14 +268,14 @@ void ESP32BLE::loop() { case BLE_COMPONENT_STATE_DISABLED: return; case BLE_COMPONENT_STATE_DISABLE: { - ESP_LOGD(TAG, "Disabling BLE..."); + ESP_LOGD(TAG, "Disabling"); for (auto *ble_event_handler : this->ble_status_event_handlers_) { ble_event_handler->ble_before_disabled_event_handler(); } if (!ble_dismantle_()) { - ESP_LOGE(TAG, "BLE could not be dismantled"); + ESP_LOGE(TAG, "Could not be dismantled"); this->mark_failed(); return; } @@ -285,11 +283,11 @@ void ESP32BLE::loop() { return; } case BLE_COMPONENT_STATE_ENABLE: { - ESP_LOGD(TAG, "Enabling BLE..."); + ESP_LOGD(TAG, "Enabling"); this->state_ = BLE_COMPONENT_STATE_OFF; if (!ble_setup_()) { - ESP_LOGE(TAG, "BLE could not be set up"); + ESP_LOGE(TAG, "Could not be set up"); this->mark_failed(); return; } @@ -304,82 +302,191 @@ void ESP32BLE::loop() { BLEEvent *ble_event = this->ble_events_.pop(); while (ble_event != nullptr) { switch (ble_event->type_) { - case BLEEvent::GATTS: - this->real_gatts_event_handler_(ble_event->event_.gatts.gatts_event, ble_event->event_.gatts.gatts_if, - &ble_event->event_.gatts.gatts_param); + case BLEEvent::GATTS: { + esp_gatts_cb_event_t event = ble_event->event_.gatts.gatts_event; + esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if; + esp_ble_gatts_cb_param_t *param = ble_event->event_.gatts.gatts_param; + ESP_LOGV(TAG, "gatts_event [esp_gatt_if: %d] - %d", gatts_if, event); + for (auto *gatts_handler : this->gatts_event_handlers_) { + gatts_handler->gatts_event_handler(event, gatts_if, param); + } break; - case BLEEvent::GATTC: - this->real_gattc_event_handler_(ble_event->event_.gattc.gattc_event, ble_event->event_.gattc.gattc_if, - &ble_event->event_.gattc.gattc_param); + } + case BLEEvent::GATTC: { + esp_gattc_cb_event_t event = ble_event->event_.gattc.gattc_event; + esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if; + esp_ble_gattc_cb_param_t *param = ble_event->event_.gattc.gattc_param; + ESP_LOGV(TAG, "gattc_event [esp_gatt_if: %d] - %d", gattc_if, event); + for (auto *gattc_handler : this->gattc_event_handlers_) { + gattc_handler->gattc_event_handler(event, gattc_if, param); + } break; - case BLEEvent::GAP: - this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param); + } + case BLEEvent::GAP: { + esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event; + switch (gap_event) { + case ESP_GAP_BLE_SCAN_RESULT_EVT: + // Use the new scan event handler - no memcpy! + for (auto *scan_handler : this->gap_scan_event_handlers_) { + scan_handler->gap_scan_event_handler(ble_event->scan_result()); + } + break; + + // Scan complete events + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: + // All three scan complete events have the same structure with just status + // The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe + // This is verified at compile-time by static_assert checks in ble_event.h + // The struct already contains our copy of the status (copied in BLEEvent constructor) + ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); + for (auto *gap_handler : this->gap_event_handlers_) { + gap_handler->gap_event_handler( + gap_event, reinterpret_cast(&ble_event->event_.gap.scan_complete)); + } + break; + + // Advertising complete events + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: + // All advertising complete events have the same structure with just status + ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); + for (auto *gap_handler : this->gap_event_handlers_) { + gap_handler->gap_event_handler( + gap_event, reinterpret_cast(&ble_event->event_.gap.adv_complete)); + } + break; + + // RSSI complete event + case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: + ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); + for (auto *gap_handler : this->gap_event_handlers_) { + gap_handler->gap_event_handler( + gap_event, reinterpret_cast(&ble_event->event_.gap.read_rssi_complete)); + } + break; + + // Security events + case ESP_GAP_BLE_AUTH_CMPL_EVT: + case ESP_GAP_BLE_SEC_REQ_EVT: + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: + case ESP_GAP_BLE_PASSKEY_REQ_EVT: + case ESP_GAP_BLE_NC_REQ_EVT: + ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); + for (auto *gap_handler : this->gap_event_handlers_) { + gap_handler->gap_event_handler( + gap_event, reinterpret_cast(&ble_event->event_.gap.security)); + } + break; + + default: + // Unknown/unhandled event + ESP_LOGW(TAG, "Unhandled GAP event type in loop: %d", gap_event); + break; + } break; + } default: break; } - ble_event->~BLEEvent(); - EVENT_ALLOCATOR.deallocate(ble_event, 1); + // Return the event to the pool + this->ble_event_pool_.release(ble_event); ble_event = this->ble_events_.pop(); } if (this->advertising_ != nullptr) { this->advertising_->loop(); } + + // Log dropped events periodically + uint16_t dropped = this->ble_events_.get_and_reset_dropped_count(); + if (dropped > 0) { + ESP_LOGW(TAG, "Dropped %u BLE events due to buffer overflow", dropped); + } } -void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { - BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); - if (new_event == nullptr) { - // Memory too fragmented to allocate new event. Can only drop it until memory comes back +// Helper function to load new event data based on type +void load_ble_event(BLEEvent *event, esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) { + event->load_gap_event(e, p); +} + +void load_ble_event(BLEEvent *event, esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) { + event->load_gattc_event(e, i, p); +} + +void load_ble_event(BLEEvent *event, esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) { + event->load_gatts_event(e, i, p); +} + +template void enqueue_ble_event(Args... args) { + // Allocate an event from the pool + BLEEvent *event = global_ble->ble_event_pool_.allocate(); + if (event == nullptr) { + // No events available - queue is full or we're out of memory + global_ble->ble_events_.increment_dropped_count(); return; } - new (new_event) BLEEvent(event, param); - global_ble->ble_events_.push(new_event); -} // NOLINT(clang-analyzer-unix.Malloc) -void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { - ESP_LOGV(TAG, "(BLE) gap_event_handler - %d", event); - for (auto *gap_handler : this->gap_event_handlers_) { - gap_handler->gap_event_handler(event, param); + // Load new event data (replaces previous event) + load_ble_event(event, args...); + + // Push the event to the queue + global_ble->ble_events_.push(event); + // Push always succeeds because we're the only producer and the pool ensures we never exceed queue size +} + +// Explicit template instantiations for the friend function +template void enqueue_ble_event(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *); +template void enqueue_ble_event(esp_gatts_cb_event_t, esp_gatt_if_t, esp_ble_gatts_cb_param_t *); +template void enqueue_ble_event(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t *); + +void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { + switch (event) { + // Queue GAP events that components need to handle + // Scanning events - used by esp32_ble_tracker + case ESP_GAP_BLE_SCAN_RESULT_EVT: + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: + // Advertising events - used by esp32_ble_beacon and esp32_ble server + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: + // Connection events - used by ble_client + case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: + // Security events - used by ble_client and bluetooth_proxy + case ESP_GAP_BLE_AUTH_CMPL_EVT: + case ESP_GAP_BLE_SEC_REQ_EVT: + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: + case ESP_GAP_BLE_PASSKEY_REQ_EVT: + case ESP_GAP_BLE_NC_REQ_EVT: + enqueue_ble_event(event, param); + return; + + // Ignore these GAP events as they are not relevant for our use case + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: + case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT: + return; + + default: + break; } + ESP_LOGW(TAG, "Ignoring unexpected GAP event type: %d", event); } void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { - BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); - if (new_event == nullptr) { - // Memory too fragmented to allocate new event. Can only drop it until memory comes back - return; - } - new (new_event) BLEEvent(event, gatts_if, param); - global_ble->ble_events_.push(new_event); -} // NOLINT(clang-analyzer-unix.Malloc) - -void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { - ESP_LOGV(TAG, "(BLE) gatts_event [esp_gatt_if: %d] - %d", gatts_if, event); - for (auto *gatts_handler : this->gatts_event_handlers_) { - gatts_handler->gatts_event_handler(event, gatts_if, param); - } + enqueue_ble_event(event, gatts_if, param); } void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { - BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); - if (new_event == nullptr) { - // Memory too fragmented to allocate new event. Can only drop it until memory comes back - return; - } - new (new_event) BLEEvent(event, gattc_if, param); - global_ble->ble_events_.push(new_event); -} // NOLINT(clang-analyzer-unix.Malloc) - -void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) { - ESP_LOGV(TAG, "(BLE) gattc_event [esp_gatt_if: %d] - %d", gattc_if, event); - for (auto *gattc_handler : this->gattc_event_handlers_) { - gattc_handler->gattc_event_handler(event, gattc_if, param); - } + enqueue_ble_event(event, gattc_if, param); } float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; } @@ -408,10 +515,12 @@ void ESP32BLE::dump_config() { io_capability_s = "invalid"; break; } - ESP_LOGCONFIG(TAG, "ESP32 BLE:"); - ESP_LOGCONFIG(TAG, " MAC address: %02X:%02X:%02X:%02X:%02X:%02X", mac_address[0], mac_address[1], mac_address[2], - mac_address[3], mac_address[4], mac_address[5]); - ESP_LOGCONFIG(TAG, " IO Capability: %s", io_capability_s); + ESP_LOGCONFIG(TAG, + "ESP32 BLE:\n" + " MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n" + " IO Capability: %s", + mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5], + io_capability_s); } else { ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled"); } diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 13ec3b6dd9..9fe996086e 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -2,6 +2,7 @@ #include "ble_advertising.h" #include "ble_uuid.h" +#include "ble_scan_result.h" #include @@ -11,6 +12,7 @@ #include "esphome/core/helpers.h" #include "ble_event.h" +#include "ble_event_pool.h" #include "queue.h" #ifdef USE_ESP32 @@ -22,6 +24,16 @@ namespace esphome { namespace esp32_ble { +// Maximum number of BLE scan results to buffer +#ifdef USE_PSRAM +static constexpr uint8_t SCAN_RESULT_BUFFER_SIZE = 32; +#else +static constexpr uint8_t SCAN_RESULT_BUFFER_SIZE = 20; +#endif + +// Maximum size of the BLE event queue - must be power of 2 for lock-free queue +static constexpr size_t MAX_BLE_QUEUE_SIZE = 64; + uint64_t ble_addr_to_uint64(const esp_bd_addr_t address); // NOLINTNEXTLINE(modernize-use-using) @@ -57,6 +69,11 @@ class GAPEventHandler { virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0; }; +class GAPScanEventHandler { + public: + virtual void gap_scan_event_handler(const BLEScanResult &scan_result) = 0; +}; + class GATTcEventHandler { public: virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, @@ -101,6 +118,9 @@ class ESP32BLE : public Component { void advertising_register_raw_advertisement_callback(std::function &&callback); void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); } + void register_gap_scan_event_handler(GAPScanEventHandler *handler) { + this->gap_scan_event_handlers_.push_back(handler); + } void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); } void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); } void register_ble_status_event_handler(BLEStatusEventHandler *handler) { @@ -113,22 +133,23 @@ class ESP32BLE : public Component { static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); - void real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); - void real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); - void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); - bool ble_setup_(); bool ble_dismantle_(); bool ble_pre_setup_(); void advertising_init_(); + private: + template friend void enqueue_ble_event(Args... args); + std::vector gap_event_handlers_; + std::vector gap_scan_event_handlers_; std::vector gattc_event_handlers_; std::vector gatts_event_handlers_; std::vector ble_status_event_handlers_; BLEComponentState state_{BLE_COMPONENT_STATE_OFF}; - Queue ble_events_; + LockFreeQueue ble_events_; + BLEEventPool ble_event_pool_; BLEAdvertising *advertising_{}; esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; uint32_t advertising_cycle_time_{}; diff --git a/esphome/components/esp32_ble/ble_event.h b/esphome/components/esp32_ble/ble_event.h index 1cf63b2fab..dd3ec3da42 100644 --- a/esphome/components/esp32_ble/ble_event.h +++ b/esphome/components/esp32_ble/ble_event.h @@ -2,92 +2,399 @@ #ifdef USE_ESP32 +#include // for offsetof #include #include #include #include +#include "ble_scan_result.h" + namespace esphome { namespace esp32_ble { + +// Compile-time verification that ESP-IDF scan complete events only contain a status field +// This ensures our reinterpret_cast in ble.cpp is safe +static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param) == sizeof(esp_bt_status_t), + "ESP-IDF scan_param_cmpl structure has unexpected size"); +static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param) == sizeof(esp_bt_status_t), + "ESP-IDF scan_start_cmpl structure has unexpected size"); +static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param) == sizeof(esp_bt_status_t), + "ESP-IDF scan_stop_cmpl structure has unexpected size"); + +// Verify the status field is at offset 0 (first member) +static_assert(offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl.status) == 0, + "status must be first member of scan_param_cmpl"); +static_assert(offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl.status) == 0, + "status must be first member of scan_start_cmpl"); +static_assert(offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl.status) == 0, + "status must be first member of scan_stop_cmpl"); + +// Compile-time verification for advertising complete events +static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_data_cmpl_evt_param) == sizeof(esp_bt_status_t), + "ESP-IDF adv_data_cmpl structure has unexpected size"); +static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_rsp_data_cmpl_evt_param) == sizeof(esp_bt_status_t), + "ESP-IDF scan_rsp_data_cmpl structure has unexpected size"); +static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_data_raw_cmpl_evt_param) == sizeof(esp_bt_status_t), + "ESP-IDF adv_data_raw_cmpl structure has unexpected size"); +static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_start_cmpl_evt_param) == sizeof(esp_bt_status_t), + "ESP-IDF adv_start_cmpl structure has unexpected size"); +static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_stop_cmpl_evt_param) == sizeof(esp_bt_status_t), + "ESP-IDF adv_stop_cmpl structure has unexpected size"); + +// Verify the status field is at offset 0 for advertising events +static_assert(offsetof(esp_ble_gap_cb_param_t, adv_data_cmpl.status) == 0, + "status must be first member of adv_data_cmpl"); +static_assert(offsetof(esp_ble_gap_cb_param_t, scan_rsp_data_cmpl.status) == 0, + "status must be first member of scan_rsp_data_cmpl"); +static_assert(offsetof(esp_ble_gap_cb_param_t, adv_data_raw_cmpl.status) == 0, + "status must be first member of adv_data_raw_cmpl"); +static_assert(offsetof(esp_ble_gap_cb_param_t, adv_start_cmpl.status) == 0, + "status must be first member of adv_start_cmpl"); +static_assert(offsetof(esp_ble_gap_cb_param_t, adv_stop_cmpl.status) == 0, + "status must be first member of adv_stop_cmpl"); + +// Compile-time verification for RSSI complete event structure +static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.status) == 0, + "status must be first member of read_rssi_cmpl"); +static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.rssi) == sizeof(esp_bt_status_t), + "rssi must immediately follow status in read_rssi_cmpl"); +static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.remote_addr) == sizeof(esp_bt_status_t) + sizeof(int8_t), + "remote_addr must follow rssi in read_rssi_cmpl"); + // Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop(). -// This class stores each event in a single type. +// This class stores each event with minimal memory usage. +// GAP events (99% of traffic) don't have the vector overhead. +// GATTC/GATTS events use heap allocation for their param and data. +// +// Event flow: +// 1. ESP-IDF BLE stack calls our static handlers in the BLE task context +// 2. The handlers create a BLEEvent instance, copying only the data we need +// 3. The event is pushed to a thread-safe queue +// 4. In the main loop(), events are popped from the queue and processed +// 5. The event destructor cleans up any external allocations +// +// Thread safety: +// - GAP events: We copy only the fields we need directly into the union +// - GATTC/GATTS events: We heap-allocate and copy the entire param struct, ensuring +// the data remains valid even after the BLE callback returns. The original +// param pointer from ESP-IDF is only valid during the callback. +// +// CRITICAL DESIGN NOTE: +// The heap allocations for GATTC/GATTS events are REQUIRED for memory safety. +// DO NOT attempt to optimize by removing these allocations or storing pointers +// to the original ESP-IDF data. The ESP-IDF callback data has a different lifetime +// than our event processing, and accessing it after the callback returns would +// result in use-after-free bugs and crashes. class BLEEvent { public: - BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) { - this->event_.gap.gap_event = e; - memcpy(&this->event_.gap.gap_param, p, sizeof(esp_ble_gap_cb_param_t)); - this->type_ = GAP; - }; - - BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) { - this->event_.gattc.gattc_event = e; - this->event_.gattc.gattc_if = i; - memcpy(&this->event_.gattc.gattc_param, p, sizeof(esp_ble_gattc_cb_param_t)); - // Need to also make a copy of relevant event data. - switch (e) { - case ESP_GATTC_NOTIFY_EVT: - this->data.assign(p->notify.value, p->notify.value + p->notify.value_len); - this->event_.gattc.gattc_param.notify.value = this->data.data(); - break; - case ESP_GATTC_READ_CHAR_EVT: - case ESP_GATTC_READ_DESCR_EVT: - this->data.assign(p->read.value, p->read.value + p->read.value_len); - this->event_.gattc.gattc_param.read.value = this->data.data(); - break; - default: - break; - } - this->type_ = GATTC; - }; - - BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) { - this->event_.gatts.gatts_event = e; - this->event_.gatts.gatts_if = i; - memcpy(&this->event_.gatts.gatts_param, p, sizeof(esp_ble_gatts_cb_param_t)); - // Need to also make a copy of relevant event data. - switch (e) { - case ESP_GATTS_WRITE_EVT: - this->data.assign(p->write.value, p->write.value + p->write.len); - this->event_.gatts.gatts_param.write.value = this->data.data(); - break; - default: - break; - } - this->type_ = GATTS; - }; - - union { - // NOLINTNEXTLINE(readability-identifier-naming) - struct gap_event { - esp_gap_ble_cb_event_t gap_event; - esp_ble_gap_cb_param_t gap_param; - } gap; - - // NOLINTNEXTLINE(readability-identifier-naming) - struct gattc_event { - esp_gattc_cb_event_t gattc_event; - esp_gatt_if_t gattc_if; - esp_ble_gattc_cb_param_t gattc_param; - } gattc; - - // NOLINTNEXTLINE(readability-identifier-naming) - struct gatts_event { - esp_gatts_cb_event_t gatts_event; - esp_gatt_if_t gatts_if; - esp_ble_gatts_cb_param_t gatts_param; - } gatts; - } event_; - - std::vector data{}; // NOLINTNEXTLINE(readability-identifier-naming) enum ble_event_t : uint8_t { GAP, GATTC, GATTS, - } type_; + }; + + // Type definitions for cleaner method signatures + struct StatusOnlyData { + esp_bt_status_t status; + }; + + struct RSSICompleteData { + esp_bt_status_t status; + int8_t rssi; + esp_bd_addr_t remote_addr; + }; + + // Constructor for GAP events - no external allocations needed + BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) { + this->type_ = GAP; + this->init_gap_data_(e, p); + } + + // Constructor for GATTC events - uses heap allocation + // IMPORTANT: The heap allocation is REQUIRED and must not be removed as an optimization. + // The param pointer from ESP-IDF is only valid during the callback execution. + // Since BLE events are processed asynchronously in the main loop, we must create + // our own copy to ensure the data remains valid until the event is processed. + BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) { + this->type_ = GATTC; + this->init_gattc_data_(e, i, p); + } + + // Constructor for GATTS events - uses heap allocation + // IMPORTANT: The heap allocation is REQUIRED and must not be removed as an optimization. + // The param pointer from ESP-IDF is only valid during the callback execution. + // Since BLE events are processed asynchronously in the main loop, we must create + // our own copy to ensure the data remains valid until the event is processed. + BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) { + this->type_ = GATTS; + this->init_gatts_data_(e, i, p); + } + + // Destructor to clean up heap allocations + ~BLEEvent() { this->cleanup_heap_data(); } + + // Default constructor for pre-allocation in pool + BLEEvent() : type_(GAP) {} + + // Clean up any heap-allocated data + void cleanup_heap_data() { + if (this->type_ == GAP) { + return; + } + if (this->type_ == GATTC) { + delete this->event_.gattc.gattc_param; + delete this->event_.gattc.data; + this->event_.gattc.gattc_param = nullptr; + this->event_.gattc.data = nullptr; + return; + } + if (this->type_ == GATTS) { + delete this->event_.gatts.gatts_param; + delete this->event_.gatts.data; + this->event_.gatts.gatts_param = nullptr; + this->event_.gatts.data = nullptr; + } + } + + // Load new event data for reuse (replaces previous event data) + void load_gap_event(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) { + this->cleanup_heap_data(); + this->type_ = GAP; + this->init_gap_data_(e, p); + } + + void load_gattc_event(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) { + this->cleanup_heap_data(); + this->type_ = GATTC; + this->init_gattc_data_(e, i, p); + } + + void load_gatts_event(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) { + this->cleanup_heap_data(); + this->type_ = GATTS; + this->init_gatts_data_(e, i, p); + } + + // Disable copy to prevent double-delete + BLEEvent(const BLEEvent &) = delete; + BLEEvent &operator=(const BLEEvent &) = delete; + + union { + // NOLINTNEXTLINE(readability-identifier-naming) + struct gap_event { + esp_gap_ble_cb_event_t gap_event; + union { + BLEScanResult scan_result; // 73 bytes - Used by: esp32_ble_tracker + // This matches ESP-IDF's scan complete event structures + // All three (scan_param_cmpl, scan_start_cmpl, scan_stop_cmpl) have identical layout + // Used by: esp32_ble_tracker + StatusOnlyData scan_complete; // 1 byte + // Advertising complete events all have same structure + // Used by: esp32_ble_beacon, esp32_ble server components + // ADV_DATA_SET, SCAN_RSP_DATA_SET, ADV_DATA_RAW_SET, ADV_START, ADV_STOP + StatusOnlyData adv_complete; // 1 byte + // RSSI complete event + // Used by: ble_client (ble_rssi_sensor component) + RSSICompleteData read_rssi_complete; // 8 bytes + // Security events - we store the full security union + // Used by: ble_client (automation), bluetooth_proxy, esp32_ble_client + esp_ble_sec_t security; // Variable size, but fits within scan_result size + }; + } gap; // 80 bytes total + + // NOLINTNEXTLINE(readability-identifier-naming) + struct gattc_event { + esp_gattc_cb_event_t gattc_event; + esp_gatt_if_t gattc_if; + esp_ble_gattc_cb_param_t *gattc_param; // Heap-allocated + std::vector *data; // Heap-allocated + } gattc; // 16 bytes (pointers only) + + // NOLINTNEXTLINE(readability-identifier-naming) + struct gatts_event { + esp_gatts_cb_event_t gatts_event; + esp_gatt_if_t gatts_if; + esp_ble_gatts_cb_param_t *gatts_param; // Heap-allocated + std::vector *data; // Heap-allocated + } gatts; // 16 bytes (pointers only) + } event_; // 80 bytes + + ble_event_t type_; + + // Helper methods to access event data + ble_event_t type() const { return type_; } + esp_gap_ble_cb_event_t gap_event_type() const { return event_.gap.gap_event; } + const BLEScanResult &scan_result() const { return event_.gap.scan_result; } + esp_bt_status_t scan_complete_status() const { return event_.gap.scan_complete.status; } + esp_bt_status_t adv_complete_status() const { return event_.gap.adv_complete.status; } + const RSSICompleteData &read_rssi_complete() const { return event_.gap.read_rssi_complete; } + const esp_ble_sec_t &security() const { return event_.gap.security; } + + private: + // Initialize GAP event data + void init_gap_data_(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) { + this->event_.gap.gap_event = e; + + if (p == nullptr) { + return; // Invalid event, but we can't log in header file + } + + // Copy data based on event type + switch (e) { + case ESP_GAP_BLE_SCAN_RESULT_EVT: + memcpy(this->event_.gap.scan_result.bda, p->scan_rst.bda, sizeof(esp_bd_addr_t)); + this->event_.gap.scan_result.ble_addr_type = p->scan_rst.ble_addr_type; + this->event_.gap.scan_result.rssi = p->scan_rst.rssi; + this->event_.gap.scan_result.adv_data_len = p->scan_rst.adv_data_len; + this->event_.gap.scan_result.scan_rsp_len = p->scan_rst.scan_rsp_len; + this->event_.gap.scan_result.search_evt = p->scan_rst.search_evt; + memcpy(this->event_.gap.scan_result.ble_adv, p->scan_rst.ble_adv, + ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX); + break; + + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: + this->event_.gap.scan_complete.status = p->scan_param_cmpl.status; + break; + + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + this->event_.gap.scan_complete.status = p->scan_start_cmpl.status; + break; + + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: + this->event_.gap.scan_complete.status = p->scan_stop_cmpl.status; + break; + + // Advertising complete events - all have same structure with just status + // Used by: esp32_ble_beacon, esp32_ble server components + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + this->event_.gap.adv_complete.status = p->adv_data_cmpl.status; + break; + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: + this->event_.gap.adv_complete.status = p->scan_rsp_data_cmpl.status; + break; + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: // Used by: esp32_ble_beacon + this->event_.gap.adv_complete.status = p->adv_data_raw_cmpl.status; + break; + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: // Used by: esp32_ble_beacon + this->event_.gap.adv_complete.status = p->adv_start_cmpl.status; + break; + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: // Used by: esp32_ble_beacon + this->event_.gap.adv_complete.status = p->adv_stop_cmpl.status; + break; + + // RSSI complete event + // Used by: ble_client (ble_rssi_sensor) + case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: + this->event_.gap.read_rssi_complete.status = p->read_rssi_cmpl.status; + this->event_.gap.read_rssi_complete.rssi = p->read_rssi_cmpl.rssi; + memcpy(this->event_.gap.read_rssi_complete.remote_addr, p->read_rssi_cmpl.remote_addr, sizeof(esp_bd_addr_t)); + break; + + // Security events - copy the entire security union + // Used by: ble_client, bluetooth_proxy, esp32_ble_client + case ESP_GAP_BLE_AUTH_CMPL_EVT: // Used by: bluetooth_proxy, esp32_ble_client + case ESP_GAP_BLE_SEC_REQ_EVT: // Used by: esp32_ble_client + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: // Used by: ble_client automation + case ESP_GAP_BLE_PASSKEY_REQ_EVT: // Used by: ble_client automation + case ESP_GAP_BLE_NC_REQ_EVT: // Used by: ble_client automation + memcpy(&this->event_.gap.security, &p->ble_security, sizeof(esp_ble_sec_t)); + break; + + default: + // We only store data for GAP events that components currently use + // Unknown events still get queued and logged in ble.cpp:375 as + // "Unhandled GAP event type in loop" - this helps identify new events + // that components might need in the future + break; + } + } + + // Initialize GATTC event data + void init_gattc_data_(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) { + this->event_.gattc.gattc_event = e; + this->event_.gattc.gattc_if = i; + + if (p == nullptr) { + this->event_.gattc.gattc_param = nullptr; + this->event_.gattc.data = nullptr; + return; // Invalid event, but we can't log in header file + } + + // Heap-allocate param and data + // Heap allocation is used because GATTC/GATTS events are rare (<1% of events) + // while GAP events (99%) are stored inline to minimize memory usage + // IMPORTANT: This heap allocation provides clear ownership semantics: + // - The BLEEvent owns the allocated memory for its lifetime + // - The data remains valid from the BLE callback context until processed in the main loop + // - Without this copy, we'd have use-after-free bugs as ESP-IDF reuses the callback memory + this->event_.gattc.gattc_param = new esp_ble_gattc_cb_param_t(*p); + + // Copy data for events that need it + // The param struct contains pointers (e.g., notify.value) that point to temporary buffers. + // We must copy this data to ensure it remains valid when the event is processed later. + switch (e) { + case ESP_GATTC_NOTIFY_EVT: + this->event_.gattc.data = new std::vector(p->notify.value, p->notify.value + p->notify.value_len); + this->event_.gattc.gattc_param->notify.value = this->event_.gattc.data->data(); + break; + case ESP_GATTC_READ_CHAR_EVT: + case ESP_GATTC_READ_DESCR_EVT: + this->event_.gattc.data = new std::vector(p->read.value, p->read.value + p->read.value_len); + this->event_.gattc.gattc_param->read.value = this->event_.gattc.data->data(); + break; + default: + this->event_.gattc.data = nullptr; + break; + } + } + + // Initialize GATTS event data + void init_gatts_data_(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) { + this->event_.gatts.gatts_event = e; + this->event_.gatts.gatts_if = i; + + if (p == nullptr) { + this->event_.gatts.gatts_param = nullptr; + this->event_.gatts.data = nullptr; + return; // Invalid event, but we can't log in header file + } + + // Heap-allocate param and data + // Heap allocation is used because GATTC/GATTS events are rare (<1% of events) + // while GAP events (99%) are stored inline to minimize memory usage + // IMPORTANT: This heap allocation provides clear ownership semantics: + // - The BLEEvent owns the allocated memory for its lifetime + // - The data remains valid from the BLE callback context until processed in the main loop + // - Without this copy, we'd have use-after-free bugs as ESP-IDF reuses the callback memory + this->event_.gatts.gatts_param = new esp_ble_gatts_cb_param_t(*p); + + // Copy data for events that need it + // The param struct contains pointers (e.g., write.value) that point to temporary buffers. + // We must copy this data to ensure it remains valid when the event is processed later. + switch (e) { + case ESP_GATTS_WRITE_EVT: + this->event_.gatts.data = new std::vector(p->write.value, p->write.value + p->write.len); + this->event_.gatts.gatts_param->write.value = this->event_.gatts.data->data(); + break; + default: + this->event_.gatts.data = nullptr; + break; + } + } }; +// Verify the gap_event struct hasn't grown beyond expected size +// The gap member in the union should be 80 bytes (including the gap_event enum) +static_assert(sizeof(decltype(((BLEEvent *) nullptr)->event_.gap)) <= 80, "gap_event struct has grown beyond 80 bytes"); + +// Verify esp_ble_sec_t fits within our union +static_assert(sizeof(esp_ble_sec_t) <= 73, "esp_ble_sec_t is larger than BLEScanResult"); + +// BLEEvent total size: 84 bytes (80 byte union + 1 byte type + 3 bytes padding) + } // namespace esp32_ble } // namespace esphome diff --git a/esphome/components/esp32_ble/ble_event_pool.h b/esphome/components/esp32_ble/ble_event_pool.h new file mode 100644 index 0000000000..ef123b1325 --- /dev/null +++ b/esphome/components/esp32_ble/ble_event_pool.h @@ -0,0 +1,72 @@ +#pragma once + +#ifdef USE_ESP32 + +#include +#include +#include "ble_event.h" +#include "queue.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace esp32_ble { + +// BLE Event Pool - On-demand pool of BLEEvent objects to avoid heap fragmentation +// Events are allocated on first use and reused thereafter, growing to peak usage +template class BLEEventPool { + public: + BLEEventPool() : total_created_(0) {} + + ~BLEEventPool() { + // Clean up any remaining events in the free list + BLEEvent *event; + while ((event = this->free_list_.pop()) != nullptr) { + delete event; + } + } + + // Allocate an event from the pool + // Returns nullptr if pool is full + BLEEvent *allocate() { + // Try to get from free list first + BLEEvent *event = this->free_list_.pop(); + if (event != nullptr) + return event; + + // Need to create a new event + if (this->total_created_ >= SIZE) { + // Pool is at capacity + return nullptr; + } + + // Use internal RAM for better performance + RAMAllocator allocator(RAMAllocator::ALLOC_INTERNAL); + event = allocator.allocate(1); + + if (event == nullptr) { + // Memory allocation failed + return nullptr; + } + + // Placement new to construct the object + new (event) BLEEvent(); + this->total_created_++; + return event; + } + + // Return an event to the pool for reuse + void release(BLEEvent *event) { + if (event != nullptr) { + this->free_list_.push(event); + } + } + + private: + LockFreeQueue free_list_; // Free events ready for reuse + uint8_t total_created_; // Total events created (high water mark) +}; + +} // namespace esp32_ble +} // namespace esphome + +#endif diff --git a/esphome/components/esp32_ble/ble_scan_result.h b/esphome/components/esp32_ble/ble_scan_result.h new file mode 100644 index 0000000000..49b0d5523d --- /dev/null +++ b/esphome/components/esp32_ble/ble_scan_result.h @@ -0,0 +1,24 @@ +#pragma once + +#ifdef USE_ESP32 + +#include + +namespace esphome { +namespace esp32_ble { + +// Structure for BLE scan results - only fields we actually use +struct __attribute__((packed)) BLEScanResult { + esp_bd_addr_t bda; + uint8_t ble_addr_type; + int8_t rssi; + uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX]; + uint8_t adv_data_len; + uint8_t scan_rsp_len; + uint8_t search_evt; +}; // ~73 bytes vs ~400 bytes for full esp_ble_gap_cb_param_t + +} // namespace esp32_ble +} // namespace esphome + +#endif diff --git a/esphome/components/esp32_ble/ble_uuid.h b/esphome/components/esp32_ble/ble_uuid.h index d90db3a599..06f84d4da7 100644 --- a/esphome/components/esp32_ble/ble_uuid.h +++ b/esphome/components/esp32_ble/ble_uuid.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #ifdef USE_ESP32 diff --git a/esphome/components/esp32_ble/queue.h b/esphome/components/esp32_ble/queue.h index c98477e121..75bf1eef25 100644 --- a/esphome/components/esp32_ble/queue.h +++ b/esphome/components/esp32_ble/queue.h @@ -2,52 +2,81 @@ #ifdef USE_ESP32 -#include -#include - -#include -#include +#include +#include /* * BLE events come in from a separate Task (thread) in the ESP32 stack. Rather - * than trying to deal with various locking strategies, all incoming GAP and GATT - * events will simply be placed on a semaphore guarded queue. The next time the - * component runs loop(), these events are popped off the queue and handed at - * this safer time. + * than using mutex-based locking, this lock-free queue allows the BLE + * task to enqueue events without blocking. The main loop() then processes + * these events at a safer time. + * + * This is a Single-Producer Single-Consumer (SPSC) lock-free ring buffer. + * The BLE task is the only producer, and the main loop() is the only consumer. */ namespace esphome { namespace esp32_ble { -template class Queue { +template class LockFreeQueue { public: - Queue() { m_ = xSemaphoreCreateMutex(); } + LockFreeQueue() : head_(0), tail_(0), dropped_count_(0) {} - void push(T *element) { + bool push(T *element) { if (element == nullptr) - return; - // It is not called from main loop. Thus it won't block main thread. - xSemaphoreTake(m_, portMAX_DELAY); - q_.push(element); - xSemaphoreGive(m_); + return false; + + uint8_t current_tail = tail_.load(std::memory_order_relaxed); + uint8_t next_tail = (current_tail + 1) % SIZE; + + if (next_tail == head_.load(std::memory_order_acquire)) { + // Buffer full + dropped_count_.fetch_add(1, std::memory_order_relaxed); + return false; + } + + buffer_[current_tail] = element; + tail_.store(next_tail, std::memory_order_release); + return true; } T *pop() { - T *element = nullptr; + uint8_t current_head = head_.load(std::memory_order_relaxed); - if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) { - if (!q_.empty()) { - element = q_.front(); - q_.pop(); - } - xSemaphoreGive(m_); + if (current_head == tail_.load(std::memory_order_acquire)) { + return nullptr; // Empty } + + T *element = buffer_[current_head]; + head_.store((current_head + 1) % SIZE, std::memory_order_release); return element; } + size_t size() const { + uint8_t tail = tail_.load(std::memory_order_acquire); + uint8_t head = head_.load(std::memory_order_acquire); + return (tail - head + SIZE) % SIZE; + } + + uint16_t get_and_reset_dropped_count() { return dropped_count_.exchange(0, std::memory_order_relaxed); } + + void increment_dropped_count() { dropped_count_.fetch_add(1, std::memory_order_relaxed); } + + bool empty() const { return head_.load(std::memory_order_acquire) == tail_.load(std::memory_order_acquire); } + + bool full() const { + uint8_t next_tail = (tail_.load(std::memory_order_relaxed) + 1) % SIZE; + return next_tail == head_.load(std::memory_order_acquire); + } + protected: - std::queue q_; - SemaphoreHandle_t m_; + T *buffer_[SIZE]; + // Atomic: written by producer (push/increment), read+reset by consumer (get_and_reset) + std::atomic dropped_count_; // 65535 max - more than enough for drop tracking + // Atomic: written by consumer (pop), read by producer (push) to check if full + std::atomic head_; + // Atomic: written by producer (push), read by consumer (pop) to check if empty + std::atomic tail_; }; } // namespace esp32_ble diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 2a1757524f..8ae1eb1bac 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -22,6 +22,16 @@ void BLEClientBase::setup() { this->connection_index_ = connection_index++; } +void BLEClientBase::set_state(espbt::ClientState st) { + ESP_LOGV(TAG, "[%d] [%s] Set state %d", this->connection_index_, this->address_str_.c_str(), (int) st); + ESPBTClient::set_state(st); + + if (st == espbt::ClientState::READY_TO_CONNECT) { + // Enable loop when we need to connect + this->enable_loop(); + } +} + void BLEClientBase::loop() { if (!esp32_ble::global_ble->is_active()) { this->set_state(espbt::ClientState::INIT); @@ -37,16 +47,23 @@ void BLEClientBase::loop() { } // READY_TO_CONNECT means we have discovered the device // and the scanner has been stopped by the tracker. - if (this->state_ == espbt::ClientState::READY_TO_CONNECT) { + else if (this->state_ == espbt::ClientState::READY_TO_CONNECT) { this->connect(); } + // If its idle, we can disable the loop as set_state + // will enable it again when we need to connect. + else if (this->state_ == espbt::ClientState::IDLE) { + this->disable_loop(); + } } float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } void BLEClientBase::dump_config() { - ESP_LOGCONFIG(TAG, " Address: %s", this->address_str().c_str()); - ESP_LOGCONFIG(TAG, " Auto-Connect: %s", TRUEFALSE(this->auto_connect_)); + ESP_LOGCONFIG(TAG, + " Address: %s\n" + " Auto-Connect: %s", + this->address_str().c_str(), TRUEFALSE(this->auto_connect_)); std::string state_name; switch (this->state()) { case espbt::ClientState::INIT: diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index 89ac04e38c..bf3b589b1b 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -93,22 +93,37 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { bool check_addr(esp_bd_addr_t &addr) { return memcmp(addr, this->remote_bda_, sizeof(esp_bd_addr_t)) == 0; } + void set_state(espbt::ClientState st) override; + protected: - int gattc_if_; - esp_bd_addr_t remote_bda_; - esp_ble_addr_type_t remote_addr_type_{BLE_ADDR_TYPE_PUBLIC}; - uint16_t conn_id_{UNSET_CONN_ID}; + // Memory optimized layout for 32-bit systems + // Group 1: 8-byte types uint64_t address_{0}; - bool auto_connect_{false}; + + // Group 2: Container types (grouped for memory optimization) std::string address_str_{}; - uint8_t connection_index_; - int16_t service_count_{0}; - uint16_t mtu_{23}; - bool paired_{false}; - espbt::ConnectionType connection_type_{espbt::ConnectionType::V1}; std::vector services_; + + // Group 3: 4-byte types + int gattc_if_; esp_gatt_status_t status_{ESP_GATT_OK}; + // Group 4: Arrays (6 bytes) + esp_bd_addr_t remote_bda_; + + // Group 5: 2-byte types + uint16_t conn_id_{UNSET_CONN_ID}; + uint16_t mtu_{23}; + + // Group 6: 1-byte types and small enums + esp_ble_addr_type_t remote_addr_type_{BLE_ADDR_TYPE_PUBLIC}; + espbt::ConnectionType connection_type_{espbt::ConnectionType::V1}; + uint8_t connection_index_; + uint8_t service_count_{0}; // ESP32 has max handles < 255, typical devices have < 50 services + bool auto_connect_{false}; + bool paired_{false}; + // 6 bytes used, 2 bytes padding + void log_event_(const char *name); }; diff --git a/esphome/components/esp32_ble_server/__init__.py b/esphome/components/esp32_ble_server/__init__.py index 0fcb5c9822..773445a1a7 100644 --- a/esphome/components/esp32_ble_server/__init__.py +++ b/esphome/components/esp32_ble_server/__init__.py @@ -4,7 +4,7 @@ from esphome import automation import esphome.codegen as cg from esphome.components import esp32_ble from esphome.components.esp32 import add_idf_sdkconfig_option -from esphome.components.esp32_ble import bt_uuid +from esphome.components.esp32_ble import BTLoggers, bt_uuid import esphome.config_validation as cv from esphome.config_validation import UNDEFINED from esphome.const import ( @@ -525,6 +525,9 @@ async def to_code_characteristic(service_var, char_conf): async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/esp32_ble_server/ble_characteristic.cpp b/esphome/components/esp32_ble_server/ble_characteristic.cpp index 15739d60bb..373d57436e 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_server/ble_characteristic.cpp @@ -2,6 +2,7 @@ #include "ble_server.h" #include "ble_service.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #ifdef USE_ESP32 diff --git a/esphome/components/esp32_ble_server/ble_server.h b/esphome/components/esp32_ble_server/ble_server.h index 43599438f3..531b52d6b9 100644 --- a/esphome/components/esp32_ble_server/ble_server.h +++ b/esphome/components/esp32_ble_server/ble_server.h @@ -43,7 +43,6 @@ class BLEServer : public Component, float get_setup_priority() const override; bool can_proceed() override; - void teardown(); bool is_running(); void set_manufacturer_data(const std::vector &data) { diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index b7eddeb0dd..2242d709a4 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -9,6 +9,7 @@ import esphome.codegen as cg from esphome.components import esp32_ble from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.components.esp32_ble import ( + BTLoggers, bt_uuid, bt_uuid16_format, bt_uuid32_format, @@ -259,11 +260,15 @@ ESP_BLE_DEVICE_SCHEMA = cv.Schema( async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.BLE_SCAN) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID]) cg.add(parent.register_gap_event_handler(var)) + cg.add(parent.register_gap_scan_event_handler(var)) cg.add(parent.register_gattc_event_handler(var)) cg.add(parent.register_ble_status_event_handler(var)) cg.add(var.set_parent(parent)) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 1a6071c9fe..4785c29230 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -50,17 +50,15 @@ void ESP32BLETracker::setup() { ESP_LOGE(TAG, "BLE Tracker was marked failed by ESP32BLE"); return; } - ExternalRAMAllocator allocator( - ExternalRAMAllocator::ALLOW_FAILURE); - this->scan_result_buffer_ = allocator.allocate(ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE); + RAMAllocator allocator; + this->scan_ring_buffer_ = allocator.allocate(SCAN_RESULT_BUFFER_SIZE); - if (this->scan_result_buffer_ == nullptr) { - ESP_LOGE(TAG, "Could not allocate buffer for BLE Tracker!"); + if (this->scan_ring_buffer_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate ring buffer for BLE Tracker!"); this->mark_failed(); } global_esp32_ble_tracker = this; - this->scan_result_lock_ = xSemaphoreCreateMutex(); #ifdef USE_OTA ota::get_global_ota_callback()->add_on_state_callback( @@ -120,27 +118,31 @@ void ESP32BLETracker::loop() { } bool promote_to_connecting = discovered && !searching && !connecting; - if (this->scanner_state_ == ScannerState::RUNNING && - this->scan_result_index_ && // if it looks like we have a scan result we will take the lock - xSemaphoreTake(this->scan_result_lock_, 0)) { - uint32_t index = this->scan_result_index_; - if (index >= ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) { - ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up."); - } + // Process scan results from lock-free SPSC ring buffer + // Consumer side: This runs in the main loop thread + if (this->scanner_state_ == ScannerState::RUNNING) { + // Load our own index with relaxed ordering (we're the only writer) + uint8_t read_idx = this->ring_read_index_.load(std::memory_order_relaxed); - if (this->raw_advertisements_) { - for (auto *listener : this->listeners_) { - listener->parse_devices(this->scan_result_buffer_, this->scan_result_index_); - } - for (auto *client : this->clients_) { - client->parse_devices(this->scan_result_buffer_, this->scan_result_index_); - } - } + // Load producer's index with acquire to see their latest writes + uint8_t write_idx = this->ring_write_index_.load(std::memory_order_acquire); - if (this->parse_advertisements_) { - for (size_t i = 0; i < index; i++) { + while (read_idx != write_idx) { + // Process one result at a time directly from ring buffer + BLEScanResult &scan_result = this->scan_ring_buffer_[read_idx]; + + if (this->raw_advertisements_) { + for (auto *listener : this->listeners_) { + listener->parse_devices(&scan_result, 1); + } + for (auto *client : this->clients_) { + client->parse_devices(&scan_result, 1); + } + } + + if (this->parse_advertisements_) { ESPBTDevice device; - device.parse_scan_rst(this->scan_result_buffer_[i]); + device.parse_scan_rst(scan_result); bool found = false; for (auto *listener : this->listeners_) { @@ -161,9 +163,19 @@ void ESP32BLETracker::loop() { this->print_bt_device_info(device); } } + + // Move to next entry in ring buffer + read_idx = (read_idx + 1) % SCAN_RESULT_BUFFER_SIZE; + + // Store with release to ensure reads complete before index update + this->ring_read_index_.store(read_idx, std::memory_order_release); + } + + // Log dropped results periodically + size_t dropped = this->scan_results_dropped_.exchange(0, std::memory_order_relaxed); + if (dropped > 0) { + ESP_LOGW(TAG, "Dropped %zu BLE scan results due to buffer overflow", dropped); } - this->scan_result_index_ = 0; - xSemaphoreGive(this->scan_result_lock_); } if (this->scanner_state_ == ScannerState::STOPPED) { this->end_of_scan_(); // Change state to IDLE @@ -172,7 +184,7 @@ void ESP32BLETracker::loop() { (this->scan_set_param_failed_ && this->scanner_state_ == ScannerState::RUNNING)) { this->stop_scan_(); if (this->scan_start_fail_count_ == std::numeric_limits::max()) { - ESP_LOGE(TAG, "ESP-IDF BLE scan could not restart after %d attempts, rebooting to restore BLE stack...", + ESP_LOGE(TAG, "Scan could not restart after %d attempts, rebooting to restore stack (IDF)", std::numeric_limits::max()); App.reboot(); } @@ -219,10 +231,10 @@ void ESP32BLETracker::loop() { for (auto *client : this->clients_) { if (client->state() == ClientState::DISCOVERED) { if (this->scanner_state_ == ScannerState::RUNNING) { - ESP_LOGD(TAG, "Stopping scan to make connection..."); + ESP_LOGD(TAG, "Stopping scan to make connection"); this->stop_scan_(); } else if (this->scanner_state_ == ScannerState::IDLE) { - ESP_LOGD(TAG, "Promoting client to connect..."); + ESP_LOGD(TAG, "Promoting client to connect"); // We only want to promote one client at a time. // once the scanner is fully stopped. #ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE @@ -306,7 +318,7 @@ void ESP32BLETracker::start_scan_(bool first) { // Start timeout before scan is started. Otherwise scan never starts if any error. this->set_timeout("scan", this->scan_duration_ * 2000, []() { - ESP_LOGE(TAG, "ESP-IDF BLE scan never terminated, rebooting to restore BLE stack..."); + ESP_LOGE(TAG, "Scan never terminated, rebooting to restore stack (IDF)"); App.reboot(); }); @@ -370,9 +382,6 @@ void ESP32BLETracker::recalculate_advertisement_parser_types() { void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { - case ESP_GAP_BLE_SCAN_RESULT_EVT: - this->gap_scan_result_(param->scan_rst); - break; case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: this->gap_scan_set_param_complete_(param->scan_param_cmpl); break; @@ -385,11 +394,57 @@ void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_ga default: break; } + // Forward all events to clients (scan results are handled separately via gap_scan_event_handler) for (auto *client : this->clients_) { client->gap_event_handler(event, param); } } +void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) { + ESP_LOGV(TAG, "gap_scan_result - event %d", scan_result.search_evt); + + if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) { + // Lock-free SPSC ring buffer write (Producer side) + // This runs in the ESP-IDF Bluetooth stack callback thread + // IMPORTANT: Only this thread writes to ring_write_index_ + + // Load our own index with relaxed ordering (we're the only writer) + uint8_t write_idx = this->ring_write_index_.load(std::memory_order_relaxed); + uint8_t next_write_idx = (write_idx + 1) % SCAN_RESULT_BUFFER_SIZE; + + // Load consumer's index with acquire to see their latest updates + uint8_t read_idx = this->ring_read_index_.load(std::memory_order_acquire); + + // Check if buffer is full + if (next_write_idx != read_idx) { + // Write to ring buffer + this->scan_ring_buffer_[write_idx] = scan_result; + + // Store with release to ensure the write is visible before index update + this->ring_write_index_.store(next_write_idx, std::memory_order_release); + } else { + // Buffer full, track dropped results + this->scan_results_dropped_.fetch_add(1, std::memory_order_relaxed); + } + } else if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) { + // Scan finished on its own + if (this->scanner_state_ != ScannerState::RUNNING) { + if (this->scanner_state_ == ScannerState::STOPPING) { + ESP_LOGE(TAG, "Scan was not running when scan completed."); + } else if (this->scanner_state_ == ScannerState::STARTING) { + ESP_LOGE(TAG, "Scan was not started when scan completed."); + } else if (this->scanner_state_ == ScannerState::FAILED) { + ESP_LOGE(TAG, "Scan was in failed state when scan completed."); + } else if (this->scanner_state_ == ScannerState::IDLE) { + ESP_LOGE(TAG, "Scan was idle when scan completed."); + } else if (this->scanner_state_ == ScannerState::STOPPED) { + ESP_LOGE(TAG, "Scan was stopped when scan completed."); + } + } + this->set_scanner_state_(ScannerState::STOPPED); + } +} + void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m) { ESP_LOGV(TAG, "gap_scan_set_param_complete - status %d", param.status); if (param.status == ESP_BT_STATUS_DONE) { @@ -444,34 +499,6 @@ void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_ this->set_scanner_state_(ScannerState::STOPPED); } -void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { - ESP_LOGV(TAG, "gap_scan_result - event %d", param.search_evt); - if (param.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) { - if (xSemaphoreTake(this->scan_result_lock_, 0)) { - if (this->scan_result_index_ < ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) { - this->scan_result_buffer_[this->scan_result_index_++] = param; - } - xSemaphoreGive(this->scan_result_lock_); - } - } else if (param.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) { - // Scan finished on its own - if (this->scanner_state_ != ScannerState::RUNNING) { - if (this->scanner_state_ == ScannerState::STOPPING) { - ESP_LOGE(TAG, "Scan was not running when scan completed."); - } else if (this->scanner_state_ == ScannerState::STARTING) { - ESP_LOGE(TAG, "Scan was not started when scan completed."); - } else if (this->scanner_state_ == ScannerState::FAILED) { - ESP_LOGE(TAG, "Scan was in failed state when scan completed."); - } else if (this->scanner_state_ == ScannerState::IDLE) { - ESP_LOGE(TAG, "Scan was idle when scan completed."); - } else if (this->scanner_state_ == ScannerState::STOPPED) { - ESP_LOGE(TAG, "Scan was stopped when scan completed."); - } - } - this->set_scanner_state_(ScannerState::STOPPED); - } -} - void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { for (auto *client : this->clients_) { @@ -494,13 +521,15 @@ optional ESPBLEiBeacon::from_manufacturer_data(const ServiceData return ESPBLEiBeacon(data.data.data()); } -void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { - this->scan_result_ = param; +void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) { for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++) - this->address_[i] = param.bda[i]; - this->address_type_ = param.ble_addr_type; - this->rssi_ = param.rssi; - this->parse_adv_(param); + this->address_[i] = scan_result.bda[i]; + this->address_type_ = static_cast(scan_result.ble_addr_type); + this->rssi_ = scan_result.rssi; + + // Parse advertisement data directly + uint8_t total_len = scan_result.adv_data_len + scan_result.scan_rsp_len; + this->parse_adv_(scan_result.ble_adv, total_len); #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE ESP_LOGVV(TAG, "Parse Result:"); @@ -558,13 +587,13 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str()); } - ESP_LOGVV(TAG, " Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str()); + ESP_LOGVV(TAG, " Adv data: %s", + format_hex_pretty(scan_result.ble_adv, scan_result.adv_data_len + scan_result.scan_rsp_len).c_str()); #endif } -void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { + +void ESPBTDevice::parse_adv_(const uint8_t *payload, uint8_t len) { size_t offset = 0; - const uint8_t *payload = param.ble_adv; - uint8_t len = param.adv_data_len + param.scan_rsp_len; while (offset + 2 < len) { const uint8_t field_length = payload[offset++]; // First byte is length of adv record @@ -731,11 +760,14 @@ uint64_t ESPBTDevice::address_uint64() const { return esp32_ble::ble_addr_to_uin void ESP32BLETracker::dump_config() { ESP_LOGCONFIG(TAG, "BLE Tracker:"); - ESP_LOGCONFIG(TAG, " Scan Duration: %" PRIu32 " s", this->scan_duration_); - ESP_LOGCONFIG(TAG, " Scan Interval: %.1f ms", this->scan_interval_ * 0.625f); - ESP_LOGCONFIG(TAG, " Scan Window: %.1f ms", this->scan_window_ * 0.625f); - ESP_LOGCONFIG(TAG, " Scan Type: %s", this->scan_active_ ? "ACTIVE" : "PASSIVE"); - ESP_LOGCONFIG(TAG, " Continuous Scanning: %s", YESNO(this->scan_continuous_)); + ESP_LOGCONFIG(TAG, + " Scan Duration: %" PRIu32 " s\n" + " Scan Interval: %.1f ms\n" + " Scan Window: %.1f ms\n" + " Scan Type: %s\n" + " Continuous Scanning: %s", + this->scan_duration_, this->scan_interval_ * 0.625f, this->scan_window_ * 0.625f, + this->scan_active_ ? "ACTIVE" : "PASSIVE", YESNO(this->scan_continuous_)); switch (this->scanner_state_) { case ScannerState::IDLE: ESP_LOGCONFIG(TAG, " Scanner State: IDLE"); diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index eea73a7d26..414c9f4b48 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -6,6 +6,7 @@ #include "esphome/core/helpers.h" #include +#include #include #include @@ -62,7 +63,7 @@ class ESPBLEiBeacon { class ESPBTDevice { public: - void parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); + void parse_scan_rst(const BLEScanResult &scan_result); std::string address_str() const; @@ -84,8 +85,6 @@ class ESPBTDevice { const std::vector &get_service_datas() const { return service_datas_; } - const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &get_scan_result() const { return scan_result_; } - bool resolve_irk(const uint8_t *irk) const; optional get_ibeacon() const { @@ -98,7 +97,7 @@ class ESPBTDevice { } protected: - void parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); + void parse_adv_(const uint8_t *payload, uint8_t len); esp_bd_addr_t address_{ 0, @@ -112,7 +111,6 @@ class ESPBTDevice { std::vector service_uuids_{}; std::vector manufacturer_datas_{}; std::vector service_datas_{}; - esp_ble_gap_cb_param_t::ble_scan_result_evt_param scan_result_{}; }; class ESP32BLETracker; @@ -121,9 +119,7 @@ class ESPBTDeviceListener { public: virtual void on_scan_end() {} virtual bool parse_device(const ESPBTDevice &device) = 0; - virtual bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) { - return false; - }; + virtual bool parse_devices(const BLEScanResult *scan_results, size_t count) { return false; }; virtual AdvertisementParserType get_advertisement_parser_type() { return AdvertisementParserType::PARSED_ADVERTISEMENTS; }; @@ -133,7 +129,7 @@ class ESPBTDeviceListener { ESP32BLETracker *parent_{nullptr}; }; -enum class ClientState { +enum class ClientState : uint8_t { // Connection is allocated INIT, // Client is disconnecting @@ -169,7 +165,7 @@ enum class ScannerState { STOPPED, }; -enum class ConnectionType { +enum class ConnectionType : uint8_t { // The default connection type, we hold all the services in ram // for the duration of the connection. V1, @@ -197,19 +193,24 @@ class ESPBTClient : public ESPBTDeviceListener { } } ClientState state() const { return state_; } - int app_id; + + // Memory optimized layout + uint8_t app_id; // App IDs are small integers assigned sequentially protected: + // Group 1: 1-byte types ClientState state_{ClientState::INIT}; // want_disconnect_ is set to true when a disconnect is requested // while the client is connecting. This is used to disconnect the // client as soon as we get the connection id (conn_id_) from the // ESP_GATTC_OPEN_EVT event. bool want_disconnect_{false}; + // 2 bytes used, 2 bytes padding }; class ESP32BLETracker : public Component, public GAPEventHandler, + public GAPScanEventHandler, public GATTcEventHandler, public BLEStatusEventHandler, public Parented { @@ -240,6 +241,7 @@ class ESP32BLETracker : public Component, void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override; void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; + void gap_scan_event_handler(const BLEScanResult &scan_result) override; void ble_before_disabled_event_handler() override; void add_scanner_state_callback(std::function &&callback) { @@ -264,7 +266,7 @@ class ESP32BLETracker : public Component, /// Called to set the scanner state. Will also call callbacks to let listeners know when state is changed. void set_scanner_state_(ScannerState state); - int app_id_{0}; + uint8_t app_id_{0}; /// Vector of addresses that have already been printed in print_bt_device_info std::vector already_discovered_; @@ -285,14 +287,16 @@ class ESP32BLETracker : public Component, bool ble_was_disabled_{true}; bool raw_advertisements_{false}; bool parse_advertisements_{false}; - SemaphoreHandle_t scan_result_lock_; - size_t scan_result_index_{0}; -#ifdef USE_PSRAM - const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32; -#else - const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 20; -#endif // USE_PSRAM - esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_; + + // Lock-free Single-Producer Single-Consumer (SPSC) ring buffer for scan results + // Producer: ESP-IDF Bluetooth stack callback (gap_scan_event_handler) + // Consumer: ESPHome main loop (loop() method) + // This design ensures zero blocking in the BT callback and prevents scan result loss + BLEScanResult *scan_ring_buffer_; + std::atomic ring_write_index_{0}; // Written only by BT callback (producer) + std::atomic ring_read_index_{0}; // Written only by main loop (consumer) + std::atomic scan_results_dropped_{0}; // Tracks buffer overflow events + esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS}; esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS}; int connecting_{0}; diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index b4038c1841..05522265ae 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -1,5 +1,6 @@ from esphome import automation, pins import esphome.codegen as cg +from esphome.components import i2c from esphome.components.esp32 import add_idf_component import esphome.config_validation as cv from esphome.const import ( @@ -7,6 +8,7 @@ from esphome.const import ( CONF_CONTRAST, CONF_DATA_PINS, CONF_FREQUENCY, + CONF_I2C_ID, CONF_ID, CONF_PIN, CONF_RESET_PIN, @@ -149,93 +151,104 @@ CONF_ON_IMAGE = "on_image" camera_range_param = cv.int_range(min=-2, max=2) -CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(ESP32Camera), - # pin assignment - cv.Required(CONF_DATA_PINS): cv.All( - [pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8) - ), - cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number, - cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number, - cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number, - cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema( - { - cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number, - cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All( - cv.frequency, cv.Range(min=8e6, max=20e6) - ), - } - ), - cv.Required(CONF_I2C_PINS): cv.Schema( - { - cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number, - cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number, - } - ), - cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, - cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number, - # image - cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum( - FRAME_SIZES, upper=True - ), - cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63), - cv.Optional(CONF_CONTRAST, default=0): camera_range_param, - cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param, - cv.Optional(CONF_SATURATION, default=0): camera_range_param, - cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean, - cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean, - cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum( - ENUM_SPECIAL_EFFECT, upper=True - ), - # exposure - cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum( - ENUM_GAIN_CONTROL_MODE, upper=True - ), - cv.Optional(CONF_AEC2, default=False): cv.boolean, - cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param, - cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200), - # gains - cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum( - ENUM_GAIN_CONTROL_MODE, upper=True - ), - cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30), - cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum( - ENUM_GAIN_CEILING, upper=True - ), - # white balance - cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(ENUM_WB_MODE, upper=True), - # test pattern - cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean, - # framerates - cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( - cv.framerate, cv.Range(min=0, min_included=False, max=60) - ), - cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All( - cv.framerate, cv.Range(min=0, max=1) - ), - cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2), - cv.Optional(CONF_ON_STREAM_START): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - ESP32CameraStreamStartTrigger - ), - } - ), - cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - ESP32CameraStreamStopTrigger - ), - } - ), - cv.Optional(CONF_ON_IMAGE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32CameraImageTrigger), - } - ), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.All( + cv.ENTITY_BASE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ESP32Camera), + # pin assignment + cv.Required(CONF_DATA_PINS): cv.All( + [pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8) + ), + cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema( + { + cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number, + cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All( + cv.frequency, cv.Range(min=8e6, max=20e6) + ), + } + ), + cv.Optional(CONF_I2C_PINS): cv.Schema( + { + cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number, + cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number, + } + ), + cv.Optional(CONF_I2C_ID): cv.Any( + cv.use_id(i2c.InternalI2CBus), + msg="I2C bus must be an internal ESP32 I2C bus", + ), + cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number, + # image + cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum( + FRAME_SIZES, upper=True + ), + cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63), + cv.Optional(CONF_CONTRAST, default=0): camera_range_param, + cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param, + cv.Optional(CONF_SATURATION, default=0): camera_range_param, + cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean, + cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean, + cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum( + ENUM_SPECIAL_EFFECT, upper=True + ), + # exposure + cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum( + ENUM_GAIN_CONTROL_MODE, upper=True + ), + cv.Optional(CONF_AEC2, default=False): cv.boolean, + cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param, + cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200), + # gains + cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum( + ENUM_GAIN_CONTROL_MODE, upper=True + ), + cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30), + cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum( + ENUM_GAIN_CEILING, upper=True + ), + # white balance + cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum( + ENUM_WB_MODE, upper=True + ), + # test pattern + cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean, + # framerates + cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( + cv.framerate, cv.Range(min=0, min_included=False, max=60) + ), + cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All( + cv.framerate, cv.Range(min=0, max=1) + ), + cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2), + cv.Optional(CONF_ON_STREAM_START): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ESP32CameraStreamStartTrigger + ), + } + ), + cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ESP32CameraStreamStopTrigger + ), + } + ), + cv.Optional(CONF_ON_IMAGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ESP32CameraImageTrigger + ), + } + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.has_exactly_one_key(CONF_I2C_PINS, CONF_I2C_ID), +) SETTERS = { # pin assignment @@ -280,8 +293,12 @@ async def to_code(config): extclk = config[CONF_EXTERNAL_CLOCK] cg.add(var.set_external_clock(extclk[CONF_PIN], extclk[CONF_FREQUENCY])) - i2c_pins = config[CONF_I2C_PINS] - cg.add(var.set_i2c_pins(i2c_pins[CONF_SDA], i2c_pins[CONF_SCL])) + if i2c_id := config.get(CONF_I2C_ID): + i2c_hub = await cg.get_variable(i2c_id) + cg.add(var.set_i2c_id(i2c_hub)) + else: + i2c_pins = config[CONF_I2C_PINS] + cg.add(var.set_i2c_pins(i2c_pins[CONF_SDA], i2c_pins[CONF_SCL])) cg.add(var.set_max_update_interval(1000 / config[CONF_MAX_FRAMERATE])) if config[CONF_IDLE_FRAMERATE] == 0: cg.add(var.set_idle_update_interval(0)) diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index cfcf7869d4..243d3d3e47 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -1,9 +1,9 @@ #ifdef USE_ESP32 #include "esp32_camera.h" -#include "esphome/core/log.h" -#include "esphome/core/hal.h" #include "esphome/core/application.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" #include @@ -16,6 +16,12 @@ static const char *const TAG = "esp32_camera"; void ESP32Camera::setup() { global_esp32_camera = this; +#ifdef USE_I2C + if (this->i2c_bus_ != nullptr) { + this->config_.sccb_i2c_port = this->i2c_bus_->get_port(); + } +#endif + /* initialize time to now */ this->last_update_ = millis(); @@ -46,17 +52,20 @@ void ESP32Camera::setup() { void ESP32Camera::dump_config() { auto conf = this->config_; - ESP_LOGCONFIG(TAG, "ESP32 Camera:"); - ESP_LOGCONFIG(TAG, " Name: %s", this->name_.c_str()); - ESP_LOGCONFIG(TAG, " Internal: %s", YESNO(this->internal_)); - ESP_LOGCONFIG(TAG, " Data Pins: D0:%d D1:%d D2:%d D3:%d D4:%d D5:%d D6:%d D7:%d", conf.pin_d0, conf.pin_d1, - conf.pin_d2, conf.pin_d3, conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7); - ESP_LOGCONFIG(TAG, " VSYNC Pin: %d", conf.pin_vsync); - ESP_LOGCONFIG(TAG, " HREF Pin: %d", conf.pin_href); - ESP_LOGCONFIG(TAG, " Pixel Clock Pin: %d", conf.pin_pclk); - ESP_LOGCONFIG(TAG, " External Clock: Pin:%d Frequency:%u", conf.pin_xclk, conf.xclk_freq_hz); - ESP_LOGCONFIG(TAG, " I2C Pins: SDA:%d SCL:%d", conf.pin_sccb_sda, conf.pin_sccb_scl); - ESP_LOGCONFIG(TAG, " Reset Pin: %d", conf.pin_reset); + ESP_LOGCONFIG(TAG, + "ESP32 Camera:\n" + " Name: %s\n" + " Internal: %s\n" + " Data Pins: D0:%d D1:%d D2:%d D3:%d D4:%d D5:%d D6:%d D7:%d\n" + " VSYNC Pin: %d\n" + " HREF Pin: %d\n" + " Pixel Clock Pin: %d\n" + " External Clock: Pin:%d Frequency:%u\n" + " I2C Pins: SDA:%d SCL:%d\n" + " Reset Pin: %d", + this->name_.c_str(), YESNO(this->is_internal()), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3, + conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7, conf.pin_vsync, conf.pin_href, conf.pin_pclk, + conf.pin_xclk, conf.xclk_freq_hz, conf.pin_sccb_sda, conf.pin_sccb_scl, conf.pin_reset); switch (this->config_.frame_size) { case FRAMESIZE_QQVGA: ESP_LOGCONFIG(TAG, " Resolution: 160x120 (QQVGA)"); @@ -123,24 +132,29 @@ void ESP32Camera::dump_config() { sensor_t *s = esp_camera_sensor_get(); auto st = s->status; - ESP_LOGCONFIG(TAG, " JPEG Quality: %u", st.quality); - ESP_LOGCONFIG(TAG, " Framebuffer Count: %u", conf.fb_count); - ESP_LOGCONFIG(TAG, " Contrast: %d", st.contrast); - ESP_LOGCONFIG(TAG, " Brightness: %d", st.brightness); - ESP_LOGCONFIG(TAG, " Saturation: %d", st.saturation); - ESP_LOGCONFIG(TAG, " Vertical Flip: %s", ONOFF(st.vflip)); - ESP_LOGCONFIG(TAG, " Horizontal Mirror: %s", ONOFF(st.hmirror)); - ESP_LOGCONFIG(TAG, " Special Effect: %u", st.special_effect); - ESP_LOGCONFIG(TAG, " White Balance Mode: %u", st.wb_mode); + ESP_LOGCONFIG(TAG, + " JPEG Quality: %u\n" + " Framebuffer Count: %u\n" + " Contrast: %d\n" + " Brightness: %d\n" + " Saturation: %d\n" + " Vertical Flip: %s\n" + " Horizontal Mirror: %s\n" + " Special Effect: %u\n" + " White Balance Mode: %u", + st.quality, conf.fb_count, st.contrast, st.brightness, st.saturation, ONOFF(st.vflip), + ONOFF(st.hmirror), st.special_effect, st.wb_mode); // ESP_LOGCONFIG(TAG, " Auto White Balance: %u", st.awb); // ESP_LOGCONFIG(TAG, " Auto White Balance Gain: %u", st.awb_gain); - ESP_LOGCONFIG(TAG, " Auto Exposure Control: %u", st.aec); - ESP_LOGCONFIG(TAG, " Auto Exposure Control 2: %u", st.aec2); - ESP_LOGCONFIG(TAG, " Auto Exposure Level: %d", st.ae_level); - ESP_LOGCONFIG(TAG, " Auto Exposure Value: %u", st.aec_value); - ESP_LOGCONFIG(TAG, " AGC: %u", st.agc); - ESP_LOGCONFIG(TAG, " AGC Gain: %u", st.agc_gain); - ESP_LOGCONFIG(TAG, " Gain Ceiling: %u", st.gainceiling); + ESP_LOGCONFIG(TAG, + " Auto Exposure Control: %u\n" + " Auto Exposure Control 2: %u\n" + " Auto Exposure Level: %d\n" + " Auto Exposure Value: %u\n" + " AGC: %u\n" + " AGC Gain: %u\n" + " Gain Ceiling: %u", + st.aec, st.aec2, st.ae_level, st.aec_value, st.agc, st.agc_gain, st.gainceiling); // ESP_LOGCONFIG(TAG, " BPC: %u", st.bpc); // ESP_LOGCONFIG(TAG, " WPC: %u", st.wpc); // ESP_LOGCONFIG(TAG, " RAW_GMA: %u", st.raw_gma); @@ -238,6 +252,13 @@ void ESP32Camera::set_i2c_pins(uint8_t sda, uint8_t scl) { this->config_.pin_sccb_sda = sda; this->config_.pin_sccb_scl = scl; } +#ifdef USE_I2C +void ESP32Camera::set_i2c_id(i2c::InternalI2CBus *i2c_bus) { + this->i2c_bus_ = i2c_bus; + this->config_.pin_sccb_sda = -1; + this->config_.pin_sccb_scl = -1; +} +#endif // USE_I2C void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; } void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; } diff --git a/esphome/components/esp32_camera/esp32_camera.h b/esphome/components/esp32_camera/esp32_camera.h index d5fe48c2a7..75139ba400 100644 --- a/esphome/components/esp32_camera/esp32_camera.h +++ b/esphome/components/esp32_camera/esp32_camera.h @@ -2,13 +2,17 @@ #ifdef USE_ESP32 +#include +#include +#include #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" -#include -#include -#include + +#ifdef USE_I2C +#include "esphome/components/i2c/i2c_bus.h" +#endif // USE_I2C namespace esphome { namespace esp32_camera { @@ -118,6 +122,9 @@ class ESP32Camera : public EntityBase, public Component { void set_pixel_clock_pin(uint8_t pin); void set_external_clock(uint8_t pin, uint32_t frequency); void set_i2c_pins(uint8_t sda, uint8_t scl); +#ifdef USE_I2C + void set_i2c_id(i2c::InternalI2CBus *i2c_bus); +#endif // USE_I2C void set_reset_pin(uint8_t pin); void set_power_down_pin(uint8_t pin); /* -- image */ @@ -210,6 +217,9 @@ class ESP32Camera : public EntityBase, public Component { uint32_t last_idle_request_{0}; uint32_t last_update_{0}; +#ifdef USE_I2C + i2c::InternalI2CBus *i2c_bus_{nullptr}; +#endif // USE_I2C }; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/esp32_camera_web_server/__init__.py b/esphome/components/esp32_camera_web_server/__init__.py index 363218bbac..df137c8ff2 100644 --- a/esphome/components/esp32_camera_web_server/__init__.py +++ b/esphome/components/esp32_camera_web_server/__init__.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_MODE, CONF_PORT CODEOWNERS = ["@ayufan"] -DEPENDENCIES = ["esp32_camera"] +DEPENDENCIES = ["esp32_camera", "network"] MULTI_CONF = True esp32_camera_web_server_ns = cg.esphome_ns.namespace("esp32_camera_web_server") diff --git a/esphome/components/esp32_camera_web_server/camera_web_server.cpp b/esphome/components/esp32_camera_web_server/camera_web_server.cpp index 7ca0c56d23..0a83128908 100644 --- a/esphome/components/esp32_camera_web_server/camera_web_server.cpp +++ b/esphome/components/esp32_camera_web_server/camera_web_server.cpp @@ -85,8 +85,10 @@ void CameraWebServer::on_shutdown() { } void CameraWebServer::dump_config() { - ESP_LOGCONFIG(TAG, "ESP32 Camera Web Server:"); - ESP_LOGCONFIG(TAG, " Port: %d", this->port_); + ESP_LOGCONFIG(TAG, + "ESP32 Camera Web Server:\n" + " Port: %d", + this->port_); if (this->mode_ == STREAM) { ESP_LOGCONFIG(TAG, " Mode: stream"); } else { diff --git a/esphome/components/esp32_dac/esp32_dac.cpp b/esphome/components/esp32_dac/esp32_dac.cpp index 46e3553559..01bf0e04c3 100644 --- a/esphome/components/esp32_dac/esp32_dac.cpp +++ b/esphome/components/esp32_dac/esp32_dac.cpp @@ -1,6 +1,6 @@ #include "esp32_dac.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ESP32 diff --git a/esphome/components/esp32_hall/esp32_hall.cpp b/esphome/components/esp32_hall/esp32_hall.cpp deleted file mode 100644 index 762497aedc..0000000000 --- a/esphome/components/esp32_hall/esp32_hall.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifdef USE_ESP32 -#include "esp32_hall.h" -#include "esphome/core/log.h" -#include "esphome/core/hal.h" -#include - -namespace esphome { -namespace esp32_hall { - -static const char *const TAG = "esp32_hall"; - -void ESP32HallSensor::update() { - adc1_config_width(ADC_WIDTH_BIT_12); - int value_int = hall_sensor_read(); - float value = (value_int / 4095.0f) * 10000.0f; - ESP_LOGD(TAG, "'%s': Got reading %.0f µT", this->name_.c_str(), value); - this->publish_state(value); -} -std::string ESP32HallSensor::unique_id() { return get_mac_address() + "-hall"; } -void ESP32HallSensor::dump_config() { LOG_SENSOR("", "ESP32 Hall Sensor", this); } - -} // namespace esp32_hall -} // namespace esphome - -#endif diff --git a/esphome/components/esp32_hall/esp32_hall.h b/esphome/components/esp32_hall/esp32_hall.h deleted file mode 100644 index 8db50c4667..0000000000 --- a/esphome/components/esp32_hall/esp32_hall.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/components/sensor/sensor.h" - -#ifdef USE_ESP32 - -namespace esphome { -namespace esp32_hall { - -class ESP32HallSensor : public sensor::Sensor, public PollingComponent { - public: - void dump_config() override; - - void update() override; - - std::string unique_id() override; -}; - -} // namespace esp32_hall -} // namespace esphome - -#endif diff --git a/esphome/components/esp32_hall/sensor.py b/esphome/components/esp32_hall/sensor.py deleted file mode 100644 index e7953d4b3d..0000000000 --- a/esphome/components/esp32_hall/sensor.py +++ /dev/null @@ -1,24 +0,0 @@ -import esphome.codegen as cg -from esphome.components import sensor -import esphome.config_validation as cv -from esphome.const import ICON_MAGNET, STATE_CLASS_MEASUREMENT, UNIT_MICROTESLA - -DEPENDENCIES = ["esp32"] - -esp32_hall_ns = cg.esphome_ns.namespace("esp32_hall") -ESP32HallSensor = esp32_hall_ns.class_( - "ESP32HallSensor", sensor.Sensor, cg.PollingComponent -) - -CONFIG_SCHEMA = sensor.sensor_schema( - ESP32HallSensor, - unit_of_measurement=UNIT_MICROTESLA, - icon=ICON_MAGNET, - accuracy_decimals=1, - state_class=STATE_CLASS_MEASUREMENT, -).extend(cv.polling_component_schema("60s")) - - -async def to_code(config): - var = await sensor.new_sensor(config) - await cg.register_component(var, config) diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index ca39c1cd36..fa33bd947a 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -1,6 +1,7 @@ from esphome import automation import esphome.codegen as cg -from esphome.components import binary_sensor, output +from esphome.components import binary_sensor, esp32_ble, output +from esphome.components.esp32_ble import BTLoggers import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_ON_STATE, CONF_TRIGGER_ID @@ -94,6 +95,9 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index d74714838f..d41094fda1 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -168,6 +168,8 @@ void ESP32ImprovComponent::loop() { case improv::STATE_PROVISIONED: { this->incoming_data_.clear(); this->set_status_indicator_state_(false); + // Provisioning complete, no further loop execution needed + this->disable_loop(); break; } } @@ -254,6 +256,7 @@ void ESP32ImprovComponent::start() { ESP_LOGD(TAG, "Setting Improv to start"); this->should_start_ = true; + this->enable_loop(); } void ESP32ImprovComponent::stop() { @@ -324,10 +327,10 @@ void ESP32ImprovComponent::process_incoming_data_() { this->incoming_data_.clear(); } } else if (this->incoming_data_.size() - 2 > length) { - ESP_LOGV(TAG, "Too much data received or data malformed; resetting buffer..."); + ESP_LOGV(TAG, "Too much data received or data malformed; resetting buffer"); this->incoming_data_.clear(); } else { - ESP_LOGV(TAG, "Waiting for split data packets..."); + ESP_LOGV(TAG, "Waiting for split data packets"); } } diff --git a/esphome/components/esp32_rmt/__init__.py b/esphome/components/esp32_rmt/__init__.py index 171c335727..1e72185e3e 100644 --- a/esphome/components/esp32_rmt/__init__.py +++ b/esphome/components/esp32_rmt/__init__.py @@ -1,48 +1,8 @@ -import esphome.codegen as cg from esphome.components import esp32 import esphome.config_validation as cv -from esphome.const import KEY_CORE, KEY_FRAMEWORK_VERSION -from esphome.core import CORE CODEOWNERS = ["@jesserockz"] -RMT_TX_CHANNELS = { - esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7], - esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3], - esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3], - esp32.const.VARIANT_ESP32C3: [0, 1], - esp32.const.VARIANT_ESP32C6: [0, 1], - esp32.const.VARIANT_ESP32H2: [0, 1], -} - -RMT_RX_CHANNELS = { - esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7], - esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3], - esp32.const.VARIANT_ESP32S3: [4, 5, 6, 7], - esp32.const.VARIANT_ESP32C3: [2, 3], - esp32.const.VARIANT_ESP32C6: [2, 3], - esp32.const.VARIANT_ESP32H2: [2, 3], -} - -rmt_channel_t = cg.global_ns.enum("rmt_channel_t") -RMT_CHANNEL_ENUMS = { - 0: rmt_channel_t.RMT_CHANNEL_0, - 1: rmt_channel_t.RMT_CHANNEL_1, - 2: rmt_channel_t.RMT_CHANNEL_2, - 3: rmt_channel_t.RMT_CHANNEL_3, - 4: rmt_channel_t.RMT_CHANNEL_4, - 5: rmt_channel_t.RMT_CHANNEL_5, - 6: rmt_channel_t.RMT_CHANNEL_6, - 7: rmt_channel_t.RMT_CHANNEL_7, -} - - -def use_new_rmt_driver(): - framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] - if CORE.using_esp_idf and framework_version >= cv.Version(5, 0, 0): - return True - return False - def validate_clock_resolution(): def _validator(value): @@ -60,21 +20,3 @@ def validate_clock_resolution(): return value return _validator - - -def validate_rmt_channel(*, tx: bool): - rmt_channels = RMT_TX_CHANNELS if tx else RMT_RX_CHANNELS - - def _validator(value): - cv.only_on_esp32(value) - value = cv.int_(value) - variant = esp32.get_esp32_variant() - if variant not in rmt_channels: - raise cv.Invalid(f"ESP32 variant {variant} does not support RMT.") - if value not in rmt_channels[variant]: - raise cv.Invalid( - f"RMT channel {value} does not support {'transmitting' if tx else 'receiving'} for ESP32 variant {variant}." - ) - return cv.enum(RMT_CHANNEL_ENUMS)(value) - - return _validator diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp index 9b15e1898e..dfdf50aa66 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.cpp +++ b/esphome/components/esp32_rmt_led_strip/led_strip.cpp @@ -42,7 +42,6 @@ void ESP32RMTLEDStripLightOutput::setup() { return; } -#if ESP_IDF_VERSION_MAJOR >= 5 RAMAllocator rmt_allocator(this->use_psram_ ? 0 : RAMAllocator::ALLOC_INTERNAL); // 8 bits per byte, 1 rmt_symbol_word_t per bit + 1 rmt_symbol_word_t for reset @@ -79,36 +78,6 @@ void ESP32RMTLEDStripLightOutput::setup() { this->mark_failed(); return; } -#else - RAMAllocator rmt_allocator(this->use_psram_ ? 0 : RAMAllocator::ALLOC_INTERNAL); - - // 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset - this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1); - - rmt_config_t config; - memset(&config, 0, sizeof(config)); - config.channel = this->channel_; - config.rmt_mode = RMT_MODE_TX; - config.gpio_num = gpio_num_t(this->pin_); - config.mem_block_num = 1; - config.clk_div = RMT_CLK_DIV; - config.tx_config.loop_en = false; - config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW; - config.tx_config.carrier_en = false; - config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; - config.tx_config.idle_output_en = true; - - if (rmt_config(&config) != ESP_OK) { - ESP_LOGE(TAG, "Cannot initialize RMT!"); - this->mark_failed(); - return; - } - if (rmt_driver_install(config.channel, 0, 0) != ESP_OK) { - ESP_LOGE(TAG, "Cannot install RMT driver!"); - this->mark_failed(); - return; - } -#endif } void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, @@ -143,13 +112,9 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { this->last_refresh_ = now; this->mark_shown_(); - ESP_LOGVV(TAG, "Writing RGB values to bus..."); + ESP_LOGVV(TAG, "Writing RGB values to bus"); -#if ESP_IDF_VERSION_MAJOR >= 5 esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000); -#else - esp_err_t error = rmt_wait_tx_done(this->channel_, pdMS_TO_TICKS(1000)); -#endif if (error != ESP_OK) { ESP_LOGE(TAG, "RMT TX timeout"); this->status_set_warning(); @@ -162,11 +127,7 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { size_t size = 0; size_t len = 0; uint8_t *psrc = this->buf_; -#if ESP_IDF_VERSION_MAJOR >= 5 rmt_symbol_word_t *pdest = this->rmt_buf_; -#else - rmt_item32_t *pdest = this->rmt_buf_; -#endif while (size < buffer_size) { uint8_t b = *psrc; for (int i = 0; i < 8; i++) { @@ -184,15 +145,11 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { len++; } -#if ESP_IDF_VERSION_MAJOR >= 5 rmt_transmit_config_t config; memset(&config, 0, sizeof(config)); config.loop_count = 0; config.flags.eot_level = 0; error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config); -#else - error = rmt_write_items(this->channel_, this->rmt_buf_, len, false); -#endif if (error != ESP_OK) { ESP_LOGE(TAG, "RMT TX error"); this->status_set_warning(); @@ -247,13 +204,11 @@ light::ESPColorView ESP32RMTLEDStripLightOutput::get_view_internal(int32_t index } void ESP32RMTLEDStripLightOutput::dump_config() { - ESP_LOGCONFIG(TAG, "ESP32 RMT LED Strip:"); - ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); -#if ESP_IDF_VERSION_MAJOR >= 5 + ESP_LOGCONFIG(TAG, + "ESP32 RMT LED Strip:\n" + " Pin: %u", + this->pin_); ESP_LOGCONFIG(TAG, " RMT Symbols: %" PRIu32, this->rmt_symbols_); -#else - ESP_LOGCONFIG(TAG, " Channel: %u", this->channel_); -#endif const char *rgb_order; switch (this->rgb_order_) { case ORDER_RGB: @@ -278,9 +233,11 @@ void ESP32RMTLEDStripLightOutput::dump_config() { rgb_order = "UNKNOWN"; break; } - ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order); - ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_); - ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_); + ESP_LOGCONFIG(TAG, + " RGB Order: %s\n" + " Max refresh rate: %" PRIu32 "\n" + " Number of LEDs: %u", + rgb_order, *this->max_refresh_rate_, this->num_leds_); } float ESP32RMTLEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; } diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.h b/esphome/components/esp32_rmt_led_strip/led_strip.h index f0cec9b291..c6a2b4bc9f 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.h +++ b/esphome/components/esp32_rmt_led_strip/led_strip.h @@ -11,12 +11,7 @@ #include #include #include - -#if ESP_IDF_VERSION_MAJOR >= 5 #include -#else -#include -#endif namespace esphome { namespace esp32_rmt_led_strip { @@ -61,11 +56,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { uint32_t reset_time_high, uint32_t reset_time_low); void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; } -#if ESP_IDF_VERSION_MAJOR >= 5 void set_rmt_symbols(uint32_t rmt_symbols) { this->rmt_symbols_ = rmt_symbols; } -#else - void set_rmt_channel(rmt_channel_t channel) { this->channel_ = channel; } -#endif void clear_effect_data() override { for (int i = 0; i < this->size(); i++) @@ -81,17 +72,11 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { uint8_t *buf_{nullptr}; uint8_t *effect_data_{nullptr}; -#if ESP_IDF_VERSION_MAJOR >= 5 rmt_channel_handle_t channel_{nullptr}; rmt_encoder_handle_t encoder_{nullptr}; rmt_symbol_word_t *rmt_buf_{nullptr}; rmt_symbol_word_t bit0_, bit1_, reset_; uint32_t rmt_symbols_{48}; -#else - rmt_item32_t *rmt_buf_{nullptr}; - rmt_item32_t bit0_, bit1_, reset_; - rmt_channel_t channel_{RMT_CHANNEL_0}; -#endif uint8_t pin_; uint16_t num_leds_; diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index ae92d99b12..33ae44e435 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -3,7 +3,7 @@ import logging from esphome import pins import esphome.codegen as cg -from esphome.components import esp32, esp32_rmt, light +from esphome.components import esp32, light import esphome.config_validation as cv from esphome.const import ( CONF_CHIPSET, @@ -13,11 +13,9 @@ from esphome.const import ( CONF_OUTPUT_ID, CONF_PIN, CONF_RGB_ORDER, - CONF_RMT_CHANNEL, CONF_RMT_SYMBOLS, CONF_USE_DMA, ) -from esphome.core import CORE _LOGGER = logging.getLogger(__name__) @@ -69,53 +67,6 @@ CONF_RESET_HIGH = "reset_high" CONF_RESET_LOW = "reset_low" -class OptionalForIDF5(cv.SplitDefault): - @property - def default(self): - if not esp32_rmt.use_new_rmt_driver(): - return cv.UNDEFINED - return super().default - - @default.setter - def default(self, value): - # Ignore default set from vol.Optional - pass - - -def only_with_new_rmt_driver(obj): - if not esp32_rmt.use_new_rmt_driver(): - raise cv.Invalid( - "This feature is only available for the IDF framework version 5." - ) - return obj - - -def not_with_new_rmt_driver(obj): - if esp32_rmt.use_new_rmt_driver(): - raise cv.Invalid( - "This feature is not available for the IDF framework version 5." - ) - return obj - - -def final_validation(config): - if not esp32_rmt.use_new_rmt_driver(): - if CONF_RMT_CHANNEL not in config: - if CORE.using_esp_idf: - raise cv.Invalid( - "rmt_channel is a required option for IDF version < 5." - ) - raise cv.Invalid( - "rmt_channel is a required option for the Arduino framework." - ) - _LOGGER.warning( - "RMT_LED_STRIP support for IDF version < 5 is deprecated and will be removed soon." - ) - - -FINAL_VALIDATE_SCHEMA = final_validation - - CONFIG_SCHEMA = cv.All( light.ADDRESSABLE_LIGHT_SCHEMA.extend( { @@ -123,25 +74,25 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number, cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True), - cv.Optional(CONF_RMT_CHANNEL): cv.All( - not_with_new_rmt_driver, esp32_rmt.validate_rmt_channel(tx=True) - ), - OptionalForIDF5( + cv.SplitDefault( CONF_RMT_SYMBOLS, - esp32_idf=192, - esp32_s2_idf=192, - esp32_s3_idf=192, - esp32_c3_idf=96, - esp32_c6_idf=96, - esp32_h2_idf=96, - ): cv.All(only_with_new_rmt_driver, cv.int_range(min=2)), + esp32=192, + esp32_s2=192, + esp32_s3=192, + esp32_p4=192, + esp32_c3=96, + esp32_c5=96, + esp32_c6=96, + esp32_h2=96, + ): cv.int_range(min=2), cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), cv.Optional(CONF_IS_RGBW, default=False): cv.boolean, cv.Optional(CONF_IS_WRGB, default=False): cv.boolean, cv.Optional(CONF_USE_DMA): cv.All( - esp32.only_on_variant(supported=[esp32.const.VARIANT_ESP32S3]), - cv.only_with_esp_idf, + esp32.only_on_variant( + supported=[esp32.const.VARIANT_ESP32S3, esp32.const.VARIANT_ESP32P4] + ), cv.boolean, ), cv.Optional(CONF_USE_PSRAM, default=True): cv.boolean, @@ -214,15 +165,6 @@ async def to_code(config): cg.add(var.set_is_rgbw(config[CONF_IS_RGBW])) cg.add(var.set_is_wrgb(config[CONF_IS_WRGB])) cg.add(var.set_use_psram(config[CONF_USE_PSRAM])) - - if esp32_rmt.use_new_rmt_driver(): - cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) - if CONF_USE_DMA in config: - cg.add(var.set_use_dma(config[CONF_USE_DMA])) - else: - rmt_channel_t = cg.global_ns.enum("rmt_channel_t") - cg.add( - var.set_rmt_channel( - getattr(rmt_channel_t, f"RMT_CHANNEL_{config[CONF_RMT_CHANNEL]}") - ) - ) + cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) + if CONF_USE_DMA in config: + cg.add(var.set_use_dma(config[CONF_USE_DMA])) diff --git a/esphome/components/esp32_touch/esp32_touch.cpp b/esphome/components/esp32_touch/esp32_touch.cpp index b6fd2dda96..366aa10697 100644 --- a/esphome/components/esp32_touch/esp32_touch.cpp +++ b/esphome/components/esp32_touch/esp32_touch.cpp @@ -75,9 +75,11 @@ void ESP32TouchComponent::setup() { } void ESP32TouchComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Config for ESP32 Touch Hub:"); - ESP_LOGCONFIG(TAG, " Meas cycle: %.2fms", this->meas_cycle_ / (8000000.0f / 1000.0f)); - ESP_LOGCONFIG(TAG, " Sleep cycle: %.2fms", this->sleep_cycle_ / (150000.0f / 1000.0f)); + ESP_LOGCONFIG(TAG, + "Config for ESP32 Touch Hub:\n" + " Meas cycle: %.2fms\n" + " Sleep cycle: %.2fms", + this->meas_cycle_ / (8000000.0f / 1000.0f), this->sleep_cycle_ / (150000.0f / 1000.0f)); const char *lv_s; switch (this->low_voltage_reference_) { @@ -171,10 +173,12 @@ void ESP32TouchComponent::dump_config() { filter_mode_s = "UNKNOWN"; break; } - ESP_LOGCONFIG(TAG, " Filter mode: %s", filter_mode_s); - ESP_LOGCONFIG(TAG, " Debounce count: %" PRIu32, this->debounce_count_); - ESP_LOGCONFIG(TAG, " Noise threshold coefficient: %" PRIu32, this->noise_threshold_); - ESP_LOGCONFIG(TAG, " Jitter filter step size: %" PRIu32, this->jitter_step_); + ESP_LOGCONFIG(TAG, + " Filter mode: %s\n" + " Debounce count: %" PRIu32 "\n" + " Noise threshold coefficient: %" PRIu32 "\n" + " Jitter filter step size: %" PRIu32, + filter_mode_s, this->debounce_count_, this->noise_threshold_, this->jitter_step_); const char *smooth_level_s; switch (this->smooth_level_) { case TOUCH_PAD_SMOOTH_OFF: diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index c949e53aa6..4b4862a1d0 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -183,6 +183,7 @@ async def to_code(config): cg.add_platformio_option("board", config[CONF_BOARD]) cg.add_build_flag("-DUSE_ESP8266") + cg.set_cpp_standard("gnu++17") cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) cg.add_define("ESPHOME_VARIANT", "ESP8266") diff --git a/esphome/components/esp8266/gpio.cpp b/esphome/components/esp8266/gpio.cpp index 9f23e8e67e..4a997a790c 100644 --- a/esphome/components/esp8266/gpio.cpp +++ b/esphome/components/esp8266/gpio.cpp @@ -40,6 +40,7 @@ struct ISRPinArg { volatile uint32_t *mode_set_reg; volatile uint32_t *mode_clr_reg; volatile uint32_t *func_reg; + volatile uint32_t *control_reg; uint32_t mask; }; @@ -54,6 +55,7 @@ ISRInternalGPIOPin ESP8266GPIOPin::to_isr() const { arg->mode_set_reg = &GPES; arg->mode_clr_reg = &GPEC; arg->func_reg = &GPF(this->pin_); + arg->control_reg = &GPC(this->pin_); arg->mask = 1 << this->pin_; } else { arg->in_reg = &GP16I; @@ -62,6 +64,7 @@ ISRInternalGPIOPin ESP8266GPIOPin::to_isr() const { arg->mode_set_reg = &GP16E; arg->mode_clr_reg = nullptr; arg->func_reg = &GPF16; + arg->control_reg = nullptr; arg->mask = 1; } return ISRInternalGPIOPin((void *) arg); @@ -143,11 +146,17 @@ void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) { if (arg->pin < 16) { if (flags & gpio::FLAG_OUTPUT) { *arg->mode_set_reg = arg->mask; - } else { + if (flags & gpio::FLAG_OPEN_DRAIN) { + *arg->control_reg |= 1 << GPCD; + } else { + *arg->control_reg &= ~(1 << GPCD); + } + } else if (flags & gpio::FLAG_INPUT) { *arg->mode_clr_reg = arg->mask; } if (flags & gpio::FLAG_PULLUP) { *arg->func_reg |= 1 << GPFPU; + *arg->control_reg |= 1 << GPCD; } else { *arg->func_reg &= ~(1 << GPFPU); } diff --git a/esphome/components/esp8266/preferences.cpp b/esphome/components/esp8266/preferences.cpp index 8ee5a8225a..efd226e8f8 100644 --- a/esphome/components/esp8266/preferences.cpp +++ b/esphome/components/esp8266/preferences.cpp @@ -169,7 +169,7 @@ class ESP8266Preferences : public ESPPreferences { void setup() { s_flash_storage = new uint32_t[ESP8266_FLASH_STORAGE_SIZE]; // NOLINT - ESP_LOGVV(TAG, "Loading preferences from flash..."); + ESP_LOGVV(TAG, "Loading preferences from flash"); { InterruptLock lock; @@ -235,7 +235,7 @@ class ESP8266Preferences : public ESPPreferences { if (s_prevent_write) return false; - ESP_LOGD(TAG, "Saving preferences to flash..."); + ESP_LOGD(TAG, "Saving"); SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK; { InterruptLock lock; @@ -245,11 +245,11 @@ class ESP8266Preferences : public ESPPreferences { } } if (erase_res != SPI_FLASH_RESULT_OK) { - ESP_LOGE(TAG, "Erase ESP8266 flash failed!"); + ESP_LOGE(TAG, "Erasing failed"); return false; } if (write_res != SPI_FLASH_RESULT_OK) { - ESP_LOGE(TAG, "Write ESP8266 flash failed!"); + ESP_LOGE(TAG, "Writing failed"); return false; } @@ -258,14 +258,14 @@ class ESP8266Preferences : public ESPPreferences { } bool reset() override { - ESP_LOGD(TAG, "Cleaning up preferences in flash..."); + ESP_LOGD(TAG, "Erasing storage"); SpiFlashOpResult erase_res; { InterruptLock lock; erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); } if (erase_res != SPI_FLASH_RESULT_OK) { - ESP_LOGE(TAG, "Erase ESP8266 flash failed!"); + ESP_LOGE(TAG, "Erasing failed"); return false; } diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.cpp b/esphome/components/esp8266_pwm/esp8266_pwm.cpp index fcf66ee1a5..03fa3c683e 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.cpp +++ b/esphome/components/esp8266_pwm/esp8266_pwm.cpp @@ -1,10 +1,10 @@ #ifdef USE_ESP8266 #include "esp8266_pwm.h" -#include "esphome/core/macros.h" #include "esphome/core/defines.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include "esphome/core/macros.h" #include diff --git a/esphome/components/esp_ldo/__init__.py b/esphome/components/esp_ldo/__init__.py new file mode 100644 index 0000000000..ce24028083 --- /dev/null +++ b/esphome/components/esp_ldo/__init__.py @@ -0,0 +1,91 @@ +from esphome.automation import Action, register_action +import esphome.codegen as cg +from esphome.components.esp32 import VARIANT_ESP32P4, only_on_variant +import esphome.config_validation as cv +from esphome.const import CONF_CHANNEL, CONF_ID, CONF_VOLTAGE +from esphome.final_validate import full_config + +CODEOWNERS = ["@clydebarrow"] + +DOMAIN = "esp_ldo" + +esp_ldo_ns = cg.esphome_ns.namespace("esp_ldo") +EspLdo = esp_ldo_ns.class_("EspLdo", cg.Component) +AdjustAction = esp_ldo_ns.class_("AdjustAction", Action) + +CHANNELS = (3, 4) +CONF_ADJUSTABLE = "adjustable" + +adjusted_ids = set() + +CONFIG_SCHEMA = cv.All( + cv.ensure_list( + { + cv.GenerateID(): cv.declare_id(EspLdo), + cv.Required(CONF_VOLTAGE): cv.All( + cv.voltage, cv.float_range(min=0.5, max=2.7) + ), + cv.Required(CONF_CHANNEL): cv.one_of(*CHANNELS, int=True), + cv.Optional(CONF_ADJUSTABLE, default=False): cv.boolean, + } + ), + cv.only_with_esp_idf, + only_on_variant(supported=[VARIANT_ESP32P4]), +) + + +async def to_code(configs): + for config in configs: + var = cg.new_Pvariable(config[CONF_ID], config[CONF_CHANNEL]) + await cg.register_component(var, config) + cg.add(var.set_voltage(config[CONF_VOLTAGE])) + cg.add(var.set_adjustable(config[CONF_ADJUSTABLE])) + + +def final_validate(configs): + for channel in CHANNELS: + used = [config for config in configs if config[CONF_CHANNEL] == channel] + if len(used) > 1: + raise cv.Invalid( + f"Multiple LDOs configured for channel {channel}. Each channel must be unique.", + path=[CONF_CHANNEL, channel], + ) + + global_config = full_config.get() + for w in adjusted_ids: + path = global_config.get_path_for_id(w) + ldo_conf = global_config.get_config_for_path(path[:-1]) + if not ldo_conf[CONF_ADJUSTABLE]: + raise cv.Invalid( + "A non adjustable LDO may not be adjusted.", + path, + ) + + +FINAL_VALIDATE_SCHEMA = final_validate + + +def adjusted_ldo_id(value): + value = cv.use_id(EspLdo)(value) + adjusted_ids.add(value) + return value + + +@register_action( + "esp_ldo.voltage.adjust", + AdjustAction, + cv.Schema( + { + cv.GenerateID(CONF_ID): adjusted_ldo_id, + cv.Required(CONF_VOLTAGE): cv.templatable( + cv.All(cv.voltage, cv.float_range(min=0.5, max=2.7)) + ), + } + ), +) +async def ldo_voltage_adjust_to_code(config, action_id, template_arg, args): + parent = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, parent) + template_ = await cg.templatable(config[CONF_VOLTAGE], args, cg.float_) + cg.add(var.set_voltage(template_)) + return var diff --git a/esphome/components/esp_ldo/esp_ldo.cpp b/esphome/components/esp_ldo/esp_ldo.cpp new file mode 100644 index 0000000000..eb04670d7e --- /dev/null +++ b/esphome/components/esp_ldo/esp_ldo.cpp @@ -0,0 +1,43 @@ +#ifdef USE_ESP32_VARIANT_ESP32P4 +#include "esp_ldo.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace esp_ldo { + +static const char *const TAG = "esp_ldo"; +void EspLdo::setup() { + esp_ldo_channel_config_t config{}; + config.chan_id = this->channel_; + config.voltage_mv = (int) (this->voltage_ * 1000.0f); + config.flags.adjustable = this->adjustable_; + auto err = esp_ldo_acquire_channel(&config, &this->handle_); + if (err != ESP_OK) { + auto msg = str_sprintf("Failed to acquire LDO channel %d with voltage %fV", this->channel_, this->voltage_); + this->mark_failed(msg.c_str()); + } else { + ESP_LOGD(TAG, "Acquired LDO channel %d with voltage %fV", this->channel_, this->voltage_); + } +} +void EspLdo::dump_config() { + ESP_LOGCONFIG(TAG, "ESP LDO Channel %d:", this->channel_); + ESP_LOGCONFIG(TAG, " Voltage: %fV", this->voltage_); + ESP_LOGCONFIG(TAG, " Adjustable: %s", YESNO(this->adjustable_)); +} + +void EspLdo::adjust_voltage(float voltage) { + if (!std::isfinite(voltage) || voltage < 0.5f || voltage > 2.7f) { + ESP_LOGE(TAG, "Invalid voltage %fV for LDO channel %d", voltage, this->channel_); + return; + } + auto erro = esp_ldo_channel_adjust_voltage(this->handle_, (int) (voltage * 1000.0f)); + if (erro != ESP_OK) { + ESP_LOGE(TAG, "Failed to adjust LDO channel %d to voltage %fV: %s", this->channel_, voltage, esp_err_to_name(erro)); + } +} + +} // namespace esp_ldo +} // namespace esphome + +#endif // USE_ESP32_VARIANT_ESP32P4 diff --git a/esphome/components/esp_ldo/esp_ldo.h b/esphome/components/esp_ldo/esp_ldo.h new file mode 100644 index 0000000000..fb5abf7a3a --- /dev/null +++ b/esphome/components/esp_ldo/esp_ldo.h @@ -0,0 +1,43 @@ +#pragma once +#ifdef USE_ESP32_VARIANT_ESP32P4 +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esp_ldo_regulator.h" + +namespace esphome { +namespace esp_ldo { + +class EspLdo : public Component { + public: + EspLdo(int channel) : channel_(channel) {} + + void setup() override; + void dump_config() override; + + void set_adjustable(bool adjustable) { this->adjustable_ = adjustable; } + void set_voltage(float voltage) { this->voltage_ = voltage; } + void adjust_voltage(float voltage); + + protected: + int channel_; + float voltage_{2.7}; + bool adjustable_{false}; + esp_ldo_channel_handle_t handle_{}; +}; + +template class AdjustAction : public Action { + public: + explicit AdjustAction(EspLdo *ldo) : ldo_(ldo) {} + + TEMPLATABLE_VALUE(float, voltage) + + void play(Ts... x) override { this->ldo_->adjust_voltage(this->voltage_.value(x...)); } + + protected: + EspLdo *ldo_; +}; + +} // namespace esp_ldo +} // namespace esphome + +#endif // USE_ESP32_VARIANT_ESP32P4 diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 6f128e548b..4cc82b9094 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -26,19 +26,19 @@ void ESPHomeOTAComponent::setup() { ota::register_ota_platform(this); #endif - server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections - if (server_ == nullptr) { + this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections + if (this->server_ == nullptr) { ESP_LOGW(TAG, "Could not create socket"); this->mark_failed(); return; } int enable = 1; - int err = server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + int err = this->server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); if (err != 0) { ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err); // we can still continue } - err = server_->setblocking(false); + err = this->server_->setblocking(false); if (err != 0) { ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err); this->mark_failed(); @@ -54,14 +54,14 @@ void ESPHomeOTAComponent::setup() { return; } - err = server_->bind((struct sockaddr *) &server, sizeof(server)); + err = this->server_->bind((struct sockaddr *) &server, sizeof(server)); if (err != 0) { ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno); this->mark_failed(); return; } - err = server_->listen(4); + err = this->server_->listen(4); if (err != 0) { ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno); this->mark_failed(); @@ -70,9 +70,11 @@ void ESPHomeOTAComponent::setup() { } void ESPHomeOTAComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Over-The-Air updates:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); - ESP_LOGCONFIG(TAG, " Version: %d", USE_OTA_VERSION); + ESP_LOGCONFIG(TAG, + "Over-The-Air updates:\n" + " Address: %s:%u\n" + " Version: %d", + network::get_use_address().c_str(), this->port_, USE_OTA_VERSION); #ifdef USE_OTA_PASSWORD if (!this->password_.empty()) { ESP_LOGCONFIG(TAG, " Password configured"); @@ -80,7 +82,14 @@ void ESPHomeOTAComponent::dump_config() { #endif } -void ESPHomeOTAComponent::loop() { this->handle_(); } +void ESPHomeOTAComponent::loop() { + // Skip handle_() call if no client connected and no incoming connections + // This optimization reduces idle loop overhead when OTA is not active + // Note: No need to check server_ for null as the component is marked failed in setup() if server_ creation fails + if (this->client_ != nullptr || this->server_->ready()) { + this->handle_(); + } +} static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01; @@ -99,27 +108,25 @@ void ESPHomeOTAComponent::handle_() { size_t size_acknowledged = 0; #endif - if (client_ == nullptr) { - // Check if the server socket is ready before accepting - if (this->server_->ready()) { - struct sockaddr_storage source_addr; - socklen_t addr_len = sizeof(source_addr); - client_ = server_->accept((struct sockaddr *) &source_addr, &addr_len); - } + if (this->client_ == nullptr) { + // We already checked server_->ready() in loop(), so we can accept directly + struct sockaddr_storage source_addr; + socklen_t addr_len = sizeof(source_addr); + this->client_ = this->server_->accept((struct sockaddr *) &source_addr, &addr_len); + if (this->client_ == nullptr) + return; } - if (client_ == nullptr) - return; int enable = 1; - int err = client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); + int err = this->client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); if (err != 0) { ESP_LOGW(TAG, "Socket could not enable TCP nodelay, errno %d", errno); - client_->close(); - client_ = nullptr; + this->client_->close(); + this->client_ = nullptr; return; } - ESP_LOGD(TAG, "Starting update from %s...", this->client_->getpeername().c_str()); + ESP_LOGD(TAG, "Starting update from %s", this->client_->getpeername().c_str()); this->status_set_warning(); #ifdef USE_OTA_STATE_CALLBACK this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0); diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index c76c5523b2..180a72ec7e 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -106,7 +106,7 @@ void EthernetComponent::setup() { .post_cb = nullptr, }; -#if USE_ESP_IDF && (ESP_IDF_VERSION_MAJOR >= 5) +#if ESP_IDF_VERSION_MAJOR >= 5 eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(host, &devcfg); #else spi_device_handle_t spi_handle = nullptr; @@ -245,35 +245,38 @@ void EthernetComponent::loop() { switch (this->state_) { case EthernetComponentState::STOPPED: if (this->started_) { - ESP_LOGI(TAG, "Starting ethernet connection"); + ESP_LOGI(TAG, "Starting connection"); this->state_ = EthernetComponentState::CONNECTING; this->start_connect_(); } break; case EthernetComponentState::CONNECTING: if (!this->started_) { - ESP_LOGI(TAG, "Stopped ethernet connection"); + ESP_LOGI(TAG, "Stopped connection"); this->state_ = EthernetComponentState::STOPPED; } else if (this->connected_) { // connection established - ESP_LOGI(TAG, "Connected via Ethernet!"); + ESP_LOGI(TAG, "Connected"); this->state_ = EthernetComponentState::CONNECTED; this->dump_connect_params_(); this->status_clear_warning(); } else if (now - this->connect_begin_ > 15000) { - ESP_LOGW(TAG, "Connecting via ethernet failed! Re-connecting..."); + ESP_LOGW(TAG, "Connecting failed; reconnecting"); this->start_connect_(); } break; case EthernetComponentState::CONNECTED: if (!this->started_) { - ESP_LOGI(TAG, "Stopped ethernet connection"); + ESP_LOGI(TAG, "Stopped connection"); this->state_ = EthernetComponentState::STOPPED; } else if (!this->connected_) { - ESP_LOGW(TAG, "Connection via Ethernet lost! Re-connecting..."); + ESP_LOGW(TAG, "Connection lost; reconnecting"); this->state_ = EthernetComponentState::CONNECTING; this->start_connect_(); + } else { + // When connected and stable, disable the loop to save CPU cycles + this->disable_loop(); } break; } @@ -326,10 +329,12 @@ void EthernetComponent::dump_config() { ESP_LOGCONFIG(TAG, "Ethernet:"); this->dump_connect_params_(); #ifdef USE_ETHERNET_SPI - ESP_LOGCONFIG(TAG, " CLK Pin: %u", this->clk_pin_); - ESP_LOGCONFIG(TAG, " MISO Pin: %u", this->miso_pin_); - ESP_LOGCONFIG(TAG, " MOSI Pin: %u", this->mosi_pin_); - ESP_LOGCONFIG(TAG, " CS Pin: %u", this->cs_pin_); + ESP_LOGCONFIG(TAG, + " CLK Pin: %u\n" + " MISO Pin: %u\n" + " MOSI Pin: %u\n" + " CS Pin: %u", + this->clk_pin_, this->miso_pin_, this->mosi_pin_, this->cs_pin_); #ifdef USE_ETHERNET_SPI_POLLING_SUPPORT if (this->polling_interval_ != 0) { ESP_LOGCONFIG(TAG, " Polling Interval: %lu ms", this->polling_interval_); @@ -338,15 +343,19 @@ void EthernetComponent::dump_config() { { ESP_LOGCONFIG(TAG, " IRQ Pin: %d", this->interrupt_pin_); } - ESP_LOGCONFIG(TAG, " Reset Pin: %d", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Clock Speed: %d MHz", this->clock_speed_ / 1000000); + ESP_LOGCONFIG(TAG, + " Reset Pin: %d\n" + " Clock Speed: %d MHz", + this->reset_pin_, this->clock_speed_ / 1000000); #else if (this->power_pin_ != -1) { ESP_LOGCONFIG(TAG, " Power Pin: %u", this->power_pin_); } - ESP_LOGCONFIG(TAG, " MDC Pin: %u", this->mdc_pin_); - ESP_LOGCONFIG(TAG, " MDIO Pin: %u", this->mdio_pin_); - ESP_LOGCONFIG(TAG, " PHY addr: %u", this->phy_addr_); + ESP_LOGCONFIG(TAG, + " MDC Pin: %u\n" + " MDIO Pin: %u\n" + " PHY addr: %u", + this->mdc_pin_, this->mdio_pin_, this->phy_addr_); #endif ESP_LOGCONFIG(TAG, " Type: %s", eth_type); } @@ -391,11 +400,13 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base case ETHERNET_EVENT_START: event_name = "ETH started"; global_eth_component->started_ = true; + global_eth_component->enable_loop_soon_any_context(); break; case ETHERNET_EVENT_STOP: event_name = "ETH stopped"; global_eth_component->started_ = false; global_eth_component->connected_ = false; + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes break; case ETHERNET_EVENT_CONNECTED: event_name = "ETH connected"; @@ -403,6 +414,7 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base case ETHERNET_EVENT_DISCONNECTED: event_name = "ETH disconnected"; global_eth_component->connected_ = false; + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes break; default: return; @@ -419,8 +431,10 @@ void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_b global_eth_component->got_ipv4_address_ = true; #if USE_NETWORK_IPV6 && (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0) global_eth_component->connected_ = global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT; + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes #else global_eth_component->connected_ = true; + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes #endif /* USE_NETWORK_IPV6 */ } @@ -433,8 +447,10 @@ void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_ #if (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0) global_eth_component->connected_ = global_eth_component->got_ipv4_address_ && (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT); + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes #else global_eth_component->connected_ = global_eth_component->got_ipv4_address_; + global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes #endif } #endif /* USE_NETWORK_IPV6 */ @@ -512,16 +528,19 @@ bool EthernetComponent::is_connected() { return this->state_ == EthernetComponen void EthernetComponent::dump_connect_params_() { esp_netif_ip_info_t ip; esp_netif_get_ip_info(this->eth_netif_, &ip); - ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(&ip.ip).str().c_str()); - ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); - ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(&ip.netmask).str().c_str()); - ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(&ip.gw).str().c_str()); - const ip_addr_t *dns_ip1 = dns_getserver(0); const ip_addr_t *dns_ip2 = dns_getserver(1); - ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1).str().c_str()); - ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2).str().c_str()); + ESP_LOGCONFIG(TAG, + " IP Address: %s\n" + " Hostname: '%s'\n" + " Subnet: %s\n" + " Gateway: %s\n" + " DNS1: %s\n" + " DNS2: %s", + network::IPAddress(&ip.ip).str().c_str(), App.get_name().c_str(), + network::IPAddress(&ip.netmask).str().c_str(), network::IPAddress(&ip.gw).str().c_str(), + network::IPAddress(dns_ip1).str().c_str(), network::IPAddress(dns_ip2).str().c_str()); #if USE_NETWORK_IPV6 struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES]; @@ -533,9 +552,12 @@ void EthernetComponent::dump_connect_params_() { } #endif /* USE_NETWORK_IPV6 */ - ESP_LOGCONFIG(TAG, " MAC Address: %s", this->get_eth_mac_address_pretty().c_str()); - ESP_LOGCONFIG(TAG, " Is Full Duplex: %s", YESNO(this->get_duplex_mode() == ETH_DUPLEX_FULL)); - ESP_LOGCONFIG(TAG, " Link Speed: %u", this->get_link_speed() == ETH_SPEED_100M ? 100 : 10); + ESP_LOGCONFIG(TAG, + " MAC Address: %s\n" + " Is Full Duplex: %s\n" + " Link Speed: %u", + this->get_eth_mac_address_pretty().c_str(), YESNO(this->get_duplex_mode() == ETH_DUPLEX_FULL), + this->get_link_speed() == ETH_SPEED_100M ? 100 : 10); } #ifdef USE_ETHERNET_SPI @@ -608,6 +630,7 @@ bool EthernetComponent::powerdown() { } this->connected_ = false; this->started_ = false; + // No need to enable_loop() here as this is only called during shutdown/reboot if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) { ESP_LOGE(TAG, "Error powering down ethernet PHY"); return false; diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index fb178431d5..7a205d89f0 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -56,7 +56,7 @@ class EthernetComponent : public Component { void dump_config() override; float get_setup_priority() const override; bool can_proceed() override; - void on_shutdown() override { powerdown(); } + void on_powerdown() override { powerdown(); } bool is_connected(); #ifdef USE_ETHERNET_SPI diff --git a/esphome/components/event/__init__.py b/esphome/components/event/__init__.py index 0e5fb43690..e7ab489a25 100644 --- a/esphome/components/event/__init__.py +++ b/esphome/components/event/__init__.py @@ -113,6 +113,7 @@ async def register_event(var, config, *, event_types: list[str]): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_event(var)) + CORE.register_platform_component("event", var) await setup_event_core_(var, config, event_types=event_types) diff --git a/esphome/components/exposure_notifications/exposure_notifications.cpp b/esphome/components/exposure_notifications/exposure_notifications.cpp index 3083cf429c..307bee26f8 100644 --- a/esphome/components/exposure_notifications/exposure_notifications.cpp +++ b/esphome/components/exposure_notifications/exposure_notifications.cpp @@ -1,6 +1,6 @@ #include "exposure_notifications.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ESP32 diff --git a/esphome/components/factory_reset/button/factory_reset_button.cpp b/esphome/components/factory_reset/button/factory_reset_button.cpp index a6e7b41e82..585975c043 100644 --- a/esphome/components/factory_reset/button/factory_reset_button.cpp +++ b/esphome/components/factory_reset/button/factory_reset_button.cpp @@ -10,7 +10,7 @@ static const char *const TAG = "factory_reset.button"; void FactoryResetButton::dump_config() { LOG_BUTTON("", "Factory Reset Button", this); } void FactoryResetButton::press_action() { - ESP_LOGI(TAG, "Resetting..."); + ESP_LOGI(TAG, "Resetting"); // Let MQTT settle a bit delay(100); // NOLINT global_preferences->reset(); diff --git a/esphome/components/factory_reset/switch/factory_reset_switch.cpp b/esphome/components/factory_reset/switch/factory_reset_switch.cpp index 116d696aa7..1282c73f4e 100644 --- a/esphome/components/factory_reset/switch/factory_reset_switch.cpp +++ b/esphome/components/factory_reset/switch/factory_reset_switch.cpp @@ -14,7 +14,7 @@ void FactoryResetSwitch::write_state(bool state) { this->publish_state(false); if (state) { - ESP_LOGI(TAG, "Resetting..."); + ESP_LOGI(TAG, "Resetting"); // Let MQTT settle a bit delay(100); // NOLINT global_preferences->reset(); diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 960809ff70..c6ff938cd6 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -296,6 +296,7 @@ async def register_fan(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_fan(var)) + CORE.register_platform_component("fan", var) await setup_fan_core_(var, config) diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index 1d560d2fc6..25f710f893 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -41,39 +41,48 @@ void FanCall::perform() { void FanCall::validate_() { auto traits = this->parent_.get_traits(); - if (this->speed_.has_value()) + if (this->speed_.has_value()) { this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count()); - if (this->binary_state_.has_value() && *this->binary_state_) { - // when turning on, if neither current nor new speed available, set speed to 100% - if (traits.supports_speed() && !this->parent_.state && this->parent_.speed == 0 && !this->speed_.has_value()) { - this->speed_ = traits.supported_speed_count(); - } - } - - if (this->oscillating_.has_value() && !traits.supports_oscillation()) { - ESP_LOGW(TAG, "'%s' - This fan does not support oscillation!", this->parent_.get_name().c_str()); - this->oscillating_.reset(); - } - - if (this->speed_.has_value() && !traits.supports_speed()) { - ESP_LOGW(TAG, "'%s' - This fan does not support speeds!", this->parent_.get_name().c_str()); - this->speed_.reset(); - } - - if (this->direction_.has_value() && !traits.supports_direction()) { - ESP_LOGW(TAG, "'%s' - This fan does not support directions!", this->parent_.get_name().c_str()); - this->direction_.reset(); + // https://developers.home-assistant.io/docs/core/entity/fan/#preset-modes + // "Manually setting a speed must disable any set preset mode" + this->preset_mode_.clear(); } if (!this->preset_mode_.empty()) { const auto &preset_modes = traits.supported_preset_modes(); if (preset_modes.find(this->preset_mode_) == preset_modes.end()) { - ESP_LOGW(TAG, "'%s' - This fan does not support preset mode '%s'!", this->parent_.get_name().c_str(), - this->preset_mode_.c_str()); + ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), this->preset_mode_.c_str()); this->preset_mode_.clear(); } } + + // when turning on... + if (!this->parent_.state && this->binary_state_.has_value() && + *this->binary_state_ + // ..,and no preset mode will be active... + && this->preset_mode_.empty() && + this->parent_.preset_mode.empty() + // ...and neither current nor new speed is available... + && traits.supports_speed() && this->parent_.speed == 0 && !this->speed_.has_value()) { + // ...set speed to 100% + this->speed_ = traits.supported_speed_count(); + } + + if (this->oscillating_.has_value() && !traits.supports_oscillation()) { + ESP_LOGW(TAG, "%s: Oscillation not supported", this->parent_.get_name().c_str()); + this->oscillating_.reset(); + } + + if (this->speed_.has_value() && !traits.supports_speed()) { + ESP_LOGW(TAG, "%s: Speed control not supported", this->parent_.get_name().c_str()); + this->speed_.reset(); + } + + if (this->direction_.has_value() && !traits.supports_direction()) { + ESP_LOGW(TAG, "%s: Direction control not supported", this->parent_.get_name().c_str()); + this->direction_.reset(); + } } FanCall FanRestoreState::to_call(Fan &fan) { @@ -189,8 +198,10 @@ void Fan::dump_traits_(const char *tag, const char *prefix) { auto traits = this->get_traits(); if (traits.supports_speed()) { - ESP_LOGCONFIG(tag, "%s Speed: YES", prefix); - ESP_LOGCONFIG(tag, "%s Speed count: %d", prefix, traits.supported_speed_count()); + ESP_LOGCONFIG(tag, + "%s Speed: YES\n" + "%s Speed count: %d", + prefix, prefix, traits.supported_speed_count()); } if (traits.supports_oscillation()) { ESP_LOGCONFIG(tag, "%s Oscillation: YES", prefix); diff --git a/esphome/components/fastled_base/fastled_light.cpp b/esphome/components/fastled_base/fastled_light.cpp index 5d5b65cb7e..bca7de811a 100644 --- a/esphome/components/fastled_base/fastled_light.cpp +++ b/esphome/components/fastled_base/fastled_light.cpp @@ -18,9 +18,11 @@ void FastLEDLightOutput::setup() { } } void FastLEDLightOutput::dump_config() { - ESP_LOGCONFIG(TAG, "FastLED light:"); - ESP_LOGCONFIG(TAG, " Num LEDs: %u", this->num_leds_); - ESP_LOGCONFIG(TAG, " Max refresh rate: %u", *this->max_refresh_rate_); + ESP_LOGCONFIG(TAG, + "FastLED light:\n" + " Num LEDs: %u\n" + " Max refresh rate: %u", + this->num_leds_, *this->max_refresh_rate_); } void FastLEDLightOutput::write_state(light::LightState *state) { // protect from refreshing too often @@ -33,7 +35,7 @@ void FastLEDLightOutput::write_state(light::LightState *state) { this->last_refresh_ = now; this->mark_shown_(); - ESP_LOGVV(TAG, "Writing RGB values to bus..."); + ESP_LOGVV(TAG, "Writing RGB values to bus"); this->controller_->showLeds(this->state_parent_->current_values.get_brightness() * 255); } diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp index 57b12ef228..e28548428c 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.cpp +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -534,11 +534,13 @@ void FingerprintGrowComponent::sensor_sleep_() { } void FingerprintGrowComponent::dump_config() { - ESP_LOGCONFIG(TAG, "GROW_FINGERPRINT_READER:"); - ESP_LOGCONFIG(TAG, " System Identifier Code: 0x%.4X", this->system_identifier_code_); - ESP_LOGCONFIG(TAG, " Touch Sensing Pin: %s", - this->has_sensing_pin_ ? this->sensing_pin_->dump_summary().c_str() : "None"); - ESP_LOGCONFIG(TAG, " Sensor Power Pin: %s", + ESP_LOGCONFIG(TAG, + "GROW_FINGERPRINT_READER:\n" + " System Identifier Code: 0x%.4X\n" + " Touch Sensing Pin: %s\n" + " Sensor Power Pin: %s", + this->system_identifier_code_, + this->has_sensing_pin_ ? this->sensing_pin_->dump_summary().c_str() : "None", this->has_power_pin_ ? this->sensor_power_pin_->dump_summary().c_str() : "None"); if (this->idle_period_to_sleep_ms_ < UINT32_MAX) { ESP_LOGCONFIG(TAG, " Idle Period to Sleep: %" PRIu32 " ms", this->idle_period_to_sleep_ms_); diff --git a/esphome/components/font/font.h b/esphome/components/font/font.h index 9ee23b3ec5..992c77cb9f 100644 --- a/esphome/components/font/font.h +++ b/esphome/components/font/font.h @@ -67,10 +67,10 @@ class Font inline int get_height() { return this->height_; } inline int get_bpp() { return this->bpp_; } - const std::vector> &get_glyphs() const { return glyphs_; } + const std::vector> &get_glyphs() const { return glyphs_; } protected: - std::vector> glyphs_; + std::vector> glyphs_; int baseline_; int height_; uint8_t bpp_; // bits per pixel diff --git a/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp b/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp index 3e12d06647..9873a88fde 100644 --- a/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp +++ b/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp @@ -1,5 +1,6 @@ #include "ft5x06_touchscreen.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -81,9 +82,11 @@ void FT5x06Touchscreen::update_touches() { } void FT5x06Touchscreen::dump_config() { - ESP_LOGCONFIG(TAG, "FT5x06 Touchscreen:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); - ESP_LOGCONFIG(TAG, " Vendor ID: 0x%X", (int) this->vendor_id_); + ESP_LOGCONFIG(TAG, + "FT5x06 Touchscreen:\n" + " Address: 0x%02X\n" + " Vendor ID: 0x%X", + this->address_, (int) this->vendor_id_); } bool FT5x06Touchscreen::err_check_(i2c::ErrorCode err, const char *msg) { diff --git a/esphome/components/ft63x6/ft63x6.cpp b/esphome/components/ft63x6/ft63x6.cpp index cd5a646d2e..ba5b2094a5 100644 --- a/esphome/components/ft63x6/ft63x6.cpp +++ b/esphome/components/ft63x6/ft63x6.cpp @@ -71,8 +71,10 @@ void FT63X6Touchscreen::dump_config() { LOG_I2C_DEVICE(this); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " X Calibration: [%d, %d]", this->x_raw_min_, this->x_raw_max_); - ESP_LOGCONFIG(TAG, " Y Calibration: [%d, %d]", this->y_raw_min_, this->y_raw_max_); + ESP_LOGCONFIG(TAG, + " X Calibration: [%d, %d]\n" + " Y Calibration: [%d, %d]", + this->x_raw_min_, this->x_raw_max_, this->y_raw_min_, this->y_raw_max_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/gcja5/gcja5.cpp b/esphome/components/gcja5/gcja5.cpp index 29b0b910a7..a7342bc828 100644 --- a/esphome/components/gcja5/gcja5.cpp +++ b/esphome/components/gcja5/gcja5.cpp @@ -76,16 +76,6 @@ bool GCJA5Component::calculate_checksum_() { return (crc == this->rx_message_[30]); } -uint32_t GCJA5Component::get_32_bit_uint_(uint8_t start_index) { - return (((uint32_t) this->rx_message_[start_index + 3]) << 24) | - (((uint32_t) this->rx_message_[start_index + 2]) << 16) | - (((uint32_t) this->rx_message_[start_index + 1]) << 8) | ((uint32_t) this->rx_message_[start_index]); -} - -uint16_t GCJA5Component::get_16_bit_uint_(uint8_t start_index) { - return (((uint32_t) this->rx_message_[start_index + 1]) << 8) | ((uint32_t) this->rx_message_[start_index]); -} - void GCJA5Component::parse_data_() { ESP_LOGVV(TAG, "GCJA5 Data: "); for (uint8_t i = 0; i < 32; i++) { diff --git a/esphome/components/gcja5/gcja5.h b/esphome/components/gcja5/gcja5.h index 844da33a54..ea1fb78bf0 100644 --- a/esphome/components/gcja5/gcja5.h +++ b/esphome/components/gcja5/gcja5.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/uart/uart.h" @@ -28,8 +29,13 @@ class GCJA5Component : public Component, public uart::UARTDevice { void parse_data_(); bool calculate_checksum_(); - uint32_t get_32_bit_uint_(uint8_t start_index); - uint16_t get_16_bit_uint_(uint8_t start_index); + uint16_t get_16_bit_uint_(uint8_t start_index) const { + return encode_uint16(this->rx_message_[start_index + 1], this->rx_message_[start_index]); + } + uint32_t get_32_bit_uint_(uint8_t start_index) const { + return encode_uint32(this->rx_message_[start_index + 3], this->rx_message_[start_index + 2], + this->rx_message_[start_index + 1], this->rx_message_[start_index]); + } uint32_t last_transmission_{0}; std::vector rx_message_; diff --git a/esphome/components/globals/globals_component.h b/esphome/components/globals/globals_component.h index 78808436af..4c6a12aa72 100644 --- a/esphome/components/globals/globals_component.h +++ b/esphome/components/globals/globals_component.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/core/automation.h" +#include "esphome/core/component.h" #include "esphome/core/helpers.h" #include @@ -39,7 +39,7 @@ template class RestoringGlobalsComponent : public Component { void setup() override { this->rtc_ = global_preferences->make_preference(1944399030U ^ this->name_hash_); this->rtc_.load(&this->value_); - memcpy(&this->prev_value_, &this->value_, sizeof(T)); + memcpy(&this->last_checked_value_, &this->value_, sizeof(T)); } float get_setup_priority() const override { return setup_priority::HARDWARE; } @@ -52,15 +52,15 @@ template class RestoringGlobalsComponent : public Component { protected: void store_value_() { - int diff = memcmp(&this->value_, &this->prev_value_, sizeof(T)); + int diff = memcmp(&this->value_, &this->last_checked_value_, sizeof(T)); if (diff != 0) { this->rtc_.save(&this->value_); - memcpy(&this->prev_value_, &this->value_, sizeof(T)); + memcpy(&this->last_checked_value_, &this->value_, sizeof(T)); } } T value_{}; - T prev_value_{}; + T last_checked_value_{}; uint32_t name_hash_{}; ESPPreferenceObject rtc_; }; @@ -85,7 +85,7 @@ template class RestoringGlobalStringComponent : public C if (hasdata) { this->value_.assign(temp + 1, temp[0]); } - this->prev_value_.assign(this->value_); + this->last_checked_value_.assign(this->value_); } float get_setup_priority() const override { return setup_priority::HARDWARE; } @@ -98,13 +98,12 @@ template class RestoringGlobalStringComponent : public C protected: void store_value_() { - int diff = this->value_.compare(this->prev_value_); + int diff = this->value_.compare(this->last_checked_value_); if (diff != 0) { // Make it into a length prefixed thing unsigned char temp[SZ]; // If string is bigger than the allocation, do not save it. - // We don't need to waste ram setting prev_value either. int size = this->value_.size(); // Less than, not less than or equal, SZ includes the length byte. if (size < SZ) { @@ -112,13 +111,17 @@ template class RestoringGlobalStringComponent : public C // SZ should be pre checked at the schema level, it can't go past the char range. temp[0] = ((unsigned char) size); this->rtc_.save(&temp); - this->prev_value_.assign(this->value_); } + // Always update last_checked_value_ to match current value, even for oversized strings. + // This prevents redundant size checks on every loop iteration when a string remains oversized. + // Without this, the diff != 0 check would pass repeatedly for the same oversized string, + // wasting CPU cycles on size comparisons. + this->last_checked_value_.assign(this->value_); } } T value_{}; - T prev_value_{}; + T last_checked_value_{}; uint32_t name_hash_{}; ESPPreferenceObject rtc_; }; diff --git a/esphome/components/gp2y1010au0f/gp2y1010au0f.cpp b/esphome/components/gp2y1010au0f/gp2y1010au0f.cpp index 95b7653e51..c8b0f13d3a 100644 --- a/esphome/components/gp2y1010au0f/gp2y1010au0f.cpp +++ b/esphome/components/gp2y1010au0f/gp2y1010au0f.cpp @@ -13,8 +13,10 @@ static const float MAX_VOLTAGE = 4.0f; void GP2Y1010AU0FSensor::dump_config() { LOG_SENSOR("", "Sharp GP2Y1010AU0F PM2.5 Sensor", this); - ESP_LOGCONFIG(TAG, " Sampling duration: %" PRId32 " ms", this->sample_duration_); - ESP_LOGCONFIG(TAG, " ADC voltage multiplier: %.3f", this->voltage_multiplier_); + ESP_LOGCONFIG(TAG, + " Sampling duration: %" PRId32 " ms\n" + " ADC voltage multiplier: %.3f", + this->sample_duration_, this->voltage_multiplier_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/gp8403/gp8403.cpp b/esphome/components/gp8403/gp8403.cpp index 7a08a18a8f..5107e96dee 100644 --- a/esphome/components/gp8403/gp8403.cpp +++ b/esphome/components/gp8403/gp8403.cpp @@ -12,8 +12,10 @@ static const uint8_t RANGE_REGISTER = 0x01; void GP8403::setup() { this->write_register(RANGE_REGISTER, (uint8_t *) (&this->voltage_), 1); } void GP8403::dump_config() { - ESP_LOGCONFIG(TAG, "GP8403:"); - ESP_LOGCONFIG(TAG, " Voltage: %dV", this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10); + ESP_LOGCONFIG(TAG, + "GP8403:\n" + " Voltage: %dV", + this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10); LOG_I2C_DEVICE(this); } diff --git a/esphome/components/gp8403/output/gp8403_output.cpp b/esphome/components/gp8403/output/gp8403_output.cpp index ff73bb4627..edb6972184 100644 --- a/esphome/components/gp8403/output/gp8403_output.cpp +++ b/esphome/components/gp8403/output/gp8403_output.cpp @@ -10,8 +10,10 @@ static const char *const TAG = "gp8403.output"; static const uint8_t OUTPUT_REGISTER = 0x02; void GP8403Output::dump_config() { - ESP_LOGCONFIG(TAG, "GP8403 Output:"); - ESP_LOGCONFIG(TAG, " Channel: %u", this->channel_); + ESP_LOGCONFIG(TAG, + "GP8403 Output:\n" + " Channel: %u", + this->channel_); } void GP8403Output::write_state(float state) { diff --git a/esphome/components/gpio/one_wire/gpio_one_wire.cpp b/esphome/components/gpio/one_wire/gpio_one_wire.cpp index 7022c5fc31..ee80fde6fa 100644 --- a/esphome/components/gpio/one_wire/gpio_one_wire.cpp +++ b/esphome/components/gpio/one_wire/gpio_one_wire.cpp @@ -1,6 +1,6 @@ #include "gpio_one_wire.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace gpio { diff --git a/esphome/components/graphical_display_menu/graphical_display_menu.cpp b/esphome/components/graphical_display_menu/graphical_display_menu.cpp index 4a4e519009..1a29536b46 100644 --- a/esphome/components/graphical_display_menu/graphical_display_menu.cpp +++ b/esphome/components/graphical_display_menu/graphical_display_menu.cpp @@ -36,14 +36,18 @@ void GraphicalDisplayMenu::setup() { } void GraphicalDisplayMenu::dump_config() { - ESP_LOGCONFIG(TAG, "Graphical Display Menu"); - ESP_LOGCONFIG(TAG, "Has Display: %s", YESNO(this->display_ != nullptr)); - ESP_LOGCONFIG(TAG, "Popup Mode: %s", YESNO(this->display_ != nullptr)); - ESP_LOGCONFIG(TAG, "Advanced Drawing Mode: %s", YESNO(this->display_ == nullptr)); - ESP_LOGCONFIG(TAG, "Has Font: %s", YESNO(this->font_ != nullptr)); - ESP_LOGCONFIG(TAG, "Mode: %s", this->mode_ == display_menu_base::MENU_MODE_ROTARY ? "Rotary" : "Joystick"); - ESP_LOGCONFIG(TAG, "Active: %s", YESNO(this->active_)); - ESP_LOGCONFIG(TAG, "Menu items:"); + ESP_LOGCONFIG(TAG, + "Graphical Display Menu\n" + "Has Display: %s\n" + "Popup Mode: %s\n" + "Advanced Drawing Mode: %s\n" + "Has Font: %s\n" + "Mode: %s\n" + "Active: %s\n" + "Menu items:", + YESNO(this->display_ != nullptr), YESNO(this->display_ != nullptr), YESNO(this->display_ == nullptr), + YESNO(this->font_ != nullptr), + this->mode_ == display_menu_base::MENU_MODE_ROTARY ? "Rotary" : "Joystick", YESNO(this->active_)); for (size_t i = 0; i < this->displayed_item_->items_size(); i++) { auto *item = this->displayed_item_->get_item(i); ESP_LOGCONFIG(TAG, " %i: %s (Type: %s, Immediate Edit: %s)", i, item->get_text().c_str(), diff --git a/esphome/components/grove_tb6612fng/grove_tb6612fng.h b/esphome/components/grove_tb6612fng/grove_tb6612fng.h index 2743ef4ed7..68281117e7 100644 --- a/esphome/components/grove_tb6612fng/grove_tb6612fng.h +++ b/esphome/components/grove_tb6612fng/grove_tb6612fng.h @@ -1,9 +1,9 @@ #pragma once #include "esphome/components/i2c/i2c.h" +#include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" -#include "esphome/core/automation.h" //#include "esphome/core/helpers.h" /* diff --git a/esphome/components/growatt_solar/growatt_solar.cpp b/esphome/components/growatt_solar/growatt_solar.cpp index 60fd1379e8..686c1c232e 100644 --- a/esphome/components/growatt_solar/growatt_solar.cpp +++ b/esphome/components/growatt_solar/growatt_solar.cpp @@ -1,6 +1,7 @@ #include "growatt_solar.h" -#include "esphome/core/log.h" #include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace growatt_solar { @@ -134,8 +135,10 @@ void GrowattSolar::on_modbus_data(const std::vector &data) { } void GrowattSolar::dump_config() { - ESP_LOGCONFIG(TAG, "GROWATT Solar:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "GROWATT Solar:\n" + " Address: 0x%02X", + this->address_); } } // namespace growatt_solar diff --git a/esphome/components/haier/haier_base.cpp b/esphome/components/haier/haier_base.cpp index f8c0a7587e..a784accdf4 100644 --- a/esphome/components/haier/haier_base.cpp +++ b/esphome/components/haier/haier_base.cpp @@ -242,7 +242,7 @@ haier_protocol::HandlerError HaierClimateBase::timeout_default_handler_(haier_pr } void HaierClimateBase::setup() { - ESP_LOGI(TAG, "Haier initialization..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Set timestamp here to give AC time to boot this->last_request_timestamp_ = std::chrono::steady_clock::now(); this->set_phase(ProtocolPhases::SENDING_INIT_1); @@ -286,7 +286,7 @@ void HaierClimateBase::loop() { if (this->action_request_.has_value() && this->prepare_pending_action()) { this->set_phase(ProtocolPhases::SENDING_ACTION_COMMAND); } else if (this->next_hvac_settings_.valid || this->force_send_control_) { - ESP_LOGV(TAG, "Control packet is pending..."); + ESP_LOGV(TAG, "Control packet is pending"); this->set_phase(ProtocolPhases::SENDING_CONTROL); if (this->next_hvac_settings_.valid) { this->current_hvac_settings_ = this->next_hvac_settings_; diff --git a/esphome/components/haier/hon_climate.cpp b/esphome/components/haier/hon_climate.cpp index 9b59dd0c10..fd2d6a5800 100644 --- a/esphome/components/haier/hon_climate.cpp +++ b/esphome/components/haier/hon_climate.cpp @@ -339,13 +339,20 @@ void HonClimate::set_handlers() { void HonClimate::dump_config() { HaierClimateBase::dump_config(); - ESP_LOGCONFIG(TAG, " Protocol version: hOn"); - ESP_LOGCONFIG(TAG, " Control method: %d", (uint8_t) this->control_method_); + ESP_LOGCONFIG(TAG, + " Protocol version: hOn\n" + " Control method: %d", + (uint8_t) this->control_method_); if (this->hvac_hardware_info_.has_value()) { - ESP_LOGCONFIG(TAG, " Device protocol version: %s", this->hvac_hardware_info_.value().protocol_version_.c_str()); - ESP_LOGCONFIG(TAG, " Device software version: %s", this->hvac_hardware_info_.value().software_version_.c_str()); - ESP_LOGCONFIG(TAG, " Device hardware version: %s", this->hvac_hardware_info_.value().hardware_version_.c_str()); - ESP_LOGCONFIG(TAG, " Device name: %s", this->hvac_hardware_info_.value().device_name_.c_str()); + ESP_LOGCONFIG(TAG, + " Device protocol version: %s\n" + " Device software version: %s\n" + " Device hardware version: %s\n" + " Device name: %s", + this->hvac_hardware_info_.value().protocol_version_.c_str(), + this->hvac_hardware_info_.value().software_version_.c_str(), + this->hvac_hardware_info_.value().hardware_version_.c_str(), + this->hvac_hardware_info_.value().device_name_.c_str()); ESP_LOGCONFIG(TAG, " Device features:%s%s%s%s%s", (this->hvac_hardware_info_.value().functions_[0] ? " interactive" : ""), (this->hvac_hardware_info_.value().functions_[1] ? " controller-device" : ""), diff --git a/esphome/components/havells_solar/havells_solar.cpp b/esphome/components/havells_solar/havells_solar.cpp index f029df10ad..20dddf39ed 100644 --- a/esphome/components/havells_solar/havells_solar.cpp +++ b/esphome/components/havells_solar/havells_solar.cpp @@ -1,5 +1,6 @@ #include "havells_solar.h" #include "havells_solar_registers.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -123,8 +124,10 @@ void HavellsSolar::on_modbus_data(const std::vector &data) { void HavellsSolar::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); } void HavellsSolar::dump_config() { - ESP_LOGCONFIG(TAG, "HAVELLS Solar:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "HAVELLS Solar:\n" + " Address: 0x%02X", + this->address_); for (uint8_t i = 0; i < 3; i++) { auto phase = this->phases_[i]; if (!phase.setup) diff --git a/esphome/components/he60r/he60r.cpp b/esphome/components/he60r/he60r.cpp index 83e895543d..ca17930272 100644 --- a/esphome/components/he60r/he60r.cpp +++ b/esphome/components/he60r/he60r.cpp @@ -40,8 +40,10 @@ CoverTraits HE60rCover::get_traits() { void HE60rCover::dump_config() { LOG_COVER("", "HE60R Cover", this); this->check_uart_settings(1200, 1, uart::UART_CONFIG_PARITY_EVEN, 8); - ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f); - ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f); + ESP_LOGCONFIG(TAG, + " Open Duration: %.1fs\n" + " Close Duration: %.1fs", + this->open_duration_ / 1e3f, this->close_duration_ / 1e3f); auto restore = this->restore_state_(); if (restore.has_value()) ESP_LOGCONFIG(TAG, " Saved position %d%%", (int) (restore->position * 100.f)); diff --git a/esphome/components/heatpumpir/climate.py b/esphome/components/heatpumpir/climate.py index c0eb8db4b3..9e5a2bf45c 100644 --- a/esphome/components/heatpumpir/climate.py +++ b/esphome/components/heatpumpir/climate.py @@ -125,6 +125,6 @@ async def to_code(config): cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE])) cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE])) - cg.add_library("tonia/HeatpumpIR", "1.0.32") + cg.add_library("tonia/HeatpumpIR", "1.0.35") if CORE.is_libretiny: CORE.add_platformio_option("lib_ignore", "IRremoteESP8266") diff --git a/esphome/components/hlw8012/hlw8012.cpp b/esphome/components/hlw8012/hlw8012.cpp index 1044a528a0..ea1d081790 100644 --- a/esphome/components/hlw8012/hlw8012.cpp +++ b/esphome/components/hlw8012/hlw8012.cpp @@ -38,9 +38,11 @@ void HLW8012Component::dump_config() { LOG_PIN(" SEL Pin: ", this->sel_pin_) LOG_PIN(" CF Pin: ", this->cf_pin_) LOG_PIN(" CF1 Pin: ", this->cf1_pin_) - ESP_LOGCONFIG(TAG, " Change measurement mode every %" PRIu32, this->change_mode_every_); - ESP_LOGCONFIG(TAG, " Current resistor: %.1f mΩ", this->current_resistor_ * 1000.0f); - ESP_LOGCONFIG(TAG, " Voltage Divider: %.1f", this->voltage_divider_); + ESP_LOGCONFIG(TAG, + " Change measurement mode every %" PRIu32 "\n" + " Current resistor: %.1f mΩ\n" + " Voltage Divider: %.1f", + this->change_mode_every_, this->current_resistor_ * 1000.0f, this->voltage_divider_); LOG_UPDATE_INTERVAL(this) LOG_SENSOR(" ", "Voltage", this->voltage_sensor_) LOG_SENSOR(" ", "Current", this->current_sensor_) diff --git a/esphome/components/homeassistant/time/homeassistant_time.cpp b/esphome/components/homeassistant/time/homeassistant_time.cpp index 9f5239404a..0a91a2f63d 100644 --- a/esphome/components/homeassistant/time/homeassistant_time.cpp +++ b/esphome/components/homeassistant/time/homeassistant_time.cpp @@ -7,8 +7,10 @@ namespace homeassistant { static const char *const TAG = "homeassistant.time"; void HomeassistantTime::dump_config() { - ESP_LOGCONFIG(TAG, "Home Assistant Time:"); - ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); + ESP_LOGCONFIG(TAG, + "Home Assistant Time:\n" + " Timezone: '%s'", + this->timezone_.c_str()); } float HomeassistantTime::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/honeywellabp/honeywellabp.cpp b/esphome/components/honeywellabp/honeywellabp.cpp index 124bd6bb95..9252e613dd 100644 --- a/esphome/components/honeywellabp/honeywellabp.cpp +++ b/esphome/components/honeywellabp/honeywellabp.cpp @@ -85,8 +85,10 @@ float HONEYWELLABPSensor::get_setup_priority() const { return setup_priority::LA void HONEYWELLABPSensor::dump_config() { // LOG_SENSOR("", "HONEYWELLABP", this); LOG_PIN(" CS Pin: ", this->cs_); - ESP_LOGCONFIG(TAG, " Min Pressure Range: %0.1f", honeywellabp_min_pressure_); - ESP_LOGCONFIG(TAG, " Max Pressure Range: %0.1f", honeywellabp_max_pressure_); + ESP_LOGCONFIG(TAG, + " Min Pressure Range: %0.1f\n" + " Max Pressure Range: %0.1f", + honeywellabp_min_pressure_, honeywellabp_max_pressure_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp b/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp index 598f69d226..11f5dbc314 100644 --- a/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp +++ b/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp @@ -1,6 +1,6 @@ #include "honeywellabp2.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace honeywellabp2_i2c { @@ -84,8 +84,10 @@ void HONEYWELLABP2Sensor::update() { } void HONEYWELLABP2Sensor::dump_config() { - ESP_LOGCONFIG(TAG, " Min Pressure Range: %0.1f", this->min_pressure_); - ESP_LOGCONFIG(TAG, " Max Pressure Range: %0.1f", this->max_pressure_); + ESP_LOGCONFIG(TAG, + " Min Pressure Range: %0.1f\n" + " Max Pressure Range: %0.1f", + this->min_pressure_, this->max_pressure_); if (this->transfer_function_ == ABP2_TRANS_FUNC_A) { ESP_LOGCONFIG(TAG, " Transfer function A"); } else { diff --git a/esphome/components/hte501/hte501.cpp b/esphome/components/hte501/hte501.cpp index e40d53b52d..0f97c67f9e 100644 --- a/esphome/components/hte501/hte501.cpp +++ b/esphome/components/hte501/hte501.cpp @@ -1,4 +1,5 @@ #include "hte501.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index 878f362f28..18373edb77 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -1,6 +1,7 @@ from esphome import automation import esphome.codegen as cg from esphome.components import esp32 +from esphome.components.const import CONF_REQUEST_HEADERS import esphome.config_validation as cv from esphome.const import ( CONF_ESP8266_DISABLE_SSL_SUPPORT, @@ -51,7 +52,6 @@ CONF_CA_CERTIFICATE_PATH = "ca_certificate_path" CONF_MAX_RESPONSE_BUFFER_SIZE = "max_response_buffer_size" CONF_ON_RESPONSE = "on_response" CONF_HEADERS = "headers" -CONF_REQUEST_HEADERS = "request_headers" CONF_COLLECT_HEADERS = "collect_headers" CONF_BODY = "body" CONF_JSON = "json" @@ -175,7 +175,7 @@ async def to_code(config): not config.get(CONF_VERIFY_SSL), ) else: - cg.add_library("WiFiClientSecure", None) + cg.add_library("NetworkClientSecure", None) cg.add_library("HTTPClient", None) if CORE.is_esp8266: cg.add_library("ESP8266HTTPClient", None) diff --git a/esphome/components/http_request/http_request.cpp b/esphome/components/http_request/http_request.cpp index ca9fd2c2dc..806354baf1 100644 --- a/esphome/components/http_request/http_request.cpp +++ b/esphome/components/http_request/http_request.cpp @@ -10,11 +10,13 @@ namespace http_request { static const char *const TAG = "http_request"; void HttpRequestComponent::dump_config() { - ESP_LOGCONFIG(TAG, "HTTP Request:"); - ESP_LOGCONFIG(TAG, " Timeout: %ums", this->timeout_); - ESP_LOGCONFIG(TAG, " User-Agent: %s", this->useragent_); - ESP_LOGCONFIG(TAG, " Follow redirects: %s", YESNO(this->follow_redirects_)); - ESP_LOGCONFIG(TAG, " Redirect limit: %d", this->redirect_limit_); + ESP_LOGCONFIG(TAG, + "HTTP Request:\n" + " Timeout: %ums\n" + " User-Agent: %s\n" + " Follow redirects: %s\n" + " Redirect limit: %d", + this->timeout_, this->useragent_, YESNO(this->follow_redirects_), this->redirect_limit_); if (this->watchdog_timeout_ > 0) { ESP_LOGCONFIG(TAG, " Watchdog Timeout: %" PRIu32 "ms", this->watchdog_timeout_); } diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index a67b04eadc..95515f731a 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -239,7 +239,7 @@ template class HttpRequestSendAction : public Action { std::string response_body; if (this->capture_response_.value(x...)) { - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; uint8_t *buf = allocator.allocate(max_length); if (buf != nullptr) { size_t read_index = 0; diff --git a/esphome/components/http_request/http_request_arduino.h b/esphome/components/http_request/http_request_arduino.h index ac9ddffbb0..44744f8c78 100644 --- a/esphome/components/http_request/http_request_arduino.h +++ b/esphome/components/http_request/http_request_arduino.h @@ -6,6 +6,7 @@ #if defined(USE_ESP32) || defined(USE_RP2040) #include +#include #endif #ifdef USE_ESP8266 #include diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index 0923062822..6a779ba03a 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -26,8 +26,10 @@ struct UserData { void HttpRequestIDF::dump_config() { HttpRequestComponent::dump_config(); - ESP_LOGCONFIG(TAG, " Buffer Size RX: %u", this->buffer_size_rx_); - ESP_LOGCONFIG(TAG, " Buffer Size TX: %u", this->buffer_size_tx_); + ESP_LOGCONFIG(TAG, + " Buffer Size RX: %u\n" + " Buffer Size TX: %u", + this->buffer_size_rx_, this->buffer_size_tx_); } esp_err_t HttpRequestIDF::http_event_handler(esp_http_client_event_t *evt) { diff --git a/esphome/components/http_request/ota/ota_http_request.cpp b/esphome/components/http_request/ota/ota_http_request.cpp index cec30d72ec..4c8d49dad5 100644 --- a/esphome/components/http_request/ota/ota_http_request.cpp +++ b/esphome/components/http_request/ota/ota_http_request.cpp @@ -48,7 +48,7 @@ void OtaHttpRequestComponent::flash() { return; } - ESP_LOGI(TAG, "Starting update..."); + ESP_LOGI(TAG, "Starting update"); #ifdef USE_OTA_STATE_CALLBACK this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0); #endif diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index d683495ac6..828fb5bd8b 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -54,7 +54,7 @@ void HttpRequestUpdate::update_task(void *params) { UPDATE_RETURN; } - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; uint8_t *data = allocator.allocate(container->content_length); if (data == nullptr) { std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length); diff --git a/esphome/components/hx711/hx711.cpp b/esphome/components/hx711/hx711.cpp index d9b82fd7cd..0fc8b29604 100644 --- a/esphome/components/hx711/hx711.cpp +++ b/esphome/components/hx711/hx711.cpp @@ -1,6 +1,6 @@ #include "hx711.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace hx711 { diff --git a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp index 86cf19eb24..2d8381b60c 100644 --- a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +++ b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp @@ -18,8 +18,10 @@ void HydreonRGxxComponent::dump_config() { ESP_LOGE(TAG, "Connection with hydreon_rgxx failed!"); } if (model_ == RG9) { - ESP_LOGCONFIG(TAG, " Model: RG9"); - ESP_LOGCONFIG(TAG, " Disable Led: %s", TRUEFALSE(this->disable_led_)); + ESP_LOGCONFIG(TAG, + " Model: RG9\n" + " Disable Led: %s", + TRUEFALSE(this->disable_led_)); } else { ESP_LOGCONFIG(TAG, " Model: RG15"); if (this->resolution_ == FORCE_HIGH) { diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index e47dec650d..d56bb2d07c 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -22,8 +22,9 @@ import esphome.final_validate as fv CODEOWNERS = ["@esphome/core"] i2c_ns = cg.esphome_ns.namespace("i2c") I2CBus = i2c_ns.class_("I2CBus") -ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", I2CBus, cg.Component) -IDFI2CBus = i2c_ns.class_("IDFI2CBus", I2CBus, cg.Component) +InternalI2CBus = i2c_ns.class_("InternalI2CBus", I2CBus) +ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", InternalI2CBus, cg.Component) +IDFI2CBus = i2c_ns.class_("IDFI2CBus", InternalI2CBus, cg.Component) I2CDevice = i2c_ns.class_("I2CDevice") @@ -71,6 +72,7 @@ CONFIG_SCHEMA = cv.All( @coroutine_with_priority(1.0) async def to_code(config): cg.add_global(i2c_ns.using) + cg.add_define("USE_I2C") var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/i2c/i2c_bus.h b/esphome/components/i2c/i2c_bus.h index fbfc88323e..5c1e15d814 100644 --- a/esphome/components/i2c/i2c_bus.h +++ b/esphome/components/i2c/i2c_bus.h @@ -1,6 +1,6 @@ #pragma once -#include #include +#include #include #include @@ -108,5 +108,12 @@ class I2CBus { bool scan_{false}; ///< Should we scan ? Can be set in the yaml }; +class InternalI2CBus : public I2CBus { + public: + /// @brief Returns the I2C port number. + /// @return the port number of the internal I2C bus + virtual int get_port() const = 0; +}; + } // namespace i2c } // namespace esphome diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index 9a5f1233b1..a85df0a4cd 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -1,11 +1,11 @@ #ifdef USE_ARDUINO #include "i2c_bus_arduino.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" -#include "esphome/core/application.h" #include #include +#include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace i2c { @@ -23,6 +23,7 @@ void ArduinoI2CBus::setup() { } else { wire_ = new TwoWire(next_bus_num); // NOLINT(cppcoreguidelines-owning-memory) } + this->port_ = next_bus_num; next_bus_num++; #elif defined(USE_ESP8266) wire_ = new TwoWire(); // NOLINT(cppcoreguidelines-owning-memory) @@ -40,7 +41,7 @@ void ArduinoI2CBus::setup() { this->initialized_ = true; if (this->scan_) { - ESP_LOGV(TAG, "Scanning i2c bus for active devices..."); + ESP_LOGV(TAG, "Scanning bus for active devices"); this->i2c_scan_(); } } @@ -70,9 +71,11 @@ void ArduinoI2CBus::set_pins_and_clock_() { void ArduinoI2CBus::dump_config() { ESP_LOGCONFIG(TAG, "I2C Bus:"); - ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); - ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); - ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); + ESP_LOGCONFIG(TAG, + " SDA Pin: GPIO%u\n" + " SCL Pin: GPIO%u\n" + " Frequency: %u Hz", + this->sda_pin_, this->scl_pin_, this->frequency_); if (timeout_ > 0) { #if defined(USE_ESP32) ESP_LOGCONFIG(TAG, " Timeout: %u ms", this->timeout_ / 1000); @@ -123,7 +126,7 @@ ErrorCode ArduinoI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) size_t to_request = 0; for (size_t i = 0; i < cnt; i++) to_request += buffers[i].len; - size_t ret = wire_->requestFrom((int) address, (int) to_request, 1); + size_t ret = wire_->requestFrom(address, to_request, true); if (ret != to_request) { ESP_LOGVV(TAG, "RX %u from %02X failed with error %u", to_request, address, ret); return ERROR_TIMEOUT; diff --git a/esphome/components/i2c/i2c_bus_arduino.h b/esphome/components/i2c/i2c_bus_arduino.h index 6a670a2a05..7e6616cbce 100644 --- a/esphome/components/i2c/i2c_bus_arduino.h +++ b/esphome/components/i2c/i2c_bus_arduino.h @@ -2,9 +2,9 @@ #ifdef USE_ARDUINO -#include "i2c_bus.h" -#include "esphome/core/component.h" #include +#include "esphome/core/component.h" +#include "i2c_bus.h" namespace esphome { namespace i2c { @@ -15,7 +15,7 @@ enum RecoveryCode { RECOVERY_COMPLETED, }; -class ArduinoI2CBus : public I2CBus, public Component { +class ArduinoI2CBus : public InternalI2CBus, public Component { public: void setup() override; void dump_config() override; @@ -29,12 +29,15 @@ class ArduinoI2CBus : public I2CBus, public Component { void set_frequency(uint32_t frequency) { frequency_ = frequency; } void set_timeout(uint32_t timeout) { timeout_ = timeout; } + int get_port() const override { return this->port_; } + private: void recover_(); void set_pins_and_clock_(); RecoveryCode recovery_result_; protected: + int8_t port_{-1}; TwoWire *wire_; uint8_t sda_pin_; uint8_t scl_pin_; diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index c99870191e..e4643405ce 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -75,15 +75,17 @@ void IDFI2CBus::setup() { } initialized_ = true; if (this->scan_) { - ESP_LOGV(TAG, "Scanning i2c bus for active devices..."); + ESP_LOGV(TAG, "Scanning bus for active devices"); this->i2c_scan_(); } } void IDFI2CBus::dump_config() { ESP_LOGCONFIG(TAG, "I2C Bus:"); - ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); - ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); - ESP_LOGCONFIG(TAG, " Frequency: %" PRIu32 " Hz", this->frequency_); + ESP_LOGCONFIG(TAG, + " SDA Pin: GPIO%u\n" + " SCL Pin: GPIO%u\n" + " Frequency: %" PRIu32 " Hz", + this->sda_pin_, this->scl_pin_, this->frequency_); if (timeout_ > 0) { ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 "us", this->timeout_); } diff --git a/esphome/components/i2c/i2c_bus_esp_idf.h b/esphome/components/i2c/i2c_bus_esp_idf.h index afb4c2d22b..ee29578944 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.h +++ b/esphome/components/i2c/i2c_bus_esp_idf.h @@ -2,9 +2,9 @@ #ifdef USE_ESP_IDF -#include "i2c_bus.h" -#include "esphome/core/component.h" #include +#include "esphome/core/component.h" +#include "i2c_bus.h" namespace esphome { namespace i2c { @@ -15,7 +15,7 @@ enum RecoveryCode { RECOVERY_COMPLETED, }; -class IDFI2CBus : public I2CBus, public Component { +class IDFI2CBus : public InternalI2CBus, public Component { public: void setup() override; void dump_config() override; @@ -31,6 +31,8 @@ class IDFI2CBus : public I2CBus, public Component { void set_frequency(uint32_t frequency) { frequency_ = frequency; } void set_timeout(uint32_t timeout) { timeout_ = timeout; } + int get_port() const override { return static_cast(this->port_); } + private: void recover_(); RecoveryCode recovery_result_; diff --git a/esphome/components/i2s_audio/i2s_audio.cpp b/esphome/components/i2s_audio/i2s_audio.cpp index 2de3f1d9f8..7ff21bba57 100644 --- a/esphome/components/i2s_audio/i2s_audio.cpp +++ b/esphome/components/i2s_audio/i2s_audio.cpp @@ -9,7 +9,7 @@ namespace i2s_audio { static const char *const TAG = "i2s_audio"; -#if defined(USE_ESP_IDF) && (ESP_IDF_VERSION_MAJOR >= 5) +#if ESP_IDF_VERSION_MAJOR >= 5 static const uint8_t I2S_NUM_MAX = SOC_I2S_NUM; // because IDF 5+ took this away :( #endif @@ -18,7 +18,7 @@ void I2SAudioComponent::setup() { static i2s_port_t next_port_num = I2S_NUM_0; if (next_port_num >= I2S_NUM_MAX) { - ESP_LOGE(TAG, "Too many I2S Audio components"); + ESP_LOGE(TAG, "Too many components"); this->mark_failed(); return; } diff --git a/esphome/components/i2s_audio/i2s_audio.h b/esphome/components/i2s_audio/i2s_audio.h index e839bcd891..cfccf7e01f 100644 --- a/esphome/components/i2s_audio/i2s_audio.h +++ b/esphome/components/i2s_audio/i2s_audio.h @@ -3,8 +3,8 @@ #ifdef USE_ESP32 #include "esphome/core/component.h" -#include "esphome/core/helpers.h" #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" #ifdef USE_I2S_LEGACY #include #else diff --git a/esphome/components/i2s_audio/media_player/__init__.py b/esphome/components/i2s_audio/media_player/__init__.py index f7ef134803..ad6665a5f5 100644 --- a/esphome/components/i2s_audio/media_player/__init__.py +++ b/esphome/components/i2s_audio/media_player/__init__.py @@ -114,7 +114,7 @@ async def to_code(config): cg.add(var.set_external_dac_channels(2 if config[CONF_MODE] == "stereo" else 1)) cg.add(var.set_i2s_comm_fmt_lsb(config[CONF_I2S_COMM_FMT] == "lsb")) - cg.add_library("WiFiClientSecure", None) + cg.add_library("NetworkClientSecure", None) cg.add_library("HTTPClient", None) - cg.add_library("esphome/ESP32-audioI2S", "2.2.0") + cg.add_library("esphome/ESP32-audioI2S", "2.3.0") cg.add_build_flag("-DAUDIO_NO_SD_FS") diff --git a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp index db6c3ae228..57e184d7f8 100644 --- a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +++ b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp @@ -244,8 +244,10 @@ void I2SAudioMediaPlayer::dump_config() { } } else { #endif - ESP_LOGCONFIG(TAG, " External DAC channels: %d", this->external_dac_channels_); - ESP_LOGCONFIG(TAG, " I2S DOUT Pin: %d", this->dout_pin_); + ESP_LOGCONFIG(TAG, + " External DAC channels: %d\n" + " I2S DOUT Pin: %d", + this->external_dac_channels_, this->dout_pin_); LOG_PIN(" Mute Pin: ", this->mute_pin_); #if SOC_I2S_SUPPORTS_DAC } diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index 1ce98d51d3..0477e0682d 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -45,7 +45,7 @@ void I2SAudioMicrophone::setup() { #if SOC_I2S_SUPPORTS_ADC if (this->adc_) { if (this->parent_->get_port() != I2S_NUM_0) { - ESP_LOGE(TAG, "Internal ADC only works on I2S0!"); + ESP_LOGE(TAG, "Internal ADC only works on I2S0"); this->mark_failed(); return; } @@ -55,7 +55,7 @@ void I2SAudioMicrophone::setup() { { if (this->pdm_) { if (this->parent_->get_port() != I2S_NUM_0) { - ESP_LOGE(TAG, "PDM only works on I2S0!"); + ESP_LOGE(TAG, "PDM only works on I2S0"); this->mark_failed(); return; } @@ -64,14 +64,14 @@ void I2SAudioMicrophone::setup() { this->active_listeners_semaphore_ = xSemaphoreCreateCounting(MAX_LISTENERS, MAX_LISTENERS); if (this->active_listeners_semaphore_ == nullptr) { - ESP_LOGE(TAG, "Failed to create semaphore"); + ESP_LOGE(TAG, "Creating semaphore failed"); this->mark_failed(); return; } this->event_group_ = xEventGroupCreate(); if (this->event_group_ == nullptr) { - ESP_LOGE(TAG, "Failed to create event group"); + ESP_LOGE(TAG, "Creating event group failed"); this->mark_failed(); return; } @@ -79,6 +79,15 @@ void I2SAudioMicrophone::setup() { this->configure_stream_settings_(); } +void I2SAudioMicrophone::dump_config() { + ESP_LOGCONFIG(TAG, + "Microphone:\n" + " Pin: %d\n" + " PDM: %s\n" + " DC offset correction: %s", + static_cast(this->din_pin_), YESNO(this->pdm_), YESNO(this->correct_dc_offset_)); +} + void I2SAudioMicrophone::configure_stream_settings_() { uint8_t channel_count = 1; #ifdef USE_I2S_LEGACY @@ -127,6 +136,7 @@ bool I2SAudioMicrophone::start_driver_() { if (!this->parent_->try_lock()) { return false; // Waiting for another i2s to return lock } + this->locked_driver_ = true; esp_err_t err; #ifdef USE_I2S_LEGACY @@ -151,7 +161,7 @@ bool I2SAudioMicrophone::start_driver_() { config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN); err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error installing I2S driver: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error installing driver: %s", esp_err_to_name(err)); return false; } @@ -174,7 +184,7 @@ bool I2SAudioMicrophone::start_driver_() { err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error installing I2S driver: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error installing driver: %s", esp_err_to_name(err)); return false; } @@ -183,7 +193,7 @@ bool I2SAudioMicrophone::start_driver_() { err = i2s_set_pin(this->parent_->get_port(), &pin_config); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error setting I2S pin: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error setting pin: %s", esp_err_to_name(err)); return false; } } @@ -198,7 +208,7 @@ bool I2SAudioMicrophone::start_driver_() { /* Allocate a new RX channel and get the handle of this channel */ err = i2s_new_channel(&chan_cfg, NULL, &this->rx_handle_); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error creating new I2S channel: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error creating channel: %s", esp_err_to_name(err)); return false; } @@ -270,14 +280,14 @@ bool I2SAudioMicrophone::start_driver_() { err = i2s_channel_init_std_mode(this->rx_handle_, &std_cfg); } if (err != ESP_OK) { - ESP_LOGE(TAG, "Error initializing I2S channel: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error initializing channel: %s", esp_err_to_name(err)); return false; } /* Before reading data, start the RX channel first */ i2s_channel_enable(this->rx_handle_); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error enabling I2S Microphone: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Enabling failed: %s", esp_err_to_name(err)); return false; } #endif @@ -304,31 +314,37 @@ void I2SAudioMicrophone::stop_driver_() { if (this->adc_) { err = i2s_adc_disable(this->parent_->get_port()); if (err != ESP_OK) { - ESP_LOGW(TAG, "Error disabling ADC - it may not have started: %s", esp_err_to_name(err)); + ESP_LOGW(TAG, "Error disabling ADC: %s", esp_err_to_name(err)); } } #endif err = i2s_stop(this->parent_->get_port()); if (err != ESP_OK) { - ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err)); + ESP_LOGW(TAG, "Error stopping: %s", esp_err_to_name(err)); } err = i2s_driver_uninstall(this->parent_->get_port()); if (err != ESP_OK) { - ESP_LOGW(TAG, "Error uninstalling I2S driver - it may not have started: %s", esp_err_to_name(err)); + ESP_LOGW(TAG, "Error uninstalling driver: %s", esp_err_to_name(err)); } #else - /* Have to stop the channel before deleting it */ - err = i2s_channel_disable(this->rx_handle_); - if (err != ESP_OK) { - ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err)); - } - /* If the handle is not needed any more, delete it to release the channel resources */ - err = i2s_del_channel(this->rx_handle_); - if (err != ESP_OK) { - ESP_LOGW(TAG, "Error deleting I2S channel - it may not have started: %s", esp_err_to_name(err)); + if (this->rx_handle_ != nullptr) { + /* Have to stop the channel before deleting it */ + err = i2s_channel_disable(this->rx_handle_); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Error stopping: %s", esp_err_to_name(err)); + } + /* If the handle is not needed any more, delete it to release the channel resources */ + err = i2s_del_channel(this->rx_handle_); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Error deleting channel: %s", esp_err_to_name(err)); + } + this->rx_handle_ = nullptr; } #endif - this->parent_->unlock(); + if (this->locked_driver_) { + this->parent_->unlock(); + this->locked_driver_ = false; + } } void I2SAudioMicrophone::mic_task(void *params) { @@ -400,7 +416,7 @@ size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_w // Ignore ESP_ERR_TIMEOUT if ticks_to_wait = 0, as it will read the data on the next call if (!this->status_has_warning()) { // Avoid spamming the logs with the error message if its repeated - ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err)); + ESP_LOGW(TAG, "Read error: %s", esp_err_to_name(err)); } this->status_set_warning(); return 0; @@ -428,19 +444,19 @@ void I2SAudioMicrophone::loop() { uint32_t event_group_bits = xEventGroupGetBits(this->event_group_); if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) { - ESP_LOGD(TAG, "Task started, attempting to allocate buffer"); + ESP_LOGV(TAG, "Task started, attempting to allocate buffer"); xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STARTING); } if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) { - ESP_LOGD(TAG, "Task is running and reading data"); + ESP_LOGV(TAG, "Task is running and reading data"); xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_RUNNING); this->state_ = microphone::STATE_RUNNING; } if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) { - ESP_LOGD(TAG, "Task finished, freeing resources and uninstalling I2S driver"); + ESP_LOGV(TAG, "Task finished, freeing resources and uninstalling driver"); vTaskDelete(this->task_handle_); this->task_handle_ = nullptr; @@ -470,7 +486,8 @@ void I2SAudioMicrophone::loop() { } if (!this->start_driver_()) { - this->status_momentary_error("I2S driver failed to start, unloading it and attempting again in 1 second", 1000); + ESP_LOGE(TAG, "Driver failed to start; retrying in 1 second"); + this->status_momentary_error("driver_fail", 1000); this->stop_driver_(); // Stop/frees whatever possibly started break; } @@ -480,7 +497,8 @@ void I2SAudioMicrophone::loop() { &this->task_handle_); if (this->task_handle_ == nullptr) { - this->status_momentary_error("Task failed to start, attempting again in 1 second", 1000); + ESP_LOGE(TAG, "Task failed to start, retrying in 1 second"); + this->status_momentary_error("task_fail", 1000); this->stop_driver_(); // Stops the driver to return the lock; will be reloaded in next attempt } } diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h index c35f88f8ee..5f66f2e962 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h @@ -18,6 +18,7 @@ namespace i2s_audio { class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, public Component { public: void setup() override; + void dump_config() override; void start() override; void stop() override; @@ -80,6 +81,7 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub bool pdm_{false}; bool correct_dc_offset_; + bool locked_driver_{false}; int32_t dc_offset_{0}; }; diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index f4c761ecc0..1042a7ebee 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -110,29 +110,48 @@ void I2SAudioSpeaker::setup() { } } +void I2SAudioSpeaker::dump_config() { + ESP_LOGCONFIG(TAG, + "Speaker:\n" + " Pin: %d\n" + " Buffer duration: %" PRIu32, + static_cast(this->dout_pin_), this->buffer_duration_ms_); + if (this->timeout_.has_value()) { + ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 " ms", this->timeout_.value()); + } +#ifdef USE_I2S_LEGACY +#if SOC_I2S_SUPPORTS_DAC + ESP_LOGCONFIG(TAG, " Internal DAC mode: %d", static_cast(this->internal_dac_mode_)); +#endif + ESP_LOGCONFIG(TAG, " Communication format: %d", static_cast(this->i2s_comm_fmt_)); +#else + ESP_LOGCONFIG(TAG, " Communication format: %s", this->i2s_comm_fmt_.c_str()); +#endif +} + void I2SAudioSpeaker::loop() { uint32_t event_group_bits = xEventGroupGetBits(this->event_group_); if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) { - ESP_LOGD(TAG, "Starting Speaker"); + ESP_LOGD(TAG, "Starting"); this->state_ = speaker::STATE_STARTING; xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING); } if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) { - ESP_LOGD(TAG, "Started Speaker"); + ESP_LOGD(TAG, "Started"); this->state_ = speaker::STATE_RUNNING; xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING); this->status_clear_warning(); this->status_clear_error(); } if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) { - ESP_LOGD(TAG, "Stopping Speaker"); + ESP_LOGD(TAG, "Stopping"); this->state_ = speaker::STATE_STOPPING; xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING); } if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) { if (!this->task_created_) { - ESP_LOGD(TAG, "Stopped Speaker"); + ESP_LOGD(TAG, "Stopped"); this->state_ = speaker::STATE_STOPPED; xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS); this->speaker_task_handle_ = nullptr; @@ -140,20 +159,19 @@ void I2SAudioSpeaker::loop() { } if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) { - this->status_set_error("Failed to start speaker task"); + this->status_set_error("Failed to start task"); xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START); } if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) { uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS; - ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits))); + ESP_LOGW(TAG, "Writing failed: %s", esp_err_to_name(err_bit_to_esp_err(error_bits))); this->status_set_warning(); } if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) { - this->status_set_error("Failed to adjust I2S bus to match the incoming audio"); - ESP_LOGE(TAG, - "Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8, + this->status_set_error("Failed to adjust bus to match incoming audio"); + ESP_LOGE(TAG, "Incompatible audio format: sample rate = %" PRIu32 ", channels = %u, bits per sample = %u", this->audio_stream_info_.get_sample_rate(), this->audio_stream_info_.get_channels(), this->audio_stream_info_.get_bits_per_sample()); } @@ -202,7 +220,7 @@ void I2SAudioSpeaker::set_mute_state(bool mute_state) { size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) { if (this->is_failed()) { - ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup"); + ESP_LOGE(TAG, "Setup failed; cannot play audio"); return 0; } if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) { @@ -466,7 +484,7 @@ bool I2SAudioSpeaker::send_esp_err_to_event_group_(esp_err_t err) { esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size) { if (this->data_buffer_ == nullptr) { // Allocate data buffer for temporarily storing audio from the ring buffer before writing to the I2S bus - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; this->data_buffer_ = allocator.allocate(data_buffer_size); } @@ -680,7 +698,7 @@ void I2SAudioSpeaker::delete_task_(size_t buffer_size) { this->audio_ring_buffer_.reset(); // Releases ownership of the shared_ptr if (this->data_buffer_ != nullptr) { - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; allocator.deallocate(this->data_buffer_, buffer_size); this->data_buffer_ = nullptr; } diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index b5e4b94bc4..eb2a0ae756 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -24,6 +24,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp float get_setup_priority() const override { return esphome::setup_priority::PROCESSOR; } void setup() override; + void dump_config() override; void loop() override; void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; } diff --git a/esphome/components/iaqcore/iaqcore.cpp b/esphome/components/iaqcore/iaqcore.cpp index 27ae776287..2a84eabf75 100644 --- a/esphome/components/iaqcore/iaqcore.cpp +++ b/esphome/components/iaqcore/iaqcore.cpp @@ -1,7 +1,7 @@ #include "iaqcore.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace iaqcore { diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index f056f0a128..41fd89cc58 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -89,8 +89,10 @@ void ILI9XXXDisplay::setup_pins_() { void ILI9XXXDisplay::dump_config() { LOG_DISPLAY("", "ili9xxx", this); - ESP_LOGCONFIG(TAG, " Width Offset: %u", this->offset_x_); - ESP_LOGCONFIG(TAG, " Height Offset: %u", this->offset_y_); + ESP_LOGCONFIG(TAG, + " Width Offset: %u\n" + " Height Offset: %u", + this->offset_x_, this->offset_y_); switch (this->buffer_color_mode_) { case BITS_8_INDEXED: ESP_LOGCONFIG(TAG, " Color mode: 8bit Indexed"); @@ -111,11 +113,14 @@ void ILI9XXXDisplay::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Busy Pin: ", this->busy_pin_); - ESP_LOGCONFIG(TAG, " Color order: %s", this->color_order_ == display::COLOR_ORDER_BGR ? "BGR" : "RGB"); - ESP_LOGCONFIG(TAG, " Swap_xy: %s", YESNO(this->swap_xy_)); - ESP_LOGCONFIG(TAG, " Mirror_x: %s", YESNO(this->mirror_x_)); - ESP_LOGCONFIG(TAG, " Mirror_y: %s", YESNO(this->mirror_y_)); - ESP_LOGCONFIG(TAG, " Invert colors: %s", YESNO(this->pre_invertcolors_)); + ESP_LOGCONFIG(TAG, + " Color order: %s\n" + " Swap_xy: %s\n" + " Mirror_x: %s\n" + " Mirror_y: %s\n" + " Invert colors: %s", + this->color_order_ == display::COLOR_ORDER_BGR ? "BGR" : "RGB", YESNO(this->swap_xy_), + YESNO(this->mirror_x_), YESNO(this->mirror_y_), YESNO(this->pre_invertcolors_)); if (this->is_failed()) { ESP_LOGCONFIG(TAG, " => Failed to init Memory: YES!"); diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index 0babcedc48..629bbb41cb 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -89,7 +89,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, void dump_config() override; void setup() override; - void on_shutdown() override { this->command(ILI9XXX_SLPIN); } + void on_powerdown() override { this->command(ILI9XXX_SLPIN); } display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, diff --git a/esphome/components/image/image.cpp b/esphome/components/image/image.cpp index 82e46e3460..7b65c4d0cb 100644 --- a/esphome/components/image/image.cpp +++ b/esphome/components/image/image.cpp @@ -1,6 +1,7 @@ #include "image.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" namespace esphome { namespace image { diff --git a/esphome/components/ina219/ina219.cpp b/esphome/components/ina219/ina219.cpp index 8d5271fa84..52a3b1e067 100644 --- a/esphome/components/ina219/ina219.cpp +++ b/esphome/components/ina219/ina219.cpp @@ -129,6 +129,13 @@ void INA219Component::setup() { } } +void INA219Component::on_powerdown() { + // Mode = 0 -> power down + if (!this->write_byte_16(INA219_REGISTER_CONFIG, 0)) { + ESP_LOGE(TAG, "powerdown error"); + } +} + void INA219Component::dump_config() { ESP_LOGCONFIG(TAG, "INA219:"); LOG_I2C_DEVICE(this); diff --git a/esphome/components/ina219/ina219.h b/esphome/components/ina219/ina219.h index a6c0f2bc4c..115fa886e0 100644 --- a/esphome/components/ina219/ina219.h +++ b/esphome/components/ina219/ina219.h @@ -15,6 +15,7 @@ class INA219Component : public PollingComponent, public i2c::I2CDevice { void dump_config() override; float get_setup_priority() const override; void update() override; + void on_powerdown() override; void set_shunt_resistance_ohm(float shunt_resistance_ohm) { shunt_resistance_ohm_ = shunt_resistance_ohm; } void set_max_current_a(float max_current_a) { max_current_a_ = max_current_a; } diff --git a/esphome/components/ina226/ina226.cpp b/esphome/components/ina226/ina226.cpp index f25e11e2e1..52e7127708 100644 --- a/esphome/components/ina226/ina226.cpp +++ b/esphome/components/ina226/ina226.cpp @@ -93,9 +93,12 @@ void INA226Component::dump_config() { } LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " ADC Conversion Time Bus Voltage: %d", INA226_ADC_TIMES[this->adc_time_voltage_ & 0b111]); - ESP_LOGCONFIG(TAG, " ADC Conversion Time Shunt Voltage: %d", INA226_ADC_TIMES[this->adc_time_current_ & 0b111]); - ESP_LOGCONFIG(TAG, " ADC Averaging Samples: %d", INA226_ADC_AVG_SAMPLES[this->adc_avg_samples_ & 0b111]); + ESP_LOGCONFIG(TAG, + " ADC Conversion Time Bus Voltage: %d\n" + " ADC Conversion Time Shunt Voltage: %d\n" + " ADC Averaging Samples: %d", + INA226_ADC_TIMES[this->adc_time_voltage_ & 0b111], INA226_ADC_TIMES[this->adc_time_current_ & 0b111], + INA226_ADC_AVG_SAMPLES[this->adc_avg_samples_ & 0b111]); LOG_SENSOR(" ", "Bus Voltage", this->bus_voltage_sensor_); LOG_SENSOR(" ", "Shunt Voltage", this->shunt_voltage_sensor_); diff --git a/esphome/components/ina2xx_base/ina2xx_base.cpp b/esphome/components/ina2xx_base/ina2xx_base.cpp index 82fd1e4d4d..2112a28b02 100644 --- a/esphome/components/ina2xx_base/ina2xx_base.cpp +++ b/esphome/components/ina2xx_base/ina2xx_base.cpp @@ -1,7 +1,7 @@ #include "ina2xx_base.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include #include @@ -206,12 +206,16 @@ void INA2XX::dump_config() { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " Shunt resistance = %f Ohm", this->shunt_resistance_ohm_); - ESP_LOGCONFIG(TAG, " Max current = %f A", this->max_current_a_); - ESP_LOGCONFIG(TAG, " Shunt temp coeff = %d ppm/°C", this->shunt_tempco_ppm_c_); - ESP_LOGCONFIG(TAG, " ADCRANGE = %d (%s)", (uint8_t) this->adc_range_, this->adc_range_ ? "±40.96 mV" : "±163.84 mV"); - ESP_LOGCONFIG(TAG, " CURRENT_LSB = %f", this->current_lsb_); - ESP_LOGCONFIG(TAG, " SHUNT_CAL = %d", this->shunt_cal_); + ESP_LOGCONFIG(TAG, + " Shunt resistance = %f Ohm\n" + " Max current = %f A\n" + " Shunt temp coeff = %d ppm/°C\n" + " ADCRANGE = %d (%s)\n" + " CURRENT_LSB = %f\n" + " SHUNT_CAL = %d", + this->shunt_resistance_ohm_, this->max_current_a_, this->shunt_tempco_ppm_c_, + (uint8_t) this->adc_range_, this->adc_range_ ? "±40.96 mV" : "±163.84 mV", this->current_lsb_, + this->shunt_cal_); ESP_LOGCONFIG(TAG, " ADC Samples = %d; ADC times: Bus = %d μs, Shunt = %d μs, Temp = %d μs", ADC_SAMPLES[0b111 & (uint8_t) this->adc_avg_samples_], diff --git a/esphome/components/inkplate6/display.py b/esphome/components/inkplate6/display.py index 492cdf9340..a7d31c0131 100644 --- a/esphome/components/inkplate6/display.py +++ b/esphome/components/inkplate6/display.py @@ -117,7 +117,6 @@ CONFIG_SCHEMA = cv.All( .extend(cv.polling_component_schema("5s")) .extend(i2c.i2c_device_schema(0x48)), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), - cv.only_with_arduino, ) diff --git a/esphome/components/inkplate6/inkplate.cpp b/esphome/components/inkplate6/inkplate.cpp index 8c853b75c2..b3d0b87e83 100644 --- a/esphome/components/inkplate6/inkplate.cpp +++ b/esphome/components/inkplate6/inkplate.cpp @@ -3,9 +3,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#ifdef USE_ESP32_FRAMEWORK_ARDUINO - -#include +#include namespace esphome { namespace inkplate6 { @@ -59,8 +57,8 @@ void Inkplate6::setup() { * Allocate buffers. May be called after setup to re-initialise if e.g. greyscale is changed. */ void Inkplate6::initialize_() { - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); - ExternalRAMAllocator allocator32(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; + RAMAllocator allocator32; uint32_t buffer_size = this->get_buffer_length_(); if (buffer_size == 0) return; @@ -186,9 +184,11 @@ void HOT Inkplate6::draw_absolute_pixel_internal(int x, int y, Color color) { void Inkplate6::dump_config() { LOG_DISPLAY("", "Inkplate", this); - ESP_LOGCONFIG(TAG, " Greyscale: %s", YESNO(this->greyscale_)); - ESP_LOGCONFIG(TAG, " Partial Updating: %s", YESNO(this->partial_updating_)); - ESP_LOGCONFIG(TAG, " Full Update Every: %d", this->full_update_every_); + ESP_LOGCONFIG(TAG, + " Greyscale: %s\n" + " Partial Updating: %s\n" + " Full Update Every: %d", + YESNO(this->greyscale_), YESNO(this->partial_updating_), this->full_update_every_); // Log pins LOG_PIN(" CKV Pin: ", this->ckv_pin_); LOG_PIN(" CL Pin: ", this->cl_pin_); @@ -721,5 +721,3 @@ void Inkplate6::pins_as_outputs_() { } // namespace inkplate6 } // namespace esphome - -#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/inkplate6/inkplate.h b/esphome/components/inkplate6/inkplate.h index 1680b84b7f..d8918bdf2a 100644 --- a/esphome/components/inkplate6/inkplate.h +++ b/esphome/components/inkplate6/inkplate.h @@ -5,8 +5,6 @@ #include "esphome/core/component.h" #include "esphome/core/hal.h" -#ifdef USE_ESP32_FRAMEWORK_ARDUINO - namespace esphome { namespace inkplate6 { @@ -254,5 +252,3 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice { } // namespace inkplate6 } // namespace esphome - -#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/integration/integration_sensor.cpp b/esphome/components/integration/integration_sensor.cpp index 2ac7caca21..c09778e79e 100644 --- a/esphome/components/integration/integration_sensor.cpp +++ b/esphome/components/integration/integration_sensor.cpp @@ -1,7 +1,7 @@ #include "integration_sensor.h" +#include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/hal.h" namespace esphome { namespace integration { diff --git a/esphome/components/key_collector/key_collector.cpp b/esphome/components/key_collector/key_collector.cpp index ffb4b47fa2..9cfc74f50e 100644 --- a/esphome/components/key_collector/key_collector.cpp +++ b/esphome/components/key_collector/key_collector.cpp @@ -32,8 +32,10 @@ void KeyCollector::dump_config() { if (!this->start_keys_.empty()) ESP_LOGCONFIG(TAG, " start keys '%s'", this->start_keys_.c_str()); if (!this->end_keys_.empty()) { - ESP_LOGCONFIG(TAG, " end keys '%s'", this->end_keys_.c_str()); - ESP_LOGCONFIG(TAG, " end key is required: %s", ONOFF(this->end_key_required_)); + ESP_LOGCONFIG(TAG, + " end keys '%s'\n" + " end key is required: %s", + this->end_keys_.c_str(), ONOFF(this->end_key_required_)); } if (!this->allowed_keys_.empty()) ESP_LOGCONFIG(TAG, " allowed keys '%s'", this->allowed_keys_.c_str()); diff --git a/esphome/components/kmeteriso/kmeteriso.cpp b/esphome/components/kmeteriso/kmeteriso.cpp index 4856ee0e8f..714df0b538 100644 --- a/esphome/components/kmeteriso/kmeteriso.cpp +++ b/esphome/components/kmeteriso/kmeteriso.cpp @@ -1,5 +1,6 @@ #include "kmeteriso.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -18,9 +19,8 @@ void KMeterISOComponent::setup() { // Mark as not failed before initializing. Some devices will turn off sensors to save on batteries // and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component. - if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) { - this->component_state_ &= ~COMPONENT_STATE_MASK; - this->component_state_ |= COMPONENT_STATE_CONSTRUCTION; + if (this->is_failed()) { + this->reset_to_construction_state(); } auto err = this->bus_->writev(this->address_, nullptr, 0); diff --git a/esphome/components/kuntze/kuntze.cpp b/esphome/components/kuntze/kuntze.cpp index 8ab7af8cd9..42545d9d54 100644 --- a/esphome/components/kuntze/kuntze.cpp +++ b/esphome/components/kuntze/kuntze.cpp @@ -77,8 +77,10 @@ void Kuntze::loop() { void Kuntze::update() { this->state_ = 1; } void Kuntze::dump_config() { - ESP_LOGCONFIG(TAG, "Kuntze:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "Kuntze:\n" + " Address: 0x%02X", + this->address_); LOG_SENSOR("", "pH", this->ph_sensor_); LOG_SENSOR("", "temperature", this->temperature_sensor_); LOG_SENSOR("", "DIS1", this->dis1_sensor_); diff --git a/esphome/components/lc709203f/__init__.py b/esphome/components/lc709203f/__init__.py new file mode 100644 index 0000000000..3be68d174f --- /dev/null +++ b/esphome/components/lc709203f/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@ilikecake"] diff --git a/esphome/components/lc709203f/lc709203f.cpp b/esphome/components/lc709203f/lc709203f.cpp new file mode 100644 index 0000000000..d95a2c1d5e --- /dev/null +++ b/esphome/components/lc709203f/lc709203f.cpp @@ -0,0 +1,299 @@ +#include "esphome/core/log.h" +#include "lc709203f.h" + +namespace esphome { +namespace lc709203f { + +static const char *const TAG = "lc709203f.sensor"; + +// Device I2C address. This address is fixed. +static const uint8_t LC709203F_I2C_ADDR_DEFAULT = 0x0B; + +// Device registers +static const uint8_t LC709203F_BEFORE_RSOC = 0x04; +static const uint8_t LC709203F_THERMISTOR_B = 0x06; +static const uint8_t LC709203F_INITIAL_RSOC = 0x07; +static const uint8_t LC709203F_CELL_TEMPERATURE = 0x08; +static const uint8_t LC709203F_CELL_VOLTAGE = 0x09; +static const uint8_t LC709203F_CURRENT_DIRECTION = 0x0A; +static const uint8_t LC709203F_APA = 0x0B; +static const uint8_t LC709203F_APT = 0x0C; +static const uint8_t LC709203F_RSOC = 0x0D; +static const uint8_t LC709203F_ITE = 0x0F; +static const uint8_t LC709203F_IC_VERSION = 0x11; +static const uint8_t LC709203F_CHANGE_OF_THE_PARAMETER = 0x12; +static const uint8_t LC709203F_ALARM_LOW_RSOC = 0x13; +static const uint8_t LC709203F_ALARM_LOW_CELL_VOLTAGE = 0x14; +static const uint8_t LC709203F_IC_POWER_MODE = 0x15; +static const uint8_t LC709203F_STATUS_BIT = 0x16; +static const uint8_t LC709203F_NUMBER_OF_THE_PARAMETER = 0x1A; + +static const uint8_t LC709203F_POWER_MODE_ON = 0x0001; +static const uint8_t LC709203F_POWER_MODE_SLEEP = 0x0002; + +// The number of times to retry an I2C transaction before giving up. In my experience, +// 10 is a good number here that will take care of most bus issues that require retry. +static const uint8_t LC709203F_I2C_RETRY_COUNT = 10; + +void Lc709203f::setup() { + // Note: The setup implements a small state machine. This is because we want to have + // delays before and after sending the RSOC command. The full init process should be: + // INIT->RSOC->TEMP_SETUP->NORMAL + // The setup() function will only perform the first part of the initialization process. + // Assuming no errors, the whole process should occur during the setup() function and + // the first two calls to update(). After that, the part should remain in normal mode + // until a device reset. + // + // This device can be picky about I2C communication and can error out occasionally. The + // get/set register functions impelment retry logic to retry the I2C transactions. The + // initialization code checks the return code from those functions. If they don't return + // NO_ERROR (0x00), that part of the initialization aborts and will be retried on the next + // call to update(). + ESP_LOGCONFIG(TAG, "Running setup"); + + // Set power mode to on. Note that, unlike some other similar devices, in sleep mode the IC + // does not record power usage. If there is significant power consumption during sleep mode, + // the pack RSOC will likely no longer be correct. Because of that, I do not implement + // sleep mode on this device. + + // Initialize device registers. If any of these fail, retry during the update() function. + if (this->set_register_(LC709203F_IC_POWER_MODE, LC709203F_POWER_MODE_ON) != i2c::NO_ERROR) { + return; + } + + if (this->set_register_(LC709203F_APA, this->apa_) != i2c::NO_ERROR) { + return; + } + + if (this->set_register_(LC709203F_CHANGE_OF_THE_PARAMETER, this->pack_voltage_) != i2c::NO_ERROR) { + return; + } + + this->state_ = STATE_RSOC; + // Note: Initialization continues in the update() function. +} + +void Lc709203f::update() { + uint16_t buffer; + + if (this->state_ == STATE_NORMAL) { + // Note: If we fail to read from the data registers, we do not report any sensor reading. + if (this->voltage_sensor_ != nullptr) { + if (this->get_register_(LC709203F_CELL_VOLTAGE, &buffer) == i2c::NO_ERROR) { + // Raw units are mV + this->voltage_sensor_->publish_state(static_cast(buffer) / 1000.0f); + this->status_clear_warning(); + } + } + if (this->battery_remaining_sensor_ != nullptr) { + if (this->get_register_(LC709203F_ITE, &buffer) == i2c::NO_ERROR) { + // Raw units are .1% + this->battery_remaining_sensor_->publish_state(static_cast(buffer) / 10.0f); + this->status_clear_warning(); + } + } + if (this->temperature_sensor_ != nullptr) { + // I can't test this with a real thermistor because I don't have a device with + // an attached thermistor. I have turned on the sensor and made sure that it + // sets up the registers properly. + if (this->get_register_(LC709203F_CELL_TEMPERATURE, &buffer) == i2c::NO_ERROR) { + // Raw units are .1 K + this->temperature_sensor_->publish_state((static_cast(buffer) / 10.0f) - 273.15f); + this->status_clear_warning(); + } + } + } else if (this->state_ == STATE_INIT) { + // Retry initializing the device registers. We should only get here if the init sequence + // failed during the setup() function. This would likely occur because of a repeated failures + // on the I2C bus. If any of these fail, retry the next time the update() function is called. + if (this->set_register_(LC709203F_IC_POWER_MODE, LC709203F_POWER_MODE_ON) != i2c::NO_ERROR) { + return; + } + + if (this->set_register_(LC709203F_APA, this->apa_) != i2c::NO_ERROR) { + return; + } + + if (this->set_register_(LC709203F_CHANGE_OF_THE_PARAMETER, this->pack_voltage_) != i2c::NO_ERROR) { + return; + } + + this->state_ = STATE_RSOC; + + } else if (this->state_ == STATE_RSOC) { + // We implement a delay here to send the initial RSOC command. + // This should run once on the first update() after initialization. + if (this->set_register_(LC709203F_INITIAL_RSOC, 0xAA55) == i2c::NO_ERROR) { + this->state_ = STATE_TEMP_SETUP; + } + } else if (this->state_ == STATE_TEMP_SETUP) { + // This should run once on the second update() after initialization. + if (this->temperature_sensor_ != nullptr) { + // This assumes that a thermistor is attached to the device as shown in the datahseet. + if (this->set_register_(LC709203F_STATUS_BIT, 0x0001) == i2c::NO_ERROR) { + if (this->set_register_(LC709203F_THERMISTOR_B, this->b_constant_) == i2c::NO_ERROR) { + this->state_ = STATE_NORMAL; + } + } + } else if (this->set_register_(LC709203F_STATUS_BIT, 0x0000) == i2c::NO_ERROR) { + // The device expects to get updates to the temperature in this mode. + // I am not doing that now. The temperature register defaults to 25C. + // In theory, we could have another temperature sensor and have ESPHome + // send updated temperature to the device occasionally, but I have no idea + // how to make that happen. + this->state_ = STATE_NORMAL; + } + } +} + +void Lc709203f::dump_config() { + ESP_LOGCONFIG(TAG, "LC709203F:"); + LOG_I2C_DEVICE(this); + + LOG_UPDATE_INTERVAL(this); + ESP_LOGCONFIG(TAG, + " Pack Size: %d mAH\n" + " Pack APA: 0x%02X", + this->pack_size_, this->apa_); + + // This is only true if the pack_voltage_ is either 0x0000 or 0x0001. The config validator + // should have already verified this. + ESP_LOGCONFIG(TAG, " Pack Rated Voltage: 3.%sV", this->pack_voltage_ == 0x0000 ? "8" : "7"); + + LOG_SENSOR(" ", "Voltage", this->voltage_sensor_); + LOG_SENSOR(" ", "Battery Remaining", this->battery_remaining_sensor_); + + if (this->temperature_sensor_ != nullptr) { + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + ESP_LOGCONFIG(TAG, " B_Constant: %d", this->b_constant_); + } else { + ESP_LOGCONFIG(TAG, " No Temperature Sensor"); + } +} + +uint8_t Lc709203f::get_register_(uint8_t register_to_read, uint16_t *register_value) { + i2c::ErrorCode return_code; + uint8_t read_buffer[6]; + + read_buffer[0] = (this->address_) << 1; + read_buffer[1] = register_to_read; + read_buffer[2] = ((this->address_) << 1) | 0x01; + + for (uint8_t i = 0; i <= LC709203F_I2C_RETRY_COUNT; i++) { + // Note: the read_register() function does not send a stop between the write and + // the read portions of the I2C transation when you set the last variable to 'false' + // as we do below. Some of the other I2C read functions such as the generic read() + // function will send a stop between the read and the write portion of the I2C + // transaction. This is bad in this case and will result in reading nothing but 0xFFFF + // from the registers. + return_code = this->read_register(register_to_read, &read_buffer[3], 3, false); + if (return_code != i2c::NO_ERROR) { + // Error on the i2c bus + this->status_set_warning( + str_sprintf("Error code %d when reading from register 0x%02X", return_code, register_to_read).c_str()); + } else if (this->crc8_(read_buffer, 5) != read_buffer[5]) { + // I2C indicated OK, but the CRC of the data does not matcth. + this->status_set_warning(str_sprintf("CRC error reading from register 0x%02X", register_to_read).c_str()); + } else { + *register_value = ((uint16_t) read_buffer[4] << 8) | (uint16_t) read_buffer[3]; + return i2c::NO_ERROR; + } + } + + // If we get here, we tried LC709203F_I2C_RETRY_COUNT times to read the register and + // failed each time. Set the register value to 0 and return the I2C error code or 0xFF + // to indicate a CRC failure. It will be up to the higher level code what to do when + // this happens. + *register_value = 0x0000; + if (return_code != i2c::NO_ERROR) { + return return_code; + } else { + return 0xFF; + } +} + +uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set) { + i2c::ErrorCode return_code; + uint8_t write_buffer[5]; + + // Note: We don't actually send byte[0] of the buffer. We include it because it is + // part of the CRC calculation. + write_buffer[0] = (this->address_) << 1; + write_buffer[1] = register_to_set; + write_buffer[2] = value_to_set & 0xFF; // Low byte + write_buffer[3] = (value_to_set >> 8) & 0xFF; // High byte + write_buffer[4] = this->crc8_(write_buffer, 4); + + for (uint8_t i = 0; i <= LC709203F_I2C_RETRY_COUNT; i++) { + // Note: we don't write the first byte of the write buffer to the device. + // This is done automatically by the write() function. + return_code = this->write(&write_buffer[1], 4, true); + if (return_code == i2c::NO_ERROR) { + return return_code; + } else { + this->status_set_warning( + str_sprintf("Error code %d when writing to register 0x%02X", return_code, register_to_set).c_str()); + } + } + + // If we get here, we tried to send the data LC709203F_I2C_RETRY_COUNT times and failed. + // We return the I2C error code, it is up to the higher level code what to do about it. + return return_code; +} + +uint8_t Lc709203f::crc8_(uint8_t *byte_buffer, uint8_t length_of_crc) { + uint8_t crc = 0x00; + const uint8_t polynomial(0x07); + + for (uint8_t j = length_of_crc; j; --j) { + crc ^= *byte_buffer++; + + for (uint8_t i = 8; i; --i) { + crc = (crc & 0x80) ? (crc << 1) ^ polynomial : (crc << 1); + } + } + return crc; +} + +void Lc709203f::set_pack_size(uint16_t pack_size) { + static const uint16_t PACK_SIZE_ARRAY[6] = {100, 200, 500, 1000, 2000, 3000}; + static const uint16_t APA_ARRAY[6] = {0x08, 0x0B, 0x10, 0x19, 0x2D, 0x36}; + float slope; + float intercept; + + this->pack_size_ = pack_size; // Pack size in mAH + + // The size is used to calculate the 'Adjustment Pack Application' number. + // Here we assume a type 01 or type 03 battery and do a linear curve fit to find the APA. + for (uint8_t i = 0; i < 6; i++) { + if (PACK_SIZE_ARRAY[i] == pack_size) { + // If the pack size is exactly one of the values in the array. + this->apa_ = APA_ARRAY[i]; + return; + } else if ((i > 0) && (PACK_SIZE_ARRAY[i] > pack_size) && (PACK_SIZE_ARRAY[i - 1] < pack_size)) { + // If the pack size is between the current array element and the previous. Do a linear + // Curve fit to determine the APA value. + + // Type casting is required here to avoid interger division + slope = static_cast(APA_ARRAY[i] - APA_ARRAY[i - 1]) / + static_cast(PACK_SIZE_ARRAY[i] - PACK_SIZE_ARRAY[i - 1]); + + // Type casting might not be needed here. + intercept = static_cast(APA_ARRAY[i]) - slope * static_cast(PACK_SIZE_ARRAY[i]); + + this->apa_ = static_cast(slope * pack_size + intercept); + return; + } + } + // We should never get here. If we do, it means we never set the pack APA. This should + // not be possible because of the config validation. However, if it does happen, the + // consequence is that the RSOC values will likley not be as accurate. However, it should + // not cause an error or crash, so I am not doing any additional checking here. +} + +void Lc709203f::set_thermistor_b_constant(uint16_t b_constant) { this->b_constant_ = b_constant; } + +void Lc709203f::set_pack_voltage(LC709203FBatteryVoltage pack_voltage) { this->pack_voltage_ = pack_voltage; } + +} // namespace lc709203f +} // namespace esphome diff --git a/esphome/components/lc709203f/lc709203f.h b/esphome/components/lc709203f/lc709203f.h new file mode 100644 index 0000000000..3b5b04775f --- /dev/null +++ b/esphome/components/lc709203f/lc709203f.h @@ -0,0 +1,55 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace lc709203f { + +enum LC709203FState { + STATE_INIT, + STATE_RSOC, + STATE_TEMP_SETUP, + STATE_NORMAL, +}; + +/// Enum listing allowable voltage settings for the LC709203F. +enum LC709203FBatteryVoltage { + LC709203F_BATTERY_VOLTAGE_3_8 = 0x0000, + LC709203F_BATTERY_VOLTAGE_3_7 = 0x0001, +}; + +class Lc709203f : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void update() override; + void dump_config() override; + + void set_pack_size(uint16_t pack_size); + void set_thermistor_b_constant(uint16_t b_constant); + void set_pack_voltage(LC709203FBatteryVoltage pack_voltage); + void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } + void set_battery_remaining_sensor(sensor::Sensor *battery_remaining_sensor) { + battery_remaining_sensor_ = battery_remaining_sensor; + } + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } + + private: + uint8_t get_register_(uint8_t register_to_read, uint16_t *register_value); + uint8_t set_register_(uint8_t register_to_set, uint16_t value_to_set); + uint8_t crc8_(uint8_t *byte_buffer, uint8_t length_of_crc); + + protected: + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *battery_remaining_sensor_{nullptr}; + sensor::Sensor *temperature_sensor_{nullptr}; + uint16_t pack_size_; + uint16_t apa_; + uint16_t b_constant_; + LC709203FState state_ = STATE_INIT; + uint16_t pack_voltage_; +}; + +} // namespace lc709203f +} // namespace esphome diff --git a/esphome/components/lc709203f/sensor.py b/esphome/components/lc709203f/sensor.py new file mode 100644 index 0000000000..eb08a522e5 --- /dev/null +++ b/esphome/components/lc709203f/sensor.py @@ -0,0 +1,93 @@ +import esphome.codegen as cg +from esphome.components import i2c, sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_BATTERY_VOLTAGE, + CONF_ID, + CONF_SIZE, + CONF_TEMPERATURE, + CONF_VOLTAGE, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLTAGE, + ENTITY_CATEGORY_DIAGNOSTIC, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_PERCENT, + UNIT_VOLT, +) + +DEPENDENCIES = ["i2c"] + +lc709203f_ns = cg.esphome_ns.namespace("lc709203f") + +CONF_B_CONSTANT = "b_constant" + +LC709203FBatteryVoltage = lc709203f_ns.enum("LC709203FBatteryVoltage") +BATTERY_VOLTAGE_OPTIONS = { + "3.7": LC709203FBatteryVoltage.LC709203F_BATTERY_VOLTAGE_3_7, + "3.8": LC709203FBatteryVoltage.LC709203F_BATTERY_VOLTAGE_3_8, +} + +lc709203f = lc709203f_ns.class_("Lc709203f", cg.PollingComponent, i2c.I2CDevice) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(lc709203f), + cv.Optional(CONF_SIZE, default="500"): cv.int_range(100, 3000), + cv.Optional(CONF_VOLTAGE, default="3.7"): cv.enum( + BATTERY_VOLTAGE_OPTIONS, upper=True + ), + cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema( + unit_of_measurement=UNIT_VOLT, + accuracy_decimals=3, + device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=3, + device_class=DEVICE_CLASS_BATTERY, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ).extend( + { + cv.Required(CONF_B_CONSTANT): cv.int_range(0, 0xFFFF), + } + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x0B)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + cg.add(var.set_pack_size(config.get(CONF_SIZE))) + cg.add(var.set_pack_voltage(BATTERY_VOLTAGE_OPTIONS[config[CONF_VOLTAGE]])) + + if voltage_config := config.get(CONF_BATTERY_VOLTAGE): + sens = await sensor.new_sensor(voltage_config) + cg.add(var.set_voltage_sensor(sens)) + + if level_config := config.get(CONF_BATTERY_LEVEL): + sens = await sensor.new_sensor(level_config) + cg.add(var.set_battery_remaining_sensor(sens)) + + if temp_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temp_config) + cg.add(var.set_temperature_sensor(sens)) + cg.add(var.set_thermistor_b_constant(temp_config[CONF_B_CONSTANT])) diff --git a/esphome/components/lcd_base/lcd_display.cpp b/esphome/components/lcd_base/lcd_display.cpp index 65d7aa508f..d3434cce10 100644 --- a/esphome/components/lcd_base/lcd_display.cpp +++ b/esphome/components/lcd_base/lcd_display.cpp @@ -1,7 +1,7 @@ #include "lcd_display.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace lcd_base { diff --git a/esphome/components/lcd_gpio/gpio_lcd_display.cpp b/esphome/components/lcd_gpio/gpio_lcd_display.cpp index 1b1b07c464..afa74643fb 100644 --- a/esphome/components/lcd_gpio/gpio_lcd_display.cpp +++ b/esphome/components/lcd_gpio/gpio_lcd_display.cpp @@ -24,8 +24,10 @@ void GPIOLCDDisplay::setup() { LCDDisplay::setup(); } void GPIOLCDDisplay::dump_config() { - ESP_LOGCONFIG(TAG, "GPIO LCD Display:"); - ESP_LOGCONFIG(TAG, " Columns: %u, Rows: %u", this->columns_, this->rows_); + ESP_LOGCONFIG(TAG, + "GPIO LCD Display:\n" + " Columns: %u, Rows: %u", + this->columns_, this->rows_); LOG_PIN(" RS Pin: ", this->rs_pin_); LOG_PIN(" RW Pin: ", this->rw_pin_); LOG_PIN(" Enable Pin: ", this->enable_pin_); diff --git a/esphome/components/lcd_menu/lcd_menu.cpp b/esphome/components/lcd_menu/lcd_menu.cpp index 74ada5e584..c664b394bf 100644 --- a/esphome/components/lcd_menu/lcd_menu.cpp +++ b/esphome/components/lcd_menu/lcd_menu.cpp @@ -19,10 +19,12 @@ void LCDCharacterMenuComponent::setup() { float LCDCharacterMenuComponent::get_setup_priority() const { return setup_priority::PROCESSOR - 1.0f; } void LCDCharacterMenuComponent::dump_config() { - ESP_LOGCONFIG(TAG, "LCD Menu"); - ESP_LOGCONFIG(TAG, " Columns: %u, Rows: %u", this->columns_, this->rows_); - ESP_LOGCONFIG(TAG, " Mark characters: %02x, %02x, %02x, %02x", this->mark_selected_, this->mark_editing_, - this->mark_submenu_, this->mark_back_); + ESP_LOGCONFIG(TAG, + "LCD Menu\n" + " Columns: %u, Rows: %u\n" + " Mark characters: %02x, %02x, %02x, %02x", + this->columns_, this->rows_, this->mark_selected_, this->mark_editing_, this->mark_submenu_, + this->mark_back_); if (this->is_failed()) { ESP_LOGE(TAG, "The connected display failed, the menu is disabled!"); } diff --git a/esphome/components/lcd_pcf8574/pcf8574_display.cpp b/esphome/components/lcd_pcf8574/pcf8574_display.cpp index 90ba3ba876..0f06548b13 100644 --- a/esphome/components/lcd_pcf8574/pcf8574_display.cpp +++ b/esphome/components/lcd_pcf8574/pcf8574_display.cpp @@ -21,8 +21,10 @@ void PCF8574LCDDisplay::setup() { LCDDisplay::setup(); } void PCF8574LCDDisplay::dump_config() { - ESP_LOGCONFIG(TAG, "PCF8574 LCD Display:"); - ESP_LOGCONFIG(TAG, " Columns: %u, Rows: %u", this->columns_, this->rows_); + ESP_LOGCONFIG(TAG, + "PCF8574 LCD Display:\n" + " Columns: %u, Rows: %u", + this->columns_, this->rows_); LOG_I2C_DEVICE(this); LOG_UPDATE_INTERVAL(this); if (this->is_failed()) { diff --git a/esphome/components/ld2410/ld2410.cpp b/esphome/components/ld2410/ld2410.cpp index 20411be078..d7007ae0bd 100644 --- a/esphome/components/ld2410/ld2410.cpp +++ b/esphome/components/ld2410/ld2410.cpp @@ -72,9 +72,11 @@ void LD2410Component::dump_config() { } #endif this->read_all_info(); - ESP_LOGCONFIG(TAG, " Throttle_ : %ums", this->throttle_); - ESP_LOGCONFIG(TAG, " MAC Address : %s", const_cast(this->mac_.c_str())); - ESP_LOGCONFIG(TAG, " Firmware Version : %s", const_cast(this->version_.c_str())); + ESP_LOGCONFIG(TAG, + " Throttle_ : %ums\n" + " MAC Address : %s\n" + " Firmware Version : %s", + this->throttle_, const_cast(this->mac_.c_str()), const_cast(this->version_.c_str())); } void LD2410Component::setup() { diff --git a/esphome/components/ld2410/number/__init__.py b/esphome/components/ld2410/number/__init__.py index 1f9c50db1f..ffa4e7e146 100644 --- a/esphome/components/ld2410/number/__init__.py +++ b/esphome/components/ld2410/number/__init__.py @@ -3,6 +3,8 @@ from esphome.components import number import esphome.config_validation as cv from esphome.const import ( CONF_ID, + CONF_MOVE_THRESHOLD, + CONF_STILL_THRESHOLD, CONF_TIMEOUT, DEVICE_CLASS_DISTANCE, DEVICE_CLASS_ILLUMINANCE, @@ -24,8 +26,6 @@ MaxDistanceTimeoutNumber = ld2410_ns.class_("MaxDistanceTimeoutNumber", number.N CONF_MAX_MOVE_DISTANCE_GATE = "max_move_distance_gate" CONF_MAX_STILL_DISTANCE_GATE = "max_still_distance_gate" CONF_LIGHT_THRESHOLD = "light_threshold" -CONF_STILL_THRESHOLD = "still_threshold" -CONF_MOVE_THRESHOLD = "move_threshold" TIMEOUT_GROUP = "timeout" diff --git a/esphome/components/ld2410/sensor.py b/esphome/components/ld2410/sensor.py index 38de1799cc..92245ea9a6 100644 --- a/esphome/components/ld2410/sensor.py +++ b/esphome/components/ld2410/sensor.py @@ -3,6 +3,7 @@ from esphome.components import sensor import esphome.config_validation as cv from esphome.const import ( CONF_LIGHT, + CONF_MOVING_DISTANCE, DEVICE_CLASS_DISTANCE, DEVICE_CLASS_ILLUMINANCE, ENTITY_CATEGORY_DIAGNOSTIC, @@ -17,7 +18,6 @@ from esphome.const import ( from . import CONF_LD2410_ID, LD2410Component DEPENDENCIES = ["ld2410"] -CONF_MOVING_DISTANCE = "moving_distance" CONF_STILL_DISTANCE = "still_distance" CONF_MOVING_ENERGY = "moving_energy" CONF_STILL_ENERGY = "still_energy" diff --git a/esphome/components/ld2410/switch/__init__.py b/esphome/components/ld2410/switch/__init__.py index aecad606be..71b8a40a29 100644 --- a/esphome/components/ld2410/switch/__init__.py +++ b/esphome/components/ld2410/switch/__init__.py @@ -2,6 +2,7 @@ import esphome.codegen as cg from esphome.components import switch import esphome.config_validation as cv from esphome.const import ( + CONF_BLUETOOTH, DEVICE_CLASS_SWITCH, ENTITY_CATEGORY_CONFIG, ICON_BLUETOOTH, @@ -14,7 +15,6 @@ BluetoothSwitch = ld2410_ns.class_("BluetoothSwitch", switch.Switch) EngineeringModeSwitch = ld2410_ns.class_("EngineeringModeSwitch", switch.Switch) CONF_ENGINEERING_MODE = "engineering_mode" -CONF_BLUETOOTH = "bluetooth" CONFIG_SCHEMA = { cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), diff --git a/esphome/components/ld2420/ld2420.cpp b/esphome/components/ld2420/ld2420.cpp index f94a5d7781..5b3206bf12 100644 --- a/esphome/components/ld2420/ld2420.cpp +++ b/esphome/components/ld2420/ld2420.cpp @@ -65,9 +65,11 @@ static const char *const TAG = "ld2420"; float LD2420Component::get_setup_priority() const { return setup_priority::BUS; } void LD2420Component::dump_config() { - ESP_LOGCONFIG(TAG, "LD2420:"); - ESP_LOGCONFIG(TAG, " Firmware Version : %7s", this->ld2420_firmware_ver_); - ESP_LOGCONFIG(TAG, "LD2420 Number:"); + ESP_LOGCONFIG(TAG, + "LD2420:\n" + " Firmware Version : %7s\n" + "LD2420 Number:", + this->ld2420_firmware_ver_); #ifdef USE_NUMBER LOG_NUMBER(TAG, " Gate Timeout:", this->gate_timeout_number_); LOG_NUMBER(TAG, " Gate Max Distance:", this->max_gate_distance_number_); @@ -158,7 +160,7 @@ void LD2420Component::apply_config_action() { ESP_LOGCONFIG(TAG, "No configuration change detected"); return; } - ESP_LOGCONFIG(TAG, "Reconfiguring LD2420..."); + ESP_LOGCONFIG(TAG, "Reconfiguring LD2420"); if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections."); this->mark_failed(); @@ -180,7 +182,7 @@ void LD2420Component::apply_config_action() { } void LD2420Component::factory_reset_action() { - ESP_LOGCONFIG(TAG, "Setting factory defaults..."); + ESP_LOGCONFIG(TAG, "Setting factory defaults"); if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections."); this->mark_failed(); @@ -209,7 +211,7 @@ void LD2420Component::factory_reset_action() { } void LD2420Component::restart_module_action() { - ESP_LOGCONFIG(TAG, "Restarting LD2420 module..."); + ESP_LOGCONFIG(TAG, "Restarting LD2420 module"); this->send_module_restart(); this->set_timeout(250, [this]() { this->set_config_mode(true); diff --git a/esphome/components/ld2420/number/__init__.py b/esphome/components/ld2420/number/__init__.py index 1558243cc2..a2637b7b06 100644 --- a/esphome/components/ld2420/number/__init__.py +++ b/esphome/components/ld2420/number/__init__.py @@ -3,6 +3,8 @@ from esphome.components import number import esphome.config_validation as cv from esphome.const import ( CONF_ID, + CONF_MOVE_THRESHOLD, + CONF_STILL_THRESHOLD, DEVICE_CLASS_DISTANCE, ENTITY_CATEGORY_CONFIG, ICON_MOTION_SENSOR, @@ -31,8 +33,6 @@ LD2420StillThresholdNumbers = ld2420_ns.class_( ) CONF_MIN_GATE_DISTANCE = "min_gate_distance" CONF_MAX_GATE_DISTANCE = "max_gate_distance" -CONF_STILL_THRESHOLD = "still_threshold" -CONF_MOVE_THRESHOLD = "move_threshold" CONF_GATE_MOVE_SENSITIVITY = "gate_move_sensitivity" CONF_GATE_STILL_SENSITIVITY = "gate_still_sensitivity" CONF_GATE_SELECT = "gate_select" diff --git a/esphome/components/ld2420/sensor/__init__.py b/esphome/components/ld2420/sensor/__init__.py index e39ca99ae1..6dde35753a 100644 --- a/esphome/components/ld2420/sensor/__init__.py +++ b/esphome/components/ld2420/sensor/__init__.py @@ -1,13 +1,17 @@ import esphome.codegen as cg from esphome.components import sensor import esphome.config_validation as cv -from esphome.const import CONF_ID, DEVICE_CLASS_DISTANCE, UNIT_CENTIMETER +from esphome.const import ( + CONF_ID, + CONF_MOVING_DISTANCE, + DEVICE_CLASS_DISTANCE, + UNIT_CENTIMETER, +) from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns LD2420Sensor = ld2420_ns.class_("LD2420Sensor", sensor.Sensor, cg.Component) -CONF_MOVING_DISTANCE = "moving_distance" CONF_GATE_ENERGY = "gate_energy" CONFIG_SCHEMA = cv.All( diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index 59aeb5ccb4..519e4d89a3 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -188,9 +188,11 @@ void LD2450Component::dump_config() { #ifdef USE_NUMBER LOG_NUMBER(" ", "PresenceTimeoutNumber", this->presence_timeout_number_); #endif - ESP_LOGCONFIG(TAG, " Throttle : %ums", this->throttle_); - ESP_LOGCONFIG(TAG, " MAC Address : %s", const_cast(this->mac_.c_str())); - ESP_LOGCONFIG(TAG, " Firmware version : %s", const_cast(this->version_.c_str())); + ESP_LOGCONFIG(TAG, + " Throttle : %ums\n" + " MAC Address : %s\n" + " Firmware version : %s", + this->throttle_, const_cast(this->mac_.c_str()), const_cast(this->version_.c_str())); } void LD2450Component::loop() { diff --git a/esphome/components/ld2450/sensor.py b/esphome/components/ld2450/sensor.py index 21580c5801..071ce8aa32 100644 --- a/esphome/components/ld2450/sensor.py +++ b/esphome/components/ld2450/sensor.py @@ -6,6 +6,8 @@ from esphome.const import ( CONF_DISTANCE, CONF_RESOLUTION, CONF_SPEED, + CONF_X, + CONF_Y, DEVICE_CLASS_DISTANCE, DEVICE_CLASS_SPEED, UNIT_DEGREES, @@ -19,8 +21,6 @@ DEPENDENCIES = ["ld2450"] CONF_MOVING_TARGET_COUNT = "moving_target_count" CONF_STILL_TARGET_COUNT = "still_target_count" CONF_TARGET_COUNT = "target_count" -CONF_X = "x" -CONF_Y = "y" ICON_ACCOUNT_GROUP = "mdi:account-group" ICON_ACCOUNT_SWITCH = "mdi:account-switch" diff --git a/esphome/components/ld2450/switch/__init__.py b/esphome/components/ld2450/switch/__init__.py index fb3969cf50..2d76b75781 100644 --- a/esphome/components/ld2450/switch/__init__.py +++ b/esphome/components/ld2450/switch/__init__.py @@ -2,6 +2,7 @@ import esphome.codegen as cg from esphome.components import switch import esphome.config_validation as cv from esphome.const import ( + CONF_BLUETOOTH, DEVICE_CLASS_SWITCH, ENTITY_CATEGORY_CONFIG, ICON_BLUETOOTH, @@ -13,7 +14,6 @@ from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns BluetoothSwitch = ld2450_ns.class_("BluetoothSwitch", switch.Switch) MultiTargetSwitch = ld2450_ns.class_("MultiTargetSwitch", switch.Switch) -CONF_BLUETOOTH = "bluetooth" CONF_MULTI_TARGET = "multi_target" CONFIG_SCHEMA = { diff --git a/esphome/components/ledc/ledc_output.cpp b/esphome/components/ledc/ledc_output.cpp index 567fa5ac07..2ae2656f54 100644 --- a/esphome/components/ledc/ledc_output.cpp +++ b/esphome/components/ledc/ledc_output.cpp @@ -3,28 +3,16 @@ #ifdef USE_ESP32 -#ifdef USE_ARDUINO -#include -#endif #include - #include #define CLOCK_FREQUENCY 80e6f -#ifdef USE_ARDUINO -#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK -#undef CLOCK_FREQUENCY -// starting with ESP32 Arduino 2.0.2, the 40MHz crystal is used as clock by default if supported -#define CLOCK_FREQUENCY 40e6f -#endif -#else #ifdef SOC_LEDC_SUPPORT_APB_CLOCK #define DEFAULT_CLK LEDC_USE_APB_CLK #else #define DEFAULT_CLK LEDC_AUTO_CLK #endif -#endif static const uint8_t SETUP_ATTEMPT_COUNT_MAX = 5; @@ -34,7 +22,6 @@ namespace ledc { static const char *const TAG = "ledc.output"; static const int MAX_RES_BITS = LEDC_TIMER_BIT_MAX - 1; -#ifdef USE_ESP_IDF #if SOC_LEDC_SUPPORT_HS_MODE // Only ESP32 has LEDC_HIGH_SPEED_MODE inline ledc_mode_t get_speed_mode(uint8_t channel) { return channel < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; } @@ -44,7 +31,6 @@ inline ledc_mode_t get_speed_mode(uint8_t channel) { return channel < 8 ? LEDC_H // https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/ledc.html#functionality-overview inline ledc_mode_t get_speed_mode(uint8_t) { return LEDC_LOW_SPEED_MODE; } #endif -#endif float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) { return static_cast(CLOCK_FREQUENCY) / static_cast(1 << bit_depth); @@ -68,7 +54,6 @@ optional ledc_bit_depth_for_frequency(float frequency) { return {}; } -#ifdef USE_ESP_IDF esp_err_t configure_timer_frequency(ledc_mode_t speed_mode, ledc_timer_t timer_num, ledc_channel_t chan_num, uint8_t channel, uint8_t &bit_depth, float frequency) { bit_depth = *ledc_bit_depth_for_frequency(frequency); @@ -98,13 +83,10 @@ esp_err_t configure_timer_frequency(ledc_mode_t speed_mode, ledc_timer_t timer_n return init_result; } -#endif -#ifdef USE_ESP_IDF constexpr int ledc_angle_to_htop(float angle, uint8_t bit_depth) { return static_cast(angle * ((1U << bit_depth) - 1) / 360.0f); } -#endif // USE_ESP_IDF void LEDCOutput::write_state(float state) { if (!this->initialized_) { @@ -120,10 +102,6 @@ void LEDCOutput::write_state(float state) { const float duty_rounded = roundf(state * max_duty); auto duty = static_cast(duty_rounded); ESP_LOGV(TAG, "Setting duty: %" PRIu32 " on channel %u", duty, this->channel_); -#ifdef USE_ARDUINO - ledcWrite(this->channel_, duty); -#endif -#ifdef USE_ESP_IDF auto speed_mode = get_speed_mode(this->channel_); auto chan_num = static_cast(this->channel_ % 8); int hpoint = ledc_angle_to_htop(this->phase_angle_, this->bit_depth_); @@ -135,18 +113,10 @@ void LEDCOutput::write_state(float state) { ledc_set_duty_with_hpoint(speed_mode, chan_num, duty, hpoint); ledc_update_duty(speed_mode, chan_num); } -#endif } void LEDCOutput::setup() { - ESP_LOGV(TAG, "Entering setup..."); -#ifdef USE_ARDUINO - this->update_frequency(this->frequency_); - this->turn_off(); - // Attach pin after setting default value - ledcAttachPin(this->pin_->get_pin(), this->channel_); -#endif -#ifdef USE_ESP_IDF + ESP_LOGCONFIG(TAG, "Running setup"); auto speed_mode = get_speed_mode(this->channel_); auto timer_num = static_cast((this->channel_ % 8) / 2); auto chan_num = static_cast(this->channel_ % 8); @@ -175,16 +145,17 @@ void LEDCOutput::setup() { ledc_channel_config(&chan_conf); this->initialized_ = true; this->status_clear_error(); -#endif } void LEDCOutput::dump_config() { ESP_LOGCONFIG(TAG, "Output:"); LOG_PIN(" Pin ", this->pin_); - ESP_LOGCONFIG(TAG, " Channel: %u", this->channel_); - ESP_LOGCONFIG(TAG, " PWM Frequency: %.1f Hz", this->frequency_); - ESP_LOGCONFIG(TAG, " Phase angle: %.1f°", this->phase_angle_); - ESP_LOGCONFIG(TAG, " Bit depth: %u", this->bit_depth_); + ESP_LOGCONFIG(TAG, + " Channel: %u\n" + " PWM Frequency: %.1f Hz\n" + " Phase angle: %.1f°\n" + " Bit depth: %u", + this->channel_, this->frequency_, this->phase_angle_, this->bit_depth_); ESP_LOGV(TAG, " Max frequency for bit depth: %f", ledc_max_frequency_for_bit_depth(this->bit_depth_)); ESP_LOGV(TAG, " Min frequency for bit depth: %f", ledc_min_frequency_for_bit_depth(this->bit_depth_, (this->frequency_ < 100))); @@ -206,38 +177,7 @@ void LEDCOutput::update_frequency(float frequency) { } this->bit_depth_ = bit_depth_opt.value_or(8); this->frequency_ = frequency; -#ifdef USE_ARDUINO - ESP_LOGV(TAG, "Using Arduino API - Trying to define channel, frequency and bit depth..."); - u_int32_t configured_frequency = 0; - // Configure LEDC channel, frequency and bit depth with fallback - int attempt_count_max = SETUP_ATTEMPT_COUNT_MAX; - while (attempt_count_max > 0 && configured_frequency == 0) { - ESP_LOGV(TAG, "Initializing channel %u with frequency %.1f and bit depth of %u...", this->channel_, - this->frequency_, this->bit_depth_); - configured_frequency = ledcSetup(this->channel_, frequency, this->bit_depth_); - if (configured_frequency != 0) { - this->initialized_ = true; - this->status_clear_error(); - ESP_LOGV(TAG, "Configured frequency: %u with bit depth: %u", configured_frequency, this->bit_depth_); - } else { - ESP_LOGW(TAG, "Unable to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_, - this->frequency_, this->bit_depth_); - // try again with a lower bit depth - this->bit_depth_--; - } - attempt_count_max--; - } - - if (configured_frequency == 0) { - ESP_LOGE(TAG, "Permanently failed to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_, - this->frequency_, this->bit_depth_); - this->status_set_error(); - return; - } - -#endif // USE_ARDUINO -#ifdef USE_ESP_IDF if (!this->initialized_) { ESP_LOGW(TAG, "Not yet initialized"); return; @@ -257,7 +197,7 @@ void LEDCOutput::update_frequency(float frequency) { } this->status_clear_error(); -#endif + // re-apply duty this->write_state(this->duty_); } diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index 5bdfb15e19..28ee1e702f 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -173,9 +173,9 @@ def _notify_old_style(config): # The dev and latest branches will be at *least* this version, which is what matters. ARDUINO_VERSIONS = { - "dev": (cv.Version(1, 7, 0), "https://github.com/libretiny-eu/libretiny.git"), - "latest": (cv.Version(1, 7, 0), "libretiny"), - "recommended": (cv.Version(1, 7, 0), None), + "dev": (cv.Version(1, 9, 1), "https://github.com/libretiny-eu/libretiny.git"), + "latest": (cv.Version(1, 9, 1), "libretiny"), + "recommended": (cv.Version(1, 9, 1), None), } @@ -264,6 +264,7 @@ async def component_to_code(config): # force using arduino framework cg.add_platformio_option("framework", "arduino") cg.add_build_flag("-DUSE_ARDUINO") + cg.set_cpp_standard("gnu++17") # disable library compatibility checks cg.add_platformio_option("lib_ldf_mode", "off") diff --git a/esphome/components/libretiny/preferences.cpp b/esphome/components/libretiny/preferences.cpp index a090f42aa7..ce4ed915c0 100644 --- a/esphome/components/libretiny/preferences.cpp +++ b/esphome/components/libretiny/preferences.cpp @@ -1,8 +1,8 @@ #ifdef USE_LIBRETINY -#include "esphome/core/preferences.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/preferences.h" #include #include #include @@ -101,7 +101,7 @@ class LibreTinyPreferences : public ESPPreferences { if (s_pending_save.empty()) return true; - ESP_LOGD(TAG, "Saving %d preferences to flash...", s_pending_save.size()); + ESP_LOGV(TAG, "Saving %d items...", s_pending_save.size()); // goal try write all pending saves even if one fails int cached = 0, written = 0, failed = 0; fdb_err_t last_err = FDB_NO_ERR; @@ -129,11 +129,10 @@ class LibreTinyPreferences : public ESPPreferences { } s_pending_save.erase(s_pending_save.begin() + i); } - ESP_LOGD(TAG, "Saving %d preferences to flash: %d cached, %d written, %d failed", cached + written + failed, cached, - written, failed); + ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written, + failed); if (failed > 0) { - ESP_LOGE(TAG, "Error saving %d preferences to flash. Last error=%d for key=%s", failed, last_err, - last_key.c_str()); + ESP_LOGE(TAG, "Writing %d items failed. Last error=%d for key=%s", failed, last_err, last_key.c_str()); } return failed == 0; @@ -158,7 +157,7 @@ class LibreTinyPreferences : public ESPPreferences { } bool reset() override { - ESP_LOGD(TAG, "Cleaning up preferences in flash..."); + ESP_LOGD(TAG, "Erasing storage"); s_pending_save.clear(); fdb_kv_set_default(&db); diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index 237ab45f38..a013029fc2 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -37,7 +37,7 @@ from esphome.const import ( CONF_WEB_SERVER, CONF_WHITE, ) -from esphome.core import coroutine_with_priority +from esphome.core import CORE, coroutine_with_priority from esphome.cpp_generator import MockObjClass from esphome.cpp_helpers import setup_entity @@ -270,6 +270,7 @@ async def setup_light_core_(light_var, output_var, config): async def register_light(output_var, config): light_var = cg.new_Pvariable(config[CONF_ID], output_var) cg.add(cg.App.register_light(light_var)) + CORE.register_platform_component("light", light_var) await cg.register_component(light_var, config) await setup_light_core_(light_var, output_var, config) diff --git a/esphome/components/light/esp_hsv_color.h b/esphome/components/light/esp_hsv_color.h index 39f5e55707..cdde91c71c 100644 --- a/esphome/components/light/esp_hsv_color.h +++ b/esphome/components/light/esp_hsv_color.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/helpers.h" #include "esphome/core/color.h" +#include "esphome/core/helpers.h" namespace esphome { namespace light { diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 0f8de7fac5..0aae6aed15 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -91,12 +91,16 @@ void LightState::setup() { void LightState::dump_config() { ESP_LOGCONFIG(TAG, "Light '%s'", this->get_name().c_str()); if (this->get_traits().supports_color_capability(ColorCapability::BRIGHTNESS)) { - ESP_LOGCONFIG(TAG, " Default Transition Length: %.1fs", this->default_transition_length_ / 1e3f); - ESP_LOGCONFIG(TAG, " Gamma Correct: %.2f", this->gamma_correct_); + ESP_LOGCONFIG(TAG, + " Default Transition Length: %.1fs\n" + " Gamma Correct: %.2f", + this->default_transition_length_ / 1e3f, this->gamma_correct_); } if (this->get_traits().supports_color_capability(ColorCapability::COLOR_TEMPERATURE)) { - ESP_LOGCONFIG(TAG, " Min Mireds: %.1f", this->get_traits().get_min_mireds()); - ESP_LOGCONFIG(TAG, " Max Mireds: %.1f", this->get_traits().get_max_mireds()); + ESP_LOGCONFIG(TAG, + " Min Mireds: %.1f\n" + " Max Mireds: %.1f", + this->get_traits().get_min_mireds(), this->get_traits().get_max_mireds()); } } void LightState::loop() { diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index acba986f24..b93823feac 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -17,7 +17,7 @@ namespace light { class LightOutput; -enum LightRestoreMode { +enum LightRestoreMode : uint8_t { LIGHT_RESTORE_DEFAULT_OFF, LIGHT_RESTORE_DEFAULT_ON, LIGHT_ALWAYS_OFF, @@ -212,12 +212,18 @@ class LightState : public EntityBase, public Component { /// Store the output to allow effects to have more access. LightOutput *output_; - /// Value for storing the index of the currently active effect. 0 if no effect is active - uint32_t active_effect_index_{}; /// The currently active transformer for this light (transition/flash). std::unique_ptr transformer_{nullptr}; - /// Whether the light value should be written in the next cycle. - bool next_write_{true}; + /// List of effects for this light. + std::vector effects_; + /// Value for storing the index of the currently active effect. 0 if no effect is active + uint32_t active_effect_index_{}; + /// Default transition length for all transitions in ms. + uint32_t default_transition_length_{}; + /// Transition length to use for flash transitions. + uint32_t flash_transition_length_{}; + /// Gamma correction factor for the light. + float gamma_correct_{}; /// Object used to store the persisted values of the light. ESPPreferenceObject rtc_; @@ -236,19 +242,13 @@ class LightState : public EntityBase, public Component { */ CallbackManager target_state_reached_callback_{}; - /// Default transition length for all transitions in ms. - uint32_t default_transition_length_{}; - /// Transition length to use for flash transitions. - uint32_t flash_transition_length_{}; - /// Gamma correction factor for the light. - float gamma_correct_{}; - /// Restore mode of the light. - LightRestoreMode restore_mode_; /// Initial state of the light. optional initial_state_{}; - /// List of effects for this light. - std::vector effects_; + /// Restore mode of the light. + LightRestoreMode restore_mode_; + /// Whether the light value should be written in the next cycle. + bool next_write_{true}; // for effects, true if a transformer (transition) is active. bool is_transformer_active_ = false; }; diff --git a/esphome/components/light/light_transformer.h b/esphome/components/light/light_transformer.h index 35b045d5b4..fb9b709187 100644 --- a/esphome/components/light/light_transformer.h +++ b/esphome/components/light/light_transformer.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "light_color_values.h" namespace esphome { diff --git a/esphome/components/lightwaverf/LwTx.cpp b/esphome/components/lightwaverf/LwTx.cpp index 2f46b04b2d..f5ef6ddb2c 100644 --- a/esphome/components/lightwaverf/LwTx.cpp +++ b/esphome/components/lightwaverf/LwTx.cpp @@ -8,8 +8,8 @@ #include "LwTx.h" #include #include -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace lightwaverf { diff --git a/esphome/components/lock/__init__.py b/esphome/components/lock/__init__.py index a96290dca6..0fb67e3948 100644 --- a/esphome/components/lock/__init__.py +++ b/esphome/components/lock/__init__.py @@ -115,6 +115,7 @@ async def register_lock(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_lock(var)) + CORE.register_platform_component("lock", var) await _setup_lock_core(var, config) diff --git a/esphome/components/lock/lock.h b/esphome/components/lock/lock.h index 7a98187a4f..2173c84903 100644 --- a/esphome/components/lock/lock.h +++ b/esphome/components/lock/lock.h @@ -2,9 +2,9 @@ #include "esphome/core/component.h" #include "esphome/core/entity_base.h" -#include "esphome/core/preferences.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/preferences.h" #include namespace esphome { diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 462cae73b6..26516e1506 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -324,7 +324,10 @@ async def to_code(config): if CORE.using_arduino: if config[CONF_HARDWARE_UART] == USB_CDC: cg.add_build_flag("-DARDUINO_USB_CDC_ON_BOOT=1") - if CORE.is_esp32 and get_esp32_variant() == VARIANT_ESP32C3: + if CORE.is_esp32 and get_esp32_variant() in ( + VARIANT_ESP32C3, + VARIANT_ESP32C6, + ): cg.add_build_flag("-DARDUINO_USB_MODE=1") if CORE.using_esp_idf: diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 014f7e3dec..28a66b23b7 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -116,7 +116,7 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr if (this->baud_rate_ > 0) { this->write_msg_(this->tx_buffer_ + msg_start); } - this->call_log_callbacks_(level, tag, this->tx_buffer_ + msg_start); + this->log_callback_.call(level, tag, this->tx_buffer_ + msg_start); global_recursion_guard_ = false; } @@ -129,19 +129,6 @@ inline int Logger::level_for(const char *tag) { return this->current_level_; } -void HOT Logger::call_log_callbacks_(int level, const char *tag, const char *msg) { -#ifdef USE_ESP32 - // Suppress network-logging if memory constrained - // In some configurations (eg BLE enabled) there may be some transient - // memory exhaustion, and trying to log when OOM can lead to a crash. Skipping - // here usually allows the stack to recover instead. - // See issue #1234 for analysis. - if (xPortGetFreeHeapSize() < 2048) - return; -#endif - this->log_callback_.call(level, tag, msg); -} - Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) { // add 1 to buffer size for null terminator this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT @@ -189,7 +176,7 @@ void Logger::loop() { this->tx_buffer_size_); this->write_footer_to_buffer_(this->tx_buffer_, &this->tx_buffer_at_, this->tx_buffer_size_); this->tx_buffer_[this->tx_buffer_at_] = '\0'; - this->call_log_callbacks_(message->level, message->tag, this->tx_buffer_); + this->log_callback_.call(message->level, message->tag, this->tx_buffer_); // At this point all the data we need from message has been transferred to the tx_buffer // so we can release the message to allow other tasks to use it as soon as possible. this->log_buffer_->release_message_main_loop(received_token); @@ -221,12 +208,16 @@ float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; static const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "VERBOSE", "VERY_VERBOSE"}; void Logger::dump_config() { - ESP_LOGCONFIG(TAG, "Logger:"); - ESP_LOGCONFIG(TAG, " Max Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]); - ESP_LOGCONFIG(TAG, " Initial Level: %s", LOG_LEVELS[this->current_level_]); + ESP_LOGCONFIG(TAG, + "Logger:\n" + " Max Level: %s\n" + " Initial Level: %s", + LOG_LEVELS[ESPHOME_LOG_LEVEL], LOG_LEVELS[this->current_level_]); #ifndef USE_HOST - ESP_LOGCONFIG(TAG, " Log Baud Rate: %" PRIu32, this->baud_rate_); - ESP_LOGCONFIG(TAG, " Hardware UART: %s", get_uart_selection_()); + ESP_LOGCONFIG(TAG, + " Log Baud Rate: %" PRIu32 "\n" + " Hardware UART: %s", + this->baud_rate_, get_uart_selection_()); #endif #ifdef USE_ESPHOME_TASK_LOG_BUFFER if (this->log_buffer_) { diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index 5c53c4d40c..9f09208b66 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -156,7 +156,6 @@ class Logger : public Component { #endif protected: - void call_log_callbacks_(int level, const char *tag, const char *msg); void write_msg_(const char *msg); // Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator @@ -191,7 +190,7 @@ class Logger : public Component { if (this->baud_rate_ > 0) { this->write_msg_(this->tx_buffer_); // If logging is enabled, write to console } - this->call_log_callbacks_(level, tag, this->tx_buffer_); + this->log_callback_.call(level, tag, this->tx_buffer_); } // Write the body of the log message to the buffer @@ -212,9 +211,9 @@ class Logger : public Component { } // Format string to explicit buffer with varargs - inline void printf_to_buffer_(const char *format, char *buffer, int *buffer_at, int buffer_size, ...) { + inline void printf_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format, ...) { va_list arg; - va_start(arg, buffer_size); + va_start(arg, format); this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, arg); va_end(arg); } @@ -312,13 +311,13 @@ class Logger : public Component { #if defined(USE_ESP32) || defined(USE_LIBRETINY) if (thread_name != nullptr) { // Non-main task with thread name - this->printf_to_buffer_("%s[%s][%s:%03u]%s[%s]%s: ", buffer, buffer_at, buffer_size, color, letter, tag, line, + this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line, ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), thread_name, color); return; } #endif // Main task or non ESP32/LibreTiny platform - this->printf_to_buffer_("%s[%s][%s:%03u]: ", buffer, buffer_at, buffer_size, color, letter, tag, line); + this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]: ", color, letter, tag, line); } inline void HOT format_body_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format, diff --git a/esphome/components/ltr390/ltr390.cpp b/esphome/components/ltr390/ltr390.cpp index 6e31fcdc42..cc7e686d13 100644 --- a/esphome/components/ltr390/ltr390.cpp +++ b/esphome/components/ltr390/ltr390.cpp @@ -187,10 +187,13 @@ void LTR390Component::setup() { void LTR390Component::dump_config() { LOG_I2C_DEVICE(this); - ESP_LOGCONFIG(TAG, " ALS Gain: X%.0f", GAINVALUES[this->gain_als_]); - ESP_LOGCONFIG(TAG, " ALS Resolution: %u-bit", RESOLUTION_BITS[this->res_als_]); - ESP_LOGCONFIG(TAG, " UV Gain: X%.0f", GAINVALUES[this->gain_uv_]); - ESP_LOGCONFIG(TAG, " UV Resolution: %u-bit", RESOLUTION_BITS[this->res_uv_]); + ESP_LOGCONFIG(TAG, + " ALS Gain: X%.0f\n" + " ALS Resolution: %u-bit\n" + " UV Gain: X%.0f\n" + " UV Resolution: %u-bit", + GAINVALUES[this->gain_als_], RESOLUTION_BITS[this->res_als_], GAINVALUES[this->gain_uv_], + RESOLUTION_BITS[this->res_uv_]); } void LTR390Component::update() { diff --git a/esphome/components/ltr501/ltr501.cpp b/esphome/components/ltr501/ltr501.cpp index 9fbc3f3b13..12f227ab91 100644 --- a/esphome/components/ltr501/ltr501.cpp +++ b/esphome/components/ltr501/ltr501.cpp @@ -1,7 +1,7 @@ #include "ltr501.h" #include "esphome/core/application.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" using esphome::i2c::ErrorCode; @@ -94,16 +94,21 @@ void LTRAlsPs501Component::dump_config() { }; LOG_I2C_DEVICE(this); - ESP_LOGCONFIG(TAG, " Device type: %s", get_device_type(this->ltr_type_)); - ESP_LOGCONFIG(TAG, " Automatic mode: %s", ONOFF(this->automatic_mode_enabled_)); - ESP_LOGCONFIG(TAG, " Gain: %.0fx", get_gain_coeff(this->gain_)); - ESP_LOGCONFIG(TAG, " Integration time: %d ms", get_itime_ms(this->integration_time_)); - ESP_LOGCONFIG(TAG, " Measurement repeat rate: %d ms", get_meas_time_ms(this->repeat_rate_)); - ESP_LOGCONFIG(TAG, " Glass attenuation factor: %f", this->glass_attenuation_factor_); - ESP_LOGCONFIG(TAG, " Proximity gain: %.0fx", get_ps_gain_coeff(this->ps_gain_)); - ESP_LOGCONFIG(TAG, " Proximity cooldown time: %d s", this->ps_cooldown_time_s_); - ESP_LOGCONFIG(TAG, " Proximity high threshold: %d", this->ps_threshold_high_); - ESP_LOGCONFIG(TAG, " Proximity low threshold: %d", this->ps_threshold_low_); + ESP_LOGCONFIG(TAG, + " Device type: %s\n" + " Automatic mode: %s\n" + " Gain: %.0fx\n" + " Integration time: %d ms\n" + " Measurement repeat rate: %d ms\n" + " Glass attenuation factor: %f\n" + " Proximity gain: %.0fx\n" + " Proximity cooldown time: %d s\n" + " Proximity high threshold: %d\n" + " Proximity low threshold: %d", + get_device_type(this->ltr_type_), ONOFF(this->automatic_mode_enabled_), get_gain_coeff(this->gain_), + get_itime_ms(this->integration_time_), get_meas_time_ms(this->repeat_rate_), + this->glass_attenuation_factor_, get_ps_gain_coeff(this->ps_gain_), this->ps_cooldown_time_s_, + this->ps_threshold_high_, this->ps_threshold_low_); LOG_UPDATE_INTERVAL(this); @@ -306,7 +311,7 @@ void LTRAlsPs501Component::configure_als_() { uint8_t tries = MAX_TRIES; do { - ESP_LOGV(TAG, "Waiting for ALS device to become active..."); + ESP_LOGV(TAG, "Waiting for ALS device to become active"); delay(2); als_ctrl.raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get(); } while (!als_ctrl.als_mode_active && tries--); // while active mode is not set - keep waiting diff --git a/esphome/components/ltr_als_ps/ltr_als_ps.cpp b/esphome/components/ltr_als_ps/ltr_als_ps.cpp index 402fd1318b..9b635a12b1 100644 --- a/esphome/components/ltr_als_ps/ltr_als_ps.cpp +++ b/esphome/components/ltr_als_ps/ltr_als_ps.cpp @@ -1,7 +1,7 @@ #include "ltr_als_ps.h" #include "esphome/core/application.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" using esphome::i2c::ErrorCode; @@ -85,21 +85,28 @@ void LTRAlsPsComponent::dump_config() { LOG_I2C_DEVICE(this); ESP_LOGCONFIG(TAG, " Device type: %s", get_device_type(this->ltr_type_)); if (this->is_als_()) { - ESP_LOGCONFIG(TAG, " Automatic mode: %s", ONOFF(this->automatic_mode_enabled_)); - ESP_LOGCONFIG(TAG, " Gain: %.0fx", get_gain_coeff(this->gain_)); - ESP_LOGCONFIG(TAG, " Integration time: %d ms", get_itime_ms(this->integration_time_)); - ESP_LOGCONFIG(TAG, " Measurement repeat rate: %d ms", get_meas_time_ms(this->repeat_rate_)); - ESP_LOGCONFIG(TAG, " Glass attenuation factor: %f", this->glass_attenuation_factor_); + ESP_LOGCONFIG(TAG, + " Automatic mode: %s\n" + " Gain: %.0fx\n" + " Integration time: %d ms\n" + " Measurement repeat rate: %d ms\n" + " Glass attenuation factor: %f", + ONOFF(this->automatic_mode_enabled_), get_gain_coeff(this->gain_), + get_itime_ms(this->integration_time_), get_meas_time_ms(this->repeat_rate_), + this->glass_attenuation_factor_); LOG_SENSOR(" ", "ALS calculated lux", this->ambient_light_sensor_); LOG_SENSOR(" ", "CH1 Infrared counts", this->infrared_counts_sensor_); LOG_SENSOR(" ", "CH0 Visible+IR counts", this->full_spectrum_counts_sensor_); LOG_SENSOR(" ", "Actual gain", this->actual_gain_sensor_); } if (this->is_ps_()) { - ESP_LOGCONFIG(TAG, " Proximity gain: %.0fx", get_ps_gain_coeff(this->ps_gain_)); - ESP_LOGCONFIG(TAG, " Proximity cooldown time: %d s", this->ps_cooldown_time_s_); - ESP_LOGCONFIG(TAG, " Proximity high threshold: %d", this->ps_threshold_high_); - ESP_LOGCONFIG(TAG, " Proximity low threshold: %d", this->ps_threshold_low_); + ESP_LOGCONFIG(TAG, + " Proximity gain: %.0fx\n" + " Proximity cooldown time: %d s\n" + " Proximity high threshold: %d\n" + " Proximity low threshold: %d", + get_ps_gain_coeff(this->ps_gain_), this->ps_cooldown_time_s_, this->ps_threshold_high_, + this->ps_threshold_low_); LOG_SENSOR(" ", "Proximity counts", this->proximity_counts_sensor_); } LOG_UPDATE_INTERVAL(this); @@ -298,7 +305,7 @@ void LTRAlsPsComponent::configure_als_() { uint8_t tries = MAX_TRIES; do { - ESP_LOGV(TAG, "Waiting for device to become active..."); + ESP_LOGV(TAG, "Waiting for device to become active"); delay(2); als_ctrl.raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get(); } while (!als_ctrl.active_mode && tries--); // while active mode is not set - keep waiting diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index f49356604b..cc0f833ced 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -21,7 +21,7 @@ from .defines import ( literal, static_cast, ) -from .lv_validation import lv_bool, lv_color, lv_image, opacity +from .lv_validation import lv_bool, lv_color, lv_image, lv_milliseconds, opacity from .lvcode import ( LVGL_COMP_ARG, UPDATE_EVENT, @@ -129,14 +129,14 @@ async def lvgl_is_paused(config, condition_id, template_arg, args): LVGL_SCHEMA.extend( { cv.Required(CONF_TIMEOUT): cv.templatable( - cv.positive_time_period_milliseconds + lv_milliseconds, ) } ), ) async def lvgl_is_idle(config, condition_id, template_arg, args): lvgl = config[CONF_LVGL_ID] - timeout = await cg.templatable(config[CONF_TIMEOUT], [], cg.uint32) + timeout = await lv_milliseconds.process(config[CONF_TIMEOUT]) async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context: lv_add(ReturnStatement(lvgl_comp.is_idle(timeout))) var = cg.new_Pvariable( diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 7783fb2321..baa9a19c51 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -519,8 +519,6 @@ CONF_UPDATE_ON_RELEASE = "update_on_release" CONF_VISIBLE_ROW_COUNT = "visible_row_count" CONF_WIDGET = "widget" CONF_WIDGETS = "widgets" -CONF_X = "x" -CONF_Y = "y" CONF_ZOOM = "zoom" # Keypad keys diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index d58fb24584..dd877df0f0 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -1,7 +1,7 @@ #include "esphome/core/defines.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "lvgl_hal.h" #include "lvgl_esphome.h" @@ -85,11 +85,14 @@ static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { lv_event_code_t lv_api_event; // NOLINT lv_event_code_t lv_update_event; // NOLINT void LvglComponent::dump_config() { - ESP_LOGCONFIG(TAG, "LVGL:"); - ESP_LOGCONFIG(TAG, " Display width/height: %d x %d", this->disp_drv_.hor_res, this->disp_drv_.ver_res); - ESP_LOGCONFIG(TAG, " Buffer size: %zu%%", 100 / this->buffer_frac_); - ESP_LOGCONFIG(TAG, " Rotation: %d", this->rotation); - ESP_LOGCONFIG(TAG, " Draw rounding: %d", (int) this->draw_rounding); + ESP_LOGCONFIG(TAG, + "LVGL:\n" + " Display width/height: %d x %d\n" + " Buffer size: %zu%%\n" + " Rotation: %d\n" + " Draw rounding: %d", + this->disp_drv_.hor_res, this->disp_drv_.ver_res, 100 / this->buffer_frac_, this->rotation, + (int) this->draw_rounding); } void LvglComponent::set_paused(bool paused, bool show_snow) { this->paused_ = paused; diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 2bae560041..fdc8750d1d 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -13,13 +13,15 @@ from esphome.const import ( CONF_TIME, CONF_TRIGGER_ID, CONF_TYPE, + CONF_X, + CONF_Y, ) from esphome.core import TimePeriod from esphome.core.config import StartupTrigger from esphome.schema_extractors import SCHEMA_EXTRACT from . import defines as df, lv_validation as lvalid -from .defines import CONF_TIME_FORMAT, CONF_X, CONF_Y, LV_GRAD_DIR +from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR from .helpers import add_lv_use, requires_component, validate_printf from .lv_validation import lv_color, lv_font, lv_gradient, lv_image, opacity from .lvcode import LvglComponent, lv_event_t_ptr @@ -354,8 +356,8 @@ ALIGN_TO_SCHEMA = { { cv.Required(CONF_ID): cv.use_id(lv_obj_t), cv.Required(df.CONF_ALIGN): df.ALIGN_ALIGNMENTS.one_of, - cv.Optional(df.CONF_X, default=0): lvalid.pixels_or_percent, - cv.Optional(df.CONF_Y, default=0): lvalid.pixels_or_percent, + cv.Optional(CONF_X, default=0): lvalid.pixels_or_percent, + cv.Optional(CONF_Y, default=0): lvalid.pixels_or_percent, } ) } diff --git a/esphome/components/lvgl/trigger.py b/esphome/components/lvgl/trigger.py index 283c9a5e56..2f8b454ec4 100644 --- a/esphome/components/lvgl/trigger.py +++ b/esphome/components/lvgl/trigger.py @@ -1,12 +1,17 @@ from esphome import automation import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_ON_BOOT, CONF_ON_VALUE, CONF_TRIGGER_ID +from esphome.const import ( + CONF_ID, + CONF_ON_BOOT, + CONF_ON_VALUE, + CONF_TRIGGER_ID, + CONF_X, + CONF_Y, +) from .defines import ( CONF_ALIGN, CONF_ALIGN_TO, - CONF_X, - CONF_Y, DIRECTIONS, LV_EVENT_MAP, LV_EVENT_TRIGGERS, diff --git a/esphome/components/lvgl/widgets/canvas.py b/esphome/components/lvgl/widgets/canvas.py index 60812093d5..4fd81b6e4a 100644 --- a/esphome/components/lvgl/widgets/canvas.py +++ b/esphome/components/lvgl/widgets/canvas.py @@ -1,6 +1,14 @@ from esphome import automation, codegen as cg, config_validation as cv from esphome.components.display_menu_base import CONF_LABEL -from esphome.const import CONF_COLOR, CONF_HEIGHT, CONF_ID, CONF_TEXT, CONF_WIDTH +from esphome.const import ( + CONF_COLOR, + CONF_HEIGHT, + CONF_ID, + CONF_TEXT, + CONF_WIDTH, + CONF_X, + CONF_Y, +) from esphome.cpp_generator import Literal, MockObj from ..automation import action_to_code @@ -13,8 +21,6 @@ from ..defines import ( CONF_POINTS, CONF_SRC, CONF_START_ANGLE, - CONF_X, - CONF_Y, literal, ) from ..lv_validation import ( diff --git a/esphome/components/lvgl/widgets/line.py b/esphome/components/lvgl/widgets/line.py index 94fdfe2346..bd90edbefc 100644 --- a/esphome/components/lvgl/widgets/line.py +++ b/esphome/components/lvgl/widgets/line.py @@ -1,8 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv +from esphome.const import CONF_X, CONF_Y from esphome.core import Lambda -from ..defines import CONF_MAIN, CONF_X, CONF_Y, call_lambda +from ..defines import CONF_MAIN, call_lambda from ..lvcode import lv_add from ..schemas import point_schema from ..types import LvCompound, LvType diff --git a/esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp b/esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp index 95fd8cb98f..0e7b902919 100644 --- a/esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp +++ b/esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp @@ -8,7 +8,7 @@ namespace m5stack_8angle { static const char *const TAG = "m5stack_8angle.light"; void M5Stack8AngleLightOutput::setup() { - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; this->buf_ = allocator.allocate(M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED); if (this->buf_ == nullptr) { ESP_LOGE(TAG, "Failed to allocate buffer of size %u", M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED); diff --git a/esphome/components/m5stack_8angle/m5stack_8angle.cpp b/esphome/components/m5stack_8angle/m5stack_8angle.cpp index f08bb1214c..416b903816 100644 --- a/esphome/components/m5stack_8angle/m5stack_8angle.cpp +++ b/esphome/components/m5stack_8angle/m5stack_8angle.cpp @@ -13,14 +13,14 @@ void M5Stack8AngleComponent::setup() { err = this->read(nullptr, 0); if (err != i2c::NO_ERROR) { - ESP_LOGE(TAG, "I2C error %02X...", err); + ESP_LOGE(TAG, "I2C error %02X", err); this->mark_failed(); return; }; err = this->read_register(M5STACK_8ANGLE_REGISTER_FW_VERSION, &this->fw_version_, 1); if (err != i2c::NO_ERROR) { - ESP_LOGE(TAG, "I2C error %02X...", err); + ESP_LOGE(TAG, "I2C error %02X", err); this->mark_failed(); return; }; diff --git a/esphome/components/matrix_keypad/matrix_keypad.cpp b/esphome/components/matrix_keypad/matrix_keypad.cpp index 6cb4fc4f3c..43a20c49d1 100644 --- a/esphome/components/matrix_keypad/matrix_keypad.cpp +++ b/esphome/components/matrix_keypad/matrix_keypad.cpp @@ -97,8 +97,8 @@ void MatrixKeypad::loop() { } void MatrixKeypad::dump_config() { - ESP_LOGCONFIG(TAG, "Matrix Keypad:"); - ESP_LOGCONFIG(TAG, " Rows:"); + ESP_LOGCONFIG(TAG, "Matrix Keypad:\n" + " Rows:"); for (auto &pin : this->rows_) { LOG_PIN(" Pin: ", pin); } diff --git a/esphome/components/max31855/max31855.cpp b/esphome/components/max31855/max31855.cpp index e4ca94c5a5..26fba428cc 100644 --- a/esphome/components/max31855/max31855.cpp +++ b/esphome/components/max31855/max31855.cpp @@ -1,5 +1,6 @@ #include "max31855.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/max31856/max31856.cpp b/esphome/components/max31856/max31856.cpp index 2eb4a7538e..c30e2e1a31 100644 --- a/esphome/components/max31856/max31856.cpp +++ b/esphome/components/max31856/max31856.cpp @@ -95,28 +95,28 @@ bool MAX31856Sensor::has_fault_() { this->status_set_warning(); if ((faults & MAX31856_FAULT_CJRANGE) == MAX31856_FAULT_CJRANGE) { - ESP_LOGW(TAG, "Cold Junction Out-of-Range: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Cold Junction Out-of-Range: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_TCRANGE) == MAX31856_FAULT_TCRANGE) { - ESP_LOGW(TAG, "Thermocouple Out-of-Range: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Thermocouple Out-of-Range: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_CJHIGH) == MAX31856_FAULT_CJHIGH) { - ESP_LOGW(TAG, "Cold-Junction High Fault: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Cold-Junction High Fault: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_CJLOW) == MAX31856_FAULT_CJLOW) { - ESP_LOGW(TAG, "Cold-Junction Low Fault: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Cold-Junction Low Fault: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_TCHIGH) == MAX31856_FAULT_TCHIGH) { - ESP_LOGW(TAG, "Thermocouple Temperature High Fault: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Thermocouple Temperature High Fault: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_TCLOW) == MAX31856_FAULT_TCLOW) { - ESP_LOGW(TAG, "Thermocouple Temperature Low Fault: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Thermocouple Temperature Low Fault: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_OVUV) == MAX31856_FAULT_OVUV) { - ESP_LOGW(TAG, "Overvoltage or Undervoltage Input Fault: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Overvoltage or Undervoltage Input Fault: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_OPEN) == MAX31856_FAULT_OPEN) { - ESP_LOGW(TAG, "Thermocouple Open-Circuit Fault (possibly not connected): '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Thermocouple Open-Circuit Fault (possibly not connected): '%s'", this->name_.c_str()); } return true; diff --git a/esphome/components/max31865/max31865.cpp b/esphome/components/max31865/max31865.cpp index ac41130a8e..4c9a4ae540 100644 --- a/esphome/components/max31865/max31865.cpp +++ b/esphome/components/max31865/max31865.cpp @@ -83,9 +83,11 @@ void MAX31865Sensor::dump_config() { LOG_SENSOR("", "MAX31865", this); LOG_PIN(" CS Pin: ", this->cs_); LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " Reference Resistance: %.2fΩ", reference_resistance_); - ESP_LOGCONFIG(TAG, " RTD: %u-wire %.2fΩ", rtd_wires_, rtd_nominal_resistance_); - ESP_LOGCONFIG(TAG, " Mains Filter: %s", + ESP_LOGCONFIG(TAG, + " Reference Resistance: %.2fΩ\n" + " RTD: %u-wire %.2fΩ\n" + " Mains Filter: %s", + reference_resistance_, rtd_wires_, rtd_nominal_resistance_, (filter_ == FILTER_60HZ ? "60 Hz" : (filter_ == FILTER_50HZ ? "50 Hz" : "Unknown!"))); } diff --git a/esphome/components/max6956/max6956.cpp b/esphome/components/max6956/max6956.cpp index 3da4dcc43d..5a1da9dc6f 100644 --- a/esphome/components/max6956/max6956.cpp +++ b/esphome/components/max6956/max6956.cpp @@ -146,8 +146,10 @@ void MAX6956::dump_config() { ESP_LOGCONFIG(TAG, "MAX6956"); if (brightness_mode_ == MAX6956CURRENTMODE::GLOBAL) { - ESP_LOGCONFIG(TAG, "current mode: global"); - ESP_LOGCONFIG(TAG, "global brightness: %u", global_brightness_); + ESP_LOGCONFIG(TAG, + "current mode: global\n" + "global brightness: %u", + global_brightness_); } else { ESP_LOGCONFIG(TAG, "current mode: segment"); } diff --git a/esphome/components/max7219/max7219.cpp b/esphome/components/max7219/max7219.cpp index 64be13d621..3f78b35bbb 100644 --- a/esphome/components/max7219/max7219.cpp +++ b/esphome/components/max7219/max7219.cpp @@ -1,7 +1,7 @@ #include "max7219.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace max7219 { @@ -133,9 +133,11 @@ void MAX7219Component::setup() { this->send_to_all_(MAX7219_REGISTER_SHUTDOWN, 1); } void MAX7219Component::dump_config() { - ESP_LOGCONFIG(TAG, "MAX7219:"); - ESP_LOGCONFIG(TAG, " Number of Chips: %u", this->num_chips_); - ESP_LOGCONFIG(TAG, " Intensity: %u", this->intensity_); + ESP_LOGCONFIG(TAG, + "MAX7219:\n" + " Number of Chips: %u\n" + " Intensity: %u", + this->num_chips_, this->intensity_); LOG_PIN(" CS Pin: ", this->cs_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/max7219digit/max7219digit.cpp b/esphome/components/max7219digit/max7219digit.cpp index a6c06b5213..1721dc80ce 100644 --- a/esphome/components/max7219digit/max7219digit.cpp +++ b/esphome/components/max7219digit/max7219digit.cpp @@ -1,8 +1,8 @@ #include "max7219digit.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" -#include "esphome/core/hal.h" #include "esphome/core/application.h" +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "max7219font.h" #include @@ -50,15 +50,18 @@ void MAX7219Component::setup() { } void MAX7219Component::dump_config() { - ESP_LOGCONFIG(TAG, "MAX7219DIGIT:"); - ESP_LOGCONFIG(TAG, " Number of Chips: %u", this->num_chips_); - ESP_LOGCONFIG(TAG, " Number of Chips Lines: %u", this->num_chip_lines_); - ESP_LOGCONFIG(TAG, " Chips Lines Style : %u", this->chip_lines_style_); - ESP_LOGCONFIG(TAG, " Intensity: %u", this->intensity_); - ESP_LOGCONFIG(TAG, " Scroll Mode: %u", this->scroll_mode_); - ESP_LOGCONFIG(TAG, " Scroll Speed: %u", this->scroll_speed_); - ESP_LOGCONFIG(TAG, " Scroll Dwell: %u", this->scroll_dwell_); - ESP_LOGCONFIG(TAG, " Scroll Delay: %u", this->scroll_delay_); + ESP_LOGCONFIG(TAG, + "MAX7219DIGIT:\n" + " Number of Chips: %u\n" + " Number of Chips Lines: %u\n" + " Chips Lines Style : %u\n" + " Intensity: %u\n" + " Scroll Mode: %u\n" + " Scroll Speed: %u\n" + " Scroll Dwell: %u\n" + " Scroll Delay: %u", + this->num_chips_, this->num_chip_lines_, this->chip_lines_style_, this->intensity_, this->scroll_mode_, + this->scroll_speed_, this->scroll_dwell_, this->scroll_delay_); LOG_PIN(" CS Pin: ", this->cs_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/max9611/max9611.cpp b/esphome/components/max9611/max9611.cpp index cf6f13d52d..e61a30ab99 100644 --- a/esphome/components/max9611/max9611.cpp +++ b/esphome/components/max9611/max9611.cpp @@ -52,8 +52,10 @@ void MAX9611Component::setup() { } } void MAX9611Component::dump_config() { - ESP_LOGCONFIG(TAG, "MAX9611:"); - ESP_LOGCONFIG(TAG, " CSA Gain Register: %x", gain_); + ESP_LOGCONFIG(TAG, + "MAX9611:\n" + " CSA Gain Register: %x", + gain_); LOG_I2C_DEVICE(this); } void MAX9611Component::update() { diff --git a/esphome/components/mcp23016/__init__.py b/esphome/components/mcp23016/__init__.py index e15c643349..3333e46c97 100644 --- a/esphome/components/mcp23016/__init__.py +++ b/esphome/components/mcp23016/__init__.py @@ -50,7 +50,7 @@ MCP23016_PIN_SCHEMA = pins.gpio_base_schema( cv.int_range(min=0, max=15), modes=[CONF_INPUT, CONF_OUTPUT], mode_validator=validate_mode, - invertable=True, + invertible=True, ).extend( { cv.Required(CONF_MCP23016): cv.use_id(MCP23016), diff --git a/esphome/components/mcp23xxx_base/__init__.py b/esphome/components/mcp23xxx_base/__init__.py index c0e44d72de..8cf0ebcd44 100644 --- a/esphome/components/mcp23xxx_base/__init__.py +++ b/esphome/components/mcp23xxx_base/__init__.py @@ -60,7 +60,7 @@ MCP23XXX_PIN_SCHEMA = pins.gpio_base_schema( cv.int_range(min=0, max=15), modes=[CONF_INPUT, CONF_OUTPUT, CONF_PULLUP], mode_validator=validate_mode, - invertable=True, + invertible=True, ).extend( { cv.Required(CONF_MCP23XXX): cv.use_id(MCP23XXXBase), diff --git a/esphome/components/mcp3008/sensor/mcp3008_sensor.cpp b/esphome/components/mcp3008/sensor/mcp3008_sensor.cpp index df2a8735f8..81eb0a812f 100644 --- a/esphome/components/mcp3008/sensor/mcp3008_sensor.cpp +++ b/esphome/components/mcp3008/sensor/mcp3008_sensor.cpp @@ -10,9 +10,11 @@ static const char *const TAG = "mcp3008.sensor"; float MCP3008Sensor::get_setup_priority() const { return setup_priority::DATA; } void MCP3008Sensor::dump_config() { - ESP_LOGCONFIG(TAG, "MCP3008Sensor:"); - ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); - ESP_LOGCONFIG(TAG, " Reference Voltage: %.2fV", this->reference_voltage_); + ESP_LOGCONFIG(TAG, + "MCP3008Sensor:\n" + " Pin: %u\n" + " Reference Voltage: %.2fV", + this->pin_, this->reference_voltage_); } float MCP3008Sensor::sample() { diff --git a/esphome/components/mcp4461/__init__.py b/esphome/components/mcp4461/__init__.py index 1764629ff3..f3ef6f4917 100644 --- a/esphome/components/mcp4461/__init__.py +++ b/esphome/components/mcp4461/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import i2c +import esphome.config_validation as cv from esphome.const import CONF_ID CODEOWNERS = ["@p1ngb4ck"] diff --git a/esphome/components/mcp4461/mcp4461.cpp b/esphome/components/mcp4461/mcp4461.cpp index 88e7475a76..39127a6c04 100644 --- a/esphome/components/mcp4461/mcp4461.cpp +++ b/esphome/components/mcp4461/mcp4461.cpp @@ -1,7 +1,7 @@ #include "mcp4461.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" namespace esphome { namespace mcp4461 { diff --git a/esphome/components/mcp4461/output/__init__.py b/esphome/components/mcp4461/output/__init__.py index ba59f97643..02bdbefed5 100644 --- a/esphome/components/mcp4461/output/__init__.py +++ b/esphome/components/mcp4461/output/__init__.py @@ -1,8 +1,9 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import output +import esphome.config_validation as cv from esphome.const import CONF_CHANNEL, CONF_ID, CONF_INITIAL_VALUE -from .. import Mcp4461Component, CONF_MCP4461_ID, mcp4461_ns + +from .. import CONF_MCP4461_ID, Mcp4461Component, mcp4461_ns DEPENDENCIES = ["mcp4461"] diff --git a/esphome/components/md5/md5.cpp b/esphome/components/md5/md5.cpp index 31f52634be..980cb98699 100644 --- a/esphome/components/md5/md5.cpp +++ b/esphome/components/md5/md5.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace md5 { -#if defined(USE_ARDUINO) && !defined(USE_RP2040) +#if defined(USE_ARDUINO) && !defined(USE_RP2040) && !defined(USE_ESP32) void MD5Digest::init() { memset(this->digest_, 0, 16); MD5Init(&this->ctx_); @@ -18,7 +18,7 @@ void MD5Digest::add(const uint8_t *data, size_t len) { MD5Update(&this->ctx_, da void MD5Digest::calculate() { MD5Final(this->digest_, &this->ctx_); } #endif // USE_ARDUINO && !USE_RP2040 -#ifdef USE_ESP_IDF +#ifdef USE_ESP32 void MD5Digest::init() { memset(this->digest_, 0, 16); esp_rom_md5_init(&this->ctx_); @@ -27,7 +27,7 @@ void MD5Digest::init() { void MD5Digest::add(const uint8_t *data, size_t len) { esp_rom_md5_update(&this->ctx_, data, len); } void MD5Digest::calculate() { esp_rom_md5_final(this->digest_, &this->ctx_); } -#endif // USE_ESP_IDF +#endif // USE_ESP32 #ifdef USE_RP2040 void MD5Digest::init() { diff --git a/esphome/components/md5/md5.h b/esphome/components/md5/md5.h index cb6accf46f..be1df40423 100644 --- a/esphome/components/md5/md5.h +++ b/esphome/components/md5/md5.h @@ -3,16 +3,11 @@ #include "esphome/core/defines.h" #ifdef USE_MD5 -#ifdef USE_ESP_IDF +#ifdef USE_ESP32 #include "esp_rom_md5.h" #define MD5_CTX_TYPE md5_context_t #endif -#if defined(USE_ARDUINO) && defined(USE_ESP32) -#include "rom/md5_hash.h" -#define MD5_CTX_TYPE MD5Context -#endif - #if defined(USE_ARDUINO) && defined(USE_ESP8266) #include #define MD5_CTX_TYPE md5_context_t diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index ffc668e218..06ca99b402 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -59,6 +59,8 @@ void MDNSComponent::compile_records_() { service.txt_records.push_back({"network", "wifi"}); #elif defined(USE_ETHERNET) service.txt_records.push_back({"network", "ethernet"}); +#elif defined(USE_OPENTHREAD) + service.txt_records.push_back({"network", "thread"}); #endif #ifdef USE_API_NOISE @@ -117,8 +119,10 @@ void MDNSComponent::compile_records_() { } void MDNSComponent::dump_config() { - ESP_LOGCONFIG(TAG, "mDNS:"); - ESP_LOGCONFIG(TAG, " Hostname: %s", this->hostname_.c_str()); + ESP_LOGCONFIG(TAG, + "mDNS:\n" + " Hostname: %s", + this->hostname_.c_str()); ESP_LOGV(TAG, " Services:"); for (const auto &service : this->services_) { ESP_LOGV(TAG, " - %s, %s, %d", service.service_type.c_str(), service.proto.c_str(), @@ -130,6 +134,8 @@ void MDNSComponent::dump_config() { } } +std::vector MDNSComponent::get_services() { return this->services_; } + } // namespace mdns } // namespace esphome #endif diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h index 9eb2ba11d0..93a16f40d2 100644 --- a/esphome/components/mdns/mdns_component.h +++ b/esphome/components/mdns/mdns_component.h @@ -33,10 +33,12 @@ class MDNSComponent : public Component { #if (defined(USE_ESP8266) || defined(USE_RP2040)) && defined(USE_ARDUINO) void loop() override; #endif - float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + float get_setup_priority() const override { return setup_priority::AFTER_CONNECTION; } void add_extra_service(MDNSService service) { services_extra_.push_back(std::move(service)); } + std::vector get_services(); + void on_shutdown() override; protected: diff --git a/esphome/components/media_player/__init__.py b/esphome/components/media_player/__init__.py index 2f5fe0c03e..ef76419de3 100644 --- a/esphome/components/media_player/__init__.py +++ b/esphome/components/media_player/__init__.py @@ -103,6 +103,7 @@ async def register_media_player(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_media_player(var)) + CORE.register_platform_component("media_player", var) await setup_media_player_core_(var, config) diff --git a/esphome/components/micro_wake_word/streaming_model.cpp b/esphome/components/micro_wake_word/streaming_model.cpp index 38b88557e6..2b073cce56 100644 --- a/esphome/components/micro_wake_word/streaming_model.cpp +++ b/esphome/components/micro_wake_word/streaming_model.cpp @@ -11,19 +11,23 @@ namespace esphome { namespace micro_wake_word { void WakeWordModel::log_model_config() { - ESP_LOGCONFIG(TAG, " - Wake Word: %s", this->wake_word_.c_str()); - ESP_LOGCONFIG(TAG, " Probability cutoff: %.2f", this->probability_cutoff_ / 255.0f); - ESP_LOGCONFIG(TAG, " Sliding window size: %d", this->sliding_window_size_); + ESP_LOGCONFIG(TAG, + " - Wake Word: %s\n" + " Probability cutoff: %.2f\n" + " Sliding window size: %d", + this->wake_word_.c_str(), this->probability_cutoff_ / 255.0f, this->sliding_window_size_); } void VADModel::log_model_config() { - ESP_LOGCONFIG(TAG, " - VAD Model"); - ESP_LOGCONFIG(TAG, " Probability cutoff: %.2f", this->probability_cutoff_ / 255.0f); - ESP_LOGCONFIG(TAG, " Sliding window size: %d", this->sliding_window_size_); + ESP_LOGCONFIG(TAG, + " - VAD Model\n" + " Probability cutoff: %.2f\n" + " Sliding window size: %d", + this->probability_cutoff_ / 255.0f, this->sliding_window_size_); } bool StreamingModel::load_model_() { - RAMAllocator arena_allocator(RAMAllocator::ALLOW_FAILURE); + RAMAllocator arena_allocator; if (this->tensor_arena_ == nullptr) { this->tensor_arena_ = arena_allocator.allocate(this->tensor_arena_size_); @@ -92,7 +96,7 @@ bool StreamingModel::load_model_() { void StreamingModel::unload_model() { this->interpreter_.reset(); - RAMAllocator arena_allocator(RAMAllocator::ALLOW_FAILURE); + RAMAllocator arena_allocator; if (this->tensor_arena_ != nullptr) { arena_allocator.deallocate(this->tensor_arena_, this->tensor_arena_size_); diff --git a/esphome/components/micronova/micronova.h b/esphome/components/micronova/micronova.h index aebef277e5..fc68d941cf 100644 --- a/esphome/components/micronova/micronova.h +++ b/esphome/components/micronova/micronova.h @@ -1,10 +1,10 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/components/uart/uart.h" -#include "esphome/core/log.h" +#include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include diff --git a/esphome/components/mics_4514/mics_4514.cpp b/esphome/components/mics_4514/mics_4514.cpp index 5fab97d57a..3a2cf22914 100644 --- a/esphome/components/mics_4514/mics_4514.cpp +++ b/esphome/components/mics_4514/mics_4514.cpp @@ -16,7 +16,7 @@ void MICS4514Component::setup() { uint8_t power_mode; this->read_register(POWER_MODE_REGISTER, &power_mode, 1); if (power_mode == 0x00) { - ESP_LOGCONFIG(TAG, "Waking up MICS 4514, sensors will have data after 3 minutes..."); + ESP_LOGCONFIG(TAG, "Waking up MICS 4514, sensors will have data after 3 minutes"); power_mode = 0x01; this->write_register(POWER_MODE_REGISTER, &power_mode, 1); delay(100); // NOLINT diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index 247aea0488..170a2f6a40 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -1,7 +1,7 @@ #ifdef USE_ARDUINO -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "air_conditioner.h" #include "ac_adapter.h" #include @@ -103,10 +103,12 @@ ClimateTraits AirConditioner::traits() { } void AirConditioner::dump_config() { - ESP_LOGCONFIG(Constants::TAG, "MideaDongle:"); - ESP_LOGCONFIG(Constants::TAG, " [x] Period: %dms", this->base_.getPeriod()); - ESP_LOGCONFIG(Constants::TAG, " [x] Response timeout: %dms", this->base_.getTimeout()); - ESP_LOGCONFIG(Constants::TAG, " [x] Request attempts: %d", this->base_.getNumAttempts()); + ESP_LOGCONFIG(Constants::TAG, + "MideaDongle:\n" + " [x] Period: %dms\n" + " [x] Response timeout: %dms\n" + " [x] Request attempts: %d", + this->base_.getPeriod(), this->base_.getTimeout(), this->base_.getNumAttempts()); #ifdef USE_REMOTE_TRANSMITTER ESP_LOGCONFIG(Constants::TAG, " [x] Using RemoteTransmitter"); #endif diff --git a/esphome/components/midea_ir/midea_ir.cpp b/esphome/components/midea_ir/midea_ir.cpp index aa5e2b46f5..c269b2f7d9 100644 --- a/esphome/components/midea_ir/midea_ir.cpp +++ b/esphome/components/midea_ir/midea_ir.cpp @@ -1,7 +1,7 @@ #include "midea_ir.h" #include "midea_data.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/coolix/coolix.h" namespace esphome { diff --git a/esphome/components/mipi_spi/display.py b/esphome/components/mipi_spi/display.py index e9ed97a2a2..061257e859 100644 --- a/esphome/components/mipi_spi/display.py +++ b/esphome/components/mipi_spi/display.py @@ -472,3 +472,4 @@ async def to_code(config): cg.add(var.set_writer(lambda_)) await display.register_display(var, config) await spi.register_spi_device(var, config) + cg.add(var.set_write_only(True)) diff --git a/esphome/components/mipi_spi/mipi_spi.cpp b/esphome/components/mipi_spi/mipi_spi.cpp index 043346411e..962575477d 100644 --- a/esphome/components/mipi_spi/mipi_spi.cpp +++ b/esphome/components/mipi_spi/mipi_spi.cpp @@ -447,21 +447,28 @@ void MipiSpi::write_command_(uint8_t cmd, const uint8_t *bytes, size_t len) { } void MipiSpi::dump_config() { - ESP_LOGCONFIG(TAG, "MIPI_SPI Display"); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_); - ESP_LOGCONFIG(TAG, " Width: %u", this->width_); - ESP_LOGCONFIG(TAG, " Height: %u", this->height_); + ESP_LOGCONFIG(TAG, + "MIPI_SPI Display\n" + " Model: %s\n" + " Width: %u\n" + " Height: %u", + this->model_, this->width_, this->height_); if (this->offset_width_ != 0) ESP_LOGCONFIG(TAG, " Offset width: %u", this->offset_width_); if (this->offset_height_ != 0) ESP_LOGCONFIG(TAG, " Offset height: %u", this->offset_height_); - ESP_LOGCONFIG(TAG, " Swap X/Y: %s", YESNO(this->madctl_ & MADCTL_MV)); - ESP_LOGCONFIG(TAG, " Mirror X: %s", YESNO(this->madctl_ & (MADCTL_MX | MADCTL_XFLIP))); - ESP_LOGCONFIG(TAG, " Mirror Y: %s", YESNO(this->madctl_ & (MADCTL_MY | MADCTL_YFLIP))); - ESP_LOGCONFIG(TAG, " Color depth: %d bits", this->color_depth_ == display::COLOR_BITNESS_565 ? 16 : 8); - ESP_LOGCONFIG(TAG, " Invert colors: %s", YESNO(this->invert_colors_)); - ESP_LOGCONFIG(TAG, " Color order: %s", this->madctl_ & MADCTL_BGR ? "BGR" : "RGB"); - ESP_LOGCONFIG(TAG, " Pixel mode: %s", this->pixel_mode_ == PIXEL_MODE_18 ? "18bit" : "16bit"); + ESP_LOGCONFIG(TAG, + " Swap X/Y: %s\n" + " Mirror X: %s\n" + " Mirror Y: %s\n" + " Color depth: %d bits\n" + " Invert colors: %s\n" + " Color order: %s\n" + " Pixel mode: %s", + YESNO(this->madctl_ & MADCTL_MV), YESNO(this->madctl_ & (MADCTL_MX | MADCTL_XFLIP)), + YESNO(this->madctl_ & (MADCTL_MY | MADCTL_YFLIP)), + this->color_depth_ == display::COLOR_BITNESS_565 ? 16 : 8, YESNO(this->invert_colors_), + this->madctl_ & MADCTL_BGR ? "BGR" : "RGB", this->pixel_mode_ == PIXEL_MODE_18 ? "18bit" : "16bit"); if (this->brightness_.has_value()) ESP_LOGCONFIG(TAG, " Brightness: %u", this->brightness_.value()); if (this->spi_16_) @@ -472,9 +479,11 @@ void MipiSpi::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" DC Pin: ", this->dc_pin_); - ESP_LOGCONFIG(TAG, " SPI Mode: %d", this->mode_); - ESP_LOGCONFIG(TAG, " SPI Data rate: %dMHz", static_cast(this->data_rate_ / 1000000)); - ESP_LOGCONFIG(TAG, " SPI Bus width: %d", this->bus_width_); + ESP_LOGCONFIG(TAG, + " SPI Mode: %d\n" + " SPI Data rate: %dMHz\n" + " SPI Bus width: %d", + this->mode_, static_cast(this->data_rate_ / 1000000), this->bus_width_); } } // namespace mipi_spi diff --git a/esphome/components/mixer/speaker/mixer_speaker.cpp b/esphome/components/mixer/speaker/mixer_speaker.cpp index 8e480dd49b..fc0517c7be 100644 --- a/esphome/components/mixer/speaker/mixer_speaker.cpp +++ b/esphome/components/mixer/speaker/mixer_speaker.cpp @@ -43,8 +43,10 @@ enum MixerEventGroupBits : uint32_t { }; void SourceSpeaker::dump_config() { - ESP_LOGCONFIG(TAG, "Mixer Source Speaker"); - ESP_LOGCONFIG(TAG, " Buffer Duration: %" PRIu32 " ms", this->buffer_duration_ms_); + ESP_LOGCONFIG(TAG, + "Mixer Source Speaker\n" + " Buffer Duration: %" PRIu32 " ms", + this->buffer_duration_ms_); if (this->timeout_ms_.has_value()) { ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 " ms", this->timeout_ms_.value()); } else { @@ -291,8 +293,10 @@ void SourceSpeaker::duck_samples(int16_t *input_buffer, uint32_t input_samples_t } void MixerSpeaker::dump_config() { - ESP_LOGCONFIG(TAG, "Speaker Mixer:"); - ESP_LOGCONFIG(TAG, " Number of output channels: %u", this->output_channels_); + ESP_LOGCONFIG(TAG, + "Speaker Mixer:\n" + " Number of output channels: %u", + this->output_channels_); } void MixerSpeaker::setup() { diff --git a/esphome/components/mlx90614/mlx90614.cpp b/esphome/components/mlx90614/mlx90614.cpp index 97888b59bd..afc565d38b 100644 --- a/esphome/components/mlx90614/mlx90614.cpp +++ b/esphome/components/mlx90614/mlx90614.cpp @@ -1,6 +1,7 @@ #include "mlx90614.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index cd5fc55689..c2efa93fae 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -1,7 +1,7 @@ #include "modbus.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace modbus { @@ -165,8 +165,10 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) { void Modbus::dump_config() { ESP_LOGCONFIG(TAG, "Modbus:"); LOG_PIN(" Flow Control Pin: ", this->flow_control_pin_); - ESP_LOGCONFIG(TAG, " Send Wait Time: %d ms", this->send_wait_time_); - ESP_LOGCONFIG(TAG, " CRC Disabled: %s", YESNO(this->disable_crc_)); + ESP_LOGCONFIG(TAG, + " Send Wait Time: %d ms\n" + " CRC Disabled: %s", + this->send_wait_time_, YESNO(this->disable_crc_)); } float Modbus::get_setup_priority() const { // After UART bus diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 3f487abc94..48ff868087 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -346,10 +346,12 @@ size_t ModbusController::create_register_ranges_() { } void ModbusController::dump_config() { - ESP_LOGCONFIG(TAG, "ModbusController:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); - ESP_LOGCONFIG(TAG, " Max Command Retries: %d", this->max_cmd_retries_); - ESP_LOGCONFIG(TAG, " Offline Skip Updates: %d", this->offline_skip_updates_); + ESP_LOGCONFIG(TAG, + "ModbusController:\n" + " Address: 0x%02X\n" + " Max Command Retries: %d\n" + " Offline Skip Updates: %d", + this->address_, this->max_cmd_retries_, this->offline_skip_updates_); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE ESP_LOGCONFIG(TAG, "sensormap"); for (auto &it : this->sensorset_) { diff --git a/esphome/components/modbus_controller/output/modbus_output.cpp b/esphome/components/modbus_controller/output/modbus_output.cpp index f0f6e64f10..45e786a704 100644 --- a/esphome/components/modbus_controller/output/modbus_output.cpp +++ b/esphome/components/modbus_controller/output/modbus_output.cpp @@ -52,9 +52,11 @@ void ModbusFloatOutput::write_state(float value) { void ModbusFloatOutput::dump_config() { ESP_LOGCONFIG(TAG, "Modbus Float Output:"); LOG_FLOAT_OUTPUT(this); - ESP_LOGCONFIG(TAG, " Device start address: 0x%X", this->start_address); - ESP_LOGCONFIG(TAG, " Register count: %d", this->register_count); - ESP_LOGCONFIG(TAG, " Value type: %d", static_cast(this->sensor_value_type)); + ESP_LOGCONFIG(TAG, + " Device start address: 0x%X\n" + " Register count: %d\n" + " Value type: %d", + this->start_address, this->register_count, static_cast(this->sensor_value_type)); } // ModbusBinaryOutput @@ -102,9 +104,11 @@ void ModbusBinaryOutput::write_state(bool state) { void ModbusBinaryOutput::dump_config() { ESP_LOGCONFIG(TAG, "Modbus Binary Output:"); LOG_BINARY_OUTPUT(this); - ESP_LOGCONFIG(TAG, " Device start address: 0x%X", this->start_address); - ESP_LOGCONFIG(TAG, " Register count: %d", this->register_count); - ESP_LOGCONFIG(TAG, " Value type: %d", static_cast(this->sensor_value_type)); + ESP_LOGCONFIG(TAG, + " Device start address: 0x%X\n" + " Register count: %d\n" + " Value type: %d", + this->start_address, this->register_count, static_cast(this->sensor_value_type)); } } // namespace modbus_controller diff --git a/esphome/components/mpl3115a2/mpl3115a2.cpp b/esphome/components/mpl3115a2/mpl3115a2.cpp index d415b77294..9b65fb04e4 100644 --- a/esphome/components/mpl3115a2/mpl3115a2.cpp +++ b/esphome/components/mpl3115a2/mpl3115a2.cpp @@ -1,5 +1,6 @@ #include "mpl3115a2.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/mpu6050/mpu6050.cpp b/esphome/components/mpu6050/mpu6050.cpp index 127d84e816..84f0fb4bae 100644 --- a/esphome/components/mpu6050/mpu6050.cpp +++ b/esphome/components/mpu6050/mpu6050.cpp @@ -29,7 +29,7 @@ void MPU6050Component::setup() { return; } - ESP_LOGV(TAG, " Setting up Power Management..."); + ESP_LOGV(TAG, " Setting up Power Management"); // Setup power management uint8_t power_management; if (!this->read_byte(MPU6050_REGISTER_POWER_MANAGEMENT_1, &power_management)) { @@ -50,7 +50,7 @@ void MPU6050Component::setup() { return; } - ESP_LOGV(TAG, " Setting up Gyro Config..."); + ESP_LOGV(TAG, " Setting up Gyro Config"); // Set scale - 2000DPS uint8_t gyro_config; if (!this->read_byte(MPU6050_REGISTER_GYRO_CONFIG, &gyro_config)) { @@ -66,7 +66,7 @@ void MPU6050Component::setup() { return; } - ESP_LOGV(TAG, " Setting up Accel Config..."); + ESP_LOGV(TAG, " Setting up Accel Config"); // Set range - 2G uint8_t accel_config; if (!this->read_byte(MPU6050_REGISTER_ACCEL_CONFIG, &accel_config)) { @@ -99,7 +99,7 @@ void MPU6050Component::dump_config() { } void MPU6050Component::update() { - ESP_LOGV(TAG, " Updating MPU6050..."); + ESP_LOGV(TAG, "Updating"); uint16_t raw_data[7]; if (!this->read_bytes_16(MPU6050_REGISTER_ACCEL_XOUT_H, raw_data, 7)) { this->status_set_warning(); diff --git a/esphome/components/mpu6886/mpu6886.cpp b/esphome/components/mpu6886/mpu6886.cpp index 8f3adeed8d..cbd8b601bd 100644 --- a/esphome/components/mpu6886/mpu6886.cpp +++ b/esphome/components/mpu6886/mpu6886.cpp @@ -33,7 +33,7 @@ void MPU6886Component::setup() { return; } - ESP_LOGV(TAG, " Setting up Power Management..."); + ESP_LOGV(TAG, " Setting up Power Management"); // Setup power management uint8_t power_management; if (!this->read_byte(MPU6886_REGISTER_POWER_MANAGEMENT_1, &power_management)) { @@ -54,7 +54,7 @@ void MPU6886Component::setup() { return; } - ESP_LOGV(TAG, " Setting up Gyroscope Config..."); + ESP_LOGV(TAG, " Setting up Gyroscope Config"); // Set scale - 2000DPS uint8_t gyro_config; if (!this->read_byte(MPU6886_REGISTER_GYRO_CONFIG, &gyro_config)) { @@ -70,7 +70,7 @@ void MPU6886Component::setup() { return; } - ESP_LOGV(TAG, " Setting up Accelerometer Config..."); + ESP_LOGV(TAG, " Setting up Accelerometer Config"); // Set range - 2G uint8_t accel_config; if (!this->read_byte(MPU6886_REGISTER_ACCEL_CONFIG, &accel_config)) { @@ -104,7 +104,7 @@ void MPU6886Component::dump_config() { } void MPU6886Component::update() { - ESP_LOGV(TAG, " Updating MPU6886..."); + ESP_LOGV(TAG, " Updating"); uint16_t raw_data[7]; if (!this->read_bytes_16(MPU6886_REGISTER_ACCEL_XOUT_H, raw_data, 7)) { this->status_set_warning(); diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp index 4cc4773bd3..0a38598679 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp @@ -45,9 +45,13 @@ void MQTTAlarmControlPanelComponent::setup() { void MQTTAlarmControlPanelComponent::dump_config() { ESP_LOGCONFIG(TAG, "MQTT alarm_control_panel '%s':", this->alarm_control_panel_->get_name().c_str()); LOG_MQTT_COMPONENT(true, true) - ESP_LOGCONFIG(TAG, " Supported Features: %" PRIu32, this->alarm_control_panel_->get_supported_features()); - ESP_LOGCONFIG(TAG, " Requires Code to Disarm: %s", YESNO(this->alarm_control_panel_->get_requires_code())); - ESP_LOGCONFIG(TAG, " Requires Code To Arm: %s", YESNO(this->alarm_control_panel_->get_requires_code_to_arm())); + ESP_LOGCONFIG(TAG, + " Supported Features: %" PRIu32 "\n" + " Requires Code to Disarm: %s\n" + " Requires Code To Arm: %s", + this->alarm_control_panel_->get_supported_features(), + YESNO(this->alarm_control_panel_->get_requires_code()), + YESNO(this->alarm_control_panel_->get_requires_code_to_arm())); } void MQTTAlarmControlPanelComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { diff --git a/esphome/components/mqtt/mqtt_backend_esp32.cpp b/esphome/components/mqtt/mqtt_backend_esp32.cpp index 2cccb957eb..64dc27d84b 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.cpp +++ b/esphome/components/mqtt/mqtt_backend_esp32.cpp @@ -4,8 +4,8 @@ #ifdef USE_ESP32 #include -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace mqtt { diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index f95096106f..ceb56bdfbe 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -149,18 +149,23 @@ void MQTTClientComponent::send_device_info_() { } void MQTTClientComponent::dump_config() { - ESP_LOGCONFIG(TAG, "MQTT:"); - ESP_LOGCONFIG(TAG, " Server Address: %s:%u (%s)", this->credentials_.address.c_str(), this->credentials_.port, - this->ip_.str().c_str()); - ESP_LOGCONFIG(TAG, " Username: " LOG_SECRET("'%s'"), this->credentials_.username.c_str()); - ESP_LOGCONFIG(TAG, " Client ID: " LOG_SECRET("'%s'"), this->credentials_.client_id.c_str()); - ESP_LOGCONFIG(TAG, " Clean Session: %s", YESNO(this->credentials_.clean_session)); + ESP_LOGCONFIG(TAG, + "MQTT:\n" + " Server Address: %s:%u (%s)\n" + " Username: " LOG_SECRET("'%s'") "\n" + " Client ID: " LOG_SECRET("'%s'") "\n" + " Clean Session: %s", + this->credentials_.address.c_str(), this->credentials_.port, this->ip_.str().c_str(), + this->credentials_.username.c_str(), this->credentials_.client_id.c_str(), + YESNO(this->credentials_.clean_session)); if (this->is_discovery_ip_enabled()) { ESP_LOGCONFIG(TAG, " Discovery IP enabled"); } if (!this->discovery_info_.prefix.empty()) { - ESP_LOGCONFIG(TAG, " Discovery prefix: '%s'", this->discovery_info_.prefix.c_str()); - ESP_LOGCONFIG(TAG, " Discovery retain: %s", YESNO(this->discovery_info_.retain)); + ESP_LOGCONFIG(TAG, + " Discovery prefix: '%s'\n" + " Discovery retain: %s", + this->discovery_info_.prefix.c_str(), YESNO(this->discovery_info_.retain)); } ESP_LOGCONFIG(TAG, " Topic Prefix: '%s'", this->topic_prefix_.c_str()); if (!this->log_message_.topic.empty()) { @@ -201,13 +206,13 @@ void MQTTClientComponent::start_dnslookup_() { } case ERR_INPROGRESS: { // wait for callback - ESP_LOGD(TAG, "Resolving MQTT broker IP address..."); + ESP_LOGD(TAG, "Resolving broker IP address"); break; } default: case ERR_ARG: { // error - ESP_LOGW(TAG, "Error resolving MQTT broker IP address: %d", err); + ESP_LOGW(TAG, "Error resolving broker IP address: %d", err); break; } } @@ -221,7 +226,7 @@ void MQTTClientComponent::check_dnslookup_() { } if (this->dns_resolve_error_) { - ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'!", this->credentials_.address.c_str()); + ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'", this->credentials_.address.c_str()); this->state_ = MQTT_CLIENT_DISCONNECTED; return; } @@ -251,7 +256,7 @@ void MQTTClientComponent::start_connect_() { if (!network::is_connected()) return; - ESP_LOGI(TAG, "Connecting to MQTT..."); + ESP_LOGI(TAG, "Connecting"); // Force disconnect first this->mqtt_backend_.disconnect(); @@ -292,7 +297,7 @@ void MQTTClientComponent::check_connected() { this->state_ = MQTT_CLIENT_CONNECTED; this->sent_birth_message_ = false; this->status_clear_warning(); - ESP_LOGI(TAG, "MQTT Connected!"); + ESP_LOGI(TAG, "Connected"); // MQTT Client needs some time to be fully set up. delay(100); // NOLINT @@ -341,7 +346,7 @@ void MQTTClientComponent::loop() { if (!network::is_connected()) { reason_s = LOG_STR("WiFi disconnected"); } - ESP_LOGW(TAG, "MQTT Disconnected: %s.", LOG_STR_ARG(reason_s)); + ESP_LOGW(TAG, "Disconnected: %s", LOG_STR_ARG(reason_s)); this->disconnect_reason_.reset(); } @@ -364,7 +369,7 @@ void MQTTClientComponent::loop() { case MQTT_CLIENT_CONNECTED: if (!this->mqtt_backend_.connected()) { this->state_ = MQTT_CLIENT_DISCONNECTED; - ESP_LOGW(TAG, "Lost MQTT Client connection!"); + ESP_LOGW(TAG, "Lost client connection"); this->start_dnslookup_(); } else { if (!this->birth_message_.topic.empty() && !this->sent_birth_message_) { @@ -378,7 +383,7 @@ void MQTTClientComponent::loop() { } if (millis() - this->last_connected_ > this->reboot_timeout_ && this->reboot_timeout_ != 0) { - ESP_LOGE(TAG, "Can't connect to MQTT... Restarting..."); + ESP_LOGE(TAG, "Can't connect; restarting"); App.reboot(); } } @@ -396,7 +401,7 @@ bool MQTTClientComponent::subscribe_(const char *topic, uint8_t qos) { ESP_LOGV(TAG, "subscribe(topic='%s')", topic); } else { delay(5); - ESP_LOGV(TAG, "Subscribe failed for topic='%s'. Will retry later.", topic); + ESP_LOGV(TAG, "Subscribe failed for topic='%s'. Will retry", topic); this->status_momentary_warning("subscribe", 1000); } return ret != 0; @@ -499,7 +504,7 @@ bool MQTTClientComponent::publish(const MQTTMessage &message) { ESP_LOGV(TAG, "Publish(topic='%s' payload='%s' retain=%d qos=%d)", message.topic.c_str(), message.payload.c_str(), message.retain, message.qos); } else { - ESP_LOGV(TAG, "Publish failed for topic='%s' (len=%u). will retry later..", message.topic.c_str(), + ESP_LOGV(TAG, "Publish failed for topic='%s' (len=%u). Will retry", message.topic.c_str(), message.payload.length()); this->status_momentary_warning("publish", 1000); } @@ -515,7 +520,7 @@ bool MQTTClientComponent::publish_json(const std::string &topic, const json::jso void MQTTClientComponent::enable() { if (this->state_ != MQTT_CLIENT_DISABLED) return; - ESP_LOGD(TAG, "Enabling MQTT..."); + ESP_LOGD(TAG, "Enabling"); this->state_ = MQTT_CLIENT_DISCONNECTED; this->last_connected_ = millis(); this->start_dnslookup_(); @@ -524,7 +529,7 @@ void MQTTClientComponent::enable() { void MQTTClientComponent::disable() { if (this->state_ == MQTT_CLIENT_DISABLED) return; - ESP_LOGD(TAG, "Disabling MQTT..."); + ESP_LOGD(TAG, "Disabling"); this->state_ = MQTT_CLIENT_DISABLED; this->on_shutdown(); } @@ -721,9 +726,11 @@ void MQTTMessageTrigger::setup() { this->qos_); } void MQTTMessageTrigger::dump_config() { - ESP_LOGCONFIG(TAG, "MQTT Message Trigger:"); - ESP_LOGCONFIG(TAG, " Topic: '%s'", this->topic_.c_str()); - ESP_LOGCONFIG(TAG, " QoS: %u", this->qos_); + ESP_LOGCONFIG(TAG, + "MQTT Message Trigger:\n" + " Topic: '%s'\n" + " QoS: %u", + this->topic_.c_str(), this->qos_); } float MQTTMessageTrigger::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 3b9d367a7b..eee5644c9d 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -64,11 +64,11 @@ bool MQTTComponent::send_discovery_() { const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info(); if (discovery_info.clean) { - ESP_LOGV(TAG, "'%s': Cleaning discovery...", this->friendly_name().c_str()); + ESP_LOGV(TAG, "'%s': Cleaning discovery", this->friendly_name().c_str()); return global_mqtt_client->publish(this->get_discovery_topic_(discovery_info), "", 0, this->qos_, true); } - ESP_LOGV(TAG, "'%s': Sending discovery...", this->friendly_name().c_str()); + ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str()); return global_mqtt_client->publish_json( this->get_discovery_topic_(discovery_info), @@ -153,7 +153,7 @@ bool MQTTComponent::send_discovery_() { if (node_friendly_name.empty()) { node_friendly_name = node_name; } - const std::string &node_area = App.get_area(); + std::string node_area = App.get_area(); JsonObject device_info = root.createNestedObject(MQTT_DEVICE); const auto mac = get_mac_address(); diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index 0718a24828..8d09d836f3 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -54,12 +54,16 @@ void MQTTCoverComponent::dump_config() { bool has_command_topic = traits.get_supports_position() || !traits.get_supports_tilt(); LOG_MQTT_COMPONENT(true, has_command_topic) if (traits.get_supports_position()) { - ESP_LOGCONFIG(TAG, " Position State Topic: '%s'", this->get_position_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Position Command Topic: '%s'", this->get_position_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Position State Topic: '%s'\n" + " Position Command Topic: '%s'", + this->get_position_state_topic().c_str(), this->get_position_command_topic().c_str()); } if (traits.get_supports_tilt()) { - ESP_LOGCONFIG(TAG, " Tilt State Topic: '%s'", this->get_tilt_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Tilt Command Topic: '%s'", this->get_tilt_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Tilt State Topic: '%s'\n" + " Tilt Command Topic: '%s'", + this->get_tilt_state_topic().c_str(), this->get_tilt_command_topic().c_str()); } } void MQTTCoverComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index 9e5ea54bee..35713bdab6 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -121,16 +121,22 @@ void MQTTFanComponent::dump_config() { ESP_LOGCONFIG(TAG, "MQTT Fan '%s': ", this->state_->get_name().c_str()); LOG_MQTT_COMPONENT(true, true); if (this->state_->get_traits().supports_direction()) { - ESP_LOGCONFIG(TAG, " Direction State Topic: '%s'", this->get_direction_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Direction Command Topic: '%s'", this->get_direction_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Direction State Topic: '%s'\n" + " Direction Command Topic: '%s'", + this->get_direction_state_topic().c_str(), this->get_direction_command_topic().c_str()); } if (this->state_->get_traits().supports_oscillation()) { - ESP_LOGCONFIG(TAG, " Oscillation State Topic: '%s'", this->get_oscillation_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Oscillation Command Topic: '%s'", this->get_oscillation_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Oscillation State Topic: '%s'\n" + " Oscillation Command Topic: '%s'", + this->get_oscillation_state_topic().c_str(), this->get_oscillation_command_topic().c_str()); } if (this->state_->get_traits().supports_speed()) { - ESP_LOGCONFIG(TAG, " Speed Level State Topic: '%s'", this->get_speed_level_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Speed Level Command Topic: '%s'", this->get_speed_level_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Speed Level State Topic: '%s'\n" + " Speed Level Command Topic: '%s'", + this->get_speed_level_state_topic().c_str(), this->get_speed_level_command_topic().c_str()); } } diff --git a/esphome/components/mqtt/mqtt_valve.cpp b/esphome/components/mqtt/mqtt_valve.cpp index 07eeca08d6..85e06fe79c 100644 --- a/esphome/components/mqtt/mqtt_valve.cpp +++ b/esphome/components/mqtt/mqtt_valve.cpp @@ -42,8 +42,10 @@ void MQTTValveComponent::dump_config() { bool has_command_topic = traits.get_supports_position(); LOG_MQTT_COMPONENT(true, has_command_topic) if (traits.get_supports_position()) { - ESP_LOGCONFIG(TAG, " Position State Topic: '%s'", this->get_position_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Position Command Topic: '%s'", this->get_position_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Position State Topic: '%s'\n" + " Position Command Topic: '%s'", + this->get_position_state_topic().c_str(), this->get_position_command_topic().c_str()); } } void MQTTValveComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { diff --git a/esphome/components/msa3xx/msa3xx.cpp b/esphome/components/msa3xx/msa3xx.cpp index 75aa139e88..17f0a9c418 100644 --- a/esphome/components/msa3xx/msa3xx.cpp +++ b/esphome/components/msa3xx/msa3xx.cpp @@ -1,7 +1,7 @@ #include "msa3xx.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace msa3xx { @@ -161,13 +161,17 @@ void MSA3xxComponent::dump_config() { if (this->is_failed()) { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } - ESP_LOGCONFIG(TAG, " Model: %s", model_to_string(this->model_)); - ESP_LOGCONFIG(TAG, " Power Mode: %s", power_mode_to_string(this->power_mode_)); - ESP_LOGCONFIG(TAG, " Bandwidth: %s", bandwidth_to_string(this->bandwidth_)); - ESP_LOGCONFIG(TAG, " Range: %s", range_to_string(this->range_)); - ESP_LOGCONFIG(TAG, " Resolution: %s", res_to_string(this->resolution_)); - ESP_LOGCONFIG(TAG, " Offsets: {%.3f m/s², %.3f m/s², %.3f m/s²}", this->offset_x_, this->offset_y_, this->offset_z_); - ESP_LOGCONFIG(TAG, " Transform: {mirror_x=%s, mirror_y=%s, mirror_z=%s, swap_xy=%s}", YESNO(this->swap_.x_polarity), + ESP_LOGCONFIG(TAG, + " Model: %s\n" + " Power Mode: %s\n" + " Bandwidth: %s\n" + " Range: %s\n" + " Resolution: %s\n" + " Offsets: {%.3f m/s², %.3f m/s², %.3f m/s²}\n" + " Transform: {mirror_x=%s, mirror_y=%s, mirror_z=%s, swap_xy=%s}", + model_to_string(this->model_), power_mode_to_string(this->power_mode_), + bandwidth_to_string(this->bandwidth_), range_to_string(this->range_), res_to_string(this->resolution_), + this->offset_x_, this->offset_y_, this->offset_z_, YESNO(this->swap_.x_polarity), YESNO(this->swap_.y_polarity), YESNO(this->swap_.z_polarity), YESNO(this->swap_.x_y_swap)); LOG_UPDATE_INTERVAL(this); @@ -248,10 +252,10 @@ void MSA3xxComponent::loop() { } void MSA3xxComponent::update() { - ESP_LOGV(TAG, "Updating MSA3xx..."); + ESP_LOGV(TAG, "Updating"); if (!this->is_ready()) { - ESP_LOGV(TAG, "Component MSA3xx not ready for update"); + ESP_LOGV(TAG, "Not ready for update"); return; } ESP_LOGV(TAG, "Acceleration: {x = %+1.3f m/s², y = %+1.3f m/s², z = %+1.3f m/s²}; ", this->data_.x, this->data_.y, diff --git a/esphome/components/my9231/my9231.cpp b/esphome/components/my9231/my9231.cpp index 5edaa83c38..691c945254 100644 --- a/esphome/components/my9231/my9231.cpp +++ b/esphome/components/my9231/my9231.cpp @@ -1,6 +1,6 @@ #include "my9231.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace my9231 { @@ -63,9 +63,11 @@ void MY9231OutputComponent::dump_config() { ESP_LOGCONFIG(TAG, "MY9231:"); LOG_PIN(" DI Pin: ", this->pin_di_); LOG_PIN(" DCKI Pin: ", this->pin_dcki_); - ESP_LOGCONFIG(TAG, " Total number of channels: %u", this->num_channels_); - ESP_LOGCONFIG(TAG, " Number of chips: %u", this->num_chips_); - ESP_LOGCONFIG(TAG, " Bit depth: %u", this->bit_depth_); + ESP_LOGCONFIG(TAG, + " Total number of channels: %u\n" + " Number of chips: %u\n" + " Bit depth: %u", + this->num_channels_, this->num_chips_, this->bit_depth_); } void MY9231OutputComponent::loop() { if (!this->update_) diff --git a/esphome/components/nau7802/nau7802.cpp b/esphome/components/nau7802/nau7802.cpp index fa4c4b64a1..edcd114852 100644 --- a/esphome/components/nau7802/nau7802.cpp +++ b/esphome/components/nau7802/nau7802.cpp @@ -131,8 +131,10 @@ void NAU7802Sensor::dump_config() { return; } // Note these may differ from the values on the device if calbration has been run - ESP_LOGCONFIG(TAG, " Offset Calibration: %s", to_string(this->offset_calibration_).c_str()); - ESP_LOGCONFIG(TAG, " Gain Calibration: %f", this->gain_calibration_); + ESP_LOGCONFIG(TAG, + " Offset Calibration: %s\n" + " Gain Calibration: %f", + to_string(this->offset_calibration_).c_str(), this->gain_calibration_); std::string voltage = "unknown"; switch (this->ldo_) { diff --git a/esphome/components/neopixelbus/light.py b/esphome/components/neopixelbus/light.py index affeb2de8f..3cd1bfd357 100644 --- a/esphome/components/neopixelbus/light.py +++ b/esphome/components/neopixelbus/light.py @@ -215,4 +215,7 @@ async def to_code(config): # https://github.com/Makuna/NeoPixelBus/blob/master/library.json # Version Listed Here: https://registry.platformio.org/libraries/makuna/NeoPixelBus/versions - cg.add_library("makuna/NeoPixelBus", "2.7.3") + if CORE.is_esp32: + cg.add_library("makuna/NeoPixelBus", "2.8.0") + else: + cg.add_library("makuna/NeoPixelBus", "2.7.3") diff --git a/esphome/components/neopixelbus/neopixelbus_light.h b/esphome/components/neopixelbus/neopixelbus_light.h index 5233886075..c27244b94d 100644 --- a/esphome/components/neopixelbus/neopixelbus_light.h +++ b/esphome/components/neopixelbus/neopixelbus_light.h @@ -1,11 +1,11 @@ #pragma once -#ifdef USE_ARDUINO +#if defined(USE_ARDUINO) && !defined(CLANG_TIDY) -#include "esphome/core/macros.h" +#include "esphome/core/color.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include "esphome/core/color.h" +#include "esphome/core/macros.h" #include "esphome/components/light/light_output.h" #include "esphome/components/light/addressable_light.h" diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index 129b1ced06..b04fca7a1c 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -2,7 +2,7 @@ import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option import esphome.config_validation as cv from esphome.const import CONF_ENABLE_IPV6, CONF_MIN_IPV6_ADDR_COUNT -from esphome.core import CORE +from esphome.core import CORE, coroutine_with_priority CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["mdns"] @@ -36,8 +36,11 @@ CONFIG_SCHEMA = cv.Schema( ) +@coroutine_with_priority(201.0) async def to_code(config): cg.add_define("USE_NETWORK") + if CORE.using_arduino and CORE.is_esp32: + cg.add_library("Networking", None) if (enable_ipv6 := config.get(CONF_ENABLE_IPV6, None)) is not None: cg.add_define("USE_NETWORK_IPV6", enable_ipv6) if enable_ipv6: diff --git a/esphome/components/network/ip_address.h b/esphome/components/network/ip_address.h index 1598daf6f9..5e6b0dbd96 100644 --- a/esphome/components/network/ip_address.h +++ b/esphome/components/network/ip_address.h @@ -5,8 +5,8 @@ #include #include #include -#include "esphome/core/macros.h" #include "esphome/core/helpers.h" +#include "esphome/core/macros.h" #if defined(USE_ESP_IDF) || defined(USE_LIBRETINY) || USE_ARDUINO_VERSION_CODE > VERSION_CODE(3, 0, 0) #include @@ -56,6 +56,7 @@ struct IPAddress { IP_ADDR4(&ip_addr_, first, second, third, fourth); } IPAddress(const ip_addr_t *other_ip) { ip_addr_copy(ip_addr_, *other_ip); } + IPAddress(const char *in_address) { ipaddr_aton(in_address, &ip_addr_); } IPAddress(const std::string &in_address) { ipaddr_aton(in_address.c_str(), &ip_addr_); } IPAddress(ip4_addr_t *other_ip) { memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip4_addr_t)); diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp index ed519f738a..a8e792a2d7 100644 --- a/esphome/components/network/util.cpp +++ b/esphome/components/network/util.cpp @@ -9,6 +9,10 @@ #include "esphome/components/ethernet/ethernet_component.h" #endif +#ifdef USE_OPENTHREAD +#include "esphome/components/openthread/openthread.h" +#endif + namespace esphome { namespace network { @@ -23,6 +27,11 @@ bool is_connected() { return wifi::global_wifi_component->is_connected(); #endif +#ifdef USE_OPENTHREAD + if (openthread::global_openthread_component != nullptr) + return openthread::global_openthread_component->is_connected(); +#endif + #ifdef USE_HOST return true; // Assume its connected #endif @@ -45,6 +54,10 @@ network::IPAddresses get_ip_addresses() { #ifdef USE_WIFI if (wifi::global_wifi_component != nullptr) return wifi::global_wifi_component->get_ip_addresses(); +#endif +#ifdef USE_OPENTHREAD + if (openthread::global_openthread_component != nullptr) + return openthread::global_openthread_component->get_ip_addresses(); #endif return {}; } diff --git a/esphome/components/nextion/base_component.py b/esphome/components/nextion/base_component.py index 0058d957dc..98dea4b513 100644 --- a/esphome/components/nextion/base_component.py +++ b/esphome/components/nextion/base_component.py @@ -14,6 +14,8 @@ CONF_COMPONENT_NAME = "component_name" CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start" CONF_FONT_ID = "font_id" CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color" +CONF_MAX_COMMANDS_PER_LOOP = "max_commands_per_loop" +CONF_MAX_QUEUE_SIZE = "max_queue_size" CONF_ON_BUFFER_OVERFLOW = "on_buffer_overflow" CONF_ON_PAGE = "on_page" CONF_ON_SETUP = "on_setup" diff --git a/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp index 499cd901c0..b6d4cc3f23 100644 --- a/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +++ b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp @@ -16,7 +16,7 @@ void NextionBinarySensor::process_bool(const std::string &variable_name, bool st if (this->variable_name_ == variable_name) { this->publish_state(state); - ESP_LOGD(TAG, "Processed binarysensor \"%s\" state %s", variable_name.c_str(), state ? "ON" : "OFF"); + ESP_LOGD(TAG, "Binary sensor: %s=%s", variable_name.c_str(), ONOFF(state)); } } @@ -56,13 +56,12 @@ void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nexti this->publish_state(state); } else { this->state = state; - this->has_state_ = true; + this->set_has_state(true); } this->update_component_settings(); - ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %s", this->variable_name_.c_str(), - ONOFF(this->variable_name_.c_str())); + ESP_LOGN(TAG, "Write: %s=%s", this->variable_name_.c_str(), ONOFF(this->state)); } } // namespace nextion diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index 2e7c1c2825..0aa5efeba7 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -16,10 +16,12 @@ from .base_component import ( CONF_AUTO_WAKE_ON_TOUCH, CONF_COMMAND_SPACING, CONF_EXIT_REPARSE_ON_START, + CONF_MAX_COMMANDS_PER_LOOP, + CONF_MAX_QUEUE_SIZE, CONF_ON_BUFFER_OVERFLOW, + CONF_ON_PAGE, CONF_ON_SETUP, CONF_ON_SLEEP, - CONF_ON_PAGE, CONF_ON_WAKE, CONF_SKIP_CONNECTION_HANDSHAKE, CONF_START_UP_PAGE, @@ -49,8 +51,27 @@ CONFIG_SCHEMA = ( display.BASIC_DISPLAY_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(Nextion), - cv.Optional(CONF_TFT_URL): cv.url, + cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean, cv.Optional(CONF_BRIGHTNESS): cv.percentage, + cv.Optional(CONF_COMMAND_SPACING): cv.All( + cv.positive_time_period_milliseconds, + cv.Range(max=TimePeriod(milliseconds=255)), + ), + cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean, + cv.Optional(CONF_MAX_COMMANDS_PER_LOOP): cv.uint16_t, + cv.Optional(CONF_MAX_QUEUE_SIZE): cv.positive_int, + cv.Optional(CONF_ON_BUFFER_OVERFLOW): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + BufferOverflowTrigger + ), + } + ), + cv.Optional(CONF_ON_PAGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PageTrigger), + } + ), cv.Optional(CONF_ON_SETUP): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SetupTrigger), @@ -61,38 +82,21 @@ CONFIG_SCHEMA = ( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SleepTrigger), } ), - cv.Optional(CONF_ON_WAKE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WakeTrigger), - } - ), - cv.Optional(CONF_ON_PAGE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PageTrigger), - } - ), cv.Optional(CONF_ON_TOUCH): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TouchTrigger), } ), - cv.Optional(CONF_ON_BUFFER_OVERFLOW): automation.validate_automation( + cv.Optional(CONF_ON_WAKE): automation.validate_automation( { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - BufferOverflowTrigger - ), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WakeTrigger), } ), + cv.Optional(CONF_SKIP_CONNECTION_HANDSHAKE, default=False): cv.boolean, + cv.Optional(CONF_START_UP_PAGE): cv.uint8_t, + cv.Optional(CONF_TFT_URL): cv.url, cv.Optional(CONF_TOUCH_SLEEP_TIMEOUT): cv.int_range(min=3, max=65535), cv.Optional(CONF_WAKE_UP_PAGE): cv.uint8_t, - cv.Optional(CONF_START_UP_PAGE): cv.uint8_t, - cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean, - cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean, - cv.Optional(CONF_SKIP_CONNECTION_HANDSHAKE, default=False): cv.boolean, - cv.Optional(CONF_COMMAND_SPACING): cv.All( - cv.positive_time_period_milliseconds, - cv.Range(max=TimePeriod(milliseconds=255)), - ), } ) .extend(cv.polling_component_schema("5s")) @@ -125,6 +129,10 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await uart.register_uart_device(var, config) + if max_queue_size := config.get(CONF_MAX_QUEUE_SIZE): + cg.add_define("USE_NEXTION_MAX_QUEUE_SIZE") + cg.add(var.set_max_queue_size(max_queue_size)) + if command_spacing := config.get(CONF_COMMAND_SPACING): cg.add_define("USE_NEXTION_COMMAND_SPACING") cg.add(var.set_command_spacing(command_spacing.total_milliseconds)) @@ -142,7 +150,7 @@ async def to_code(config): cg.add_define("USE_NEXTION_TFT_UPLOAD") cg.add(var.set_tft_url(config[CONF_TFT_URL])) if CORE.is_esp32 and CORE.using_arduino: - cg.add_library("WiFiClientSecure", None) + cg.add_library("NetworkClientSecure", None) cg.add_library("HTTPClient", None) elif CORE.is_esp32 and CORE.using_esp_idf: esp32.add_idf_sdkconfig_option("CONFIG_ESP_TLS_INSECURE", True) @@ -167,6 +175,10 @@ async def to_code(config): cg.add(var.set_skip_connection_handshake(config[CONF_SKIP_CONNECTION_HANDSHAKE])) + if max_commands_per_loop := config.get(CONF_MAX_COMMANDS_PER_LOOP): + cg.add_define("USE_NEXTION_MAX_COMMANDS_PER_LOOP") + cg.add(var.set_max_commands_per_loop(max_commands_per_loop)) + await display.register_display(var, config) for conf in config.get(CONF_ON_SETUP, []): diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 66812170be..4f08fcb393 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -1,8 +1,8 @@ #include "nextion.h" -#include "esphome/core/util.h" -#include "esphome/core/log.h" -#include "esphome/core/application.h" #include +#include "esphome/core/application.h" +#include "esphome/core/log.h" +#include "esphome/core/util.h" namespace esphome { namespace nextion { @@ -33,20 +33,17 @@ bool Nextion::send_command_(const std::string &command) { #ifdef USE_NEXTION_COMMAND_SPACING if (!this->ignore_is_setup_ && !this->command_pacer_.can_send()) { + ESP_LOGN(TAG, "Command spacing: delaying command '%s'", command.c_str()); return false; } #endif // USE_NEXTION_COMMAND_SPACING - ESP_LOGN(TAG, "send_command %s", command.c_str()); + ESP_LOGN(TAG, "cmd: %s", command.c_str()); this->write_str(command.c_str()); const uint8_t to_send[3] = {0xFF, 0xFF, 0xFF}; this->write_array(to_send, sizeof(to_send)); -#ifdef USE_NEXTION_COMMAND_SPACING - this->command_pacer_.mark_sent(); -#endif // USE_NEXTION_COMMAND_SPACING - return true; } @@ -57,7 +54,7 @@ bool Nextion::check_connect_() { // Check if the handshake should be skipped for the Nextion connection if (this->skip_connection_handshake_) { // Log the connection status without handshake - ESP_LOGW(TAG, "Nextion display set as connected without performing handshake"); + ESP_LOGW(TAG, "Connected (no handshake)"); // Set the connection status to true this->is_connected_ = true; // Return true indicating the connection is set @@ -74,13 +71,13 @@ bool Nextion::check_connect_() { } this->send_command_("connect"); - this->comok_sent_ = millis(); + this->comok_sent_ = App.get_loop_component_start_time(); this->ignore_is_setup_ = false; return false; } - if (millis() - this->comok_sent_ <= 500) // Wait 500 ms + if (App.get_loop_component_start_time() - this->comok_sent_ <= 500) // Wait 500 ms return false; std::string response; @@ -88,27 +85,27 @@ bool Nextion::check_connect_() { this->recv_ret_string_(response, 0, false); if (!response.empty() && response[0] == 0x1A) { // Swallow invalid variable name responses that may be caused by the above commands - ESP_LOGD(TAG, "0x1A error ignored during setup"); + ESP_LOGD(TAG, "0x1A error ignored (setup)"); return false; } if (response.empty() || response.find("comok") == std::string::npos) { #ifdef NEXTION_PROTOCOL_LOG - ESP_LOGN(TAG, "Bad connect request %s", response.c_str()); + ESP_LOGN(TAG, "Bad connect: %s", response.c_str()); for (size_t i = 0; i < response.length(); i++) { - ESP_LOGN(TAG, "response %s %d %d %c", response.c_str(), i, response[i], response[i]); + ESP_LOGN(TAG, "resp: %s %d %d %c", response.c_str(), i, response[i], response[i]); } #endif - ESP_LOGW(TAG, "Nextion is not connected! "); + ESP_LOGW(TAG, "Not connected"); comok_sent_ = 0; return false; } this->ignore_is_setup_ = true; - ESP_LOGI(TAG, "Nextion is connected"); + ESP_LOGI(TAG, "Connected"); this->is_connected_ = true; - ESP_LOGN(TAG, "connect request %s", response.c_str()); + ESP_LOGN(TAG, "connect: %s", response.c_str()); size_t start; size_t end = 0; @@ -120,14 +117,14 @@ bool Nextion::check_connect_() { this->is_detected_ = (connect_info.size() == 7); if (this->is_detected_) { - ESP_LOGN(TAG, "Received connect_info %zu", connect_info.size()); + ESP_LOGN(TAG, "Connect info: %zu", connect_info.size()); this->device_model_ = connect_info[2]; this->firmware_version_ = connect_info[3]; this->serial_number_ = connect_info[5]; this->flash_size_ = connect_info[6]; } else { - ESP_LOGE(TAG, "Nextion returned bad connect value \"%s\"", response.c_str()); + ESP_LOGE(TAG, "Bad connect value: '%s'", response.c_str()); } this->ignore_is_setup_ = false; @@ -148,31 +145,43 @@ void Nextion::reset_(bool reset_nextion) { void Nextion::dump_config() { ESP_LOGCONFIG(TAG, "Nextion:"); if (this->skip_connection_handshake_) { - ESP_LOGCONFIG(TAG, " Skip handshake: %s", YESNO(this->skip_connection_handshake_)); + ESP_LOGCONFIG(TAG, " Skip handshake: %s", YESNO(this->skip_connection_handshake_)); } else { - ESP_LOGCONFIG(TAG, " Device Model: %s", this->device_model_.c_str()); - ESP_LOGCONFIG(TAG, " Firmware Version: %s", this->firmware_version_.c_str()); - ESP_LOGCONFIG(TAG, " Serial Number: %s", this->serial_number_.c_str()); - ESP_LOGCONFIG(TAG, " Flash Size: %s", this->flash_size_.c_str()); + ESP_LOGCONFIG(TAG, + " Device Model: %s\n" + " FW Version: %s\n" + " Serial Number: %s\n" + " Flash Size: %s", + this->device_model_.c_str(), this->firmware_version_.c_str(), this->serial_number_.c_str(), + this->flash_size_.c_str()); } - ESP_LOGCONFIG(TAG, " Wake On Touch: %s", YESNO(this->auto_wake_on_touch_)); - ESP_LOGCONFIG(TAG, " Exit reparse: %s", YESNO(this->exit_reparse_on_start_)); + ESP_LOGCONFIG(TAG, + " Wake On Touch: %s\n" + " Exit reparse: %s", + YESNO(this->auto_wake_on_touch_), YESNO(this->exit_reparse_on_start_)); +#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP + ESP_LOGCONFIG(TAG, " Max commands per loop: %u", this->max_commands_per_loop_); +#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP if (this->touch_sleep_timeout_ != 0) { - ESP_LOGCONFIG(TAG, " Touch Timeout: %" PRIu32, this->touch_sleep_timeout_); + ESP_LOGCONFIG(TAG, " Touch Timeout: %" PRIu32, this->touch_sleep_timeout_); } if (this->wake_up_page_ != -1) { - ESP_LOGCONFIG(TAG, " Wake Up Page: %" PRId16, this->wake_up_page_); + ESP_LOGCONFIG(TAG, " Wake Up Page: %d", this->wake_up_page_); } if (this->start_up_page_ != -1) { - ESP_LOGCONFIG(TAG, " Start Up Page: %" PRId16, this->start_up_page_); + ESP_LOGCONFIG(TAG, " Start Up Page: %d", this->start_up_page_); } #ifdef USE_NEXTION_COMMAND_SPACING - ESP_LOGCONFIG(TAG, " Command spacing: %" PRIu8 "ms", this->command_pacer_.get_spacing()); + ESP_LOGCONFIG(TAG, " Cmd spacing: %u ms", this->command_pacer_.get_spacing()); #endif // USE_NEXTION_COMMAND_SPACING + +#ifdef USE_NEXTION_MAX_QUEUE_SIZE + ESP_LOGCONFIG(TAG, " Max queue size: %zu", this->max_queue_size_); +#endif } float Nextion::get_setup_priority() const { return setup_priority::DATA; } @@ -248,7 +257,7 @@ bool Nextion::send_command_printf(const char *format, ...) { int ret = vsnprintf(buffer, sizeof(buffer), format, arg); va_end(arg); if (ret <= 0) { - ESP_LOGW(TAG, "Building command for format '%s' failed!", format); + ESP_LOGW(TAG, "Bad cmd format: '%s'", format); return false; } @@ -269,9 +278,9 @@ void Nextion::print_queue_members_() { break; if (i == nullptr) { - ESP_LOGN(TAG, "Nextion queue is null"); + ESP_LOGN(TAG, "Queue null"); } else { - ESP_LOGN(TAG, "Nextion queue type: %d:%s , name: %s", i->component->get_queue_type(), + ESP_LOGN(TAG, "Queue type: %d:%s, name: %s", i->component->get_queue_type(), i->component->get_queue_type_string().c_str(), i->component->get_variable_name().c_str()); } } @@ -309,32 +318,55 @@ void Nextion::loop() { if (!this->nextion_reports_is_setup_) { if (this->started_ms_ == 0) - this->started_ms_ = millis(); + this->started_ms_ = App.get_loop_component_start_time(); - if (this->started_ms_ + this->startup_override_ms_ < millis()) { - ESP_LOGD(TAG, "Manually set nextion report ready"); + if (this->started_ms_ + this->startup_override_ms_ < App.get_loop_component_start_time()) { + ESP_LOGD(TAG, "Manual ready set"); this->nextion_reports_is_setup_ = true; } } + +#ifdef USE_NEXTION_COMMAND_SPACING + // Try to send any pending commands if spacing allows + this->process_pending_in_queue_(); +#endif // USE_NEXTION_COMMAND_SPACING } +#ifdef USE_NEXTION_COMMAND_SPACING +void Nextion::process_pending_in_queue_() { + if (this->nextion_queue_.empty() || !this->command_pacer_.can_send()) { + return; + } + + // Check if first item in queue has a pending command + auto *front_item = this->nextion_queue_.front(); + if (front_item && !front_item->pending_command.empty()) { + if (this->send_command_(front_item->pending_command)) { + // Command sent successfully, clear the pending command + front_item->pending_command.clear(); + ESP_LOGVV(TAG, "Pending command sent: %s", front_item->component->get_variable_name().c_str()); + } + } +} +#endif // USE_NEXTION_COMMAND_SPACING + bool Nextion::remove_from_q_(bool report_empty) { if (this->nextion_queue_.empty()) { if (report_empty) { - ESP_LOGE(TAG, "Nextion queue is empty!"); + ESP_LOGE(TAG, "Queue empty"); } return false; } NextionQueue *nb = this->nextion_queue_.front(); if (!nb || !nb->component) { - ESP_LOGE(TAG, "Invalid queue entry!"); + ESP_LOGE(TAG, "Invalid queue"); this->nextion_queue_.pop_front(); return false; } NextionComponentBase *component = nb->component; - ESP_LOGN(TAG, "Removing %s from the queue", component->get_variable_name().c_str()); + ESP_LOGN(TAG, "Removed: %s", component->get_variable_name().c_str()); if (component->get_queue_type() == NextionQueueType::NO_RESULT) { if (component->get_variable_name() == "sleep_wake") { @@ -361,25 +393,29 @@ void Nextion::process_nextion_commands_() { return; } -#ifdef USE_NEXTION_COMMAND_SPACING - if (!this->command_pacer_.can_send()) { - return; // Will try again in next loop iteration - } -#endif +#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP + size_t commands_processed = 0; +#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP size_t to_process_length = 0; std::string to_process; - ESP_LOGN(TAG, "this->command_data_ %s length %d", this->command_data_.c_str(), this->command_data_.length()); + ESP_LOGN(TAG, "command_data_ %s len %d", this->command_data_.c_str(), this->command_data_.length()); #ifdef NEXTION_PROTOCOL_LOG this->print_queue_members_(); #endif while ((to_process_length = this->command_data_.find(COMMAND_DELIMITER)) != std::string::npos) { - ESP_LOGN(TAG, "print_queue_members_ size %zu", this->nextion_queue_.size()); +#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP + if (++commands_processed > this->max_commands_per_loop_) { + ESP_LOGW(TAG, "Command processing limit exceeded"); + break; + } +#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP + ESP_LOGN(TAG, "queue size: %zu", this->nextion_queue_.size()); while (to_process_length + COMMAND_DELIMITER.length() < this->command_data_.length() && static_cast(this->command_data_[to_process_length + COMMAND_DELIMITER.length()]) == 0xFF) { ++to_process_length; - ESP_LOGN(TAG, "Add extra 0xFF to process"); + ESP_LOGN(TAG, "Add 0xFF"); } this->nextion_event_ = this->command_data_[0]; @@ -389,118 +425,114 @@ void Nextion::process_nextion_commands_() { switch (this->nextion_event_) { case 0x00: // instruction sent by user has failed - ESP_LOGW(TAG, "Nextion reported invalid instruction!"); + ESP_LOGW(TAG, "Invalid instruction"); this->remove_from_q_(); break; case 0x01: // instruction sent by user was successful - ESP_LOGVV(TAG, "instruction sent by user was successful"); - ESP_LOGN(TAG, "this->nextion_queue_.empty() %s", this->nextion_queue_.empty() ? "True" : "False"); + ESP_LOGVV(TAG, "Cmd OK"); + ESP_LOGN(TAG, "this->nextion_queue_.empty() %s", YESNO(this->nextion_queue_.empty())); this->remove_from_q_(); if (!this->is_setup_) { if (this->nextion_queue_.empty()) { - ESP_LOGD(TAG, "Nextion is setup"); + ESP_LOGD(TAG, "Setup complete"); this->is_setup_ = true; this->setup_callback_.call(); } } #ifdef USE_NEXTION_COMMAND_SPACING this->command_pacer_.mark_sent(); // Here is where we should mark the command as sent + ESP_LOGN(TAG, "Command spacing: marked command sent"); #endif break; case 0x02: // invalid Component ID or name was used - ESP_LOGW(TAG, "Nextion reported component ID or name invalid!"); + ESP_LOGW(TAG, "Invalid component ID/name"); this->remove_from_q_(); break; case 0x03: // invalid Page ID or name was used - ESP_LOGW(TAG, "Nextion reported page ID invalid!"); + ESP_LOGW(TAG, "Invalid page ID"); this->remove_from_q_(); break; case 0x04: // invalid Picture ID was used - ESP_LOGW(TAG, "Nextion reported picture ID invalid!"); + ESP_LOGW(TAG, "Invalid picture ID"); this->remove_from_q_(); break; case 0x05: // invalid Font ID was used - ESP_LOGW(TAG, "Nextion reported font ID invalid!"); + ESP_LOGW(TAG, "Invalid font ID"); this->remove_from_q_(); break; case 0x06: // File operation fails - ESP_LOGW(TAG, "Nextion File operation fail!"); + ESP_LOGW(TAG, "File operation failed"); break; case 0x09: // Instructions with CRC validation fails their CRC check - ESP_LOGW(TAG, "Nextion Instructions with CRC validation fails their CRC check!"); + ESP_LOGW(TAG, "CRC validation failed"); break; case 0x11: // invalid Baud rate was used - ESP_LOGW(TAG, "Nextion reported baud rate invalid!"); + ESP_LOGW(TAG, "Invalid baud rate"); break; case 0x12: // invalid Waveform ID or Channel # was used if (this->waveform_queue_.empty()) { - ESP_LOGW(TAG, - "Nextion reported invalid Waveform ID or Channel # was used but no waveform sensor in queue found!"); + ESP_LOGW(TAG, "Waveform ID/ch used but no sensor queued"); } else { auto &nb = this->waveform_queue_.front(); NextionComponentBase *component = nb->component; - ESP_LOGW(TAG, "Nextion reported invalid Waveform ID %d or Channel # %d was used!", - component->get_component_id(), component->get_wave_channel_id()); + ESP_LOGW(TAG, "Invalid waveform ID %d/ch %d", component->get_component_id(), + component->get_wave_channel_id()); - ESP_LOGN(TAG, "Removing waveform from queue with component id %d and waveform id %d", - component->get_component_id(), component->get_wave_channel_id()); + ESP_LOGN(TAG, "Remove waveform ID %d/ch %d", component->get_component_id(), component->get_wave_channel_id()); delete nb; // NOLINT(cppcoreguidelines-owning-memory) this->waveform_queue_.pop_front(); } break; case 0x1A: // variable name invalid - ESP_LOGW(TAG, "Nextion reported variable name invalid!"); + ESP_LOGW(TAG, "Invalid variable name"); this->remove_from_q_(); break; case 0x1B: // variable operation invalid - ESP_LOGW(TAG, "Nextion reported variable operation invalid!"); + ESP_LOGW(TAG, "Invalid variable operation"); this->remove_from_q_(); break; case 0x1C: // failed to assign - ESP_LOGW(TAG, "Nextion reported failed to assign variable!"); + ESP_LOGW(TAG, "Variable assign failed"); this->remove_from_q_(); break; case 0x1D: // operate EEPROM failed - ESP_LOGW(TAG, "Nextion reported operating EEPROM failed!"); + ESP_LOGW(TAG, "EEPROM operation failed"); break; case 0x1E: // parameter quantity invalid - ESP_LOGW(TAG, "Nextion reported parameter quantity invalid!"); + ESP_LOGW(TAG, "Invalid parameter count"); this->remove_from_q_(); break; case 0x1F: // IO operation failed - ESP_LOGW(TAG, "Nextion reported component I/O operation invalid!"); + ESP_LOGW(TAG, "Invalid component I/O"); break; case 0x20: // undefined escape characters - ESP_LOGW(TAG, "Nextion reported undefined escape characters!"); + ESP_LOGW(TAG, "Undefined escape chars"); this->remove_from_q_(); break; case 0x23: // too long variable name - ESP_LOGW(TAG, "Nextion reported too long variable name!"); + ESP_LOGW(TAG, "Variable name too long"); this->remove_from_q_(); break; case 0x24: // Serial Buffer overflow occurs // Buffer will continue to receive the current instruction, all previous instructions are lost. - ESP_LOGE(TAG, "Nextion reported Serial Buffer overflow!"); + ESP_LOGE(TAG, "Serial buffer overflow"); this->buffer_overflow_callback_.call(); break; case 0x65: { // touch event return data if (to_process_length != 3) { - ESP_LOGW(TAG, "Touch event data is expecting 3, received %zu", to_process_length); + ESP_LOGW(TAG, "Incorrect touch len: %zu (need 3)", to_process_length); break; } uint8_t page_id = to_process[0]; uint8_t component_id = to_process[1]; uint8_t touch_event = to_process[2]; // 0 -> release, 1 -> press - ESP_LOGD(TAG, "Got touch event:"); - ESP_LOGD(TAG, " page_id: %u", page_id); - ESP_LOGD(TAG, " component_id: %u", component_id); - ESP_LOGD(TAG, " event type: %s", touch_event ? "PRESS" : "RELEASE"); + ESP_LOGD(TAG, "Touch %s: page %u comp %u", touch_event ? "PRESS" : "RELEASE", page_id, component_id); for (auto *touch : this->touch_) { touch->process_touch(page_id, component_id, touch_event != 0); } @@ -510,12 +542,12 @@ void Nextion::process_nextion_commands_() { case 0x66: { // Nextion initiated new page event return data. // Also is used for sendme command which we never explicitly initiate if (to_process_length != 1) { - ESP_LOGW(TAG, "New page event data is expecting 1, received %zu", to_process_length); + ESP_LOGW(TAG, "Page event: expect 1, got %zu", to_process_length); break; } uint8_t page_id = to_process[0]; - ESP_LOGD(TAG, "Got new page: %u", page_id); + ESP_LOGD(TAG, "New page: %u", page_id); this->page_callback_.call(page_id); break; } @@ -525,7 +557,7 @@ void Nextion::process_nextion_commands_() { case 0x68: { // touch coordinate data (sleep) if (to_process_length != 5) { - ESP_LOGW(TAG, "Touch coordinate data is expecting 5, received %zu", to_process_length); + ESP_LOGW(TAG, "Touch coordinate: expect 5, got %zu", to_process_length); ESP_LOGW(TAG, "%s", to_process.c_str()); break; } @@ -533,10 +565,7 @@ void Nextion::process_nextion_commands_() { uint16_t x = (uint16_t(to_process[0]) << 8) | to_process[1]; uint16_t y = (uint16_t(to_process[2]) << 8) | to_process[3]; uint8_t touch_event = to_process[4]; // 0 -> release, 1 -> press - ESP_LOGD(TAG, "Got touch event:"); - ESP_LOGD(TAG, " x: %u", x); - ESP_LOGD(TAG, " y: %u", y); - ESP_LOGD(TAG, " type: %s", touch_event ? "PRESS" : "RELEASE"); + ESP_LOGD(TAG, "Touch %s at %u,%u", touch_event ? "PRESS" : "RELEASE", x, y); break; } @@ -547,25 +576,23 @@ void Nextion::process_nextion_commands_() { case 0x70: // string variable data return { if (this->nextion_queue_.empty()) { - ESP_LOGW(TAG, "ERROR: Received string return but the queue is empty"); + ESP_LOGW(TAG, "String return but queue is empty"); break; } NextionQueue *nb = this->nextion_queue_.front(); if (!nb || !nb->component) { - ESP_LOGE(TAG, "Invalid queue entry!"); + ESP_LOGE(TAG, "Invalid queue entry"); this->nextion_queue_.pop_front(); return; } NextionComponentBase *component = nb->component; if (component->get_queue_type() != NextionQueueType::TEXT_SENSOR) { - ESP_LOGE(TAG, "ERROR: Received string return but next in queue \"%s\" is not a text sensor", - component->get_variable_name().c_str()); + ESP_LOGE(TAG, "String return but '%s' not text sensor", component->get_variable_name().c_str()); } else { - ESP_LOGN(TAG, "Received get_string response: \"%s\" for component id: %s, type: %s", to_process.c_str(), - component->get_variable_name().c_str(), component->get_queue_type_string().c_str()); - component->set_state_from_string(to_process, true, false); + ESP_LOGN(TAG, "String resp: '%s' id: %s type: %s", to_process.c_str(), component->get_variable_name().c_str(), + component->get_queue_type_string().c_str()); } delete nb; // NOLINT(cppcoreguidelines-owning-memory) @@ -581,12 +608,12 @@ void Nextion::process_nextion_commands_() { case 0x71: // numeric variable data return { if (this->nextion_queue_.empty()) { - ESP_LOGE(TAG, "ERROR: Received numeric return but the queue is empty"); + ESP_LOGE(TAG, "Numeric return but queue empty"); break; } if (to_process_length == 0) { - ESP_LOGE(TAG, "ERROR: Received numeric return but no data!"); + ESP_LOGE(TAG, "Numeric return but no data"); break; } @@ -598,7 +625,7 @@ void Nextion::process_nextion_commands_() { NextionQueue *nb = this->nextion_queue_.front(); if (!nb || !nb->component) { - ESP_LOGE(TAG, "Invalid queue entry!"); + ESP_LOGE(TAG, "Invalid queue"); this->nextion_queue_.pop_front(); return; } @@ -607,12 +634,11 @@ void Nextion::process_nextion_commands_() { if (component->get_queue_type() != NextionQueueType::SENSOR && component->get_queue_type() != NextionQueueType::BINARY_SENSOR && component->get_queue_type() != NextionQueueType::SWITCH) { - ESP_LOGE(TAG, "ERROR: Received numeric return but next in queue \"%s\" is not a valid sensor type %d", - component->get_variable_name().c_str(), component->get_queue_type()); + ESP_LOGE(TAG, "Numeric return but '%s' invalid type %d", component->get_variable_name().c_str(), + component->get_queue_type()); } else { - ESP_LOGN(TAG, "Received numeric return for variable %s, queue type %d:%s, value %d", - component->get_variable_name().c_str(), component->get_queue_type(), - component->get_queue_type_string().c_str(), value); + ESP_LOGN(TAG, "Numeric: %s type %d:%s val %d", component->get_variable_name().c_str(), + component->get_queue_type(), component->get_queue_type_string().c_str(), value); component->set_state_from_int(value, true, false); } @@ -623,14 +649,14 @@ void Nextion::process_nextion_commands_() { } case 0x86: { // device automatically enters into sleep mode - ESP_LOGVV(TAG, "Received Nextion entering sleep automatically"); + ESP_LOGVV(TAG, "Auto sleep"); this->is_sleeping_ = true; this->sleep_callback_.call(); break; } case 0x87: // device automatically wakes up { - ESP_LOGVV(TAG, "Received Nextion leaves sleep automatically"); + ESP_LOGVV(TAG, "Auto wake"); this->is_sleeping_ = false; this->wake_callback_.call(); this->all_components_send_state_(false); @@ -638,7 +664,7 @@ void Nextion::process_nextion_commands_() { } case 0x88: // system successful start up { - ESP_LOGD(TAG, "system successful start up %zu", to_process_length); + ESP_LOGD(TAG, "System start: %zu", to_process_length); this->nextion_reports_is_setup_ = true; break; } @@ -657,17 +683,15 @@ void Nextion::process_nextion_commands_() { // Get variable name auto index = to_process.find('\0'); if (index == std::string::npos || (to_process_length - index - 1) < 1) { - ESP_LOGE(TAG, "Bad switch component data received for 0x90 event!"); - ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + ESP_LOGE(TAG, "Bad switch data (0x90)"); + ESP_LOGN(TAG, "proc: %s %zu %d", to_process.c_str(), to_process_length, index); break; } variable_name = to_process.substr(0, index); ++index; - ESP_LOGN(TAG, "Got Switch:"); - ESP_LOGN(TAG, " variable_name: %s", variable_name.c_str()); - ESP_LOGN(TAG, " value: %d", to_process[0] != 0); + ESP_LOGN(TAG, "Switch %s: %s", ONOFF(to_process[index] != 0), variable_name.c_str()); for (auto *switchtype : this->switchtype_) { switchtype->process_bool(variable_name, to_process[index] != 0); @@ -685,8 +709,8 @@ void Nextion::process_nextion_commands_() { auto index = to_process.find('\0'); if (index == std::string::npos || (to_process_length - index - 1) != 4) { - ESP_LOGE(TAG, "Bad sensor component data received for 0x91 event!"); - ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + ESP_LOGE(TAG, "Bad sensor data (0x91)"); + ESP_LOGN(TAG, "proc: %s %zu %d", to_process.c_str(), to_process_length, index); break; } @@ -698,9 +722,7 @@ void Nextion::process_nextion_commands_() { value += to_process[i + index + 1] << (8 * i); } - ESP_LOGN(TAG, "Got sensor:"); - ESP_LOGN(TAG, " variable_name: %s", variable_name.c_str()); - ESP_LOGN(TAG, " value: %d", value); + ESP_LOGN(TAG, "Sensor: %s=%d", variable_name.c_str(), value); for (auto *sensor : this->sensortype_) { sensor->process_sensor(variable_name, value); @@ -722,8 +744,8 @@ void Nextion::process_nextion_commands_() { // Get variable name auto index = to_process.find('\0'); if (index == std::string::npos || (to_process_length - index - 1) < 1) { - ESP_LOGE(TAG, "Bad text sensor component data received for 0x92 event!"); - ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + ESP_LOGE(TAG, "Bad text data (0x92)"); + ESP_LOGN(TAG, "proc: %s %zu %d", to_process.c_str(), to_process_length, index); break; } @@ -732,9 +754,7 @@ void Nextion::process_nextion_commands_() { text_value = to_process.substr(index); - ESP_LOGN(TAG, "Got Text Sensor:"); - ESP_LOGN(TAG, " variable_name: %s", variable_name.c_str()); - ESP_LOGN(TAG, " value: %s", text_value.c_str()); + ESP_LOGN(TAG, "Text sensor: %s='%s'", variable_name.c_str(), text_value.c_str()); // NextionTextSensorResponseQueue *nq = new NextionTextSensorResponseQueue; // nq->variable_name = variable_name; @@ -757,17 +777,15 @@ void Nextion::process_nextion_commands_() { // Get variable name auto index = to_process.find('\0'); if (index == std::string::npos || (to_process_length - index - 1) < 1) { - ESP_LOGE(TAG, "Bad binary sensor component data received for 0x92 event!"); - ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + ESP_LOGE(TAG, "Bad binary data (0x92)"); + ESP_LOGN(TAG, "proc: %s %zu %d", to_process.c_str(), to_process_length, index); break; } variable_name = to_process.substr(0, index); ++index; - ESP_LOGN(TAG, "Got Binary Sensor:"); - ESP_LOGN(TAG, " variable_name: %s", variable_name.c_str()); - ESP_LOGN(TAG, " value: %d", to_process[index] != 0); + ESP_LOGN(TAG, "Binary sensor: %s=%s", variable_name.c_str(), ONOFF(to_process[index] != 0)); for (auto *binarysensortype : this->binarysensortype_) { binarysensortype->process_bool(&variable_name[0], to_process[index] != 0); @@ -775,14 +793,14 @@ void Nextion::process_nextion_commands_() { break; } case 0xFD: { // data transparent transmit finished - ESP_LOGVV(TAG, "Nextion reported data transmit finished!"); + ESP_LOGVV(TAG, "Data transmit done"); this->check_pending_waveform_(); break; } case 0xFE: { // data transparent transmit ready - ESP_LOGVV(TAG, "Nextion reported ready for transmit!"); + ESP_LOGVV(TAG, "Ready for transmit"); if (this->waveform_queue_.empty()) { - ESP_LOGE(TAG, "No waveforms in queue to send data!"); + ESP_LOGE(TAG, "No waveforms queued"); break; } @@ -793,8 +811,8 @@ void Nextion::process_nextion_commands_() { this->write_array(component->get_wave_buffer().data(), static_cast(buffer_to_send)); - ESP_LOGN(TAG, "Nextion sending waveform data for component id %d and waveform id %d, size %zu", - component->get_component_id(), component->get_wave_channel_id(), buffer_to_send); + ESP_LOGN(TAG, "Send waveform: component id %d, waveform id %d, size %zu", component->get_component_id(), + component->get_wave_channel_id(), buffer_to_send); component->clear_wave_buffer(buffer_to_send); delete nb; // NOLINT(cppcoreguidelines-owning-memory) @@ -802,32 +820,30 @@ void Nextion::process_nextion_commands_() { break; } default: - ESP_LOGW(TAG, "Received unknown event from nextion: 0x%02X", this->nextion_event_); + ESP_LOGW(TAG, "Unknown event: 0x%02X", this->nextion_event_); break; } // ESP_LOGN(TAG, "nextion_event_ deleting from 0 to %d", to_process_length + COMMAND_DELIMITER.length() + 1); this->command_data_.erase(0, to_process_length + COMMAND_DELIMITER.length() + 1); - // App.feed_wdt(); Remove before master merge - this->process_serial_(); } - uint32_t ms = millis(); + uint32_t ms = App.get_loop_component_start_time(); if (!this->nextion_queue_.empty() && this->nextion_queue_.front()->queue_time + this->max_q_age_ms_ < ms) { for (size_t i = 0; i < this->nextion_queue_.size(); i++) { NextionComponentBase *component = this->nextion_queue_[i]->component; if (this->nextion_queue_[i]->queue_time + this->max_q_age_ms_ < ms) { if (this->nextion_queue_[i]->queue_time == 0) { - ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\" queue_time 0", - component->get_queue_type_string().c_str(), component->get_variable_name().c_str()); + ESP_LOGD(TAG, "Remove old queue '%s':'%s' (t=0)", component->get_queue_type_string().c_str(), + component->get_variable_name().c_str()); } if (component->get_variable_name() == "sleep_wake") { this->is_sleeping_ = false; } - ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\"", component->get_queue_type_string().c_str(), + ESP_LOGD(TAG, "Remove old queue '%s':'%s'", component->get_queue_type_string().c_str(), component->get_variable_name().c_str()); if (component->get_queue_type() == NextionQueueType::NO_RESULT) { @@ -847,20 +863,17 @@ void Nextion::process_nextion_commands_() { } } } - ESP_LOGN(TAG, "Loop End"); + ESP_LOGN(TAG, "Loop end"); // App.feed_wdt(); Remove before master merge this->process_serial_(); -} // namespace nextion +} // Nextion::process_nextion_commands_() void Nextion::set_nextion_sensor_state(int queue_type, const std::string &name, float state) { this->set_nextion_sensor_state(static_cast(queue_type), name, state); } void Nextion::set_nextion_sensor_state(NextionQueueType queue_type, const std::string &name, float state) { - ESP_LOGN(TAG, "Received state:"); - ESP_LOGN(TAG, " variable: %s", name.c_str()); - ESP_LOGN(TAG, " state: %lf", state); - ESP_LOGN(TAG, " queue type: %d", queue_type); + ESP_LOGN(TAG, "State: %s=%lf (type %d)", name.c_str(), state, queue_type); switch (queue_type) { case NextionQueueType::SENSOR: { @@ -891,15 +904,13 @@ void Nextion::set_nextion_sensor_state(NextionQueueType queue_type, const std::s break; } default: { - ESP_LOGW(TAG, "set_nextion_sensor_state does not support a queue type %d", queue_type); + ESP_LOGW(TAG, "set_sensor_state: bad type %d", queue_type); } } } void Nextion::set_nextion_text_state(const std::string &name, const std::string &state) { - ESP_LOGD(TAG, "Received state:"); - ESP_LOGD(TAG, " variable: %s", name.c_str()); - ESP_LOGD(TAG, " state: %s", state.c_str()); + ESP_LOGD(TAG, "State: %s='%s'", name.c_str(), state.c_str()); for (auto *sensor : this->textsensortype_) { if (name == sensor->get_variable_name()) { @@ -910,7 +921,7 @@ void Nextion::set_nextion_text_state(const std::string &name, const std::string } void Nextion::all_components_send_state_(bool force_update) { - ESP_LOGD(TAG, "all_components_send_state_ "); + ESP_LOGD(TAG, "Send states"); for (auto *binarysensortype : this->binarysensortype_) { if (force_update || binarysensortype->get_needs_to_send_update()) binarysensortype->send_state_to_nextion(); @@ -956,9 +967,9 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool bool exit_flag = false; bool ff_flag = false; - start = millis(); + start = App.get_loop_component_start_time(); - while ((timeout == 0 && this->available()) || millis() - start <= timeout) { + while ((timeout == 0 && this->available()) || App.get_loop_component_start_time() - start <= timeout) { if (!this->available()) { App.feed_wdt(); delay(1); @@ -998,15 +1009,27 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool } /** - * @brief + * @brief Add a command to the Nextion queue that expects no response. * - * @param variable_name Name for the queue + * This is typically used for write-only operations such as variable assignments or component updates + * where no return value or acknowledgment is expected from the display. + * + * If the `max_queue_size` limit is configured and reached, the command will be skipped. + * + * @param variable_name Name of the variable or component associated with the command. */ void Nextion::add_no_result_to_queue_(const std::string &variable_name) { - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); +#ifdef USE_NEXTION_MAX_QUEUE_SIZE + if (this->max_queue_size_ > 0 && this->nextion_queue_.size() >= this->max_queue_size_) { + ESP_LOGW(TAG, "Queue full (%zu), drop: %s", this->nextion_queue_.size(), variable_name.c_str()); + return; + } +#endif + + RAMAllocator allocator; nextion::NextionQueue *nextion_queue = allocator.allocate(1); if (nextion_queue == nullptr) { - ESP_LOGW(TAG, "Failed to allocate NextionQueue"); + ESP_LOGW(TAG, "Queue alloc failed"); return; } new (nextion_queue) nextion::NextionQueue(); @@ -1015,11 +1038,11 @@ void Nextion::add_no_result_to_queue_(const std::string &variable_name) { nextion_queue->component = new nextion::NextionComponentBase; nextion_queue->component->set_variable_name(variable_name); - nextion_queue->queue_time = millis(); + nextion_queue->queue_time = App.get_loop_component_start_time(); this->nextion_queue_.push_back(nextion_queue); - ESP_LOGN(TAG, "Add to queue type: NORESULT component %s", nextion_queue->component->get_variable_name().c_str()); + ESP_LOGN(TAG, "Queue NORESULT: %s", nextion_queue->component->get_variable_name().c_str()); } /** @@ -1034,9 +1057,42 @@ void Nextion::add_no_result_to_queue_with_command_(const std::string &variable_n if (this->send_command_(command)) { this->add_no_result_to_queue_(variable_name); +#ifdef USE_NEXTION_COMMAND_SPACING + } else { + // Command blocked by spacing, add to queue WITH the command for retry + this->add_no_result_to_queue_with_pending_command_(variable_name, command); +#endif // USE_NEXTION_COMMAND_SPACING } } +#ifdef USE_NEXTION_COMMAND_SPACING +void Nextion::add_no_result_to_queue_with_pending_command_(const std::string &variable_name, + const std::string &command) { +#ifdef USE_NEXTION_MAX_QUEUE_SIZE + if (this->max_queue_size_ > 0 && this->nextion_queue_.size() >= this->max_queue_size_) { + ESP_LOGW(TAG, "Queue full (%zu), drop: %s", this->nextion_queue_.size(), variable_name.c_str()); + return; + } +#endif + + RAMAllocator allocator; + nextion::NextionQueue *nextion_queue = allocator.allocate(1); + if (nextion_queue == nullptr) { + ESP_LOGW(TAG, "Queue alloc failed"); + return; + } + new (nextion_queue) nextion::NextionQueue(); + + nextion_queue->component = new nextion::NextionComponentBase; + nextion_queue->component->set_variable_name(variable_name); + nextion_queue->queue_time = App.get_loop_component_start_time(); + nextion_queue->pending_command = command; // Store command for retry + + this->nextion_queue_.push_back(nextion_queue); + ESP_LOGVV(TAG, "Queue with pending command: %s", variable_name.c_str()); +} +#endif // USE_NEXTION_COMMAND_SPACING + bool Nextion::add_no_result_to_queue_with_ignore_sleep_printf_(const std::string &variable_name, const char *format, ...) { if ((!this->is_setup() && !this->ignore_is_setup_)) @@ -1048,7 +1104,7 @@ bool Nextion::add_no_result_to_queue_with_ignore_sleep_printf_(const std::string int ret = vsnprintf(buffer, sizeof(buffer), format, arg); va_end(arg); if (ret <= 0) { - ESP_LOGW(TAG, "Building command for format '%s' failed!", format); + ESP_LOGW(TAG, "Bad cmd format: '%s'", format); return false; } @@ -1073,7 +1129,7 @@ bool Nextion::add_no_result_to_queue_with_printf_(const std::string &variable_na int ret = vsnprintf(buffer, sizeof(buffer), format, arg); va_end(arg); if (ret <= 0) { - ESP_LOGW(TAG, "Building command for format '%s' failed!", format); + ESP_LOGW(TAG, "Bad cmd format: '%s'", format); return false; } @@ -1138,23 +1194,39 @@ void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &varia state_value.c_str()); } +/** + * @brief Queue a GET command for a component that expects a response from the Nextion display. + * + * This method is used for querying values such as sensor states, text content, or switch status. + * The component will be added to the Nextion queue only if the display is already set up, + * the queue has not reached the configured maximum size (if set), and the command is sent successfully. + * + * @param component Pointer to the Nextion component that will handle the response. + */ void Nextion::add_to_get_queue(NextionComponentBase *component) { if ((!this->is_setup() && !this->ignore_is_setup_)) return; - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); +#ifdef USE_NEXTION_MAX_QUEUE_SIZE + if (this->max_queue_size_ > 0 && this->nextion_queue_.size() >= this->max_queue_size_) { + ESP_LOGW(TAG, "Queue full (%zu), drop GET: %s", this->nextion_queue_.size(), + component->get_variable_name().c_str()); + return; + } +#endif + + RAMAllocator allocator; nextion::NextionQueue *nextion_queue = allocator.allocate(1); if (nextion_queue == nullptr) { - ESP_LOGW(TAG, "Failed to allocate NextionQueue"); + ESP_LOGW(TAG, "Queue alloc failed"); return; } new (nextion_queue) nextion::NextionQueue(); nextion_queue->component = component; - nextion_queue->queue_time = millis(); + nextion_queue->queue_time = App.get_loop_component_start_time(); - ESP_LOGN(TAG, "Add to queue type: %s component %s", component->get_queue_type_string().c_str(), - component->get_variable_name().c_str()); + ESP_LOGN(TAG, "Queue %s: %s", component->get_queue_type_string().c_str(), component->get_variable_name().c_str()); std::string command = "get " + component->get_variable_name_to_send(); @@ -1175,16 +1247,16 @@ void Nextion::add_addt_command_to_queue(NextionComponentBase *component) { if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) return; - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; nextion::NextionQueue *nextion_queue = allocator.allocate(1); if (nextion_queue == nullptr) { - ESP_LOGW(TAG, "Failed to allocate NextionQueue"); + ESP_LOGW(TAG, "Queue alloc failed"); return; } new (nextion_queue) nextion::NextionQueue(); nextion_queue->component = component; - nextion_queue->queue_time = millis(); + nextion_queue->queue_time = App.get_loop_component_start_time(); this->waveform_queue_.push_back(nextion_queue); if (this->waveform_queue_.size() == 1) @@ -1210,8 +1282,8 @@ void Nextion::check_pending_waveform_() { void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; } -ESPDEPRECATED("set_wait_for_ack(bool) is deprecated and has no effect", "v1.20") -void Nextion::set_wait_for_ack(bool wait_for_ack) { ESP_LOGE(TAG, "This command is deprecated"); } +ESPDEPRECATED("set_wait_for_ack(bool) deprecated, no effect", "v1.20") +void Nextion::set_wait_for_ack(bool wait_for_ack) { ESP_LOGE(TAG, "Deprecated"); } bool Nextion::is_updating() { return this->is_updating_; } diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 4bc5305923..0cd559d251 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -75,6 +75,36 @@ class NextionCommandPacer { class Nextion : public NextionBase, public PollingComponent, public uart::UARTDevice { public: +#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP + /** + * @brief Set the maximum number of commands to process in each loop iteration + * @param value Maximum number of commands (default: 20) + * + * Limiting the number of commands per loop helps prevent stack overflows + * when a large number of commands are queued at once, especially during boot. + */ + inline void set_max_commands_per_loop(uint16_t value) { this->max_commands_per_loop_ = value; } + + /** + * @brief Get the current maximum number of commands allowed per loop iteration + * @return Configured command limit per loop + */ + inline uint16_t get_max_commands_per_loop() const { return this->max_commands_per_loop_; } +#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP +#ifdef USE_NEXTION_MAX_QUEUE_SIZE + /** + * @brief Set the maximum allowed queue size + * @param size Max number of entries allowed in nextion_queue_ + */ + inline void set_max_queue_size(size_t size) { this->max_queue_size_ = size; } + + /** + * @brief Get the maximum allowed queue size + * @return Current limit (0 = unlimited) + */ + inline size_t get_max_queue_size() const { return this->max_queue_size_; } +#endif // USE_NEXTION_MAX_QUEUE_SIZE + #ifdef USE_NEXTION_COMMAND_SPACING /** * @brief Set the command spacing for the display @@ -1273,9 +1303,29 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe bool is_connected() { return this->is_connected_; } protected: +#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP + uint16_t max_commands_per_loop_{1000}; +#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP +#ifdef USE_NEXTION_MAX_QUEUE_SIZE + size_t max_queue_size_{0}; +#endif // USE_NEXTION_MAX_QUEUE_SIZE + #ifdef USE_NEXTION_COMMAND_SPACING NextionCommandPacer command_pacer_{0}; + + /** + * @brief Process any commands in the queue that are pending due to command spacing + * + * This method checks if the first item in the nextion_queue_ has a pending command + * that was previously blocked by command spacing. If spacing now allows and a + * pending command exists, it attempts to send the command. Once successfully sent, + * the pending command is cleared and the queue item continues normal processing. + * + * Called from loop() to retry sending commands that were delayed by spacing. + */ + void process_pending_in_queue_(); #endif // USE_NEXTION_COMMAND_SPACING + std::deque nextion_queue_; std::deque waveform_queue_; uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag); @@ -1312,6 +1362,23 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe __attribute__((format(printf, 3, 4))); void add_no_result_to_queue_with_command_(const std::string &variable_name, const std::string &command); +#ifdef USE_NEXTION_COMMAND_SPACING + /** + * @brief Add a command to the Nextion queue with a pending command for retry + * + * This method creates a queue entry for a command that was blocked by command spacing. + * The command string is stored in the queue item's pending_command field so it can + * be retried later when spacing allows. This ensures commands are not lost when + * sent too quickly. + * + * If the max_queue_size limit is configured and reached, the command will be dropped. + * + * @param variable_name Name of the variable or component associated with the command + * @param command The actual command string to be sent when spacing allows + */ + void add_no_result_to_queue_with_pending_command_(const std::string &variable_name, const std::string &command); +#endif // USE_NEXTION_COMMAND_SPACING + bool add_no_result_to_queue_with_printf_(const std::string &variable_name, const char *format, ...) __attribute__((format(printf, 3, 4))); diff --git a/esphome/components/nextion/nextion_commands.cpp b/esphome/components/nextion/nextion_commands.cpp index e3172c8c1b..0226e0a13c 100644 --- a/esphome/components/nextion/nextion_commands.cpp +++ b/esphome/components/nextion/nextion_commands.cpp @@ -17,7 +17,7 @@ void Nextion::set_wake_up_page(uint8_t wake_up_page) { void Nextion::set_touch_sleep_timeout(uint32_t touch_sleep_timeout) { if (touch_sleep_timeout < 3) { - ESP_LOGD(TAG, "Sleep timeout out of bounds, range 3-65535"); + ESP_LOGD(TAG, "Sleep timeout out of bounds (3-65535)"); return; } @@ -37,7 +37,7 @@ void Nextion::sleep(bool sleep) { // Protocol reparse mode bool Nextion::set_protocol_reparse_mode(bool active_mode) { - ESP_LOGV(TAG, "Set Nextion protocol reparse mode: %s", YESNO(active_mode)); + ESP_LOGV(TAG, "Reparse mode: %s", YESNO(active_mode)); this->ignore_is_setup_ = true; // if not in reparse mode setup will fail, so it should be ignored bool all_commands_sent = true; if (active_mode) { // Sets active protocol reparse mode @@ -184,7 +184,7 @@ void Nextion::goto_page(uint8_t page) { this->add_no_result_to_queue_with_printf void Nextion::set_backlight_brightness(float brightness) { if (brightness < 0 || brightness > 1.0) { - ESP_LOGD(TAG, "Brightness out of bounds, percentage range 0-1.0"); + ESP_LOGD(TAG, "Brightness out of bounds (0-1.0)"); return; } this->add_no_result_to_queue_with_printf_("backlight_brightness", "dim=%d", static_cast(brightness * 100)); diff --git a/esphome/components/nextion/nextion_component_base.h b/esphome/components/nextion/nextion_component_base.h index 42e1b00998..fe0692b875 100644 --- a/esphome/components/nextion/nextion_component_base.h +++ b/esphome/components/nextion/nextion_component_base.h @@ -25,6 +25,9 @@ class NextionQueue { virtual ~NextionQueue() = default; NextionComponentBase *component; uint32_t queue_time = 0; + + // Store command for retry if spacing blocked it + std::string pending_command; // Empty if command was sent successfully }; class NextionComponentBase { diff --git a/esphome/components/nextion/nextion_upload_arduino.cpp b/esphome/components/nextion/nextion_upload_arduino.cpp index 1187c77c8e..c2d0f2a22d 100644 --- a/esphome/components/nextion/nextion_upload_arduino.cpp +++ b/esphome/components/nextion/nextion_upload_arduino.cpp @@ -3,12 +3,12 @@ #ifdef USE_NEXTION_TFT_UPLOAD #ifdef USE_ARDUINO +#include +#include "esphome/components/network/util.h" #include "esphome/core/application.h" #include "esphome/core/defines.h" -#include "esphome/core/util.h" #include "esphome/core/log.h" -#include "esphome/components/network/util.h" -#include +#include "esphome/core/util.h" #ifdef USE_ESP32 #include @@ -31,7 +31,7 @@ inline uint32_t Nextion::get_free_heap_() { int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { uint32_t range_size = this->tft_size_ - range_start; - ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_()); uint32_t range_end = ((upload_first_chunk_sent_ or this->tft_size_ < 4096) ? this->tft_size_ : 4096) - 1; ESP_LOGD(TAG, "Range start: %" PRIu32, range_start); if (range_size <= 0 or range_end <= range_start) { @@ -43,19 +43,19 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { char range_header[32]; sprintf(range_header, "bytes=%" PRIu32 "-%" PRIu32, range_start, range_end); - ESP_LOGV(TAG, "Requesting range: %s", range_header); + ESP_LOGV(TAG, "Range: %s", range_header); http_client.addHeader("Range", range_header); int code = http_client.GET(); if (code != HTTP_CODE_OK and code != HTTP_CODE_PARTIAL_CONTENT) { - ESP_LOGW(TAG, "HTTP Request failed; Error: %s", HTTPClient::errorToString(code).c_str()); + ESP_LOGW(TAG, "HTTP failed: %s", HTTPClient::errorToString(code).c_str()); return -1; } // Allocate the buffer dynamically - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; uint8_t *buffer = allocator.allocate(4096); if (!buffer) { - ESP_LOGE(TAG, "Failed to allocate upload buffer"); + ESP_LOGE(TAG, "Buffer alloc failed"); return -1; } @@ -64,11 +64,11 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { App.feed_wdt(); const uint16_t buffer_size = this->content_length_ < 4096 ? this->content_length_ : 4096; // Limits buffer to the remaining data - ESP_LOGV(TAG, "Fetching %" PRIu16 " bytes from HTTP", buffer_size); + ESP_LOGV(TAG, "Fetch %" PRIu16 " bytes", buffer_size); uint16_t read_len = 0; int partial_read_len = 0; - const uint32_t start_time = millis(); - while (read_len < buffer_size && millis() - start_time < 5000) { + const uint32_t start_time = App.get_loop_component_start_time(); + while (read_len < buffer_size && App.get_loop_component_start_time() - start_time < 5000) { if (http_client.getStreamPtr()->available() > 0) { partial_read_len = http_client.getStreamPtr()->readBytes(reinterpret_cast(buffer) + read_len, buffer_size - read_len); @@ -81,14 +81,13 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { } if (read_len != buffer_size) { // Did not receive the full package within the timeout period - ESP_LOGE(TAG, "Failed to read full package, received only %" PRIu16 " of %" PRIu16 " bytes", read_len, - buffer_size); + ESP_LOGE(TAG, "Read failed: %" PRIu16 "/%" PRIu16 " bytes", read_len, buffer_size); // Deallocate buffer allocator.deallocate(buffer, 4096); buffer = nullptr; return -1; } - ESP_LOGV(TAG, "%d bytes fetched, writing it to UART", read_len); + ESP_LOGV(TAG, "Fetched %d bytes", read_len); if (read_len > 0) { recv_string.clear(); this->write_array(buffer, buffer_size); @@ -97,25 +96,23 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { this->content_length_ -= read_len; const float upload_percentage = 100.0f * (this->tft_size_ - this->content_length_) / this->tft_size_; #if defined(USE_ESP32) && defined(USE_PSRAM) - ESP_LOGD( - TAG, - "Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " (DRAM) + %" PRIu32 " (PSRAM) bytes", - upload_percentage, this->content_length_, static_cast(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)), - static_cast(heap_caps_get_free_size(MALLOC_CAP_SPIRAM))); + ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 "+%" PRIu32 ")", upload_percentage, + this->content_length_, static_cast(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)), + static_cast(heap_caps_get_free_size(MALLOC_CAP_SPIRAM))); #else - ESP_LOGD(TAG, "Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " bytes", upload_percentage, - this->content_length_, this->get_free_heap_()); + ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 ")", upload_percentage, this->content_length_, + this->get_free_heap_()); #endif upload_first_chunk_sent_ = true; if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request - ESP_LOGD(TAG, "recv_string [%s]", + ESP_LOGD(TAG, "Recv: [%s]", format_hex_pretty(reinterpret_cast(recv_string.data()), recv_string.size()).c_str()); uint32_t result = 0; for (int j = 0; j < 4; ++j) { result += static_cast(recv_string[j + 1]) << (8 * j); } if (result > 0) { - ESP_LOGI(TAG, "Nextion reported new range %" PRIu32, result); + ESP_LOGI(TAG, "New range: %" PRIu32, result); this->content_length_ = this->tft_size_ - result; range_start = result; } else { @@ -126,7 +123,7 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { buffer = nullptr; return range_end + 1; } else if (recv_string[0] != 0x05 and recv_string[0] != 0x08) { // 0x05 == "ok" - ESP_LOGE(TAG, "Invalid response from Nextion: [%s]", + ESP_LOGE(TAG, "Invalid response: [%s]", format_hex_pretty(reinterpret_cast(recv_string.data()), recv_string.size()).c_str()); // Deallocate buffer allocator.deallocate(buffer, 4096); @@ -136,10 +133,10 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { recv_string.clear(); } else if (read_len == 0) { - ESP_LOGV(TAG, "End of HTTP response reached"); + ESP_LOGV(TAG, "HTTP end"); break; // Exit the loop if there is no more data to read } else { - ESP_LOGE(TAG, "Failed to read from HTTP client, error code: %d", read_len); + ESP_LOGE(TAG, "HTTP read failed: %d", read_len); break; // Exit the loop on error } } @@ -151,26 +148,26 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { } bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { - ESP_LOGD(TAG, "Nextion TFT upload requested"); + ESP_LOGD(TAG, "TFT upload requested"); ESP_LOGD(TAG, "Exit reparse: %s", YESNO(exit_reparse)); ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str()); if (this->is_updating_) { - ESP_LOGW(TAG, "Currently uploading"); + ESP_LOGW(TAG, "Upload in progress"); return false; } if (!network::is_connected()) { - ESP_LOGE(TAG, "Network is not connected"); + ESP_LOGE(TAG, "No network"); return false; } this->is_updating_ = true; if (exit_reparse) { - ESP_LOGD(TAG, "Exiting Nextion reparse mode"); + ESP_LOGD(TAG, "Exit reparse mode"); if (!this->set_protocol_reparse_mode(false)) { - ESP_LOGW(TAG, "Failed to request Nextion to exit reparse mode"); + ESP_LOGW(TAG, "Exit reparse failed"); return false; } } @@ -185,8 +182,8 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate); // Define the configuration for the HTTP client - ESP_LOGV(TAG, "Initializing HTTP client"); - ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGV(TAG, "Init HTTP client"); + ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_()); HTTPClient http_client; http_client.setTimeout(15000); // Yes 15 seconds.... Helps 8266s along @@ -215,7 +212,7 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { http_client.addHeader("Range", "bytes=0-255"); const char *header_names[] = {"Content-Range"}; http_client.collectHeaders(header_names, 1); - ESP_LOGD(TAG, "Requesting URL: %s", this->tft_url_.c_str()); + ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str()); http_client.setReuse(true); // try up to 5 times. DNS sometimes needs a second try or so int tries = 1; @@ -224,7 +221,7 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { App.feed_wdt(); while (code != 200 && code != 206 && tries <= 5) { - ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s, retrying (%d/5)", this->tft_url_.c_str(), + ESP_LOGW(TAG, "HTTP fail: URL: %s; Error: %s, retry %d/5", this->tft_url_.c_str(), HTTPClient::errorToString(code).c_str(), tries); delay(250); // NOLINT @@ -241,27 +238,27 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { content_range_string.remove(0, 12); this->tft_size_ = content_range_string.toInt(); - ESP_LOGD(TAG, "TFT file size: %zu bytes", this->tft_size_); + ESP_LOGD(TAG, "TFT size: %zu bytes", this->tft_size_); if (this->tft_size_ < 4096) { - ESP_LOGE(TAG, "File size check failed."); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "Size check failed"); + ESP_LOGD(TAG, "Close HTTP"); http_client.end(); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } else { - ESP_LOGV(TAG, "File size check passed. Proceeding..."); + ESP_LOGV(TAG, "Size check OK"); } this->content_length_ = this->tft_size_; - ESP_LOGD(TAG, "Uploading Nextion"); + ESP_LOGD(TAG, "Uploading"); // The Nextion will ignore the upload command if it is sleeping - ESP_LOGV(TAG, "Wake-up Nextion"); + ESP_LOGV(TAG, "Wake-up"); this->ignore_is_setup_ = true; this->send_command_("sleep=0"); this->send_command_("dim=100"); delay(250); // NOLINT - ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_()); App.feed_wdt(); char command[128]; @@ -271,16 +268,16 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { sprintf(command, "whmi-wris %d,%d,1", this->content_length_, baud_rate); // Clear serial receive buffer - ESP_LOGV(TAG, "Clear serial receive buffer"); + ESP_LOGV(TAG, "Clear RX buffer"); this->reset_(false); delay(250); // NOLINT - ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_()); - ESP_LOGV(TAG, "Send upload instruction: %s", command); + ESP_LOGV(TAG, "Upload cmd: %s", command); this->send_command_(command); if (baud_rate != this->original_baud_rate_) { - ESP_LOGD(TAG, "Changing baud rate from %" PRIu32 " to %" PRIu32 " bps", this->original_baud_rate_, baud_rate); + ESP_LOGD(TAG, "Baud: %" PRIu32 "->%" PRIu32, this->original_baud_rate_, baud_rate); this->parent_->set_baud_rate(baud_rate); this->parent_->load_settings(); } @@ -288,75 +285,78 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { App.feed_wdt(); std::string response; - ESP_LOGV(TAG, "Waiting for upgrade response"); + ESP_LOGV(TAG, "Wait upload resp"); this->recv_ret_string_(response, 5000, true); // This can take some time to return // The Nextion display will, if it's ready to accept data, send a 0x05 byte. - ESP_LOGD(TAG, "Upgrade response is [%s] - %zu byte(s)", + ESP_LOGD(TAG, "Upload resp: [%s] %zu B", format_hex_pretty(reinterpret_cast(response.data()), response.size()).c_str(), response.length()); - ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_()); if (response.find(0x05) != std::string::npos) { - ESP_LOGV(TAG, "Preparation for TFT upload done"); + ESP_LOGV(TAG, "Upload prep done"); } else { - ESP_LOGE(TAG, "Preparation for TFT upload failed %d \"%s\"", response[0], response.c_str()); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "Prep failed %d '%s'", response[0], response.c_str()); + ESP_LOGD(TAG, "Close HTTP"); http_client.end(); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } - ESP_LOGD(TAG, "Uploading TFT to Nextion:"); - ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str()); - ESP_LOGD(TAG, " File size: %d bytes", this->content_length_); - ESP_LOGD(TAG, " Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGD(TAG, "Upload TFT:"); + ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str()); + ESP_LOGD(TAG, " Size: %d bytes", this->content_length_); + ESP_LOGD(TAG, " Heap: %" PRIu32, this->get_free_heap_()); // Proceed with the content download as before - ESP_LOGV(TAG, "Starting transfer by chunks loop"); + ESP_LOGV(TAG, "Start chunk transfer"); uint32_t position = 0; while (this->content_length_ > 0) { int upload_result = upload_by_chunks_(http_client, position); if (upload_result < 0) { - ESP_LOGE(TAG, "Error uploading TFT to Nextion!"); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "Upload error"); + ESP_LOGD(TAG, "Close HTTP"); http_client.end(); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } App.feed_wdt(); - ESP_LOGV(TAG, "Free heap: %" PRIu32 ", Bytes left: %" PRIu32, this->get_free_heap_(), this->content_length_); + ESP_LOGV(TAG, "Heap: %" PRIu32 " left: %" PRIu32, this->get_free_heap_(), this->content_length_); } - ESP_LOGD(TAG, "Successfully uploaded TFT to Nextion!"); + ESP_LOGD(TAG, "Upload complete"); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGV(TAG, "Close HTTP"); http_client.end(); ESP_LOGV(TAG, "Connection closed"); return upload_end_(true); } bool Nextion::upload_end_(bool successful) { - ESP_LOGD(TAG, "Nextion TFT upload finished: %s", YESNO(successful)); - this->is_updating_ = false; - this->ignore_is_setup_ = false; - - uint32_t baud_rate = this->parent_->get_baud_rate(); - if (baud_rate != this->original_baud_rate_) { - ESP_LOGD(TAG, "Changing baud rate back from %" PRIu32 " to %" PRIu32 " bps", baud_rate, this->original_baud_rate_); - this->parent_->set_baud_rate(this->original_baud_rate_); - this->parent_->load_settings(); - } + ESP_LOGD(TAG, "TFT upload done: %s", YESNO(successful)); if (successful) { - ESP_LOGD(TAG, "Restarting ESPHome"); + ESP_LOGD(TAG, "Restart"); + delay(1500); // NOLINT + App.safe_reboot(); delay(1500); // NOLINT - arch_restart(); } else { - ESP_LOGE(TAG, "Nextion TFT upload failed"); + ESP_LOGE(TAG, "TFT upload failed"); + + this->is_updating_ = false; + this->ignore_is_setup_ = false; + + uint32_t baud_rate = this->parent_->get_baud_rate(); + if (baud_rate != this->original_baud_rate_) { + ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_); + this->parent_->set_baud_rate(this->original_baud_rate_); + this->parent_->load_settings(); + } } + return successful; } diff --git a/esphome/components/nextion/nextion_upload_idf.cpp b/esphome/components/nextion/nextion_upload_idf.cpp index 7541a57d56..43b80f7761 100644 --- a/esphome/components/nextion/nextion_upload_idf.cpp +++ b/esphome/components/nextion/nextion_upload_idf.cpp @@ -3,14 +3,14 @@ #ifdef USE_NEXTION_TFT_UPLOAD #ifdef USE_ESP_IDF -#include "esphome/core/application.h" -#include "esphome/core/defines.h" -#include "esphome/core/util.h" -#include "esphome/core/log.h" -#include "esphome/components/network/util.h" -#include #include #include +#include +#include "esphome/components/network/util.h" +#include "esphome/core/application.h" +#include "esphome/core/defines.h" +#include "esphome/core/log.h" +#include "esphome/core/util.h" namespace esphome { namespace nextion { @@ -21,7 +21,7 @@ static const char *const TAG = "nextion.upload.idf"; int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &range_start) { uint32_t range_size = this->tft_size_ - range_start; - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); uint32_t range_end = ((upload_first_chunk_sent_ or this->tft_size_ < 4096) ? this->tft_size_ : 4096) - 1; ESP_LOGD(TAG, "Range start: %" PRIu32, range_start); if (range_size <= 0 or range_end <= range_start) { @@ -33,28 +33,28 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r char range_header[32]; sprintf(range_header, "bytes=%" PRIu32 "-%" PRIu32, range_start, range_end); - ESP_LOGV(TAG, "Requesting range: %s", range_header); + ESP_LOGV(TAG, "Range: %s", range_header); esp_http_client_set_header(http_client, "Range", range_header); - ESP_LOGV(TAG, "Opening HTTP connetion"); + ESP_LOGV(TAG, "Open HTTP"); esp_err_t err = esp_http_client_open(http_client, 0); if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "HTTP open failed: %s", esp_err_to_name(err)); return -1; } - ESP_LOGV(TAG, "Fetch content length"); + ESP_LOGV(TAG, "Fetch length"); const int chunk_size = esp_http_client_fetch_headers(http_client); - ESP_LOGV(TAG, "content_length = %d", chunk_size); + ESP_LOGV(TAG, "Length: %d", chunk_size); if (chunk_size <= 0) { - ESP_LOGE(TAG, "Failed to get chunk's content length: %d", chunk_size); + ESP_LOGE(TAG, "Get length failed: %d", chunk_size); return -1; } // Allocate the buffer dynamically - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; uint8_t *buffer = allocator.allocate(4096); if (!buffer) { - ESP_LOGE(TAG, "Failed to allocate upload buffer"); + ESP_LOGE(TAG, "Buffer alloc failed"); return -1; } @@ -63,7 +63,7 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r App.feed_wdt(); const uint16_t buffer_size = this->content_length_ < 4096 ? this->content_length_ : 4096; // Limits buffer to the remaining data - ESP_LOGV(TAG, "Fetching %" PRIu16 " bytes from HTTP", buffer_size); + ESP_LOGV(TAG, "Fetch %" PRIu16 " bytes", buffer_size); uint16_t read_len = 0; int partial_read_len = 0; uint8_t retries = 0; @@ -84,14 +84,13 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r } if (read_len != buffer_size) { // Did not receive the full package within the timeout period - ESP_LOGE(TAG, "Failed to read full package, received only %" PRIu16 " of %" PRIu16 " bytes", read_len, - buffer_size); + ESP_LOGE(TAG, "Read failed: %" PRIu16 "/%" PRIu16 " bytes", read_len, buffer_size); // Deallocate buffer allocator.deallocate(buffer, 4096); buffer = nullptr; return -1; } - ESP_LOGV(TAG, "%d bytes fetched, writing it to UART", read_len); + ESP_LOGV(TAG, "Fetched %d bytes", read_len); if (read_len > 0) { recv_string.clear(); this->write_array(buffer, buffer_size); @@ -100,25 +99,23 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r this->content_length_ -= read_len; const float upload_percentage = 100.0f * (this->tft_size_ - this->content_length_) / this->tft_size_; #ifdef USE_PSRAM - ESP_LOGD( - TAG, - "Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " (DRAM) + %" PRIu32 " (PSRAM) bytes", - upload_percentage, this->content_length_, static_cast(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)), - static_cast(heap_caps_get_free_size(MALLOC_CAP_SPIRAM))); + ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 "+%" PRIu32 ")", upload_percentage, + this->content_length_, static_cast(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)), + static_cast(heap_caps_get_free_size(MALLOC_CAP_SPIRAM))); #else - ESP_LOGD(TAG, "Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " bytes", upload_percentage, - this->content_length_, static_cast(esp_get_free_heap_size())); + ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 ")", upload_percentage, this->content_length_, + static_cast(esp_get_free_heap_size())); #endif upload_first_chunk_sent_ = true; if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request - ESP_LOGD(TAG, "recv_string [%s]", + ESP_LOGD(TAG, "Recv: [%s]", format_hex_pretty(reinterpret_cast(recv_string.data()), recv_string.size()).c_str()); uint32_t result = 0; for (int j = 0; j < 4; ++j) { result += static_cast(recv_string[j + 1]) << (8 * j); } if (result > 0) { - ESP_LOGI(TAG, "Nextion reported new range %" PRIu32, result); + ESP_LOGI(TAG, "New range: %" PRIu32, result); this->content_length_ = this->tft_size_ - result; range_start = result; } else { @@ -129,7 +126,7 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r buffer = nullptr; return range_end + 1; } else if (recv_string[0] != 0x05 and recv_string[0] != 0x08) { // 0x05 == "ok" - ESP_LOGE(TAG, "Invalid response from Nextion: [%s]", + ESP_LOGE(TAG, "Invalid response: [%s]", format_hex_pretty(reinterpret_cast(recv_string.data()), recv_string.size()).c_str()); // Deallocate buffer allocator.deallocate(buffer, 4096); @@ -139,10 +136,10 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r recv_string.clear(); } else if (read_len == 0) { - ESP_LOGV(TAG, "End of HTTP response reached"); + ESP_LOGV(TAG, "HTTP end"); break; // Exit the loop if there is no more data to read } else { - ESP_LOGE(TAG, "Failed to read from HTTP client, error code: %" PRIu16, read_len); + ESP_LOGE(TAG, "HTTP read failed: %" PRIu16, read_len); break; // Exit the loop on error } } @@ -154,26 +151,26 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r } bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { - ESP_LOGD(TAG, "Nextion TFT upload requested"); + ESP_LOGD(TAG, "TFT upload requested"); ESP_LOGD(TAG, "Exit reparse: %s", YESNO(exit_reparse)); ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str()); if (this->is_updating_) { - ESP_LOGW(TAG, "Currently uploading"); + ESP_LOGW(TAG, "Upload in progress"); return false; } if (!network::is_connected()) { - ESP_LOGE(TAG, "Network is not connected"); + ESP_LOGE(TAG, "No network"); return false; } this->is_updating_ = true; if (exit_reparse) { - ESP_LOGD(TAG, "Exiting Nextion reparse mode"); + ESP_LOGD(TAG, "Exit reparse mode"); if (!this->set_protocol_reparse_mode(false)) { - ESP_LOGW(TAG, "Failed to request Nextion to exit reparse mode"); + ESP_LOGW(TAG, "Exit reparse failed"); return false; } } @@ -188,8 +185,8 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate); // Define the configuration for the HTTP client - ESP_LOGV(TAG, "Initializing HTTP client"); - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Init HTTP client"); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); esp_http_client_config_t config = { .url = this->tft_url_.c_str(), .cert_pem = nullptr, @@ -201,30 +198,30 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { // Initialize the HTTP client with the configuration esp_http_client_handle_t http_client = esp_http_client_init(&config); if (!http_client) { - ESP_LOGE(TAG, "Failed to initialize HTTP client."); + ESP_LOGE(TAG, "HTTP init failed"); return this->upload_end_(false); } esp_err_t err = esp_http_client_set_header(http_client, "Connection", "keep-alive"); if (err != ESP_OK) { - ESP_LOGE(TAG, "HTTP set header failed: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Set header failed: %s", esp_err_to_name(err)); esp_http_client_cleanup(http_client); return this->upload_end_(false); } // Perform the HTTP request - ESP_LOGV(TAG, "Check if the client could connect"); - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Check connection"); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); err = esp_http_client_perform(http_client); if (err != ESP_OK) { - ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "HTTP failed: %s", esp_err_to_name(err)); esp_http_client_cleanup(http_client); return this->upload_end_(false); } // Check the HTTP Status Code - ESP_LOGV(TAG, "Check the HTTP Status Code"); - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Check status"); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); int status_code = esp_http_client_get_status_code(http_client); if (status_code != 200 && status_code != 206) { return this->upload_end_(false); @@ -232,28 +229,28 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { this->tft_size_ = esp_http_client_get_content_length(http_client); - ESP_LOGD(TAG, "TFT file size: %zu bytes", this->tft_size_); + ESP_LOGD(TAG, "TFT size: %zu bytes", this->tft_size_); if (this->tft_size_ < 4096 || this->tft_size_ > 134217728) { - ESP_LOGE(TAG, "File size check failed."); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "Size check failed"); + ESP_LOGD(TAG, "Close HTTP"); esp_http_client_close(http_client); esp_http_client_cleanup(http_client); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } else { - ESP_LOGV(TAG, "File size check passed. Proceeding..."); + ESP_LOGV(TAG, "Size check OK"); } this->content_length_ = this->tft_size_; - ESP_LOGD(TAG, "Uploading Nextion"); + ESP_LOGD(TAG, "Uploading"); // The Nextion will ignore the upload command if it is sleeping - ESP_LOGV(TAG, "Wake-up Nextion"); + ESP_LOGV(TAG, "Wake-up"); this->ignore_is_setup_ = true; this->send_command_("sleep=0"); this->send_command_("dim=100"); vTaskDelay(pdMS_TO_TICKS(250)); // NOLINT - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); App.feed_wdt(); char command[128]; @@ -263,75 +260,75 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { sprintf(command, "whmi-wris %" PRIu32 ",%" PRIu32 ",1", this->content_length_, baud_rate); // Clear serial receive buffer - ESP_LOGV(TAG, "Clear serial receive buffer"); + ESP_LOGV(TAG, "Clear RX buffer"); this->reset_(false); vTaskDelay(pdMS_TO_TICKS(250)); // NOLINT - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); - ESP_LOGV(TAG, "Send upload instruction: %s", command); + ESP_LOGV(TAG, "Upload cmd: %s", command); this->send_command_(command); if (baud_rate != this->original_baud_rate_) { - ESP_LOGD(TAG, "Changing baud rate from %" PRIu32 " to %" PRIu32 " bps", this->original_baud_rate_, baud_rate); + ESP_LOGD(TAG, "Baud: %" PRIu32 "->%" PRIu32, this->original_baud_rate_, baud_rate); this->parent_->set_baud_rate(baud_rate); this->parent_->load_settings(); } std::string response; - ESP_LOGV(TAG, "Waiting for upgrade response"); + ESP_LOGV(TAG, "Wait upload resp"); this->recv_ret_string_(response, 5000, true); // This can take some time to return // The Nextion display will, if it's ready to accept data, send a 0x05 byte. - ESP_LOGD(TAG, "Upgrade response is [%s] - %zu byte(s)", + ESP_LOGD(TAG, "Upload resp: [%s] %zu B", format_hex_pretty(reinterpret_cast(response.data()), response.size()).c_str(), response.length()); - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); if (response.find(0x05) != std::string::npos) { - ESP_LOGV(TAG, "Preparation for TFT upload done"); + ESP_LOGV(TAG, "Upload prep done"); } else { - ESP_LOGE(TAG, "Preparation for TFT upload failed %d \"%s\"", response[0], response.c_str()); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "Upload prep failed %d '%s'", response[0], response.c_str()); + ESP_LOGD(TAG, "Close HTTP"); esp_http_client_close(http_client); esp_http_client_cleanup(http_client); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } - ESP_LOGV(TAG, "Change the method to GET before starting the download"); + ESP_LOGV(TAG, "Set method to GET"); esp_err_t set_method_result = esp_http_client_set_method(http_client, HTTP_METHOD_GET); if (set_method_result != ESP_OK) { - ESP_LOGE(TAG, "Failed to set HTTP method to GET: %s", esp_err_to_name(set_method_result)); + ESP_LOGE(TAG, "Set GET failed: %s", esp_err_to_name(set_method_result)); return this->upload_end_(false); } - ESP_LOGD(TAG, "Uploading TFT to Nextion:"); - ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str()); - ESP_LOGD(TAG, " File size: %" PRIu32 " bytes", this->content_length_); - ESP_LOGD(TAG, " Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGD(TAG, "Uploading TFT:"); + ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str()); + ESP_LOGD(TAG, " Size: %" PRIu32 " bytes", this->content_length_); + ESP_LOGD(TAG, " Heap: %" PRIu32, esp_get_free_heap_size()); // Proceed with the content download as before - ESP_LOGV(TAG, "Starting transfer by chunks loop"); + ESP_LOGV(TAG, "Start chunk transfer"); uint32_t position = 0; while (this->content_length_ > 0) { int upload_result = upload_by_chunks_(http_client, position); if (upload_result < 0) { - ESP_LOGE(TAG, "Error uploading TFT to Nextion!"); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "TFT upload error"); + ESP_LOGD(TAG, "Close HTTP"); esp_http_client_close(http_client); esp_http_client_cleanup(http_client); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } App.feed_wdt(); - ESP_LOGV(TAG, "Free heap: %" PRIu32 ", Bytes left: %" PRIu32, esp_get_free_heap_size(), this->content_length_); + ESP_LOGV(TAG, "Heap: %" PRIu32 " left: %" PRIu32, esp_get_free_heap_size(), this->content_length_); } - ESP_LOGD(TAG, "Successfully uploaded TFT to Nextion!"); + ESP_LOGD(TAG, "TFT upload complete"); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGD(TAG, "Close HTTP"); esp_http_client_close(http_client); esp_http_client_cleanup(http_client); ESP_LOGV(TAG, "Connection closed"); @@ -339,24 +336,26 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { } bool Nextion::upload_end_(bool successful) { - ESP_LOGD(TAG, "Nextion TFT upload finished: %s", YESNO(successful)); - this->is_updating_ = false; - this->ignore_is_setup_ = false; - - uint32_t baud_rate = this->parent_->get_baud_rate(); - if (baud_rate != this->original_baud_rate_) { - ESP_LOGD(TAG, "Changing baud rate back from %" PRIu32 " to %" PRIu32 " bps", baud_rate, this->original_baud_rate_); - this->parent_->set_baud_rate(this->original_baud_rate_); - this->parent_->load_settings(); - } + ESP_LOGD(TAG, "TFT upload done: %s", YESNO(successful)); if (successful) { - ESP_LOGD(TAG, "Restarting ESPHome"); + ESP_LOGD(TAG, "Restart"); delay(1500); // NOLINT - arch_restart(); + App.safe_reboot(); } else { - ESP_LOGE(TAG, "Nextion TFT upload failed"); + ESP_LOGE(TAG, "TFT upload failed"); + + this->is_updating_ = false; + this->ignore_is_setup_ = false; + + uint32_t baud_rate = this->parent_->get_baud_rate(); + if (baud_rate != this->original_baud_rate_) { + ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_); + this->parent_->set_baud_rate(this->original_baud_rate_); + this->parent_->load_settings(); + } } + return successful; } diff --git a/esphome/components/nextion/sensor/nextion_sensor.cpp b/esphome/components/nextion/sensor/nextion_sensor.cpp index 6cc641fcf3..0ed9da95d4 100644 --- a/esphome/components/nextion/sensor/nextion_sensor.cpp +++ b/esphome/components/nextion/sensor/nextion_sensor.cpp @@ -13,7 +13,7 @@ void NextionSensor::process_sensor(const std::string &variable_name, int state) if (this->wave_chan_id_ == UINT8_MAX && this->variable_name_ == variable_name) { this->publish_state(state); - ESP_LOGD(TAG, "Processed sensor \"%s\" state %d", variable_name.c_str(), state); + ESP_LOGD(TAG, "Sensor: %s=%d", variable_name.c_str(), state); } } @@ -88,12 +88,12 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { } else { this->raw_state = state; this->state = state; - this->has_state_ = true; + this->set_has_state(true); } } this->update_component_settings(); - ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %lf", this->variable_name_.c_str(), published_state); + ESP_LOGN(TAG, "Write: %s=%lf", this->variable_name_.c_str(), published_state); } void NextionSensor::wave_update_() { @@ -105,8 +105,8 @@ void NextionSensor::wave_update_() { size_t buffer_to_send = this->wave_buffer_.size() < 255 ? this->wave_buffer_.size() : 255; // ADDT command can only send 255 - ESP_LOGN(TAG, "wave_update send %zu of %zu value(s) to wave nextion component id %d and wave channel id %d", - buffer_to_send, this->wave_buffer_.size(), this->component_id_, this->wave_chan_id_); + ESP_LOGN(TAG, "Wave update: %zu/%zu vals to comp %d ch %d", buffer_to_send, this->wave_buffer_.size(), + this->component_id_, this->wave_chan_id_); #endif this->nextion_->add_addt_command_to_queue(this); diff --git a/esphome/components/nextion/switch/nextion_switch.cpp b/esphome/components/nextion/switch/nextion_switch.cpp index 63c1882b48..fe71182496 100644 --- a/esphome/components/nextion/switch/nextion_switch.cpp +++ b/esphome/components/nextion/switch/nextion_switch.cpp @@ -13,7 +13,7 @@ void NextionSwitch::process_bool(const std::string &variable_name, bool on) { if (this->variable_name_ == variable_name) { this->publish_state(on); - ESP_LOGD(TAG, "Processed switch \"%s\" state %s", variable_name.c_str(), state ? "ON" : "OFF"); + ESP_LOGD(TAG, "Switch: %s=%s", variable_name.c_str(), ONOFF(on)); } } @@ -43,7 +43,7 @@ void NextionSwitch::set_state(bool state, bool publish, bool send_to_nextion) { this->update_component_settings(); - ESP_LOGN(TAG, "Updated switch \"%s\" state %s", this->variable_name_.c_str(), ONOFF(state)); + ESP_LOGN(TAG, "Write: %s=%s", this->variable_name_.c_str(), ONOFF(state)); } void NextionSwitch::write_state(bool state) { this->set_state(state); } diff --git a/esphome/components/nextion/text_sensor/nextion_textsensor.cpp b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp index a3fc9390f5..e08cbb02ca 100644 --- a/esphome/components/nextion/text_sensor/nextion_textsensor.cpp +++ b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp @@ -11,7 +11,7 @@ void NextionTextSensor::process_text(const std::string &variable_name, const std return; if (this->variable_name_ == variable_name) { this->publish_state(text_value); - ESP_LOGD(TAG, "Processed text_sensor \"%s\" state \"%s\"", variable_name.c_str(), text_value.c_str()); + ESP_LOGD(TAG, "Text sensor: %s='%s'", variable_name.c_str(), text_value.c_str()); } } @@ -37,12 +37,12 @@ void NextionTextSensor::set_state(const std::string &state, bool publish, bool s this->publish_state(state); } else { this->state = state; - this->has_state_ = true; + this->set_has_state(true); } this->update_component_settings(); - ESP_LOGN(TAG, "Wrote state for text_sensor \"%s\" state \"%s\"", this->variable_name_.c_str(), state.c_str()); + ESP_LOGN(TAG, "Write: %s='%s'", this->variable_name_.c_str(), state.c_str()); } } // namespace nextion diff --git a/esphome/components/nfc/nci_message.h b/esphome/components/nfc/nci_message.h index c6b8537402..0c5c871f74 100644 --- a/esphome/components/nfc/nci_message.h +++ b/esphome/components/nfc/nci_message.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include diff --git a/esphome/components/nfc/ndef_record.h b/esphome/components/nfc/ndef_record.h index 20542bf24b..76d8b6a50a 100644 --- a/esphome/components/nfc/ndef_record.h +++ b/esphome/components/nfc/ndef_record.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include diff --git a/esphome/components/nfc/ndef_record_text.h b/esphome/components/nfc/ndef_record_text.h index aa8f13bb4b..e6c15704f0 100644 --- a/esphome/components/nfc/ndef_record_text.h +++ b/esphome/components/nfc/ndef_record_text.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "ndef_record.h" #include diff --git a/esphome/components/nfc/ndef_record_uri.h b/esphome/components/nfc/ndef_record_uri.h index fc8f2d9a73..1eadda1b4f 100644 --- a/esphome/components/nfc/ndef_record_uri.h +++ b/esphome/components/nfc/ndef_record_uri.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "ndef_record.h" #include diff --git a/esphome/components/nfc/nfc.h b/esphome/components/nfc/nfc.h index 23bfdd8ef0..2e5c5cd9c5 100644 --- a/esphome/components/nfc/nfc.h +++ b/esphome/components/nfc/nfc.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "ndef_record.h" #include "ndef_message.h" #include "nfc_tag.h" diff --git a/esphome/components/nfc/nfc_tag.h b/esphome/components/nfc/nfc_tag.h index 58875a744d..55600c3bd9 100644 --- a/esphome/components/nfc/nfc_tag.h +++ b/esphome/components/nfc/nfc_tag.h @@ -3,8 +3,8 @@ #include #include -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "ndef_message.h" namespace esphome { diff --git a/esphome/components/noblex/noblex.cpp b/esphome/components/noblex/noblex.cpp index 3521745bdc..53f807809e 100644 --- a/esphome/components/noblex/noblex.cpp +++ b/esphome/components/noblex/noblex.cpp @@ -1,6 +1,6 @@ #include "noblex.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace noblex { diff --git a/esphome/components/npi19/npi19.cpp b/esphome/components/npi19/npi19.cpp index 3da5a9dbf8..17ca0ef23e 100644 --- a/esphome/components/npi19/npi19.cpp +++ b/esphome/components/npi19/npi19.cpp @@ -1,7 +1,7 @@ #include "npi19.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace npi19 { diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index fd9e948ea3..2567d9ffe1 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -277,6 +277,7 @@ async def register_number( if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_number(var)) + CORE.register_platform_component("number", var) await setup_number_core_( var, config, min_value=min_value, max_value=max_value, step=step ) diff --git a/esphome/components/number/number.cpp b/esphome/components/number/number.cpp index fda4f43e34..b6a845b19b 100644 --- a/esphome/components/number/number.cpp +++ b/esphome/components/number/number.cpp @@ -7,7 +7,7 @@ namespace number { static const char *const TAG = "number"; void Number::publish_state(float state) { - this->has_state_ = true; + this->set_has_state(true); this->state = state; ESP_LOGD(TAG, "'%s': Sending state %f", this->get_name().c_str(), state); this->state_callback_.call(state); diff --git a/esphome/components/number/number.h b/esphome/components/number/number.h index d839d12ad1..49bcbb857c 100644 --- a/esphome/components/number/number.h +++ b/esphome/components/number/number.h @@ -48,9 +48,6 @@ class Number : public EntityBase { NumberTraits traits; - /// Return whether this number has gotten a full state yet. - bool has_state() const { return has_state_; } - protected: friend class NumberCall; @@ -63,7 +60,6 @@ class Number : public EntityBase { virtual void control(float value) = 0; CallbackManager state_callback_; - bool has_state_{false}; }; } // namespace number diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index 7ef80ff92f..9380cf1b1b 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -2,6 +2,7 @@ import logging from esphome import automation import esphome.codegen as cg +from esphome.components.const import CONF_REQUEST_HEADERS from esphome.components.http_request import CONF_HTTP_REQUEST_ID, HttpRequestComponent from esphome.components.image import ( CONF_INVERT_ALPHA, @@ -24,6 +25,7 @@ from esphome.const import ( CONF_TYPE, CONF_URL, ) +from esphome.core import Lambda AUTO_LOAD = ["image"] DEPENDENCIES = ["display", "http_request"] @@ -124,6 +126,9 @@ ONLINE_IMAGE_SCHEMA = ( cv.GenerateID(CONF_HTTP_REQUEST_ID): cv.use_id(HttpRequestComponent), # Online Image specific options cv.Required(CONF_URL): cv.url, + cv.Optional(CONF_REQUEST_HEADERS): cv.All( + cv.Schema({cv.string: cv.templatable(cv.string)}) + ), cv.Required(CONF_FORMAT): cv.one_of(*IMAGE_FORMATS, upper=True), cv.Optional(CONF_PLACEHOLDER): cv.use_id(Image_), cv.Optional(CONF_BUFFER_SIZE, default=65536): cv.int_range(256, 65536), @@ -207,6 +212,13 @@ async def to_code(config): await cg.register_component(var, config) await cg.register_parented(var, config[CONF_HTTP_REQUEST_ID]) + for key, value in config.get(CONF_REQUEST_HEADERS, {}).items(): + if isinstance(value, Lambda): + template_ = await cg.templatable(value, [], cg.std_string) + cg.add(var.add_request_header(key, template_)) + else: + cg.add(var.add_request_header(key, value)) + if placeholder_id := config.get(CONF_PLACEHOLDER): placeholder = await cg.get_variable(placeholder_id) cg.add(var.set_placeholder(placeholder)) diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index 5c0ffc1cb2..e21b2528d5 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -56,7 +56,7 @@ void OnlineImage::draw(int x, int y, display::Display *display, Color color_on, void OnlineImage::release() { if (this->buffer_) { - ESP_LOGV(TAG, "Deallocating old buffer..."); + ESP_LOGV(TAG, "Deallocating old buffer"); this->allocator_.deallocate(this->buffer_, this->get_buffer_size_()); this->data_start_ = nullptr; this->buffer_ = nullptr; @@ -143,6 +143,10 @@ void OnlineImage::update() { headers.push_back(accept_header); + for (auto &header : this->request_headers_) { + headers.push_back(http_request::Header{header.first, header.second.value()}); + } + this->downloader_ = this->parent_->get(this->url_, headers, {ETAG_HEADER_NAME, LAST_MODIFIED_HEADER_NAME}); if (this->downloader_ == nullptr) { @@ -174,18 +178,21 @@ void OnlineImage::update() { if (this->format_ == ImageFormat::BMP) { ESP_LOGD(TAG, "Allocating BMP decoder"); this->decoder_ = make_unique(this); + this->enable_loop(); } #endif // USE_ONLINE_IMAGE_BMP_SUPPORT #ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT if (this->format_ == ImageFormat::JPEG) { ESP_LOGD(TAG, "Allocating JPEG decoder"); this->decoder_ = esphome::make_unique(this); + this->enable_loop(); } #endif // USE_ONLINE_IMAGE_JPEG_SUPPORT #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT if (this->format_ == ImageFormat::PNG) { ESP_LOGD(TAG, "Allocating PNG decoder"); this->decoder_ = make_unique(this); + this->enable_loop(); } #endif // USE_ONLINE_IMAGE_PNG_SUPPORT @@ -208,6 +215,7 @@ void OnlineImage::update() { void OnlineImage::loop() { if (!this->decoder_) { // Not decoding at the moment => nothing to do. + this->disable_loop(); return; } if (!this->downloader_ || this->decoder_->is_finished()) { @@ -216,7 +224,7 @@ void OnlineImage::loop() { this->height_ = buffer_height_; ESP_LOGD(TAG, "Image fully downloaded, read %zu bytes, width/height = %d/%d", this->downloader_->get_bytes_read(), this->width_, this->height_); - ESP_LOGD(TAG, "Total time: %lds", ::time(nullptr) - this->start_time_); + ESP_LOGD(TAG, "Total time: %" PRIu32 "s", (uint32_t) (::time(nullptr) - this->start_time_)); this->etag_ = this->downloader_->get_response_header(ETAG_HEADER_NAME); this->last_modified_ = this->downloader_->get_response_header(LAST_MODIFIED_HEADER_NAME); this->download_finished_callback_.call(false); diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 920aee2796..6ed9c7956f 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -67,6 +67,11 @@ class OnlineImage : public PollingComponent, this->last_modified_ = ""; } + /** Add the request header */ + template void add_request_header(const std::string &header, V value) { + this->request_headers_.push_back(std::pair >(header, value)); + } + /** * @brief Set the image that needs to be shown as long as the downloaded image * is not available. @@ -153,6 +158,8 @@ class OnlineImage : public PollingComponent, std::string url_{""}; + std::vector > > request_headers_; + /** width requested on configuration, or 0 if non specified. */ const int fixed_width_; /** height requested on configuration, or 0 if non specified. */ diff --git a/esphome/components/opentherm/hub.cpp b/esphome/components/opentherm/hub.cpp index 97adf71752..0a4ef98507 100644 --- a/esphome/components/opentherm/hub.cpp +++ b/esphome/components/opentherm/hub.cpp @@ -399,13 +399,17 @@ void OpenthermHub::dump_config() { ESP_LOGCONFIG(TAG, "OpenTherm:"); LOG_PIN(" In: ", this->in_pin_); LOG_PIN(" Out: ", this->out_pin_); - ESP_LOGCONFIG(TAG, " Sync mode: %s", YESNO(this->sync_mode_)); - ESP_LOGCONFIG(TAG, " Sensors: %s", SHOW(OPENTHERM_SENSOR_LIST(ID, ))); - ESP_LOGCONFIG(TAG, " Binary sensors: %s", SHOW(OPENTHERM_BINARY_SENSOR_LIST(ID, ))); - ESP_LOGCONFIG(TAG, " Switches: %s", SHOW(OPENTHERM_SWITCH_LIST(ID, ))); - ESP_LOGCONFIG(TAG, " Input sensors: %s", SHOW(OPENTHERM_INPUT_SENSOR_LIST(ID, ))); - ESP_LOGCONFIG(TAG, " Outputs: %s", SHOW(OPENTHERM_OUTPUT_LIST(ID, ))); - ESP_LOGCONFIG(TAG, " Numbers: %s", SHOW(OPENTHERM_NUMBER_LIST(ID, ))); + ESP_LOGCONFIG(TAG, + " Sync mode: %s\n" + " Sensors: %s\n" + " Binary sensors: %s\n" + " Switches: %s\n" + " Input sensors: %s\n" + " Outputs: %s\n" + " Numbers: %s", + YESNO(this->sync_mode_), SHOW(OPENTHERM_SENSOR_LIST(ID, )), SHOW(OPENTHERM_BINARY_SENSOR_LIST(ID, )), + SHOW(OPENTHERM_SWITCH_LIST(ID, )), SHOW(OPENTHERM_INPUT_SENSOR_LIST(ID, )), + SHOW(OPENTHERM_OUTPUT_LIST(ID, )), SHOW(OPENTHERM_NUMBER_LIST(ID, ))); ESP_LOGCONFIG(TAG, " Initial requests:"); for (auto type : initial_messages) { ESP_LOGCONFIG(TAG, " - %d (%s)", type, this->opentherm_->message_id_to_str(type)); diff --git a/esphome/components/opentherm/number/number.cpp b/esphome/components/opentherm/number/number.cpp index d02b99ee9c..90ab5d6490 100644 --- a/esphome/components/opentherm/number/number.cpp +++ b/esphome/components/opentherm/number/number.cpp @@ -31,9 +31,11 @@ void OpenthermNumber::setup() { void OpenthermNumber::dump_config() { LOG_NUMBER("", "OpenTherm Number", this); - ESP_LOGCONFIG(TAG, " Restore value: %d", this->restore_value_); - ESP_LOGCONFIG(TAG, " Initial value: %.2f", this->initial_value_); - ESP_LOGCONFIG(TAG, " Current value: %.2f", this->state); + ESP_LOGCONFIG(TAG, + " Restore value: %d\n" + " Initial value: %.2f\n" + " Current value: %.2f", + this->restore_value_, this->initial_value_, this->state); } } // namespace opentherm diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h index 4280832d09..a5822cdfe1 100644 --- a/esphome/components/opentherm/opentherm.h +++ b/esphome/components/opentherm/opentherm.h @@ -9,8 +9,8 @@ #include #include "esphome/core/hal.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #if defined(ESP32) || defined(USE_ESP_IDF) #include "driver/timer.h" diff --git a/esphome/components/openthread/__init__.py b/esphome/components/openthread/__init__.py new file mode 100644 index 0000000000..5b1ea491e3 --- /dev/null +++ b/esphome/components/openthread/__init__.py @@ -0,0 +1,146 @@ +import esphome.codegen as cg +from esphome.components.esp32 import ( + VARIANT_ESP32C6, + VARIANT_ESP32H2, + add_idf_sdkconfig_option, + only_on_variant, +) +from esphome.components.mdns import MDNSComponent +import esphome.config_validation as cv +from esphome.const import CONF_CHANNEL, CONF_ENABLE_IPV6, CONF_ID +import esphome.final_validate as fv + +from .const import ( + CONF_EXT_PAN_ID, + CONF_FORCE_DATASET, + CONF_MDNS_ID, + CONF_MESH_LOCAL_PREFIX, + CONF_NETWORK_KEY, + CONF_NETWORK_NAME, + CONF_PAN_ID, + CONF_PSKC, + CONF_SRP_ID, + CONF_TLV, +) +from .tlv import parse_tlv + +CODEOWNERS = ["@mrene"] + +AUTO_LOAD = ["network"] + +# Wi-fi / Bluetooth / Thread coexistence isn't implemented at this time +# TODO: Doesn't conflict with wifi if you're using another ESP as an RCP (radio coprocessor), but this isn't implemented yet +CONFLICTS_WITH = ["wifi"] +DEPENDENCIES = ["esp32"] + + +def set_sdkconfig_options(config): + # and expose options for using SPI/UART RCPs + add_idf_sdkconfig_option("CONFIG_IEEE802154_ENABLED", True) + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_RADIO_NATIVE", True) + + # There is a conflict if the logger's uart also uses the default UART, which is seen as a watchdog failure on "ot_cli" + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_CLI", False) + + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_ENABLED", True) + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PANID", config[CONF_PAN_ID]) + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_CHANNEL", config[CONF_CHANNEL]) + add_idf_sdkconfig_option( + "CONFIG_OPENTHREAD_NETWORK_MASTERKEY", f"{config[CONF_NETWORK_KEY]:X}" + ) + + if network_name := config.get(CONF_NETWORK_NAME): + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_NAME", network_name) + + if (ext_pan_id := config.get(CONF_EXT_PAN_ID)) is not None: + add_idf_sdkconfig_option( + "CONFIG_OPENTHREAD_NETWORK_EXTPANID", f"{ext_pan_id:X}" + ) + if (mesh_local_prefix := config.get(CONF_MESH_LOCAL_PREFIX)) is not None: + add_idf_sdkconfig_option( + "CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX", f"{mesh_local_prefix:X}" + ) + if (pskc := config.get(CONF_PSKC)) is not None: + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PSKC", f"{pskc:X}") + + if CONF_FORCE_DATASET in config: + if config[CONF_FORCE_DATASET]: + cg.add_define("CONFIG_OPENTHREAD_FORCE_DATASET") + + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_DNS64_CLIENT", True) + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT", True) + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT_MAX_SERVICES", 5) + + # TODO: Add suport for sleepy end devices + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_FTD", True) # Full Thread Device + + +openthread_ns = cg.esphome_ns.namespace("openthread") +OpenThreadComponent = openthread_ns.class_("OpenThreadComponent", cg.Component) +OpenThreadSrpComponent = openthread_ns.class_("OpenThreadSrpComponent", cg.Component) + + +def _convert_tlv(config): + if tlv := config.get(CONF_TLV): + config = config.copy() + parsed_tlv = parse_tlv(tlv) + validated = _CONNECTION_SCHEMA(parsed_tlv) + config.update(validated) + del config[CONF_TLV] + return config + + +_CONNECTION_SCHEMA = cv.Schema( + { + cv.Inclusive(CONF_PAN_ID, "manual"): cv.hex_int, + cv.Inclusive(CONF_CHANNEL, "manual"): cv.int_, + cv.Inclusive(CONF_NETWORK_KEY, "manual"): cv.hex_int, + cv.Optional(CONF_EXT_PAN_ID): cv.hex_int, + cv.Optional(CONF_NETWORK_NAME): cv.string_strict, + cv.Optional(CONF_PSKC): cv.hex_int, + cv.Optional(CONF_MESH_LOCAL_PREFIX): cv.hex_int, + } +) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(OpenThreadComponent), + cv.GenerateID(CONF_SRP_ID): cv.declare_id(OpenThreadSrpComponent), + cv.GenerateID(CONF_MDNS_ID): cv.use_id(MDNSComponent), + cv.Optional(CONF_FORCE_DATASET): cv.boolean, + cv.Optional(CONF_TLV): cv.string_strict, + } + ).extend(_CONNECTION_SCHEMA), + cv.has_exactly_one_key(CONF_PAN_ID, CONF_TLV), + _convert_tlv, + cv.only_with_esp_idf, + only_on_variant(supported=[VARIANT_ESP32C6, VARIANT_ESP32H2]), +) + + +def _final_validate(_): + full_config = fv.full_config.get() + network_config = full_config.get("network", {}) + if not network_config.get(CONF_ENABLE_IPV6, False): + raise cv.Invalid( + "OpenThread requires IPv6 to be enabled in the network component. " + "Please set `enable_ipv6: true` in the `network` configuration." + ) + + +FINAL_VALIDATE_SCHEMA = _final_validate + + +async def to_code(config): + cg.add_define("USE_OPENTHREAD") + + ot = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(ot, config) + + srp = cg.new_Pvariable(config[CONF_SRP_ID]) + mdns_component = await cg.get_variable(config[CONF_MDNS_ID]) + cg.add(srp.set_mdns(mdns_component)) + await cg.register_component(srp, config) + + set_sdkconfig_options(config) diff --git a/esphome/components/openthread/const.py b/esphome/components/openthread/const.py new file mode 100644 index 0000000000..7837e69eea --- /dev/null +++ b/esphome/components/openthread/const.py @@ -0,0 +1,10 @@ +CONF_EXT_PAN_ID = "ext_pan_id" +CONF_FORCE_DATASET = "force_dataset" +CONF_MDNS_ID = "mdns_id" +CONF_MESH_LOCAL_PREFIX = "mesh_local_prefix" +CONF_NETWORK_NAME = "network_name" +CONF_NETWORK_KEY = "network_key" +CONF_PAN_ID = "pan_id" +CONF_PSKC = "pskc" +CONF_SRP_ID = "srp_id" +CONF_TLV = "tlv" diff --git a/esphome/components/openthread/openthread.cpp b/esphome/components/openthread/openthread.cpp new file mode 100644 index 0000000000..f40a56952a --- /dev/null +++ b/esphome/components/openthread/openthread.cpp @@ -0,0 +1,206 @@ +#include "esphome/core/defines.h" +#ifdef USE_OPENTHREAD +#include "openthread.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +static const char *const TAG = "openthread"; + +namespace esphome { +namespace openthread { + +OpenThreadComponent *global_openthread_component = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +OpenThreadComponent::OpenThreadComponent() { global_openthread_component = this; } + +OpenThreadComponent::~OpenThreadComponent() { + auto lock = InstanceLock::try_acquire(100); + if (!lock) { + ESP_LOGW(TAG, "Failed to acquire OpenThread lock in destructor, leaking memory"); + return; + } + otInstance *instance = lock->get_instance(); + otSrpClientClearHostAndServices(instance); + otSrpClientBuffersFreeAllServices(instance); + global_openthread_component = nullptr; +} + +bool OpenThreadComponent::is_connected() { + auto lock = InstanceLock::try_acquire(100); + if (!lock) { + ESP_LOGW(TAG, "Failed to acquire OpenThread lock in is_connected"); + return false; + } + + otInstance *instance = lock->get_instance(); + if (instance == nullptr) { + return false; + } + + otDeviceRole role = otThreadGetDeviceRole(instance); + + // TODO: If we're a leader, check that there is at least 1 known peer + return role >= OT_DEVICE_ROLE_CHILD; +} + +// Gets the off-mesh routable address +std::optional OpenThreadComponent::get_omr_address() { + InstanceLock lock = InstanceLock::acquire(); + return this->get_omr_address_(lock); +} + +std::optional OpenThreadComponent::get_omr_address_(InstanceLock &lock) { + otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; + otInstance *instance = nullptr; + + instance = lock.get_instance(); + + otBorderRouterConfig config; + if (otNetDataGetNextOnMeshPrefix(instance, &iterator, &config) != OT_ERROR_NONE) { + return std::nullopt; + } + + const otIp6Prefix *omr_prefix = &config.mPrefix; + const otNetifAddress *unicast_addresses = otIp6GetUnicastAddresses(instance); + for (const otNetifAddress *addr = unicast_addresses; addr; addr = addr->mNext) { + const otIp6Address *local_ip = &addr->mAddress; + if (otIp6PrefixMatch(&omr_prefix->mPrefix, local_ip)) { + return *local_ip; + } + } + return {}; +} + +void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrpClientService *services, + const otSrpClientService *removed_services, void *context) { + if (err != 0) { + ESP_LOGW(TAG, "SRP client reported an error: %s", otThreadErrorToString(err)); + for (const otSrpClientHostInfo *host = host_info; host; host = nullptr) { + ESP_LOGW(TAG, " Host: %s", host->mName); + } + for (const otSrpClientService *service = services; service; service = service->mNext) { + ESP_LOGW(TAG, " Service: %s", service->mName); + } + } +} + +void srp_start_callback(const otSockAddr *server_socket_address, void *context) { + ESP_LOGI(TAG, "SRP client has started"); +} + +void OpenThreadSrpComponent::setup() { + otError error; + InstanceLock lock = InstanceLock::acquire(); + otInstance *instance = lock.get_instance(); + + otSrpClientSetCallback(instance, srp_callback, nullptr); + + // set the host name + uint16_t size; + char *existing_host_name = otSrpClientBuffersGetHostNameString(instance, &size); + const std::string &host_name = App.get_name(); + uint16_t host_name_len = host_name.size(); + if (host_name_len > size) { + ESP_LOGW(TAG, "Hostname is too long, choose a shorter project name"); + return; + } + memset(existing_host_name, 0, size); + memcpy(existing_host_name, host_name.c_str(), host_name_len); + + error = otSrpClientSetHostName(instance, existing_host_name); + if (error != 0) { + ESP_LOGW(TAG, "Could not set host name"); + return; + } + + error = otSrpClientEnableAutoHostAddress(instance); + if (error != 0) { + ESP_LOGW(TAG, "Could not enable auto host address"); + return; + } + + // Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this + // component + this->mdns_services_ = this->mdns_->get_services(); + ESP_LOGW(TAG, "Setting up SRP services. count = %d\n", this->mdns_services_.size()); + for (const auto &service : this->mdns_services_) { + otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance); + if (!entry) { + ESP_LOGW(TAG, "Failed to allocate service entry"); + continue; + } + + // Set service name + char *string = otSrpClientBuffersGetServiceEntryServiceNameString(entry, &size); + std::string full_service = service.service_type + "." + service.proto; + if (full_service.size() > size) { + ESP_LOGW(TAG, "Service name too long: %s", full_service.c_str()); + continue; + } + memcpy(string, full_service.c_str(), full_service.size() + 1); + + // Set instance name (using host_name) + string = otSrpClientBuffersGetServiceEntryInstanceNameString(entry, &size); + if (host_name_len > size) { + ESP_LOGW(TAG, "Instance name too long: %s", host_name.c_str()); + continue; + } + memset(string, 0, size); + memcpy(string, host_name.c_str(), host_name_len); + + // Set port + entry->mService.mPort = const_cast &>(service.port).value(); + + otDnsTxtEntry *txt_entries = + reinterpret_cast(this->pool_alloc_(sizeof(otDnsTxtEntry) * service.txt_records.size())); + // Set TXT records + entry->mService.mNumTxtEntries = service.txt_records.size(); + for (size_t i = 0; i < service.txt_records.size(); i++) { + const auto &txt = service.txt_records[i]; + auto value = const_cast &>(txt.value).value(); + txt_entries[i].mKey = strdup(txt.key.c_str()); + txt_entries[i].mValue = reinterpret_cast(strdup(value.c_str())); + txt_entries[i].mValueLength = value.size(); + } + entry->mService.mTxtEntries = txt_entries; + entry->mService.mNumTxtEntries = service.txt_records.size(); + + // Add service + error = otSrpClientAddService(instance, &entry->mService); + if (error != OT_ERROR_NONE) { + ESP_LOGW(TAG, "Failed to add service: %s", otThreadErrorToString(error)); + } + ESP_LOGW(TAG, "Added service: %s", full_service.c_str()); + } + + otSrpClientEnableAutoStartMode(instance, srp_start_callback, nullptr); + ESP_LOGW(TAG, "Finished SRP setup"); +} + +void *OpenThreadSrpComponent::pool_alloc_(size_t size) { + uint8_t *ptr = new uint8_t[size]; + this->memory_pool_.emplace_back(std::unique_ptr(ptr)); + return ptr; +} + +void OpenThreadSrpComponent::set_mdns(esphome::mdns::MDNSComponent *mdns) { this->mdns_ = mdns; } + +} // namespace openthread +} // namespace esphome + +#endif diff --git a/esphome/components/openthread/openthread.h b/esphome/components/openthread/openthread.h new file mode 100644 index 0000000000..77fd58851a --- /dev/null +++ b/esphome/components/openthread/openthread.h @@ -0,0 +1,68 @@ +#pragma once +#include "esphome/core/defines.h" +#ifdef USE_OPENTHREAD + +#include "esphome/components/mdns/mdns_component.h" +#include "esphome/components/network/ip_address.h" +#include "esphome/core/component.h" + +#include + +#include +#include + +namespace esphome { +namespace openthread { + +class InstanceLock; + +class OpenThreadComponent : public Component { + public: + OpenThreadComponent(); + ~OpenThreadComponent(); + void setup() override; + float get_setup_priority() const override { return setup_priority::WIFI; } + + bool is_connected(); + network::IPAddresses get_ip_addresses(); + std::optional get_omr_address(); + void ot_main(); + + protected: + std::optional get_omr_address_(InstanceLock &lock); +}; + +extern OpenThreadComponent *global_openthread_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +class OpenThreadSrpComponent : public Component { + public: + void set_mdns(esphome::mdns::MDNSComponent *mdns); + // This has to run after the mdns component or else no services are available to advertise + float get_setup_priority() const override { return this->mdns_->get_setup_priority() - 1.0; } + void setup() override; + + protected: + esphome::mdns::MDNSComponent *mdns_{nullptr}; + std::vector mdns_services_; + std::vector> memory_pool_; + void *pool_alloc_(size_t size); +}; + +class InstanceLock { + public: + static std::optional try_acquire(int delay); + static InstanceLock acquire(); + ~InstanceLock(); + + // Returns the global openthread instance guarded by this lock + otInstance *get_instance(); + + private: + // Use a private constructor in order to force thehandling + // of acquisition failure + InstanceLock() {} +}; + +} // namespace openthread +} // namespace esphome +#endif diff --git a/esphome/components/openthread/openthread_esp.cpp b/esphome/components/openthread/openthread_esp.cpp new file mode 100644 index 0000000000..c5c817382f --- /dev/null +++ b/esphome/components/openthread/openthread_esp.cpp @@ -0,0 +1,164 @@ +#include "esphome/core/defines.h" +#if defined(USE_OPENTHREAD) && defined(USE_ESP_IDF) +#include +#include "openthread.h" + +#include "esp_log.h" +#include "esp_openthread.h" +#include "esp_openthread_lock.h" + +#include "esp_task_wdt.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#include "esp_err.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "esp_netif_types.h" +#include "esp_openthread_cli.h" +#include "esp_openthread_netif_glue.h" +#include "esp_vfs_eventfd.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "nvs_flash.h" + +static const char *const TAG = "openthread"; + +namespace esphome { +namespace openthread { + +void OpenThreadComponent::setup() { + ESP_LOGCONFIG(TAG, "Running setup"); + // Used eventfds: + // * netif + // * ot task queue + // * radio driver + esp_vfs_eventfd_config_t eventfd_config = { + .max_fds = 3, + }; + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); + + xTaskCreate( + [](void *arg) { + static_cast(arg)->ot_main(); + vTaskDelete(nullptr); + }, + "ot_main", 10240, this, 5, nullptr); +} + +static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config) { + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); + esp_netif_t *netif = esp_netif_new(&cfg); + assert(netif != nullptr); + ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config))); + + return netif; +} + +void OpenThreadComponent::ot_main() { + esp_openthread_platform_config_t config = { + .radio_config = + { + .radio_mode = RADIO_MODE_NATIVE, + .radio_uart_config = {}, + }, + .host_config = + { + // There is a conflict between esphome's logger which also + // claims the usb serial jtag device. + // .host_connection_mode = HOST_CONNECTION_MODE_CLI_USB, + // .host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(), + }, + .port_config = + { + .storage_partition_name = "nvs", + .netif_queue_size = 10, + .task_queue_size = 10, + }, + }; + + // Initialize the OpenThread stack + // otLoggingSetLevel(OT_LOG_LEVEL_DEBG); + ESP_ERROR_CHECK(esp_openthread_init(&config)); + +#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE + ESP_ERROR_CHECK(esp_openthread_state_indicator_init(esp_openthread_get_instance())); +#endif + +#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC + // The OpenThread log level directly matches ESP log level + (void) otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); +#endif + // Initialize the OpenThread cli +#if CONFIG_OPENTHREAD_CLI + esp_openthread_cli_init(); +#endif + + esp_netif_t *openthread_netif; + // Initialize the esp_netif bindings + openthread_netif = init_openthread_netif(&config); + esp_netif_set_default_netif(openthread_netif); + +#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION + esp_cli_custom_command_init(); +#endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION + + // Run the main loop +#if CONFIG_OPENTHREAD_CLI + esp_openthread_cli_create_task(); +#endif + ESP_LOGI(TAG, "Activating dataset..."); + otOperationalDatasetTlvs dataset; + +#ifdef CONFIG_OPENTHREAD_FORCE_DATASET + ESP_ERROR_CHECK(esp_openthread_auto_start(NULL)); +#else + otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); + ESP_ERROR_CHECK(esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL)); +#endif + esp_openthread_launch_mainloop(); + + // Clean up + esp_openthread_netif_glue_deinit(); + esp_netif_destroy(openthread_netif); + + esp_vfs_eventfd_unregister(); +} + +network::IPAddresses OpenThreadComponent::get_ip_addresses() { + network::IPAddresses addresses; + struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES]; + uint8_t count = 0; + esp_netif_t *netif = esp_netif_get_default_netif(); + count = esp_netif_get_all_ip6(netif, if_ip6s); + assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES); + for (int i = 0; i < count; i++) { + addresses[i + 1] = network::IPAddress(&if_ip6s[i]); + } + return addresses; +} + +std::optional InstanceLock::try_acquire(int delay) { + if (esp_openthread_lock_acquire(delay)) { + return InstanceLock(); + } + return {}; +} + +InstanceLock InstanceLock::acquire() { + while (!esp_openthread_lock_acquire(100)) { + esp_task_wdt_reset(); + } + return InstanceLock(); +} + +otInstance *InstanceLock::get_instance() { return esp_openthread_get_instance(); } + +InstanceLock::~InstanceLock() { esp_openthread_lock_release(); } + +} // namespace openthread +} // namespace esphome +#endif diff --git a/esphome/components/openthread/tlv.py b/esphome/components/openthread/tlv.py new file mode 100644 index 0000000000..45c8c47227 --- /dev/null +++ b/esphome/components/openthread/tlv.py @@ -0,0 +1,58 @@ +# Sourced from https://gist.github.com/agners/0338576e0003318b63ec1ea75adc90f9 +import binascii + +from esphome.const import CONF_CHANNEL + +from . import ( + CONF_EXT_PAN_ID, + CONF_MESH_LOCAL_PREFIX, + CONF_NETWORK_KEY, + CONF_NETWORK_NAME, + CONF_PAN_ID, + CONF_PSKC, +) + +TLV_TYPES = { + 0: CONF_CHANNEL, + 1: CONF_PAN_ID, + 2: CONF_EXT_PAN_ID, + 3: CONF_NETWORK_NAME, + 4: CONF_PSKC, + 5: CONF_NETWORK_KEY, + 7: CONF_MESH_LOCAL_PREFIX, +} + + +def parse_tlv(tlv) -> dict: + data = binascii.a2b_hex(tlv) + output = {} + pos = 0 + while pos < len(data): + tag = data[pos] + pos += 1 + _len = data[pos] + pos += 1 + val = data[pos : pos + _len] + pos += _len + if tag in TLV_TYPES: + if tag == 3: + output[TLV_TYPES[tag]] = val.decode("utf-8") + else: + output[TLV_TYPES[tag]] = int.from_bytes(val) + return output + + +def main(): + import sys + + args = sys.argv[1:] + parsed = parse_tlv(args[0]) + # print the parsed TLV data + for key, value in parsed.items(): + if isinstance(value, bytes): + value = value.hex() + print(f"{key}: {value}") + + +if __name__ == "__main__": + main() diff --git a/esphome/components/esp32_hall/__init__.py b/esphome/components/openthread_info/__init__.py similarity index 100% rename from esphome/components/esp32_hall/__init__.py rename to esphome/components/openthread_info/__init__.py diff --git a/esphome/components/openthread_info/openthread_info_text_sensor.cpp b/esphome/components/openthread_info/openthread_info_text_sensor.cpp new file mode 100644 index 0000000000..10724f3e2f --- /dev/null +++ b/esphome/components/openthread_info/openthread_info_text_sensor.cpp @@ -0,0 +1,24 @@ + +#include "openthread_info_text_sensor.h" +#ifdef USE_OPENTHREAD +#include "esphome/core/log.h" + +namespace esphome { +namespace openthread_info { + +static const char *const TAG = "openthread_info"; + +void IPAddressOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "IPAddress", this); } +void RoleOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Role", this); } +void ChannelOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Channel", this); } +void Rloc16OpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Rloc16", this); } +void ExtAddrOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "ExtAddr", this); } +void Eui64OpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Eui64", this); } +void NetworkNameOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Network Name", this); } +void NetworkKeyOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Network Key", this); } +void PanIdOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "PAN ID", this); } +void ExtPanIdOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Extended PAN ID", this); } + +} // namespace openthread_info +} // namespace esphome +#endif diff --git a/esphome/components/openthread_info/openthread_info_text_sensor.h b/esphome/components/openthread_info/openthread_info_text_sensor.h new file mode 100644 index 0000000000..bbcd2d4655 --- /dev/null +++ b/esphome/components/openthread_info/openthread_info_text_sensor.h @@ -0,0 +1,218 @@ +#pragma once + +#include "esphome/components/openthread/openthread.h" +#include "esphome/components/text_sensor/text_sensor.h" +#include "esphome/core/component.h" +#ifdef USE_OPENTHREAD + +namespace esphome { +namespace openthread_info { + +using esphome::openthread::InstanceLock; + +class OpenThreadInstancePollingComponent : public PollingComponent { + public: + void update() override { + auto lock = InstanceLock::try_acquire(10); + if (!lock) { + return; + } + + this->update_instance(lock->get_instance()); + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + + protected: + virtual void update_instance(otInstance *instance) = 0; +}; + +class IPAddressOpenThreadInfo : public PollingComponent, public text_sensor::TextSensor { + public: + void update() override { + std::optional address = openthread::global_openthread_component->get_omr_address(); + if (!address) { + return; + } + + char address_as_string[40]; + otIp6AddressToString(&*address, address_as_string, 40); + std::string ip = address_as_string; + + if (this->last_ip_ != ip) { + this->last_ip_ = ip; + this->publish_state(this->last_ip_); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::string last_ip_; +}; + +class RoleOpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor { + public: + void update_instance(otInstance *instance) override { + otDeviceRole role = otThreadGetDeviceRole(instance); + + if (this->last_role_ != role) { + this->last_role_ = role; + this->publish_state(otThreadDeviceRoleToString(this->last_role_)); + } + } + void dump_config() override; + + protected: + otDeviceRole last_role_; +}; + +class Rloc16OpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor { + public: + void update_instance(otInstance *instance) override { + uint16_t rloc16 = otThreadGetRloc16(instance); + if (this->last_rloc16_ != rloc16) { + this->last_rloc16_ = rloc16; + char buf[5]; + snprintf(buf, sizeof(buf), "%04x", rloc16); + this->publish_state(buf); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + uint16_t last_rloc16_; +}; + +class ExtAddrOpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor { + public: + void update_instance(otInstance *instance) override { + const auto *extaddr = otLinkGetExtendedAddress(instance); + if (!std::equal(this->last_extaddr_.begin(), this->last_extaddr_.end(), extaddr->m8)) { + std::copy(extaddr->m8, extaddr->m8 + 8, this->last_extaddr_.begin()); + this->publish_state(format_hex(extaddr->m8, 8)); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::array last_extaddr_{}; +}; + +class Eui64OpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor { + public: + void update_instance(otInstance *instance) override { + otExtAddress addr; + otLinkGetFactoryAssignedIeeeEui64(instance, &addr); + + if (!std::equal(this->last_eui64_.begin(), this->last_eui64_.end(), addr.m8)) { + std::copy(addr.m8, addr.m8 + 8, this->last_eui64_.begin()); + this->publish_state(format_hex(this->last_eui64_.begin(), 8)); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::array last_eui64_{}; +}; + +class ChannelOpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor { + public: + void update_instance(otInstance *instance) override { + uint8_t channel = otLinkGetChannel(instance); + if (this->last_channel_ != channel) { + this->last_channel_ = channel; + this->publish_state(std::to_string(this->last_channel_)); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + uint8_t last_channel_; +}; + +class DatasetOpenThreadInfo : public OpenThreadInstancePollingComponent { + public: + void update_instance(otInstance *instance) override { + otOperationalDataset dataset; + if (otDatasetGetActive(instance, &dataset) != OT_ERROR_NONE) { + return; + } + + this->update_dataset(&dataset); + } + + protected: + virtual void update_dataset(otOperationalDataset *dataset) = 0; +}; + +class NetworkNameOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor { + public: + void update_dataset(otOperationalDataset *dataset) override { + if (this->last_network_name_ != dataset->mNetworkName.m8) { + this->last_network_name_ = dataset->mNetworkName.m8; + this->publish_state(this->last_network_name_); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::string last_network_name_; +}; + +class NetworkKeyOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor { + public: + void update_dataset(otOperationalDataset *dataset) override { + if (!std::equal(this->last_key_.begin(), this->last_key_.end(), dataset->mNetworkKey.m8)) { + std::copy(dataset->mNetworkKey.m8, dataset->mNetworkKey.m8 + 16, this->last_key_.begin()); + this->publish_state(format_hex(dataset->mNetworkKey.m8, 16)); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::array last_key_{}; +}; + +class PanIdOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor { + public: + void update_dataset(otOperationalDataset *dataset) override { + uint16_t panid = dataset->mPanId; + if (this->last_panid_ != panid) { + this->last_panid_ = panid; + char buf[5]; + snprintf(buf, sizeof(buf), "%04x", panid); + this->publish_state(buf); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + uint16_t last_panid_; +}; + +class ExtPanIdOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor { + public: + void update_dataset(otOperationalDataset *dataset) override { + if (!std::equal(this->last_extpanid_.begin(), this->last_extpanid_.end(), dataset->mExtendedPanId.m8)) { + std::copy(dataset->mExtendedPanId.m8, dataset->mExtendedPanId.m8 + 8, this->last_extpanid_.begin()); + this->publish_state(format_hex(this->last_extpanid_.begin(), 8)); + } + } + + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::array last_extpanid_{}; +}; + +} // namespace openthread_info +} // namespace esphome +#endif diff --git a/esphome/components/openthread_info/text_sensor.py b/esphome/components/openthread_info/text_sensor.py new file mode 100644 index 0000000000..ddec8f264c --- /dev/null +++ b/esphome/components/openthread_info/text_sensor.py @@ -0,0 +1,105 @@ +import esphome.codegen as cg +from esphome.components import text_sensor +from esphome.components.openthread.const import ( + CONF_EXT_PAN_ID, + CONF_NETWORK_KEY, + CONF_NETWORK_NAME, + CONF_PAN_ID, +) +import esphome.config_validation as cv +from esphome.const import CONF_CHANNEL, CONF_IP_ADDRESS, ENTITY_CATEGORY_DIAGNOSTIC + +CONF_ROLE = "role" +CONF_RLOC16 = "rloc16" +CONF_EUI64 = "eui64" +CONF_EXT_ADDR = "ext_addr" + + +DEPENDENCIES = ["openthread"] + +openthread_info_ns = cg.esphome_ns.namespace("openthread_info") +IPAddressOpenThreadInfo = openthread_info_ns.class_( + "IPAddressOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +RoleOpenThreadInfo = openthread_info_ns.class_( + "RoleOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +Rloc16OpenThreadInfo = openthread_info_ns.class_( + "Rloc16OpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +ExtAddrOpenThreadInfo = openthread_info_ns.class_( + "ExtAddrOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +Eui64OpenThreadInfo = openthread_info_ns.class_( + "Eui64OpenThreadInfo", text_sensor.TextSensor, cg.Component +) +ChannelOpenThreadInfo = openthread_info_ns.class_( + "ChannelOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +NetworkNameOpenThreadInfo = openthread_info_ns.class_( + "NetworkNameOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +NetworkKeyOpenThreadInfo = openthread_info_ns.class_( + "NetworkKeyOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +PanIdOpenThreadInfo = openthread_info_ns.class_( + "PanIdOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +ExtPanIdOpenThreadInfo = openthread_info_ns.class_( + "ExtPanIdOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) + + +CONFIG_SCHEMA = cv.Schema( + { + cv.Optional(CONF_IP_ADDRESS): text_sensor.text_sensor_schema( + IPAddressOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ), + cv.Optional(CONF_ROLE): text_sensor.text_sensor_schema( + RoleOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_RLOC16): text_sensor.text_sensor_schema( + Rloc16OpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_EXT_ADDR): text_sensor.text_sensor_schema( + ExtAddrOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_EUI64): text_sensor.text_sensor_schema( + Eui64OpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1h")), + cv.Optional(CONF_CHANNEL): text_sensor.text_sensor_schema( + ChannelOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_NETWORK_NAME): text_sensor.text_sensor_schema( + NetworkNameOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_NETWORK_KEY): text_sensor.text_sensor_schema( + NetworkKeyOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_PAN_ID): text_sensor.text_sensor_schema( # noqa: F821 + PanIdOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_EXT_PAN_ID): text_sensor.text_sensor_schema( + ExtPanIdOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + } +) + + +async def setup_conf(config: dict, key: str): + if conf := config.get(key): + var = await text_sensor.new_text_sensor(conf) + await cg.register_component(var, conf) + + +async def to_code(config): + await setup_conf(config, CONF_IP_ADDRESS) + await setup_conf(config, CONF_ROLE) + await setup_conf(config, CONF_RLOC16) + await setup_conf(config, CONF_EXT_ADDR) + await setup_conf(config, CONF_EUI64) + await setup_conf(config, CONF_CHANNEL) + await setup_conf(config, CONF_NETWORK_NAME) + await setup_conf(config, CONF_NETWORK_KEY) + await setup_conf(config, CONF_PAN_ID) + await setup_conf(config, CONF_EXT_PAN_ID) diff --git a/esphome/components/output/float_output.cpp b/esphome/components/output/float_output.cpp index e7dba1d81d..3b83c85716 100644 --- a/esphome/components/output/float_output.cpp +++ b/esphome/components/output/float_output.cpp @@ -1,6 +1,6 @@ #include "float_output.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace output { diff --git a/esphome/components/packet_transport/packet_transport.cpp b/esphome/components/packet_transport/packet_transport.cpp index be2f77e379..5c721002b0 100644 --- a/esphome/components/packet_transport/packet_transport.cpp +++ b/esphome/components/packet_transport/packet_transport.cpp @@ -474,10 +474,12 @@ void PacketTransport::process_(const std::vector &data) { } void PacketTransport::dump_config() { - ESP_LOGCONFIG(TAG, "Packet Transport:"); - ESP_LOGCONFIG(TAG, " Platform: %s", this->platform_name_); - ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(this->is_encrypted_())); - ESP_LOGCONFIG(TAG, " Ping-pong: %s", YESNO(this->ping_pong_enable_)); + ESP_LOGCONFIG(TAG, + "Packet Transport:\n" + " Platform: %s\n" + " Encrypted: %s\n" + " Ping-pong: %s", + this->platform_name_, YESNO(this->is_encrypted_()), YESNO(this->ping_pong_enable_)); #ifdef USE_SENSOR for (auto sensor : this->sensors_) ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.id); diff --git a/esphome/components/pca9554/pca9554.cpp b/esphome/components/pca9554/pca9554.cpp index fe4375c171..6b3f2d20af 100644 --- a/esphome/components/pca9554/pca9554.cpp +++ b/esphome/components/pca9554/pca9554.cpp @@ -45,8 +45,10 @@ void PCA9554Component::loop() { } void PCA9554Component::dump_config() { - ESP_LOGCONFIG(TAG, "PCA9554:"); - ESP_LOGCONFIG(TAG, " I/O Pins: %d", this->pin_count_); + ESP_LOGCONFIG(TAG, + "PCA9554:\n" + " I/O Pins: %d", + this->pin_count_); LOG_I2C_DEVICE(this) if (this->is_failed()) { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); diff --git a/esphome/components/pca9685/pca9685_output.cpp b/esphome/components/pca9685/pca9685_output.cpp index 504adc43ff..2fe22fd1cc 100644 --- a/esphome/components/pca9685/pca9685_output.cpp +++ b/esphome/components/pca9685/pca9685_output.cpp @@ -1,7 +1,7 @@ #include "pca9685_output.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pca9685 { @@ -28,7 +28,7 @@ static const uint8_t PCA9685_MODE1_SLEEP = 0b00010000; void PCA9685Output::setup() { ESP_LOGCONFIG(TAG, "Running setup"); - ESP_LOGV(TAG, " Resetting devices..."); + ESP_LOGV(TAG, " Resetting devices"); if (!this->write_bytes(PCA9685_REGISTER_SOFTWARE_RESET, nullptr, 0)) { this->mark_failed(); return; @@ -83,13 +83,17 @@ void PCA9685Output::setup() { } void PCA9685Output::dump_config() { - ESP_LOGCONFIG(TAG, "PCA9685:"); - ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_); + ESP_LOGCONFIG(TAG, + "PCA9685:\n" + " Mode: 0x%02X", + this->mode_); if (this->extclk_) { ESP_LOGCONFIG(TAG, " EXTCLK: enabled"); } else { - ESP_LOGCONFIG(TAG, " EXTCLK: disabled"); - ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_); + ESP_LOGCONFIG(TAG, + " EXTCLK: disabled\n" + " Frequency: %.0f Hz", + this->frequency_); } if (this->is_failed()) { ESP_LOGE(TAG, "Setting up PCA9685 failed!"); diff --git a/esphome/components/pcd8544/pcd_8544.cpp b/esphome/components/pcd8544/pcd_8544.cpp index 5651d60b15..f5b018b127 100644 --- a/esphome/components/pcd8544/pcd_8544.cpp +++ b/esphome/components/pcd8544/pcd_8544.cpp @@ -1,7 +1,7 @@ #include "pcd_8544.h" -#include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pcd8544 { diff --git a/esphome/components/pcf8574/__init__.py b/esphome/components/pcf8574/__init__.py index 64bef86443..ff7c314bcd 100644 --- a/esphome/components/pcf8574/__init__.py +++ b/esphome/components/pcf8574/__init__.py @@ -53,7 +53,7 @@ PCF8574_PIN_SCHEMA = pins.gpio_base_schema( cv.int_range(min=0, max=17), modes=[CONF_INPUT, CONF_OUTPUT], mode_validator=validate_mode, - invertable=True, + invertible=True, ).extend( { cv.Required(CONF_PCF8574): cv.use_id(PCF8574Component), diff --git a/esphome/components/pid/pid_climate.cpp b/esphome/components/pid/pid_climate.cpp index 93b6999a00..8b3be36dcc 100644 --- a/esphome/components/pid/pid_climate.cpp +++ b/esphome/components/pid/pid_climate.cpp @@ -73,15 +73,18 @@ climate::ClimateTraits PIDClimate::traits() { } void PIDClimate::dump_config() { LOG_CLIMATE("", "PID Climate", this); - ESP_LOGCONFIG(TAG, " Control Parameters:"); - ESP_LOGCONFIG(TAG, " kp: %.5f, ki: %.5f, kd: %.5f, output samples: %d", controller_.kp_, controller_.ki_, - controller_.kd_, controller_.output_samples_); + ESP_LOGCONFIG(TAG, + " Control Parameters:\n" + " kp: %.5f, ki: %.5f, kd: %.5f, output samples: %d", + controller_.kp_, controller_.ki_, controller_.kd_, controller_.output_samples_); if (controller_.threshold_low_ == 0 && controller_.threshold_high_ == 0) { ESP_LOGCONFIG(TAG, " Deadband disabled."); } else { - ESP_LOGCONFIG(TAG, " Deadband Parameters:"); - ESP_LOGCONFIG(TAG, " threshold: %0.5f to %0.5f, multipliers(kp: %.5f, ki: %.5f, kd: %.5f), output samples: %d", + ESP_LOGCONFIG(TAG, + " Deadband Parameters:\n" + " threshold: %0.5f to %0.5f, multipliers(kp: %.5f, ki: %.5f, kd: %.5f), " + "output samples: %d", controller_.threshold_low_, controller_.threshold_high_, controller_.kp_multiplier_, controller_.ki_multiplier_, controller_.kd_multiplier_, controller_.deadband_output_samples_); } diff --git a/esphome/components/pid/pid_climate.h b/esphome/components/pid/pid_climate.h index b5275e9775..1a09ffdd20 100644 --- a/esphome/components/pid/pid_climate.h +++ b/esphome/components/pid/pid_climate.h @@ -1,8 +1,8 @@ #pragma once +#include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include "esphome/core/automation.h" #include "esphome/components/climate/climate.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/output/float_output.h" diff --git a/esphome/components/pid/sensor/pid_climate_sensor.cpp b/esphome/components/pid/sensor/pid_climate_sensor.cpp index 2a76c775d3..41ca027d8d 100644 --- a/esphome/components/pid/sensor/pid_climate_sensor.cpp +++ b/esphome/components/pid/sensor/pid_climate_sensor.cpp @@ -1,6 +1,6 @@ #include "pid_climate_sensor.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pid { diff --git a/esphome/components/pipsolar/output/pipsolar_output.cpp b/esphome/components/pipsolar/output/pipsolar_output.cpp index b843f1f3e6..00ec73b56a 100644 --- a/esphome/components/pipsolar/output/pipsolar_output.cpp +++ b/esphome/components/pipsolar/output/pipsolar_output.cpp @@ -1,6 +1,6 @@ #include "pipsolar_output.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pipsolar { diff --git a/esphome/components/pipsolar/pipsolar.cpp b/esphome/components/pipsolar/pipsolar.cpp index 03c699e7d4..40405114a4 100644 --- a/esphome/components/pipsolar/pipsolar.cpp +++ b/esphome/components/pipsolar/pipsolar.cpp @@ -1,6 +1,6 @@ #include "pipsolar.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pipsolar { @@ -861,8 +861,8 @@ void Pipsolar::switch_command(const std::string &command) { queue_command_(command.c_str(), command.length()); } void Pipsolar::dump_config() { - ESP_LOGCONFIG(TAG, "Pipsolar:"); - ESP_LOGCONFIG(TAG, "used commands:"); + ESP_LOGCONFIG(TAG, "Pipsolar:\n" + "used commands:"); for (auto &used_polling_command : this->used_polling_commands_) { if (used_polling_command.length != 0) { ESP_LOGCONFIG(TAG, "%s", used_polling_command.command); diff --git a/esphome/components/pm1006/pm1006.cpp b/esphome/components/pm1006/pm1006.cpp index f28c647721..c466c4bb25 100644 --- a/esphome/components/pm1006/pm1006.cpp +++ b/esphome/components/pm1006/pm1006.cpp @@ -95,9 +95,5 @@ void PM1006Component::parse_data_() { } } -uint16_t PM1006Component::get_16_bit_uint_(uint8_t start_index) const { - return encode_uint16(this->data_[start_index], this->data_[start_index + 1]); -} - } // namespace pm1006 } // namespace esphome diff --git a/esphome/components/pm1006/pm1006.h b/esphome/components/pm1006/pm1006.h index 238ac67006..98637dad71 100644 --- a/esphome/components/pm1006/pm1006.h +++ b/esphome/components/pm1006/pm1006.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/uart/uart.h" @@ -22,8 +23,10 @@ class PM1006Component : public PollingComponent, public uart::UARTDevice { protected: optional check_byte_() const; void parse_data_(); - uint16_t get_16_bit_uint_(uint8_t start_index) const; uint8_t pm1006_checksum_(const uint8_t *command_data, uint8_t length) const; + uint16_t get_16_bit_uint_(uint8_t start_index) const { + return encode_uint16(this->data_[start_index], this->data_[start_index + 1]); + } sensor::Sensor *pm_2_5_sensor_{nullptr}; diff --git a/esphome/components/pm2005/pm2005.cpp b/esphome/components/pm2005/pm2005.cpp index 4b00cd6f2e..57c616c4c6 100644 --- a/esphome/components/pm2005/pm2005.cpp +++ b/esphome/components/pm2005/pm2005.cpp @@ -103,8 +103,10 @@ void PM2005Component::update() { } void PM2005Component::dump_config() { - ESP_LOGCONFIG(TAG, "PM2005:"); - ESP_LOGCONFIG(TAG, " Type: PM2%u05", this->sensor_type_ == PM2105); + ESP_LOGCONFIG(TAG, + "PM2005:\n" + " Type: PM2%u05", + this->sensor_type_ == PM2105); LOG_I2C_DEVICE(this); if (this->is_failed()) { diff --git a/esphome/components/pmsa003i/pmsa003i.cpp b/esphome/components/pmsa003i/pmsa003i.cpp index 3a5473d759..4702c0cf5f 100644 --- a/esphome/components/pmsa003i/pmsa003i.cpp +++ b/esphome/components/pmsa003i/pmsa003i.cpp @@ -1,6 +1,6 @@ #include "pmsa003i.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include namespace esphome { diff --git a/esphome/components/pmsx003/pmsx003.cpp b/esphome/components/pmsx003/pmsx003.cpp index 0abed8a5a4..eb10d19c91 100644 --- a/esphome/components/pmsx003/pmsx003.cpp +++ b/esphome/components/pmsx003/pmsx003.cpp @@ -314,9 +314,5 @@ void PMSX003Component::parse_data_() { this->status_clear_warning(); } -uint16_t PMSX003Component::get_16_bit_uint_(uint8_t start_index) { - return (uint16_t(this->data_[start_index]) << 8) | uint16_t(this->data_[start_index + 1]); -} - } // namespace pmsx003 } // namespace esphome diff --git a/esphome/components/pmsx003/pmsx003.h b/esphome/components/pmsx003/pmsx003.h index 85bb1ff9f3..e422d4165b 100644 --- a/esphome/components/pmsx003/pmsx003.h +++ b/esphome/components/pmsx003/pmsx003.h @@ -1,8 +1,9 @@ #pragma once +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/uart/uart.h" -#include "esphome/core/component.h" namespace esphome { namespace pmsx003 { @@ -77,7 +78,9 @@ class PMSX003Component : public uart::UARTDevice, public Component { void parse_data_(); bool check_payload_length_(uint16_t payload_length); void send_command_(PMSX0003Command cmd, uint16_t data); - uint16_t get_16_bit_uint_(uint8_t start_index); + uint16_t get_16_bit_uint_(uint8_t start_index) const { + return encode_uint16(this->data_[start_index], this->data_[start_index + 1]); + } uint8_t data_[64]; uint8_t data_index_{0}; diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index facbd0a857..da5598bf10 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -19,7 +19,7 @@ void PN532::setup() { // Get version data if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) { - ESP_LOGW(TAG, "Error sending version command, trying again..."); + ESP_LOGW(TAG, "Error sending version command, trying again"); if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) { ESP_LOGE(TAG, "Error sending version command"); this->mark_failed(); @@ -208,21 +208,21 @@ void PN532::loop() { } } } else if (next_task_ == CLEAN) { - ESP_LOGD(TAG, " Tag cleaning..."); + ESP_LOGD(TAG, " Tag cleaning"); if (!this->clean_tag_(nfcid)) { ESP_LOGE(TAG, " Tag was not fully cleaned successfully"); } ESP_LOGD(TAG, " Tag cleaned!"); } else if (next_task_ == FORMAT) { - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag formatting"); if (!this->format_tag_(nfcid)) { ESP_LOGE(TAG, "Error formatting tag as NDEF"); } ESP_LOGD(TAG, " Tag formatted!"); } else if (next_task_ == WRITE) { if (this->next_task_message_to_write_ != nullptr) { - ESP_LOGD(TAG, " Tag writing..."); - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag writing"); + ESP_LOGD(TAG, " Tag formatting"); if (!this->format_tag_(nfcid)) { ESP_LOGE(TAG, " Tag could not be formatted for writing"); } else { @@ -281,7 +281,7 @@ bool PN532::write_command_(const std::vector &data) { } bool PN532::read_ack_() { - ESP_LOGV(TAG, "Reading ACK..."); + ESP_LOGV(TAG, "Reading ACK"); std::vector data; if (!this->read_data(data, 6)) { diff --git a/esphome/components/pn532/pn532.h b/esphome/components/pn532/pn532.h index 8194d86477..c8e9a40008 100644 --- a/esphome/components/pn532/pn532.h +++ b/esphome/components/pn532/pn532.h @@ -38,7 +38,7 @@ class PN532 : public PollingComponent { float get_setup_priority() const override; void loop() override; - void on_shutdown() override { powerdown(); } + void on_powerdown() override { powerdown(); } void register_tag(PN532BinarySensor *tag) { this->binary_sensors_.push_back(tag); } void register_ontag_trigger(nfc::NfcOnTagTrigger *trig) { this->triggers_ontag_.push_back(trig); } diff --git a/esphome/components/pn532_spi/pn532_spi.cpp b/esphome/components/pn532_spi/pn532_spi.cpp index d55d8161d8..2e66d4ed83 100644 --- a/esphome/components/pn532_spi/pn532_spi.cpp +++ b/esphome/components/pn532_spi/pn532_spi.cpp @@ -51,7 +51,7 @@ bool PN532Spi::read_data(std::vector &data, uint8_t len) { delay(2); this->write_byte(0x03); - ESP_LOGV(TAG, "Reading data..."); + ESP_LOGV(TAG, "Reading data"); data.resize(len); this->read_array(data.data(), len); diff --git a/esphome/components/pn7150/pn7150.cpp b/esphome/components/pn7150/pn7150.cpp index be4d6c1bb7..971ddd23cb 100644 --- a/esphome/components/pn7150/pn7150.cpp +++ b/esphome/components/pn7150/pn7150.cpp @@ -830,7 +830,7 @@ void PN7150::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi switch (this->next_task_) { case EP_CLEAN: - ESP_LOGD(TAG, " Tag cleaning..."); + ESP_LOGD(TAG, " Tag cleaning"); if (this->clean_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, " Tag cleaning incomplete"); } @@ -838,7 +838,7 @@ void PN7150::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi break; case EP_FORMAT: - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag formatting"); if (this->format_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, "Error formatting tag as NDEF"); } @@ -847,8 +847,8 @@ void PN7150::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi case EP_WRITE: if (this->next_task_message_to_write_ != nullptr) { - ESP_LOGD(TAG, " Tag writing..."); - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag writing"); + ESP_LOGD(TAG, " Tag formatting"); if (this->format_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, " Tag could not be formatted for writing"); } else { diff --git a/esphome/components/pn7160/pn7160.cpp b/esphome/components/pn7160/pn7160.cpp index a7d3b38fb7..2a1de20657 100644 --- a/esphome/components/pn7160/pn7160.cpp +++ b/esphome/components/pn7160/pn7160.cpp @@ -854,7 +854,7 @@ void PN7160::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi switch (this->next_task_) { case EP_CLEAN: - ESP_LOGD(TAG, " Tag cleaning..."); + ESP_LOGD(TAG, " Tag cleaning"); if (this->clean_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, " Tag cleaning incomplete"); } @@ -862,7 +862,7 @@ void PN7160::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi break; case EP_FORMAT: - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag formatting"); if (this->format_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, "Error formatting tag as NDEF"); } @@ -871,8 +871,8 @@ void PN7160::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi case EP_WRITE: if (this->next_task_message_to_write_ != nullptr) { - ESP_LOGD(TAG, " Tag writing..."); - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag writing"); + ESP_LOGD(TAG, " Tag formatting"); if (this->format_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, " Tag could not be formatted for writing"); } else { diff --git a/esphome/components/power_supply/power_supply.cpp b/esphome/components/power_supply/power_supply.cpp index 3c449aadd5..6fbadc73ae 100644 --- a/esphome/components/power_supply/power_supply.cpp +++ b/esphome/components/power_supply/power_supply.cpp @@ -17,8 +17,10 @@ void PowerSupply::setup() { void PowerSupply::dump_config() { ESP_LOGCONFIG(TAG, "Power Supply:"); LOG_PIN(" Pin: ", this->pin_); - ESP_LOGCONFIG(TAG, " Time to enable: %" PRIu32 " ms", this->enable_time_); - ESP_LOGCONFIG(TAG, " Keep on time: %.1f s", this->keep_on_time_ / 1000.0f); + ESP_LOGCONFIG(TAG, + " Time to enable: %" PRIu32 " ms\n" + " Keep on time: %.1f s", + this->enable_time_, this->keep_on_time_ / 1000.0f); if (this->enable_on_boot_) ESP_LOGCONFIG(TAG, " Enabled at startup: True"); } @@ -50,7 +52,7 @@ void PowerSupply::unrequest_high_power() { }); } } -void PowerSupply::on_shutdown() { +void PowerSupply::on_powerdown() { this->active_requests_ = 0; this->pin_->digital_write(false); } diff --git a/esphome/components/power_supply/power_supply.h b/esphome/components/power_supply/power_supply.h index 0b06105ae9..3959f6f299 100644 --- a/esphome/components/power_supply/power_supply.h +++ b/esphome/components/power_supply/power_supply.h @@ -32,7 +32,7 @@ class PowerSupply : public Component { /// Hardware setup priority (+1). float get_setup_priority() const override; - void on_shutdown() override; + void on_powerdown() override; protected: GPIOPin *pin_; diff --git a/esphome/components/preferences/syncer.h b/esphome/components/preferences/syncer.h index 8976a1fe15..b6b422d4ba 100644 --- a/esphome/components/preferences/syncer.h +++ b/esphome/components/preferences/syncer.h @@ -12,6 +12,8 @@ class IntervalSyncer : public Component { void setup() override { if (this->write_interval_ != 0) { set_interval(this->write_interval_, []() { global_preferences->sync(); }); + // When using interval-based syncing, we don't need the loop + this->disable_loop(); } } void loop() override { diff --git a/esphome/components/prometheus/__init__.py b/esphome/components/prometheus/__init__.py index b899fe7642..26a9e70f7c 100644 --- a/esphome/components/prometheus/__init__.py +++ b/esphome/components/prometheus/__init__.py @@ -31,7 +31,6 @@ CONFIG_SCHEMA = cv.Schema( } ), }, - cv.only_with_arduino, ).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index bdc3d971ce..c4598f44b0 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -40,7 +40,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component { */ void add_label_name(EntityBase *obj, const std::string &value) { relabel_map_name_.insert({obj, value}); } - bool canHandle(AsyncWebServerRequest *request) override { + bool canHandle(AsyncWebServerRequest *request) const override { if (request->method() == HTTP_GET) { if (request->url() == "/metrics") return true; diff --git a/esphome/components/psram/__init__.py b/esphome/components/psram/__init__.py index c00cbb2ba2..9299cdcd0e 100644 --- a/esphome/components/psram/__init__.py +++ b/esphome/components/psram/__init__.py @@ -7,9 +7,12 @@ from esphome.components.esp32 import ( VARIANT_ESP32, add_idf_sdkconfig_option, get_esp32_variant, - only_on_variant, ) -from esphome.components.esp32.const import VARIANT_ESP32S2, VARIANT_ESP32S3 +from esphome.components.esp32.const import ( + VARIANT_ESP32P4, + VARIANT_ESP32S2, + VARIANT_ESP32S3, +) import esphome.config_validation as cv from esphome.const import ( CONF_ADVANCED, @@ -35,24 +38,31 @@ PsramComponent = psram_ns.class_("PsramComponent", cg.Component) TYPE_QUAD = "quad" TYPE_OCTAL = "octal" +TYPE_HEX = "hex" + +SDK_MODES = {TYPE_QUAD: "QUAD", TYPE_OCTAL: "OCT", TYPE_HEX: "HEX"} CONF_ENABLE_ECC = "enable_ecc" SPIRAM_MODES = { - TYPE_QUAD: "CONFIG_SPIRAM_MODE_QUAD", - TYPE_OCTAL: "CONFIG_SPIRAM_MODE_OCT", + VARIANT_ESP32: (TYPE_QUAD,), + VARIANT_ESP32S2: (TYPE_QUAD,), + VARIANT_ESP32S3: (TYPE_QUAD, TYPE_OCTAL), + VARIANT_ESP32P4: (TYPE_HEX,), } + SPIRAM_SPEEDS = { - 40e6: "CONFIG_SPIRAM_SPEED_40M", - 80e6: "CONFIG_SPIRAM_SPEED_80M", - 120e6: "CONFIG_SPIRAM_SPEED_120M", + VARIANT_ESP32: (40, 80, 120), + VARIANT_ESP32S2: (40, 80, 120), + VARIANT_ESP32S3: (40, 80, 120), + VARIANT_ESP32P4: (20, 100, 200), } def validate_psram_mode(config): esp32_config = fv.full_config.get()[PLATFORM_ESP32] - if config[CONF_SPEED] == 120e6: + if config[CONF_SPEED] == "120MHZ": if esp32_config[CONF_CPU_FREQUENCY] != "240MHZ": raise cv.Invalid( "PSRAM 120MHz requires 240MHz CPU frequency (set in esp32 component)" @@ -79,23 +89,23 @@ def validate_psram_mode(config): return config -CONFIG_SCHEMA = cv.All( - cv.Schema( +def get_config_schema(config): + variant = get_esp32_variant() + speeds = [f"{s}MHZ" for s in SPIRAM_SPEEDS.get(variant, [])] + if not speeds: + return cv.Invalid("PSRAM is not supported on this chip") + modes = SPIRAM_MODES[variant] + return cv.Schema( { cv.GenerateID(): cv.declare_id(PsramComponent), - cv.Optional(CONF_MODE, default=TYPE_QUAD): cv.enum( - SPIRAM_MODES, lower=True - ), + cv.Optional(CONF_MODE, default=modes[0]): cv.one_of(*modes, lower=True), cv.Optional(CONF_ENABLE_ECC, default=False): cv.boolean, - cv.Optional(CONF_SPEED, default=40e6): cv.All( - cv.frequency, cv.one_of(*SPIRAM_SPEEDS) - ), + cv.Optional(CONF_SPEED, default=speeds[0]): cv.one_of(*speeds, upper=True), } - ), - only_on_variant( - supported=[VARIANT_ESP32, VARIANT_ESP32S3, VARIANT_ESP32S2], - ), -) + )(config) + + +CONFIG_SCHEMA = get_config_schema FINAL_VALIDATE_SCHEMA = validate_psram_mode @@ -110,15 +120,23 @@ async def to_code(config): add_idf_sdkconfig_option( f"CONFIG_{get_esp32_variant().upper()}_SPIRAM_SUPPORT", True ) + add_idf_sdkconfig_option("CONFIG_SOC_SPIRAM_SUPPORTED", True) add_idf_sdkconfig_option("CONFIG_SPIRAM", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_USE", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_USE_CAPS_ALLOC", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_IGNORE_NOTFOUND", True) - add_idf_sdkconfig_option(f"{SPIRAM_MODES[config[CONF_MODE]]}", True) - add_idf_sdkconfig_option(f"{SPIRAM_SPEEDS[config[CONF_SPEED]]}", True) - if config[CONF_MODE] == TYPE_OCTAL and config[CONF_SPEED] == 120e6: + add_idf_sdkconfig_option( + f"CONFIG_SPIRAM_MODE_{SDK_MODES[config[CONF_MODE]]}", True + ) + + # Remove MHz suffix, convert to int + speed = int(config[CONF_SPEED][:-3]) + add_idf_sdkconfig_option(f"CONFIG_SPIRAM_SPEED_{speed}M", True) + add_idf_sdkconfig_option("CONFIG_SPIRAM_SPEED", speed) + if config[CONF_MODE] == TYPE_OCTAL and speed == 120: add_idf_sdkconfig_option("CONFIG_ESPTOOLPY_FLASHFREQ_120M", True) + add_idf_sdkconfig_option("CONFIG_BOOTLOADER_FLASH_DC_AWARE", True) if CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(5, 4, 0): add_idf_sdkconfig_option( "CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR", True diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.cpp b/esphome/components/pulse_counter/pulse_counter_sensor.cpp index 5ad3f1fc33..bfca0c6a4e 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.cpp +++ b/esphome/components/pulse_counter/pulse_counter_sensor.cpp @@ -171,9 +171,12 @@ void PulseCounterSensor::set_total_pulses(uint32_t pulses) { void PulseCounterSensor::dump_config() { LOG_SENSOR("", "Pulse Counter", this); LOG_PIN(" Pin: ", this->pin_); - ESP_LOGCONFIG(TAG, " Rising Edge: %s", EDGE_MODE_TO_STRING[this->storage_.rising_edge_mode]); - ESP_LOGCONFIG(TAG, " Falling Edge: %s", EDGE_MODE_TO_STRING[this->storage_.falling_edge_mode]); - ESP_LOGCONFIG(TAG, " Filtering pulses shorter than %" PRIu32 " µs", this->storage_.filter_us); + ESP_LOGCONFIG(TAG, + " Rising Edge: %s\n" + " Falling Edge: %s\n" + " Filtering pulses shorter than %" PRIu32 " µs", + EDGE_MODE_TO_STRING[this->storage_.rising_edge_mode], + EDGE_MODE_TO_STRING[this->storage_.falling_edge_mode], this->storage_.filter_us); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp index 1856a023cc..74f63a9640 100644 --- a/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +++ b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp @@ -8,11 +8,14 @@ namespace pvvx_mithermometer { static const char *const TAG = "display.pvvx_mithermometer"; void PVVXDisplay::dump_config() { - ESP_LOGCONFIG(TAG, "PVVX MiThermometer display:"); - ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent_->address_str().c_str()); - ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Characteristic UUID : %s", this->char_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Auto clear : %s", YESNO(this->auto_clear_enabled_)); + ESP_LOGCONFIG(TAG, + "PVVX MiThermometer display:\n" + " MAC address : %s\n" + " Service UUID : %s\n" + " Characteristic UUID : %s\n" + " Auto clear : %s", + this->parent_->address_str().c_str(), this->service_uuid_.to_string().c_str(), + this->char_uuid_.to_string().c_str(), YESNO(this->auto_clear_enabled_)); #ifdef USE_TIME ESP_LOGCONFIG(TAG, " Set time on connection: %s", YESNO(this->time_ != nullptr)); #endif diff --git a/esphome/components/pylontech/pylontech.cpp b/esphome/components/pylontech/pylontech.cpp index cf133742dc..ef3de069ca 100644 --- a/esphome/components/pylontech/pylontech.cpp +++ b/esphome/components/pylontech/pylontech.cpp @@ -1,6 +1,6 @@ #include "pylontech.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pylontech { diff --git a/esphome/components/pylontech/sensor/pylontech_sensor.cpp b/esphome/components/pylontech/sensor/pylontech_sensor.cpp index 5b5db0731e..11437369ed 100644 --- a/esphome/components/pylontech/sensor/pylontech_sensor.cpp +++ b/esphome/components/pylontech/sensor/pylontech_sensor.cpp @@ -10,8 +10,10 @@ static const char *const TAG = "pylontech.sensor"; PylontechSensor::PylontechSensor(int8_t bat_num) { this->bat_num_ = bat_num; } void PylontechSensor::dump_config() { - ESP_LOGCONFIG(TAG, "Pylontech Sensor:"); - ESP_LOGCONFIG(TAG, " Battery %d", this->bat_num_); + ESP_LOGCONFIG(TAG, + "Pylontech Sensor:\n" + " Battery %d", + this->bat_num_); LOG_SENSOR(" ", "Voltage", this->voltage_sensor_); LOG_SENSOR(" ", "Current", this->current_sensor_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); diff --git a/esphome/components/pylontech/text_sensor/pylontech_text_sensor.cpp b/esphome/components/pylontech/text_sensor/pylontech_text_sensor.cpp index 9e894bc570..55e02f3e33 100644 --- a/esphome/components/pylontech/text_sensor/pylontech_text_sensor.cpp +++ b/esphome/components/pylontech/text_sensor/pylontech_text_sensor.cpp @@ -10,8 +10,10 @@ static const char *const TAG = "pylontech.textsensor"; PylontechTextSensor::PylontechTextSensor(int8_t bat_num) { this->bat_num_ = bat_num; } void PylontechTextSensor::dump_config() { - ESP_LOGCONFIG(TAG, "Pylontech Text Sensor:"); - ESP_LOGCONFIG(TAG, " Battery %d", this->bat_num_); + ESP_LOGCONFIG(TAG, + "Pylontech Text Sensor:\n" + " Battery %d", + this->bat_num_); LOG_TEXT_SENSOR(" ", "Base state", this->base_state_text_sensor_); LOG_TEXT_SENSOR(" ", "Voltage state", this->voltage_state_text_sensor_); LOG_TEXT_SENSOR(" ", "Current state", this->current_state_text_sensor_); diff --git a/esphome/components/pzemac/pzemac.cpp b/esphome/components/pzemac/pzemac.cpp index c3738d1852..0dbe0e761d 100644 --- a/esphome/components/pzemac/pzemac.cpp +++ b/esphome/components/pzemac/pzemac.cpp @@ -64,8 +64,10 @@ void PZEMAC::on_modbus_data(const std::vector &data) { void PZEMAC::update() { this->send(PZEM_CMD_READ_IN_REGISTERS, 0, PZEM_REGISTER_COUNT); } void PZEMAC::dump_config() { - ESP_LOGCONFIG(TAG, "PZEMAC:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "PZEMAC:\n" + " Address: 0x%02X", + this->address_); LOG_SENSOR("", "Voltage", this->voltage_sensor_); LOG_SENSOR("", "Current", this->current_sensor_); LOG_SENSOR("", "Power", this->power_sensor_); diff --git a/esphome/components/pzemdc/pzemdc.cpp b/esphome/components/pzemdc/pzemdc.cpp index 28e4210ff7..428bcc1fcf 100644 --- a/esphome/components/pzemdc/pzemdc.cpp +++ b/esphome/components/pzemdc/pzemdc.cpp @@ -54,8 +54,10 @@ void PZEMDC::on_modbus_data(const std::vector &data) { void PZEMDC::update() { this->send(PZEM_CMD_READ_IN_REGISTERS, 0, 8); } void PZEMDC::dump_config() { - ESP_LOGCONFIG(TAG, "PZEMDC:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "PZEMDC:\n" + " Address: 0x%02X", + this->address_); LOG_SENSOR("", "Voltage", this->voltage_sensor_); LOG_SENSOR("", "Current", this->current_sensor_); LOG_SENSOR("", "Power", this->power_sensor_); diff --git a/esphome/components/qmp6988/qmp6988.h b/esphome/components/qmp6988/qmp6988.h index f0c11adf43..61b46a4189 100644 --- a/esphome/components/qmp6988/qmp6988.h +++ b/esphome/components/qmp6988/qmp6988.h @@ -1,9 +1,9 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" diff --git a/esphome/components/qr_code/qr_code.cpp b/esphome/components/qr_code/qr_code.cpp index b60e60a4b0..c2db741e17 100644 --- a/esphome/components/qr_code/qr_code.cpp +++ b/esphome/components/qr_code/qr_code.cpp @@ -9,8 +9,10 @@ namespace qr_code { static const char *const TAG = "qr_code"; void QrCode::dump_config() { - ESP_LOGCONFIG(TAG, "QR code:"); - ESP_LOGCONFIG(TAG, " Value: '%s'", this->value_.c_str()); + ESP_LOGCONFIG(TAG, + "QR code:\n" + " Value: '%s'", + this->value_.c_str()); } void QrCode::set_value(const std::string &value) { @@ -24,7 +26,7 @@ void QrCode::set_ecc(qrcodegen_Ecc ecc) { } void QrCode::generate_qr_code() { - ESP_LOGV(TAG, "Generating QR code..."); + ESP_LOGV(TAG, "Generating QR code"); uint8_t tempbuffer[qrcodegen_BUFFER_LEN_MAX]; if (!qrcodegen_encodeText(this->value_.c_str(), tempbuffer, this->qr_, this->ecc_, qrcodegen_VERSION_MIN, diff --git a/esphome/components/qwiic_pir/qwiic_pir.cpp b/esphome/components/qwiic_pir/qwiic_pir.cpp index df9da561a2..6a5196f831 100644 --- a/esphome/components/qwiic_pir/qwiic_pir.cpp +++ b/esphome/components/qwiic_pir/qwiic_pir.cpp @@ -11,31 +11,24 @@ void QwiicPIRComponent::setup() { // Verify I2C communcation by reading and verifying the chip ID uint8_t chip_id; - if (!this->read_byte(QWIIC_PIR_CHIP_ID, &chip_id)) { - ESP_LOGE(TAG, "Failed to read the chip's ID"); - + ESP_LOGE(TAG, "Failed to read chip ID"); this->error_code_ = ERROR_COMMUNICATION_FAILED; this->mark_failed(); - return; } if (chip_id != QWIIC_PIR_DEVICE_ID) { - ESP_LOGE(TAG, "Unknown chip ID, is this a Qwiic PIR?"); - + ESP_LOGE(TAG, "Unknown chip ID"); this->error_code_ = ERROR_WRONG_CHIP_ID; this->mark_failed(); - return; } if (!this->write_byte_16(QWIIC_PIR_DEBOUNCE_TIME, this->debounce_time_)) { - ESP_LOGE(TAG, "Failed to configure debounce time."); - + ESP_LOGE(TAG, "Failed to configure debounce time"); this->error_code_ = ERROR_COMMUNICATION_FAILED; this->mark_failed(); - return; } @@ -43,11 +36,9 @@ void QwiicPIRComponent::setup() { // Publish the starting raw state of the PIR sensor // If NATIVE mode, the binary_sensor state would be unknown until a motion event if (!this->read_byte(QWIIC_PIR_EVENT_STATUS, &this->event_register_.reg)) { - ESP_LOGE(TAG, "Failed to read initial sensor state."); - + ESP_LOGE(TAG, "Failed to read initial state"); this->error_code_ = ERROR_COMMUNICATION_FAILED; this->mark_failed(); - return; } @@ -59,7 +50,6 @@ void QwiicPIRComponent::loop() { // Read Event Register if (!this->read_byte(QWIIC_PIR_EVENT_STATUS, &this->event_register_.reg)) { ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); - return; } @@ -98,39 +88,47 @@ void QwiicPIRComponent::loop() { } void QwiicPIRComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Qwiic PIR:"); + static const char *const RAW = "RAW"; + static const char *const NATIVE = "NATIVE"; + static const char *const HYBRID = "HYBRID"; - if (this->debounce_mode_ == RAW_DEBOUNCE_MODE) { - ESP_LOGCONFIG(TAG, " Debounce Mode: RAW"); - } else if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) { - ESP_LOGCONFIG(TAG, " Debounce Mode: NATIVE"); - ESP_LOGCONFIG(TAG, " Debounce Time: %ums", this->debounce_time_); + const char *debounce_mode_str = RAW; + if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) { + debounce_mode_str = NATIVE; } else if (this->debounce_mode_ == HYBRID_DEBOUNCE_MODE) { - ESP_LOGCONFIG(TAG, " Debounce Mode: HYBRID"); + debounce_mode_str = HYBRID; + } + + ESP_LOGCONFIG(TAG, + "Qwiic PIR:\n" + " Debounce Mode: %s", + debounce_mode_str); + if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) { + ESP_LOGCONFIG(TAG, " Debounce Time: %ums", this->debounce_time_); } switch (this->error_code_) { case NONE: break; case ERROR_COMMUNICATION_FAILED: - ESP_LOGE(TAG, " Communication with Qwiic PIR failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case ERROR_WRONG_CHIP_ID: - ESP_LOGE(TAG, " Qwiic PIR has wrong chip ID - please verify you are using a Qwiic PIR"); + ESP_LOGE(TAG, "Unknown chip ID"); break; default: - ESP_LOGE(TAG, " Qwiic PIR error code %d", (int) this->error_code_); + ESP_LOGE(TAG, "Error %d", (int) this->error_code_); break; } LOG_I2C_DEVICE(this); - LOG_BINARY_SENSOR(" ", "Qwiic PIR Binary Sensor", this); + LOG_BINARY_SENSOR(" ", "Binary Sensor", this); } void QwiicPIRComponent::clear_events_() { // Clear event status register if (!this->write_byte(QWIIC_PIR_EVENT_STATUS, 0x00)) - ESP_LOGW(TAG, "Failed to clear events on sensor"); + ESP_LOGW(TAG, "Failed to clear events"); } } // namespace qwiic_pir diff --git a/esphome/components/rc522/rc522.cpp b/esphome/components/rc522/rc522.cpp index e2146dd14e..018b714190 100644 --- a/esphome/components/rc522/rc522.cpp +++ b/esphome/components/rc522/rc522.cpp @@ -46,7 +46,7 @@ void RC522::setup() { reset_pin_->pin_mode(gpio::FLAG_INPUT); if (!reset_pin_->digital_read()) { // The MFRC522 chip is in power down mode. - ESP_LOGV(TAG, "Power down mode detected. Hard resetting..."); + ESP_LOGV(TAG, "Power down mode detected. Hard resetting"); reset_pin_->pin_mode(gpio::FLAG_OUTPUT); // Now set the resetPowerDownPin as digital output. reset_pin_->digital_write(false); // Make sure we have a clean LOW state. delayMicroseconds(2); // 8.8.1 Reset timing requirements says about 100ns. Let us be generous: 2μsl @@ -101,7 +101,7 @@ void RC522::dump_config() { case NONE: break; case RESET_FAILED: - ESP_LOGE(TAG, "Reset command failed!"); + ESP_LOGE(TAG, "Reset command failed"); break; } @@ -292,7 +292,7 @@ void RC522::pcd_reset_() { return; if (reset_count_ == RESET_COUNT) { - ESP_LOGI(TAG, "Soft reset..."); + ESP_LOGI(TAG, "Soft reset"); // Issue the SoftReset command. pcd_write_register(COMMAND_REG, PCD_SOFT_RESET); } @@ -300,14 +300,14 @@ void RC522::pcd_reset_() { // Expect the PowerDown bit in CommandReg to be cleared (max 3x50ms) if ((pcd_read_register(COMMAND_REG) & (1 << 4)) == 0) { reset_count_ = 0; - ESP_LOGI(TAG, "Device online."); + ESP_LOGI(TAG, "Device online"); // Wait for initialize reset_timeout_ = millis(); return; } if (--reset_count_ == 0) { - ESP_LOGE(TAG, "Unable to reset RC522."); + ESP_LOGE(TAG, "Unable to reset"); this->error_code_ = RESET_FAILED; mark_failed(); } diff --git a/esphome/components/rdm6300/rdm6300.cpp b/esphome/components/rdm6300/rdm6300.cpp index bfdd880079..f9b6126c4b 100644 --- a/esphome/components/rdm6300/rdm6300.cpp +++ b/esphome/components/rdm6300/rdm6300.cpp @@ -1,4 +1,5 @@ #include "rdm6300.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/remote_base/remote_base.cpp b/esphome/components/remote_base/remote_base.cpp index 5dff2c6a38..987286b345 100644 --- a/esphome/components/remote_base/remote_base.cpp +++ b/esphome/components/remote_base/remote_base.cpp @@ -8,27 +8,6 @@ namespace remote_base { static const char *const TAG = "remote_base"; -#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5 -RemoteRMTChannel::RemoteRMTChannel(uint8_t mem_block_num) : mem_block_num_(mem_block_num) { - static rmt_channel_t next_rmt_channel = RMT_CHANNEL_0; - this->channel_ = next_rmt_channel; - next_rmt_channel = rmt_channel_t(int(next_rmt_channel) + mem_block_num); -} - -RemoteRMTChannel::RemoteRMTChannel(rmt_channel_t channel, uint8_t mem_block_num) - : channel_(channel), mem_block_num_(mem_block_num) {} - -void RemoteRMTChannel::config_rmt(rmt_config_t &rmt) { - if (rmt_channel_t(int(this->channel_) + this->mem_block_num_) > RMT_CHANNEL_MAX) { - this->mem_block_num_ = int(RMT_CHANNEL_MAX) - int(this->channel_); - ESP_LOGW(TAG, "Not enough RMT memory blocks available, reduced to %i blocks.", this->mem_block_num_); - } - rmt.channel = this->channel_; - rmt.clk_div = this->clock_divider_; - rmt.mem_block_num = this->mem_block_num_; -} -#endif - /* RemoteReceiveData */ bool RemoteReceiveData::peek_mark(uint32_t length, uint32_t offset) const { diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index 4131d080f5..a18dd0ed7e 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -8,10 +8,6 @@ #include "esphome/core/component.h" #include "esphome/core/hal.h" -#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5 -#include -#endif - namespace esphome { namespace remote_base { @@ -112,43 +108,21 @@ class RemoteComponentBase { #ifdef USE_ESP32 class RemoteRMTChannel { public: -#if ESP_IDF_VERSION_MAJOR >= 5 void set_clock_resolution(uint32_t clock_resolution) { this->clock_resolution_ = clock_resolution; } void set_rmt_symbols(uint32_t rmt_symbols) { this->rmt_symbols_ = rmt_symbols; } -#else - explicit RemoteRMTChannel(uint8_t mem_block_num = 1); - explicit RemoteRMTChannel(rmt_channel_t channel, uint8_t mem_block_num = 1); - - void config_rmt(rmt_config_t &rmt); - void set_clock_divider(uint8_t clock_divider) { this->clock_divider_ = clock_divider; } -#endif protected: uint32_t from_microseconds_(uint32_t us) { -#if ESP_IDF_VERSION_MAJOR >= 5 const uint32_t ticks_per_ten_us = this->clock_resolution_ / 100000u; -#else - const uint32_t ticks_per_ten_us = 80000000u / this->clock_divider_ / 100000u; -#endif return us * ticks_per_ten_us / 10; } uint32_t to_microseconds_(uint32_t ticks) { -#if ESP_IDF_VERSION_MAJOR >= 5 const uint32_t ticks_per_ten_us = this->clock_resolution_ / 100000u; -#else - const uint32_t ticks_per_ten_us = 80000000u / this->clock_divider_ / 100000u; -#endif return (ticks * 10) / ticks_per_ten_us; } RemoteComponentBase *remote_base_; -#if ESP_IDF_VERSION_MAJOR >= 5 uint32_t clock_resolution_{1000000}; uint32_t rmt_symbols_; -#else - rmt_channel_t channel_{RMT_CHANNEL_0}; - uint8_t mem_block_num_; - uint8_t clock_divider_{80}; -#endif }; #endif diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index 8c21cb210c..321cfc93ff 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -1,18 +1,15 @@ from esphome import pins import esphome.codegen as cg -from esphome.components import esp32_rmt, remote_base +from esphome.components import esp32, esp32_rmt, remote_base import esphome.config_validation as cv from esphome.const import ( CONF_BUFFER_SIZE, - CONF_CLOCK_DIVIDER, CONF_CLOCK_RESOLUTION, CONF_DUMP, CONF_FILTER, CONF_ID, CONF_IDLE, - CONF_MEMORY_BLOCKS, CONF_PIN, - CONF_RMT_CHANNEL, CONF_RMT_SYMBOLS, CONF_TOLERANCE, CONF_TYPE, @@ -103,43 +100,38 @@ CONFIG_SCHEMA = remote_base.validate_triggers( cv.positive_time_period_microseconds, cv.Range(max=TimePeriod(microseconds=4294967295)), ), - cv.SplitDefault(CONF_CLOCK_DIVIDER, esp32_arduino=80): cv.All( - cv.only_on_esp32, - cv.only_with_arduino, - cv.int_range(min=1, max=255), - ), cv.Optional(CONF_CLOCK_RESOLUTION): cv.All( cv.only_on_esp32, - cv.only_with_esp_idf, esp32_rmt.validate_clock_resolution(), ), cv.Optional(CONF_IDLE, default="10ms"): cv.All( cv.positive_time_period_microseconds, cv.Range(max=TimePeriod(microseconds=4294967295)), ), - cv.SplitDefault(CONF_MEMORY_BLOCKS, esp32_arduino=3): cv.All( - cv.only_with_arduino, cv.int_range(min=1, max=8) - ), - cv.Optional(CONF_RMT_CHANNEL): cv.All( - cv.only_with_arduino, esp32_rmt.validate_rmt_channel(tx=False) - ), cv.SplitDefault( CONF_RMT_SYMBOLS, - esp32_idf=192, - esp32_s2_idf=192, - esp32_s3_idf=192, - esp32_c3_idf=96, - esp32_c6_idf=96, - esp32_h2_idf=96, - ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), + esp32=192, + esp32_s2=192, + esp32_s3=192, + esp32_p4=192, + esp32_c3=96, + esp32_c5=96, + esp32_c6=96, + esp32_h2=96, + ): cv.All(cv.only_on_esp32, cv.int_range(min=2)), cv.Optional(CONF_FILTER_SYMBOLS): cv.All( - cv.only_with_esp_idf, cv.int_range(min=0) + cv.only_on_esp32, cv.int_range(min=0) ), cv.SplitDefault( CONF_RECEIVE_SYMBOLS, - esp32_idf=192, - ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), - cv.Optional(CONF_USE_DMA): cv.All(cv.only_with_esp_idf, cv.boolean), + esp32=192, + ): cv.All(cv.only_on_esp32, cv.int_range(min=2)), + cv.Optional(CONF_USE_DMA): cv.All( + esp32.only_on_variant( + supported=[esp32.const.VARIANT_ESP32S3, esp32.const.VARIANT_ESP32P4] + ), + cv.boolean, + ), } ).extend(cv.COMPONENT_SCHEMA) ) @@ -148,24 +140,15 @@ CONFIG_SCHEMA = remote_base.validate_triggers( async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_PIN]) if CORE.is_esp32: - if esp32_rmt.use_new_rmt_driver(): - var = cg.new_Pvariable(config[CONF_ID], pin) - cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) - cg.add(var.set_receive_symbols(config[CONF_RECEIVE_SYMBOLS])) - if CONF_USE_DMA in config: - cg.add(var.set_with_dma(config[CONF_USE_DMA])) - if CONF_CLOCK_RESOLUTION in config: - cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION])) - if CONF_FILTER_SYMBOLS in config: - cg.add(var.set_filter_symbols(config[CONF_FILTER_SYMBOLS])) - else: - if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: - var = cg.new_Pvariable( - config[CONF_ID], pin, rmt_channel, config[CONF_MEMORY_BLOCKS] - ) - else: - var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS]) - cg.add(var.set_clock_divider(config[CONF_CLOCK_DIVIDER])) + var = cg.new_Pvariable(config[CONF_ID], pin) + cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) + cg.add(var.set_receive_symbols(config[CONF_RECEIVE_SYMBOLS])) + if CONF_USE_DMA in config: + cg.add(var.set_with_dma(config[CONF_USE_DMA])) + if CONF_CLOCK_RESOLUTION in config: + cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION])) + if CONF_FILTER_SYMBOLS in config: + cg.add(var.set_filter_symbols(config[CONF_FILTER_SYMBOLS])) else: var = cg.new_Pvariable(config[CONF_ID], pin) diff --git a/esphome/components/remote_receiver/remote_receiver.h b/esphome/components/remote_receiver/remote_receiver.h index 8d19d5490f..9d844eee66 100644 --- a/esphome/components/remote_receiver/remote_receiver.h +++ b/esphome/components/remote_receiver/remote_receiver.h @@ -5,7 +5,7 @@ #include -#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 +#if defined(USE_ESP32) #include #endif @@ -29,7 +29,7 @@ struct RemoteReceiverComponentStore { uint32_t filter_us{10}; ISRInternalGPIOPin pin; }; -#elif defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 +#elif defined(USE_ESP32) struct RemoteReceiverComponentStore { /// Stores RMT symbols and rx done event data volatile uint8_t *buffer{nullptr}; @@ -55,21 +55,13 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase, { public: -#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5 - RemoteReceiverComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1) - : RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {} - - RemoteReceiverComponent(InternalGPIOPin *pin, rmt_channel_t channel, uint8_t mem_block_num = 1) - : RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(channel, mem_block_num) {} -#else RemoteReceiverComponent(InternalGPIOPin *pin) : RemoteReceiverBase(pin) {} -#endif void setup() override; void dump_config() override; void loop() override; float get_setup_priority() const override { return setup_priority::DATA; } -#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 +#ifdef USE_ESP32 void set_filter_symbols(uint32_t filter_symbols) { this->filter_symbols_ = filter_symbols; } void set_receive_symbols(uint32_t receive_symbols) { this->receive_symbols_ = receive_symbols; } void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; } @@ -80,21 +72,16 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase, protected: #ifdef USE_ESP32 -#if ESP_IDF_VERSION_MAJOR >= 5 void decode_rmt_(rmt_symbol_word_t *item, size_t item_count); rmt_channel_handle_t channel_{NULL}; uint32_t filter_symbols_{0}; uint32_t receive_symbols_{0}; bool with_dma_{false}; -#else - void decode_rmt_(rmt_item32_t *item, size_t item_count); - RingbufHandle_t ringbuf_; -#endif esp_err_t error_code_{ESP_OK}; std::string error_string_{""}; #endif -#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || (defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5) +#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_ESP32) RemoteReceiverComponentStore store_; HighFrequencyLoopRequester high_freq_; #endif diff --git a/esphome/components/remote_receiver/remote_receiver_esp32.cpp b/esphome/components/remote_receiver/remote_receiver_esp32.cpp index 18bf01c8e8..3d6346baec 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp32.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp32.cpp @@ -14,7 +14,6 @@ static const uint32_t RMT_CLK_FREQ = 32000000; static const uint32_t RMT_CLK_FREQ = 80000000; #endif -#if ESP_IDF_VERSION_MAJOR >= 5 static bool IRAM_ATTR HOT rmt_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *event, void *arg) { RemoteReceiverComponentStore *store = (RemoteReceiverComponentStore *) arg; rmt_rx_done_event_data_t *event_buffer = (rmt_rx_done_event_data_t *) (store->buffer + store->buffer_write); @@ -37,11 +36,9 @@ static bool IRAM_ATTR HOT rmt_callback(rmt_channel_handle_t channel, const rmt_r store->buffer_write = next_write; return false; } -#endif void RemoteReceiverComponent::setup() { ESP_LOGCONFIG(TAG, "Running setup"); -#if ESP_IDF_VERSION_MAJOR >= 5 rmt_rx_channel_config_t channel; memset(&channel, 0, sizeof(channel)); channel.clk_src = RMT_CLK_SRC_DEFAULT; @@ -105,79 +102,22 @@ void RemoteReceiverComponent::setup() { this->mark_failed(); return; } -#else - this->pin_->setup(); - rmt_config_t rmt{}; - this->config_rmt(rmt); - rmt.gpio_num = gpio_num_t(this->pin_->get_pin()); - rmt.rmt_mode = RMT_MODE_RX; - if (this->filter_us_ == 0) { - rmt.rx_config.filter_en = false; - } else { - rmt.rx_config.filter_en = true; - rmt.rx_config.filter_ticks_thresh = static_cast( - std::min(this->from_microseconds_(this->filter_us_) * this->clock_divider_, (uint32_t) 255)); - } - rmt.rx_config.idle_threshold = - static_cast(std::min(this->from_microseconds_(this->idle_us_), (uint32_t) 65535)); - - esp_err_t error = rmt_config(&rmt); - if (error != ESP_OK) { - this->error_code_ = error; - this->error_string_ = "in rmt_config"; - this->mark_failed(); - return; - } - - error = rmt_driver_install(this->channel_, this->buffer_size_, 0); - if (error != ESP_OK) { - this->error_code_ = error; - if (error == ESP_ERR_INVALID_STATE) { - this->error_string_ = str_sprintf("RMT channel %i is already in use by another component", this->channel_); - } else { - this->error_string_ = "in rmt_driver_install"; - } - this->mark_failed(); - return; - } - error = rmt_get_ringbuf_handle(this->channel_, &this->ringbuf_); - if (error != ESP_OK) { - this->error_code_ = error; - this->error_string_ = "in rmt_get_ringbuf_handle"; - this->mark_failed(); - return; - } - error = rmt_rx_start(this->channel_, true); - if (error != ESP_OK) { - this->error_code_ = error; - this->error_string_ = "in rmt_rx_start"; - this->mark_failed(); - return; - } -#endif } void RemoteReceiverComponent::dump_config() { ESP_LOGCONFIG(TAG, "Remote Receiver:"); LOG_PIN(" Pin: ", this->pin_); -#if ESP_IDF_VERSION_MAJOR >= 5 - ESP_LOGCONFIG(TAG, " Clock resolution: %" PRIu32 " hz", this->clock_resolution_); - ESP_LOGCONFIG(TAG, " RMT symbols: %" PRIu32, this->rmt_symbols_); - ESP_LOGCONFIG(TAG, " Filter symbols: %" PRIu32, this->filter_symbols_); - ESP_LOGCONFIG(TAG, " Receive symbols: %" PRIu32, this->receive_symbols_); -#else - if (this->pin_->digital_read()) { - ESP_LOGW(TAG, "Remote Receiver Signal starts with a HIGH value. Usually this means you have to " - "invert the signal using 'inverted: True' in the pin schema!"); - } - ESP_LOGCONFIG(TAG, " Channel: %d", this->channel_); - ESP_LOGCONFIG(TAG, " RMT memory blocks: %d", this->mem_block_num_); - ESP_LOGCONFIG(TAG, " Clock divider: %u", this->clock_divider_); -#endif - ESP_LOGCONFIG(TAG, " Tolerance: %" PRIu32 "%s", this->tolerance_, - (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%"); - ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %" PRIu32 " us", this->filter_us_); - ESP_LOGCONFIG(TAG, " Signal is done after %" PRIu32 " us of no changes", this->idle_us_); + ESP_LOGCONFIG(TAG, + " Clock resolution: %" PRIu32 " hz\n" + " RMT symbols: %" PRIu32 "\n" + " Filter symbols: %" PRIu32 "\n" + " Receive symbols: %" PRIu32 "\n" + " Tolerance: %" PRIu32 "%s\n" + " Filter out pulses shorter than: %" PRIu32 " us\n" + " Signal is done after %" PRIu32 " us of no changes", + this->clock_resolution_, this->rmt_symbols_, this->filter_symbols_, this->receive_symbols_, + this->tolerance_, (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", + this->filter_us_, this->idle_us_); if (this->is_failed()) { ESP_LOGE(TAG, "Configuring RMT driver failed: %s (%s)", esp_err_to_name(this->error_code_), this->error_string_.c_str()); @@ -185,7 +125,6 @@ void RemoteReceiverComponent::dump_config() { } void RemoteReceiverComponent::loop() { -#if ESP_IDF_VERSION_MAJOR >= 5 if (this->store_.error != ESP_OK) { ESP_LOGE(TAG, "Receive error"); this->error_code_ = this->store_.error; @@ -211,25 +150,9 @@ void RemoteReceiverComponent::loop() { this->call_listeners_dumpers_(); } } -#else - size_t len = 0; - auto *item = (rmt_item32_t *) xRingbufferReceive(this->ringbuf_, &len, 0); - if (item != nullptr) { - this->decode_rmt_(item, len / sizeof(rmt_item32_t)); - vRingbufferReturnItem(this->ringbuf_, item); - - if (!this->temp_.empty()) { - this->call_listeners_dumpers_(); - } - } -#endif } -#if ESP_IDF_VERSION_MAJOR >= 5 void RemoteReceiverComponent::decode_rmt_(rmt_symbol_word_t *item, size_t item_count) { -#else -void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t item_count) { -#endif bool prev_level = false; bool idle_level = false; uint32_t prev_length = 0; diff --git a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp index 4c743a6eed..a0fd56bcf4 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp @@ -1,7 +1,7 @@ #include "remote_receiver.h" #include "esphome/core/hal.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ESP8266 @@ -63,11 +63,14 @@ void RemoteReceiverComponent::dump_config() { ESP_LOGW(TAG, "Remote Receiver Signal starts with a HIGH value. Usually this means you have to " "invert the signal using 'inverted: True' in the pin schema!"); } - ESP_LOGCONFIG(TAG, " Buffer Size: %u", this->buffer_size_); - ESP_LOGCONFIG(TAG, " Tolerance: %u%s", this->tolerance_, - (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%"); - ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %u us", this->filter_us_); - ESP_LOGCONFIG(TAG, " Signal is done after %u us of no changes", this->idle_us_); + ESP_LOGCONFIG(TAG, + " Buffer Size: %u\n" + " Tolerance: %u%s\n" + " Filter out pulses shorter than: %u us\n" + " Signal is done after %u us of no changes", + this->buffer_size_, this->tolerance_, + (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", this->filter_us_, + this->idle_us_); } void RemoteReceiverComponent::loop() { diff --git a/esphome/components/remote_receiver/remote_receiver_libretiny.cpp b/esphome/components/remote_receiver/remote_receiver_libretiny.cpp index fe84799cae..7a6054737e 100644 --- a/esphome/components/remote_receiver/remote_receiver_libretiny.cpp +++ b/esphome/components/remote_receiver/remote_receiver_libretiny.cpp @@ -1,7 +1,7 @@ #include "remote_receiver.h" #include "esphome/core/hal.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_LIBRETINY @@ -63,11 +63,14 @@ void RemoteReceiverComponent::dump_config() { ESP_LOGW(TAG, "Remote Receiver Signal starts with a HIGH value. Usually this means you have to " "invert the signal using 'inverted: True' in the pin schema!"); } - ESP_LOGCONFIG(TAG, " Buffer Size: %u", this->buffer_size_); - ESP_LOGCONFIG(TAG, " Tolerance: %u%s", this->tolerance_, - (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%"); - ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %u us", this->filter_us_); - ESP_LOGCONFIG(TAG, " Signal is done after %u us of no changes", this->idle_us_); + ESP_LOGCONFIG(TAG, + " Buffer Size: %u\n" + " Tolerance: %u%s\n" + " Filter out pulses shorter than: %u us\n" + " Signal is done after %u us of no changes", + this->buffer_size_, this->tolerance_, + (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", this->filter_us_, + this->idle_us_); } void RemoteReceiverComponent::loop() { diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index e7a94c175e..713cee0186 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -1,17 +1,15 @@ from esphome import automation, pins import esphome.codegen as cg -from esphome.components import esp32_rmt, remote_base +from esphome.components import esp32, esp32_rmt, remote_base import esphome.config_validation as cv from esphome.const import ( CONF_CARRIER_DUTY_PERCENT, - CONF_CLOCK_DIVIDER, CONF_CLOCK_RESOLUTION, CONF_ID, CONF_INVERTED, CONF_MODE, CONF_OPEN_DRAIN, CONF_PIN, - CONF_RMT_CHANNEL, CONF_RMT_SYMBOLS, CONF_USE_DMA, ) @@ -38,26 +36,26 @@ CONFIG_SCHEMA = cv.Schema( ), cv.Optional(CONF_CLOCK_RESOLUTION): cv.All( cv.only_on_esp32, - cv.only_with_esp_idf, esp32_rmt.validate_clock_resolution(), ), - cv.Optional(CONF_CLOCK_DIVIDER): cv.All( - cv.only_on_esp32, cv.only_with_arduino, cv.int_range(min=1, max=255) + cv.Optional(CONF_EOT_LEVEL): cv.All(cv.only_on_esp32, cv.boolean), + cv.Optional(CONF_USE_DMA): cv.All( + esp32.only_on_variant( + supported=[esp32.const.VARIANT_ESP32S3, esp32.const.VARIANT_ESP32P4] + ), + cv.boolean, ), - cv.Optional(CONF_EOT_LEVEL): cv.All(cv.only_with_esp_idf, cv.boolean), - cv.Optional(CONF_USE_DMA): cv.All(cv.only_with_esp_idf, cv.boolean), cv.SplitDefault( CONF_RMT_SYMBOLS, - esp32_idf=64, - esp32_s2_idf=64, - esp32_s3_idf=48, - esp32_c3_idf=48, - esp32_c6_idf=48, - esp32_h2_idf=48, - ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), - cv.Optional(CONF_RMT_CHANNEL): cv.All( - cv.only_with_arduino, esp32_rmt.validate_rmt_channel(tx=True) - ), + esp32=64, + esp32_s2=64, + esp32_s3=48, + esp32_p4=48, + esp32_c3=48, + esp32_c5=48, + esp32_c6=48, + esp32_h2=48, + ): cv.All(cv.only_on_esp32, cv.int_range(min=2)), cv.Optional(CONF_ON_TRANSMIT): automation.validate_automation(single=True), cv.Optional(CONF_ON_COMPLETE): automation.validate_automation(single=True), } @@ -67,30 +65,21 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_PIN]) if CORE.is_esp32: - if esp32_rmt.use_new_rmt_driver(): - var = cg.new_Pvariable(config[CONF_ID], pin) - cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) - if CONF_CLOCK_RESOLUTION in config: - cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION])) - if CONF_USE_DMA in config: - cg.add(var.set_with_dma(config[CONF_USE_DMA])) - if CONF_EOT_LEVEL in config: - cg.add(var.set_eot_level(config[CONF_EOT_LEVEL])) - else: - cg.add( - var.set_eot_level( - config[CONF_PIN][CONF_MODE][CONF_OPEN_DRAIN] - or config[CONF_PIN][CONF_INVERTED] - ) - ) + var = cg.new_Pvariable(config[CONF_ID], pin) + cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) + if CONF_CLOCK_RESOLUTION in config: + cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION])) + if CONF_USE_DMA in config: + cg.add(var.set_with_dma(config[CONF_USE_DMA])) + if CONF_EOT_LEVEL in config: + cg.add(var.set_eot_level(config[CONF_EOT_LEVEL])) else: - if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: - var = cg.new_Pvariable(config[CONF_ID], pin, rmt_channel) - else: - var = cg.new_Pvariable(config[CONF_ID], pin) - if CONF_CLOCK_DIVIDER in config: - cg.add(var.set_clock_divider(config[CONF_CLOCK_DIVIDER])) - + cg.add( + var.set_eot_level( + config[CONF_PIN][CONF_MODE][CONF_OPEN_DRAIN] + or config[CONF_PIN][CONF_INVERTED] + ) + ) else: var = cg.new_Pvariable(config[CONF_ID], pin) await cg.register_component(var, config) diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index 0a8f354c72..f0dab2aaf8 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -5,7 +5,7 @@ #include -#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 +#if defined(USE_ESP32) #include #endif @@ -20,15 +20,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #endif { public: -#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5 - RemoteTransmitterComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1) - : remote_base::RemoteTransmitterBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {} - - RemoteTransmitterComponent(InternalGPIOPin *pin, rmt_channel_t channel, uint8_t mem_block_num = 1) - : remote_base::RemoteTransmitterBase(pin), remote_base::RemoteRMTChannel(channel, mem_block_num) {} -#else explicit RemoteTransmitterComponent(InternalGPIOPin *pin) : remote_base::RemoteTransmitterBase(pin) {} -#endif void setup() override; void dump_config() override; @@ -38,7 +30,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, void set_carrier_duty_percent(uint8_t carrier_duty_percent) { this->carrier_duty_percent_ = carrier_duty_percent; } -#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 +#if defined(USE_ESP32) void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; } void set_eot_level(bool eot_level) { this->eot_level_ = eot_level; } void digital_write(bool value); @@ -65,15 +57,11 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, uint32_t current_carrier_frequency_{38000}; bool initialized_{false}; -#if ESP_IDF_VERSION_MAJOR >= 5 std::vector rmt_temp_; bool with_dma_{false}; bool eot_level_{false}; rmt_channel_handle_t channel_{NULL}; rmt_encoder_handle_t encoder_{NULL}; -#else - std::vector rmt_temp_; -#endif esp_err_t error_code_{ESP_OK}; std::string error_string_{""}; bool inverted_{false}; diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index a5512c2a55..411e380670 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -18,14 +18,10 @@ void RemoteTransmitterComponent::setup() { void RemoteTransmitterComponent::dump_config() { ESP_LOGCONFIG(TAG, "Remote Transmitter:"); -#if ESP_IDF_VERSION_MAJOR >= 5 - ESP_LOGCONFIG(TAG, " Clock resolution: %" PRIu32 " hz", this->clock_resolution_); - ESP_LOGCONFIG(TAG, " RMT symbols: %" PRIu32, this->rmt_symbols_); -#else - ESP_LOGCONFIG(TAG, " Channel: %d", this->channel_); - ESP_LOGCONFIG(TAG, " RMT memory blocks: %d", this->mem_block_num_); - ESP_LOGCONFIG(TAG, " Clock divider: %u", this->clock_divider_); -#endif + ESP_LOGCONFIG(TAG, + " Clock resolution: %" PRIu32 " hz\n" + " RMT symbols: %" PRIu32, + this->clock_resolution_, this->rmt_symbols_); LOG_PIN(" Pin: ", this->pin_); if (this->current_carrier_frequency_ != 0 && this->carrier_duty_percent_ != 100) { @@ -38,7 +34,6 @@ void RemoteTransmitterComponent::dump_config() { } } -#if ESP_IDF_VERSION_MAJOR >= 5 void RemoteTransmitterComponent::digital_write(bool value) { rmt_symbol_word_t symbol = { .duration0 = 1, @@ -61,10 +56,8 @@ void RemoteTransmitterComponent::digital_write(bool value) { this->status_set_warning(); } } -#endif void RemoteTransmitterComponent::configure_rmt_() { -#if ESP_IDF_VERSION_MAJOR >= 5 esp_err_t error; if (!this->initialized_) { @@ -136,54 +129,6 @@ void RemoteTransmitterComponent::configure_rmt_() { this->mark_failed(); return; } -#else - rmt_config_t c{}; - - this->config_rmt(c); - c.rmt_mode = RMT_MODE_TX; - c.gpio_num = gpio_num_t(this->pin_->get_pin()); - c.tx_config.loop_en = false; - - if (this->current_carrier_frequency_ == 0 || this->carrier_duty_percent_ == 100) { - c.tx_config.carrier_en = false; - } else { - c.tx_config.carrier_en = true; - c.tx_config.carrier_freq_hz = this->current_carrier_frequency_; - c.tx_config.carrier_duty_percent = this->carrier_duty_percent_; - } - - c.tx_config.idle_output_en = true; - if (!this->inverted_) { - c.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH; - c.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; - } else { - c.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW; - c.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH; - } - - esp_err_t error = rmt_config(&c); - if (error != ESP_OK) { - this->error_code_ = error; - this->error_string_ = "in rmt_config"; - this->mark_failed(); - return; - } - - if (!this->initialized_) { - error = rmt_driver_install(this->channel_, 0, 0); - if (error != ESP_OK) { - this->error_code_ = error; - if (error == ESP_ERR_INVALID_STATE) { - this->error_string_ = str_sprintf("RMT channel %i is already in use by another component", this->channel_); - } else { - this->error_string_ = "in rmt_driver_install"; - } - this->mark_failed(); - return; - } - this->initialized_ = true; - } -#endif } void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { @@ -198,11 +143,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen this->rmt_temp_.clear(); this->rmt_temp_.reserve((this->temp_.get_data().size() + 1) / 2); uint32_t rmt_i = 0; -#if ESP_IDF_VERSION_MAJOR >= 5 rmt_symbol_word_t rmt_item; -#else - rmt_item32_t rmt_item; -#endif for (int32_t val : this->temp_.get_data()) { bool level = val >= 0; @@ -237,7 +178,6 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen return; } this->transmit_trigger_->trigger(); -#if ESP_IDF_VERSION_MAJOR >= 5 for (uint32_t i = 0; i < send_times; i++) { rmt_transmit_config_t config; memset(&config, 0, sizeof(config)); @@ -259,19 +199,6 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen if (i + 1 < send_times) delayMicroseconds(send_wait); } -#else - for (uint32_t i = 0; i < send_times; i++) { - esp_err_t error = rmt_write_items(this->channel_, this->rmt_temp_.data(), this->rmt_temp_.size(), true); - if (error != ESP_OK) { - ESP_LOGW(TAG, "rmt_write_items failed: %s", esp_err_to_name(error)); - this->status_set_warning(); - } else { - this->status_clear_warning(); - } - if (i + 1 < send_times) - delayMicroseconds(send_wait); - } -#endif this->complete_trigger_->trigger(); } diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp index 09cc16e975..73a1a7754f 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp @@ -15,8 +15,10 @@ void RemoteTransmitterComponent::setup() { } void RemoteTransmitterComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Remote Transmitter..."); - ESP_LOGCONFIG(TAG, " Carrier Duty: %u%%", this->carrier_duty_percent_); + ESP_LOGCONFIG(TAG, + "Remote Transmitter:\n" + " Carrier Duty: %u%%", + this->carrier_duty_percent_); LOG_PIN(" Pin: ", this->pin_); } @@ -72,7 +74,7 @@ void RemoteTransmitterComponent::space_(uint32_t usec) { } void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { - ESP_LOGD(TAG, "Sending remote code..."); + ESP_LOGD(TAG, "Sending remote code"); uint32_t on_time, off_time; this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); this->target_time_ = 0; diff --git a/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp b/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp index 20d8736c00..42bf5bd95b 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp @@ -15,8 +15,10 @@ void RemoteTransmitterComponent::setup() { } void RemoteTransmitterComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Remote Transmitter..."); - ESP_LOGCONFIG(TAG, " Carrier Duty: %u%%", this->carrier_duty_percent_); + ESP_LOGCONFIG(TAG, + "Remote Transmitter:\n" + " Carrier Duty: %u%%", + this->carrier_duty_percent_); LOG_PIN(" Pin: ", this->pin_); } @@ -74,7 +76,7 @@ void RemoteTransmitterComponent::space_(uint32_t usec) { } void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { - ESP_LOGD(TAG, "Sending remote code..."); + ESP_LOGD(TAG, "Sending remote code"); uint32_t on_time, off_time; this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); this->target_time_ = 0; diff --git a/esphome/components/resistance/resistance_sensor.cpp b/esphome/components/resistance/resistance_sensor.cpp index e13959fd37..6e57214449 100644 --- a/esphome/components/resistance/resistance_sensor.cpp +++ b/esphome/components/resistance/resistance_sensor.cpp @@ -8,9 +8,12 @@ static const char *const TAG = "resistance"; void ResistanceSensor::dump_config() { LOG_SENSOR("", "Resistance Sensor", this); - ESP_LOGCONFIG(TAG, " Configuration: %s", this->configuration_ == UPSTREAM ? "UPSTREAM" : "DOWNSTREAM"); - ESP_LOGCONFIG(TAG, " Resistor: %.2fΩ", this->resistor_); - ESP_LOGCONFIG(TAG, " Reference Voltage: %.1fV", this->reference_voltage_); + ESP_LOGCONFIG(TAG, + " Configuration: %s\n" + " Resistor: %.2fΩ\n" + " Reference Voltage: %.1fV", + this->configuration_ == UPSTREAM ? "UPSTREAM" : "DOWNSTREAM", this->resistor_, + this->reference_voltage_); } void ResistanceSensor::process_(float value) { if (std::isnan(value)) { diff --git a/esphome/components/restart/button/restart_button.cpp b/esphome/components/restart/button/restart_button.cpp index d8ff061355..accb1a8356 100644 --- a/esphome/components/restart/button/restart_button.cpp +++ b/esphome/components/restart/button/restart_button.cpp @@ -9,7 +9,7 @@ namespace restart { static const char *const TAG = "restart.button"; void RestartButton::press_action() { - ESP_LOGI(TAG, "Restarting device..."); + ESP_LOGI(TAG, "Restarting device"); // Let MQTT settle a bit delay(100); // NOLINT App.safe_reboot(); diff --git a/esphome/components/restart/switch/restart_switch.cpp b/esphome/components/restart/switch/restart_switch.cpp index 3076fde99e..422e85f4cd 100644 --- a/esphome/components/restart/switch/restart_switch.cpp +++ b/esphome/components/restart/switch/restart_switch.cpp @@ -13,7 +13,7 @@ void RestartSwitch::write_state(bool state) { this->publish_state(false); if (state) { - ESP_LOGI(TAG, "Restarting device..."); + ESP_LOGI(TAG, "Restarting device"); // Let MQTT settle a bit delay(100); // NOLINT App.safe_reboot(); diff --git a/esphome/components/rotary_encoder/rotary_encoder.cpp b/esphome/components/rotary_encoder/rotary_encoder.cpp index 9a9c3a6338..79bc123597 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.cpp +++ b/esphome/components/rotary_encoder/rotary_encoder.cpp @@ -1,6 +1,6 @@ #include "rotary_encoder.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace rotary_encoder { diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index c3e11336a9..2718e3050f 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -167,6 +167,7 @@ async def to_code(config): cg.add_platformio_option("lib_ldf_mode", "chain+") cg.add_platformio_option("board", config[CONF_BOARD]) cg.add_build_flag("-DUSE_RP2040") + cg.set_cpp_standard("gnu++17") cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) cg.add_define("ESPHOME_VARIANT", "RP2040") diff --git a/esphome/components/rp2040/preferences.cpp b/esphome/components/rp2040/preferences.cpp index e7aa9ab28d..cbf2b00641 100644 --- a/esphome/components/rp2040/preferences.cpp +++ b/esphome/components/rp2040/preferences.cpp @@ -87,7 +87,7 @@ class RP2040Preferences : public ESPPreferences { RP2040Preferences() : eeprom_sector_(&_EEPROM_start) {} void setup() { s_flash_storage = new uint8_t[RP2040_FLASH_STORAGE_SIZE]; // NOLINT - ESP_LOGVV(TAG, "Loading preferences from flash..."); + ESP_LOGVV(TAG, "Loading preferences from flash"); memcpy(s_flash_storage, this->eeprom_sector_, RP2040_FLASH_STORAGE_SIZE); } @@ -114,7 +114,7 @@ class RP2040Preferences : public ESPPreferences { if (s_prevent_write) return false; - ESP_LOGD(TAG, "Saving preferences to flash..."); + ESP_LOGD(TAG, "Saving"); { InterruptLock lock; @@ -129,7 +129,7 @@ class RP2040Preferences : public ESPPreferences { } bool reset() override { - ESP_LOGD(TAG, "Cleaning up preferences in flash..."); + ESP_LOGD(TAG, "Erasing storage"); { InterruptLock lock; ::rp2040.idleOtherCore(); diff --git a/esphome/components/rp2040_pio_led_strip/led_strip.cpp b/esphome/components/rp2040_pio_led_strip/led_strip.cpp index b675277914..42f7e9cf52 100644 --- a/esphome/components/rp2040_pio_led_strip/led_strip.cpp +++ b/esphome/components/rp2040_pio_led_strip/led_strip.cpp @@ -9,8 +9,8 @@ #include #include #include -#include #include +#include namespace esphome { namespace rp2040_pio_led_strip { @@ -44,7 +44,7 @@ void RP2040PIOLEDStripLightOutput::setup() { size_t buffer_size = this->get_buffer_size_(); - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; this->buf_ = allocator.allocate(buffer_size); if (this->buf_ == nullptr) { ESP_LOGE(TAG, "Failed to allocate buffer of size %u", buffer_size); @@ -138,7 +138,7 @@ void RP2040PIOLEDStripLightOutput::setup() { } void RP2040PIOLEDStripLightOutput::write_state(light::LightState *state) { - ESP_LOGVV(TAG, "Writing state..."); + ESP_LOGVV(TAG, "Writing state"); if (this->is_failed()) { ESP_LOGW(TAG, "Light is in failed state, not writing state."); @@ -199,12 +199,15 @@ light::ESPColorView RP2040PIOLEDStripLightOutput::get_view_internal(int32_t inde } void RP2040PIOLEDStripLightOutput::dump_config() { - ESP_LOGCONFIG(TAG, "RP2040 PIO LED Strip Light Output:"); - ESP_LOGCONFIG(TAG, " Pin: GPIO%d", this->pin_); - ESP_LOGCONFIG(TAG, " Number of LEDs: %d", this->num_leds_); - ESP_LOGCONFIG(TAG, " RGBW: %s", YESNO(this->is_rgbw_)); - ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order_to_string(this->rgb_order_)); - ESP_LOGCONFIG(TAG, " Max Refresh Rate: %f Hz", this->max_refresh_rate_); + ESP_LOGCONFIG(TAG, + "RP2040 PIO LED Strip Light Output:\n" + " Pin: GPIO%d\n" + " Number of LEDs: %d\n" + " RGBW: %s\n" + " RGB Order: %s\n" + " Max Refresh Rate: %f Hz", + this->pin_, this->num_leds_, YESNO(this->is_rgbw_), rgb_order_to_string(this->rgb_order_), + this->max_refresh_rate_); } float RP2040PIOLEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; } diff --git a/esphome/components/rp2040_pio_led_strip/light.py b/esphome/components/rp2040_pio_led_strip/light.py index 4b6a80e78b..9107db9b7f 100644 --- a/esphome/components/rp2040_pio_led_strip/light.py +++ b/esphome/components/rp2040_pio_led_strip/light.py @@ -173,7 +173,7 @@ RGB_ORDERS = { CHIPSET_TIMINGS = { "WS2812": LEDStripTimings(20, 40, 46, 34), "WS2812B": LEDStripTimings(23, 49, 46, 26), - "SK6812": LEDStripTimings(17, 52, 34, 34), + "SK6812": LEDStripTimings(20, 54, 38, 38), "SM16703": LEDStripTimings(17, 52, 52, 17), } diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index 6b7071609f..2c4a0f917f 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -27,8 +27,10 @@ inline double deg2rad(double degrees) { } void Rtttl::dump_config() { - ESP_LOGCONFIG(TAG, "Rtttl:"); - ESP_LOGCONFIG(TAG, " Gain: %f", this->gain_); + ESP_LOGCONFIG(TAG, + "Rtttl:\n" + " Gain: %f", + this->gain_); } void Rtttl::play(std::string rtttl) { @@ -140,8 +142,10 @@ void Rtttl::stop() { } void Rtttl::loop() { - if (this->note_duration_ == 0 || this->state_ == State::STATE_STOPPED) + if (this->note_duration_ == 0 || this->state_ == State::STATE_STOPPED) { + this->disable_loop(); return; + } #ifdef USE_SPEAKER if (this->speaker_ != nullptr) { @@ -389,6 +393,11 @@ void Rtttl::set_state_(State state) { this->state_ = state; ESP_LOGV(TAG, "State changed from %s to %s", LOG_STR_ARG(state_to_string(old_state)), LOG_STR_ARG(state_to_string(state))); + + // Clear loop_done when transitioning from STOPPED to any other state + if (old_state == State::STATE_STOPPED && state != State::STATE_STOPPED) { + this->enable_loop(); + } } } // namespace rtttl diff --git a/esphome/components/safe_mode/safe_mode.cpp b/esphome/components/safe_mode/safe_mode.cpp index 245d217501..5a62604269 100644 --- a/esphome/components/safe_mode/safe_mode.cpp +++ b/esphome/components/safe_mode/safe_mode.cpp @@ -16,10 +16,12 @@ static const char *const TAG = "safe_mode"; void SafeModeComponent::dump_config() { ESP_LOGCONFIG(TAG, "Safe Mode:"); - ESP_LOGCONFIG(TAG, " Boot considered successful after %" PRIu32 " seconds", - this->safe_mode_boot_is_good_after_ / 1000); // because milliseconds - ESP_LOGCONFIG(TAG, " Invoke after %u boot attempts", this->safe_mode_num_attempts_); - ESP_LOGCONFIG(TAG, " Remain for %" PRIu32 " seconds", + ESP_LOGCONFIG(TAG, + " Boot considered successful after %" PRIu32 " seconds\n" + " Invoke after %u boot attempts\n" + " Remain for %" PRIu32 " seconds", + this->safe_mode_boot_is_good_after_ / 1000, // because milliseconds + this->safe_mode_num_attempts_, this->safe_mode_enable_time_ / 1000); // because milliseconds if (this->safe_mode_rtc_value_ > 1 && this->safe_mode_rtc_value_ != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) { @@ -40,6 +42,8 @@ void SafeModeComponent::loop() { ESP_LOGI(TAG, "Boot seems successful; resetting boot loop counter"); this->clean_rtc(); this->boot_successful_ = true; + // Disable loop since we no longer need to check + this->disable_loop(); } } diff --git a/esphome/components/scd30/scd30.cpp b/esphome/components/scd30/scd30.cpp index a7283eb680..8561732d8b 100644 --- a/esphome/components/scd30/scd30.cpp +++ b/esphome/components/scd30/scd30.cpp @@ -140,10 +140,13 @@ void SCD30Component::dump_config() { } else { ESP_LOGCONFIG(TAG, " Altitude compensation: %dm", this->altitude_compensation_); } - ESP_LOGCONFIG(TAG, " Automatic self calibration: %s", ONOFF(this->enable_asc_)); - ESP_LOGCONFIG(TAG, " Ambient pressure compensation: %dmBar", this->ambient_pressure_compensation_); - ESP_LOGCONFIG(TAG, " Temperature offset: %.2f °C", this->temperature_offset_); - ESP_LOGCONFIG(TAG, " Update interval: %ds", this->update_interval_); + ESP_LOGCONFIG(TAG, + " Automatic self calibration: %s\n" + " Ambient pressure compensation: %dmBar\n" + " Temperature offset: %.2f °C\n" + " Update interval: %ds", + ONOFF(this->enable_asc_), this->ambient_pressure_compensation_, this->temperature_offset_, + this->update_interval_); LOG_SENSOR(" ", "CO2", this->co2_sensor_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/scd30/sensor.py b/esphome/components/scd30/sensor.py index fb3ad713bb..f341d2a47b 100644 --- a/esphome/components/scd30/sensor.py +++ b/esphome/components/scd30/sensor.py @@ -3,6 +3,8 @@ import esphome.codegen as cg from esphome.components import i2c, sensirion_common, sensor import esphome.config_validation as cv from esphome.const import ( + CONF_AMBIENT_PRESSURE_COMPENSATION, + CONF_AUTOMATIC_SELF_CALIBRATION, CONF_CO2, CONF_HUMIDITY, CONF_ID, @@ -18,8 +20,6 @@ from esphome.const import ( UNIT_CELSIUS, UNIT_PARTS_PER_MILLION, UNIT_PERCENT, - CONF_AUTOMATIC_SELF_CALIBRATION, - CONF_AMBIENT_PRESSURE_COMPENSATION, ) DEPENDENCIES = ["i2c"] diff --git a/esphome/components/scd4x/scd4x.cpp b/esphome/components/scd4x/scd4x.cpp index 8cd81f802f..f617ffe276 100644 --- a/esphome/components/scd4x/scd4x.cpp +++ b/esphome/components/scd4x/scd4x.cpp @@ -115,11 +115,15 @@ void SCD4XComponent::dump_config() { this->ambient_pressure_source_->get_name().c_str()); } else { if (this->ambient_pressure_compensation_) { - ESP_LOGCONFIG(TAG, " Altitude compensation disabled"); - ESP_LOGCONFIG(TAG, " Ambient pressure compensation: %dmBar", this->ambient_pressure_); + ESP_LOGCONFIG(TAG, + " Altitude compensation disabled\n" + " Ambient pressure compensation: %dmBar", + this->ambient_pressure_); } else { - ESP_LOGCONFIG(TAG, " Ambient pressure compensation disabled"); - ESP_LOGCONFIG(TAG, " Altitude compensation: %dm", this->altitude_compensation_); + ESP_LOGCONFIG(TAG, + " Ambient pressure compensation disabled\n" + " Altitude compensation: %dm", + this->altitude_compensation_); } } switch (this->measurement_mode_) { diff --git a/esphome/components/scd4x/sensor.py b/esphome/components/scd4x/sensor.py index f753f54c3b..fc859d63b8 100644 --- a/esphome/components/scd4x/sensor.py +++ b/esphome/components/scd4x/sensor.py @@ -4,9 +4,13 @@ import esphome.codegen as cg from esphome.components import i2c, sensirion_common, sensor import esphome.config_validation as cv from esphome.const import ( + CONF_AMBIENT_PRESSURE_COMPENSATION, + CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE, + CONF_AUTOMATIC_SELF_CALIBRATION, CONF_CO2, CONF_HUMIDITY, CONF_ID, + CONF_MEASUREMENT_MODE, CONF_TEMPERATURE, CONF_TEMPERATURE_OFFSET, CONF_VALUE, @@ -20,10 +24,6 @@ from esphome.const import ( UNIT_CELSIUS, UNIT_PARTS_PER_MILLION, UNIT_PERCENT, - CONF_AUTOMATIC_SELF_CALIBRATION, - CONF_AMBIENT_PRESSURE_COMPENSATION, - CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE, - CONF_MEASUREMENT_MODE, ) CODEOWNERS = ["@sjtrny", "@martgras"] diff --git a/esphome/components/sdl/display.py b/esphome/components/sdl/display.py index 66709cc834..ae8b0fd43a 100644 --- a/esphome/components/sdl/display.py +++ b/esphome/components/sdl/display.py @@ -8,16 +8,28 @@ from esphome.const import ( CONF_HEIGHT, CONF_ID, CONF_LAMBDA, + CONF_POSITION, CONF_WIDTH, + CONF_X, + CONF_Y, PLATFORM_HOST, ) sdl_ns = cg.esphome_ns.namespace("sdl") Sdl = sdl_ns.class_("Sdl", display.Display, cg.Component) +sdl_window_flags = cg.global_ns.enum("SDL_WindowFlags") CONF_SDL_OPTIONS = "sdl_options" CONF_SDL_ID = "sdl_id" +CONF_WINDOW_OPTIONS = "window_options" +WINDOW_OPTIONS = ( + "borderless", + "always_on_top", + "fullscreen", + "skip_taskbar", + "resizable", +) def get_sdl_options(value): @@ -29,6 +41,10 @@ def get_sdl_options(value): raise cv.Invalid("Unable to run sdl2-config - have you installed sdl2?") from e +def get_window_options(): + return {cv.Optional(option, default=False): cv.boolean for option in WINDOW_OPTIONS} + + CONFIG_SCHEMA = cv.All( display.FULL_DISPLAY_SCHEMA.extend( cv.Schema( @@ -44,6 +60,17 @@ CONFIG_SCHEMA = cv.All( } ), ), + cv.Optional(CONF_WINDOW_OPTIONS): cv.Schema( + { + cv.Optional(CONF_POSITION): cv.Schema( + { + cv.Required(CONF_X): cv.int_, + cv.Required(CONF_Y): cv.int_, + } + ), + **get_window_options(), + } + ), } ) ), @@ -65,6 +92,19 @@ async def to_code(config): (width, height) = dimensions cg.add(var.set_dimensions(width, height)) + if window_options := config.get(CONF_WINDOW_OPTIONS): + create_flags = 0 + for option in WINDOW_OPTIONS: + value = window_options.get(option, False) + if value: + create_flags = create_flags | getattr( + sdl_window_flags, "SDL_WINDOW_" + option.upper() + ) + cg.add(var.set_window_options(create_flags)) + + if position := window_options.get(CONF_POSITION): + cg.add(var.set_position(position[CONF_X], position[CONF_Y])) + if lamb := config.get(CONF_LAMBDA): lambda_ = await cg.process_lambda( lamb, [(display.DisplayRef, "it")], return_type=cg.void diff --git a/esphome/components/sdl/sdl_esphome.cpp b/esphome/components/sdl/sdl_esphome.cpp index 42dfe687e9..e55bff58fe 100644 --- a/esphome/components/sdl/sdl_esphome.cpp +++ b/esphome/components/sdl/sdl_esphome.cpp @@ -8,8 +8,8 @@ namespace sdl { void Sdl::setup() { ESP_LOGD(TAG, "Starting setup"); SDL_Init(SDL_INIT_VIDEO); - this->window_ = SDL_CreateWindow(App.get_name().c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - this->width_, this->height_, SDL_WINDOW_RESIZABLE); + this->window_ = SDL_CreateWindow(App.get_name().c_str(), this->pos_x_, this->pos_y_, this->width_, this->height_, + this->window_options_); this->renderer_ = SDL_CreateRenderer(this->window_, -1, SDL_RENDERER_SOFTWARE); SDL_RenderSetLogicalSize(this->renderer_, this->width_, this->height_); this->texture_ = diff --git a/esphome/components/sdl/sdl_esphome.h b/esphome/components/sdl/sdl_esphome.h index 39ea3ed417..bf5fde1428 100644 --- a/esphome/components/sdl/sdl_esphome.h +++ b/esphome/components/sdl/sdl_esphome.h @@ -28,6 +28,11 @@ class Sdl : public display::Display { this->width_ = width; this->height_ = height; } + void set_window_options(uint32_t window_options) { this->window_options_ = window_options; } + void set_position(uint16_t pos_x, uint16_t pos_y) { + this->pos_x_ = pos_x; + this->pos_y_ = pos_y; + } int get_width() override { return this->width_; } int get_height() override { return this->height_; } float get_setup_priority() const override { return setup_priority::HARDWARE; } @@ -49,6 +54,9 @@ class Sdl : public display::Display { void redraw_(SDL_Rect &rect); int width_{}; int height_{}; + uint32_t window_options_{0}; + int pos_x_{SDL_WINDOWPOS_UNDEFINED}; + int pos_y_{SDL_WINDOWPOS_UNDEFINED}; SDL_Renderer *renderer_{}; SDL_Window *window_{}; SDL_Texture *texture_{}; diff --git a/esphome/components/sdm_meter/sdm_meter.cpp b/esphome/components/sdm_meter/sdm_meter.cpp index 18e06e2b04..12a3277269 100644 --- a/esphome/components/sdm_meter/sdm_meter.cpp +++ b/esphome/components/sdm_meter/sdm_meter.cpp @@ -1,5 +1,6 @@ #include "sdm_meter.h" #include "sdm_meter_registers.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -84,8 +85,10 @@ void SDMMeter::on_modbus_data(const std::vector &data) { void SDMMeter::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); } void SDMMeter::dump_config() { - ESP_LOGCONFIG(TAG, "SDM Meter:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "SDM Meter:\n" + " Address: 0x%02X", + this->address_); for (uint8_t i = 0; i < 3; i++) { auto phase = this->phases_[i]; if (!phase.setup) diff --git a/esphome/components/sdp3x/sdp3x.cpp b/esphome/components/sdp3x/sdp3x.cpp index 7a957e55ac..58aefe09d7 100644 --- a/esphome/components/sdp3x/sdp3x.cpp +++ b/esphome/components/sdp3x/sdp3x.cpp @@ -1,7 +1,7 @@ #include "sdp3x.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace sdp3x { @@ -17,31 +17,31 @@ static const uint16_t SDP3X_STOP_MEAS = 0x3FF9; void SDP3XComponent::update() { this->read_pressure_(); } void SDP3XComponent::setup() { - ESP_LOGD(TAG, "Setting up SDP3X..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->write_command(SDP3X_STOP_MEAS)) { - ESP_LOGW(TAG, "Stop SDP3X failed!"); // This sometimes fails for no good reason + ESP_LOGW(TAG, "Stop failed"); // This sometimes fails for no good reason } if (!this->write_command(SDP3X_SOFT_RESET)) { - ESP_LOGW(TAG, "Soft Reset SDP3X failed!"); // This sometimes fails for no good reason + ESP_LOGW(TAG, "Soft Reset failed"); // This sometimes fails for no good reason } this->set_timeout(20, [this] { if (!this->write_command(SDP3X_READ_ID1)) { - ESP_LOGE(TAG, "Read ID1 SDP3X failed!"); + ESP_LOGE(TAG, "Read ID1 failed"); this->mark_failed(); return; } if (!this->write_command(SDP3X_READ_ID2)) { - ESP_LOGE(TAG, "Read ID2 SDP3X failed!"); + ESP_LOGE(TAG, "Read ID2 failed"); this->mark_failed(); return; } uint16_t data[6]; if (!this->read_data(data, 6)) { - ESP_LOGE(TAG, "Read ID SDP3X failed!"); + ESP_LOGE(TAG, "Read ID failed"); this->mark_failed(); return; } @@ -79,18 +79,18 @@ void SDP3XComponent::setup() { } if (!this->write_command(measurement_mode_ == DP_AVG ? SDP3X_START_DP_AVG : SDP3X_START_MASS_FLOW_AVG)) { - ESP_LOGE(TAG, "Start Measurements SDP3X failed!"); + ESP_LOGE(TAG, "Start Measurements failed"); this->mark_failed(); return; } - ESP_LOGCONFIG(TAG, "SDP3X started!"); + ESP_LOGCONFIG(TAG, "started"); }); } void SDP3XComponent::dump_config() { LOG_SENSOR(" ", "SDP3X", this); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, " Connection with SDP3X failed!"); + ESP_LOGE(TAG, " Connection with failed"); } LOG_UPDATE_INTERVAL(this); } @@ -98,7 +98,7 @@ void SDP3XComponent::dump_config() { void SDP3XComponent::read_pressure_() { uint16_t data[3]; if (!this->read_data(data, 3)) { - ESP_LOGW(TAG, "Couldn't read SDP3X data!"); + ESP_LOGW(TAG, "Couldn't read data"); this->status_set_warning(); return; } diff --git a/esphome/components/sdp3x/sensor.py b/esphome/components/sdp3x/sensor.py index 7cda2779ce..169ed374ed 100644 --- a/esphome/components/sdp3x/sensor.py +++ b/esphome/components/sdp3x/sensor.py @@ -2,10 +2,10 @@ import esphome.codegen as cg from esphome.components import i2c, sensirion_common, sensor import esphome.config_validation as cv from esphome.const import ( + CONF_MEASUREMENT_MODE, DEVICE_CLASS_PRESSURE, STATE_CLASS_MEASUREMENT, UNIT_HECTOPASCAL, - CONF_MEASUREMENT_MODE, ) DEPENDENCIES = ["i2c"] diff --git a/esphome/components/sds011/sds011.cpp b/esphome/components/sds011/sds011.cpp index a34059d85d..4e12c0e322 100644 --- a/esphome/components/sds011/sds011.cpp +++ b/esphome/components/sds011/sds011.cpp @@ -67,9 +67,11 @@ void SDS011Component::set_working_state(bool working_state) { } void SDS011Component::dump_config() { - ESP_LOGCONFIG(TAG, "SDS011:"); - ESP_LOGCONFIG(TAG, " Update Interval: %u min", this->update_interval_min_); - ESP_LOGCONFIG(TAG, " RX-only mode: %s", ONOFF(this->rx_mode_only_)); + ESP_LOGCONFIG(TAG, + "SDS011:\n" + " Update Interval: %u min\n" + " RX-only mode: %s", + this->update_interval_min_, ONOFF(this->rx_mode_only_)); LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_); LOG_SENSOR(" ", "PM10.0", this->pm_10_0_sensor_); this->check_uart_settings(9600); @@ -180,9 +182,6 @@ void SDS011Component::parse_data_() { } } -uint16_t SDS011Component::get_16_bit_uint_(uint8_t start_index) const { - return (uint16_t(this->data_[start_index + 1]) << 8) | uint16_t(this->data_[start_index]); -} void SDS011Component::set_update_interval_min(uint8_t update_interval_min) { this->update_interval_min_ = update_interval_min; } diff --git a/esphome/components/sds011/sds011.h b/esphome/components/sds011/sds011.h index 19e0cd3efe..1f404601b1 100644 --- a/esphome/components/sds011/sds011.h +++ b/esphome/components/sds011/sds011.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/uart/uart.h" @@ -32,7 +33,9 @@ class SDS011Component : public Component, public uart::UARTDevice { uint8_t sds011_checksum_(const uint8_t *command_data, uint8_t length) const; optional check_byte_() const; void parse_data_(); - uint16_t get_16_bit_uint_(uint8_t start_index) const; + uint16_t get_16_bit_uint_(uint8_t start_index) const { + return encode_uint16(this->data_[start_index + 1], this->data_[start_index]); + } sensor::Sensor *pm_2_5_sensor_{nullptr}; sensor::Sensor *pm_10_0_sensor_{nullptr}; diff --git a/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp b/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp index 05d0c6eb21..8683d6cad7 100644 --- a/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp +++ b/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp @@ -1,5 +1,6 @@ #include "seeed_mr24hpc1.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #include @@ -533,7 +534,7 @@ void MR24HPC1Component::r24_frame_parse_work_status_(uint8_t *data) { this->custom_mode_number_->publish_state(0); } if (this->custom_mode_end_text_sensor_ != nullptr) { - this->custom_mode_end_text_sensor_->publish_state("Setup in progress..."); + this->custom_mode_end_text_sensor_->publish_state("Setup in progress"); } } else if (data[FRAME_COMMAND_WORD_INDEX] == 0x81) { ESP_LOGD(TAG, "Reply: get radar init status 0x%02X", data[FRAME_DATA_INDEX]); diff --git a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp index 75f3f092a6..c815c98419 100644 --- a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +++ b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp @@ -1,4 +1,5 @@ #include "seeed_mr60bha2.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #include diff --git a/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp index 6bbef085a5..e40cd9c0c7 100644 --- a/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +++ b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp @@ -1,4 +1,5 @@ #include "seeed_mr60fda2.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #include @@ -335,7 +336,7 @@ void MR60FDA2Component::set_install_height(uint8_t index) { void MR60FDA2Component::set_height_threshold(uint8_t index) { uint8_t send_data[13] = {0x01, 0x00, 0x00, 0x00, 0x04, 0x0E, 0x08, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00}; - float_to_bytes(INSTALL_HEIGHT[index], &send_data[8]); + float_to_bytes(HEIGHT_THRESHOLD[index], &send_data[8]); send_data[12] = calculate_checksum(send_data + 8, 4); this->write_array(send_data, 13); ESP_LOGV(TAG, "SEND HEIGHT THRESHOLD: %s", format_hex_pretty(send_data, 13).c_str()); diff --git a/esphome/components/selec_meter/selec_meter.cpp b/esphome/components/selec_meter/selec_meter.cpp index 8bcf91f3c0..ae41327896 100644 --- a/esphome/components/selec_meter/selec_meter.cpp +++ b/esphome/components/selec_meter/selec_meter.cpp @@ -1,5 +1,6 @@ #include "selec_meter.h" #include "selec_meter_registers.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -83,8 +84,10 @@ void SelecMeter::on_modbus_data(const std::vector &data) { void SelecMeter::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); } void SelecMeter::dump_config() { - ESP_LOGCONFIG(TAG, "SELEC Meter:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "SELEC Meter:\n" + " Address: 0x%02X", + this->address_); LOG_SENSOR(" ", "Total Active Energy", this->total_active_energy_sensor_); LOG_SENSOR(" ", "Import Active Energy", this->import_active_energy_sensor_); LOG_SENSOR(" ", "Export Active Energy", this->export_active_energy_sensor_); diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index ecbba8677b..e14a9351a0 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -111,6 +111,7 @@ async def register_select(var, config, *, options: list[str]): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_select(var)) + CORE.register_platform_component("select", var) await setup_select_core_(var, config, options=options) diff --git a/esphome/components/select/select.cpp b/esphome/components/select/select.cpp index 806882ad94..37887da27c 100644 --- a/esphome/components/select/select.cpp +++ b/esphome/components/select/select.cpp @@ -10,7 +10,7 @@ void Select::publish_state(const std::string &state) { auto index = this->index_of(state); const auto *name = this->get_name().c_str(); if (index.has_value()) { - this->has_state_ = true; + this->set_has_state(true); this->state = state; ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", name, state.c_str(), index.value()); this->state_callback_.call(state, index.value()); diff --git a/esphome/components/select/select.h b/esphome/components/select/select.h index 8ca9a69d1c..3ab651b241 100644 --- a/esphome/components/select/select.h +++ b/esphome/components/select/select.h @@ -35,9 +35,6 @@ class Select : public EntityBase { void publish_state(const std::string &state); - /// Return whether this select component has gotten a full state yet. - bool has_state() const { return has_state_; } - /// Instantiate a SelectCall object to modify this select component's state. SelectCall make_call() { return SelectCall(this); } @@ -73,7 +70,6 @@ class Select : public EntityBase { virtual void control(const std::string &value) = 0; CallbackManager state_callback_; - bool has_state_{false}; }; } // namespace select diff --git a/esphome/components/sen5x/sen5x.cpp b/esphome/components/sen5x/sen5x.cpp index d7e1f5f62e..c7fd997b0c 100644 --- a/esphome/components/sen5x/sen5x.cpp +++ b/esphome/components/sen5x/sen5x.cpp @@ -264,9 +264,12 @@ void SEN5XComponent::dump_config() { break; } } - ESP_LOGCONFIG(TAG, " Productname: %s", this->product_name_.c_str()); - ESP_LOGCONFIG(TAG, " Firmware version: %d", this->firmware_version_); - ESP_LOGCONFIG(TAG, " Serial number %02d.%02d.%02d", serial_number_[0], serial_number_[1], serial_number_[2]); + ESP_LOGCONFIG(TAG, + " Productname: %s\n" + " Firmware version: %d\n" + " Serial number %02d.%02d.%02d", + this->product_name_.c_str(), this->firmware_version_, serial_number_[0], serial_number_[1], + serial_number_[2]); if (this->auto_cleaning_interval_.has_value()) { ESP_LOGCONFIG(TAG, " Auto cleaning interval %" PRId32 " seconds", auto_cleaning_interval_.value()); } diff --git a/esphome/components/sensirion_common/i2c_sensirion.cpp b/esphome/components/sensirion_common/i2c_sensirion.cpp index d8ab817a73..f71b3c14cb 100644 --- a/esphome/components/sensirion_common/i2c_sensirion.cpp +++ b/esphome/components/sensirion_common/i2c_sensirion.cpp @@ -1,6 +1,7 @@ #include "i2c_sensirion.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include namespace esphome { diff --git a/esphome/components/sensirion_common/i2c_sensirion.h b/esphome/components/sensirion_common/i2c_sensirion.h index 24b706cf36..aba93d6cc3 100644 --- a/esphome/components/sensirion_common/i2c_sensirion.h +++ b/esphome/components/sensirion_common/i2c_sensirion.h @@ -1,5 +1,6 @@ #pragma once #include "esphome/components/i2c/i2c.h" +#include "esphome/core/helpers.h" #include diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index c6b3469ebe..1ad3cfabee 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -167,7 +167,6 @@ DEVICE_CLASSES = [ ] _LOGGER = logging.getLogger(__name__) - sensor_ns = cg.esphome_ns.namespace("sensor") StateClasses = sensor_ns.enum("StateClass") STATE_CLASSES = { @@ -840,6 +839,7 @@ async def register_sensor(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_sensor(var)) + CORE.register_platform_component("sensor", var) await setup_sensor_core_(var, config) diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 3cfaebb708..94fec8208b 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -3,9 +3,9 @@ #include #include #include +#include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include "esphome/core/automation.h" namespace esphome { namespace sensor { diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index 14a8b3d490..6d6cff0400 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -38,7 +38,9 @@ StateClass Sensor::get_state_class() { void Sensor::publish_state(float state) { this->raw_state = state; - this->raw_callback_.call(state); + if (this->raw_callback_) { + this->raw_callback_->call(state); + } ESP_LOGV(TAG, "'%s': Received new state %f", this->name_.c_str(), state); @@ -51,7 +53,10 @@ void Sensor::publish_state(float state) { void Sensor::add_on_state_callback(std::function &&callback) { this->callback_.add(std::move(callback)); } void Sensor::add_on_raw_state_callback(std::function &&callback) { - this->raw_callback_.add(std::move(callback)); + if (!this->raw_callback_) { + this->raw_callback_ = make_unique>(); + } + this->raw_callback_->add(std::move(callback)); } void Sensor::add_filter(Filter *filter) { @@ -88,13 +93,12 @@ float Sensor::get_raw_state() const { return this->raw_state; } std::string Sensor::unique_id() { return ""; } void Sensor::internal_send_state_to_frontend(float state) { - this->has_state_ = true; + this->set_has_state(true); this->state = state; ESP_LOGD(TAG, "'%s': Sending state %.5f %s with %d decimals of accuracy", this->get_name().c_str(), state, this->get_unit_of_measurement().c_str(), this->get_accuracy_decimals()); this->callback_.call(state); } -bool Sensor::has_state() const { return this->has_state_; } } // namespace sensor } // namespace esphome diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index 98356c943d..456e876497 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -1,25 +1,30 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/component.h" #include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/sensor/filter.h" #include +#include namespace esphome { namespace sensor { #define LOG_SENSOR(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ + ESP_LOGCONFIG(TAG, \ + "%s%s '%s'\n" \ + "%s State Class: '%s'\n" \ + "%s Unit of Measurement: '%s'\n" \ + "%s Accuracy Decimals: %d", \ + prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str(), prefix, \ + state_class_to_string((obj)->get_state_class()).c_str(), prefix, \ + (obj)->get_unit_of_measurement().c_str(), prefix, (obj)->get_accuracy_decimals()); \ if (!(obj)->get_device_class().empty()) { \ ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ } \ - ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, state_class_to_string((obj)->get_state_class()).c_str()); \ - ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->get_unit_of_measurement().c_str()); \ - ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, (obj)->get_accuracy_decimals()); \ if (!(obj)->get_icon().empty()) { \ ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ } \ @@ -136,9 +141,6 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa */ float raw_state; - /// Return whether this sensor has gotten a full state (that passed through all filters) yet. - bool has_state() const; - /** Override this method to set the unique ID of this sensor. * * @deprecated Do not use for new sensors, a suitable unique ID is automatically generated (2023.4). @@ -148,15 +150,14 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa void internal_send_state_to_frontend(float state); protected: - CallbackManager raw_callback_; ///< Storage for raw state callbacks. - CallbackManager callback_; ///< Storage for filtered state callbacks. + std::unique_ptr> raw_callback_; ///< Storage for raw state callbacks (lazy allocated). + CallbackManager callback_; ///< Storage for filtered state callbacks. Filter *filter_list_{nullptr}; ///< Store all active filters. optional accuracy_decimals_; ///< Accuracy in decimals override optional state_class_{STATE_CLASS_NONE}; ///< State class override bool force_update_{false}; ///< Force update mode - bool has_state_{false}; }; } // namespace sensor diff --git a/esphome/components/servo/servo.cpp b/esphome/components/servo/servo.cpp index 18e8c8087e..b8546d345c 100644 --- a/esphome/components/servo/servo.cpp +++ b/esphome/components/servo/servo.cpp @@ -11,12 +11,15 @@ static const char *const TAG = "servo"; uint32_t global_servo_id = 1911044085ULL; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void Servo::dump_config() { - ESP_LOGCONFIG(TAG, "Servo:"); - ESP_LOGCONFIG(TAG, " Idle Level: %.1f%%", this->idle_level_ * 100.0f); - ESP_LOGCONFIG(TAG, " Min Level: %.1f%%", this->min_level_ * 100.0f); - ESP_LOGCONFIG(TAG, " Max Level: %.1f%%", this->max_level_ * 100.0f); - ESP_LOGCONFIG(TAG, " auto detach time: %" PRIu32 " ms", this->auto_detach_time_); - ESP_LOGCONFIG(TAG, " run duration: %" PRIu32 " ms", this->transition_length_); + ESP_LOGCONFIG(TAG, + "Servo:\n" + " Idle Level: %.1f%%\n" + " Min Level: %.1f%%\n" + " Max Level: %.1f%%\n" + " Auto-detach time: %" PRIu32 " ms\n" + " Run duration: %" PRIu32 " ms", + this->idle_level_ * 100.0f, this->min_level_ * 100.0f, this->max_level_ * 100.0f, + this->auto_detach_time_, this->transition_length_); } void Servo::setup() { @@ -41,7 +44,7 @@ void Servo::loop() { if (millis() - this->start_millis_ > this->auto_detach_time_) { this->detach(); this->start_millis_ = 0; - ESP_LOGD(TAG, "Servo detached on auto_detach_time"); + ESP_LOGD(TAG, "Detached on auto_detach_time"); } } if (this->target_value_ != this->current_value_ && this->state_ == STATE_ATTACHED) { @@ -63,7 +66,7 @@ void Servo::loop() { if (this->target_value_ == this->current_value_ && this->state_ == STATE_ATTACHED) { this->state_ = STATE_TARGET_REACHED; this->start_millis_ = millis(); // set current stamp for potential auto_detach_time_ check - ESP_LOGD(TAG, "Servo reached target"); + ESP_LOGD(TAG, "Reached target"); } } @@ -78,7 +81,7 @@ void Servo::write(float value) { this->source_value_ = this->current_value_; this->state_ = STATE_ATTACHED; this->start_millis_ = millis(); - ESP_LOGD(TAG, "Servo new target: %f", value); + ESP_LOGD(TAG, "New target: %f", value); } void Servo::internal_write(float value) { diff --git a/esphome/components/servo/servo.h b/esphome/components/servo/servo.h index 13a7472ae5..92d18bf601 100644 --- a/esphome/components/servo/servo.h +++ b/esphome/components/servo/servo.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/core/automation.h" +#include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" #include "esphome/components/output/float_output.h" diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 21f98602eb..0c7f25b699 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -250,9 +250,11 @@ void SGP30Component::dump_config() { } else { ESP_LOGCONFIG(TAG, " Serial number: %" PRIu64, this->serial_number_); if (this->eco2_baseline_ != 0x0000 && this->tvoc_baseline_ != 0x0000) { - ESP_LOGCONFIG(TAG, " Baseline:"); - ESP_LOGCONFIG(TAG, " eCO2 Baseline: 0x%04X", this->eco2_baseline_); - ESP_LOGCONFIG(TAG, " TVOC Baseline: 0x%04X", this->tvoc_baseline_); + ESP_LOGCONFIG(TAG, + " Baseline:\n" + " eCO2 Baseline: 0x%04X\n" + " TVOC Baseline: 0x%04X", + this->eco2_baseline_, this->tvoc_baseline_); } else { ESP_LOGCONFIG(TAG, " Baseline: No baseline configured"); } diff --git a/esphome/components/sgp4x/sgp4x.cpp b/esphome/components/sgp4x/sgp4x.cpp index 1571a8c1e3..bd84ae97f3 100644 --- a/esphome/components/sgp4x/sgp4x.cpp +++ b/esphome/components/sgp4x/sgp4x.cpp @@ -14,29 +14,29 @@ void SGP4xComponent::setup() { // Serial Number identification uint16_t raw_serial_number[3]; if (!this->get_register(SGP4X_CMD_GET_SERIAL_ID, raw_serial_number, 3, 1)) { - ESP_LOGE(TAG, "Failed to read serial number"); + ESP_LOGE(TAG, "Get serial number failed"); this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED; this->mark_failed(); return; } this->serial_number_ = (uint64_t(raw_serial_number[0]) << 24) | (uint64_t(raw_serial_number[1]) << 16) | (uint64_t(raw_serial_number[2])); - ESP_LOGD(TAG, "Serial Number: %" PRIu64, this->serial_number_); + ESP_LOGD(TAG, "Serial number: %" PRIu64, this->serial_number_); // Featureset identification for future use - uint16_t raw_featureset; - if (!this->get_register(SGP4X_CMD_GET_FEATURESET, raw_featureset, 1)) { - ESP_LOGD(TAG, "raw_featureset write_command_ failed"); + uint16_t featureset; + if (!this->get_register(SGP4X_CMD_GET_FEATURESET, featureset, 1)) { + ESP_LOGD(TAG, "Get feature set failed"); this->mark_failed(); return; } - this->featureset_ = raw_featureset; - if ((this->featureset_ & 0x1FF) == SGP40_FEATURESET) { - sgp_type_ = SGP40; - self_test_time_ = SPG40_SELFTEST_TIME; - measure_time_ = SGP40_MEASURE_TIME; + featureset &= 0x1FF; + if (featureset == SGP40_FEATURESET) { + this->sgp_type_ = SGP40; + this->self_test_time_ = SPG40_SELFTEST_TIME; + this->measure_time_ = SGP40_MEASURE_TIME; if (this->nox_sensor_) { - ESP_LOGE(TAG, "Measuring NOx requires a SGP41 sensor but a SGP40 sensor is detected"); + ESP_LOGE(TAG, "SGP41 required for NOx"); // disable the sensor this->nox_sensor_->set_disabled_by_default(true); // make sure it's not visible in HA @@ -45,20 +45,17 @@ void SGP4xComponent::setup() { // remove pointer to sensor this->nox_sensor_ = nullptr; } + } else if (featureset == SGP41_FEATURESET) { + this->sgp_type_ = SGP41; + this->self_test_time_ = SPG41_SELFTEST_TIME; + this->measure_time_ = SGP41_MEASURE_TIME; } else { - if ((this->featureset_ & 0x1FF) == SGP41_FEATURESET) { - sgp_type_ = SGP41; - self_test_time_ = SPG41_SELFTEST_TIME; - measure_time_ = SGP41_MEASURE_TIME; - } else { - ESP_LOGD(TAG, "Product feature set failed 0x%0X , expecting 0x%0X", uint16_t(this->featureset_ & 0x1FF), - SGP40_FEATURESET); - this->mark_failed(); - return; - } + ESP_LOGD(TAG, "Unknown feature set 0x%0X", featureset); + this->mark_failed(); + return; } - ESP_LOGD(TAG, "Product version: 0x%0X", uint16_t(this->featureset_ & 0x1FF)); + ESP_LOGD(TAG, "Version 0x%0X", featureset); if (this->store_baseline_) { // Hash with compilation time and serial number @@ -70,7 +67,7 @@ void SGP4xComponent::setup() { if (this->pref_.load(&this->voc_baselines_storage_)) { this->voc_state0_ = this->voc_baselines_storage_.state0; this->voc_state1_ = this->voc_baselines_storage_.state1; - ESP_LOGI(TAG, "Loaded VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, + ESP_LOGV(TAG, "Loaded VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, this->voc_baselines_storage_.state0, voc_baselines_storage_.state1); } @@ -78,7 +75,7 @@ void SGP4xComponent::setup() { this->seconds_since_last_store_ = 0; if (this->voc_baselines_storage_.state0 > 0 && this->voc_baselines_storage_.state1 > 0) { - ESP_LOGI(TAG, "Setting VOC baseline from save state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, + ESP_LOGV(TAG, "Setting VOC baseline from save state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, this->voc_baselines_storage_.state0, voc_baselines_storage_.state1); voc_algorithm_.set_states(this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1); } @@ -110,39 +107,29 @@ void SGP4xComponent::setup() { limit the amount of communication done over wifi for power consumption or to keep the number of records reported from being overwhelming. */ - ESP_LOGD(TAG, "Component requires sampling of 1Hz, setting up background sampler"); + ESP_LOGV(TAG, "Component requires sampling of 1Hz, setting up background sampler"); this->set_interval(1000, [this]() { this->take_sample(); }); } void SGP4xComponent::self_test_() { - ESP_LOGD(TAG, "Self-test started"); + ESP_LOGD(TAG, "Starting self-test"); if (!this->write_command(SGP4X_CMD_SELF_TEST)) { this->error_code_ = COMMUNICATION_FAILED; - ESP_LOGD(TAG, "Self-test communication failed"); + ESP_LOGD(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); } - this->set_timeout(self_test_time_, [this]() { - uint16_t reply; - if (!this->read_data(reply)) { + this->set_timeout(this->self_test_time_, [this]() { + uint16_t reply = 0; + if (!this->read_data(reply) || (reply != 0xD400)) { this->error_code_ = SELF_TEST_FAILED; - ESP_LOGD(TAG, "Self-test read_data_ failed"); + ESP_LOGW(TAG, "Self-test failed (0x%X)", reply); this->mark_failed(); return; } - if (reply == 0xD400) { - this->self_test_complete_ = true; - ESP_LOGD(TAG, "Self-test completed"); - return; - } else { - this->error_code_ = SELF_TEST_FAILED; - ESP_LOGD(TAG, "Self-test failed 0x%X", reply); - return; - } - - ESP_LOGD(TAG, "Self-test failed 0x%X", reply); - this->mark_failed(); + this->self_test_complete_ = true; + ESP_LOGD(TAG, "Self-test complete"); }); } @@ -150,7 +137,7 @@ void SGP4xComponent::update_gas_indices_() { this->voc_index_ = this->voc_algorithm_.process(this->voc_sraw_); if (this->nox_sensor_ != nullptr) this->nox_index_ = this->nox_algorithm_.process(this->nox_sraw_); - ESP_LOGV(TAG, "VOC = %" PRId32 ", NOx = %" PRId32, this->voc_index_, this->nox_index_); + ESP_LOGV(TAG, "VOC: %" PRId32 ", NOx: %" PRId32, this->voc_index_, this->nox_index_); // Store baselines after defined interval or if the difference between current and stored baseline becomes too // much if (this->store_baseline_ && this->seconds_since_last_store_ > SHORTEST_BASELINE_STORE_INTERVAL) { @@ -162,18 +149,18 @@ void SGP4xComponent::update_gas_indices_() { this->voc_baselines_storage_.state1 = this->voc_state1_; if (this->pref_.save(&this->voc_baselines_storage_)) { - ESP_LOGI(TAG, "Stored VOC baseline state0: 0x%04" PRIX32 " ,state1: 0x%04" PRIX32, + ESP_LOGV(TAG, "Stored VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1); } else { - ESP_LOGW(TAG, "Could not store VOC baselines"); + ESP_LOGW(TAG, "Storing VOC baselines failed"); } } } if (this->samples_read_ < this->samples_to_stabilize_) { this->samples_read_++; - ESP_LOGD(TAG, "Sensor has not collected enough samples yet. (%d/%d) VOC index is: %" PRIu32, this->samples_read_, - this->samples_to_stabilize_, this->voc_index_); + ESP_LOGD(TAG, "Stabilizing (%d/%d); VOC index: %" PRIu32, this->samples_read_, this->samples_to_stabilize_, + this->voc_index_); } } @@ -182,7 +169,7 @@ void SGP4xComponent::measure_raw_() { static uint32_t nox_conditioning_start = millis(); if (!this->self_test_complete_) { - ESP_LOGD(TAG, "Self-test not yet complete"); + ESP_LOGW(TAG, "Self-test incomplete"); return; } if (this->humidity_sensor_ != nullptr) { @@ -270,7 +257,7 @@ void SGP4xComponent::update() { void SGP4xComponent::dump_config() { ESP_LOGCONFIG(TAG, "SGP4x:"); LOG_I2C_DEVICE(this); - ESP_LOGCONFIG(TAG, " store_baseline: %d", this->store_baseline_); + ESP_LOGCONFIG(TAG, " Store baseline: %s", YESNO(this->store_baseline_)); if (this->is_failed()) { switch (this->error_code_) { @@ -278,29 +265,30 @@ void SGP4xComponent::dump_config() { ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); break; case SERIAL_NUMBER_IDENTIFICATION_FAILED: - ESP_LOGW(TAG, "Get Serial number failed"); + ESP_LOGW(TAG, "Get serial number failed"); break; case SELF_TEST_FAILED: - ESP_LOGW(TAG, "Self test failed"); + ESP_LOGW(TAG, "Self-test failed"); break; - default: - ESP_LOGW(TAG, "Unknown setup error"); + ESP_LOGW(TAG, "Unknown error"); break; } } else { - ESP_LOGCONFIG(TAG, " Type: %s", sgp_type_ == SGP41 ? "SGP41" : "SPG40"); - ESP_LOGCONFIG(TAG, " Serial number: %" PRIu64, this->serial_number_); - ESP_LOGCONFIG(TAG, " Minimum Samples: %f", GasIndexAlgorithm_INITIAL_BLACKOUT); + ESP_LOGCONFIG(TAG, + " Type: %s\n" + " Serial number: %" PRIu64 "\n" + " Minimum Samples: %f", + sgp_type_ == SGP41 ? "SGP41" : "SPG40", this->serial_number_, GasIndexAlgorithm_INITIAL_BLACKOUT); } LOG_UPDATE_INTERVAL(this); + ESP_LOGCONFIG(TAG, " Compensation:"); if (this->humidity_sensor_ != nullptr || this->temperature_sensor_ != nullptr) { - ESP_LOGCONFIG(TAG, " Compensation:"); LOG_SENSOR(" ", "Temperature Source:", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity Source:", this->humidity_sensor_); } else { - ESP_LOGCONFIG(TAG, " Compensation: No source configured"); + ESP_LOGCONFIG(TAG, " No source configured"); } LOG_SENSOR(" ", "VOC", this->voc_sensor_); LOG_SENSOR(" ", "NOx", this->nox_sensor_); diff --git a/esphome/components/sgp4x/sgp4x.h b/esphome/components/sgp4x/sgp4x.h index 959ff12c27..45ee66af68 100644 --- a/esphome/components/sgp4x/sgp4x.h +++ b/esphome/components/sgp4x/sgp4x.h @@ -115,7 +115,6 @@ class SGP4xComponent : public PollingComponent, public sensor::Sensor, public se SgpType sgp_type_{SGP40}; uint64_t serial_number_; - uint16_t featureset_; bool self_test_complete_; uint16_t self_test_time_; diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.cpp b/esphome/components/shelly_dimmer/shelly_dimmer.cpp index d4229b2384..6b4ab13c48 100644 --- a/esphome/components/shelly_dimmer/shelly_dimmer.cpp +++ b/esphome/components/shelly_dimmer/shelly_dimmer.cpp @@ -101,7 +101,7 @@ void ShellyDimmer::setup() { this->pin_nrst_->setup(); this->pin_boot0_->setup(); - ESP_LOGI(TAG, "Initializing Shelly Dimmer..."); + ESP_LOGI(TAG, "Initializing"); this->handle_firmware(); @@ -119,17 +119,21 @@ void ShellyDimmer::dump_config() { LOG_PIN(" NRST Pin: ", this->pin_nrst_); LOG_PIN(" BOOT0 Pin: ", this->pin_boot0_); - ESP_LOGCONFIG(TAG, " Leading Edge: %s", YESNO(this->leading_edge_)); - ESP_LOGCONFIG(TAG, " Warmup Brightness: %d", this->warmup_brightness_); + ESP_LOGCONFIG(TAG, + " Leading Edge: %s\n" + " Warmup Brightness: %d\n" + " Minimum Brightness: %d\n" + " Maximum Brightness: %d", + YESNO(this->leading_edge_), this->warmup_brightness_, this->min_brightness_, this->max_brightness_); // ESP_LOGCONFIG(TAG, " Warmup Time: %d", this->warmup_time_); // ESP_LOGCONFIG(TAG, " Fade Rate: %d", this->fade_rate_); - ESP_LOGCONFIG(TAG, " Minimum Brightness: %d", this->min_brightness_); - ESP_LOGCONFIG(TAG, " Maximum Brightness: %d", this->max_brightness_); LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " STM32 current firmware version: %d.%d ", this->version_major_, this->version_minor_); - ESP_LOGCONFIG(TAG, " STM32 required firmware version: %d.%d", USE_SHD_FIRMWARE_MAJOR_VERSION, + ESP_LOGCONFIG(TAG, + " STM32 current firmware version: %d.%d \n" + " STM32 required firmware version: %d.%d", + this->version_major_, this->version_minor_, USE_SHD_FIRMWARE_MAJOR_VERSION, USE_SHD_FIRMWARE_MINOR_VERSION); if (this->version_major_ != USE_SHD_FIRMWARE_MAJOR_VERSION || diff --git a/esphome/components/shtcx/shtcx.cpp b/esphome/components/shtcx/shtcx.cpp index 770bcf6c6d..5420119bd6 100644 --- a/esphome/components/shtcx/shtcx.cpp +++ b/esphome/components/shtcx/shtcx.cpp @@ -20,7 +20,7 @@ inline const char *to_string(SHTCXType type) { case SHTCX_TYPE_SHTC1: return "SHTC1"; default: - return "[Unknown model]"; + return "UNKNOWN"; } } @@ -29,15 +29,9 @@ void SHTCXComponent::setup() { this->wake_up(); this->soft_reset(); - if (!this->write_command(SHTCX_COMMAND_READ_ID_REGISTER)) { - ESP_LOGE(TAG, "Error requesting Device ID"); - this->mark_failed(); - return; - } - uint16_t device_id_register; - if (!this->read_data(&device_id_register, 1)) { - ESP_LOGE(TAG, "Error reading Device ID"); + if (!this->write_command(SHTCX_COMMAND_READ_ID_REGISTER) || !this->read_data(&device_id_register, 1)) { + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return; } @@ -53,8 +47,8 @@ void SHTCXComponent::setup() { } else { this->type_ = SHTCX_TYPE_UNKNOWN; } - ESP_LOGCONFIG(TAG, " Device identified: %s (%04x)", to_string(this->type_), device_id_register); } + void SHTCXComponent::dump_config() { ESP_LOGCONFIG(TAG, "SHTCx:"); ESP_LOGCONFIG(TAG, " Model: %s (%04x)", to_string(this->type_), this->sensor_id_); @@ -67,17 +61,19 @@ void SHTCXComponent::dump_config() { LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); } + float SHTCXComponent::get_setup_priority() const { return setup_priority::DATA; } + void SHTCXComponent::update() { if (this->status_has_warning()) { - ESP_LOGW(TAG, "Retrying to reconnect the sensor."); + ESP_LOGW(TAG, "Retrying communication"); this->soft_reset(); } if (this->type_ != SHTCX_TYPE_SHTC1) { this->wake_up(); } if (!this->write_command(SHTCX_COMMAND_POLLING_H)) { - ESP_LOGE(TAG, "sensor polling failed"); + ESP_LOGE(TAG, "Polling failed"); if (this->temperature_sensor_ != nullptr) this->temperature_sensor_->publish_state(NAN); if (this->humidity_sensor_ != nullptr) @@ -91,13 +87,13 @@ void SHTCXComponent::update() { float humidity = NAN; uint16_t raw_data[2]; if (!this->read_data(raw_data, 2)) { - ESP_LOGE(TAG, "sensor read failed"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); } else { temperature = 175.0f * float(raw_data[0]) / 65536.0f - 45.0f; humidity = 100.0f * float(raw_data[1]) / 65536.0f; - ESP_LOGD(TAG, "Got temperature=%.2f°C humidity=%.2f%%", temperature, humidity); + ESP_LOGD(TAG, "Temperature=%.2f°C Humidity=%.2f%%", temperature, humidity); } if (this->temperature_sensor_ != nullptr) this->temperature_sensor_->publish_state(temperature); @@ -114,6 +110,7 @@ void SHTCXComponent::soft_reset() { this->write_command(SHTCX_COMMAND_SOFT_RESET); delayMicroseconds(200); } + void SHTCXComponent::sleep() { this->write_command(SHTCX_COMMAND_SLEEP); } void SHTCXComponent::wake_up() { diff --git a/esphome/components/shutdown/button/shutdown_button.cpp b/esphome/components/shutdown/button/shutdown_button.cpp index be88a10d49..b40af7517b 100644 --- a/esphome/components/shutdown/button/shutdown_button.cpp +++ b/esphome/components/shutdown/button/shutdown_button.cpp @@ -17,7 +17,7 @@ static const char *const TAG = "shutdown.button"; void ShutdownButton::dump_config() { LOG_BUTTON("", "Shutdown Button", this); } void ShutdownButton::press_action() { - ESP_LOGI(TAG, "Shutting down..."); + ESP_LOGI(TAG, "Shutting down"); // Let MQTT settle a bit delay(100); // NOLINT App.run_safe_shutdown_hooks(); diff --git a/esphome/components/shutdown/switch/shutdown_switch.cpp b/esphome/components/shutdown/switch/shutdown_switch.cpp index a5f9a92982..b685ab14ab 100644 --- a/esphome/components/shutdown/switch/shutdown_switch.cpp +++ b/esphome/components/shutdown/switch/shutdown_switch.cpp @@ -21,7 +21,7 @@ void ShutdownSwitch::write_state(bool state) { this->publish_state(false); if (state) { - ESP_LOGI(TAG, "Shutting down..."); + ESP_LOGI(TAG, "Shutting down"); delay(100); // NOLINT App.run_safe_shutdown_hooks(); diff --git a/esphome/components/sim800l/sim800l.cpp b/esphome/components/sim800l/sim800l.cpp index 38775b0062..d97b0ae364 100644 --- a/esphome/components/sim800l/sim800l.cpp +++ b/esphome/components/sim800l/sim800l.cpp @@ -28,7 +28,7 @@ void Sim800LComponent::update() { this->state_ = STATE_DIALING1; } else if (this->registered_ && this->connect_pending_) { this->connect_pending_ = false; - ESP_LOGI(TAG, "Connecting..."); + ESP_LOGI(TAG, "Connecting"); this->send_cmd_("ATA"); this->state_ = STATE_ATA_SENT; } else if (this->registered_ && this->send_ussd_pending_) { @@ -36,7 +36,7 @@ void Sim800LComponent::update() { this->state_ = STATE_SEND_USSD1; } else if (this->registered_ && this->disconnect_pending_) { this->disconnect_pending_ = false; - ESP_LOGI(TAG, "Disconnecting..."); + ESP_LOGI(TAG, "Disconnecting"); this->send_cmd_("ATH"); } else if (this->registered_ && this->call_state_ != 6) { send_cmd_("AT+CLCC"); diff --git a/esphome/components/slow_pwm/slow_pwm_output.cpp b/esphome/components/slow_pwm/slow_pwm_output.cpp index 643294303c..48ded94b3a 100644 --- a/esphome/components/slow_pwm/slow_pwm_output.cpp +++ b/esphome/components/slow_pwm/slow_pwm_output.cpp @@ -63,8 +63,10 @@ void SlowPWMOutput::dump_config() { if (this->turn_off_trigger_) { ESP_LOGCONFIG(TAG, " Turn off automation configured"); } - ESP_LOGCONFIG(TAG, " Period: %d ms", this->period_); - ESP_LOGCONFIG(TAG, " Restart cycle on state change: %s", YESNO(this->restart_cycle_on_state_change_)); + ESP_LOGCONFIG(TAG, + " Period: %d ms\n" + " Restart cycle on state change: %s", + this->period_, YESNO(this->restart_cycle_on_state_change_)); LOG_FLOAT_OUTPUT(this); } diff --git a/esphome/components/sm2235/sm2235.cpp b/esphome/components/sm2235/sm2235.cpp index 3edd1e24a7..e9f84773e2 100644 --- a/esphome/components/sm2235/sm2235.cpp +++ b/esphome/components/sm2235/sm2235.cpp @@ -19,8 +19,10 @@ void SM2235::dump_config() { ESP_LOGCONFIG(TAG, "sm2235:"); LOG_PIN(" Data Pin: ", this->data_pin_); LOG_PIN(" Clock Pin: ", this->clock_pin_); - ESP_LOGCONFIG(TAG, " Color Channels Max Power: %u", this->max_power_color_channels_); - ESP_LOGCONFIG(TAG, " White Channels Max Power: %u", this->max_power_white_channels_); + ESP_LOGCONFIG(TAG, + " Color Channels Max Power: %u\n" + " White Channels Max Power: %u", + this->max_power_color_channels_, this->max_power_white_channels_); } } // namespace sm2235 diff --git a/esphome/components/sm2335/sm2335.cpp b/esphome/components/sm2335/sm2335.cpp index a891b14ea0..99b722a639 100644 --- a/esphome/components/sm2335/sm2335.cpp +++ b/esphome/components/sm2335/sm2335.cpp @@ -19,8 +19,10 @@ void SM2335::dump_config() { ESP_LOGCONFIG(TAG, "sm2335:"); LOG_PIN(" Data Pin: ", this->data_pin_); LOG_PIN(" Clock Pin: ", this->clock_pin_); - ESP_LOGCONFIG(TAG, " Color Channels Max Power: %u", this->max_power_color_channels_); - ESP_LOGCONFIG(TAG, " White Channels Max Power: %u", this->max_power_white_channels_); + ESP_LOGCONFIG(TAG, + " Color Channels Max Power: %u\n" + " White Channels Max Power: %u", + this->max_power_color_channels_, this->max_power_white_channels_); } } // namespace sm2335 diff --git a/esphome/components/sml/sml.cpp b/esphome/components/sml/sml.cpp index 7ae9044c72..c1ffdc0e68 100644 --- a/esphome/components/sml/sml.cpp +++ b/esphome/components/sml/sml.cpp @@ -1,6 +1,6 @@ #include "sml.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "sml_parser.h" namespace esphome { diff --git a/esphome/components/sn74hc595/__init__.py b/esphome/components/sn74hc595/__init__.py index db18b00cd1..26e5c03802 100644 --- a/esphome/components/sn74hc595/__init__.py +++ b/esphome/components/sn74hc595/__init__.py @@ -95,7 +95,7 @@ SN74HC595_PIN_SCHEMA = pins.gpio_base_schema( cv.int_range(min=0, max=2047), modes=[CONF_OUTPUT], mode_validator=_validate_output_mode, - invertable=True, + invertible=True, ).extend( { cv.Required(CONF_SN74HC595): cv.use_id(SN74HC595Component), diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index f9a9981c52..d5839c1a2b 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -1,7 +1,7 @@ #include "sntp_component.h" #include "esphome/core/log.h" -#ifdef USE_ESP_IDF +#ifdef USE_ESP32 #include "esp_sntp.h" #elif USE_ESP8266 #include "sntp.h" @@ -16,7 +16,7 @@ static const char *const TAG = "sntp"; void SNTPComponent::setup() { ESP_LOGCONFIG(TAG, "Running setup"); -#if defined(USE_ESP_IDF) +#if defined(USE_ESP32) if (esp_sntp_enabled()) { esp_sntp_stop(); } @@ -46,7 +46,7 @@ void SNTPComponent::dump_config() { } } void SNTPComponent::update() { -#if !defined(USE_ESP_IDF) +#if !defined(USE_ESP32) // force resync if (sntp_enabled()) { sntp_stop(); @@ -67,6 +67,12 @@ void SNTPComponent::loop() { time.minute, time.second); this->time_sync_callback_.call(); this->has_time_ = true; + +#ifdef USE_ESP_IDF + // On ESP-IDF, time sync is permanent and update() doesn't force resync + // Time is now synchronized, no need to check anymore + this->disable_loop(); +#endif } } // namespace sntp diff --git a/esphome/components/sonoff_d1/sonoff_d1.cpp b/esphome/components/sonoff_d1/sonoff_d1.cpp index e70ec7b70d..e3d55681c5 100644 --- a/esphome/components/sonoff_d1/sonoff_d1.cpp +++ b/esphome/components/sonoff_d1/sonoff_d1.cpp @@ -286,10 +286,13 @@ void SonoffD1Output::write_state(light::LightState *state) { } void SonoffD1Output::dump_config() { - ESP_LOGCONFIG(TAG, "Sonoff D1 Dimmer: '%s'", this->light_state_ ? this->light_state_->get_name().c_str() : ""); - ESP_LOGCONFIG(TAG, " Use RM433 Remote: %s", ONOFF(this->use_rm433_remote_)); - ESP_LOGCONFIG(TAG, " Minimal brightness: %d", this->min_value_); - ESP_LOGCONFIG(TAG, " Maximal brightness: %d", this->max_value_); + ESP_LOGCONFIG(TAG, + "Sonoff D1 Dimmer: '%s'\n" + " Use RM433 Remote: %s\n" + " Minimal brightness: %d\n" + " Maximal brightness: %d", + this->light_state_ ? this->light_state_->get_name().c_str() : "", ONOFF(this->use_rm433_remote_), + this->min_value_, this->max_value_); } void SonoffD1Output::loop() { diff --git a/esphome/components/sonoff_d1/sonoff_d1.h b/esphome/components/sonoff_d1/sonoff_d1.h index 4df0f5afb2..19ff83f378 100644 --- a/esphome/components/sonoff_d1/sonoff_d1.h +++ b/esphome/components/sonoff_d1/sonoff_d1.h @@ -31,9 +31,9 @@ ----- */ -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/component.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/uart/uart.h" #include "esphome/components/light/light_output.h" #include "esphome/components/light/light_state.h" diff --git a/esphome/components/sound_level/sound_level.cpp b/esphome/components/sound_level/sound_level.cpp index f8447ce436..decf630aba 100644 --- a/esphome/components/sound_level/sound_level.cpp +++ b/esphome/components/sound_level/sound_level.cpp @@ -19,8 +19,10 @@ static const uint32_t RING_BUFFER_DURATION_MS = 120; static const double MAX_SAMPLE_SQUARED_DENOMINATOR = INT16_MIN * INT16_MIN; void SoundLevelComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Sound Level Component:"); - ESP_LOGCONFIG(TAG, " Measurement Duration: %" PRIu32 " ms", measurement_duration_ms_); + ESP_LOGCONFIG(TAG, + "Sound Level Component:\n" + " Measurement Duration: %" PRIu32 " ms", + measurement_duration_ms_); LOG_SENSOR(" ", "Peak:", this->peak_sensor_); LOG_SENSOR(" ", "RMS:", this->rms_sensor_); diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 5b28b3546b..55a4b9c8f6 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -2,12 +2,14 @@ import re from esphome import pins import esphome.codegen as cg +from esphome.components.esp32 import only_on_variant from esphome.components.esp32.const import ( KEY_ESP32, VARIANT_ESP32C2, VARIANT_ESP32C3, VARIANT_ESP32C6, VARIANT_ESP32H2, + VARIANT_ESP32P4, VARIANT_ESP32S2, VARIANT_ESP32S3, ) @@ -77,6 +79,7 @@ CONF_SPI_MODE = "spi_mode" CONF_FORCE_SW = "force_sw" CONF_INTERFACE = "interface" CONF_INTERFACE_INDEX = "interface_index" +CONF_RELEASE_DEVICE = "release_device" TYPE_SINGLE = "single" TYPE_QUAD = "quad" TYPE_OCTAL = "octal" @@ -287,7 +290,15 @@ def spi_mode_schema(mode): if mode == TYPE_SINGLE: return SPI_SINGLE_SCHEMA pin_count = 4 if mode == TYPE_QUAD else 8 + onlys = [cv.only_on([PLATFORM_ESP32]), cv.only_with_esp_idf] + if pin_count == 8: + onlys.append( + only_on_variant( + supported=[VARIANT_ESP32S3, VARIANT_ESP32S2, VARIANT_ESP32P4] + ) + ) return cv.All( + *onlys, cv.Schema( { cv.GenerateID(): cv.declare_id(TYPE_CLASS[mode]), @@ -308,8 +319,6 @@ def spi_mode_schema(mode): ), } ), - cv.only_on([PLATFORM_ESP32]), - cv.only_with_esp_idf, ) @@ -370,6 +379,7 @@ def spi_device_schema( cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum( SPI_MODE_OPTIONS, upper=True ), + cv.Optional(CONF_RELEASE_DEVICE): cv.All(cv.boolean, cv.only_with_esp_idf), } if cs_pin_required: schema[cv.Required(CONF_CS_PIN)] = pins.gpio_output_pin_schema @@ -381,13 +391,15 @@ def spi_device_schema( async def register_spi_device(var, config): parent = await cg.get_variable(config[CONF_SPI_ID]) cg.add(var.set_spi_parent(parent)) - if CONF_CS_PIN in config: - pin = await cg.gpio_pin_expression(config[CONF_CS_PIN]) + if cs_pin := config.get(CONF_CS_PIN): + pin = await cg.gpio_pin_expression(cs_pin) cg.add(var.set_cs_pin(pin)) - if CONF_DATA_RATE in config: - cg.add(var.set_data_rate(config[CONF_DATA_RATE])) - if CONF_SPI_MODE in config: - cg.add(var.set_mode(config[CONF_SPI_MODE])) + if data_rate := config.get(CONF_DATA_RATE): + cg.add(var.set_data_rate(data_rate)) + if spi_mode := config.get(CONF_SPI_MODE): + cg.add(var.set_mode(spi_mode)) + if release_device := config.get(CONF_RELEASE_DEVICE): + cg.add(var.set_release_device(release_device)) def final_validate_device_schema(name: str, *, require_mosi: bool, require_miso: bool): diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index 18f7852757..805a774ceb 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -16,19 +16,20 @@ bool SPIDelegate::is_ready() { return true; } GPIOPin *const NullPin::NULL_PIN = new NullPin(); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) SPIDelegate *SPIComponent::register_device(SPIClient *device, SPIMode mode, SPIBitOrder bit_order, uint32_t data_rate, - GPIOPin *cs_pin) { + GPIOPin *cs_pin, bool release_device, bool write_only) { if (this->devices_.count(device) != 0) { - ESP_LOGE(TAG, "SPI device already registered"); + ESP_LOGE(TAG, "Device already registered"); return this->devices_[device]; } - SPIDelegate *delegate = this->spi_bus_->get_delegate(data_rate, bit_order, mode, cs_pin); // NOLINT + SPIDelegate *delegate = + this->spi_bus_->get_delegate(data_rate, bit_order, mode, cs_pin, release_device, write_only); // NOLINT this->devices_[device] = delegate; return delegate; } void SPIComponent::unregister_device(SPIClient *device) { if (this->devices_.count(device) == 0) { - esph_log_e(TAG, "SPI device not registered"); + esph_log_e(TAG, "Device not registered"); return; } delete this->devices_[device]; // NOLINT @@ -36,14 +37,14 @@ void SPIComponent::unregister_device(SPIClient *device) { } void SPIComponent::setup() { - ESP_LOGD(TAG, "Setting up SPI bus..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->sdo_pin_ == nullptr) this->sdo_pin_ = NullPin::NULL_PIN; if (this->sdi_pin_ == nullptr) this->sdi_pin_ = NullPin::NULL_PIN; if (this->clk_pin_ == nullptr) { - ESP_LOGE(TAG, "No clock pin for SPI"); + ESP_LOGE(TAG, "No clock pin"); this->mark_failed(); return; } diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index f96d3da251..5bc80350da 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -317,7 +317,8 @@ class SPIBus { SPIBus(GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi) : clk_pin_(clk), sdo_pin_(sdo), sdi_pin_(sdi) {} - virtual SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin) { + virtual SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin, + bool release_device, bool write_only) { return new SPIDelegateBitBash(data_rate, bit_order, mode, cs_pin, this->clk_pin_, this->sdo_pin_, this->sdi_pin_); } @@ -334,7 +335,7 @@ class SPIClient; class SPIComponent : public Component { public: SPIDelegate *register_device(SPIClient *device, SPIMode mode, SPIBitOrder bit_order, uint32_t data_rate, - GPIOPin *cs_pin); + GPIOPin *cs_pin, bool release_device, bool write_only); void unregister_device(SPIClient *device); void set_clk(GPIOPin *clk) { this->clk_pin_ = clk; } @@ -390,7 +391,8 @@ class SPIClient { virtual void spi_setup() { esph_log_d("spi_device", "mode %u, data_rate %ukHz", (unsigned) this->mode_, (unsigned) (this->data_rate_ / 1000)); - this->delegate_ = this->parent_->register_device(this, this->mode_, this->bit_order_, this->data_rate_, this->cs_); + this->delegate_ = this->parent_->register_device(this, this->mode_, this->bit_order_, this->data_rate_, this->cs_, + this->release_device_, this->write_only_); } virtual void spi_teardown() { @@ -399,6 +401,8 @@ class SPIClient { } bool spi_is_ready() { return this->delegate_->is_ready(); } + void set_release_device(bool release) { this->release_device_ = release; } + void set_write_only(bool write_only) { this->write_only_ = write_only; } protected: SPIBitOrder bit_order_{BIT_ORDER_MSB_FIRST}; @@ -406,6 +410,8 @@ class SPIClient { uint32_t data_rate_{1000000}; SPIComponent *parent_{nullptr}; GPIOPin *cs_{nullptr}; + bool release_device_{false}; + bool write_only_{false}; SPIDelegate *delegate_{SPIDelegate::NULL_DELEGATE}; }; diff --git a/esphome/components/spi/spi_arduino.cpp b/esphome/components/spi/spi_arduino.cpp index f7fe523a33..a34e3c3c82 100644 --- a/esphome/components/spi/spi_arduino.cpp +++ b/esphome/components/spi/spi_arduino.cpp @@ -3,7 +3,6 @@ namespace esphome { namespace spi { - #ifdef USE_ARDUINO static const char *const TAG = "spi-esp-arduino"; @@ -38,17 +37,28 @@ class SPIDelegateHw : public SPIDelegate { void write16(uint16_t data) override { this->channel_->transfer16(data); } -#ifdef USE_RP2040 void write_array(const uint8_t *ptr, size_t length) override { - // avoid overwriting the supplied buffer - uint8_t *rxbuf = new uint8_t[length]; // NOLINT(cppcoreguidelines-owning-memory) - memcpy(rxbuf, ptr, length); - this->channel_->transfer((void *) rxbuf, length); - delete[] rxbuf; // NOLINT(cppcoreguidelines-owning-memory) - } + if (length == 1) { + this->channel_->transfer(*ptr); + return; + } +#ifdef USE_RP2040 + this->channel_->transfer(ptr, nullptr, length); +#elif defined(USE_ESP8266) + // ESP8266 SPI library requires the pointer to be word aligned, but the data may not be + // so we need to copy the data to a temporary buffer + if (reinterpret_cast(ptr) & 0x3) { + ESP_LOGVV(TAG, "SPI write buffer not word aligned, copying to temporary buffer"); + auto txbuf = std::vector(length); + memcpy(txbuf.data(), ptr, length); + this->channel_->writeBytes(txbuf.data(), length); + } else { + this->channel_->writeBytes(ptr, length); + } #else - void write_array(const uint8_t *ptr, size_t length) override { this->channel_->writeBytes(ptr, length); } + this->channel_->writeBytes(ptr, length); #endif + } void read_array(uint8_t *ptr, size_t length) override { this->channel_->transfer(ptr, length); } @@ -76,7 +86,8 @@ class SPIBusHw : public SPIBus { #endif } - SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin) override { + SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin, + bool release_device, bool write_only) override { return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin); } diff --git a/esphome/components/spi/spi_esp_idf.cpp b/esphome/components/spi/spi_esp_idf.cpp index a78da2cd9a..549f516eb1 100644 --- a/esphome/components/spi/spi_esp_idf.cpp +++ b/esphome/components/spi/spi_esp_idf.cpp @@ -11,34 +11,26 @@ static const size_t MAX_TRANSFER_SIZE = 4092; // dictated by ESP-IDF API. class SPIDelegateHw : public SPIDelegate { public: SPIDelegateHw(SPIInterface channel, uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin, - bool write_only) - : SPIDelegate(data_rate, bit_order, mode, cs_pin), channel_(channel), write_only_(write_only) { - spi_device_interface_config_t config = {}; - config.mode = static_cast(mode); - config.clock_speed_hz = static_cast(data_rate); - config.spics_io_num = -1; - config.flags = 0; - config.queue_size = 1; - config.pre_cb = nullptr; - config.post_cb = nullptr; - if (bit_order == BIT_ORDER_LSB_FIRST) - config.flags |= SPI_DEVICE_BIT_LSBFIRST; - if (write_only) - config.flags |= SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_DUMMY; - esp_err_t const err = spi_bus_add_device(channel, &config, &this->handle_); - if (err != ESP_OK) - ESP_LOGE(TAG, "Add device failed - err %X", err); + bool release_device, bool write_only) + : SPIDelegate(data_rate, bit_order, mode, cs_pin), + channel_(channel), + release_device_(release_device), + write_only_(write_only) { + if (!this->release_device_) + add_device_(); } bool is_ready() override { return this->handle_ != nullptr; } void begin_transaction() override { + if (this->release_device_) + this->add_device_(); if (this->is_ready()) { if (spi_device_acquire_bus(this->handle_, portMAX_DELAY) != ESP_OK) ESP_LOGE(TAG, "Failed to acquire SPI bus"); SPIDelegate::begin_transaction(); } else { - ESP_LOGW(TAG, "spi_setup called before initialisation"); + ESP_LOGW(TAG, "SPI device not ready, cannot begin transaction"); } } @@ -46,6 +38,10 @@ class SPIDelegateHw : public SPIDelegate { if (this->is_ready()) { SPIDelegate::end_transaction(); spi_device_release_bus(this->handle_); + if (this->release_device_) { + spi_bus_remove_device(this->handle_); + this->handle_ = nullptr; // reset handle to indicate no device is registered + } } } @@ -189,8 +185,30 @@ class SPIDelegateHw : public SPIDelegate { void read_array(uint8_t *ptr, size_t length) override { this->transfer(nullptr, ptr, length); } protected: + bool add_device_() { + spi_device_interface_config_t config = {}; + config.mode = static_cast(this->mode_); + config.clock_speed_hz = static_cast(this->data_rate_); + config.spics_io_num = -1; + config.flags = 0; + config.queue_size = 1; + config.pre_cb = nullptr; + config.post_cb = nullptr; + if (this->bit_order_ == BIT_ORDER_LSB_FIRST) + config.flags |= SPI_DEVICE_BIT_LSBFIRST; + if (this->write_only_) + config.flags |= SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_DUMMY; + esp_err_t const err = spi_bus_add_device(this->channel_, &config, &this->handle_); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Add device failed - err %X", err); + return false; + } + return true; + } + SPIInterface channel_{}; spi_device_handle_t handle_{}; + bool release_device_{false}; bool write_only_{false}; }; @@ -231,9 +249,10 @@ class SPIBusHw : public SPIBus { ESP_LOGE(TAG, "Bus init failed - err %X", err); } - SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin) override { - return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin, - Utility::get_pin_no(this->sdi_pin_) == -1); + SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin, + bool release_device, bool write_only) override { + return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin, release_device, + write_only || Utility::get_pin_no(this->sdi_pin_) == -1); } protected: diff --git a/esphome/components/spi_device/spi_device.cpp b/esphome/components/spi_device/spi_device.cpp index 1f579cb802..872b3054e6 100644 --- a/esphome/components/spi_device/spi_device.cpp +++ b/esphome/components/spi_device/spi_device.cpp @@ -9,9 +9,8 @@ namespace spi_device { static const char *const TAG = "spi_device"; void SPIDeviceComponent::setup() { - ESP_LOGD(TAG, "Setting up SPIDevice..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); - ESP_LOGCONFIG(TAG, "SPIDevice started!"); } void SPIDeviceComponent::dump_config() { diff --git a/esphome/components/spi_led_strip/spi_led_strip.cpp b/esphome/components/spi_led_strip/spi_led_strip.cpp index 46243c0686..85c10ee87d 100644 --- a/esphome/components/spi_led_strip/spi_led_strip.cpp +++ b/esphome/components/spi_led_strip/spi_led_strip.cpp @@ -5,7 +5,7 @@ namespace spi_led_strip { SpiLedStrip::SpiLedStrip(uint16_t num_leds) { this->num_leds_ = num_leds; - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; this->buffer_size_ = num_leds * 4 + 8; this->buf_ = allocator.allocate(this->buffer_size_); if (this->buf_ == nullptr) { diff --git a/esphome/components/sprinkler/sprinkler.cpp b/esphome/components/sprinkler/sprinkler.cpp index 50ea3eff51..e191498857 100644 --- a/esphome/components/sprinkler/sprinkler.cpp +++ b/esphome/components/sprinkler/sprinkler.cpp @@ -1712,15 +1712,18 @@ void Sprinkler::dump_config() { if (this->valve_overlap_) { ESP_LOGCONFIG(TAG, " Valve Overlap: %" PRIu32 " seconds", this->switching_delay_.value_or(0)); } else { - ESP_LOGCONFIG(TAG, " Valve Open Delay: %" PRIu32 " seconds", this->switching_delay_.value_or(0)); - ESP_LOGCONFIG(TAG, " Pump Switch Off During Valve Open Delay: %s", - YESNO(this->pump_switch_off_during_valve_open_delay_)); + ESP_LOGCONFIG(TAG, + " Valve Open Delay: %" PRIu32 " seconds\n" + " Pump Switch Off During Valve Open Delay: %s", + this->switching_delay_.value_or(0), YESNO(this->pump_switch_off_during_valve_open_delay_)); } } for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) { - ESP_LOGCONFIG(TAG, " Valve %zu:", valve_number); - ESP_LOGCONFIG(TAG, " Name: %s", this->valve_name(valve_number)); - ESP_LOGCONFIG(TAG, " Run Duration: %" PRIu32 " seconds", this->valve_run_duration(valve_number)); + ESP_LOGCONFIG(TAG, + " Valve %zu:\n" + " Name: %s\n" + " Run Duration: %" PRIu32 " seconds", + valve_number, this->valve_name(valve_number), this->valve_run_duration(valve_number)); if (this->valve_[valve_number].valve_switch.pulse_duration()) { ESP_LOGCONFIG(TAG, " Pulse Duration: %" PRIu32 " milliseconds", this->valve_[valve_number].valve_switch.pulse_duration()); diff --git a/esphome/components/sps30/sps30.cpp b/esphome/components/sps30/sps30.cpp index 5a0335998f..c0df539867 100644 --- a/esphome/components/sps30/sps30.cpp +++ b/esphome/components/sps30/sps30.cpp @@ -96,9 +96,10 @@ void SPS30Component::dump_config() { } } LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " Serial Number: '%s'", this->serial_number_); - ESP_LOGCONFIG(TAG, " Firmware version v%0d.%0d", (raw_firmware_version_ >> 8), - uint16_t(raw_firmware_version_ & 0xFF)); + ESP_LOGCONFIG(TAG, + " Serial Number: '%s'\n" + " Firmware version v%0d.%0d", + this->serial_number_, (raw_firmware_version_ >> 8), uint16_t(raw_firmware_version_ & 0xFF)); LOG_SENSOR(" ", "PM1.0 Weight Concentration", this->pm_1_0_sensor_); LOG_SENSOR(" ", "PM2.5 Weight Concentration", this->pm_2_5_sensor_); LOG_SENSOR(" ", "PM4 Weight Concentration", this->pm_4_0_sensor_); @@ -113,18 +114,18 @@ void SPS30Component::dump_config() { void SPS30Component::update() { /// Check if warning flag active (sensor reconnected?) if (this->status_has_warning()) { - ESP_LOGD(TAG, "Trying to reconnect the sensor..."); + ESP_LOGD(TAG, "Trying to reconnect"); if (this->write_command(SPS30_CMD_SOFT_RESET)) { - ESP_LOGD(TAG, "Sensor has soft-reset successfully. Waiting for reconnection in 500ms..."); + ESP_LOGD(TAG, "Soft-reset successful. Waiting for reconnection in 500 ms"); this->set_timeout(500, [this]() { this->start_continuous_measurement_(); /// Sensor restarted and reading attempt made next cycle this->status_clear_warning(); this->skipped_data_read_cycles_ = 0; - ESP_LOGD(TAG, "Sensor reconnected successfully. Resuming continuous measurement!"); + ESP_LOGD(TAG, "Reconnect successful. Resuming continuous measurement"); }); } else { - ESP_LOGD(TAG, "Sensor soft-reset failed. Is the sensor offline?"); + ESP_LOGD(TAG, "Soft-reset failed"); } return; } @@ -136,19 +137,19 @@ void SPS30Component::update() { uint16_t raw_read_status; if (!this->read_data(&raw_read_status, 1) || raw_read_status == 0x00) { - ESP_LOGD(TAG, "Sensor measurement not ready yet."); + ESP_LOGD(TAG, "Not ready yet"); this->skipped_data_read_cycles_++; /// The following logic is required to address the cases when a sensor is quickly replaced before it's marked /// as failed so that new sensor is eventually forced to be reinitialized for continuous measurement. if (this->skipped_data_read_cycles_ > MAX_SKIPPED_DATA_CYCLES_BEFORE_ERROR) { - ESP_LOGD(TAG, "Sensor exceeded max allowed attempts. Sensor communication will be reinitialized."); + ESP_LOGD(TAG, "Exceeded max allowed attempts; communication will be reinitialized"); this->status_set_warning(); } return; } if (!this->write_command(SPS30_CMD_READ_MEASUREMENT)) { - ESP_LOGW(TAG, "Error reading measurement status!"); + ESP_LOGW(TAG, "Error reading status"); this->status_set_warning(); return; } @@ -156,7 +157,7 @@ void SPS30Component::update() { this->set_timeout(50, [this]() { uint16_t raw_data[20]; if (!this->read_data(raw_data, 20)) { - ESP_LOGW(TAG, "Error reading measurement data!"); + ESP_LOGW(TAG, "Error reading data"); this->status_set_warning(); return; } diff --git a/esphome/components/ssd1306_base/ssd1306_base.cpp b/esphome/components/ssd1306_base/ssd1306_base.cpp index 90b805a79f..0547a77184 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.cpp +++ b/esphome/components/ssd1306_base/ssd1306_base.cpp @@ -1,6 +1,6 @@ #include "ssd1306_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1306_base { diff --git a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp index 4013ae7209..f9a2609948 100644 --- a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +++ b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp @@ -24,12 +24,15 @@ void I2CSSD1306::dump_config() { LOG_I2C_DEVICE(this); ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " External VCC: %s", YESNO(this->external_vcc_)); - ESP_LOGCONFIG(TAG, " Flip X: %s", YESNO(this->flip_x_)); - ESP_LOGCONFIG(TAG, " Flip Y: %s", YESNO(this->flip_y_)); - ESP_LOGCONFIG(TAG, " Offset X: %d", this->offset_x_); - ESP_LOGCONFIG(TAG, " Offset Y: %d", this->offset_y_); - ESP_LOGCONFIG(TAG, " Inverted Color: %s", YESNO(this->invert_)); + ESP_LOGCONFIG(TAG, + " External VCC: %s\n" + " Flip X: %s\n" + " Flip Y: %s\n" + " Offset X: %d\n" + " Offset Y: %d\n" + " Inverted Color: %s", + YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_), this->offset_x_, + this->offset_y_, YESNO(this->invert_)); LOG_UPDATE_INTERVAL(this); if (this->error_code_ == COMMUNICATION_FAILED) { diff --git a/esphome/components/ssd1306_spi/ssd1306_spi.cpp b/esphome/components/ssd1306_spi/ssd1306_spi.cpp index 28425da587..249e6593ae 100644 --- a/esphome/components/ssd1306_spi/ssd1306_spi.cpp +++ b/esphome/components/ssd1306_spi/ssd1306_spi.cpp @@ -21,12 +21,15 @@ void SPISSD1306::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " External VCC: %s", YESNO(this->external_vcc_)); - ESP_LOGCONFIG(TAG, " Flip X: %s", YESNO(this->flip_x_)); - ESP_LOGCONFIG(TAG, " Flip Y: %s", YESNO(this->flip_y_)); - ESP_LOGCONFIG(TAG, " Offset X: %d", this->offset_x_); - ESP_LOGCONFIG(TAG, " Offset Y: %d", this->offset_y_); - ESP_LOGCONFIG(TAG, " Inverted Color: %s", YESNO(this->invert_)); + ESP_LOGCONFIG(TAG, + " External VCC: %s\n" + " Flip X: %s\n" + " Flip Y: %s\n" + " Offset X: %d\n" + " Offset Y: %d\n" + " Inverted Color: %s", + YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_), this->offset_x_, + this->offset_y_, YESNO(this->invert_)); LOG_UPDATE_INTERVAL(this); } void SPISSD1306::command(uint8_t value) { diff --git a/esphome/components/ssd1322_base/ssd1322_base.cpp b/esphome/components/ssd1322_base/ssd1322_base.cpp index 520248a66e..eb8d87998f 100644 --- a/esphome/components/ssd1322_base/ssd1322_base.cpp +++ b/esphome/components/ssd1322_base/ssd1322_base.cpp @@ -1,6 +1,6 @@ #include "ssd1322_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1322_base { diff --git a/esphome/components/ssd1325_base/ssd1325_base.cpp b/esphome/components/ssd1325_base/ssd1325_base.cpp index 711f9e5197..e7d2386ac7 100644 --- a/esphome/components/ssd1325_base/ssd1325_base.cpp +++ b/esphome/components/ssd1325_base/ssd1325_base.cpp @@ -1,6 +1,6 @@ #include "ssd1325_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1325_base { diff --git a/esphome/components/ssd1327_base/ssd1327_base.cpp b/esphome/components/ssd1327_base/ssd1327_base.cpp index 4223a013a4..6b83ec5f9d 100644 --- a/esphome/components/ssd1327_base/ssd1327_base.cpp +++ b/esphome/components/ssd1327_base/ssd1327_base.cpp @@ -1,6 +1,6 @@ #include "ssd1327_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1327_base { diff --git a/esphome/components/ssd1331_base/ssd1331_base.cpp b/esphome/components/ssd1331_base/ssd1331_base.cpp index 14f94dee4f..8ee12387e4 100644 --- a/esphome/components/ssd1331_base/ssd1331_base.cpp +++ b/esphome/components/ssd1331_base/ssd1331_base.cpp @@ -1,6 +1,6 @@ #include "ssd1331_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1331_base { diff --git a/esphome/components/ssd1351_base/ssd1351_base.cpp b/esphome/components/ssd1351_base/ssd1351_base.cpp index 38ea05a0b8..09530e8a27 100644 --- a/esphome/components/ssd1351_base/ssd1351_base.cpp +++ b/esphome/components/ssd1351_base/ssd1351_base.cpp @@ -1,6 +1,6 @@ #include "ssd1351_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1351_base { diff --git a/esphome/components/st7567_base/st7567_base.cpp b/esphome/components/st7567_base/st7567_base.cpp index b22a7d7fd5..0afd2a70ba 100644 --- a/esphome/components/st7567_base/st7567_base.cpp +++ b/esphome/components/st7567_base/st7567_base.cpp @@ -1,6 +1,6 @@ #include "st7567_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace st7567_base { @@ -13,7 +13,7 @@ void ST7567::setup() { } void ST7567::display_init_() { - ESP_LOGD(TAG, "Initializing ST7567 display..."); + ESP_LOGD(TAG, "Initializing display"); this->display_init_registers_(); this->clear(); this->write_display_data(); @@ -42,7 +42,7 @@ void ST7567::display_init_registers_() { } void ST7567::display_sw_refresh_() { - ESP_LOGD(TAG, "Performing refresh sequence..."); + ESP_LOGD(TAG, "Performing refresh sequence"); this->command(ST7567_SW_REFRESH); this->display_init_registers_(); } diff --git a/esphome/components/st7567_i2c/st7567_i2c.cpp b/esphome/components/st7567_i2c/st7567_i2c.cpp index 9180409cc0..0640d3be8d 100644 --- a/esphome/components/st7567_i2c/st7567_i2c.cpp +++ b/esphome/components/st7567_i2c/st7567_i2c.cpp @@ -24,9 +24,11 @@ void I2CST7567::dump_config() { LOG_I2C_DEVICE(this); ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Mirror X: %s", YESNO(this->mirror_x_)); - ESP_LOGCONFIG(TAG, " Mirror Y: %s", YESNO(this->mirror_y_)); - ESP_LOGCONFIG(TAG, " Invert Colors: %s", YESNO(this->invert_colors_)); + ESP_LOGCONFIG(TAG, + " Mirror X: %s\n" + " Mirror Y: %s\n" + " Invert Colors: %s", + YESNO(this->mirror_x_), YESNO(this->mirror_y_), YESNO(this->invert_colors_)); LOG_UPDATE_INTERVAL(this); if (this->error_code_ == COMMUNICATION_FAILED) { diff --git a/esphome/components/st7701s/st7701s.cpp b/esphome/components/st7701s/st7701s.cpp index 403bff789d..6261f33b77 100644 --- a/esphome/components/st7701s/st7701s.cpp +++ b/esphome/components/st7701s/st7701s.cpp @@ -181,8 +181,10 @@ void ST7701S::write_init_sequence_() { void ST7701S::dump_config() { ESP_LOGCONFIG("", "ST7701S RGB LCD"); - ESP_LOGCONFIG(TAG, " Height: %u", this->height_); - ESP_LOGCONFIG(TAG, " Width: %u", this->width_); + ESP_LOGCONFIG(TAG, + " Height: %u\n" + " Width: %u", + this->height_, this->width_); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" DE Pin: ", this->de_pin_); diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index f37770a767..9c9c0a3df5 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -1,7 +1,7 @@ #include "st7735.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace st7735 { diff --git a/esphome/components/st7789v/st7789v.cpp b/esphome/components/st7789v/st7789v.cpp index 72460d8c89..1f3cd50d6c 100644 --- a/esphome/components/st7789v/st7789v.cpp +++ b/esphome/components/st7789v/st7789v.cpp @@ -122,12 +122,15 @@ void ST7789V::setup() { void ST7789V::dump_config() { LOG_DISPLAY("", "SPI ST7789V", this); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_); - ESP_LOGCONFIG(TAG, " Height: %u", this->height_); - ESP_LOGCONFIG(TAG, " Width: %u", this->width_); - ESP_LOGCONFIG(TAG, " Height Offset: %u", this->offset_height_); - ESP_LOGCONFIG(TAG, " Width Offset: %u", this->offset_width_); - ESP_LOGCONFIG(TAG, " 8-bit color mode: %s", YESNO(this->eightbitcolor_)); + ESP_LOGCONFIG(TAG, + " Model: %s\n" + " Height: %u\n" + " Width: %u\n" + " Height Offset: %u\n" + " Width Offset: %u\n" + " 8-bit color mode: %s", + this->model_str_, this->height_, this->width_, this->offset_height_, this->offset_width_, + YESNO(this->eightbitcolor_)); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); diff --git a/esphome/components/st7920/st7920.cpp b/esphome/components/st7920/st7920.cpp index 0623125bfb..54ac6d2efd 100644 --- a/esphome/components/st7920/st7920.cpp +++ b/esphome/components/st7920/st7920.cpp @@ -95,8 +95,10 @@ void ST7920::fill(Color color) { memset(this->buffer_, color.is_on() ? 0xFF : 0x void ST7920::dump_config() { LOG_DISPLAY("", "ST7920", this); LOG_PIN(" CS Pin: ", this->cs_); - ESP_LOGCONFIG(TAG, " Height: %d", this->height_); - ESP_LOGCONFIG(TAG, " Width: %d", this->width_); + ESP_LOGCONFIG(TAG, + " Height: %d\n" + " Width: %d", + this->height_, this->width_); } float ST7920::get_setup_priority() const { return setup_priority::PROCESSOR; } @@ -129,7 +131,7 @@ void HOT ST7920::draw_absolute_pixel_internal(int x, int y, Color color) { } void ST7920::display_init_() { - ESP_LOGD(TAG, "Initializing display..."); + ESP_LOGD(TAG, "Initializing display"); this->command_(LCD_BASIC); // 8bit mode this->command_(LCD_BASIC); // 8bit mode this->command_(LCD_CLS); // clear screen diff --git a/esphome/components/statsd/statsd.cpp b/esphome/components/statsd/statsd.cpp index b7fad19332..05f71c7b24 100644 --- a/esphome/components/statsd/statsd.cpp +++ b/esphome/components/statsd/statsd.cpp @@ -38,17 +38,21 @@ StatsdComponent::~StatsdComponent() { } void StatsdComponent::dump_config() { - ESP_LOGCONFIG(TAG, "statsD:"); - ESP_LOGCONFIG(TAG, " host: %s", this->host_); - ESP_LOGCONFIG(TAG, " port: %d", this->port_); + ESP_LOGCONFIG(TAG, + "statsD:\n" + " host: %s\n" + " port: %d", + this->host_, this->port_); if (this->prefix_) { ESP_LOGCONFIG(TAG, " prefix: %s", this->prefix_); } ESP_LOGCONFIG(TAG, " metrics:"); for (sensors_t s : this->sensors_) { - ESP_LOGCONFIG(TAG, " - name: %s", s.name); - ESP_LOGCONFIG(TAG, " type: %d", s.type); + ESP_LOGCONFIG(TAG, + " - name: %s\n" + " type: %d", + s.name, s.type); } } diff --git a/esphome/components/status_led/light/status_led_light.cpp b/esphome/components/status_led/light/status_led_light.cpp index 6d38833ebd..dc4820f6da 100644 --- a/esphome/components/status_led/light/status_led_light.cpp +++ b/esphome/components/status_led/light/status_led_light.cpp @@ -9,10 +9,10 @@ namespace status_led { static const char *const TAG = "status_led"; void StatusLEDLightOutput::loop() { - uint32_t new_state = App.get_app_state() & STATUS_LED_MASK; + uint8_t new_state = App.get_app_state() & STATUS_LED_MASK; if (new_state != this->last_app_state_) { - ESP_LOGV(TAG, "New app state 0x%08" PRIX32, new_state); + ESP_LOGV(TAG, "New app state 0x%02X", new_state); } if ((new_state & STATUS_LED_ERROR) != 0u) { diff --git a/esphome/components/status_led/light/status_led_light.h b/esphome/components/status_led/light/status_led_light.h index e711a2e749..bfa144526a 100644 --- a/esphome/components/status_led/light/status_led_light.h +++ b/esphome/components/status_led/light/status_led_light.h @@ -36,7 +36,7 @@ class StatusLEDLightOutput : public light::LightOutput, public Component { GPIOPin *pin_{nullptr}; output::BinaryOutput *output_{nullptr}; light::LightState *lightstate_{}; - uint32_t last_app_state_{0xFFFF}; + uint8_t last_app_state_{0xFF}; void output_state_(bool state); }; diff --git a/esphome/components/stepper/stepper.h b/esphome/components/stepper/stepper.h index ba2b3182d7..1cf4830b1f 100644 --- a/esphome/components/stepper/stepper.h +++ b/esphome/components/stepper/stepper.h @@ -7,9 +7,11 @@ namespace esphome { namespace stepper { #define LOG_STEPPER(this) \ - ESP_LOGCONFIG(TAG, " Acceleration: %.0f steps/s^2", this->acceleration_); \ - ESP_LOGCONFIG(TAG, " Deceleration: %.0f steps/s^2", this->deceleration_); \ - ESP_LOGCONFIG(TAG, " Max Speed: %.0f steps/s", this->max_speed_); + ESP_LOGCONFIG(TAG, \ + " Acceleration: %.0f steps/s^2\n" \ + " Deceleration: %.0f steps/s^2\n" \ + " Max Speed: %.0f steps/s", \ + this->acceleration_, this->deceleration_, this->max_speed_); class Stepper { public: diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index e7445051e0..0211c648fc 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -159,6 +159,7 @@ async def register_switch(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_switch(var)) + CORE.register_platform_component("switch", var) await setup_switch_core_(var, config) diff --git a/esphome/components/switch/switch.cpp b/esphome/components/switch/switch.cpp index 96611b0b87..c204895755 100644 --- a/esphome/components/switch/switch.cpp +++ b/esphome/components/switch/switch.cpp @@ -65,7 +65,24 @@ bool Switch::is_inverted() const { return this->inverted_; } void log_switch(const char *tag, const char *prefix, const char *type, Switch *obj) { if (obj != nullptr) { - ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str()); + // Prepare restore mode string + const LogString *onoff = LOG_STR(""), *inverted = onoff, *restore; + if (obj->restore_mode & RESTORE_MODE_DISABLED_MASK) { + restore = LOG_STR("disabled"); + } else { + onoff = obj->restore_mode & RESTORE_MODE_ON_MASK ? LOG_STR("ON") : LOG_STR("OFF"); + inverted = obj->restore_mode & RESTORE_MODE_INVERTED_MASK ? LOG_STR("inverted ") : LOG_STR(""); + restore = obj->restore_mode & RESTORE_MODE_PERSISTENT_MASK ? LOG_STR("restore defaults to") : LOG_STR("always"); + } + + // Build the base message with mandatory fields + ESP_LOGCONFIG(tag, + "%s%s '%s'\n" + "%s Restore Mode: %s%s %s", + prefix, type, obj->get_name().c_str(), prefix, LOG_STR_ARG(inverted), LOG_STR_ARG(restore), + LOG_STR_ARG(onoff)); + + // Add optional fields separately if (!obj->get_icon().empty()) { ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon().c_str()); } @@ -78,17 +95,6 @@ void log_switch(const char *tag, const char *prefix, const char *type, Switch *o if (!obj->get_device_class().empty()) { ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); } - const LogString *onoff = LOG_STR(""), *inverted = onoff, *restore; - if (obj->restore_mode & RESTORE_MODE_DISABLED_MASK) { - restore = LOG_STR("disabled"); - } else { - onoff = obj->restore_mode & RESTORE_MODE_ON_MASK ? LOG_STR("ON") : LOG_STR("OFF"); - inverted = obj->restore_mode & RESTORE_MODE_INVERTED_MASK ? LOG_STR("inverted ") : LOG_STR(""); - restore = obj->restore_mode & RESTORE_MODE_PERSISTENT_MASK ? LOG_STR("restore defaults to") : LOG_STR("always"); - } - - ESP_LOGCONFIG(tag, "%s Restore Mode: %s%s %s", prefix, LOG_STR_ARG(inverted), LOG_STR_ARG(restore), - LOG_STR_ARG(onoff)); } } diff --git a/esphome/components/switch/switch.h b/esphome/components/switch/switch.h index b5395a2c83..b999296564 100644 --- a/esphome/components/switch/switch.h +++ b/esphome/components/switch/switch.h @@ -2,8 +2,8 @@ #include "esphome/core/component.h" #include "esphome/core/entity_base.h" -#include "esphome/core/preferences.h" #include "esphome/core/helpers.h" +#include "esphome/core/preferences.h" namespace esphome { namespace switch_ { @@ -21,7 +21,7 @@ const int RESTORE_MODE_PERSISTENT_MASK = 0x02; const int RESTORE_MODE_INVERTED_MASK = 0x04; const int RESTORE_MODE_DISABLED_MASK = 0x08; -enum SwitchRestoreMode { +enum SwitchRestoreMode : uint8_t { SWITCH_ALWAYS_OFF = !RESTORE_MODE_ON_MASK, SWITCH_ALWAYS_ON = RESTORE_MODE_ON_MASK, SWITCH_RESTORE_DEFAULT_OFF = RESTORE_MODE_PERSISTENT_MASK, @@ -49,12 +49,12 @@ class Switch : public EntityBase, public EntityBase_DeviceClass { */ void publish_state(bool state); - /// The current reported state of the binary sensor. - bool state; - /// Indicates whether or not state is to be retrieved from flash and how SwitchRestoreMode restore_mode{SWITCH_RESTORE_DEFAULT_OFF}; + /// The current reported state of the binary sensor. + bool state; + /** Turn this switch on. This is called by the front-end. * * For implementing switches, please override write_state. @@ -123,10 +123,16 @@ class Switch : public EntityBase, public EntityBase_DeviceClass { */ virtual void write_state(bool state) = 0; - CallbackManager state_callback_{}; - bool inverted_{false}; - Deduplicator publish_dedup_; + // Pointer first (4 bytes) ESPPreferenceObject rtc_; + + // CallbackManager (12 bytes on 32-bit - contains vector) + CallbackManager state_callback_{}; + + // Small types grouped together + Deduplicator publish_dedup_; // 2 bytes (bool has_value_ + bool last_value_) + bool inverted_{false}; // 1 byte + // Total: 3 bytes, 1 byte padding }; #define LOG_SWITCH(prefix, type, obj) log_switch((TAG), (prefix), LOG_STR_LITERAL(type), (obj)) diff --git a/esphome/components/sx1509/sx1509.cpp b/esphome/components/sx1509/sx1509.cpp index 865bf6f25b..d323c9a92c 100644 --- a/esphome/components/sx1509/sx1509.cpp +++ b/esphome/components/sx1509/sx1509.cpp @@ -10,7 +10,7 @@ static const char *const TAG = "sx1509"; void SX1509Component::setup() { ESP_LOGCONFIG(TAG, "Running setup"); - ESP_LOGV(TAG, " Resetting devices..."); + ESP_LOGV(TAG, " Resetting devices"); if (!this->write_byte(REG_RESET, 0x12)) { this->mark_failed(); return; diff --git a/esphome/components/t6615/t6615.cpp b/esphome/components/t6615/t6615.cpp index 1c78833500..c2ac88ee2e 100644 --- a/esphome/components/t6615/t6615.cpp +++ b/esphome/components/t6615/t6615.cpp @@ -1,4 +1,5 @@ #include "t6615.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/tca9555/__init__.py b/esphome/components/tca9555/__init__.py index db0451d4e6..f42e0fe398 100644 --- a/esphome/components/tca9555/__init__.py +++ b/esphome/components/tca9555/__init__.py @@ -53,7 +53,7 @@ TCA9555_PIN_SCHEMA = pins.gpio_base_schema( cv.int_range(min=0, max=15), modes=[CONF_INPUT, CONF_OUTPUT], mode_validator=validate_mode, - invertable=True, + invertible=True, ).extend( { cv.Required(CONF_TCA9555): cv.use_id(TCA9555Component), diff --git a/esphome/components/tcs34725/tcs34725.cpp b/esphome/components/tcs34725/tcs34725.cpp index 0aed2c8f46..9926ebc553 100644 --- a/esphome/components/tcs34725/tcs34725.cpp +++ b/esphome/components/tcs34725/tcs34725.cpp @@ -1,8 +1,8 @@ #include "tcs34725.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" -#include #include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include namespace esphome { namespace tcs34725 { diff --git a/esphome/components/tee501/tee501.cpp b/esphome/components/tee501/tee501.cpp index 329aee7c52..45241627f9 100644 --- a/esphome/components/tee501/tee501.cpp +++ b/esphome/components/tee501/tee501.cpp @@ -1,4 +1,5 @@ #include "tee501.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/tem3200/tem3200.cpp b/esphome/components/tem3200/tem3200.cpp index 05bf580e11..c0655d02b8 100644 --- a/esphome/components/tem3200/tem3200.cpp +++ b/esphome/components/tem3200/tem3200.cpp @@ -1,7 +1,7 @@ #include "tem3200.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tem3200 { @@ -43,7 +43,6 @@ void TEM3200Component::setup() { this->status_set_warning(); break; } - ESP_LOGCONFIG(TAG, " Success..."); } void TEM3200Component::dump_config() { diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp index 4b81ab1c35..6f743a77ef 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp @@ -29,8 +29,10 @@ void TemplateAlarmControlPanel::add_sensor(binary_sensor::BinarySensor *sensor, void TemplateAlarmControlPanel::dump_config() { ESP_LOGCONFIG(TAG, "TemplateAlarmControlPanel:"); - ESP_LOGCONFIG(TAG, " Current State: %s", LOG_STR_ARG(alarm_control_panel_state_to_string(this->current_state_))); - ESP_LOGCONFIG(TAG, " Number of Codes: %u", this->codes_.size()); + ESP_LOGCONFIG(TAG, + " Current State: %s\n" + " Number of Codes: %u", + LOG_STR_ARG(alarm_control_panel_state_to_string(this->current_state_)), this->codes_.size()); if (!this->codes_.empty()) ESP_LOGCONFIG(TAG, " Requires Code To Arm: %s", YESNO(this->requires_code_to_arm_)); ESP_LOGCONFIG(TAG, " Arming Away Time: %" PRIu32 "s", (this->arming_away_time_ / 1000)); @@ -38,19 +40,25 @@ void TemplateAlarmControlPanel::dump_config() { ESP_LOGCONFIG(TAG, " Arming Home Time: %" PRIu32 "s", (this->arming_home_time_ / 1000)); if (this->arming_night_time_ != 0) ESP_LOGCONFIG(TAG, " Arming Night Time: %" PRIu32 "s", (this->arming_night_time_ / 1000)); - ESP_LOGCONFIG(TAG, " Pending Time: %" PRIu32 "s", (this->pending_time_ / 1000)); - ESP_LOGCONFIG(TAG, " Trigger Time: %" PRIu32 "s", (this->trigger_time_ / 1000)); - ESP_LOGCONFIG(TAG, " Supported Features: %" PRIu32, this->get_supported_features()); + ESP_LOGCONFIG(TAG, + " Pending Time: %" PRIu32 "s\n" + " Trigger Time: %" PRIu32 "s\n" + " Supported Features: %" PRIu32, + (this->pending_time_ / 1000), (this->trigger_time_ / 1000), this->get_supported_features()); #ifdef USE_BINARY_SENSOR for (auto sensor_info : this->sensor_map_) { ESP_LOGCONFIG(TAG, " Binary Sensor:"); - ESP_LOGCONFIG(TAG, " Name: %s", sensor_info.first->get_name().c_str()); - ESP_LOGCONFIG(TAG, " Armed home bypass: %s", - TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)); - ESP_LOGCONFIG(TAG, " Armed night bypass: %s", - TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT)); - ESP_LOGCONFIG(TAG, " Auto bypass: %s", TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO)); - ESP_LOGCONFIG(TAG, " Chime mode: %s", TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME)); + ESP_LOGCONFIG(TAG, + " Name: %s\n" + " Armed home bypass: %s\n" + " Armed night bypass: %s\n" + " Auto bypass: %s\n" + " Chime mode: %s", + sensor_info.first->get_name().c_str(), + TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME), + TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT), + TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO), + TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME)); const char *sensor_type; switch (sensor_info.second.type) { case ALARM_SENSOR_TYPE_INSTANT: @@ -102,15 +110,7 @@ void TemplateAlarmControlPanel::loop() { delay = this->arming_night_time_; } if ((millis() - this->last_update_) > delay) { -#ifdef USE_BINARY_SENSOR - for (auto sensor_info : this->sensor_map_) { - // Check for sensors left on and set to bypass automatically and remove them from monitoring - if ((sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor_info.first->state)) { - ESP_LOGW(TAG, "%s is left on and will be automatically bypassed", sensor_info.first->get_name().c_str()); - this->bypassed_sensor_indicies_.push_back(sensor_info.second.store_index); - } - } -#endif + this->bypass_before_arming(); this->publish_state(this->desired_state_); } return; @@ -251,10 +251,23 @@ void TemplateAlarmControlPanel::arm_(optional code, alarm_control_p if (delay > 0) { this->publish_state(ACP_STATE_ARMING); } else { + this->bypass_before_arming(); this->publish_state(state); } } +void TemplateAlarmControlPanel::bypass_before_arming() { +#ifdef USE_BINARY_SENSOR + for (auto sensor_info : this->sensor_map_) { + // Check for sensors left on and set to bypass automatically and remove them from monitoring + if ((sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor_info.first->state)) { + ESP_LOGW(TAG, "'%s' is left on and will be automatically bypassed", sensor_info.first->get_name().c_str()); + this->bypassed_sensor_indicies_.push_back(sensor_info.second.store_index); + } + } +#endif +} + void TemplateAlarmControlPanel::control(const AlarmControlPanelCall &call) { if (call.get_state()) { if (call.get_state() == ACP_STATE_ARMED_AWAY) { diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h index 0e3a5c77cf..c3b28e8efa 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h @@ -60,6 +60,7 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, bool get_requires_code_to_arm() const override { return this->requires_code_to_arm_; } bool get_all_sensors_ready() { return this->sensors_ready_; }; void set_restore_mode(TemplateAlarmControlPanelRestoreMode restore_mode) { this->restore_mode_ = restore_mode; } + void bypass_before_arming(); #ifdef USE_BINARY_SENSOR /** Add a binary_sensor to the alarm_panel. diff --git a/esphome/components/template/binary_sensor/template_binary_sensor.cpp b/esphome/components/template/binary_sensor/template_binary_sensor.cpp index 5ce8894a8a..d1fb618695 100644 --- a/esphome/components/template/binary_sensor/template_binary_sensor.cpp +++ b/esphome/components/template/binary_sensor/template_binary_sensor.cpp @@ -6,16 +6,8 @@ namespace template_ { static const char *const TAG = "template.binary_sensor"; -void TemplateBinarySensor::setup() { - if (!this->publish_initial_state_) - return; +void TemplateBinarySensor::setup() { this->loop(); } - if (this->f_ != nullptr) { - this->publish_initial_state(this->f_().value_or(false)); - } else { - this->publish_initial_state(false); - } -} void TemplateBinarySensor::loop() { if (this->f_ == nullptr) return; diff --git a/esphome/components/template/select/template_select.cpp b/esphome/components/template/select/template_select.cpp index 599226f4ba..0160fab04b 100644 --- a/esphome/components/template/select/template_select.cpp +++ b/esphome/components/template/select/template_select.cpp @@ -66,9 +66,11 @@ void TemplateSelect::dump_config() { LOG_UPDATE_INTERVAL(this); if (this->f_.has_value()) return; - ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_)); - ESP_LOGCONFIG(TAG, " Initial Option: %s", this->initial_option_.c_str()); - ESP_LOGCONFIG(TAG, " Restore Value: %s", YESNO(this->restore_value_)); + ESP_LOGCONFIG(TAG, + " Optimistic: %s\n" + " Initial Option: %s\n" + " Restore Value: %s", + YESNO(this->optimistic_), this->initial_option_.c_str(), YESNO(this->restore_value_)); } } // namespace template_ diff --git a/esphome/components/template/valve/template_valve.cpp b/esphome/components/template/valve/template_valve.cpp index 99bf5e6d8a..8421f5e06f 100644 --- a/esphome/components/template/valve/template_valve.cpp +++ b/esphome/components/template/valve/template_valve.cpp @@ -66,8 +66,10 @@ Trigger<> *TemplateValve::get_toggle_trigger() const { return this->toggle_trigg void TemplateValve::dump_config() { LOG_VALVE("", "Template Valve", this); - ESP_LOGCONFIG(TAG, " Has position: %s", YESNO(this->has_position_)); - ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_)); + ESP_LOGCONFIG(TAG, + " Has position: %s\n" + " Optimistic: %s", + YESNO(this->has_position_), YESNO(this->optimistic_)); } void TemplateValve::control(const ValveCall &call) { diff --git a/esphome/components/text/__init__.py b/esphome/components/text/__init__.py index a864a0ba4f..40b3a90d6b 100644 --- a/esphome/components/text/__init__.py +++ b/esphome/components/text/__init__.py @@ -126,6 +126,7 @@ async def register_text( if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_text(var)) + CORE.register_platform_component("text", var) await setup_text_core_( var, config, min_length=min_length, max_length=max_length, pattern=pattern ) diff --git a/esphome/components/text/text.cpp b/esphome/components/text/text.cpp index 8f0242e747..654893d4e4 100644 --- a/esphome/components/text/text.cpp +++ b/esphome/components/text/text.cpp @@ -7,7 +7,7 @@ namespace text { static const char *const TAG = "text"; void Text::publish_state(const std::string &state) { - this->has_state_ = true; + this->set_has_state(true); this->state = state; if (this->traits.get_mode() == TEXT_MODE_PASSWORD) { ESP_LOGD(TAG, "'%s': Sending state " LOG_SECRET("'%s'"), this->get_name().c_str(), state.c_str()); diff --git a/esphome/components/text/text.h b/esphome/components/text/text.h index f71dde69ba..3cc0cefc3e 100644 --- a/esphome/components/text/text.h +++ b/esphome/components/text/text.h @@ -28,9 +28,6 @@ class Text : public EntityBase { void publish_state(const std::string &state); - /// Return whether this text input has gotten a full state yet. - bool has_state() const { return has_state_; } - /// Instantiate a TextCall object to modify this text component's state. TextCall make_call() { return TextCall(this); } @@ -48,7 +45,6 @@ class Text : public EntityBase { virtual void control(const std::string &value) = 0; CallbackManager state_callback_; - bool has_state_{false}; }; } // namespace text diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index 888b65745f..c7ac17c35a 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -215,6 +215,7 @@ async def register_text_sensor(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_text_sensor(var)) + CORE.register_platform_component("text_sensor", var) await setup_text_sensor_core_(var, config) diff --git a/esphome/components/text_sensor/text_sensor.cpp b/esphome/components/text_sensor/text_sensor.cpp index f10cd50267..c57e0ffefb 100644 --- a/esphome/components/text_sensor/text_sensor.cpp +++ b/esphome/components/text_sensor/text_sensor.cpp @@ -8,7 +8,9 @@ static const char *const TAG = "text_sensor"; void TextSensor::publish_state(const std::string &state) { this->raw_state = state; - this->raw_callback_.call(state); + if (this->raw_callback_) { + this->raw_callback_->call(state); + } ESP_LOGV(TAG, "'%s': Received new state %s", this->name_.c_str(), state.c_str()); @@ -53,20 +55,22 @@ void TextSensor::add_on_state_callback(std::function callback this->callback_.add(std::move(callback)); } void TextSensor::add_on_raw_state_callback(std::function callback) { - this->raw_callback_.add(std::move(callback)); + if (!this->raw_callback_) { + this->raw_callback_ = make_unique>(); + } + this->raw_callback_->add(std::move(callback)); } std::string TextSensor::get_state() const { return this->state; } std::string TextSensor::get_raw_state() const { return this->raw_state; } void TextSensor::internal_send_state_to_frontend(const std::string &state) { this->state = state; - this->has_state_ = true; + this->set_has_state(true); ESP_LOGD(TAG, "'%s': Sending state '%s'", this->name_.c_str(), state.c_str()); this->callback_.call(state); } std::string TextSensor::unique_id() { return ""; } -bool TextSensor::has_state() { return this->has_state_; } } // namespace text_sensor } // namespace esphome diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h index bd72ea70e3..b27145aa18 100644 --- a/esphome/components/text_sensor/text_sensor.h +++ b/esphome/components/text_sensor/text_sensor.h @@ -6,6 +6,7 @@ #include "esphome/components/text_sensor/filter.h" #include +#include namespace esphome { namespace text_sensor { @@ -33,6 +34,8 @@ namespace text_sensor { class TextSensor : public EntityBase, public EntityBase_DeviceClass { public: + TextSensor() = default; + /// Getter-syntax for .state. std::string get_state() const; /// Getter-syntax for .raw_state @@ -67,17 +70,14 @@ class TextSensor : public EntityBase, public EntityBase_DeviceClass { */ virtual std::string unique_id(); - bool has_state(); - void internal_send_state_to_frontend(const std::string &state); protected: - CallbackManager raw_callback_; ///< Storage for raw state callbacks. - CallbackManager callback_; ///< Storage for filtered state callbacks. + std::unique_ptr> + raw_callback_; ///< Storage for raw state callbacks (lazy allocated). + CallbackManager callback_; ///< Storage for filtered state callbacks. Filter *filter_list_{nullptr}; ///< Store all active filters. - - bool has_state_{false}; }; } // namespace text_sensor diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index f7b3410df9..fe6ed8b159 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -537,7 +537,7 @@ void ThermostatClimate::switch_to_supplemental_action_(climate::ClimateAction ac default: return; } - ESP_LOGVV(TAG, "Updating supplemental action..."); + ESP_LOGVV(TAG, "Updating supplemental action"); this->supplemental_action_ = action; this->trigger_supplemental_action_(); } @@ -1300,37 +1300,48 @@ void ThermostatClimate::dump_config() { } ESP_LOGCONFIG(TAG, " Start-up Delay Enabled: %s", YESNO(this->use_startup_delay_)); if (this->supports_cool_) { - ESP_LOGCONFIG(TAG, " Cooling Parameters:"); - ESP_LOGCONFIG(TAG, " Deadband: %.1f°C", this->cooling_deadband_); - ESP_LOGCONFIG(TAG, " Overrun: %.1f°C", this->cooling_overrun_); + ESP_LOGCONFIG(TAG, + " Cooling Parameters:\n" + " Deadband: %.1f°C\n" + " Overrun: %.1f°C", + this->cooling_deadband_, this->cooling_overrun_); if ((this->supplemental_cool_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) > 0)) { - ESP_LOGCONFIG(TAG, " Supplemental Delta: %.1f°C", this->supplemental_cool_delta_); - ESP_LOGCONFIG(TAG, " Maximum Run Time: %" PRIu32 "s", + ESP_LOGCONFIG(TAG, + " Supplemental Delta: %.1f°C\n" + " Maximum Run Time: %" PRIu32 "s", + this->supplemental_cool_delta_, this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) / 1000); } - ESP_LOGCONFIG(TAG, " Minimum Off Time: %" PRIu32 "s", - this->timer_duration_(thermostat::TIMER_COOLING_OFF) / 1000); - ESP_LOGCONFIG(TAG, " Minimum Run Time: %" PRIu32 "s", + ESP_LOGCONFIG(TAG, + " Minimum Off Time: %" PRIu32 "s\n" + " Minimum Run Time: %" PRIu32 "s", + this->timer_duration_(thermostat::TIMER_COOLING_OFF) / 1000, this->timer_duration_(thermostat::TIMER_COOLING_ON) / 1000); } if (this->supports_heat_) { - ESP_LOGCONFIG(TAG, " Heating Parameters:"); - ESP_LOGCONFIG(TAG, " Deadband: %.1f°C", this->heating_deadband_); - ESP_LOGCONFIG(TAG, " Overrun: %.1f°C", this->heating_overrun_); + ESP_LOGCONFIG(TAG, + " Heating Parameters:\n" + " Deadband: %.1f°C\n" + " Overrun: %.1f°C", + this->heating_deadband_, this->heating_overrun_); if ((this->supplemental_heat_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) > 0)) { - ESP_LOGCONFIG(TAG, " Supplemental Delta: %.1f°C", this->supplemental_heat_delta_); - ESP_LOGCONFIG(TAG, " Maximum Run Time: %" PRIu32 "s", + ESP_LOGCONFIG(TAG, + " Supplemental Delta: %.1f°C\n" + " Maximum Run Time: %" PRIu32 "s", + this->supplemental_heat_delta_, this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) / 1000); } - ESP_LOGCONFIG(TAG, " Minimum Off Time: %" PRIu32 "s", - this->timer_duration_(thermostat::TIMER_HEATING_OFF) / 1000); - ESP_LOGCONFIG(TAG, " Minimum Run Time: %" PRIu32 "s", + ESP_LOGCONFIG(TAG, + " Minimum Off Time: %" PRIu32 "s\n" + " Minimum Run Time: %" PRIu32 "s", + this->timer_duration_(thermostat::TIMER_HEATING_OFF) / 1000, this->timer_duration_(thermostat::TIMER_HEATING_ON) / 1000); } if (this->supports_fan_only_) { - ESP_LOGCONFIG(TAG, " Fanning Minimum Off Time: %" PRIu32 "s", - this->timer_duration_(thermostat::TIMER_FANNING_OFF) / 1000); - ESP_LOGCONFIG(TAG, " Fanning Minimum Run Time: %" PRIu32 "s", + ESP_LOGCONFIG(TAG, + " Fanning Minimum Off Time: %" PRIu32 "s\n" + " Fanning Minimum Run Time: %" PRIu32 "s", + this->timer_duration_(thermostat::TIMER_FANNING_OFF) / 1000, this->timer_duration_(thermostat::TIMER_FANNING_ON) / 1000); } if (this->supports_fan_mode_on_ || this->supports_fan_mode_off_ || this->supports_fan_mode_auto_ || @@ -1341,14 +1352,17 @@ void ThermostatClimate::dump_config() { this->timer_duration_(thermostat::TIMER_FAN_MODE) / 1000); } ESP_LOGCONFIG(TAG, " Minimum Idle Time: %" PRIu32 "s", this->timer_[thermostat::TIMER_IDLE_ON].time / 1000); - ESP_LOGCONFIG(TAG, " Supports AUTO: %s", YESNO(this->supports_auto_)); - ESP_LOGCONFIG(TAG, " Supports HEAT/COOL: %s", YESNO(this->supports_heat_cool_)); - ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_)); - ESP_LOGCONFIG(TAG, " Supports DRY: %s", YESNO(this->supports_dry_)); - ESP_LOGCONFIG(TAG, " Supports FAN_ONLY: %s", YESNO(this->supports_fan_only_)); - ESP_LOGCONFIG(TAG, " Supports FAN_ONLY_ACTION_USES_FAN_MODE_TIMER: %s", - YESNO(this->supports_fan_only_action_uses_fan_mode_timer_)); - ESP_LOGCONFIG(TAG, " Supports FAN_ONLY_COOLING: %s", YESNO(this->supports_fan_only_cooling_)); + ESP_LOGCONFIG(TAG, + " Supports AUTO: %s\n" + " Supports HEAT/COOL: %s\n" + " Supports COOL: %s\n" + " Supports DRY: %s\n" + " Supports FAN_ONLY: %s\n" + " Supports FAN_ONLY_ACTION_USES_FAN_MODE_TIMER: %s\n" + " Supports FAN_ONLY_COOLING: %s", + YESNO(this->supports_auto_), YESNO(this->supports_heat_cool_), YESNO(this->supports_cool_), + YESNO(this->supports_dry_), YESNO(this->supports_fan_only_), + YESNO(this->supports_fan_only_action_uses_fan_mode_timer_), YESNO(this->supports_fan_only_cooling_)); if (this->supports_cool_) { ESP_LOGCONFIG(TAG, " Supports FAN_WITH_COOLING: %s", YESNO(this->supports_fan_with_cooling_)); } @@ -1356,21 +1370,31 @@ void ThermostatClimate::dump_config() { ESP_LOGCONFIG(TAG, " Supports FAN_WITH_HEATING: %s", YESNO(this->supports_fan_with_heating_)); } ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE ON: %s", YESNO(this->supports_fan_mode_on_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE OFF: %s", YESNO(this->supports_fan_mode_off_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE AUTO: %s", YESNO(this->supports_fan_mode_auto_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE LOW: %s", YESNO(this->supports_fan_mode_low_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE MEDIUM: %s", YESNO(this->supports_fan_mode_medium_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE HIGH: %s", YESNO(this->supports_fan_mode_high_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE MIDDLE: %s", YESNO(this->supports_fan_mode_middle_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE FOCUS: %s", YESNO(this->supports_fan_mode_focus_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE DIFFUSE: %s", YESNO(this->supports_fan_mode_diffuse_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE QUIET: %s", YESNO(this->supports_fan_mode_quiet_)); - ESP_LOGCONFIG(TAG, " Supports SWING MODE BOTH: %s", YESNO(this->supports_swing_mode_both_)); - ESP_LOGCONFIG(TAG, " Supports SWING MODE OFF: %s", YESNO(this->supports_swing_mode_off_)); - ESP_LOGCONFIG(TAG, " Supports SWING MODE HORIZONTAL: %s", YESNO(this->supports_swing_mode_horizontal_)); - ESP_LOGCONFIG(TAG, " Supports SWING MODE VERTICAL: %s", YESNO(this->supports_swing_mode_vertical_)); - ESP_LOGCONFIG(TAG, " Supports TWO SET POINTS: %s", YESNO(this->supports_two_points_)); + ESP_LOGCONFIG(TAG, + " Supports FAN MODE ON: %s\n" + " Supports FAN MODE OFF: %s\n" + " Supports FAN MODE AUTO: %s\n" + " Supports FAN MODE LOW: %s\n" + " Supports FAN MODE MEDIUM: %s\n" + " Supports FAN MODE HIGH: %s\n" + " Supports FAN MODE MIDDLE: %s\n" + " Supports FAN MODE FOCUS: %s\n" + " Supports FAN MODE DIFFUSE: %s\n" + " Supports FAN MODE QUIET: %s", + YESNO(this->supports_fan_mode_on_), YESNO(this->supports_fan_mode_off_), + YESNO(this->supports_fan_mode_auto_), YESNO(this->supports_fan_mode_low_), + YESNO(this->supports_fan_mode_medium_), YESNO(this->supports_fan_mode_high_), + YESNO(this->supports_fan_mode_middle_), YESNO(this->supports_fan_mode_focus_), + YESNO(this->supports_fan_mode_diffuse_), YESNO(this->supports_fan_mode_quiet_)); + ESP_LOGCONFIG(TAG, + " Supports SWING MODE BOTH: %s\n" + " Supports SWING MODE OFF: %s\n" + " Supports SWING MODE HORIZONTAL: %s\n" + " Supports SWING MODE VERTICAL: %s\n" + " Supports TWO SET POINTS: %s", + YESNO(this->supports_swing_mode_both_), YESNO(this->supports_swing_mode_off_), + YESNO(this->supports_swing_mode_horizontal_), YESNO(this->supports_swing_mode_vertical_), + YESNO(this->supports_two_points_)); ESP_LOGCONFIG(TAG, " Supported PRESETS: "); for (auto &it : this->preset_config_) { diff --git a/esphome/components/time_based/time_based_cover.cpp b/esphome/components/time_based/time_based_cover.cpp index ec219d6db7..1eb591fe6e 100644 --- a/esphome/components/time_based/time_based_cover.cpp +++ b/esphome/components/time_based/time_based_cover.cpp @@ -12,8 +12,10 @@ using namespace esphome::cover; void TimeBasedCover::dump_config() { LOG_COVER("", "Time Based Cover", this); - ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f); - ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f); + ESP_LOGCONFIG(TAG, + " Open Duration: %.1fs\n" + " Close Duration: %.1fs", + this->open_duration_ / 1e3f, this->close_duration_ / 1e3f); } void TimeBasedCover::setup() { auto restore = this->restore_state_(); diff --git a/esphome/components/tlc59208f/tlc59208f_output.cpp b/esphome/components/tlc59208f/tlc59208f_output.cpp index 3441a41a59..b1aad42bd7 100644 --- a/esphome/components/tlc59208f/tlc59208f_output.cpp +++ b/esphome/components/tlc59208f/tlc59208f_output.cpp @@ -1,7 +1,7 @@ #include "tlc59208f_output.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tlc59208f { @@ -73,7 +73,7 @@ static const uint8_t LDR_GRPPWM = 0x03; void TLC59208FOutput::setup() { ESP_LOGCONFIG(TAG, "Running setup"); - ESP_LOGV(TAG, " Resetting all devices on the bus..."); + ESP_LOGV(TAG, " Resetting all devices on the bus"); // Reset all devices on the bus if (this->bus_->write(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ, 2) != i2c::ERROR_OK) { @@ -111,8 +111,10 @@ void TLC59208FOutput::setup() { } void TLC59208FOutput::dump_config() { - ESP_LOGCONFIG(TAG, "TLC59208F:"); - ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_); + ESP_LOGCONFIG(TAG, + "TLC59208F:\n" + " Mode: 0x%02X", + this->mode_); if (this->is_failed()) { ESP_LOGE(TAG, "Setting up TLC59208F failed!"); diff --git a/esphome/components/tlc5971/tlc5971.cpp b/esphome/components/tlc5971/tlc5971.cpp index ebcc3af361..05ff0a0080 100644 --- a/esphome/components/tlc5971/tlc5971.cpp +++ b/esphome/components/tlc5971/tlc5971.cpp @@ -24,8 +24,10 @@ void TLC5971::dump_config() { } void TLC5971::loop() { - if (!this->update_) + if (!this->update_) { + this->disable_loop(); return; + } uint32_t command; @@ -93,6 +95,7 @@ void TLC5971::set_channel_value(uint16_t channel, uint16_t value) { return; if (this->pwm_amounts_[channel] != value) { this->update_ = true; + this->enable_loop(); } this->pwm_amounts_[channel] = value; } diff --git a/esphome/components/tm1621/tm1621.cpp b/esphome/components/tm1621/tm1621.cpp index 8f18a3d384..502e45b35e 100644 --- a/esphome/components/tm1621/tm1621.cpp +++ b/esphome/components/tm1621/tm1621.cpp @@ -1,7 +1,7 @@ #include "tm1621.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tm1621 { diff --git a/esphome/components/tm1637/tm1637.cpp b/esphome/components/tm1637/tm1637.cpp index db117645bd..358a683efb 100644 --- a/esphome/components/tm1637/tm1637.cpp +++ b/esphome/components/tm1637/tm1637.cpp @@ -1,7 +1,7 @@ #include "tm1637.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tm1637 { @@ -135,10 +135,12 @@ void TM1637Display::setup() { this->display(); } void TM1637Display::dump_config() { - ESP_LOGCONFIG(TAG, "TM1637:"); - ESP_LOGCONFIG(TAG, " Intensity: %d", this->intensity_); - ESP_LOGCONFIG(TAG, " Inverted: %d", this->inverted_); - ESP_LOGCONFIG(TAG, " Length: %d", this->length_); + ESP_LOGCONFIG(TAG, + "TM1637:\n" + " Intensity: %d\n" + " Inverted: %d\n" + " Length: %d", + this->intensity_, this->inverted_, this->length_); LOG_PIN(" CLK Pin: ", this->clk_pin_); LOG_PIN(" DIO Pin: ", this->dio_pin_); LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/tm1638/tm1638.cpp b/esphome/components/tm1638/tm1638.cpp index a15b006046..f43b496b35 100644 --- a/esphome/components/tm1638/tm1638.cpp +++ b/esphome/components/tm1638/tm1638.cpp @@ -1,8 +1,8 @@ #include "tm1638.h" #include "sevenseg.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tm1638 { @@ -20,7 +20,7 @@ static const uint8_t TM1638_UNKNOWN_CHAR = 0b11111111; static const uint8_t TM1638_SHIFT_DELAY = 4; // clock pause between commands, default 4ms void TM1638Component::setup() { - ESP_LOGD(TAG, "Setting up TM1638..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->clk_pin_->setup(); // OUTPUT this->dio_pin_->setup(); // OUTPUT @@ -43,8 +43,10 @@ void TM1638Component::setup() { } void TM1638Component::dump_config() { - ESP_LOGCONFIG(TAG, "TM1638:"); - ESP_LOGCONFIG(TAG, " Intensity: %u", this->intensity_); + ESP_LOGCONFIG(TAG, + "TM1638:\n" + " Intensity: %u", + this->intensity_); LOG_PIN(" CLK Pin: ", this->clk_pin_); LOG_PIN(" DIO Pin: ", this->dio_pin_); LOG_PIN(" STB Pin: ", this->stb_pin_); diff --git a/esphome/components/tm1651/tm1651.cpp b/esphome/components/tm1651/tm1651.cpp index 97beefff64..64c3e62b32 100644 --- a/esphome/components/tm1651/tm1651.cpp +++ b/esphome/components/tm1651/tm1651.cpp @@ -1,8 +1,8 @@ #ifdef USE_ARDUINO #include "tm1651.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tm1651 { diff --git a/esphome/components/tmp1075/tmp1075.cpp b/esphome/components/tmp1075/tmp1075.cpp index b8f80e4abe..831f905bd2 100644 --- a/esphome/components/tmp1075/tmp1075.cpp +++ b/esphome/components/tmp1075/tmp1075.cpp @@ -47,14 +47,17 @@ void TMP1075Sensor::dump_config() { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); return; } - ESP_LOGCONFIG(TAG, " limit low : %.4f °C", alert_limit_low_); - ESP_LOGCONFIG(TAG, " limit high : %.4f °C", alert_limit_high_); - ESP_LOGCONFIG(TAG, " oneshot : %d", config_.fields.oneshot); - ESP_LOGCONFIG(TAG, " rate : %d", config_.fields.rate); - ESP_LOGCONFIG(TAG, " fault_count: %d", config_.fields.faults); - ESP_LOGCONFIG(TAG, " polarity : %d", config_.fields.polarity); - ESP_LOGCONFIG(TAG, " alert_mode : %d", config_.fields.alert_mode); - ESP_LOGCONFIG(TAG, " shutdown : %d", config_.fields.shutdown); + ESP_LOGCONFIG(TAG, + " limit low : %.4f °C\n" + " limit high : %.4f °C\n" + " oneshot : %d\n" + " rate : %d\n" + " fault_count: %d\n" + " polarity : %d\n" + " alert_mode : %d\n" + " shutdown : %d", + alert_limit_low_, alert_limit_high_, config_.fields.oneshot, config_.fields.rate, config_.fields.faults, + config_.fields.polarity, config_.fields.alert_mode, config_.fields.shutdown); } void TMP1075Sensor::set_fault_count(const int faults) { diff --git a/esphome/components/tormatic/tormatic_cover.cpp b/esphome/components/tormatic/tormatic_cover.cpp index 35224c8ec7..be412d62a8 100644 --- a/esphome/components/tormatic/tormatic_cover.cpp +++ b/esphome/components/tormatic/tormatic_cover.cpp @@ -34,8 +34,10 @@ void Tormatic::dump_config() { LOG_COVER("", "Tormatic Cover", this); this->check_uart_settings(9600, 1, uart::UART_CONFIG_PARITY_NONE, 8); - ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f); - ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f); + ESP_LOGCONFIG(TAG, + " Open Duration: %.1fs\n" + " Close Duration: %.1fs", + this->open_duration_ / 1e3f, this->close_duration_ / 1e3f); auto restore = this->restore_state_(); if (restore.has_value()) { diff --git a/esphome/components/tsl2561/tsl2561.cpp b/esphome/components/tsl2561/tsl2561.cpp index 4a24153dd5..1b5c9f2635 100644 --- a/esphome/components/tsl2561/tsl2561.cpp +++ b/esphome/components/tsl2561/tsl2561.cpp @@ -45,8 +45,10 @@ void TSL2561Sensor::dump_config() { } int gain = this->gain_ == TSL2561_GAIN_1X ? 1 : 16; - ESP_LOGCONFIG(TAG, " Gain: %dx", gain); - ESP_LOGCONFIG(TAG, " Integration Time: %.1f ms", this->get_integration_time_ms_()); + ESP_LOGCONFIG(TAG, + " Gain: %dx\n" + " Integration Time: %.1f ms", + gain, this->get_integration_time_ms_()); LOG_UPDATE_INTERVAL(this); } @@ -65,7 +67,7 @@ void TSL2561Sensor::update() { float TSL2561Sensor::calculate_lx_(uint16_t ch0, uint16_t ch1) { if ((ch0 == 0xFFFF) || (ch1 == 0xFFFF)) { - ESP_LOGW(TAG, "TSL2561 sensor is saturated."); + ESP_LOGW(TAG, "Sensor is saturated"); return NAN; } diff --git a/esphome/components/tsl2591/tsl2591.cpp b/esphome/components/tsl2591/tsl2591.cpp index 7799d727ba..1734d83dd2 100644 --- a/esphome/components/tsl2591/tsl2591.cpp +++ b/esphome/components/tsl2591/tsl2591.cpp @@ -26,13 +26,13 @@ static const char *const TAG = "tsl2591.sensor"; void TSL2591Component::enable() { // Enable the device by setting the control bit to 0x01. Also turn on ADCs. if (!this->write_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_ENABLE, TSL2591_ENABLE_POWERON | TSL2591_ENABLE_AEN)) { - ESP_LOGE(TAG, "Failed I2C write during enable()"); + ESP_LOGE(TAG, "I2C write failed"); } } void TSL2591Component::disable() { if (!this->write_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_ENABLE, TSL2591_ENABLE_POWEROFF)) { - ESP_LOGE(TAG, "Failed I2C write during disable()"); + ESP_LOGE(TAG, "I2C write failed"); } } @@ -43,6 +43,7 @@ void TSL2591Component::disable_if_power_saving_() { } void TSL2591Component::setup() { + ESP_LOGCONFIG(TAG, "Running setup for address 0x%02X", this->address_); switch (this->component_gain_) { case TSL2591_CGAIN_LOW: this->gain_ = TSL2591_GAIN_LOW; @@ -61,21 +62,15 @@ void TSL2591Component::setup() { break; } - uint8_t address = this->address_; - ESP_LOGI(TAG, "Setting up TSL2591 sensor at I2C address 0x%02X", address); - uint8_t id; if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_DEVICE_ID, &id)) { - ESP_LOGE(TAG, "Failed I2C read during setup()"); + ESP_LOGE(TAG, "I2C read failed"); this->mark_failed(); return; } if (id != 0x50) { - ESP_LOGE(TAG, - "Could not find the TSL2591 sensor. The ID register of the device at address 0x%02X reported 0x%02X " - "instead of 0x50.", - address, id); + ESP_LOGE(TAG, "Unknown chip ID"); this->mark_failed(); return; } @@ -119,13 +114,16 @@ void TSL2591Component::dump_config() { gain_word = "auto"; break; } - ESP_LOGCONFIG(TAG, " Gain: %dx (%s)", gain, gain_word.c_str()); TSL2591IntegrationTime raw_timing = this->integration_time_; int timing_ms = (1 + raw_timing) * 100; - ESP_LOGCONFIG(TAG, " Integration Time: %d ms", timing_ms); - ESP_LOGCONFIG(TAG, " Power save mode enabled: %s", ONOFF(this->power_save_mode_enabled_)); - ESP_LOGCONFIG(TAG, " Device factor: %f", this->device_factor_); - ESP_LOGCONFIG(TAG, " Glass attenuation factor: %f", this->glass_attenuation_factor_); + ESP_LOGCONFIG(TAG, + " Gain: %dx (%s)" + " Integration Time: %d ms\n" + " Power save mode enabled: %s\n" + " Device factor: %f\n" + " Glass attenuation factor: %f", + gain, gain_word.c_str(), timing_ms, ONOFF(this->power_save_mode_enabled_), this->device_factor_, + this->glass_attenuation_factor_); LOG_SENSOR(" ", "Full spectrum:", this->full_spectrum_sensor_); LOG_SENSOR(" ", "Infrared:", this->infrared_sensor_); LOG_SENSOR(" ", "Visible:", this->visible_sensor_); @@ -174,7 +172,7 @@ void TSL2591Component::interval_function_for_update_() { uint64_t now = millis(); ESP_LOGD(TAG, "Elapsed %3llu ms; still waiting for valid ADC", (now - this->interval_start_)); if (now > this->interval_timeout_) { - ESP_LOGW(TAG, "Interval timeout for TSL2591 '%s' expired before ADCs became valid.", this->name_); + ESP_LOGW(TAG, "Interval timeout for '%s' expired before ADCs became valid", this->name_); this->cancel_interval(interval_name); } return; @@ -235,7 +233,7 @@ void TSL2591Component::set_integration_time_and_gain(TSL2591IntegrationTime inte this->gain_ = gain; if (!this->write_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CONTROL, this->integration_time_ | this->gain_)) { // NOLINT - ESP_LOGE(TAG, "Failed I2C write during set_integration_time_and_gain()"); + ESP_LOGE(TAG, "I2C write failed"); } // The ADC values can be confused if gain or integration time are changed in the middle of a cycle. // So, we unconditionally disable the device to turn the ADCs off. When re-enabling, the ADCs @@ -255,7 +253,7 @@ float TSL2591Component::get_setup_priority() const { return setup_priority::DATA bool TSL2591Component::is_adc_valid() { uint8_t status; if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_STATUS, &status)) { - ESP_LOGE(TAG, "Failed I2C read during is_adc_valid()"); + ESP_LOGE(TAG, "I2C read failed"); return false; } return status & 0x01; @@ -281,7 +279,7 @@ uint32_t TSL2591Component::get_combined_illuminance() { if (!avalid) { // still not valid after a sutiable delay // we don't mark the device as failed since it might come around in the future (probably not :-() - ESP_LOGE(TAG, "tsl2591 device '%s' did not return valid readings.", this->name_); + ESP_LOGE(TAG, "Device '%s' returned invalid readings", this->name_); this->disable_if_power_saving_(); return 0; } @@ -295,20 +293,20 @@ uint32_t TSL2591Component::get_combined_illuminance() { uint16_t ch0_16; uint16_t ch1_16; if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CHAN0_LOW, &ch0low)) { - ESP_LOGE(TAG, "Failed I2C read during get_combined_illuminance()"); + ESP_LOGE(TAG, "I2C read failed"); return 0; } if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CHAN0_HIGH, &ch0high)) { - ESP_LOGE(TAG, "Failed I2C read during get_combined_illuminance()"); + ESP_LOGE(TAG, "I2C read failed"); return 0; } ch0_16 = (ch0high << 8) | ch0low; if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CHAN1_LOW, &ch1low)) { - ESP_LOGE(TAG, "Failed I2C read during get_combined_illuminance()"); + ESP_LOGE(TAG, "I2C read failed"); return 0; } if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CHAN1_HIGH, &ch1high)) { - ESP_LOGE(TAG, "Failed I2C read during get_combined_illuminance()"); + ESP_LOGE(TAG, "I2C read failed"); return 0; } ch1_16 = (ch1high << 8) | ch1low; @@ -335,7 +333,7 @@ uint16_t TSL2591Component::get_illuminance(TSL2591SensorChannel channel, uint32_ return ((combined_illuminance & 0xFFFF) - (combined_illuminance >> 16)); } // unknown channel! - ESP_LOGE(TAG, "TSL2591Component::get_illuminance() caller requested an unknown channel: %d", channel); + ESP_LOGE(TAG, "get_illuminance() caller requested an unknown channel: %d", channel); return 0; } @@ -358,13 +356,13 @@ float TSL2591Component::get_calculated_lux(uint16_t full_spectrum, uint16_t infr uint16_t max_count = (this->integration_time_ == TSL2591_INTEGRATION_TIME_100MS ? 36863 : 65535); if ((full_spectrum == max_count) || (infrared == max_count)) { // Signal an overflow - ESP_LOGW(TAG, "Apparent saturation on TSL2591 (%s). You could reduce the gain or integration time.", this->name_); + ESP_LOGW(TAG, "Apparent saturation on '%s'; try reducing the gain or integration time", this->name_); return NAN; } if ((full_spectrum == 0) && (infrared == 0)) { // trivial conversion; avoids divide by 0 - ESP_LOGW(TAG, "Zero reading on both TSL2591 (%s) sensors. Is the device having a problem?", this->name_); + ESP_LOGW(TAG, "Zero reading on both '%s' sensors", this->name_); return 0.0F; } diff --git a/esphome/components/tuya/select/tuya_select.cpp b/esphome/components/tuya/select/tuya_select.cpp index 02643e97f4..07b0ff2815 100644 --- a/esphome/components/tuya/select/tuya_select.cpp +++ b/esphome/components/tuya/select/tuya_select.cpp @@ -44,9 +44,11 @@ void TuyaSelect::control(const std::string &value) { void TuyaSelect::dump_config() { LOG_SELECT("", "Tuya Select", this); - ESP_LOGCONFIG(TAG, " Select has datapoint ID %u", this->select_id_); - ESP_LOGCONFIG(TAG, " Data type: %s", this->is_int_ ? "int" : "enum"); - ESP_LOGCONFIG(TAG, " Options are:"); + ESP_LOGCONFIG(TAG, + " Select has datapoint ID %u\n" + " Data type: %s\n" + " Options are:", + this->select_id_, this->is_int_ ? "int" : "enum"); auto options = this->traits.get_options(); for (auto i = 0; i < this->mappings_.size(); i++) { ESP_LOGCONFIG(TAG, " %i: %s", this->mappings_.at(i), options.at(i).c_str()); diff --git a/esphome/components/tx20/tx20.cpp b/esphome/components/tx20/tx20.cpp index 4d614c1e4e..73b865e8b8 100644 --- a/esphome/components/tx20/tx20.cpp +++ b/esphome/components/tx20/tx20.cpp @@ -1,6 +1,6 @@ #include "tx20.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include @@ -44,7 +44,7 @@ float Tx20Component::get_setup_priority() const { return setup_priority::DATA; } std::string Tx20Component::get_wind_cardinal_direction() const { return this->wind_cardinal_direction_; } void Tx20Component::decode_and_publish_() { - ESP_LOGVV(TAG, "Decode Tx20..."); + ESP_LOGVV(TAG, "Decode Tx20"); std::string string_buffer; std::string string_buffer_2; diff --git a/esphome/components/uart/button/uart_button.cpp b/esphome/components/uart/button/uart_button.cpp index 4db164c400..dd228b9bb7 100644 --- a/esphome/components/uart/button/uart_button.cpp +++ b/esphome/components/uart/button/uart_button.cpp @@ -7,7 +7,7 @@ namespace uart { static const char *const TAG = "uart.button"; void UARTButton::press_action() { - ESP_LOGD(TAG, "'%s': Sending data...", this->get_name().c_str()); + ESP_LOGD(TAG, "'%s': Sending data", this->get_name().c_str()); this->write_array(this->data_.data(), this->data_.size()); } diff --git a/esphome/components/uart/switch/uart_switch.cpp b/esphome/components/uart/switch/uart_switch.cpp index 96f50ff50f..4f5ff9fc99 100644 --- a/esphome/components/uart/switch/uart_switch.cpp +++ b/esphome/components/uart/switch/uart_switch.cpp @@ -19,11 +19,11 @@ void UARTSwitch::loop() { void UARTSwitch::write_command_(bool state) { if (state && !this->data_on_.empty()) { - ESP_LOGD(TAG, "'%s': Sending on data...", this->get_name().c_str()); + ESP_LOGD(TAG, "'%s': Sending on data", this->get_name().c_str()); this->write_array(this->data_on_.data(), this->data_on_.size()); } if (!state && !this->data_off_.empty()) { - ESP_LOGD(TAG, "'%s': Sending off data...", this->get_name().c_str()); + ESP_LOGD(TAG, "'%s': Sending off data", this->get_name().c_str()); this->write_array(this->data_off_.data(), this->data_off_.size()); } } diff --git a/esphome/components/uart/uart.cpp b/esphome/components/uart/uart.cpp index 9834462ff9..b18454bf9d 100644 --- a/esphome/components/uart/uart.cpp +++ b/esphome/components/uart/uart.cpp @@ -1,8 +1,8 @@ #include "uart.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/application.h" #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include namespace esphome { diff --git a/esphome/components/uart/uart_component_esp32_arduino.cpp b/esphome/components/uart/uart_component_esp32_arduino.cpp index 0ed1a724e9..7441d8c1b3 100644 --- a/esphome/components/uart/uart_component_esp32_arduino.cpp +++ b/esphome/components/uart/uart_component_esp32_arduino.cpp @@ -154,10 +154,12 @@ void ESP32ArduinoUARTComponent::dump_config() { if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); } - ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + ESP_LOGCONFIG(TAG, + " Baud Rate: %u baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %u", + this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_); this->check_logger_conflict(); } @@ -191,7 +193,7 @@ bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) { int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); } void ESP32ArduinoUARTComponent::flush() { - ESP_LOGVV(TAG, " Flushing..."); + ESP_LOGVV(TAG, " Flushing"); this->hw_serial_->flush(); } diff --git a/esphome/components/uart/uart_component_esp8266.cpp b/esphome/components/uart/uart_component_esp8266.cpp index b06f553a47..7f4cc7b37c 100644 --- a/esphome/components/uart/uart_component_esp8266.cpp +++ b/esphome/components/uart/uart_component_esp8266.cpp @@ -99,7 +99,7 @@ void ESP8266UartComponent::setup() { } void ESP8266UartComponent::load_settings(bool dump_config) { - ESP_LOGCONFIG(TAG, "Loading UART bus settings..."); + ESP_LOGCONFIG(TAG, "Loading UART bus settings"); if (this->hw_serial_ != nullptr) { SerialConfig config = static_cast(get_config()); this->hw_serial_->begin(this->baud_rate_, config); @@ -121,10 +121,12 @@ void ESP8266UartComponent::dump_config() { if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); // NOLINT } - ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + ESP_LOGCONFIG(TAG, + " Baud Rate: %u baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %u", + this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_); if (this->hw_serial_ != nullptr) { ESP_LOGCONFIG(TAG, " Using hardware serial interface."); } else { @@ -193,7 +195,7 @@ int ESP8266UartComponent::available() { } } void ESP8266UartComponent::flush() { - ESP_LOGVV(TAG, " Flushing..."); + ESP_LOGVV(TAG, " Flushing"); if (this->hw_serial_ != nullptr) { this->hw_serial_->flush(); } else { diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 9d5d22da6b..84bd48d530 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -162,10 +162,12 @@ void IDFUARTComponent::dump_config() { if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); } - ESP_LOGCONFIG(TAG, " Baud Rate: %" PRIu32 " baud", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + ESP_LOGCONFIG(TAG, + " Baud Rate: %" PRIu32 " baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %u", + this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_); this->check_logger_conflict(); } @@ -234,7 +236,7 @@ int IDFUARTComponent::available() { } void IDFUARTComponent::flush() { - ESP_LOGVV(TAG, " Flushing..."); + ESP_LOGVV(TAG, " Flushing"); xSemaphoreTake(this->lock_, portMAX_DELAY); uart_wait_tx_done(this->uart_num_, portMAX_DELAY); xSemaphoreGive(this->lock_); diff --git a/esphome/components/uart/uart_component_host.cpp b/esphome/components/uart/uart_component_host.cpp index 40d3e91ab2..adb11266c5 100644 --- a/esphome/components/uart/uart_component_host.cpp +++ b/esphome/components/uart/uart_component_host.cpp @@ -109,7 +109,7 @@ HostUartComponent::~HostUartComponent() { } void HostUartComponent::setup() { - ESP_LOGCONFIG(TAG, "Opening UART port..."); + ESP_LOGCONFIG(TAG, "Opening UART port"); speed_t baud = get_baud(this->baud_rate_); if (baud == B0) { ESP_LOGE(TAG, "Unsupported baud rate: %d", this->baud_rate_); @@ -188,14 +188,17 @@ void HostUartComponent::dump_config() { } return; } - ESP_LOGCONFIG(TAG, " Port status: opened"); - ESP_LOGCONFIG(TAG, " Baud Rate: %d", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %d", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", + ESP_LOGCONFIG(TAG, + " Port status: opened\n" + " Baud Rate: %d\n" + " Data Bits: %d\n" + " Parity: %s\n" + " Stop Bits: %d", + this->baud_rate_, this->data_bits_, this->parity_ == UART_CONFIG_PARITY_NONE ? "None" : this->parity_ == UART_CONFIG_PARITY_EVEN ? "Even" - : "Odd"); - ESP_LOGCONFIG(TAG, " Stop Bits: %d", this->stop_bits_); + : "Odd", + this->stop_bits_); this->check_logger_conflict(); } @@ -283,7 +286,7 @@ void HostUartComponent::flush() { return; } tcflush(this->file_descriptor_, TCIOFLUSH); - ESP_LOGV(TAG, " Flushing..."); + ESP_LOGV(TAG, " Flushing"); } void HostUartComponent::update_error_(const std::string &error) { diff --git a/esphome/components/uart/uart_component_libretiny.cpp b/esphome/components/uart/uart_component_libretiny.cpp index bfb183d964..ffdb329669 100644 --- a/esphome/components/uart/uart_component_libretiny.cpp +++ b/esphome/components/uart/uart_component_libretiny.cpp @@ -108,10 +108,12 @@ void LibreTinyUARTComponent::dump_config() { if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); } - ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + ESP_LOGCONFIG(TAG, + " Baud Rate: %u baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %u", + this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_); this->check_logger_conflict(); } @@ -145,7 +147,7 @@ bool LibreTinyUARTComponent::read_array(uint8_t *data, size_t len) { int LibreTinyUARTComponent::available() { return this->serial_->available(); } void LibreTinyUARTComponent::flush() { - ESP_LOGVV(TAG, " Flushing..."); + ESP_LOGVV(TAG, " Flushing"); this->serial_->flush(); } diff --git a/esphome/components/uart/uart_component_rp2040.cpp b/esphome/components/uart/uart_component_rp2040.cpp index c1a2e50ae0..f375d4a93f 100644 --- a/esphome/components/uart/uart_component_rp2040.cpp +++ b/esphome/components/uart/uart_component_rp2040.cpp @@ -136,10 +136,12 @@ void RP2040UartComponent::dump_config() { if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); } - ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + ESP_LOGCONFIG(TAG, + " Baud Rate: %u baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %u", + this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_); if (this->hw_serial_) { ESP_LOGCONFIG(TAG, " Using hardware serial"); } else { @@ -174,7 +176,7 @@ bool RP2040UartComponent::read_array(uint8_t *data, size_t len) { } int RP2040UartComponent::available() { return this->serial_->available(); } void RP2040UartComponent::flush() { - ESP_LOGVV(TAG, " Flushing..."); + ESP_LOGVV(TAG, " Flushing"); this->serial_->flush(); } diff --git a/esphome/components/udp/udp_component.cpp b/esphome/components/udp/udp_component.cpp index 222c73f82e..62a1189355 100644 --- a/esphome/components/udp/udp_component.cpp +++ b/esphome/components/udp/udp_component.cpp @@ -122,16 +122,20 @@ void UDPComponent::loop() { } void UDPComponent::dump_config() { - ESP_LOGCONFIG(TAG, "UDP:"); - ESP_LOGCONFIG(TAG, " Listen Port: %u", this->listen_port_); - ESP_LOGCONFIG(TAG, " Broadcast Port: %u", this->broadcast_port_); + ESP_LOGCONFIG(TAG, + "UDP:\n" + " Listen Port: %u\n" + " Broadcast Port: %u", + this->listen_port_, this->broadcast_port_); for (const auto &address : this->addresses_) ESP_LOGCONFIG(TAG, " Address: %s", address.c_str()); if (this->listen_address_.has_value()) { ESP_LOGCONFIG(TAG, " Listen address: %s", this->listen_address_.value().str().c_str()); } - ESP_LOGCONFIG(TAG, " Broadcasting: %s", YESNO(this->should_broadcast_)); - ESP_LOGCONFIG(TAG, " Listening: %s", YESNO(this->should_listen_)); + ESP_LOGCONFIG(TAG, + " Broadcasting: %s\n" + " Listening: %s", + YESNO(this->should_broadcast_), YESNO(this->should_listen_)); } void UDPComponent::send_packet(const uint8_t *data, size_t size) { diff --git a/esphome/components/ufire_ec/ufire_ec.cpp b/esphome/components/ufire_ec/ufire_ec.cpp index 6a418e51b7..813e667a00 100644 --- a/esphome/components/ufire_ec/ufire_ec.cpp +++ b/esphome/components/ufire_ec/ufire_ec.cpp @@ -110,8 +110,10 @@ void UFireECComponent::dump_config() { LOG_SENSOR(" ", "EC Sensor", this->ec_sensor_) LOG_SENSOR(" ", "Temperature Sensor", this->temperature_sensor_) LOG_SENSOR(" ", "Temperature Sensor external", this->temperature_sensor_external_) - ESP_LOGCONFIG(TAG, " Temperature Compensation: %f", this->temperature_compensation_); - ESP_LOGCONFIG(TAG, " Temperature Coefficient: %f", this->temperature_coefficient_); + ESP_LOGCONFIG(TAG, + " Temperature Compensation: %f\n" + " Temperature Coefficient: %f", + this->temperature_compensation_, this->temperature_coefficient_); } } // namespace ufire_ec diff --git a/esphome/components/ultrasonic/ultrasonic_sensor.cpp b/esphome/components/ultrasonic/ultrasonic_sensor.cpp index b3ed6833ec..b737dfa4cd 100644 --- a/esphome/components/ultrasonic/ultrasonic_sensor.cpp +++ b/esphome/components/ultrasonic/ultrasonic_sensor.cpp @@ -45,8 +45,10 @@ void UltrasonicSensorComponent::dump_config() { LOG_SENSOR("", "Ultrasonic Sensor", this); LOG_PIN(" Echo Pin: ", this->echo_pin_); LOG_PIN(" Trigger Pin: ", this->trigger_pin_); - ESP_LOGCONFIG(TAG, " Pulse time: %" PRIu32 " µs", this->pulse_time_us_); - ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 " µs", this->timeout_us_); + ESP_LOGCONFIG(TAG, + " Pulse time: %" PRIu32 " µs\n" + " Timeout: %" PRIu32 " µs", + this->pulse_time_us_, this->timeout_us_); LOG_UPDATE_INTERVAL(this); } float UltrasonicSensorComponent::us_to_m(uint32_t us) { diff --git a/esphome/components/update/__init__.py b/esphome/components/update/__init__.py index c2654520fd..09b0698903 100644 --- a/esphome/components/update/__init__.py +++ b/esphome/components/update/__init__.py @@ -111,6 +111,7 @@ async def register_update(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_update(var)) + CORE.register_platform_component("update", var) await setup_update_core_(var, config) diff --git a/esphome/components/update/update_entity.cpp b/esphome/components/update/update_entity.cpp index ed9a0480d8..ce97fb1b77 100644 --- a/esphome/components/update/update_entity.cpp +++ b/esphome/components/update/update_entity.cpp @@ -30,7 +30,7 @@ void UpdateEntity::publish_state() { ESP_LOGD(TAG, " Progress: %.0f%%", this->update_info_.progress); } - this->has_state_ = true; + this->set_has_state(true); this->state_callback_.call(); } diff --git a/esphome/components/update/update_entity.h b/esphome/components/update/update_entity.h index cc269e288f..169e580457 100644 --- a/esphome/components/update/update_entity.h +++ b/esphome/components/update/update_entity.h @@ -28,8 +28,6 @@ enum UpdateState : uint8_t { class UpdateEntity : public EntityBase, public EntityBase_DeviceClass { public: - bool has_state() const { return this->has_state_; } - void publish_state(); void perform() { this->perform(false); } @@ -44,7 +42,6 @@ class UpdateEntity : public EntityBase, public EntityBase_DeviceClass { protected: UpdateState state_{UPDATE_STATE_UNKNOWN}; UpdateInfo update_info_; - bool has_state_{false}; CallbackManager state_callback_{}; }; diff --git a/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp b/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp index cc9b8a0f90..d7e672d8cf 100644 --- a/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +++ b/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp @@ -1,7 +1,7 @@ #include "uponor_smatrix_climate.h" +#include "esphome/core/application.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/application.h" namespace esphome { namespace uponor_smatrix { diff --git a/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp b/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp index 47ff3a17f5..452660dc14 100644 --- a/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +++ b/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp @@ -7,8 +7,10 @@ namespace uponor_smatrix { static const char *const TAG = "uponor_smatrix.sensor"; void UponorSmatrixSensor::dump_config() { - ESP_LOGCONFIG(TAG, "Uponor Smatrix Sensor"); - ESP_LOGCONFIG(TAG, " Device address: 0x%04X", this->address_); + ESP_LOGCONFIG(TAG, + "Uponor Smatrix Sensor\n" + " Device address: 0x%04X", + this->address_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "External Temperature", this->external_temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/uponor_smatrix/uponor_smatrix.cpp b/esphome/components/uponor_smatrix/uponor_smatrix.cpp index 2dbbef72ab..a0017518bf 100644 --- a/esphome/components/uponor_smatrix/uponor_smatrix.cpp +++ b/esphome/components/uponor_smatrix/uponor_smatrix.cpp @@ -1,6 +1,7 @@ #include "uponor_smatrix.h" -#include "esphome/core/log.h" #include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace uponor_smatrix { diff --git a/esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp b/esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp index fa8cb2bb61..69033be11c 100644 --- a/esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp +++ b/esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp @@ -13,7 +13,7 @@ static const char *const TAG = "uptime.sensor"; void UptimeTimestampSensor::setup() { this->time_->add_on_time_sync_callback([this]() { - if (this->has_state_) + if (this->has_state()) return; // No need to update the timestamp if it's already set auto now = this->time_->now(); diff --git a/esphome/components/usb_host/__init__.py b/esphome/components/usb_host/__init__.py index b6ca779706..3204562dc8 100644 --- a/esphome/components/usb_host/__init__.py +++ b/esphome/components/usb_host/__init__.py @@ -19,6 +19,7 @@ USBClient = usb_host_ns.class_("USBClient", Component) CONF_DEVICES = "devices" CONF_VID = "vid" CONF_PID = "pid" +CONF_ENABLE_HUBS = "enable_hubs" def usb_device_schema(cls=USBClient, vid: int = None, pid: [int] = None) -> cv.Schema: @@ -42,6 +43,7 @@ CONFIG_SCHEMA = cv.All( cv.COMPONENT_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(USBHost), + cv.Optional(CONF_ENABLE_HUBS, default=False): cv.boolean, cv.Optional(CONF_DEVICES): cv.ensure_list(usb_device_schema()), } ), @@ -58,6 +60,8 @@ async def register_usb_client(config): async def to_code(config): add_idf_sdkconfig_option("CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE", 1024) + if config.get(CONF_ENABLE_HUBS): + add_idf_sdkconfig_option("CONFIG_USB_HOST_HUBS_SUPPORTED", True) var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) for device in config.get(CONF_DEVICES) or (): diff --git a/esphome/components/usb_host/usb_host_client.cpp b/esphome/components/usb_host/usb_host_client.cpp index 09422f570f..fc5f772f41 100644 --- a/esphome/components/usb_host/usb_host_client.cpp +++ b/esphome/components/usb_host/usb_host_client.cpp @@ -381,9 +381,11 @@ void USBClient::transfer_out(uint8_t ep_address, const transfer_cb_t &callback, } } void USBClient::dump_config() { - ESP_LOGCONFIG(TAG, "USBClient"); - ESP_LOGCONFIG(TAG, " Vendor id %04X", this->vid_); - ESP_LOGCONFIG(TAG, " Product id %04X", this->pid_); + ESP_LOGCONFIG(TAG, + "USBClient\n" + " Vendor id %04X\n" + " Product id %04X", + this->vid_, this->pid_); } void USBClient::release_trq(TransferRequest *trq) { this->trq_pool_.push_back(trq); } diff --git a/esphome/components/usb_uart/usb_uart.cpp b/esphome/components/usb_uart/usb_uart.cpp index 30a45f9cb0..e599409f0c 100644 --- a/esphome/components/usb_uart/usb_uart.cpp +++ b/esphome/components/usb_uart/usb_uart.cpp @@ -178,13 +178,16 @@ void USBUartComponent::loop() { USBClient::loop(); } void USBUartComponent::dump_config() { USBClient::dump_config(); for (auto &channel : this->channels_) { - ESP_LOGCONFIG(TAG, " UART Channel %d", channel->index_); - ESP_LOGCONFIG(TAG, " Baud Rate: %" PRIu32 " baud", channel->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %u", channel->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", PARITY_NAMES[channel->parity_]); - ESP_LOGCONFIG(TAG, " Stop bits: %s", STOP_BITS_NAMES[channel->stop_bits_]); - ESP_LOGCONFIG(TAG, " Debug: %s", YESNO(channel->debug_)); - ESP_LOGCONFIG(TAG, " Dummy receiver: %s", YESNO(channel->dummy_receiver_)); + ESP_LOGCONFIG(TAG, + " UART Channel %d\n" + " Baud Rate: %" PRIu32 " baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %s\n" + " Debug: %s\n" + " Dummy receiver: %s", + channel->index_, channel->baud_rate_, channel->data_bits_, PARITY_NAMES[channel->parity_], + STOP_BITS_NAMES[channel->stop_bits_], YESNO(channel->debug_), YESNO(channel->dummy_receiver_)); } } void USBUartComponent::start_input(USBUartChannel *channel) { diff --git a/esphome/components/valve/__init__.py b/esphome/components/valve/__init__.py index f3c0353777..a6f1428cd2 100644 --- a/esphome/components/valve/__init__.py +++ b/esphome/components/valve/__init__.py @@ -163,6 +163,7 @@ async def register_valve(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_valve(var)) + CORE.register_platform_component("valve", var) await _setup_valve_core(var, config) diff --git a/esphome/components/veml3235/veml3235.cpp b/esphome/components/veml3235/veml3235.cpp index 965e167942..d5489216b6 100644 --- a/esphome/components/veml3235/veml3235.cpp +++ b/esphome/components/veml3235/veml3235.cpp @@ -217,13 +217,17 @@ void VEML3235Sensor::dump_config() { LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, " Auto-gain enabled: %s", YESNO(this->auto_gain_)); if (this->auto_gain_) { - ESP_LOGCONFIG(TAG, " Auto-gain upper threshold: %f%%", this->auto_gain_threshold_high_ * 100.0); - ESP_LOGCONFIG(TAG, " Auto-gain lower threshold: %f%%", this->auto_gain_threshold_low_ * 100.0); - ESP_LOGCONFIG(TAG, " Values below will be used as initial values only"); + ESP_LOGCONFIG(TAG, + " Auto-gain upper threshold: %f%%\n" + " Auto-gain lower threshold: %f%%\n" + " Values below will be used as initial values only", + this->auto_gain_threshold_high_ * 100.0, this->auto_gain_threshold_low_ * 100.0); } - ESP_LOGCONFIG(TAG, " Digital gain: %uX", digital_gain); - ESP_LOGCONFIG(TAG, " Gain: %uX", gain); - ESP_LOGCONFIG(TAG, " Integration time: %ums", integration_time); + ESP_LOGCONFIG(TAG, + " Digital gain: %uX\n" + " Gain: %uX\n" + " Integration time: %ums", + digital_gain, gain, integration_time); } } // namespace veml3235 diff --git a/esphome/components/veml7700/veml7700.cpp b/esphome/components/veml7700/veml7700.cpp index 25f6c761c8..9d87a639a6 100644 --- a/esphome/components/veml7700/veml7700.cpp +++ b/esphome/components/veml7700/veml7700.cpp @@ -93,11 +93,15 @@ void VEML7700Component::dump_config() { LOG_I2C_DEVICE(this); ESP_LOGCONFIG(TAG, " Automatic gain/time: %s", YESNO(this->automatic_mode_enabled_)); if (!this->automatic_mode_enabled_) { - ESP_LOGCONFIG(TAG, " Gain: %s", get_gain_str(this->gain_)); - ESP_LOGCONFIG(TAG, " Integration time: %d ms", get_itime_ms(this->integration_time_)); + ESP_LOGCONFIG(TAG, + " Gain: %s\n" + " Integration time: %d ms", + get_gain_str(this->gain_), get_itime_ms(this->integration_time_)); } - ESP_LOGCONFIG(TAG, " Lux compensation: %s", YESNO(this->lux_compensation_enabled_)); - ESP_LOGCONFIG(TAG, " Glass attenuation factor: %f", this->glass_attenuation_factor_); + ESP_LOGCONFIG(TAG, + " Lux compensation: %s\n" + " Glass attenuation factor: %f", + YESNO(this->lux_compensation_enabled_), this->glass_attenuation_factor_); LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "ALS channel lux", this->ambient_light_sensor_); diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 1aafea7d85..366a020d1c 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -85,7 +85,7 @@ bool VoiceAssistant::start_udp_socket_() { bool VoiceAssistant::allocate_buffers_() { #ifdef USE_SPEAKER if ((this->speaker_ != nullptr) && (this->speaker_buffer_ == nullptr)) { - ExternalRAMAllocator speaker_allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator speaker_allocator; this->speaker_buffer_ = speaker_allocator.allocate(SPEAKER_BUFFER_SIZE); if (this->speaker_buffer_ == nullptr) { ESP_LOGW(TAG, "Could not allocate speaker buffer"); @@ -103,7 +103,7 @@ bool VoiceAssistant::allocate_buffers_() { } if (this->send_buffer_ == nullptr) { - ExternalRAMAllocator send_allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator send_allocator; this->send_buffer_ = send_allocator.allocate(SEND_BUFFER_SIZE); if (send_buffer_ == nullptr) { ESP_LOGW(TAG, "Could not allocate send buffer"); @@ -136,7 +136,7 @@ void VoiceAssistant::clear_buffers_() { void VoiceAssistant::deallocate_buffers_() { if (this->send_buffer_ != nullptr) { - ExternalRAMAllocator send_deallocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator send_deallocator; send_deallocator.deallocate(this->send_buffer_, SEND_BUFFER_SIZE); this->send_buffer_ = nullptr; } @@ -147,7 +147,7 @@ void VoiceAssistant::deallocate_buffers_() { #ifdef USE_SPEAKER if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) { - ExternalRAMAllocator speaker_deallocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator speaker_deallocator; speaker_deallocator.deallocate(this->speaker_buffer_, SPEAKER_BUFFER_SIZE); this->speaker_buffer_ = nullptr; } @@ -204,7 +204,7 @@ void VoiceAssistant::loop() { break; } case State::START_PIPELINE: { - ESP_LOGD(TAG, "Requesting start..."); + ESP_LOGD(TAG, "Requesting start"); uint32_t flags = 0; if (!this->continue_conversation_ && this->use_wake_word_) flags |= api::enums::VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD; @@ -223,7 +223,7 @@ void VoiceAssistant::loop() { msg.wake_word_phrase = this->wake_word_; this->wake_word_ = ""; - if (this->api_client_ == nullptr || !this->api_client_->send_voice_assistant_request(msg)) { + if (this->api_client_ == nullptr || !this->api_client_->send_message(msg)) { ESP_LOGW(TAG, "Could not request start"); this->error_trigger_->trigger("not-connected", "Could not request start"); this->continuous_ = false; @@ -245,7 +245,7 @@ void VoiceAssistant::loop() { if (this->audio_mode_ == AUDIO_MODE_API) { api::VoiceAssistantAudio msg; msg.data.assign((char *) this->send_buffer_, read_bytes); - this->api_client_->send_voice_assistant_audio(msg); + this->api_client_->send_message(msg); } else { if (!this->udp_socket_running_) { if (!this->start_udp_socket_()) { @@ -331,7 +331,7 @@ void VoiceAssistant::loop() { api::VoiceAssistantAnnounceFinished msg; msg.success = true; - this->api_client_->send_voice_assistant_announce_finished(msg); + this->api_client_->send_message(msg); break; } } @@ -577,10 +577,10 @@ void VoiceAssistant::signal_stop_() { if (this->api_client_ == nullptr) { return; } - ESP_LOGD(TAG, "Signaling stop..."); + ESP_LOGD(TAG, "Signaling stop"); api::VoiceAssistantRequest msg; msg.start = false; - this->api_client_->send_voice_assistant_request(msg); + this->api_client_->send_message(msg); } void VoiceAssistant::start_playback_timeout_() { @@ -590,7 +590,7 @@ void VoiceAssistant::start_playback_timeout_() { api::VoiceAssistantAnnounceFinished msg; msg.success = true; - this->api_client_->send_voice_assistant_announce_finished(msg); + this->api_client_->send_message(msg); }); } diff --git a/esphome/components/wake_on_lan/wake_on_lan.cpp b/esphome/components/wake_on_lan/wake_on_lan.cpp index d18cdf89c8..bed098755a 100644 --- a/esphome/components/wake_on_lan/wake_on_lan.cpp +++ b/esphome/components/wake_on_lan/wake_on_lan.cpp @@ -30,7 +30,7 @@ void WakeOnLanButton::press_action() { ESP_LOGW(TAG, "Network not connected"); return; } - ESP_LOGI(TAG, "Sending Wake-on-LAN Packet..."); + ESP_LOGI(TAG, "Sending Wake-on-LAN Packet"); #if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) struct sockaddr_storage saddr {}; auto addr_len = diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 79aae70e41..575234e780 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -1,9 +1,9 @@ #include "waveshare_epaper.h" -#include "esphome/core/log.h" +#include +#include #include "esphome/core/application.h" #include "esphome/core/helpers.h" -#include -#include +#include "esphome/core/log.h" namespace esphome { namespace waveshare_epaper { @@ -185,7 +185,7 @@ void WaveshareEPaper7C::setup() { this->initialize(); } void WaveshareEPaper7C::init_internal_7c_(uint32_t buffer_length) { - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; uint32_t small_buffer_length = buffer_length / NUM_BUFFERS; for (int i = 0; i < NUM_BUFFERS; i++) { @@ -2054,7 +2054,7 @@ void GDEW029T5::initialize() { this->deep_sleep_between_updates_ = true; // old buffer for partial update - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; this->old_buffer_ = allocator.allocate(this->get_buffer_length_()); if (this->old_buffer_ == nullptr) { ESP_LOGE(TAG, "Could not allocate old buffer for display!"); @@ -2199,7 +2199,7 @@ void GDEW029T5::dump_config() { void GDEW0154M09::initialize() { this->init_internal_(); - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; this->lastbuff_ = allocator.allocate(this->get_buffer_length_()); if (this->lastbuff_ != nullptr) { memset(this->lastbuff_, 0xff, sizeof(uint8_t) * this->get_buffer_length_()); diff --git a/esphome/components/web_server/server_index_v2.h b/esphome/components/web_server/server_index_v2.h index 21a2a97465..ec093d3186 100644 --- a/esphome/components/web_server/server_index_v2.h +++ b/esphome/components/web_server/server_index_v2.h @@ -11,636 +11,638 @@ namespace web_server { const uint8_t INDEX_GZ[] PROGMEM = { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xcd, 0x7d, 0xdb, 0x72, 0xdb, 0xc6, 0xb6, 0xe0, 0xf3, - 0xe4, 0x2b, 0x20, 0x44, 0x5b, 0x41, 0x6f, 0x36, 0x21, 0x92, 0x92, 0x6c, 0x19, 0x64, 0x93, 0x5b, 0x96, 0x9d, 0xed, - 0xec, 0xf8, 0x92, 0x58, 0x76, 0xb2, 0x13, 0x86, 0x5b, 0x82, 0x88, 0x26, 0xd9, 0x36, 0x88, 0x66, 0x80, 0x26, 0x25, - 0x85, 0xc4, 0xa9, 0xf9, 0x80, 0xa9, 0x9a, 0xaa, 0x79, 0x9a, 0x97, 0xa9, 0x39, 0x0f, 0xf3, 0x11, 0xf3, 0x7c, 0x3e, - 0xe5, 0xfc, 0xc0, 0xcc, 0x27, 0x4c, 0xad, 0xbe, 0x00, 0x0d, 0x5e, 0x64, 0xe5, 0x72, 0xce, 0x99, 0x4a, 0xc5, 0x22, - 0xfa, 0xba, 0x7a, 0xf5, 0xea, 0x75, 0x6f, 0xa0, 0xb3, 0x17, 0xf1, 0xa1, 0xb8, 0x9b, 0x51, 0x67, 0x22, 0xa6, 0x71, - 0xb7, 0xa3, 0xff, 0xa5, 0x61, 0xd4, 0xed, 0xc4, 0x2c, 0xf9, 0xe8, 0xa4, 0x34, 0x26, 0x6c, 0xc8, 0x13, 0x67, 0x92, - 0xd2, 0x11, 0x89, 0x42, 0x11, 0x06, 0x6c, 0x1a, 0x8e, 0xa9, 0x73, 0xd8, 0xed, 0x4c, 0xa9, 0x08, 0x9d, 0xe1, 0x24, - 0x4c, 0x33, 0x2a, 0xc8, 0xfb, 0x77, 0x5f, 0xd6, 0x4f, 0xbb, 0x9d, 0x6c, 0x98, 0xb2, 0x99, 0x70, 0x60, 0x48, 0x32, - 0xe5, 0xd1, 0x3c, 0xa6, 0xdd, 0xc3, 0xc3, 0x9b, 0x9b, 0x1b, 0xff, 0x43, 0xf6, 0xd9, 0x90, 0x27, 0x99, 0x70, 0x5e, - 0x92, 0x1b, 0x96, 0x44, 0xfc, 0x06, 0x53, 0x41, 0x5e, 0xfa, 0x17, 0x93, 0x30, 0xe2, 0x37, 0x6f, 0x39, 0x17, 0x07, - 0x07, 0x9e, 0x7a, 0xbc, 0x3b, 0xbf, 0xb8, 0x20, 0x84, 0x2c, 0x38, 0x8b, 0x9c, 0xc6, 0x6a, 0x55, 0x16, 0xfa, 0x49, - 0x28, 0xd8, 0x82, 0xaa, 0x2e, 0xe8, 0xe0, 0xc0, 0x0d, 0x23, 0x3e, 0x13, 0x34, 0xba, 0x10, 0x77, 0x31, 0xbd, 0x98, - 0x50, 0x2a, 0x32, 0x97, 0x25, 0xce, 0x33, 0x3e, 0x9c, 0x4f, 0x69, 0x22, 0xfc, 0x59, 0xca, 0x05, 0x07, 0x48, 0x0e, - 0x0e, 0xdc, 0x94, 0xce, 0xe2, 0x70, 0x48, 0xa1, 0xfe, 0xfc, 0xe2, 0xa2, 0xec, 0x51, 0x36, 0xc2, 0x4c, 0x90, 0x8b, - 0xbb, 0xe9, 0x35, 0x8f, 0x3d, 0x84, 0x63, 0x41, 0x12, 0x7a, 0xe3, 0x7c, 0x4f, 0xc3, 0x8f, 0xaf, 0xc2, 0x59, 0x7b, - 0x18, 0x87, 0x59, 0xe6, 0x5c, 0x8b, 0xa5, 0x5c, 0x42, 0x3a, 0x1f, 0x0a, 0x9e, 0x7a, 0x02, 0x53, 0xcc, 0xd0, 0x92, - 0x8d, 0x3c, 0x31, 0x61, 0x99, 0x7f, 0xb9, 0x3f, 0xcc, 0xb2, 0xb7, 0x34, 0x9b, 0xc7, 0x62, 0x9f, 0xec, 0x35, 0x30, - 0xdb, 0x23, 0x84, 0x09, 0x24, 0x26, 0x29, 0xbf, 0x71, 0x9e, 0xa7, 0x29, 0x4f, 0x3d, 0xf7, 0xfc, 0xe2, 0x42, 0xb5, - 0x70, 0x58, 0xe6, 0x24, 0x5c, 0x38, 0xc5, 0x78, 0xe1, 0x75, 0x4c, 0x7d, 0xe7, 0x7d, 0x46, 0x9d, 0xab, 0x79, 0x92, - 0x85, 0x23, 0x7a, 0x7e, 0x71, 0x71, 0xe5, 0xf0, 0xd4, 0xb9, 0x1a, 0x66, 0xd9, 0x95, 0xc3, 0x92, 0x4c, 0xd0, 0x30, - 0xf2, 0x5d, 0xd4, 0x96, 0x93, 0x0d, 0xb3, 0xec, 0x1d, 0xbd, 0x15, 0x44, 0x60, 0xf9, 0x28, 0x08, 0xcd, 0xc7, 0x54, - 0x38, 0x59, 0xb1, 0x2e, 0x0f, 0x2d, 0x63, 0x2a, 0x1c, 0x41, 0x64, 0x3d, 0x6f, 0x2b, 0xdc, 0x53, 0xf5, 0x28, 0xda, - 0x6c, 0xe4, 0x51, 0x71, 0x70, 0x20, 0x0a, 0x3c, 0x23, 0xb5, 0x34, 0x87, 0x11, 0xba, 0x67, 0xca, 0x0e, 0x0e, 0xa8, - 0x1f, 0xd3, 0x64, 0x2c, 0x26, 0x84, 0x90, 0x66, 0x9b, 0x1d, 0x1c, 0x78, 0x82, 0xc4, 0xc2, 0x1f, 0x53, 0xe1, 0x51, - 0x84, 0x70, 0xd9, 0xfb, 0xe0, 0xc0, 0x53, 0x48, 0xe0, 0x44, 0x21, 0xae, 0x82, 0x63, 0xe4, 0x6b, 0xec, 0x5f, 0xdc, - 0x25, 0x43, 0xcf, 0x86, 0x1f, 0x61, 0x76, 0x70, 0x10, 0x0b, 0x3f, 0x83, 0x11, 0xb1, 0x40, 0x28, 0x4f, 0xa9, 0x98, - 0xa7, 0x89, 0x23, 0x72, 0xc1, 0x2f, 0x44, 0xca, 0x92, 0xb1, 0x87, 0x96, 0xa6, 0xcc, 0xea, 0x98, 0xe7, 0x0a, 0xdc, - 0x6f, 0x04, 0x49, 0x49, 0x17, 0x66, 0xbc, 0x16, 0x1e, 0xec, 0x22, 0x1f, 0x39, 0x29, 0x21, 0x6e, 0x26, 0xfb, 0xba, - 0xbd, 0x34, 0x48, 0x6b, 0xae, 0x8b, 0x15, 0x94, 0x98, 0x09, 0x84, 0x3f, 0x12, 0x2f, 0xc5, 0xbe, 0xef, 0x0b, 0x44, - 0xba, 0x4b, 0x83, 0x95, 0xd4, 0x5a, 0x67, 0x2f, 0xed, 0x37, 0x06, 0x81, 0xf0, 0x53, 0x1a, 0xcd, 0x87, 0xd4, 0xf3, - 0x18, 0xce, 0x70, 0x82, 0x48, 0x97, 0xd5, 0x3c, 0x4e, 0xba, 0xb0, 0xdd, 0xbc, 0xba, 0xd7, 0x84, 0xec, 0x35, 0x90, - 0x86, 0x91, 0x1b, 0x00, 0x01, 0xc3, 0x1a, 0x1e, 0x4e, 0x88, 0x9b, 0xcc, 0xa7, 0xd7, 0x34, 0x75, 0x8b, 0x66, 0xed, - 0x0a, 0x59, 0xcc, 0x33, 0xea, 0x0c, 0xb3, 0xcc, 0x19, 0xcd, 0x93, 0xa1, 0x60, 0x3c, 0x71, 0xdc, 0x1a, 0xaf, 0xb9, - 0x8a, 0x1c, 0x0a, 0x6a, 0x70, 0x51, 0x8e, 0xbc, 0x0c, 0xd5, 0xd2, 0x7e, 0x52, 0x6b, 0x0e, 0x30, 0x40, 0x89, 0xda, - 0x7a, 0x3c, 0x8d, 0x00, 0x8a, 0x53, 0x58, 0x63, 0x8e, 0xdf, 0x0b, 0x58, 0xa5, 0x5c, 0x22, 0x15, 0xbd, 0xd4, 0xdf, - 0x3c, 0x28, 0x44, 0xf8, 0xd3, 0x70, 0xe6, 0x51, 0xd2, 0xa5, 0x92, 0xb8, 0xc2, 0x64, 0x08, 0xb0, 0x56, 0xf6, 0xad, - 0x47, 0x03, 0xea, 0x97, 0x24, 0x85, 0x02, 0xe1, 0x8f, 0x78, 0xfa, 0x3c, 0x1c, 0x4e, 0xa0, 0x5f, 0x41, 0x30, 0x91, - 0x39, 0x6f, 0xc3, 0x94, 0x86, 0x82, 0x3e, 0x8f, 0x29, 0x3c, 0x79, 0xae, 0xec, 0xe9, 0x22, 0x9c, 0x91, 0x97, 0x7e, - 0xcc, 0xc4, 0x6b, 0x9e, 0x0c, 0x69, 0x3b, 0xb3, 0xa8, 0x8b, 0xc1, 0xbe, 0x9f, 0x09, 0x91, 0xb2, 0xeb, 0xb9, 0xa0, - 0x9e, 0x9b, 0x40, 0x0b, 0x17, 0x67, 0x08, 0x33, 0x5f, 0xd0, 0x5b, 0x71, 0xce, 0x13, 0x41, 0x13, 0x41, 0xa8, 0x41, - 0x2a, 0x4e, 0xfd, 0x70, 0x36, 0xa3, 0x49, 0x74, 0x3e, 0x61, 0x71, 0xe4, 0x31, 0x94, 0xa3, 0x1c, 0x87, 0x82, 0xc0, - 0x1a, 0x49, 0x37, 0x0d, 0xe0, 0x9f, 0xdd, 0xab, 0xf1, 0x04, 0xe9, 0xca, 0x43, 0x41, 0x89, 0xeb, 0xb6, 0x47, 0x3c, - 0xf5, 0xf4, 0x0a, 0x1c, 0x3e, 0x72, 0x04, 0xcc, 0xf1, 0x76, 0x1e, 0xd3, 0x0c, 0xd1, 0x1a, 0x61, 0xc5, 0x36, 0x6a, - 0x04, 0x7f, 0x03, 0x14, 0x9f, 0x23, 0x2f, 0x45, 0x41, 0xda, 0x5e, 0x84, 0xa9, 0xf3, 0xa5, 0x3e, 0x51, 0xcf, 0x0c, - 0x37, 0x9b, 0x08, 0xf2, 0xcc, 0x17, 0xe9, 0x3c, 0x13, 0x34, 0x7a, 0x77, 0x37, 0xa3, 0x19, 0x7e, 0x27, 0xc8, 0x44, - 0xf4, 0x26, 0xc2, 0xa7, 0xd3, 0x99, 0xb8, 0xbb, 0x90, 0x8c, 0x31, 0x70, 0x5d, 0x3c, 0x84, 0x96, 0x29, 0x0d, 0x87, - 0xc0, 0xcc, 0x34, 0xb6, 0xbe, 0xe1, 0xf1, 0xdd, 0x88, 0xc5, 0xf1, 0xc5, 0x7c, 0x36, 0xe3, 0xa9, 0xc0, 0x7f, 0x25, - 0x4b, 0xc1, 0x4b, 0xd4, 0xc0, 0x5e, 0x2e, 0xb3, 0x1b, 0x26, 0x86, 0x13, 0x4f, 0xa0, 0xe5, 0x30, 0xcc, 0xa8, 0xf3, - 0x94, 0xf3, 0x98, 0x86, 0x49, 0x90, 0x92, 0xb4, 0xf7, 0x4e, 0x04, 0xc9, 0x3c, 0x8e, 0xdb, 0xd7, 0x29, 0x0d, 0x3f, - 0xb6, 0x65, 0xf5, 0x9b, 0xeb, 0x0f, 0x74, 0x28, 0x02, 0xf9, 0xfb, 0x2c, 0x4d, 0xc3, 0x3b, 0x68, 0x48, 0x08, 0x34, - 0xeb, 0xa5, 0xc1, 0xdf, 0x2e, 0xde, 0xbc, 0xf6, 0xd5, 0x21, 0x61, 0xa3, 0x3b, 0x2f, 0x2d, 0x0e, 0x5e, 0x9a, 0xe3, - 0x51, 0xca, 0xa7, 0x6b, 0x53, 0x2b, 0xac, 0xa5, 0xed, 0x1d, 0x20, 0x50, 0x92, 0xee, 0xa9, 0xa1, 0x6d, 0x08, 0x5e, - 0x4b, 0x9a, 0x87, 0x4a, 0xa2, 0xe7, 0x85, 0x7f, 0x02, 0x55, 0xec, 0xa5, 0xe8, 0x7e, 0x68, 0x45, 0x7a, 0xb7, 0xa4, - 0x44, 0xc2, 0x39, 0x03, 0x09, 0x03, 0x30, 0x0e, 0x43, 0x31, 0x9c, 0x2c, 0xa9, 0x1c, 0x2c, 0x37, 0x10, 0xd3, 0x3c, - 0xc7, 0x37, 0x05, 0xbd, 0x8b, 0x3d, 0x42, 0x52, 0xc9, 0xa8, 0x88, 0x58, 0xad, 0x52, 0x42, 0x52, 0x84, 0xbf, 0x27, - 0xcb, 0xd0, 0xac, 0x27, 0xd8, 0x6b, 0x60, 0x38, 0x97, 0x81, 0xe2, 0x2e, 0x78, 0xc8, 0x93, 0x05, 0x4d, 0x05, 0x4d, - 0x83, 0xbf, 0xe2, 0x94, 0x8e, 0x62, 0x80, 0x62, 0xaf, 0x89, 0x27, 0x61, 0x76, 0x3e, 0x09, 0x93, 0x31, 0x8d, 0x82, - 0x1b, 0x91, 0xe3, 0xbf, 0x13, 0x77, 0xc4, 0x92, 0x30, 0x66, 0xbf, 0xd0, 0xc8, 0xd5, 0xd2, 0xe0, 0xcc, 0xa1, 0xb7, - 0x82, 0x26, 0x51, 0xe6, 0xbc, 0x78, 0xf7, 0xea, 0xa5, 0xde, 0xc7, 0x8a, 0x80, 0x40, 0xcb, 0x6c, 0x3e, 0xa3, 0xa9, - 0x87, 0xb0, 0x16, 0x10, 0xcf, 0x99, 0x64, 0x8e, 0xaf, 0xc2, 0x99, 0x2a, 0x61, 0xd9, 0xfb, 0x59, 0x14, 0x0a, 0xfa, - 0x0d, 0x4d, 0x22, 0x96, 0x8c, 0xc9, 0x5e, 0x53, 0x95, 0x4f, 0x42, 0x5d, 0x11, 0x15, 0x45, 0x97, 0xfb, 0xcf, 0x63, - 0xb9, 0xee, 0xe2, 0x71, 0xee, 0xa1, 0x3c, 0x13, 0xa1, 0x60, 0x43, 0x27, 0x8c, 0xa2, 0xaf, 0x12, 0x26, 0x98, 0x04, - 0x30, 0x85, 0xed, 0x01, 0x12, 0xa5, 0x4a, 0x54, 0x18, 0xc0, 0x3d, 0x84, 0x3d, 0x4f, 0x0b, 0x80, 0x09, 0xd2, 0xfb, - 0x75, 0x70, 0x50, 0xb2, 0xfb, 0x1e, 0x0d, 0x54, 0x25, 0xe9, 0x0f, 0x90, 0x3f, 0x9b, 0x67, 0xb0, 0xd1, 0x66, 0x0a, - 0x90, 0x2e, 0xfc, 0x3a, 0xa3, 0xe9, 0x82, 0x46, 0x05, 0x71, 0x64, 0x1e, 0x5a, 0xae, 0xcd, 0xa1, 0x8f, 0x85, 0x20, - 0xfd, 0x41, 0xdb, 0xe6, 0xdb, 0x54, 0xd3, 0x79, 0xca, 0x67, 0x34, 0x15, 0x8c, 0x66, 0x05, 0x2b, 0xf1, 0x40, 0x8a, - 0x16, 0xec, 0x24, 0x23, 0x66, 0x7d, 0x33, 0x8f, 0x61, 0x8a, 0x2a, 0x0c, 0xc3, 0x08, 0xda, 0xe7, 0x0b, 0x29, 0x31, - 0x32, 0xcc, 0x10, 0x16, 0x0a, 0xd2, 0x0c, 0xa1, 0x1c, 0x61, 0x61, 0xc0, 0x55, 0xac, 0x48, 0xcf, 0x76, 0x07, 0xa2, - 0x9a, 0x7c, 0x2f, 0x45, 0x35, 0x30, 0xb4, 0x50, 0xd0, 0x83, 0x03, 0x8f, 0xfa, 0x05, 0x51, 0x90, 0xbd, 0xa6, 0xde, - 0x23, 0x0b, 0x59, 0x3b, 0xc0, 0x86, 0x89, 0x05, 0xa6, 0x08, 0xef, 0x51, 0x3f, 0xe1, 0x67, 0xc3, 0x21, 0xcd, 0x32, - 0x9e, 0x1e, 0x1c, 0xec, 0xc9, 0xf6, 0x85, 0x36, 0x01, 0x7b, 0xf8, 0xe6, 0x26, 0x29, 0x21, 0x40, 0xa5, 0x84, 0xd5, - 0x72, 0x41, 0x80, 0x9c, 0x92, 0x0a, 0x87, 0xdb, 0x33, 0x8a, 0x47, 0xe0, 0x5e, 0x5e, 0xba, 0x35, 0x81, 0x35, 0x1a, - 0xc6, 0xd4, 0x4c, 0x7d, 0xf7, 0x8c, 0x2a, 0xd5, 0x4a, 0x2a, 0x1e, 0x1b, 0x98, 0x51, 0xe7, 0xc7, 0x8f, 0xe8, 0x88, - 0x25, 0xd6, 0xb2, 0x2b, 0x20, 0x61, 0x81, 0x33, 0x94, 0x5b, 0x1b, 0xba, 0x75, 0x68, 0xa9, 0xd3, 0xa8, 0x9d, 0x5b, - 0x8e, 0xa5, 0x1e, 0x61, 0x6d, 0x63, 0x9f, 0x0e, 0x72, 0x2c, 0x51, 0x6f, 0x56, 0x93, 0x48, 0x40, 0xfb, 0x62, 0xd0, - 0xd6, 0xf5, 0x24, 0x53, 0x98, 0x4b, 0xe9, 0xcf, 0x73, 0x9a, 0x09, 0x45, 0xc7, 0x9e, 0xc0, 0x09, 0x66, 0x28, 0x87, - 0xe3, 0x36, 0x62, 0xe3, 0x79, 0x0a, 0xea, 0x0e, 0x1c, 0x45, 0x9a, 0xcc, 0xa7, 0xd4, 0x3c, 0x6d, 0x83, 0xed, 0xcd, - 0x0c, 0x04, 0x62, 0x06, 0x34, 0x7d, 0x3f, 0x39, 0x01, 0xac, 0x02, 0xad, 0x56, 0xdf, 0x9b, 0x41, 0xca, 0xad, 0x2c, - 0x54, 0xb4, 0xb5, 0x3d, 0xf9, 0x3b, 0xd2, 0xf2, 0x78, 0xaf, 0xa9, 0xa0, 0xff, 0xfb, 0x80, 0xec, 0x35, 0x0a, 0x0a, - 0xd6, 0x38, 0x55, 0xc0, 0x28, 0x14, 0xbe, 0x51, 0x03, 0x21, 0x29, 0xdd, 0x2b, 0xc4, 0xe2, 0x4f, 0x36, 0xe8, 0x74, - 0x42, 0xfa, 0xa0, 0x67, 0xf8, 0x93, 0xc1, 0x2e, 0x62, 0x32, 0xdc, 0xc0, 0x13, 0x9b, 0x75, 0x25, 0xd3, 0x58, 0x54, - 0x99, 0xc6, 0xda, 0x22, 0xdc, 0x59, 0xd1, 0xc5, 0x2d, 0x68, 0x4c, 0x1f, 0xf3, 0xb2, 0x0a, 0x33, 0x09, 0x4c, 0xb9, - 0x24, 0x6b, 0x88, 0xd7, 0xe1, 0x94, 0x66, 0x1e, 0x45, 0x78, 0x57, 0x03, 0x45, 0x9c, 0xd0, 0x64, 0x60, 0x89, 0xcd, - 0x0c, 0xc4, 0x26, 0x43, 0x4a, 0x2b, 0xab, 0x1e, 0xb7, 0x0c, 0xd3, 0x7e, 0x36, 0x28, 0x95, 0x39, 0x6b, 0xf1, 0x52, - 0x1e, 0x6b, 0xea, 0x36, 0xf8, 0x53, 0x65, 0x0a, 0x69, 0x52, 0x69, 0xc8, 0x10, 0xde, 0x6b, 0xac, 0xef, 0xa3, 0x69, - 0x55, 0xae, 0xb1, 0x3f, 0x80, 0x7d, 0x90, 0xe2, 0xc2, 0x67, 0x99, 0xfc, 0x5b, 0x39, 0x67, 0x80, 0xb6, 0x0b, 0x20, - 0x0b, 0x7f, 0x14, 0x87, 0xc2, 0x6b, 0x1e, 0x36, 0x40, 0x13, 0x5d, 0x50, 0x90, 0x26, 0x08, 0x6d, 0x2e, 0x85, 0xfa, - 0xf3, 0x24, 0x9b, 0xb0, 0x91, 0xf0, 0x42, 0x21, 0x19, 0x0a, 0x8d, 0x33, 0xea, 0x88, 0x8a, 0x3e, 0x2c, 0x99, 0x4d, - 0x08, 0xa4, 0x56, 0x28, 0x5f, 0xd4, 0x40, 0x2a, 0x99, 0x16, 0xf0, 0x86, 0x52, 0x97, 0x2e, 0x79, 0x8c, 0x69, 0xcd, - 0x40, 0x5f, 0x6c, 0xf6, 0xd4, 0x88, 0x81, 0x66, 0x05, 0xcc, 0x52, 0x59, 0x59, 0x60, 0xf3, 0x07, 0x5d, 0x28, 0x7c, - 0xc1, 0x5f, 0xf2, 0x1b, 0x9a, 0x9e, 0x87, 0x00, 0x7c, 0xa0, 0xba, 0xe7, 0x4a, 0x0c, 0x48, 0x6e, 0x2f, 0xda, 0x86, - 0x5e, 0x2e, 0xe5, 0xc2, 0xbf, 0x49, 0xf9, 0x94, 0x65, 0x14, 0x34, 0x35, 0x85, 0xff, 0x04, 0x4e, 0x99, 0x3c, 0x8e, - 0x20, 0x6a, 0x68, 0x41, 0x5f, 0x67, 0x2f, 0xab, 0xf4, 0x75, 0xb9, 0xff, 0x7c, 0x6c, 0xd8, 0x5f, 0xf5, 0x10, 0x23, - 0xec, 0x69, 0x7b, 0xc2, 0x92, 0x72, 0xfe, 0x04, 0x69, 0xf1, 0xbe, 0x5a, 0x09, 0xcb, 0x6c, 0xab, 0xe8, 0x8a, 0x54, - 0x1d, 0x1b, 0x94, 0x87, 0x51, 0x04, 0x5a, 0x5d, 0xca, 0xe3, 0xd8, 0x12, 0x54, 0x98, 0xb5, 0x0b, 0xd1, 0x74, 0xb9, - 0xff, 0xfc, 0xe2, 0x3e, 0xe9, 0x04, 0xf5, 0xb6, 0x80, 0x32, 0x80, 0x26, 0x11, 0x4d, 0xc1, 0x8c, 0xb4, 0x76, 0x4b, - 0xcb, 0xd8, 0x73, 0x9e, 0x24, 0x74, 0x28, 0x68, 0x04, 0x56, 0x0a, 0x23, 0xc2, 0x9f, 0xf0, 0x4c, 0x14, 0x85, 0x25, - 0xf4, 0xcc, 0x82, 0x9e, 0xf9, 0xc3, 0x30, 0x8e, 0x3d, 0x65, 0x91, 0x4c, 0xf9, 0x82, 0x6e, 0x81, 0xba, 0x5d, 0x01, - 0xb9, 0x18, 0x86, 0x5a, 0xc3, 0x50, 0x3f, 0x9b, 0xc5, 0x6c, 0x48, 0x0b, 0xc1, 0x75, 0xe1, 0xb3, 0x24, 0xa2, 0xb7, - 0xc0, 0x47, 0x50, 0xb7, 0xdb, 0x6d, 0xe0, 0x26, 0xca, 0x15, 0xc2, 0x97, 0x1b, 0x88, 0xbd, 0x47, 0x64, 0x02, 0x91, - 0x91, 0xee, 0x72, 0x1b, 0x3f, 0xa0, 0xc8, 0x92, 0x93, 0xcc, 0x58, 0x56, 0x8a, 0x37, 0x23, 0x1c, 0xd1, 0x98, 0x0a, - 0x6a, 0x78, 0x39, 0xe8, 0xcf, 0xea, 0xe8, 0xbe, 0x2d, 0xf0, 0x57, 0x90, 0x93, 0x39, 0x65, 0x66, 0xcf, 0xb3, 0xc2, - 0x52, 0x2f, 0xb7, 0xa7, 0xc4, 0x76, 0x4f, 0xa8, 0xed, 0x09, 0x85, 0x08, 0x87, 0x13, 0x65, 0xa2, 0x7b, 0x1b, 0x4b, - 0x2a, 0xc7, 0xd0, 0x7c, 0xbd, 0x38, 0x44, 0xef, 0x0d, 0x98, 0xdb, 0x50, 0x70, 0xa1, 0x99, 0x02, 0x05, 0xab, 0x4f, - 0x6d, 0xdb, 0x79, 0x18, 0xc7, 0xd7, 0xe1, 0xf0, 0x63, 0x95, 0xfa, 0x4b, 0x32, 0x20, 0xeb, 0xdc, 0xd8, 0xaa, 0xb2, - 0x58, 0x96, 0xbd, 0x6e, 0xc3, 0xa5, 0x2b, 0x07, 0xc5, 0xdb, 0x6b, 0x94, 0x64, 0x5f, 0xdd, 0xe8, 0x9d, 0xd4, 0x2e, - 0x21, 0x62, 0x7a, 0x65, 0x1e, 0x70, 0x81, 0x4f, 0x52, 0x9c, 0xe1, 0x07, 0x9a, 0xee, 0xc0, 0xd6, 0xc8, 0xd7, 0x00, - 0x11, 0x68, 0x99, 0x47, 0x2c, 0xdb, 0x8d, 0x81, 0x3f, 0x04, 0xca, 0x67, 0xd6, 0x0c, 0x0f, 0x05, 0xb4, 0xe0, 0x71, - 0x5a, 0x65, 0x2e, 0x20, 0xd3, 0xda, 0x84, 0x61, 0x34, 0x5f, 0x83, 0xe6, 0x22, 0xe9, 0xfd, 0x8d, 0xaa, 0x02, 0x9d, - 0x0c, 0xa0, 0xc8, 0xda, 0xb6, 0x32, 0x51, 0xa1, 0x00, 0xcd, 0x53, 0x99, 0x14, 0xb9, 0x49, 0xc5, 0x78, 0xd4, 0xea, - 0xba, 0xb2, 0xbf, 0x35, 0xcb, 0xe5, 0xc4, 0xf3, 0xbc, 0x0c, 0xec, 0x37, 0xa3, 0xd7, 0x97, 0x8b, 0xc8, 0x36, 0x16, - 0x91, 0xf9, 0x96, 0x91, 0x85, 0x4a, 0x5a, 0xb6, 0xba, 0x07, 0x7f, 0x45, 0x76, 0x23, 0x50, 0x56, 0x7d, 0xe0, 0xcf, - 0xa8, 0x60, 0xb7, 0x31, 0x11, 0x98, 0x6b, 0x03, 0x47, 0x53, 0x1a, 0x30, 0x8c, 0xb2, 0x4b, 0x82, 0xd4, 0xd1, 0xa8, - 0x18, 0xbb, 0x09, 0xe6, 0x68, 0x4d, 0xb3, 0xcf, 0x73, 0x8d, 0x23, 0x8a, 0xf4, 0xde, 0x54, 0x54, 0x62, 0x0b, 0x2b, - 0x38, 0x21, 0x5a, 0x0d, 0x56, 0x5a, 0xcf, 0x3a, 0x6e, 0x8a, 0x71, 0xe1, 0xa0, 0x96, 0xa8, 0xa9, 0xe8, 0x93, 0x46, - 0xb1, 0x4a, 0x10, 0x9e, 0x18, 0x8d, 0x94, 0x97, 0xeb, 0x26, 0xc4, 0x35, 0xde, 0x08, 0xb7, 0xb7, 0xac, 0x98, 0x84, - 0x81, 0xd5, 0x2c, 0x0f, 0x80, 0xa5, 0xf2, 0x6d, 0xe8, 0xde, 0x46, 0x33, 0x95, 0x71, 0x2c, 0x84, 0x73, 0x1b, 0xe1, - 0x16, 0x66, 0x13, 0xc5, 0xb9, 0x92, 0x01, 0x99, 0x54, 0xfb, 0x7a, 0x14, 0x73, 0xb5, 0x0f, 0x1b, 0x48, 0x5c, 0x57, - 0x3c, 0x25, 0x09, 0x82, 0x01, 0x9b, 0x81, 0x72, 0x67, 0xcb, 0x07, 0x0f, 0x60, 0x67, 0xab, 0xd5, 0x06, 0xd1, 0x6d, - 0xd5, 0x3f, 0x91, 0x5f, 0x1a, 0x85, 0xab, 0xd5, 0x8d, 0x40, 0x9e, 0xd6, 0x7c, 0x31, 0x45, 0x3d, 0xc3, 0x71, 0xcf, - 0x5e, 0x42, 0x2b, 0xa9, 0x88, 0x96, 0x25, 0x85, 0xc9, 0x50, 0xa5, 0xd9, 0xea, 0x3e, 0x09, 0x8b, 0x6d, 0x9f, 0x6f, - 0x70, 0x2f, 0x59, 0xa8, 0xc5, 0x74, 0xb9, 0xe4, 0x73, 0x3d, 0x34, 0x43, 0x08, 0x05, 0x99, 0xb4, 0x62, 0xf6, 0xb6, - 0x19, 0x96, 0x07, 0x07, 0x99, 0x35, 0xd0, 0x65, 0xc1, 0x26, 0x3e, 0x78, 0x20, 0x92, 0xb3, 0xbb, 0x44, 0xea, 0x2e, - 0x1f, 0x8c, 0x10, 0xda, 0x30, 0x4b, 0x1b, 0x6d, 0xb0, 0xc6, 0xc3, 0x9b, 0x90, 0x09, 0xa7, 0x18, 0x45, 0x59, 0xe3, - 0x1e, 0x45, 0x4b, 0xad, 0x6a, 0xf8, 0x29, 0x05, 0xe5, 0x11, 0x78, 0x82, 0x51, 0xa1, 0x15, 0xdd, 0x0f, 0x27, 0x14, - 0x1c, 0xc1, 0x46, 0x8b, 0x28, 0xec, 0xc2, 0x3d, 0x2d, 0x45, 0xf4, 0xc0, 0xdb, 0x61, 0xcf, 0xd7, 0xbb, 0x57, 0xec, - 0x80, 0x19, 0x4d, 0x47, 0x3c, 0x9d, 0x9a, 0xba, 0x7c, 0xed, 0x59, 0x73, 0x46, 0x36, 0xf2, 0xb6, 0x8e, 0xad, 0xd5, - 0xff, 0xf6, 0x9a, 0xd1, 0x5d, 0x9a, 0xeb, 0x15, 0x51, 0x5a, 0x48, 0x5f, 0xe5, 0x0f, 0x34, 0x94, 0x99, 0xd9, 0xe6, - 0xbd, 0x76, 0xa6, 0xb6, 0x95, 0xc3, 0x64, 0xaf, 0xd9, 0x2e, 0x6c, 0x3e, 0x43, 0x0d, 0x6d, 0xe5, 0xd8, 0xd0, 0x22, - 0x95, 0xcf, 0xe3, 0x48, 0x03, 0xcb, 0x10, 0xa6, 0x9a, 0x8e, 0x6e, 0x58, 0x1c, 0x97, 0xa5, 0xbf, 0x86, 0xaf, 0x67, - 0x9a, 0xaf, 0x27, 0x86, 0xaf, 0x03, 0xa7, 0x00, 0xbe, 0xae, 0x86, 0x2b, 0xbb, 0x27, 0x1b, 0xa7, 0x33, 0x51, 0x1c, - 0x3d, 0x93, 0x76, 0x34, 0xcc, 0x37, 0x37, 0x10, 0xa0, 0x42, 0xf3, 0xfa, 0xe8, 0x69, 0x27, 0x0c, 0x18, 0x80, 0xca, - 0x85, 0x49, 0x6d, 0x17, 0xc5, 0x47, 0x0f, 0xe1, 0x2c, 0xa7, 0x05, 0x65, 0x9f, 0x3d, 0x07, 0x27, 0x9d, 0xb5, 0x1c, - 0x10, 0x62, 0xb2, 0xf8, 0x57, 0x29, 0x51, 0x66, 0x75, 0x4c, 0xaf, 0x2e, 0x33, 0xab, 0x03, 0x4e, 0x5f, 0xae, 0x2e, - 0xba, 0x9f, 0xd7, 0xcb, 0xe5, 0xb1, 0x62, 0x79, 0xe5, 0x7e, 0xaf, 0x56, 0xde, 0x5a, 0x09, 0xf8, 0xef, 0xb5, 0x89, - 0x92, 0x16, 0xa3, 0x03, 0x0f, 0xb0, 0x31, 0x03, 0x05, 0xb9, 0x5a, 0x74, 0x21, 0xe2, 0x5e, 0x7e, 0xca, 0xc1, 0x23, - 0xdd, 0xf4, 0xaa, 0xff, 0x39, 0x9f, 0xce, 0x40, 0x1b, 0x5b, 0x23, 0xe9, 0x31, 0xd5, 0x13, 0x96, 0xf5, 0xf9, 0x96, - 0xb2, 0x4a, 0x1f, 0x79, 0x1e, 0x2b, 0xd4, 0x54, 0xd8, 0xcb, 0x7b, 0x8d, 0x7c, 0x5e, 0x14, 0x15, 0x8c, 0x63, 0x9b, - 0x53, 0xe5, 0x7c, 0xdd, 0x25, 0x63, 0x2a, 0xde, 0x78, 0x4c, 0xf1, 0x61, 0x06, 0xbc, 0xce, 0x62, 0x3f, 0x86, 0xdc, - 0xed, 0xfd, 0xcf, 0x4b, 0xe4, 0x2c, 0xf3, 0x35, 0xf4, 0x2d, 0xf3, 0xfc, 0x4c, 0x19, 0xd9, 0xf8, 0x6c, 0xb7, 0x35, - 0x5c, 0xd6, 0x69, 0x63, 0xb1, 0x3f, 0xc0, 0x67, 0x9b, 0xaa, 0x23, 0x59, 0x4e, 0x79, 0x44, 0x03, 0x97, 0xcf, 0x68, - 0xe2, 0xe6, 0xe0, 0x55, 0xd5, 0x7b, 0x3f, 0x14, 0xde, 0xf2, 0x6d, 0xd5, 0xbd, 0x1a, 0x9c, 0xe5, 0xe0, 0xfd, 0xfa, - 0x72, 0xd3, 0xf1, 0xfa, 0x1d, 0x4d, 0x33, 0xa9, 0x88, 0x16, 0x3a, 0xed, 0x97, 0xa5, 0x58, 0xfa, 0x32, 0xd8, 0xd9, - 0xbe, 0x34, 0x41, 0xdc, 0xa6, 0xff, 0xc8, 0x3f, 0x72, 0x91, 0x74, 0x0b, 0xff, 0xa8, 0x0f, 0xfc, 0x07, 0xe3, 0x16, - 0x7e, 0x4e, 0x3e, 0x54, 0xbd, 0xc2, 0x91, 0x20, 0xcf, 0x7b, 0xcf, 0x8d, 0xc5, 0xcc, 0x63, 0x36, 0xbc, 0xf3, 0xdc, - 0x98, 0x89, 0x3a, 0x84, 0xde, 0x5c, 0xbc, 0x54, 0x15, 0xe0, 0x52, 0x94, 0xee, 0xec, 0xdc, 0xd8, 0x7a, 0x58, 0x08, - 0xe2, 0xee, 0xc7, 0x4c, 0xec, 0xbb, 0x78, 0x4a, 0xae, 0xe0, 0xc7, 0xfe, 0xd2, 0x7b, 0x15, 0x8a, 0x89, 0x9f, 0x86, - 0x49, 0xc4, 0xa7, 0x1e, 0xaa, 0xb9, 0x2e, 0xf2, 0x33, 0x69, 0x6f, 0x3c, 0x41, 0xf9, 0xfe, 0x15, 0xbe, 0x15, 0xc4, - 0xed, 0xb9, 0xb5, 0x29, 0x7e, 0x2d, 0xc8, 0x55, 0x67, 0x7f, 0x79, 0x2b, 0xf2, 0xee, 0x15, 0xbe, 0x2d, 0x3c, 0xf6, - 0xf8, 0x1b, 0xe2, 0x21, 0xd2, 0xbd, 0xd5, 0xd0, 0x9c, 0xf3, 0xa9, 0xf2, 0xdc, 0xbb, 0x08, 0xbf, 0x87, 0xb8, 0x4a, - 0x5a, 0x72, 0x1b, 0x1d, 0x5a, 0xd9, 0x23, 0x2e, 0x97, 0x2e, 0x02, 0xf7, 0xe0, 0xc0, 0x2a, 0x2b, 0x54, 0x05, 0x7c, - 0x26, 0x48, 0xc5, 0x20, 0xc7, 0x6f, 0x65, 0x84, 0xe6, 0x4c, 0x78, 0x29, 0x32, 0xc3, 0x78, 0xc6, 0x0f, 0xad, 0x8f, - 0x66, 0xda, 0x57, 0x1e, 0x06, 0x9f, 0x09, 0x9a, 0x86, 0x82, 0xa7, 0x03, 0x64, 0xab, 0x1f, 0xf8, 0x6f, 0xe4, 0xaa, - 0xef, 0xfc, 0xa7, 0xcf, 0x7e, 0x1a, 0xfd, 0x94, 0x0e, 0xae, 0xf0, 0x1b, 0x72, 0xd8, 0xf1, 0x7a, 0x81, 0xb7, 0x57, - 0xaf, 0xaf, 0x7e, 0x3a, 0xec, 0xff, 0x23, 0xac, 0xff, 0x72, 0x56, 0xff, 0x71, 0x80, 0x56, 0xde, 0x4f, 0x87, 0xbd, - 0xbe, 0x7e, 0xea, 0xff, 0xa3, 0xfb, 0x53, 0x36, 0xf8, 0xb3, 0x2a, 0xdc, 0x47, 0xe8, 0x70, 0x8c, 0xe7, 0x82, 0x1c, - 0xd6, 0xeb, 0xdd, 0xc3, 0x31, 0x9e, 0x09, 0x72, 0x08, 0x7f, 0xaf, 0xc9, 0x5b, 0x3a, 0x7e, 0x7e, 0x3b, 0xf3, 0xae, - 0xba, 0xab, 0xfd, 0xe5, 0xdf, 0x72, 0x18, 0xb5, 0xff, 0x8f, 0x9f, 0x7e, 0xca, 0xdc, 0x2f, 0xba, 0xe4, 0x70, 0x50, - 0x43, 0x1e, 0x94, 0xfe, 0x99, 0xc8, 0x7f, 0xbd, 0x5e, 0xd0, 0xff, 0x87, 0x86, 0xc2, 0xfd, 0xe2, 0xa7, 0xab, 0x4e, - 0x97, 0x0c, 0x56, 0x9e, 0xbb, 0xfa, 0x02, 0xad, 0x10, 0x5a, 0xed, 0xa3, 0x2b, 0xec, 0x8e, 0x5d, 0x84, 0xc7, 0x82, - 0x1c, 0x7e, 0x71, 0x38, 0xc6, 0x0b, 0x41, 0x0e, 0xdd, 0xc3, 0x31, 0x7e, 0x2e, 0xc8, 0xe1, 0x3f, 0xbc, 0x5e, 0xa0, - 0x3c, 0x6c, 0x2b, 0xe9, 0xde, 0x58, 0x41, 0x70, 0x23, 0x4c, 0x69, 0xb8, 0x12, 0x4c, 0xc4, 0x14, 0xed, 0x1f, 0x32, - 0x7c, 0x21, 0xd1, 0xe4, 0x09, 0x70, 0xc2, 0x80, 0x6d, 0xe7, 0x2d, 0x2f, 0x61, 0xb3, 0x81, 0x66, 0xf6, 0x83, 0x14, - 0x2b, 0x3f, 0x40, 0x16, 0x08, 0xbc, 0x08, 0xe3, 0x39, 0xcd, 0x02, 0x9a, 0x23, 0x3c, 0x24, 0x17, 0xc2, 0x6b, 0x22, - 0xfc, 0x42, 0xc0, 0x8f, 0x16, 0xc2, 0x17, 0x3a, 0x80, 0x09, 0x07, 0x59, 0x11, 0x55, 0xc2, 0x95, 0xc6, 0xe2, 0x22, - 0x3c, 0xdb, 0x52, 0x29, 0x26, 0xe0, 0x5d, 0x40, 0x78, 0xbf, 0x12, 0xee, 0xc4, 0x37, 0xc4, 0x90, 0xc4, 0xbb, 0x94, - 0xd2, 0xef, 0xc3, 0xf8, 0x23, 0x4d, 0xbd, 0x5b, 0xdc, 0x6c, 0x3d, 0xc1, 0xd2, 0x05, 0xbd, 0xd7, 0x44, 0xed, 0x22, - 0x56, 0x75, 0x2e, 0x54, 0x8c, 0x00, 0x84, 0x6c, 0xd5, 0x17, 0x03, 0x3b, 0xbe, 0x97, 0x6e, 0x38, 0xac, 0xd2, 0xf0, - 0xc6, 0x45, 0xd5, 0xb8, 0x28, 0x4b, 0x16, 0x61, 0xcc, 0x22, 0x47, 0xd0, 0xe9, 0x2c, 0x0e, 0x05, 0x75, 0xf4, 0x7a, - 0x9d, 0x10, 0x06, 0x72, 0x0b, 0x95, 0x21, 0xb2, 0x0c, 0xce, 0xc8, 0x04, 0x9c, 0xe0, 0xac, 0x78, 0x10, 0x9d, 0xd2, - 0x6a, 0xc7, 0xd3, 0x32, 0xf8, 0xb5, 0x1e, 0xdf, 0xab, 0x37, 0xc1, 0x11, 0x36, 0x90, 0xe2, 0x39, 0xc3, 0x09, 0x01, - 0x21, 0xda, 0xea, 0xb9, 0x9d, 0x6c, 0x31, 0xee, 0xba, 0x10, 0x9b, 0xe1, 0xe4, 0x8d, 0xf4, 0x0b, 0x41, 0x83, 0x09, - 0x69, 0xb4, 0x27, 0x1d, 0xda, 0x9e, 0xd4, 0x6a, 0x46, 0x87, 0x8e, 0x49, 0xda, 0x9f, 0xa8, 0xee, 0x21, 0x8e, 0xf0, - 0x9c, 0xd4, 0x9b, 0x78, 0x4c, 0x1a, 0xb2, 0x4b, 0x7b, 0xdc, 0x89, 0xf5, 0x34, 0x07, 0x07, 0x1e, 0xf7, 0xe3, 0x30, - 0x13, 0x5f, 0x81, 0xb1, 0x4f, 0xc6, 0x38, 0x22, 0xdc, 0xa7, 0xb7, 0x74, 0xe8, 0xc5, 0x08, 0x47, 0x9a, 0xd3, 0xa0, - 0x36, 0x1a, 0x13, 0xab, 0x19, 0x18, 0x11, 0xe4, 0x4d, 0x2f, 0xea, 0x37, 0x07, 0x84, 0x10, 0x77, 0xaf, 0x5e, 0x77, - 0x7b, 0x9c, 0xcc, 0x45, 0x00, 0x25, 0x96, 0xaa, 0x4c, 0x66, 0x50, 0xd4, 0xb2, 0x8a, 0xbc, 0xe7, 0xc2, 0x17, 0x34, - 0x13, 0x1e, 0x14, 0x83, 0xf9, 0x9f, 0x19, 0xc2, 0x76, 0x3b, 0x87, 0x6e, 0x0d, 0x4a, 0x25, 0x71, 0x22, 0xcc, 0xc9, - 0x35, 0x0a, 0xa2, 0xfe, 0xd1, 0xc0, 0xe6, 0xff, 0xb2, 0x10, 0x26, 0xbf, 0xee, 0x45, 0xfd, 0x86, 0x9c, 0xbc, 0xeb, - 0xf6, 0x3c, 0x4e, 0x32, 0xa5, 0xa0, 0xf5, 0xb2, 0xe0, 0x8d, 0x5c, 0x2a, 0x0a, 0x34, 0x70, 0x7a, 0xde, 0x39, 0xa9, - 0xb7, 0x02, 0x6f, 0x6e, 0x2f, 0xa2, 0x0e, 0x93, 0x69, 0x2c, 0xe0, 0x90, 0x40, 0x7b, 0xcc, 0x09, 0xcc, 0x58, 0x76, - 0xbb, 0x0e, 0xf4, 0xf3, 0x17, 0xee, 0x17, 0xbd, 0x85, 0x08, 0xc6, 0x42, 0x4d, 0xbf, 0x10, 0xab, 0x15, 0xfc, 0x1d, - 0x8b, 0x1e, 0x27, 0xd7, 0xb2, 0x68, 0xae, 0x8b, 0x66, 0x50, 0xf4, 0x26, 0x00, 0x50, 0x71, 0x56, 0x28, 0x59, 0x6a, - 0x4f, 0x16, 0x44, 0xc2, 0x7e, 0x70, 0x90, 0xf6, 0x27, 0xb5, 0xe6, 0x00, 0xfc, 0xfb, 0xa9, 0xc8, 0xbe, 0x67, 0x62, - 0xe2, 0xb9, 0x87, 0x5d, 0x17, 0xf5, 0x5c, 0x07, 0xb6, 0xb6, 0x9d, 0xd4, 0x88, 0xc2, 0x70, 0x5c, 0x7b, 0x2d, 0x82, - 0x79, 0x97, 0x34, 0x7a, 0x1e, 0xd3, 0xfe, 0x3c, 0x84, 0x63, 0xcd, 0x38, 0x1b, 0x78, 0x8e, 0x6a, 0x42, 0xd4, 0xcc, - 0xf3, 0x1c, 0xd5, 0xa6, 0xb5, 0x05, 0x0a, 0xe2, 0xda, 0xb4, 0xe6, 0xcd, 0x09, 0x21, 0xf5, 0x56, 0xd1, 0xcd, 0x48, - 0xbf, 0x09, 0x0a, 0x16, 0xc6, 0xd9, 0xd9, 0x97, 0xc7, 0x21, 0xa9, 0x79, 0x69, 0x9f, 0x0e, 0x56, 0x2b, 0xb7, 0xd3, - 0xeb, 0xba, 0xa8, 0xe6, 0x19, 0x42, 0x3b, 0x34, 0x94, 0x86, 0x10, 0x66, 0x83, 0x5c, 0x87, 0x92, 0xde, 0x55, 0xc2, - 0x46, 0xcb, 0xf2, 0xb0, 0x5b, 0x3c, 0x80, 0xe6, 0x85, 0x1d, 0xa3, 0xf4, 0xd5, 0x19, 0x2c, 0xd3, 0x10, 0x73, 0x42, - 0x1a, 0x98, 0x13, 0xe3, 0xbb, 0x9e, 0x10, 0x51, 0x12, 0x7c, 0x4c, 0xca, 0xe6, 0xb8, 0x1f, 0xe2, 0x68, 0x40, 0x9e, - 0x2a, 0x7b, 0xa4, 0x6d, 0xfc, 0xe2, 0x34, 0x26, 0xef, 0xd6, 0xa2, 0xb7, 0x21, 0xc4, 0x56, 0x6e, 0xfc, 0xe1, 0x3c, - 0x4d, 0x69, 0x22, 0x5e, 0xf3, 0x48, 0xab, 0x69, 0x34, 0x06, 0x4b, 0x09, 0xc2, 0xb2, 0x18, 0x74, 0xb4, 0x96, 0x39, - 0x19, 0xf3, 0x8d, 0xea, 0x31, 0x99, 0x2b, 0xf5, 0x49, 0x06, 0x6b, 0xdb, 0x63, 0x6d, 0x17, 0x7b, 0x08, 0xcf, 0x75, - 0x14, 0xd7, 0xf3, 0x7d, 0x7f, 0xec, 0x0f, 0xa1, 0x1a, 0x26, 0xc8, 0x50, 0x2e, 0xcf, 0x91, 0x97, 0x91, 0x1b, 0x3f, - 0xa1, 0xb7, 0x72, 0x56, 0x0f, 0x95, 0x92, 0xd9, 0x1c, 0xaf, 0xce, 0xa4, 0x2d, 0xd9, 0x4d, 0xe6, 0x27, 0x3c, 0xa2, - 0x80, 0x1e, 0x88, 0xdb, 0xeb, 0xa2, 0x49, 0x98, 0xd9, 0xf1, 0xa9, 0x12, 0xbe, 0xbe, 0xed, 0xbc, 0x1e, 0x83, 0xc7, - 0x57, 0xea, 0x5a, 0x45, 0x63, 0xe5, 0x06, 0x47, 0x88, 0x8d, 0xbc, 0xb1, 0x0f, 0x71, 0x3d, 0x49, 0x42, 0x02, 0x4c, - 0xb9, 0xb1, 0x4d, 0x54, 0xd3, 0x62, 0xcc, 0x05, 0x89, 0xfa, 0xbc, 0x56, 0x93, 0x5e, 0xe8, 0xb9, 0x22, 0x89, 0x31, - 0xc2, 0x8b, 0xe2, 0x6c, 0x99, 0x76, 0x6f, 0x04, 0xa9, 0x4e, 0xe5, 0x2d, 0xaa, 0xee, 0xdc, 0x9a, 0x10, 0x48, 0x7a, - 0x0a, 0x85, 0x37, 0x45, 0xf8, 0x15, 0x39, 0xf4, 0xfa, 0x7e, 0xef, 0x2f, 0x03, 0xd4, 0xf3, 0xfc, 0x3f, 0xa3, 0x43, - 0xc5, 0x39, 0x16, 0xa8, 0x1d, 0xab, 0x39, 0x96, 0x32, 0x7e, 0xd9, 0xc4, 0xd2, 0x93, 0x18, 0x24, 0x38, 0x09, 0xa7, - 0x34, 0x78, 0x05, 0x87, 0xdc, 0x10, 0xce, 0x1b, 0x81, 0x81, 0x92, 0x82, 0x57, 0x9a, 0x97, 0xf8, 0x6e, 0xef, 0xa5, - 0x28, 0x9e, 0x7a, 0x6e, 0xef, 0x43, 0xf9, 0xf4, 0x17, 0xb7, 0xf7, 0x95, 0x08, 0x7e, 0xc9, 0xb5, 0xb7, 0xbb, 0x32, - 0xc7, 0x23, 0x33, 0x47, 0xae, 0xb6, 0xc6, 0xc2, 0xdd, 0x1c, 0x6d, 0x3a, 0x3a, 0xc6, 0x28, 0x67, 0xa3, 0x82, 0x19, - 0x65, 0xbe, 0x08, 0xc7, 0x80, 0x54, 0x6b, 0x0f, 0x32, 0x3b, 0xae, 0x5f, 0xae, 0x18, 0x48, 0xc5, 0xd0, 0x2b, 0x20, - 0x73, 0xdc, 0x6d, 0xa0, 0x65, 0xa5, 0xad, 0xd4, 0x99, 0xaa, 0x71, 0xf4, 0x82, 0x4f, 0x2f, 0x48, 0xa3, 0xbd, 0xe8, - 0x8c, 0xdb, 0x8b, 0x5a, 0x0d, 0x65, 0x86, 0xb4, 0xe6, 0xfd, 0xc5, 0x00, 0x7f, 0x03, 0x4e, 0x3d, 0x9b, 0x96, 0x70, - 0x65, 0x79, 0x2d, 0xbd, 0xbc, 0x5a, 0x2d, 0xc9, 0x51, 0xdb, 0xea, 0x3a, 0x56, 0x5d, 0xf3, 0x5c, 0xe1, 0x64, 0x9d, - 0xd4, 0x4e, 0x91, 0x2c, 0x81, 0x64, 0x28, 0x42, 0xc8, 0xad, 0x40, 0x5b, 0x47, 0x85, 0x31, 0xa1, 0xbb, 0x3c, 0xb3, - 0xc0, 0x3e, 0x95, 0x94, 0xf0, 0x00, 0x0b, 0xd0, 0xb5, 0xf0, 0x04, 0x4f, 0xf1, 0xbc, 0xd6, 0x94, 0x64, 0x5e, 0x6f, - 0xb6, 0xab, 0x63, 0x3d, 0x2e, 0xc7, 0xc2, 0xf3, 0x1a, 0x99, 0x16, 0x58, 0xca, 0x93, 0x5a, 0x2d, 0xaf, 0x06, 0x3b, - 0xcd, 0xc9, 0xad, 0x04, 0x20, 0x6e, 0xd7, 0x93, 0x32, 0x8c, 0x84, 0x2d, 0x65, 0x2a, 0xf3, 0x59, 0x92, 0xd0, 0x14, - 0xa4, 0x28, 0x11, 0x98, 0xe5, 0x79, 0x29, 0xd9, 0x41, 0x8c, 0x62, 0x4a, 0x52, 0xe0, 0x3c, 0xd2, 0xee, 0xc2, 0x09, - 0xe6, 0x78, 0x22, 0xf9, 0x06, 0x21, 0xe4, 0xc2, 0xa4, 0xb3, 0x08, 0xc9, 0x83, 0x62, 0xc2, 0x2c, 0x99, 0x94, 0x11, - 0xea, 0x5f, 0xee, 0x9f, 0xf3, 0x7b, 0x6d, 0xb2, 0x3e, 0x1b, 0x04, 0xb2, 0x59, 0xac, 0x39, 0x57, 0x48, 0xde, 0x7b, - 0x02, 0x15, 0xd1, 0x11, 0x5f, 0x32, 0xc0, 0x67, 0x2c, 0xa5, 0x52, 0x07, 0xdf, 0x37, 0x76, 0x5f, 0x5c, 0x55, 0x20, - 0x63, 0xdb, 0x7b, 0x03, 0x88, 0x0c, 0xc1, 0xb9, 0x93, 0x90, 0x8d, 0x66, 0x97, 0xfb, 0x67, 0x6f, 0xb6, 0xd9, 0xc0, - 0xab, 0x95, 0xb6, 0x7e, 0xa5, 0x6e, 0x83, 0xc3, 0x12, 0xd2, 0x58, 0xff, 0x08, 0xbc, 0x58, 0xaa, 0x48, 0xa1, 0x97, - 0x02, 0x15, 0x5d, 0xee, 0x9f, 0xbd, 0xf3, 0x52, 0xe9, 0x5b, 0x42, 0xd8, 0x5e, 0xb6, 0xc7, 0x89, 0x37, 0x21, 0x14, - 0xa9, 0xb5, 0x17, 0xac, 0x8b, 0x5b, 0x02, 0x3c, 0x98, 0xc8, 0x4a, 0xb0, 0x20, 0xfa, 0x6c, 0x40, 0x62, 0x8d, 0x01, - 0x12, 0x23, 0x1c, 0x57, 0xec, 0x32, 0x02, 0x1b, 0x20, 0xe7, 0xba, 0x80, 0x9d, 0xf0, 0x95, 0xea, 0x87, 0x70, 0x2c, - 0x67, 0x15, 0xb9, 0x12, 0x1e, 0xaf, 0x36, 0xb2, 0xd2, 0x4a, 0x73, 0xf4, 0x3b, 0xb0, 0x9d, 0xcc, 0xc3, 0x6b, 0x62, - 0x2c, 0x09, 0x5d, 0xf0, 0xcc, 0xa4, 0x8f, 0x5d, 0xee, 0x9f, 0xbd, 0xd2, 0x19, 0x64, 0xb3, 0xd0, 0xf0, 0xfb, 0x0d, - 0x13, 0xf3, 0xec, 0x95, 0x5f, 0xd6, 0xca, 0xc6, 0x97, 0xfb, 0x67, 0xef, 0xb7, 0x35, 0x83, 0xf2, 0x7c, 0x5e, 0xda, - 0xf8, 0x12, 0xbe, 0x25, 0x8d, 0x83, 0xa5, 0x16, 0x0e, 0x01, 0xcb, 0xb1, 0x14, 0x48, 0x41, 0x96, 0x17, 0xae, 0x91, - 0x67, 0x38, 0x21, 0x32, 0x0c, 0x54, 0xdd, 0x35, 0xad, 0xe6, 0x31, 0x9e, 0x5c, 0x0c, 0xf9, 0x8c, 0xee, 0x88, 0x0d, - 0xdd, 0x22, 0x9f, 0x4d, 0x21, 0x75, 0x46, 0x82, 0xce, 0xf0, 0x5e, 0x03, 0xb5, 0xab, 0xe2, 0x2b, 0x91, 0x44, 0xca, - 0x2b, 0xb2, 0x05, 0x4f, 0x48, 0x03, 0xc7, 0xa4, 0x81, 0x43, 0x92, 0xf5, 0x1b, 0x4a, 0x40, 0xb4, 0xc3, 0x62, 0x5c, - 0x25, 0x66, 0x20, 0x2b, 0x4c, 0x9f, 0x56, 0x25, 0x80, 0xa3, 0x76, 0x28, 0x7d, 0x8f, 0x52, 0xa6, 0x47, 0x92, 0x2c, - 0xde, 0x7a, 0x1c, 0x73, 0x39, 0xf0, 0x05, 0xbb, 0x8e, 0x21, 0xb1, 0x04, 0x56, 0x85, 0x05, 0x0a, 0x8a, 0xa6, 0x4d, - 0xdd, 0x34, 0xf4, 0xe5, 0x3e, 0x71, 0x1c, 0xfa, 0xc0, 0xb9, 0x71, 0xa8, 0xf3, 0x70, 0xb2, 0xcd, 0x2e, 0x8f, 0x0e, - 0x0e, 0x3c, 0xd5, 0xe9, 0x67, 0xe1, 0x71, 0x53, 0x5f, 0x46, 0xee, 0xbe, 0x53, 0xbc, 0x22, 0x42, 0x12, 0xfe, 0x5a, - 0x2d, 0x1e, 0xe4, 0x10, 0x86, 0xf6, 0xc2, 0x2a, 0x06, 0x0d, 0xf0, 0x52, 0xd7, 0xab, 0x2e, 0xbf, 0x56, 0x2b, 0xa2, - 0xb4, 0x55, 0x6c, 0xdd, 0xe2, 0x24, 0x5f, 0x78, 0x45, 0xea, 0x4f, 0x63, 0x23, 0x5f, 0xca, 0x80, 0x80, 0x98, 0x4d, - 0xb3, 0xcc, 0x2c, 0xc6, 0x3a, 0x12, 0x0c, 0xda, 0x7d, 0xa5, 0xb3, 0x16, 0xb0, 0xcc, 0xae, 0xd2, 0x8d, 0x0c, 0x3b, - 0x6b, 0xa1, 0xc0, 0x34, 0x82, 0xa8, 0x14, 0x34, 0xaa, 0xe5, 0x9a, 0xbc, 0xdf, 0x6e, 0xe6, 0x5c, 0xe2, 0x0c, 0x69, - 0x27, 0x97, 0x84, 0x42, 0x22, 0xab, 0x55, 0x20, 0xe5, 0x05, 0x99, 0xed, 0x26, 0xf9, 0x33, 0x8b, 0xe4, 0x9f, 0x12, - 0x6a, 0x91, 0xbf, 0x72, 0x71, 0xf8, 0x5c, 0x3b, 0x17, 0x32, 0x53, 0x75, 0x3e, 0x23, 0xe0, 0x44, 0xab, 0x62, 0xb4, - 0x12, 0x56, 0xdc, 0xc1, 0x50, 0xec, 0x13, 0x22, 0xdd, 0x90, 0xd8, 0xc4, 0x80, 0xbd, 0x32, 0xa8, 0x06, 0x53, 0x6f, - 0xf3, 0xe9, 0xd9, 0x1c, 0xf0, 0xec, 0xfd, 0xfd, 0xf1, 0xd0, 0xf3, 0xd9, 0xe6, 0xc9, 0xb5, 0x72, 0x3f, 0x61, 0xd5, - 0xd6, 0xc1, 0xad, 0x66, 0x82, 0xc2, 0xfc, 0x45, 0x1c, 0xbb, 0xca, 0x7c, 0xd6, 0x0e, 0xa1, 0x91, 0x7f, 0x00, 0x6d, - 0xb3, 0x29, 0x5b, 0x50, 0x6b, 0x58, 0xe0, 0x47, 0x2a, 0x03, 0x35, 0x4c, 0x77, 0xb0, 0x8f, 0x33, 0xd9, 0x80, 0x26, - 0xd1, 0xf6, 0xea, 0xa7, 0xb9, 0x26, 0x13, 0x05, 0x1a, 0x5a, 0x02, 0xff, 0x53, 0x24, 0x0f, 0x74, 0x23, 0xe5, 0x02, - 0x20, 0x68, 0x26, 0xf1, 0x54, 0x22, 0xcc, 0x75, 0x4b, 0xef, 0xfb, 0x8b, 0x3d, 0x42, 0x66, 0xa5, 0xf7, 0xf1, 0x6d, - 0x99, 0x7a, 0x05, 0x64, 0x81, 0x02, 0x30, 0x1f, 0x8b, 0x02, 0x15, 0xbe, 0xbc, 0x30, 0xcd, 0xa5, 0x09, 0xe9, 0x97, - 0x1a, 0xb7, 0x15, 0xda, 0x94, 0x6e, 0x39, 0x55, 0x6f, 0xd0, 0xb0, 0x56, 0xbb, 0x0f, 0xb5, 0x6f, 0x85, 0x84, 0x11, - 0x9e, 0xdf, 0xc9, 0xd6, 0x66, 0xdc, 0xfc, 0xe3, 0x7a, 0xfe, 0xca, 0xda, 0xa6, 0xf8, 0x2c, 0xc9, 0x68, 0x2a, 0x9e, - 0xd2, 0x11, 0x4f, 0x21, 0x66, 0x51, 0xe0, 0x04, 0xe5, 0xfb, 0x96, 0xdf, 0x4e, 0xae, 0xcf, 0x0a, 0x14, 0xac, 0x2d, - 0x50, 0xfe, 0xfa, 0x28, 0x83, 0xd6, 0x97, 0xeb, 0xbd, 0x66, 0x07, 0x07, 0xef, 0x4b, 0x34, 0x69, 0x28, 0x25, 0x14, - 0x16, 0xd3, 0x52, 0x2a, 0x8d, 0x8e, 0xe4, 0xee, 0x7b, 0x85, 0x13, 0xc0, 0x30, 0x0c, 0x9b, 0xf7, 0xbc, 0x20, 0x22, - 0x1f, 0xaf, 0xb3, 0x78, 0xed, 0x9c, 0x60, 0xb6, 0xe1, 0x02, 0x1c, 0x1e, 0x4c, 0x6d, 0xe5, 0x2d, 0xca, 0xca, 0x64, - 0xd8, 0x02, 0x86, 0x73, 0x40, 0x96, 0x27, 0xcd, 0x10, 0x8b, 0x02, 0xb7, 0x9a, 0x25, 0xe7, 0xa0, 0x57, 0x4e, 0x70, - 0xe6, 0x4f, 0x20, 0xfd, 0xb5, 0x72, 0x64, 0x11, 0xc2, 0x2a, 0x31, 0xc7, 0x4a, 0x25, 0x38, 0x7b, 0xb1, 0xcd, 0xa5, - 0x6c, 0x88, 0x9a, 0x4a, 0xa9, 0x23, 0x5b, 0xa0, 0xa2, 0x83, 0xbf, 0xf0, 0x98, 0x56, 0xdc, 0x4c, 0xdc, 0x4c, 0xfa, - 0x25, 0x85, 0xa7, 0x82, 0x51, 0x20, 0x33, 0xb8, 0x3f, 0xf7, 0x2a, 0x53, 0xb7, 0xb9, 0xec, 0x86, 0x35, 0xe2, 0x26, - 0x36, 0x9a, 0xb8, 0x8c, 0xeb, 0x9d, 0x97, 0xbc, 0x74, 0x5f, 0x65, 0x50, 0x0b, 0xc3, 0x05, 0xcb, 0x44, 0x12, 0x6b, - 0xf9, 0xfb, 0x2a, 0x29, 0xba, 0x68, 0x84, 0xa9, 0x04, 0xe3, 0x9d, 0xdc, 0x03, 0x9a, 0xc3, 0xdf, 0xe5, 0x99, 0xb0, - 0x76, 0xd4, 0x38, 0xb1, 0xe5, 0x9c, 0x96, 0xd4, 0x7f, 0x0b, 0xa9, 0x2e, 0xeb, 0x67, 0xfe, 0x85, 0x94, 0x85, 0x0c, - 0x67, 0x15, 0xc6, 0x9e, 0x48, 0xc6, 0x8e, 0x40, 0x4f, 0x33, 0x89, 0xdf, 0x3d, 0x9d, 0xf1, 0xc2, 0xb4, 0x94, 0xd3, - 0x24, 0xf6, 0x4d, 0x11, 0x2d, 0xb7, 0x7e, 0xaf, 0xed, 0x46, 0xc0, 0x08, 0x64, 0x01, 0x61, 0xcd, 0xd9, 0x13, 0x84, - 0xb3, 0x5a, 0xad, 0x9d, 0x75, 0x68, 0xe9, 0x24, 0x29, 0x61, 0x64, 0x10, 0xd0, 0x05, 0x82, 0xaf, 0xc8, 0x50, 0x08, - 0xf9, 0x9b, 0xcc, 0xec, 0x0c, 0x7c, 0xed, 0x67, 0x6f, 0x3d, 0x9b, 0xab, 0xd9, 0x6d, 0x8b, 0xa0, 0x29, 0xac, 0xc7, - 0x2b, 0x03, 0x2e, 0xdf, 0xdc, 0x9f, 0xe0, 0x01, 0x70, 0xef, 0x35, 0x31, 0xa4, 0xa2, 0xa1, 0xb6, 0x50, 0x2c, 0xa1, - 0x38, 0x7d, 0x6d, 0x54, 0x66, 0x25, 0xda, 0x93, 0xb5, 0x45, 0x69, 0xcc, 0x0a, 0x92, 0xe5, 0x79, 0x46, 0xcb, 0xf0, - 0xfe, 0x5a, 0xfa, 0xa5, 0x14, 0x2e, 0x9b, 0xde, 0xf6, 0xf3, 0x19, 0x11, 0xd8, 0x22, 0xd4, 0x6f, 0x76, 0xc5, 0x3e, - 0x4a, 0x30, 0xe1, 0x5c, 0x6b, 0xa1, 0xf8, 0xcb, 0x36, 0xa1, 0x88, 0x13, 0x7d, 0xe4, 0xa5, 0x40, 0x6c, 0x3e, 0x40, - 0x20, 0x6a, 0x37, 0xbb, 0x91, 0x89, 0xa0, 0x8e, 0x54, 0x64, 0x62, 0x75, 0x4b, 0x49, 0x82, 0x99, 0xde, 0x8d, 0x6e, - 0x6b, 0xb5, 0x62, 0xfd, 0x06, 0xb8, 0x91, 0x5c, 0x17, 0x7e, 0x36, 0xd5, 0x4f, 0x8b, 0x13, 0x2b, 0x37, 0xb0, 0xc7, - 0x0a, 0x93, 0x05, 0xf9, 0x90, 0xe0, 0xec, 0xc9, 0xa4, 0x2c, 0x49, 0xd3, 0x9a, 0x82, 0x34, 0x81, 0x13, 0x56, 0x84, - 0x99, 0x00, 0x62, 0x29, 0x2b, 0xb4, 0x01, 0xe9, 0x6d, 0xcd, 0xfd, 0x33, 0xe6, 0xe5, 0xa7, 0x35, 0xd1, 0x8a, 0x5c, - 0x51, 0xea, 0x43, 0x25, 0xdf, 0x40, 0x43, 0xa0, 0xf5, 0xc3, 0x3d, 0x69, 0x82, 0x96, 0xa2, 0x1c, 0xd9, 0x72, 0x08, - 0x37, 0xc0, 0x89, 0xb6, 0xf7, 0x5e, 0x45, 0x78, 0xb7, 0x48, 0x13, 0xcc, 0x2d, 0xba, 0x7e, 0x41, 0x44, 0x85, 0x95, - 0x4c, 0x88, 0xb6, 0x94, 0x70, 0x28, 0xc9, 0x54, 0x90, 0xa4, 0xdf, 0x18, 0x80, 0x02, 0xda, 0x8e, 0x3b, 0x49, 0x69, - 0x02, 0xc7, 0xb5, 0x1a, 0x0a, 0xcd, 0xac, 0x93, 0x3e, 0xab, 0xc5, 0x03, 0x4c, 0x71, 0xac, 0x0c, 0x93, 0x8b, 0x83, - 0x03, 0x2f, 0x2c, 0xe7, 0xed, 0xc7, 0x03, 0x84, 0xf9, 0x6a, 0xe5, 0x49, 0xb0, 0x42, 0xb4, 0x5a, 0x85, 0x36, 0x58, - 0xb2, 0x1a, 0xba, 0xcd, 0x7a, 0x82, 0xcc, 0xa4, 0x00, 0x9c, 0x01, 0x84, 0x35, 0xe2, 0x85, 0xda, 0xbd, 0x17, 0x82, - 0x3b, 0xaa, 0x96, 0xf4, 0xe3, 0x5a, 0x73, 0x60, 0x31, 0xae, 0x7e, 0x3c, 0x20, 0x61, 0xce, 0x0f, 0x0e, 0xf6, 0x32, - 0x2d, 0x22, 0x3f, 0x80, 0x28, 0xfb, 0x20, 0x25, 0x8b, 0x1a, 0xd0, 0xde, 0x8d, 0x75, 0x67, 0x40, 0x41, 0x51, 0x7a, - 0x5b, 0x4d, 0xbb, 0x4a, 0x16, 0x44, 0xd1, 0x08, 0xeb, 0x60, 0x70, 0x0f, 0x2c, 0xfb, 0x82, 0xcc, 0x5f, 0x8a, 0x22, - 0xc7, 0xfa, 0x97, 0xad, 0x99, 0xd5, 0xbe, 0xef, 0x87, 0xe9, 0x58, 0xc6, 0x32, 0x4c, 0x18, 0x56, 0x12, 0xff, 0x91, - 0x06, 0xd3, 0x9a, 0xb8, 0x5f, 0xcc, 0x35, 0x20, 0x0a, 0x7c, 0xa3, 0xda, 0x98, 0xbb, 0x24, 0xcf, 0xb6, 0x7a, 0x19, - 0x14, 0x24, 0x1f, 0x7e, 0x2b, 0x24, 0xc7, 0x1a, 0x12, 0x45, 0x1e, 0x6b, 0x38, 0xdb, 0x81, 0x8b, 0x67, 0x62, 0x0d, - 0x67, 0xbb, 0x71, 0x6b, 0x30, 0xf5, 0xd5, 0x2e, 0xf8, 0x2c, 0xde, 0xa0, 0x00, 0x2d, 0x0b, 0x2c, 0x28, 0x4f, 0xd6, - 0x75, 0x2f, 0xc5, 0x4a, 0x41, 0x98, 0x0a, 0xe2, 0xb1, 0xea, 0x01, 0x28, 0xb5, 0x51, 0xcb, 0xf0, 0x65, 0xc1, 0x0c, - 0x59, 0x2e, 0x81, 0x6a, 0xea, 0x0a, 0x90, 0x93, 0xf6, 0xb6, 0xcf, 0x0e, 0x0e, 0xc0, 0x36, 0x00, 0x25, 0xce, 0x1f, - 0x86, 0x33, 0x31, 0x4f, 0x41, 0x95, 0xca, 0xcc, 0x6f, 0x28, 0x86, 0x5b, 0x20, 0xb2, 0x0c, 0x7e, 0x40, 0xc1, 0x2c, - 0xcc, 0x32, 0xb6, 0x50, 0x65, 0xfa, 0x37, 0xe6, 0xc4, 0x90, 0x72, 0xa6, 0x74, 0xc2, 0x04, 0xb5, 0x13, 0x4d, 0xa7, - 0x55, 0xb4, 0x3d, 0x5f, 0xd0, 0x44, 0xbc, 0x64, 0x99, 0xa0, 0x09, 0x2c, 0xbf, 0xa4, 0x38, 0x58, 0x51, 0x86, 0xe0, - 0xc0, 0x56, 0x7a, 0x85, 0x51, 0x74, 0x6f, 0x17, 0x51, 0xd5, 0x81, 0x26, 0x61, 0x12, 0xc5, 0x6a, 0x12, 0x3b, 0x9f, - 0xd1, 0xe4, 0x70, 0x16, 0x2d, 0xed, 0x7c, 0x9a, 0x52, 0xd9, 0x90, 0xdc, 0xdd, 0x63, 0xc4, 0x48, 0x02, 0x23, 0x3d, - 0xef, 0xd5, 0x5a, 0x20, 0xe2, 0xbd, 0x63, 0x13, 0xec, 0x95, 0x60, 0x61, 0x71, 0x54, 0xbf, 0x0a, 0xa7, 0xa1, 0x9b, - 0x9f, 0xb7, 0x5e, 0x69, 0xdb, 0x26, 0x1c, 0x24, 0x9d, 0x3c, 0xda, 0x6d, 0x59, 0xbd, 0x32, 0x92, 0xc3, 0x48, 0x0b, - 0xf6, 0x50, 0xc6, 0x8c, 0x96, 0x86, 0xbc, 0x90, 0x39, 0x8a, 0x23, 0x41, 0x3e, 0xc0, 0x9d, 0xa1, 0x17, 0x62, 0x1a, - 0xaf, 0x5d, 0x8d, 0x69, 0x8f, 0x0a, 0xed, 0x7f, 0x24, 0xbc, 0x77, 0xf8, 0x2d, 0x04, 0x76, 0x7f, 0x2c, 0x9b, 0x6f, - 0x06, 0x74, 0x7f, 0x2c, 0x11, 0xf4, 0x63, 0xb0, 0xd1, 0xce, 0x0a, 0xe4, 0xb6, 0xfc, 0x53, 0xbf, 0xe1, 0x1a, 0x6d, - 0xe9, 0x17, 0x15, 0x46, 0x52, 0x99, 0x96, 0xf2, 0x3c, 0xe0, 0x32, 0x4f, 0x0d, 0xf2, 0xe5, 0xaa, 0x16, 0x12, 0xd5, - 0x19, 0x86, 0x4a, 0x87, 0xdf, 0xb5, 0x3d, 0x5a, 0xc6, 0x24, 0xca, 0xce, 0xf8, 0x26, 0x4c, 0xc5, 0x3e, 0x9c, 0x32, - 0xbe, 0x71, 0x0f, 0x6f, 0x42, 0xc0, 0x83, 0xf6, 0xb0, 0x29, 0x2c, 0x63, 0x3b, 0x53, 0xf7, 0x80, 0xec, 0xf1, 0x09, - 0x37, 0xba, 0x5b, 0xd5, 0xca, 0xf8, 0x06, 0xec, 0x7f, 0x84, 0x27, 0xe6, 0x72, 0x1c, 0xd5, 0x1c, 0x98, 0x06, 0xcb, - 0xbc, 0x70, 0x0a, 0x70, 0xa5, 0xbc, 0xa5, 0x08, 0xf3, 0x5c, 0x06, 0xb8, 0xbf, 0xc6, 0xdf, 0x6a, 0x96, 0xb8, 0x5f, - 0x70, 0x9c, 0xb3, 0x87, 0x72, 0x44, 0x05, 0x7e, 0x11, 0xbf, 0x07, 0x3a, 0x96, 0x14, 0x9a, 0x1b, 0x2a, 0x7a, 0xc6, - 0xf5, 0x42, 0x76, 0xa6, 0xa5, 0x62, 0x5a, 0xa4, 0xd4, 0xc8, 0x69, 0xb6, 0xe4, 0x71, 0x1a, 0x2b, 0x5b, 0x14, 0xa7, - 0xaa, 0x32, 0x2f, 0xda, 0x81, 0xc5, 0x32, 0xb4, 0xb8, 0x5a, 0x79, 0x55, 0x54, 0x13, 0x66, 0x45, 0x32, 0x10, 0x66, - 0x56, 0x46, 0x45, 0x45, 0xb3, 0x56, 0x7d, 0x3c, 0xb4, 0x9e, 0x50, 0x64, 0x74, 0xf3, 0x0a, 0x1c, 0xb6, 0x0b, 0x41, - 0x75, 0xb7, 0x7d, 0x0a, 0x58, 0xad, 0xae, 0x98, 0xc8, 0xc2, 0xd0, 0x2f, 0x45, 0xaa, 0x6c, 0x99, 0xd3, 0xba, 0x05, - 0xbf, 0xe8, 0x9e, 0x64, 0x59, 0x8d, 0xba, 0xcd, 0x7a, 0x2b, 0xd9, 0xe8, 0x19, 0xdf, 0x95, 0x6c, 0x54, 0xd1, 0x76, - 0xf7, 0x1a, 0xe8, 0xfe, 0xb4, 0x54, 0x35, 0xd7, 0xf6, 0x26, 0xbf, 0x61, 0xba, 0x26, 0xd0, 0xa6, 0x42, 0xb3, 0xe1, - 0x2a, 0x17, 0x79, 0xbe, 0x5f, 0x5c, 0x26, 0x90, 0xb9, 0x3b, 0xfb, 0x8a, 0xfe, 0xb5, 0xd5, 0x28, 0xaf, 0xe3, 0x7a, - 0x5f, 0x93, 0x71, 0xcc, 0xaf, 0xc3, 0xf8, 0x1d, 0xcc, 0x57, 0x56, 0xbe, 0xb8, 0x8b, 0xd2, 0x50, 0x50, 0xcd, 0x5d, - 0x4a, 0x18, 0xbe, 0xb6, 0x60, 0xf8, 0x5a, 0xf1, 0xe9, 0xb2, 0x3f, 0x5e, 0xbe, 0x2c, 0x06, 0x08, 0xf6, 0x73, 0xc3, - 0x32, 0x2e, 0xc5, 0xf6, 0x39, 0xd6, 0x59, 0xd8, 0x65, 0xc1, 0xc2, 0x2e, 0x85, 0xb7, 0x3e, 0x94, 0xe7, 0x7d, 0xbb, - 0x7d, 0x94, 0x4d, 0xce, 0xf6, 0x6d, 0x79, 0xf0, 0xbf, 0x0d, 0xee, 0xed, 0x63, 0x71, 0xb9, 0x23, 0xff, 0x48, 0xa6, - 0xab, 0x28, 0x90, 0x5f, 0x40, 0xda, 0x81, 0x20, 0x5d, 0xeb, 0xce, 0x41, 0x29, 0xa7, 0x4c, 0x22, 0x90, 0x37, 0x9c, - 0x67, 0x82, 0x4f, 0xf5, 0x98, 0x99, 0xbe, 0x66, 0x24, 0x2b, 0xc1, 0x15, 0x2d, 0xa3, 0xed, 0x41, 0xf5, 0x22, 0xd7, - 0xf2, 0x23, 0x4b, 0xa2, 0x20, 0xc3, 0x5a, 0x8a, 0x64, 0x41, 0x92, 0x13, 0x93, 0x6c, 0xbc, 0x59, 0x87, 0x47, 0x2c, - 0x61, 0xd9, 0x84, 0xa6, 0x1e, 0x47, 0xcb, 0x5d, 0x93, 0x71, 0x08, 0xc8, 0xa8, 0xc9, 0xf0, 0x77, 0xe5, 0x85, 0x3f, - 0x1f, 0x46, 0x03, 0x3f, 0xd0, 0x94, 0x8a, 0x09, 0x8f, 0x20, 0x31, 0xc5, 0x8f, 0x8a, 0x1b, 0x4d, 0x07, 0x07, 0x7b, - 0x9e, 0x2b, 0xdd, 0x12, 0x70, 0xf5, 0xdb, 0xae, 0x41, 0xbd, 0x25, 0x5c, 0xcf, 0x29, 0xa7, 0xa6, 0x68, 0x49, 0xd7, - 0x6f, 0xb2, 0x08, 0xff, 0x23, 0xbd, 0xc3, 0x29, 0xca, 0xf3, 0x40, 0x41, 0xed, 0x8e, 0x18, 0x8d, 0x23, 0x17, 0x7f, - 0xa4, 0x77, 0x41, 0x71, 0x5b, 0x5c, 0x5e, 0x6e, 0x96, 0x1b, 0xe8, 0xf2, 0x9b, 0xc4, 0xc5, 0xe5, 0x24, 0xc1, 0x32, - 0xc7, 0x3c, 0x65, 0x63, 0x20, 0xce, 0xaf, 0xe9, 0x5d, 0xa0, 0xc6, 0x63, 0xd6, 0x65, 0x3d, 0xb4, 0x34, 0xa8, 0xf7, - 0xad, 0x62, 0x7b, 0x1b, 0xb4, 0x41, 0xd1, 0x97, 0x7d, 0x07, 0xa4, 0xd2, 0xae, 0x34, 0x0f, 0x11, 0xca, 0x1f, 0xba, - 0x14, 0xfc, 0xa5, 0x2d, 0xda, 0x44, 0x25, 0xf5, 0x75, 0xad, 0x13, 0x85, 0x0e, 0x65, 0xae, 0xc7, 0xa5, 0x97, 0x9a, - 0x53, 0xa7, 0xef, 0x20, 0x58, 0x8e, 0xb0, 0x2f, 0x85, 0x1e, 0x34, 0xf8, 0x4e, 0xa5, 0x84, 0x94, 0x91, 0xa4, 0xa7, - 0x65, 0x3f, 0xe7, 0xd2, 0x03, 0xbc, 0x43, 0x4a, 0x4b, 0x28, 0xaf, 0x63, 0xe6, 0x26, 0x5d, 0xf4, 0x7b, 0x41, 0xbc, - 0xa5, 0x59, 0x42, 0x90, 0xda, 0x58, 0x14, 0x39, 0x50, 0xa1, 0xa6, 0x2f, 0x95, 0x01, 0xc8, 0x46, 0x1e, 0xdb, 0x90, - 0x9a, 0x89, 0x94, 0x9a, 0xbe, 0x85, 0xf1, 0x1d, 0x52, 0x92, 0x4a, 0x64, 0x48, 0x25, 0x52, 0x0a, 0x3d, 0xbd, 0xb9, - 0x9a, 0x84, 0xec, 0x0d, 0x2d, 0xae, 0xcf, 0xa9, 0x3d, 0x4f, 0x2a, 0x60, 0x79, 0x72, 0x1c, 0x94, 0x07, 0xb0, 0x24, - 0xaa, 0x1a, 0xe4, 0xc6, 0x9d, 0x93, 0x9a, 0xfc, 0x56, 0x8f, 0xfb, 0x66, 0x59, 0xc4, 0xa0, 0xc4, 0x9b, 0xa0, 0x65, - 0xea, 0x4d, 0x70, 0x02, 0xf9, 0x88, 0x3c, 0x2f, 0xe0, 0xa7, 0xf6, 0x6e, 0x54, 0xb2, 0x95, 0xb7, 0x5f, 0xf1, 0x03, - 0x65, 0x5e, 0x40, 0x8e, 0x26, 0x4e, 0x0d, 0x4f, 0x49, 0x3d, 0x79, 0xd7, 0xce, 0xda, 0xb6, 0x1f, 0x75, 0x8a, 0x8e, - 0x06, 0xec, 0x7b, 0xe1, 0x2d, 0xad, 0x55, 0xd8, 0x77, 0xb9, 0xf5, 0x95, 0x3f, 0x1d, 0xec, 0x2b, 0x93, 0x48, 0xbd, - 0x8c, 0xac, 0x49, 0x9c, 0xfb, 0x73, 0x2d, 0x7f, 0x9e, 0xd3, 0xf4, 0xee, 0x82, 0x42, 0xae, 0x33, 0x87, 0xbb, 0xbe, - 0xe5, 0x36, 0x94, 0x79, 0xea, 0xbd, 0x44, 0x2a, 0x2b, 0x79, 0xf5, 0x12, 0xe0, 0xfa, 0x15, 0xc1, 0x5c, 0x46, 0x1b, - 0x2d, 0x47, 0x8c, 0x3a, 0x2d, 0x74, 0xe7, 0xe5, 0x49, 0xda, 0x66, 0xe0, 0x5f, 0x2b, 0x31, 0xad, 0x83, 0x05, 0x98, - 0xdb, 0x17, 0x52, 0xfb, 0xd9, 0x60, 0xdd, 0x2b, 0x03, 0x45, 0x10, 0xbe, 0x4b, 0x76, 0x2f, 0x75, 0x5b, 0xd6, 0xec, - 0xee, 0xa5, 0x56, 0x82, 0x7e, 0x32, 0xe5, 0x07, 0xeb, 0x79, 0x8a, 0xcb, 0xcb, 0x2c, 0xcf, 0x51, 0x0e, 0xe0, 0xfd, - 0xd0, 0xf6, 0xbc, 0x1f, 0x74, 0xd2, 0xa0, 0x0f, 0xb1, 0xd8, 0x8b, 0x98, 0x1b, 0x26, 0x5e, 0xce, 0xff, 0xc3, 0xc6, - 0xfc, 0x3f, 0x58, 0x57, 0x4e, 0xc1, 0x34, 0x1a, 0x27, 0x34, 0x32, 0xac, 0x13, 0x29, 0x02, 0x94, 0x7a, 0x5b, 0x26, - 0xc8, 0xc7, 0xab, 0x00, 0x34, 0xae, 0xe5, 0x88, 0x27, 0xa2, 0x3e, 0x0a, 0xa7, 0x2c, 0xbe, 0x0b, 0xe6, 0xac, 0x3e, - 0xe5, 0x09, 0xcf, 0x66, 0xe1, 0x90, 0xe2, 0xec, 0x2e, 0x13, 0x74, 0x5a, 0x9f, 0x33, 0xfc, 0x82, 0xc6, 0x0b, 0x2a, - 0xd8, 0x30, 0xc4, 0xee, 0x59, 0xca, 0xc2, 0xd8, 0x79, 0x1d, 0xa6, 0x29, 0xbf, 0x71, 0xf1, 0x5b, 0x7e, 0xcd, 0x05, - 0xc7, 0x6f, 0x6e, 0xef, 0xc6, 0x34, 0xc1, 0xef, 0xaf, 0xe7, 0x89, 0x98, 0xe3, 0x2c, 0x4c, 0xb2, 0x7a, 0x46, 0x53, - 0x36, 0x6a, 0x0f, 0x79, 0xcc, 0xd3, 0x3a, 0xa4, 0x6c, 0x4f, 0x69, 0x10, 0xb3, 0xf1, 0x44, 0x38, 0x51, 0x98, 0x7e, - 0x6c, 0xd7, 0xeb, 0xb3, 0x94, 0x4d, 0xc3, 0xf4, 0xae, 0x2e, 0x5b, 0x04, 0x9f, 0x37, 0x8e, 0xc2, 0x27, 0xa3, 0xe3, - 0xb6, 0x48, 0xc3, 0x24, 0x63, 0xb0, 0x4d, 0x41, 0x18, 0xc7, 0xce, 0xd1, 0x49, 0x63, 0x9a, 0xed, 0xa9, 0x40, 0x5e, - 0x98, 0x88, 0xfc, 0x0a, 0x7f, 0x04, 0xb8, 0xfd, 0x6b, 0x91, 0xe0, 0xeb, 0xb9, 0x10, 0x3c, 0x59, 0x0e, 0xe7, 0x69, - 0xc6, 0xd3, 0x60, 0xc6, 0x59, 0x22, 0x68, 0xda, 0xbe, 0xe6, 0x69, 0x44, 0xd3, 0x7a, 0x1a, 0x46, 0x6c, 0x9e, 0x05, - 0xc7, 0xb3, 0xdb, 0x36, 0x68, 0x16, 0xe3, 0x94, 0xcf, 0x93, 0x48, 0xcf, 0xc5, 0x92, 0x09, 0x4d, 0x99, 0xb0, 0x2b, - 0xe4, 0x2b, 0x4c, 0x82, 0x98, 0x25, 0x34, 0x4c, 0xeb, 0x63, 0xe8, 0x0c, 0x66, 0x51, 0x23, 0xa2, 0x63, 0x9c, 0x8e, - 0xaf, 0x43, 0xaf, 0xd9, 0x7a, 0x8c, 0xcd, 0xff, 0xfe, 0x09, 0x72, 0x1a, 0xdb, 0x8b, 0x9b, 0x8d, 0xc6, 0x9f, 0x50, - 0x7b, 0x6d, 0x16, 0x09, 0x50, 0xd0, 0x9c, 0xdd, 0x3a, 0x19, 0x87, 0x9c, 0xb6, 0x6d, 0x3d, 0xdb, 0xb3, 0x30, 0x82, - 0x84, 0xe0, 0xa0, 0x35, 0xbb, 0xcd, 0x61, 0x75, 0x81, 0x4a, 0x32, 0xd5, 0x8b, 0xd4, 0x4f, 0xcb, 0xdf, 0x0a, 0xf1, - 0xe9, 0x76, 0x88, 0x5b, 0x06, 0xe2, 0x12, 0xeb, 0xf5, 0x68, 0x9e, 0xca, 0xd8, 0x6a, 0xd0, 0xcc, 0x14, 0x20, 0x13, - 0xbe, 0xa0, 0xa9, 0x81, 0x43, 0x3e, 0xfc, 0x66, 0x30, 0x5a, 0xdb, 0xc1, 0x38, 0xfd, 0x14, 0x18, 0x69, 0x12, 0x2d, - 0xab, 0xfb, 0xda, 0x4c, 0xe9, 0xb4, 0x3d, 0xa1, 0x40, 0x4f, 0x41, 0x0b, 0x7e, 0xdf, 0xb0, 0x48, 0x4c, 0xd4, 0x4f, - 0x49, 0xce, 0x37, 0xaa, 0xee, 0xa4, 0xd1, 0x50, 0xcf, 0x19, 0xfb, 0x85, 0x06, 0x4d, 0x1f, 0x1a, 0xe4, 0x57, 0xf8, - 0x6f, 0xc5, 0x65, 0xde, 0x2a, 0xf7, 0xc4, 0x5f, 0xdb, 0xb7, 0x7c, 0xad, 0x24, 0xc5, 0xf2, 0x46, 0x34, 0x4e, 0x8d, - 0xac, 0x54, 0xc2, 0x07, 0xdc, 0x76, 0xf2, 0x3c, 0x11, 0xd6, 0x2d, 0x6e, 0x71, 0xb2, 0xde, 0xd7, 0x2a, 0xef, 0x22, - 0x80, 0x48, 0x87, 0x95, 0x6c, 0xc8, 0xdb, 0x49, 0x97, 0x34, 0xda, 0x49, 0xbd, 0x8e, 0x3c, 0x4e, 0xd2, 0x7e, 0xa2, - 0xd3, 0xf3, 0x3c, 0xd6, 0xe3, 0xd2, 0xd8, 0xce, 0x50, 0xc0, 0xe1, 0xaa, 0xe9, 0x6a, 0x55, 0x86, 0x01, 0x98, 0xbc, - 0xae, 0xf1, 0x37, 0xa1, 0x1b, 0xe0, 0xcc, 0xe2, 0xe4, 0x89, 0x79, 0xb1, 0x4b, 0x6a, 0x78, 0x45, 0xcc, 0x87, 0x12, - 0x73, 0xfe, 0x2c, 0x14, 0x13, 0xf0, 0x52, 0x14, 0xe2, 0xa7, 0x4c, 0x62, 0x72, 0x0f, 0x5d, 0xd4, 0x4b, 0x8b, 0x0c, - 0x37, 0xc8, 0xe4, 0x4b, 0x73, 0x18, 0xe5, 0x5b, 0x41, 0x60, 0x44, 0xfc, 0x15, 0x51, 0x36, 0x9d, 0xb1, 0xe8, 0xf6, - 0x1f, 0x6a, 0xd1, 0xd1, 0x44, 0x30, 0x99, 0xbb, 0x6d, 0x22, 0x0e, 0x93, 0x30, 0xbb, 0x1c, 0xaa, 0xbb, 0x92, 0x59, - 0x79, 0x33, 0x20, 0x94, 0xd0, 0x2b, 0x23, 0x8d, 0xa6, 0xd2, 0x1e, 0xfd, 0x41, 0xec, 0xb4, 0x4f, 0xd2, 0xfb, 0xec, - 0x93, 0x62, 0xe1, 0x19, 0x9f, 0xa7, 0x43, 0x08, 0x47, 0x6a, 0xa9, 0xb7, 0xe9, 0xb8, 0x71, 0xa5, 0x8a, 0xe1, 0x62, - 0x61, 0x65, 0x82, 0x0a, 0xcc, 0xec, 0x97, 0x4a, 0x50, 0x19, 0xf2, 0x52, 0xf7, 0x35, 0xb4, 0x88, 0x33, 0x4b, 0x02, - 0x99, 0x1d, 0xc9, 0xa4, 0x46, 0x2f, 0x21, 0xdd, 0xc4, 0x9f, 0x27, 0xec, 0xe7, 0x39, 0xbd, 0x64, 0xa0, 0x6b, 0x32, - 0x9f, 0x45, 0x32, 0xd6, 0x04, 0xb2, 0xaf, 0xde, 0x84, 0xe0, 0x05, 0x8b, 0xd4, 0xc6, 0x24, 0xb2, 0x52, 0xe7, 0x36, - 0xb9, 0x75, 0x17, 0xfc, 0xc5, 0xa0, 0x1d, 0x30, 0x1c, 0xf1, 0x69, 0xc8, 0x92, 0x40, 0xba, 0x7c, 0x8b, 0xc1, 0x02, - 0x68, 0x8d, 0x59, 0x14, 0x24, 0x7a, 0x7b, 0x9a, 0xc8, 0xff, 0xc0, 0x59, 0x22, 0xbb, 0xe6, 0x6d, 0x2e, 0x11, 0xaa, - 0xd0, 0x47, 0x0c, 0x82, 0xcf, 0x94, 0x5c, 0xe3, 0x08, 0xdb, 0xd5, 0xc5, 0xb5, 0xf3, 0xca, 0x0e, 0x34, 0xd6, 0x36, - 0x4a, 0x19, 0x01, 0x7c, 0xbd, 0x34, 0xe3, 0xa9, 0xf0, 0xbc, 0x09, 0x8e, 0x11, 0xe9, 0x4e, 0xa4, 0xb3, 0xab, 0x13, - 0xcb, 0x3f, 0xbd, 0x7a, 0x33, 0x68, 0x16, 0xe6, 0x7b, 0xe5, 0x36, 0xb0, 0x4a, 0x8e, 0xd2, 0x37, 0x4a, 0xe5, 0x32, - 0x8a, 0xdf, 0x6a, 0xa9, 0xe5, 0x73, 0xb1, 0x5c, 0xac, 0x8f, 0x9b, 0x12, 0x55, 0x5e, 0x05, 0x08, 0x19, 0x2c, 0xda, - 0x31, 0x15, 0xca, 0xcb, 0x75, 0x17, 0xaa, 0xe4, 0x95, 0x12, 0xd1, 0x97, 0xfb, 0xcb, 0x54, 0xcf, 0x98, 0x5f, 0x31, - 0xe3, 0x64, 0xaa, 0x92, 0x5c, 0xae, 0x31, 0x62, 0xe9, 0xa1, 0xdb, 0x9a, 0x29, 0x58, 0xee, 0x48, 0xba, 0x95, 0x6e, - 0x7d, 0xf5, 0x48, 0x53, 0x52, 0x86, 0xbb, 0x36, 0x06, 0x80, 0x5c, 0xbd, 0x6d, 0x80, 0x81, 0xd9, 0x9a, 0x09, 0xb3, - 0x04, 0xd0, 0xc6, 0x46, 0x14, 0x2e, 0xd2, 0x5c, 0xed, 0x2f, 0xbf, 0x15, 0xf9, 0xa1, 0xd5, 0x54, 0xfe, 0x66, 0x11, - 0xfc, 0x05, 0x09, 0xb8, 0x54, 0x4a, 0x69, 0xe0, 0x7e, 0xf3, 0xe6, 0xe2, 0x9d, 0x8b, 0xe1, 0xdd, 0x5c, 0x34, 0xcd, - 0x82, 0xa5, 0xab, 0x53, 0xe3, 0xea, 0x10, 0x66, 0x75, 0x03, 0x37, 0x9c, 0xc1, 0x55, 0x63, 0xc9, 0x0b, 0x0e, 0x6f, - 0xeb, 0x37, 0x37, 0x37, 0x75, 0xb8, 0x09, 0x55, 0x9f, 0xa7, 0x31, 0x4d, 0x86, 0x3c, 0xa2, 0x91, 0x9b, 0xe7, 0xc8, - 0x17, 0x13, 0x9a, 0x14, 0x6f, 0xef, 0xe1, 0x31, 0xf5, 0x63, 0x3e, 0x56, 0xb7, 0x38, 0xd7, 0xad, 0xea, 0xe1, 0x55, - 0x47, 0xbe, 0x95, 0xaa, 0xdb, 0x11, 0xea, 0x7d, 0x60, 0x22, 0x85, 0x9f, 0x5d, 0x88, 0xb9, 0x74, 0x0e, 0xc5, 0x44, - 0x3e, 0x5c, 0xc0, 0x09, 0x93, 0x4f, 0xfb, 0xcb, 0x0d, 0xea, 0xeb, 0xc1, 0x10, 0x93, 0xae, 0x5a, 0x73, 0x26, 0x5b, - 0x5d, 0x05, 0xc3, 0xab, 0xab, 0xbc, 0x73, 0x08, 0x63, 0x1d, 0x9a, 0x71, 0xaf, 0x79, 0x74, 0x67, 0xfa, 0x17, 0x14, - 0x09, 0x6f, 0x27, 0x4a, 0x49, 0x17, 0x86, 0x80, 0x79, 0xa3, 0x2e, 0x60, 0x05, 0x28, 0x12, 0x7a, 0x47, 0x45, 0x89, - 0x3c, 0xe2, 0xaa, 0x68, 0x17, 0x04, 0xaa, 0x61, 0x79, 0x50, 0x94, 0xfb, 0xb5, 0x24, 0x08, 0x03, 0x52, 0x64, 0x43, - 0x77, 0x85, 0xe0, 0xaf, 0x84, 0xac, 0x73, 0xa8, 0xf0, 0x70, 0x65, 0xbf, 0x0b, 0x45, 0xbd, 0xa7, 0xa0, 0xc0, 0x56, - 0x3f, 0x13, 0xf8, 0xa3, 0xc0, 0x1f, 0xaf, 0x64, 0x53, 0x23, 0xbd, 0x40, 0xad, 0x02, 0x29, 0xdf, 0x30, 0x6a, 0xca, - 0x90, 0xc7, 0x71, 0x38, 0xcb, 0x68, 0x60, 0x7e, 0x68, 0x41, 0x06, 0xf2, 0x70, 0x53, 0x73, 0xd0, 0xf9, 0x38, 0xe7, - 0xa0, 0x5f, 0x6c, 0xaa, 0x35, 0x8b, 0x30, 0xf5, 0xea, 0xf5, 0x61, 0xfd, 0x7a, 0x8c, 0x72, 0x31, 0x59, 0xda, 0x62, - 0xf0, 0x51, 0xa3, 0xd1, 0x86, 0xe4, 0xc9, 0x7a, 0x18, 0xb3, 0x71, 0x12, 0xc4, 0x74, 0x24, 0x72, 0x01, 0xd7, 0xda, - 0x96, 0x46, 0xef, 0xf0, 0x5b, 0x27, 0x29, 0x9d, 0x3a, 0x3e, 0xfc, 0x7b, 0xff, 0xc4, 0xb9, 0x88, 0x82, 0x44, 0x4c, - 0xea, 0x32, 0x4d, 0x17, 0x2e, 0x19, 0x88, 0x49, 0xe5, 0x79, 0x69, 0x4d, 0x34, 0xa4, 0xa0, 0x93, 0xe5, 0x22, 0x75, - 0xc4, 0x04, 0x8b, 0xd4, 0x6e, 0x97, 0xa0, 0xe5, 0xc6, 0x0a, 0x36, 0x55, 0x83, 0x23, 0x94, 0x67, 0x52, 0x93, 0xde, - 0x6c, 0x6c, 0xf4, 0xab, 0xea, 0xd3, 0x06, 0xfa, 0x2c, 0x4d, 0x30, 0x57, 0x9e, 0xe8, 0xa5, 0xea, 0xf1, 0x10, 0x64, - 0x56, 0x74, 0x54, 0x6c, 0xf7, 0x40, 0x39, 0x4b, 0x66, 0x73, 0xd1, 0x97, 0x5e, 0xf0, 0x14, 0x6e, 0x54, 0x0c, 0xb0, - 0x55, 0x02, 0x38, 0x18, 0x2c, 0x15, 0x30, 0xc3, 0x30, 0x1e, 0x7a, 0x00, 0x91, 0x53, 0x77, 0x4e, 0x53, 0x3a, 0x45, - 0xed, 0x29, 0x4b, 0xea, 0xaa, 0xee, 0xc4, 0xd2, 0x63, 0xfc, 0xc7, 0xf0, 0x94, 0xfb, 0x72, 0x34, 0x2c, 0x93, 0x5d, - 0xb7, 0xe0, 0xf2, 0x6a, 0x90, 0xe7, 0xed, 0x54, 0x78, 0xfd, 0xa7, 0x1e, 0x1a, 0xe0, 0xaf, 0xac, 0xb7, 0xb9, 0xb8, - 0xe6, 0xa8, 0xb8, 0xb8, 0x85, 0x76, 0x34, 0xb1, 0xcf, 0x82, 0x6c, 0xf6, 0x15, 0x81, 0x86, 0x2f, 0x3c, 0x97, 0x66, - 0xb3, 0xba, 0x62, 0x76, 0x75, 0x49, 0xb2, 0x2e, 0x74, 0x45, 0xda, 0xb5, 0xfb, 0x83, 0x58, 0x4a, 0x3e, 0xa6, 0x6f, - 0x75, 0x28, 0xef, 0xc3, 0xa0, 0xb8, 0x05, 0xa4, 0x9f, 0xed, 0x7b, 0x3f, 0xa8, 0xc2, 0x4f, 0xae, 0xce, 0xaa, 0x4c, - 0x11, 0x18, 0x59, 0xf1, 0xc6, 0xbb, 0x30, 0x8e, 0x61, 0xc2, 0x2b, 0xa3, 0xef, 0xd8, 0x6f, 0x09, 0xe9, 0x8b, 0x81, - 0x87, 0x72, 0x7d, 0x4e, 0x9f, 0x4a, 0x1d, 0xd4, 0x7a, 0xcd, 0xde, 0x9e, 0x30, 0xd1, 0x25, 0x25, 0xae, 0x19, 0xc4, - 0xc7, 0x2b, 0x89, 0xd4, 0xed, 0x92, 0x77, 0x29, 0x0d, 0xd6, 0x91, 0x0b, 0x22, 0x6e, 0x9a, 0x44, 0xae, 0xf3, 0x97, - 0x61, 0xcc, 0x86, 0x1f, 0x89, 0xbb, 0xbf, 0xf4, 0xd0, 0xe6, 0x3d, 0x49, 0xc9, 0x15, 0x0c, 0x87, 0x47, 0x55, 0xcf, - 0x7b, 0xe2, 0x5b, 0xcc, 0x5b, 0xbd, 0x46, 0xc7, 0xed, 0xee, 0x2f, 0x81, 0xf1, 0xa8, 0x79, 0xba, 0x57, 0xf9, 0x65, - 0xf9, 0x72, 0xac, 0x12, 0x0a, 0x40, 0xb3, 0x2a, 0x77, 0x24, 0x51, 0x11, 0xf7, 0x93, 0x94, 0xe6, 0x3a, 0x8a, 0xa9, - 0x01, 0x9c, 0x42, 0xf3, 0x37, 0xd7, 0xf9, 0x4b, 0x51, 0x46, 0x0b, 0x17, 0x88, 0xcc, 0xe1, 0x20, 0x2e, 0xcc, 0x05, - 0x76, 0xaf, 0x1f, 0x51, 0x11, 0xb2, 0x58, 0x75, 0x69, 0x1b, 0x8b, 0x7d, 0x6d, 0x45, 0xab, 0x55, 0x56, 0x5d, 0x0b, - 0xab, 0x62, 0x50, 0xae, 0xac, 0x73, 0x58, 0xc2, 0x2d, 0x57, 0x26, 0xcf, 0xa4, 0x1d, 0x4b, 0x2c, 0x57, 0xa8, 0xea, - 0x9c, 0xbf, 0x0c, 0xe5, 0x3d, 0x23, 0x00, 0x90, 0x6b, 0x00, 0x21, 0xca, 0xad, 0xee, 0xd1, 0x78, 0x31, 0xe1, 0xbe, - 0x08, 0xd3, 0x31, 0x15, 0x6b, 0x88, 0x8d, 0x55, 0x52, 0x6b, 0xdb, 0x44, 0xb4, 0x37, 0xa0, 0x0d, 0xab, 0xd0, 0x5e, - 0x01, 0xd2, 0x7b, 0xfb, 0x4b, 0x96, 0x93, 0xfd, 0xa5, 0x92, 0x6b, 0xef, 0xdf, 0x7e, 0x05, 0xb7, 0x22, 0x79, 0x02, - 0x96, 0xc8, 0x04, 0x81, 0xa4, 0x95, 0x9b, 0xa3, 0x44, 0x08, 0x97, 0x22, 0x44, 0x71, 0x02, 0x47, 0xce, 0x25, 0x41, - 0xcc, 0x5d, 0xa7, 0xa7, 0x20, 0xa7, 0x91, 0x82, 0x99, 0x24, 0xb2, 0x17, 0xcf, 0x3b, 0x87, 0xaa, 0xb5, 0x12, 0x01, - 0xaa, 0x11, 0x20, 0x41, 0x9e, 0xd3, 0x12, 0x07, 0x90, 0x08, 0x6d, 0xe3, 0x21, 0x62, 0x8b, 0x82, 0xd8, 0xe4, 0x8d, - 0xab, 0x6e, 0x27, 0x0e, 0xaf, 0x69, 0xdc, 0xdd, 0x5f, 0x26, 0xab, 0x55, 0x23, 0xef, 0x1c, 0xaa, 0x47, 0xa7, 0x23, - 0xf9, 0x86, 0x7a, 0x43, 0xa6, 0xdc, 0x62, 0xb8, 0xc6, 0x08, 0xe9, 0xa1, 0x26, 0x2f, 0x2a, 0xd0, 0x03, 0xe4, 0xae, - 0x23, 0x33, 0x32, 0x64, 0xa3, 0x42, 0x83, 0xca, 0x5d, 0x87, 0x45, 0x9b, 0x65, 0x99, 0xa0, 0x33, 0x28, 0x9d, 0xac, - 0x56, 0xcd, 0xdc, 0x75, 0xa6, 0x2c, 0x81, 0xa7, 0x64, 0xb5, 0x92, 0x37, 0x04, 0xa7, 0x2c, 0xf1, 0x1a, 0x40, 0xb6, - 0xae, 0x33, 0x0d, 0x6f, 0xe5, 0x82, 0x4d, 0x4d, 0x78, 0xeb, 0x35, 0x75, 0x95, 0x5f, 0xe0, 0x27, 0x03, 0x8a, 0x2b, - 0x77, 0x34, 0xd6, 0x3b, 0x1a, 0xe1, 0xb9, 0xba, 0xfb, 0x44, 0xbc, 0x88, 0xc4, 0xdb, 0x77, 0x34, 0x32, 0x3b, 0x3a, - 0xdf, 0xb1, 0xa3, 0xf3, 0x7b, 0x76, 0x34, 0xd4, 0xbb, 0xe7, 0x14, 0xb8, 0xe3, 0xab, 0x55, 0xb3, 0x51, 0x62, 0xaf, - 0x73, 0x18, 0xb1, 0x05, 0xec, 0x06, 0xe8, 0x85, 0x82, 0x4d, 0xe9, 0x76, 0xa2, 0xac, 0xa2, 0x98, 0xfe, 0x2a, 0x4c, - 0x96, 0x58, 0x48, 0xaa, 0x58, 0xb0, 0xe9, 0xba, 0x08, 0xd2, 0xfd, 0x91, 0x94, 0xcd, 0x00, 0x0f, 0x19, 0xe0, 0x61, - 0x62, 0xde, 0x98, 0xe9, 0xb9, 0xef, 0x5c, 0xec, 0x3a, 0xae, 0x21, 0xeb, 0xab, 0xfc, 0x12, 0x64, 0x84, 0x5c, 0xdf, - 0x83, 0x68, 0x11, 0x5a, 0xbb, 0xdd, 0xdd, 0x34, 0x07, 0xf1, 0xf4, 0x1b, 0x9e, 0x46, 0x6e, 0xa0, 0x9a, 0xfe, 0x2a, - 0x54, 0x4d, 0x59, 0xa2, 0xb3, 0xb3, 0x76, 0xd2, 0x5a, 0x59, 0x6f, 0x53, 0x5c, 0xeb, 0xe4, 0x44, 0xb5, 0x98, 0x85, - 0x42, 0xd0, 0x34, 0xd1, 0x94, 0xeb, 0xba, 0xff, 0x5f, 0x50, 0xe1, 0x16, 0xbe, 0x12, 0x9a, 0x0d, 0x30, 0x04, 0xa8, - 0x35, 0x7c, 0xcd, 0xf3, 0x95, 0x78, 0xda, 0x2b, 0x35, 0xd8, 0x3b, 0x64, 0x5b, 0x19, 0xaa, 0x08, 0x8c, 0x9e, 0xf9, - 0x94, 0x46, 0x97, 0x92, 0x41, 0xf7, 0x86, 0x57, 0x5a, 0x61, 0x5d, 0x13, 0x77, 0x65, 0x07, 0xec, 0xfe, 0x34, 0x6f, - 0x3d, 0x3e, 0x3e, 0x77, 0xb1, 0xe2, 0xf1, 0x7c, 0x34, 0x72, 0x51, 0xee, 0x3c, 0xac, 0x5b, 0xf3, 0xf8, 0xa7, 0xf9, - 0x97, 0xcf, 0x1b, 0x5f, 0x16, 0x9d, 0x13, 0x20, 0x22, 0x9d, 0x10, 0x60, 0x44, 0x95, 0x05, 0xaf, 0x59, 0xd1, 0x28, - 0x4c, 0x76, 0x2f, 0xa7, 0x6f, 0x2f, 0x27, 0x9b, 0x51, 0x1a, 0x01, 0x71, 0xe2, 0x8d, 0xd2, 0xcb, 0x98, 0x2e, 0xa8, - 0x79, 0x55, 0xe1, 0x96, 0xc9, 0xb6, 0xf4, 0x18, 0xf2, 0x79, 0x22, 0x74, 0x66, 0x84, 0x66, 0xb5, 0xd6, 0x92, 0xae, - 0xe4, 0x1a, 0x6c, 0x1b, 0xe1, 0x4e, 0xc9, 0xb9, 0xaa, 0xf4, 0xca, 0xaf, 0xb0, 0x6b, 0x01, 0xb0, 0x13, 0xb2, 0xde, - 0x8e, 0xf2, 0xa0, 0x81, 0x1b, 0xbb, 0x60, 0xc3, 0x4d, 0x14, 0xb8, 0xee, 0xc0, 0xe0, 0x49, 0x3a, 0x37, 0x2b, 0x6f, - 0x98, 0xd8, 0x89, 0xaf, 0x4f, 0x62, 0xe0, 0x3a, 0x85, 0xc1, 0x12, 0x9a, 0x65, 0x3b, 0x11, 0x50, 0x6c, 0x22, 0x76, - 0xcb, 0xd6, 0xee, 0x8e, 0x51, 0x70, 0x03, 0xc3, 0x09, 0x93, 0x00, 0x17, 0x21, 0x56, 0xdd, 0x8a, 0x8e, 0x46, 0x74, - 0x58, 0xf8, 0x86, 0x21, 0x58, 0x36, 0x62, 0xb1, 0x80, 0x98, 0x91, 0x0c, 0xe6, 0xb8, 0xaf, 0x79, 0x42, 0x5d, 0x64, - 0xd2, 0x3f, 0x35, 0xfc, 0x5a, 0xfe, 0x6f, 0x87, 0x47, 0x8d, 0x58, 0x85, 0x45, 0xcf, 0xb2, 0x5a, 0x19, 0xbf, 0x50, - 0xa5, 0xbc, 0x8a, 0x48, 0x2e, 0x1d, 0x3f, 0xbb, 0x0e, 0xd0, 0xc3, 0x8e, 0xc9, 0xb2, 0xf9, 0xe5, 0x49, 0xb3, 0x91, - 0xbb, 0xd8, 0x85, 0xe1, 0x1e, 0x7a, 0x4a, 0x64, 0xaf, 0x23, 0xe8, 0x35, 0x4f, 0x7e, 0x4d, 0xbf, 0x56, 0xf3, 0x49, - 0xd3, 0xc5, 0xea, 0xcd, 0x03, 0x28, 0x2f, 0x98, 0xc1, 0x10, 0xbc, 0xa5, 0xbf, 0x7b, 0x29, 0xd5, 0xc1, 0x1f, 0x06, - 0xcf, 0xa3, 0x66, 0xc3, 0xc5, 0x6e, 0x26, 0xf8, 0xec, 0x57, 0x2c, 0xe1, 0xc8, 0xc5, 0xee, 0x30, 0xe6, 0x19, 0xb5, - 0xd7, 0xa0, 0xd4, 0xd9, 0xdf, 0xbf, 0x08, 0x05, 0xd1, 0x2c, 0xa5, 0x59, 0xe6, 0xd8, 0xe3, 0x6b, 0x52, 0xfa, 0x04, - 0xc3, 0xdc, 0x4a, 0x71, 0x19, 0x15, 0x12, 0x2f, 0xea, 0xa5, 0x00, 0x36, 0x55, 0xa9, 0xb2, 0x0d, 0x62, 0x93, 0x22, - 0xa0, 0x60, 0x6c, 0x4a, 0xbb, 0xfa, 0xe4, 0xcc, 0x5b, 0x8e, 0x9e, 0x9a, 0x58, 0x05, 0x91, 0x37, 0x27, 0xa8, 0x94, - 0x4c, 0x59, 0x72, 0xb9, 0xa5, 0x34, 0xbc, 0xdd, 0x52, 0x0a, 0x2a, 0x5b, 0x01, 0x9d, 0x7e, 0x5f, 0xcd, 0xa7, 0xb1, - 0x5e, 0x2a, 0x3e, 0x36, 0x88, 0x91, 0x74, 0x74, 0x7e, 0x02, 0x52, 0x6b, 0x1b, 0xe4, 0x08, 0xbf, 0x7d, 0x3a, 0x28, - 0xf9, 0x35, 0xd3, 0x15, 0xa3, 0xfc, 0xbe, 0x15, 0x42, 0x69, 0x1d, 0x1c, 0xde, 0xf1, 0xaf, 0x5a, 0x2b, 0xbd, 0xfd, - 0x34, 0xc1, 0x59, 0x5a, 0xd5, 0xef, 0xd8, 0x7a, 0x7d, 0xf1, 0x7d, 0x7d, 0xef, 0xb7, 0x14, 0x6b, 0xc5, 0xa7, 0xd8, - 0xff, 0x61, 0xcc, 0xa6, 0x25, 0x09, 0x6c, 0x82, 0x29, 0x35, 0x1e, 0xc8, 0x7e, 0xb2, 0x07, 0x51, 0xaa, 0xcf, 0x25, - 0xdc, 0xe9, 0x84, 0x17, 0x67, 0xcc, 0x53, 0x7a, 0x19, 0xf3, 0x9b, 0xf5, 0x17, 0x81, 0xed, 0x6e, 0x3c, 0x61, 0xe3, - 0x89, 0x75, 0x51, 0x8b, 0x92, 0x62, 0x13, 0xee, 0x9d, 0x20, 0xff, 0x97, 0x7f, 0xf6, 0xfd, 0x7f, 0xf9, 0xe7, 0x4f, - 0x36, 0x85, 0xe1, 0xf3, 0x2b, 0x2c, 0xca, 0x61, 0x77, 0x9f, 0xae, 0xed, 0x33, 0x55, 0x71, 0xbe, 0xbd, 0xcd, 0xc6, - 0x26, 0x40, 0xfd, 0xc6, 0x16, 0x6c, 0x14, 0xaa, 0xd3, 0xe7, 0xfc, 0x16, 0xc0, 0x60, 0x5d, 0x9f, 0x84, 0x0c, 0x1a, - 0xfd, 0x2e, 0xd0, 0xae, 0x50, 0xf0, 0xa0, 0x1d, 0xf9, 0xed, 0x18, 0xfe, 0xd4, 0x1a, 0x7e, 0x27, 0xf8, 0xda, 0x3f, - 0x31, 0xbc, 0xba, 0x2a, 0x32, 0xf2, 0xec, 0xae, 0x70, 0xe3, 0xbf, 0xb7, 0x51, 0xa2, 0x15, 0x8f, 0xa0, 0x81, 0xba, - 0xf2, 0x3e, 0x21, 0x19, 0x5e, 0xbd, 0x82, 0xd7, 0xfc, 0x74, 0xae, 0x53, 0xe3, 0xe0, 0xbd, 0x47, 0x38, 0xc0, 0x10, - 0xd5, 0x55, 0xc9, 0x41, 0x37, 0x24, 0x03, 0x94, 0x82, 0xb9, 0x01, 0x60, 0xe2, 0xe1, 0x95, 0xb6, 0x36, 0xcf, 0x95, - 0x1b, 0x26, 0x58, 0x27, 0x6d, 0xed, 0x9e, 0xa9, 0x20, 0x1d, 0x3b, 0xef, 0x24, 0xbe, 0x64, 0x63, 0x5a, 0x5a, 0xf7, - 0xd2, 0xd5, 0x05, 0x76, 0x44, 0xc1, 0x7e, 0x16, 0x61, 0xbc, 0x78, 0x18, 0xe3, 0xdb, 0x2d, 0x50, 0x57, 0xce, 0xea, - 0xdf, 0x5a, 0x25, 0x58, 0xd5, 0x57, 0x15, 0x7d, 0x40, 0x66, 0x25, 0xfc, 0x75, 0x57, 0xe0, 0xf4, 0xef, 0x9f, 0x0e, - 0x9c, 0xf2, 0x07, 0x05, 0x4e, 0xff, 0xfe, 0x87, 0x07, 0x4e, 0xff, 0x6a, 0x07, 0x4e, 0x81, 0x04, 0x7f, 0x7e, 0x50, - 0x70, 0xd3, 0x04, 0x9e, 0xf8, 0x4d, 0x46, 0x9a, 0xda, 0x08, 0x88, 0xf9, 0x18, 0x22, 0x9b, 0xff, 0xf6, 0x81, 0xca, - 0x98, 0x8f, 0xed, 0x30, 0x25, 0xbc, 0xa4, 0x16, 0xe2, 0x92, 0x6d, 0x13, 0x50, 0xd4, 0xa1, 0xc1, 0x46, 0x71, 0x01, - 0xa7, 0x7e, 0x6c, 0x5e, 0x18, 0xe1, 0x06, 0xc5, 0x4b, 0x9f, 0x1a, 0xb8, 0x65, 0x82, 0x87, 0x81, 0x8c, 0x3b, 0x16, - 0x1d, 0x5b, 0x35, 0x73, 0xbb, 0xc4, 0x1e, 0xa1, 0x6d, 0x5e, 0x6a, 0xa3, 0x5e, 0x36, 0xb0, 0x74, 0x7f, 0xba, 0x6d, - 0x3e, 0xed, 0x37, 0xdb, 0x47, 0xcd, 0xa9, 0x1b, 0xb8, 0x20, 0xe0, 0x65, 0x41, 0xa3, 0x7d, 0x74, 0x04, 0x05, 0x37, - 0x56, 0x41, 0x0b, 0x0a, 0x98, 0x55, 0x70, 0x02, 0x05, 0x43, 0xab, 0xe0, 0x11, 0x14, 0x44, 0x56, 0xc1, 0x63, 0x28, - 0x58, 0xb8, 0x79, 0x9f, 0x15, 0xe0, 0x3e, 0x46, 0x03, 0xac, 0xec, 0x2e, 0x53, 0xf6, 0x18, 0x37, 0x21, 0x62, 0x19, - 0x8e, 0x65, 0xa2, 0x15, 0xb8, 0x33, 0x03, 0x8e, 0x6f, 0x26, 0x34, 0x09, 0x20, 0x66, 0xfc, 0x4c, 0x4a, 0x48, 0x5f, - 0xf0, 0x77, 0x6c, 0x4a, 0xcd, 0xe7, 0x41, 0x0c, 0x1e, 0x1c, 0x17, 0xf5, 0x1b, 0x83, 0xbc, 0x5d, 0xec, 0x9c, 0x0a, - 0x75, 0xea, 0xa4, 0x1b, 0xb5, 0x97, 0x65, 0x9d, 0x9a, 0xae, 0x5e, 0xec, 0xf9, 0x8e, 0x08, 0x98, 0xe5, 0x49, 0x19, - 0xc5, 0xfc, 0xa6, 0x7e, 0xeb, 0x76, 0xb7, 0x47, 0xc5, 0x00, 0xa2, 0x22, 0x2a, 0x26, 0xd7, 0x54, 0x3c, 0xbd, 0x0b, - 0xc7, 0xc5, 0xef, 0x57, 0x34, 0xcb, 0xc2, 0xb1, 0x6e, 0xb9, 0x3b, 0x0a, 0x26, 0x41, 0xb4, 0x23, 0x60, 0x06, 0x08, - 0x88, 0x64, 0xc1, 0x66, 0x81, 0x27, 0x42, 0x07, 0xb6, 0x00, 0x3b, 0xd5, 0x98, 0x98, 0x9c, 0xbe, 0x5a, 0x24, 0xc2, - 0x71, 0x59, 0xd0, 0x99, 0xa5, 0x54, 0x96, 0x2a, 0x0c, 0xe7, 0x9d, 0x43, 0x28, 0x50, 0xd5, 0x3b, 0x62, 0x5f, 0xc6, - 0xed, 0xb1, 0x3b, 0x02, 0xe6, 0x98, 0xd8, 0x97, 0x9d, 0x5e, 0x54, 0xe4, 0x16, 0x6d, 0x46, 0x5c, 0x3e, 0x6f, 0x0e, - 0xe1, 0x3f, 0x1d, 0xcf, 0xf9, 0x7c, 0x34, 0x1a, 0xdd, 0x1b, 0x0b, 0xfb, 0x3c, 0x1a, 0xd1, 0x16, 0x3d, 0x69, 0x43, - 0xea, 0x49, 0x5d, 0x47, 0x50, 0x9a, 0xb9, 0xc4, 0xdd, 0xf2, 0x61, 0x8d, 0x21, 0xd8, 0x22, 0x26, 0xcb, 0x87, 0xc7, - 0xc5, 0xf2, 0x59, 0x4a, 0x97, 0xd3, 0x30, 0x1d, 0xb3, 0x24, 0x68, 0xe4, 0xfe, 0x42, 0x07, 0x92, 0x3e, 0x3f, 0x3d, - 0x3d, 0xcd, 0xfd, 0xc8, 0x3c, 0x35, 0xa2, 0x28, 0xf7, 0x87, 0xcb, 0x62, 0x19, 0x8d, 0xc6, 0x68, 0x94, 0xfb, 0xcc, - 0x14, 0x1c, 0xb5, 0x86, 0xd1, 0x51, 0x2b, 0xf7, 0x6f, 0xac, 0x16, 0xb9, 0x4f, 0xf5, 0x53, 0x4a, 0xa3, 0x4a, 0xfe, - 0xca, 0xe3, 0x46, 0x23, 0xf7, 0x15, 0xa1, 0x2d, 0xc1, 0x98, 0x54, 0x3f, 0x83, 0x70, 0x2e, 0x38, 0xb0, 0xe4, 0x36, - 0x17, 0x5e, 0xff, 0x52, 0xbf, 0x1b, 0x44, 0x7d, 0x47, 0x23, 0x47, 0x03, 0xfc, 0xb3, 0x1d, 0xf2, 0x01, 0x62, 0x96, - 0xa1, 0x1e, 0x6e, 0x22, 0x42, 0x95, 0x6a, 0xa0, 0x2c, 0x59, 0xfd, 0x33, 0xe1, 0x65, 0x24, 0x08, 0xf8, 0x0f, 0xb4, - 0x54, 0x2f, 0xb1, 0x13, 0x74, 0x07, 0xd7, 0xa7, 0xf4, 0x93, 0x5c, 0xff, 0xee, 0x21, 0x4c, 0x9f, 0xd2, 0x3f, 0x9a, - 0xe9, 0xeb, 0x37, 0xbd, 0x2a, 0xa6, 0xaf, 0xd8, 0xda, 0x54, 0x10, 0x77, 0x38, 0xa1, 0xc3, 0x8f, 0xd7, 0xfc, 0xb6, - 0x0e, 0x47, 0x22, 0x75, 0x25, 0x3f, 0x1d, 0xfd, 0xd6, 0x5c, 0x17, 0x33, 0x98, 0xf5, 0x19, 0x0e, 0x29, 0xf4, 0xdf, - 0x24, 0xc4, 0x7d, 0x63, 0x2c, 0x52, 0x55, 0x32, 0x1a, 0x11, 0xf7, 0xcd, 0x68, 0xe4, 0x9a, 0x1b, 0x8e, 0xa1, 0xa0, - 0xb2, 0xd5, 0xeb, 0x4a, 0x89, 0x6c, 0xf5, 0xe5, 0x97, 0x76, 0x99, 0x5d, 0xa0, 0x03, 0x46, 0x76, 0x70, 0x48, 0xd7, - 0x44, 0x2c, 0x83, 0xa3, 0x06, 0x5f, 0x07, 0xa9, 0xbe, 0x62, 0x31, 0xad, 0xbc, 0x0d, 0xbb, 0x00, 0x78, 0xcb, 0x2b, - 0xbc, 0xd7, 0xaf, 0xf7, 0x8f, 0xa9, 0xc9, 0x36, 0x7c, 0x7a, 0xf7, 0x55, 0xe4, 0x4d, 0x05, 0xca, 0x59, 0xf6, 0x26, - 0x59, 0xbb, 0xba, 0xa3, 0x60, 0x24, 0xc4, 0x5e, 0x56, 0x2e, 0xf8, 0x78, 0x1c, 0xc3, 0xf7, 0x59, 0x96, 0x95, 0xd7, - 0xbe, 0xaa, 0xee, 0xbd, 0xca, 0x7a, 0x03, 0xbb, 0xa3, 0x7e, 0x49, 0xaa, 0xfc, 0x5c, 0x94, 0x4a, 0xf9, 0x5e, 0xe8, - 0xef, 0x06, 0x49, 0x63, 0x76, 0xa9, 0xf9, 0xff, 0x52, 0x25, 0x0a, 0x0b, 0x48, 0x92, 0x51, 0x03, 0x47, 0x79, 0xae, - 0xaf, 0x58, 0x44, 0x2c, 0x9b, 0xc1, 0xeb, 0x48, 0x55, 0x4f, 0xfa, 0x29, 0x16, 0x9e, 0xdd, 0x58, 0x51, 0x99, 0xca, - 0x76, 0xe5, 0x26, 0x2c, 0xa3, 0xdc, 0xdc, 0x53, 0x91, 0xbb, 0xda, 0x5b, 0x6e, 0x90, 0xe8, 0x3a, 0x0a, 0x9f, 0x2a, - 0x5e, 0x64, 0xad, 0x10, 0x5c, 0xd6, 0xc5, 0x86, 0x98, 0x2a, 0x53, 0x90, 0xdb, 0x51, 0x47, 0x59, 0xa3, 0xb0, 0x25, - 0x63, 0x1c, 0xd9, 0x2c, 0x4c, 0x14, 0x1b, 0x25, 0xae, 0xe2, 0x07, 0xfb, 0xcb, 0x72, 0xe7, 0x73, 0xd7, 0x80, 0xad, - 0x88, 0xb7, 0xdb, 0x39, 0x84, 0x0e, 0x5d, 0xa7, 0x02, 0x7a, 0xb2, 0x11, 0x1a, 0xf9, 0x44, 0x92, 0xc2, 0x95, 0x9f, - 0xdd, 0x60, 0x3f, 0xbb, 0x71, 0xfe, 0xbc, 0xac, 0xdf, 0xd0, 0xeb, 0x8f, 0x4c, 0xd4, 0x45, 0x38, 0xab, 0x83, 0xb9, - 0x22, 0x5d, 0x9a, 0x9a, 0x3d, 0xcb, 0xdc, 0x3c, 0xf5, 0x82, 0x82, 0xf6, 0x3c, 0x83, 0x5c, 0x06, 0xa9, 0x74, 0x07, - 0x09, 0x4f, 0x68, 0xbb, 0x9a, 0x83, 0x69, 0x87, 0xc6, 0x0d, 0xb6, 0x06, 0x4b, 0x0e, 0xb9, 0x0f, 0xe2, 0x2e, 0x68, - 0x68, 0xb6, 0xde, 0x30, 0x71, 0xef, 0xc6, 0xd6, 0xf6, 0x81, 0x46, 0x6e, 0x4d, 0x4a, 0xaf, 0x74, 0x33, 0xfe, 0xbf, - 0x2b, 0x7e, 0xff, 0xa9, 0x8c, 0x44, 0x70, 0x84, 0x9a, 0xff, 0xad, 0x54, 0xce, 0xf5, 0x62, 0x99, 0x91, 0xf8, 0x10, - 0xc8, 0x82, 0x70, 0x24, 0x68, 0x8a, 0x1f, 0xd2, 0xf2, 0x5a, 0x5e, 0x1e, 0x5a, 0x82, 0x98, 0x09, 0x9a, 0xa7, 0xb3, - 0xdb, 0x87, 0x0f, 0x7f, 0xff, 0xf2, 0x73, 0x8d, 0x23, 0xf3, 0x32, 0x1d, 0xd7, 0x6d, 0xc3, 0x41, 0x88, 0xc3, 0xbb, - 0x80, 0x25, 0x52, 0xe6, 0x5d, 0x83, 0x37, 0xb3, 0x3d, 0xe3, 0x3a, 0xb5, 0x36, 0xa5, 0xb1, 0xfc, 0x72, 0x9e, 0xde, - 0x8a, 0xa3, 0x47, 0xb3, 0x5b, 0xb3, 0x1b, 0xcd, 0xb5, 0x94, 0xd9, 0x3f, 0x34, 0x33, 0x76, 0x77, 0x2a, 0x6e, 0x35, - 0xbb, 0xf3, 0x64, 0x76, 0xdb, 0x56, 0x82, 0xb6, 0x9e, 0x2a, 0xa8, 0x1a, 0xb3, 0x5b, 0x3b, 0x37, 0xb8, 0x1c, 0xc8, - 0xf1, 0x8f, 0x32, 0x87, 0x86, 0x19, 0x6d, 0xc3, 0xeb, 0xc2, 0xd9, 0x30, 0x8c, 0xb5, 0x30, 0x9f, 0xb2, 0x28, 0x8a, - 0x69, 0xdb, 0xc8, 0x6b, 0xa7, 0xf9, 0x08, 0x52, 0x6b, 0xed, 0x2d, 0xab, 0xee, 0x8a, 0x85, 0xbc, 0x02, 0x4f, 0xe1, - 0x75, 0xc6, 0x63, 0xf8, 0x56, 0xc7, 0x56, 0x74, 0xea, 0x9c, 0xd3, 0x46, 0x89, 0x3c, 0xf9, 0xbb, 0xba, 0x96, 0x93, - 0xc6, 0x9f, 0xda, 0x72, 0xc3, 0x1b, 0x6d, 0xc1, 0x67, 0x41, 0xfd, 0xa8, 0xba, 0x10, 0xa8, 0x2a, 0x96, 0x80, 0xb7, - 0x2c, 0x0b, 0x83, 0xb4, 0x52, 0x7c, 0xda, 0xf1, 0x9b, 0xba, 0x4c, 0x0e, 0x00, 0xd9, 0x5c, 0x45, 0x51, 0x5e, 0x5d, - 0xcc, 0xbf, 0xcd, 0x69, 0x79, 0xb2, 0xfd, 0xb4, 0x3c, 0x31, 0xa7, 0xe5, 0x7e, 0x8a, 0xfd, 0x7c, 0xd4, 0x84, 0xff, - 0xda, 0xe5, 0x82, 0x82, 0x86, 0x73, 0x34, 0xbb, 0x75, 0x40, 0x4f, 0xab, 0xb7, 0x66, 0xb7, 0x2a, 0x33, 0x1a, 0x22, - 0x2e, 0x0d, 0xc8, 0x15, 0xc6, 0x0d, 0x07, 0x0a, 0xe1, 0xff, 0x46, 0xa5, 0xaa, 0x79, 0x0c, 0x75, 0xd0, 0xeb, 0x64, - 0xb3, 0xae, 0x75, 0xff, 0xa1, 0x0d, 0x12, 0x2e, 0xbc, 0xc0, 0x70, 0x63, 0xe4, 0x8b, 0xf0, 0xfa, 0x9a, 0x46, 0xc1, - 0x88, 0x0f, 0xe7, 0xd9, 0x3f, 0x69, 0xf8, 0x35, 0x12, 0xef, 0x3d, 0xd2, 0x6b, 0xe3, 0x98, 0xae, 0x2a, 0x4f, 0xdb, - 0x8c, 0xb0, 0x2c, 0xf6, 0x29, 0xc8, 0x86, 0x61, 0x4c, 0xbd, 0x96, 0x7f, 0xbc, 0xe5, 0x10, 0xfc, 0xbb, 0xec, 0xcd, - 0xd6, 0xc5, 0xfc, 0x5e, 0x64, 0xdc, 0x8b, 0x84, 0x5f, 0x85, 0x03, 0x7b, 0x0f, 0x1b, 0xa7, 0xdb, 0xc1, 0xed, 0x9b, - 0x99, 0x06, 0x46, 0x28, 0x68, 0xb9, 0x13, 0xd1, 0x51, 0x38, 0x8f, 0xc5, 0xfd, 0xa3, 0xee, 0xa2, 0x8c, 0x8d, 0x51, - 0xef, 0x61, 0xe8, 0x65, 0xdb, 0x07, 0x72, 0xe9, 0xcf, 0x9f, 0x1c, 0xc3, 0x7f, 0x2a, 0x6b, 0xeb, 0xae, 0xd4, 0xd5, - 0x95, 0xad, 0x0a, 0xba, 0xfa, 0xa8, 0xa2, 0x8c, 0x2b, 0x11, 0x2e, 0xf5, 0xf1, 0x87, 0xb6, 0x06, 0xad, 0xf2, 0x41, - 0xcd, 0xb5, 0x96, 0xf5, 0xab, 0x5a, 0xff, 0xba, 0xc1, 0x1f, 0xd8, 0x76, 0xa8, 0x34, 0xd7, 0x6a, 0x5b, 0xfd, 0xe9, - 0xc0, 0x8d, 0xc6, 0x06, 0xe3, 0xb2, 0xfd, 0x88, 0xdc, 0x15, 0x26, 0x8a, 0x8a, 0xa1, 0x82, 0x95, 0x32, 0x52, 0x56, - 0x0a, 0xa3, 0xe4, 0xaa, 0xd3, 0xbb, 0x9d, 0xc6, 0xce, 0x42, 0x5d, 0x72, 0x24, 0x6e, 0xd3, 0x6f, 0xb8, 0x8e, 0x8c, - 0xde, 0xc3, 0xcb, 0xd6, 0x5d, 0xf9, 0x49, 0x5a, 0xb7, 0x07, 0x9a, 0xd6, 0x62, 0x2c, 0x35, 0xbb, 0x97, 0xe1, 0x1d, - 0x4d, 0x2f, 0x5b, 0xae, 0x03, 0xde, 0x95, 0xba, 0x4a, 0x74, 0x90, 0x65, 0x4e, 0xcb, 0x75, 0x6e, 0xa7, 0x71, 0x92, - 0x11, 0x77, 0x22, 0xc4, 0x2c, 0x50, 0xdf, 0xac, 0xbd, 0x39, 0xf2, 0x79, 0x3a, 0x3e, 0x6c, 0x35, 0x1a, 0x0d, 0x78, - 0x71, 0xab, 0xeb, 0x2c, 0x18, 0xbd, 0x79, 0xca, 0x6f, 0x89, 0xdb, 0x70, 0x1a, 0x4e, 0xb3, 0x75, 0xea, 0x34, 0x5b, - 0xc7, 0xfe, 0xa3, 0x53, 0xb7, 0xfb, 0x99, 0xe3, 0x74, 0x22, 0x3a, 0xca, 0xe0, 0x87, 0xe3, 0x74, 0xa4, 0xe2, 0xa5, - 0x7e, 0x3b, 0x8e, 0x3f, 0x8c, 0xb3, 0x7a, 0xd3, 0x59, 0xea, 0x47, 0xc7, 0x81, 0xab, 0xa0, 0x81, 0xf3, 0xf9, 0xa8, - 0x35, 0x3a, 0x1e, 0x3d, 0x69, 0xeb, 0xe2, 0xfc, 0xb3, 0x4a, 0x73, 0xac, 0xfe, 0xb6, 0xac, 0x6e, 0x99, 0x48, 0xf9, - 0x47, 0xaa, 0x33, 0x09, 0x1d, 0x10, 0x3d, 0x5b, 0xbb, 0xb6, 0x36, 0x67, 0x6a, 0x9e, 0x5e, 0x0f, 0x47, 0xad, 0xb2, - 0xb9, 0x84, 0xf1, 0xb0, 0x00, 0xb2, 0x73, 0x68, 0x40, 0xef, 0xd8, 0x68, 0x6a, 0xd6, 0xb7, 0x21, 0xaa, 0xe9, 0xea, - 0x35, 0x8e, 0xcd, 0xfa, 0x3a, 0x70, 0xf3, 0xc0, 0xe8, 0xaa, 0x12, 0x02, 0xd7, 0x89, 0x88, 0xfb, 0xaa, 0xd9, 0x3a, - 0xc5, 0xcd, 0xe6, 0x23, 0xff, 0xd1, 0xe9, 0xb0, 0x81, 0x8f, 0xfd, 0xe3, 0xfa, 0x91, 0xff, 0x08, 0x9f, 0xd6, 0x4f, - 0xf1, 0xe9, 0x8b, 0xd3, 0x61, 0xfd, 0xd8, 0x3f, 0xc6, 0x8d, 0xfa, 0x29, 0x14, 0xd6, 0x4f, 0xeb, 0xa7, 0x8b, 0xfa, - 0xf1, 0xe9, 0xb0, 0x21, 0x4b, 0x5b, 0xfe, 0xc9, 0x49, 0xbd, 0xd9, 0xf0, 0x4f, 0x4e, 0xf0, 0x89, 0xff, 0xe8, 0x51, - 0xbd, 0x79, 0xe4, 0x3f, 0x7a, 0xf4, 0xf2, 0xe4, 0xd4, 0x3f, 0x82, 0xba, 0xa3, 0xa3, 0xe1, 0x91, 0xdf, 0x6c, 0xd6, - 0xe1, 0x1f, 0x7c, 0xea, 0xb7, 0xd4, 0x8f, 0x66, 0xd3, 0x3f, 0x6a, 0xe2, 0x46, 0x7c, 0xd2, 0xf2, 0x1f, 0x3d, 0xc1, - 0xf2, 0x5f, 0xd9, 0x0c, 0xcb, 0x7f, 0x60, 0x18, 0xfc, 0xc4, 0x6f, 0x3d, 0x52, 0xbf, 0xe4, 0x80, 0x8b, 0xe3, 0xd3, - 0x1f, 0xdd, 0xc3, 0x9d, 0x6b, 0x68, 0xaa, 0x35, 0x9c, 0x9e, 0xf8, 0x47, 0x47, 0xf8, 0xb8, 0xe9, 0x9f, 0x1e, 0x4d, - 0xea, 0xc7, 0x2d, 0xff, 0xd1, 0xe3, 0x61, 0xbd, 0xe9, 0x3f, 0x7e, 0x8c, 0x1b, 0xf5, 0x23, 0xbf, 0x85, 0x9b, 0xfe, - 0xf1, 0x91, 0xfc, 0x71, 0xe4, 0xb7, 0x16, 0x8f, 0x9f, 0xf8, 0x8f, 0x4e, 0x26, 0x8f, 0xfc, 0xe3, 0xef, 0x8e, 0x4f, - 0xfd, 0xd6, 0xd1, 0xe4, 0xe8, 0x91, 0xdf, 0x7a, 0xbc, 0x78, 0xe4, 0x1f, 0x4f, 0xea, 0xad, 0x47, 0xf7, 0xf6, 0x6c, - 0xb6, 0x7c, 0xc0, 0x91, 0xac, 0x86, 0x0a, 0xac, 0x2b, 0xe0, 0xff, 0x89, 0xec, 0xfb, 0xef, 0x38, 0x4c, 0xb6, 0xd9, - 0xf5, 0x89, 0x7f, 0xfa, 0x78, 0xa8, 0x9a, 0x43, 0x41, 0xdd, 0xb4, 0x80, 0x2e, 0x8b, 0xba, 0x9a, 0x56, 0x0e, 0x57, - 0x37, 0x03, 0x99, 0xff, 0xf5, 0x64, 0x8b, 0x3a, 0x4c, 0xac, 0xe6, 0xfd, 0x0f, 0x1d, 0xa7, 0xd8, 0xf2, 0xce, 0xe1, - 0x58, 0x91, 0xfe, 0xb8, 0xfb, 0x99, 0x7a, 0x2b, 0xf3, 0x67, 0x57, 0x38, 0xdb, 0xe5, 0xf8, 0x48, 0x3f, 0xed, 0xf8, - 0x48, 0xe8, 0x43, 0x3c, 0x1f, 0xe9, 0x1f, 0xee, 0xf9, 0xc8, 0xe8, 0x9a, 0xbb, 0xfb, 0x4e, 0x6c, 0x38, 0x38, 0xd6, - 0xad, 0xe2, 0x17, 0xc2, 0xeb, 0x33, 0xf8, 0xfc, 0x57, 0xde, 0xbe, 0x83, 0x37, 0xbf, 0xdb, 0x7e, 0x20, 0x0e, 0x2c, - 0xf6, 0x4e, 0x28, 0x1e, 0xcb, 0x77, 0x21, 0x24, 0xfe, 0x34, 0x42, 0xbe, 0x7b, 0x08, 0x3e, 0xe2, 0x3f, 0x1c, 0x1f, - 0xdc, 0xc6, 0x47, 0xc5, 0x03, 0x2f, 0x3d, 0x0d, 0xd2, 0x53, 0x70, 0x21, 0x9f, 0x3d, 0xb8, 0xfa, 0x54, 0x73, 0x0f, - 0x29, 0x14, 0x65, 0xae, 0x8a, 0x57, 0xbd, 0xfe, 0x35, 0xc1, 0x02, 0x75, 0xcf, 0x91, 0xb8, 0xda, 0x2d, 0x33, 0x93, - 0x52, 0x47, 0x3f, 0x14, 0x42, 0xa9, 0xe5, 0x37, 0xfc, 0x46, 0xe1, 0xd2, 0x81, 0xbb, 0xad, 0x64, 0xc9, 0x45, 0x08, - 0x5f, 0x99, 0x8d, 0xf9, 0x58, 0x7e, 0x8f, 0x16, 0xbe, 0x02, 0x20, 0xbf, 0x0c, 0xac, 0x3e, 0xc0, 0x10, 0xb8, 0xae, - 0x7e, 0x23, 0x06, 0xdc, 0x9d, 0xfc, 0x16, 0xee, 0x97, 0x9a, 0x58, 0xc2, 0x14, 0xbc, 0x1d, 0xaf, 0x68, 0xc4, 0x42, - 0xcf, 0xf5, 0x66, 0x29, 0x1d, 0xd1, 0x34, 0xab, 0x57, 0x2e, 0x5d, 0xca, 0xfb, 0x96, 0xc8, 0x35, 0xdf, 0x33, 0x4d, - 0xe1, 0xad, 0xd6, 0xa4, 0xaf, 0xfd, 0x8d, 0xae, 0x36, 0xc0, 0xdc, 0x1c, 0x9b, 0x92, 0x14, 0x64, 0x6d, 0xa9, 0xb4, - 0xb9, 0x4a, 0x6b, 0x6b, 0xfa, 0xad, 0x13, 0xe4, 0xc8, 0x62, 0x78, 0x5b, 0xf0, 0x0f, 0x5e, 0xfd, 0xa8, 0xf1, 0x27, - 0x64, 0x75, 0x2b, 0x06, 0x1a, 0x68, 0x77, 0x5b, 0x5a, 0x7e, 0x07, 0xba, 0x7a, 0x23, 0xd6, 0x55, 0x14, 0xf1, 0xb9, - 0x5a, 0x3b, 0xbc, 0x77, 0x58, 0xc7, 0xa5, 0xd5, 0x7b, 0x1d, 0x46, 0x6c, 0xec, 0xd9, 0x5f, 0xf9, 0x55, 0x6f, 0x23, - 0x96, 0x1f, 0x07, 0x47, 0x79, 0xd9, 0x24, 0x45, 0x4b, 0x19, 0x25, 0x61, 0x89, 0x93, 0xae, 0x56, 0x5e, 0x0a, 0x2e, - 0x72, 0x62, 0xe1, 0x14, 0x9e, 0x51, 0x05, 0xc9, 0x29, 0x2e, 0x00, 0x92, 0x08, 0x26, 0xa9, 0xfa, 0x5b, 0x16, 0x9b, - 0x1f, 0xda, 0xf1, 0xe5, 0xc7, 0x61, 0x32, 0x06, 0x2a, 0x0c, 0x93, 0xf1, 0x86, 0x5b, 0x4d, 0x05, 0x7a, 0xd6, 0x4a, - 0xcb, 0xa1, 0x4a, 0xf7, 0x59, 0xf6, 0xf4, 0xee, 0x9d, 0x7e, 0x6d, 0x99, 0x0b, 0xde, 0x69, 0x19, 0x95, 0x28, 0x5f, - 0xb1, 0x5c, 0x23, 0x5f, 0x74, 0xa6, 0x54, 0x84, 0x2a, 0xcb, 0x12, 0xf4, 0x09, 0xb8, 0xeb, 0xea, 0x68, 0x6b, 0x94, - 0xb8, 0x52, 0xba, 0x93, 0x88, 0x2e, 0xd8, 0x50, 0x8b, 0x7a, 0xec, 0xe8, 0xfb, 0xfe, 0x75, 0xb9, 0x35, 0xa4, 0x89, - 0x95, 0x3f, 0x66, 0x18, 0xca, 0x3c, 0x7a, 0x92, 0x70, 0xb7, 0xfb, 0x45, 0xf1, 0xd1, 0xd2, 0x5d, 0x9b, 0x10, 0xb3, - 0xe4, 0x63, 0x3f, 0xa5, 0xf1, 0x3f, 0x91, 0x2f, 0xd8, 0x90, 0x27, 0x5f, 0x0c, 0xe0, 0x4b, 0xf2, 0xfe, 0x24, 0xa5, - 0x23, 0xf2, 0x05, 0xc8, 0xf8, 0x40, 0x5a, 0x1f, 0xc0, 0x08, 0x6b, 0xb7, 0xd3, 0x18, 0x4b, 0x8d, 0xe9, 0x01, 0x0a, - 0x91, 0x02, 0xd7, 0x6d, 0x9d, 0xb8, 0x8e, 0xb2, 0x89, 0xe5, 0xef, 0xae, 0x12, 0xa7, 0x52, 0x09, 0x70, 0x9a, 0x2d, - 0xff, 0x64, 0xd2, 0xf2, 0x9f, 0x2c, 0x1e, 0xfb, 0xa7, 0x93, 0xe6, 0xe3, 0x45, 0x1d, 0xfe, 0xb6, 0xfc, 0x27, 0x71, - 0xbd, 0xe5, 0x3f, 0x81, 0xff, 0xbf, 0x3b, 0xf6, 0x4f, 0x26, 0xf5, 0xa6, 0x7f, 0xba, 0x38, 0xf2, 0x8f, 0x5e, 0x36, - 0x5b, 0xfe, 0x91, 0xd3, 0x74, 0x54, 0x3f, 0x60, 0xd7, 0x8a, 0x3b, 0x7f, 0xb1, 0x76, 0x20, 0xb6, 0x04, 0xd1, 0x54, - 0xa6, 0xa8, 0x8b, 0xbd, 0xe2, 0xd3, 0x88, 0xfa, 0x7c, 0x6a, 0x67, 0xdd, 0xb3, 0x30, 0x85, 0xef, 0xd3, 0x54, 0xcf, - 0x6e, 0xa5, 0x0e, 0x57, 0xf8, 0xc5, 0x96, 0x29, 0xe0, 0x84, 0xbb, 0xd8, 0xbe, 0x30, 0x0f, 0xb7, 0xcd, 0xe5, 0xdb, - 0xbc, 0xcd, 0x4b, 0x0d, 0x77, 0x93, 0xb6, 0x6a, 0x68, 0x5e, 0x9c, 0x28, 0x99, 0x05, 0x93, 0x5f, 0x4e, 0x90, 0x93, - 0x7c, 0x15, 0xe5, 0xeb, 0xf3, 0x43, 0xc2, 0x6a, 0xca, 0xad, 0x77, 0x06, 0xd0, 0xf2, 0x9a, 0x45, 0xc4, 0xe0, 0x2d, - 0x0f, 0x79, 0x6e, 0x40, 0xaf, 0xb8, 0x69, 0x4b, 0x2c, 0x49, 0x7e, 0x41, 0xb3, 0x9e, 0x0b, 0x45, 0x6e, 0xe0, 0x4a, - 0x17, 0x9f, 0x5b, 0x7c, 0xa3, 0xa7, 0x20, 0xec, 0xb2, 0x00, 0xcb, 0xab, 0x52, 0x70, 0x6a, 0x01, 0x3f, 0x2e, 0x3a, - 0x38, 0xd8, 0x79, 0x5e, 0xa4, 0x02, 0x09, 0x6b, 0x2d, 0xbf, 0xed, 0x61, 0xb3, 0x22, 0xd7, 0x46, 0x74, 0x31, 0xae, - 0x44, 0x21, 0xd2, 0x78, 0xba, 0xa6, 0xa1, 0xf0, 0xc3, 0x44, 0xa5, 0xbe, 0x58, 0x0c, 0x0b, 0x37, 0xe9, 0x11, 0xca, - 0xb9, 0x08, 0xad, 0x8f, 0xf7, 0xea, 0x73, 0xce, 0x45, 0x68, 0x6e, 0xc0, 0x26, 0xa2, 0x72, 0xe3, 0x63, 0xd2, 0xea, - 0xbe, 0x79, 0x77, 0xe6, 0xa8, 0xe3, 0xd9, 0x39, 0x9c, 0xb4, 0xba, 0x1d, 0xe9, 0x33, 0x51, 0xf7, 0xe7, 0x88, 0xba, - 0x3f, 0xe7, 0xe8, 0xbb, 0x94, 0x10, 0x49, 0xcb, 0x0f, 0xd5, 0xb2, 0xa5, 0xcd, 0xa0, 0xbc, 0xbd, 0xd3, 0x79, 0x2c, - 0x18, 0xbc, 0x99, 0xfa, 0x50, 0x5e, 0x9e, 0x83, 0x0d, 0x2b, 0xb2, 0xa7, 0xb5, 0x76, 0x78, 0x2d, 0x12, 0xe3, 0x1b, - 0x1e, 0xb1, 0x98, 0x9a, 0x7c, 0x69, 0x3d, 0x54, 0x91, 0xdf, 0xbf, 0xd9, 0x3a, 0x9b, 0x5f, 0x4f, 0x99, 0x70, 0xcd, - 0x2d, 0x84, 0xf7, 0xba, 0x43, 0x47, 0x4e, 0xd5, 0xbd, 0xca, 0xb5, 0xf3, 0xda, 0x7c, 0x85, 0xa7, 0xba, 0xa5, 0x7a, - 0xf5, 0x5a, 0x42, 0xc0, 0xbd, 0xb6, 0xc9, 0x51, 0xb7, 0x70, 0x17, 0xdb, 0x75, 0x79, 0xe7, 0x70, 0x72, 0xd4, 0xbd, - 0x0a, 0x66, 0x7a, 0xbc, 0x97, 0x7c, 0xbc, 0x7d, 0xac, 0x98, 0x8f, 0x7b, 0xf2, 0x02, 0x87, 0xba, 0x5a, 0x6c, 0x94, - 0x5f, 0x1e, 0xbb, 0xdd, 0x8e, 0x56, 0x06, 0x1c, 0x19, 0x0e, 0x77, 0x4f, 0x1a, 0xe6, 0x4e, 0x48, 0xcc, 0xc7, 0x70, - 0x20, 0x55, 0x17, 0x6b, 0x92, 0x8a, 0xc7, 0x7d, 0xd2, 0xec, 0x76, 0x42, 0x47, 0xf2, 0x16, 0xc9, 0x3c, 0xb2, 0xe0, - 0x10, 0x3a, 0x4f, 0xf8, 0x94, 0xfa, 0x8c, 0x1f, 0xde, 0xd0, 0xeb, 0x7a, 0x38, 0x63, 0xa5, 0x7b, 0x1b, 0x94, 0x8e, - 0x62, 0x4a, 0x6e, 0x3c, 0xe2, 0xfa, 0xc6, 0x54, 0xab, 0x74, 0xb7, 0x1d, 0x83, 0xcd, 0x63, 0x5c, 0x73, 0xd2, 0x27, - 0x67, 0x81, 0xc5, 0xbb, 0x9d, 0xc3, 0x70, 0x0d, 0x23, 0x92, 0xdf, 0xe7, 0xda, 0xd1, 0x0e, 0x86, 0x0d, 0xd0, 0x9b, - 0xeb, 0x28, 0x71, 0x60, 0x1c, 0xf2, 0x5a, 0x50, 0xe7, 0x6e, 0xf7, 0x5f, 0xff, 0xc7, 0xff, 0xd2, 0x3e, 0xf6, 0xce, - 0xe1, 0xa4, 0x69, 0xc6, 0x5a, 0xdb, 0x95, 0xbc, 0x03, 0x97, 0x34, 0xcb, 0xa0, 0x30, 0xbd, 0xad, 0x8f, 0x53, 0x16, - 0xd5, 0x27, 0x61, 0x3c, 0x72, 0xbb, 0xbb, 0xb1, 0x69, 0x5f, 0xb6, 0xd2, 0x50, 0x57, 0x8b, 0x80, 0x5e, 0x7f, 0xd3, - 0x75, 0x21, 0x73, 0xeb, 0x44, 0x1e, 0x6d, 0xfb, 0xf2, 0x50, 0x79, 0xfa, 0x2a, 0x17, 0x88, 0x52, 0xfd, 0x65, 0x2f, - 0xcd, 0x01, 0xd3, 0xca, 0xbd, 0xa1, 0xdc, 0x75, 0x8a, 0xa0, 0xd6, 0xff, 0xfd, 0x9f, 0xff, 0xe5, 0xbf, 0x99, 0x47, - 0x88, 0x55, 0xfd, 0xeb, 0x7f, 0xff, 0xcf, 0xff, 0xe7, 0x7f, 0xff, 0x57, 0xb8, 0x6b, 0xa2, 0xe3, 0x59, 0x92, 0xa9, - 0x38, 0x65, 0x30, 0x4b, 0x71, 0x17, 0x07, 0xd2, 0x31, 0xa7, 0x2c, 0x13, 0x6c, 0x58, 0xbd, 0x49, 0x74, 0x21, 0x27, - 0x94, 0x27, 0x53, 0x43, 0x27, 0x4f, 0x78, 0x5e, 0x12, 0x54, 0x05, 0xe5, 0x92, 0x70, 0xf3, 0xce, 0x21, 0xe0, 0xfb, - 0x61, 0x97, 0x2f, 0xfd, 0x62, 0x3b, 0x96, 0x86, 0x4c, 0xa0, 0x24, 0x2f, 0xcb, 0x1d, 0x88, 0xad, 0x2c, 0xe1, 0x31, - 0x68, 0x59, 0xc5, 0x72, 0xf7, 0x2a, 0x7d, 0xda, 0x1f, 0xe6, 0x99, 0x60, 0x23, 0x40, 0xb9, 0xf2, 0x13, 0xcb, 0x30, - 0x76, 0x1d, 0x74, 0xc5, 0xf8, 0x2e, 0x97, 0xa3, 0x28, 0x02, 0x3d, 0x3e, 0xfd, 0x53, 0xfe, 0x97, 0x29, 0x68, 0x64, - 0x8e, 0x37, 0x0d, 0x6f, 0xb5, 0x79, 0xfe, 0xa8, 0xd1, 0x98, 0xdd, 0xa2, 0x65, 0x39, 0x03, 0xde, 0x35, 0x99, 0xa4, - 0x63, 0x7b, 0x40, 0x19, 0xff, 0x2e, 0xdc, 0xd8, 0x0d, 0x07, 0x7c, 0xe1, 0x4e, 0x23, 0xcf, 0xff, 0xbc, 0x94, 0x9e, - 0x54, 0xf6, 0x0b, 0xc4, 0xa9, 0xb5, 0xd3, 0xf9, 0x9a, 0xdb, 0x8b, 0x5b, 0x5a, 0xbd, 0x5a, 0xaa, 0xd7, 0xa4, 0xb9, - 0x79, 0xa7, 0xd0, 0x8e, 0xb3, 0xdb, 0x11, 0xf2, 0x63, 0x88, 0x79, 0x4f, 0x9a, 0x78, 0xd2, 0x5a, 0x16, 0xc3, 0x0b, - 0xc1, 0xa7, 0x76, 0x60, 0x9d, 0x86, 0x74, 0x48, 0x47, 0xc6, 0x59, 0xaf, 0xeb, 0x55, 0xd0, 0x3c, 0x9f, 0x1c, 0x6d, - 0x99, 0x4b, 0x83, 0x24, 0x03, 0xea, 0x4e, 0x23, 0xff, 0x1c, 0x4e, 0xe0, 0x72, 0x14, 0xf3, 0x50, 0x04, 0x92, 0x60, - 0xdb, 0x76, 0x78, 0x3e, 0x04, 0x9e, 0xc4, 0x97, 0x16, 0x3c, 0x6d, 0xd5, 0x14, 0xdc, 0xe6, 0xd5, 0x9b, 0x9f, 0xb9, - 0x2f, 0xbb, 0xdb, 0x43, 0x29, 0xaf, 0xdb, 0x77, 0x3a, 0xea, 0xfd, 0xba, 0xe2, 0xae, 0xd2, 0x02, 0xa9, 0x85, 0xb6, - 0xd7, 0x2b, 0xb9, 0xae, 0x6a, 0x7f, 0x14, 0x9e, 0x2b, 0xc1, 0x74, 0xd7, 0x5b, 0xc9, 0x42, 0x68, 0xf5, 0x9a, 0x7c, - 0x57, 0x98, 0x4c, 0xe1, 0x6c, 0x26, 0x1b, 0xa2, 0x76, 0xe7, 0x50, 0x69, 0xba, 0xc0, 0x3d, 0x64, 0x4a, 0x87, 0xca, - 0xa0, 0xd0, 0x8d, 0xf4, 0x51, 0x50, 0xbf, 0x74, 0x6e, 0x05, 0x7c, 0xf2, 0xad, 0xfb, 0xff, 0x00, 0x1a, 0x47, 0x0e, - 0xb2, 0x83, 0x89, 0x00, 0x00}; + 0xe4, 0x2b, 0x20, 0x44, 0x5b, 0x41, 0x6f, 0x36, 0x21, 0x92, 0x92, 0x6c, 0x19, 0x54, 0x93, 0x5b, 0x96, 0x9d, 0xed, + 0x64, 0xfb, 0x16, 0xcb, 0x4e, 0x76, 0xc2, 0x68, 0x4b, 0x10, 0xd1, 0x24, 0x3a, 0x06, 0xd1, 0x0c, 0xd0, 0xa4, 0xa4, + 0x90, 0x38, 0x35, 0x1f, 0x30, 0x55, 0x53, 0x35, 0x4f, 0xf3, 0x32, 0x35, 0xe7, 0x61, 0x3e, 0x62, 0x9e, 0xcf, 0xa7, + 0x9c, 0x1f, 0x98, 0xf9, 0x84, 0xa9, 0xd5, 0x17, 0xa0, 0xc1, 0x8b, 0xac, 0x5c, 0xce, 0x39, 0x53, 0x2e, 0xdb, 0x44, + 0xa3, 0x2f, 0xab, 0x57, 0xaf, 0x5e, 0xf7, 0x6e, 0x9c, 0xec, 0x44, 0x7c, 0x28, 0xee, 0xa6, 0xd4, 0x89, 0xc5, 0x24, + 0xe9, 0x9d, 0xe8, 0x7f, 0x69, 0x18, 0xf5, 0x4e, 0x12, 0x96, 0x7e, 0x74, 0x32, 0x9a, 0x10, 0x36, 0xe4, 0xa9, 0x13, + 0x67, 0x74, 0x44, 0xa2, 0x50, 0x84, 0x01, 0x9b, 0x84, 0x63, 0xea, 0xec, 0xf7, 0x4e, 0x26, 0x54, 0x84, 0xce, 0x30, + 0x0e, 0xb3, 0x9c, 0x0a, 0xf2, 0xe1, 0xfd, 0x97, 0xcd, 0xe3, 0xde, 0x49, 0x3e, 0xcc, 0xd8, 0x54, 0x38, 0xd0, 0x25, + 0x99, 0xf0, 0x68, 0x96, 0xd0, 0xde, 0xfe, 0xfe, 0xcd, 0xcd, 0x8d, 0xff, 0x53, 0xfe, 0xd9, 0x90, 0xa7, 0xb9, 0x70, + 0x5e, 0x92, 0x1b, 0x96, 0x46, 0xfc, 0x06, 0x33, 0x41, 0x5e, 0xfa, 0xe7, 0x71, 0x18, 0xf1, 0x9b, 0x77, 0x9c, 0x8b, + 0xbd, 0x3d, 0x4f, 0x3d, 0xde, 0x9d, 0x9d, 0x9f, 0x13, 0x42, 0xe6, 0x9c, 0x45, 0x4e, 0x6b, 0xb9, 0xac, 0x0a, 0xfd, + 0x34, 0x14, 0x6c, 0x4e, 0x55, 0x13, 0xb4, 0xb7, 0xe7, 0x86, 0x11, 0x9f, 0x0a, 0x1a, 0x9d, 0x8b, 0xbb, 0x84, 0x9e, + 0xc7, 0x94, 0x8a, 0xdc, 0x65, 0xa9, 0xf3, 0x8c, 0x0f, 0x67, 0x13, 0x9a, 0x0a, 0x7f, 0x9a, 0x71, 0xc1, 0x01, 0x92, + 0xbd, 0x3d, 0x37, 0xa3, 0xd3, 0x24, 0x1c, 0x52, 0x78, 0x7f, 0x76, 0x7e, 0x5e, 0xb5, 0xa8, 0x2a, 0xe1, 0x5c, 0x90, + 0xf3, 0xbb, 0xc9, 0x35, 0x4f, 0x3c, 0x84, 0x43, 0x41, 0x52, 0x7a, 0xe3, 0x7c, 0x47, 0xc3, 0x8f, 0xaf, 0xc2, 0x69, + 0x77, 0x98, 0x84, 0x79, 0xee, 0xdc, 0x88, 0x85, 0x9c, 0x42, 0x36, 0x1b, 0x0a, 0x9e, 0x79, 0x02, 0x53, 0xcc, 0xd0, + 0x82, 0x8d, 0x3c, 0x11, 0xb3, 0xdc, 0xbf, 0xdc, 0x1d, 0xe6, 0xf9, 0x3b, 0x9a, 0xcf, 0x12, 0xb1, 0x4b, 0x76, 0x5a, + 0x98, 0xed, 0x10, 0x92, 0x0b, 0x24, 0xe2, 0x8c, 0xdf, 0x38, 0xcf, 0xb3, 0x8c, 0x67, 0x9e, 0x7b, 0x76, 0x7e, 0xae, + 0x6a, 0x38, 0x2c, 0x77, 0x52, 0x2e, 0x9c, 0xb2, 0xbf, 0xf0, 0x3a, 0xa1, 0xbe, 0xf3, 0x21, 0xa7, 0xce, 0xd5, 0x2c, + 0xcd, 0xc3, 0x11, 0x3d, 0x3b, 0x3f, 0xbf, 0x72, 0x78, 0xe6, 0x5c, 0x0d, 0xf3, 0xfc, 0xca, 0x61, 0x69, 0x2e, 0x68, + 0x18, 0xf9, 0x2e, 0xea, 0xca, 0xc1, 0x86, 0x79, 0xfe, 0x9e, 0xde, 0x0a, 0x22, 0xb0, 0x7c, 0x14, 0x84, 0x16, 0x63, + 0x2a, 0x9c, 0xbc, 0x9c, 0x97, 0x87, 0x16, 0x09, 0x15, 0x8e, 0x20, 0xf2, 0x3d, 0xef, 0x2a, 0xdc, 0x53, 0xf5, 0x28, + 0xba, 0x6c, 0xe4, 0x31, 0xb1, 0xb7, 0x27, 0x4a, 0x3c, 0x23, 0x35, 0x35, 0x87, 0x11, 0xba, 0x63, 0xca, 0xf6, 0xf6, + 0xa8, 0x9f, 0xd0, 0x74, 0x2c, 0x62, 0x42, 0x48, 0xbb, 0xcb, 0xf6, 0xf6, 0x3c, 0x41, 0x42, 0xe1, 0x8f, 0xa9, 0xf0, + 0x28, 0x42, 0xb8, 0x6a, 0xbd, 0xb7, 0xe7, 0x29, 0x24, 0x70, 0xa2, 0x10, 0x57, 0xc3, 0x31, 0xf2, 0x35, 0xf6, 0xcf, + 0xef, 0xd2, 0xa1, 0x67, 0xc3, 0x8f, 0x30, 0xdb, 0xdb, 0x0b, 0x85, 0x9f, 0x43, 0x8f, 0x58, 0x20, 0x54, 0x64, 0x54, + 0xcc, 0xb2, 0xd4, 0x11, 0x85, 0xe0, 0xe7, 0x22, 0x63, 0xe9, 0xd8, 0x43, 0x0b, 0x53, 0x66, 0x35, 0x2c, 0x0a, 0x05, + 0xee, 0x07, 0x41, 0x52, 0xd2, 0x83, 0x11, 0x6f, 0x84, 0x07, 0xab, 0xc8, 0x47, 0x4e, 0x4a, 0x88, 0x9b, 0xcb, 0xb6, + 0x6e, 0x3f, 0x0d, 0xd2, 0x86, 0xeb, 0x62, 0x05, 0x25, 0xce, 0x05, 0xc2, 0x6f, 0x88, 0x97, 0x62, 0xdf, 0xf7, 0x05, + 0x22, 0xbd, 0x85, 0xc1, 0x4a, 0x6a, 0xcd, 0xb3, 0x9f, 0x0e, 0x5a, 0x17, 0x81, 0xf0, 0x33, 0x1a, 0xcd, 0x86, 0xd4, + 0xf3, 0x18, 0xce, 0x71, 0x86, 0x48, 0x8f, 0x35, 0x3c, 0x4e, 0x7a, 0xb0, 0xdc, 0xbc, 0xbe, 0xd6, 0x84, 0xec, 0xb4, + 0x90, 0x86, 0x91, 0x1b, 0x00, 0x01, 0xc3, 0x1a, 0x1e, 0x4e, 0x88, 0x9b, 0xce, 0x26, 0xd7, 0x34, 0x73, 0xcb, 0x6a, + 0xdd, 0x1a, 0x59, 0xcc, 0x72, 0xea, 0x0c, 0xf3, 0xdc, 0x19, 0xcd, 0xd2, 0xa1, 0x60, 0x3c, 0x75, 0xdc, 0x06, 0x6f, + 0xb8, 0x8a, 0x1c, 0x4a, 0x6a, 0x70, 0x51, 0x81, 0xbc, 0x1c, 0x35, 0xd2, 0x41, 0xd6, 0x68, 0x5f, 0x60, 0x80, 0x12, + 0x75, 0x75, 0x7f, 0x1a, 0x01, 0x14, 0xa7, 0x30, 0xc7, 0x02, 0xbf, 0x17, 0x30, 0x4b, 0x39, 0x45, 0x26, 0xfa, 0xa9, + 0xbf, 0xbe, 0x51, 0x88, 0xf0, 0x27, 0xe1, 0xd4, 0xa3, 0xa4, 0x47, 0x25, 0x71, 0x85, 0xe9, 0x10, 0x60, 0xad, 0xad, + 0x5b, 0x9f, 0x06, 0xd4, 0xaf, 0x48, 0x0a, 0x05, 0xc2, 0x1f, 0xf1, 0xec, 0x79, 0x38, 0x8c, 0xa1, 0x5d, 0x49, 0x30, + 0x91, 0xd9, 0x6f, 0xc3, 0x8c, 0x86, 0x82, 0x3e, 0x4f, 0x28, 0x3c, 0x79, 0xae, 0x6c, 0xe9, 0x22, 0x9c, 0x93, 0x97, + 0x7e, 0xc2, 0xc4, 0x6b, 0x9e, 0x0e, 0x69, 0x37, 0xb7, 0xa8, 0x8b, 0xc1, 0xba, 0x9f, 0x0a, 0x91, 0xb1, 0xeb, 0x99, + 0xa0, 0x9e, 0x9b, 0x42, 0x0d, 0x17, 0xe7, 0x08, 0x33, 0x5f, 0xd0, 0x5b, 0x71, 0xc6, 0x53, 0x41, 0x53, 0x41, 0xa8, + 0x41, 0x2a, 0x4e, 0xfd, 0x70, 0x3a, 0xa5, 0x69, 0x74, 0x16, 0xb3, 0x24, 0xf2, 0x18, 0x2a, 0x50, 0x81, 0x63, 0x41, + 0x60, 0x8e, 0xa4, 0x97, 0x06, 0xf0, 0xcf, 0xf6, 0xd9, 0x78, 0x82, 0xf4, 0xe4, 0xa6, 0xa0, 0xc4, 0x75, 0xbb, 0x23, + 0x9e, 0x79, 0x7a, 0x06, 0x0e, 0x1f, 0x39, 0x02, 0xc6, 0x78, 0x37, 0x4b, 0x68, 0x8e, 0x68, 0x83, 0xb0, 0x72, 0x19, + 0x35, 0x82, 0x3f, 0x00, 0xc5, 0x17, 0xc8, 0x4b, 0x51, 0x90, 0x76, 0xe7, 0x61, 0xe6, 0x7c, 0xa7, 0x77, 0xd4, 0x33, + 0xc3, 0xcd, 0x86, 0x82, 0x3c, 0xf3, 0x45, 0x36, 0xcb, 0x05, 0x8d, 0xde, 0xdf, 0x4d, 0x69, 0x8e, 0x5f, 0x0b, 0x32, + 0x14, 0xfd, 0xa1, 0xf0, 0xe9, 0x64, 0x2a, 0xee, 0xce, 0x25, 0x63, 0x0c, 0x5c, 0x17, 0x47, 0x50, 0x33, 0xa3, 0xe1, + 0x10, 0x98, 0x99, 0xc6, 0xd6, 0x5b, 0x9e, 0xdc, 0x8d, 0x58, 0x92, 0x9c, 0xcf, 0xa6, 0x53, 0x9e, 0x09, 0xfc, 0x77, + 0xb2, 0x10, 0xbc, 0x42, 0x0d, 0xac, 0xe5, 0x22, 0xbf, 0x61, 0x62, 0x18, 0x7b, 0x02, 0x2d, 0x86, 0x61, 0x4e, 0x9d, + 0xa7, 0x9c, 0x27, 0x34, 0x84, 0x49, 0xa7, 0xfd, 0xd7, 0x22, 0x48, 0x67, 0x49, 0xd2, 0xbd, 0xce, 0x68, 0xf8, 0xb1, + 0x2b, 0x5f, 0xbf, 0xb9, 0xfe, 0x89, 0x0e, 0x45, 0x20, 0x7f, 0x9f, 0x66, 0x59, 0x78, 0x07, 0x15, 0x09, 0x81, 0x6a, + 0xfd, 0x34, 0xf8, 0xfa, 0xfc, 0xcd, 0x6b, 0x5f, 0x6d, 0x12, 0x36, 0xba, 0xf3, 0xd2, 0x72, 0xe3, 0xa5, 0x05, 0x1e, + 0x65, 0x7c, 0xb2, 0x32, 0xb4, 0xc2, 0x5a, 0xda, 0xdd, 0x02, 0x02, 0x25, 0xe9, 0x8e, 0xea, 0xda, 0x86, 0xe0, 0xb5, + 0xa4, 0x79, 0x78, 0x49, 0xcc, 0xb8, 0xb3, 0x24, 0x09, 0x54, 0xb1, 0x97, 0xa2, 0xfb, 0xa1, 0x15, 0xd9, 0xdd, 0x82, + 0x12, 0x09, 0xe7, 0x14, 0x24, 0x0c, 0xc0, 0x38, 0x0c, 0xc5, 0x30, 0x5e, 0x50, 0xd9, 0x59, 0x61, 0x20, 0xa6, 0x45, + 0x81, 0x6f, 0x4b, 0x7a, 0x17, 0x00, 0x88, 0x64, 0x54, 0x44, 0x2c, 0x97, 0x30, 0x61, 0x84, 0x7f, 0x20, 0x8b, 0xd0, + 0xcc, 0x27, 0xd8, 0x69, 0x61, 0xd8, 0x97, 0x81, 0xe2, 0x2e, 0x78, 0xc8, 0xd3, 0x39, 0xcd, 0x04, 0xcd, 0x82, 0xbf, + 0xe3, 0x8c, 0x8e, 0x12, 0x80, 0x62, 0xa7, 0x8d, 0xe3, 0x30, 0x3f, 0x8b, 0xc3, 0x74, 0x4c, 0xa3, 0xe0, 0x56, 0x14, + 0x58, 0x08, 0xe2, 0x8e, 0x58, 0x1a, 0x26, 0xec, 0x17, 0x1a, 0xb9, 0x5a, 0x1c, 0x3c, 0x77, 0xe8, 0xad, 0xa0, 0x69, + 0x94, 0x3b, 0x2f, 0xde, 0xbf, 0x7a, 0xa9, 0x17, 0xb2, 0x26, 0x21, 0xd0, 0x22, 0x9f, 0x4d, 0x69, 0xe6, 0x21, 0xac, + 0x25, 0xc4, 0x73, 0x26, 0xb9, 0xe3, 0xab, 0x70, 0xaa, 0x4a, 0x58, 0xfe, 0x61, 0x1a, 0x85, 0x82, 0xbe, 0xa5, 0x69, + 0xc4, 0xd2, 0x31, 0xd9, 0x69, 0xab, 0xf2, 0x38, 0xd4, 0x2f, 0xa2, 0xb2, 0xe8, 0x72, 0xf7, 0x79, 0x22, 0x27, 0x5e, + 0x3e, 0xce, 0x3c, 0x54, 0xe4, 0x22, 0x14, 0x6c, 0xe8, 0x84, 0x51, 0xf4, 0x55, 0xca, 0x04, 0x93, 0x00, 0x66, 0xb0, + 0x3e, 0x40, 0xa3, 0x54, 0xc9, 0x0a, 0x03, 0xb8, 0x87, 0xb0, 0xe7, 0x69, 0x09, 0x10, 0x23, 0xbd, 0x60, 0x7b, 0x7b, + 0x15, 0xbf, 0xef, 0xd3, 0x40, 0xbd, 0x24, 0x83, 0x0b, 0xe4, 0x4f, 0x67, 0x39, 0xac, 0xb4, 0x19, 0x02, 0xc4, 0x0b, + 0xbf, 0xce, 0x69, 0x36, 0xa7, 0x51, 0x49, 0x1d, 0xb9, 0x87, 0x16, 0x2b, 0x63, 0xe8, 0x7d, 0x21, 0xc8, 0xe0, 0xa2, + 0x6b, 0x33, 0x6e, 0xaa, 0x09, 0x3d, 0xe3, 0x53, 0x9a, 0x09, 0x46, 0xf3, 0x92, 0x97, 0x78, 0x20, 0x46, 0x4b, 0x7e, + 0x92, 0x13, 0x33, 0xbf, 0xa9, 0xc7, 0x30, 0x45, 0x35, 0x8e, 0x61, 0x24, 0xed, 0xf3, 0xb9, 0x14, 0x19, 0x39, 0x66, + 0x08, 0x0b, 0x05, 0x69, 0x8e, 0x50, 0x81, 0xb0, 0x30, 0xe0, 0x2a, 0x5e, 0xa4, 0x47, 0xbb, 0x03, 0x59, 0x4d, 0x7e, + 0x90, 0xb2, 0x1a, 0x38, 0x5a, 0x28, 0xe8, 0xde, 0x9e, 0x47, 0xfd, 0x92, 0x2a, 0xc8, 0x4e, 0x5b, 0xaf, 0x91, 0x85, + 0xac, 0x2d, 0x60, 0xc3, 0xc0, 0x02, 0x53, 0x84, 0x77, 0xa8, 0x9f, 0xf2, 0xd3, 0xe1, 0x90, 0xe6, 0x39, 0xcf, 0xf6, + 0xf6, 0x76, 0x64, 0xfd, 0x52, 0x9d, 0x80, 0x35, 0x7c, 0x73, 0x93, 0x56, 0x10, 0xa0, 0x4a, 0xc4, 0x6a, 0xc1, 0x20, + 0x40, 0x50, 0x49, 0x8d, 0xc3, 0xed, 0x1b, 0xcd, 0x23, 0x70, 0x2f, 0x2f, 0xdd, 0x86, 0xc0, 0x1a, 0x0d, 0x63, 0x6a, + 0x86, 0xbe, 0x7b, 0x46, 0x95, 0x6e, 0x25, 0x35, 0x8f, 0x35, 0xcc, 0xa8, 0x0d, 0xe4, 0x47, 0x74, 0xc4, 0x52, 0x6b, + 0xda, 0x35, 0x90, 0xb0, 0xc0, 0x39, 0x2a, 0xac, 0x05, 0xdd, 0xd8, 0xb5, 0x54, 0x6a, 0xd4, 0xca, 0x2d, 0xc6, 0x52, + 0x91, 0xb0, 0x96, 0x71, 0x40, 0x2f, 0x0a, 0x2c, 0x51, 0x6f, 0x66, 0x93, 0x49, 0x40, 0x07, 0xe2, 0xa2, 0xab, 0xdf, + 0x93, 0x5c, 0x61, 0x2e, 0xa3, 0x3f, 0xcf, 0x68, 0x2e, 0x14, 0x1d, 0x7b, 0x02, 0x67, 0x98, 0xa1, 0x02, 0xf6, 0xdb, + 0x88, 0x8d, 0x67, 0x19, 0xe8, 0x3b, 0xb0, 0x17, 0x69, 0x3a, 0x9b, 0x50, 0xf3, 0xb4, 0x09, 0xb6, 0x37, 0x53, 0x90, + 0x88, 0x39, 0xd0, 0xf4, 0xfd, 0xe4, 0x04, 0xb0, 0x0a, 0xb4, 0x5c, 0xfe, 0x60, 0x3a, 0xa9, 0x96, 0xb2, 0xd4, 0xd1, + 0x56, 0xd7, 0x44, 0x20, 0x2d, 0x91, 0x77, 0xda, 0x0a, 0x7c, 0x21, 0x2e, 0xc8, 0x4e, 0xab, 0xa4, 0x61, 0x8d, 0x55, + 0x05, 0x8e, 0x42, 0xe2, 0x1b, 0xd5, 0x15, 0x92, 0x02, 0xbe, 0x46, 0x2e, 0x7e, 0xbc, 0x46, 0xa9, 0x31, 0x19, 0x80, + 0xaa, 0xe1, 0xc7, 0x17, 0xdb, 0xc8, 0xc9, 0xf0, 0x03, 0x4f, 0xac, 0xbf, 0xab, 0xd8, 0xc6, 0xbc, 0xce, 0x36, 0x56, + 0xa6, 0xe1, 0x4e, 0xcb, 0x26, 0x6e, 0x49, 0x65, 0x7a, 0xa3, 0x57, 0xaf, 0x30, 0x93, 0xc0, 0x54, 0x53, 0xb2, 0xba, + 0x78, 0x1d, 0x4e, 0x68, 0xee, 0x51, 0x84, 0xb7, 0x55, 0x50, 0xe4, 0x09, 0x55, 0x2e, 0x2c, 0xc9, 0x99, 0x83, 0xe4, + 0x64, 0x48, 0x29, 0x66, 0xf5, 0x0d, 0x97, 0x63, 0x3a, 0xc8, 0x2f, 0x2a, 0x7d, 0xce, 0x9a, 0xbc, 0x14, 0xc9, 0x9a, + 0xbe, 0x0d, 0xfe, 0x54, 0x99, 0x42, 0x9a, 0xd4, 0x1b, 0x72, 0x84, 0x77, 0x5a, 0xab, 0x2b, 0x69, 0x6a, 0x55, 0x73, + 0x1c, 0x5c, 0xc0, 0x3a, 0x48, 0x89, 0xe1, 0xb3, 0x5c, 0xfe, 0x5f, 0xdb, 0x69, 0x80, 0xb6, 0x73, 0x20, 0x0c, 0x7f, + 0x94, 0x84, 0xc2, 0x6b, 0xef, 0xb7, 0x40, 0x19, 0x9d, 0x53, 0x10, 0x28, 0x08, 0xad, 0x4f, 0x85, 0xfa, 0xb3, 0x34, + 0x8f, 0xd9, 0x48, 0x78, 0xb1, 0x90, 0x2c, 0x85, 0x26, 0x39, 0x75, 0x44, 0x4d, 0x25, 0x96, 0xec, 0x26, 0x06, 0x62, + 0x2b, 0xf5, 0x2f, 0x6a, 0x20, 0x95, 0x6c, 0x0b, 0xb8, 0x43, 0xa5, 0x4e, 0x57, 0x5c, 0xc6, 0xd4, 0x66, 0xa0, 0x32, + 0xb6, 0xfb, 0xaa, 0xc7, 0x40, 0x33, 0x03, 0x66, 0x69, 0xad, 0x2c, 0xb0, 0x39, 0x84, 0x2e, 0x14, 0xbe, 0xe0, 0x2f, + 0xf9, 0x0d, 0xcd, 0xce, 0x42, 0x00, 0x3e, 0x50, 0xcd, 0x0b, 0x25, 0x08, 0x24, 0xbf, 0x17, 0x5d, 0x43, 0x2f, 0x97, + 0x72, 0xe2, 0x6f, 0x33, 0x3e, 0x61, 0x39, 0x05, 0x65, 0x4d, 0xe1, 0x3f, 0x85, 0x7d, 0x26, 0x37, 0x24, 0x08, 0x1b, + 0x5a, 0xd2, 0xd7, 0xe9, 0xcb, 0x3a, 0x7d, 0x5d, 0xee, 0x3e, 0x1f, 0x1b, 0x06, 0x58, 0xdf, 0xc6, 0x08, 0x7b, 0xda, + 0xa4, 0xb0, 0xe4, 0x9c, 0x1f, 0x23, 0x2d, 0xe1, 0x97, 0x4b, 0x61, 0x59, 0x6e, 0x35, 0x75, 0x91, 0xaa, 0x6d, 0x83, + 0x8a, 0x30, 0x8a, 0x40, 0xb1, 0xcb, 0x78, 0x92, 0x58, 0xa2, 0x0a, 0xb3, 0x6e, 0x29, 0x9c, 0x2e, 0x77, 0x9f, 0x9f, + 0xdf, 0x27, 0x9f, 0xe0, 0xbd, 0x2d, 0xa2, 0x0c, 0xa0, 0x69, 0x44, 0x33, 0xb0, 0x24, 0xad, 0xd5, 0xd2, 0x52, 0xf6, + 0x8c, 0xa7, 0x29, 0x1d, 0x0a, 0x1a, 0x81, 0xa1, 0xc2, 0x88, 0xf0, 0x63, 0x9e, 0x8b, 0xb2, 0xb0, 0x82, 0x9e, 0x59, + 0xd0, 0x33, 0x7f, 0x18, 0x26, 0x89, 0xa7, 0x8c, 0x92, 0x09, 0x9f, 0xd3, 0x0d, 0x50, 0x77, 0x6b, 0x20, 0x97, 0xdd, + 0x50, 0xab, 0x1b, 0xea, 0xe7, 0xd3, 0x84, 0x0d, 0x69, 0x29, 0xba, 0xce, 0x7d, 0x96, 0x46, 0xf4, 0x16, 0xf8, 0x08, + 0xea, 0xf5, 0x7a, 0x2d, 0xdc, 0x46, 0x85, 0x42, 0xf8, 0x62, 0x0d, 0xb1, 0xf7, 0x08, 0x4d, 0x20, 0x32, 0xd2, 0x5b, + 0x6c, 0xe2, 0x07, 0x14, 0x59, 0x92, 0x92, 0x19, 0xe3, 0x4a, 0x71, 0x67, 0x84, 0x23, 0x9a, 0x50, 0x41, 0x0d, 0x37, + 0x07, 0x15, 0x5a, 0x6d, 0xdd, 0x77, 0x25, 0xfe, 0x4a, 0x72, 0x32, 0xbb, 0xcc, 0xac, 0x79, 0x5e, 0x1a, 0xeb, 0xd5, + 0xf2, 0x54, 0xd8, 0xee, 0x0b, 0xb5, 0x3c, 0xa1, 0x10, 0xe1, 0x30, 0x56, 0x56, 0xba, 0xb7, 0x36, 0xa5, 0xaa, 0x0f, + 0xcd, 0xd9, 0xcb, 0x4d, 0xf4, 0xde, 0x80, 0xb9, 0x09, 0x05, 0xe7, 0x9a, 0x29, 0x50, 0x30, 0xfc, 0xd4, 0xb2, 0x9d, + 0x85, 0x49, 0x72, 0x1d, 0x0e, 0x3f, 0xd6, 0xa9, 0xbf, 0x22, 0x03, 0xb2, 0xca, 0x8d, 0xad, 0x57, 0x16, 0xcb, 0xb2, + 0xe7, 0x6d, 0xb8, 0x74, 0x6d, 0xa3, 0x78, 0x3b, 0xad, 0x8a, 0xec, 0xeb, 0x0b, 0xbd, 0x95, 0xda, 0x25, 0x44, 0x4c, + 0xcf, 0xcc, 0x03, 0x2e, 0xf0, 0x49, 0x8a, 0x33, 0xfc, 0x40, 0xd3, 0x1d, 0x98, 0x1b, 0xc5, 0x0a, 0x20, 0x02, 0x2d, + 0x8a, 0x88, 0xe5, 0xdb, 0x31, 0xf0, 0x87, 0x40, 0xf9, 0xcc, 0x1a, 0xe1, 0xa1, 0x80, 0x96, 0x3c, 0x4e, 0x6b, 0xcd, + 0x25, 0x64, 0x5a, 0x9f, 0x30, 0x8c, 0xe6, 0x6f, 0xa0, 0xbb, 0x48, 0x7a, 0x7f, 0xa3, 0x5e, 0x81, 0x56, 0x06, 0x50, + 0xe4, 0x5d, 0x5b, 0x9d, 0xa8, 0x51, 0x80, 0xe6, 0xa9, 0x4c, 0x8a, 0xdc, 0xac, 0x66, 0x3f, 0x6a, 0x8d, 0x5d, 0x99, + 0xe0, 0x9a, 0xe5, 0x72, 0xe2, 0x79, 0x5e, 0x0e, 0x26, 0x9c, 0x51, 0xed, 0xab, 0x49, 0xe4, 0x6b, 0x93, 0xc8, 0x7d, + 0xcb, 0xce, 0x42, 0x15, 0x2d, 0x5b, 0xcd, 0x83, 0xbf, 0x23, 0xbb, 0x12, 0xa8, 0xab, 0x3e, 0xf0, 0x67, 0x54, 0xb2, + 0xdb, 0x84, 0x08, 0xcc, 0xb5, 0x8d, 0xa3, 0x29, 0x0d, 0x18, 0x46, 0xd5, 0x24, 0x43, 0x6a, 0x6b, 0xd4, 0xec, 0xdd, + 0x0c, 0x73, 0xb4, 0xa2, 0xdb, 0x17, 0x85, 0xc6, 0x11, 0x45, 0x7a, 0x6d, 0x6a, 0x4a, 0xb1, 0x85, 0x15, 0x9c, 0x11, + 0xad, 0x08, 0x2b, 0xbd, 0x67, 0x15, 0x37, 0x65, 0xbf, 0x3b, 0x84, 0x64, 0x15, 0x6a, 0x6a, 0x1a, 0xa5, 0x51, 0xad, + 0x32, 0x84, 0x63, 0xa3, 0x93, 0xf2, 0x6a, 0xde, 0x84, 0xb8, 0xc6, 0x21, 0xe1, 0xf6, 0x17, 0x35, 0xab, 0x30, 0xb0, + 0xaa, 0x15, 0x01, 0xb0, 0x54, 0xbe, 0x09, 0xdd, 0x9b, 0x68, 0xa6, 0xd6, 0x8f, 0x85, 0x70, 0x6e, 0x23, 0xdc, 0xc2, + 0x6c, 0xa6, 0x38, 0x57, 0x76, 0x41, 0xe2, 0x7a, 0x5b, 0x8f, 0x62, 0xae, 0xd6, 0x61, 0x0d, 0x89, 0xab, 0xaa, 0xa7, + 0x24, 0x41, 0xb0, 0x61, 0x73, 0x50, 0xee, 0x6c, 0xf9, 0xe0, 0x01, 0xec, 0x6c, 0xb9, 0x5c, 0x23, 0xba, 0x8d, 0x1a, + 0x28, 0xf2, 0x2b, 0xbb, 0x70, 0xb9, 0xbc, 0x15, 0xc8, 0xd3, 0xba, 0x2f, 0xa6, 0xa8, 0x6f, 0x38, 0xee, 0xe9, 0x4b, + 0xa8, 0x25, 0x55, 0xd1, 0xaa, 0xa4, 0x34, 0x1a, 0xea, 0x34, 0x5b, 0x5f, 0x27, 0x61, 0xb1, 0xed, 0xb3, 0x35, 0xee, + 0x25, 0x0b, 0xb5, 0x98, 0xae, 0xa6, 0x7c, 0xa6, 0xbb, 0x66, 0x08, 0xa1, 0x20, 0x97, 0x76, 0xcc, 0xce, 0x26, 0xd3, + 0x72, 0x6f, 0x2f, 0xb7, 0x3a, 0xba, 0x2c, 0xd9, 0xc4, 0x4f, 0x1e, 0x88, 0xe4, 0xfc, 0x2e, 0x95, 0xba, 0xcb, 0x4f, + 0x46, 0x08, 0xad, 0x19, 0xa6, 0xad, 0x2e, 0x18, 0xe4, 0xe1, 0x4d, 0xc8, 0x84, 0x53, 0xf6, 0xa2, 0x0c, 0x72, 0x8f, + 0xa2, 0x85, 0x56, 0x35, 0xfc, 0x8c, 0x82, 0xf2, 0x08, 0x3c, 0xc1, 0xa8, 0xd0, 0x8a, 0xee, 0x87, 0x31, 0x05, 0x5f, + 0xb0, 0xd1, 0x22, 0x4a, 0xcb, 0x70, 0x47, 0x4b, 0x11, 0xdd, 0xf1, 0x66, 0xd8, 0x8b, 0xd5, 0xe6, 0x35, 0x4b, 0x60, + 0x4a, 0xb3, 0x11, 0xcf, 0x26, 0xe6, 0x5d, 0xb1, 0xf2, 0xac, 0x39, 0x23, 0x1b, 0x79, 0x1b, 0xfb, 0xd6, 0xfa, 0x7f, + 0x77, 0xc5, 0xec, 0xae, 0x0c, 0xf6, 0x9a, 0x28, 0x2d, 0xa5, 0xaf, 0x72, 0x09, 0x1a, 0xca, 0xcc, 0x6d, 0x03, 0x5f, + 0xfb, 0x53, 0xbb, 0xca, 0x67, 0xb2, 0xd3, 0xee, 0x96, 0x56, 0x9f, 0xa1, 0x86, 0xae, 0xf2, 0x6d, 0x68, 0x91, 0xca, + 0x67, 0x49, 0xa4, 0x81, 0x65, 0x08, 0x53, 0x4d, 0x47, 0x37, 0x2c, 0x49, 0xaa, 0xd2, 0x5f, 0xc3, 0xd7, 0x73, 0xcd, + 0xd7, 0x33, 0xc3, 0xd7, 0x81, 0x53, 0x00, 0x5f, 0x57, 0xdd, 0x55, 0xcd, 0xb3, 0xb5, 0xdd, 0x99, 0x29, 0x8e, 0x9e, + 0x4b, 0x4b, 0x1a, 0xc6, 0x9b, 0x19, 0x08, 0x50, 0xa9, 0x79, 0x7d, 0xf4, 0xb4, 0x1f, 0x06, 0x4c, 0x40, 0xe5, 0xc5, + 0xa4, 0xb6, 0x93, 0xe2, 0xa3, 0x87, 0x70, 0x5e, 0xd0, 0x92, 0xb2, 0x4f, 0x9f, 0x83, 0x9f, 0xce, 0x9a, 0x0e, 0x08, + 0x31, 0x59, 0xfc, 0xab, 0x94, 0x28, 0x33, 0x3b, 0xa6, 0x67, 0x97, 0x9b, 0xd9, 0x01, 0xa7, 0xaf, 0x66, 0x17, 0xdd, + 0xcf, 0xeb, 0xe5, 0xf4, 0x58, 0x39, 0xbd, 0x6a, 0xbd, 0x97, 0x4b, 0x6f, 0xa5, 0x04, 0x5c, 0xf8, 0xda, 0x44, 0xc9, + 0xca, 0xde, 0x81, 0x07, 0xd8, 0x98, 0x81, 0x82, 0x42, 0x4d, 0xba, 0x14, 0x71, 0x2f, 0x3f, 0xe5, 0xe2, 0x91, 0x9e, + 0x7a, 0xd5, 0xfe, 0x8c, 0x4f, 0xa6, 0xa0, 0x8d, 0xad, 0x90, 0xf4, 0x98, 0xea, 0x01, 0xab, 0xf7, 0xc5, 0x86, 0xb2, + 0x5a, 0x1b, 0xb9, 0x1f, 0x6b, 0xd4, 0x54, 0x5a, 0xcc, 0x3b, 0xad, 0x62, 0x56, 0x16, 0x95, 0x8c, 0x63, 0x93, 0x5b, + 0xe5, 0x6c, 0xd5, 0x29, 0x63, 0x5e, 0xbc, 0xf1, 0x98, 0xe2, 0xc3, 0x0c, 0x78, 0x9d, 0xc5, 0x7e, 0x0c, 0xb9, 0xdb, + 0xeb, 0x5f, 0x54, 0xc8, 0x59, 0x14, 0x2b, 0xe8, 0x5b, 0x14, 0xc5, 0x73, 0x6d, 0x65, 0xe3, 0xe7, 0xdb, 0xcd, 0xe1, + 0xea, 0x9d, 0xb6, 0x16, 0x07, 0x17, 0xf8, 0xf9, 0xba, 0xee, 0x48, 0x16, 0x13, 0x1e, 0xd1, 0xc0, 0xe5, 0x53, 0x9a, + 0xba, 0x05, 0x78, 0x56, 0xf5, 0xe2, 0x47, 0xc2, 0x5b, 0xbc, 0xab, 0xbb, 0x58, 0x83, 0xe7, 0x05, 0x38, 0xc0, 0xbe, + 0x5b, 0x77, 0xbe, 0x7e, 0x4b, 0xb3, 0x5c, 0x6a, 0xa2, 0xa5, 0x52, 0xfb, 0x5d, 0x25, 0x97, 0xbe, 0x0b, 0xb6, 0xd6, + 0xaf, 0x6c, 0x10, 0xb7, 0xed, 0x3f, 0xf2, 0x0f, 0x5c, 0x24, 0x5d, 0xc3, 0x5f, 0xeb, 0x1d, 0xff, 0x93, 0x71, 0x0d, + 0x9f, 0x93, 0x9f, 0xea, 0x9e, 0xe1, 0x99, 0x20, 0xe7, 0xfd, 0x73, 0x63, 0x32, 0xf3, 0x84, 0x0d, 0xef, 0x3c, 0x37, + 0x61, 0xa2, 0x09, 0xe1, 0x37, 0x17, 0x2f, 0xd4, 0x0b, 0xf0, 0x2a, 0x4a, 0x97, 0x76, 0x61, 0x8c, 0x3d, 0x4c, 0x05, + 0x71, 0x77, 0x13, 0x26, 0x76, 0x5d, 0x3c, 0x21, 0x57, 0xf0, 0x63, 0x77, 0xe1, 0xbd, 0x0a, 0x45, 0xec, 0x67, 0x61, + 0x1a, 0xf1, 0x89, 0x87, 0x1a, 0xae, 0x8b, 0xfc, 0x5c, 0x1a, 0x1c, 0x4f, 0x50, 0xb1, 0x7b, 0x85, 0x4f, 0x05, 0x71, + 0xfb, 0x6e, 0x63, 0x82, 0xdf, 0x09, 0x72, 0x75, 0xb2, 0xbb, 0x38, 0x15, 0x45, 0xef, 0x0a, 0xdf, 0x96, 0x5e, 0x7b, + 0xfc, 0x81, 0x78, 0x88, 0xf4, 0x6e, 0x35, 0x34, 0x67, 0x7c, 0xa2, 0xbc, 0xf7, 0x2e, 0xc2, 0xef, 0x65, 0x6c, 0xa5, + 0x62, 0x37, 0x3a, 0xbc, 0xb2, 0x43, 0x5c, 0x2e, 0x7d, 0x04, 0xee, 0xde, 0x9e, 0x55, 0x56, 0xea, 0x0a, 0xf8, 0xb9, + 0x20, 0x35, 0x8b, 0x1c, 0xbf, 0x90, 0x51, 0x9a, 0xe7, 0xc2, 0x4b, 0x91, 0xe9, 0xc6, 0x33, 0xbe, 0x68, 0xbd, 0x37, + 0xd3, 0x81, 0x72, 0x31, 0xf8, 0x4c, 0xd0, 0x2c, 0x14, 0x3c, 0xbb, 0x40, 0xb6, 0xfe, 0x81, 0xff, 0x46, 0xae, 0x06, + 0xce, 0x7f, 0xfa, 0xec, 0xc7, 0xd1, 0x8f, 0xd9, 0xc5, 0x15, 0x7e, 0x4b, 0xf6, 0x4f, 0xbc, 0x7e, 0xe0, 0xed, 0x34, + 0x9b, 0xcb, 0x1f, 0xf7, 0x07, 0xff, 0x08, 0x9b, 0xbf, 0x9c, 0x36, 0x7f, 0xb8, 0x40, 0x4b, 0xef, 0xc7, 0xfd, 0xfe, + 0x40, 0x3f, 0x0d, 0xfe, 0xd1, 0xfb, 0x31, 0xbf, 0xf8, 0xb3, 0x2a, 0xdc, 0x45, 0x68, 0x7f, 0x8c, 0xa7, 0x82, 0xec, + 0x37, 0x9b, 0xbd, 0xfd, 0x31, 0x1e, 0x0b, 0xb2, 0x0f, 0xff, 0x5f, 0x93, 0x77, 0x74, 0xfc, 0xfc, 0x76, 0xea, 0x5d, + 0xf5, 0x96, 0xbb, 0x8b, 0xbf, 0x15, 0xd0, 0xeb, 0xe0, 0x1f, 0x3f, 0xfe, 0x98, 0xbb, 0x5f, 0xf4, 0xc8, 0xfe, 0x45, + 0x03, 0x79, 0x50, 0xfa, 0x67, 0x22, 0xff, 0xf5, 0xfa, 0xc1, 0xe0, 0x1f, 0x1a, 0x0a, 0xf7, 0x8b, 0x1f, 0xaf, 0x4e, + 0x7a, 0xe4, 0x62, 0xe9, 0xb9, 0xcb, 0x2f, 0xd0, 0x12, 0xa1, 0xe5, 0x2e, 0xba, 0xc2, 0xee, 0xd8, 0x45, 0x78, 0x2e, + 0xc8, 0xfe, 0x17, 0xfb, 0x63, 0x3c, 0x12, 0x64, 0xdf, 0xdd, 0x1f, 0xe3, 0x73, 0x41, 0xf6, 0xff, 0xe1, 0xf5, 0x03, + 0xe5, 0x64, 0x5b, 0x4a, 0xff, 0xc6, 0x12, 0x02, 0x1c, 0x61, 0x46, 0xc3, 0xa5, 0x60, 0x22, 0xa1, 0x68, 0x77, 0x9f, + 0xe1, 0x33, 0x89, 0x26, 0x4f, 0x80, 0x17, 0x06, 0x8c, 0x3b, 0x6f, 0x71, 0x09, 0x8b, 0x0d, 0x34, 0xb3, 0x1b, 0x40, + 0x64, 0x07, 0x1c, 0x01, 0x79, 0x20, 0xf0, 0x3c, 0x4c, 0x66, 0x34, 0x0f, 0x68, 0x81, 0xf0, 0x90, 0x9c, 0x09, 0xaf, + 0x8d, 0xf0, 0x53, 0x01, 0x3f, 0x3a, 0x08, 0x9f, 0xe9, 0x20, 0x26, 0xec, 0x64, 0x45, 0x54, 0x29, 0x57, 0x2a, 0x8b, + 0x8b, 0xf0, 0x74, 0xc3, 0x4b, 0x11, 0x83, 0x7b, 0x01, 0xe1, 0xdd, 0x5a, 0xc8, 0x13, 0xdf, 0x10, 0x43, 0x12, 0xef, + 0x33, 0x4a, 0xbf, 0x0b, 0x93, 0x8f, 0x34, 0xf3, 0x6e, 0x71, 0xbb, 0xf3, 0x04, 0x4b, 0x2f, 0xf4, 0x4e, 0x1b, 0x75, + 0xcb, 0x78, 0xd5, 0x47, 0xa1, 0xe2, 0x04, 0x20, 0x65, 0xeb, 0xce, 0x18, 0x58, 0xf1, 0x9d, 0x74, 0xcd, 0x63, 0x95, + 0x85, 0x37, 0x2e, 0xaa, 0xc7, 0x46, 0x59, 0x3a, 0x0f, 0x13, 0x16, 0x39, 0x82, 0x4e, 0xa6, 0x49, 0x28, 0xa8, 0xa3, + 0xe7, 0xeb, 0x84, 0xd0, 0x91, 0x5b, 0xea, 0x0c, 0x33, 0xcb, 0xe2, 0x9c, 0x99, 0xa0, 0x13, 0xec, 0x15, 0x0f, 0x22, + 0x54, 0x5a, 0xef, 0x78, 0x55, 0x05, 0xc0, 0x56, 0x63, 0x7c, 0xcd, 0x36, 0x78, 0xc2, 0x2e, 0xa4, 0x7c, 0xce, 0x71, + 0x46, 0x40, 0x8a, 0x76, 0xfa, 0xee, 0x49, 0x3e, 0x1f, 0xf7, 0x5c, 0x88, 0xcf, 0x70, 0xf2, 0x56, 0x3a, 0x86, 0xa0, + 0x42, 0x4c, 0x5a, 0xdd, 0xf8, 0x84, 0x76, 0xe3, 0x46, 0xc3, 0x28, 0xd1, 0x09, 0x49, 0x07, 0xb1, 0x6a, 0x1e, 0xe2, + 0x08, 0xcf, 0x48, 0xb3, 0x8d, 0xc7, 0xa4, 0x25, 0x9b, 0x74, 0xc7, 0x27, 0x89, 0x1e, 0x66, 0x6f, 0xcf, 0xe3, 0x7e, + 0x12, 0xe6, 0xe2, 0x2b, 0xb0, 0xf6, 0xc9, 0x18, 0x47, 0x84, 0xfb, 0xf4, 0x96, 0x0e, 0xbd, 0x04, 0xe1, 0x48, 0x73, + 0x1a, 0xd4, 0x45, 0x63, 0x62, 0x55, 0x03, 0x2b, 0x82, 0xbc, 0xed, 0x47, 0x83, 0xf6, 0x05, 0x21, 0xc4, 0xdd, 0x69, + 0x36, 0xdd, 0x3e, 0x27, 0x53, 0x11, 0x40, 0x89, 0xa5, 0x2b, 0x93, 0x31, 0x14, 0x75, 0xac, 0x22, 0xef, 0x5c, 0xf8, + 0x82, 0xe6, 0xc2, 0x83, 0x62, 0xb0, 0xff, 0x73, 0x43, 0xd8, 0xee, 0xc9, 0xbe, 0xdb, 0x80, 0x52, 0x49, 0x9c, 0x08, + 0x73, 0x72, 0x8d, 0x82, 0x68, 0x70, 0x70, 0x61, 0x0b, 0x00, 0x59, 0x08, 0x83, 0x5f, 0xf7, 0xa3, 0x41, 0x4b, 0x0e, + 0xde, 0x73, 0xfb, 0x1e, 0x27, 0xb9, 0xd2, 0xd0, 0xfa, 0x79, 0xf0, 0x56, 0x4e, 0x15, 0x05, 0x1a, 0x38, 0xb3, 0x02, + 0xa4, 0xd9, 0x09, 0xbc, 0x99, 0x3d, 0x89, 0x26, 0x0c, 0xa6, 0xb1, 0x80, 0x43, 0x02, 0xf5, 0x31, 0x27, 0x30, 0x62, + 0xd5, 0xec, 0x3a, 0xd0, 0xcf, 0x5f, 0xb8, 0x5f, 0xf4, 0x47, 0x22, 0x98, 0x0b, 0x35, 0xfc, 0x48, 0x2c, 0x97, 0xf0, + 0xff, 0x5c, 0xf4, 0x39, 0xb9, 0x96, 0x45, 0x53, 0x5d, 0x34, 0x86, 0xa2, 0xb7, 0x01, 0x80, 0x8a, 0xf3, 0x52, 0xcb, + 0x52, 0x6b, 0x32, 0x27, 0x12, 0xf6, 0xbd, 0xbd, 0x74, 0x10, 0x37, 0xda, 0x17, 0xe0, 0xe2, 0xcf, 0x44, 0xfe, 0x1d, + 0x13, 0xb1, 0xe7, 0xee, 0xf7, 0x5c, 0xd4, 0x77, 0x1d, 0x58, 0xda, 0x6e, 0xd6, 0x20, 0x0a, 0xc3, 0x49, 0xe3, 0x9d, + 0x08, 0x66, 0x3d, 0xd2, 0xea, 0x7b, 0x4c, 0xb1, 0xf0, 0x10, 0xe1, 0x44, 0x33, 0xce, 0x16, 0x9e, 0xa1, 0x06, 0x15, + 0x0d, 0xf3, 0x3c, 0x43, 0x8d, 0x49, 0x63, 0x8e, 0x82, 0xa4, 0x31, 0x69, 0x78, 0x33, 0x42, 0x48, 0xb3, 0x53, 0x36, + 0x33, 0xe2, 0x2f, 0x46, 0xc1, 0xdc, 0x78, 0x3b, 0x07, 0x72, 0x3b, 0x64, 0x0d, 0x2f, 0x1d, 0xd0, 0x8b, 0xe5, 0xd2, + 0x3d, 0xe9, 0xf7, 0x5c, 0xd4, 0xf0, 0x0c, 0xa1, 0xed, 0x1b, 0x4a, 0x43, 0x08, 0xb3, 0x8b, 0x42, 0x47, 0x93, 0x5e, + 0xd7, 0x22, 0x47, 0x8b, 0x6a, 0xb3, 0x5b, 0x3c, 0x80, 0x16, 0xa5, 0x21, 0xa3, 0x14, 0xd6, 0x29, 0x4c, 0xd3, 0x10, + 0x73, 0x46, 0x5a, 0x98, 0x13, 0xe3, 0xbc, 0x8e, 0x89, 0xa8, 0x08, 0x3e, 0x21, 0x55, 0x75, 0x3c, 0x08, 0x71, 0x74, + 0x41, 0x5e, 0x29, 0x83, 0xa4, 0x6b, 0x5c, 0xe3, 0x34, 0x21, 0xaf, 0x57, 0x22, 0xb8, 0x21, 0x84, 0x57, 0x6e, 0xfc, + 0xe1, 0x2c, 0xcb, 0x68, 0x2a, 0x5e, 0xf3, 0x48, 0xeb, 0x69, 0x34, 0x01, 0x53, 0x09, 0x42, 0xb3, 0x18, 0x94, 0xb4, + 0x8e, 0xd9, 0x19, 0xb3, 0xb5, 0xd7, 0x63, 0x32, 0x53, 0xfa, 0x93, 0x0c, 0xd8, 0x76, 0xc7, 0xda, 0x30, 0xf6, 0x10, + 0x9e, 0xe9, 0x48, 0xae, 0xe7, 0xfb, 0xfe, 0xd8, 0x1f, 0xc2, 0x6b, 0x18, 0x20, 0x47, 0x85, 0xdc, 0x47, 0x5e, 0x4e, + 0x6e, 0xfc, 0x94, 0xde, 0xca, 0x51, 0x3d, 0x54, 0x49, 0x66, 0xb3, 0xbd, 0x4e, 0xe2, 0xae, 0x64, 0x37, 0xb9, 0x9f, + 0xf2, 0x88, 0x02, 0x7a, 0x20, 0x76, 0xaf, 0x8b, 0xe2, 0x30, 0xb7, 0x43, 0x54, 0x15, 0x7c, 0x03, 0xdb, 0x7b, 0x3d, + 0x06, 0x97, 0xaf, 0x54, 0xb6, 0xca, 0xca, 0xca, 0x0f, 0x8e, 0x10, 0x1b, 0x79, 0x63, 0x1f, 0x42, 0x7b, 0x92, 0x84, + 0x28, 0xd8, 0x72, 0x63, 0x9b, 0xa8, 0x26, 0x65, 0x9f, 0x73, 0x12, 0x0d, 0x78, 0xa3, 0x21, 0xdd, 0xd0, 0x33, 0x45, + 0x12, 0x63, 0x84, 0xe7, 0xe5, 0xde, 0x32, 0xf5, 0xbe, 0x24, 0xf5, 0x91, 0xbc, 0x79, 0xdd, 0x9d, 0xdb, 0x80, 0x34, + 0x09, 0xf0, 0x14, 0x0a, 0x6f, 0x82, 0xf0, 0x29, 0xd9, 0xf7, 0x06, 0x7e, 0xff, 0x2f, 0x17, 0xa8, 0xef, 0xf9, 0x7f, + 0x46, 0xfb, 0x8a, 0x71, 0xcc, 0x51, 0x37, 0x51, 0x43, 0x2c, 0x64, 0x08, 0xb3, 0x8d, 0xa5, 0x27, 0x31, 0xc8, 0x70, + 0x1a, 0x4e, 0x68, 0x70, 0x0a, 0x7b, 0xdc, 0xd0, 0xcd, 0x97, 0x18, 0xe8, 0x28, 0x38, 0xd5, 0x9c, 0xc4, 0x77, 0xfb, + 0xcf, 0x44, 0xf9, 0xd4, 0x77, 0xfb, 0x5f, 0x55, 0x4f, 0x7f, 0x71, 0xfb, 0x3f, 0x8b, 0xe0, 0x97, 0x42, 0x3b, 0xbb, + 0x6b, 0x43, 0x3c, 0x32, 0x43, 0x14, 0x6a, 0x61, 0x2c, 0xcc, 0xcd, 0xd0, 0xba, 0x9f, 0x63, 0x8c, 0x0a, 0x36, 0x2a, + 0x59, 0x51, 0xee, 0x8b, 0x70, 0x0c, 0x28, 0xb5, 0x56, 0x20, 0xb7, 0x23, 0xfb, 0xd5, 0x84, 0x81, 0x50, 0x0c, 0xb5, + 0x02, 0x2a, 0xc7, 0xbd, 0x16, 0x5a, 0xd4, 0xea, 0x4a, 0x8d, 0xa9, 0x1e, 0x49, 0x2f, 0xb9, 0xf4, 0x9c, 0xb4, 0xba, + 0xf3, 0x93, 0x71, 0x77, 0xde, 0x68, 0xa0, 0xdc, 0x10, 0xd6, 0x6c, 0x30, 0xbf, 0xc0, 0x1f, 0xc0, 0xa7, 0x67, 0x53, + 0x12, 0xae, 0x4d, 0xaf, 0xa3, 0xa7, 0xd7, 0x68, 0x64, 0x05, 0xea, 0x5a, 0x4d, 0xc7, 0xaa, 0x69, 0x51, 0x28, 0x9c, + 0xac, 0x12, 0xda, 0x31, 0x92, 0x25, 0x90, 0x0e, 0x45, 0x08, 0x39, 0x15, 0x68, 0x63, 0xaf, 0xd0, 0x27, 0x34, 0x97, + 0x3b, 0x16, 0x98, 0xa7, 0x92, 0x11, 0x1e, 0x60, 0x01, 0x9a, 0x96, 0x8e, 0xe0, 0x09, 0x9e, 0x35, 0xda, 0x92, 0xc8, + 0x9b, 0xed, 0x6e, 0xbd, 0xaf, 0xc7, 0x55, 0x5f, 0x78, 0xd6, 0x20, 0x93, 0x12, 0x4b, 0x45, 0xd6, 0x68, 0x14, 0xf5, + 0x68, 0xa7, 0xd9, 0xb7, 0xb5, 0xf8, 0xc3, 0xed, 0x6a, 0x5a, 0x86, 0x91, 0xaf, 0x95, 0x44, 0x65, 0x3e, 0x4b, 0x53, + 0x9a, 0x81, 0x0c, 0x25, 0x02, 0xb3, 0xa2, 0xa8, 0xe4, 0x3a, 0x08, 0x51, 0x4c, 0x49, 0x0a, 0x7c, 0x47, 0x9a, 0x5d, + 0x38, 0xc3, 0x1c, 0xc7, 0x92, 0x6b, 0x10, 0x42, 0xce, 0x4c, 0x42, 0x8b, 0x90, 0x1c, 0x28, 0x21, 0xcc, 0x92, 0x48, + 0x39, 0xa1, 0xfe, 0xe5, 0xee, 0x19, 0xbf, 0xd7, 0x24, 0x1b, 0xb0, 0x8b, 0x40, 0x56, 0x4b, 0x34, 0xdf, 0x0a, 0xc9, + 0x7b, 0x4f, 0xa0, 0x32, 0x38, 0xe2, 0x4b, 0xf6, 0xf7, 0x8c, 0x65, 0x54, 0x6a, 0xe0, 0xbb, 0xc6, 0xec, 0x4b, 0xea, + 0xea, 0x63, 0x62, 0x3b, 0x6f, 0x00, 0x91, 0x21, 0xf8, 0x76, 0x32, 0xb2, 0x56, 0xed, 0x72, 0xf7, 0xf4, 0xcd, 0x26, + 0x13, 0x78, 0xb9, 0xd4, 0xc6, 0xaf, 0xd4, 0x6c, 0x70, 0x58, 0x41, 0x9a, 0xe8, 0x1f, 0x81, 0x97, 0x48, 0x05, 0x29, + 0xf4, 0x52, 0xa0, 0xa2, 0xcb, 0xdd, 0xd3, 0xf7, 0x5e, 0x2a, 0x5d, 0x4b, 0x08, 0xdb, 0xd3, 0xf6, 0x38, 0xf1, 0x62, + 0x42, 0x91, 0x9a, 0x7b, 0xc9, 0xb8, 0xb8, 0x25, 0xbe, 0x83, 0x58, 0xbe, 0x04, 0xfb, 0x61, 0xc0, 0x2e, 0x48, 0xa2, + 0x31, 0x40, 0x12, 0x84, 0x93, 0x9a, 0x59, 0x46, 0x60, 0x01, 0xe4, 0x58, 0xe7, 0xb0, 0x12, 0xbe, 0x52, 0xfc, 0x10, + 0x4e, 0xe4, 0xa8, 0xa2, 0x50, 0xa2, 0xe3, 0xe5, 0x5a, 0x5e, 0x5a, 0x65, 0x8d, 0x7e, 0x0b, 0x96, 0x93, 0x79, 0x78, + 0xad, 0xbb, 0x2e, 0x0b, 0x9e, 0x99, 0x04, 0xb2, 0xcb, 0xdd, 0xd3, 0x57, 0x3a, 0x87, 0x6c, 0x1a, 0x1a, 0x6e, 0xbf, + 0x66, 0x61, 0x9e, 0xbe, 0xf2, 0xab, 0xb7, 0xb2, 0xf2, 0xe5, 0xee, 0xe9, 0x87, 0x4d, 0xd5, 0xa0, 0xbc, 0x98, 0x55, + 0x26, 0xbe, 0x84, 0x6f, 0x41, 0x93, 0x60, 0xa1, 0x45, 0x43, 0xc0, 0x0a, 0x2c, 0xc5, 0x51, 0x90, 0x17, 0xa5, 0x67, + 0xe4, 0x19, 0xce, 0x88, 0x8c, 0x02, 0xd5, 0x57, 0x4d, 0x2b, 0x79, 0x8c, 0xa7, 0xe7, 0x43, 0x3e, 0xa5, 0x5b, 0x42, + 0x43, 0xb7, 0xc8, 0x67, 0x13, 0x48, 0x9e, 0x91, 0xa0, 0x33, 0xbc, 0xd3, 0x42, 0xdd, 0xba, 0xf0, 0xca, 0x24, 0x91, + 0xf2, 0x9a, 0x64, 0xc1, 0x31, 0x69, 0xe1, 0x84, 0xb4, 0x70, 0x48, 0xf2, 0x41, 0x4b, 0x89, 0x87, 0x6e, 0x58, 0xf6, + 0xab, 0x84, 0x0c, 0xe4, 0x85, 0xe9, 0xdd, 0xaa, 0xc4, 0x6f, 0xd4, 0x0d, 0xa5, 0xeb, 0x51, 0x4a, 0xf4, 0x48, 0x92, + 0xc5, 0x0b, 0x8f, 0x63, 0x2e, 0x3b, 0x3e, 0x67, 0xd7, 0x09, 0xa4, 0x96, 0xc0, 0xac, 0xb0, 0x40, 0x41, 0x59, 0xb5, + 0xad, 0xab, 0x86, 0xbe, 0x5c, 0x27, 0x8e, 0x43, 0x1f, 0x18, 0x37, 0x0e, 0x75, 0x26, 0x4e, 0xbe, 0xde, 0xe4, 0xd1, + 0xde, 0x9e, 0xa7, 0x1a, 0xfd, 0x22, 0x3c, 0x6e, 0xde, 0x57, 0x81, 0xbb, 0x6f, 0x15, 0xaf, 0x88, 0x90, 0x84, 0xbf, + 0xd1, 0x48, 0x2e, 0x0a, 0x88, 0x42, 0x7b, 0x61, 0x1d, 0x83, 0x06, 0x78, 0xa9, 0xe9, 0xd5, 0xa7, 0xdf, 0x68, 0x94, + 0x41, 0xda, 0x3a, 0xb6, 0x6e, 0x71, 0x56, 0xcc, 0xbd, 0x32, 0xf9, 0xa7, 0xb5, 0x96, 0x31, 0x65, 0x40, 0x40, 0xcc, + 0xa6, 0x59, 0x66, 0x26, 0x63, 0x6d, 0x09, 0x06, 0xf5, 0xbe, 0xd2, 0x69, 0x0b, 0x58, 0xe6, 0x57, 0xe9, 0x4a, 0x86, + 0x9d, 0x75, 0x50, 0x60, 0x2a, 0x41, 0x50, 0x0a, 0x2a, 0x35, 0x0a, 0x4d, 0xde, 0x2f, 0xd6, 0xb3, 0x2e, 0x71, 0x8e, + 0xb4, 0x8f, 0x4b, 0x42, 0x21, 0x91, 0xd5, 0x29, 0x91, 0xf2, 0x82, 0x4c, 0xb7, 0x93, 0xfc, 0xa9, 0x45, 0xf2, 0x4f, + 0x09, 0xb5, 0xc8, 0x5f, 0x79, 0x38, 0x7c, 0xae, 0x5d, 0x0b, 0xb9, 0x79, 0x75, 0x36, 0x25, 0xe0, 0x43, 0xab, 0x63, + 0xb4, 0x16, 0x55, 0xdc, 0xc2, 0x50, 0xec, 0x1d, 0x22, 0xbd, 0x90, 0xd8, 0x84, 0x80, 0xbd, 0x2a, 0xa6, 0x06, 0x43, + 0x6f, 0x72, 0xe9, 0xd9, 0x1c, 0xf0, 0xf4, 0xc3, 0xfd, 0xe1, 0xd0, 0xb3, 0xe9, 0xfa, 0xce, 0xb5, 0xb2, 0x3f, 0x61, + 0xd6, 0xd6, 0xc6, 0xad, 0xe7, 0x82, 0xc2, 0xf8, 0x65, 0x18, 0xbb, 0xce, 0x7c, 0x56, 0x36, 0xa1, 0x91, 0x7f, 0x00, + 0x6d, 0xbb, 0x2d, 0x6b, 0x50, 0xab, 0x5b, 0xe0, 0x47, 0x2a, 0x07, 0x35, 0xcc, 0xb6, 0xb0, 0x8f, 0x53, 0x59, 0x81, + 0xa6, 0xd1, 0xe6, 0xd7, 0x4f, 0x0b, 0x4d, 0x26, 0x0a, 0x34, 0xb4, 0x00, 0xfe, 0xa7, 0x48, 0x1e, 0xe8, 0x46, 0xca, + 0x05, 0x40, 0xd0, 0x54, 0xe2, 0xa9, 0x42, 0x98, 0xeb, 0x56, 0xce, 0xf7, 0x17, 0x3b, 0x84, 0x4c, 0x2b, 0xe7, 0xe3, + 0xbb, 0x2a, 0xf7, 0x0a, 0xc8, 0x02, 0x05, 0x60, 0x3c, 0x96, 0x05, 0x2a, 0x7a, 0x79, 0x66, 0xaa, 0x4b, 0x03, 0xd2, + 0xaf, 0xf4, 0x6d, 0x2b, 0xb2, 0x29, 0xbd, 0x72, 0xea, 0xbd, 0x41, 0xc3, 0xca, 0xdb, 0x5d, 0x78, 0xfb, 0x42, 0x48, + 0x18, 0xe1, 0xf9, 0xbd, 0xac, 0x6d, 0xfa, 0x2d, 0x3e, 0xae, 0x26, 0xb0, 0xac, 0x2c, 0x8a, 0xcf, 0xd2, 0x9c, 0x66, + 0xe2, 0x29, 0x1d, 0xf1, 0x0c, 0x42, 0x16, 0x25, 0x4e, 0x50, 0xb1, 0x6b, 0xb9, 0xed, 0xe4, 0xfc, 0xac, 0x38, 0xc1, + 0xca, 0x04, 0xe5, 0xaf, 0x8f, 0x32, 0x66, 0x7d, 0xb9, 0xda, 0x6a, 0xba, 0xb7, 0xf7, 0xbe, 0x42, 0x93, 0x86, 0x52, + 0x42, 0x61, 0x31, 0x2d, 0xa5, 0xd2, 0xe8, 0x40, 0xee, 0xae, 0x57, 0xba, 0x00, 0x0c, 0xc3, 0xb0, 0x79, 0xcf, 0x0b, + 0x22, 0x8a, 0xf1, 0x2a, 0x8b, 0xd7, 0xae, 0x09, 0x66, 0x9b, 0x2d, 0xc0, 0xe1, 0xc1, 0xd0, 0x56, 0xbe, 0xa2, 0xbc, + 0x4a, 0x87, 0x2d, 0x61, 0x38, 0x03, 0x64, 0x79, 0xd2, 0x08, 0xb1, 0x28, 0x70, 0xa3, 0x51, 0xf2, 0x11, 0xf4, 0xca, + 0x18, 0xe7, 0x7e, 0x0c, 0x09, 0xb0, 0xb5, 0x2d, 0x8b, 0x10, 0x56, 0x79, 0x39, 0x56, 0x26, 0xc1, 0xe9, 0x8b, 0x4d, + 0x1e, 0x65, 0x43, 0xd4, 0x54, 0x4a, 0x1d, 0xa8, 0x91, 0xa1, 0xb2, 0x81, 0x3f, 0xf7, 0x98, 0x56, 0xdc, 0x4c, 0xd8, + 0x0c, 0x18, 0xf0, 0x4b, 0xe1, 0xa9, 0x58, 0x14, 0xc8, 0x0c, 0xee, 0xcf, 0xbc, 0xda, 0xd0, 0x5d, 0x2e, 0x9b, 0x61, + 0x8d, 0xb8, 0xd8, 0x46, 0x13, 0x97, 0x61, 0xbd, 0xb3, 0x8a, 0x97, 0xee, 0xaa, 0x1c, 0x6a, 0x61, 0xb8, 0x60, 0x95, + 0x47, 0x62, 0x4d, 0x7f, 0x57, 0xa5, 0x45, 0x97, 0x95, 0x40, 0x0d, 0xa3, 0x37, 0xce, 0x6b, 0xb9, 0x06, 0xb4, 0x00, + 0xfa, 0x5a, 0x3c, 0x17, 0xd6, 0x8a, 0x1a, 0x1f, 0xb6, 0x1c, 0xd3, 0x92, 0xfa, 0xef, 0x20, 0xd3, 0x65, 0x75, 0xcf, + 0xbf, 0x90, 0xb2, 0x90, 0xe1, 0xbc, 0xc6, 0xd8, 0x33, 0xc9, 0xd8, 0x11, 0xe8, 0x69, 0x26, 0xf5, 0xbb, 0xaf, 0x13, + 0x5e, 0x98, 0x96, 0x72, 0x9a, 0xc4, 0x3e, 0x94, 0xc1, 0x72, 0xeb, 0xf7, 0xca, 0x6a, 0x04, 0x8c, 0x40, 0x12, 0x10, + 0xd6, 0x9c, 0x3d, 0x43, 0x38, 0x6f, 0x34, 0xba, 0xf9, 0x09, 0xad, 0x5c, 0x24, 0x15, 0x8c, 0x0c, 0xe2, 0xb9, 0x40, + 0xf0, 0x35, 0x19, 0x0a, 0x11, 0x7f, 0x93, 0x9b, 0x9d, 0x83, 0xab, 0xfd, 0xf4, 0x9d, 0x67, 0x73, 0x35, 0xbb, 0x6e, + 0x19, 0x33, 0x85, 0xf9, 0x78, 0x55, 0xbc, 0xe5, 0xed, 0xfd, 0xf9, 0x1d, 0x00, 0xf7, 0x4e, 0x1b, 0x43, 0x2e, 0x1a, + 0xea, 0x0a, 0xc5, 0x12, 0xca, 0xdd, 0xd7, 0x45, 0x55, 0x5a, 0xa2, 0x3d, 0x58, 0x57, 0x54, 0xa6, 0xac, 0x20, 0x79, + 0x51, 0xe4, 0xb4, 0x8a, 0xee, 0xaf, 0xe4, 0x5f, 0x4a, 0xe1, 0xb2, 0xee, 0x6c, 0x3f, 0x9b, 0x12, 0x81, 0x2d, 0x42, + 0x7d, 0xbb, 0x2d, 0xf4, 0x51, 0x81, 0x09, 0xfb, 0x5a, 0x0b, 0xc5, 0x5f, 0x36, 0x09, 0x45, 0x9c, 0xe9, 0x2d, 0x2f, + 0x05, 0x62, 0xfb, 0x01, 0x02, 0x51, 0x3b, 0xd9, 0x8d, 0x4c, 0x04, 0x75, 0xa4, 0x26, 0x13, 0xeb, 0x4b, 0x4a, 0x32, + 0xcc, 0xf4, 0x6a, 0xf4, 0x3a, 0xcb, 0x25, 0x1b, 0xb4, 0xc0, 0x89, 0xe4, 0xba, 0xf0, 0xb3, 0xad, 0x7e, 0x5a, 0x9c, + 0x58, 0x39, 0x81, 0x3d, 0x56, 0x9a, 0x2c, 0xc8, 0x87, 0x14, 0x67, 0x4f, 0xe6, 0x64, 0x49, 0x9a, 0xd6, 0x14, 0xa4, + 0x09, 0x9c, 0xb0, 0x32, 0xca, 0x04, 0x10, 0x4b, 0x59, 0xa1, 0x0d, 0x48, 0x6f, 0x63, 0xf2, 0x9f, 0x31, 0x2f, 0x3f, + 0xad, 0x89, 0xd6, 0xe4, 0x8a, 0x52, 0x1f, 0x6a, 0xe9, 0x06, 0x1a, 0x02, 0xad, 0x1f, 0xee, 0x48, 0x13, 0xb4, 0x12, + 0xe5, 0xc8, 0x96, 0x43, 0xb8, 0x05, 0x2e, 0xb4, 0x9d, 0xf7, 0x2a, 0xc0, 0xbb, 0x41, 0x9a, 0x60, 0x6e, 0xd1, 0xf5, + 0x0b, 0x22, 0x6a, 0xac, 0x24, 0x26, 0xda, 0x52, 0xc2, 0xa1, 0x24, 0x53, 0x41, 0xb2, 0x41, 0xeb, 0x02, 0x14, 0xd0, + 0x6e, 0x72, 0x92, 0x55, 0x26, 0x70, 0xd2, 0x68, 0xa0, 0xd0, 0x8c, 0x1a, 0x0f, 0x58, 0x23, 0xb9, 0xc0, 0x14, 0x27, + 0xca, 0x30, 0x39, 0xdb, 0xdb, 0xf3, 0xc2, 0x6a, 0xdc, 0x41, 0x72, 0x81, 0x30, 0x5f, 0x2e, 0x3d, 0x09, 0x56, 0x88, + 0x96, 0xcb, 0xd0, 0x06, 0x4b, 0xbe, 0x86, 0x66, 0xd3, 0xbe, 0x20, 0x53, 0x29, 0x00, 0xa7, 0x00, 0x61, 0x83, 0x78, + 0xa1, 0x76, 0xee, 0x85, 0xe0, 0x8c, 0x6a, 0x64, 0x83, 0xa4, 0xd1, 0xbe, 0xb0, 0x18, 0xd7, 0x20, 0xb9, 0x20, 0x61, + 0xc1, 0xf7, 0xf6, 0x76, 0x72, 0x2d, 0x22, 0x7f, 0x02, 0x51, 0xf6, 0x93, 0x94, 0x2c, 0xaa, 0x43, 0x7b, 0x35, 0x56, + 0x9d, 0x01, 0x25, 0x45, 0xe9, 0x65, 0x35, 0xf5, 0x6a, 0x49, 0x10, 0x65, 0x25, 0xac, 0x63, 0xc1, 0x7d, 0xb0, 0xec, + 0x4b, 0x32, 0x7f, 0x26, 0xca, 0x24, 0xeb, 0x5f, 0x36, 0xa6, 0x56, 0xfb, 0xbe, 0x1f, 0x66, 0x63, 0x19, 0xc9, 0x30, + 0x51, 0x58, 0x49, 0xfc, 0x07, 0x1a, 0x4c, 0x6b, 0xe0, 0x41, 0x39, 0xd6, 0x05, 0x51, 0xe0, 0x1b, 0xd5, 0xc6, 0x9c, + 0x26, 0xf9, 0x69, 0xa3, 0x97, 0x41, 0x41, 0xf2, 0xd5, 0x6f, 0x85, 0xe4, 0x50, 0x43, 0xa2, 0xc8, 0x63, 0x05, 0x67, + 0x5b, 0x70, 0xf1, 0x93, 0x58, 0xc1, 0xd9, 0x76, 0xdc, 0x1a, 0x4c, 0xfd, 0xbc, 0x0d, 0x3e, 0x8b, 0x37, 0x28, 0x40, + 0xab, 0x02, 0x0b, 0xca, 0xa3, 0x55, 0xdd, 0x4b, 0xb1, 0x52, 0x10, 0xa6, 0x82, 0x78, 0xac, 0xbe, 0x01, 0x2a, 0x6d, + 0xd4, 0x32, 0x7c, 0x59, 0x30, 0x45, 0x96, 0x4b, 0xa0, 0x9e, 0xb9, 0x02, 0xe4, 0xa4, 0x7d, 0xed, 0xd3, 0xbd, 0x3d, + 0xb0, 0x0d, 0x40, 0x89, 0xf3, 0x87, 0xe1, 0x54, 0xcc, 0x32, 0x50, 0xa5, 0x72, 0xf3, 0x1b, 0x8a, 0xe1, 0x1c, 0x88, + 0x2c, 0x83, 0x1f, 0x50, 0x30, 0x0d, 0xf3, 0x9c, 0xcd, 0x55, 0x99, 0xfe, 0x8d, 0x39, 0x31, 0xa4, 0x9c, 0x2b, 0x9d, + 0x30, 0x43, 0xdd, 0x4c, 0xd3, 0x69, 0x1d, 0x6d, 0xcf, 0xe7, 0x34, 0x15, 0x2f, 0x59, 0x2e, 0x68, 0x0a, 0xd3, 0xaf, + 0x28, 0x0e, 0x66, 0x94, 0x23, 0xd8, 0xb0, 0xb5, 0x56, 0x61, 0x14, 0xdd, 0xdb, 0x44, 0xd4, 0x75, 0xa0, 0x38, 0x4c, + 0xa3, 0x44, 0x0d, 0x62, 0xa7, 0x33, 0x9a, 0x14, 0xce, 0xb2, 0xa6, 0x9d, 0x4e, 0x53, 0x29, 0x1b, 0x92, 0xbb, 0x7b, + 0x8c, 0x18, 0x49, 0x60, 0xa4, 0xe7, 0xbd, 0x5a, 0x0b, 0x04, 0xbc, 0xb7, 0x2c, 0x82, 0x3d, 0x13, 0x2c, 0x2c, 0x8e, + 0xea, 0xd7, 0xe1, 0x2c, 0x05, 0xc9, 0xc6, 0x43, 0x6d, 0x9b, 0x84, 0x83, 0xa4, 0x93, 0x47, 0xdb, 0x2d, 0xab, 0x57, + 0x46, 0x72, 0x18, 0x69, 0xc1, 0x1e, 0xca, 0x98, 0xd1, 0xc2, 0x90, 0x17, 0x32, 0x5b, 0xf1, 0x52, 0x90, 0x9f, 0xe0, + 0xd4, 0xd0, 0x0b, 0x31, 0x49, 0x56, 0x0e, 0xc7, 0x74, 0x2f, 0x4b, 0xed, 0xff, 0x52, 0x78, 0xaf, 0xf1, 0x0b, 0x08, + 0xeb, 0x7e, 0x5d, 0x55, 0x5f, 0x0f, 0xe7, 0x7e, 0x5d, 0x21, 0xe8, 0xeb, 0x60, 0xad, 0x9e, 0x15, 0xc6, 0xed, 0xf8, + 0xc7, 0x7e, 0xcb, 0x35, 0xda, 0xd2, 0xb7, 0x2a, 0x88, 0xa4, 0x12, 0x2d, 0xe5, 0x7e, 0xc0, 0x55, 0x9a, 0x1a, 0xa4, + 0xcb, 0xd5, 0x2d, 0x24, 0xaa, 0x13, 0x0c, 0x95, 0x0e, 0xbf, 0x6d, 0x79, 0xb4, 0x8c, 0xc9, 0x94, 0x9d, 0xf1, 0x36, + 0xcc, 0xc4, 0x2e, 0xec, 0x32, 0xbe, 0x76, 0x12, 0x2f, 0x26, 0xe0, 0x41, 0x7b, 0xd8, 0x10, 0x96, 0xb1, 0x9d, 0xab, + 0x93, 0x40, 0x76, 0xff, 0x84, 0x1b, 0xdd, 0xad, 0x6e, 0x65, 0x7c, 0x00, 0xfb, 0x1f, 0xe1, 0xd8, 0x1c, 0x8f, 0xa3, + 0x9a, 0x03, 0xd3, 0x60, 0x51, 0x94, 0x4e, 0x01, 0xae, 0x94, 0xb7, 0x14, 0x61, 0x5e, 0xc8, 0xf0, 0xf6, 0x37, 0xf8, + 0x7b, 0xcd, 0x12, 0x47, 0x25, 0xc7, 0x79, 0xfe, 0x50, 0x8e, 0xa8, 0xc0, 0x2f, 0xa3, 0xf7, 0x40, 0xc7, 0x92, 0x42, + 0x0b, 0x43, 0x45, 0xcf, 0xb8, 0x9e, 0xc8, 0xd6, 0xac, 0x54, 0x4c, 0xcb, 0x8c, 0x1a, 0x39, 0xcc, 0x86, 0x34, 0x4e, + 0x63, 0x65, 0x8b, 0x72, 0x57, 0xd5, 0xc6, 0x45, 0x5b, 0xb0, 0x58, 0x05, 0x16, 0x97, 0x4b, 0xaf, 0x8e, 0x6a, 0xc2, + 0xac, 0x38, 0x06, 0xc2, 0xcc, 0x4a, 0xa8, 0xa8, 0x69, 0xd6, 0xaa, 0x8d, 0x87, 0x56, 0xf3, 0x89, 0x8c, 0x6e, 0x5e, + 0x83, 0xc3, 0x76, 0x21, 0xa8, 0xe6, 0xb6, 0x4f, 0x01, 0xab, 0xd9, 0x95, 0x03, 0x59, 0x18, 0xfa, 0xb6, 0xcc, 0x94, + 0xad, 0x52, 0x5a, 0x37, 0xe0, 0x17, 0xdd, 0x93, 0x2b, 0xab, 0x51, 0xb7, 0xfe, 0xde, 0xca, 0x35, 0x7a, 0xc6, 0xb7, + 0xe5, 0x1a, 0xd5, 0xb4, 0xdd, 0x9d, 0x16, 0xba, 0x3f, 0x2b, 0x55, 0x8d, 0xb5, 0xb9, 0xca, 0x6f, 0x18, 0xae, 0x0d, + 0xb4, 0xa9, 0xd0, 0x6c, 0xb8, 0xca, 0x59, 0x51, 0x8c, 0xca, 0xb3, 0x04, 0x32, 0x75, 0x67, 0xa4, 0xe8, 0x5f, 0x5b, + 0x8d, 0xf2, 0x40, 0xae, 0xf7, 0x0d, 0x19, 0x27, 0xfc, 0x3a, 0x4c, 0xde, 0xc3, 0x78, 0xd5, 0xcb, 0x17, 0x77, 0x51, + 0x16, 0x0a, 0xaa, 0xb9, 0x4b, 0x05, 0xc3, 0x37, 0x16, 0x0c, 0xdf, 0x28, 0x3e, 0x5d, 0xb5, 0xc7, 0x8b, 0x97, 0x65, + 0x07, 0xc1, 0xa8, 0x30, 0x2c, 0x63, 0x22, 0x36, 0x8f, 0xb1, 0xca, 0xc2, 0x26, 0x25, 0x0b, 0x9b, 0x08, 0x6f, 0xb5, + 0x2b, 0xcf, 0xfb, 0x7e, 0x73, 0x2f, 0xeb, 0x9c, 0xed, 0xfb, 0x6a, 0xe3, 0x7f, 0x1f, 0xdc, 0xdb, 0xc6, 0xe2, 0x72, + 0x07, 0xfe, 0x81, 0x4c, 0x56, 0x51, 0x20, 0x3f, 0x85, 0xa4, 0x03, 0x41, 0x7a, 0xd6, 0x91, 0x83, 0x4a, 0x4e, 0x99, + 0x3c, 0x20, 0x6f, 0x38, 0xcb, 0x05, 0x9f, 0xe8, 0x3e, 0x73, 0x7d, 0xce, 0x48, 0xbe, 0x04, 0x57, 0xb4, 0x8c, 0xb5, + 0x07, 0xf5, 0x93, 0x5c, 0x8b, 0x8f, 0x2c, 0x8d, 0x82, 0x1c, 0x6b, 0x29, 0x92, 0x07, 0x59, 0x41, 0x4c, 0xae, 0xf1, + 0xfa, 0x3b, 0x3c, 0x62, 0x29, 0xcb, 0x63, 0x9a, 0x79, 0x1c, 0x2d, 0xb6, 0x0d, 0xc6, 0x21, 0x20, 0xa3, 0x06, 0xc3, + 0x5f, 0x56, 0x47, 0xfe, 0x7c, 0xe8, 0x0d, 0xfc, 0x40, 0x13, 0x2a, 0x62, 0x1e, 0x41, 0x5a, 0x8a, 0x1f, 0x95, 0x47, + 0x9a, 0xf6, 0xf6, 0x76, 0x3c, 0x57, 0xba, 0x25, 0xe0, 0xf0, 0xb7, 0xfd, 0x06, 0xf5, 0x17, 0x70, 0x3a, 0xa7, 0x1a, + 0x9a, 0xa2, 0x05, 0x5d, 0x3d, 0xc8, 0x22, 0xfc, 0x8f, 0xf4, 0x0e, 0xa7, 0xa8, 0x28, 0x02, 0x05, 0xb5, 0x3b, 0x62, + 0x34, 0x89, 0x5c, 0xfc, 0x91, 0xde, 0x05, 0xe5, 0x79, 0x71, 0x79, 0xbc, 0x59, 0x2e, 0xa0, 0xcb, 0x6f, 0x52, 0x17, + 0x57, 0x83, 0x04, 0x8b, 0x02, 0xf3, 0x8c, 0x8d, 0x81, 0x38, 0xff, 0x46, 0xef, 0x02, 0xd5, 0x1f, 0xb3, 0x4e, 0xeb, + 0xa1, 0x85, 0x41, 0xbd, 0x6f, 0x15, 0xdb, 0xcb, 0xa0, 0x0d, 0x8a, 0x81, 0x6c, 0x7b, 0x41, 0x6a, 0xf5, 0x2a, 0xf3, + 0x10, 0xa1, 0xe2, 0xa1, 0x53, 0xc1, 0xdf, 0xd9, 0xa2, 0x4d, 0xd4, 0x32, 0x5f, 0x57, 0x1a, 0x51, 0x68, 0x50, 0x65, + 0x7a, 0x5c, 0x7a, 0xa9, 0xd9, 0x75, 0xfa, 0x08, 0x82, 0xe5, 0x08, 0xfb, 0x4e, 0xe8, 0x4e, 0x83, 0x2f, 0x55, 0x42, + 0x48, 0x15, 0x49, 0x7a, 0x55, 0xb5, 0x73, 0x2e, 0x3d, 0xc0, 0x3b, 0x24, 0xb4, 0x84, 0xf2, 0x40, 0x66, 0x61, 0xb2, + 0x45, 0x7f, 0x10, 0xc4, 0x5b, 0x98, 0x29, 0x04, 0xa9, 0x8d, 0x45, 0x51, 0x00, 0x15, 0x6a, 0xfa, 0x52, 0x09, 0x80, + 0x70, 0x86, 0x7d, 0x4d, 0x6a, 0x66, 0x52, 0x6a, 0xfa, 0x16, 0xc6, 0xb7, 0x48, 0x49, 0x2a, 0x91, 0x21, 0x95, 0x48, + 0x29, 0xf4, 0xf4, 0xe2, 0x6a, 0x12, 0xb2, 0x17, 0xb4, 0x3c, 0x3f, 0xa7, 0xd6, 0x3c, 0xab, 0x81, 0xe5, 0xc9, 0x7e, + 0x50, 0x11, 0xc0, 0x94, 0xa8, 0xaa, 0x50, 0x94, 0xc7, 0xb2, 0x4d, 0x7a, 0xab, 0xc7, 0x7d, 0x33, 0x2d, 0x62, 0x50, + 0xe2, 0xc5, 0x68, 0x91, 0x7a, 0x31, 0xce, 0x20, 0x1d, 0x91, 0x17, 0x25, 0xfc, 0xd4, 0x5e, 0x8d, 0x5a, 0xb2, 0xf2, + 0xe6, 0x33, 0x7e, 0xa0, 0xcc, 0x0b, 0x48, 0xd1, 0xc4, 0xa9, 0xe1, 0x29, 0xa9, 0x27, 0x0f, 0xdb, 0x59, 0xcb, 0xf6, + 0xb5, 0x4e, 0xd0, 0xd1, 0x80, 0xfd, 0x20, 0xbc, 0x85, 0x35, 0x0b, 0xfb, 0x34, 0xb7, 0x3e, 0xf3, 0xa7, 0x83, 0x7d, + 0x55, 0x0e, 0xa9, 0x97, 0x93, 0x15, 0x89, 0x73, 0x7f, 0xaa, 0xe5, 0xcf, 0x33, 0x9a, 0xdd, 0x9d, 0x53, 0x48, 0x75, + 0xe6, 0x70, 0xda, 0xb7, 0x5a, 0x86, 0x2a, 0x4d, 0xbd, 0x9f, 0x49, 0x65, 0xa5, 0xa8, 0x9f, 0x02, 0x5c, 0x3d, 0x23, + 0x58, 0xc8, 0x68, 0xa3, 0xe5, 0x88, 0x51, 0xbb, 0x85, 0x6e, 0x3d, 0x3d, 0x49, 0xbb, 0x0c, 0xfc, 0x6b, 0x15, 0xa6, + 0x75, 0xb0, 0x00, 0x73, 0xfb, 0x44, 0xea, 0x20, 0xbf, 0x58, 0xf5, 0xca, 0x40, 0x11, 0x84, 0xef, 0xb2, 0xed, 0x53, + 0xdd, 0x94, 0x34, 0xbb, 0x7d, 0xaa, 0xb5, 0xa0, 0x9f, 0x4c, 0xf8, 0xc1, 0x7a, 0x9c, 0xf2, 0xf8, 0x32, 0x2b, 0x0a, + 0x54, 0x00, 0x78, 0x7f, 0xed, 0x7a, 0xde, 0x5f, 0x75, 0xca, 0xa0, 0x0f, 0xb1, 0xd8, 0xf3, 0x84, 0x1b, 0x26, 0x5e, + 0x8d, 0xff, 0xd7, 0xb5, 0xf1, 0xff, 0x6a, 0x9d, 0x39, 0x05, 0xd3, 0x68, 0x9c, 0xd2, 0xc8, 0xb0, 0x4e, 0xa4, 0x08, + 0x50, 0xea, 0x6d, 0xa9, 0x20, 0x6f, 0xae, 0x02, 0xd0, 0xb8, 0x16, 0x23, 0x9e, 0x8a, 0xe6, 0x28, 0x9c, 0xb0, 0xe4, + 0x2e, 0x98, 0xb1, 0xe6, 0x84, 0xa7, 0x3c, 0x9f, 0x86, 0x43, 0x8a, 0xf3, 0xbb, 0x5c, 0xd0, 0x49, 0x73, 0xc6, 0xf0, + 0x0b, 0x9a, 0xcc, 0xa9, 0x60, 0xc3, 0x10, 0xbb, 0xa7, 0x19, 0x0b, 0x13, 0xe7, 0x75, 0x98, 0x65, 0xfc, 0xc6, 0xc5, + 0xef, 0xf8, 0x35, 0x17, 0x1c, 0xbf, 0xb9, 0xbd, 0x1b, 0xd3, 0x14, 0x7f, 0xb8, 0x9e, 0xa5, 0x62, 0x86, 0xf3, 0x30, + 0xcd, 0x9b, 0x39, 0xcd, 0xd8, 0xa8, 0x3b, 0xe4, 0x09, 0xcf, 0x9a, 0x90, 0xb1, 0x3d, 0xa1, 0x41, 0xc2, 0xc6, 0xb1, + 0x70, 0xa2, 0x30, 0xfb, 0xd8, 0x6d, 0x36, 0xa7, 0x19, 0x9b, 0x84, 0xd9, 0x5d, 0x53, 0xd6, 0x08, 0x3e, 0x6f, 0x1d, + 0x84, 0x4f, 0x46, 0x87, 0x5d, 0x91, 0x85, 0x69, 0xce, 0x60, 0x99, 0x82, 0x30, 0x49, 0x9c, 0x83, 0xa3, 0xd6, 0x24, + 0xdf, 0x51, 0x81, 0xbc, 0x30, 0x15, 0xc5, 0x15, 0x7e, 0x03, 0x70, 0xfb, 0xd7, 0x22, 0xc5, 0xd7, 0x33, 0x21, 0x78, + 0xba, 0x18, 0xce, 0xb2, 0x9c, 0x67, 0xc1, 0x94, 0xb3, 0x54, 0xd0, 0xac, 0x7b, 0xcd, 0xb3, 0x88, 0x66, 0xcd, 0x2c, + 0x8c, 0xd8, 0x2c, 0x0f, 0x0e, 0xa7, 0xb7, 0x5d, 0xd0, 0x2c, 0xc6, 0x19, 0x9f, 0xa5, 0x91, 0x1e, 0x8b, 0xa5, 0x31, + 0xcd, 0x98, 0xb0, 0x5f, 0xc8, 0x4b, 0x4c, 0x82, 0x84, 0xa5, 0x34, 0xcc, 0x9a, 0x63, 0x68, 0x0c, 0x66, 0x51, 0x2b, + 0xa2, 0x63, 0x9c, 0x8d, 0xaf, 0x43, 0xaf, 0xdd, 0x79, 0x8c, 0xcd, 0x5f, 0xff, 0x08, 0x39, 0xad, 0xcd, 0xc5, 0xed, + 0x56, 0xeb, 0x4f, 0xa8, 0xbb, 0x32, 0x8a, 0x04, 0x28, 0x68, 0x4f, 0x6f, 0x9d, 0x9c, 0x43, 0x46, 0xdb, 0xa6, 0x96, + 0xdd, 0x69, 0x18, 0x41, 0x3e, 0x70, 0xd0, 0x99, 0xde, 0x16, 0x30, 0xbb, 0x40, 0xa5, 0x98, 0xea, 0x49, 0xea, 0xa7, + 0xc5, 0x6f, 0x85, 0xf8, 0x78, 0x33, 0xc4, 0x1d, 0x03, 0x71, 0x85, 0xf5, 0x66, 0x34, 0xcb, 0x64, 0x6c, 0x35, 0x68, + 0xe7, 0x0a, 0x90, 0x98, 0xcf, 0x69, 0x66, 0xe0, 0x90, 0x0f, 0xbf, 0x19, 0x8c, 0xce, 0x66, 0x30, 0x8e, 0x3f, 0x05, + 0x46, 0x96, 0x46, 0x8b, 0xfa, 0xba, 0xb6, 0x33, 0x3a, 0xe9, 0xc6, 0x14, 0xe8, 0x29, 0xe8, 0xc0, 0xef, 0x1b, 0x16, + 0x89, 0x58, 0xfd, 0x94, 0xe4, 0x7c, 0xa3, 0xde, 0x1d, 0xb5, 0x5a, 0xea, 0x39, 0x67, 0xbf, 0xd0, 0xa0, 0xed, 0x43, + 0x85, 0xe2, 0x0a, 0xff, 0xad, 0x3c, 0xcb, 0x5b, 0xe7, 0x9e, 0xf8, 0x1b, 0xfb, 0x90, 0xaf, 0x95, 0xa2, 0x58, 0x1d, + 0x89, 0xc6, 0x99, 0x91, 0x95, 0x4a, 0xf8, 0x80, 0xdb, 0x4e, 0x72, 0x47, 0xc2, 0x7a, 0xe5, 0x21, 0x4e, 0xd6, 0xff, + 0x46, 0xe5, 0x5d, 0x04, 0x10, 0xe9, 0xb0, 0x52, 0x0d, 0x79, 0x37, 0xeb, 0x91, 0x56, 0x37, 0x6b, 0x36, 0x91, 0xc7, + 0x49, 0x3a, 0xc8, 0x74, 0x72, 0x9e, 0xc7, 0xfa, 0x5c, 0x1a, 0xdb, 0x39, 0x0a, 0x38, 0x9c, 0x34, 0x5d, 0x2e, 0xab, + 0x30, 0x00, 0x93, 0xa7, 0x35, 0xfe, 0x26, 0x74, 0x05, 0x9c, 0x5b, 0x9c, 0x9c, 0x9b, 0xab, 0x5d, 0x52, 0xc3, 0x2b, + 0x12, 0x3e, 0x94, 0x98, 0xf3, 0xa7, 0xa1, 0x88, 0xc1, 0x4b, 0x51, 0x8a, 0x9f, 0x2a, 0x85, 0xc9, 0xdd, 0x77, 0x51, + 0x3f, 0x2d, 0xf3, 0xdb, 0x20, 0x8f, 0x2f, 0x2d, 0xa0, 0x97, 0xef, 0x05, 0x81, 0x1e, 0xf1, 0x57, 0x44, 0xd9, 0x74, + 0xc6, 0xa2, 0x1b, 0x3d, 0xd4, 0xa2, 0xa3, 0xa9, 0x60, 0x32, 0x73, 0xdb, 0x44, 0x1c, 0xe2, 0x30, 0xbf, 0x1c, 0xaa, + 0xa3, 0x92, 0x79, 0x75, 0x30, 0x20, 0x94, 0xd0, 0x2b, 0x23, 0x8d, 0x66, 0xd2, 0x1e, 0xfd, 0xab, 0xd8, 0x6a, 0x9f, + 0xa4, 0xf7, 0xd9, 0x27, 0xe5, 0xc4, 0x73, 0x3e, 0xcb, 0x86, 0x10, 0x8e, 0xd4, 0x52, 0x6f, 0xdd, 0x71, 0xe3, 0x4a, + 0x15, 0xc3, 0xc5, 0xc2, 0xca, 0x03, 0x15, 0x98, 0xd9, 0xd7, 0x4a, 0x50, 0x19, 0xf2, 0x52, 0xc7, 0x35, 0xb4, 0x88, + 0x33, 0x53, 0x02, 0x99, 0x1d, 0xc9, 0x94, 0x46, 0x2f, 0x23, 0xbd, 0xcc, 0x9f, 0xa5, 0xec, 0xe7, 0x19, 0xbd, 0x64, + 0xa0, 0x6b, 0x32, 0x9f, 0x45, 0x32, 0xd6, 0x04, 0xb2, 0xaf, 0xd9, 0x86, 0xe0, 0x05, 0x8b, 0xd4, 0xc2, 0x64, 0xf2, + 0xa5, 0xce, 0x6d, 0x72, 0x9b, 0x2e, 0xf8, 0x8b, 0x41, 0x3b, 0x60, 0x38, 0xe2, 0x93, 0x90, 0xa5, 0x81, 0x74, 0xf9, + 0x96, 0x9d, 0x05, 0x50, 0x1b, 0xb3, 0x28, 0xc8, 0xf4, 0xf2, 0xb4, 0x91, 0xff, 0x13, 0x67, 0xa9, 0x6c, 0x5a, 0x74, + 0xb9, 0x44, 0xa8, 0x42, 0x1f, 0x31, 0x08, 0x3e, 0x55, 0x72, 0x8d, 0x23, 0x6c, 0xbf, 0x2e, 0x4f, 0x9d, 0xd7, 0x56, + 0xa0, 0xb5, 0xb2, 0x50, 0xca, 0x08, 0xe0, 0xab, 0xa5, 0x39, 0xcf, 0x84, 0xe7, 0xc5, 0x38, 0x41, 0xa4, 0x17, 0x4b, + 0x67, 0xd7, 0x49, 0x22, 0xff, 0xeb, 0x37, 0xdb, 0x41, 0xbb, 0x34, 0xdf, 0x6b, 0x87, 0x81, 0x55, 0x72, 0x94, 0x3e, + 0x50, 0x2a, 0xa7, 0x51, 0xfe, 0x56, 0x53, 0xad, 0x9e, 0xcb, 0xe9, 0x62, 0xbd, 0xdd, 0x94, 0xa8, 0xf2, 0x6a, 0x40, + 0xc8, 0x60, 0xd1, 0x96, 0xa1, 0x50, 0x51, 0xcd, 0xbb, 0x54, 0x25, 0xaf, 0x94, 0x88, 0xbe, 0xdc, 0x5d, 0xa4, 0x7a, + 0xc4, 0xe2, 0x8a, 0x19, 0x27, 0x53, 0x9d, 0xe4, 0x0a, 0x8d, 0x11, 0x4b, 0x0f, 0xdd, 0x54, 0x4d, 0xc1, 0x72, 0x47, + 0xd2, 0x8d, 0x74, 0xeb, 0xab, 0x47, 0xaa, 0x14, 0x84, 0xcd, 0x55, 0x64, 0xaa, 0xde, 0x26, 0xc0, 0xc0, 0x6c, 0xcd, + 0x85, 0x99, 0x02, 0x68, 0x63, 0x23, 0x0a, 0xe7, 0x68, 0xae, 0x76, 0x17, 0xdf, 0x8b, 0x62, 0xdf, 0xaa, 0x2a, 0x7f, + 0xb3, 0x08, 0xfe, 0x07, 0x09, 0xb8, 0x50, 0x4a, 0x69, 0xe0, 0xbe, 0x7d, 0x73, 0xfe, 0xde, 0xc5, 0x70, 0x3b, 0x17, + 0xcd, 0xf2, 0x60, 0xe1, 0xea, 0xd4, 0xb8, 0x26, 0x84, 0x59, 0xdd, 0xc0, 0x0d, 0xa7, 0x70, 0xd2, 0x58, 0xf2, 0x82, + 0xfd, 0xdb, 0xe6, 0xcd, 0xcd, 0x4d, 0x13, 0x0e, 0x42, 0x35, 0x67, 0x59, 0x42, 0xd3, 0x21, 0x8f, 0x68, 0xe4, 0x16, + 0x05, 0xf2, 0x45, 0x4c, 0xd3, 0xf2, 0xfe, 0x1e, 0x9e, 0x50, 0x3f, 0xe1, 0x63, 0x75, 0x88, 0x73, 0xd5, 0xaa, 0x1e, + 0x5e, 0x9d, 0xc8, 0x7b, 0xa9, 0x7a, 0x27, 0x42, 0xdd, 0x08, 0x26, 0x32, 0xf8, 0xd9, 0x83, 0x98, 0xcb, 0xc9, 0xbe, + 0x88, 0xe5, 0xc3, 0x39, 0xec, 0x30, 0xf9, 0xb4, 0xbb, 0x58, 0xa3, 0xbe, 0x3e, 0x74, 0x11, 0xf7, 0xd4, 0x9c, 0x73, + 0x59, 0xeb, 0x2a, 0x18, 0x5e, 0x5d, 0x15, 0x27, 0xfb, 0xd0, 0xd7, 0xbe, 0xe9, 0xf7, 0x9a, 0x47, 0x77, 0xa6, 0x7d, + 0x49, 0x91, 0x70, 0x3f, 0x51, 0x4a, 0x7a, 0xd0, 0x05, 0x8c, 0x1b, 0xf5, 0x00, 0x2b, 0x40, 0x91, 0xd0, 0x3a, 0x2a, + 0x4b, 0xe4, 0x16, 0x57, 0x45, 0xdb, 0x20, 0x50, 0x15, 0xab, 0x8d, 0xa2, 0xdc, 0xaf, 0x15, 0x41, 0x18, 0x90, 0x22, + 0x1b, 0xba, 0x2b, 0x04, 0xff, 0x4b, 0xc8, 0x4e, 0xf6, 0x15, 0x1e, 0xae, 0xec, 0xcb, 0x50, 0xd4, 0x35, 0x05, 0x25, + 0xb6, 0x06, 0xa9, 0xc0, 0x6f, 0x04, 0x7e, 0x73, 0x25, 0xab, 0x1a, 0xe9, 0x05, 0x6a, 0x15, 0x48, 0xf9, 0x96, 0x51, + 0x53, 0x86, 0x3c, 0x49, 0xc2, 0x69, 0x4e, 0x03, 0xf3, 0x43, 0x0b, 0x32, 0x90, 0x87, 0xeb, 0x9a, 0x83, 0xce, 0xc7, + 0x39, 0x03, 0xfd, 0x62, 0x5d, 0xad, 0x99, 0x87, 0x99, 0xd7, 0x6c, 0x0e, 0x9b, 0xd7, 0x63, 0x54, 0x88, 0x78, 0x61, + 0x8b, 0xc1, 0x47, 0xad, 0x56, 0x17, 0x92, 0x27, 0x9b, 0x61, 0xc2, 0xc6, 0x69, 0x90, 0xd0, 0x91, 0x28, 0x04, 0x9c, + 0x6a, 0x5b, 0x18, 0xbd, 0xc3, 0xef, 0x1c, 0x65, 0x74, 0xe2, 0xf8, 0xf0, 0xef, 0xfd, 0x03, 0x17, 0x22, 0x0a, 0x52, + 0x11, 0x37, 0x65, 0x92, 0x2e, 0x1c, 0x31, 0x10, 0x71, 0xed, 0x79, 0x61, 0x0d, 0x34, 0xa4, 0xa0, 0x93, 0x15, 0x22, + 0x73, 0x44, 0x8c, 0x45, 0x66, 0xd7, 0x4b, 0xd1, 0x62, 0x6d, 0x06, 0xeb, 0xaa, 0xc1, 0x01, 0x2a, 0x72, 0xa9, 0x49, + 0xaf, 0x57, 0x36, 0xfa, 0x55, 0xfd, 0x69, 0x0d, 0x7d, 0x96, 0x26, 0x58, 0x28, 0x4f, 0xf4, 0x42, 0xb5, 0x78, 0x08, + 0x32, 0x6b, 0x3a, 0x2a, 0xb6, 0x5b, 0xa0, 0x82, 0xa5, 0xd3, 0x99, 0x18, 0x48, 0x2f, 0x78, 0x06, 0xe7, 0x29, 0x2e, + 0xb0, 0x55, 0x02, 0x38, 0xb8, 0x58, 0x28, 0x60, 0x86, 0x61, 0x32, 0xf4, 0x00, 0x22, 0xa7, 0xe9, 0x1c, 0x67, 0x74, + 0x82, 0xba, 0x13, 0x96, 0x36, 0xd5, 0xbb, 0x23, 0x4b, 0x8f, 0xf1, 0x1f, 0xc3, 0x53, 0xe1, 0xcb, 0xde, 0xb0, 0x4c, + 0x76, 0xdd, 0x80, 0xcb, 0xab, 0x8b, 0xa2, 0xe8, 0x66, 0xc2, 0x1b, 0xbc, 0xf2, 0xd0, 0x05, 0xfe, 0xca, 0xba, 0xce, + 0xc5, 0x35, 0x5b, 0xc5, 0xc5, 0x1d, 0xb4, 0xa5, 0x8a, 0xbd, 0x17, 0x64, 0xb5, 0xaf, 0x08, 0x54, 0x7c, 0xea, 0xb9, + 0x34, 0x9f, 0x36, 0x15, 0xb3, 0x6b, 0x4a, 0x92, 0x75, 0xa1, 0x29, 0xd2, 0xae, 0xdd, 0xbf, 0x8a, 0x85, 0xe4, 0x63, + 0xfa, 0x4c, 0x87, 0xf2, 0x3e, 0x5c, 0x94, 0x67, 0x80, 0xf4, 0xb3, 0x7d, 0xea, 0x07, 0xd5, 0xf8, 0xc9, 0xd5, 0x69, + 0x9d, 0x29, 0x02, 0x23, 0x2b, 0xef, 0xbc, 0x0b, 0x93, 0x04, 0x06, 0xbc, 0x32, 0xfa, 0x8e, 0x7d, 0x49, 0xc8, 0x40, + 0x5c, 0x78, 0xa8, 0xd0, 0xfb, 0xf4, 0xa9, 0xd4, 0x41, 0xad, 0x8b, 0xf6, 0x76, 0x84, 0x89, 0x2e, 0x29, 0x71, 0xcd, + 0x20, 0x3e, 0x5e, 0xcb, 0xa3, 0xee, 0x56, 0xbc, 0x4b, 0x69, 0xb0, 0x8e, 0x9c, 0x10, 0x71, 0xb3, 0x34, 0x72, 0x9d, + 0xbf, 0x0c, 0x13, 0x36, 0xfc, 0x48, 0xdc, 0xdd, 0x85, 0x87, 0xd6, 0x8f, 0x49, 0x4a, 0xae, 0x60, 0x38, 0x3c, 0xaa, + 0x7b, 0xde, 0x33, 0xdf, 0x62, 0xde, 0xea, 0x1e, 0x1d, 0xb7, 0xb7, 0xbb, 0x00, 0xc6, 0xa3, 0xc6, 0xe9, 0x5d, 0x15, + 0x97, 0xd5, 0xf5, 0x58, 0x15, 0x14, 0x80, 0x66, 0x55, 0xee, 0x48, 0xa2, 0x22, 0xee, 0x27, 0x29, 0xcd, 0x75, 0x14, + 0x53, 0x03, 0x38, 0x85, 0xe6, 0x6f, 0xae, 0xf3, 0x97, 0xb2, 0x8c, 0x96, 0x2e, 0x10, 0x99, 0xc3, 0x41, 0x5c, 0x18, + 0x0b, 0xec, 0x5e, 0x3f, 0xa2, 0x22, 0x64, 0x89, 0x6a, 0xd2, 0x35, 0x16, 0xfb, 0xca, 0x8c, 0x96, 0xcb, 0xbc, 0x3e, + 0x17, 0x56, 0xc7, 0xa0, 0x9c, 0xd9, 0xc9, 0x7e, 0x05, 0xb7, 0x9c, 0x99, 0xdc, 0x93, 0x76, 0x2c, 0xb1, 0x9a, 0xa1, + 0x7a, 0xe7, 0xfc, 0x65, 0x28, 0x4f, 0x19, 0x01, 0x80, 0x5c, 0x03, 0x08, 0x51, 0x6e, 0x75, 0x8a, 0xc6, 0x4b, 0x08, + 0xf7, 0x45, 0x98, 0x8d, 0xa9, 0x58, 0x41, 0x6c, 0xa2, 0x92, 0x5a, 0xbb, 0x26, 0xa2, 0xbd, 0x06, 0x6d, 0x58, 0x87, + 0xf6, 0x0a, 0x90, 0xde, 0xdf, 0x5d, 0xb0, 0x82, 0xec, 0x2e, 0x94, 0x5c, 0xfb, 0xf0, 0xee, 0x2b, 0x38, 0x14, 0xc9, + 0x53, 0xb0, 0x44, 0x62, 0x04, 0x92, 0x56, 0x2e, 0x8e, 0x12, 0x21, 0x5c, 0x8a, 0x10, 0xc5, 0x09, 0x1c, 0x39, 0x96, + 0x04, 0xb1, 0x70, 0x9d, 0xbe, 0x82, 0x9c, 0x46, 0x0a, 0x66, 0x92, 0xc9, 0x56, 0xbc, 0x38, 0xd9, 0x57, 0xb5, 0x95, + 0x08, 0x50, 0x95, 0x00, 0x09, 0x72, 0x9f, 0x56, 0x38, 0x80, 0x44, 0x68, 0x1b, 0x0f, 0x11, 0x9b, 0x97, 0xc4, 0x26, + 0xcf, 0x5b, 0xf5, 0x4e, 0x92, 0xf0, 0x9a, 0x26, 0xbd, 0xdd, 0x45, 0xb6, 0x5c, 0xb6, 0x8a, 0x93, 0x7d, 0xf5, 0xe8, + 0x9c, 0x48, 0xbe, 0xa1, 0xee, 0xc8, 0x94, 0x4b, 0x0c, 0x87, 0x18, 0x21, 0x3d, 0xd4, 0xe4, 0x45, 0x05, 0xba, 0x83, + 0xc2, 0x75, 0x64, 0x46, 0x86, 0xac, 0x54, 0x6a, 0x50, 0x85, 0xeb, 0xb0, 0x68, 0xbd, 0x2c, 0x17, 0x74, 0x0a, 0xa5, + 0xf1, 0x72, 0xd9, 0x2e, 0x5c, 0x67, 0xc2, 0x52, 0x78, 0xca, 0x96, 0x4b, 0x79, 0x3e, 0x70, 0xc2, 0x52, 0xaf, 0x05, + 0x64, 0xeb, 0x3a, 0x93, 0xf0, 0x56, 0x4e, 0xd8, 0xbc, 0x09, 0x6f, 0xbd, 0xb6, 0x7e, 0xe5, 0x97, 0xf8, 0xc9, 0x81, + 0xe2, 0xaa, 0x15, 0x4d, 0xf4, 0x8a, 0x46, 0x78, 0xa6, 0x4e, 0x3e, 0x11, 0x2f, 0x22, 0xc9, 0xe6, 0x15, 0x8d, 0xcc, + 0x8a, 0xce, 0xb6, 0xac, 0xe8, 0xec, 0x9e, 0x15, 0x0d, 0xf5, 0xea, 0x39, 0x25, 0xee, 0xf8, 0x72, 0xd9, 0x6e, 0x55, + 0xd8, 0x3b, 0xd9, 0x8f, 0xd8, 0x1c, 0x56, 0x03, 0xf4, 0x42, 0xc1, 0x26, 0x74, 0x33, 0x51, 0xd6, 0x51, 0x4c, 0x7f, + 0x15, 0x26, 0x2b, 0x2c, 0x64, 0x75, 0x2c, 0xd8, 0x74, 0x5d, 0x06, 0xe9, 0xfe, 0x48, 0xca, 0x66, 0x80, 0x87, 0x1c, + 0xf0, 0x10, 0x9b, 0x3b, 0x33, 0x3d, 0xf7, 0xbd, 0x8b, 0x5d, 0xc7, 0x35, 0x64, 0x7d, 0x55, 0x5c, 0x82, 0x8c, 0x90, + 0xf3, 0x7b, 0x10, 0x2d, 0x42, 0x6d, 0xb7, 0xb7, 0x9d, 0xe6, 0x20, 0x9e, 0x7e, 0xc3, 0xb3, 0xc8, 0x0d, 0x54, 0xd5, + 0x5f, 0x85, 0xaa, 0x09, 0x4b, 0x75, 0x76, 0xd6, 0x56, 0x5a, 0xab, 0xde, 0xdb, 0x14, 0xd7, 0x39, 0x3a, 0x52, 0x35, + 0xa6, 0xa1, 0x10, 0x34, 0x4b, 0x35, 0xe5, 0xba, 0xee, 0xff, 0x17, 0x54, 0xb8, 0x81, 0xaf, 0x84, 0x66, 0x01, 0x0c, + 0x01, 0x6a, 0x0d, 0x5f, 0xf3, 0x7c, 0x25, 0x9e, 0x76, 0x2a, 0x0d, 0xf6, 0x0e, 0xd9, 0x56, 0x86, 0x2a, 0x02, 0xa3, + 0x67, 0x36, 0xa1, 0xd1, 0xa5, 0x64, 0xd0, 0xfd, 0xe1, 0x95, 0x56, 0x58, 0x57, 0xc4, 0x5d, 0xd5, 0x00, 0xbb, 0x3f, + 0xce, 0x3a, 0x8f, 0x0f, 0xcf, 0x5c, 0xac, 0x78, 0x3c, 0x1f, 0x8d, 0x5c, 0x54, 0x38, 0x0f, 0x6b, 0xd6, 0x3e, 0xfc, + 0x71, 0xf6, 0xe5, 0xf3, 0xd6, 0x97, 0x65, 0xe3, 0x14, 0x88, 0x48, 0x27, 0x04, 0x18, 0x51, 0x65, 0xc1, 0x6b, 0x66, + 0x34, 0x0a, 0xd3, 0xed, 0xd3, 0x19, 0xd8, 0xd3, 0xc9, 0xa7, 0x94, 0x46, 0x40, 0x9c, 0x78, 0xad, 0xf4, 0x32, 0xa1, + 0x73, 0x6a, 0xee, 0x2a, 0xdc, 0x30, 0xd8, 0x86, 0x16, 0x43, 0x3e, 0x4b, 0x85, 0xce, 0x8c, 0xd0, 0xac, 0xd6, 0x9a, + 0xd2, 0x95, 0x9c, 0x83, 0x6d, 0x23, 0xdc, 0x29, 0x39, 0x57, 0x97, 0x5e, 0xc5, 0x15, 0x76, 0x2d, 0x00, 0xb6, 0x42, + 0xd6, 0xdf, 0x52, 0x1e, 0xb4, 0x70, 0x6b, 0x1b, 0x6c, 0xb8, 0x8d, 0x02, 0xd7, 0xbd, 0x30, 0x78, 0x92, 0xce, 0xcd, + 0xda, 0x05, 0x13, 0x5b, 0xf1, 0xf5, 0x49, 0x0c, 0x5c, 0x67, 0xd0, 0x59, 0x4a, 0xf3, 0x7c, 0x2b, 0x02, 0xca, 0x45, + 0xc4, 0x6e, 0x55, 0xdb, 0xdd, 0xd2, 0x0b, 0x6e, 0x61, 0xd8, 0x61, 0x12, 0xe0, 0x32, 0xc4, 0xaa, 0x6b, 0xd1, 0xd1, + 0x88, 0x0e, 0x4b, 0xdf, 0x30, 0x04, 0xcb, 0x46, 0x2c, 0x11, 0x10, 0x33, 0x92, 0xc1, 0x1c, 0xf7, 0x35, 0x4f, 0xa9, + 0x8b, 0x4c, 0xfa, 0xa7, 0x86, 0x5f, 0xcb, 0xff, 0xcd, 0xf0, 0xa8, 0x1e, 0xeb, 0xb0, 0xe8, 0x51, 0x96, 0x4b, 0xe3, + 0x17, 0xaa, 0x95, 0xd7, 0x11, 0xc9, 0xa5, 0xe3, 0x67, 0xdb, 0x06, 0x7a, 0xd8, 0x36, 0x59, 0xb4, 0xbf, 0x3c, 0x6a, + 0xb7, 0x0a, 0x17, 0xbb, 0xd0, 0xdd, 0x43, 0x77, 0x89, 0x6c, 0x75, 0x00, 0xad, 0x66, 0xe9, 0xaf, 0x69, 0xd7, 0x69, + 0x3f, 0x69, 0xbb, 0x58, 0xdd, 0x3b, 0x80, 0x8a, 0x92, 0x19, 0x0c, 0xc1, 0x5b, 0xfa, 0xbb, 0xa7, 0x52, 0xef, 0xfc, + 0x61, 0xf0, 0x3c, 0x6a, 0xb7, 0x5c, 0xec, 0xe6, 0x82, 0x4f, 0x7f, 0xc5, 0x14, 0x0e, 0x5c, 0xec, 0x0e, 0x13, 0x9e, + 0x53, 0x7b, 0x0e, 0x4a, 0x9d, 0xfd, 0xfd, 0x93, 0x50, 0x10, 0x4d, 0x33, 0x9a, 0xe7, 0x8e, 0xdd, 0xbf, 0x26, 0xa5, + 0x4f, 0x30, 0xcc, 0x8d, 0x14, 0x97, 0x53, 0x21, 0xf1, 0xa2, 0xae, 0x04, 0xb0, 0xa9, 0x4a, 0x95, 0xad, 0x11, 0x9b, + 0x14, 0x01, 0x25, 0x63, 0x53, 0xda, 0xd5, 0x27, 0x47, 0xde, 0xb0, 0xf5, 0xd4, 0xc0, 0x2a, 0x88, 0xbc, 0x3e, 0x40, + 0xad, 0x64, 0xc2, 0xd2, 0xcb, 0x0d, 0xa5, 0xe1, 0xed, 0x86, 0x52, 0x50, 0xd9, 0x4a, 0xe8, 0xf4, 0x75, 0x35, 0x9f, + 0xc6, 0x7a, 0xa5, 0xf8, 0xd8, 0x20, 0x46, 0xd2, 0xd1, 0xf9, 0x09, 0x48, 0xad, 0x65, 0x90, 0x3d, 0xfc, 0xf6, 0xe1, + 0xa0, 0xe4, 0xd7, 0x0c, 0x57, 0xf6, 0xf2, 0xfb, 0x66, 0x08, 0xa5, 0x4d, 0x70, 0x78, 0x27, 0xbf, 0x6a, 0xae, 0xf4, + 0xf6, 0xd3, 0x04, 0x67, 0x69, 0x55, 0xbf, 0x63, 0xe9, 0xf5, 0xb1, 0xf7, 0xd5, 0xb5, 0xdf, 0x50, 0xac, 0x15, 0x9f, + 0x72, 0xfd, 0x87, 0x09, 0x9b, 0x54, 0x24, 0xb0, 0x0e, 0xa6, 0xd4, 0x78, 0x20, 0xfb, 0xc9, 0xee, 0x44, 0xa9, 0x3e, + 0x97, 0x70, 0xa6, 0x13, 0xae, 0xcd, 0x98, 0x65, 0xf4, 0x32, 0xe1, 0x37, 0xab, 0xf7, 0x80, 0x6d, 0xaf, 0x1c, 0xb3, + 0x71, 0x6c, 0x1d, 0xd4, 0xa2, 0xa4, 0x5c, 0x84, 0x7b, 0x07, 0x28, 0xfe, 0xe5, 0x9f, 0x7d, 0xff, 0x5f, 0xfe, 0xf9, + 0x93, 0x55, 0xa1, 0xfb, 0xe2, 0x0a, 0x8b, 0xaa, 0xdb, 0xed, 0xbb, 0x6b, 0xf3, 0x48, 0x75, 0x9c, 0x6f, 0xae, 0xb3, + 0xb6, 0x08, 0xf0, 0x7e, 0x6d, 0x09, 0xd6, 0x0a, 0xd5, 0xee, 0x73, 0x7e, 0x0b, 0x60, 0x30, 0xaf, 0x4f, 0x42, 0x06, + 0x95, 0x7e, 0x17, 0x68, 0x57, 0x28, 0x78, 0xd0, 0x8a, 0xfc, 0x76, 0x0c, 0x7f, 0x6a, 0x0e, 0xbf, 0x13, 0x7c, 0xed, + 0x9f, 0x18, 0x5e, 0x5d, 0x95, 0x19, 0x79, 0x76, 0x53, 0x38, 0xef, 0xdf, 0x5f, 0x2b, 0xd1, 0x8a, 0x47, 0xd0, 0x42, + 0x3d, 0x79, 0x9e, 0x90, 0x0c, 0xaf, 0x5e, 0xc1, 0x25, 0x3f, 0x27, 0xd7, 0x99, 0x71, 0xf0, 0xde, 0x23, 0x1c, 0xa0, + 0x8b, 0xfa, 0xac, 0x64, 0xa7, 0x6b, 0x92, 0x01, 0x4a, 0xc1, 0xdc, 0x00, 0x30, 0xf1, 0xf0, 0x4a, 0x5b, 0x9b, 0x67, + 0xca, 0x0d, 0x13, 0xac, 0x92, 0xb6, 0x76, 0xcf, 0xd4, 0x90, 0x8e, 0x9d, 0xf7, 0x12, 0x5f, 0xb2, 0x32, 0xad, 0xac, + 0x7b, 0xe9, 0xea, 0x02, 0x3b, 0xa2, 0x64, 0x3f, 0xf3, 0x30, 0x99, 0x3f, 0x8c, 0xf1, 0x6d, 0x17, 0xa8, 0x4b, 0x67, + 0xf9, 0x6f, 0xad, 0x12, 0x2c, 0x9b, 0xcb, 0x9a, 0x3e, 0x20, 0xb3, 0x12, 0xfe, 0xbe, 0x2d, 0x70, 0x2a, 0xe8, 0x27, + 0x03, 0xa7, 0xc9, 0x83, 0x02, 0xa7, 0xea, 0x86, 0xbe, 0x3f, 0x32, 0x70, 0xfa, 0x77, 0x3b, 0x70, 0x0a, 0x24, 0xf8, + 0xf3, 0x83, 0x82, 0x9b, 0x26, 0xf0, 0xc4, 0x6f, 0x72, 0xd2, 0xd6, 0x46, 0x40, 0xc2, 0xc7, 0x10, 0xd9, 0xfc, 0xb7, + 0x0f, 0x54, 0x26, 0x7c, 0x6c, 0x87, 0x29, 0xe1, 0x8e, 0x5a, 0x88, 0x4b, 0xe2, 0x8c, 0x2c, 0xdc, 0x1f, 0x6f, 0xdb, + 0x4f, 0x07, 0xed, 0xee, 0x41, 0x7b, 0xe2, 0x06, 0x2e, 0x48, 0x5d, 0x59, 0xd0, 0xea, 0x1e, 0x1c, 0x40, 0xc1, 0x8d, + 0x55, 0xd0, 0x81, 0x02, 0x66, 0x15, 0x1c, 0x41, 0xc1, 0xd0, 0x2a, 0x78, 0x04, 0x05, 0x91, 0x55, 0xf0, 0x18, 0x0a, + 0xe6, 0x6e, 0x31, 0x60, 0x65, 0x74, 0xf8, 0x31, 0x92, 0xd7, 0x59, 0xec, 0x64, 0xf5, 0x54, 0xfe, 0x98, 0x98, 0x2a, + 0x8f, 0xcb, 0x63, 0x40, 0xcd, 0x43, 0x73, 0x6b, 0xc5, 0xd5, 0x67, 0x57, 0x08, 0x27, 0x04, 0x4e, 0xe5, 0x61, 0x30, + 0xca, 0x55, 0xcd, 0x03, 0xf3, 0xda, 0x0d, 0xca, 0x7b, 0xa9, 0x5a, 0xb8, 0x63, 0x22, 0x9c, 0x81, 0x8b, 0xf0, 0xac, + 0xac, 0x7c, 0xd4, 0x88, 0x74, 0xb7, 0x70, 0x21, 0x44, 0x75, 0x1b, 0xcb, 0x01, 0xc2, 0xea, 0x02, 0xec, 0x67, 0x52, + 0x3e, 0xfa, 0x82, 0xbf, 0x67, 0x13, 0x6a, 0x3e, 0x0f, 0x62, 0x06, 0x70, 0x5c, 0x04, 0x07, 0xb8, 0xe3, 0xea, 0x0a, + 0xb3, 0x2f, 0xf1, 0x69, 0x75, 0x01, 0xd0, 0x5b, 0x41, 0xd4, 0x8d, 0x0a, 0x19, 0x56, 0x86, 0xde, 0x18, 0x8b, 0x70, + 0x1c, 0x40, 0xc8, 0x12, 0x7c, 0xa6, 0xc1, 0x29, 0x21, 0xa4, 0xd5, 0x9f, 0x05, 0x5f, 0xe2, 0x9b, 0x98, 0xa6, 0xc1, + 0xbc, 0xe8, 0x96, 0x04, 0xa0, 0x22, 0xa6, 0x6f, 0x45, 0x79, 0x6f, 0x9c, 0xa4, 0x8a, 0xea, 0xb5, 0x82, 0xb3, 0x59, + 0x52, 0xcf, 0x96, 0x58, 0x9a, 0xe5, 0x93, 0x19, 0x25, 0xfc, 0xa6, 0x79, 0xeb, 0xf6, 0x36, 0xc7, 0xd7, 0x60, 0x76, + 0x65, 0x7c, 0x4d, 0x02, 0x5b, 0x3e, 0xbd, 0x0f, 0xc7, 0xe5, 0xef, 0x57, 0x34, 0xcf, 0xc3, 0xb1, 0xae, 0xb9, 0x3d, + 0x9e, 0x26, 0x41, 0xb4, 0x63, 0x69, 0x06, 0x08, 0x88, 0x89, 0x01, 0x46, 0xc0, 0xa7, 0xa1, 0x43, 0x64, 0x30, 0xf5, + 0x7a, 0x74, 0x4d, 0x0e, 0x5f, 0x2f, 0x12, 0xe1, 0xb8, 0x2a, 0x38, 0x99, 0x66, 0x54, 0x96, 0x2a, 0x34, 0x16, 0x27, + 0xfb, 0x50, 0xa0, 0x5e, 0x6f, 0x89, 0xa2, 0x19, 0x07, 0xca, 0xf6, 0x58, 0x9a, 0x63, 0xa2, 0x68, 0x76, 0xa2, 0x52, + 0x99, 0xa5, 0xb4, 0x1e, 0xbb, 0xf9, 0xbc, 0x3d, 0x84, 0x3f, 0x3a, 0x32, 0xf4, 0xf9, 0x68, 0x34, 0xba, 0x37, 0xaa, + 0xf6, 0x79, 0x34, 0xa2, 0x1d, 0x7a, 0xd4, 0x85, 0x24, 0x96, 0xa6, 0x8e, 0xc5, 0xb4, 0x0b, 0x89, 0xbb, 0xc5, 0xc3, + 0x2a, 0x43, 0xd8, 0x46, 0xc4, 0x8b, 0x87, 0x47, 0xd8, 0x8a, 0x69, 0x46, 0x17, 0x93, 0x30, 0x1b, 0xb3, 0x34, 0x68, + 0x15, 0xfe, 0x5c, 0x87, 0xa4, 0x3e, 0x3f, 0x3e, 0x3e, 0x2e, 0xfc, 0xc8, 0x3c, 0xb5, 0xa2, 0xa8, 0xf0, 0x87, 0x8b, + 0x72, 0x1a, 0xad, 0xd6, 0x68, 0x54, 0xf8, 0xcc, 0x14, 0x1c, 0x74, 0x86, 0xd1, 0x41, 0xa7, 0xf0, 0x6f, 0xac, 0x1a, + 0x85, 0x4f, 0xf5, 0x53, 0x46, 0xa3, 0x5a, 0x26, 0xcc, 0xe3, 0x56, 0xab, 0xf0, 0x15, 0xa1, 0x2d, 0xc0, 0x2c, 0x55, + 0x3f, 0x83, 0x70, 0x26, 0x38, 0x30, 0xf7, 0x6e, 0x22, 0xbc, 0xc1, 0xa5, 0xbe, 0x65, 0x44, 0x7d, 0x93, 0xa3, 0x40, + 0x17, 0xf8, 0x67, 0x3b, 0x78, 0x04, 0xc4, 0x2c, 0x83, 0x46, 0x89, 0x89, 0x2d, 0xd5, 0x5e, 0x03, 0x65, 0xc9, 0xd7, + 0x3f, 0x93, 0xa4, 0x8a, 0x29, 0x01, 0x27, 0x83, 0x9a, 0xea, 0x32, 0x3c, 0x4a, 0xb7, 0xc8, 0x0f, 0xf6, 0x69, 0xf9, + 0x71, 0xf7, 0x10, 0xf1, 0xc1, 0xfe, 0x70, 0xf1, 0x41, 0xa9, 0x25, 0x3e, 0x14, 0xf3, 0xb8, 0x13, 0xc4, 0x1d, 0xc6, + 0x74, 0xf8, 0xf1, 0x9a, 0xdf, 0x36, 0x61, 0x4b, 0x64, 0xae, 0x14, 0x2c, 0xbb, 0xbf, 0x35, 0x6b, 0xc6, 0x74, 0x66, + 0x7d, 0xd1, 0x43, 0xaa, 0x0f, 0x6f, 0x52, 0xe2, 0xbe, 0x31, 0xb6, 0xad, 0x2a, 0x19, 0x8d, 0x88, 0xfb, 0x66, 0x34, + 0x72, 0xcd, 0x59, 0xc9, 0x50, 0x50, 0x59, 0xeb, 0x75, 0xad, 0x44, 0xd6, 0xfa, 0xf2, 0x4b, 0xbb, 0xcc, 0x2e, 0xd0, + 0xa1, 0x27, 0x3b, 0xcc, 0xa4, 0xdf, 0x44, 0x2c, 0x87, 0xad, 0x06, 0x1f, 0x1a, 0xa9, 0xdf, 0xd5, 0x98, 0xd6, 0xae, + 0xd5, 0x2e, 0x01, 0xde, 0x70, 0x17, 0xf8, 0xea, 0x45, 0x01, 0x63, 0x6a, 0xf2, 0x16, 0x9f, 0xde, 0x7d, 0x15, 0x79, + 0x77, 0x02, 0x15, 0x2c, 0x7f, 0x93, 0xae, 0x1c, 0x02, 0x52, 0x30, 0x12, 0x62, 0x4f, 0xab, 0x10, 0x7c, 0x3c, 0x4e, + 0xe0, 0x5b, 0x2f, 0x8b, 0xda, 0xfd, 0xb1, 0xaa, 0x79, 0xbf, 0x36, 0xdf, 0xc0, 0x6e, 0xa8, 0x6f, 0x5b, 0x95, 0x9f, + 0x9e, 0x52, 0xc9, 0xe3, 0x73, 0xfd, 0x0d, 0x22, 0x69, 0x16, 0x2f, 0x34, 0x93, 0x5f, 0xa8, 0x94, 0x63, 0x01, 0xe9, + 0x36, 0xaa, 0xe3, 0xa8, 0x28, 0xf4, 0x61, 0x8d, 0x88, 0xe5, 0x53, 0xb8, 0xd7, 0x54, 0xb5, 0xa4, 0x9f, 0x62, 0xe1, + 0xf9, 0x8d, 0x15, 0xdf, 0xa9, 0x2d, 0x57, 0x61, 0x02, 0x3c, 0xca, 0x61, 0x7e, 0x27, 0x0a, 0x57, 0xfb, 0xdd, 0x0d, + 0x12, 0x5d, 0x47, 0xe1, 0x53, 0x45, 0x9e, 0xac, 0x19, 0x82, 0xf3, 0xbb, 0x5c, 0x10, 0xf3, 0xca, 0x14, 0x14, 0x76, + 0xfc, 0x52, 0xbe, 0x51, 0xd8, 0x92, 0xd1, 0x92, 0x7c, 0x1a, 0xa6, 0x8a, 0x8d, 0x12, 0x57, 0xf1, 0x83, 0xdd, 0x45, + 0xb5, 0xf2, 0x85, 0x6b, 0xc0, 0x56, 0xc4, 0xdb, 0x3b, 0xd9, 0x87, 0x06, 0x3d, 0xa7, 0x06, 0x7a, 0xba, 0x16, 0x64, + 0xf9, 0x44, 0xba, 0xc3, 0x95, 0x9f, 0xdf, 0x60, 0x3f, 0xbf, 0x71, 0xfe, 0xbc, 0x68, 0xde, 0xd0, 0xeb, 0x8f, 0x4c, + 0x34, 0x45, 0x38, 0x6d, 0x82, 0xe1, 0x23, 0x9d, 0xa3, 0x9a, 0x3d, 0xcb, 0x2c, 0x3f, 0x75, 0xd5, 0x41, 0x77, 0x96, + 0x43, 0x56, 0x84, 0x54, 0xdf, 0x83, 0x94, 0xa7, 0xb4, 0x5b, 0xcf, 0xe6, 0xb4, 0x83, 0xec, 0x06, 0x5b, 0x17, 0x0b, + 0x0e, 0x59, 0x14, 0xe2, 0x2e, 0x68, 0x69, 0xb6, 0xde, 0x32, 0x11, 0xf4, 0xd6, 0xc6, 0xfa, 0x81, 0x46, 0x6e, 0x43, + 0x4a, 0xaf, 0x6c, 0x3d, 0x93, 0x60, 0x5b, 0x26, 0xc0, 0xa7, 0x72, 0x1b, 0xc1, 0xa5, 0x6a, 0xfe, 0x5a, 0x49, 0xa1, + 0xab, 0xc5, 0x32, 0xb7, 0xf1, 0x21, 0x90, 0x05, 0xe1, 0x48, 0xd0, 0x0c, 0x3f, 0xa4, 0xe6, 0xb5, 0x3c, 0x86, 0xb4, + 0x00, 0x31, 0x13, 0xb4, 0x8f, 0xa7, 0xb7, 0x0f, 0xef, 0xfe, 0xfe, 0xe9, 0x17, 0x1a, 0x47, 0xe6, 0x5a, 0x1e, 0xd7, + 0xed, 0xc2, 0x46, 0x48, 0xc2, 0xbb, 0x80, 0xa5, 0x52, 0xe6, 0x5d, 0x83, 0x5f, 0xb4, 0x3b, 0xe5, 0x3a, 0x49, 0x37, + 0xa3, 0x89, 0xfc, 0x0a, 0x9f, 0x5e, 0x8a, 0x83, 0x47, 0xd3, 0x5b, 0xb3, 0x1a, 0xed, 0x95, 0xe4, 0xdb, 0x3f, 0x34, + 0xc7, 0x76, 0x7b, 0x52, 0x6f, 0x3d, 0x4f, 0xf4, 0x68, 0x7a, 0xdb, 0x55, 0x82, 0xb6, 0x99, 0x29, 0xa8, 0x5a, 0xd3, + 0x5b, 0x3b, 0xcb, 0xb8, 0xea, 0xc8, 0xf1, 0x0f, 0x72, 0x87, 0x86, 0x39, 0xed, 0xc2, 0xbd, 0xe3, 0x6c, 0x18, 0x26, + 0x5a, 0x98, 0x4f, 0x58, 0x14, 0x25, 0xb4, 0x6b, 0xe4, 0xb5, 0xd3, 0x7e, 0x04, 0x49, 0xba, 0xf6, 0x92, 0xd5, 0x57, + 0xc5, 0x42, 0x5e, 0x89, 0xa7, 0xf0, 0x3a, 0xe7, 0x09, 0x7c, 0xf4, 0x63, 0x23, 0x3a, 0x75, 0xf6, 0x6a, 0xab, 0x42, + 0x9e, 0xfc, 0x5d, 0x9f, 0xcb, 0x51, 0xeb, 0x4f, 0x5d, 0xb9, 0xe0, 0xad, 0xae, 0xe0, 0xd3, 0xa0, 0x79, 0x50, 0x9f, + 0x08, 0xbc, 0x2a, 0xa7, 0x80, 0x37, 0x4c, 0x0b, 0x83, 0xb4, 0x52, 0x7c, 0xda, 0xf1, 0xdb, 0xba, 0x4c, 0x76, 0x00, + 0x79, 0x61, 0x65, 0x51, 0x51, 0x9f, 0xcc, 0xbf, 0xcd, 0x6e, 0x79, 0xb2, 0x79, 0xb7, 0x3c, 0x31, 0xbb, 0xe5, 0x7e, + 0x8a, 0xfd, 0x7c, 0xd4, 0x86, 0x3f, 0xdd, 0x6a, 0x42, 0x41, 0xcb, 0x39, 0x98, 0xde, 0x3a, 0xa0, 0xa7, 0x35, 0x3b, + 0xd3, 0x5b, 0x95, 0x63, 0x0d, 0xb1, 0x9b, 0x16, 0x64, 0x1d, 0xe3, 0x96, 0x03, 0x85, 0xf0, 0xb7, 0x55, 0x7b, 0xd5, + 0x3e, 0x84, 0x77, 0xd0, 0xea, 0x68, 0xfd, 0x5d, 0xe7, 0xfe, 0x4d, 0x1b, 0xa4, 0x5c, 0x78, 0x81, 0xe1, 0xc6, 0xc8, + 0x17, 0xe1, 0xf5, 0x35, 0x8d, 0x82, 0x11, 0x1f, 0xce, 0xf2, 0x7f, 0xd2, 0xf0, 0x6b, 0x24, 0xde, 0xbb, 0xa5, 0x57, + 0xfa, 0x31, 0x4d, 0x55, 0xc6, 0xb7, 0xe9, 0x61, 0x51, 0xae, 0x53, 0x90, 0x0f, 0xc3, 0x84, 0x7a, 0x1d, 0xff, 0x70, + 0xc3, 0x26, 0xf8, 0x77, 0x59, 0x9b, 0x8d, 0x93, 0xf9, 0xbd, 0xc8, 0xb8, 0x17, 0x09, 0xbf, 0x0a, 0x07, 0xf6, 0x1a, + 0xb6, 0x8e, 0x37, 0x83, 0x3b, 0x30, 0x23, 0x5d, 0x18, 0xa1, 0xa0, 0xe5, 0x4e, 0x44, 0x47, 0xe1, 0x2c, 0x11, 0xf7, + 0xf7, 0xba, 0x8d, 0x32, 0xd6, 0x7a, 0xbd, 0x87, 0xa1, 0x57, 0x75, 0x1f, 0xc8, 0xa5, 0x3f, 0x7f, 0x72, 0x08, 0x7f, + 0x54, 0xfe, 0xd7, 0x5d, 0xa5, 0xab, 0x2b, 0xbb, 0x17, 0x74, 0xf5, 0xdd, 0x9a, 0x32, 0xae, 0x44, 0xb8, 0xd4, 0xc7, + 0x1f, 0x5a, 0x1b, 0xb4, 0xca, 0x07, 0x55, 0xd7, 0x5a, 0xd6, 0xaf, 0xaa, 0xfd, 0xeb, 0x3a, 0x7f, 0x60, 0xdd, 0xa1, + 0xd2, 0x5c, 0xeb, 0x75, 0xf5, 0x67, 0x08, 0xd7, 0x2a, 0x1b, 0x8c, 0xcb, 0xfa, 0xbb, 0xe4, 0xae, 0x34, 0x51, 0x54, + 0x34, 0x16, 0xac, 0x94, 0x5d, 0x65, 0xa5, 0xe4, 0x94, 0x5c, 0x9d, 0xf4, 0x6f, 0x27, 0x89, 0x33, 0x57, 0xc7, 0x25, + 0x89, 0xdb, 0xf6, 0x5b, 0xae, 0x23, 0xf3, 0x00, 0xe0, 0xd6, 0x76, 0x57, 0x7e, 0xde, 0xd6, 0xed, 0x83, 0xa6, 0x35, + 0x1f, 0x4b, 0xcd, 0xee, 0x65, 0x78, 0x47, 0xb3, 0xcb, 0x8e, 0xeb, 0x80, 0x9f, 0xa6, 0xa9, 0x52, 0x26, 0x64, 0x99, + 0xd3, 0x71, 0x9d, 0xdb, 0x49, 0x92, 0xe6, 0xc4, 0x8d, 0x85, 0x98, 0x06, 0xea, 0xfb, 0xb7, 0x37, 0x07, 0x3e, 0xcf, + 0xc6, 0xfb, 0x9d, 0x56, 0xab, 0x05, 0x17, 0xc0, 0xba, 0xce, 0x9c, 0xd1, 0x9b, 0xa7, 0xfc, 0x96, 0xb8, 0x2d, 0xa7, + 0xe5, 0xb4, 0x3b, 0xc7, 0x4e, 0xbb, 0x73, 0xe8, 0x3f, 0x3a, 0x76, 0x7b, 0x9f, 0x39, 0xce, 0x49, 0x44, 0x47, 0x39, + 0xfc, 0x70, 0x9c, 0x13, 0xa9, 0x78, 0xa9, 0xdf, 0x8e, 0xe3, 0x0f, 0x93, 0xbc, 0xd9, 0x76, 0x16, 0xfa, 0xd1, 0x71, + 0xe0, 0x50, 0x69, 0xe0, 0x7c, 0x3e, 0xea, 0x8c, 0x0e, 0x47, 0x4f, 0xba, 0xba, 0xb8, 0xf8, 0xac, 0x56, 0x1d, 0xab, + 0xff, 0x3b, 0x56, 0xb3, 0x5c, 0x64, 0xfc, 0x23, 0xd5, 0x39, 0x89, 0x0e, 0x88, 0x9e, 0x8d, 0x4d, 0x3b, 0xeb, 0x23, + 0xb5, 0x8f, 0xaf, 0x87, 0xa3, 0x4e, 0x55, 0x5d, 0xc2, 0xb8, 0x5f, 0x02, 0x79, 0xb2, 0x6f, 0x40, 0x3f, 0xb1, 0xd1, + 0xd4, 0x6e, 0x6e, 0x42, 0x54, 0xdb, 0xd5, 0x73, 0x1c, 0x9b, 0xf9, 0x9d, 0xc0, 0x19, 0x06, 0xa3, 0xab, 0x4a, 0x08, + 0x5c, 0x27, 0x22, 0xee, 0xab, 0x76, 0xe7, 0x18, 0xb7, 0xdb, 0x8f, 0xfc, 0x47, 0xc7, 0xc3, 0x16, 0x3e, 0xf4, 0x0f, + 0x9b, 0x07, 0xfe, 0x23, 0x7c, 0xdc, 0x3c, 0xc6, 0xc7, 0x2f, 0x8e, 0x87, 0xcd, 0x43, 0xff, 0x10, 0xb7, 0x9a, 0xc7, + 0x50, 0xd8, 0x3c, 0x6e, 0x1e, 0xcf, 0x9b, 0x87, 0xc7, 0xc3, 0x96, 0x2c, 0xed, 0xf8, 0x47, 0x47, 0xcd, 0x76, 0xcb, + 0x3f, 0x3a, 0xc2, 0x47, 0xfe, 0xa3, 0x47, 0xcd, 0xf6, 0x81, 0xff, 0xe8, 0xd1, 0xcb, 0xa3, 0x63, 0xff, 0x00, 0xde, + 0x1d, 0x1c, 0x0c, 0x0f, 0xfc, 0x76, 0xbb, 0x09, 0xff, 0xe0, 0x63, 0xbf, 0xa3, 0x7e, 0xb4, 0xdb, 0xfe, 0x41, 0x1b, + 0xb7, 0x92, 0xa3, 0x8e, 0xff, 0xe8, 0x09, 0x96, 0xff, 0xca, 0x6a, 0x58, 0xfe, 0x03, 0xdd, 0xe0, 0x27, 0x7e, 0xe7, + 0x91, 0xfa, 0x25, 0x3b, 0x9c, 0x1f, 0x1e, 0xff, 0xe0, 0xee, 0x6f, 0x9d, 0x43, 0x5b, 0xcd, 0xe1, 0xf8, 0xc8, 0x3f, + 0x38, 0xc0, 0x87, 0x6d, 0xff, 0xf8, 0x20, 0x6e, 0x1e, 0x76, 0xfc, 0x47, 0x8f, 0x87, 0xcd, 0xb6, 0xff, 0xf8, 0x31, + 0x6e, 0x35, 0x0f, 0xfc, 0x0e, 0x6e, 0xfb, 0x87, 0x07, 0xf2, 0xc7, 0x81, 0xdf, 0x99, 0x3f, 0x7e, 0xe2, 0x3f, 0x3a, + 0x8a, 0x1f, 0xf9, 0x87, 0xdf, 0x1e, 0x1e, 0xfb, 0x9d, 0x83, 0xf8, 0xe0, 0x91, 0xdf, 0x79, 0x3c, 0x7f, 0xe4, 0x1f, + 0xc6, 0xcd, 0xce, 0xa3, 0x7b, 0x5b, 0xb6, 0x3b, 0x3e, 0xe0, 0x48, 0xbe, 0x86, 0x17, 0x58, 0xbf, 0x80, 0xbf, 0xb1, + 0x6c, 0xfb, 0xef, 0xd8, 0x4d, 0xbe, 0xde, 0xf4, 0x89, 0x7f, 0xfc, 0x78, 0xa8, 0xaa, 0x43, 0x41, 0xd3, 0xd4, 0x80, + 0x26, 0xf3, 0xa6, 0x1a, 0x56, 0x76, 0xd7, 0x34, 0x1d, 0x99, 0xbf, 0x7a, 0xb0, 0x79, 0x13, 0x06, 0x56, 0xe3, 0xfe, + 0x87, 0xf6, 0x53, 0x2e, 0xf9, 0xc9, 0xfe, 0x58, 0x91, 0xfe, 0xb8, 0xf7, 0x99, 0xba, 0xdd, 0xf9, 0xb3, 0x2b, 0x9c, + 0x6e, 0x73, 0x7c, 0x64, 0x9f, 0x76, 0x7c, 0x70, 0xfa, 0x10, 0xcf, 0x47, 0xf6, 0x87, 0x7b, 0x3e, 0x52, 0xba, 0xe2, + 0x38, 0xbf, 0x16, 0x6b, 0x0e, 0x8e, 0x55, 0xab, 0xf8, 0xa9, 0xf0, 0x06, 0x39, 0x7c, 0x47, 0xac, 0xe8, 0x5e, 0x0b, + 0xc2, 0xa9, 0xed, 0x07, 0xe2, 0xc0, 0x62, 0xaf, 0x85, 0xe2, 0xb1, 0xc9, 0x36, 0x84, 0x84, 0x9f, 0x46, 0xc8, 0xb7, + 0x0f, 0xc1, 0x47, 0xf8, 0x87, 0xe3, 0x23, 0xb1, 0xf1, 0x51, 0xf3, 0xe5, 0x4b, 0x4f, 0x83, 0xf4, 0x14, 0x9c, 0xcb, + 0x67, 0x0f, 0x0e, 0x51, 0x35, 0xdc, 0x7d, 0x0a, 0x45, 0xb9, 0xab, 0x22, 0x5f, 0xef, 0x7e, 0x4d, 0xd8, 0x41, 0x9d, + 0x98, 0x24, 0xae, 0x76, 0xcb, 0x4c, 0xa5, 0xd4, 0xd1, 0x0f, 0xa5, 0x50, 0xea, 0xf8, 0x2d, 0xbf, 0x55, 0xba, 0x74, + 0xe0, 0x94, 0x2c, 0x59, 0x70, 0x11, 0xc2, 0x17, 0x6b, 0x13, 0x3e, 0x96, 0xdf, 0xb6, 0x85, 0xaf, 0x09, 0x40, 0xd2, + 0xcf, 0x50, 0x7d, 0xc8, 0x21, 0x70, 0x5d, 0x7d, 0xb7, 0x06, 0x9c, 0xc2, 0xfc, 0x06, 0x4e, 0xaa, 0x9a, 0xa8, 0xc4, + 0x04, 0xbc, 0x1d, 0xaf, 0x68, 0xc4, 0x42, 0xcf, 0xf5, 0xa6, 0x19, 0x1d, 0xd1, 0x2c, 0x6f, 0xd6, 0x8e, 0x6f, 0xca, + 0x93, 0x9b, 0xc8, 0x35, 0x9f, 0x46, 0xcd, 0xe0, 0x76, 0x6c, 0x32, 0xd0, 0xfe, 0x46, 0x57, 0x1b, 0x60, 0x6e, 0x81, + 0x4d, 0x49, 0x06, 0xb2, 0xb6, 0x52, 0xda, 0x5c, 0xa5, 0xb5, 0xb5, 0xfd, 0xce, 0x11, 0x72, 0x64, 0x31, 0xdc, 0x3b, + 0xfc, 0xbd, 0xd7, 0x3c, 0x68, 0xfd, 0x09, 0x59, 0xcd, 0xca, 0x8e, 0x2e, 0xb4, 0xbb, 0x2d, 0xad, 0xbe, 0x29, 0x5d, + 0x3f, 0x5b, 0xeb, 0x2a, 0x8a, 0xf8, 0x5c, 0xcd, 0xdd, 0x45, 0xdd, 0x54, 0x47, 0xb8, 0xd5, 0x0d, 0x11, 0x23, 0x36, + 0xf6, 0xec, 0x2f, 0x06, 0xab, 0x7b, 0x8d, 0xe5, 0x87, 0xc6, 0x51, 0x51, 0x55, 0x49, 0xd1, 0x42, 0xc6, 0x5b, 0x58, + 0xea, 0xa4, 0xcb, 0xa5, 0x97, 0x82, 0x8b, 0x9c, 0x58, 0x38, 0x85, 0x67, 0x54, 0x43, 0x72, 0x8a, 0x4b, 0x80, 0x24, + 0x82, 0x49, 0xaa, 0xfe, 0xaf, 0x8a, 0xcd, 0x0f, 0xed, 0xf8, 0xf2, 0x93, 0x30, 0x1d, 0x03, 0x15, 0x86, 0xe9, 0x78, + 0xcd, 0xad, 0xa6, 0x42, 0x46, 0x2b, 0xa5, 0x55, 0x57, 0x95, 0xfb, 0x2c, 0x7f, 0x7a, 0xf7, 0x5e, 0x5f, 0x80, 0xe6, + 0x82, 0x77, 0x5a, 0x46, 0x38, 0xaa, 0xcb, 0x9a, 0x1b, 0xe4, 0x8b, 0x93, 0x09, 0x15, 0xa1, 0xca, 0xd7, 0x04, 0x7d, + 0x02, 0x4e, 0xcd, 0x3a, 0xda, 0x1a, 0x25, 0xae, 0x94, 0xee, 0x24, 0xa2, 0x73, 0x36, 0xd4, 0xa2, 0x1e, 0x3b, 0xfa, + 0xe6, 0x80, 0xa6, 0x5c, 0x1a, 0xd2, 0xc6, 0xca, 0x1f, 0x33, 0x0c, 0x65, 0x46, 0x3e, 0x49, 0xb9, 0xdb, 0xfb, 0xa2, + 0xfc, 0xfa, 0xe9, 0xb6, 0x45, 0x48, 0x58, 0xfa, 0x71, 0x90, 0xd1, 0xe4, 0x9f, 0xc8, 0x17, 0x6c, 0xc8, 0xd3, 0x2f, + 0x2e, 0xe0, 0xab, 0xf4, 0x7e, 0x9c, 0xd1, 0x11, 0xf9, 0x02, 0x64, 0x7c, 0x20, 0xad, 0x0f, 0x60, 0x84, 0x8d, 0xdb, + 0x49, 0x82, 0xa5, 0xc6, 0xf4, 0x00, 0x85, 0x48, 0x81, 0xeb, 0x76, 0x8e, 0x5c, 0x47, 0xd9, 0xc4, 0xf2, 0x77, 0x4f, + 0x89, 0x53, 0xa9, 0x04, 0x38, 0xed, 0x8e, 0x7f, 0x14, 0x77, 0xfc, 0x27, 0xf3, 0xc7, 0xfe, 0x71, 0xdc, 0x7e, 0x3c, + 0x6f, 0xc2, 0xff, 0x1d, 0xff, 0x49, 0xd2, 0xec, 0xf8, 0x4f, 0xe0, 0xef, 0xb7, 0x87, 0xfe, 0x51, 0xdc, 0x6c, 0xfb, + 0xc7, 0xf3, 0x03, 0xff, 0xe0, 0x65, 0xbb, 0xe3, 0x1f, 0x38, 0x6d, 0x47, 0xb5, 0x03, 0x76, 0xad, 0xb8, 0xf3, 0x17, + 0x2b, 0x1b, 0x62, 0x43, 0x38, 0x4e, 0xe5, 0x9c, 0xba, 0xd8, 0x2b, 0xbf, 0xb1, 0xa8, 0xf7, 0xa7, 0x76, 0xd6, 0x3d, + 0x0b, 0x33, 0xf8, 0xd0, 0x4d, 0x7d, 0xef, 0xd6, 0xde, 0xe1, 0x1a, 0xbf, 0xd8, 0x30, 0x04, 0xec, 0x70, 0x17, 0xdb, + 0x47, 0xef, 0xe1, 0xdc, 0xba, 0xbc, 0x17, 0xdc, 0x5c, 0x8f, 0xb8, 0x9d, 0xb4, 0x55, 0x45, 0x73, 0x05, 0xa3, 0x64, + 0x16, 0x4c, 0x7e, 0x81, 0x41, 0x0e, 0xf2, 0x55, 0x54, 0xac, 0x8e, 0x0f, 0xa9, 0xaf, 0x19, 0xb7, 0x6e, 0x1f, 0xa0, + 0xd5, 0x81, 0x8d, 0x88, 0xc1, 0x7d, 0x11, 0x45, 0x61, 0x40, 0xaf, 0xb9, 0x69, 0x2b, 0x2c, 0x49, 0x7e, 0x41, 0xf3, + 0xbe, 0x0b, 0x45, 0x6e, 0xe0, 0x4a, 0x17, 0x9f, 0x5b, 0x7e, 0xec, 0xa7, 0x24, 0xec, 0xaa, 0x00, 0xcb, 0x43, 0x57, + 0xb0, 0x6b, 0x01, 0x3f, 0x2e, 0xda, 0xdb, 0xdb, 0xba, 0x5f, 0xa4, 0x02, 0x09, 0x73, 0xad, 0xbe, 0x11, 0x62, 0xb3, + 0x22, 0xd7, 0x46, 0x74, 0xd9, 0xaf, 0x44, 0x21, 0xd2, 0x78, 0xba, 0xa6, 0xa1, 0xf0, 0xc3, 0x54, 0x25, 0xd1, 0x58, + 0x0c, 0x0b, 0xb7, 0xe9, 0x01, 0x2a, 0xb8, 0x08, 0xad, 0xef, 0x00, 0xeb, 0x7d, 0xce, 0x45, 0x68, 0xce, 0xd2, 0x5a, + 0xd7, 0x06, 0x81, 0xa3, 0x37, 0xee, 0xf4, 0xde, 0xbc, 0x3f, 0x75, 0xd4, 0xf6, 0x3c, 0xd9, 0x8f, 0x3b, 0xbd, 0x13, + 0xe9, 0x33, 0x51, 0x27, 0xf1, 0x88, 0x3a, 0x89, 0xe7, 0xe8, 0x53, 0x99, 0x10, 0x49, 0x2b, 0xf6, 0xd5, 0xb4, 0xa5, + 0xcd, 0xa0, 0xbc, 0xbd, 0x93, 0x59, 0x22, 0x18, 0xdc, 0x71, 0xbd, 0x2f, 0x8f, 0xe1, 0xc1, 0x82, 0x95, 0x79, 0xd8, + 0x5a, 0x3b, 0xbc, 0x16, 0xa9, 0xf1, 0x0d, 0x8f, 0x58, 0x42, 0x4d, 0xe6, 0xb5, 0xee, 0xaa, 0x3c, 0x29, 0xb0, 0x5e, + 0x3b, 0x9f, 0x5d, 0x4f, 0x98, 0x70, 0xcd, 0x79, 0x86, 0x0f, 0xba, 0xc1, 0x89, 0x1c, 0xaa, 0x77, 0x55, 0x68, 0xe7, + 0xb5, 0xf9, 0x9a, 0x4f, 0x7d, 0x49, 0xf5, 0xec, 0xb5, 0x84, 0x80, 0x13, 0x72, 0xf1, 0x41, 0xaf, 0x74, 0x17, 0xdb, + 0xef, 0x8a, 0x93, 0xfd, 0xf8, 0xa0, 0x77, 0x15, 0x4c, 0x75, 0x7f, 0x2f, 0xf9, 0x78, 0x73, 0x5f, 0x09, 0x1f, 0xf7, + 0xe5, 0x51, 0x10, 0x75, 0x48, 0xd9, 0x28, 0xbf, 0x3c, 0x71, 0x7b, 0x27, 0x5a, 0x19, 0x70, 0x64, 0x60, 0xdd, 0x3d, + 0x6a, 0x99, 0xd3, 0x25, 0x09, 0x1f, 0xc3, 0x86, 0x54, 0x4d, 0xac, 0x41, 0x6a, 0x1e, 0xf7, 0xb8, 0xdd, 0x3b, 0x09, + 0x1d, 0xc9, 0x5b, 0x24, 0xf3, 0xc8, 0x83, 0x7d, 0x68, 0x1c, 0xf3, 0x09, 0xf5, 0x19, 0xdf, 0xbf, 0xa1, 0xd7, 0xcd, + 0x70, 0xca, 0x2a, 0xf7, 0x36, 0x28, 0x1d, 0xe5, 0x90, 0xdc, 0x78, 0xc4, 0xf5, 0xd9, 0xab, 0x4e, 0xe5, 0x6e, 0x3b, + 0x04, 0x9b, 0xc7, 0xb8, 0xe6, 0xa4, 0x4f, 0xce, 0x02, 0x8b, 0xf7, 0x4e, 0xf6, 0xc3, 0x15, 0x8c, 0x48, 0x7e, 0x5f, + 0x68, 0x47, 0x3b, 0x18, 0x36, 0x40, 0x6f, 0xae, 0xa3, 0xc4, 0x81, 0x71, 0xc8, 0x6b, 0x41, 0x5d, 0xb8, 0xbd, 0x7f, + 0xfd, 0x1f, 0xff, 0x4b, 0xfb, 0xd8, 0x4f, 0xf6, 0xe3, 0xb6, 0xe9, 0x6b, 0x65, 0x55, 0x8a, 0x13, 0x38, 0xee, 0x59, + 0x05, 0x85, 0xe9, 0x6d, 0x73, 0x9c, 0xb1, 0xa8, 0x19, 0x87, 0xc9, 0xc8, 0xed, 0x6d, 0xc7, 0xa6, 0x7d, 0x6c, 0x4b, + 0x43, 0x5d, 0x2f, 0x02, 0x7a, 0xfd, 0x4d, 0x07, 0x8f, 0xcc, 0xf9, 0x15, 0xb9, 0xb5, 0xed, 0x63, 0x48, 0xd5, 0xee, + 0xab, 0x1d, 0x45, 0x4a, 0xf5, 0x27, 0xc2, 0x34, 0x07, 0x4c, 0x6b, 0x27, 0x90, 0x0a, 0xd7, 0x29, 0x83, 0x5a, 0xff, + 0xf7, 0x7f, 0xfe, 0x97, 0xff, 0x66, 0x1e, 0x21, 0x56, 0xf5, 0xaf, 0xff, 0xfd, 0x3f, 0xff, 0x9f, 0xff, 0xfd, 0x5f, + 0xe1, 0xd4, 0x8a, 0x8e, 0x67, 0x49, 0xa6, 0xe2, 0x54, 0xc1, 0x2c, 0xc5, 0x5d, 0x1c, 0x48, 0xec, 0x9c, 0xb0, 0x5c, + 0xb0, 0x61, 0xfd, 0x4c, 0xd2, 0xb9, 0x1c, 0x50, 0xee, 0x4c, 0x0d, 0x9d, 0xdc, 0xe1, 0x45, 0x45, 0x50, 0x35, 0x94, + 0x4b, 0xc2, 0x2d, 0x4e, 0xf6, 0x01, 0xdf, 0x0f, 0x3b, 0xc6, 0xe9, 0x97, 0xcb, 0xb1, 0x30, 0x64, 0x02, 0x25, 0x45, + 0x55, 0xee, 0x40, 0x6c, 0x65, 0x01, 0x8f, 0x41, 0xc7, 0x2a, 0x96, 0xab, 0x57, 0x6b, 0xd3, 0xfd, 0x69, 0x96, 0x0b, + 0x36, 0x02, 0x94, 0x2b, 0x3f, 0xb1, 0x0c, 0x63, 0x37, 0x41, 0x57, 0x4c, 0xee, 0x0a, 0xd9, 0x8b, 0x22, 0xd0, 0xc3, + 0xe3, 0x3f, 0x15, 0x7f, 0x99, 0x80, 0x46, 0xe6, 0x78, 0x93, 0xf0, 0x56, 0x9b, 0xe7, 0x8f, 0x5a, 0xad, 0xe9, 0x2d, + 0x5a, 0x54, 0x23, 0xe0, 0x6d, 0x83, 0x49, 0x3a, 0xb6, 0x3b, 0x94, 0xf1, 0xef, 0xd2, 0x8d, 0xdd, 0x72, 0xc0, 0x17, + 0xee, 0xb4, 0x8a, 0xe2, 0xcf, 0x0b, 0xe9, 0x49, 0x65, 0xbf, 0x40, 0x9c, 0x5a, 0x3b, 0x9d, 0xaf, 0xb9, 0x3d, 0xb9, + 0x85, 0xd5, 0xaa, 0xa3, 0x5a, 0xc5, 0xed, 0xf5, 0xd3, 0x89, 0x76, 0x9c, 0xdd, 0x8e, 0x90, 0x1f, 0x42, 0xcc, 0x3b, + 0x6e, 0xe3, 0xb8, 0xb3, 0x28, 0xbb, 0x17, 0x82, 0x4f, 0xec, 0xc0, 0x3a, 0x0d, 0xe9, 0x90, 0x8e, 0x8c, 0xb3, 0x5e, + 0xbf, 0x57, 0x41, 0xf3, 0x22, 0x3e, 0xd8, 0x30, 0x96, 0x06, 0x49, 0x06, 0xd4, 0x9d, 0x56, 0xf1, 0x39, 0xec, 0xc0, + 0xc5, 0x28, 0xe1, 0xa1, 0x08, 0x24, 0xc1, 0x76, 0xed, 0xf0, 0x7c, 0x08, 0x3c, 0x89, 0x2f, 0x2c, 0x78, 0xba, 0xaa, + 0x2a, 0xb8, 0xcd, 0xeb, 0x67, 0x48, 0x0b, 0x5f, 0x36, 0xb7, 0xbb, 0x52, 0x5e, 0xb7, 0x6f, 0x75, 0xd4, 0xfb, 0x5d, + 0xcd, 0x5d, 0xa5, 0x05, 0x52, 0x07, 0x6d, 0x7e, 0xaf, 0xe4, 0xba, 0x7a, 0xfb, 0xb5, 0xf0, 0x5c, 0x09, 0xa6, 0xbb, + 0x5a, 0x4b, 0x16, 0x42, 0xad, 0x77, 0xe4, 0xdb, 0xd2, 0x64, 0x0a, 0xa7, 0x53, 0x59, 0x11, 0x75, 0x4f, 0xf6, 0x95, + 0xa6, 0x0b, 0xdc, 0x43, 0xa6, 0x74, 0xa8, 0x0c, 0x0a, 0x5d, 0x49, 0x6f, 0x05, 0xf5, 0x4b, 0xe7, 0x56, 0xc0, 0xa7, + 0xe3, 0x7a, 0xff, 0x0f, 0x82, 0x7a, 0x0b, 0xa7, 0xcf, 0x89, 0x00, 0x00}; } // namespace web_server } // namespace esphome diff --git a/esphome/components/web_server/server_index_v3.h b/esphome/components/web_server/server_index_v3.h index 98a4f255f5..39518197a3 100644 --- a/esphome/components/web_server/server_index_v3.h +++ b/esphome/components/web_server/server_index_v3.h @@ -3636,415 +3636,417 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0x9d, 0x85, 0xdf, 0xf1, 0x24, 0xec, 0x6a, 0x98, 0x92, 0x35, 0x98, 0x2e, 0x20, 0x16, 0xef, 0xfe, 0x43, 0xc1, 0x7e, 0x86, 0xbf, 0xb3, 0x14, 0xfe, 0x56, 0xb5, 0x77, 0x30, 0xbc, 0x7b, 0x7b, 0xf6, 0x01, 0x14, 0x1c, 0x31, 0x6a, 0x24, 0x37, 0x81, 0x36, 0x66, 0x18, 0x82, 0xca, 0x20, 0x88, 0x82, 0x78, 0x05, 0x27, 0x3b, 0xb2, 0x8e, 0x87, 0x77, 0xc3, - 0xdb, 0xdb, 0xdb, 0xff, 0xd3, 0xdc, 0xd3, 0x6e, 0xb7, 0x6d, 0x23, 0xfb, 0xbf, 0x4f, 0xc1, 0x30, 0xd9, 0x94, 0x4c, - 0x48, 0x9a, 0x94, 0x2c, 0x5b, 0x91, 0x2c, 0xb9, 0xcd, 0x47, 0xb7, 0xee, 0xba, 0x4d, 0x4f, 0xe2, 0xf6, 0xee, 0xae, - 0xeb, 0x63, 0x51, 0x12, 0x24, 0x71, 0x43, 0x91, 0x3a, 0x24, 0xe5, 0x8f, 0x2a, 0xdc, 0x67, 0xd9, 0x47, 0xb8, 0xcf, - 0xd0, 0x27, 0xbb, 0x67, 0x66, 0x00, 0x12, 0xfc, 0x92, 0xe4, 0x4d, 0xda, 0xde, 0xd3, 0x26, 0x11, 0x41, 0x00, 0x04, - 0x06, 0xc0, 0x60, 0xbe, 0xc7, 0x04, 0xd3, 0x46, 0x73, 0x1d, 0xf9, 0x2c, 0x98, 0x84, 0x90, 0x98, 0x24, 0x15, 0x08, - 0x9f, 0x95, 0x10, 0x3e, 0xc4, 0x45, 0xe5, 0x89, 0x35, 0xde, 0x2f, 0xc2, 0xdb, 0xaf, 0x7d, 0x5f, 0xe6, 0xdf, 0x05, - 0xd1, 0xc7, 0x59, 0xda, 0x02, 0x02, 0xd1, 0x40, 0x0d, 0x61, 0x79, 0xf1, 0x35, 0x57, 0x1c, 0x4f, 0xaf, 0xc7, 0xf7, - 0xd7, 0x5c, 0x38, 0x9d, 0x05, 0xa6, 0x7d, 0x35, 0x3a, 0x99, 0x7a, 0x37, 0x0a, 0x52, 0xa6, 0x03, 0x15, 0xbc, 0x7a, - 0x7c, 0x36, 0x5e, 0x27, 0x49, 0x18, 0x98, 0x51, 0x78, 0xab, 0x0e, 0x4f, 0xe8, 0x41, 0x54, 0x70, 0xe9, 0x51, 0x55, - 0xbe, 0x9a, 0xf8, 0xde, 0xe4, 0xc3, 0x40, 0x7d, 0xb2, 0xf1, 0x06, 0xc3, 0x12, 0xfd, 0x69, 0xa7, 0xea, 0x10, 0xc6, - 0xaa, 0x7c, 0xed, 0xfb, 0x27, 0x07, 0xd4, 0x62, 0x78, 0x72, 0x30, 0xf5, 0x6e, 0x86, 0x52, 0x8e, 0x10, 0xae, 0x40, - 0x1b, 0xf0, 0x58, 0x8c, 0x99, 0xc9, 0x51, 0x8c, 0xce, 0xfd, 0x13, 0xa6, 0xe5, 0x5c, 0x10, 0x04, 0x1d, 0xa1, 0xf1, - 0x6a, 0x13, 0x94, 0xab, 0xfa, 0x40, 0xf3, 0x7f, 0xfc, 0xa8, 0x65, 0x06, 0x89, 0x0b, 0x29, 0x5a, 0x17, 0xea, 0x7b, - 0xb0, 0x8a, 0x81, 0x21, 0x47, 0x74, 0x4d, 0xc4, 0x14, 0xf3, 0x75, 0x63, 0x92, 0x1a, 0x98, 0x6a, 0xc5, 0x5d, 0x01, - 0x8d, 0xc0, 0x7f, 0x4a, 0xac, 0xd1, 0x04, 0xd2, 0x2b, 0x4b, 0x08, 0x5e, 0x97, 0x84, 0xef, 0x74, 0x36, 0x79, 0xc0, - 0x38, 0x90, 0x9a, 0xe3, 0x77, 0x48, 0x20, 0xae, 0xf9, 0x3a, 0xa4, 0xf7, 0xca, 0xa2, 0xb4, 0xb8, 0xa9, 0x48, 0xa8, - 0x25, 0xe0, 0x72, 0x5a, 0x58, 0xa1, 0x5e, 0x79, 0xbd, 0x44, 0xf8, 0xc0, 0x47, 0x71, 0xd3, 0x92, 0x81, 0x32, 0x47, - 0x4b, 0x8c, 0xd2, 0x7d, 0x0c, 0xee, 0x5d, 0x92, 0x06, 0x81, 0x19, 0xda, 0x65, 0x6c, 0x84, 0x57, 0xf9, 0x1d, 0x16, - 0x13, 0xfa, 0xec, 0x85, 0x69, 0x1e, 0xc9, 0x97, 0x56, 0x7d, 0xf8, 0x64, 0x13, 0xe0, 0xa5, 0x17, 0x0f, 0x86, 0xc5, - 0x7d, 0x90, 0xb8, 0x63, 0x93, 0x36, 0xb3, 0xaa, 0x7c, 0x35, 0x1d, 0xfb, 0xd9, 0x62, 0xd3, 0xd1, 0x58, 0xb8, 0xc1, - 0xd4, 0x67, 0x17, 0xee, 0xf8, 0x5b, 0xac, 0xf3, 0x7a, 0xec, 0xbf, 0x82, 0x0a, 0xa9, 0x3a, 0x7c, 0xb2, 0xa1, 0x6b, - 0xbd, 0x0e, 0x8d, 0xa7, 0xb4, 0x05, 0xca, 0xdf, 0xe1, 0xb9, 0x77, 0x58, 0x44, 0xad, 0x71, 0xb0, 0x74, 0x15, 0x13, - 0x9e, 0x2d, 0x8e, 0x8c, 0xe7, 0x7e, 0x81, 0xbd, 0xa9, 0xf0, 0x43, 0x09, 0xe3, 0x0a, 0xc5, 0x01, 0x95, 0x77, 0xa6, - 0x3c, 0x58, 0xba, 0x72, 0xdf, 0x85, 0xb7, 0x62, 0xa4, 0x1c, 0x00, 0x14, 0xab, 0xf0, 0xf4, 0xd5, 0xe8, 0x44, 0xd6, - 0x0f, 0xa0, 0x10, 0x95, 0xfa, 0x85, 0x5f, 0xa9, 0xaa, 0xe4, 0x99, 0x80, 0x56, 0x77, 0xea, 0xf0, 0xe4, 0x40, 0xae, - 0x3d, 0x1c, 0xf5, 0xce, 0xa5, 0xc9, 0x61, 0xaf, 0x00, 0x84, 0x62, 0x59, 0xe5, 0xd6, 0x81, 0xe4, 0x78, 0xf9, 0xbd, - 0x44, 0xdb, 0x43, 0xa0, 0xc5, 0x50, 0xef, 0x65, 0x6b, 0x44, 0x36, 0x78, 0xa2, 0xb7, 0x11, 0xff, 0x37, 0x9f, 0x33, - 0xca, 0x34, 0x59, 0x10, 0x87, 0x91, 0x0a, 0xf3, 0x28, 0x67, 0xc8, 0x51, 0xa4, 0xcc, 0x5c, 0x38, 0xa3, 0xda, 0xdb, - 0x14, 0x20, 0x72, 0x50, 0x6e, 0x2a, 0x4d, 0x6c, 0xa4, 0xe7, 0x3f, 0x14, 0x3e, 0x99, 0x12, 0x56, 0xca, 0x06, 0xd8, - 0x9c, 0x79, 0xe8, 0xf2, 0xad, 0x67, 0xfc, 0x4f, 0x68, 0xcc, 0x5d, 0x63, 0xe9, 0x1a, 0xef, 0x83, 0xab, 0xb4, 0x76, - 0x75, 0xb2, 0xac, 0x61, 0x06, 0xeb, 0x6b, 0x10, 0x6b, 0x87, 0x4b, 0x40, 0xb8, 0x5c, 0xc0, 0xb3, 0xb8, 0x75, 0xc0, - 0x85, 0x1b, 0xcd, 0x99, 0x48, 0xd6, 0x25, 0xde, 0x26, 0x1c, 0x2a, 0xba, 0x04, 0x16, 0x08, 0x44, 0x25, 0x18, 0x1c, - 0xcf, 0x9a, 0x24, 0x91, 0xff, 0x37, 0x76, 0x0f, 0x9c, 0x67, 0x9c, 0x84, 0x2b, 0x90, 0x4e, 0xb8, 0x73, 0x2e, 0x6d, - 0x36, 0x80, 0x96, 0xd9, 0xe7, 0x73, 0x1f, 0x3f, 0x32, 0x29, 0x7f, 0x54, 0x12, 0xce, 0xe7, 0x3e, 0xd3, 0xa4, 0x3c, - 0x53, 0xd9, 0x67, 0x4e, 0x1f, 0xd9, 0x22, 0x46, 0xb1, 0x9e, 0x36, 0x9d, 0x9c, 0x9c, 0x14, 0x14, 0x7a, 0x5d, 0x62, - 0xd6, 0x51, 0x1b, 0x75, 0x83, 0x0a, 0x5d, 0xbe, 0x2e, 0xf9, 0xc9, 0x34, 0xa7, 0xe1, 0x7a, 0xec, 0x33, 0x13, 0xb7, - 0x3b, 0x7c, 0x72, 0x33, 0x5e, 0x8f, 0xc7, 0x3e, 0x25, 0x86, 0x82, 0x48, 0x5b, 0x61, 0x8c, 0x12, 0xb0, 0x54, 0xef, - 0x23, 0x81, 0x96, 0x94, 0x87, 0x0f, 0xd6, 0x71, 0xc0, 0x36, 0xd0, 0x07, 0x12, 0x90, 0x76, 0x55, 0x0f, 0xed, 0x40, - 0x05, 0x76, 0x85, 0xc5, 0x6a, 0xbf, 0x86, 0x92, 0x1b, 0x5c, 0xaa, 0xef, 0x11, 0xc2, 0x98, 0xbd, 0xfe, 0x15, 0xed, - 0x5d, 0xd5, 0x50, 0xc9, 0xc8, 0x87, 0xe7, 0x11, 0x53, 0x0d, 0xf5, 0xb5, 0xe7, 0xce, 0x83, 0x30, 0x4e, 0xbc, 0x89, - 0x7a, 0xd5, 0x3f, 0xf3, 0xb4, 0xcb, 0x65, 0xa2, 0xe9, 0x57, 0xc6, 0x5f, 0xe5, 0x8c, 0x4f, 0x02, 0x15, 0x62, 0xc2, - 0xa7, 0x86, 0x3a, 0xf2, 0xe9, 0xd9, 0x56, 0x4f, 0xa0, 0x5c, 0xac, 0xf3, 0xd7, 0x01, 0xd4, 0x2a, 0xe5, 0x8e, 0xc2, - 0xa4, 0x80, 0x90, 0x3b, 0xea, 0xaf, 0x7a, 0x9f, 0xa4, 0x32, 0xaf, 0xd6, 0x1b, 0xa4, 0x15, 0x92, 0xfc, 0x76, 0xc5, - 0x70, 0xe7, 0xc2, 0x47, 0x90, 0x9e, 0x1f, 0xc9, 0xf6, 0xed, 0x85, 0x7b, 0x7a, 0xf4, 0x75, 0x91, 0xf0, 0x00, 0x02, - 0x01, 0x8c, 0xcb, 0x82, 0x30, 0x51, 0x20, 0x86, 0x17, 0x7c, 0x70, 0x54, 0xb6, 0x87, 0xe5, 0xbd, 0x6a, 0x7a, 0xca, - 0xb1, 0xc0, 0x4b, 0xbc, 0x2c, 0x45, 0xf6, 0x77, 0x0c, 0x47, 0x59, 0x88, 0xd8, 0xc3, 0xbd, 0xb0, 0x60, 0xf9, 0x0a, - 0x64, 0x9b, 0x84, 0xd8, 0x8b, 0x17, 0xf6, 0x93, 0x4d, 0x7c, 0x2a, 0x6e, 0xed, 0xb3, 0x18, 0xd7, 0x12, 0xe8, 0x11, - 0x7e, 0x8d, 0xa7, 0xaa, 0x72, 0x2a, 0xae, 0x1a, 0xac, 0x5b, 0xc0, 0x9f, 0x9a, 0xa0, 0x72, 0x45, 0x52, 0x77, 0x8d, - 0xa7, 0xa0, 0x16, 0x74, 0x57, 0xe9, 0xe8, 0x41, 0x58, 0x9e, 0x8c, 0xa4, 0x4a, 0xc0, 0xb6, 0x16, 0x6f, 0x04, 0xc0, - 0x5c, 0x9c, 0x08, 0x18, 0xa5, 0xd7, 0x40, 0x3f, 0x42, 0xac, 0x2a, 0x31, 0x47, 0x23, 0x94, 0xd3, 0x85, 0x79, 0xc1, - 0x6a, 0x9d, 0x60, 0x0c, 0x72, 0x18, 0x00, 0x4b, 0x55, 0x05, 0xb9, 0x45, 0x40, 0xe6, 0x39, 0x17, 0x94, 0xaa, 0x8a, - 0x37, 0xad, 0x96, 0x71, 0xd1, 0x0d, 0xe0, 0x38, 0x9c, 0x06, 0x4a, 0xf0, 0xe1, 0x31, 0xe2, 0xd3, 0x98, 0x18, 0x79, - 0x02, 0x0f, 0x6d, 0x82, 0x9a, 0xee, 0x1a, 0x04, 0x32, 0xa1, 0x7e, 0xfa, 0x9a, 0x5f, 0x3b, 0x59, 0x88, 0x4b, 0x5c, - 0x98, 0xe6, 0xe8, 0xc9, 0x26, 0x48, 0x4f, 0x01, 0x76, 0x83, 0x27, 0x1b, 0x37, 0x33, 0xa2, 0x52, 0x2f, 0x54, 0xb2, - 0xa0, 0x1a, 0x21, 0x18, 0x46, 0xe9, 0x75, 0xee, 0xd2, 0x98, 0xcf, 0x17, 0xb6, 0x24, 0x95, 0x2b, 0x68, 0xd3, 0x34, - 0xe0, 0x96, 0x4b, 0xab, 0xc8, 0x5b, 0xba, 0xd1, 0x3d, 0x19, 0x3a, 0x19, 0xb2, 0x35, 0x94, 0xae, 0x2a, 0x74, 0x1f, - 0x10, 0x00, 0xe8, 0x6a, 0x50, 0x95, 0xaf, 0xb2, 0x32, 0xc6, 0x67, 0x9b, 0x59, 0x7b, 0xc0, 0xb7, 0xae, 0xd5, 0xe7, - 0xcc, 0x22, 0x91, 0x06, 0x35, 0xe9, 0x6b, 0x71, 0xc3, 0xf4, 0xe2, 0xe2, 0xf4, 0x82, 0xe2, 0x46, 0xc3, 0xc9, 0xd0, - 0x4d, 0x41, 0xe3, 0xc6, 0x99, 0x61, 0xba, 0xc3, 0xfa, 0x15, 0xa5, 0x77, 0x7f, 0xe8, 0x72, 0x30, 0x58, 0x8e, 0x00, - 0x96, 0x83, 0xa8, 0xeb, 0x9f, 0xde, 0x9d, 0x65, 0xf9, 0x15, 0x41, 0x34, 0x3e, 0xe2, 0x1b, 0x33, 0x46, 0x32, 0x23, - 0x42, 0x12, 0x83, 0x32, 0x21, 0x2a, 0xd9, 0x16, 0x8a, 0xe0, 0x68, 0xd0, 0xd8, 0xe9, 0x68, 0x44, 0x83, 0x41, 0x88, - 0xad, 0xa2, 0xf4, 0xe4, 0x80, 0x6a, 0xd3, 0xa5, 0x48, 0x95, 0x00, 0x0c, 0x11, 0xcc, 0x30, 0x87, 0x02, 0xa4, 0x82, - 0x1e, 0x38, 0x39, 0x7f, 0x63, 0x2d, 0x51, 0x01, 0xe9, 0x9c, 0x16, 0x29, 0x1a, 0x6c, 0xa5, 0x0e, 0x4f, 0x30, 0xb9, - 0x23, 0x5c, 0xeb, 0x10, 0xfe, 0xe8, 0xe4, 0x80, 0x1e, 0x95, 0xd2, 0x89, 0xc8, 0x3b, 0x11, 0x02, 0xca, 0x1e, 0xef, - 0xe0, 0x41, 0x47, 0x25, 0x4e, 0xd8, 0x0a, 0x4a, 0xdd, 0x54, 0x55, 0x96, 0x9c, 0x82, 0xe2, 0x71, 0xd6, 0x20, 0x08, - 0x8b, 0x0d, 0xc6, 0xef, 0xaa, 0xb2, 0x74, 0xef, 0x70, 0xe6, 0xe2, 0x8d, 0x7b, 0xa7, 0x39, 0xfc, 0x55, 0x7e, 0xd6, - 0xe2, 0xe2, 0x59, 0x9b, 0xf0, 0xc5, 0x05, 0x0f, 0x2b, 0x41, 0x39, 0x6b, 0x0b, 0xb4, 0x5c, 0xa9, 0x59, 0xdc, 0x85, - 0x58, 0xdc, 0x69, 0xc3, 0xe2, 0x4e, 0xb7, 0x2c, 0xae, 0xcf, 0x17, 0x52, 0xc9, 0x40, 0x17, 0xa1, 0xd7, 0x6c, 0x06, - 0x3c, 0x4e, 0x8f, 0xf4, 0xf8, 0x39, 0x43, 0x38, 0x99, 0xb1, 0x0f, 0x56, 0xa3, 0x0d, 0xb0, 0xaa, 0x83, 0x8b, 0x04, - 0x88, 0xea, 0xc4, 0xb3, 0x53, 0x37, 0x91, 0x04, 0x02, 0x9a, 0x5f, 0x9e, 0x2f, 0xec, 0x52, 0x6c, 0x68, 0x68, 0x8b, - 0x86, 0x99, 0x2e, 0xb6, 0xcc, 0x74, 0x52, 0x38, 0xba, 0x7c, 0xda, 0x74, 0x08, 0xe5, 0x49, 0xc1, 0x1e, 0x04, 0x4b, - 0x7a, 0xdc, 0x32, 0xc5, 0x7d, 0xd8, 0x8c, 0x63, 0xa5, 0x1d, 0xb5, 0x72, 0xe3, 0xf8, 0x36, 0x8c, 0x40, 0x15, 0x0d, - 0xdd, 0x3c, 0x6c, 0x4b, 0x2d, 0xbd, 0x80, 0x47, 0xb9, 0x6a, 0xdc, 0x4c, 0xf9, 0x7b, 0x79, 0x4b, 0xb5, 0x3a, 0x1d, - 0xaa, 0xb1, 0x72, 0x93, 0x84, 0x45, 0x08, 0x74, 0x17, 0xd2, 0x21, 0xfc, 0x3f, 0xd9, 0x66, 0x35, 0x38, 0xc4, 0x97, - 0xb0, 0x3a, 0x62, 0xe8, 0x15, 0x90, 0x60, 0xa4, 0x7b, 0x0a, 0xf4, 0x8d, 0x14, 0x31, 0x33, 0xca, 0x00, 0xff, 0x03, - 0x1e, 0x57, 0x2d, 0x92, 0x7c, 0x3a, 0x9d, 0x23, 0xdd, 0x5a, 0xb9, 0xd3, 0xf7, 0x60, 0xf1, 0xa0, 0xb5, 0x0c, 0xf0, - 0x5e, 0x90, 0xe3, 0x63, 0x46, 0x44, 0x13, 0x4e, 0x72, 0x24, 0x89, 0x58, 0x92, 0xdb, 0x86, 0x82, 0x5b, 0xb9, 0x6b, - 0xce, 0xae, 0x36, 0xad, 0xf4, 0x60, 0xee, 0xe9, 0x15, 0xac, 0x09, 0xa8, 0xcd, 0x1f, 0x0c, 0x33, 0x59, 0x9b, 0x6f, - 0x38, 0x47, 0x3a, 0xa8, 0xc4, 0x2e, 0x21, 0xf1, 0xb5, 0x2d, 0xb8, 0xe5, 0x51, 0x04, 0xb7, 0xd6, 0xa5, 0x7d, 0x95, - 0x3e, 0x9d, 0xe3, 0x2f, 0xe7, 0x2a, 0x7d, 0x3a, 0xc6, 0x5f, 0xad, 0x2b, 0x4c, 0xe9, 0x59, 0x23, 0x26, 0x90, 0xe6, - 0xac, 0x0e, 0x0b, 0xfb, 0x89, 0x0c, 0x73, 0x1f, 0xb0, 0x6d, 0xf8, 0x02, 0x3f, 0x7e, 0xb2, 0x89, 0xc1, 0x15, 0x5d, - 0x9e, 0x43, 0x60, 0x45, 0x7a, 0x5a, 0x5b, 0x3e, 0x6f, 0x28, 0x1f, 0xeb, 0x7f, 0xf0, 0xc5, 0x8f, 0xbb, 0x24, 0xcc, - 0xef, 0x94, 0xa2, 0x90, 0xe3, 0x7a, 0xec, 0x05, 0x6e, 0x74, 0x7f, 0x4d, 0x5c, 0x88, 0x26, 0x49, 0x7b, 0x1f, 0xe5, - 0xdc, 0xff, 0x7d, 0xd1, 0x0e, 0x20, 0x91, 0x74, 0x59, 0xf7, 0xfc, 0xa2, 0x1f, 0xfc, 0x3d, 0x92, 0xe8, 0xbb, 0x02, - 0x9f, 0xca, 0x17, 0xa4, 0xf0, 0xa1, 0xeb, 0x27, 0x1b, 0x8d, 0x55, 0xbb, 0x29, 0xcd, 0xb6, 0x44, 0x40, 0xc2, 0xf2, - 0x20, 0xcf, 0xbb, 0x9c, 0x7a, 0x3d, 0x54, 0xf4, 0x8f, 0xc3, 0x3b, 0xf3, 0xc9, 0x26, 0x39, 0x55, 0x97, 0x6e, 0xf4, - 0x81, 0x4d, 0xcd, 0x89, 0x17, 0x4d, 0x7c, 0x20, 0x1e, 0xc7, 0xbe, 0x1b, 0x7c, 0xe0, 0x8f, 0x66, 0xb8, 0x4e, 0xd0, - 0x74, 0x67, 0x27, 0x8b, 0x2c, 0x60, 0x42, 0xf2, 0x43, 0xa4, 0x6a, 0x6b, 0xa0, 0xa0, 0xbc, 0xca, 0xe4, 0x6f, 0x39, - 0xa1, 0x98, 0xd7, 0x32, 0xc0, 0xf2, 0x1c, 0xac, 0x89, 0xc0, 0x95, 0xdf, 0x50, 0x71, 0xbd, 0x54, 0x43, 0x9e, 0x2a, - 0xa9, 0xdc, 0xb2, 0x5c, 0xb4, 0xd7, 0xd8, 0xc3, 0x7f, 0xff, 0x39, 0x28, 0x79, 0xc8, 0xe7, 0xb2, 0x5e, 0x3e, 0x6d, - 0x86, 0x50, 0x6a, 0x92, 0x0b, 0xd9, 0x03, 0x3e, 0xce, 0x09, 0xcc, 0xe6, 0x4f, 0xcb, 0x8d, 0xdd, 0x38, 0x5e, 0x2f, - 0xd9, 0x94, 0x54, 0x6b, 0xa7, 0xf9, 0xa0, 0x8a, 0x7c, 0x88, 0x3c, 0xb0, 0x5f, 0xd6, 0xad, 0xe3, 0xc3, 0x57, 0x60, - 0xca, 0x05, 0x04, 0x65, 0x38, 0x9b, 0xa9, 0xb9, 0x28, 0x60, 0x47, 0x33, 0xe7, 0xf0, 0x97, 0xf5, 0x37, 0x6f, 0xec, - 0x6f, 0xb2, 0xc6, 0x01, 0x10, 0xc6, 0xc2, 0x2e, 0x85, 0xd3, 0xc5, 0xd2, 0x78, 0xc5, 0x8c, 0x66, 0x6e, 0xd0, 0x3c, - 0x9d, 0xcb, 0xc2, 0x16, 0x5f, 0x31, 0x36, 0x05, 0x82, 0xdb, 0xa8, 0x94, 0x5e, 0xfb, 0xec, 0x86, 0x65, 0x36, 0x2f, - 0xd5, 0x8f, 0xd5, 0xb4, 0xc0, 0xa0, 0x9c, 0x5c, 0x93, 0xc9, 0xa9, 0x3a, 0x69, 0x4a, 0x23, 0x9c, 0x03, 0x9f, 0xb9, - 0x7c, 0xc4, 0x4a, 0x47, 0x6a, 0x64, 0xa8, 0xd2, 0x00, 0x1a, 0x47, 0x76, 0xda, 0x50, 0xde, 0x03, 0x44, 0xdd, 0x30, - 0x36, 0xc3, 0xd1, 0x7b, 0x90, 0xc4, 0x80, 0xc3, 0xc9, 0x87, 0x93, 0xa7, 0xe5, 0x52, 0x93, 0x26, 0x88, 0xd5, 0xc9, - 0xd2, 0x54, 0x12, 0xd2, 0x08, 0x33, 0x70, 0xf4, 0x87, 0x10, 0xe2, 0xaa, 0xda, 0xb5, 0x51, 0x8a, 0x33, 0x1f, 0x63, - 0x8a, 0xef, 0x80, 0xc5, 0x71, 0x23, 0xc0, 0xb2, 0x45, 0x37, 0xd4, 0xbc, 0x76, 0x11, 0x1e, 0x79, 0xb9, 0x61, 0x1b, - 0x40, 0x12, 0xe0, 0x04, 0xcb, 0xdf, 0xc2, 0xeb, 0xe5, 0x7a, 0xc9, 0x0d, 0xf9, 0xa2, 0xf9, 0x58, 0xe5, 0x46, 0x56, - 0x4d, 0xef, 0x6f, 0x55, 0x3e, 0xa8, 0xc2, 0x35, 0x5d, 0x3b, 0x34, 0xad, 0x80, 0x7a, 0x2b, 0x52, 0x25, 0xec, 0x40, - 0x8c, 0xa9, 0x84, 0x5f, 0xd9, 0x6c, 0xc6, 0x26, 0x49, 0xac, 0x0b, 0x19, 0x53, 0x16, 0x56, 0x1b, 0xb4, 0x77, 0x8f, - 0x06, 0xea, 0x0f, 0x10, 0x5c, 0x44, 0x44, 0x9f, 0xe3, 0x03, 0x12, 0x3c, 0x53, 0x3d, 0x98, 0xa8, 0xc7, 0x22, 0x88, - 0xf8, 0x57, 0x40, 0xcc, 0x5c, 0x53, 0x8e, 0x43, 0xe3, 0xf7, 0x4f, 0xbe, 0x2f, 0xc2, 0xcc, 0xdc, 0x6f, 0x3b, 0x2a, - 0xda, 0x76, 0x7c, 0x37, 0xce, 0x37, 0x1d, 0xc7, 0x4e, 0x55, 0x03, 0x9c, 0x5a, 0x3f, 0x94, 0xb6, 0x31, 0x5d, 0x50, - 0x03, 0xf5, 0xfc, 0xed, 0xab, 0xbf, 0xbd, 0x79, 0xbd, 0x2f, 0x46, 0xc0, 0x2e, 0xdb, 0xd0, 0xe5, 0x3a, 0xd8, 0xd2, - 0xe9, 0x4f, 0x3f, 0x3c, 0xac, 0xdb, 0x96, 0xf3, 0xc2, 0x51, 0x0d, 0xb2, 0x43, 0x96, 0xf0, 0xe2, 0x24, 0xbc, 0x61, - 0xd1, 0x27, 0x83, 0x41, 0xee, 0xbc, 0x7e, 0xb8, 0x6f, 0x7f, 0x7c, 0xf3, 0xc3, 0xde, 0x43, 0x3d, 0x72, 0x6c, 0xc0, - 0xed, 0x49, 0xb8, 0x7a, 0xc0, 0xec, 0xda, 0xaa, 0xa1, 0x4e, 0xfc, 0x30, 0x66, 0x0d, 0x23, 0x78, 0x75, 0xfe, 0xf6, - 0x3d, 0x82, 0x2b, 0x27, 0x41, 0xa8, 0xab, 0x4f, 0x9b, 0xfc, 0x8f, 0xef, 0xde, 0xbc, 0x7f, 0xaf, 0x1a, 0x98, 0x96, - 0x39, 0x96, 0x7b, 0xe7, 0x9b, 0x78, 0xc7, 0x8d, 0x53, 0xbb, 0xd7, 0xe9, 0x56, 0x23, 0x46, 0xba, 0x38, 0x1b, 0x2a, - 0xab, 0x6c, 0x73, 0x7e, 0xdb, 0xf1, 0x2f, 0x13, 0xf7, 0xbb, 0xd7, 0xbc, 0x6a, 0xf0, 0xd1, 0xf6, 0x2b, 0xb5, 0x50, - 0xb2, 0xf4, 0x82, 0xeb, 0x9a, 0x52, 0xf7, 0xae, 0xa6, 0x14, 0xd8, 0xc7, 0x0a, 0x7e, 0x5c, 0x87, 0x4b, 0x89, 0x1c, - 0x61, 0x77, 0xbb, 0xc1, 0x25, 0xf1, 0x70, 0x9f, 0x30, 0x68, 0x9e, 0x56, 0xa3, 0x3c, 0xea, 0x9a, 0x62, 0xce, 0x78, - 0x65, 0xb0, 0x9d, 0xf8, 0x60, 0x7d, 0xcd, 0x64, 0x3d, 0x63, 0x91, 0x54, 0xe5, 0xbe, 0x13, 0x83, 0x12, 0x57, 0x40, - 0xcd, 0x48, 0x37, 0xc3, 0xef, 0x94, 0x95, 0x3b, 0x05, 0x93, 0x66, 0x73, 0x1c, 0x26, 0x49, 0xb8, 0xec, 0x39, 0xf6, - 0xea, 0x4e, 0x55, 0xfa, 0x42, 0xd8, 0xc1, 0x2d, 0xae, 0x7b, 0xbf, 0xfd, 0xa7, 0x84, 0xe6, 0xa9, 0xfc, 0x3a, 0x61, - 0xcb, 0x15, 0x8b, 0xdc, 0x64, 0x1d, 0xb1, 0x54, 0xf9, 0xed, 0x7f, 0x5f, 0x95, 0x18, 0xfb, 0xbe, 0xdc, 0x86, 0x48, - 0x7a, 0xb9, 0xc9, 0xb5, 0x1f, 0xde, 0x3e, 0xca, 0x7d, 0xab, 0x76, 0x54, 0x5e, 0x78, 0xf3, 0x45, 0x56, 0xfb, 0x34, - 0xd9, 0x32, 0x37, 0x31, 0x7a, 0xd2, 0x07, 0x28, 0xe7, 0xe1, 0x6d, 0xef, 0xb7, 0xff, 0x64, 0x02, 0x9b, 0x9d, 0xbb, - 0xae, 0x7e, 0xa0, 0xc5, 0x15, 0xad, 0xaf, 0x53, 0x59, 0x62, 0x78, 0x5f, 0x59, 0xe0, 0x4a, 0x21, 0xed, 0xca, 0xaa, - 0xf2, 0x6d, 0xcb, 0x9c, 0xbe, 0xf5, 0xe6, 0x8b, 0x4f, 0x9d, 0x14, 0x00, 0x74, 0xe7, 0xac, 0xa0, 0xd2, 0x67, 0x98, - 0xd6, 0xa8, 0xb7, 0xff, 0x82, 0x7d, 0xe2, 0xbc, 0x76, 0x4d, 0xe9, 0x73, 0xcc, 0x86, 0x4b, 0x6e, 0x5f, 0x8d, 0x46, - 0x59, 0x5a, 0x52, 0xb9, 0x3d, 0x78, 0x87, 0x9d, 0x56, 0x4a, 0x38, 0x79, 0xd1, 0xb3, 0x75, 0x0a, 0xdb, 0xb2, 0x07, - 0x40, 0xd0, 0xce, 0xb9, 0x06, 0x1c, 0xcd, 0xf8, 0x9a, 0xdc, 0x95, 0x2a, 0xdf, 0xae, 0x20, 0x6b, 0x28, 0xc5, 0x94, - 0x96, 0x99, 0xd6, 0xd0, 0xa8, 0x1f, 0xce, 0x6d, 0xe4, 0xae, 0x48, 0x49, 0xa0, 0xa0, 0xc6, 0x04, 0x84, 0x2e, 0x25, - 0x2e, 0xfa, 0xc6, 0xf5, 0x6f, 0xf6, 0x63, 0xa8, 0x9a, 0x6f, 0x30, 0xbc, 0x9a, 0xff, 0xbc, 0xcb, 0x1b, 0xef, 0xe5, - 0xfd, 0xef, 0x6e, 0x4c, 0x15, 0xf6, 0xb6, 0xc9, 0xbc, 0xfa, 0xc7, 0xdd, 0xe6, 0xd5, 0x17, 0x7b, 0x99, 0x57, 0xff, - 0xf8, 0xd9, 0xcd, 0xab, 0xdf, 0xca, 0xe6, 0xd5, 0xb0, 0x89, 0xdf, 0xb0, 0xbd, 0x8c, 0x9e, 0x85, 0x91, 0x51, 0x78, - 0x1b, 0x0f, 0x1c, 0xce, 0xf4, 0xc4, 0x93, 0x05, 0x03, 0x29, 0x12, 0x07, 0x97, 0x1f, 0xce, 0xc1, 0x36, 0xb9, 0xd9, - 0xfa, 0xf8, 0x73, 0xd9, 0x1e, 0xfb, 0xe1, 0x5c, 0x95, 0x82, 0xa5, 0x07, 0x3c, 0x58, 0x7a, 0x1e, 0x00, 0x91, 0x0c, - 0xe6, 0x6c, 0x43, 0x84, 0x4b, 0x34, 0x0f, 0x75, 0x61, 0x78, 0xd7, 0x53, 0xf5, 0xcc, 0xf0, 0xbb, 0x25, 0x4c, 0xea, - 0x7a, 0x2a, 0x84, 0x4e, 0xca, 0x1a, 0xb6, 0x9e, 0x8b, 0xb0, 0x50, 0x72, 0x0f, 0x99, 0x83, 0x0d, 0x85, 0x58, 0xda, - 0xa8, 0xbf, 0xdc, 0x39, 0x2f, 0x2f, 0x9d, 0x7e, 0xdb, 0x81, 0xb8, 0x26, 0x20, 0x83, 0xc0, 0x02, 0xbb, 0xdf, 0x6e, - 0x43, 0xc1, 0xad, 0x54, 0xd0, 0x82, 0x02, 0x4f, 0x2a, 0xe8, 0x40, 0xc1, 0x44, 0x2a, 0x38, 0x82, 0x82, 0xa9, 0x54, - 0x70, 0x0c, 0x05, 0x37, 0x6a, 0x7a, 0x19, 0x64, 0xc3, 0x3d, 0xd6, 0xaf, 0x0c, 0x62, 0x3b, 0x45, 0xd9, 0xb1, 0xe1, - 0x80, 0x59, 0x9c, 0x3b, 0xef, 0x85, 0x06, 0xc9, 0x9f, 0x7b, 0x91, 0x71, 0xbb, 0x60, 0x94, 0x63, 0xe1, 0x35, 0x52, - 0x08, 0x56, 0x12, 0x82, 0xcb, 0x91, 0x88, 0x5d, 0x24, 0xe0, 0xa0, 0xa8, 0x3a, 0x88, 0x14, 0xfb, 0xd9, 0xca, 0x89, - 0xf8, 0x4f, 0xd2, 0x5a, 0xe6, 0xef, 0xe8, 0x73, 0x66, 0xb6, 0x05, 0x72, 0x8b, 0x27, 0x4d, 0x96, 0x5b, 0x7f, 0x0e, - 0xfb, 0x94, 0xd7, 0x6c, 0xbc, 0x9e, 0x2b, 0xe7, 0xe1, 0x7c, 0xa7, 0x2d, 0x8a, 0xfc, 0x0a, 0x46, 0xa9, 0x92, 0x82, - 0xce, 0x14, 0xdb, 0x92, 0x7f, 0x8b, 0x1e, 0xd3, 0x62, 0xfd, 0x04, 0xc6, 0xa6, 0x24, 0x84, 0x6a, 0xe1, 0x3b, 0x00, - 0x23, 0xc9, 0x18, 0xe4, 0x1c, 0xe0, 0x2c, 0x3d, 0x5f, 0xb8, 0xd2, 0x78, 0x86, 0xdf, 0xb3, 0x38, 0x76, 0xe7, 0xa2, - 0x7e, 0x75, 0x9c, 0xe0, 0x1b, 0x93, 0x71, 0xe8, 0x08, 0x40, 0x90, 0xf5, 0x7a, 0x15, 0x1b, 0x1e, 0x30, 0x41, 0x06, - 0x73, 0x35, 0xd8, 0x50, 0xb9, 0xc1, 0x8b, 0x67, 0xc1, 0x12, 0x16, 0x4d, 0x53, 0xe0, 0xf0, 0xdf, 0x30, 0xbf, 0x5c, - 0x98, 0xb8, 0xf3, 0x72, 0x11, 0xed, 0x83, 0x54, 0x1e, 0x5b, 0x66, 0x19, 0x52, 0x28, 0xfc, 0x14, 0x53, 0x07, 0x3f, - 0x9c, 0xff, 0xae, 0x76, 0x0e, 0x5b, 0xec, 0x53, 0xde, 0x07, 0x46, 0x90, 0x8c, 0x2c, 0x84, 0xb1, 0x62, 0x01, 0x08, - 0x7b, 0x41, 0xb2, 0x30, 0xd1, 0x2b, 0x5b, 0x6b, 0x05, 0xba, 0x61, 0xe1, 0xda, 0x6e, 0xca, 0xb1, 0x28, 0x7a, 0xd1, - 0x7c, 0xec, 0x6a, 0x4e, 0xeb, 0xd8, 0x10, 0x7f, 0x2c, 0xbb, 0xa3, 0xa7, 0xd8, 0x83, 0x32, 0xf5, 0x6e, 0x36, 0xb3, - 0x30, 0x48, 0xcc, 0x99, 0xbb, 0xf4, 0xfc, 0xfb, 0xde, 0x32, 0x0c, 0xc2, 0x78, 0xe5, 0x4e, 0x58, 0x3f, 0x17, 0xb9, - 0xf4, 0x31, 0xca, 0x11, 0x77, 0xb4, 0x77, 0xac, 0x56, 0xc4, 0x96, 0xd4, 0x3a, 0x0b, 0x62, 0x34, 0xf3, 0xd9, 0x5d, - 0xca, 0x3f, 0x5f, 0xa8, 0x4c, 0x55, 0x71, 0xcb, 0x51, 0x0b, 0xe0, 0x1f, 0x78, 0x84, 0x24, 0x88, 0x0b, 0xd8, 0xe7, - 0x44, 0x78, 0xcf, 0x6a, 0x75, 0x22, 0xb6, 0x54, 0xac, 0x4e, 0x63, 0xe7, 0x51, 0x78, 0x3b, 0x84, 0xd1, 0x62, 0x63, - 0x33, 0x66, 0xfe, 0x0c, 0xdf, 0x98, 0xe8, 0x94, 0x29, 0xfa, 0x31, 0x51, 0x54, 0x03, 0xbd, 0xb1, 0x65, 0x1f, 0x5e, - 0xf7, 0x5a, 0x8a, 0xdd, 0x5f, 0x7a, 0x81, 0x49, 0xd3, 0x39, 0xb6, 0x57, 0x52, 0x5f, 0x32, 0xfc, 0xf4, 0x0d, 0x56, - 0x77, 0x14, 0xbb, 0x0f, 0x2c, 0xf9, 0xcc, 0x0f, 0x6f, 0x7b, 0x0b, 0x6f, 0x3a, 0x65, 0x41, 0x1f, 0xc7, 0x9c, 0x15, - 0x32, 0xdf, 0xf7, 0x56, 0xb1, 0x17, 0xf7, 0x97, 0xee, 0x1d, 0xef, 0xf5, 0xb0, 0xa9, 0xd7, 0x36, 0xef, 0xb5, 0xbd, - 0x77, 0xaf, 0x52, 0x37, 0xe0, 0x00, 0x4a, 0xfd, 0xf0, 0xa1, 0x75, 0x14, 0xbb, 0x34, 0xcf, 0xbd, 0x7b, 0x5d, 0x45, - 0x6c, 0xb3, 0x74, 0xa3, 0xb9, 0x17, 0xf4, 0xec, 0xd4, 0xba, 0xd9, 0xd0, 0xc6, 0x78, 0xdc, 0xed, 0x76, 0x53, 0x6b, - 0x2a, 0x9e, 0xec, 0xe9, 0x34, 0xb5, 0x26, 0xe2, 0x69, 0x36, 0xb3, 0xed, 0xd9, 0x2c, 0xb5, 0x3c, 0x51, 0xd0, 0x6e, - 0x4d, 0xa6, 0xed, 0x56, 0x6a, 0xdd, 0x4a, 0x35, 0x52, 0x8b, 0xf1, 0xa7, 0x88, 0x4d, 0xfb, 0xb8, 0x91, 0xb8, 0x3d, - 0xf9, 0xb1, 0x6d, 0xa7, 0x88, 0x01, 0x2e, 0x0b, 0xb8, 0x09, 0xa5, 0x81, 0x57, 0x9b, 0xbd, 0x6b, 0x2a, 0xf9, 0xe7, - 0x26, 0x93, 0xda, 0x7a, 0x53, 0x37, 0xfa, 0x70, 0xa5, 0x48, 0xb3, 0x70, 0x5d, 0xaa, 0xb6, 0x11, 0x60, 0x30, 0xef, - 0x7a, 0x10, 0xed, 0xb2, 0x3f, 0x0e, 0x23, 0x38, 0xb3, 0x91, 0x3b, 0xf5, 0xd6, 0x71, 0xcf, 0x69, 0xad, 0xee, 0x44, - 0x11, 0xdf, 0xeb, 0x79, 0x01, 0x9e, 0xbd, 0x5e, 0x1c, 0xfa, 0xde, 0x54, 0x14, 0x35, 0x9d, 0x25, 0xa7, 0xa5, 0xf7, - 0x31, 0xd6, 0x8b, 0x87, 0x11, 0x8b, 0x5c, 0xdf, 0x57, 0xac, 0x76, 0xac, 0x30, 0x37, 0x46, 0x0d, 0x84, 0x62, 0xc7, - 0x04, 0x17, 0x8c, 0xeb, 0xe2, 0x1c, 0xae, 0xee, 0xb2, 0x3d, 0xef, 0x1c, 0xad, 0xee, 0xd2, 0xaf, 0x96, 0x6c, 0xea, - 0xb9, 0x8a, 0x96, 0xef, 0x26, 0xc7, 0x06, 0x2d, 0x85, 0xbe, 0x69, 0xd8, 0xa6, 0xe2, 0x58, 0x40, 0x54, 0xe0, 0x47, - 0xde, 0x72, 0x15, 0x46, 0x89, 0x1b, 0x24, 0x69, 0x3a, 0xba, 0x4a, 0xd3, 0xfe, 0x85, 0xa7, 0x5d, 0xfe, 0x43, 0xa3, - 0x7b, 0x9a, 0xb4, 0x7a, 0xa9, 0x7e, 0x65, 0xbc, 0x61, 0xb2, 0x05, 0x12, 0x5c, 0x63, 0x68, 0x7d, 0x24, 0x57, 0xa6, - 0x5b, 0xb2, 0x5a, 0x99, 0x80, 0x9c, 0x55, 0x27, 0x83, 0xa6, 0x62, 0x15, 0xbc, 0x81, 0xa0, 0xc2, 0x1b, 0x36, 0xb8, - 0x90, 0xcc, 0x99, 0x80, 0x58, 0xc1, 0xca, 0xe4, 0x92, 0xf7, 0xa4, 0x89, 0x66, 0xfc, 0x7a, 0x37, 0xcd, 0xf8, 0xcf, - 0x64, 0x1f, 0x9a, 0xf1, 0xeb, 0xcf, 0x4e, 0x33, 0x3e, 0xa9, 0xba, 0xe4, 0x9d, 0x85, 0x03, 0x35, 0xd3, 0x41, 0xc1, - 0xd5, 0x14, 0x51, 0xb0, 0xbb, 0xb3, 0xe4, 0xbf, 0x75, 0xa1, 0x13, 0xbd, 0x51, 0xfa, 0x56, 0xba, 0xb9, 0x81, 0xf0, - 0x7e, 0x1b, 0x0c, 0xfe, 0x1e, 0xc9, 0xcf, 0xb3, 0xd9, 0xe0, 0x75, 0x28, 0x15, 0x64, 0x4f, 0xdc, 0x3c, 0xa7, 0x10, - 0x98, 0x88, 0xde, 0x64, 0x06, 0x54, 0x90, 0xba, 0x09, 0xe2, 0x9a, 0x90, 0x91, 0xfc, 0x34, 0x33, 0x63, 0xec, 0x17, - 0x87, 0xa0, 0x65, 0x86, 0xc1, 0xc2, 0x7b, 0xb5, 0x22, 0x6c, 0x9e, 0xb3, 0x84, 0x87, 0x9b, 0x78, 0x79, 0x7f, 0x36, - 0xd5, 0xce, 0x42, 0x3d, 0xf5, 0xe2, 0xb7, 0x65, 0xdf, 0x51, 0xc1, 0x3a, 0xc8, 0xd3, 0x49, 0xb9, 0x29, 0xa2, 0x14, - 0x22, 0x06, 0x5f, 0x53, 0xf3, 0xd3, 0xc2, 0x4c, 0x7b, 0x72, 0x43, 0x9e, 0x23, 0xb2, 0x72, 0x19, 0x73, 0x57, 0xbc, - 0x0d, 0xa7, 0x00, 0x31, 0xed, 0x25, 0x86, 0xdc, 0x98, 0x52, 0x73, 0x6f, 0x9a, 0xa6, 0x7a, 0x5f, 0x00, 0x42, 0xba, - 0x68, 0xd9, 0x2e, 0x22, 0x2e, 0xce, 0x19, 0x51, 0xae, 0x43, 0x26, 0x05, 0xf1, 0x19, 0x98, 0x5c, 0x70, 0x75, 0x32, - 0x87, 0x99, 0xaa, 0x10, 0xf8, 0xc8, 0x14, 0x47, 0x9a, 0x10, 0xd8, 0x08, 0xc8, 0x06, 0x6c, 0x85, 0x05, 0xa9, 0x1a, - 0x03, 0x13, 0x70, 0xd0, 0x66, 0x04, 0x02, 0xe4, 0x08, 0x29, 0x15, 0xa1, 0x1d, 0x5e, 0x07, 0x1f, 0x52, 0x35, 0xa3, - 0xfd, 0x70, 0xfb, 0x0d, 0x4f, 0x0e, 0xa0, 0xc1, 0xb0, 0x24, 0x81, 0xda, 0x61, 0xea, 0x0a, 0xa4, 0x44, 0x7c, 0x6b, - 0x58, 0xf1, 0xad, 0xf2, 0x6c, 0x23, 0x82, 0x4b, 0x25, 0xee, 0xca, 0x04, 0xb1, 0x07, 0xe2, 0x5e, 0x8e, 0xf1, 0xa4, - 0x38, 0x56, 0xfd, 0x75, 0x0c, 0xb8, 0x11, 0x39, 0x70, 0xc4, 0x3f, 0xfd, 0xc9, 0x3a, 0x8a, 0xc3, 0xa8, 0xb7, 0x0a, - 0xbd, 0x20, 0x61, 0x51, 0x8a, 0xa0, 0xba, 0x44, 0xf8, 0x08, 0xf0, 0x5c, 0x6d, 0xc2, 0x95, 0x3b, 0xf1, 0x92, 0xfb, - 0x9e, 0xcd, 0x49, 0x0a, 0xbb, 0xcf, 0xa9, 0x03, 0xbb, 0xb6, 0x7e, 0x8f, 0x43, 0xf3, 0x39, 0x12, 0x7e, 0x51, 0x95, - 0x9c, 0x91, 0xb7, 0x79, 0x5f, 0x7a, 0x4b, 0xe1, 0xb5, 0x80, 0xfc, 0x70, 0x23, 0x73, 0x0e, 0x58, 0x1e, 0x96, 0xda, - 0x9e, 0xb2, 0xb9, 0x81, 0x58, 0x1b, 0x34, 0x37, 0xe2, 0x8f, 0xd5, 0xd1, 0x15, 0xbb, 0xbe, 0x18, 0x28, 0x1e, 0x7d, - 0x9f, 0x91, 0xf5, 0x5c, 0x48, 0x46, 0x69, 0xec, 0x53, 0x73, 0xcc, 0x66, 0x61, 0xc4, 0x28, 0x14, 0xbb, 0xd3, 0x5d, - 0xdd, 0xed, 0xdf, 0xfd, 0xf6, 0xe9, 0xd7, 0xf7, 0x13, 0x84, 0x89, 0x26, 0x3a, 0xd3, 0x77, 0xf4, 0x56, 0xbd, 0xcf, - 0x80, 0x34, 0x24, 0xc8, 0x4f, 0xc8, 0x51, 0xa4, 0xa7, 0xaa, 0xfd, 0xda, 0x88, 0x97, 0xab, 0x90, 0xdf, 0x79, 0x11, - 0xf3, 0xdd, 0xc4, 0xbb, 0x11, 0x34, 0x63, 0xfb, 0x68, 0x75, 0x27, 0xd6, 0x18, 0x2f, 0xbc, 0x07, 0x2c, 0x52, 0x69, - 0x28, 0x62, 0x91, 0xca, 0xc5, 0xb8, 0x48, 0xfd, 0xca, 0x6c, 0x44, 0x10, 0xa8, 0xd2, 0x4d, 0xdf, 0x59, 0xdd, 0xc9, - 0x57, 0x74, 0xde, 0x2c, 0xbb, 0xa9, 0xcb, 0xd1, 0x3b, 0x97, 0xde, 0x74, 0xea, 0xb3, 0xb4, 0xb0, 0xd0, 0xc5, 0xb5, - 0x94, 0x80, 0x93, 0xc1, 0xc1, 0x1d, 0xc7, 0xa1, 0xbf, 0x4e, 0x58, 0x3d, 0xb8, 0x08, 0x38, 0x2d, 0x3b, 0x07, 0x0e, - 0xfe, 0x2e, 0x8e, 0xb5, 0x03, 0xe4, 0x36, 0x6c, 0x13, 0xbb, 0x0f, 0xc1, 0xfa, 0xcd, 0x76, 0x71, 0xe8, 0xf0, 0x2a, - 0x1b, 0xb4, 0x51, 0x33, 0x11, 0x03, 0xae, 0x25, 0xc2, 0xde, 0x8a, 0xe5, 0xf0, 0xb2, 0x2c, 0x60, 0x79, 0x56, 0x94, - 0x16, 0x27, 0xf3, 0xfb, 0x9c, 0xb1, 0x17, 0xf5, 0x67, 0xec, 0x85, 0x38, 0x63, 0xdb, 0x77, 0xe6, 0xe3, 0x99, 0x03, - 0xff, 0xf5, 0xf3, 0x09, 0xf5, 0x6c, 0xa5, 0xbd, 0xba, 0x53, 0x9c, 0xd5, 0x9d, 0x62, 0xb6, 0x56, 0x77, 0x0a, 0x76, - 0x8d, 0x16, 0x43, 0x86, 0xd5, 0xd2, 0x0d, 0x5b, 0x81, 0x42, 0xf8, 0x63, 0x17, 0x5e, 0x39, 0x87, 0xf0, 0x0e, 0x5a, - 0x75, 0xaa, 0xef, 0x5a, 0xdb, 0x8f, 0x3a, 0x9d, 0x25, 0x81, 0xb4, 0x75, 0x2b, 0x71, 0xc7, 0x63, 0x36, 0xed, 0xcd, - 0xc2, 0xc9, 0x3a, 0xfe, 0x37, 0x1f, 0x3f, 0x07, 0xe2, 0x56, 0x44, 0x50, 0xea, 0x47, 0x34, 0x05, 0xa9, 0xdc, 0x0d, - 0x13, 0x3d, 0x6c, 0xb2, 0x75, 0xea, 0x51, 0x66, 0x81, 0x96, 0x75, 0x58, 0xb3, 0xc9, 0xeb, 0x01, 0xfd, 0xbb, 0xad, - 0x52, 0x33, 0x8a, 0xf9, 0x04, 0xb0, 0x6c, 0x05, 0xc7, 0xc3, 0xa1, 0xc1, 0x57, 0xd3, 0xee, 0xd6, 0x0f, 0xf7, 0x52, - 0x7c, 0xe9, 0x4a, 0x5c, 0x2a, 0xfc, 0xde, 0xe2, 0xee, 0x4b, 0xdb, 0x7b, 0x6d, 0xda, 0x23, 0x95, 0x5e, 0xb7, 0x5c, - 0x08, 0x79, 0xdd, 0x3d, 0xb1, 0xfc, 0xe3, 0x17, 0x87, 0xf0, 0x1f, 0x51, 0xf5, 0xff, 0x4c, 0xea, 0x08, 0xf5, 0xb3, - 0xa4, 0x40, 0xa8, 0x13, 0xa9, 0x84, 0x84, 0xf8, 0xfe, 0xf5, 0x67, 0xb3, 0x87, 0x35, 0xd8, 0xbb, 0x36, 0x19, 0xdb, - 0x95, 0x6b, 0xbf, 0x0c, 0x43, 0xc8, 0x7a, 0x5d, 0xad, 0x2e, 0xc0, 0x43, 0x9e, 0x13, 0xc9, 0x00, 0x1a, 0x09, 0x3e, - 0x82, 0xec, 0x3c, 0x54, 0x6c, 0x43, 0xac, 0xc4, 0x9b, 0x26, 0x56, 0xe2, 0xf5, 0x6e, 0x56, 0xe2, 0xbb, 0xbd, 0x58, - 0x89, 0xd7, 0x9f, 0x9d, 0x95, 0x78, 0x53, 0x65, 0x25, 0x2e, 0x42, 0x61, 0x61, 0x6d, 0x9c, 0xad, 0xf9, 0xcf, 0x9f, - 0x49, 0x85, 0x7a, 0x1e, 0x0e, 0x3a, 0x36, 0x65, 0x0b, 0xb8, 0xf8, 0xaf, 0x19, 0x0b, 0xdc, 0x88, 0xef, 0xd0, 0xe0, - 0x30, 0x67, 0x2d, 0x38, 0x66, 0xc7, 0xef, 0x48, 0xc5, 0x7e, 0x18, 0xcc, 0x7f, 0x04, 0x15, 0x3a, 0x88, 0x03, 0x23, - 0xe9, 0x85, 0x17, 0xff, 0x18, 0xae, 0xd6, 0xab, 0x33, 0xe8, 0xeb, 0x67, 0x2f, 0xf6, 0xc6, 0x3e, 0xcb, 0xa2, 0x78, - 0x90, 0x81, 0x24, 0x97, 0x89, 0x83, 0x4d, 0xb2, 0xf8, 0xe9, 0xde, 0x89, 0x9f, 0x68, 0xb5, 0xcc, 0x7f, 0x93, 0xe5, - 0xa5, 0x5a, 0xcf, 0x88, 0x40, 0xb8, 0xbb, 0xd2, 0xa0, 0x1f, 0xce, 0x8c, 0x5c, 0x84, 0x7a, 0xcd, 0x2c, 0x85, 0x45, - 0x4c, 0x63, 0x3f, 0xac, 0xc2, 0xd4, 0xac, 0x75, 0x23, 0x8b, 0x5e, 0x59, 0x15, 0xc3, 0x2f, 0xc3, 0x75, 0xcc, 0xa6, - 0xe1, 0x6d, 0xa0, 0x1a, 0x01, 0x37, 0xe3, 0xa4, 0x04, 0x80, 0x59, 0x1b, 0xcc, 0xbb, 0xfc, 0x1e, 0x09, 0x65, 0x88, - 0x74, 0x00, 0x69, 0xbf, 0xd7, 0x2b, 0x93, 0x0c, 0x03, 0x4c, 0x9c, 0xa2, 0x9a, 0x25, 0x08, 0x7c, 0xa4, 0x69, 0xe1, - 0xe0, 0x61, 0x2d, 0x85, 0x31, 0x4f, 0x68, 0x71, 0xa9, 0x70, 0xac, 0x05, 0x42, 0xb8, 0x28, 0x42, 0x48, 0xd5, 0x2c, - 0x1c, 0x7f, 0x43, 0x21, 0x3a, 0xf2, 0xb7, 0x10, 0xd1, 0x21, 0x5d, 0xf3, 0xf5, 0xe0, 0x01, 0x95, 0xe8, 0xf1, 0x95, - 0x04, 0xc6, 0xb7, 0x37, 0x2c, 0xf2, 0xdd, 0x7b, 0x4d, 0x4f, 0xc3, 0xe0, 0x7b, 0x00, 0xc0, 0xeb, 0xf0, 0x36, 0x90, - 0x2b, 0x60, 0x9e, 0xb3, 0x9a, 0xbd, 0x54, 0x1b, 0xfa, 0x0b, 0xdc, 0xa0, 0xa4, 0x11, 0x40, 0x86, 0xf9, 0x39, 0xfb, - 0xbb, 0x41, 0xff, 0xfe, 0x43, 0x4f, 0x8d, 0xf3, 0x30, 0xfb, 0xd0, 0x4f, 0xab, 0x3d, 0x3e, 0xf3, 0xf4, 0xe9, 0xa3, - 0xe6, 0x69, 0x6b, 0x13, 0x9f, 0xb9, 0x22, 0x6f, 0xbc, 0x56, 0xd3, 0x5a, 0x6f, 0x3c, 0x05, 0x30, 0x8a, 0x8b, 0x70, - 0x3d, 0x59, 0xa0, 0x29, 0xf4, 0xe7, 0x9b, 0x6f, 0x02, 0x7d, 0x62, 0x82, 0xef, 0x6c, 0xea, 0xa5, 0xa2, 0x1c, 0x0a, - 0xf8, 0xfd, 0x37, 0x10, 0xbb, 0xfa, 0x4f, 0x04, 0x43, 0x75, 0xd7, 0x64, 0x6e, 0xd2, 0x0f, 0xda, 0xbc, 0x7d, 0xc8, - 0x43, 0xcd, 0xa3, 0x42, 0x09, 0xe5, 0x5a, 0x3d, 0x92, 0x49, 0xcb, 0x40, 0x93, 0x23, 0xb0, 0x36, 0x05, 0x97, 0x15, - 0x5f, 0x61, 0x16, 0xb1, 0xe9, 0xdc, 0x15, 0xc5, 0x60, 0x1c, 0x5b, 0x95, 0x90, 0x0c, 0x37, 0x50, 0x61, 0x88, 0xbe, - 0xca, 0xef, 0x96, 0x5e, 0x60, 0x60, 0x02, 0x95, 0xea, 0x1b, 0xf7, 0x0e, 0x52, 0x08, 0x00, 0x72, 0x2b, 0xbf, 0x82, - 0x42, 0x43, 0x76, 0xc0, 0x84, 0x2c, 0x89, 0x6a, 0x2d, 0x24, 0x84, 0x16, 0x6f, 0xf4, 0x85, 0xa2, 0x28, 0x4a, 0xc6, - 0x46, 0x28, 0x19, 0x1f, 0x81, 0xe5, 0xc8, 0x0e, 0x80, 0xb6, 0x24, 0x5d, 0xdd, 0x51, 0x09, 0x70, 0x06, 0xa8, 0x92, - 0x16, 0x05, 0x3c, 0x4a, 0x6e, 0xc7, 0x16, 0x05, 0x82, 0xa1, 0x87, 0x08, 0xa7, 0x6e, 0x04, 0xc1, 0xf4, 0x7b, 0x0a, - 0x32, 0xec, 0xf8, 0x96, 0x4b, 0x82, 0x15, 0x9b, 0x1e, 0x47, 0x7d, 0x56, 0x1f, 0x4e, 0x35, 0x90, 0xb0, 0x20, 0x68, - 0x1d, 0x4a, 0xd9, 0x11, 0x0c, 0x56, 0x83, 0x1b, 0x91, 0x2f, 0xba, 0x4b, 0x96, 0x2c, 0x58, 0xab, 0x98, 0x4e, 0x11, - 0xc3, 0xdb, 0x42, 0x9d, 0xd7, 0x44, 0x6c, 0x01, 0xb6, 0xa9, 0x6f, 0xb9, 0xa0, 0xbb, 0x30, 0xe6, 0x28, 0xd5, 0x35, - 0x26, 0x5c, 0xb1, 0x19, 0x73, 0xdc, 0x56, 0xbe, 0x21, 0xf8, 0x92, 0x86, 0x45, 0x6c, 0xce, 0xbd, 0x88, 0x91, 0x52, - 0xa0, 0xc8, 0x52, 0x5c, 0x5c, 0x24, 0xc0, 0xae, 0xb9, 0xe5, 0x45, 0xcb, 0x34, 0x32, 0x6e, 0x49, 0x50, 0x14, 0xe9, - 0xd5, 0x6e, 0xf8, 0x38, 0x21, 0xa6, 0x5f, 0x63, 0x3f, 0x93, 0x4a, 0x3f, 0x0d, 0x93, 0xfe, 0xc0, 0xee, 0xe9, 0x22, - 0x21, 0x50, 0x7d, 0x60, 0xf7, 0xa0, 0x6f, 0x7f, 0x03, 0xd2, 0x14, 0x75, 0x0b, 0xba, 0x36, 0x20, 0x4b, 0xce, 0x04, - 0xe2, 0x3c, 0x6e, 0x39, 0x40, 0x76, 0xba, 0x05, 0x8b, 0x23, 0x88, 0x03, 0x23, 0xee, 0x8b, 0x43, 0xcc, 0x9d, 0x40, - 0xb4, 0x5a, 0x18, 0x9b, 0x35, 0x47, 0x43, 0x7f, 0xe6, 0xd8, 0xf6, 0x41, 0xa5, 0x3e, 0x08, 0xb2, 0xeb, 0x6a, 0xeb, - 0x46, 0x32, 0x70, 0x6c, 0xd3, 0x7b, 0x66, 0xb5, 0xfa, 0x95, 0x3b, 0x5a, 0x0a, 0xc3, 0x3c, 0x42, 0xf1, 0xd7, 0xf0, - 0xc9, 0x46, 0xab, 0x1c, 0x48, 0xbd, 0xec, 0x54, 0x81, 0x63, 0x4b, 0xb9, 0xfc, 0x6b, 0x54, 0xbd, 0xfa, 0x29, 0x08, - 0x34, 0xa5, 0x04, 0x1b, 0x41, 0x22, 0x01, 0x0d, 0x8e, 0xd1, 0x5f, 0x94, 0xe7, 0x8a, 0x46, 0xc7, 0x47, 0xd7, 0x47, - 0x7d, 0x81, 0x51, 0x84, 0xd7, 0xa1, 0xdc, 0x41, 0xe9, 0x8b, 0x71, 0x19, 0xc3, 0xf1, 0x90, 0xe5, 0x2c, 0xd7, 0xe8, - 0x6d, 0xa5, 0x16, 0xb0, 0xff, 0x86, 0xeb, 0xd3, 0x1a, 0x43, 0x5c, 0x0c, 0xa8, 0x01, 0x69, 0x47, 0x76, 0x76, 0x08, - 0xe1, 0x8e, 0xe4, 0xee, 0x8a, 0x97, 0xe4, 0xfe, 0x9d, 0xe1, 0xa5, 0x83, 0x3a, 0xb4, 0xac, 0xbf, 0xfa, 0xeb, 0xee, - 0x81, 0x5d, 0xb2, 0x60, 0x5a, 0xec, 0xb0, 0x74, 0x7f, 0xed, 0xdf, 0x5d, 0x01, 0xa3, 0x40, 0x3e, 0x9e, 0xb0, 0x06, - 0xa3, 0xa4, 0x61, 0x80, 0x9b, 0x9f, 0x8e, 0x9b, 0xb7, 0x17, 0x15, 0x83, 0x0d, 0x28, 0x98, 0x66, 0xd6, 0x4c, 0x12, - 0x8a, 0x43, 0xd2, 0x07, 0x74, 0x4a, 0xd6, 0x04, 0x21, 0xda, 0xb8, 0x13, 0x13, 0x61, 0x01, 0x9a, 0xb7, 0xf1, 0x78, - 0x18, 0xe7, 0x7d, 0xa5, 0xd6, 0xde, 0x6e, 0xa9, 0x75, 0xb2, 0x4b, 0x6a, 0x4d, 0x0e, 0x77, 0x64, 0xb6, 0x94, 0x39, - 0x1e, 0x0a, 0xe2, 0x5c, 0x76, 0xdd, 0x2c, 0x88, 0xba, 0xd1, 0x3f, 0x4f, 0xb4, 0xaa, 0xf4, 0x46, 0x36, 0x9d, 0x28, - 0xfe, 0x96, 0x18, 0x14, 0xa1, 0x50, 0x97, 0x65, 0xe3, 0x17, 0xb9, 0x6c, 0x9c, 0xb8, 0x9a, 0xdc, 0xd5, 0x4a, 0x50, - 0xff, 0x92, 0x1b, 0x63, 0xc6, 0x1d, 0xe4, 0xee, 0x8c, 0xf9, 0x48, 0x25, 0x07, 0xbd, 0x9c, 0xd1, 0x90, 0xdc, 0x3e, - 0x05, 0x97, 0x51, 0xf4, 0xfe, 0x2c, 0x56, 0xcd, 0xfd, 0xf3, 0xf2, 0x72, 0x90, 0xba, 0xe3, 0x90, 0xb3, 0x62, 0x79, - 0xdb, 0x14, 0x1d, 0xb4, 0xe4, 0xd7, 0xd2, 0x26, 0xc9, 0x3c, 0xa9, 0x08, 0xc0, 0x42, 0x4c, 0x5f, 0xd2, 0x6b, 0x67, - 0x36, 0x10, 0x38, 0xc8, 0x1a, 0xc7, 0xcf, 0xdd, 0xd2, 0x79, 0x4a, 0x35, 0x94, 0xab, 0xae, 0x1d, 0xbc, 0xdd, 0x49, - 0x13, 0x2c, 0xcb, 0x23, 0x10, 0xd6, 0x57, 0x92, 0x04, 0xa1, 0x67, 0x2b, 0x76, 0xbf, 0x86, 0x00, 0xc0, 0xfb, 0xbf, - 0xfc, 0xcc, 0x49, 0x01, 0x90, 0x44, 0x2a, 0xb6, 0xac, 0xf3, 0xc7, 0x43, 0x6c, 0x92, 0xd9, 0x18, 0x56, 0xad, 0x7e, - 0x93, 0xe4, 0x3d, 0x1b, 0xee, 0x66, 0x55, 0x14, 0xe7, 0xf3, 0x1a, 0x3d, 0x31, 0x0e, 0xbe, 0xcb, 0xa2, 0x75, 0x80, - 0x19, 0x64, 0xcc, 0x24, 0x72, 0x27, 0x1f, 0x36, 0xd2, 0xf7, 0xb8, 0x48, 0x14, 0xc4, 0xc5, 0x45, 0xa5, 0x42, 0xdf, - 0xc5, 0x80, 0xcb, 0xac, 0x67, 0xb5, 0x62, 0x49, 0x50, 0xd3, 0x7b, 0x6c, 0xb7, 0xdd, 0x17, 0xb3, 0xc3, 0x92, 0xfc, - 0xb4, 0xd5, 0x29, 0x4a, 0xd7, 0xb3, 0x71, 0x2c, 0xc3, 0x5f, 0xb9, 0x43, 0xea, 0x1f, 0xff, 0xe9, 0x98, 0x7f, 0xb3, - 0xb4, 0x46, 0x9f, 0x32, 0x04, 0x68, 0x5f, 0x50, 0x4c, 0xcb, 0x6a, 0x9a, 0x4a, 0x49, 0xd3, 0xb0, 0x66, 0x9e, 0xef, - 0x9b, 0x3e, 0xb8, 0x05, 0x6d, 0x3e, 0x69, 0x7a, 0xd8, 0xcf, 0x1a, 0x42, 0xfd, 0x7f, 0x42, 0x3f, 0xc5, 0x9d, 0x92, - 0x2c, 0xd6, 0xcb, 0xf1, 0x46, 0x16, 0x94, 0x4b, 0xf2, 0xf3, 0xaa, 0xcc, 0x5c, 0xfe, 0xec, 0x6c, 0x36, 0x2b, 0x4a, - 0x8d, 0x6d, 0xe5, 0x10, 0x25, 0xbf, 0x8f, 0x6d, 0xdb, 0x2e, 0xc3, 0xb7, 0xe9, 0xa0, 0xd0, 0xc1, 0x30, 0x51, 0x08, - 0xdf, 0xdd, 0xbd, 0xa7, 0xfe, 0xa0, 0xd1, 0x52, 0x57, 0x4d, 0xe7, 0x91, 0xb6, 0xda, 0xff, 0x8b, 0xa1, 0x20, 0x6a, - 0xd8, 0x75, 0xfc, 0xab, 0x7b, 0x65, 0x4b, 0x4f, 0xe5, 0x03, 0xfc, 0xb0, 0xc6, 0x3b, 0xf6, 0xfa, 0x1e, 0x4d, 0x9b, - 0xb6, 0x77, 0x6a, 0xe5, 0xd7, 0x6e, 0xc1, 0x66, 0xa9, 0x4f, 0x96, 0x4a, 0x5e, 0xc2, 0x96, 0x71, 0x6f, 0xc2, 0x50, - 0x41, 0x6a, 0x49, 0xb7, 0x2d, 0x5a, 0xf5, 0x98, 0x73, 0xb0, 0xe3, 0x72, 0x04, 0x1e, 0xb6, 0x15, 0x54, 0x56, 0x55, - 0x34, 0x6b, 0xe2, 0x23, 0x78, 0x8b, 0x6d, 0xaa, 0x0a, 0x27, 0xdc, 0xa6, 0x1d, 0xfb, 0x2f, 0x85, 0x7a, 0x0a, 0x50, - 0xa7, 0x1b, 0x61, 0x6d, 0x42, 0xca, 0x13, 0xfc, 0x3b, 0x53, 0xce, 0xbd, 0x58, 0xdd, 0x15, 0x8d, 0xbb, 0xba, 0xa0, - 0x6e, 0xca, 0xaf, 0x32, 0x1a, 0x75, 0x1d, 0xea, 0xcb, 0x4c, 0x80, 0x66, 0xb2, 0x75, 0x0b, 0x58, 0xd0, 0x14, 0x12, - 0xdb, 0xd5, 0xe8, 0xc6, 0x90, 0x9d, 0x85, 0x9d, 0x97, 0xcb, 0xf7, 0xf3, 0xb4, 0xcc, 0x30, 0x07, 0xe3, 0x79, 0x17, - 0x95, 0x7b, 0x85, 0xad, 0x8a, 0xa6, 0x32, 0xb8, 0x07, 0x04, 0x47, 0xaa, 0xac, 0x23, 0xdf, 0xa4, 0xac, 0x6f, 0x9a, - 0xbe, 0xa9, 0xce, 0xbb, 0xb9, 0x7b, 0xa7, 0x03, 0x7a, 0x8d, 0x2a, 0xa8, 0xf6, 0x52, 0xed, 0x95, 0x75, 0xd8, 0x62, - 0x9c, 0xb0, 0x02, 0xe0, 0x42, 0xa2, 0xa0, 0xd1, 0x90, 0x52, 0xc2, 0x7d, 0x34, 0xe9, 0xec, 0xad, 0x8c, 0xac, 0xc5, - 0x3c, 0xb1, 0xbb, 0xfa, 0x2a, 0xd4, 0xb7, 0xd0, 0x0c, 0x02, 0xec, 0x38, 0x76, 0xc2, 0x67, 0x13, 0x76, 0x8c, 0x8c, - 0xae, 0x1c, 0xdc, 0x41, 0x78, 0x4a, 0x4d, 0x8a, 0x4b, 0x4b, 0xa7, 0x14, 0x75, 0x09, 0xdf, 0xd5, 0x0a, 0xef, 0x2f, - 0x0a, 0xd2, 0x78, 0xee, 0xc7, 0xd3, 0xd2, 0xf7, 0xaa, 0xbd, 0xf4, 0x82, 0xfd, 0xeb, 0xba, 0x77, 0x7b, 0xd7, 0x05, - 0xe2, 0x70, 0xef, 0xca, 0x40, 0x5d, 0x92, 0x95, 0x52, 0x32, 0xf8, 0x4e, 0x52, 0x1e, 0xc8, 0x31, 0x28, 0x54, 0x6c, - 0x45, 0x1c, 0xfd, 0xc5, 0x7a, 0x30, 0x3a, 0x39, 0xbd, 0x5b, 0xfa, 0xca, 0x0d, 0x8b, 0x20, 0x03, 0xe6, 0x40, 0x75, - 0x2c, 0x5b, 0x55, 0x30, 0xa2, 0x82, 0x17, 0xcc, 0x07, 0xea, 0x4f, 0x17, 0xdf, 0x98, 0x5d, 0xf5, 0x14, 0xcc, 0x31, - 0x6e, 0xe6, 0x48, 0xe2, 0x9e, 0xbb, 0xf7, 0x2c, 0xba, 0x6e, 0xa9, 0x0a, 0x26, 0xba, 0x24, 0xe2, 0x16, 0xcb, 0x94, - 0x96, 0xba, 0x47, 0x3e, 0x35, 0x45, 0xa4, 0x44, 0x56, 0x01, 0xb1, 0x3a, 0xad, 0xae, 0xe2, 0xb4, 0x0e, 0xad, 0xa3, - 0xae, 0x3a, 0xfc, 0x42, 0x51, 0x4e, 0xa6, 0x6c, 0x16, 0x0f, 0x51, 0x1c, 0x73, 0x82, 0xf4, 0x20, 0xfd, 0x56, 0x14, - 0x6b, 0xe2, 0xc7, 0xa6, 0xa3, 0x6c, 0xf8, 0xa3, 0xa2, 0x00, 0x32, 0xea, 0x29, 0x8f, 0x67, 0xad, 0xd9, 0xe1, 0xec, - 0x45, 0x9f, 0x17, 0xa7, 0x5f, 0x14, 0xaa, 0x1b, 0xf4, 0x6f, 0x4b, 0x6a, 0x16, 0x27, 0x51, 0xf8, 0x81, 0x71, 0x5a, - 0x52, 0xc9, 0x04, 0x45, 0xe5, 0xa6, 0xad, 0xea, 0x97, 0x9c, 0xee, 0x78, 0x32, 0x6b, 0xe5, 0xd5, 0x71, 0x8c, 0x07, - 0xd9, 0x20, 0x4f, 0x0e, 0xc4, 0xd0, 0x4f, 0x64, 0x30, 0x39, 0x66, 0x1d, 0xa0, 0x1c, 0x95, 0xcf, 0x71, 0x2e, 0xe6, - 0x77, 0x02, 0xe1, 0xca, 0x73, 0xcf, 0x8b, 0x18, 0x9b, 0x0d, 0xd4, 0xef, 0x9d, 0x56, 0xd7, 0x70, 0x9c, 0x23, 0xeb, - 0xa8, 0x3b, 0xb1, 0x8d, 0x43, 0xeb, 0xd0, 0x6c, 0x5b, 0x47, 0x46, 0xd7, 0xec, 0x1a, 0xdd, 0x6f, 0xbb, 0x13, 0xf3, - 0xd0, 0x3a, 0x34, 0x6c, 0xb3, 0x0b, 0x85, 0x66, 0xd7, 0xec, 0xde, 0x98, 0x87, 0xdd, 0x89, 0x8d, 0xa5, 0x2d, 0xab, - 0xd3, 0x31, 0x1d, 0xdb, 0xea, 0x74, 0x8c, 0x8e, 0x75, 0x74, 0x64, 0x3a, 0x6d, 0xeb, 0xe8, 0xe8, 0xbc, 0xd3, 0xb5, - 0xda, 0xf0, 0xae, 0xdd, 0x9e, 0xb4, 0x2d, 0xc7, 0x31, 0xe1, 0x2f, 0xa3, 0x6b, 0xb5, 0xe8, 0x87, 0xe3, 0x58, 0x6d, - 0xc7, 0xb0, 0xfd, 0x4e, 0xcb, 0x3a, 0x7a, 0x61, 0xe0, 0xdf, 0x58, 0xcd, 0xc0, 0xbf, 0xa0, 0x1b, 0xe3, 0x85, 0xd5, - 0x3a, 0xa2, 0x5f, 0xd8, 0xe1, 0xcd, 0x61, 0xf7, 0x9f, 0xea, 0x41, 0xe3, 0x1c, 0x1c, 0x9a, 0x43, 0xb7, 0x63, 0xb5, - 0xdb, 0xc6, 0xa1, 0x63, 0x75, 0xdb, 0x0b, 0xf3, 0xb0, 0x65, 0x1d, 0x1d, 0x4f, 0x4c, 0xc7, 0x3a, 0x3e, 0x36, 0x6c, - 0xb3, 0x6d, 0xb5, 0x0c, 0xc7, 0x3a, 0x6c, 0xe3, 0x8f, 0xb6, 0xd5, 0xba, 0x39, 0x7e, 0x61, 0x1d, 0x75, 0x16, 0x47, - 0xd6, 0xe1, 0xcf, 0x87, 0x5d, 0xab, 0xd5, 0x5e, 0xb4, 0x8f, 0xac, 0xd6, 0xf1, 0xcd, 0x91, 0x75, 0xb8, 0x30, 0x5b, - 0x47, 0x5b, 0x5b, 0x3a, 0x2d, 0x0b, 0x60, 0x84, 0xaf, 0xe1, 0x85, 0xc1, 0x5f, 0xc0, 0x9f, 0x05, 0xb6, 0xfd, 0x03, - 0xbb, 0x89, 0xab, 0x4d, 0x5f, 0x58, 0xdd, 0xe3, 0x09, 0x55, 0x87, 0x02, 0x53, 0xd4, 0x80, 0x26, 0x37, 0x26, 0x7d, - 0x16, 0xbb, 0x33, 0x45, 0x47, 0xe2, 0x0f, 0xff, 0xd8, 0x8d, 0x09, 0x1f, 0xa6, 0xef, 0xfe, 0xa9, 0xfd, 0x64, 0x4b, - 0x0e, 0x09, 0xde, 0xbf, 0xe0, 0xff, 0x50, 0x6e, 0xc4, 0x91, 0x71, 0xde, 0xa4, 0x94, 0x7c, 0xb7, 0x5b, 0x29, 0xf9, - 0xcd, 0x7a, 0x1f, 0xa5, 0xe4, 0xbb, 0xcf, 0xae, 0x94, 0x3c, 0x2f, 0xfb, 0xc4, 0xbc, 0x2b, 0xa7, 0x70, 0xfa, 0x6e, - 0x53, 0x16, 0x39, 0x78, 0xae, 0x76, 0x79, 0xb1, 0xbe, 0x82, 0x90, 0x7c, 0xef, 0xc2, 0xc1, 0x37, 0xeb, 0x82, 0xc1, - 0x67, 0x08, 0x38, 0xf6, 0x5d, 0x48, 0x38, 0xf6, 0x87, 0xf5, 0x00, 0xac, 0xcc, 0x38, 0x99, 0xe3, 0x4d, 0xcd, 0x85, - 0xeb, 0xcf, 0x32, 0x12, 0x09, 0x4a, 0xfa, 0x58, 0x0c, 0x0e, 0x67, 0x70, 0x3d, 0x03, 0x27, 0xb3, 0x5e, 0x06, 0x31, - 0x58, 0x04, 0x83, 0x25, 0xc7, 0x2c, 0x4a, 0x4b, 0x8d, 0x2d, 0x11, 0xc4, 0xf0, 0x9a, 0x7b, 0x2f, 0x35, 0xbe, 0x47, - 0x03, 0xe0, 0xfa, 0xde, 0x9d, 0x6a, 0xbf, 0x0a, 0x58, 0xd6, 0x09, 0x03, 0x69, 0xa0, 0xf6, 0xeb, 0xde, 0x17, 0xcd, - 0x70, 0x4b, 0x86, 0xd7, 0xcd, 0x23, 0x85, 0x91, 0x94, 0xdb, 0x3b, 0x45, 0x33, 0xde, 0x5d, 0xd3, 0xac, 0xf9, 0x7c, - 0xa1, 0xf9, 0x16, 0x1b, 0xe2, 0xac, 0xe3, 0x32, 0xa8, 0x4a, 0x09, 0x88, 0x6b, 0x01, 0x92, 0x33, 0xa8, 0xb9, 0xa1, - 0x71, 0x4e, 0xa9, 0xda, 0x0a, 0xd2, 0x3b, 0xb6, 0xf4, 0xae, 0xd0, 0xa7, 0x6c, 0x9c, 0xfc, 0x6c, 0x83, 0x7c, 0x85, - 0xf7, 0x2b, 0x50, 0xa2, 0x9c, 0xe2, 0x19, 0x87, 0x32, 0x9c, 0x37, 0x52, 0xbf, 0x24, 0x8d, 0x48, 0x17, 0xce, 0xa6, - 0x4a, 0x8b, 0x36, 0xba, 0x25, 0x38, 0x6c, 0x29, 0xa8, 0x20, 0xfc, 0x3c, 0x39, 0x01, 0xa4, 0xe4, 0xa8, 0x81, 0x7e, - 0x0e, 0xdb, 0x3a, 0x13, 0xf5, 0x1e, 0xc3, 0x26, 0xe6, 0xc1, 0x92, 0x15, 0x39, 0x4a, 0xcc, 0x66, 0xe6, 0x87, 0x6e, - 0xd2, 0x43, 0x32, 0x4d, 0x22, 0x79, 0x5b, 0xe8, 0xb1, 0xd0, 0xdf, 0x62, 0x4c, 0x27, 0x77, 0xcc, 0x3b, 0x41, 0xcf, - 0x87, 0x6d, 0xf6, 0x77, 0x99, 0xa3, 0xd8, 0xa6, 0x60, 0x8e, 0xe2, 0x74, 0x8e, 0x0d, 0xe7, 0xc8, 0xb0, 0x8e, 0x3b, - 0x7a, 0x2a, 0x0e, 0x9c, 0xdc, 0x65, 0x01, 0x20, 0xe0, 0x00, 0x91, 0x0d, 0xd3, 0x0b, 0xbc, 0xc4, 0x73, 0xfd, 0x14, - 0xe8, 0xe1, 0x22, 0x93, 0xf2, 0xaf, 0x75, 0x9c, 0xc0, 0x1c, 0x05, 0xd1, 0x8b, 0xce, 0x1f, 0xe6, 0x98, 0x25, 0xb7, - 0x8c, 0x05, 0x0d, 0x86, 0x31, 0x65, 0x5f, 0x92, 0xdf, 0xcf, 0xb2, 0x3e, 0x25, 0xab, 0xb5, 0x71, 0x12, 0xf0, 0xfd, - 0x21, 0x1c, 0x1f, 0xd2, 0x91, 0xf1, 0x6b, 0x13, 0xc2, 0xfd, 0xd7, 0x6e, 0x84, 0x9b, 0xb0, 0x7d, 0x10, 0xee, 0xbf, - 0x3e, 0x3b, 0xc2, 0xfd, 0x55, 0x46, 0xb8, 0x05, 0xbf, 0xbf, 0x5c, 0xc3, 0xf4, 0x1e, 0x9f, 0x35, 0x48, 0x7e, 0xf2, - 0x5c, 0x3d, 0x20, 0x02, 0x1e, 0xf2, 0x42, 0x88, 0xe8, 0x5b, 0x2f, 0x0b, 0x49, 0x36, 0x51, 0x00, 0x8a, 0x89, 0x35, - 0x28, 0xa1, 0x9f, 0x37, 0x18, 0x0c, 0xec, 0x2c, 0xa9, 0x1f, 0xbb, 0x55, 0xce, 0x82, 0xc4, 0xb7, 0xde, 0x71, 0x3e, - 0x12, 0x14, 0xba, 0xdf, 0x84, 0xd1, 0xd2, 0xc5, 0xa8, 0xad, 0x2a, 0x26, 0xe7, 0x86, 0x07, 0x1b, 0x9c, 0x68, 0x27, - 0x61, 0x30, 0xcd, 0xb4, 0x92, 0x6c, 0x70, 0x49, 0x14, 0xb7, 0x7a, 0xcf, 0xdc, 0x48, 0x35, 0xe8, 0x35, 0x2c, 0xee, - 0xb3, 0xb6, 0xfd, 0xac, 0x75, 0xf8, 0xec, 0xc8, 0x86, 0xff, 0x1d, 0xd6, 0x4e, 0x0d, 0x5e, 0x71, 0x19, 0x06, 0x90, - 0x1f, 0x50, 0xd4, 0x6c, 0xaa, 0x76, 0xcb, 0xd8, 0x87, 0xbc, 0xd6, 0x71, 0x7d, 0xa5, 0xa9, 0x7b, 0x9f, 0xd7, 0xa9, - 0xad, 0xb1, 0x08, 0xd7, 0xd2, 0xb0, 0x6a, 0x46, 0xe3, 0x05, 0x6b, 0x90, 0xb3, 0x4b, 0x35, 0xe4, 0xd7, 0x7c, 0xba, - 0xf9, 0xbc, 0x58, 0x3b, 0xbd, 0xca, 0x93, 0x90, 0x8a, 0x64, 0x88, 0x3b, 0x21, 0xc8, 0x55, 0x94, 0x36, 0xc6, 0xe9, - 0xc6, 0x4c, 0x11, 0x10, 0xa5, 0x3b, 0x4b, 0x1d, 0xe9, 0xd2, 0x02, 0x25, 0xd1, 0x3a, 0x98, 0x68, 0xf8, 0xd3, 0x1d, - 0xc7, 0x9a, 0x77, 0x10, 0x59, 0xfc, 0xc3, 0x3a, 0xae, 0x9a, 0x3b, 0xb4, 0xf3, 0x8c, 0x6d, 0xb1, 0x58, 0x15, 0xf7, - 0x59, 0x62, 0x44, 0xa8, 0xc7, 0xa6, 0xa5, 0x35, 0x07, 0xee, 0xb3, 0xac, 0xe1, 0xb3, 0xc4, 0x08, 0x9e, 0x83, 0xee, - 0x73, 0x60, 0x3f, 0x7d, 0x4a, 0xb5, 0x20, 0xfb, 0x39, 0x4d, 0xeb, 0x74, 0x92, 0x07, 0xfb, 0x54, 0xdc, 0x79, 0x48, - 0xf1, 0x3e, 0x7b, 0x13, 0x23, 0x7c, 0xfe, 0x7c, 0x38, 0x70, 0x74, 0xcc, 0x06, 0x2a, 0xb2, 0x7a, 0xf3, 0x44, 0xb3, - 0xe7, 0xfb, 0x19, 0x1a, 0xe9, 0xb5, 0x2e, 0xb0, 0x2b, 0xe0, 0x99, 0x6c, 0xe1, 0x8e, 0xc0, 0xb1, 0x17, 0x24, 0x7e, - 0x23, 0x83, 0x02, 0x57, 0x18, 0xfc, 0x88, 0x3a, 0x19, 0xd7, 0xd5, 0xb6, 0x6c, 0xcb, 0x56, 0xb3, 0x86, 0x33, 0x6f, - 0x3e, 0xd8, 0x84, 0x89, 0x0b, 0x29, 0x34, 0xfd, 0x70, 0x0e, 0x7e, 0x74, 0x89, 0x97, 0xf8, 0x90, 0x8f, 0x11, 0x1c, - 0xea, 0x96, 0xc4, 0x97, 0xa7, 0xdc, 0xbb, 0xc1, 0x8d, 0x3e, 0x60, 0x4e, 0x6e, 0xe1, 0x42, 0x8b, 0xf1, 0xe7, 0xbe, - 0x87, 0xcb, 0x50, 0x53, 0x35, 0x90, 0x0d, 0xb0, 0x28, 0x36, 0x65, 0x6f, 0xa1, 0x9e, 0x02, 0x6d, 0x74, 0x95, 0x4f, - 0x62, 0x16, 0xb9, 0x4b, 0xc8, 0x5d, 0xb4, 0x49, 0x0d, 0x8e, 0x69, 0x55, 0x8e, 0x6a, 0x15, 0xe7, 0xc5, 0x91, 0xa1, - 0xb4, 0x1c, 0x43, 0xb1, 0x01, 0xdd, 0xaa, 0xa9, 0xb1, 0x49, 0xaf, 0xfa, 0xbb, 0x0c, 0x1e, 0x08, 0xbf, 0x3c, 0xa6, - 0x79, 0x90, 0xa9, 0x03, 0x57, 0x25, 0x25, 0x14, 0x77, 0x58, 0x93, 0x32, 0x92, 0x78, 0xa4, 0xf4, 0xbc, 0x60, 0x77, - 0x89, 0x8e, 0xf9, 0x0a, 0x79, 0x15, 0x4f, 0xdf, 0xa0, 0xa3, 0xaf, 0x17, 0x28, 0xde, 0xc7, 0x8f, 0x9a, 0x07, 0xce, - 0x4c, 0x03, 0x09, 0x3e, 0xf0, 0xac, 0x17, 0x00, 0xe6, 0xe5, 0x6a, 0x7a, 0x04, 0x16, 0x78, 0x1a, 0xc2, 0xbf, 0x79, - 0xb1, 0xf8, 0xc1, 0xcd, 0x24, 0x2c, 0xdf, 0x0d, 0xe6, 0x80, 0xd2, 0xdc, 0x60, 0x5e, 0x31, 0xc7, 0x22, 0x5f, 0xe5, - 0x52, 0x69, 0xde, 0x55, 0x6e, 0x2a, 0x15, 0xbf, 0xbc, 0xbf, 0xa0, 0x7c, 0xac, 0x9a, 0x0a, 0xb7, 0x1c, 0x3a, 0xd6, - 0xe6, 0x9a, 0xdc, 0xe7, 0x83, 0x2f, 0x4f, 0x96, 0x2c, 0x71, 0x49, 0x0d, 0x04, 0xcc, 0x2f, 0x90, 0x03, 0x0a, 0xbf, - 0x68, 0x78, 0x4c, 0xa7, 0xc1, 0x94, 0xdd, 0x78, 0x13, 0xce, 0x97, 0x1a, 0x0a, 0xbf, 0xa7, 0x4c, 0xb4, 0xf8, 0x1c, - 0x38, 0x06, 0x39, 0x1c, 0x4c, 0x5c, 0x0c, 0x51, 0x3c, 0x08, 0x42, 0x75, 0xf8, 0x65, 0xe6, 0x9b, 0xd9, 0xb4, 0x08, - 0x90, 0x14, 0xfd, 0x32, 0x62, 0xfe, 0xbf, 0x07, 0x5f, 0xc2, 0xc5, 0xfd, 0xe5, 0x95, 0xaa, 0xf7, 0x13, 0x6b, 0x11, - 0xb1, 0xd9, 0xe0, 0xcb, 0x9a, 0xe4, 0xe0, 0xc8, 0xde, 0xd3, 0x58, 0xd4, 0x76, 0x2b, 0x0f, 0x15, 0xd7, 0xde, 0x8b, - 0xa9, 0x1f, 0x72, 0x6e, 0x1d, 0x38, 0xc0, 0x4d, 0x81, 0xc7, 0x76, 0xfa, 0xc8, 0x3f, 0x8f, 0x7d, 0x77, 0xf2, 0xa1, - 0x4f, 0x6f, 0x0a, 0x0f, 0x26, 0xdc, 0xd6, 0x13, 0x77, 0xd5, 0xc3, 0xeb, 0x55, 0x2e, 0x04, 0xd7, 0x6c, 0x2a, 0xcd, - 0x28, 0xbb, 0xda, 0xbd, 0x8c, 0x5b, 0x79, 0x83, 0x5f, 0xc6, 0x4f, 0xdd, 0x2e, 0xbc, 0x84, 0x89, 0x4f, 0xe1, 0x43, - 0x9a, 0x0a, 0x46, 0x9d, 0x58, 0x54, 0x64, 0xac, 0xad, 0xb6, 0xe2, 0x74, 0xbf, 0xed, 0xdc, 0x38, 0xf6, 0xa2, 0xe5, - 0x58, 0xdd, 0x9f, 0x9d, 0xee, 0xa2, 0x6d, 0x1d, 0xfb, 0x66, 0xdb, 0x3a, 0x86, 0x3f, 0x3f, 0x1f, 0x5b, 0xdd, 0x85, - 0xd9, 0xb2, 0x0e, 0x7f, 0x76, 0x5a, 0xbe, 0xd9, 0xb5, 0x8e, 0xe1, 0xcf, 0x39, 0xb5, 0x02, 0x06, 0x88, 0xf8, 0x9d, - 0x2f, 0x0b, 0x58, 0x40, 0xfa, 0x9d, 0xe9, 0x64, 0x8d, 0xc2, 0xf5, 0x56, 0xa3, 0xd7, 0x05, 0x94, 0x41, 0x29, 0x7b, - 0xd0, 0x14, 0xa1, 0xaf, 0x05, 0x03, 0x46, 0x49, 0x7a, 0x84, 0x79, 0x9b, 0xf0, 0x41, 0x17, 0x79, 0x51, 0x6a, 0x8f, - 0x11, 0x6f, 0x53, 0x9f, 0x0b, 0x44, 0x24, 0x70, 0x25, 0x45, 0xf0, 0x4f, 0x2b, 0x0c, 0x69, 0x27, 0xd2, 0x5e, 0x49, - 0x58, 0x29, 0x4f, 0x22, 0x9e, 0xee, 0x1e, 0x38, 0x7a, 0xe1, 0xb3, 0x2c, 0x89, 0xe5, 0x67, 0xed, 0x5b, 0xca, 0x2e, - 0xf6, 0x49, 0xfd, 0x60, 0x56, 0xa5, 0x3c, 0x21, 0x12, 0x44, 0x02, 0x9f, 0x7a, 0x51, 0x36, 0x3c, 0x09, 0x45, 0x3b, - 0xf5, 0x49, 0x54, 0x74, 0xc8, 0xf6, 0x77, 0x06, 0x54, 0xf2, 0x8d, 0xeb, 0x4b, 0x86, 0x6c, 0x52, 0xcb, 0x47, 0x19, - 0xe6, 0x7f, 0xfa, 0x34, 0x1f, 0x9c, 0x59, 0x1a, 0xf7, 0x89, 0xd3, 0x81, 0x6b, 0xb7, 0xc3, 0xda, 0x5b, 0x6d, 0x2a, - 0x77, 0xc7, 0x90, 0xcf, 0x83, 0x47, 0x0b, 0xbb, 0x29, 0x61, 0xb1, 0xd1, 0x68, 0xd8, 0x59, 0xb1, 0xd7, 0x80, 0xe8, - 0xfb, 0x25, 0x56, 0x47, 0xd5, 0xfb, 0x81, 0x30, 0x3f, 0x08, 0xb6, 0xc4, 0xcd, 0xe7, 0xbc, 0x98, 0x0a, 0xa0, 0xd9, - 0x32, 0x8f, 0x1d, 0x0e, 0xe2, 0x7f, 0xf6, 0x24, 0xd0, 0x59, 0x13, 0xec, 0x25, 0x4a, 0xa7, 0xb5, 0xe0, 0xbc, 0x97, - 0xdd, 0xab, 0x74, 0xa1, 0xb2, 0xf8, 0x54, 0x85, 0x22, 0x48, 0x03, 0x8b, 0x99, 0x9f, 0x33, 0x63, 0xd1, 0xec, 0xb6, - 0xc8, 0x0b, 0x0c, 0x0f, 0x93, 0x90, 0x08, 0xc7, 0x51, 0xfd, 0xe9, 0xd3, 0xc6, 0x4b, 0x88, 0x8c, 0x73, 0x62, 0x96, - 0x64, 0xb9, 0x29, 0x55, 0x19, 0xbf, 0xa9, 0x32, 0x8a, 0xc9, 0xfa, 0x45, 0xac, 0x21, 0x6c, 0x5c, 0x69, 0xef, 0xe1, - 0xcf, 0x31, 0x73, 0x13, 0x8b, 0x2b, 0x4b, 0x35, 0xe9, 0x72, 0x37, 0x1c, 0xd6, 0x06, 0xeb, 0x56, 0x1e, 0xf9, 0x92, - 0x47, 0x96, 0x7d, 0xb2, 0x79, 0xb9, 0xe6, 0x51, 0x1d, 0xa0, 0x8f, 0x8f, 0x76, 0x1e, 0x38, 0xec, 0x6d, 0xe2, 0x52, - 0x40, 0x17, 0xf9, 0xca, 0x0d, 0x13, 0x57, 0xa4, 0x5b, 0x04, 0xba, 0xbc, 0x5f, 0x6b, 0x7e, 0x21, 0x45, 0x7e, 0x18, - 0xbe, 0xbd, 0xf8, 0x5a, 0xe1, 0xfb, 0x9f, 0xac, 0x05, 0x90, 0x91, 0xa1, 0x94, 0x3c, 0x03, 0x4a, 0xc9, 0xa3, 0xf0, - 0x9c, 0x50, 0x90, 0xa7, 0x26, 0x3d, 0x20, 0x08, 0xa2, 0x00, 0x9a, 0x6c, 0x28, 0x96, 0x6b, 0x3f, 0xf1, 0x56, 0x6e, - 0x94, 0x1c, 0x60, 0x3e, 0x1e, 0x40, 0x72, 0x6a, 0x53, 0x3c, 0x08, 0x32, 0xc3, 0x10, 0x01, 0x57, 0x93, 0x40, 0xd8, - 0x61, 0xcc, 0x3c, 0x3f, 0x33, 0xc3, 0x10, 0x1f, 0x70, 0x27, 0x13, 0xb6, 0x4a, 0x06, 0x85, 0xbc, 0x3f, 0xe1, 0x24, - 0x61, 0x89, 0x19, 0x27, 0x11, 0x73, 0x97, 0x6a, 0x16, 0xd8, 0xbb, 0xda, 0x5f, 0xbc, 0x1e, 0x2f, 0xbd, 0x24, 0x8b, - 0x8c, 0x4b, 0x13, 0x04, 0x83, 0x08, 0x18, 0xe2, 0x70, 0x94, 0x72, 0x10, 0x9e, 0x87, 0xf3, 0xd2, 0x8e, 0xca, 0x29, - 0x97, 0x53, 0x8c, 0xbb, 0x4e, 0x9c, 0x0c, 0x48, 0x8b, 0x27, 0xa1, 0x7f, 0xcd, 0x63, 0x58, 0x64, 0x01, 0x7c, 0xd5, - 0xe1, 0x09, 0x67, 0x6f, 0x15, 0x0c, 0xbb, 0xa2, 0x76, 0x6c, 0x88, 0x2c, 0xdf, 0x14, 0xdd, 0xe2, 0x80, 0x57, 0x86, - 0xab, 0x89, 0x7a, 0xc6, 0xe4, 0x20, 0x34, 0x96, 0x0b, 0x20, 0x84, 0x0a, 0x06, 0x33, 0x0b, 0x67, 0x98, 0xb9, 0x53, - 0xe2, 0xa8, 0x90, 0x56, 0xfa, 0xf8, 0xf1, 0xd5, 0xe8, 0xb7, 0xff, 0x40, 0x06, 0x93, 0x85, 0x23, 0x62, 0x4a, 0x5c, - 0xca, 0xb5, 0x38, 0xf5, 0x69, 0x8c, 0xd0, 0x58, 0x8a, 0x4d, 0x45, 0x68, 0x1d, 0xb1, 0xb5, 0xd2, 0xd1, 0x95, 0x08, - 0xad, 0x08, 0xc9, 0x8d, 0x74, 0x11, 0xf9, 0x62, 0x04, 0xcb, 0x3b, 0x12, 0x01, 0x57, 0x94, 0x5f, 0xee, 0x5e, 0x1e, - 0x2b, 0x79, 0xec, 0xa1, 0x3a, 0x8b, 0x1e, 0xda, 0x43, 0xc3, 0x13, 0x57, 0x41, 0xa2, 0x05, 0xc9, 0x8f, 0xb8, 0x77, - 0x00, 0xd3, 0x5c, 0x84, 0x4b, 0x66, 0x79, 0xe1, 0xc1, 0x2d, 0x1b, 0x9b, 0xee, 0xca, 0x23, 0xbb, 0x1c, 0x94, 0xbb, - 0x29, 0x44, 0xf9, 0x65, 0xe6, 0x2e, 0x44, 0x5f, 0xa7, 0x39, 0x28, 0xc3, 0x62, 0x2c, 0xcd, 0x4e, 0x2b, 0xd7, 0x03, - 0x42, 0xfc, 0x02, 0x09, 0x8e, 0xe1, 0xf0, 0xe4, 0xc0, 0x1d, 0x16, 0x83, 0xf9, 0x5a, 0x22, 0xeb, 0x4c, 0xf1, 0x12, - 0x38, 0xa5, 0x98, 0xbc, 0x22, 0xfc, 0x6e, 0xfe, 0x60, 0x86, 0xb3, 0x99, 0x1c, 0x80, 0xd7, 0x2a, 0x0e, 0x2f, 0x03, - 0x5a, 0xbe, 0xa5, 0xc3, 0x15, 0x7d, 0xa9, 0xfa, 0x89, 0xec, 0xa7, 0xda, 0xc3, 0xc8, 0xdb, 0x30, 0x67, 0x38, 0xee, - 0x95, 0x40, 0xbe, 0x19, 0xc4, 0x1e, 0x53, 0x25, 0x8e, 0x47, 0xca, 0x69, 0x23, 0x1a, 0x28, 0x97, 0x47, 0x83, 0x01, - 0xa1, 0xb9, 0x32, 0xb6, 0x03, 0x20, 0xd6, 0x64, 0xe0, 0x81, 0xc9, 0x26, 0xd0, 0xd0, 0x24, 0x77, 0x59, 0x6c, 0x54, - 0x9e, 0x4e, 0x75, 0x8c, 0x07, 0xae, 0xd8, 0x7e, 0x85, 0x0d, 0x0a, 0x1b, 0x8f, 0xaf, 0x3b, 0xe0, 0x77, 0xd1, 0x4f, - 0x09, 0xcd, 0x2b, 0x5f, 0x11, 0x46, 0x37, 0x7d, 0xf7, 0x3e, 0x94, 0xcc, 0x98, 0x78, 0x44, 0x93, 0x73, 0x2c, 0xbd, - 0x10, 0x9e, 0xc4, 0x95, 0x83, 0x96, 0x25, 0x32, 0xa9, 0x1e, 0x36, 0x39, 0xf9, 0xc8, 0xae, 0xb3, 0x26, 0xd7, 0x2d, - 0x4e, 0x06, 0x91, 0x67, 0x9a, 0x9f, 0xc3, 0xc2, 0x4b, 0x44, 0x0b, 0xe9, 0xc9, 0x01, 0xcc, 0x0f, 0xa2, 0xb0, 0x14, - 0x08, 0x27, 0x4f, 0x87, 0x50, 0x2f, 0x6e, 0x4c, 0xa6, 0x58, 0x67, 0x53, 0x41, 0xf3, 0x21, 0x63, 0x29, 0x65, 0xe5, - 0x93, 0xaa, 0x54, 0x69, 0x19, 0xbb, 0x9e, 0x08, 0xdc, 0x9d, 0xf5, 0xe7, 0x87, 0x35, 0xa6, 0xfc, 0x49, 0xfb, 0x09, - 0x13, 0x41, 0x0e, 0xce, 0x93, 0x86, 0x38, 0x08, 0x4d, 0x55, 0x88, 0x9e, 0xdd, 0x52, 0x21, 0xdf, 0xc7, 0xdb, 0x6a, - 0xe5, 0x94, 0x53, 0x56, 0x6d, 0xe5, 0x6a, 0xea, 0x63, 0xdc, 0xf1, 0x95, 0xda, 0x58, 0x0a, 0xf5, 0xce, 0x93, 0x01, - 0x54, 0x15, 0xb2, 0x78, 0x77, 0xb5, 0xa2, 0xca, 0x7a, 0xff, 0xe4, 0x80, 0xd8, 0xd2, 0x21, 0xed, 0xb0, 0xe1, 0x09, - 0x98, 0x72, 0xd3, 0xa2, 0xbb, 0xab, 0x15, 0x5f, 0x52, 0xfa, 0x45, 0x6f, 0x0e, 0x16, 0xc9, 0xd2, 0x1f, 0xfe, 0x1f, - 0x59, 0xea, 0xe1, 0x3d, 0xe8, 0x69, 0x03, 0x00}; + 0xdb, 0xdb, 0xdb, 0xff, 0xd3, 0xdc, 0xb3, 0x6e, 0xb7, 0x6d, 0x23, 0xfd, 0xbf, 0x4f, 0xc1, 0x30, 0xd9, 0x94, 0x4c, + 0x48, 0x9a, 0x94, 0x2c, 0x5b, 0x91, 0x2c, 0xb9, 0xcd, 0xa5, 0x5b, 0x77, 0xdd, 0xa6, 0x27, 0x71, 0xfb, 0xed, 0xae, + 0xeb, 0x63, 0x51, 0x12, 0x24, 0x71, 0x43, 0x91, 0x3a, 0x24, 0xe5, 0x4b, 0x15, 0xee, 0xb3, 0xec, 0x23, 0x7c, 0xcf, + 0xd0, 0x27, 0xfb, 0xce, 0xcc, 0x00, 0x24, 0x78, 0x93, 0xe4, 0x4d, 0xda, 0x7e, 0xa7, 0x4d, 0x22, 0x82, 0x00, 0x08, + 0x0c, 0x80, 0xb9, 0x61, 0x2e, 0x26, 0x98, 0x36, 0x9a, 0xeb, 0xc8, 0x67, 0xc1, 0x24, 0x84, 0xc4, 0x24, 0xa9, 0x40, + 0xf8, 0xac, 0x84, 0xf0, 0x21, 0x2e, 0x2a, 0x4f, 0xac, 0xf1, 0x7e, 0x11, 0xde, 0x7e, 0xed, 0xfb, 0xb2, 0xfc, 0x2e, + 0x98, 0x3e, 0x2e, 0xd2, 0x16, 0x10, 0x88, 0x06, 0xd7, 0x10, 0x96, 0x17, 0x5f, 0xf3, 0x8b, 0xe3, 0xe9, 0xf5, 0xf8, + 0xfe, 0x9a, 0x2b, 0xa7, 0xb3, 0xc0, 0xb4, 0xaf, 0x46, 0x27, 0x53, 0xef, 0x46, 0x41, 0xce, 0x74, 0xa0, 0x82, 0x57, + 0x8f, 0xcf, 0xc6, 0xeb, 0x24, 0x09, 0x03, 0x33, 0x0a, 0x6f, 0xd5, 0xe1, 0x09, 0x3d, 0x88, 0x0a, 0x2e, 0x3d, 0xaa, + 0xca, 0x57, 0x13, 0xdf, 0x9b, 0x7c, 0x18, 0xa8, 0x4f, 0x36, 0xde, 0x60, 0x58, 0xe2, 0x3f, 0xed, 0x54, 0x1d, 0xc2, + 0x58, 0x95, 0xaf, 0x7d, 0xff, 0xe4, 0x80, 0x5a, 0x0c, 0x4f, 0x0e, 0xa6, 0xde, 0xcd, 0x50, 0xca, 0x11, 0xc2, 0x2f, + 0xd0, 0x06, 0x3c, 0x16, 0x63, 0x66, 0x72, 0x14, 0xa3, 0x73, 0xff, 0x84, 0x69, 0xb9, 0x14, 0x04, 0x41, 0x47, 0x68, + 0xbc, 0xda, 0x04, 0xf5, 0xaa, 0x3e, 0xf0, 0xfc, 0x1f, 0x3f, 0x6a, 0x99, 0x41, 0xe2, 0x42, 0x8a, 0xd6, 0x85, 0xf7, + 0x3d, 0x58, 0xc5, 0xc0, 0x90, 0x23, 0xba, 0x26, 0x62, 0x8a, 0xf9, 0xba, 0x31, 0x49, 0x0d, 0x4c, 0xb5, 0xe2, 0xae, + 0x80, 0x47, 0xe0, 0x3f, 0x25, 0xd1, 0x68, 0x02, 0xe9, 0x95, 0x25, 0x04, 0xaf, 0x4b, 0xca, 0x77, 0x3a, 0x9b, 0x3c, + 0x60, 0x1c, 0x68, 0xcd, 0xf1, 0x3b, 0xa4, 0x10, 0xd7, 0x7c, 0x1d, 0xd2, 0x7b, 0x65, 0x51, 0x5a, 0xdc, 0x54, 0x24, + 0xd4, 0x12, 0x70, 0x39, 0x2d, 0xac, 0x50, 0xaf, 0xbc, 0x5e, 0x22, 0x7c, 0xe0, 0xa3, 0xb8, 0x69, 0xc9, 0xe0, 0x32, + 0x47, 0x4b, 0x8c, 0x12, 0x3d, 0x06, 0xf7, 0x2e, 0xe9, 0x06, 0x81, 0x19, 0xda, 0x65, 0x6c, 0x84, 0x57, 0x39, 0x0d, + 0x8b, 0x09, 0x7d, 0xf6, 0xc2, 0x34, 0x8f, 0xe4, 0x4b, 0xab, 0x3e, 0x7c, 0xb2, 0x09, 0x90, 0xe8, 0xc5, 0x83, 0x61, + 0x71, 0x1f, 0x24, 0xee, 0xd8, 0xa4, 0xcd, 0xac, 0x2a, 0x5f, 0x4d, 0xc7, 0x7e, 0xb6, 0xd8, 0x74, 0x34, 0x16, 0x6e, + 0x30, 0xf5, 0xd9, 0x85, 0x3b, 0xfe, 0x16, 0xeb, 0xbc, 0x1e, 0xfb, 0xaf, 0xa0, 0x42, 0xaa, 0x0e, 0x9f, 0x6c, 0x88, + 0xac, 0xd7, 0xa1, 0xf1, 0x94, 0xb6, 0x40, 0xf9, 0x3b, 0x3c, 0xf7, 0x0e, 0x8b, 0xa8, 0x35, 0x0e, 0x96, 0x48, 0x31, + 0xe1, 0xd9, 0xe2, 0xc8, 0x78, 0xee, 0x17, 0xd8, 0x9b, 0x0a, 0x3f, 0x94, 0x30, 0xae, 0x50, 0x1c, 0x50, 0x79, 0x67, + 0xca, 0x83, 0x25, 0x92, 0xfb, 0x2e, 0xbc, 0x15, 0x23, 0xe5, 0x00, 0xa0, 0x58, 0x85, 0xa7, 0xaf, 0x46, 0x27, 0xf2, + 0xfd, 0x00, 0x2a, 0x51, 0xa9, 0x5f, 0xf8, 0x95, 0xaa, 0x4a, 0x9e, 0x09, 0x68, 0x75, 0xa7, 0x0e, 0x4f, 0x0e, 0xe4, + 0xda, 0xc3, 0x51, 0xef, 0x5c, 0x9a, 0x1c, 0xf6, 0x0a, 0x40, 0x28, 0x96, 0x55, 0xa8, 0x0e, 0x24, 0xc7, 0xcb, 0xe9, + 0x12, 0x6d, 0x0f, 0x81, 0x16, 0x43, 0xbd, 0x97, 0xad, 0x11, 0xd9, 0xe0, 0x89, 0xde, 0x46, 0xfc, 0xdf, 0x7c, 0xce, + 0xa8, 0xd3, 0x64, 0x41, 0x1c, 0x46, 0x2a, 0xcc, 0xa3, 0x9c, 0x21, 0x47, 0x91, 0x32, 0x73, 0xe1, 0x8c, 0x6a, 0xa9, + 0x29, 0x40, 0xe4, 0xa0, 0xdc, 0x54, 0x9a, 0xd8, 0x48, 0xcf, 0x7f, 0x28, 0x7c, 0x32, 0x25, 0xac, 0x94, 0x0d, 0xb0, + 0x39, 0xf3, 0xd0, 0xe5, 0x5b, 0xcf, 0xf8, 0x9f, 0xd0, 0x98, 0xbb, 0xc6, 0xd2, 0x35, 0xde, 0x07, 0x57, 0x69, 0xed, + 0xea, 0x64, 0x59, 0xc3, 0x0c, 0xd6, 0xd7, 0x20, 0xd6, 0x0e, 0xd7, 0x80, 0x70, 0xbd, 0x80, 0x67, 0x71, 0xeb, 0x80, + 0x0b, 0x37, 0x9a, 0x33, 0x91, 0xac, 0x4b, 0xbc, 0x4d, 0x38, 0x54, 0x74, 0x09, 0x2c, 0x10, 0x88, 0x4a, 0x08, 0x38, + 0x9e, 0x35, 0x49, 0x22, 0xff, 0x6f, 0xec, 0x1e, 0x24, 0xcf, 0x38, 0x09, 0x57, 0xa0, 0x9d, 0x70, 0xe7, 0x5c, 0xdb, + 0x6c, 0x00, 0x2f, 0xb3, 0xcf, 0xe7, 0x3e, 0x7e, 0x64, 0x52, 0xfe, 0xa8, 0x24, 0x9c, 0xcf, 0x7d, 0xa6, 0x49, 0x79, + 0xa6, 0xb2, 0xcf, 0x9c, 0x3e, 0xb2, 0x45, 0x8c, 0x62, 0x3d, 0x6d, 0x3a, 0x39, 0x39, 0x2b, 0x28, 0xee, 0x75, 0x49, + 0x58, 0xc7, 0xdb, 0xa8, 0x1b, 0xbc, 0xd0, 0xe5, 0xeb, 0x92, 0x9f, 0x4c, 0x73, 0x1a, 0xae, 0xc7, 0x3e, 0x33, 0x71, + 0xbb, 0xc3, 0x27, 0x37, 0xe3, 0xf5, 0x78, 0xec, 0x53, 0x62, 0x28, 0x88, 0xb4, 0x15, 0xc6, 0xa8, 0x01, 0x4b, 0xf5, + 0x3e, 0x32, 0x68, 0x49, 0x79, 0xf8, 0x60, 0x1d, 0x07, 0x62, 0x03, 0x7d, 0x20, 0x01, 0x6d, 0x57, 0xf5, 0xd0, 0x0e, + 0x54, 0x10, 0x57, 0x58, 0xac, 0xf6, 0x6b, 0x38, 0xb9, 0xc1, 0xa5, 0xfa, 0x1e, 0x21, 0x8c, 0xd9, 0xeb, 0x5f, 0xd1, + 0xde, 0x55, 0x0d, 0x95, 0x8c, 0x7c, 0x78, 0x1e, 0x31, 0xd5, 0x50, 0x5f, 0x7b, 0xee, 0x3c, 0x08, 0xe3, 0xc4, 0x9b, + 0xa8, 0x57, 0xfd, 0x33, 0x4f, 0xbb, 0x5c, 0x26, 0x9a, 0x7e, 0x65, 0xfc, 0x55, 0xce, 0xf8, 0x24, 0x50, 0x21, 0x26, + 0x7c, 0x6a, 0xa8, 0x23, 0x9f, 0x9e, 0x6d, 0xf5, 0x04, 0xca, 0xc5, 0x3a, 0x7f, 0x1d, 0x40, 0xad, 0x52, 0xee, 0x28, + 0x4c, 0x0a, 0x08, 0xb9, 0xa3, 0xfe, 0xaa, 0xf7, 0x49, 0x2b, 0xf3, 0x6a, 0xbd, 0x41, 0x5e, 0x21, 0xc9, 0xa9, 0x2b, + 0x86, 0x3b, 0x17, 0x3e, 0x82, 0xf4, 0xfc, 0x48, 0xb6, 0x6f, 0x2f, 0xd0, 0xe9, 0xd1, 0xd7, 0x45, 0xc6, 0x03, 0x18, + 0x04, 0x30, 0x2e, 0x0b, 0xc2, 0x44, 0x81, 0x18, 0x5e, 0xf0, 0xc1, 0x51, 0xd9, 0x1e, 0x96, 0xf7, 0xaa, 0xe9, 0x29, + 0xc7, 0x02, 0x2f, 0x91, 0x58, 0x8a, 0xec, 0xef, 0x18, 0x8e, 0xb2, 0x10, 0xb1, 0x87, 0x7b, 0x61, 0xc1, 0xf2, 0x15, + 0xd8, 0x36, 0x09, 0xb1, 0x17, 0x09, 0xf6, 0x93, 0x4d, 0x7c, 0x2a, 0xa8, 0xf6, 0x59, 0x8c, 0x6b, 0x09, 0xfc, 0x08, + 0x27, 0xe3, 0xa9, 0xaa, 0x9c, 0x0a, 0x52, 0x83, 0x75, 0x0b, 0xf8, 0x53, 0x13, 0x5c, 0xae, 0x48, 0xea, 0xae, 0xf1, + 0x14, 0xd4, 0x82, 0xef, 0x2a, 0x1d, 0x3d, 0x08, 0xcb, 0x93, 0xb1, 0x54, 0x09, 0xd8, 0xd6, 0x22, 0x45, 0x00, 0xcc, + 0xc5, 0x99, 0x80, 0x51, 0x7a, 0x0d, 0xfc, 0x23, 0xc4, 0xaa, 0x12, 0x73, 0x34, 0x42, 0x39, 0x5d, 0x98, 0x17, 0xac, + 0xd6, 0x09, 0xc6, 0x20, 0x87, 0x01, 0xb0, 0x54, 0x55, 0x50, 0x5a, 0x04, 0x64, 0x9e, 0x4b, 0x41, 0xa9, 0xaa, 0x78, + 0xd3, 0x6a, 0x19, 0x57, 0xdd, 0x00, 0x8e, 0xc3, 0x69, 0xa0, 0x06, 0x1f, 0x1e, 0x23, 0x3e, 0x8d, 0x89, 0x91, 0x27, + 0xf0, 0xd0, 0x26, 0x78, 0xd3, 0x5d, 0x83, 0x40, 0x26, 0xd4, 0x4f, 0x5f, 0xf3, 0x6b, 0x27, 0x0b, 0x71, 0x89, 0x0b, + 0xd3, 0x1c, 0x3d, 0xd9, 0x04, 0xe9, 0x29, 0xc0, 0x6e, 0xf0, 0x64, 0xe3, 0x66, 0x46, 0x54, 0xea, 0x85, 0x4a, 0x16, + 0x54, 0x23, 0x04, 0xc3, 0x28, 0xbd, 0xce, 0x5d, 0x1a, 0xf3, 0xf9, 0xc2, 0x96, 0xa4, 0x72, 0x05, 0x6d, 0x9a, 0x06, + 0xdc, 0x72, 0x69, 0x15, 0x79, 0x4b, 0x37, 0xba, 0x27, 0x43, 0x27, 0x43, 0xb6, 0x86, 0xd2, 0x55, 0x85, 0xe8, 0x01, + 0x01, 0x80, 0x48, 0x83, 0xaa, 0x7c, 0x95, 0x95, 0x31, 0x3e, 0xdb, 0xcc, 0xda, 0x03, 0xbe, 0x75, 0xad, 0x3e, 0x67, + 0x16, 0xa9, 0x34, 0xa8, 0x49, 0x5f, 0x8b, 0x1b, 0xa6, 0x17, 0x17, 0xa7, 0x17, 0x14, 0x37, 0x1a, 0x4e, 0x86, 0x28, + 0x05, 0x8d, 0x1b, 0x67, 0x86, 0xe9, 0x0e, 0xeb, 0x57, 0x94, 0xde, 0xfd, 0xa1, 0xcb, 0xc1, 0x60, 0x39, 0x02, 0x58, + 0x0e, 0xe2, 0xae, 0x7f, 0x7a, 0x77, 0x96, 0xe5, 0x57, 0x04, 0xd5, 0xf8, 0x88, 0x6f, 0xcc, 0x18, 0xd9, 0x8c, 0x08, + 0x59, 0x0c, 0xca, 0x84, 0xa8, 0x64, 0x5b, 0x28, 0x82, 0xa3, 0x41, 0x63, 0xa7, 0xa3, 0x11, 0x0d, 0x06, 0x21, 0xb6, + 0x8a, 0xd2, 0x93, 0x03, 0xaa, 0x4d, 0x44, 0x91, 0x2a, 0x01, 0x18, 0x22, 0x98, 0x61, 0x0e, 0x05, 0x48, 0x05, 0x3d, + 0x70, 0x72, 0xf9, 0xc6, 0x5a, 0xe2, 0x05, 0xa4, 0x73, 0x5a, 0xe4, 0x68, 0xb0, 0x95, 0x3a, 0x3c, 0xc1, 0xe4, 0x8e, + 0x40, 0xd6, 0x21, 0xfc, 0xd1, 0xc9, 0x01, 0x3d, 0x2a, 0xa5, 0x13, 0x91, 0x77, 0x22, 0x14, 0x94, 0x3d, 0xde, 0xc1, + 0x83, 0x8e, 0x4a, 0x9c, 0xb0, 0x15, 0x94, 0xba, 0xa9, 0xaa, 0x2c, 0x39, 0x07, 0xc5, 0xe3, 0xac, 0x41, 0x10, 0x16, + 0x1b, 0x8c, 0xdf, 0x55, 0x65, 0xe9, 0xde, 0xe1, 0xcc, 0xc5, 0x1b, 0xf7, 0x4e, 0x73, 0xf8, 0xab, 0xfc, 0xac, 0xc5, + 0xc5, 0xb3, 0x36, 0xe1, 0x8b, 0x0b, 0x1e, 0x56, 0x82, 0x73, 0xd6, 0x16, 0x68, 0xb9, 0x52, 0xb3, 0xb8, 0x0b, 0xb1, + 0xb8, 0xd3, 0x86, 0xc5, 0x9d, 0x6e, 0x59, 0x5c, 0x9f, 0x2f, 0xa4, 0x92, 0x81, 0x2e, 0x42, 0xaf, 0xd9, 0x0c, 0x78, + 0x9c, 0x1f, 0xe9, 0xf1, 0x73, 0x86, 0x70, 0x32, 0x63, 0x1f, 0xac, 0x46, 0x1b, 0x60, 0x55, 0x07, 0x17, 0x09, 0x10, + 0xd5, 0x89, 0x67, 0xa7, 0x6e, 0x22, 0x29, 0x04, 0x34, 0xbf, 0x3c, 0x5f, 0xd8, 0xa5, 0xd8, 0xd0, 0xd0, 0x16, 0x0d, + 0x33, 0x5d, 0x6c, 0x99, 0xe9, 0xa4, 0x70, 0x74, 0xf9, 0xb4, 0xe9, 0x10, 0xca, 0x93, 0x82, 0x3d, 0x08, 0x96, 0xf4, + 0xb8, 0x65, 0x8a, 0xfb, 0xb0, 0x19, 0xc7, 0x4a, 0x3b, 0x6a, 0xe5, 0xc6, 0xf1, 0x6d, 0x18, 0xc1, 0x55, 0x34, 0x74, + 0xf3, 0xb0, 0x2d, 0xb5, 0xf4, 0x02, 0x1e, 0xe5, 0xaa, 0x71, 0x33, 0xe5, 0xef, 0xe5, 0x2d, 0xd5, 0xea, 0x74, 0xa8, + 0xc6, 0xca, 0x4d, 0x12, 0x16, 0x21, 0xd0, 0x5d, 0x48, 0x87, 0xf0, 0xff, 0x64, 0x9b, 0xd5, 0xe0, 0x10, 0x5f, 0xc2, + 0xea, 0x88, 0xa1, 0x57, 0xc0, 0x82, 0xd1, 0xdd, 0x53, 0xa0, 0x6f, 0xa4, 0x88, 0x99, 0x51, 0x06, 0xf8, 0x1f, 0xf0, + 0xb8, 0x6a, 0x91, 0xe4, 0xd3, 0xe9, 0x1c, 0xe9, 0xd6, 0xca, 0x9d, 0xbe, 0x07, 0x8b, 0x07, 0xad, 0x65, 0x80, 0xf7, + 0x82, 0x1c, 0x1f, 0x33, 0x22, 0x9e, 0x70, 0x92, 0x23, 0x49, 0xc4, 0x92, 0xdc, 0x36, 0x14, 0xdc, 0xca, 0x5d, 0x73, + 0x76, 0xb5, 0x69, 0xa5, 0x07, 0x73, 0x4f, 0xaf, 0x60, 0x4d, 0x40, 0x6d, 0xfe, 0x60, 0x98, 0xe9, 0xda, 0x7c, 0xc3, + 0x39, 0xd2, 0xe1, 0x4a, 0xec, 0x12, 0x12, 0x5f, 0xdb, 0x42, 0x5a, 0x1e, 0x45, 0x40, 0xb5, 0x2e, 0xed, 0xab, 0xf4, + 0xe9, 0x1c, 0x7f, 0x39, 0x57, 0xe9, 0xd3, 0x31, 0xfe, 0x6a, 0x5d, 0x61, 0x4a, 0xcf, 0x1a, 0x35, 0x81, 0x34, 0x67, + 0x75, 0x58, 0xd8, 0x4f, 0x64, 0x98, 0xfb, 0x80, 0x6d, 0xc3, 0x17, 0xf8, 0xf1, 0x93, 0x4d, 0x0c, 0xae, 0xe8, 0xf2, + 0x1c, 0x02, 0x2b, 0xd2, 0xd3, 0xda, 0xf2, 0x79, 0x43, 0xf9, 0x58, 0xff, 0x83, 0x09, 0x3f, 0xee, 0x92, 0x30, 0xa7, + 0x29, 0x45, 0x25, 0xc7, 0xf5, 0xd8, 0x0b, 0xdc, 0xe8, 0xfe, 0x9a, 0xa4, 0x10, 0x4d, 0xd2, 0xf6, 0x3e, 0xca, 0xa5, + 0xff, 0xfb, 0xa2, 0x1d, 0x40, 0x22, 0xdd, 0x65, 0xdd, 0x73, 0x42, 0x3f, 0xf8, 0x7b, 0x24, 0xf1, 0x77, 0x05, 0x39, + 0x95, 0x2f, 0x48, 0xe1, 0x43, 0xd7, 0x4f, 0x36, 0x1a, 0xab, 0x76, 0x53, 0x9a, 0x6d, 0x89, 0x81, 0x84, 0xe5, 0x41, + 0x99, 0x77, 0x39, 0xf5, 0x7a, 0x78, 0xd1, 0x3f, 0x0e, 0xef, 0xcc, 0x27, 0x9b, 0xe4, 0x54, 0x5d, 0xba, 0xd1, 0x07, + 0x36, 0x35, 0x27, 0x5e, 0x34, 0xf1, 0x81, 0x79, 0x1c, 0xfb, 0x6e, 0xf0, 0x81, 0x3f, 0x9a, 0xe1, 0x3a, 0x41, 0xd3, + 0x9d, 0x9d, 0x22, 0xb2, 0x80, 0x09, 0xe9, 0x0f, 0x91, 0xab, 0xad, 0x81, 0x82, 0xf2, 0x2a, 0xd3, 0xbf, 0xe5, 0x8c, + 0x62, 0x5e, 0xcb, 0x00, 0xcb, 0x73, 0xb0, 0x26, 0x02, 0x57, 0x7e, 0x43, 0xc5, 0xf5, 0x52, 0x0d, 0x79, 0xaa, 0x74, + 0xe5, 0x96, 0xe5, 0xa2, 0xbd, 0xc6, 0x1e, 0xfe, 0xfb, 0xcf, 0x41, 0xc9, 0x43, 0x3e, 0x97, 0xf5, 0xf2, 0x69, 0x33, + 0x84, 0x52, 0x93, 0x5c, 0xc8, 0x1e, 0xf0, 0x71, 0xce, 0x60, 0x36, 0x7f, 0x5a, 0x6e, 0xec, 0xc6, 0xf1, 0x7a, 0xc9, + 0xa6, 0x74, 0xb5, 0x76, 0x9a, 0x0f, 0xaa, 0x28, 0x87, 0xc8, 0x03, 0xfb, 0x65, 0xdd, 0x3a, 0x3e, 0x7c, 0x05, 0xa6, + 0x5c, 0xc0, 0x50, 0x86, 0xb3, 0x99, 0x9a, 0xab, 0x02, 0x76, 0x34, 0x73, 0x0e, 0x7f, 0x59, 0x7f, 0xf3, 0xc6, 0xfe, + 0x26, 0x6b, 0x1c, 0x00, 0x63, 0x2c, 0xec, 0x52, 0x38, 0x5f, 0x2c, 0x8d, 0x57, 0xcc, 0x68, 0xe6, 0x06, 0xcd, 0xd3, + 0xb9, 0x2c, 0x6c, 0xf1, 0x15, 0x63, 0x53, 0x60, 0xb8, 0x8d, 0x4a, 0xe9, 0xb5, 0xcf, 0x6e, 0x58, 0x66, 0xf3, 0x52, + 0xfd, 0x58, 0x4d, 0x0b, 0x0c, 0xca, 0xc9, 0x6f, 0x32, 0x39, 0x57, 0x27, 0x4d, 0x69, 0x84, 0x73, 0xe0, 0x33, 0x97, + 0x8f, 0x58, 0xe9, 0x48, 0x8d, 0x0c, 0x55, 0x1a, 0x40, 0xe3, 0xc8, 0x4e, 0x1b, 0xca, 0x7b, 0x80, 0xa8, 0x1b, 0xc6, + 0x66, 0x38, 0x7a, 0x0f, 0x92, 0x18, 0x70, 0x38, 0xf9, 0x70, 0xf2, 0xb4, 0x5c, 0x6b, 0xd2, 0x04, 0xb1, 0x3a, 0x5d, + 0x9a, 0x4a, 0x4a, 0x1a, 0x61, 0x06, 0x8e, 0xfe, 0x10, 0x42, 0x5d, 0x55, 0xbb, 0x36, 0x4a, 0x71, 0xe6, 0x63, 0x4c, + 0xf1, 0x1d, 0xb0, 0x38, 0x6e, 0x04, 0x58, 0xb6, 0xe8, 0x86, 0x9a, 0xd7, 0x2e, 0xc2, 0x23, 0x2f, 0x37, 0x6c, 0x03, + 0x58, 0x02, 0x9c, 0x60, 0xf9, 0x5b, 0x48, 0x5e, 0xae, 0x97, 0xdc, 0x90, 0x2f, 0x9a, 0x8f, 0x55, 0x6e, 0x64, 0xd5, + 0xf4, 0xfe, 0x56, 0xe5, 0x83, 0x2a, 0x90, 0xe9, 0xda, 0xa1, 0x69, 0x05, 0xd4, 0x5b, 0xd1, 0x2a, 0x61, 0x07, 0x62, + 0x4c, 0x25, 0xfc, 0xca, 0x66, 0x33, 0x36, 0x49, 0x62, 0x5d, 0xe8, 0x98, 0xb2, 0xb0, 0xda, 0x70, 0x7b, 0xf7, 0x68, + 0xa0, 0xfe, 0x00, 0xc1, 0x45, 0x44, 0xf4, 0x39, 0x3e, 0x20, 0x21, 0x33, 0xd5, 0x83, 0x89, 0x7a, 0x2c, 0x82, 0x88, + 0x7f, 0x05, 0xd4, 0xcc, 0x35, 0xe5, 0x38, 0x34, 0x4e, 0x7f, 0xf2, 0x7d, 0x11, 0x66, 0xe6, 0x7e, 0xdb, 0x51, 0xd1, + 0xb6, 0xe3, 0xbb, 0x71, 0xbe, 0xe9, 0x38, 0x76, 0xaa, 0x1a, 0xe0, 0xd4, 0xfa, 0xa1, 0xb4, 0x8d, 0x89, 0x40, 0x0d, + 0xd4, 0xf3, 0xb7, 0xaf, 0xfe, 0xf6, 0xe6, 0xf5, 0xbe, 0x18, 0x01, 0xbb, 0x6c, 0x43, 0x97, 0xeb, 0x60, 0x4b, 0xa7, + 0x3f, 0xfd, 0xf0, 0xb0, 0x6e, 0x5b, 0xce, 0x0b, 0x47, 0x35, 0xc8, 0x0e, 0x59, 0xc2, 0x8b, 0x93, 0xf0, 0x86, 0x45, + 0x9f, 0x0c, 0x06, 0xb9, 0xf3, 0xfa, 0xe1, 0xbe, 0xfd, 0xf1, 0xcd, 0x0f, 0x7b, 0x0f, 0xf5, 0xc8, 0xb1, 0x01, 0xb7, + 0x27, 0xe1, 0xea, 0x01, 0xb3, 0x6b, 0xab, 0x86, 0x3a, 0xf1, 0xc3, 0x98, 0x35, 0x8c, 0xe0, 0xd5, 0xf9, 0xdb, 0xf7, + 0x08, 0xae, 0x9c, 0x05, 0xa1, 0xae, 0x3e, 0x6d, 0xf2, 0x3f, 0xbe, 0x7b, 0xf3, 0xfe, 0xbd, 0x6a, 0x60, 0x5a, 0xe6, + 0x58, 0xee, 0x9d, 0x6f, 0xe2, 0x1d, 0x14, 0xa7, 0x76, 0xaf, 0x13, 0x55, 0x23, 0x41, 0xba, 0x38, 0x1b, 0x2a, 0xab, + 0x6c, 0x73, 0x4e, 0xed, 0xf8, 0x97, 0x49, 0xfa, 0xdd, 0x6b, 0x5e, 0x35, 0xf8, 0x68, 0x3b, 0x49, 0x2d, 0x94, 0x2c, + 0xbd, 0xe0, 0xba, 0xa6, 0xd4, 0xbd, 0xab, 0x29, 0x05, 0xf1, 0xb1, 0x82, 0x1f, 0xd7, 0xe1, 0x52, 0x62, 0x47, 0xd8, + 0xdd, 0x6e, 0x70, 0x49, 0x32, 0xdc, 0x27, 0x0c, 0x9a, 0xa7, 0xd5, 0x28, 0x8f, 0xba, 0xa6, 0x98, 0x0b, 0x5e, 0x19, + 0x6c, 0x27, 0x3e, 0x58, 0x5f, 0x33, 0xf9, 0x9e, 0xb1, 0xc8, 0xaa, 0x72, 0xdf, 0x89, 0x41, 0x49, 0x2a, 0xa0, 0x66, + 0x74, 0x37, 0xc3, 0x69, 0xca, 0xca, 0x9d, 0x82, 0x49, 0xb3, 0x39, 0x0e, 0x93, 0x24, 0x5c, 0xf6, 0x1c, 0x7b, 0x75, + 0xa7, 0x2a, 0x7d, 0xa1, 0xec, 0xe0, 0x16, 0xd7, 0xbd, 0xdf, 0xfe, 0x53, 0x42, 0xf3, 0x54, 0x7e, 0x9d, 0xb0, 0xe5, + 0x8a, 0x45, 0x6e, 0xb2, 0x8e, 0x58, 0xaa, 0xfc, 0xf6, 0xbf, 0xaf, 0x4a, 0x82, 0x7d, 0x5f, 0x6e, 0x43, 0x2c, 0xbd, + 0xdc, 0xe4, 0xda, 0x0f, 0x6f, 0x1f, 0xe5, 0xbe, 0x55, 0x3b, 0x2a, 0x2f, 0xbc, 0xf9, 0x22, 0xab, 0x7d, 0x9a, 0x6c, + 0x99, 0x9b, 0x18, 0x3d, 0xdd, 0x07, 0x28, 0xe7, 0xe1, 0x6d, 0xef, 0xb7, 0xff, 0x64, 0x0a, 0x9b, 0x9d, 0xbb, 0xae, + 0x7e, 0xa0, 0xc5, 0x15, 0xad, 0xaf, 0x53, 0x59, 0x62, 0x78, 0x5f, 0x59, 0xe0, 0x4a, 0x21, 0xed, 0xca, 0xea, 0xe5, + 0xdb, 0x96, 0x39, 0x7d, 0xeb, 0xcd, 0x17, 0x9f, 0x3a, 0x29, 0x00, 0xe8, 0xce, 0x59, 0x41, 0xa5, 0xcf, 0x30, 0xad, + 0x51, 0x6f, 0xff, 0x05, 0xfb, 0xc4, 0x79, 0xed, 0x9a, 0xd2, 0xe7, 0x98, 0x0d, 0xd7, 0xdc, 0xbe, 0x1a, 0x8d, 0xb2, + 0xb4, 0xa4, 0x72, 0x7b, 0xf0, 0x0e, 0x3b, 0xad, 0x94, 0x70, 0xf6, 0xa2, 0x67, 0xeb, 0x14, 0xb6, 0x65, 0x0f, 0x80, + 0xa0, 0x9d, 0x73, 0x0d, 0x38, 0x9a, 0xf1, 0x35, 0xb9, 0x2b, 0x55, 0xbe, 0x5d, 0x41, 0xd6, 0x50, 0x8a, 0x29, 0x2d, + 0xb3, 0x5b, 0x43, 0xa3, 0x7e, 0x38, 0xb7, 0x91, 0xbb, 0xa2, 0x4b, 0x02, 0x05, 0x6f, 0x4c, 0x40, 0xe9, 0x52, 0x92, + 0xa2, 0x6f, 0x5c, 0xff, 0x66, 0x3f, 0x81, 0xaa, 0x99, 0x82, 0x21, 0x69, 0xfe, 0xf3, 0x88, 0x37, 0xd2, 0xe5, 0xfd, + 0x69, 0x37, 0xa6, 0x0a, 0x7b, 0xdb, 0x64, 0x5e, 0xfd, 0xe3, 0x6e, 0xf3, 0xea, 0x8b, 0xbd, 0xcc, 0xab, 0x7f, 0xfc, + 0xec, 0xe6, 0xd5, 0x6f, 0x65, 0xf3, 0x6a, 0xd8, 0xc4, 0x6f, 0xd8, 0x5e, 0x46, 0xcf, 0xc2, 0xc8, 0x28, 0xbc, 0x8d, + 0x07, 0x0e, 0x17, 0x7a, 0xe2, 0xc9, 0x82, 0x81, 0x16, 0x89, 0x83, 0xcb, 0x0f, 0xe7, 0x60, 0x9b, 0xdc, 0x6c, 0x7d, + 0xfc, 0xb9, 0x6c, 0x8f, 0xfd, 0x70, 0xae, 0x4a, 0xc1, 0xd2, 0x03, 0x11, 0x2c, 0x1d, 0x1c, 0xc4, 0x7f, 0xb9, 0x73, + 0x5e, 0x5e, 0x3a, 0xfd, 0xb6, 0x03, 0xc1, 0x46, 0x40, 0x31, 0x80, 0x05, 0x76, 0xbf, 0xdd, 0x86, 0x82, 0x5b, 0xa9, + 0xa0, 0x05, 0x05, 0x9e, 0x54, 0xd0, 0x81, 0x82, 0x89, 0x54, 0x70, 0x04, 0x05, 0x53, 0xa9, 0xe0, 0x18, 0x0a, 0x6e, + 0xd4, 0xf4, 0x32, 0xc8, 0x8c, 0xc7, 0x8f, 0xf5, 0xab, 0x42, 0x9e, 0x8c, 0x3c, 0xff, 0x3c, 0xaf, 0x72, 0x6c, 0x88, + 0xa0, 0x8d, 0xe6, 0xa1, 0xce, 0xcd, 0xff, 0x46, 0x5f, 0x8c, 0xc0, 0x9d, 0x1a, 0x94, 0x7a, 0x06, 0xa8, 0x44, 0xa9, + 0x66, 0x5b, 0xbc, 0x56, 0x7b, 0xaa, 0x9e, 0x7d, 0xa0, 0x25, 0xec, 0xfe, 0x7a, 0xe8, 0x4a, 0x23, 0x2a, 0x77, 0x9e, + 0x2f, 0xb2, 0x08, 0x4e, 0xeb, 0x41, 0xee, 0x91, 0xd6, 0x86, 0x38, 0xb6, 0x70, 0x35, 0xfd, 0x1a, 0xf9, 0x03, 0x2b, + 0x09, 0xc1, 0xe1, 0x48, 0x44, 0x2e, 0x12, 0x1f, 0x50, 0x54, 0xfd, 0xd2, 0xbe, 0xea, 0xbb, 0x79, 0x90, 0x29, 0x1e, + 0xef, 0x8c, 0x46, 0xbf, 0xcc, 0xc2, 0x48, 0x91, 0x98, 0xbb, 0x36, 0x12, 0x77, 0xde, 0x5b, 0x18, 0xa4, 0xe3, 0xee, + 0xcd, 0x21, 0x2e, 0xe8, 0xe9, 0xb4, 0xb7, 0x32, 0x6e, 0x17, 0x2c, 0xe8, 0xcd, 0xb8, 0x39, 0x28, 0xac, 0x3f, 0x59, + 0xf1, 0x2c, 0x75, 0x61, 0x94, 0x86, 0x7b, 0x22, 0x7f, 0x4b, 0xa3, 0x34, 0xb3, 0xad, 0x94, 0x5b, 0x4e, 0x69, 0xb2, + 0xfe, 0xfb, 0x73, 0xd8, 0xb9, 0xbc, 0x66, 0xe3, 0xf5, 0x5c, 0x39, 0x0f, 0xe7, 0x3b, 0x6d, 0x5a, 0xe4, 0x57, 0x30, + 0x4a, 0x95, 0x2e, 0xfa, 0x4c, 0xb1, 0xbd, 0xf9, 0xb7, 0xe8, 0x31, 0x2d, 0xd6, 0x4f, 0x60, 0x6c, 0x4a, 0x42, 0x28, + 0x1b, 0xbe, 0x03, 0xd0, 0x96, 0x8c, 0x4a, 0xce, 0x01, 0x7e, 0xd2, 0xf3, 0x85, 0x2b, 0x8d, 0x67, 0xf8, 0x3d, 0x8b, + 0x63, 0x77, 0x2e, 0xea, 0x57, 0xc7, 0x09, 0x3e, 0x36, 0x99, 0xa4, 0x8f, 0x00, 0x04, 0x9d, 0xb1, 0x57, 0xb1, 0x05, + 0x02, 0x53, 0x66, 0x30, 0x7b, 0x83, 0x45, 0xcb, 0x0d, 0x67, 0x3c, 0x0b, 0x96, 0xa7, 0x68, 0xe2, 0x02, 0x48, 0xe4, + 0x86, 0xf9, 0xe5, 0xc2, 0xc4, 0x9d, 0x97, 0x8b, 0x68, 0xad, 0x53, 0x79, 0x6c, 0x99, 0x85, 0x49, 0xa1, 0xf0, 0x53, + 0x4c, 0x26, 0xfc, 0x70, 0xfe, 0xbb, 0xda, 0x4b, 0x6c, 0xb1, 0x73, 0x79, 0x1f, 0x18, 0x41, 0x32, 0xb2, 0x10, 0xc6, + 0x8a, 0x05, 0x20, 0xec, 0x05, 0xc9, 0xc2, 0x44, 0xef, 0x6e, 0xad, 0x15, 0xe8, 0x86, 0x85, 0x6b, 0xbb, 0x29, 0xc7, + 0xb4, 0xe8, 0x45, 0xf3, 0xb1, 0xab, 0x39, 0xad, 0x63, 0x43, 0xfc, 0xb1, 0xec, 0x8e, 0x9e, 0x62, 0x0f, 0xca, 0xd4, + 0xbb, 0xd9, 0xcc, 0xc2, 0x20, 0x31, 0x67, 0xee, 0xd2, 0xf3, 0xef, 0x7b, 0xcb, 0x30, 0x08, 0xe3, 0x95, 0x3b, 0x61, + 0xfd, 0x5c, 0x75, 0xd3, 0xc7, 0x68, 0x49, 0xdc, 0x61, 0xdf, 0xb1, 0x5a, 0x11, 0x5b, 0x52, 0xeb, 0x2c, 0x18, 0xd2, + 0xcc, 0x67, 0x77, 0x29, 0xff, 0x7c, 0xa1, 0x32, 0x55, 0xc5, 0x2d, 0x47, 0x2d, 0x40, 0x0e, 0xe1, 0x91, 0x96, 0x20, + 0xbe, 0x60, 0x9f, 0x33, 0xf3, 0x3d, 0xab, 0xd5, 0x89, 0xd8, 0x52, 0xb1, 0x3a, 0x8d, 0x9d, 0x47, 0xe1, 0xed, 0x10, + 0x46, 0x8b, 0x8d, 0xcd, 0x98, 0xf9, 0x33, 0x7c, 0x63, 0xa2, 0x73, 0xa7, 0xe8, 0xc7, 0x44, 0x95, 0x0f, 0xf4, 0xc6, + 0x96, 0x7d, 0x78, 0xdd, 0x6b, 0x29, 0x76, 0x7f, 0xe9, 0x05, 0x26, 0x4d, 0xe7, 0xd8, 0x5e, 0x49, 0x7d, 0xc9, 0xf0, + 0xd3, 0x37, 0x58, 0xdd, 0x51, 0xec, 0x3e, 0x88, 0xf6, 0x33, 0x3f, 0xbc, 0xed, 0x2d, 0xbc, 0xe9, 0x94, 0x05, 0x7d, + 0x1c, 0x73, 0x56, 0xc8, 0x7c, 0xdf, 0x5b, 0xc5, 0x5e, 0xdc, 0x5f, 0xba, 0x77, 0xbc, 0xd7, 0xc3, 0xa6, 0x5e, 0xdb, + 0xbc, 0xd7, 0xf6, 0xde, 0xbd, 0x4a, 0xdd, 0x80, 0x23, 0x29, 0xf5, 0xc3, 0x87, 0xd6, 0x51, 0xec, 0xd2, 0x3c, 0xf7, + 0xee, 0x75, 0x15, 0xb1, 0xcd, 0xd2, 0x8d, 0xe6, 0x5e, 0xd0, 0xb3, 0x53, 0xeb, 0x66, 0x43, 0x1b, 0xe3, 0x71, 0xb7, + 0xdb, 0x4d, 0xad, 0xa9, 0x78, 0xb2, 0xa7, 0xd3, 0xd4, 0x9a, 0x88, 0xa7, 0xd9, 0xcc, 0xb6, 0x67, 0xb3, 0xd4, 0xf2, + 0x44, 0x41, 0xbb, 0x35, 0x99, 0xb6, 0x5b, 0xa9, 0x75, 0x2b, 0xd5, 0x48, 0x2d, 0xc6, 0x9f, 0x22, 0x36, 0xed, 0xe3, + 0x46, 0xe2, 0x76, 0xe9, 0xc7, 0xb6, 0x9d, 0x22, 0x06, 0xb8, 0x2c, 0xe0, 0x26, 0xd4, 0x2a, 0x5e, 0x6d, 0xf6, 0xae, + 0xa9, 0xe4, 0x9f, 0x9b, 0x4c, 0x6a, 0xeb, 0x4d, 0xdd, 0xe8, 0xc3, 0x95, 0x22, 0xcd, 0xc2, 0x75, 0xa9, 0xda, 0x46, + 0x80, 0xc1, 0xbc, 0xeb, 0x41, 0xd4, 0xcc, 0xfe, 0x38, 0x8c, 0xe0, 0xcc, 0x46, 0xee, 0xd4, 0x5b, 0xc7, 0x3d, 0xa7, + 0xb5, 0xba, 0x13, 0x45, 0x7c, 0xaf, 0xe7, 0x05, 0x78, 0xf6, 0x7a, 0x71, 0xe8, 0x7b, 0x53, 0x51, 0xd4, 0x74, 0x96, + 0x9c, 0x96, 0xde, 0xc7, 0x98, 0x31, 0x1e, 0x46, 0x3e, 0x72, 0x7d, 0x5f, 0xb1, 0xda, 0xb1, 0xc2, 0xdc, 0x18, 0x6f, + 0x32, 0x14, 0x3b, 0x26, 0xb8, 0x60, 0x7c, 0x18, 0xe7, 0x70, 0x75, 0x97, 0xed, 0x79, 0xe7, 0x68, 0x75, 0x97, 0x7e, + 0xb5, 0x64, 0x53, 0xcf, 0x55, 0xb4, 0x7c, 0x37, 0x39, 0x36, 0xdc, 0x76, 0xe8, 0x9b, 0x86, 0x6d, 0x2a, 0x8e, 0x05, + 0x44, 0x17, 0x7e, 0xe4, 0x2d, 0x57, 0x61, 0x94, 0xb8, 0x41, 0x92, 0xa6, 0xa3, 0xab, 0x34, 0xed, 0x5f, 0x78, 0xda, + 0xe5, 0x3f, 0x34, 0xa2, 0x85, 0x74, 0x3b, 0x98, 0xea, 0x57, 0xc6, 0x1b, 0x26, 0x5b, 0x32, 0x01, 0x19, 0x43, 0x2b, + 0x26, 0xb9, 0x32, 0xd1, 0xdb, 0x6a, 0x65, 0x02, 0x72, 0x56, 0x9d, 0x0c, 0xa3, 0x8a, 0x55, 0x90, 0x02, 0x41, 0x85, + 0x37, 0x6c, 0x70, 0x21, 0x99, 0x45, 0x01, 0xd3, 0x83, 0x95, 0xc9, 0xb5, 0xef, 0x49, 0x13, 0xef, 0xf9, 0xf5, 0x6e, + 0xde, 0xf3, 0x9f, 0xc9, 0x3e, 0xbc, 0xe7, 0xd7, 0x9f, 0x9d, 0xf7, 0x7c, 0x52, 0x75, 0xed, 0x3b, 0x0b, 0x07, 0x6a, + 0x76, 0x97, 0x05, 0xa4, 0x29, 0xa2, 0xa0, 0x79, 0x67, 0xc9, 0x7f, 0xeb, 0x8a, 0x27, 0x7a, 0xa3, 0x34, 0xb0, 0x44, + 0xb9, 0x81, 0x81, 0x7f, 0x1b, 0x0c, 0xfe, 0x1e, 0xc9, 0xcf, 0xb3, 0xd9, 0xe0, 0x75, 0x28, 0x15, 0x64, 0x4f, 0xdc, + 0xcc, 0xa7, 0x10, 0xe0, 0x88, 0xde, 0x64, 0x86, 0x58, 0x90, 0x02, 0x0a, 0xe2, 0xa3, 0x90, 0xb1, 0xfd, 0x34, 0x33, + 0x87, 0xec, 0x17, 0x87, 0xa0, 0x65, 0x06, 0xc6, 0xc2, 0x0b, 0xb6, 0xa2, 0xb4, 0x9e, 0xb3, 0x84, 0x87, 0xad, 0x78, + 0x79, 0x7f, 0x36, 0xd5, 0xce, 0x42, 0x3d, 0xf5, 0xe2, 0xb7, 0x65, 0x1f, 0x54, 0x21, 0x82, 0xc8, 0xd3, 0x49, 0xb9, + 0x49, 0xa3, 0x14, 0x6a, 0x06, 0x5f, 0x53, 0xf3, 0xd3, 0xc2, 0x4c, 0x7b, 0x72, 0x43, 0x9e, 0x6b, 0xb2, 0x42, 0x8c, + 0xb9, 0x4b, 0xdf, 0x86, 0x73, 0x79, 0x98, 0x3e, 0x13, 0x43, 0x77, 0x4c, 0xa9, 0xb9, 0x37, 0x4d, 0x53, 0xbd, 0x2f, + 0x00, 0x21, 0x11, 0x5a, 0xb6, 0x8b, 0x89, 0x8b, 0x73, 0x81, 0x96, 0xdf, 0x45, 0xd3, 0x45, 0xf3, 0x19, 0x98, 0x6e, + 0xf0, 0x6b, 0x69, 0x0e, 0x33, 0x55, 0x21, 0xf0, 0x91, 0x49, 0x8f, 0x34, 0x21, 0xb0, 0x35, 0x90, 0x0d, 0xe1, 0x0a, + 0x0b, 0x52, 0x35, 0x2a, 0x26, 0xe0, 0xa0, 0xed, 0x09, 0x04, 0xda, 0x11, 0xda, 0x2e, 0x42, 0x3b, 0xbc, 0x0e, 0x3e, + 0xa4, 0x6a, 0xc6, 0xfb, 0xe1, 0xf6, 0x1b, 0x9e, 0x1c, 0x40, 0x83, 0x61, 0x49, 0x93, 0xb5, 0xc3, 0x64, 0x16, 0x58, + 0x89, 0xf8, 0xd6, 0xb0, 0xe2, 0x5b, 0xe5, 0xd9, 0x46, 0x04, 0xa9, 0x4a, 0xdc, 0x95, 0x09, 0xea, 0x13, 0xc4, 0xbd, + 0x1c, 0xe3, 0x49, 0xf1, 0xb0, 0xfa, 0xeb, 0x18, 0x70, 0x23, 0x4a, 0xf2, 0x88, 0x7f, 0xfa, 0x93, 0x75, 0x14, 0x87, + 0x51, 0x6f, 0x15, 0x7a, 0x41, 0xc2, 0xa2, 0x14, 0x41, 0x75, 0x89, 0xf0, 0x11, 0xe0, 0xb9, 0xda, 0x84, 0x2b, 0x77, + 0xe2, 0x25, 0xf7, 0x3d, 0x9b, 0xb3, 0x14, 0x76, 0x9f, 0x73, 0x07, 0x76, 0x6d, 0xfd, 0x1e, 0x87, 0xe6, 0x73, 0x64, + 0xfc, 0xa2, 0x2a, 0x3b, 0x23, 0x6f, 0xf3, 0xbe, 0xf4, 0x96, 0xc2, 0x74, 0x01, 0xfb, 0xe1, 0x46, 0xe6, 0x1c, 0xb0, + 0x3c, 0x2c, 0xb5, 0x3d, 0x65, 0x73, 0x03, 0xb1, 0x36, 0xdc, 0x00, 0x89, 0x3f, 0x56, 0x47, 0x57, 0xec, 0xfa, 0x62, + 0xe0, 0x78, 0xf4, 0x7d, 0x46, 0xd6, 0x73, 0x21, 0xa9, 0xa5, 0xb1, 0x4f, 0xcd, 0x31, 0x9b, 0x85, 0x11, 0xa3, 0x90, + 0xee, 0x4e, 0x77, 0x75, 0xb7, 0x7f, 0xf7, 0xdb, 0xa7, 0x5f, 0xdf, 0x4f, 0x10, 0x26, 0x9a, 0xe8, 0x4c, 0xdf, 0xd1, + 0x5b, 0x95, 0x9e, 0x01, 0x6b, 0x48, 0x90, 0x9f, 0x90, 0xc3, 0x49, 0x4f, 0x55, 0xfb, 0xb5, 0x91, 0x33, 0x57, 0x21, + 0xa7, 0x79, 0x11, 0xf3, 0xdd, 0xc4, 0xbb, 0x11, 0x3c, 0x63, 0xfb, 0x68, 0x75, 0x27, 0xd6, 0x18, 0x09, 0xde, 0x03, + 0x16, 0xa9, 0x34, 0x14, 0xb1, 0x48, 0xe5, 0x62, 0x5c, 0xa4, 0x7e, 0x65, 0x36, 0x22, 0x98, 0x54, 0x89, 0xd2, 0x77, + 0x56, 0x77, 0x32, 0x89, 0xce, 0x9b, 0x65, 0x94, 0xba, 0x1c, 0x05, 0x74, 0xe9, 0x4d, 0xa7, 0x3e, 0x4b, 0x0b, 0x0b, + 0x5d, 0x5c, 0x4b, 0x09, 0x38, 0x19, 0x1c, 0xdc, 0x71, 0x1c, 0xfa, 0xeb, 0x84, 0xd5, 0x83, 0x8b, 0x80, 0xd3, 0xb2, + 0x73, 0xe0, 0xe0, 0xef, 0xe2, 0x58, 0x3b, 0xc0, 0x6e, 0xc3, 0x36, 0xb1, 0xfb, 0x10, 0xf4, 0xdf, 0x6c, 0x17, 0x87, + 0x0e, 0xaf, 0xb2, 0x41, 0x1b, 0x35, 0x13, 0x31, 0x80, 0x2c, 0x11, 0xf6, 0x56, 0x2c, 0x87, 0x97, 0x65, 0x81, 0xcf, + 0xb3, 0xa2, 0xb4, 0x38, 0x99, 0xdf, 0xe7, 0x8c, 0xbd, 0xa8, 0x3f, 0x63, 0x2f, 0xc4, 0x19, 0xdb, 0xbe, 0x33, 0x1f, + 0xcf, 0x1c, 0xf8, 0xaf, 0x9f, 0x4f, 0xa8, 0x67, 0x2b, 0xed, 0xd5, 0x9d, 0xe2, 0xac, 0xee, 0x14, 0xb3, 0xb5, 0xba, + 0x53, 0xb0, 0x6b, 0xb4, 0x3c, 0x32, 0xac, 0x96, 0x6e, 0xd8, 0x0a, 0x14, 0xc2, 0x1f, 0xbb, 0xf0, 0xca, 0x39, 0x84, + 0x77, 0xd0, 0xaa, 0x53, 0x7d, 0xd7, 0xda, 0x7e, 0xd4, 0xe9, 0x2c, 0x09, 0xa4, 0xad, 0x5b, 0x89, 0x3b, 0x1e, 0xb3, + 0x69, 0x6f, 0x16, 0x4e, 0xd6, 0xf1, 0xbf, 0xf9, 0xf8, 0x39, 0x10, 0xb7, 0x22, 0x82, 0x52, 0x3f, 0xa2, 0x29, 0x68, + 0xf7, 0x6e, 0x98, 0xe8, 0x61, 0x93, 0xad, 0x53, 0x8f, 0x32, 0x14, 0xb4, 0xac, 0xc3, 0x9a, 0x4d, 0x5e, 0x0f, 0xe8, + 0xdf, 0x6d, 0x95, 0x9a, 0x51, 0xcc, 0x27, 0x80, 0x65, 0x2b, 0x38, 0x1e, 0x0e, 0x0d, 0xbe, 0x9a, 0x76, 0xb7, 0x7e, + 0xb8, 0x97, 0xe2, 0x4b, 0x57, 0x82, 0xa8, 0x70, 0xba, 0xc5, 0xdd, 0xa0, 0xb6, 0xf7, 0xda, 0xb4, 0x47, 0x2a, 0xbd, + 0x6e, 0x21, 0x08, 0x79, 0xdd, 0x3d, 0xb1, 0xfc, 0xe3, 0x17, 0x87, 0xf0, 0x1f, 0x71, 0xf5, 0xff, 0x4c, 0xea, 0x18, + 0xf5, 0xb3, 0xa4, 0xc0, 0xa8, 0x13, 0xab, 0x84, 0x8c, 0xf8, 0xfe, 0xf5, 0x67, 0xb3, 0x87, 0x35, 0xd8, 0xbb, 0x36, + 0x19, 0xed, 0x95, 0x6b, 0xbf, 0x0c, 0x43, 0xc8, 0x9e, 0x5d, 0xad, 0x2e, 0xc0, 0x43, 0x1e, 0x18, 0xc9, 0x00, 0x1a, + 0x09, 0x39, 0x82, 0xec, 0x45, 0x54, 0x6c, 0x43, 0xa2, 0xc4, 0x9b, 0x26, 0x51, 0xe2, 0xf5, 0x6e, 0x51, 0xe2, 0xbb, + 0xbd, 0x44, 0x89, 0xd7, 0x9f, 0x5d, 0x94, 0x78, 0x53, 0x15, 0x25, 0x2e, 0x42, 0x61, 0xa9, 0x6d, 0x9c, 0xad, 0xf9, + 0xcf, 0x9f, 0xe9, 0x2a, 0xf6, 0x3c, 0x1c, 0x74, 0x6c, 0xca, 0x3a, 0x70, 0xf1, 0x5f, 0x0b, 0x16, 0xb8, 0x11, 0xdf, + 0xa1, 0xe1, 0x62, 0x2e, 0x5a, 0x70, 0xcc, 0x8e, 0xdf, 0x91, 0x8a, 0xfd, 0x30, 0x98, 0xff, 0x08, 0x57, 0xf1, 0xa0, + 0x0e, 0x8c, 0xa4, 0x17, 0x5e, 0xfc, 0x63, 0xb8, 0x5a, 0xaf, 0xce, 0xa0, 0xaf, 0x9f, 0xbd, 0xd8, 0x1b, 0xfb, 0x2c, + 0x8b, 0x06, 0x42, 0x86, 0x96, 0x5c, 0xb7, 0x0e, 0xb6, 0xcd, 0xe2, 0xa7, 0x7b, 0x27, 0x7e, 0xa2, 0xf5, 0x33, 0xff, + 0x4d, 0x16, 0x9c, 0x6a, 0xbd, 0x20, 0x02, 0x61, 0xf3, 0x4a, 0x83, 0x7e, 0xb8, 0x30, 0x72, 0x11, 0xea, 0x35, 0xb3, + 0x14, 0x96, 0x35, 0x8d, 0xfd, 0xb0, 0x8a, 0x50, 0xb3, 0xd6, 0x8d, 0x2c, 0x0a, 0x66, 0x55, 0x9d, 0xbf, 0x0c, 0xd7, + 0x31, 0x9b, 0x86, 0xb7, 0x81, 0x6a, 0x04, 0xdc, 0x1c, 0x94, 0x12, 0x09, 0x66, 0x6d, 0x30, 0x7f, 0xf3, 0x7b, 0x64, + 0x94, 0x21, 0x62, 0x02, 0xa4, 0x0f, 0x5f, 0xaf, 0x4c, 0x32, 0x30, 0x30, 0x71, 0x8a, 0x6a, 0x96, 0x68, 0xf0, 0x91, + 0xa6, 0x85, 0x83, 0x87, 0xb5, 0x14, 0x46, 0x41, 0xa1, 0xc5, 0xb5, 0xc2, 0xb1, 0x16, 0x08, 0xe5, 0xa2, 0x08, 0x45, + 0x55, 0xb3, 0x70, 0xfc, 0x0d, 0x85, 0xfa, 0xc8, 0xdf, 0x42, 0x64, 0x88, 0x74, 0xcd, 0xd7, 0x83, 0x07, 0x66, 0xa2, + 0xc7, 0x57, 0x12, 0x18, 0xdf, 0xde, 0xb0, 0xc8, 0x77, 0xef, 0x35, 0x3d, 0x0d, 0x83, 0xef, 0x01, 0x00, 0xaf, 0xc3, + 0xdb, 0x40, 0xae, 0x80, 0xf9, 0xd2, 0x6a, 0xf6, 0x52, 0x6d, 0x08, 0x31, 0x70, 0xa7, 0x92, 0x46, 0x00, 0x99, 0xea, + 0xe7, 0xec, 0xef, 0x06, 0xfd, 0xfb, 0x0f, 0x3d, 0x35, 0xce, 0xc3, 0xec, 0x43, 0x3f, 0xad, 0xf6, 0xf8, 0xcc, 0xd3, + 0xa7, 0x8f, 0x9a, 0xa7, 0xad, 0x4d, 0x7c, 0xe6, 0x8a, 0xfc, 0xf3, 0x5a, 0x4d, 0x6b, 0xbd, 0xf1, 0x14, 0xc0, 0x28, + 0x2e, 0xc2, 0xf5, 0x64, 0x81, 0x26, 0xd5, 0x9f, 0x6f, 0xbe, 0x09, 0xf4, 0x89, 0x89, 0xc2, 0xb3, 0xa9, 0x97, 0x8a, + 0x72, 0x28, 0xe0, 0xf7, 0xdf, 0x40, 0x0c, 0xec, 0x3f, 0x11, 0x0c, 0xd5, 0x5d, 0x93, 0xb9, 0x5b, 0x3f, 0x68, 0xf3, + 0xf6, 0x21, 0x9f, 0x35, 0x8f, 0x2e, 0x25, 0x2e, 0xe9, 0xea, 0x91, 0x4c, 0x5a, 0x06, 0x9a, 0x1c, 0xc9, 0xb5, 0x29, + 0x48, 0xad, 0xf8, 0x0a, 0xb3, 0x48, 0x4c, 0xe7, 0x2e, 0x2d, 0x06, 0xe3, 0xd8, 0xaa, 0x84, 0x64, 0xb8, 0xa1, 0x0b, + 0x43, 0xf4, 0x55, 0x7e, 0xb7, 0xf4, 0x02, 0x03, 0x13, 0xb1, 0x54, 0xdf, 0xb8, 0x77, 0x90, 0x8a, 0x00, 0x90, 0x5b, + 0xf9, 0x15, 0x14, 0x1a, 0xb2, 0x23, 0x27, 0x64, 0x5b, 0x54, 0x6b, 0x21, 0x21, 0x6e, 0x03, 0x47, 0x5f, 0x28, 0x8a, + 0xa2, 0x64, 0x62, 0x84, 0x92, 0xc9, 0x11, 0x58, 0x8e, 0xe2, 0x00, 0xdc, 0x96, 0xa4, 0xab, 0x3b, 0x2a, 0x01, 0xc9, + 0x00, 0xaf, 0xb6, 0x45, 0x01, 0x8f, 0xb6, 0xdb, 0xb1, 0x45, 0x81, 0x10, 0xe8, 0x21, 0x52, 0xaa, 0x1b, 0x41, 0x50, + 0xfe, 0x9e, 0x82, 0x02, 0x3b, 0xbe, 0xe5, 0x9a, 0x60, 0xc5, 0xa6, 0xc7, 0x51, 0x9f, 0xd5, 0x87, 0x65, 0x0d, 0x24, + 0x2c, 0x08, 0xb7, 0x0e, 0xa5, 0x2c, 0x0b, 0x06, 0xab, 0xc1, 0x8d, 0x28, 0x17, 0xdd, 0x25, 0x4b, 0x16, 0xac, 0x55, + 0x4c, 0xcb, 0x88, 0x61, 0x72, 0xa1, 0xce, 0x6b, 0x62, 0xb6, 0x00, 0xdb, 0xd4, 0xb7, 0x5c, 0x10, 0x2d, 0x8c, 0x39, + 0x4a, 0x75, 0x8d, 0x09, 0xf7, 0x4d, 0x8c, 0x39, 0x6e, 0x2b, 0x53, 0x08, 0xbe, 0xa4, 0x61, 0x11, 0x9b, 0x73, 0x6f, + 0x64, 0xe4, 0x14, 0x28, 0x42, 0x15, 0x57, 0x17, 0x09, 0xb0, 0x6b, 0x6e, 0x79, 0xd1, 0xb2, 0x1b, 0x19, 0xb7, 0xa4, + 0x28, 0x8a, 0xf4, 0x6a, 0x37, 0x7c, 0x9c, 0x10, 0x1b, 0xb0, 0xb1, 0x9f, 0x49, 0xa5, 0x9f, 0x86, 0x49, 0x7f, 0x60, + 0xf7, 0x44, 0x48, 0x08, 0x54, 0x1f, 0xd8, 0x3d, 0xdc, 0xdb, 0xbf, 0x01, 0x6d, 0x8a, 0xba, 0x05, 0x5d, 0x1b, 0x90, + 0x6d, 0x67, 0x02, 0xf1, 0x22, 0xb7, 0x1c, 0x20, 0x3b, 0xdd, 0x82, 0xc5, 0x11, 0xc4, 0x81, 0x11, 0xf7, 0xc5, 0x21, + 0xe6, 0xce, 0x24, 0x5a, 0x2d, 0x8c, 0xcd, 0x9a, 0xa3, 0xa1, 0x3f, 0x73, 0x6c, 0xfb, 0xa0, 0x52, 0x1f, 0x14, 0xd9, + 0x75, 0xb5, 0x75, 0x23, 0x19, 0x38, 0xb6, 0xe9, 0x3d, 0xb3, 0x5a, 0xfd, 0x0a, 0x8d, 0x96, 0xc2, 0x39, 0x8f, 0x50, + 0xfd, 0x35, 0x7c, 0xb2, 0xd1, 0x2a, 0x07, 0x52, 0x2f, 0x3b, 0x67, 0xe0, 0xd8, 0x52, 0xae, 0xff, 0x1a, 0x55, 0x49, + 0x3f, 0x05, 0x93, 0xa6, 0xd4, 0x62, 0x23, 0x48, 0x48, 0xa0, 0xc1, 0x31, 0xfa, 0x8b, 0xf2, 0x5c, 0xd1, 0xe8, 0xf8, + 0xe8, 0xfa, 0xa8, 0x2f, 0x30, 0x8a, 0xf0, 0x5e, 0x94, 0x3b, 0x28, 0x7d, 0x31, 0x2e, 0x63, 0x38, 0x1e, 0xfa, 0x9c, + 0xe5, 0x37, 0x7a, 0x5b, 0xb9, 0x05, 0xec, 0xbf, 0x81, 0x7c, 0x5a, 0x63, 0x88, 0xaf, 0x01, 0x35, 0x20, 0x7d, 0xc9, + 0xce, 0x0e, 0x21, 0x6c, 0x92, 0xdc, 0x5d, 0x91, 0x48, 0xee, 0xdf, 0x19, 0x12, 0x1d, 0xbc, 0x43, 0xcb, 0xfa, 0xab, + 0x27, 0x77, 0x0f, 0xec, 0x92, 0x05, 0xd3, 0x62, 0x87, 0x25, 0xfa, 0xb5, 0x7f, 0x77, 0x05, 0x8c, 0x02, 0x79, 0x7d, + 0xc2, 0x1a, 0x8c, 0x92, 0x86, 0x01, 0x6e, 0x7e, 0x3a, 0x6e, 0xde, 0x5e, 0x5c, 0x0c, 0x36, 0xa0, 0xa0, 0x9c, 0x59, + 0x33, 0x49, 0x29, 0x0e, 0xc9, 0x23, 0xd0, 0xb9, 0x59, 0x13, 0x8c, 0x68, 0xe3, 0x4e, 0x4c, 0x84, 0x25, 0x69, 0xde, + 0xc6, 0xe3, 0xe1, 0xa0, 0xf7, 0xd5, 0x5a, 0x7b, 0xbb, 0xb5, 0xd6, 0xc9, 0x2e, 0xad, 0x35, 0x39, 0xee, 0x91, 0xf9, + 0x53, 0xe6, 0xc0, 0x28, 0x98, 0x73, 0xd9, 0x05, 0xb4, 0xa0, 0xea, 0x46, 0x3f, 0x3f, 0xd1, 0xaa, 0xd2, 0x1b, 0xd9, + 0x86, 0xa2, 0xfa, 0x5b, 0x12, 0x50, 0xc4, 0x85, 0xba, 0xac, 0x1b, 0xbf, 0xc8, 0x75, 0xe3, 0x24, 0xd5, 0xe4, 0x2e, + 0x5b, 0x82, 0xfb, 0x97, 0xdc, 0x21, 0x33, 0xe9, 0x20, 0x77, 0x8b, 0xcc, 0x47, 0x2a, 0x39, 0xfa, 0xe5, 0x82, 0x86, + 0xe4, 0x3e, 0x2a, 0xa4, 0x8c, 0xa2, 0x17, 0x69, 0xb1, 0x6a, 0xee, 0xe7, 0x97, 0x97, 0x83, 0xd6, 0x1d, 0x87, 0x9c, + 0x15, 0xcb, 0xdb, 0xa6, 0xe8, 0xe8, 0x25, 0xbf, 0x96, 0x36, 0x49, 0xe6, 0x91, 0x45, 0x00, 0x16, 0x6a, 0xfa, 0xd2, + 0xbd, 0x76, 0x66, 0x03, 0x81, 0x83, 0xac, 0x71, 0x20, 0xdd, 0xad, 0x9d, 0xa7, 0x94, 0x45, 0xf9, 0xd5, 0xb5, 0x83, + 0xd4, 0x9d, 0x6e, 0x82, 0x65, 0x7d, 0x04, 0xc2, 0xfa, 0x4a, 0xd2, 0x20, 0xf4, 0x6c, 0xc5, 0xee, 0xd7, 0x30, 0x00, + 0x48, 0xff, 0xcb, 0xcf, 0x9c, 0x15, 0x00, 0x4d, 0xa4, 0x62, 0xcb, 0x77, 0xfe, 0x78, 0x88, 0x4d, 0x32, 0x3f, 0xc3, + 0xaa, 0xd5, 0x6f, 0x92, 0xbe, 0x67, 0xc3, 0xdd, 0xb5, 0x8a, 0xea, 0x7c, 0x5e, 0xa3, 0x27, 0xc6, 0xc1, 0x77, 0x59, + 0xb4, 0x0e, 0x30, 0x13, 0x8d, 0x99, 0x44, 0xee, 0xe4, 0xc3, 0x46, 0xfa, 0x1e, 0x57, 0x89, 0x82, 0xba, 0xb8, 0x78, + 0xa9, 0xd0, 0x77, 0x31, 0x70, 0x33, 0xeb, 0x59, 0xad, 0x58, 0x52, 0xd4, 0xf4, 0x1e, 0xdb, 0x6d, 0xf7, 0xc5, 0xec, + 0xb0, 0xa4, 0x3f, 0x6d, 0x75, 0x8a, 0xda, 0xf5, 0x6c, 0x1c, 0xcb, 0xf0, 0x57, 0xee, 0xd8, 0xfa, 0xc7, 0x7f, 0x3a, + 0xe6, 0xdf, 0x2c, 0xad, 0xd1, 0xa7, 0x0c, 0x01, 0xda, 0x17, 0x2e, 0xa6, 0xe5, 0x6b, 0x9a, 0x4a, 0x49, 0xd3, 0xb0, + 0x66, 0x9e, 0xef, 0x9b, 0x3e, 0xb8, 0x17, 0x6d, 0x3e, 0x69, 0x7a, 0xd8, 0xcf, 0x1a, 0x52, 0x06, 0x7c, 0x42, 0x3f, + 0xc5, 0x9d, 0x92, 0x2c, 0xd6, 0xcb, 0xf1, 0x46, 0x56, 0x94, 0x4b, 0xfa, 0xf3, 0xaa, 0xce, 0x5c, 0xfe, 0xec, 0x6c, + 0x36, 0x2b, 0x6a, 0x8d, 0x6d, 0xe5, 0x10, 0x35, 0xbf, 0x8f, 0x6d, 0xdb, 0x2e, 0xc3, 0xb7, 0xe9, 0xa0, 0xd0, 0xc1, + 0x30, 0x51, 0x09, 0xdf, 0xdd, 0xbd, 0xa7, 0xfe, 0xa0, 0xd1, 0x52, 0x57, 0x4d, 0xe7, 0x91, 0xb6, 0xda, 0xff, 0x8b, + 0xa1, 0x20, 0x6a, 0xd8, 0x75, 0xfc, 0xab, 0x7b, 0x65, 0x4b, 0x4f, 0xe5, 0x03, 0xfc, 0xb0, 0xc6, 0x3b, 0xf6, 0xfa, + 0x1e, 0x4d, 0x9b, 0xb6, 0x77, 0x6a, 0xe5, 0x64, 0xb7, 0x60, 0xb3, 0xd4, 0x27, 0x4b, 0x25, 0x2f, 0x61, 0xcb, 0xb8, + 0x37, 0x61, 0x78, 0x41, 0x6a, 0x49, 0xd4, 0x16, 0xad, 0x7a, 0xcc, 0x39, 0xd8, 0x71, 0x39, 0x02, 0x0f, 0xdb, 0x0a, + 0x5e, 0x56, 0x55, 0x6e, 0xd6, 0xc4, 0x47, 0x90, 0x8a, 0x6d, 0xaa, 0x17, 0x4e, 0xb8, 0x4d, 0x3b, 0xf6, 0x5f, 0x0a, + 0xf5, 0x14, 0xe0, 0x4e, 0x37, 0xc2, 0xda, 0x84, 0x2e, 0x4f, 0xf0, 0xef, 0xec, 0x72, 0xee, 0xc5, 0xea, 0xae, 0x68, + 0xdc, 0xd5, 0x85, 0xeb, 0xa6, 0x9c, 0x94, 0xd1, 0xa8, 0xeb, 0x50, 0x5f, 0x66, 0x02, 0x34, 0x93, 0xad, 0x5b, 0xc0, + 0x82, 0xa6, 0x90, 0x20, 0xaf, 0xe6, 0x6e, 0x0c, 0xc5, 0x59, 0xd8, 0x79, 0xb9, 0x7e, 0x3f, 0x4f, 0xef, 0x0c, 0x73, + 0x30, 0x9e, 0x77, 0xf1, 0x72, 0xaf, 0xb0, 0x55, 0xd1, 0x54, 0x06, 0xf7, 0x80, 0x90, 0x48, 0x95, 0x75, 0xe4, 0x9b, + 0x94, 0x3d, 0x4e, 0xd3, 0x37, 0xd5, 0x79, 0x37, 0x77, 0xef, 0x74, 0xe0, 0x5e, 0xa3, 0x0a, 0xaa, 0xbd, 0xae, 0xf6, + 0xca, 0x77, 0xd8, 0x62, 0x9c, 0xb0, 0x02, 0xe0, 0x8a, 0xa2, 0xa0, 0xd1, 0x90, 0x52, 0xc2, 0x7d, 0x34, 0xe9, 0xec, + 0xad, 0x8c, 0xac, 0xc5, 0x3c, 0xb1, 0xbb, 0xfa, 0x2a, 0xd4, 0xb7, 0xb8, 0x19, 0x04, 0xd8, 0x71, 0xec, 0x84, 0xcf, + 0x26, 0xec, 0x18, 0x19, 0x5d, 0x39, 0xb8, 0x83, 0xf0, 0x94, 0x9a, 0x14, 0xdf, 0x96, 0x4e, 0x29, 0xde, 0x25, 0x7c, + 0x57, 0xab, 0xbc, 0xbf, 0x28, 0x68, 0xe3, 0xb9, 0x3f, 0x50, 0x4b, 0xdf, 0xab, 0xf6, 0xd2, 0x0b, 0xf6, 0xaf, 0xeb, + 0xde, 0xed, 0x5d, 0x17, 0x98, 0xc3, 0xbd, 0x2b, 0x03, 0x77, 0x49, 0x56, 0x4a, 0xc9, 0xe0, 0x3b, 0xe9, 0xf2, 0x40, + 0x8e, 0x65, 0xa1, 0x62, 0x2b, 0x92, 0xe8, 0x2f, 0xd6, 0x83, 0xd1, 0xc9, 0xe9, 0xdd, 0xd2, 0x57, 0x6e, 0x58, 0x04, + 0x99, 0x34, 0x07, 0xaa, 0x63, 0xd9, 0xaa, 0x82, 0x91, 0x19, 0xbc, 0x60, 0x3e, 0x50, 0x7f, 0xba, 0xf8, 0xc6, 0xec, + 0xaa, 0xa7, 0x60, 0x8e, 0x71, 0x33, 0x47, 0x16, 0xf7, 0xdc, 0xbd, 0x67, 0xd1, 0x75, 0x4b, 0x55, 0x30, 0x61, 0x26, + 0x31, 0xb7, 0x58, 0xa6, 0xb4, 0xd4, 0x3d, 0xf2, 0xb2, 0x29, 0x22, 0xb5, 0xb2, 0x0a, 0x88, 0xd5, 0x69, 0x75, 0x15, + 0xa7, 0x75, 0x68, 0x1d, 0x75, 0xd5, 0xe1, 0x17, 0x8a, 0x72, 0x32, 0x65, 0xb3, 0x78, 0x88, 0xea, 0x98, 0x13, 0xe4, + 0x07, 0xe9, 0xb7, 0xa2, 0x58, 0x13, 0x3f, 0x36, 0x1d, 0x65, 0xc3, 0x1f, 0x15, 0x05, 0x90, 0x51, 0x4f, 0x79, 0x3c, + 0x6b, 0xcd, 0x0e, 0x67, 0x2f, 0xfa, 0xbc, 0x38, 0xfd, 0xa2, 0x50, 0xdd, 0xa0, 0x7f, 0x5b, 0x52, 0xb3, 0x38, 0x89, + 0xc2, 0x0f, 0x8c, 0xf3, 0x92, 0x4a, 0xa6, 0x28, 0x2a, 0x37, 0x6d, 0x55, 0xbf, 0xe4, 0x74, 0xc7, 0x93, 0x59, 0x2b, + 0xaf, 0x8e, 0x63, 0x3c, 0xc8, 0x06, 0x79, 0x72, 0x20, 0x86, 0x7e, 0x22, 0x83, 0xc9, 0x31, 0xeb, 0x00, 0xe5, 0xa8, + 0x7c, 0x8e, 0x73, 0x31, 0xbf, 0x13, 0x08, 0x7b, 0x9e, 0x7b, 0x70, 0xc4, 0xd8, 0x6c, 0xa0, 0x7e, 0xef, 0xb4, 0xba, + 0x86, 0xe3, 0x1c, 0x59, 0x47, 0xdd, 0x89, 0x6d, 0x1c, 0x5a, 0x87, 0x66, 0xdb, 0x3a, 0x32, 0xba, 0x66, 0xd7, 0xe8, + 0x7e, 0xdb, 0x9d, 0x98, 0x87, 0xd6, 0xa1, 0x61, 0x9b, 0x5d, 0x28, 0x34, 0xbb, 0x66, 0xf7, 0xc6, 0x3c, 0xec, 0x4e, + 0x6c, 0x2c, 0x6d, 0x59, 0x9d, 0x8e, 0xe9, 0xd8, 0x56, 0xa7, 0x63, 0x74, 0xac, 0xa3, 0x23, 0xd3, 0x69, 0x5b, 0x47, + 0x47, 0xe7, 0x9d, 0xae, 0xd5, 0x86, 0x77, 0xed, 0xf6, 0xa4, 0x6d, 0x39, 0x8e, 0x09, 0x7f, 0x19, 0x5d, 0xab, 0x45, + 0x3f, 0x1c, 0xc7, 0x6a, 0x3b, 0x86, 0xed, 0x77, 0x5a, 0xd6, 0xd1, 0x0b, 0x03, 0xff, 0xc6, 0x6a, 0x06, 0xfe, 0x05, + 0xdd, 0x18, 0x2f, 0xac, 0xd6, 0x11, 0xfd, 0xc2, 0x0e, 0x6f, 0x0e, 0xbb, 0xff, 0x54, 0x0f, 0x1a, 0xe7, 0xe0, 0xd0, + 0x1c, 0xba, 0x1d, 0xab, 0xdd, 0x36, 0x0e, 0x1d, 0xab, 0xdb, 0x5e, 0x98, 0x87, 0x2d, 0xeb, 0xe8, 0x78, 0x62, 0x3a, + 0xd6, 0xf1, 0xb1, 0x61, 0x9b, 0x6d, 0xab, 0x65, 0x38, 0xd6, 0x61, 0x1b, 0x7f, 0xb4, 0xad, 0xd6, 0xcd, 0xf1, 0x0b, + 0xeb, 0xa8, 0xb3, 0x38, 0xb2, 0x0e, 0x7f, 0x3e, 0xec, 0x5a, 0xad, 0xf6, 0xa2, 0x7d, 0x64, 0xb5, 0x8e, 0x6f, 0x8e, + 0xac, 0xc3, 0x85, 0xd9, 0x3a, 0xda, 0xda, 0xd2, 0x69, 0x59, 0x00, 0x23, 0x7c, 0x0d, 0x2f, 0x0c, 0xfe, 0x02, 0xfe, + 0x2c, 0xb0, 0xed, 0x1f, 0xd8, 0x4d, 0x5c, 0x6d, 0xfa, 0xc2, 0xea, 0x1e, 0x4f, 0xa8, 0x3a, 0x14, 0x98, 0xa2, 0x06, + 0x34, 0xb9, 0x31, 0xe9, 0xb3, 0xd8, 0x9d, 0x29, 0x3a, 0x12, 0x7f, 0xf8, 0xc7, 0x6e, 0x4c, 0xf8, 0x30, 0x7d, 0xf7, + 0x4f, 0xed, 0x27, 0x5b, 0x72, 0x48, 0x14, 0xff, 0x05, 0xff, 0x87, 0x72, 0x2c, 0x8e, 0x8c, 0xf3, 0xa6, 0x4b, 0xc9, + 0x77, 0xbb, 0x2f, 0x25, 0xbf, 0x59, 0xef, 0x73, 0x29, 0xf9, 0xee, 0xb3, 0x5f, 0x4a, 0x9e, 0x97, 0x7d, 0x6b, 0xde, + 0x95, 0x53, 0x41, 0x7d, 0xb7, 0x29, 0xab, 0x1c, 0x3c, 0x57, 0xbb, 0xbc, 0x58, 0x5f, 0x41, 0x68, 0xbf, 0x77, 0xe1, + 0xe0, 0x9b, 0x75, 0xc1, 0xe0, 0x33, 0x04, 0x1c, 0xfb, 0x2e, 0x24, 0x1c, 0xfb, 0xc3, 0x7a, 0x00, 0x56, 0x66, 0x9c, + 0xcd, 0xf1, 0xa6, 0xe6, 0xc2, 0xf5, 0x67, 0x19, 0x8b, 0x04, 0x25, 0x7d, 0x2c, 0x06, 0xc7, 0x35, 0x20, 0xcf, 0x20, + 0xc9, 0xac, 0x97, 0x41, 0x0c, 0x16, 0xc1, 0x60, 0xc9, 0x31, 0x8b, 0xd2, 0x52, 0x63, 0x4b, 0x04, 0x43, 0xbc, 0xe6, + 0x5e, 0x50, 0x8d, 0xef, 0xd1, 0x00, 0xb8, 0xbe, 0x77, 0xa7, 0xda, 0xaf, 0x02, 0x96, 0x75, 0xc2, 0x40, 0x1a, 0xb8, + 0xfd, 0xba, 0xf7, 0x45, 0x33, 0xdc, 0x92, 0xe1, 0x75, 0xf3, 0x48, 0x61, 0x24, 0xe5, 0xf6, 0x4e, 0xd1, 0x8c, 0x77, + 0xd7, 0x34, 0x6b, 0x3e, 0x5f, 0x68, 0xbe, 0xc5, 0x86, 0x38, 0xeb, 0xb8, 0x0c, 0xaa, 0x52, 0x22, 0xe3, 0x5a, 0x80, + 0xe4, 0x02, 0x6a, 0x6e, 0x68, 0x9c, 0x73, 0xaa, 0xb6, 0x82, 0xfc, 0x8e, 0x2d, 0xbd, 0x2b, 0xf4, 0x29, 0x1b, 0x27, + 0x3f, 0xdb, 0xa0, 0x5c, 0xe1, 0xfd, 0x0a, 0x9c, 0x28, 0xe7, 0x78, 0xc6, 0xa1, 0x0c, 0xe7, 0x8d, 0xd4, 0x2f, 0x69, + 0x23, 0xd2, 0x85, 0xb3, 0xa9, 0xf2, 0xa2, 0x8d, 0x6e, 0x09, 0x0e, 0x5b, 0x0a, 0x2e, 0x08, 0x3f, 0x4f, 0x4e, 0x00, + 0x29, 0x39, 0x6a, 0xa0, 0x9f, 0xc3, 0xb6, 0xce, 0x44, 0xbd, 0xc7, 0xb0, 0x89, 0x79, 0xd0, 0x65, 0x45, 0x8e, 0x36, + 0xb3, 0x99, 0xf9, 0xa1, 0x9b, 0xf4, 0x90, 0x4d, 0x93, 0x58, 0xde, 0x16, 0x7a, 0x2c, 0xf4, 0xb7, 0x18, 0xd3, 0xc9, + 0x1d, 0xf3, 0x4e, 0xd0, 0xf3, 0x61, 0x9b, 0xfd, 0x5d, 0xe6, 0x70, 0xb6, 0x29, 0x98, 0xa3, 0x38, 0x9d, 0x63, 0xc3, + 0x39, 0x32, 0xac, 0xe3, 0x8e, 0x9e, 0x8a, 0x03, 0x27, 0x77, 0x59, 0x00, 0x08, 0x38, 0x40, 0x64, 0xc3, 0xf4, 0x02, + 0x2f, 0xf1, 0x5c, 0x3f, 0x05, 0x7e, 0xb8, 0x28, 0xa4, 0xfc, 0x6b, 0x1d, 0x27, 0x30, 0x47, 0xc1, 0xf4, 0xa2, 0xf3, + 0x87, 0x39, 0x66, 0xc9, 0x2d, 0x63, 0x41, 0x83, 0x61, 0x4c, 0xd9, 0x97, 0xe4, 0xf7, 0xb3, 0xac, 0x4f, 0xc9, 0x6a, + 0x6d, 0x9c, 0x04, 0x7c, 0x7f, 0x08, 0xc7, 0x87, 0x74, 0x64, 0xfc, 0xda, 0x84, 0x70, 0xff, 0xb5, 0x1b, 0xe1, 0x26, + 0x6c, 0x1f, 0x84, 0xfb, 0xaf, 0xcf, 0x8e, 0x70, 0x7f, 0x95, 0x11, 0x6e, 0xc1, 0x7f, 0x30, 0xbf, 0x61, 0x7a, 0x8f, + 0xcf, 0x1a, 0x24, 0x51, 0x79, 0xae, 0x1e, 0x10, 0x03, 0x0f, 0xf9, 0x25, 0x44, 0x14, 0xaf, 0x97, 0x85, 0x64, 0x9d, + 0xa8, 0x00, 0xc5, 0x04, 0x1d, 0x94, 0x18, 0xd0, 0x03, 0x57, 0xb7, 0x2c, 0x39, 0x20, 0xbb, 0x55, 0xce, 0x82, 0xc4, + 0xb7, 0xde, 0x71, 0x39, 0x12, 0x2e, 0x74, 0xbf, 0x09, 0xa3, 0xa5, 0x8b, 0xd1, 0x5f, 0x55, 0x4c, 0xf2, 0x0d, 0x0f, + 0x36, 0x38, 0xe3, 0x4e, 0xc2, 0x60, 0x9a, 0xdd, 0x4a, 0xb2, 0xc1, 0x25, 0x71, 0xdc, 0xea, 0x3d, 0x73, 0x23, 0xd5, + 0xa0, 0xd7, 0xb0, 0xb8, 0xcf, 0xda, 0xf6, 0xb3, 0xd6, 0xe1, 0xb3, 0x23, 0x1b, 0xfe, 0x77, 0x58, 0x3b, 0x35, 0x78, + 0xc5, 0x65, 0x18, 0x40, 0x9e, 0x41, 0x51, 0xb3, 0xa9, 0xda, 0x2d, 0x63, 0x1f, 0xf2, 0x5a, 0xc7, 0xf5, 0x95, 0xa6, + 0xee, 0x7d, 0x5e, 0xa7, 0xb6, 0xc6, 0x22, 0x5c, 0x4b, 0xc3, 0xaa, 0x19, 0x8d, 0x17, 0xac, 0x41, 0xcf, 0x2e, 0xd5, + 0x90, 0x5f, 0xf3, 0xe9, 0xe6, 0xf3, 0x62, 0xed, 0xf4, 0x2a, 0x4f, 0x66, 0x2a, 0x92, 0x2a, 0xee, 0x84, 0x20, 0xbf, + 0xa2, 0xb4, 0x31, 0xde, 0x37, 0x66, 0x9c, 0x80, 0x68, 0xdf, 0x59, 0x0a, 0x4a, 0x97, 0x16, 0x28, 0x89, 0xd6, 0xc1, + 0x44, 0xc3, 0x9f, 0xee, 0x38, 0xd6, 0xbc, 0x83, 0xc8, 0xe2, 0x1f, 0xd6, 0x71, 0xd5, 0xdc, 0xa1, 0x9d, 0x67, 0x7e, + 0x8b, 0xc5, 0xaa, 0xb8, 0xcf, 0x12, 0x23, 0xc2, 0x7b, 0x6c, 0x5a, 0x5a, 0x73, 0xe0, 0x3e, 0xcb, 0x1a, 0x3e, 0x4b, + 0x8c, 0xe0, 0x39, 0xdc, 0x7d, 0x0e, 0xec, 0xa7, 0x4f, 0xa9, 0x16, 0x64, 0x51, 0xa7, 0x69, 0x9d, 0x4e, 0xf2, 0xa0, + 0xa1, 0x8a, 0x3b, 0x0f, 0x29, 0x6e, 0x68, 0x6f, 0x62, 0x84, 0xcf, 0x9f, 0x0f, 0x07, 0x8e, 0x8e, 0x59, 0x45, 0x45, + 0x76, 0x70, 0x9e, 0xb0, 0xf6, 0x7c, 0x3f, 0x43, 0x23, 0xbd, 0xd6, 0x95, 0x76, 0x05, 0x32, 0x93, 0x2d, 0xdc, 0x11, + 0x38, 0xf6, 0x82, 0x04, 0x72, 0x64, 0x50, 0xe0, 0x0a, 0x83, 0x1f, 0x51, 0x27, 0x93, 0xba, 0xda, 0x96, 0x6d, 0xd9, + 0x6a, 0xd6, 0x70, 0xe6, 0xcd, 0x07, 0x9b, 0x30, 0x71, 0x21, 0x15, 0xa7, 0x1f, 0xce, 0xc1, 0x8f, 0x2e, 0xf1, 0x12, + 0x1f, 0xf2, 0x3a, 0x82, 0x43, 0xdd, 0x92, 0xe4, 0xf2, 0x94, 0x7b, 0x37, 0xb8, 0xd1, 0x07, 0xcc, 0xed, 0x2d, 0x5c, + 0x71, 0x31, 0x8e, 0xdd, 0xf7, 0x40, 0x0c, 0x35, 0x55, 0x03, 0xdd, 0x00, 0x8b, 0x62, 0x53, 0xf6, 0x16, 0xea, 0x29, + 0xd0, 0x46, 0x57, 0xf9, 0x24, 0x66, 0x91, 0xbb, 0x84, 0x1c, 0x48, 0x9b, 0xd4, 0xe0, 0x98, 0x56, 0xe5, 0xa8, 0x56, + 0x71, 0x5e, 0x1c, 0x19, 0x4a, 0xcb, 0x31, 0x14, 0x1b, 0xd0, 0xad, 0x9a, 0x1a, 0x9b, 0xf4, 0xaa, 0xbf, 0xcb, 0xe0, + 0x81, 0xf0, 0xcb, 0x63, 0x9a, 0x07, 0x99, 0x3a, 0xf0, 0xab, 0xa4, 0x84, 0xe2, 0x17, 0x6b, 0x52, 0x66, 0x13, 0x8f, + 0x2e, 0x3d, 0x2f, 0xd8, 0x5d, 0xa2, 0x63, 0xde, 0x43, 0x5e, 0xc5, 0xd3, 0x37, 0xe8, 0x30, 0xec, 0x05, 0x8a, 0xf7, + 0xf1, 0xa3, 0xe6, 0x81, 0x33, 0xd3, 0x40, 0x82, 0x0f, 0x3c, 0xeb, 0x05, 0x80, 0x79, 0xf9, 0x35, 0x3d, 0x02, 0x0b, + 0x3c, 0x0d, 0xe1, 0xdf, 0xbc, 0x58, 0xfc, 0xe0, 0x66, 0x12, 0x96, 0xef, 0x06, 0x73, 0x40, 0x69, 0x6e, 0x30, 0xaf, + 0x98, 0x63, 0x91, 0xcf, 0x73, 0xa9, 0x34, 0xef, 0x2a, 0x37, 0x95, 0x8a, 0x5f, 0xde, 0x5f, 0x50, 0x5e, 0x57, 0x4d, + 0x05, 0x2a, 0x87, 0x2e, 0xba, 0xf9, 0x4d, 0xee, 0xf3, 0xc1, 0x97, 0x27, 0x4b, 0x96, 0xb8, 0x74, 0x0d, 0x04, 0xc2, + 0x2f, 0xb0, 0x03, 0x0a, 0x27, 0x34, 0x3c, 0x36, 0xd4, 0x60, 0xca, 0x6e, 0xbc, 0x09, 0x97, 0x4b, 0x0d, 0x85, 0xd3, + 0x29, 0x13, 0x2d, 0x3e, 0x07, 0x8e, 0x41, 0x0e, 0x07, 0x13, 0x17, 0x43, 0x1d, 0x0f, 0x82, 0x50, 0x1d, 0x7e, 0x99, + 0xf9, 0x66, 0x36, 0x2d, 0x02, 0x24, 0x57, 0xbf, 0x8c, 0x98, 0xff, 0xef, 0xc1, 0x97, 0x40, 0xb8, 0xbf, 0xbc, 0x52, + 0xf5, 0x7e, 0x62, 0x2d, 0x22, 0x36, 0x1b, 0x7c, 0x59, 0x93, 0x64, 0x1c, 0xc5, 0x7b, 0x1a, 0x8b, 0xda, 0x6e, 0xe5, + 0x21, 0xe7, 0xda, 0x7b, 0x09, 0xf5, 0x43, 0x2e, 0xad, 0x83, 0x04, 0xb8, 0x29, 0xc8, 0xd8, 0x4e, 0x1f, 0xe5, 0xe7, + 0xb1, 0xef, 0x4e, 0x3e, 0xf4, 0xe9, 0x4d, 0xe1, 0xc1, 0x04, 0x6a, 0x3d, 0x71, 0x57, 0x3d, 0x24, 0xaf, 0x72, 0x21, + 0x78, 0x4f, 0x53, 0x69, 0xc6, 0xd9, 0xd5, 0xee, 0x65, 0xdc, 0xca, 0x1b, 0xfc, 0x32, 0x7e, 0xea, 0x76, 0xe1, 0x25, + 0x4c, 0x7c, 0x0a, 0x1f, 0xd2, 0x54, 0x08, 0xea, 0x24, 0xa2, 0xa2, 0x60, 0x6d, 0xb5, 0x15, 0xa7, 0xfb, 0x6d, 0xe7, + 0xc6, 0xb1, 0x17, 0x2d, 0xc7, 0xea, 0xfe, 0xec, 0x74, 0x17, 0x6d, 0xeb, 0xd8, 0x37, 0xdb, 0xd6, 0x31, 0xfc, 0xf9, + 0xf9, 0xd8, 0xea, 0x2e, 0xcc, 0x96, 0x75, 0xf8, 0xb3, 0xd3, 0xf2, 0xcd, 0xae, 0x75, 0x0c, 0x7f, 0xce, 0xa9, 0x15, + 0x08, 0x40, 0x24, 0xef, 0x7c, 0x59, 0xc0, 0x02, 0xd2, 0xef, 0xec, 0x4e, 0xd6, 0x28, 0x90, 0xb7, 0x9a, 0x7b, 0x5d, + 0x40, 0x19, 0x94, 0xfa, 0x07, 0x4d, 0x11, 0xfa, 0x5a, 0x30, 0x60, 0x94, 0xec, 0x47, 0x98, 0xb7, 0x09, 0x3f, 0x74, + 0x91, 0x5f, 0xa5, 0xf6, 0x18, 0xf1, 0x36, 0xf5, 0x39, 0x45, 0x44, 0x22, 0x58, 0xba, 0x08, 0xfe, 0x69, 0x85, 0xa1, + 0xf1, 0x44, 0xfa, 0x2c, 0x09, 0x2b, 0xe5, 0xc9, 0xc8, 0xd3, 0xdd, 0x03, 0x47, 0x6f, 0x7e, 0x96, 0x25, 0xc3, 0xfc, + 0xac, 0x7d, 0x4b, 0x59, 0xca, 0x3e, 0xa9, 0x1f, 0xcc, 0xce, 0x94, 0x27, 0x56, 0x82, 0x88, 0xe2, 0x53, 0x2f, 0xca, + 0x86, 0x27, 0xa1, 0x68, 0xa7, 0x3e, 0x19, 0x8b, 0x0e, 0x59, 0x03, 0xcf, 0x80, 0x4b, 0xbe, 0x71, 0x7d, 0xc9, 0x90, + 0x4d, 0x6a, 0xf9, 0x28, 0xc3, 0xfc, 0x4f, 0x9f, 0xe6, 0x83, 0x33, 0x4b, 0xe3, 0x3e, 0x71, 0x3a, 0x40, 0x76, 0x3b, + 0xac, 0xbd, 0xd5, 0xa6, 0x72, 0x77, 0x2c, 0xfa, 0x3c, 0x08, 0xb5, 0xb0, 0x9b, 0x12, 0x16, 0x1b, 0x8d, 0x86, 0x9d, + 0x15, 0x7b, 0x0d, 0x88, 0xe2, 0x5f, 0x12, 0x75, 0x54, 0xbd, 0x1f, 0x08, 0xf3, 0x83, 0x60, 0x4b, 0xfc, 0x7d, 0x2e, + 0x8b, 0xa9, 0x00, 0x9a, 0x2d, 0xf3, 0xd8, 0xe1, 0x20, 0xfe, 0x67, 0x4f, 0x02, 0x9d, 0x35, 0xc1, 0x5e, 0xa2, 0x74, + 0x5a, 0x0b, 0xce, 0x7b, 0x19, 0x5d, 0x25, 0x82, 0xca, 0xe2, 0x53, 0x15, 0x8a, 0x20, 0x9d, 0x2c, 0x66, 0x90, 0xce, + 0x8c, 0x45, 0x33, 0x6a, 0x91, 0x17, 0x18, 0x1e, 0x26, 0x33, 0x11, 0x8e, 0xa3, 0xfa, 0xd3, 0xa7, 0x8d, 0x44, 0x88, + 0x8c, 0x73, 0x62, 0x96, 0x64, 0x39, 0x2e, 0x55, 0x19, 0xbf, 0xa9, 0x32, 0x8a, 0xc9, 0xfa, 0x45, 0xac, 0x21, 0x6c, + 0x5c, 0x69, 0xef, 0xe1, 0xcf, 0x31, 0x73, 0x13, 0x8b, 0x5f, 0x96, 0x6a, 0x12, 0x71, 0x37, 0x1c, 0xd6, 0x06, 0xeb, + 0x56, 0x1e, 0x41, 0x93, 0x47, 0xa8, 0x7d, 0xb2, 0x79, 0xb9, 0xe6, 0x51, 0x1d, 0xa0, 0x8f, 0x8f, 0x76, 0x1e, 0x80, + 0xec, 0x6d, 0xe2, 0x52, 0x60, 0x18, 0x99, 0xe4, 0x86, 0x89, 0x2b, 0xd2, 0x36, 0x02, 0x5f, 0xde, 0xaf, 0x35, 0xbf, + 0x90, 0x22, 0x3f, 0x0c, 0xdf, 0x5e, 0x7c, 0xad, 0xf0, 0xfd, 0x4f, 0xd6, 0x02, 0x28, 0xc8, 0x50, 0x6a, 0x9f, 0x01, + 0xa5, 0xf6, 0x51, 0x78, 0x6e, 0x29, 0xc8, 0x77, 0x93, 0x1e, 0x10, 0x04, 0x51, 0x01, 0x4d, 0x36, 0x14, 0xcb, 0xb5, + 0x9f, 0x78, 0x2b, 0x37, 0x4a, 0x0e, 0x30, 0xaf, 0x0f, 0x20, 0x39, 0xb5, 0x29, 0x1e, 0x04, 0x99, 0x61, 0x88, 0xc0, + 0xad, 0x49, 0x20, 0xec, 0x30, 0x66, 0x9e, 0x9f, 0x99, 0x61, 0x88, 0x0f, 0xb8, 0x93, 0x09, 0x5b, 0x25, 0x83, 0x42, + 0xfe, 0xa0, 0x70, 0x92, 0xb0, 0xc4, 0x8c, 0x93, 0x88, 0xb9, 0x4b, 0x35, 0x0b, 0x10, 0x5e, 0xed, 0x2f, 0x5e, 0x8f, + 0x97, 0x5e, 0x92, 0x45, 0xd8, 0xa5, 0x09, 0x82, 0x41, 0x04, 0x0c, 0x71, 0x38, 0x4a, 0x39, 0x08, 0xcf, 0xc3, 0x79, + 0x69, 0x47, 0xe5, 0x9c, 0xcb, 0x29, 0xc6, 0x6f, 0x27, 0x49, 0x06, 0xb4, 0xc5, 0x93, 0xd0, 0xbf, 0xe6, 0x31, 0x2c, + 0xb2, 0x40, 0xc0, 0xea, 0xf0, 0x84, 0x8b, 0xb7, 0x0a, 0x86, 0x6f, 0x51, 0x3b, 0x36, 0x44, 0xa8, 0x6f, 0x8a, 0x6e, + 0x71, 0xc0, 0x2b, 0x03, 0x69, 0xa2, 0x9e, 0x31, 0xc9, 0x08, 0x8d, 0xe5, 0x02, 0x18, 0xa1, 0x82, 0xc1, 0xcc, 0xc2, + 0x19, 0x66, 0xee, 0x94, 0x38, 0x2a, 0xe4, 0x95, 0x3e, 0x7e, 0x7c, 0x35, 0xfa, 0xed, 0x3f, 0x90, 0x09, 0x65, 0xe1, + 0x88, 0x98, 0x12, 0x97, 0x72, 0x2d, 0xce, 0x7d, 0x1a, 0x23, 0x34, 0x96, 0x62, 0x53, 0x11, 0xa2, 0x47, 0x6c, 0xad, + 0x74, 0x74, 0x25, 0x42, 0x34, 0x42, 0x92, 0x24, 0x5d, 0x44, 0xbe, 0x18, 0xc1, 0xf2, 0x8e, 0x44, 0x4c, 0x14, 0xe5, + 0x97, 0xbb, 0x97, 0xc7, 0x4a, 0x1e, 0xc3, 0xa8, 0xce, 0xa2, 0x87, 0xf6, 0xd0, 0xf0, 0xc4, 0x55, 0x90, 0x69, 0x41, + 0xf6, 0x23, 0xee, 0x1d, 0xc0, 0x34, 0x17, 0xe1, 0x92, 0x59, 0x5e, 0x78, 0x70, 0xcb, 0xc6, 0xa6, 0xbb, 0xf2, 0xc8, + 0x2e, 0x07, 0xf5, 0x6e, 0x0a, 0x71, 0x7e, 0x99, 0xb9, 0x0b, 0xf1, 0xd7, 0x69, 0x0e, 0xca, 0xb0, 0x18, 0x93, 0xb3, + 0xd3, 0xca, 0xef, 0x01, 0x21, 0x7e, 0x81, 0x04, 0xc7, 0x70, 0x78, 0x72, 0xe0, 0x0e, 0x8b, 0x41, 0x81, 0x2d, 0x91, + 0xbd, 0xa6, 0x48, 0x04, 0x4e, 0x29, 0xb6, 0xaf, 0x08, 0xe3, 0x9b, 0x3f, 0x98, 0xe1, 0x6c, 0x26, 0x07, 0xf2, 0xb5, + 0x8a, 0xc3, 0xcb, 0x80, 0x96, 0x6f, 0xe9, 0x70, 0x45, 0x5f, 0xaa, 0x7e, 0x22, 0xfb, 0xa9, 0xf6, 0x30, 0x82, 0x37, + 0xcc, 0x19, 0x8e, 0x7b, 0x25, 0x20, 0x70, 0x06, 0xb1, 0xc7, 0x54, 0x89, 0xe3, 0x91, 0x72, 0xfa, 0x89, 0x06, 0xce, + 0xe5, 0xd1, 0x60, 0x40, 0x68, 0xae, 0x8c, 0xed, 0x00, 0x88, 0x35, 0x99, 0x7c, 0x60, 0xb2, 0x09, 0x34, 0x34, 0xc9, + 0x5d, 0x16, 0x1b, 0x95, 0xa7, 0x53, 0x1d, 0xe3, 0x81, 0x2b, 0xb6, 0x5f, 0x61, 0x83, 0xc2, 0xc6, 0xe3, 0xeb, 0x0e, + 0xf8, 0x5d, 0xf4, 0x53, 0x42, 0xf3, 0xca, 0x57, 0x84, 0xd1, 0x4d, 0xdf, 0xbd, 0x0f, 0x25, 0x33, 0x26, 0x1e, 0xd1, + 0xe4, 0x1c, 0x4b, 0x2f, 0x84, 0x27, 0x71, 0xe5, 0xa0, 0x65, 0x09, 0x51, 0xaa, 0x87, 0x4d, 0x4e, 0x62, 0xb2, 0xeb, + 0xac, 0xc9, 0x75, 0x8b, 0x93, 0x41, 0xe4, 0x99, 0xe6, 0xe7, 0xb0, 0xf0, 0x12, 0xd1, 0x42, 0x7a, 0x72, 0x00, 0xf3, + 0x83, 0x28, 0x2c, 0x05, 0xc6, 0xc9, 0xd3, 0x21, 0xd4, 0x8b, 0x1b, 0x93, 0x29, 0xd6, 0xd9, 0x54, 0xf0, 0x7c, 0x28, + 0x58, 0x4a, 0xd9, 0xfd, 0xa4, 0x2a, 0x55, 0x5e, 0xc6, 0xae, 0x67, 0x02, 0x77, 0x67, 0x0f, 0xfa, 0x61, 0x8d, 0xa9, + 0x83, 0xd2, 0x7e, 0xc2, 0x44, 0x90, 0x83, 0xf3, 0xa4, 0x21, 0x0e, 0x42, 0x53, 0x15, 0xe2, 0x67, 0xb7, 0x54, 0xc8, + 0xf7, 0xf1, 0xb6, 0x5a, 0x39, 0xe7, 0x94, 0x55, 0x5b, 0xb9, 0x9a, 0xfa, 0x18, 0x77, 0x7c, 0xa5, 0x36, 0x96, 0x42, + 0xbd, 0xf3, 0x64, 0x00, 0x55, 0x85, 0x2e, 0xde, 0x5d, 0xad, 0xa8, 0xb2, 0xde, 0x3f, 0x39, 0x20, 0xb1, 0x74, 0x48, + 0x3b, 0x6c, 0x78, 0x02, 0xa6, 0xdc, 0xb4, 0xe8, 0xee, 0x6a, 0xc5, 0x97, 0x94, 0x7e, 0xd1, 0x9b, 0x83, 0x45, 0xb2, + 0xf4, 0x87, 0xff, 0x07, 0x52, 0xaf, 0x09, 0x6c, 0x30, 0x6a, 0x03, 0x00}; } // namespace web_server } // namespace esphome diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 4caac025aa..becb5bc2c7 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -91,7 +91,7 @@ void DeferredUpdateEventSource::process_deferred_queue_() { while (!deferred_queue_.empty()) { DeferredEvent &de = deferred_queue_.front(); std::string message = de.message_generator_(web_server_, de.source_); - if (this->try_send(message.c_str(), "state")) { + if (this->send(message.c_str(), "state") != DISCARDED) { // O(n) but memory efficiency is more important than speed here which is why std::vector was chosen deferred_queue_.erase(deferred_queue_.begin()); } else { @@ -131,7 +131,7 @@ void DeferredUpdateEventSource::deferrable_send_state(void *source, const char * deq_push_back_with_dedup_(source, message_generator); } else { std::string message = message_generator(web_server_, source); - if (!this->try_send(message.c_str(), "state")) { + if (this->send(message.c_str(), "state") == DISCARDED) { deq_push_back_with_dedup_(source, message_generator); } } @@ -171,8 +171,8 @@ void DeferredUpdateEventSourceList::add_new_client(WebServer *ws, AsyncWebServer ws->defer([this, ws, es]() { this->on_client_connect_(ws, es); }); }); - es->onDisconnect([this, ws](AsyncEventSource *source, AsyncEventSourceClient *client) { - ws->defer([this, source]() { this->on_client_disconnect_((DeferredUpdateEventSource *) source); }); + es->onDisconnect([this, ws, es](AsyncEventSourceClient *client) { + ws->defer([this, es]() { this->on_client_disconnect_((DeferredUpdateEventSource *) es); }); }); es->handleRequest(request); @@ -282,21 +282,32 @@ void WebServer::loop() { this->events_.loop(); } void WebServer::dump_config() { - ESP_LOGCONFIG(TAG, "Web Server:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->base_->get_port()); + ESP_LOGCONFIG(TAG, + "Web Server:\n" + " Address: %s:%u", + network::get_use_address().c_str(), this->base_->get_port()); } float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f; } #ifdef USE_WEBSERVER_LOCAL void WebServer::handle_index_request(AsyncWebServerRequest *request) { +#ifndef USE_ESP8266 + AsyncWebServerResponse *response = request->beginResponse(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ)); +#else AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ)); +#endif response->addHeader("Content-Encoding", "gzip"); request->send(response); } #elif USE_WEBSERVER_VERSION >= 2 void WebServer::handle_index_request(AsyncWebServerRequest *request) { +#ifndef USE_ESP8266 + AsyncWebServerResponse *response = + request->beginResponse(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE); +#else AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE); +#endif // No gzip header here because the HTML file is so small request->send(response); } @@ -315,8 +326,13 @@ void WebServer::handle_pna_cors_request(AsyncWebServerRequest *request) { #ifdef USE_WEBSERVER_CSS_INCLUDE void WebServer::handle_css_request(AsyncWebServerRequest *request) { +#ifndef USE_ESP8266 + AsyncWebServerResponse *response = + request->beginResponse(200, "text/css", ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE); +#else AsyncWebServerResponse *response = request->beginResponse_P(200, "text/css", ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE); +#endif response->addHeader("Content-Encoding", "gzip"); request->send(response); } @@ -324,8 +340,13 @@ void WebServer::handle_css_request(AsyncWebServerRequest *request) { #ifdef USE_WEBSERVER_JS_INCLUDE void WebServer::handle_js_request(AsyncWebServerRequest *request) { +#ifndef USE_ESP8266 + AsyncWebServerResponse *response = + request->beginResponse(200, "text/javascript", ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE); +#else AsyncWebServerResponse *response = request->beginResponse_P(200, "text/javascript", ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE); +#endif response->addHeader("Content-Encoding", "gzip"); request->send(response); } @@ -553,7 +574,7 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) #endif #ifdef USE_BINARY_SENSOR -void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) { +void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) { if (this->events_.empty()) return; this->events_.deferrable_send_state(obj, "state", binary_sensor_state_json_generator); @@ -1835,7 +1856,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c } #endif -bool WebServer::canHandle(AsyncWebServerRequest *request) { +bool WebServer::canHandle(AsyncWebServerRequest *request) const { if (request->url() == "/") return true; @@ -1857,12 +1878,6 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) { #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) { -#ifdef USE_ARDUINO - // Header needs to be added to interesting header list for it to not be - // nuked by the time we handle the request later. - // Only required in Arduino framework. - request->addInterestingHeader(HEADER_CORS_REQ_PNA); -#endif return true; } #endif @@ -2143,7 +2158,7 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { #endif } -bool WebServer::isRequestHandlerTrivial() { return false; } +bool WebServer::isRequestHandlerTrivial() const { return false; } void WebServer::add_entity_config(EntityBase *entity, float weight, uint64_t group) { this->sorting_entitys_[entity] = SortingComponents{weight, group}; diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index e4f044c50b..53ee4d1212 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -99,7 +99,7 @@ class DeferredUpdateEventSource : public AsyncEventSource { protected: // surface a couple methods from the base class using AsyncEventSource::handleRequest; - using AsyncEventSource::try_send; + using AsyncEventSource::send; ListEntitiesIterator entities_iterator_; // vector is used very specifically for its zero memory overhead even though items are popped from the front (memory @@ -269,7 +269,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif #ifdef USE_BINARY_SENSOR - void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override; + void on_binary_sensor_update(binary_sensor::BinarySensor *obj) override; /// Handle a binary sensor request under '/binary_sensor/'. void handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match); @@ -468,11 +468,11 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif /// Override the web handler's canHandle method. - bool canHandle(AsyncWebServerRequest *request) override; + bool canHandle(AsyncWebServerRequest *request) const override; /// Override the web handler's handleRequest method. void handleRequest(AsyncWebServerRequest *request) override; /// This web handle is not trivial. - bool isRequestHandlerTrivial() override; // NOLINT(readability-identifier-naming) + bool isRequestHandlerTrivial() const override; // NOLINT(readability-identifier-naming) void add_entity_config(EntityBase *entity, float weight, uint64_t group); void add_sorting_group(uint64_t group_id, const std::string &group_name, float weight); diff --git a/esphome/components/web_server_base/__init__.py b/esphome/components/web_server_base/__init__.py index f50ee59b9c..c17bab2128 100644 --- a/esphome/components/web_server_base/__init__.py +++ b/esphome/components/web_server_base/__init__.py @@ -36,5 +36,7 @@ async def to_code(config): cg.add_library("WiFi", None) cg.add_library("FS", None) cg.add_library("Update", None) - # https://github.com/esphome/ESPAsyncWebServer/blob/master/library.json - cg.add_library("esphome/ESPAsyncWebServer-esphome", "3.3.0") + if CORE.is_esp8266: + cg.add_library("ESP8266WiFi", None) + # https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/library.json + cg.add_library("ESP32Async/ESPAsyncWebServer", "3.7.8") diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 7c09022f27..2835585387 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -1,8 +1,8 @@ #include "web_server_base.h" #ifdef USE_NETWORK -#include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ARDUINO #include diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index f876d163bc..641006cb99 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -23,7 +23,7 @@ class MiddlewareHandler : public AsyncWebHandler { public: MiddlewareHandler(AsyncWebHandler *next) : next_(next) {} - bool canHandle(AsyncWebServerRequest *request) override { return next_->canHandle(request); } + bool canHandle(AsyncWebServerRequest *request) const override { return next_->canHandle(request); } void handleRequest(AsyncWebServerRequest *request) override { next_->handleRequest(request); } void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) override { @@ -32,7 +32,7 @@ class MiddlewareHandler : public AsyncWebHandler { void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override { next_->handleBody(request, data, len, index, total); } - bool isRequestHandlerTrivial() override { return next_->isRequestHandlerTrivial(); } + bool isRequestHandlerTrivial() const override { return next_->isRequestHandlerTrivial(); } protected: AsyncWebHandler *next_; @@ -131,12 +131,12 @@ class OTARequestHandler : public AsyncWebHandler { void handleRequest(AsyncWebServerRequest *request) override; void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) override; - bool canHandle(AsyncWebServerRequest *request) override { + bool canHandle(AsyncWebServerRequest *request) const override { return request->url() == "/update" && request->method() == HTTP_POST; } // NOLINTNEXTLINE(readability-identifier-naming) - bool isRequestHandlerTrivial() override { return false; } + bool isRequestHandlerTrivial() const override { return false; } protected: uint32_t last_ota_progress_{0}; diff --git a/esphome/components/web_server_idf/__init__.py b/esphome/components/web_server_idf/__init__.py index 73c51f8cb5..506e1c5c13 100644 --- a/esphome/components/web_server_idf/__init__.py +++ b/esphome/components/web_server_idf/__init__.py @@ -8,8 +8,6 @@ CONFIG_SCHEMA = cv.All( cv.only_with_esp_idf, ) -AUTO_LOAD = ["web_server"] - async def to_code(config): # Increase the maximum supported size of headers section in HTTP request packet to be processed by the server diff --git a/esphome/components/web_server_idf/utils.cpp b/esphome/components/web_server_idf/utils.cpp index 6299937ce1..349acce50d 100644 --- a/esphome/components/web_server_idf/utils.cpp +++ b/esphome/components/web_server_idf/utils.cpp @@ -1,7 +1,7 @@ #ifdef USE_ESP_IDF #include -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "http_parser.h" #include "utils.h" diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index 428bd262e8..90fdf720cd 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -2,17 +2,19 @@ #include -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esp_tls_crypto.h" #include "utils.h" +#include "web_server_idf.h" + +#ifdef USE_WEBSERVER #include "esphome/components/web_server/web_server.h" #include "esphome/components/web_server/list_entities.h" - -#include "web_server_idf.h" +#endif // USE_WEBSERVER namespace esphome { namespace web_server_idf { @@ -273,6 +275,7 @@ void AsyncResponseStream::printf(const char *fmt, ...) { this->print(str); } +#ifdef USE_WEBSERVER AsyncEventSource::~AsyncEventSource() { for (auto *ses : this->sessions_) { delete ses; // NOLINT(cppcoreguidelines-owning-memory) @@ -511,6 +514,7 @@ void AsyncEventSourceResponse::deferrable_send_state(void *source, const char *e } } } +#endif } // namespace web_server_idf } // namespace esphome diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index 13a3ef168d..8dafdf11ef 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -1,6 +1,7 @@ #pragma once #ifdef USE_ESP_IDF +#include "esphome/core/defines.h" #include #include @@ -12,10 +13,12 @@ #include namespace esphome { +#ifdef USE_WEBSERVER namespace web_server { class WebServer; class ListEntitiesIterator; }; // namespace web_server +#endif namespace web_server_idf { #define F(string_literal) (string_literal) @@ -132,8 +135,8 @@ class AsyncWebServerRequest { return res; } // NOLINTNEXTLINE(readability-identifier-naming) - AsyncWebServerResponse *beginResponse_P(int code, const char *content_type, const uint8_t *data, - const size_t data_size) { + AsyncWebServerResponse *beginResponse(int code, const char *content_type, const uint8_t *data, + const size_t data_size) { auto *res = new AsyncWebServerResponseProgmem(this, data, data_size); // NOLINT(cppcoreguidelines-owning-memory) this->init_response_(res, code, content_type); return res; @@ -208,7 +211,7 @@ class AsyncWebHandler { public: virtual ~AsyncWebHandler() {} // NOLINTNEXTLINE(readability-identifier-naming) - virtual bool canHandle(AsyncWebServerRequest *request) { return false; } + virtual bool canHandle(AsyncWebServerRequest *request) const { return false; } // NOLINTNEXTLINE(readability-identifier-naming) virtual void handleRequest(AsyncWebServerRequest *request) {} // NOLINTNEXTLINE(readability-identifier-naming) @@ -217,9 +220,10 @@ class AsyncWebHandler { // NOLINTNEXTLINE(readability-identifier-naming) virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {} // NOLINTNEXTLINE(readability-identifier-naming) - virtual bool isRequestHandlerTrivial() { return true; } + virtual bool isRequestHandlerTrivial() const { return true; } }; +#ifdef USE_WEBSERVER class AsyncEventSource; class AsyncEventSourceResponse; @@ -286,7 +290,7 @@ class AsyncEventSource : public AsyncWebHandler { ~AsyncEventSource() override; // NOLINTNEXTLINE(readability-identifier-naming) - bool canHandle(AsyncWebServerRequest *request) override { + bool canHandle(AsyncWebServerRequest *request) const override { return request->method() == HTTP_GET && request->url() == this->url_; } // NOLINTNEXTLINE(readability-identifier-naming) @@ -307,10 +311,13 @@ class AsyncEventSource : public AsyncWebHandler { connect_handler_t on_connect_{}; esphome::web_server::WebServer *web_server_; }; +#endif // USE_WEBSERVER class DefaultHeaders { friend class AsyncWebServerRequest; +#ifdef USE_WEBSERVER friend class AsyncEventSourceResponse; +#endif public: // NOLINTNEXTLINE(readability-identifier-naming) diff --git a/esphome/components/weikai/weikai.cpp b/esphome/components/weikai/weikai.cpp index 19aa09e20d..ebe987cc65 100644 --- a/esphome/components/weikai/weikai.cpp +++ b/esphome/components/weikai/weikai.cpp @@ -102,7 +102,7 @@ WeikaiRegister &WeikaiRegister::operator|=(uint8_t value) { // The WeikaiComponent methods /////////////////////////////////////////////////////////////////////////////// void WeikaiComponent::loop() { - if ((this->component_state_ & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP) + if (!this->is_in_loop_state()) return; // If there are some bytes in the receive FIFO we transfers them to the ring buffers @@ -112,7 +112,7 @@ void WeikaiComponent::loop() { transferred += child->xfer_fifo_to_buffer_(); } if (transferred > 0) { - ESP_LOGV(TAG, "we transferred %d bytes from fifo to buffer...", transferred); + ESP_LOGV(TAG, "transferred %d bytes from fifo to buffer", transferred); } #ifdef TEST_COMPONENT @@ -121,8 +121,8 @@ void WeikaiComponent::loop() { uint32_t time = 0; if (test_mode_ == 1) { // test component in loopback - ESP_LOGI(TAG, "Component loop %" PRIu32 " for %s : %" PRIu32 " ms since last call ...", loop_count++, - this->get_name(), millis() - loop_time); + ESP_LOGI(TAG, "Component loop %" PRIu32 " for %s : %" PRIu32 " ms since last call", loop_count++, this->get_name(), + millis() - loop_time); loop_time = millis(); char message[64]; elapsed_ms(time); // set time to now @@ -134,14 +134,14 @@ void WeikaiComponent::loop() { uint32_t const start_time = millis(); while (children_[i]->tx_fifo_is_not_empty_()) { // wait until buffer empty if (millis() - start_time > 1500) { - ESP_LOGE(TAG, "timeout while flushing - %d bytes left in buffer...", children_[i]->tx_in_fifo_()); + ESP_LOGE(TAG, "timeout while flushing - %d bytes left in buffer", children_[i]->tx_in_fifo_()); break; } yield(); // reschedule our thread to avoid blocking } bool status = children_[i]->uart_receive_test_(message); - ESP_LOGI(TAG, "Test %s => send/received %u bytes %s - execution time %" PRIu32 " ms...", message, - RING_BUFFER_SIZE, status ? "correctly" : "with error", elapsed_ms(time)); + ESP_LOGI(TAG, "Test %s => send/received %u bytes %s - execution time %" PRIu32 " ms", message, RING_BUFFER_SIZE, + status ? "correctly" : "with error", elapsed_ms(time)); } } @@ -255,10 +255,10 @@ std::string WeikaiGPIOPin::dump_summary() const { // The WeikaiChannel methods /////////////////////////////////////////////////////////////////////////////// void WeikaiChannel::setup_channel() { - ESP_LOGCONFIG(TAG, " Setting up UART %s:%s ...", this->parent_->get_name(), this->get_channel_name()); + ESP_LOGCONFIG(TAG, " Setting up UART %s:%s", this->parent_->get_name(), this->get_channel_name()); // we enable transmit and receive on this channel if (this->check_channel_down()) { - ESP_LOGCONFIG(TAG, " Error channel %s not working...", this->get_channel_name()); + ESP_LOGCONFIG(TAG, " Error channel %s not working", this->get_channel_name()); } this->reset_fifo_(); this->receive_buffer_.clear(); @@ -267,11 +267,13 @@ void WeikaiChannel::setup_channel() { } void WeikaiChannel::dump_channel() { - ESP_LOGCONFIG(TAG, " UART %s ...", this->get_channel_name()); - ESP_LOGCONFIG(TAG, " Baud rate: %" PRIu32 " Bd", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", p2s(this->parity_)); + ESP_LOGCONFIG(TAG, + " UART %s\n" + " Baud rate: %" PRIu32 " Bd\n" + " Data bits: %u\n" + " Stop bits: %u\n" + " Parity: %s", + this->get_channel_name(), this->baud_rate_, this->data_bits_, this->stop_bits_, p2s(this->parity_)); } void WeikaiChannel::reset_fifo_() { @@ -407,7 +409,7 @@ bool WeikaiChannel::read_array(uint8_t *buffer, size_t length) { bool status = true; auto available = this->receive_buffer_.count(); if (length > available) { - ESP_LOGW(TAG, "read_array: buffer underflow requested %d bytes only %d bytes available...", length, available); + ESP_LOGW(TAG, "read_array: buffer underflow requested %d bytes only %d bytes available", length, available); length = available; status = false; } @@ -422,7 +424,7 @@ bool WeikaiChannel::read_array(uint8_t *buffer, size_t length) { void WeikaiChannel::write_array(const uint8_t *buffer, size_t length) { if (length > XFER_MAX_SIZE) { - ESP_LOGE(TAG, "Write_array: invalid call - requested %d bytes but max size %d ...", length, XFER_MAX_SIZE); + ESP_LOGE(TAG, "Write_array: invalid call - requested %d bytes but max size %d", length, XFER_MAX_SIZE); length = XFER_MAX_SIZE; } this->reg(0).write_fifo(const_cast(buffer), length); @@ -432,7 +434,7 @@ void WeikaiChannel::flush() { uint32_t const start_time = millis(); while (this->tx_fifo_is_not_empty_()) { // wait until buffer empty if (millis() - start_time > 200) { - ESP_LOGW(TAG, "WARNING flush timeout - still %d bytes not sent after 200 ms...", this->tx_in_fifo_()); + ESP_LOGW(TAG, "WARNING flush timeout - still %d bytes not sent after 200 ms", this->tx_in_fifo_()); return; } yield(); // reschedule our thread to avoid blocking @@ -509,7 +511,7 @@ void WeikaiChannel::uart_send_test_(char *message) { this->flush(); to_send -= XFER_MAX_SIZE; } - ESP_LOGV(TAG, "%s => sent %d bytes - exec time %d µs ...", message, RING_BUFFER_SIZE, micros() - start_exec); + ESP_LOGV(TAG, "%s => sent %d bytes - exec time %d µs", message, RING_BUFFER_SIZE, micros() - start_exec); } /// @brief test read_array method @@ -526,7 +528,7 @@ bool WeikaiChannel::uart_receive_test_(char *message) { while (XFER_MAX_SIZE > this->available()) { this->xfer_fifo_to_buffer_(); if (millis() - start_time > 1500) { - ESP_LOGE(TAG, "uart_receive_test_() timeout: only %d bytes received...", this->available()); + ESP_LOGE(TAG, "uart_receive_test_() timeout: only %d bytes received", this->available()); break; } yield(); // reschedule our thread to avoid blocking @@ -538,20 +540,20 @@ bool WeikaiChannel::uart_receive_test_(char *message) { uint8_t peek_value = 0; this->peek_byte(&peek_value); if (peek_value != 0) { - ESP_LOGE(TAG, "Peek first byte value error..."); + ESP_LOGE(TAG, "Peek first byte value error"); status = false; } for (size_t i = 0; i < RING_BUFFER_SIZE; i++) { if (buffer[i] != i % XFER_MAX_SIZE) { - ESP_LOGE(TAG, "Read buffer contains error...b=%x i=%x", buffer[i], i % XFER_MAX_SIZE); + ESP_LOGE(TAG, "Read buffer contains error b=%x i=%x", buffer[i], i % XFER_MAX_SIZE); print_buffer(buffer); status = false; break; } } - ESP_LOGV(TAG, "%s => received %d bytes status %s - exec time %d µs ...", message, received, status ? "OK" : "ERROR", + ESP_LOGV(TAG, "%s => received %d bytes status %s - exec time %d µs", message, received, status ? "OK" : "ERROR", micros() - start_exec); return status; } diff --git a/esphome/components/weikai_i2c/weikai_i2c.cpp b/esphome/components/weikai_i2c/weikai_i2c.cpp index 3b353eb662..32e7ec4f23 100644 --- a/esphome/components/weikai_i2c/weikai_i2c.cpp +++ b/esphome/components/weikai_i2c/weikai_i2c.cpp @@ -96,7 +96,7 @@ void WeikaiRegisterI2C::read_fifo(uint8_t *data, size_t length) const { #endif } else { // error this->comp_->status_set_warning(); - ESP_LOGE(TAG, "WeikaiRegisterI2C::read_fifo() @%02X reg=N/A ch=%d I2C_code:%d len=%d buf=%02X...", address, + ESP_LOGE(TAG, "WeikaiRegisterI2C::read_fifo() @%02X reg=N/A ch=%d I2C_code:%d len=%d buf=%02X", address, this->channel_, (int) error, length, data[0]); } } @@ -131,8 +131,8 @@ void WeikaiRegisterI2C::write_fifo(uint8_t *data, size_t length) { #endif } else { // error this->comp_->status_set_warning(); - ESP_LOGE(TAG, "WK2168Reg::write_fifo() @%02X reg=N/A, ch=%d I2C_code:%d len=%d, buf=%02X...", address, - this->channel_, (int) error, length, data[0]); + ESP_LOGE(TAG, "WK2168Reg::write_fifo() @%02X reg=N/A, ch=%d I2C_code:%d len=%d, buf=%02X", address, this->channel_, + (int) error, length, data[0]); } } @@ -160,11 +160,16 @@ void WeikaiComponentI2C::setup() { } void WeikaiComponentI2C::dump_config() { - ESP_LOGCONFIG(TAG, "Initialization of %s with %d UARTs completed", this->get_name(), this->children_.size()); - ESP_LOGCONFIG(TAG, " Crystal: %" PRIu32, this->crystal_); - if (test_mode_) - ESP_LOGCONFIG(TAG, " Test mode: %d", test_mode_); - ESP_LOGCONFIG(TAG, " Transfer buffer size: %d", XFER_MAX_SIZE); + ESP_LOGCONFIG(TAG, + "Initialization of %s with %d UARTs completed\n" + " Crystal: %" PRIu32, + this->get_name(), this->children_.size(), this->crystal_); + if (test_mode_) { + ESP_LOGCONFIG(TAG, + " Test mode: %d\n" + " Transfer buffer size: %d", + test_mode_, XFER_MAX_SIZE); + } this->address_ = this->base_address_; // we restore the base_address before display (less confusing) LOG_I2C_DEVICE(this); diff --git a/esphome/components/weikai_spi/weikai_spi.cpp b/esphome/components/weikai_spi/weikai_spi.cpp index a81f69a4aa..a43e0e6599 100644 --- a/esphome/components/weikai_spi/weikai_spi.cpp +++ b/esphome/components/weikai_spi/weikai_spi.cpp @@ -156,7 +156,7 @@ void WeikaiRegisterSPI::write_fifo(uint8_t *data, size_t length) { /////////////////////////////////////////////////////////////////////////////// void WeikaiComponentSPI::setup() { using namespace weikai; - ESP_LOGCONFIG(TAG, "Running setup for '%s' with %d UARTs...", this->get_name(), this->children_.size()); + ESP_LOGCONFIG(TAG, "Running setup for '%s' with %d UARTs", this->get_name(), this->children_.size()); this->spi_setup(); // enable all channels this->reg(WKREG_GENA, 0) = GENA_C1EN | GENA_C2EN | GENA_C3EN | GENA_C4EN; @@ -173,11 +173,16 @@ void WeikaiComponentSPI::setup() { } void WeikaiComponentSPI::dump_config() { - ESP_LOGCONFIG(TAG, "Initialization of %s with %d UARTs completed", this->get_name(), this->children_.size()); - ESP_LOGCONFIG(TAG, " Crystal: %" PRIu32 "", this->crystal_); - if (test_mode_) - ESP_LOGCONFIG(TAG, " Test mode: %d", test_mode_); - ESP_LOGCONFIG(TAG, " Transfer buffer size: %d", XFER_MAX_SIZE); + ESP_LOGCONFIG(TAG, + "Initialization of %s with %d UARTs completed\n" + " Crystal: %" PRIu32, + this->get_name(), this->children_.size(), this->crystal_); + if (test_mode_) { + ESP_LOGCONFIG(TAG, + " Test mode: %d\n" + " Transfer buffer size: %d", + test_mode_, XFER_MAX_SIZE); + } LOG_PIN(" CS Pin: ", this->cs_); for (auto *child : this->children_) { diff --git a/esphome/components/wiegand/wiegand.cpp b/esphome/components/wiegand/wiegand.cpp index 10c77a8aa2..5a2bb8deee 100644 --- a/esphome/components/wiegand/wiegand.cpp +++ b/esphome/components/wiegand/wiegand.cpp @@ -1,6 +1,6 @@ #include "wiegand.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace wiegand { diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index baa273d39f..51ae1c9f8e 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -58,8 +58,10 @@ void WiFiComponent::setup() { } void WiFiComponent::start() { - ESP_LOGCONFIG(TAG, "Starting"); - ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty().c_str()); + ESP_LOGCONFIG(TAG, + "Starting\n" + " Local MAC: %s", + get_mac_address_pretty().c_str()); this->last_connected_ = millis(); uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time()) : 88491487UL; @@ -71,7 +73,7 @@ void WiFiComponent::start() { SavedWifiSettings save{}; if (this->pref_.load(&save)) { - ESP_LOGD(TAG, "Loaded saved settings: %s", save.ssid); + ESP_LOGD(TAG, "Loaded settings: %s", save.ssid); WiFiAP sta{}; sta.set_ssid(save.ssid); @@ -82,11 +84,11 @@ void WiFiComponent::start() { if (this->has_sta()) { this->wifi_sta_pre_setup_(); if (this->output_power_.has_value() && !this->wifi_apply_output_power_(*this->output_power_)) { - ESP_LOGV(TAG, "Setting Output Power Option failed!"); + ESP_LOGV(TAG, "Setting Output Power Option failed"); } if (!this->wifi_apply_power_save_()) { - ESP_LOGV(TAG, "Setting Power Save Option failed!"); + ESP_LOGV(TAG, "Setting Power Save Option failed"); } if (this->fast_connect_) { @@ -100,7 +102,7 @@ void WiFiComponent::start() { } else if (this->has_ap()) { this->setup_ap_config_(); if (this->output_power_.has_value() && !this->wifi_apply_output_power_(*this->output_power_)) { - ESP_LOGV(TAG, "Setting Output Power Option failed!"); + ESP_LOGV(TAG, "Setting Output Power Option failed"); } #ifdef USE_CAPTIVE_PORTAL if (captive_portal::global_captive_portal != nullptr) { @@ -179,7 +181,7 @@ void WiFiComponent::loop() { #ifdef USE_WIFI_AP if (this->has_ap() && !this->ap_setup_) { if (this->ap_timeout_ != 0 && (now - this->last_connected_ > this->ap_timeout_)) { - ESP_LOGI(TAG, "Starting fallback AP!"); + ESP_LOGI(TAG, "Starting fallback AP"); this->setup_ap_config_(); #ifdef USE_CAPTIVE_PORTAL if (captive_portal::global_captive_portal != nullptr) @@ -260,15 +262,19 @@ void WiFiComponent::setup_ap_config_() { this->ap_.set_ssid(name); } - ESP_LOGCONFIG(TAG, "Setting up AP..."); + ESP_LOGCONFIG(TAG, "Setting up AP"); - ESP_LOGCONFIG(TAG, " AP SSID: '%s'", this->ap_.get_ssid().c_str()); - ESP_LOGCONFIG(TAG, " AP Password: '%s'", this->ap_.get_password().c_str()); + ESP_LOGCONFIG(TAG, + " AP SSID: '%s'\n" + " AP Password: '%s'", + this->ap_.get_ssid().c_str(), this->ap_.get_password().c_str()); if (this->ap_.get_manual_ip().has_value()) { auto manual = *this->ap_.get_manual_ip(); - ESP_LOGCONFIG(TAG, " AP Static IP: '%s'", manual.static_ip.str().c_str()); - ESP_LOGCONFIG(TAG, " AP Gateway: '%s'", manual.gateway.str().c_str()); - ESP_LOGCONFIG(TAG, " AP Subnet: '%s'", manual.subnet.str().c_str()); + ESP_LOGCONFIG(TAG, + " AP Static IP: '%s'\n" + " AP Gateway: '%s'\n" + " AP Subnet: '%s'", + manual.static_ip.str().c_str(), manual.gateway.str().c_str(), manual.subnet.str().c_str()); } this->ap_setup_ = this->wifi_start_ap_(this->ap_); @@ -353,7 +359,7 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { if (ap.get_channel().has_value()) { ESP_LOGV(TAG, " Channel: %u", *ap.get_channel()); } else { - ESP_LOGV(TAG, " Channel: Not Set"); + ESP_LOGV(TAG, " Channel not set"); } if (ap.get_manual_ip().has_value()) { ManualIP m = *ap.get_manual_ip(); @@ -366,7 +372,7 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { #endif if (!this->wifi_sta_connect_(ap)) { - ESP_LOGE(TAG, "wifi_sta_connect_ failed!"); + ESP_LOGE(TAG, "wifi_sta_connect_ failed"); this->retry_connect(); return; } @@ -436,22 +442,29 @@ void WiFiComponent::print_connect_params_() { ESP_LOGCONFIG(TAG, " IP Address: %s", ip.str().c_str()); } } - ESP_LOGCONFIG(TAG, " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X"), bssid[0], bssid[1], bssid[2], bssid[3], - bssid[4], bssid[5]); - ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); int8_t rssi = wifi_rssi(); - ESP_LOGCONFIG(TAG, " Signal strength: %d dB %s", rssi, LOG_STR_ARG(get_signal_bars(rssi))); + ESP_LOGCONFIG(TAG, + " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X") "\n" + " Hostname: '%s'\n" + " Signal strength: %d dB %s", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], App.get_name().c_str(), rssi, + LOG_STR_ARG(get_signal_bars(rssi))); if (this->selected_ap_.get_bssid().has_value()) { ESP_LOGV(TAG, " Priority: %.1f", this->get_sta_priority(*this->selected_ap_.get_bssid())); } - ESP_LOGCONFIG(TAG, " Channel: %" PRId32, get_wifi_channel()); - ESP_LOGCONFIG(TAG, " Subnet: %s", wifi_subnet_mask_().str().c_str()); - ESP_LOGCONFIG(TAG, " Gateway: %s", wifi_gateway_ip_().str().c_str()); - ESP_LOGCONFIG(TAG, " DNS1: %s", wifi_dns_ip_(0).str().c_str()); - ESP_LOGCONFIG(TAG, " DNS2: %s", wifi_dns_ip_(1).str().c_str()); + ESP_LOGCONFIG(TAG, + " Channel: %" PRId32 "\n" + " Subnet: %s\n" + " Gateway: %s\n" + " DNS1: %s\n" + " DNS2: %s", + get_wifi_channel(), wifi_subnet_mask_().str().c_str(), wifi_gateway_ip_().str().c_str(), + wifi_dns_ip_(0).str().c_str(), wifi_dns_ip_(1).str().c_str()); #ifdef USE_WIFI_11KV_SUPPORT - ESP_LOGCONFIG(TAG, " BTM: %s", this->btm_ ? "enabled" : "disabled"); - ESP_LOGCONFIG(TAG, " RRM: %s", this->rrm_ ? "enabled" : "disabled"); + ESP_LOGCONFIG(TAG, + " BTM: %s\n" + " RRM: %s", + this->btm_ ? "enabled" : "disabled", this->rrm_ ? "enabled" : "disabled"); #endif } @@ -479,7 +492,7 @@ bool WiFiComponent::is_disabled() { return this->state_ == WIFI_COMPONENT_STATE_ void WiFiComponent::start_scanning() { this->action_started_ = millis(); - ESP_LOGD(TAG, "Starting scan..."); + ESP_LOGD(TAG, "Starting scan"); this->wifi_scan_start_(this->passive_scan_); this->state_ = WIFI_COMPONENT_STATE_STA_SCANNING; } @@ -487,20 +500,20 @@ void WiFiComponent::start_scanning() { void WiFiComponent::check_scanning_finished() { if (!this->scan_done_) { if (millis() - this->action_started_ > 30000) { - ESP_LOGE(TAG, "Scan timeout!"); + ESP_LOGE(TAG, "Scan timeout"); this->retry_connect(); } return; } this->scan_done_ = false; - ESP_LOGD(TAG, "Found networks:"); if (this->scan_result_.empty()) { - ESP_LOGD(TAG, " No network found!"); + ESP_LOGW(TAG, "No networks found"); this->retry_connect(); return; } + ESP_LOGD(TAG, "Found networks:"); for (auto &res : this->scan_result_) { for (auto &ap : this->sta_) { if (res.matches(ap)) { @@ -548,7 +561,7 @@ void WiFiComponent::check_scanning_finished() { } if (!this->scan_result_[0].get_matches()) { - ESP_LOGW(TAG, "No matching network found!"); + ESP_LOGW(TAG, "No matching network found"); this->retry_connect(); return; } @@ -606,7 +619,7 @@ void WiFiComponent::check_connecting_finished() { if (status == WiFiSTAConnectStatus::CONNECTED) { if (wifi_ssid().empty()) { - ESP_LOGW(TAG, "Incomplete connection."); + ESP_LOGW(TAG, "Connection incomplete"); this->retry_connect(); return; } @@ -623,7 +636,7 @@ void WiFiComponent::check_connecting_finished() { captive_portal::global_captive_portal->end(); } #endif - ESP_LOGD(TAG, "Disabling AP..."); + ESP_LOGD(TAG, "Disabling AP"); this->wifi_mode_({}, false); } #ifdef USE_IMPROV @@ -650,7 +663,7 @@ void WiFiComponent::check_connecting_finished() { } if (this->error_from_callback_) { - ESP_LOGW(TAG, "Error while connecting to network."); + ESP_LOGW(TAG, "Connecting to network failed"); this->retry_connect(); return; } @@ -666,7 +679,7 @@ void WiFiComponent::check_connecting_finished() { } if (status == WiFiSTAConnectStatus::ERROR_CONNECT_FAILED) { - ESP_LOGW(TAG, "Connection failed. Check credentials"); + ESP_LOGW(TAG, "Connecting to network failed"); this->retry_connect(); return; } @@ -687,14 +700,14 @@ void WiFiComponent::retry_connect() { (this->num_retried_ > 3 || this->error_from_callback_)) { if (this->num_retried_ > 5) { // If retry failed for more than 5 times, let's restart STA - ESP_LOGW(TAG, "Restarting WiFi adapter..."); + ESP_LOGW(TAG, "Restarting adapter"); this->wifi_mode_(false, {}); delay(100); // NOLINT this->num_retried_ = 0; this->retry_hidden_ = false; } else { // Try hidden networks after 3 failed retries - ESP_LOGD(TAG, "Retrying with hidden networks..."); + ESP_LOGD(TAG, "Retrying with hidden networks"); this->retry_hidden_ = true; this->num_retried_++; } @@ -757,7 +770,7 @@ void WiFiComponent::load_fast_connect_settings_() { this->selected_ap_.set_bssid(bssid); this->selected_ap_.set_channel(fast_connect_save.channel); - ESP_LOGD(TAG, "Loaded saved fast_connect wifi settings"); + ESP_LOGD(TAG, "Loaded fast_connect settings"); } } @@ -773,7 +786,7 @@ void WiFiComponent::save_fast_connect_settings_() { this->fast_connect_pref_.save(&fast_connect_save); - ESP_LOGD(TAG, "Saved fast_connect wifi settings"); + ESP_LOGD(TAG, "Saved fast_connect settings"); } } diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index 23fc03f471..3fc2c009db 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -9,7 +9,7 @@ #include #include #ifdef USE_WIFI_WPA2_EAP -#include +#include #endif #ifdef USE_WIFI_AP @@ -78,14 +78,14 @@ bool WiFiComponent::wifi_mode_(optional sta, optional ap) { return true; if (set_sta && !current_sta) { - ESP_LOGV(TAG, "Enabling STA."); + ESP_LOGV(TAG, "Enabling STA"); } else if (!set_sta && current_sta) { - ESP_LOGV(TAG, "Disabling STA."); + ESP_LOGV(TAG, "Disabling STA"); } if (set_ap && !current_ap) { - ESP_LOGV(TAG, "Enabling AP."); + ESP_LOGV(TAG, "Enabling AP"); } else if (!set_ap && current_ap) { - ESP_LOGV(TAG, "Disabling AP."); + ESP_LOGV(TAG, "Disabling AP"); } bool ret = WiFiClass::mode(set_mode); @@ -147,11 +147,11 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); if (ap.get_ssid().size() > sizeof(conf.sta.ssid)) { - ESP_LOGE(TAG, "SSID is too long"); + ESP_LOGE(TAG, "SSID too long"); return false; } if (ap.get_password().size() > sizeof(conf.sta.password)) { - ESP_LOGE(TAG, "password is too long"); + ESP_LOGE(TAG, "Password too long"); return false; } memcpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); @@ -228,43 +228,43 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { if (ap.get_eap().has_value()) { // note: all certificates and keys have to be null terminated. Lengths are appended by +1 to include \0. EAPAuth eap = ap.get_eap().value(); - err = esp_wifi_sta_wpa2_ent_set_identity((uint8_t *) eap.identity.c_str(), eap.identity.length()); + err = esp_eap_client_set_identity((uint8_t *) eap.identity.c_str(), eap.identity.length()); if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_identity failed! %d", err); + ESP_LOGV(TAG, "esp_eap_client_set_identity failed: %d", err); } int ca_cert_len = strlen(eap.ca_cert); int client_cert_len = strlen(eap.client_cert); int client_key_len = strlen(eap.client_key); if (ca_cert_len) { - err = esp_wifi_sta_wpa2_ent_set_ca_cert((uint8_t *) eap.ca_cert, ca_cert_len + 1); + err = esp_eap_client_set_ca_cert((uint8_t *) eap.ca_cert, ca_cert_len + 1); if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ca_cert failed! %d", err); + ESP_LOGV(TAG, "esp_eap_client_set_ca_cert failed: %d", err); } } // workout what type of EAP this is // validation is not required as the config tool has already validated it if (client_cert_len && client_key_len) { // if we have certs, this must be EAP-TLS - err = esp_wifi_sta_wpa2_ent_set_cert_key((uint8_t *) eap.client_cert, client_cert_len + 1, - (uint8_t *) eap.client_key, client_key_len + 1, - (uint8_t *) eap.password.c_str(), strlen(eap.password.c_str())); + err = esp_eap_client_set_certificate_and_key((uint8_t *) eap.client_cert, client_cert_len + 1, + (uint8_t *) eap.client_key, client_key_len + 1, + (uint8_t *) eap.password.c_str(), strlen(eap.password.c_str())); if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_cert_key failed! %d", err); + ESP_LOGV(TAG, "esp_eap_client_set_certificate_and_key failed: %d", err); } } else { // in the absence of certs, assume this is username/password based - err = esp_wifi_sta_wpa2_ent_set_username((uint8_t *) eap.username.c_str(), eap.username.length()); + err = esp_eap_client_set_username((uint8_t *) eap.username.c_str(), eap.username.length()); if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_username failed! %d", err); + ESP_LOGV(TAG, "esp_eap_client_set_username failed: %d", err); } - err = esp_wifi_sta_wpa2_ent_set_password((uint8_t *) eap.password.c_str(), eap.password.length()); + err = esp_eap_client_set_password((uint8_t *) eap.password.c_str(), eap.password.length()); if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed! %d", err); + ESP_LOGV(TAG, "esp_eap_client_set_password failed: %d", err); } } - err = esp_wifi_sta_wpa2_ent_enable(); + err = esp_wifi_sta_enterprise_enable(); if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_enable failed! %d", err); + ESP_LOGV(TAG, "esp_wifi_sta_enterprise_enable failed: %d", err); } } #endif // USE_WIFI_WPA2_EAP @@ -319,7 +319,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { if (dhcp_status != ESP_NETIF_DHCP_STARTED) { err = esp_netif_dhcpc_start(s_sta_netif); if (err != ESP_OK) { - ESP_LOGV(TAG, "Starting DHCP client failed! %d", err); + ESP_LOGV(TAG, "Starting DHCP client failed: %d", err); } return err == ESP_OK; } @@ -332,12 +332,12 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { info.netmask = manual_ip->subnet; err = esp_netif_dhcpc_stop(s_sta_netif); if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) { - ESP_LOGV(TAG, "Stopping DHCP client failed! %s", esp_err_to_name(err)); + ESP_LOGV(TAG, "Stopping DHCP client failed: %s", esp_err_to_name(err)); } err = esp_netif_set_ip_info(s_sta_netif, &info); if (err != ESP_OK) { - ESP_LOGV(TAG, "Setting manual IP info failed! %s", esp_err_to_name(err)); + ESP_LOGV(TAG, "Setting manual IP info failed: %s", esp_err_to_name(err)); } esp_netif_dns_info_t dns; @@ -520,18 +520,18 @@ using esphome_wifi_event_info_t = arduino_event_info_t; void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_wifi_event_info_t info) { switch (event) { case ESPHOME_EVENT_ID_WIFI_READY: { - ESP_LOGV(TAG, "Event: WiFi ready"); + ESP_LOGV(TAG, "Ready"); break; } case ESPHOME_EVENT_ID_WIFI_SCAN_DONE: { auto it = info.wifi_scan_done; - ESP_LOGV(TAG, "Event: WiFi Scan Done status=%u number=%u scan_id=%u", it.status, it.number, it.scan_id); + ESP_LOGV(TAG, "Scan done: status=%u number=%u scan_id=%u", it.status, it.number, it.scan_id); this->wifi_scan_done_callback_(); break; } case ESPHOME_EVENT_ID_WIFI_STA_START: { - ESP_LOGV(TAG, "Event: WiFi STA start"); + ESP_LOGV(TAG, "STA start"); // apply hostname s_sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); esp_err_t err = esp_netif_set_hostname(s_sta_netif, App.get_name().c_str()); @@ -541,7 +541,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ break; } case ESPHOME_EVENT_ID_WIFI_STA_STOP: { - ESP_LOGV(TAG, "Event: WiFi STA stop"); + ESP_LOGV(TAG, "STA stop"); break; } case ESPHOME_EVENT_ID_WIFI_STA_CONNECTED: { @@ -549,10 +549,10 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ char buf[33]; memcpy(buf, it.ssid, it.ssid_len); buf[it.ssid_len] = '\0'; - ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, + ESP_LOGV(TAG, "Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); #if USE_NETWORK_IPV6 - this->set_timeout(100, [] { WiFi.enableIpV6(); }); + this->set_timeout(100, [] { WiFi.enableIPv6(); }); #endif /* USE_NETWORK_IPV6 */ break; @@ -563,9 +563,9 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ memcpy(buf, it.ssid, it.ssid_len); buf[it.ssid_len] = '\0'; if (it.reason == WIFI_REASON_NO_AP_FOUND) { - ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); + ESP_LOGW(TAG, "Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); } else { - ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, + ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); } @@ -585,12 +585,11 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ } case ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE: { auto it = info.wifi_sta_authmode_change; - ESP_LOGV(TAG, "Event: Authmode Change old=%s new=%s", get_auth_mode_str(it.old_mode), - get_auth_mode_str(it.new_mode)); + ESP_LOGV(TAG, "Authmode Change old=%s new=%s", get_auth_mode_str(it.old_mode), get_auth_mode_str(it.new_mode)); // Mitigate CVE-2020-12638 // https://lbsfilm.at/blog/wpa2-authenticationmode-downgrade-in-espressif-microprocessors if (it.old_mode != WIFI_AUTH_OPEN && it.new_mode == WIFI_AUTH_OPEN) { - ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting..."); + ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting"); // we can't call retry_connect() from this context, so disconnect immediately // and notify main thread with error_from_callback_ err_t err = esp_wifi_disconnect(); @@ -603,8 +602,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ } case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP: { auto it = info.got_ip.ip_info; - ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip).c_str(), - format_ip4_addr(it.gw).c_str()); + ESP_LOGV(TAG, "static_ip=%s gateway=%s", format_ip4_addr(it.ip).c_str(), format_ip4_addr(it.gw).c_str()); this->got_ipv4_address_ = true; #if USE_NETWORK_IPV6 s_sta_connecting = this->num_ipv6_addresses_ < USE_NETWORK_MIN_IPV6_ADDR_COUNT; @@ -616,44 +614,44 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ #if USE_NETWORK_IPV6 case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6: { auto it = info.got_ip6.ip6_info; - ESP_LOGV(TAG, "Got IPv6 address=" IPV6STR, IPV62STR(it.ip)); + ESP_LOGV(TAG, "IPv6 address=" IPV6STR, IPV62STR(it.ip)); this->num_ipv6_addresses_++; s_sta_connecting = !(this->got_ipv4_address_ & (this->num_ipv6_addresses_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT)); break; } #endif /* USE_NETWORK_IPV6 */ case ESPHOME_EVENT_ID_WIFI_STA_LOST_IP: { - ESP_LOGV(TAG, "Event: Lost IP"); + ESP_LOGV(TAG, "Lost IP"); this->got_ipv4_address_ = false; break; } case ESPHOME_EVENT_ID_WIFI_AP_START: { - ESP_LOGV(TAG, "Event: WiFi AP start"); + ESP_LOGV(TAG, "AP start"); break; } case ESPHOME_EVENT_ID_WIFI_AP_STOP: { - ESP_LOGV(TAG, "Event: WiFi AP stop"); + ESP_LOGV(TAG, "AP stop"); break; } case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: { auto it = info.wifi_sta_connected; auto &mac = it.bssid; - ESP_LOGV(TAG, "Event: AP client connected MAC=%s", format_mac_addr(mac).c_str()); + ESP_LOGV(TAG, "AP client connected MAC=%s", format_mac_addr(mac).c_str()); break; } case ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED: { auto it = info.wifi_sta_disconnected; auto &mac = it.bssid; - ESP_LOGV(TAG, "Event: AP client disconnected MAC=%s", format_mac_addr(mac).c_str()); + ESP_LOGV(TAG, "AP client disconnected MAC=%s", format_mac_addr(mac).c_str()); break; } case ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED: { - ESP_LOGV(TAG, "Event: AP client assigned IP"); + ESP_LOGV(TAG, "AP client assigned IP"); break; } case ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED: { auto it = info.wifi_ap_probereqrecved; - ESP_LOGVV(TAG, "Event: AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); + ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); break; } default: @@ -662,12 +660,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ } WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() { -#if USE_ARDUINO_VERSION_CODE < VERSION_CODE(3, 1, 0) - const auto status = WiFiClass::status(); -#else const auto status = WiFi.status(); -#endif - if (status == WL_CONNECT_FAILED || status == WL_CONNECTION_LOST) { return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED; } @@ -690,7 +683,7 @@ bool WiFiComponent::wifi_scan_start_(bool passive) { // need to use WiFi because of WiFiScanClass allocations :( int16_t err = WiFi.scanNetworks(true, true, passive, 200); if (err != WIFI_SCAN_RUNNING) { - ESP_LOGV(TAG, "WiFi.scanNetworks failed! %d", err); + ESP_LOGV(TAG, "WiFi.scanNetworks failed: %d", err); return false; } @@ -746,7 +739,7 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { err = esp_netif_set_ip_info(s_ap_netif, &info); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_netif_set_ip_info failed! %d", err); + ESP_LOGE(TAG, "esp_netif_set_ip_info failed: %d", err); return false; } @@ -762,14 +755,14 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { err = esp_netif_dhcps_option(s_ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_REQUESTED_IP_ADDRESS, &lease, sizeof(lease)); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_netif_dhcps_option failed! %d", err); + ESP_LOGE(TAG, "esp_netif_dhcps_option failed: %d", err); return false; } err = esp_netif_dhcps_start(s_ap_netif); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_netif_dhcps_start failed! %d", err); + ESP_LOGE(TAG, "esp_netif_dhcps_start failed: %d", err); return false; } @@ -784,7 +777,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); if (ap.get_ssid().size() > sizeof(conf.ap.ssid)) { - ESP_LOGE(TAG, "AP SSID is too long"); + ESP_LOGE(TAG, "AP SSID too long"); return false; } memcpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); @@ -799,7 +792,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { } else { conf.ap.authmode = WIFI_AUTH_WPA2_PSK; if (ap.get_password().size() > sizeof(conf.ap.password)) { - ESP_LOGE(TAG, "AP password is too long"); + ESP_LOGE(TAG, "AP password too long"); return false; } memcpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), ap.get_password().size()); @@ -810,14 +803,14 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { esp_err_t err = esp_wifi_set_config(WIFI_IF_AP, &conf); if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_set_config failed! %d", err); + ESP_LOGV(TAG, "esp_wifi_set_config failed: %d", err); return false; } yield(); if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { - ESP_LOGV(TAG, "wifi_ap_ip_config_ failed!"); + ESP_LOGV(TAG, "wifi_ap_ip_config_ failed"); return false; } diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 8e1c2e70d8..594bc79e54 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -32,11 +32,11 @@ extern "C" { #endif } +#include "esphome/core/application.h" +#include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/hal.h" #include "esphome/core/util.h" -#include "esphome/core/application.h" namespace esphome { namespace wifi { @@ -59,17 +59,17 @@ bool WiFiComponent::wifi_mode_(optional sta, optional ap) { return true; if (target_sta && !current_sta) { - ESP_LOGV(TAG, "Enabling STA."); + ESP_LOGV(TAG, "Enabling STA"); } else if (!target_sta && current_sta) { - ESP_LOGV(TAG, "Disabling STA."); + ESP_LOGV(TAG, "Disabling STA"); // Stop DHCP client when disabling STA // See https://github.com/esp8266/Arduino/pull/5703 wifi_station_dhcpc_stop(); } if (target_ap && !current_ap) { - ESP_LOGV(TAG, "Enabling AP."); + ESP_LOGV(TAG, "Enabling AP"); } else if (!target_ap && current_ap) { - ESP_LOGV(TAG, "Disabling AP."); + ESP_LOGV(TAG, "Disabling AP"); } ETS_UART_INTR_DISABLE(); @@ -82,7 +82,7 @@ bool WiFiComponent::wifi_mode_(optional sta, optional ap) { ETS_UART_INTR_ENABLE(); if (!ret) { - ESP_LOGW(TAG, "Setting WiFi mode failed!"); + ESP_LOGW(TAG, "Set mode failed"); } return ret; @@ -133,7 +133,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { if (dhcp_status != DHCP_STARTED) { bool ret = wifi_station_dhcpc_start(); if (!ret) { - ESP_LOGV(TAG, "Starting DHCP client failed!"); + ESP_LOGV(TAG, "Starting DHCP client failed"); } return ret; } @@ -157,13 +157,13 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { if (dhcp_status == DHCP_STARTED) { bool dhcp_stop_ret = wifi_station_dhcpc_stop(); if (!dhcp_stop_ret) { - ESP_LOGV(TAG, "Stopping DHCP client failed!"); + ESP_LOGV(TAG, "Stopping DHCP client failed"); ret = false; } } bool wifi_set_info_ret = wifi_set_ip_info(STATION_IF, &info); if (!wifi_set_info_ret) { - ESP_LOGV(TAG, "Setting manual IP info failed!"); + ESP_LOGV(TAG, "Set manual IP info failed"); ret = false; } @@ -202,7 +202,7 @@ bool WiFiComponent::wifi_apply_hostname_() { const std::string &hostname = App.get_name(); bool ret = wifi_station_set_hostname(const_cast(hostname.c_str())); if (!ret) { - ESP_LOGV(TAG, "Setting WiFi Hostname failed!"); + ESP_LOGV(TAG, "Set hostname failed"); } // inform dhcp server of hostname change using dhcp_renew() @@ -237,11 +237,11 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { struct station_config conf {}; memset(&conf, 0, sizeof(conf)); if (ap.get_ssid().size() > sizeof(conf.ssid)) { - ESP_LOGE(TAG, "SSID is too long"); + ESP_LOGE(TAG, "SSID too long"); return false; } if (ap.get_password().size() > sizeof(conf.password)) { - ESP_LOGE(TAG, "password is too long"); + ESP_LOGE(TAG, "Password too long"); return false; } memcpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); @@ -269,7 +269,7 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { ETS_UART_INTR_ENABLE(); if (!ret) { - ESP_LOGV(TAG, "Setting WiFi Station config failed!"); + ESP_LOGV(TAG, "Set Station config failed"); return false; } @@ -284,7 +284,7 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { EAPAuth eap = ap.get_eap().value(); ret = wifi_station_set_enterprise_identity((uint8_t *) eap.identity.c_str(), eap.identity.length()); if (ret) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_identity failed! %d", ret); + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_identity failed: %d", ret); } int ca_cert_len = strlen(eap.ca_cert); int client_cert_len = strlen(eap.client_cert); @@ -292,7 +292,7 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { if (ca_cert_len) { ret = wifi_station_set_enterprise_ca_cert((uint8_t *) eap.ca_cert, ca_cert_len + 1); if (ret) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ca_cert failed! %d", ret); + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ca_cert failed: %d", ret); } } // workout what type of EAP this is @@ -303,22 +303,22 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { (uint8_t *) eap.client_key, client_key_len + 1, (uint8_t *) eap.password.c_str(), strlen(eap.password.c_str())); if (ret) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_cert_key failed! %d", ret); + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_cert_key failed: %d", ret); } } else { // in the absence of certs, assume this is username/password based ret = wifi_station_set_enterprise_username((uint8_t *) eap.username.c_str(), eap.username.length()); if (ret) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_username failed! %d", ret); + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_username failed: %d", ret); } ret = wifi_station_set_enterprise_password((uint8_t *) eap.password.c_str(), eap.password.length()); if (ret) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed! %d", ret); + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed: %d", ret); } } ret = wifi_station_set_wpa2_enterprise_auth(true); if (ret) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_enable failed! %d", ret); + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_enable failed: %d", ret); } } #endif // USE_WIFI_WPA2_EAP @@ -337,7 +337,7 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { ret = wifi_station_connect(); ETS_UART_INTR_ENABLE(); if (!ret) { - ESP_LOGV(TAG, "wifi_station_connect failed!"); + ESP_LOGV(TAG, "wifi_station_connect failed"); return false; } @@ -359,7 +359,7 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { if (ap.get_channel().has_value()) { ret = wifi_set_channel(*ap.get_channel()); if (!ret) { - ESP_LOGV(TAG, "wifi_set_channel failed!"); + ESP_LOGV(TAG, "wifi_set_channel failed"); return false; } } @@ -496,8 +496,7 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { char buf[33]; memcpy(buf, it.ssid, it.ssid_len); buf[it.ssid_len] = '\0'; - ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=%s channel=%u", buf, format_mac_addr(it.bssid).c_str(), - it.channel); + ESP_LOGV(TAG, "Connected ssid='%s' bssid=%s channel=%u", buf, format_mac_addr(it.bssid).c_str(), it.channel); s_sta_connected = true; break; } @@ -507,10 +506,10 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { memcpy(buf, it.ssid, it.ssid_len); buf[it.ssid_len] = '\0'; if (it.reason == REASON_NO_AP_FOUND) { - ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); + ESP_LOGW(TAG, "Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); s_sta_connect_not_found = true; } else { - ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, + ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, format_mac_addr(it.bssid).c_str(), LOG_STR_ARG(get_disconnect_reason_str(it.reason))); s_sta_connect_error = true; } @@ -520,12 +519,12 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { } case EVENT_STAMODE_AUTHMODE_CHANGE: { auto it = event->event_info.auth_change; - ESP_LOGV(TAG, "Event: Changed AuthMode old=%s new=%s", LOG_STR_ARG(get_auth_mode_str(it.old_mode)), + ESP_LOGV(TAG, "Changed Authmode old=%s new=%s", LOG_STR_ARG(get_auth_mode_str(it.old_mode)), LOG_STR_ARG(get_auth_mode_str(it.new_mode))); // Mitigate CVE-2020-12638 // https://lbsfilm.at/blog/wpa2-authenticationmode-downgrade-in-espressif-microprocessors if (it.old_mode != AUTH_OPEN && it.new_mode == AUTH_OPEN) { - ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting..."); + ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting"); // we can't call retry_connect() from this context, so disconnect immediately // and notify main thread with error_from_callback_ wifi_station_disconnect(); @@ -535,40 +534,40 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { } case EVENT_STAMODE_GOT_IP: { auto it = event->event_info.got_ip; - ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s netmask=%s", format_ip_addr(it.ip).c_str(), - format_ip_addr(it.gw).c_str(), format_ip_addr(it.mask).c_str()); + ESP_LOGV(TAG, "static_ip=%s gateway=%s netmask=%s", format_ip_addr(it.ip).c_str(), format_ip_addr(it.gw).c_str(), + format_ip_addr(it.mask).c_str()); s_sta_got_ip = true; break; } case EVENT_STAMODE_DHCP_TIMEOUT: { - ESP_LOGW(TAG, "Event: Getting IP address timeout"); + ESP_LOGW(TAG, "DHCP request timeout"); break; } case EVENT_SOFTAPMODE_STACONNECTED: { auto it = event->event_info.sta_connected; - ESP_LOGV(TAG, "Event: AP client connected MAC=%s aid=%u", format_mac_addr(it.mac).c_str(), it.aid); + ESP_LOGV(TAG, "AP client connected MAC=%s aid=%u", format_mac_addr(it.mac).c_str(), it.aid); break; } case EVENT_SOFTAPMODE_STADISCONNECTED: { auto it = event->event_info.sta_disconnected; - ESP_LOGV(TAG, "Event: AP client disconnected MAC=%s aid=%u", format_mac_addr(it.mac).c_str(), it.aid); + ESP_LOGV(TAG, "AP client disconnected MAC=%s aid=%u", format_mac_addr(it.mac).c_str(), it.aid); break; } case EVENT_SOFTAPMODE_PROBEREQRECVED: { auto it = event->event_info.ap_probereqrecved; - ESP_LOGVV(TAG, "Event: AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); + ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); break; } #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 4, 0) case EVENT_OPMODE_CHANGED: { auto it = event->event_info.opmode_changed; - ESP_LOGV(TAG, "Event: Changed Mode old=%s new=%s", LOG_STR_ARG(get_op_mode_str(it.old_opmode)), + ESP_LOGV(TAG, "Changed Mode old=%s new=%s", LOG_STR_ARG(get_op_mode_str(it.old_opmode)), LOG_STR_ARG(get_op_mode_str(it.new_opmode))); break; } case EVENT_SOFTAPMODE_DISTRIBUTE_STA_IP: { auto it = event->event_info.distribute_sta_ip; - ESP_LOGV(TAG, "Event: AP Distribute Station IP MAC=%s IP=%s aid=%u", format_mac_addr(it.mac).c_str(), + ESP_LOGV(TAG, "AP Distribute Station IP MAC=%s IP=%s aid=%u", format_mac_addr(it.mac).c_str(), format_ip_addr(it.ip).c_str(), it.aid); break; } @@ -600,7 +599,7 @@ bool WiFiComponent::wifi_sta_pre_setup_() { ETS_UART_INTR_ENABLE(); if (!ret1 || !ret2) { - ESP_LOGV(TAG, "Disabling Auto-Connect failed!"); + ESP_LOGV(TAG, "Disabling Auto-Connect failed"); } delay(10); @@ -666,7 +665,7 @@ bool WiFiComponent::wifi_scan_start_(bool passive) { first_scan = false; bool ret = wifi_station_scan(&config, &WiFiComponent::s_wifi_scan_done_callback); if (!ret) { - ESP_LOGV(TAG, "wifi_station_scan failed!"); + ESP_LOGV(TAG, "wifi_station_scan failed"); return false; } @@ -692,7 +691,7 @@ void WiFiComponent::wifi_scan_done_callback_(void *arg, STATUS status) { this->scan_result_.clear(); if (status != OK) { - ESP_LOGV(TAG, "Scan failed! %d", status); + ESP_LOGV(TAG, "Scan failed: %d", status); this->retry_connect(); return; } @@ -725,12 +724,12 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { if (wifi_softap_dhcps_status() == DHCP_STARTED) { if (!wifi_softap_dhcps_stop()) { - ESP_LOGW(TAG, "Stopping DHCP server failed!"); + ESP_LOGW(TAG, "Stopping DHCP server failed"); } } if (!wifi_set_ip_info(SOFTAP_IF, &info)) { - ESP_LOGE(TAG, "Setting SoftAP info failed!"); + ESP_LOGE(TAG, "Set SoftAP info failed"); return false; } @@ -748,13 +747,13 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { lease.end_ip = start_address; ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str()); if (!wifi_softap_set_dhcps_lease(&lease)) { - ESP_LOGE(TAG, "Setting SoftAP DHCP lease failed!"); + ESP_LOGE(TAG, "Set SoftAP DHCP lease failed"); return false; } // lease time 1440 minutes (=24 hours) if (!wifi_softap_set_dhcps_lease_time(1440)) { - ESP_LOGE(TAG, "Setting SoftAP DHCP lease time failed!"); + ESP_LOGE(TAG, "Set SoftAP DHCP lease time failed"); return false; } @@ -764,13 +763,13 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { uint8_t mode = 1; // bit0, 1 enables router information from ESP8266 SoftAP DHCP server. if (!wifi_softap_set_dhcps_offer_option(OFFER_ROUTER, &mode)) { - ESP_LOGE(TAG, "wifi_softap_set_dhcps_offer_option failed!"); + ESP_LOGE(TAG, "wifi_softap_set_dhcps_offer_option failed"); return false; } #endif if (!wifi_softap_dhcps_start()) { - ESP_LOGE(TAG, "Starting SoftAP DHCPS failed!"); + ESP_LOGE(TAG, "Starting SoftAP DHCPS failed"); return false; } @@ -784,7 +783,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { struct softap_config conf {}; if (ap.get_ssid().size() > sizeof(conf.ssid)) { - ESP_LOGE(TAG, "AP SSID is too long"); + ESP_LOGE(TAG, "AP SSID too long"); return false; } memcpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); @@ -800,7 +799,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { } else { conf.authmode = AUTH_WPA2_PSK; if (ap.get_password().size() > sizeof(conf.password)) { - ESP_LOGE(TAG, "AP password is too long"); + ESP_LOGE(TAG, "AP password too long"); return false; } memcpy(reinterpret_cast(conf.password), ap.get_password().c_str(), ap.get_password().size()); @@ -811,12 +810,12 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { ETS_UART_INTR_ENABLE(); if (!ret) { - ESP_LOGV(TAG, "wifi_softap_set_config_current failed!"); + ESP_LOGV(TAG, "wifi_softap_set_config_current failed"); return false; } if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { - ESP_LOGV(TAG, "wifi_ap_ip_config_ failed!"); + ESP_LOGV(TAG, "wifi_ap_ip_config_ failed"); return false; } diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 1af271345f..e767e7ffc1 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -219,14 +219,14 @@ bool WiFiComponent::wifi_mode_(optional sta, optional ap) { return true; if (set_sta && !current_sta) { - ESP_LOGV(TAG, "Enabling STA."); + ESP_LOGV(TAG, "Enabling STA"); } else if (!set_sta && current_sta) { - ESP_LOGV(TAG, "Disabling STA."); + ESP_LOGV(TAG, "Disabling STA"); } if (set_ap && !current_ap) { - ESP_LOGV(TAG, "Enabling AP."); + ESP_LOGV(TAG, "Enabling AP"); } else if (!set_ap && current_ap) { - ESP_LOGV(TAG, "Disabling AP."); + ESP_LOGV(TAG, "Disabling AP"); } if (set_mode == WIFI_MODE_NULL && s_wifi_started) { @@ -290,11 +290,11 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); if (ap.get_ssid().size() > sizeof(conf.sta.ssid)) { - ESP_LOGE(TAG, "SSID is too long"); + ESP_LOGE(TAG, "SSID too long"); return false; } if (ap.get_password().size() > sizeof(conf.sta.password)) { - ESP_LOGE(TAG, "password is too long"); + ESP_LOGE(TAG, "Password too long"); return false; } memcpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); @@ -490,7 +490,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { if (dhcp_status != ESP_NETIF_DHCP_STARTED) { err = esp_netif_dhcpc_start(s_sta_netif); if (err != ESP_OK) { - ESP_LOGV(TAG, "Starting DHCP client failed! %d", err); + ESP_LOGV(TAG, "Starting DHCP client failed: %d", err); } return err == ESP_OK; } @@ -503,12 +503,12 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { info.netmask = manual_ip->subnet; err = esp_netif_dhcpc_stop(s_sta_netif); if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) { - ESP_LOGV(TAG, "Stopping DHCP client failed! %s", esp_err_to_name(err)); + ESP_LOGV(TAG, "Stopping DHCP client failed: %s", esp_err_to_name(err)); } err = esp_netif_set_ip_info(s_sta_netif, &info); if (err != ESP_OK) { - ESP_LOGV(TAG, "Setting manual IP info failed! %s", esp_err_to_name(err)); + ESP_LOGV(TAG, "Setting manual IP info failed: %s", esp_err_to_name(err)); } esp_netif_dns_info_t dns; @@ -665,7 +665,7 @@ void WiFiComponent::wifi_loop_() { void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { esp_err_t err; if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_START) { - ESP_LOGV(TAG, "Event: WiFi STA start"); + ESP_LOGV(TAG, "STA start"); // apply hostname err = esp_netif_set_hostname(s_sta_netif, App.get_name().c_str()); if (err != ERR_OK) { @@ -677,13 +677,12 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { wifi_apply_power_save_(); } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_STOP) { - ESP_LOGV(TAG, "Event: WiFi STA stop"); + ESP_LOGV(TAG, "STA stop"); s_sta_started = false; } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_AUTHMODE_CHANGE) { const auto &it = data->data.sta_authmode_change; - ESP_LOGV(TAG, "Event: Authmode Change old=%s new=%s", get_auth_mode_str(it.old_mode), - get_auth_mode_str(it.new_mode)); + ESP_LOGV(TAG, "Authmode Change old=%s new=%s", get_auth_mode_str(it.old_mode), get_auth_mode_str(it.new_mode)); } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_CONNECTED) { const auto &it = data->data.sta_connected; @@ -691,7 +690,7 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { assert(it.ssid_len <= 32); memcpy(buf, it.ssid, it.ssid_len); buf[it.ssid_len] = '\0'; - ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, + ESP_LOGV(TAG, "Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); s_sta_connected = true; @@ -702,13 +701,13 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { memcpy(buf, it.ssid, it.ssid_len); buf[it.ssid_len] = '\0'; if (it.reason == WIFI_REASON_NO_AP_FOUND) { - ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); + ESP_LOGW(TAG, "Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); s_sta_connect_not_found = true; } else if (it.reason == WIFI_REASON_ROAMING) { - ESP_LOGI(TAG, "Event: Disconnected ssid='%s' reason='Station Roaming'", buf); + ESP_LOGI(TAG, "Disconnected ssid='%s' reason='Station Roaming'", buf); return; } else { - ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, + ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); s_sta_connect_error = true; } @@ -721,24 +720,24 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { #if USE_NETWORK_IPV6 esp_netif_create_ip6_linklocal(s_sta_netif); #endif /* USE_NETWORK_IPV6 */ - ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip_info.ip).c_str(), + ESP_LOGV(TAG, "static_ip=%s gateway=%s", format_ip4_addr(it.ip_info.ip).c_str(), format_ip4_addr(it.ip_info.gw).c_str()); this->got_ipv4_address_ = true; #if USE_NETWORK_IPV6 } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_GOT_IP6) { const auto &it = data->data.ip_got_ip6; - ESP_LOGV(TAG, "Event: Got IPv6 address=%s", format_ip6_addr(it.ip6_info.ip).c_str()); + ESP_LOGV(TAG, "IPv6 address=%s", format_ip6_addr(it.ip6_info.ip).c_str()); this->num_ipv6_addresses_++; #endif /* USE_NETWORK_IPV6 */ } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_LOST_IP) { - ESP_LOGV(TAG, "Event: Lost IP"); + ESP_LOGV(TAG, "Lost IP"); this->got_ipv4_address_ = false; } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_SCAN_DONE) { const auto &it = data->data.sta_scan_done; - ESP_LOGV(TAG, "Event: WiFi Scan Done status=%" PRIu32 " number=%u scan_id=%u", it.status, it.number, it.scan_id); + ESP_LOGV(TAG, "Scan done: status=%" PRIu32 " number=%u scan_id=%u", it.status, it.number, it.scan_id); scan_result_.clear(); this->scan_done_ = true; @@ -772,28 +771,28 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { } } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_START) { - ESP_LOGV(TAG, "Event: WiFi AP start"); + ESP_LOGV(TAG, "AP start"); s_ap_started = true; } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STOP) { - ESP_LOGV(TAG, "Event: WiFi AP stop"); + ESP_LOGV(TAG, "AP stop"); s_ap_started = false; } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_PROBEREQRECVED) { const auto &it = data->data.ap_probe_req_rx; - ESP_LOGVV(TAG, "Event: AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); + ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STACONNECTED) { const auto &it = data->data.ap_staconnected; - ESP_LOGV(TAG, "Event: AP client connected MAC=%s", format_mac_addr(it.mac).c_str()); + ESP_LOGV(TAG, "AP client connected MAC=%s", format_mac_addr(it.mac).c_str()); } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STADISCONNECTED) { const auto &it = data->data.ap_stadisconnected; - ESP_LOGV(TAG, "Event: AP client disconnected MAC=%s", format_mac_addr(it.mac).c_str()); + ESP_LOGV(TAG, "AP client disconnected MAC=%s", format_mac_addr(it.mac).c_str()); } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_AP_STAIPASSIGNED) { const auto &it = data->data.ip_ap_staipassigned; - ESP_LOGV(TAG, "Event: AP client assigned IP %s", format_ip4_addr(it.ip).c_str()); + ESP_LOGV(TAG, "AP client assigned IP %s", format_ip4_addr(it.ip).c_str()); } } @@ -873,7 +872,7 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { err = esp_netif_set_ip_info(s_ap_netif, &info); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_netif_set_ip_info failed! %d", err); + ESP_LOGE(TAG, "esp_netif_set_ip_info failed: %d", err); return false; } @@ -889,14 +888,14 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { err = esp_netif_dhcps_option(s_ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_REQUESTED_IP_ADDRESS, &lease, sizeof(lease)); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_netif_dhcps_option failed! %d", err); + ESP_LOGE(TAG, "esp_netif_dhcps_option failed: %d", err); return false; } err = esp_netif_dhcps_start(s_ap_netif); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_netif_dhcps_start failed! %d", err); + ESP_LOGE(TAG, "esp_netif_dhcps_start failed: %d", err); return false; } @@ -911,7 +910,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); if (ap.get_ssid().size() > sizeof(conf.ap.ssid)) { - ESP_LOGE(TAG, "AP SSID is too long"); + ESP_LOGE(TAG, "AP SSID too long"); return false; } memcpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); @@ -926,7 +925,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { } else { conf.ap.authmode = WIFI_AUTH_WPA2_PSK; if (ap.get_password().size() > sizeof(conf.ap.password)) { - ESP_LOGE(TAG, "AP password is too long"); + ESP_LOGE(TAG, "AP password too long"); return false; } memcpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), ap.get_password().size()); @@ -937,12 +936,12 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { esp_err_t err = esp_wifi_set_config(WIFI_IF_AP, &conf); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_wifi_set_config failed! %d", err); + ESP_LOGE(TAG, "esp_wifi_set_config failed: %d", err); return false; } if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { - ESP_LOGE(TAG, "wifi_ap_ip_config_ failed!"); + ESP_LOGE(TAG, "wifi_ap_ip_config_ failed:"); return false; } diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index 9f50b62924..0f7b688290 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -9,10 +9,10 @@ #include "lwip/err.h" #include "lwip/dns.h" +#include "esphome/core/application.h" +#include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/hal.h" -#include "esphome/core/application.h" #include "esphome/core/util.h" namespace esphome { @@ -32,14 +32,14 @@ bool WiFiComponent::wifi_mode_(optional sta, optional ap) { return true; if (enable_sta && !current_sta) { - ESP_LOGV(TAG, "Enabling STA."); + ESP_LOGV(TAG, "Enabling STA"); } else if (!enable_sta && current_sta) { - ESP_LOGV(TAG, "Disabling STA."); + ESP_LOGV(TAG, "Disabling STA"); } if (enable_ap && !current_ap) { - ESP_LOGV(TAG, "Enabling AP."); + ESP_LOGV(TAG, "Enabling AP"); } else if (!enable_ap && current_ap) { - ESP_LOGV(TAG, "Disabling AP."); + ESP_LOGV(TAG, "Disabling AP"); } uint8_t mode = 0; @@ -124,7 +124,7 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { ap.get_channel().has_value() ? *ap.get_channel() : 0, ap.get_bssid().has_value() ? ap.get_bssid()->data() : NULL); if (status != WL_CONNECTED) { - ESP_LOGW(TAG, "esp_wifi_connect failed! %d", status); + ESP_LOGW(TAG, "esp_wifi_connect failed: %d", status); return false; } @@ -256,23 +256,23 @@ using esphome_wifi_event_info_t = arduino_event_info_t; void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_wifi_event_info_t info) { switch (event) { case ESPHOME_EVENT_ID_WIFI_READY: { - ESP_LOGV(TAG, "Event: WiFi ready"); + ESP_LOGV(TAG, "Ready"); break; } case ESPHOME_EVENT_ID_WIFI_SCAN_DONE: { auto it = info.wifi_scan_done; - ESP_LOGV(TAG, "Event: WiFi Scan Done status=%u number=%u scan_id=%u", it.status, it.number, it.scan_id); + ESP_LOGV(TAG, "Scan done: status=%u number=%u scan_id=%u", it.status, it.number, it.scan_id); this->wifi_scan_done_callback_(); break; } case ESPHOME_EVENT_ID_WIFI_STA_START: { - ESP_LOGV(TAG, "Event: WiFi STA start"); + ESP_LOGV(TAG, "STA start"); WiFi.setHostname(App.get_name().c_str()); break; } case ESPHOME_EVENT_ID_WIFI_STA_STOP: { - ESP_LOGV(TAG, "Event: WiFi STA stop"); + ESP_LOGV(TAG, "STA stop"); break; } case ESPHOME_EVENT_ID_WIFI_STA_CONNECTED: { @@ -280,7 +280,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ char buf[33]; memcpy(buf, it.ssid, it.ssid_len); buf[it.ssid_len] = '\0'; - ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, + ESP_LOGV(TAG, "Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); break; @@ -291,9 +291,9 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ memcpy(buf, it.ssid, it.ssid_len); buf[it.ssid_len] = '\0'; if (it.reason == WIFI_REASON_NO_AP_FOUND) { - ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); + ESP_LOGW(TAG, "Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); } else { - ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, + ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); } @@ -310,12 +310,11 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ } case ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE: { auto it = info.wifi_sta_authmode_change; - ESP_LOGV(TAG, "Event: Authmode Change old=%s new=%s", get_auth_mode_str(it.old_mode), - get_auth_mode_str(it.new_mode)); + ESP_LOGV(TAG, "Authmode Change old=%s new=%s", get_auth_mode_str(it.old_mode), get_auth_mode_str(it.new_mode)); // Mitigate CVE-2020-12638 // https://lbsfilm.at/blog/wpa2-authenticationmode-downgrade-in-espressif-microprocessors if (it.old_mode != WIFI_AUTH_OPEN && it.new_mode == WIFI_AUTH_OPEN) { - ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting..."); + ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting"); // we can't call retry_connect() from this context, so disconnect immediately // and notify main thread with error_from_callback_ WiFi.disconnect(); @@ -325,47 +324,47 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ } case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP: { // auto it = info.got_ip.ip_info; - ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(WiFi.localIP()).c_str(), + ESP_LOGV(TAG, "static_ip=%s gateway=%s", format_ip4_addr(WiFi.localIP()).c_str(), format_ip4_addr(WiFi.gatewayIP()).c_str()); s_sta_connecting = false; break; } case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6: { // auto it = info.got_ip.ip_info; - ESP_LOGV(TAG, "Event: Got IPv6"); + ESP_LOGV(TAG, "Got IPv6"); break; } case ESPHOME_EVENT_ID_WIFI_STA_LOST_IP: { - ESP_LOGV(TAG, "Event: Lost IP"); + ESP_LOGV(TAG, "Lost IP"); break; } case ESPHOME_EVENT_ID_WIFI_AP_START: { - ESP_LOGV(TAG, "Event: WiFi AP start"); + ESP_LOGV(TAG, "AP start"); break; } case ESPHOME_EVENT_ID_WIFI_AP_STOP: { - ESP_LOGV(TAG, "Event: WiFi AP stop"); + ESP_LOGV(TAG, "AP stop"); break; } case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: { auto it = info.wifi_sta_connected; auto &mac = it.bssid; - ESP_LOGV(TAG, "Event: AP client connected MAC=%s", format_mac_addr(mac).c_str()); + ESP_LOGV(TAG, "AP client connected MAC=%s", format_mac_addr(mac).c_str()); break; } case ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED: { auto it = info.wifi_sta_disconnected; auto &mac = it.bssid; - ESP_LOGV(TAG, "Event: AP client disconnected MAC=%s", format_mac_addr(mac).c_str()); + ESP_LOGV(TAG, "AP client disconnected MAC=%s", format_mac_addr(mac).c_str()); break; } case ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED: { - ESP_LOGV(TAG, "Event: AP client assigned IP"); + ESP_LOGV(TAG, "AP client assigned IP"); break; } case ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED: { auto it = info.wifi_ap_probereqrecved; - ESP_LOGVV(TAG, "Event: AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); + ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); break; } default: @@ -399,7 +398,7 @@ bool WiFiComponent::wifi_scan_start_(bool passive) { // need to use WiFi because of WiFiScanClass allocations :( int16_t err = WiFi.scanNetworks(true, true, passive, 200); if (err != WIFI_SCAN_RUNNING) { - ESP_LOGV(TAG, "WiFi.scanNetworks failed! %d", err); + ESP_LOGV(TAG, "WiFi.scanNetworks failed: %d", err); return false; } @@ -447,7 +446,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return false; if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { - ESP_LOGV(TAG, "wifi_ap_ip_config_ failed!"); + ESP_LOGV(TAG, "wifi_ap_ip_config_ failed"); return false; } diff --git a/esphome/components/wifi/wifi_component_pico_w.cpp b/esphome/components/wifi/wifi_component_pico_w.cpp index 23fd766abe..bf15892cd5 100644 --- a/esphome/components/wifi/wifi_component_pico_w.cpp +++ b/esphome/components/wifi/wifi_component_pico_w.cpp @@ -134,7 +134,7 @@ bool WiFiComponent::wifi_scan_start_(bool passive) { scan_options.scan_type = passive ? 1 : 0; int err = cyw43_wifi_scan(&cyw43_state, &scan_options, nullptr, &s_wifi_scan_result); if (err) { - ESP_LOGV(TAG, "cyw43_wifi_scan failed!"); + ESP_LOGV(TAG, "cyw43_wifi_scan failed"); } return err == 0; return true; @@ -162,7 +162,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { if (!this->wifi_mode_({}, true)) return false; if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { - ESP_LOGV(TAG, "wifi_ap_ip_config_ failed!"); + ESP_LOGV(TAG, "wifi_ap_ip_config_ failed"); return false; } @@ -209,7 +209,7 @@ network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { void WiFiComponent::wifi_loop_() { if (this->state_ == WIFI_COMPONENT_STATE_STA_SCANNING && !cyw43_wifi_scan_active(&cyw43_state)) { this->scan_done_ = true; - ESP_LOGV(TAG, "Scan done!"); + ESP_LOGV(TAG, "Scan done"); } } diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.cpp b/esphome/components/wifi_info/wifi_info_text_sensor.cpp index 150c7229f8..2612e4af8d 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.cpp +++ b/esphome/components/wifi_info/wifi_info_text_sensor.cpp @@ -7,12 +7,12 @@ namespace wifi_info { static const char *const TAG = "wifi_info"; -void IPAddressWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo IPAddress", this); } -void ScanResultsWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo Scan Results", this); } -void SSIDWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo SSID", this); } -void BSSIDWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo BSSID", this); } -void MacAddressWifiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo Mac Address", this); } -void DNSAddressWifiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo DNS Address", this); } +void IPAddressWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "IP Address", this); } +void ScanResultsWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "Scan Results", this); } +void SSIDWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "SSID", this); } +void BSSIDWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "BSSID", this); } +void MacAddressWifiInfo::dump_config() { LOG_TEXT_SENSOR("", "MAC Address", this); } +void DNSAddressWifiInfo::dump_config() { LOG_TEXT_SENSOR("", "DNS Address", this); } } // namespace wifi_info } // namespace esphome diff --git a/esphome/components/wireguard/wireguard.cpp b/esphome/components/wireguard/wireguard.cpp index 431bf52227..1f61e2dda3 100644 --- a/esphome/components/wireguard/wireguard.cpp +++ b/esphome/components/wireguard/wireguard.cpp @@ -21,13 +21,13 @@ static const char *const TAG = "wireguard"; * Cannot use `static const char*` for LOGMSG_PEER_STATUS on esp8266 platform * because log messages in `Wireguard::update()` method fail. */ -#define LOGMSG_PEER_STATUS "WireGuard remote peer is %s (latest handshake %s)" +#define LOGMSG_PEER_STATUS "Remote peer is %s (latest handshake %s)" static const char *const LOGMSG_ONLINE = "online"; static const char *const LOGMSG_OFFLINE = "offline"; void Wireguard::setup() { - ESP_LOGD(TAG, "initializing WireGuard..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->wg_config_.address = this->address_.c_str(); this->wg_config_.private_key = this->private_key_.c_str(); @@ -45,7 +45,7 @@ void Wireguard::setup() { this->wg_initialized_ = esp_wireguard_init(&(this->wg_config_), &(this->wg_ctx_)); if (this->wg_initialized_ == ESP_OK) { - ESP_LOGI(TAG, "WireGuard initialized"); + ESP_LOGI(TAG, "Initialized"); this->wg_peer_offline_time_ = millis(); this->srctime_->add_on_time_sync_callback(std::bind(&Wireguard::start_connection_, this)); this->defer(std::bind(&Wireguard::start_connection_, this)); // defer to avoid blocking setup @@ -56,7 +56,7 @@ void Wireguard::setup() { } #endif } else { - ESP_LOGE(TAG, "cannot initialize WireGuard, error code %d", this->wg_initialized_); + ESP_LOGE(TAG, "Cannot initialize: error code %d", this->wg_initialized_); this->mark_failed(); } } @@ -67,7 +67,7 @@ void Wireguard::loop() { } if ((this->wg_initialized_ == ESP_OK) && (this->wg_connected_ == ESP_OK) && (!network::is_connected())) { - ESP_LOGV(TAG, "local network connection has been lost, stopping WireGuard..."); + ESP_LOGV(TAG, "Local network connection has been lost, stopping"); this->stop_connection_(); } } @@ -109,7 +109,7 @@ void Wireguard::update() { // check reboot timeout every time the peer is down if (this->enabled_ && this->reboot_timeout_ > 0) { if (millis() - this->wg_peer_offline_time_ > this->reboot_timeout_) { - ESP_LOGE(TAG, "WireGuard remote peer is unreachable, rebooting..."); + ESP_LOGE(TAG, "Remote peer is unreachable; rebooting"); App.reboot(); } } @@ -201,14 +201,14 @@ void Wireguard::disable_auto_proceed() { this->proceed_allowed_ = false; } void Wireguard::enable() { this->enabled_ = true; - ESP_LOGI(TAG, "WireGuard enabled"); + ESP_LOGI(TAG, "Enabled"); this->publish_enabled_state(); } void Wireguard::disable() { this->enabled_ = false; this->defer(std::bind(&Wireguard::stop_connection_, this)); // defer to avoid blocking running loop - ESP_LOGI(TAG, "WireGuard disabled"); + ESP_LOGI(TAG, "Disabled"); this->publish_enabled_state(); } @@ -224,44 +224,44 @@ bool Wireguard::is_enabled() { return this->enabled_; } void Wireguard::start_connection_() { if (!this->enabled_) { - ESP_LOGV(TAG, "WireGuard is disabled, cannot start connection"); + ESP_LOGV(TAG, "Disabled, cannot start connection"); return; } if (this->wg_initialized_ != ESP_OK) { - ESP_LOGE(TAG, "cannot start WireGuard, initialization in error with code %d", this->wg_initialized_); + ESP_LOGE(TAG, "Cannot start: error code %d", this->wg_initialized_); return; } if (!network::is_connected()) { - ESP_LOGD(TAG, "WireGuard is waiting for local network connection to be available"); + ESP_LOGD(TAG, "Waiting for local network connection to be available"); return; } if (!this->srctime_->now().is_valid()) { - ESP_LOGD(TAG, "WireGuard is waiting for system time to be synchronized"); + ESP_LOGD(TAG, "Waiting for system time to be synchronized"); return; } if (this->wg_connected_ == ESP_OK) { - ESP_LOGV(TAG, "WireGuard connection already started"); + ESP_LOGV(TAG, "Connection already started"); return; } - ESP_LOGD(TAG, "starting WireGuard connection..."); + ESP_LOGD(TAG, "Starting connection"); this->wg_connected_ = esp_wireguard_connect(&(this->wg_ctx_)); if (this->wg_connected_ == ESP_OK) { - ESP_LOGI(TAG, "WireGuard connection started"); + ESP_LOGI(TAG, "Connection started"); } else if (this->wg_connected_ == ESP_ERR_RETRY) { - ESP_LOGD(TAG, "WireGuard is waiting for endpoint IP address to be available"); + ESP_LOGD(TAG, "Waiting for endpoint IP address to be available"); return; } else { - ESP_LOGW(TAG, "cannot start WireGuard connection, error code %d", this->wg_connected_); + ESP_LOGW(TAG, "Cannot start connection, error code %d", this->wg_connected_); return; } - ESP_LOGD(TAG, "configuring WireGuard allowed IPs list..."); + ESP_LOGD(TAG, "Configuring allowed IPs list"); bool allowed_ips_ok = true; for (std::tuple ip : this->allowed_ips_) { allowed_ips_ok &= @@ -269,9 +269,9 @@ void Wireguard::start_connection_() { } if (allowed_ips_ok) { - ESP_LOGD(TAG, "allowed IPs list configured correctly"); + ESP_LOGD(TAG, "Allowed IPs list configured correctly"); } else { - ESP_LOGE(TAG, "cannot configure WireGuard allowed IPs list, aborting..."); + ESP_LOGE(TAG, "Cannot configure allowed IPs list, aborting"); this->stop_connection_(); this->mark_failed(); } @@ -279,7 +279,7 @@ void Wireguard::start_connection_() { void Wireguard::stop_connection_() { if (this->wg_initialized_ == ESP_OK && this->wg_connected_ == ESP_OK) { - ESP_LOGD(TAG, "stopping WireGuard connection..."); + ESP_LOGD(TAG, "Stopping connection"); esp_wireguard_disconnect(&(this->wg_ctx_)); this->wg_connected_ = ESP_FAIL; } diff --git a/esphome/components/wled/wled_light_effect.cpp b/esphome/components/wled/wled_light_effect.cpp index 84842dff39..25577ccc11 100644 --- a/esphome/components/wled/wled_light_effect.cpp +++ b/esphome/components/wled/wled_light_effect.cpp @@ -1,8 +1,8 @@ #ifdef USE_ARDUINO #include "wled_light_effect.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ESP32 #include diff --git a/esphome/components/x9c/x9c.cpp b/esphome/components/x9c/x9c.cpp index caad16edf9..ccd0c60b50 100644 --- a/esphome/components/x9c/x9c.cpp +++ b/esphome/components/x9c/x9c.cpp @@ -68,8 +68,10 @@ void X9cOutput::dump_config() { LOG_PIN(" Chip Select Pin: ", this->cs_pin_); LOG_PIN(" Increment Pin: ", this->inc_pin_); LOG_PIN(" Up/Down Pin: ", this->ud_pin_); - ESP_LOGCONFIG(TAG, " Initial Value: %f", this->initial_value_); - ESP_LOGCONFIG(TAG, " Step Delay: %d", this->step_delay_); + ESP_LOGCONFIG(TAG, + " Initial Value: %f\n" + " Step Delay: %d", + this->initial_value_, this->step_delay_); LOG_FLOAT_OUTPUT(this); } diff --git a/esphome/components/xgzp68xx/xgzp68xx.cpp b/esphome/components/xgzp68xx/xgzp68xx.cpp index ad6217845d..52933ebdef 100644 --- a/esphome/components/xgzp68xx/xgzp68xx.cpp +++ b/esphome/components/xgzp68xx/xgzp68xx.cpp @@ -1,7 +1,7 @@ #include "xgzp68xx.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/i2c/i2c.h" #include @@ -69,22 +69,24 @@ void XGZP68XXComponent::update() { } void XGZP68XXComponent::setup() { - ESP_LOGD(TAG, "Setting up XGZP68xx..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t config; // Display some sample bits to confirm we are talking to the sensor this->read_register(SYSCONFIG_ADDRESS, &config, 1); - ESP_LOGCONFIG(TAG, "Gain value is %d", (config >> 3) & 0b111); - ESP_LOGCONFIG(TAG, "XGZP68xx started!"); + ESP_LOGCONFIG(TAG, + "Gain value is %d\n" + "XGZP68xx started!", + (config >> 3) & 0b111); } void XGZP68XXComponent::dump_config() { - ESP_LOGCONFIG(TAG, "XGZP68xx"); + ESP_LOGCONFIG(TAG, "XGZP68xx:"); LOG_SENSOR(" ", "Temperature: ", this->temperature_sensor_); LOG_SENSOR(" ", "Pressure: ", this->pressure_sensor_); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, " Connection with XGZP68xx failed!"); + ESP_LOGE(TAG, " Connection failed"); } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.cpp b/esphome/components/xiaomi_ble/xiaomi_ble.cpp index 04e0724ba7..a80daa0b80 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.cpp +++ b/esphome/components/xiaomi_ble/xiaomi_ble.cpp @@ -91,6 +91,13 @@ bool parse_xiaomi_value(uint16_t value_type, const uint8_t *data, uint8_t value_ // MiaoMiaoce humidity, 1 byte, 8-bit unsigned integer, 1 % else if ((value_type == 0x4C02) && (value_length == 1)) { result.humidity = data[0]; + } + // XMWSDJ04MMC humidity, 4 bytes, float, 0.1 °C + else if ((value_type == 0x4C08) && (value_length == 4)) { + const uint32_t int_number = encode_uint32(data[3], data[2], data[1], data[0]); + float humidity; + std::memcpy(&humidity, &int_number, sizeof(humidity)); + result.humidity = humidity; } else { return false; } @@ -219,6 +226,11 @@ optional parse_xiaomi_header(const esp32_ble_tracker::Service } else if (device_uuid == 0x055b) { // small square body, segment LCD, encrypted result.type = XiaomiParseResult::TYPE_LYWSD03MMC; result.name = "LYWSD03MMC"; + } else if (device_uuid == 0x1203) { // small square body, e-ink display, encrypted + result.type = XiaomiParseResult::TYPE_XMWSDJ04MMC; + result.name = "XMWSDJ04MMC"; + if (raw.size() == 19) + result.raw_offset -= 6; } else if (device_uuid == 0x07f6) { // Xiaomi-Yeelight BLE nightlight result.type = XiaomiParseResult::TYPE_MJYD02YLA; result.name = "MJYD02YLA"; diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.h b/esphome/components/xiaomi_ble/xiaomi_ble.h index 6978be97f4..77fb04fd78 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.h +++ b/esphome/components/xiaomi_ble/xiaomi_ble.h @@ -20,6 +20,7 @@ struct XiaomiParseResult { TYPE_LYWSD02MMC, TYPE_CGG1, TYPE_LYWSD03MMC, + TYPE_XMWSDJ04MMC, TYPE_CGD1, TYPE_CGDK2, TYPE_JQJCY01YM, diff --git a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp index baf9cb8075..4642768f90 100644 --- a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp +++ b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp @@ -9,8 +9,10 @@ namespace xiaomi_cgd1 { static const char *const TAG = "xiaomi_cgd1"; void XiaomiCGD1::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi CGD1"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi CGD1\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp index c74794f4f4..0dcbcbd05c 100644 --- a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp +++ b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp @@ -9,8 +9,10 @@ namespace xiaomi_cgdk2 { static const char *const TAG = "xiaomi_cgdk2"; void XiaomiCGDK2::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi CGDK2"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi CGDK2\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp index c20c7578d0..f9fffa3f20 100644 --- a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp +++ b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp @@ -9,8 +9,10 @@ namespace xiaomi_cgg1 { static const char *const TAG = "xiaomi_cgg1"; void XiaomiCGG1::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi CGG1"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi CGG1\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp b/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp index 45d4cceb52..2bc52b8085 100644 --- a/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp +++ b/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp @@ -1,6 +1,6 @@ #include "xiaomi_hhccjcy10.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ESP32 diff --git a/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp index cc122f2264..dff1228f64 100644 --- a/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp +++ b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp @@ -9,8 +9,10 @@ namespace xiaomi_lywsd02mmc { static const char *const TAG = "xiaomi_lywsd02mmc"; void XiaomiLYWSD02MMC::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi LYWSD02MMC"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi LYWSD02MMC\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp index d0319c9474..fb0165a21f 100644 --- a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp +++ b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp @@ -9,8 +9,10 @@ namespace xiaomi_lywsd03mmc { static const char *const TAG = "xiaomi_lywsd03mmc"; void XiaomiLYWSD03MMC::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi LYWSD03MMC"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi LYWSD03MMC\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp index 9ec2b10e12..90b654873b 100644 --- a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp @@ -9,8 +9,10 @@ namespace xiaomi_mhoc401 { static const char *const TAG = "xiaomi_mhoc401"; void XiaomiMHOC401::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi MHOC401"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi MHOC401\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xiaomi_xmwsdj04mmc/__init__.py b/esphome/components/xiaomi_xmwsdj04mmc/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/xiaomi_xmwsdj04mmc/sensor.py b/esphome/components/xiaomi_xmwsdj04mmc/sensor.py new file mode 100644 index 0000000000..b41a775f35 --- /dev/null +++ b/esphome/components/xiaomi_xmwsdj04mmc/sensor.py @@ -0,0 +1,77 @@ +import esphome.codegen as cg +from esphome.components import esp32_ble_tracker, sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_BINDKEY, + CONF_HUMIDITY, + CONF_ID, + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ENTITY_CATEGORY_DIAGNOSTIC, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_PERCENT, +) + +AUTO_LOAD = ["xiaomi_ble"] +CODEOWNERS = ["@medusalix"] +DEPENDENCIES = ["esp32_ble_tracker"] + +xiaomi_xmwsdj04mmc_ns = cg.esphome_ns.namespace("xiaomi_xmwsdj04mmc") +XiaomiXMWSDJ04MMC = xiaomi_xmwsdj04mmc_ns.class_( + "XiaomiXMWSDJ04MMC", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiXMWSDJ04MMC), + cv.Required(CONF_BINDKEY): cv.bind_key, + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_HUMIDITY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_BATTERY, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await esp32_ble_tracker.register_ble_device(var, config) + + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + cg.add(var.set_bindkey(config[CONF_BINDKEY])) + + if temperature_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temperature_config) + cg.add(var.set_temperature(sens)) + if humidity_config := config.get(CONF_HUMIDITY): + sens = await sensor.new_sensor(humidity_config) + cg.add(var.set_humidity(sens)) + if battery_level_config := config.get(CONF_BATTERY_LEVEL): + sens = await sensor.new_sensor(battery_level_config) + cg.add(var.set_battery_level(sens)) diff --git a/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp b/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp new file mode 100644 index 0000000000..f8712e7fd4 --- /dev/null +++ b/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp @@ -0,0 +1,77 @@ +#include "xiaomi_xmwsdj04mmc.h" +#include "esphome/core/log.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace xiaomi_xmwsdj04mmc { + +static const char *const TAG = "xiaomi_xmwsdj04mmc"; + +void XiaomiXMWSDJ04MMC::dump_config() { + ESP_LOGCONFIG(TAG, "Xiaomi XMWSDJ04MMC"); + ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + LOG_SENSOR(" ", "Temperature", this->temperature_); + LOG_SENSOR(" ", "Humidity", this->humidity_); + LOG_SENSOR(" ", "Battery Level", this->battery_level_); +} + +bool XiaomiXMWSDJ04MMC::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { + if (device.address_uint64() != this->address_) { + ESP_LOGVV(TAG, "parse_device(): unknown MAC address."); + return false; + } + ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str()); + + bool success = false; + for (auto &service_data : device.get_service_datas()) { + auto res = xiaomi_ble::parse_xiaomi_header(service_data); + if (!res.has_value()) { + continue; + } + if (res->is_duplicate) { + continue; + } + if (res->has_encryption && + (!(xiaomi_ble::decrypt_xiaomi_payload(const_cast &>(service_data.data), this->bindkey_, + this->address_)))) { + continue; + } + if (!(xiaomi_ble::parse_xiaomi_message(service_data.data, *res))) { + continue; + } + if (res->humidity.has_value() && this->humidity_ != nullptr) { + // see https://github.com/custom-components/sensor.mitemp_bt/issues/7#issuecomment-595948254 + *res->humidity = trunc(*res->humidity); + } + if (!(xiaomi_ble::report_xiaomi_results(res, device.address_str()))) { + continue; + } + if (res->temperature.has_value() && this->temperature_ != nullptr) + this->temperature_->publish_state(*res->temperature); + if (res->humidity.has_value() && this->humidity_ != nullptr) + this->humidity_->publish_state(*res->humidity); + if (res->battery_level.has_value() && this->battery_level_ != nullptr) + this->battery_level_->publish_state(*res->battery_level); + success = true; + } + + return success; +} + +void XiaomiXMWSDJ04MMC::set_bindkey(const std::string &bindkey) { + memset(this->bindkey_, 0, 16); + if (bindkey.size() != 32) { + return; + } + char temp[3] = {0}; + for (int i = 0; i < 16; i++) { + strncpy(temp, &(bindkey.c_str()[i * 2]), 2); + this->bindkey_[i] = std::strtoul(temp, nullptr, 16); + } +} + +} // namespace xiaomi_xmwsdj04mmc +} // namespace esphome + +#endif diff --git a/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h b/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h new file mode 100644 index 0000000000..9ce02bb64e --- /dev/null +++ b/esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h @@ -0,0 +1,37 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include "esphome/components/xiaomi_ble/xiaomi_ble.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace xiaomi_xmwsdj04mmc { + +class XiaomiXMWSDJ04MMC : public Component, public esp32_ble_tracker::ESPBTDeviceListener { + public: + void set_address(uint64_t address) { this->address_ = address; } + void set_bindkey(const std::string &bindkey); + + bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; + + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void set_temperature(sensor::Sensor *temperature) { this->temperature_ = temperature; } + void set_humidity(sensor::Sensor *humidity) { this->humidity_ = humidity; } + void set_battery_level(sensor::Sensor *battery_level) { this->battery_level_ = battery_level; } + + protected: + uint64_t address_; + uint8_t bindkey_[16]; + sensor::Sensor *temperature_{nullptr}; + sensor::Sensor *humidity_{nullptr}; + sensor::Sensor *battery_level_{nullptr}; +}; + +} // namespace xiaomi_xmwsdj04mmc +} // namespace esphome + +#endif diff --git a/esphome/components/xpt2046/touchscreen/xpt2046.cpp b/esphome/components/xpt2046/touchscreen/xpt2046.cpp index aa11ed4b77..fa99e3afa7 100644 --- a/esphome/components/xpt2046/touchscreen/xpt2046.cpp +++ b/esphome/components/xpt2046/touchscreen/xpt2046.cpp @@ -1,6 +1,6 @@ #include "xpt2046.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include @@ -62,16 +62,17 @@ void XPT2046Component::dump_config() { ESP_LOGCONFIG(TAG, "XPT2046:"); LOG_PIN(" IRQ Pin: ", this->irq_pin_); - ESP_LOGCONFIG(TAG, " X min: %d", this->x_raw_min_); - ESP_LOGCONFIG(TAG, " X max: %d", this->x_raw_max_); - ESP_LOGCONFIG(TAG, " Y min: %d", this->y_raw_min_); - ESP_LOGCONFIG(TAG, " Y max: %d", this->y_raw_max_); - - ESP_LOGCONFIG(TAG, " Swap X/Y: %s", YESNO(this->swap_x_y_)); - ESP_LOGCONFIG(TAG, " Invert X: %s", YESNO(this->invert_x_)); - ESP_LOGCONFIG(TAG, " Invert Y: %s", YESNO(this->invert_y_)); - - ESP_LOGCONFIG(TAG, " threshold: %d", this->threshold_); + ESP_LOGCONFIG(TAG, + " X min: %d\n" + " X max: %d\n" + " Y min: %d\n" + " Y max: %d\n" + " Swap X/Y: %s\n" + " Invert X: %s\n" + " Invert Y: %s\n" + " threshold: %d", + this->x_raw_min_, this->x_raw_max_, this->y_raw_min_, this->y_raw_max_, YESNO(this->swap_x_y_), + YESNO(this->invert_x_), YESNO(this->invert_y_), this->threshold_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/xpt2046/touchscreen/xpt2046.h b/esphome/components/xpt2046/touchscreen/xpt2046.h index a635c08f82..f691ae2c7b 100644 --- a/esphome/components/xpt2046/touchscreen/xpt2046.h +++ b/esphome/components/xpt2046/touchscreen/xpt2046.h @@ -1,9 +1,9 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/core/automation.h" #include "esphome/components/spi/spi.h" #include "esphome/components/touchscreen/touchscreen.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" diff --git a/esphome/config.py b/esphome/config.py index c6351cdabd..ca3686a0e6 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -389,6 +389,7 @@ class LoadValidationStep(ConfigValidationStep): result.add_str_error(f"Platform not found: '{p_domain}'", path) continue CORE.loaded_integrations.add(p_name) + CORE.loaded_platforms.add(f"{self.domain}/{p_name}") # Process AUTO_LOAD for load in platform.auto_load: diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 53a85dc6ed..072b4d69d1 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1676,6 +1676,26 @@ class OnlyWith(Optional): pass +class OnlyWithout(Optional): + """Set the default value only if the given component is NOT loaded.""" + + def __init__(self, key, component, default=None): + super().__init__(key) + self._component = component + self._default = vol.default_factory(default) + + @property + def default(self): + if self._component not in CORE.loaded_integrations: + return self._default + return vol.UNDEFINED + + @default.setter + def default(self, value): + # Ignore default set from vol.Optional + pass + + def _entity_base_validator(config): if CONF_NAME not in config and CONF_ID not in config: raise Invalid("At least one of 'id:' or 'name:' is required!") diff --git a/esphome/const.py b/esphome/const.py index ce3a987cc4..3a5cd2215f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.6.0-dev" +__version__ = "2025.7.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( @@ -89,6 +89,7 @@ CONF_BIT_DEPTH = "bit_depth" CONF_BITS_PER_SAMPLE = "bits_per_sample" CONF_BLOCK = "block" CONF_BLUE = "blue" +CONF_BLUETOOTH = "bluetooth" CONF_BOARD = "board" CONF_BOARD_FLASH_MODE = "board_flash_mode" CONF_BORDER = "border" @@ -528,7 +529,9 @@ CONF_MONTH = "month" CONF_MONTHS = "months" CONF_MOSI_PIN = "mosi_pin" CONF_MOTION = "motion" +CONF_MOVE_THRESHOLD = "move_threshold" CONF_MOVEMENT_COUNTER = "movement_counter" +CONF_MOVING_DISTANCE = "moving_distance" CONF_MQTT = "mqtt" CONF_MQTT_ID = "mqtt_id" CONF_MULTIPLE = "multiple" @@ -836,6 +839,7 @@ CONF_STEP = "step" CONF_STEP_DELAY = "step_delay" CONF_STEP_MODE = "step_mode" CONF_STEP_PIN = "step_pin" +CONF_STILL_THRESHOLD = "still_threshold" CONF_STOP = "stop" CONF_STOP_ACTION = "stop_action" CONF_STORE_BASELINE = "store_baseline" @@ -978,7 +982,9 @@ CONF_WIND_DIRECTION_DEGREES = "wind_direction_degrees" CONF_WIND_SPEED = "wind_speed" CONF_WINDOW_SIZE = "window_size" CONF_WRITE_PIN = "write_pin" +CONF_X = "x" CONF_X_GRID = "x_grid" +CONF_Y = "y" CONF_Y_GRID = "y_grid" CONF_YEAR = "year" CONF_ZERO = "zero" diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index bf61307021..bc98ff54db 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -1,3 +1,4 @@ +from collections import defaultdict import logging import math import os @@ -506,14 +507,21 @@ class EsphomeCore: self.libraries: list[Library] = [] # A set of build flags to set in the platformio project self.build_flags: set[str] = set() + # A set of build unflags to set in the platformio project + self.build_unflags: set[str] = set() # A set of defines to set for the compile process in esphome/core/defines.h self.defines: set[Define] = set() # A map of all platformio options to apply self.platformio_options: dict[str, str | list[str]] = {} # A set of strings of names of loaded integrations, used to find namespace ID conflicts self.loaded_integrations = set() + # A set of strings for platform/integration combos + self.loaded_platforms: set[str] = set() # A set of component IDs to track what Component subclasses are declared self.component_ids = set() + # Dict to track platform entity counts for pre-allocation + # Key: platform name (e.g. "sensor", "binary_sensor"), Value: count + self.platform_counts: defaultdict[str, int] = defaultdict(int) # Whether ESPHome was started in verbose mode self.verbose = False # Whether ESPHome was started in quiet mode @@ -539,10 +547,12 @@ class EsphomeCore: self.global_statements = [] self.libraries = [] self.build_flags = set() + self.build_unflags = set() self.defines = set() self.platformio_options = {} self.loaded_integrations = set() self.component_ids = set() + self.platform_counts = defaultdict(int) PIN_SCHEMA_REGISTRY.reset() @property @@ -667,16 +677,17 @@ class EsphomeCore: def using_esp_idf(self): return self.target_framework == "esp-idf" - def add_job(self, func, *args, **kwargs): + def add_job(self, func, *args, **kwargs) -> None: self.event_loop.add_job(func, *args, **kwargs) - def flush_tasks(self): + def flush_tasks(self) -> None: try: self.event_loop.flush_tasks() except RuntimeError as e: raise EsphomeError(str(e)) from e - def add(self, expression): + def add(self, expression, prepend=False) -> "Statement": + """Add an expression or statement to the main setup() block.""" from esphome.cpp_generator import Expression, Statement, statement if isinstance(expression, Expression): @@ -686,11 +697,14 @@ class EsphomeCore: f"Add '{expression}' must be expression or statement, not {type(expression)}" ) - self.main_statements.append(expression) + if prepend: + self.main_statements.insert(0, expression) + else: + self.main_statements.append(expression) _LOGGER.debug("Adding: %s", expression) return expression - def add_global(self, expression, prepend=False): + def add_global(self, expression, prepend=False) -> "Statement": from esphome.cpp_generator import Expression, Statement, statement if isinstance(expression, Expression): @@ -755,11 +769,15 @@ class EsphomeCore: self.libraries.append(library) return library - def add_build_flag(self, build_flag): + def add_build_flag(self, build_flag: str) -> str: self.build_flags.add(build_flag) _LOGGER.debug("Adding build flag: %s", build_flag) return build_flag + def add_build_unflag(self, build_unflag: str) -> None: + self.build_unflags.add(build_unflag) + _LOGGER.debug("Adding build unflag: %s", build_unflag) + def add_define(self, define): if isinstance(define, str): define = Define(define) @@ -820,6 +838,14 @@ class EsphomeCore: def has_id(self, id): return id in self.variables + def register_platform_component(self, platform_name: str, var) -> None: + """Register a component for a platform and track its count. + + :param platform_name: The name of the platform (e.g., 'sensor', 'binary_sensor') + :param var: The variable (component) being registered (currently unused but kept for future use) + """ + self.platform_counts[platform_name] += 1 + @property def cpp_main_section(self): from esphome.cpp_generator import statement diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index ad1ec33436..49c1e5fd61 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -45,8 +45,8 @@ void Application::register_component_(Component *comp) { this->components_.push_back(comp); } void Application::setup() { - ESP_LOGI(TAG, "Running through setup()..."); - ESP_LOGV(TAG, "Sorting components by setup priority..."); + ESP_LOGI(TAG, "Running through setup()"); + ESP_LOGV(TAG, "Sorting components by setup priority"); std::stable_sort(this->components_.begin(), this->components_.end(), [](const Component *a, const Component *b) { return a->get_actual_setup_priority() > b->get_actual_setup_priority(); }); @@ -66,7 +66,7 @@ void Application::setup() { [](Component *a, Component *b) { return a->get_loop_priority() > b->get_loop_priority(); }); do { - uint32_t new_app_state = STATUS_LED_WARNING; + uint8_t new_app_state = STATUS_LED_WARNING; this->scheduler.call(); this->feed_wdt(); for (uint32_t j = 0; j <= i; j++) { @@ -87,7 +87,7 @@ void Application::setup() { this->calculate_looping_components_(); } void Application::loop() { - uint32_t new_app_state = 0; + uint8_t new_app_state = 0; this->scheduler.call(); @@ -97,7 +97,27 @@ void Application::loop() { // Feed WDT with time this->feed_wdt(last_op_end_time); - for (Component *component : this->looping_components_) { + // Process any pending enable_loop requests from ISRs + // This must be done before marking in_loop_ = true to avoid race conditions + if (this->has_pending_enable_loop_requests_) { + // Clear flag BEFORE processing to avoid race condition + // If ISR sets it during processing, we'll catch it next loop iteration + // This is safe because: + // 1. Each component has its own pending_enable_loop_ flag that we check + // 2. If we can't process a component (wrong state), enable_pending_loops_() + // will set this flag back to true + // 3. Any new ISR requests during processing will set the flag again + this->has_pending_enable_loop_requests_ = false; + this->enable_pending_loops_(); + } + + // Mark that we're in the loop for safe reentrant modifications + this->in_loop_ = true; + + for (this->current_loop_index_ = 0; this->current_loop_index_ < this->looping_components_active_end_; + this->current_loop_index_++) { + Component *component = this->looping_components_[this->current_loop_index_]; + // Update the cached time before each component runs this->loop_component_start_time_ = last_op_end_time; @@ -112,12 +132,16 @@ void Application::loop() { this->app_state_ |= new_app_state; this->feed_wdt(last_op_end_time); } + + this->in_loop_ = false; this->app_state_ = new_app_state; // Use the last component's end time instead of calling millis() again auto elapsed = last_op_end_time - this->last_loop_; if (elapsed >= this->loop_interval_ || HighFrequencyLoopRequester::is_high_frequency()) { - yield(); + // Even if we overran the loop interval, we still need to select() + // to know if any sockets have data ready + this->yield_with_select_(0); } else { uint32_t delay_time = this->loop_interval_ - elapsed; uint32_t next_schedule = this->scheduler.next_schedule_in().value_or(delay_time); @@ -126,63 +150,7 @@ void Application::loop() { next_schedule = std::max(next_schedule, delay_time / 2); delay_time = std::min(next_schedule, delay_time); -#ifdef USE_SOCKET_SELECT_SUPPORT - if (!this->socket_fds_.empty()) { - // Use select() with timeout when we have sockets to monitor - - // Update fd_set if socket list has changed - if (this->socket_fds_changed_) { - FD_ZERO(&this->base_read_fds_); - for (int fd : this->socket_fds_) { - if (fd >= 0 && fd < FD_SETSIZE) { - FD_SET(fd, &this->base_read_fds_); - } - } - this->socket_fds_changed_ = false; - } - - // Copy base fd_set before each select - this->read_fds_ = this->base_read_fds_; - - // Convert delay_time (milliseconds) to timeval - struct timeval tv; - tv.tv_sec = delay_time / 1000; - tv.tv_usec = (delay_time - tv.tv_sec * 1000) * 1000; - - // Call select with timeout -#if defined(USE_SOCKET_IMPL_LWIP_SOCKETS) || (defined(USE_ESP32) && defined(USE_SOCKET_IMPL_BSD_SOCKETS)) - // Use lwip_select() on platforms with lwIP - it's faster - // Note: On ESP32 with BSD sockets, select() is already mapped to lwip_select() via macros, - // but we explicitly call lwip_select() for clarity and to ensure we get the optimized version - int ret = lwip_select(this->max_fd_ + 1, &this->read_fds_, nullptr, nullptr, &tv); -#else - // Use standard select() on other platforms (e.g., host/native builds) - int ret = ::select(this->max_fd_ + 1, &this->read_fds_, nullptr, nullptr, &tv); -#endif - - // Process select() result: - // ret < 0: error (except EINTR which is normal) - // ret > 0: socket(s) have data ready - normal and expected - // ret == 0: timeout occurred - normal and expected - if (ret < 0) { - if (errno == EINTR) { - // Interrupted by signal - this is normal, just continue - // No need to delay as some time has already passed - ESP_LOGVV(TAG, "select() interrupted by signal"); - } else { - // Actual error - log and fall back to delay - ESP_LOGW(TAG, "select() failed with errno %d", errno); - delay(delay_time); - } - } - } else { - // No sockets registered, use regular delay - delay(delay_time); - } -#else - // No select support, use regular delay - delay(delay_time); -#endif + this->yield_with_select_(delay_time); } this->last_loop_ = last_op_end_time; @@ -215,15 +183,17 @@ void IRAM_ATTR HOT Application::feed_wdt(uint32_t time) { } } void Application::reboot() { - ESP_LOGI(TAG, "Forcing a reboot..."); + ESP_LOGI(TAG, "Forcing a reboot"); for (auto it = this->components_.rbegin(); it != this->components_.rend(); ++it) { (*it)->on_shutdown(); } arch_restart(); } void Application::safe_reboot() { - ESP_LOGI(TAG, "Rebooting safely..."); + ESP_LOGI(TAG, "Rebooting safely"); run_safe_shutdown_hooks(); + teardown_components(TEARDOWN_TIMEOUT_REBOOT_MS); + run_powerdown_hooks(); arch_restart(); } @@ -236,10 +206,175 @@ void Application::run_safe_shutdown_hooks() { } } +void Application::run_powerdown_hooks() { + for (auto it = this->components_.rbegin(); it != this->components_.rend(); ++it) { + (*it)->on_powerdown(); + } +} + +void Application::teardown_components(uint32_t timeout_ms) { + uint32_t start_time = millis(); + + // Copy all components in reverse order using reverse iterators + // Reverse order matches the behavior of run_safe_shutdown_hooks() above and ensures + // components are torn down in the opposite order of their setup_priority (which is + // used to sort components during Application::setup()) + std::vector pending_components(this->components_.rbegin(), this->components_.rend()); + + uint32_t now = start_time; + while (!pending_components.empty() && (now - start_time) < timeout_ms) { + // Feed watchdog during teardown to prevent triggering + this->feed_wdt(now); + + // Use iterator to safely erase elements + for (auto it = pending_components.begin(); it != pending_components.end();) { + if ((*it)->teardown()) { + // Component finished teardown, erase it + it = pending_components.erase(it); + } else { + // Component still needs time + ++it; + } + } + + // Give some time for I/O operations if components are still pending + if (!pending_components.empty()) { + this->yield_with_select_(1); + } + + // Update time for next iteration + now = millis(); + } + + if (!pending_components.empty()) { + // Note: At this point, connections are either disconnected or in a bad state, + // so this warning will only appear via serial rather than being transmitted to clients + for (auto *component : pending_components) { + ESP_LOGW(TAG, "%s did not complete teardown within %" PRIu32 " ms", component->get_component_source(), + timeout_ms); + } + } +} + void Application::calculate_looping_components_() { + // First add all active components for (auto *obj : this->components_) { - if (obj->has_overridden_loop()) + if (obj->has_overridden_loop() && + (obj->get_component_state() & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) { this->looping_components_.push_back(obj); + } + } + + this->looping_components_active_end_ = this->looping_components_.size(); + + // Then add all inactive (LOOP_DONE) components + // This handles components that called disable_loop() during setup, before this method runs + for (auto *obj : this->components_) { + if (obj->has_overridden_loop() && + (obj->get_component_state() & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) { + this->looping_components_.push_back(obj); + } + } +} + +void Application::disable_component_loop_(Component *component) { + // This method must be reentrant - components can disable themselves during their own loop() call + // Linear search to find component in active section + // Most configs have 10-30 looping components (30 is on the high end) + // O(n) is acceptable here as we optimize for memory, not complexity + for (uint16_t i = 0; i < this->looping_components_active_end_; i++) { + if (this->looping_components_[i] == component) { + // Move last active component to this position + this->looping_components_active_end_--; + if (i != this->looping_components_active_end_) { + std::swap(this->looping_components_[i], this->looping_components_[this->looping_components_active_end_]); + + // If we're currently iterating and just swapped the current position + if (this->in_loop_ && i == this->current_loop_index_) { + // Decrement so we'll process the swapped component next + this->current_loop_index_--; + } + } + return; + } + } +} + +void Application::activate_looping_component_(uint16_t index) { + // Helper to move component from inactive to active section + if (index != this->looping_components_active_end_) { + std::swap(this->looping_components_[index], this->looping_components_[this->looping_components_active_end_]); + } + this->looping_components_active_end_++; +} + +void Application::enable_component_loop_(Component *component) { + // This method is only called when component state is LOOP_DONE, so we know + // the component must be in the inactive section (if it exists in looping_components_) + // Only search the inactive portion for better performance + // With typical 0-5 inactive components, O(k) is much faster than O(n) + const uint16_t size = this->looping_components_.size(); + for (uint16_t i = this->looping_components_active_end_; i < size; i++) { + if (this->looping_components_[i] == component) { + // Found in inactive section - move to active + this->activate_looping_component_(i); + return; + } + } + // Component not found in looping_components_ - this is normal for components + // that don't have loop() or were not included in the partitioned vector +} + +void Application::enable_pending_loops_() { + // Process components that requested enable_loop from ISR context + // Only iterate through inactive looping_components_ (typically 0-5) instead of all components + // + // Race condition handling: + // 1. We check if component is already in LOOP state first - if so, just clear the flag + // This handles reentrancy where enable_loop() was called between ISR and processing + // 2. We only clear pending_enable_loop_ after checking state, preventing lost requests + // 3. If any components aren't in LOOP_DONE state, we set has_pending_enable_loop_requests_ + // back to true to ensure we check again next iteration + // 4. ISRs can safely set flags at any time - worst case is we process them next iteration + // 5. The global flag (has_pending_enable_loop_requests_) is cleared before this method, + // so any ISR that fires during processing will be caught in the next loop + const uint16_t size = this->looping_components_.size(); + bool has_pending = false; + + for (uint16_t i = this->looping_components_active_end_; i < size; i++) { + Component *component = this->looping_components_[i]; + if (!component->pending_enable_loop_) { + continue; // Skip components without pending requests + } + + // Check current state + uint8_t state = component->component_state_ & COMPONENT_STATE_MASK; + + // If already in LOOP state, nothing to do - clear flag and continue + if (state == COMPONENT_STATE_LOOP) { + component->pending_enable_loop_ = false; + continue; + } + + // If not in LOOP_DONE state, can't enable yet - keep flag set + if (state != COMPONENT_STATE_LOOP_DONE) { + has_pending = true; // Keep tracking this component + continue; // Keep the flag set - try again next iteration + } + + // Clear the pending flag and enable the loop + component->pending_enable_loop_ = false; + ESP_LOGD(TAG, "%s loop enabled from ISR", component->get_component_source()); + component->component_state_ &= ~COMPONENT_STATE_MASK; + component->component_state_ |= COMPONENT_STATE_LOOP; + + // Move to active section + this->activate_looping_component_(i); + } + + // If we couldn't process some requests, ensure we check again next iteration + if (has_pending) { + this->has_pending_enable_loop_requests_ = true; } } @@ -304,6 +439,60 @@ bool Application::is_socket_ready(int fd) const { } #endif +void Application::yield_with_select_(uint32_t delay_ms) { + // Delay while monitoring sockets. When delay_ms is 0, always yield() to ensure other tasks run + // since select() with 0 timeout only polls without yielding. +#ifdef USE_SOCKET_SELECT_SUPPORT + if (!this->socket_fds_.empty()) { + // Update fd_set if socket list has changed + if (this->socket_fds_changed_) { + FD_ZERO(&this->base_read_fds_); + for (int fd : this->socket_fds_) { + if (fd >= 0 && fd < FD_SETSIZE) { + FD_SET(fd, &this->base_read_fds_); + } + } + this->socket_fds_changed_ = false; + } + + // Copy base fd_set before each select + this->read_fds_ = this->base_read_fds_; + + // Convert delay_ms to timeval + struct timeval tv; + tv.tv_sec = delay_ms / 1000; + tv.tv_usec = (delay_ms - tv.tv_sec * 1000) * 1000; + + // Call select with timeout +#if defined(USE_SOCKET_IMPL_LWIP_SOCKETS) || (defined(USE_ESP32) && defined(USE_SOCKET_IMPL_BSD_SOCKETS)) + int ret = lwip_select(this->max_fd_ + 1, &this->read_fds_, nullptr, nullptr, &tv); +#else + int ret = ::select(this->max_fd_ + 1, &this->read_fds_, nullptr, nullptr, &tv); +#endif + + // Process select() result: + // ret < 0: error (except EINTR which is normal) + // ret > 0: socket(s) have data ready - normal and expected + // ret == 0: timeout occurred - normal and expected + if (ret < 0 && errno != EINTR) { + // Actual error - log and fall back to delay + ESP_LOGW(TAG, "select() failed with errno %d", errno); + delay(delay_ms); + } + // When delay_ms is 0, we need to yield since select(0) doesn't yield + if (delay_ms == 0) { + yield(); + } + } else { + // No sockets registered, use regular delay + delay(delay_ms); + } +#else + // No select support, use regular delay + delay(delay_ms); +#endif +} + Application App; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esphome diff --git a/esphome/core/application.h b/esphome/core/application.h index 8cf0d87035..c17fd8ba74 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -83,10 +83,16 @@ namespace esphome { +// Teardown timeout constant (in milliseconds) +// For reboots, it's more important to shut down quickly than disconnect cleanly +// since we're not entering deep sleep. The only consequence of not shutting down +// cleanly is a warning in the log. +static const uint32_t TEARDOWN_TIMEOUT_REBOOT_MS = 1000; // 1 second for quick reboot + class Application { public: - void pre_setup(const std::string &name, const std::string &friendly_name, const std::string &area, - const char *comment, const char *compilation_time, bool name_add_mac_suffix) { + void pre_setup(const std::string &name, const std::string &friendly_name, const char *area, const char *comment, + const char *compilation_time, bool name_add_mac_suffix) { arch_init(); this->name_add_mac_suffix_ = name_add_mac_suffix; if (name_add_mac_suffix) { @@ -200,6 +206,73 @@ class Application { void register_update(update::UpdateEntity *update) { this->updates_.push_back(update); } #endif + /// Reserve space for components to avoid memory fragmentation + void reserve_components(size_t count) { this->components_.reserve(count); } + +#ifdef USE_BINARY_SENSOR + void reserve_binary_sensor(size_t count) { this->binary_sensors_.reserve(count); } +#endif +#ifdef USE_SWITCH + void reserve_switch(size_t count) { this->switches_.reserve(count); } +#endif +#ifdef USE_BUTTON + void reserve_button(size_t count) { this->buttons_.reserve(count); } +#endif +#ifdef USE_SENSOR + void reserve_sensor(size_t count) { this->sensors_.reserve(count); } +#endif +#ifdef USE_TEXT_SENSOR + void reserve_text_sensor(size_t count) { this->text_sensors_.reserve(count); } +#endif +#ifdef USE_FAN + void reserve_fan(size_t count) { this->fans_.reserve(count); } +#endif +#ifdef USE_COVER + void reserve_cover(size_t count) { this->covers_.reserve(count); } +#endif +#ifdef USE_CLIMATE + void reserve_climate(size_t count) { this->climates_.reserve(count); } +#endif +#ifdef USE_LIGHT + void reserve_light(size_t count) { this->lights_.reserve(count); } +#endif +#ifdef USE_NUMBER + void reserve_number(size_t count) { this->numbers_.reserve(count); } +#endif +#ifdef USE_DATETIME_DATE + void reserve_date(size_t count) { this->dates_.reserve(count); } +#endif +#ifdef USE_DATETIME_TIME + void reserve_time(size_t count) { this->times_.reserve(count); } +#endif +#ifdef USE_DATETIME_DATETIME + void reserve_datetime(size_t count) { this->datetimes_.reserve(count); } +#endif +#ifdef USE_SELECT + void reserve_select(size_t count) { this->selects_.reserve(count); } +#endif +#ifdef USE_TEXT + void reserve_text(size_t count) { this->texts_.reserve(count); } +#endif +#ifdef USE_LOCK + void reserve_lock(size_t count) { this->locks_.reserve(count); } +#endif +#ifdef USE_VALVE + void reserve_valve(size_t count) { this->valves_.reserve(count); } +#endif +#ifdef USE_MEDIA_PLAYER + void reserve_media_player(size_t count) { this->media_players_.reserve(count); } +#endif +#ifdef USE_ALARM_CONTROL_PANEL + void reserve_alarm_control_panel(size_t count) { this->alarm_control_panels_.reserve(count); } +#endif +#ifdef USE_EVENT + void reserve_event(size_t count) { this->events_.reserve(count); } +#endif +#ifdef USE_UPDATE + void reserve_update(size_t count) { this->updates_.reserve(count); } +#endif + /// Register the component in this Application instance. template C *register_component(C *c) { static_assert(std::is_base_of::value, "Only Component subclasses can be registered"); @@ -220,7 +293,7 @@ class Application { const std::string &get_friendly_name() const { return this->friendly_name_; } /// Get the area of this Application set by pre_setup(). - const std::string &get_area() const { return this->area_; } + std::string get_area() const { return this->area_ == nullptr ? "" : this->area_; } /// Get the comment of this Application set by pre_setup(). std::string get_comment() const { return this->comment_; } @@ -259,7 +332,15 @@ class Application { void run_safe_shutdown_hooks(); - uint32_t get_app_state() const { return this->app_state_; } + void run_powerdown_hooks(); + + /** Teardown all components with a timeout. + * + * @param timeout_ms Maximum time to wait for teardown in milliseconds + */ + void teardown_components(uint32_t timeout_ms); + + uint8_t get_app_state() const { return this->app_state_; } #ifdef USE_SUB_DEVICE const std::vector &get_sub_devices() { return this->sub_devices_; } @@ -511,10 +592,43 @@ class Application { void calculate_looping_components_(); + // These methods are called by Component::disable_loop() and Component::enable_loop() + // Components should not call these directly - use this->disable_loop() or this->enable_loop() + // to ensure component state is properly updated along with the loop partition + void disable_component_loop_(Component *component); + void enable_component_loop_(Component *component); + void enable_pending_loops_(); + void activate_looping_component_(uint16_t index); + void feed_wdt_arch_(); + /// Perform a delay while also monitoring socket file descriptors for readiness + void yield_with_select_(uint32_t delay_ms); + std::vector components_{}; + + // Partitioned vector design for looping components + // ================================================= + // Components are partitioned into [active | inactive] sections: + // + // looping_components_: [A, B, C, D | E, F] + // ^ + // looping_components_active_end_ (4) + // + // - Components A,B,C,D are active and will be called in loop() + // - Components E,F are inactive (disabled/failed) and won't be called + // - No flag checking needed during iteration - just loop 0 to active_end_ + // - When a component is disabled, it's swapped with the last active component + // and active_end_ is decremented + // - When a component is enabled, it's swapped with the first inactive component + // and active_end_ is incremented + // - This eliminates branch mispredictions from flag checking in the hot loop std::vector looping_components_{}; + uint16_t looping_components_active_end_{0}; + + // For safe reentrant modifications during iteration + uint16_t current_loop_index_{0}; + bool in_loop_{false}; #ifdef USE_SUB_DEVICE std::vector sub_devices_{}; @@ -585,14 +699,15 @@ class Application { std::string name_; std::string friendly_name_; - std::string area_; + const char *area_{nullptr}; const char *comment_{nullptr}; const char *compilation_time_{nullptr}; bool name_add_mac_suffix_; uint32_t last_loop_{0}; uint32_t loop_interval_{16}; size_t dump_config_at_{SIZE_MAX}; - uint32_t app_state_{0}; + uint8_t app_state_{0}; + volatile bool has_pending_enable_loop_requests_{false}; Component *current_component_{nullptr}; uint32_t loop_component_start_time_{0}; diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index 1141e4067d..625a7b2125 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -1,6 +1,7 @@ #include "esphome/core/component.h" #include +#include #include #include "esphome/core/application.h" #include "esphome/core/hal.h" @@ -29,18 +30,21 @@ const float LATE = -100.0f; } // namespace setup_priority -const uint32_t COMPONENT_STATE_MASK = 0xFF; -const uint32_t COMPONENT_STATE_CONSTRUCTION = 0x00; -const uint32_t COMPONENT_STATE_SETUP = 0x01; -const uint32_t COMPONENT_STATE_LOOP = 0x02; -const uint32_t COMPONENT_STATE_FAILED = 0x03; -const uint32_t STATUS_LED_MASK = 0xFF00; -const uint32_t STATUS_LED_OK = 0x0000; -const uint32_t STATUS_LED_WARNING = 0x0100; -const uint32_t STATUS_LED_ERROR = 0x0200; +// Component state uses bits 0-2 (8 states, 5 used) +const uint8_t COMPONENT_STATE_MASK = 0x07; +const uint8_t COMPONENT_STATE_CONSTRUCTION = 0x00; +const uint8_t COMPONENT_STATE_SETUP = 0x01; +const uint8_t COMPONENT_STATE_LOOP = 0x02; +const uint8_t COMPONENT_STATE_FAILED = 0x03; +const uint8_t COMPONENT_STATE_LOOP_DONE = 0x04; +// Status LED uses bits 3-4 +const uint8_t STATUS_LED_MASK = 0x18; +const uint8_t STATUS_LED_OK = 0x00; +const uint8_t STATUS_LED_WARNING = 0x08; // Bit 3 +const uint8_t STATUS_LED_ERROR = 0x10; // Bit 4 -const uint32_t WARN_IF_BLOCKING_OVER_MS = 50U; ///< Initial blocking time allowed without warning -const uint32_t WARN_IF_BLOCKING_INCREMENT_MS = 10U; ///< How long the blocking time must be larger to warn again +const uint16_t WARN_IF_BLOCKING_OVER_MS = 50U; ///< Initial blocking time allowed without warning +const uint16_t WARN_IF_BLOCKING_INCREMENT_MS = 10U; ///< How long the blocking time must be larger to warn again uint32_t global_state = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) @@ -82,13 +86,14 @@ void Component::call_setup() { this->setup(); } void Component::call_dump_config() { this->dump_config(); if (this->is_failed()) { - ESP_LOGE(TAG, " Component %s is marked FAILED: %s", this->get_component_source(), this->error_message_.c_str()); + ESP_LOGE(TAG, " Component %s is marked FAILED: %s", this->get_component_source(), + this->error_message_ ? this->error_message_ : "unspecified"); } } -uint32_t Component::get_component_state() const { return this->component_state_; } +uint8_t Component::get_component_state() const { return this->component_state_; } void Component::call() { - uint32_t state = this->component_state_ & COMPONENT_STATE_MASK; + uint8_t state = this->component_state_ & COMPONENT_STATE_MASK; switch (state) { case COMPONENT_STATE_CONSTRUCTION: // State Construction: Call setup and set state to setup @@ -109,6 +114,9 @@ void Component::call() { case COMPONENT_STATE_FAILED: // NOLINT(bugprone-branch-clone) // State failed: Do nothing break; + case COMPONENT_STATE_LOOP_DONE: // NOLINT(bugprone-branch-clone) + // State loop done: Do nothing, component has finished its work + break; default: break; } @@ -120,16 +128,65 @@ const char *Component::get_component_source() const { } bool Component::should_warn_of_blocking(uint32_t blocking_time) { if (blocking_time > this->warn_if_blocking_over_) { - this->warn_if_blocking_over_ = blocking_time + WARN_IF_BLOCKING_INCREMENT_MS; + // Prevent overflow when adding increment - if we're about to overflow, just max out + if (blocking_time + WARN_IF_BLOCKING_INCREMENT_MS < blocking_time || + blocking_time + WARN_IF_BLOCKING_INCREMENT_MS > std::numeric_limits::max()) { + this->warn_if_blocking_over_ = std::numeric_limits::max(); + } else { + this->warn_if_blocking_over_ = static_cast(blocking_time + WARN_IF_BLOCKING_INCREMENT_MS); + } return true; } return false; } void Component::mark_failed() { - ESP_LOGE(TAG, "Component %s was marked as failed.", this->get_component_source()); + ESP_LOGE(TAG, "Component %s was marked as failed", this->get_component_source()); this->component_state_ &= ~COMPONENT_STATE_MASK; this->component_state_ |= COMPONENT_STATE_FAILED; this->status_set_error(); + // Also remove from loop since failed components shouldn't loop + App.disable_component_loop_(this); +} +void Component::disable_loop() { + if ((this->component_state_ & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) { + ESP_LOGD(TAG, "%s loop disabled", this->get_component_source()); + this->component_state_ &= ~COMPONENT_STATE_MASK; + this->component_state_ |= COMPONENT_STATE_LOOP_DONE; + App.disable_component_loop_(this); + } +} +void Component::enable_loop() { + if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) { + ESP_LOGD(TAG, "%s loop enabled", this->get_component_source()); + this->component_state_ &= ~COMPONENT_STATE_MASK; + this->component_state_ |= COMPONENT_STATE_LOOP; + App.enable_component_loop_(this); + } +} +void IRAM_ATTR HOT Component::enable_loop_soon_any_context() { + // This method is thread and ISR-safe because: + // 1. Only performs simple assignments to volatile variables (atomic on all platforms) + // 2. No read-modify-write operations that could be interrupted + // 3. No memory allocation, object construction, or function calls + // 4. IRAM_ATTR ensures code is in IRAM, not flash (required for ISR execution) + // 5. Components are never destroyed, so no use-after-free concerns + // 6. App is guaranteed to be initialized before any ISR could fire + // 7. Multiple ISR/thread calls are safe - just sets the same flags to true + // 8. Race condition with main loop is handled by clearing flag before processing + this->pending_enable_loop_ = true; + App.has_pending_enable_loop_requests_ = true; +} +void Component::reset_to_construction_state() { + if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) { + ESP_LOGI(TAG, "Component %s is being reset to construction state", this->get_component_source()); + this->component_state_ &= ~COMPONENT_STATE_MASK; + this->component_state_ |= COMPONENT_STATE_CONSTRUCTION; + // Clear error status when resetting + this->status_clear_error(); + } +} +bool Component::is_in_loop_state() const { + return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP; } void Component::defer(std::function &&f) { // NOLINT App.scheduler.set_timeout(this, "", 0, std::move(f)); @@ -254,8 +311,8 @@ uint32_t WarnIfComponentBlockingGuard::finish() { } if (should_warn) { const char *src = component_ == nullptr ? "" : component_->get_component_source(); - ESP_LOGW(TAG, "Component %s took a long time for an operation (%" PRIu32 " ms).", src, blocking_time); - ESP_LOGW(TAG, "Components should block for at most 30 ms."); + ESP_LOGW(TAG, "Component %s took a long time for an operation (%" PRIu32 " ms)", src, blocking_time); + ESP_LOGW(TAG, "Components should block for at most 30 ms"); } return curr_time; diff --git a/esphome/core/component.h b/esphome/core/component.h index 7b3e12eb59..7f2bdd8414 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -53,19 +53,20 @@ static const uint32_t SCHEDULER_DONT_RUN = 4294967295UL; ESP_LOGCONFIG(TAG, " Update Interval: %.1fs", this->get_update_interval() / 1000.0f); \ } -extern const uint32_t COMPONENT_STATE_MASK; -extern const uint32_t COMPONENT_STATE_CONSTRUCTION; -extern const uint32_t COMPONENT_STATE_SETUP; -extern const uint32_t COMPONENT_STATE_LOOP; -extern const uint32_t COMPONENT_STATE_FAILED; -extern const uint32_t STATUS_LED_MASK; -extern const uint32_t STATUS_LED_OK; -extern const uint32_t STATUS_LED_WARNING; -extern const uint32_t STATUS_LED_ERROR; +extern const uint8_t COMPONENT_STATE_MASK; +extern const uint8_t COMPONENT_STATE_CONSTRUCTION; +extern const uint8_t COMPONENT_STATE_SETUP; +extern const uint8_t COMPONENT_STATE_LOOP; +extern const uint8_t COMPONENT_STATE_FAILED; +extern const uint8_t COMPONENT_STATE_LOOP_DONE; +extern const uint8_t STATUS_LED_MASK; +extern const uint8_t STATUS_LED_OK; +extern const uint8_t STATUS_LED_WARNING; +extern const uint8_t STATUS_LED_ERROR; enum class RetryResult { DONE, RETRY }; -extern const uint32_t WARN_IF_BLOCKING_OVER_MS; +extern const uint16_t WARN_IF_BLOCKING_OVER_MS; class Component { public: @@ -110,7 +111,32 @@ class Component { virtual void on_shutdown() {} virtual void on_safe_shutdown() {} - uint32_t get_component_state() const; + /** Called during teardown to allow component to gracefully finish operations. + * + * @return true if teardown is complete, false if more time is needed + */ + virtual bool teardown() { return true; } + + /** Called after teardown is complete to power down hardware. + * + * This is called after all components have finished their teardown process, + * making it safe to power down hardware like ethernet PHY. + */ + virtual void on_powerdown() {} + + uint8_t get_component_state() const; + + /** Reset this component back to the construction state to allow setup to run again. + * + * This can be used by components that have recoverable failures to attempt setup again. + */ + void reset_to_construction_state(); + + /** Check if this component has completed setup and is in the loop state. + * + * @return True if in loop state, false otherwise. + */ + bool is_in_loop_state() const; /** Mark this component as failed. Any future timeouts/intervals/setup/loop will no longer be called. * @@ -125,6 +151,47 @@ class Component { this->mark_failed(); } + /** Disable this component's loop. The loop() method will no longer be called. + * + * This is useful for components that only need to run for a certain period of time + * or when inactive, saving CPU cycles. + * + * @note Components should call this->disable_loop() on themselves, not on other components. + * This ensures the component's state is properly updated along with the loop partition. + */ + void disable_loop(); + + /** Enable this component's loop. The loop() method will be called normally. + * + * This is useful for components that transition between active and inactive states + * and need to re-enable their loop() method when becoming active again. + * + * @note Components should call this->enable_loop() on themselves, not on other components. + * This ensures the component's state is properly updated along with the loop partition. + */ + void enable_loop(); + + /** Thread and ISR-safe version of enable_loop() that can be called from any context. + * + * This method defers the actual enable via enable_pending_loops_ to the main loop, + * making it safe to call from ISR handlers, timer callbacks, other threads, + * or any interrupt context. + * + * @note The actual loop enabling will happen on the next main loop iteration. + * @note Only one pending enable request is tracked per component. + * @note There is no disable_loop_soon_any_context() on purpose - it would race + * against enable calls and synchronization would get too complex + * to provide a safe version that would work for each component. + * + * Use disable_loop() from the main thread only. + * + * If you need to disable the loop from ISR, carefully implement + * it in the component itself, with an ISR safe approach, and call + * disable_loop() in its next ::loop() iteration. Implementations + * will need to carefully consider all possible race conditions. + */ + void enable_loop_soon_any_context(); + bool is_failed() const; bool is_ready() const; @@ -285,11 +352,18 @@ class Component { /// Cancel a defer callback using the specified name, name must not be empty. bool cancel_defer(const std::string &name); // NOLINT - uint32_t component_state_{0x0000}; ///< State of this component. + // Ordered for optimal packing on 32-bit systems float setup_priority_override_{NAN}; const char *component_source_{nullptr}; - uint32_t warn_if_blocking_over_{WARN_IF_BLOCKING_OVER_MS}; - std::string error_message_{}; + const char *error_message_{nullptr}; + uint16_t warn_if_blocking_over_{WARN_IF_BLOCKING_OVER_MS}; ///< Warn if blocked for this many ms (max 65.5s) + /// State of this component - each bit has a purpose: + /// Bits 0-1: Component state (0x00=CONSTRUCTION, 0x01=SETUP, 0x02=LOOP, 0x03=FAILED) + /// Bit 2: STATUS_LED_WARNING + /// Bit 3: STATUS_LED_ERROR + /// Bits 4-7: Unused - reserved for future expansion (50% of the bits are free) + uint8_t component_state_{0x00}; + volatile bool pending_enable_loop_{false}; ///< ISR-safe flag for enable_loop_soon_any_context }; /** This class simplifies creating components that periodically check a state. diff --git a/esphome/core/config.py b/esphome/core/config.py index 22bb7b0472..484f0dbac0 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -345,6 +345,12 @@ async def _add_automations(config): await automation.build_automation(trigger, [], conf) +@coroutine_with_priority(-100.0) +async def _add_platform_reserves() -> None: + for platform_name, count in sorted(CORE.platform_counts.items()): + cg.add(cg.RawStatement(f"App.reserve_{platform_name}({count});"), prepend=True) + + @coroutine_with_priority(100.0) async def to_code(config): cg.add_global(cg.global_ns.namespace("esphome").using) @@ -363,6 +369,12 @@ async def to_code(config): config[CONF_NAME_ADD_MAC_SUFFIX], ) ) + # Reserve space for components to avoid reallocation during registration + cg.add( + cg.RawStatement(f"App.reserve_components({len(CORE.component_ids)});"), + ) + + CORE.add_job(_add_platform_reserves) CORE.add_job(_add_automations, config) diff --git a/esphome/core/controller.cpp b/esphome/core/controller.cpp index d6d98a4316..f7ff5a9734 100644 --- a/esphome/core/controller.cpp +++ b/esphome/core/controller.cpp @@ -7,8 +7,10 @@ namespace esphome { void Controller::setup_controller(bool include_internal) { #ifdef USE_BINARY_SENSOR for (auto *obj : App.get_binary_sensors()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj](bool state) { this->on_binary_sensor_update(obj, state); }); + if (include_internal || !obj->is_internal()) { + obj->add_full_state_callback( + [this, obj](optional previous, optional state) { this->on_binary_sensor_update(obj); }); + } } #endif #ifdef USE_FAN diff --git a/esphome/core/controller.h b/esphome/core/controller.h index 39e0b2ba26..1a5b9ea6b4 100644 --- a/esphome/core/controller.h +++ b/esphome/core/controller.h @@ -71,7 +71,7 @@ class Controller { public: void setup_controller(bool include_internal = false); #ifdef USE_BINARY_SENSOR - virtual void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state){}; + virtual void on_binary_sensor_update(binary_sensor::BinarySensor *obj){}; #endif #ifdef USE_FAN virtual void on_fan_update(fan::Fan *obj){}; diff --git a/esphome/core/defines.h b/esphome/core/defines.h index f07a25f2fc..32625a6a04 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -137,6 +137,7 @@ #define USE_ESP32_BLE_CLIENT #define USE_ESP32_BLE_SERVER #define USE_ESP32_CAMERA +#define USE_I2C #define USE_IMPROV #define USE_MICROPHONE #define USE_PSRAM @@ -150,14 +151,17 @@ #define USE_WIFI_11KV_SUPPORT #ifdef USE_ARDUINO -#define USE_ARDUINO_VERSION_CODE VERSION_CODE(2, 0, 5) +#define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 1, 3) #define USE_ETHERNET #endif #ifdef USE_ESP_IDF -#define USE_ESP_IDF_VERSION_CODE VERSION_CODE(5, 1, 6) +#define USE_ESP_IDF_VERSION_CODE VERSION_CODE(5, 3, 2) #define USE_MICRO_WAKE_WORD #define USE_MICRO_WAKE_WORD_VAD +#if defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32H2) +#define USE_OPENTHREAD +#endif #endif #if defined(USE_ESP32_VARIANT_ESP32S2) @@ -177,6 +181,7 @@ #define USE_CAPTIVE_PORTAL #define USE_ESP8266_PREFERENCES_FLASH #define USE_HTTP_REQUEST_ESP8266_HTTPS +#define USE_I2C #define USE_SOCKET_IMPL_LWIP_TCP #define USE_SPI @@ -193,6 +198,7 @@ #ifdef USE_RP2040 #define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 3, 0) +#define USE_I2C #define USE_LOGGER_USB_CDC #define USE_SOCKET_IMPL_LWIP_TCP #define USE_SPI diff --git a/esphome/core/entity_base.cpp b/esphome/core/entity_base.cpp index 725a8569a3..791b6615a1 100644 --- a/esphome/core/entity_base.cpp +++ b/esphome/core/entity_base.cpp @@ -12,20 +12,12 @@ void EntityBase::set_name(const char *name) { this->name_ = StringRef(name); if (this->name_.empty()) { this->name_ = StringRef(App.get_friendly_name()); - this->has_own_name_ = false; + this->flags_.has_own_name = false; } else { - this->has_own_name_ = true; + this->flags_.has_own_name = true; } } -// Entity Internal -bool EntityBase::is_internal() const { return this->internal_; } -void EntityBase::set_internal(bool internal) { this->internal_ = internal; } - -// Entity Disabled by Default -bool EntityBase::is_disabled_by_default() const { return this->disabled_by_default_; } -void EntityBase::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; } - // Entity Icon std::string EntityBase::get_icon() const { if (this->icon_c_str_ == nullptr) { @@ -35,14 +27,10 @@ std::string EntityBase::get_icon() const { } void EntityBase::set_icon(const char *icon) { this->icon_c_str_ = icon; } -// Entity Category -EntityCategory EntityBase::get_entity_category() const { return this->entity_category_; } -void EntityBase::set_entity_category(EntityCategory entity_category) { this->entity_category_ = entity_category; } - // Entity Object ID std::string EntityBase::get_object_id() const { // Check if `App.get_friendly_name()` is constant or dynamic. - if (!this->has_own_name_ && App.is_name_add_mac_suffix_enabled()) { + if (!this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled()) { // `App.get_friendly_name()` is dynamic. return str_sanitize(str_snake_case(App.get_friendly_name())); } else { @@ -61,7 +49,7 @@ void EntityBase::set_object_id(const char *object_id) { // Calculate Object ID Hash from Entity Name void EntityBase::calc_object_id_() { // Check if `App.get_friendly_name()` is constant or dynamic. - if (!this->has_own_name_ && App.is_name_add_mac_suffix_enabled()) { + if (!this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled()) { // `App.get_friendly_name()` is dynamic. const auto object_id = str_sanitize(str_snake_case(App.get_friendly_name())); // FNV-1 hash diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index 60db74e616..165ae0e7cd 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -3,6 +3,8 @@ #include #include #include "string_ref.h" +#include "helpers.h" +#include "log.h" namespace esphome { @@ -20,7 +22,7 @@ class EntityBase { void set_name(const char *name); // Get whether this Entity has its own name or it should use the device friendly_name. - bool has_own_name() const { return this->has_own_name_; } + bool has_own_name() const { return this->flags_.has_own_name; } // Get the sanitized name of this Entity as an ID. std::string get_object_id() const; @@ -29,19 +31,21 @@ class EntityBase { // Get the unique Object ID of this Entity uint32_t get_object_id_hash(); - // Get/set whether this Entity should be hidden from outside of ESPHome - bool is_internal() const; - void set_internal(bool internal); + // Get/set whether this Entity should be hidden outside ESPHome + bool is_internal() const { return this->flags_.internal; } + void set_internal(bool internal) { this->flags_.internal = internal; } // Check if this object is declared to be disabled by default. // That means that when the device gets added to Home Assistant (or other clients) it should // not be added to the default view by default, and a user action is necessary to manually add it. - bool is_disabled_by_default() const; - void set_disabled_by_default(bool disabled_by_default); + bool is_disabled_by_default() const { return this->flags_.disabled_by_default; } + void set_disabled_by_default(bool disabled_by_default) { this->flags_.disabled_by_default = disabled_by_default; } // Get/set the entity category. - EntityCategory get_entity_category() const; - void set_entity_category(EntityCategory entity_category); + EntityCategory get_entity_category() const { return static_cast(this->flags_.entity_category); } + void set_entity_category(EntityCategory entity_category) { + this->flags_.entity_category = static_cast(entity_category); + } // Get/set this entity's icon std::string get_icon() const; @@ -51,6 +55,12 @@ class EntityBase { uint32_t get_device_uid() const { return this->device_uid_; } void set_device_uid(const uint32_t device_uid) { this->device_uid_ = device_uid; } + // Check if this entity has state + bool has_state() const { return this->flags_.has_state; } + + // Set has_state - for components that need to manually set this + void set_has_state(bool state) { this->flags_.has_state = state; } + protected: /// The hash_base() function has been deprecated. It is kept in this /// class for now, to prevent external components from not compiling. @@ -60,12 +70,18 @@ class EntityBase { StringRef name_; const char *object_id_c_str_{nullptr}; const char *icon_c_str_{nullptr}; - uint32_t object_id_hash_; - bool has_own_name_{false}; - bool internal_{false}; - bool disabled_by_default_{false}; - EntityCategory entity_category_{ENTITY_CATEGORY_NONE}; + uint32_t object_id_hash_{}; uint32_t device_uid_{}; + + // Bit-packed flags to save memory (1 byte instead of 5) + struct EntityFlags { + uint8_t has_own_name : 1; + uint8_t internal : 1; + uint8_t disabled_by_default : 1; + uint8_t has_state : 1; + uint8_t entity_category : 2; // Supports up to 4 categories + uint8_t reserved : 2; // Reserved for future use + } flags_{}; }; class EntityBase_DeviceClass { // NOLINT(readability-identifier-naming) @@ -90,4 +106,58 @@ class EntityBase_UnitOfMeasurement { // NOLINT(readability-identifier-naming) const char *unit_of_measurement_{nullptr}; ///< Unit of measurement override }; +/** + * An entity that has a state. + * @tparam T The type of the state + */ +template class StatefulEntityBase : public EntityBase { + public: + virtual bool has_state() const { return this->state_.has_value(); } + virtual const T &get_state() const { return this->state_.value(); } + virtual T get_state_default(T default_value) const { return this->state_.value_or(default_value); } + void invalidate_state() { this->set_state_({}); } + + void add_full_state_callback(std::function previous, optional current)> &&callback) { + if (this->full_state_callbacks_ == nullptr) + this->full_state_callbacks_ = new CallbackManager previous, optional current)>(); // NOLINT + this->full_state_callbacks_->add(std::move(callback)); + } + void add_on_state_callback(std::function &&callback) { + if (this->state_callbacks_ == nullptr) + this->state_callbacks_ = new CallbackManager(); // NOLINT + this->state_callbacks_->add(std::move(callback)); + } + + void set_trigger_on_initial_state(bool trigger_on_initial_state) { + this->trigger_on_initial_state_ = trigger_on_initial_state; + } + + protected: + optional state_{}; + /** + * Set a new state for this entity. This will trigger callbacks only if the new state is different from the previous. + * + * @param state The new state. + * @return True if the state was changed, false if it was the same as before. + */ + bool set_state_(const optional &state) { + if (this->state_ != state) { + // call the full state callbacks with the previous and new state + if (this->full_state_callbacks_ != nullptr) + this->full_state_callbacks_->call(this->state_, state); + // trigger legacy callbacks only if the new state is valid and either the trigger on initial state is enabled or + // the previous state was valid + auto had_state = this->has_state(); + this->state_ = state; + if (this->state_callbacks_ != nullptr && state.has_value() && (this->trigger_on_initial_state_ || had_state)) + this->state_callbacks_->call(state.value()); + return true; + } + return false; + } + bool trigger_on_initial_state_{true}; + // callbacks with full state and previous state + CallbackManager previous, optional current)> *full_state_callbacks_{}; + CallbackManager *state_callbacks_{}; +}; } // namespace esphome diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 36bc7f949b..ec79cb8bbb 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -30,7 +30,6 @@ #elif defined(USE_ESP_IDF) #include #include -#include "esp_mac.h" #include "esp_random.h" #include "esp_system.h" #elif defined(USE_RP2040) @@ -45,6 +44,7 @@ #endif #ifdef USE_ESP32 #include "rom/crc.h" +#include "esp_mac.h" #include "esp_efuse.h" #include "esp_efuse_table.h" #endif diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 4212aeca98..477f260bf0 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -192,15 +192,15 @@ bool random_bytes(uint8_t *data, size_t len); constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb) { return (static_cast(msb) << 8) | (static_cast(lsb)); } +/// Encode a 24-bit value given three bytes in most to least significant byte order. +constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3) { + return (static_cast(byte1) << 16) | (static_cast(byte2) << 8) | (static_cast(byte3)); +} /// Encode a 32-bit value given four bytes in most to least significant byte order. constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4) { return (static_cast(byte1) << 24) | (static_cast(byte2) << 16) | (static_cast(byte3) << 8) | (static_cast(byte4)); } -/// Encode a 24-bit value given three bytes in most to least significant byte order. -constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3) { - return ((static_cast(byte1) << 16) | (static_cast(byte2) << 8) | (static_cast(byte3))); -} /// Encode a value from its constituent bytes (from most to least significant) in an array with length sizeof(T). template::value, int> = 0> @@ -438,7 +438,7 @@ template::value, int> = 0> std::stri } /// Return values for parse_on_off(). -enum ParseOnOffState { +enum ParseOnOffState : uint8_t { PARSE_NONE = 0, PARSE_ON, PARSE_OFF, diff --git a/esphome/core/log.h b/esphome/core/log.h index adf72e4bac..cade6a74c1 100644 --- a/esphome/core/log.h +++ b/esphome/core/log.h @@ -165,6 +165,8 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #define YESNO(b) ((b) ? "YES" : "NO") #define ONOFF(b) ((b) ? "ON" : "OFF") #define TRUEFALSE(b) ((b) ? "TRUE" : "FALSE") +// for use with optional values +#define ONOFFMAYBE(b) (((b).has_value()) ? ONOFF((b).value()) : "UNKNOWN") // Helper class that identifies strings that may be stored in flash storage (similar to Arduino's __FlashStringHelper) struct LogString; diff --git a/esphome/core/log_const_en.h b/esphome/core/log_const_en.h index 75e71ecd81..ccb1f446e4 100644 --- a/esphome/core/log_const_en.h +++ b/esphome/core/log_const_en.h @@ -1,11 +1,4 @@ #pragma once -#include "defines.h" - -#ifdef USE_ESP8266 #define ESP_LOG_MSG_COMM_FAIL "Communication failed" #define ESP_LOG_MSG_COMM_FAIL_FOR "Communication failed for '%s'" -#else -constexpr const char *const ESP_LOG_MSG_COMM_FAIL = "Communication failed"; -constexpr const char *const ESP_LOG_MSG_COMM_FAIL_FOR = "Communication failed for '%s'"; -#endif diff --git a/esphome/core/optional.h b/esphome/core/optional.h index 591bc7aa68..7f9db7817d 100644 --- a/esphome/core/optional.h +++ b/esphome/core/optional.h @@ -52,6 +52,11 @@ template class optional { // NOLINT reset(); return *this; } + bool operator==(optional const &rhs) const { + if (has_value() && rhs.has_value()) + return value() == rhs.value(); + return !has_value() && !rhs.has_value(); + } template optional &operator=(optional const &other) { has_value_ = other.has_value(); diff --git a/esphome/core/ring_buffer.cpp b/esphome/core/ring_buffer.cpp index f779531263..b77a02b2a7 100644 --- a/esphome/core/ring_buffer.cpp +++ b/esphome/core/ring_buffer.cpp @@ -14,7 +14,7 @@ static const char *const TAG = "ring_buffer"; RingBuffer::~RingBuffer() { if (this->handle_ != nullptr) { vRingbufferDelete(this->handle_); - RAMAllocator allocator(RAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; allocator.deallocate(this->storage_, this->size_); } } @@ -24,7 +24,7 @@ std::unique_ptr RingBuffer::create(size_t len) { rb->size_ = len; - RAMAllocator allocator(RAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator; rb->storage_ = allocator.allocate(rb->size_); if (rb->storage_ == nullptr) { return nullptr; diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 2dea450ead..eed222c974 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -2,9 +2,9 @@ #include "application.h" #include "esphome/core/defines.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include #include diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index e2d067390d..4641f69bdd 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -579,13 +579,13 @@ def new_Pvariable(id_: ID, *args: SafeExpType) -> Pvariable: return Pvariable(id_, rhs) -def add(expression: Expression | Statement): +def add(expression: Expression | Statement, prepend: bool = False): """Add an expression to the codegen section. After this is called, the given given expression will show up in the setup() function after this has been called. """ - CORE.add(expression) + CORE.add(expression, prepend) def add_global(expression: SafeExpType | Statement, prepend: bool = False): @@ -608,6 +608,17 @@ def add_build_flag(build_flag: str): CORE.add_build_flag(build_flag) +def add_build_unflag(build_unflag: str) -> None: + """Add a global build unflag to the compiler flags.""" + CORE.add_build_unflag(build_unflag) + + +def set_cpp_standard(standard: str) -> None: + """Set C++ standard with compiler flag `-std={standard}`.""" + CORE.add_build_unflag("-std=gnu++11") + CORE.add_build_flag(f"-std={standard}") + + def add_define(name: str, value: SafeExpType = None): """Add a global define to the auto-generated defines.h file. diff --git a/esphome/cpp_types.py b/esphome/cpp_types.py index dab993f87f..a0dd62cb4e 100644 --- a/esphome/cpp_types.py +++ b/esphome/cpp_types.py @@ -29,7 +29,9 @@ Component = esphome_ns.class_("Component") ComponentPtr = Component.operator("ptr") PollingComponent = esphome_ns.class_("PollingComponent", Component) Application = esphome_ns.class_("Application") -optional = esphome_ns.class_("optional") +# Create optional with explicit namespace to avoid ambiguity with std::optional +# The generated code will use esphome::optional instead of just optional +optional = global_ns.namespace("esphome").class_("optional") arduino_json_ns = global_ns.namespace("ArduinoJson") JsonObject = arduino_json_ns.class_("JsonObject") JsonObjectConst = arduino_json_ns.class_("JsonObjectConst") diff --git a/esphome/log.py b/esphome/log.py index 7e69a2fef8..0e91eb32c2 100644 --- a/esphome/log.py +++ b/esphome/log.py @@ -59,7 +59,13 @@ class ESPHomeLogFormatter(logging.Formatter): "ERROR": AnsiFore.RED.value, "CRITICAL": AnsiFore.RED.value, }.get(record.levelname, "") - return f"{prefix}{formatted}{AnsiStyle.RESET_ALL.value}" + message = f"{prefix}{formatted}{AnsiStyle.RESET_ALL.value}" + if CORE.dashboard: + try: + message = message.replace("\033", "\\033") + except UnicodeEncodeError: + pass + return message def setup_log( diff --git a/esphome/pins.py b/esphome/pins.py index 724cd25d82..0dfd5a245b 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -1,5 +1,8 @@ +from collections.abc import Callable from functools import reduce +from logging import Logger import operator +from typing import Any import esphome.config_validation as cv from esphome.const import ( @@ -15,6 +18,7 @@ from esphome.const import ( CONF_PULLUP, ) from esphome.core import CORE +from esphome.cpp_generator import MockObjClass class PinRegistry(dict): @@ -262,7 +266,7 @@ internal_gpio_input_pullup_pin_number = _internal_number_creator( ) -def check_strapping_pin(conf, strapping_pin_list, logger): +def check_strapping_pin(conf, strapping_pin_list: set[int], logger: Logger): num = conf[CONF_NUMBER] if num in strapping_pin_list and not conf.get(CONF_IGNORE_STRAPPING_WARNING): logger.warning( @@ -291,11 +295,11 @@ def gpio_validate_modes(value): def gpio_base_schema( - pin_type, - number_validator, + pin_type: MockObjClass, + number_validator: Callable[[Any], Any], modes=GPIO_STANDARD_MODES, - mode_validator=gpio_validate_modes, - invertable=True, + mode_validator: Callable[[Any], Any] = gpio_validate_modes, + invertible: bool = True, ): """ Generate a base gpio pin schema @@ -303,7 +307,7 @@ def gpio_base_schema( :param number_validator: A validator for the pin number :param modes: The available modes, default is all standard modes :param mode_validator: A validator function for the pin mode - :param invertable: If the pin supports hardware inversion + :param invertible: If the pin supports hardware inversion :return: A schema for the pin """ mode_default = len(modes) == 1 @@ -328,7 +332,7 @@ def gpio_base_schema( } ) - if invertable: + if invertible: return schema.extend({cv.Optional(CONF_INVERTED, default=False): cv.boolean}) return schema diff --git a/esphome/storage_json.py b/esphome/storage_json.py index fa9fe43d4d..b69dc2dd3f 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -46,15 +46,16 @@ class StorageJSON: storage_version: int, name: str, friendly_name: str, - comment: str, - esphome_version: str, + comment: str | None, + esphome_version: str | None, src_version: int | None, address: str, web_port: int | None, target_platform: str, - build_path: str, - firmware_bin_path: str, + build_path: str | None, + firmware_bin_path: str | None, loaded_integrations: set[str], + loaded_platforms: set[str], no_mdns: bool, framework: str | None = None, core_platform: str | None = None, @@ -86,6 +87,8 @@ class StorageJSON: self.firmware_bin_path = firmware_bin_path # A set of strings of names of loaded integrations self.loaded_integrations = loaded_integrations + # A set of strings for platform/integration combos + self.loaded_platforms = loaded_platforms # Is mDNS disabled self.no_mdns = no_mdns # The framework used to compile the firmware @@ -107,6 +110,7 @@ class StorageJSON: "build_path": self.build_path, "firmware_bin_path": self.firmware_bin_path, "loaded_integrations": sorted(self.loaded_integrations), + "loaded_platforms": sorted(self.loaded_platforms), "no_mdns": self.no_mdns, "framework": self.framework, "core_platform": self.core_platform, @@ -138,6 +142,7 @@ class StorageJSON: build_path=esph.build_path, firmware_bin_path=esph.firmware_bin, loaded_integrations=esph.loaded_integrations, + loaded_platforms=esph.loaded_platforms, no_mdns=( CONF_MDNS in esph.config and CONF_DISABLED in esph.config[CONF_MDNS] @@ -164,6 +169,7 @@ class StorageJSON: build_path=None, firmware_bin_path=None, loaded_integrations=set(), + loaded_platforms=set(), no_mdns=False, framework=None, core_platform=platform.lower(), @@ -187,6 +193,7 @@ class StorageJSON: build_path = storage.get("build_path") firmware_bin_path = storage.get("firmware_bin_path") loaded_integrations = set(storage.get("loaded_integrations", [])) + loaded_platforms = set(storage.get("loaded_platforms", [])) no_mdns = storage.get("no_mdns", False) framework = storage.get("framework") core_platform = storage.get("core_platform") @@ -203,6 +210,7 @@ class StorageJSON: build_path, firmware_bin_path, loaded_integrations, + loaded_platforms, no_mdns, framework, core_platform, @@ -252,7 +260,7 @@ class EsphomeStorageJSON: def last_update_check(self, new: datetime) -> None: self.last_update_check_str = new.strftime("%Y-%m-%dT%H:%M:%S") - def to_json(self) -> dict: + def to_json(self) -> str: return f"{json.dumps(self.as_dict(), indent=2)}\n" def save(self, path: str) -> None: diff --git a/esphome/wizard.py b/esphome/wizard.py index ca987304e2..7b4d87be63 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -67,20 +67,6 @@ esp8266: """ ESP32_CONFIG = """ -esp32: - board: {board} - framework: - type: arduino -""" - -ESP32S2_CONFIG = """ -esp32: - board: {board} - framework: - type: esp-idf -""" - -ESP32C3_CONFIG = """ esp32: board: {board} framework: @@ -105,8 +91,6 @@ rtl87xx: HARDWARE_BASE_CONFIGS = { "ESP8266": ESP8266_CONFIG, "ESP32": ESP32_CONFIG, - "ESP32S2": ESP32S2_CONFIG, - "ESP32C3": ESP32C3_CONFIG, "RP2040": RP2040_CONFIG, "BK72XX": BK72XX_CONFIG, "RTL87XX": RTL87XX_CONFIG, diff --git a/esphome/writer.py b/esphome/writer.py index 0452098e24..7a5089e384 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -107,7 +107,10 @@ def storage_should_clean(old: StorageJSON, new: StorageJSON) -> bool: return True if old.build_path != new.build_path: return True - if old.loaded_integrations != new.loaded_integrations: + if ( + old.loaded_integrations != new.loaded_integrations + or old.loaded_platforms != new.loaded_platforms + ): if new.core_platform == PLATFORM_ESP32: from esphome.components.esp32 import FRAMEWORK_ESP_IDF @@ -150,6 +153,9 @@ def get_ini_content(): # Sort to avoid changing build flags order CORE.add_platformio_option("build_flags", sorted(CORE.build_flags)) + # Sort to avoid changing build unflags order + CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags)) + content = "[platformio]\n" content += f"description = ESPHome {__version__}\n" diff --git a/platformio.ini b/platformio.ini index 0e7bd80bc6..f67226d657 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,7 +34,6 @@ build_flags = [common] lib_deps = esphome/noise-c@0.1.4 ; api - makuna/NeoPixelBus@2.7.3 ; neopixelbus improv/Improv@1.2.4 ; improv_serial / esp32_improv bblanchon/ArduinoJson@6.18.5 ; json wjtje/qr-code-generator-library@1.7.0 ; qr_code @@ -48,6 +47,9 @@ lib_deps = lvgl/lvgl@8.4.0 ; lvgl build_flags = -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE + -std=gnu++17 +build_unflags = + -std=gnu++11 src_filter = +<./> +<../tests/dummy_main.cpp> @@ -62,17 +64,19 @@ lib_deps = SPI ; spi (Arduino built-in) Wire ; i2c (Arduino built-int) heman/AsyncMqttClient-esphome@1.0.0 ; mqtt - esphome/ESPAsyncWebServer-esphome@3.3.0 ; web_server_base + ESP32Async/ESPAsyncWebServer@3.7.8 ; web_server_base fastled/FastLED@3.9.16 ; fastled_base mikalhart/TinyGPSPlus@1.1.0 ; gps freekode/TM1651@1.0.1 ; tm1651 glmnet/Dsmr@0.7 ; dsmr rweather/Crypto@0.4.0 ; dsmr dudanov/MideaUART@1.1.9 ; midea - tonia/HeatpumpIR@1.0.32 ; heatpumpir + tonia/HeatpumpIR@1.0.35 ; heatpumpir build_flags = ${common.build_flags} -DUSE_ARDUINO +build_unflags = + ${common.build_unflags} ; This are common settings for all IDF-framework based environments. [common:idf] @@ -80,6 +84,8 @@ extends = common build_flags = ${common.build_flags} -DUSE_ESP_IDF +build_unflags = + ${common.build_unflags} ; This are common settings for the ESP8266 using Arduino. [common:esp8266-arduino] @@ -93,7 +99,8 @@ lib_deps = ${common:arduino.lib_deps} ESP8266WiFi ; wifi (Arduino built-in) Update ; ota (Arduino built-in) - esphome/ESPAsyncTCP-esphome@2.0.0 ; async_tcp + ESP32Async/ESPAsyncTCP@2.0.0 ; async_tcp + makuna/NeoPixelBus@2.7.3 ; neopixelbus ESP8266HTTPClient ; http_request (Arduino built-in) ESP8266mDNS ; mdns (Arduino built-in) DNSServer ; captive_portal (Arduino built-in) @@ -104,29 +111,34 @@ build_flags = -Wno-nonnull-compare -DUSE_ESP8266 -DUSE_ESP8266_FRAMEWORK_ARDUINO +build_unflags = + ${common.build_unflags} extra_scripts = post:esphome/components/esp8266/post_build.py.script ; This are common settings for the ESP32 (all variants) using Arduino. [common:esp32-arduino] extends = common:arduino -platform = platformio/espressif32@5.4.0 +platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.13/platform-espressif32.zip platform_packages = - platformio/framework-arduinoespressif32@~3.20005.0 + pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/3.1.3/esp32-3.1.3.zip framework = arduino lib_deps = ; order matters with lib-deps; some of the libs in common:arduino.lib_deps ; don't declare built-in libraries as dependencies, so they have to be declared first FS ; web_server_base (Arduino built-in) + Networking ; wifi,web_server_base,ethernet (Arduino built-in) WiFi ; wifi,web_server_base,ethernet (Arduino built-in) Update ; ota,web_server_base (Arduino built-in) ${common:arduino.lib_deps} - esphome/AsyncTCP-esphome@2.1.4 ; async_tcp - WiFiClientSecure ; http_request,nextion (Arduino built-in) + ESP32Async/AsyncTCP@3.4.4 ; async_tcp + NetworkClientSecure ; http_request,nextion (Arduino built-in) HTTPClient ; http_request,nextion (Arduino built-in) ESPmDNS ; mdns (Arduino built-in) + ESP32 Async UDP ; captive_portal (Arduino built-in) DNSServer ; captive_portal (Arduino built-in) - esphome/ESP32-audioI2S@2.2.0 ; i2s_audio + makuna/NeoPixelBus@2.8.0 ; neopixelbus + esphome/ESP32-audioI2S@2.3.0 ; i2s_audio droscy/esp_wireguard@0.4.2 ; wireguard esphome/esp-audio-libs@1.1.4 ; audio @@ -135,6 +147,8 @@ build_flags = -DUSE_ESP32 -DUSE_ESP32_FRAMEWORK_ARDUINO -DAUDIO_NO_SD_FS ; i2s_audio +build_unflags = + ${common.build_unflags} extra_scripts = post:esphome/components/esp32/post_build.py.script ; This are common settings for the ESP32 (all variants) using IDF. @@ -155,6 +169,8 @@ build_flags = -Wno-nonnull-compare -DUSE_ESP32 -DUSE_ESP32_FRAMEWORK_ESP_IDF +build_unflags = + ${common.build_unflags} extra_scripts = post:esphome/components/esp32/post_build.py.script ; This are common settings for the ESP32 using the latest ESP-IDF version. @@ -181,17 +197,21 @@ build_flags = ${common:arduino.build_flags} -DUSE_RP2040 -DUSE_RP2040_FRAMEWORK_ARDUINO +build_unflags = + ${common.build_unflags} ; This are common settings for the LibreTiny (all variants) using Arduino. [common:libretiny-arduino] extends = common:arduino -platform = libretiny +platform = libretiny@1.9.1 framework = arduino lib_deps = droscy/esp_wireguard@0.4.2 ; wireguard build_flags = ${common:arduino.build_flags} -DUSE_LIBRETINY +build_unflags = + ${common.build_unflags} build_src_flags = -include Arduino.h ; This is the common settings for the nRF52 using Zephyr. @@ -224,6 +244,8 @@ board = nodemcuv2 build_flags = ${common:esp8266-arduino.build_flags} ${flags:runtime.build_flags} +build_unflags = + ${common.build_unflags} [env:esp8266-arduino-tidy] extends = common:esp8266-arduino @@ -231,6 +253,8 @@ board = nodemcuv2 build_flags = ${common:esp8266-arduino.build_flags} ${flags:clangtidy.build_flags} +build_unflags = + ${common.build_unflags} ;;;;;;;; ESP32 ;;;;;;;; @@ -242,6 +266,8 @@ build_flags = ${common:esp32-arduino.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32 +build_unflags = + ${common.build_unflags} [env:esp32-arduino-tidy] extends = common:esp32-arduino @@ -250,6 +276,8 @@ build_flags = ${common:esp32-arduino.build_flags} ${flags:clangtidy.build_flags} -DUSE_ESP32_VARIANT_ESP32 +build_unflags = + ${common.build_unflags} [env:esp32-idf] extends = common:esp32-idf @@ -259,6 +287,8 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32 +build_unflags = + ${common.build_unflags} [env:esp32-idf-5_3] extends = common:esp32-idf-5_3 @@ -268,6 +298,8 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32 +build_unflags = + ${common.build_unflags} [env:esp32-idf-tidy] extends = common:esp32-idf @@ -277,6 +309,8 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:clangtidy.build_flags} -DUSE_ESP32_VARIANT_ESP32 +build_unflags = + ${common.build_unflags} ;;;;;;;; ESP32-C3 ;;;;;;;; @@ -287,6 +321,8 @@ build_flags = ${common:esp32-arduino.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32C3 +build_unflags = + ${common.build_unflags} [env:esp32c3-arduino-tidy] extends = common:esp32-arduino @@ -295,6 +331,8 @@ build_flags = ${common:esp32-arduino.build_flags} ${flags:clangtidy.build_flags} -DUSE_ESP32_VARIANT_ESP32C3 +build_unflags = + ${common.build_unflags} [env:esp32c3-idf] extends = common:esp32-idf @@ -304,6 +342,8 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32C3 +build_unflags = + ${common.build_unflags} [env:esp32c3-idf-5_3] extends = common:esp32-idf-5_3 @@ -313,6 +353,8 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32C3 +build_unflags = + ${common.build_unflags} [env:esp32c3-idf-tidy] extends = common:esp32-idf @@ -322,6 +364,8 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:clangtidy.build_flags} -DUSE_ESP32_VARIANT_ESP32C3 +build_unflags = + ${common.build_unflags} ;;;;;;;; ESP32-C6 ;;;;;;;; @@ -343,6 +387,8 @@ build_flags = ${common:esp32-arduino.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32S2 +build_unflags = + ${common.build_unflags} [env:esp32s2-arduino-tidy] extends = common:esp32-arduino @@ -351,6 +397,8 @@ build_flags = ${common:esp32-arduino.build_flags} ${flags:clangtidy.build_flags} -DUSE_ESP32_VARIANT_ESP32S2 +build_unflags = + ${common.build_unflags} [env:esp32s2-idf] extends = common:esp32-idf @@ -360,6 +408,8 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32S2 +build_unflags = + ${common.build_unflags} [env:esp32s2-idf-5_3] extends = common:esp32-idf-5_3 @@ -369,6 +419,8 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32S2 +build_unflags = + ${common.build_unflags} [env:esp32s2-idf-tidy] extends = common:esp32-idf @@ -378,6 +430,8 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:clangtidy.build_flags} -DUSE_ESP32_VARIANT_ESP32S2 +build_unflags = + ${common.build_unflags} ;;;;;;;; ESP32-S3 ;;;;;;;; @@ -388,6 +442,8 @@ build_flags = ${common:esp32-arduino.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32S3 +build_unflags = + ${common.build_unflags} [env:esp32s3-arduino-tidy] extends = common:esp32-arduino @@ -396,6 +452,8 @@ build_flags = ${common:esp32-arduino.build_flags} ${flags:clangtidy.build_flags} -DUSE_ESP32_VARIANT_ESP32S3 +build_unflags = + ${common.build_unflags} [env:esp32s3-idf] extends = common:esp32-idf @@ -405,6 +463,8 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32S3 +build_unflags = + ${common.build_unflags} [env:esp32s3-idf-5_3] extends = common:esp32-idf-5_3 @@ -414,6 +474,8 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32S3 +build_unflags = + ${common.build_unflags} [env:esp32s3-idf-tidy] extends = common:esp32-idf @@ -423,6 +485,20 @@ build_flags = ${common:esp32-idf.build_flags} ${flags:clangtidy.build_flags} -DUSE_ESP32_VARIANT_ESP32S3 +build_unflags = + ${common.build_unflags} + +;;;;;;;; ESP32-P4 ;;;;;;;; + +[env:esp32p4-idf] +extends = common:esp32-idf +board = esp32-p4-evboard + +board_build.esp-idf.sdkconfig_path = .temp/sdkconfig-esp32p4-idf +build_flags = + ${common:esp32-idf.build_flags} + ${flags:runtime.build_flags} + -DUSE_ESP32_VARIANT_ESP32P4 ;;;;;;;; RP2040 ;;;;;;;; @@ -432,6 +508,8 @@ board = rpipico build_flags = ${common:rp2040-arduino.build_flags} ${flags:runtime.build_flags} +build_unflags = + ${common.build_unflags} ;;;;;;;; LibreTiny ;;;;;;;; @@ -443,6 +521,8 @@ build_flags = ${flags:runtime.build_flags} -DUSE_BK72XX -DUSE_LIBRETINY_VARIANT_BK7231N +build_unflags = + ${common.build_unflags} [env:rtl87xxb-arduino] extends = common:libretiny-arduino @@ -452,6 +532,8 @@ build_flags = ${flags:runtime.build_flags} -DUSE_RTL87XX -DUSE_LIBRETINY_VARIANT_RTL8710B +build_unflags = + ${common.build_unflags} [env:rtl87xxc-arduino] extends = common:libretiny-arduino @@ -461,6 +543,8 @@ build_flags = ${flags:runtime.build_flags} -DUSE_RTL87XX -DUSE_LIBRETINY_VARIANT_RTL8720C +build_unflags = + ${common.build_unflags} [env:host] extends = common @@ -471,6 +555,8 @@ build_flags = ${common.build_flags} -DUSE_HOST -std=c++17 +build_unflags = + ${common.build_unflags} ;;;;;;;; nRF52 ;;;;;;;; @@ -480,6 +566,8 @@ board = adafruit_feather_nrf52840 build_flags = ${common:nrf52-zephyr.build_flags} ${flags:runtime.build_flags} +build_unflags = + ${common.build_unflags} [env:nrf52-tidy] extends = common:nrf52-zephyr @@ -487,3 +575,5 @@ board = adafruit_feather_nrf52840 build_flags = ${common:nrf52-zephyr.build_flags} ${flags:clangtidy.build_flags} +build_unflags = + ${common.build_unflags} diff --git a/pyproject.toml b/pyproject.toml index 4ee2d3a390..97b0df9eff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -120,12 +120,15 @@ select = [ ignore = [ "E501", # line too long + "PLC0415", # `import` should be at the top-level of a file "PLR0911", # Too many return statements ({returns} > {max_returns}) "PLR0912", # Too many branches ({branches} > {max_branches}) "PLR0913", # Too many arguments to function call ({c_args} > {max_args}) "PLR0915", # Too many statements ({statements} > {max_statements}) + "PLW1641", # Object does not implement `__hash__` method "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target + "UP038", # https://github.com/astral-sh/ruff/issues/7871 https://github.com/astral-sh/ruff/pull/16681 ] [tool.ruff.lint.isort] diff --git a/requirements.txt b/requirements.txt index 87319dbba0..76a58bf622 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,10 +13,10 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==4.8.1 click==8.1.7 esphome-dashboard==20250514.0 -aioesphomeapi==31.1.0 +aioesphomeapi==32.2.4 zeroconf==0.147.0 puremagic==1.29 -ruamel.yaml==0.18.11 # dashboard_import +ruamel.yaml==0.18.14 # dashboard_import esphome-glyphsets==0.2.0 pillow==10.4.0 cairosvg==2.8.2 diff --git a/requirements_test.txt b/requirements_test.txt index ebbc933622..9263d165ac 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,14 +1,14 @@ pylint==3.3.7 flake8==7.2.0 # also change in .pre-commit-config.yaml when updating -ruff==0.11.11 # also change in .pre-commit-config.yaml when updating +ruff==0.12.0 # also change in .pre-commit-config.yaml when updating pyupgrade==3.20.0 # also change in .pre-commit-config.yaml when updating pre-commit # Unit tests -pytest==8.3.5 -pytest-cov==6.1.1 +pytest==8.4.1 +pytest-cov==6.2.1 pytest-mock==3.14.1 -pytest-asyncio==0.26.0 +pytest-asyncio==1.0.0 pytest-xdist==3.7.0 asyncmock==0.4.2 hypothesis==6.92.1 diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 63c1efa1ee..bd1be66649 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -258,6 +258,14 @@ class TypeInfo(ABC): force: Whether to force encoding the field even if it has a default value """ + @abstractmethod + def get_estimated_size(self) -> int: + """Get estimated size in bytes for this field with typical values. + + Returns: + Estimated size in bytes including field ID and typical data + """ + TYPE_INFO: dict[int, TypeInfo] = {} @@ -291,6 +299,9 @@ class DoubleType(TypeInfo): o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0.0, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 8 # field ID + 8 bytes for double + @register_type(2) class FloatType(TypeInfo): @@ -310,6 +321,9 @@ class FloatType(TypeInfo): o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0.0f, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 4 # field ID + 4 bytes for float + @register_type(3) class Int64Type(TypeInfo): @@ -329,6 +343,9 @@ class Int64Type(TypeInfo): o = f"ProtoSize::add_int64_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + @register_type(4) class UInt64Type(TypeInfo): @@ -348,6 +365,9 @@ class UInt64Type(TypeInfo): o = f"ProtoSize::add_uint64_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + @register_type(5) class Int32Type(TypeInfo): @@ -367,6 +387,9 @@ class Int32Type(TypeInfo): o = f"ProtoSize::add_int32_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + @register_type(6) class Fixed64Type(TypeInfo): @@ -386,6 +409,9 @@ class Fixed64Type(TypeInfo): o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 8 # field ID + 8 bytes fixed + @register_type(7) class Fixed32Type(TypeInfo): @@ -405,6 +431,9 @@ class Fixed32Type(TypeInfo): o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 4 # field ID + 4 bytes fixed + @register_type(8) class BoolType(TypeInfo): @@ -423,6 +452,9 @@ class BoolType(TypeInfo): o = f"ProtoSize::add_bool_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 1 # field ID + 1 byte + @register_type(9) class StringType(TypeInfo): @@ -443,6 +475,9 @@ class StringType(TypeInfo): o = f"ProtoSize::add_string_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string + @register_type(11) class MessageType(TypeInfo): @@ -478,6 +513,11 @@ class MessageType(TypeInfo): o = f"ProtoSize::add_message_object(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return ( + self.calculate_field_id_size() + 16 + ) # field ID + 16 bytes estimated submessage + @register_type(12) class BytesType(TypeInfo): @@ -498,6 +538,9 @@ class BytesType(TypeInfo): o = f"ProtoSize::add_string_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical bytes + @register_type(13) class UInt32Type(TypeInfo): @@ -517,6 +560,9 @@ class UInt32Type(TypeInfo): o = f"ProtoSize::add_uint32_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + @register_type(14) class EnumType(TypeInfo): @@ -544,6 +590,9 @@ class EnumType(TypeInfo): o = f"ProtoSize::add_enum_field(total_size, {field_id_size}, static_cast({name}), {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 1 # field ID + 1 byte typical enum + @register_type(15) class SFixed32Type(TypeInfo): @@ -563,6 +612,9 @@ class SFixed32Type(TypeInfo): o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 4 # field ID + 4 bytes fixed + @register_type(16) class SFixed64Type(TypeInfo): @@ -582,6 +634,9 @@ class SFixed64Type(TypeInfo): o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 8 # field ID + 8 bytes fixed + @register_type(17) class SInt32Type(TypeInfo): @@ -601,6 +656,9 @@ class SInt32Type(TypeInfo): o = f"ProtoSize::add_sint32_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + @register_type(18) class SInt64Type(TypeInfo): @@ -620,6 +678,9 @@ class SInt64Type(TypeInfo): o = f"ProtoSize::add_sint64_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + class RepeatedTypeInfo(TypeInfo): def __init__(self, field: descriptor.FieldDescriptorProto) -> None: @@ -738,6 +799,15 @@ class RepeatedTypeInfo(TypeInfo): o += "}" return o + def get_estimated_size(self) -> int: + # For repeated fields, estimate underlying type size * 2 (assume 2 items typically) + underlying_size = ( + self._ti.get_estimated_size() + if hasattr(self._ti, "get_estimated_size") + else 8 + ) + return underlying_size * 2 + def build_enum_type(desc) -> tuple[str, str]: """Builds the enum type.""" @@ -762,7 +832,26 @@ def build_enum_type(desc) -> tuple[str, str]: return out, cpp -def build_message_type(desc: descriptor.DescriptorProto) -> tuple[str, str]: +def calculate_message_estimated_size(desc: descriptor.DescriptorProto) -> int: + """Calculate estimated size for a complete message based on typical values.""" + total_size = 0 + + for field in desc.field: + if field.label == 3: # repeated + ti = RepeatedTypeInfo(field) + else: + ti = TYPE_INFO[field.type](field) + + # Add estimated size for this field + total_size += ti.get_estimated_size() + + return total_size + + +def build_message_type( + desc: descriptor.DescriptorProto, + base_class_fields: dict[str, list[descriptor.FieldDescriptorProto]] = None, +) -> tuple[str, str]: public_content: list[str] = [] protected_content: list[str] = [] decode_varint: list[str] = [] @@ -773,13 +862,47 @@ def build_message_type(desc: descriptor.DescriptorProto) -> tuple[str, str]: dump: list[str] = [] size_calc: list[str] = [] + # Check if this message has a base class + base_class = get_base_class(desc) + common_field_names = set() + if base_class and base_class_fields and base_class in base_class_fields: + common_field_names = {f.name for f in base_class_fields[base_class]} + + # Get message ID if it's a service message + message_id: int | None = get_opt(desc, pb.id) + + # Add MESSAGE_TYPE method if this is a service message + if message_id is not None: + # Add static constexpr for message type + public_content.append(f"static constexpr uint16_t MESSAGE_TYPE = {message_id};") + + # Add estimated size constant + estimated_size = calculate_message_estimated_size(desc) + public_content.append( + f"static constexpr uint16_t ESTIMATED_SIZE = {estimated_size};" + ) + + # Add message_name method for debugging + public_content.append("#ifdef HAS_PROTO_MESSAGE_DUMP") + snake_name = camel_to_snake(desc.name) + public_content.append( + f'static constexpr const char *message_name() {{ return "{snake_name}"; }}' + ) + public_content.append("#endif") + for field in desc.field: if field.label == 3: ti = RepeatedTypeInfo(field) else: ti = TYPE_INFO[field.type](field) - protected_content.extend(ti.protected_content) - public_content.extend(ti.public_content) + + # Skip field declarations for fields that are in the base class + # but include their encode/decode logic + if field.name not in common_field_names: + protected_content.extend(ti.protected_content) + public_content.extend(ti.public_content) + + # Always include encode/decode logic for all fields encode.append(ti.encode_content) size_calc.append(ti.get_size_calculation(f"this->{ti.field_name}")) @@ -836,36 +959,35 @@ def build_message_type(desc: descriptor.DescriptorProto) -> tuple[str, str]: prot = "bool decode_64bit(uint32_t field_id, Proto64Bit value) override;" protected_content.insert(0, prot) - o = f"void {desc.name}::encode(ProtoWriteBuffer buffer) const {{" + # Only generate encode method if there are fields to encode if encode: + o = f"void {desc.name}::encode(ProtoWriteBuffer buffer) const {{" if len(encode) == 1 and len(encode[0]) + len(o) + 3 < 120: o += f" {encode[0]} " else: o += "\n" o += indent("\n".join(encode)) + "\n" - o += "}\n" - cpp += o - prot = "void encode(ProtoWriteBuffer buffer) const override;" - public_content.append(prot) + o += "}\n" + cpp += o + prot = "void encode(ProtoWriteBuffer buffer) const override;" + public_content.append(prot) + # If no fields to encode, the default implementation in ProtoMessage will be used - # Add calculate_size method - o = f"void {desc.name}::calculate_size(uint32_t &total_size) const {{" - - # Add a check for empty/default objects to short-circuit the calculation - # Only add this optimization if we have fields to check + # Add calculate_size method only if there are fields if size_calc: + o = f"void {desc.name}::calculate_size(uint32_t &total_size) const {{" # For a single field, just inline it for simplicity if len(size_calc) == 1 and len(size_calc[0]) + len(o) + 3 < 120: o += f" {size_calc[0]} " else: - # For multiple fields, add a short-circuit check + # For multiple fields o += "\n" - # Performance optimization: add all the size calculations o += indent("\n".join(size_calc)) + "\n" - o += "}\n" - cpp += o - prot = "void calculate_size(uint32_t &total_size) const override;" - public_content.append(prot) + o += "}\n" + cpp += o + prot = "void calculate_size(uint32_t &total_size) const override;" + public_content.append(prot) + # If no fields to calculate size for, the default implementation in ProtoMessage will be used o = f"void {desc.name}::dump_to(std::string &out) const {{" if dump: @@ -893,7 +1015,10 @@ def build_message_type(desc: descriptor.DescriptorProto) -> tuple[str, str]: prot += "#endif\n" public_content.append(prot) - out = f"class {desc.name} : public ProtoMessage {{\n" + if base_class: + out = f"class {desc.name} : public {base_class} {{\n" + else: + out = f"class {desc.name} : public ProtoMessage {{\n" out += " public:\n" out += indent("\n".join(public_content)) + "\n" out += "\n" @@ -925,6 +1050,132 @@ def get_opt( return desc.options.Extensions[opt] +def get_base_class(desc: descriptor.DescriptorProto) -> str | None: + """Get the base_class option from a message descriptor.""" + if not desc.options.HasExtension(pb.base_class): + return None + return desc.options.Extensions[pb.base_class] + + +def collect_messages_by_base_class( + messages: list[descriptor.DescriptorProto], +) -> dict[str, list[descriptor.DescriptorProto]]: + """Group messages by their base_class option.""" + base_class_groups = {} + + for msg in messages: + base_class = get_base_class(msg) + if base_class: + if base_class not in base_class_groups: + base_class_groups[base_class] = [] + base_class_groups[base_class].append(msg) + + return base_class_groups + + +def find_common_fields( + messages: list[descriptor.DescriptorProto], +) -> list[descriptor.FieldDescriptorProto]: + """Find fields that are common to all messages in the list.""" + if not messages: + return [] + + # Start with fields from the first message + first_msg_fields = {field.name: field for field in messages[0].field} + common_fields = [] + + # Check each field to see if it exists in all messages with same type + # Field numbers can vary between messages - derived classes handle the mapping + for field_name, field in first_msg_fields.items(): + is_common = True + + for msg in messages[1:]: + found = False + for other_field in msg.field: + if ( + other_field.name == field_name + and other_field.type == field.type + and other_field.label == field.label + ): + found = True + break + + if not found: + is_common = False + break + + if is_common: + common_fields.append(field) + + # Sort by field number to maintain order + common_fields.sort(key=lambda f: f.number) + return common_fields + + +def build_base_class( + base_class_name: str, + common_fields: list[descriptor.FieldDescriptorProto], +) -> tuple[str, str]: + """Build the base class definition and implementation.""" + public_content = [] + protected_content = [] + + # For base classes, we only declare the fields but don't handle encode/decode + # The derived classes will handle encoding/decoding with their specific field numbers + for field in common_fields: + if field.label == 3: # repeated + ti = RepeatedTypeInfo(field) + else: + ti = TYPE_INFO[field.type](field) + + # Only add field declarations, not encode/decode logic + protected_content.extend(ti.protected_content) + public_content.extend(ti.public_content) + + # Build header + out = f"class {base_class_name} : public ProtoMessage {{\n" + out += " public:\n" + + # Add destructor with override + public_content.insert(0, f"~{base_class_name}() override = default;") + + # Base classes don't implement encode/decode/calculate_size + # Derived classes handle these with their specific field numbers + cpp = "" + + out += indent("\n".join(public_content)) + "\n" + out += "\n" + out += " protected:\n" + out += indent("\n".join(protected_content)) + if protected_content: + out += "\n" + out += "};\n" + + # No implementation needed for base classes + + return out, cpp + + +def generate_base_classes( + base_class_groups: dict[str, list[descriptor.DescriptorProto]], +) -> tuple[str, str]: + """Generate all base classes.""" + all_headers = [] + all_cpp = [] + + for base_class_name, messages in base_class_groups.items(): + # Find common fields + common_fields = find_common_fields(messages) + + if common_fields: + # Generate base class + header, cpp = build_base_class(base_class_name, common_fields) + all_headers.append(header) + all_cpp.append(cpp) + + return "\n".join(all_headers), "\n".join(all_cpp) + + def build_service_message_type( mt: descriptor.DescriptorProto, ) -> tuple[str, str] | None: @@ -941,24 +1192,18 @@ def build_service_message_type( hout = "" cout = "" + # Store ifdef for later use if ifdef is not None: ifdefs[str(mt.name)] = ifdef - hout += f"#ifdef {ifdef}\n" - cout += f"#ifdef {ifdef}\n" if source in (SOURCE_BOTH, SOURCE_SERVER): - # Generate send - func = f"send_{snake}" - hout += f"bool {func}(const {mt.name} &msg);\n" - cout += f"bool APIServerConnectionBase::{func}(const {mt.name} &msg) {{\n" - if log: - cout += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" - cout += f' ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n' - cout += "#endif\n" - # cout += f' this->set_nodelay({str(nodelay).lower()});\n' - cout += f" return this->send_message_<{mt.name}>(msg, {id_});\n" - cout += "}\n" + # Don't generate individual send methods anymore + # The generic send_message method will be used instead + pass if source in (SOURCE_BOTH, SOURCE_CLIENT): + # Only add ifdef when we're actually generating content + if ifdef is not None: + hout += f"#ifdef {ifdef}\n" # Generate receive func = f"on_{snake}" hout += f"virtual void {func}(const {mt.name} &value){{}};\n" @@ -977,9 +1222,9 @@ def build_service_message_type( case += "break;" RECEIVE_CASES[id_] = case - if ifdef is not None: - hout += "#endif\n" - cout += "#endif\n" + # Only close ifdef if we opened it + if ifdef is not None: + hout += "#endif\n" return hout, cout @@ -1032,8 +1277,25 @@ def main() -> None: mt = file.message_type + # Collect messages by base class + base_class_groups = collect_messages_by_base_class(mt) + + # Find common fields for each base class + base_class_fields = {} + for base_class_name, messages in base_class_groups.items(): + common_fields = find_common_fields(messages) + if common_fields: + base_class_fields[base_class_name] = common_fields + + # Generate base classes + if base_class_fields: + base_headers, base_cpp = generate_base_classes(base_class_groups) + content += base_headers + cpp += base_cpp + + # Generate message types with base class information for m in mt: - s, c = build_message_type(m) + s, c = build_message_type(m, base_class_fields) content += s cpp += c @@ -1083,6 +1345,29 @@ def main() -> None: hpp += f"class {class_name} : public ProtoService {{\n" hpp += " public:\n" + # Add logging helper method declaration + hpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" + hpp += " protected:\n" + hpp += " void log_send_message_(const char *name, const std::string &dump);\n" + hpp += " public:\n" + hpp += "#endif\n\n" + + # Add generic send_message method + hpp += " template\n" + hpp += " bool send_message(const T &msg) {\n" + hpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" + hpp += " this->log_send_message_(T::message_name(), msg.dump());\n" + hpp += "#endif\n" + hpp += " return this->send_message_(msg, T::MESSAGE_TYPE);\n" + hpp += " }\n\n" + + # Add logging helper method implementation to cpp + cpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" + cpp += f"void {class_name}::log_send_message_(const char *name, const std::string &dump) {{\n" + cpp += ' ESP_LOGVV(TAG, "send_message %s: %s", name, dump.c_str());\n' + cpp += "}\n" + cpp += "#endif\n\n" + for mt in file.message_type: obj = build_service_message_type(mt) if obj is None: @@ -1139,26 +1424,40 @@ def main() -> None: hpp_protected += f" void {on_func}(const {inp} &msg) override;\n" hpp += f" virtual {ret} {func}(const {inp} &msg) = 0;\n" cpp += f"void {class_name}::{on_func}(const {inp} &msg) {{\n" - body = "" - if needs_conn: - body += "if (!this->is_connection_setup()) {\n" - body += " this->on_no_setup_connection();\n" - body += " return;\n" - body += "}\n" - if needs_auth: - body += "if (!this->is_authenticated()) {\n" - body += " this->on_unauthenticated_access();\n" - body += " return;\n" - body += "}\n" - if is_void: - body += f"this->{func}(msg);\n" - else: - body += f"{ret} ret = this->{func}(msg);\n" - ret_snake = camel_to_snake(ret) - body += f"if (!this->send_{ret_snake}(ret)) {{\n" - body += " this->on_fatal_error();\n" + # Start with authentication/connection check if needed + if needs_auth or needs_conn: + # Determine which check to use + if needs_auth: + check_func = "this->check_authenticated_()" + else: + check_func = "this->check_connection_setup_()" + + body = f"if ({check_func}) {{\n" + + # Add the actual handler code, indented + handler_body = "" + if is_void: + handler_body = f"this->{func}(msg);\n" + else: + handler_body = f"{ret} ret = this->{func}(msg);\n" + handler_body += "if (!this->send_message(ret)) {\n" + handler_body += " this->on_fatal_error();\n" + handler_body += "}\n" + + body += indent(handler_body) + "\n" body += "}\n" + else: + # No auth check needed, just call the handler + body = "" + if is_void: + body += f"this->{func}(msg);\n" + else: + body += f"{ret} ret = this->{func}(msg);\n" + body += "if (!this->send_message(ret)) {\n" + body += " this->on_fatal_error();\n" + body += "}\n" + cpp += indent(body) + "\n" + "}\n" if ifdef is not None: diff --git a/script/list-components.py b/script/list-components.py index 0d4777436b..0afcaa0f9d 100755 --- a/script/list-components.py +++ b/script/list-components.py @@ -56,6 +56,8 @@ def create_components_graph(): CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] components_graph = {} + platforms = [] + components = [] for path in components_dir.iterdir(): if not path.is_dir(): @@ -70,6 +72,13 @@ def create_components_graph(): ) sys.exit(1) + components.append((comp, name, path)) + if comp.is_platform_component: + platforms.append(name) + + platforms = set(platforms) + + for comp, name, path in components: for dependency in comp.dependencies: add_item_to_components_graph( components_graph, dependency.split(".")[0], name @@ -84,6 +93,8 @@ def create_components_graph(): for platform_path in path.iterdir(): platform_name = platform_path.stem + if platform_name == name or platform_name not in platforms: + continue platform = get_platform(platform_name, name) if platform is None: continue diff --git a/tests/component_tests/text/test_text.py b/tests/component_tests/text/test_text.py index 51fcb3d382..75f1c4b88b 100644 --- a/tests/component_tests/text/test_text.py +++ b/tests/component_tests/text/test_text.py @@ -66,5 +66,5 @@ def test_text_config_lamda_is_set(generate_main): main_cpp = generate_main("tests/component_tests/text/test_text.yaml") # Then - assert "it_4->set_template([=]() -> optional {" in main_cpp + assert "it_4->set_template([=]() -> esphome::optional {" in main_cpp assert 'return std::string{"Hello"};' in main_cpp diff --git a/tests/components/addressable_light/common-ard-esp32_rmt_led_strip.yaml b/tests/components/addressable_light/common-ard-esp32_rmt_led_strip.yaml index 9c5e63cdc6..a071f9df91 100644 --- a/tests/components/addressable_light/common-ard-esp32_rmt_led_strip.yaml +++ b/tests/components/addressable_light/common-ard-esp32_rmt_led_strip.yaml @@ -6,7 +6,6 @@ light: rgb_order: GRB num_leds: 256 pin: ${pin} - rmt_channel: 0 display: - platform: addressable_light diff --git a/tests/components/binary_sensor/common.yaml b/tests/components/binary_sensor/common.yaml new file mode 100644 index 0000000000..148b7d2405 --- /dev/null +++ b/tests/components/binary_sensor/common.yaml @@ -0,0 +1,15 @@ +binary_sensor: + - platform: template + trigger_on_initial_state: true + id: some_binary_sensor + name: "Random binary" + lambda: return (random_uint32() & 1) == 0; + on_state_change: + then: + - logger.log: + format: "Old state was %s" + args: ['x_previous.has_value() ? ONOFF(x_previous) : "Unknown"'] + - logger.log: + format: "New state is %s" + args: ['x.has_value() ? ONOFF(x) : "Unknown"'] + - binary_sensor.invalidate_state: some_binary_sensor diff --git a/tests/components/binary_sensor/test.bk72xx-ard.yaml b/tests/components/binary_sensor/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/binary_sensor/test.bk72xx-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/binary_sensor/test.esp32-ard.yaml b/tests/components/binary_sensor/test.esp32-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/binary_sensor/test.esp32-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/binary_sensor/test.esp32-c3-ard.yaml b/tests/components/binary_sensor/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/binary_sensor/test.esp32-c3-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/binary_sensor/test.esp32-c3-idf.yaml b/tests/components/binary_sensor/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/binary_sensor/test.esp32-c3-idf.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/binary_sensor/test.esp32-idf.yaml b/tests/components/binary_sensor/test.esp32-idf.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/binary_sensor/test.esp32-idf.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/binary_sensor/test.esp32-s3-idf.yaml b/tests/components/binary_sensor/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/binary_sensor/test.esp32-s3-idf.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/binary_sensor/test.esp8266-ard.yaml b/tests/components/binary_sensor/test.esp8266-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/binary_sensor/test.esp8266-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/binary_sensor/test.rp2040-ard.yaml b/tests/components/binary_sensor/test.rp2040-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/binary_sensor/test.rp2040-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/bluetooth_proxy/test.esp32-c3-ard.yaml b/tests/components/bluetooth_proxy/test.esp32-c3-ard.yaml deleted file mode 100644 index bf01b65b6f..0000000000 --- a/tests/components/bluetooth_proxy/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -<<: !include common.yaml - -esp32_ble_tracker: - max_connections: 3 - -bluetooth_proxy: - active: true - connection_slots: 2 diff --git a/tests/components/bluetooth_proxy/test.esp32-c3-idf.yaml b/tests/components/bluetooth_proxy/test.esp32-c6-idf.yaml similarity index 100% rename from tests/components/bluetooth_proxy/test.esp32-c3-idf.yaml rename to tests/components/bluetooth_proxy/test.esp32-c6-idf.yaml diff --git a/tests/components/bluetooth_proxy/test.esp32-ard.yaml b/tests/components/bluetooth_proxy/test.esp32-s3-ard.yaml similarity index 100% rename from tests/components/bluetooth_proxy/test.esp32-ard.yaml rename to tests/components/bluetooth_proxy/test.esp32-s3-ard.yaml diff --git a/tests/components/bluetooth_proxy/test.esp32-idf.yaml b/tests/components/bluetooth_proxy/test.esp32-s3-idf.yaml similarity index 100% rename from tests/components/bluetooth_proxy/test.esp32-idf.yaml rename to tests/components/bluetooth_proxy/test.esp32-s3-idf.yaml diff --git a/tests/components/e131/common-ard.yaml b/tests/components/e131/common-ard.yaml index 418453d6ef..8300dbb01b 100644 --- a/tests/components/e131/common-ard.yaml +++ b/tests/components/e131/common-ard.yaml @@ -8,7 +8,6 @@ light: rgb_order: GRB num_leds: 256 pin: ${pin} - rmt_channel: 0 effects: - e131: universe: 1 diff --git a/tests/components/esp32/test.esp32-idf.yaml b/tests/components/esp32/test.esp32-idf.yaml new file mode 100644 index 0000000000..582b2c22ce --- /dev/null +++ b/tests/components/esp32/test.esp32-idf.yaml @@ -0,0 +1,12 @@ +esp32: + board: esp32dev + framework: + type: esp-idf + advanced: + enable_lwip_mdns_queries: true + enable_lwip_bridge_interface: true + +wifi: + ssid: MySSID + password: password1 + diff --git a/tests/components/esp32_ble/test.esp32-c3-idf.yaml b/tests/components/esp32_ble/test.esp32-c3-idf.yaml index dade44d145..f8defaf28f 100644 --- a/tests/components/esp32_ble/test.esp32-c3-idf.yaml +++ b/tests/components/esp32_ble/test.esp32-c3-idf.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +esp32_ble: + io_capability: keyboard_only + disable_bt_logs: false diff --git a/tests/components/esp32_ble/test.esp32-idf.yaml b/tests/components/esp32_ble/test.esp32-idf.yaml index dade44d145..f8defaf28f 100644 --- a/tests/components/esp32_ble/test.esp32-idf.yaml +++ b/tests/components/esp32_ble/test.esp32-idf.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +esp32_ble: + io_capability: keyboard_only + disable_bt_logs: false diff --git a/tests/components/esp32_camera_web_server/common.yaml b/tests/components/esp32_camera_web_server/common.yaml index 5edefdf0a8..fe2a6a2739 100644 --- a/tests/components/esp32_camera_web_server/common.yaml +++ b/tests/components/esp32_camera_web_server/common.yaml @@ -32,3 +32,7 @@ esp32_camera_web_server: mode: stream - port: 8081 mode: snapshot + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/esp32_hall/test.esp32-ard.yaml b/tests/components/esp32_hall/test.esp32-ard.yaml deleted file mode 100644 index f8429f5aa0..0000000000 --- a/tests/components/esp32_hall/test.esp32-ard.yaml +++ /dev/null @@ -1,3 +0,0 @@ -sensor: - - platform: esp32_hall - name: ESP32 Hall Sensor diff --git a/tests/components/esp32_rmt_led_strip/common-ard.yaml b/tests/components/esp32_rmt_led_strip/common-ard.yaml deleted file mode 100644 index 287690e86e..0000000000 --- a/tests/components/esp32_rmt_led_strip/common-ard.yaml +++ /dev/null @@ -1,18 +0,0 @@ -light: - - platform: esp32_rmt_led_strip - id: led_strip1 - pin: ${pin1} - num_leds: 60 - rmt_channel: 0 - rgb_order: GRB - chipset: ws2812 - - platform: esp32_rmt_led_strip - id: led_strip2 - pin: ${pin2} - num_leds: 60 - rmt_channel: 1 - rgb_order: RGB - bit0_high: 100us - bit0_low: 100us - bit1_high: 100us - bit1_low: 100us diff --git a/tests/components/esp32_rmt_led_strip/common-idf.yaml b/tests/components/esp32_rmt_led_strip/common.yaml similarity index 100% rename from tests/components/esp32_rmt_led_strip/common-idf.yaml rename to tests/components/esp32_rmt_led_strip/common.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml index c75ac73ace..0949b676d5 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml @@ -2,4 +2,5 @@ substitutions: pin1: GPIO13 pin2: GPIO14 -<<: !include common-ard.yaml +packages: + common: !include common.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml index 7b4560a0d7..6cc0667e77 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml @@ -2,4 +2,5 @@ substitutions: pin1: GPIO3 pin2: GPIO4 -<<: !include common-ard.yaml +packages: + common: !include common.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml index b4110199f1..6cc0667e77 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml @@ -2,4 +2,5 @@ substitutions: pin1: GPIO3 pin2: GPIO4 -<<: !include common-idf.yaml +packages: + common: !include common.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml index 5adeba4f8c..0949b676d5 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml @@ -2,4 +2,5 @@ substitutions: pin1: GPIO13 pin2: GPIO14 -<<: !include common-idf.yaml +packages: + common: !include common.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-s3-idf.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..ad273903b2 --- /dev/null +++ b/tests/components/esp32_rmt_led_strip/test.esp32-s3-idf.yaml @@ -0,0 +1,12 @@ +substitutions: + pin1: GPIO3 + pin2: GPIO4 + +packages: + common: !include common.yaml + +light: + - id: !extend led_strip1 + use_dma: "true" + - id: !extend led_strip2 + use_dma: "false" diff --git a/tests/components/esp_ldo/test.esp32-p4-idf.yaml b/tests/components/esp_ldo/test.esp32-p4-idf.yaml new file mode 100644 index 0000000000..0e91aaf082 --- /dev/null +++ b/tests/components/esp_ldo/test.esp32-p4-idf.yaml @@ -0,0 +1,15 @@ +esp_ldo: + - id: ldo_id + channel: 3 + voltage: 2.5V + adjustable: true + - id: ldo_4 + channel: 4 + voltage: 2.0V + +esphome: + on_boot: + then: + - esp_ldo.voltage.adjust: + id: ldo_id + voltage: !lambda return 2.5; diff --git a/tests/components/inkplate6/test.esp32-idf.yaml b/tests/components/inkplate6/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/inkplate6/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.esp32-s3-ard.yaml b/tests/components/internal_temperature/test.esp32-s3-ard.yaml index bdd704756c..dade44d145 100644 --- a/tests/components/internal_temperature/test.esp32-s3-ard.yaml +++ b/tests/components/internal_temperature/test.esp32-s3-ard.yaml @@ -1,5 +1 @@ <<: !include common.yaml - -esp32: - framework: - version: 2.0.9 diff --git a/tests/components/lc709203f/common.yaml b/tests/components/lc709203f/common.yaml new file mode 100644 index 0000000000..53177c0d4a --- /dev/null +++ b/tests/components/lc709203f/common.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_lc709203f + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: lc709203f + size: 2000 + voltage: 3.7 + battery_voltage: + name: "Battery Voltage" + battery_level: + name: "Battery" + temperature: + name: "Pack Temperature" + b_constant: 0xA5A5 diff --git a/tests/components/lc709203f/test.esp32-ard.yaml b/tests/components/lc709203f/test.esp32-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/lc709203f/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp32-c3-ard.yaml b/tests/components/lc709203f/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/lc709203f/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp32-c3-idf.yaml b/tests/components/lc709203f/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/lc709203f/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp32-idf.yaml b/tests/components/lc709203f/test.esp32-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/lc709203f/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp8266-ard.yaml b/tests/components/lc709203f/test.esp8266-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/lc709203f/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/lc709203f/test.rp2040-ard.yaml b/tests/components/lc709203f/test.rp2040-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/lc709203f/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index 174df56749..59602414a7 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -63,7 +63,7 @@ binary_sensor: id: lvgl_pressbutton name: Pressbutton widget: spin_up - publish_initial_state: true + trigger_on_initial_state: true - platform: lvgl name: ButtonMatrix button widget: button_a diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index c77983461d..d8452bdd2a 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -170,6 +170,12 @@ lvgl: lvgl.page.is_showing: page1 then: logger.log: "Yes, page1 showing" + - if: + condition: + lvgl.is_idle: + timeout: !lambda return 5000; + then: + logger.log: LVGL is idle on_unload: - logger.log: page unloaded - lvgl.widget.focus: mark diff --git a/tests/components/nextion/common.yaml b/tests/components/nextion/common.yaml index 44d6cdfbc9..767c868d0b 100644 --- a/tests/components/nextion/common.yaml +++ b/tests/components/nextion/common.yaml @@ -281,6 +281,8 @@ display: id: main_lcd update_interval: 5s command_spacing: 5ms + max_commands_per_loop: 20 + max_queue_size: 50 on_sleep: then: lambda: 'ESP_LOGD("display","Display went to sleep");' diff --git a/tests/components/online_image/common.yaml b/tests/components/online_image/common.yaml index 25809930b5..422a24b540 100644 --- a/tests/components/online_image/common.yaml +++ b/tests/components/online_image/common.yaml @@ -11,6 +11,9 @@ online_image: format: PNG type: BINARY resize: 50x50 + request_headers: + X-Test1: 'Test1' + X-Test2: !lambda 'static int x; return to_string(x++);' on_download_finished: lambda: |- if (cached) { diff --git a/tests/components/openthread/test.esp32-c6-idf.yaml b/tests/components/openthread/test.esp32-c6-idf.yaml new file mode 100644 index 0000000000..482fd1a453 --- /dev/null +++ b/tests/components/openthread/test.esp32-c6-idf.yaml @@ -0,0 +1,11 @@ +network: + enable_ipv6: true + +openthread: + channel: 13 + network_name: OpenThread-8f28 + network_key: 0xdfd34f0f05cad978ec4e32b0413038ff + pan_id: 0x8f28 + ext_pan_id: 0xd63e8e3e495ebbc3 + pskc: 0xc23a76e98f1a6483639b1ac1271e2e27 + force_dataset: true diff --git a/tests/components/openthread_info/test.esp32-c6-idf.yaml b/tests/components/openthread_info/test.esp32-c6-idf.yaml new file mode 100644 index 0000000000..ded0f17611 --- /dev/null +++ b/tests/components/openthread_info/test.esp32-c6-idf.yaml @@ -0,0 +1,30 @@ +network: + enable_ipv6: true + +openthread: + channel: 13 + network_key: 0xdfd34f0f05cad978ec4e32b0413038ff + pan_id: 0x8f28 + +text_sensor: + - platform: openthread_info + ip_address: + name: "Off-mesh routable IP Address" + channel: + name: "Channel" + role: + name: "Device Role" + rloc16: + name: "RLOC16" + ext_addr: + name: "Extended Address" + eui64: + name: "EUI64" + network_name: + name: "Network Name" + network_key: + name: "Network Key" + pan_id: + name: "PAN ID" + ext_pan_id: + name: "Extended PAN ID" diff --git a/tests/components/partition/common-ard.yaml b/tests/components/partition/common-ard.yaml index 654eacf54f..b2ceadd6f7 100644 --- a/tests/components/partition/common-ard.yaml +++ b/tests/components/partition/common-ard.yaml @@ -5,7 +5,6 @@ light: chipset: ws2812 num_leds: 256 rgb_order: GRB - rmt_channel: 1 pin: ${pin} - platform: partition name: Partition Light diff --git a/tests/components/psram/test.esp32-p4-idf.yaml b/tests/components/psram/test.esp32-p4-idf.yaml new file mode 100644 index 0000000000..9ebd80328d --- /dev/null +++ b/tests/components/psram/test.esp32-p4-idf.yaml @@ -0,0 +1,9 @@ +esp32: + cpu_frequency: 360MHz + framework: + type: esp-idf + advanced: + enable_idf_experimental_features: yes + +psram: + speed: 200MHz diff --git a/tests/components/remote_receiver/esp32-common-ard.yaml b/tests/components/remote_receiver/esp32-common-ard.yaml deleted file mode 100644 index e331a35307..0000000000 --- a/tests/components/remote_receiver/esp32-common-ard.yaml +++ /dev/null @@ -1,14 +0,0 @@ -remote_receiver: - - id: rcvr - pin: ${pin} - rmt_channel: ${rmt_channel} - dump: all - tolerance: 25% - <<: !include common-actions.yaml - -binary_sensor: - - platform: remote_receiver - name: Panasonic Remote Input - panasonic: - address: 0x4004 - command: 0x100BCBD diff --git a/tests/components/remote_receiver/esp32-common-idf.yaml b/tests/components/remote_receiver/esp32-common.yaml similarity index 94% rename from tests/components/remote_receiver/esp32-common-idf.yaml rename to tests/components/remote_receiver/esp32-common.yaml index b314880f8a..14effcbd2c 100644 --- a/tests/components/remote_receiver/esp32-common-idf.yaml +++ b/tests/components/remote_receiver/esp32-common.yaml @@ -7,7 +7,6 @@ remote_receiver: filter_symbols: ${filter_symbols} receive_symbols: ${receive_symbols} rmt_symbols: ${rmt_symbols} - use_dma: ${use_dma} <<: !include common-actions.yaml binary_sensor: diff --git a/tests/components/remote_receiver/test.esp32-ard.yaml b/tests/components/remote_receiver/test.esp32-ard.yaml index 5d29187206..10dd767598 100644 --- a/tests/components/remote_receiver/test.esp32-ard.yaml +++ b/tests/components/remote_receiver/test.esp32-ard.yaml @@ -1,6 +1,9 @@ substitutions: pin: GPIO2 - rmt_channel: "2" + clock_resolution: "2000000" + filter_symbols: "2" + receive_symbols: "4" + rmt_symbols: "64" packages: - common: !include esp32-common-ard.yaml + common: !include esp32-common.yaml diff --git a/tests/components/remote_receiver/test.esp32-c3-ard.yaml b/tests/components/remote_receiver/test.esp32-c3-ard.yaml index 5d29187206..10dd767598 100644 --- a/tests/components/remote_receiver/test.esp32-c3-ard.yaml +++ b/tests/components/remote_receiver/test.esp32-c3-ard.yaml @@ -1,6 +1,9 @@ substitutions: pin: GPIO2 - rmt_channel: "2" + clock_resolution: "2000000" + filter_symbols: "2" + receive_symbols: "4" + rmt_symbols: "64" packages: - common: !include esp32-common-ard.yaml + common: !include esp32-common.yaml diff --git a/tests/components/remote_receiver/test.esp32-c3-idf.yaml b/tests/components/remote_receiver/test.esp32-c3-idf.yaml index 495bb293c3..10dd767598 100644 --- a/tests/components/remote_receiver/test.esp32-c3-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-c3-idf.yaml @@ -4,7 +4,6 @@ substitutions: filter_symbols: "2" receive_symbols: "4" rmt_symbols: "64" - use_dma: "true" packages: - common: !include esp32-common-idf.yaml + common: !include esp32-common.yaml diff --git a/tests/components/remote_receiver/test.esp32-idf.yaml b/tests/components/remote_receiver/test.esp32-idf.yaml index 495bb293c3..10dd767598 100644 --- a/tests/components/remote_receiver/test.esp32-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-idf.yaml @@ -4,7 +4,6 @@ substitutions: filter_symbols: "2" receive_symbols: "4" rmt_symbols: "64" - use_dma: "true" packages: - common: !include esp32-common-idf.yaml + common: !include esp32-common.yaml diff --git a/tests/components/remote_receiver/test.esp32-s3-idf.yaml b/tests/components/remote_receiver/test.esp32-s3-idf.yaml index e678ba456d..cdae8b1e4e 100644 --- a/tests/components/remote_receiver/test.esp32-s3-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-s3-idf.yaml @@ -4,7 +4,10 @@ substitutions: filter_symbols: "2" receive_symbols: "4" rmt_symbols: "64" - use_dma: "true" packages: - common: !include esp32-common-idf.yaml + common: !include esp32-common.yaml + +remote_receiver: + - id: !extend rcvr + use_dma: "true" diff --git a/tests/components/remote_transmitter/esp32-common-ard.yaml b/tests/components/remote_transmitter/esp32-common-ard.yaml deleted file mode 100644 index 420cea326d..0000000000 --- a/tests/components/remote_transmitter/esp32-common-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -remote_transmitter: - - id: xmitr - pin: ${pin} - rmt_channel: ${rmt_channel} - carrier_duty_percent: 50% - -packages: - buttons: !include common-buttons.yaml diff --git a/tests/components/remote_transmitter/esp32-common-idf.yaml b/tests/components/remote_transmitter/esp32-common.yaml similarity index 89% rename from tests/components/remote_transmitter/esp32-common-idf.yaml rename to tests/components/remote_transmitter/esp32-common.yaml index c5d4ab7b96..8b26c45149 100644 --- a/tests/components/remote_transmitter/esp32-common-idf.yaml +++ b/tests/components/remote_transmitter/esp32-common.yaml @@ -4,7 +4,6 @@ remote_transmitter: carrier_duty_percent: 50% clock_resolution: ${clock_resolution} rmt_symbols: ${rmt_symbols} - use_dma: ${use_dma} packages: buttons: !include common-buttons.yaml diff --git a/tests/components/remote_transmitter/test.esp32-ard.yaml b/tests/components/remote_transmitter/test.esp32-ard.yaml index 5d29187206..0522f4d181 100644 --- a/tests/components/remote_transmitter/test.esp32-ard.yaml +++ b/tests/components/remote_transmitter/test.esp32-ard.yaml @@ -1,6 +1,7 @@ substitutions: pin: GPIO2 - rmt_channel: "2" + clock_resolution: "2000000" + rmt_symbols: "64" packages: - common: !include esp32-common-ard.yaml + common: !include esp32-common.yaml diff --git a/tests/components/remote_transmitter/test.esp32-c3-ard.yaml b/tests/components/remote_transmitter/test.esp32-c3-ard.yaml index c755b11563..0522f4d181 100644 --- a/tests/components/remote_transmitter/test.esp32-c3-ard.yaml +++ b/tests/components/remote_transmitter/test.esp32-c3-ard.yaml @@ -1,6 +1,7 @@ substitutions: pin: GPIO2 - rmt_channel: "1" + clock_resolution: "2000000" + rmt_symbols: "64" packages: - common: !include esp32-common-ard.yaml + common: !include esp32-common.yaml diff --git a/tests/components/remote_transmitter/test.esp32-c3-idf.yaml b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml index be526014bd..0522f4d181 100644 --- a/tests/components/remote_transmitter/test.esp32-c3-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml @@ -2,7 +2,6 @@ substitutions: pin: GPIO2 clock_resolution: "2000000" rmt_symbols: "64" - use_dma: "true" packages: - common: !include esp32-common-idf.yaml + common: !include esp32-common.yaml diff --git a/tests/components/remote_transmitter/test.esp32-idf.yaml b/tests/components/remote_transmitter/test.esp32-idf.yaml index be526014bd..0522f4d181 100644 --- a/tests/components/remote_transmitter/test.esp32-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-idf.yaml @@ -2,7 +2,6 @@ substitutions: pin: GPIO2 clock_resolution: "2000000" rmt_symbols: "64" - use_dma: "true" packages: - common: !include esp32-common-idf.yaml + common: !include esp32-common.yaml diff --git a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml index cb86020064..fe4c46d9e7 100644 --- a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml @@ -2,7 +2,10 @@ substitutions: pin: GPIO38 clock_resolution: "2000000" rmt_symbols: "64" - use_dma: "true" packages: - common: !include esp32-common-idf.yaml + common: !include esp32-common.yaml + +remote_transmitter: + - id: !extend xmitr + use_dma: "true" diff --git a/tests/components/spi/test.esp32-p4-idf.yaml b/tests/components/spi/test.esp32-p4-idf.yaml new file mode 100644 index 0000000000..061e3dd44a --- /dev/null +++ b/tests/components/spi/test.esp32-p4-idf.yaml @@ -0,0 +1,38 @@ +spi: + - id: quad_spi + type: quad + interface: spi3 + clk_pin: + number: 47 + data_pins: + - allow_other_uses: true + number: 40 + - allow_other_uses: true + number: 41 + - allow_other_uses: true + number: 42 + - allow_other_uses: true + number: 43 + - id: octal_spi + type: octal + interface: hardware + clk_pin: + number: 0 + data_pins: + - 36 + - 37 + - 38 + - 39 + - allow_other_uses: true + number: 40 + - allow_other_uses: true + number: 41 + - allow_other_uses: true + number: 42 + - allow_other_uses: true + number: 43 + - id: spi_id_3 + interface: any + clk_pin: 8 + mosi_pin: 9 + diff --git a/tests/components/spi_device/common.yaml b/tests/components/spi_device/common.yaml index 636d82202b..0f6a5038fb 100644 --- a/tests/components/spi_device/common.yaml +++ b/tests/components/spi_device/common.yaml @@ -5,7 +5,7 @@ spi: miso_pin: ${miso_pin} spi_device: - id: spi_device_test - data_rate: 2MHz - spi_mode: 3 - bit_order: lsb_first + - id: spi_device_test + data_rate: 2MHz + spi_mode: 3 + bit_order: lsb_first diff --git a/tests/components/spi_device/test.esp32-idf.yaml b/tests/components/spi_device/test.esp32-idf.yaml index 448e54fea6..c4989cccbf 100644 --- a/tests/components/spi_device/test.esp32-idf.yaml +++ b/tests/components/spi_device/test.esp32-idf.yaml @@ -4,3 +4,8 @@ substitutions: miso_pin: GPIO15 <<: !include common.yaml +spi_device: + - id: spi_device_test + release_device: true + data_rate: 1MHz + spi_mode: 0 diff --git a/tests/components/wled/test.esp32-ard.yaml b/tests/components/wled/test.esp32-ard.yaml index a24f28e154..156b31181e 100644 --- a/tests/components/wled/test.esp32-ard.yaml +++ b/tests/components/wled/test.esp32-ard.yaml @@ -12,6 +12,5 @@ light: rgb_order: GRB num_leds: 256 pin: 2 - rmt_channel: 0 effects: - wled: diff --git a/tests/components/wled/test.esp32-c3-ard.yaml b/tests/components/wled/test.esp32-c3-ard.yaml index a24f28e154..156b31181e 100644 --- a/tests/components/wled/test.esp32-c3-ard.yaml +++ b/tests/components/wled/test.esp32-c3-ard.yaml @@ -12,6 +12,5 @@ light: rgb_order: GRB num_leds: 256 pin: 2 - rmt_channel: 0 effects: - wled: diff --git a/tests/components/xiaomi_xmwsdj04mmc/common.yaml b/tests/components/xiaomi_xmwsdj04mmc/common.yaml new file mode 100644 index 0000000000..fe7a11efc5 --- /dev/null +++ b/tests/components/xiaomi_xmwsdj04mmc/common.yaml @@ -0,0 +1,12 @@ +esp32_ble_tracker: + +sensor: + - platform: xiaomi_xmwsdj04mmc + mac_address: 84:B4:DB:5D:A3:8F + bindkey: d8ca2ed09bb5541dc8f045ca360b00ea + temperature: + name: Xiaomi XMWSDJ04MMC Temperature + humidity: + name: Xiaomi XMWSDJ04MMC Humidity + battery_level: + name: Xiaomi XMWSDJ04MMC Battery Level diff --git a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-ard.yaml b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-ard.yaml b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-idf.yaml b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 4c798c6b72..525e3541b3 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -3,7 +3,7 @@ from __future__ import annotations import asyncio -from collections.abc import AsyncGenerator, Generator +from collections.abc import AsyncGenerator, Callable, Generator from contextlib import AbstractAsyncContextManager, asynccontextmanager import logging import os @@ -15,7 +15,7 @@ import sys import tempfile from typing import TextIO -from aioesphomeapi import APIClient, APIConnectionError, ReconnectLogic +from aioesphomeapi import APIClient, APIConnectionError, LogParser, ReconnectLogic import pytest import pytest_asyncio @@ -46,6 +46,7 @@ if platform.system() == "Windows": "Integration tests are not supported on Windows", allow_module_level=True ) + import pty # not available on Windows @@ -119,6 +120,21 @@ async def yaml_config(request: pytest.FixtureRequest, unused_tcp_port: int) -> s # Add port configuration after api: content = content.replace("api:", f"api:\n port: {unused_tcp_port}") + # Add debug build flags for integration tests to enable assertions + if "esphome:" in content: + # Check if platformio_options already exists + if "platformio_options:" not in content: + # Add platformio_options with debug flags after esphome: + content = content.replace( + "esphome:", + "esphome:\n" + " # Enable assertions for integration tests\n" + " platformio_options:\n" + " build_flags:\n" + ' - "-DDEBUG" # Enable assert() statements\n' + ' - "-g" # Add debug symbols', + ) + return content @@ -347,14 +363,30 @@ async def api_client_connected( async def _read_stream_lines( - stream: asyncio.StreamReader, lines: list[str], output_stream: TextIO + stream: asyncio.StreamReader, + lines: list[str], + output_stream: TextIO, + line_callback: Callable[[str], None] | None = None, ) -> None: """Read lines from a stream, append to list, and echo to output stream.""" + log_parser = LogParser() while line := await stream.readline(): - decoded_line = line.decode("utf-8", errors="replace") + decoded_line = ( + line.replace(b"\r", b"") + .replace(b"\n", b"") + .decode("utf8", "backslashreplace") + ) lines.append(decoded_line.rstrip()) # Echo to stdout/stderr in real-time - print(decoded_line.rstrip(), file=output_stream, flush=True) + # Print without newline to avoid double newlines + print( + log_parser.parse_line(decoded_line, timestamp=""), + file=output_stream, + flush=True, + ) + # Call the callback if provided + if line_callback: + line_callback(decoded_line.rstrip()) @asynccontextmanager @@ -363,6 +395,7 @@ async def run_binary_and_wait_for_port( host: str, port: int, timeout: float = PORT_WAIT_TIMEOUT, + line_callback: Callable[[str], None] | None = None, ) -> AsyncGenerator[None]: """Run a binary, wait for it to open a port, and clean up on exit.""" # Create a pseudo-terminal to make the binary think it's running interactively @@ -410,7 +443,9 @@ async def run_binary_and_wait_for_port( # Read from output stream output_tasks = [ asyncio.create_task( - _read_stream_lines(output_reader, stdout_lines, sys.stdout) + _read_stream_lines( + output_reader, stdout_lines, sys.stdout, line_callback + ) ) ] @@ -490,6 +525,7 @@ async def run_compiled_context( compile_esphome: CompileFunction, port: int, port_socket: socket.socket | None = None, + line_callback: Callable[[str], None] | None = None, ) -> AsyncGenerator[None]: """Context manager to write, compile and run an ESPHome configuration.""" # Write the YAML config @@ -503,7 +539,9 @@ async def run_compiled_context( port_socket.close() # Run the binary and wait for the API server to start - async with run_binary_and_wait_for_port(binary_path, LOCALHOST, port): + async with run_binary_and_wait_for_port( + binary_path, LOCALHOST, port, line_callback=line_callback + ): yield @@ -517,7 +555,9 @@ async def run_compiled( port, port_socket = reserved_tcp_port def _run_compiled( - yaml_content: str, filename: str | None = None + yaml_content: str, + filename: str | None = None, + line_callback: Callable[[str], None] | None = None, ) -> AbstractAsyncContextManager[asyncio.subprocess.Process]: return run_compiled_context( yaml_content, @@ -526,6 +566,7 @@ async def run_compiled( compile_esphome, port, port_socket, + line_callback=line_callback, ) yield _run_compiled diff --git a/tests/integration/fixtures/api_message_size_batching.yaml b/tests/integration/fixtures/api_message_size_batching.yaml new file mode 100644 index 0000000000..c730dc1aa3 --- /dev/null +++ b/tests/integration/fixtures/api_message_size_batching.yaml @@ -0,0 +1,161 @@ +esphome: + name: message-size-batching-test +host: +api: +# Default batch_delay to test batching +logger: + +# Create entities that will produce different protobuf header sizes +# Header size depends on: 1 byte indicator + varint(payload_size) + varint(message_type) +# 4-byte header: type < 128, payload < 128 +# 5-byte header: type < 128, payload 128-16383 OR type 128+, payload < 128 +# 6-byte header: type 128+, payload 128-16383 + +# Small select with few options - produces small message +select: + - platform: template + name: "Small Select" + id: small_select + optimistic: true + options: + - "Option A" + - "Option B" + initial_option: "Option A" + update_interval: 5.0s + + # Medium select with more options - produces medium message + - platform: template + name: "Medium Select" + id: medium_select + optimistic: true + options: + - "Option 001" + - "Option 002" + - "Option 003" + - "Option 004" + - "Option 005" + - "Option 006" + - "Option 007" + - "Option 008" + - "Option 009" + - "Option 010" + - "Option 011" + - "Option 012" + - "Option 013" + - "Option 014" + - "Option 015" + - "Option 016" + - "Option 017" + - "Option 018" + - "Option 019" + - "Option 020" + initial_option: "Option 001" + update_interval: 5.0s + + # Large select with many options - produces larger message + - platform: template + name: "Large Select with Many Options to Create Larger Payload" + id: large_select + optimistic: true + options: + - "Long Option Name 001 - This is a longer option name to increase message size" + - "Long Option Name 002 - This is a longer option name to increase message size" + - "Long Option Name 003 - This is a longer option name to increase message size" + - "Long Option Name 004 - This is a longer option name to increase message size" + - "Long Option Name 005 - This is a longer option name to increase message size" + - "Long Option Name 006 - This is a longer option name to increase message size" + - "Long Option Name 007 - This is a longer option name to increase message size" + - "Long Option Name 008 - This is a longer option name to increase message size" + - "Long Option Name 009 - This is a longer option name to increase message size" + - "Long Option Name 010 - This is a longer option name to increase message size" + - "Long Option Name 011 - This is a longer option name to increase message size" + - "Long Option Name 012 - This is a longer option name to increase message size" + - "Long Option Name 013 - This is a longer option name to increase message size" + - "Long Option Name 014 - This is a longer option name to increase message size" + - "Long Option Name 015 - This is a longer option name to increase message size" + - "Long Option Name 016 - This is a longer option name to increase message size" + - "Long Option Name 017 - This is a longer option name to increase message size" + - "Long Option Name 018 - This is a longer option name to increase message size" + - "Long Option Name 019 - This is a longer option name to increase message size" + - "Long Option Name 020 - This is a longer option name to increase message size" + - "Long Option Name 021 - This is a longer option name to increase message size" + - "Long Option Name 022 - This is a longer option name to increase message size" + - "Long Option Name 023 - This is a longer option name to increase message size" + - "Long Option Name 024 - This is a longer option name to increase message size" + - "Long Option Name 025 - This is a longer option name to increase message size" + - "Long Option Name 026 - This is a longer option name to increase message size" + - "Long Option Name 027 - This is a longer option name to increase message size" + - "Long Option Name 028 - This is a longer option name to increase message size" + - "Long Option Name 029 - This is a longer option name to increase message size" + - "Long Option Name 030 - This is a longer option name to increase message size" + - "Long Option Name 031 - This is a longer option name to increase message size" + - "Long Option Name 032 - This is a longer option name to increase message size" + - "Long Option Name 033 - This is a longer option name to increase message size" + - "Long Option Name 034 - This is a longer option name to increase message size" + - "Long Option Name 035 - This is a longer option name to increase message size" + - "Long Option Name 036 - This is a longer option name to increase message size" + - "Long Option Name 037 - This is a longer option name to increase message size" + - "Long Option Name 038 - This is a longer option name to increase message size" + - "Long Option Name 039 - This is a longer option name to increase message size" + - "Long Option Name 040 - This is a longer option name to increase message size" + - "Long Option Name 041 - This is a longer option name to increase message size" + - "Long Option Name 042 - This is a longer option name to increase message size" + - "Long Option Name 043 - This is a longer option name to increase message size" + - "Long Option Name 044 - This is a longer option name to increase message size" + - "Long Option Name 045 - This is a longer option name to increase message size" + - "Long Option Name 046 - This is a longer option name to increase message size" + - "Long Option Name 047 - This is a longer option name to increase message size" + - "Long Option Name 048 - This is a longer option name to increase message size" + - "Long Option Name 049 - This is a longer option name to increase message size" + - "Long Option Name 050 - This is a longer option name to increase message size" + initial_option: "Long Option Name 001 - This is a longer option name to increase message size" + update_interval: 5.0s + +# Text sensors with different value lengths +text_sensor: + - platform: template + name: "Short Text Sensor" + id: short_text_sensor + lambda: |- + return {"OK"}; + update_interval: 5.0s + + - platform: template + name: "Medium Text Sensor" + id: medium_text_sensor + lambda: |- + return {"This is a medium length text sensor value that should produce a medium sized message"}; + update_interval: 5.0s + + - platform: template + name: "Long Text Sensor with Very Long Value" + id: long_text_sensor + lambda: |- + return {"This is a very long text sensor value that contains a lot of text to ensure we get a larger protobuf message. The message should be long enough to require a 2-byte varint for the payload size, which happens when the payload exceeds 127 bytes. Let's add even more text here to make sure we exceed that threshold and test the batching of messages with different header sizes properly."}; + update_interval: 5.0s + +# Text input which can have various lengths +text: + - platform: template + name: "Test Text Input" + id: test_text_input + optimistic: true + mode: text + min_length: 0 + max_length: 255 + initial_value: "Initial value" + update_interval: 5.0s + +# Number entity to add variety (different message type number) +# The ListEntitiesNumberResponse has message type 49 +# The NumberStateResponse has message type 50 +number: + - platform: template + name: "Test Number with Long Name to Increase Message Size" + id: test_number + optimistic: true + min_value: 0 + max_value: 1000 + step: 0.1 + initial_value: 42.0 + update_interval: 5.0s diff --git a/tests/integration/fixtures/external_components/loop_test_component/__init__.py b/tests/integration/fixtures/external_components/loop_test_component/__init__.py new file mode 100644 index 0000000000..b66d4598f4 --- /dev/null +++ b/tests/integration/fixtures/external_components/loop_test_component/__init__.py @@ -0,0 +1,96 @@ +from esphome import automation +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_NAME + +CODEOWNERS = ["@esphome/tests"] + +loop_test_component_ns = cg.esphome_ns.namespace("loop_test_component") +LoopTestComponent = loop_test_component_ns.class_("LoopTestComponent", cg.Component) +LoopTestISRComponent = loop_test_component_ns.class_( + "LoopTestISRComponent", cg.Component +) + +CONF_DISABLE_AFTER = "disable_after" +CONF_TEST_REDUNDANT_OPERATIONS = "test_redundant_operations" +CONF_ISR_COMPONENTS = "isr_components" + +COMPONENT_CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(LoopTestComponent), + cv.Required(CONF_NAME): cv.string, + cv.Optional(CONF_DISABLE_AFTER, default=0): cv.int_, + cv.Optional(CONF_TEST_REDUNDANT_OPERATIONS, default=False): cv.boolean, + } +) + +ISR_COMPONENT_CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(LoopTestISRComponent), + cv.Required(CONF_NAME): cv.string, + } +) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(LoopTestComponent), + cv.Required(CONF_COMPONENTS): cv.ensure_list(COMPONENT_CONFIG_SCHEMA), + cv.Optional(CONF_ISR_COMPONENTS): cv.ensure_list(ISR_COMPONENT_CONFIG_SCHEMA), + } +).extend(cv.COMPONENT_SCHEMA) + +# Define actions +EnableAction = loop_test_component_ns.class_("EnableAction", automation.Action) +DisableAction = loop_test_component_ns.class_("DisableAction", automation.Action) + + +@automation.register_action( + "loop_test_component.enable", + EnableAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(LoopTestComponent), + } + ), +) +async def enable_to_code(config, action_id, template_arg, args): + parent = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, parent) + return var + + +@automation.register_action( + "loop_test_component.disable", + DisableAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(LoopTestComponent), + } + ), +) +async def disable_to_code(config, action_id, template_arg, args): + parent = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, parent) + return var + + +async def to_code(config): + # The parent config doesn't actually create a component + # We just create each sub-component + for comp_config in config[CONF_COMPONENTS]: + var = cg.new_Pvariable(comp_config[CONF_ID]) + await cg.register_component(var, comp_config) + + cg.add(var.set_name(comp_config[CONF_NAME])) + cg.add(var.set_disable_after(comp_config[CONF_DISABLE_AFTER])) + cg.add( + var.set_test_redundant_operations( + comp_config[CONF_TEST_REDUNDANT_OPERATIONS] + ) + ) + + # Create ISR test components + for isr_config in config.get(CONF_ISR_COMPONENTS, []): + var = cg.new_Pvariable(isr_config[CONF_ID]) + await cg.register_component(var, isr_config) + cg.add(var.set_name(isr_config[CONF_NAME])) diff --git a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.cpp b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.cpp new file mode 100644 index 0000000000..470740c534 --- /dev/null +++ b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.cpp @@ -0,0 +1,43 @@ +#include "loop_test_component.h" + +namespace esphome { +namespace loop_test_component { + +void LoopTestComponent::setup() { ESP_LOGI(TAG, "[%s] Setup called", this->name_.c_str()); } + +void LoopTestComponent::loop() { + this->loop_count_++; + ESP_LOGI(TAG, "[%s] Loop count: %d", this->name_.c_str(), this->loop_count_); + + // Test self-disable after specified count + if (this->disable_after_ > 0 && this->loop_count_ == this->disable_after_) { + ESP_LOGI(TAG, "[%s] Disabling self after %d loops", this->name_.c_str(), this->disable_after_); + this->disable_loop(); + } + + // Test redundant operations + if (this->test_redundant_operations_ && this->loop_count_ == 5) { + if (this->name_ == "redundant_enable") { + ESP_LOGI(TAG, "[%s] Testing enable when already enabled", this->name_.c_str()); + this->enable_loop(); + } else if (this->name_ == "redundant_disable") { + ESP_LOGI(TAG, "[%s] Testing disable when will be disabled", this->name_.c_str()); + // We'll disable at count 10, but try to disable again at 5 + this->disable_loop(); + ESP_LOGI(TAG, "[%s] First disable complete", this->name_.c_str()); + } + } +} + +void LoopTestComponent::service_enable() { + ESP_LOGI(TAG, "[%s] Service enable called", this->name_.c_str()); + this->enable_loop(); +} + +void LoopTestComponent::service_disable() { + ESP_LOGI(TAG, "[%s] Service disable called", this->name_.c_str()); + this->disable_loop(); +} + +} // namespace loop_test_component +} // namespace esphome diff --git a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h new file mode 100644 index 0000000000..5c43dd4b43 --- /dev/null +++ b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h @@ -0,0 +1,58 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/log.h" +#include "esphome/core/application.h" +#include "esphome/core/automation.h" + +namespace esphome { +namespace loop_test_component { + +static const char *const TAG = "loop_test_component"; + +class LoopTestComponent : public Component { + public: + void set_name(const std::string &name) { this->name_ = name; } + void set_disable_after(int count) { this->disable_after_ = count; } + void set_test_redundant_operations(bool test) { this->test_redundant_operations_ = test; } + + void setup() override; + void loop() override; + + // Service methods for external control + void service_enable(); + void service_disable(); + + int get_loop_count() const { return this->loop_count_; } + + float get_setup_priority() const override { return setup_priority::DATA; } + + protected: + std::string name_; + int loop_count_{0}; + int disable_after_{0}; + bool test_redundant_operations_{false}; +}; + +template class EnableAction : public Action { + public: + EnableAction(LoopTestComponent *parent) : parent_(parent) {} + + void play(Ts... x) override { this->parent_->service_enable(); } + + protected: + LoopTestComponent *parent_; +}; + +template class DisableAction : public Action { + public: + DisableAction(LoopTestComponent *parent) : parent_(parent) {} + + void play(Ts... x) override { this->parent_->service_disable(); } + + protected: + LoopTestComponent *parent_; +}; + +} // namespace loop_test_component +} // namespace esphome diff --git a/tests/integration/fixtures/external_components/loop_test_component/loop_test_isr_component.cpp b/tests/integration/fixtures/external_components/loop_test_component/loop_test_isr_component.cpp new file mode 100644 index 0000000000..30afec0422 --- /dev/null +++ b/tests/integration/fixtures/external_components/loop_test_component/loop_test_isr_component.cpp @@ -0,0 +1,80 @@ +#include "loop_test_isr_component.h" +#include "esphome/core/hal.h" +#include "esphome/core/application.h" + +namespace esphome { +namespace loop_test_component { + +static const char *const ISR_TAG = "loop_test_isr_component"; + +void LoopTestISRComponent::setup() { + ESP_LOGI(ISR_TAG, "[%s] ISR component setup called", this->name_.c_str()); + this->last_check_time_ = millis(); +} + +void LoopTestISRComponent::loop() { + this->loop_count_++; + ESP_LOGI(ISR_TAG, "[%s] ISR component loop count: %d", this->name_.c_str(), this->loop_count_); + + // Disable after 5 loops + if (this->loop_count_ == 5) { + ESP_LOGI(ISR_TAG, "[%s] Disabling after 5 loops", this->name_.c_str()); + this->disable_loop(); + this->last_disable_time_ = millis(); + // Simulate ISR after disabling + this->set_timeout("simulate_isr_1", 50, [this]() { + ESP_LOGI(ISR_TAG, "[%s] Simulating ISR enable", this->name_.c_str()); + this->simulate_isr_enable(); + // Test reentrancy - call enable_loop() directly after ISR + // This simulates another thread calling enable_loop while processing ISR enables + this->set_timeout("test_reentrant", 10, [this]() { + ESP_LOGI(ISR_TAG, "[%s] Testing reentrancy - calling enable_loop() directly", this->name_.c_str()); + this->enable_loop(); + }); + }); + } + + // If we get here after being disabled, it means ISR re-enabled us + if (this->loop_count_ > 5 && this->loop_count_ < 10) { + ESP_LOGI(ISR_TAG, "[%s] Running after ISR re-enable! ISR was called %d times", this->name_.c_str(), + this->isr_call_count_); + } + + // Disable again after 10 loops to test multiple ISR enables + if (this->loop_count_ == 10) { + ESP_LOGI(ISR_TAG, "[%s] Disabling again after 10 loops", this->name_.c_str()); + this->disable_loop(); + this->last_disable_time_ = millis(); + + // Test pure ISR enable without any main loop enable + this->set_timeout("simulate_isr_2", 50, [this]() { + ESP_LOGI(ISR_TAG, "[%s] Testing pure ISR enable (no main loop enable)", this->name_.c_str()); + this->simulate_isr_enable(); + // DO NOT call enable_loop() - test that ISR alone works + }); + } + + // Log when we're running after second ISR enable + if (this->loop_count_ > 10) { + ESP_LOGI(ISR_TAG, "[%s] Running after pure ISR re-enable! ISR was called %d times total", this->name_.c_str(), + this->isr_call_count_); + } +} + +void IRAM_ATTR LoopTestISRComponent::simulate_isr_enable() { + // This simulates what would happen in a real ISR + // In a real scenario, this would be called from an actual interrupt handler + + this->isr_call_count_++; + + // Call enable_loop_soon_any_context multiple times to test that it's safe + this->enable_loop_soon_any_context(); + this->enable_loop_soon_any_context(); // Test multiple calls + this->enable_loop_soon_any_context(); // Should be idempotent + + // Note: In a real ISR, we cannot use ESP_LOG* macros as they're not ISR-safe + // For testing, we'll track the call count and log it from the main loop +} + +} // namespace loop_test_component +} // namespace esphome diff --git a/tests/integration/fixtures/external_components/loop_test_component/loop_test_isr_component.h b/tests/integration/fixtures/external_components/loop_test_component/loop_test_isr_component.h new file mode 100644 index 0000000000..20e11b5ecd --- /dev/null +++ b/tests/integration/fixtures/external_components/loop_test_component/loop_test_isr_component.h @@ -0,0 +1,32 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/log.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace loop_test_component { + +class LoopTestISRComponent : public Component { + public: + void set_name(const std::string &name) { this->name_ = name; } + + void setup() override; + void loop() override; + + // Simulates an ISR calling enable_loop_soon_any_context + void simulate_isr_enable(); + + float get_setup_priority() const override { return setup_priority::DATA; } + + protected: + std::string name_; + int loop_count_{0}; + uint32_t last_disable_time_{0}; + uint32_t last_check_time_{0}; + bool isr_enable_pending_{false}; + int isr_call_count_{0}; +}; + +} // namespace loop_test_component +} // namespace esphome diff --git a/tests/integration/fixtures/host_mode_batch_delay.yaml b/tests/integration/fixtures/host_mode_batch_delay.yaml new file mode 100644 index 0000000000..8abe1da6fb --- /dev/null +++ b/tests/integration/fixtures/host_mode_batch_delay.yaml @@ -0,0 +1,55 @@ +esphome: + name: host-batch-delay-test +host: +api: + batch_delay: 0ms +logger: + +# Add multiple sensors to test batching +sensor: + - platform: template + name: "Test Sensor 1" + id: test_sensor1 + lambda: |- + return 1.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 2" + id: test_sensor2 + lambda: |- + return 2.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 3" + id: test_sensor3 + lambda: |- + return 3.0; + update_interval: 0.1s + +binary_sensor: + - platform: template + name: "Test Binary Sensor 1" + id: test_binary_sensor1 + lambda: |- + return millis() % 1000 < 500; + - platform: template + name: "Test Binary Sensor 2" + id: test_binary_sensor2 + lambda: |- + return millis() % 2000 < 1000; + +switch: + - platform: template + name: "Test Switch 1" + id: test_switch1 + turn_on_action: + - logger.log: "Switch 1 turned on" + turn_off_action: + - logger.log: "Switch 1 turned off" + - platform: template + name: "Test Switch 2" + id: test_switch2 + turn_on_action: + - logger.log: "Switch 2 turned on" + turn_off_action: + - logger.log: "Switch 2 turned off" diff --git a/tests/integration/fixtures/host_mode_empty_string_options.yaml b/tests/integration/fixtures/host_mode_empty_string_options.yaml new file mode 100644 index 0000000000..ab8e6cd005 --- /dev/null +++ b/tests/integration/fixtures/host_mode_empty_string_options.yaml @@ -0,0 +1,58 @@ +esphome: + name: host-empty-string-test + +host: + +api: + batch_delay: 50ms + +select: + - platform: template + name: "Select Empty First" + id: select_empty_first + optimistic: true + options: + - "" # Empty string at the beginning + - "Option A" + - "Option B" + - "Option C" + initial_option: "Option A" + + - platform: template + name: "Select Empty Middle" + id: select_empty_middle + optimistic: true + options: + - "Option 1" + - "Option 2" + - "" # Empty string in the middle + - "Option 3" + - "Option 4" + initial_option: "Option 1" + + - platform: template + name: "Select Empty Last" + id: select_empty_last + optimistic: true + options: + - "Choice X" + - "Choice Y" + - "Choice Z" + - "" # Empty string at the end + initial_option: "Choice X" + +# Add a sensor to ensure we have other entities in the list +sensor: + - platform: template + name: "Test Sensor" + id: test_sensor + lambda: |- + return 42.0; + update_interval: 60s + +binary_sensor: + - platform: template + name: "Test Binary Sensor" + id: test_binary_sensor + lambda: |- + return true; diff --git a/tests/integration/fixtures/host_mode_entity_fields.yaml b/tests/integration/fixtures/host_mode_entity_fields.yaml new file mode 100644 index 0000000000..0bd87ee794 --- /dev/null +++ b/tests/integration/fixtures/host_mode_entity_fields.yaml @@ -0,0 +1,108 @@ +esphome: + name: host-test + +host: + +api: + +logger: + +# Test various entity types with different flag combinations +sensor: + - platform: template + name: "Test Normal Sensor" + id: normal_sensor + update_interval: 1s + lambda: |- + return 42.0; + + - platform: template + name: "Test Internal Sensor" + id: internal_sensor + internal: true + update_interval: 1s + lambda: |- + return 43.0; + + - platform: template + name: "Test Disabled Sensor" + id: disabled_sensor + disabled_by_default: true + update_interval: 1s + lambda: |- + return 44.0; + + - platform: template + name: "Test Mixed Flags Sensor" + id: mixed_flags_sensor + internal: true + entity_category: diagnostic + update_interval: 1s + lambda: |- + return 45.0; + + - platform: template + name: "Test Diagnostic Sensor" + id: diagnostic_sensor + entity_category: diagnostic + update_interval: 1s + lambda: |- + return 46.0; + + - platform: template + name: "Test All Flags Sensor" + id: all_flags_sensor + internal: true + disabled_by_default: true + entity_category: diagnostic + update_interval: 1s + lambda: |- + return 47.0; + +# Also test other entity types to ensure bit-packing works across all +binary_sensor: + - platform: template + name: "Test Binary Sensor" + entity_category: config + lambda: |- + return true; + +text_sensor: + - platform: template + name: "Test Text Sensor" + disabled_by_default: true + lambda: |- + return {"Hello"}; + +number: + - platform: template + name: "Test Number" + initial_value: 50 + min_value: 0 + max_value: 100 + step: 1 + optimistic: true + entity_category: diagnostic + +select: + - platform: template + name: "Test Select" + options: + - "Option 1" + - "Option 2" + initial_option: "Option 1" + optimistic: true + internal: true + +switch: + - platform: template + name: "Test Switch" + optimistic: true + disabled_by_default: true + entity_category: config + +button: + - platform: template + name: "Test Button" + on_press: + - logger.log: "Button pressed" diff --git a/tests/integration/fixtures/host_mode_fan_preset.yaml b/tests/integration/fixtures/host_mode_fan_preset.yaml new file mode 100644 index 0000000000..003f4a7760 --- /dev/null +++ b/tests/integration/fixtures/host_mode_fan_preset.yaml @@ -0,0 +1,34 @@ +esphome: + name: host-test + +host: + +api: + +logger: + +# Test fan with preset modes and speed settings +fan: + - platform: template + name: "Test Fan with Presets" + id: test_fan_presets + speed_count: 5 + preset_modes: + - "Eco" + - "Sleep" + - "Turbo" + has_oscillating: true + has_direction: true + + - platform: template + name: "Test Fan Simple" + id: test_fan_simple + speed_count: 3 + has_oscillating: false + has_direction: false + + - platform: template + name: "Test Fan No Speed" + id: test_fan_no_speed + has_oscillating: true + has_direction: false diff --git a/tests/integration/fixtures/host_mode_many_entities.yaml b/tests/integration/fixtures/host_mode_many_entities.yaml new file mode 100644 index 0000000000..3d1aa36196 --- /dev/null +++ b/tests/integration/fixtures/host_mode_many_entities.yaml @@ -0,0 +1,322 @@ +esphome: + name: host-mode-many-entities + friendly_name: "Host Mode Many Entities Test" + +logger: + +host: + +api: + +sensor: + # 50 test sensors with predictable values for batching test + - platform: template + name: "Test Sensor 1" + lambda: return 1.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 2" + lambda: return 2.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 3" + lambda: return 3.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 4" + lambda: return 4.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 5" + lambda: return 5.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 6" + lambda: return 6.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 7" + lambda: return 7.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 8" + lambda: return 8.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 9" + lambda: return 9.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 10" + lambda: return 10.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 11" + lambda: return 11.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 12" + lambda: return 12.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 13" + lambda: return 13.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 14" + lambda: return 14.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 15" + lambda: return 15.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 16" + lambda: return 16.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 17" + lambda: return 17.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 18" + lambda: return 18.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 19" + lambda: return 19.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 20" + lambda: return 20.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 21" + lambda: return 21.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 22" + lambda: return 22.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 23" + lambda: return 23.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 24" + lambda: return 24.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 25" + lambda: return 25.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 26" + lambda: return 26.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 27" + lambda: return 27.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 28" + lambda: return 28.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 29" + lambda: return 29.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 30" + lambda: return 30.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 31" + lambda: return 31.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 32" + lambda: return 32.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 33" + lambda: return 33.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 34" + lambda: return 34.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 35" + lambda: return 35.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 36" + lambda: return 36.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 37" + lambda: return 37.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 38" + lambda: return 38.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 39" + lambda: return 39.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 40" + lambda: return 40.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 41" + lambda: return 41.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 42" + lambda: return 42.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 43" + lambda: return 43.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 44" + lambda: return 44.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 45" + lambda: return 45.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 46" + lambda: return 46.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 47" + lambda: return 47.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 48" + lambda: return 48.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 49" + lambda: return 49.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 50" + lambda: return 50.0; + update_interval: 0.1s + +# Mixed entity types for comprehensive batching test +binary_sensor: + - platform: template + name: "Test Binary Sensor 1" + lambda: return millis() % 1000 < 500; + - platform: template + name: "Test Binary Sensor 2" + lambda: return millis() % 2000 < 1000; + +switch: + - platform: template + name: "Test Switch 1" + lambda: return true; + turn_on_action: + - logger.log: "Switch 1 ON" + turn_off_action: + - logger.log: "Switch 1 OFF" + - platform: template + name: "Test Switch 2" + lambda: return false; + turn_on_action: + - logger.log: "Switch 2 ON" + turn_off_action: + - logger.log: "Switch 2 OFF" + +text_sensor: + - platform: template + name: "Test Text Sensor 1" + lambda: return std::string("Test Value 1"); + - platform: template + name: "Test Text Sensor 2" + lambda: return std::string("Test Value 2"); + - platform: version + name: "ESPHome Version" + +number: + - platform: template + name: "Test Number" + min_value: 0 + max_value: 100 + step: 1 + lambda: return 50.0; + set_action: + - logger.log: "Number set" + +select: + - platform: template + name: "Test Select" + options: + - "Option 1" + - "Option 2" + initial_option: "Option 1" + optimistic: true + set_action: + - logger.log: "Select changed" + +text: + - platform: template + name: "Test Text" + mode: text + initial_value: "Hello" + set_action: + - logger.log: "Text changed" + +valve: + - platform: template + name: "Test Valve" + open_action: + - logger.log: "Valve opening" + close_action: + - logger.log: "Valve closing" + stop_action: + - logger.log: "Valve stopping" + +alarm_control_panel: + - platform: template + name: "Test Alarm" + codes: + - "1234" + arming_away_time: 0s + arming_home_time: 0s + pending_time: 0s + trigger_time: 300s + restore_mode: ALWAYS_DISARMED + on_disarmed: + - logger.log: "Alarm disarmed" + on_arming: + - logger.log: "Alarm arming" + on_armed_away: + - logger.log: "Alarm armed away" + on_armed_home: + - logger.log: "Alarm armed home" + on_pending: + - logger.log: "Alarm pending" + on_triggered: + - logger.log: "Alarm triggered" + +event: + - platform: template + name: "Test Event" + event_types: + - first_event + - second_event + +button: + - platform: template + name: "Test Button" + on_press: + - logger.log: "Button pressed" diff --git a/tests/integration/fixtures/host_mode_many_entities_multiple_connections.yaml b/tests/integration/fixtures/host_mode_many_entities_multiple_connections.yaml new file mode 100644 index 0000000000..296cb2455f --- /dev/null +++ b/tests/integration/fixtures/host_mode_many_entities_multiple_connections.yaml @@ -0,0 +1,136 @@ +esphome: + name: host-mode-many-entities-multi + friendly_name: "Host Mode Many Entities Multiple Connections Test" + +logger: + +host: + +api: + +sensor: + # 20 test sensors for faster testing with multiple connections + - platform: template + name: "Test Sensor 1" + lambda: return 1.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 2" + lambda: return 2.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 3" + lambda: return 3.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 4" + lambda: return 4.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 5" + lambda: return 5.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 6" + lambda: return 6.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 7" + lambda: return 7.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 8" + lambda: return 8.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 9" + lambda: return 9.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 10" + lambda: return 10.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 11" + lambda: return 11.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 12" + lambda: return 12.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 13" + lambda: return 13.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 14" + lambda: return 14.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 15" + lambda: return 15.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 16" + lambda: return 16.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 17" + lambda: return 17.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 18" + lambda: return 18.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 19" + lambda: return 19.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 20" + lambda: return 20.0; + update_interval: 0.1s + +# Mixed entity types for comprehensive batching test +binary_sensor: + - platform: template + name: "Test Binary Sensor 1" + lambda: return millis() % 1000 < 500; + - platform: template + name: "Test Binary Sensor 2" + lambda: return millis() % 2000 < 1000; + +text_sensor: + - platform: template + name: "Test Text Sensor 1" + lambda: return std::string("Test Value 1"); + - platform: template + name: "Test Text Sensor 2" + lambda: return std::string("Test Value 2"); + - platform: version + name: "ESPHome Version" + +switch: + - platform: template + name: "Test Switch 1" + lambda: return true; + turn_on_action: + - logger.log: "Switch 1 ON" + turn_off_action: + - logger.log: "Switch 1 OFF" + +button: + - platform: template + name: "Test Button" + on_press: + - logger.log: "Button pressed" + +number: + - platform: template + name: "Test Number" + min_value: 0 + max_value: 100 + step: 1 + lambda: return 50.0; + set_action: + - logger.log: "Number set" diff --git a/tests/integration/fixtures/host_mode_noise_encryption.yaml b/tests/integration/fixtures/host_mode_noise_encryption.yaml index 83605e28a3..da817fdc3b 100644 --- a/tests/integration/fixtures/host_mode_noise_encryption.yaml +++ b/tests/integration/fixtures/host_mode_noise_encryption.yaml @@ -5,3 +5,46 @@ api: encryption: key: N4Yle5YirwZhPiHHsdZLdOA73ndj/84veVaLhTvxCuU= logger: + +# Test sensors to verify batching works with noise encryption +sensor: + - platform: template + name: "Noise Test Sensor 1" + lambda: return 1.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 2" + lambda: return 2.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 3" + lambda: return 3.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 4" + lambda: return 4.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 5" + lambda: return 5.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 6" + lambda: return 6.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 7" + lambda: return 7.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 8" + lambda: return 8.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 9" + lambda: return 9.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 10" + lambda: return 10.0; + update_interval: 2s diff --git a/tests/integration/fixtures/large_message_batching.yaml b/tests/integration/fixtures/large_message_batching.yaml new file mode 100644 index 0000000000..1b2d817cd4 --- /dev/null +++ b/tests/integration/fixtures/large_message_batching.yaml @@ -0,0 +1,137 @@ +esphome: + name: large-message-test +host: +api: +logger: + +# Create a select entity with many options to exceed 1390 bytes +select: + - platform: template + name: "Large Select" + id: large_select + optimistic: true + options: + - "Option 000 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 001 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 002 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 003 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 004 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 005 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 006 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 007 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 008 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 009 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 010 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 011 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 012 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 013 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 014 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 015 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 016 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 017 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 018 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 019 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 020 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 021 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 022 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 023 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 024 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 025 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 026 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 027 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 028 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 029 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 030 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 031 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 032 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 033 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 034 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 035 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 036 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 037 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 038 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 039 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 040 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 041 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 042 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 043 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 044 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 045 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 046 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 047 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 048 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 049 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 050 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 051 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 052 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 053 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 054 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 055 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 056 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 057 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 058 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 059 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 060 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 061 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 062 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 063 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 064 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 065 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 066 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 067 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 068 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 069 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 070 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 071 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 072 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 073 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 074 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 075 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 076 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 077 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 078 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 079 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 080 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 081 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 082 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 083 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 084 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 085 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 086 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 087 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 088 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 089 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 090 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 091 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 092 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 093 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 094 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 095 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 096 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 097 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 098 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 099 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + initial_option: "Option 000 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + +# Add some other entities to test batching with the large select +sensor: + - platform: template + name: "Test Sensor" + id: test_sensor + lambda: |- + return 42.0; + update_interval: 1s + +binary_sensor: + - platform: template + name: "Test Binary Sensor" + id: test_binary_sensor + lambda: |- + return true; + +switch: + - platform: template + name: "Test Switch" + id: test_switch + optimistic: true + diff --git a/tests/integration/fixtures/loop_disable_enable.yaml b/tests/integration/fixtures/loop_disable_enable.yaml new file mode 100644 index 0000000000..f19d7f60ca --- /dev/null +++ b/tests/integration/fixtures/loop_disable_enable.yaml @@ -0,0 +1,53 @@ +esphome: + name: loop-test + +host: +api: +logger: + level: DEBUG + +external_components: + - source: + type: local + path: EXTERNAL_COMPONENT_PATH + +loop_test_component: + components: + # Component that disables itself after 10 loops + - id: self_disable_10 + name: "self_disable_10" + disable_after: 10 + + # Component that never disables itself (for re-enable test) + - id: normal_component + name: "normal_component" + disable_after: 0 + + # Component that tests enable when already enabled + - id: redundant_enable + name: "redundant_enable" + test_redundant_operations: true + disable_after: 0 + + # Component that tests disable when already disabled + - id: redundant_disable + name: "redundant_disable" + test_redundant_operations: true + disable_after: 10 + + # ISR test component that uses enable_loop_soon_any_context + isr_components: + - id: isr_test + name: "isr_test" + +# Interval to re-enable the self_disable_10 component after some time +interval: + - interval: 0.5s + then: + - if: + condition: + lambda: 'return id(self_disable_10).get_loop_count() == 10;' + then: + - logger.log: "Re-enabling self_disable_10 via service" + - loop_test_component.enable: + id: self_disable_10 diff --git a/tests/integration/test_api_message_size_batching.py b/tests/integration/test_api_message_size_batching.py new file mode 100644 index 0000000000..631e64825e --- /dev/null +++ b/tests/integration/test_api_message_size_batching.py @@ -0,0 +1,194 @@ +"""Integration test for API batching with various message sizes.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityState, NumberInfo, SelectInfo, TextInfo, TextSensorInfo +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_api_message_size_batching( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test API can batch messages of various sizes correctly.""" + # Write, compile and run the ESPHome device, then connect to API + loop = asyncio.get_running_loop() + async with run_compiled(yaml_config), api_client_connected() as client: + # Verify we can get device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "message-size-batching-test" + + # List entities - this will batch various sized messages together + entity_info, services = await asyncio.wait_for( + client.list_entities_services(), timeout=5.0 + ) + + # Count different entity types + selects = [] + text_sensors = [] + text_inputs = [] + numbers = [] + other_entities = [] + + for entity in entity_info: + if isinstance(entity, SelectInfo): + selects.append(entity) + elif isinstance(entity, TextSensorInfo): + text_sensors.append(entity) + elif isinstance(entity, TextInfo): + text_inputs.append(entity) + elif isinstance(entity, NumberInfo): + numbers.append(entity) + else: + other_entities.append(entity) + + # Verify we have our test entities - exact counts + assert len(selects) == 3, ( + f"Expected exactly 3 select entities, got {len(selects)}" + ) + assert len(text_sensors) == 3, ( + f"Expected exactly 3 text sensor entities, got {len(text_sensors)}" + ) + assert len(text_inputs) == 1, ( + f"Expected exactly 1 text input entity, got {len(text_inputs)}" + ) + + # Collect all select entity object_ids for error messages + select_ids = [s.object_id for s in selects] + + # Find our specific test entities + small_select = None + medium_select = None + large_select = None + + for select in selects: + if select.object_id == "small_select": + small_select = select + elif select.object_id == "medium_select": + medium_select = select + elif ( + select.object_id + == "large_select_with_many_options_to_create_larger_payload" + ): + large_select = select + + assert small_select is not None, ( + f"Could not find small_select entity. Found: {select_ids}" + ) + assert medium_select is not None, ( + f"Could not find medium_select entity. Found: {select_ids}" + ) + assert large_select is not None, ( + f"Could not find large_select entity. Found: {select_ids}" + ) + + # Verify the selects have the expected number of options + assert len(small_select.options) == 2, ( + f"Expected 2 options for small_select, got {len(small_select.options)}" + ) + assert len(medium_select.options) == 20, ( + f"Expected 20 options for medium_select, got {len(medium_select.options)}" + ) + assert len(large_select.options) == 50, ( + f"Expected 50 options for large_select, got {len(large_select.options)}" + ) + + # Collect all text sensor object_ids for error messages + text_sensor_ids = [t.object_id for t in text_sensors] + + # Verify text sensors with different value lengths + short_text_sensor = None + medium_text_sensor = None + long_text_sensor = None + + for text_sensor in text_sensors: + if text_sensor.object_id == "short_text_sensor": + short_text_sensor = text_sensor + elif text_sensor.object_id == "medium_text_sensor": + medium_text_sensor = text_sensor + elif text_sensor.object_id == "long_text_sensor_with_very_long_value": + long_text_sensor = text_sensor + + assert short_text_sensor is not None, ( + f"Could not find short_text_sensor. Found: {text_sensor_ids}" + ) + assert medium_text_sensor is not None, ( + f"Could not find medium_text_sensor. Found: {text_sensor_ids}" + ) + assert long_text_sensor is not None, ( + f"Could not find long_text_sensor. Found: {text_sensor_ids}" + ) + + # Check text input which can have a long max_length + text_input = None + text_input_ids = [t.object_id for t in text_inputs] + + for ti in text_inputs: + if ti.object_id == "test_text_input": + text_input = ti + break + + assert text_input is not None, ( + f"Could not find test_text_input. Found: {text_input_ids}" + ) + assert text_input.max_length == 255, ( + f"Expected max_length 255, got {text_input.max_length}" + ) + + # Verify total entity count - messages of various sizes were batched successfully + # We have: 3 selects + 3 text sensors + 1 text input + 1 number = 8 total + total_entities = len(entity_info) + assert total_entities == 8, f"Expected exactly 8 entities, got {total_entities}" + + # Check we have the expected entity types + assert len(numbers) == 1, ( + f"Expected exactly 1 number entity, got {len(numbers)}" + ) + assert len(other_entities) == 0, ( + f"Unexpected entity types found: {[type(e).__name__ for e in other_entities]}" + ) + + # Subscribe to state changes to verify batching works + # Collect keys from entity info to know what states to expect + expected_keys = {entity.key for entity in entity_info} + assert len(expected_keys) == 8, ( + f"Expected 8 unique entity keys, got {len(expected_keys)}" + ) + + received_keys: set[int] = set() + states_future: asyncio.Future[None] = loop.create_future() + + def on_state(state: EntityState) -> None: + """Track when states are received.""" + received_keys.add(state.key) + # Check if we've received states from all expected entities + if expected_keys.issubset(received_keys) and not states_future.done(): + states_future.set_result(None) + + client.subscribe_states(on_state) + + # Wait for states with timeout + try: + await asyncio.wait_for(states_future, timeout=5.0) + except asyncio.TimeoutError: + missing_keys = expected_keys - received_keys + pytest.fail( + f"Did not receive states from all entities within 5 seconds. " + f"Missing keys: {missing_keys}, " + f"Received {len(received_keys)} of {len(expected_keys)} expected states" + ) + + # Verify we received states from all entities + assert expected_keys.issubset(received_keys) + + # Check that various message sizes were handled correctly + # Small messages (4-byte header): type < 128, payload < 128 + # Medium messages (5-byte header): type < 128, payload 128-16383 OR type 128+, payload < 128 + # Large messages (6-byte header): type 128+, payload 128-16383 diff --git a/tests/integration/test_host_mode_batch_delay.py b/tests/integration/test_host_mode_batch_delay.py new file mode 100644 index 0000000000..5165b90e47 --- /dev/null +++ b/tests/integration/test_host_mode_batch_delay.py @@ -0,0 +1,80 @@ +"""Integration test for API batch_delay setting.""" + +from __future__ import annotations + +import asyncio +import time + +from aioesphomeapi import EntityState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_batch_delay( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test API with batch_delay set to 0ms - messages should be sent immediately without batching.""" + # Write, compile and run the ESPHome device, then connect to API + loop = asyncio.get_running_loop() + async with run_compiled(yaml_config), api_client_connected() as client: + # Verify we can get device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "host-batch-delay-test" + + # Subscribe to state changes + states: dict[int, EntityState] = {} + state_timestamps: dict[int, float] = {} + entity_count_future: asyncio.Future[int] = loop.create_future() + + def on_state(state: EntityState) -> None: + """Track when states are received.""" + states[state.key] = state + state_timestamps[state.key] = time.monotonic() + # When we have received all expected entities, resolve the future + if len(states) >= 7 and not entity_count_future.done(): + entity_count_future.set_result(len(states)) + + client.subscribe_states(on_state) + + # Wait for states from all entities with timeout + try: + entity_count = await asyncio.wait_for(entity_count_future, timeout=5.0) + except asyncio.TimeoutError: + pytest.fail( + f"Did not receive states from at least 7 entities within 5 seconds. " + f"Received {len(states)} states" + ) + + # Verify we received all states + assert entity_count >= 7, f"Expected at least 7 entities, got {entity_count}" + assert len(states) >= 7 # 3 sensors + 2 binary sensors + 2 switches + + # When batch_delay is 0ms, states are sent immediately without batching + # This means each state arrives in its own packet, which may actually be slower + # than batching due to network overhead + if state_timestamps: + first_timestamp = min(state_timestamps.values()) + last_timestamp = max(state_timestamps.values()) + time_spread = last_timestamp - first_timestamp + + # With batch_delay=0ms, states arrive individually which may take longer + # We just verify they all arrive within a reasonable time + assert time_spread < 1.0, f"States took {time_spread:.3f}s to arrive" + + # Also test list_entities - with batch_delay=0ms each entity is sent separately + start_time = time.monotonic() + entity_info, services = await client.list_entities_services() + end_time = time.monotonic() + + list_time = end_time - start_time + + # Verify we got all expected entities + assert len(entity_info) >= 7 # 3 sensors + 2 binary sensors + 2 switches + + # With batch_delay=0ms, listing sends each entity separately which may be slower + assert list_time < 1.0, f"list_entities took {list_time:.3f}s" diff --git a/tests/integration/test_host_mode_empty_string_options.py b/tests/integration/test_host_mode_empty_string_options.py new file mode 100644 index 0000000000..d2df839a75 --- /dev/null +++ b/tests/integration/test_host_mode_empty_string_options.py @@ -0,0 +1,110 @@ +"""Integration test for protobuf encoding of empty string options in select entities.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityState, SelectInfo +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_empty_string_options( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that select entities with empty string options are correctly encoded in protobuf messages. + + This tests the fix for the bug where the force parameter was not passed in encode_string, + causing empty strings in repeated fields to be skipped during encoding but included in + size calculation, leading to protobuf decoding errors. + """ + # Write, compile and run the ESPHome device, then connect to API + loop = asyncio.get_running_loop() + async with run_compiled(yaml_config), api_client_connected() as client: + # Verify we can get device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "host-empty-string-test" + + # Get list of entities - this will encode ListEntitiesSelectResponse messages + # with empty string options that would trigger the bug + entity_info, services = await client.list_entities_services() + + # Find our select entities + select_entities = [e for e in entity_info if isinstance(e, SelectInfo)] + assert len(select_entities) == 3, ( + f"Expected 3 select entities, got {len(select_entities)}" + ) + + # Verify each select entity by name and check their options + selects_by_name = {e.name: e for e in select_entities} + + # Check "Select Empty First" - empty string at beginning + assert "Select Empty First" in selects_by_name + empty_first = selects_by_name["Select Empty First"] + assert len(empty_first.options) == 4 + assert empty_first.options[0] == "" # Empty string at beginning + assert empty_first.options[1] == "Option A" + assert empty_first.options[2] == "Option B" + assert empty_first.options[3] == "Option C" + + # Check "Select Empty Middle" - empty string in middle + assert "Select Empty Middle" in selects_by_name + empty_middle = selects_by_name["Select Empty Middle"] + assert len(empty_middle.options) == 5 + assert empty_middle.options[0] == "Option 1" + assert empty_middle.options[1] == "Option 2" + assert empty_middle.options[2] == "" # Empty string in middle + assert empty_middle.options[3] == "Option 3" + assert empty_middle.options[4] == "Option 4" + + # Check "Select Empty Last" - empty string at end + assert "Select Empty Last" in selects_by_name + empty_last = selects_by_name["Select Empty Last"] + assert len(empty_last.options) == 4 + assert empty_last.options[0] == "Choice X" + assert empty_last.options[1] == "Choice Y" + assert empty_last.options[2] == "Choice Z" + assert empty_last.options[3] == "" # Empty string at end + + # If we got here without protobuf decoding errors, the fix is working + # The bug would have caused "Invalid protobuf message" errors with trailing bytes + + # Also verify we can interact with the select entities + # Subscribe to state changes + states: dict[int, EntityState] = {} + state_change_future: asyncio.Future[None] = loop.create_future() + + def on_state(state: EntityState) -> None: + """Track state changes.""" + states[state.key] = state + # When we receive the state change for our select, resolve the future + if state.key == empty_first.key and not state_change_future.done(): + state_change_future.set_result(None) + + client.subscribe_states(on_state) + + # Try setting a select to an empty string option + # This further tests that empty strings are handled correctly + client.select_command(empty_first.key, "") + + # Wait for state update with timeout + try: + await asyncio.wait_for(state_change_future, timeout=5.0) + except asyncio.TimeoutError: + pytest.fail( + "Did not receive state update after setting select to empty string" + ) + + # Verify the state was set to empty string + assert empty_first.key in states + select_state = states[empty_first.key] + assert hasattr(select_state, "state") + assert select_state.state == "" + + # The test passes if no protobuf decoding errors occurred + # With the bug, we would have gotten "Invalid protobuf message" errors diff --git a/tests/integration/test_host_mode_entity_fields.py b/tests/integration/test_host_mode_entity_fields.py new file mode 100644 index 0000000000..cf3fa6916a --- /dev/null +++ b/tests/integration/test_host_mode_entity_fields.py @@ -0,0 +1,93 @@ +"""Integration test for entity bit-packed fields.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityCategory, EntityState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_entity_fields( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test entity bit-packed fields work correctly with all possible values.""" + # Write, compile and run the ESPHome device, then connect to API + async with run_compiled(yaml_config), api_client_connected() as client: + # Get all entities + entities = await client.list_entities_services() + + # Create a map of entity names to entity info + entity_map = {} + for entity in entities[0]: + if hasattr(entity, "name"): + entity_map[entity.name] = entity + + # Test entities that should be visible via API (non-internal) + visible_test_cases = [ + # (entity_name, expected_disabled_by_default, expected_entity_category) + ("Test Normal Sensor", False, EntityCategory.NONE), + ("Test Disabled Sensor", True, EntityCategory.NONE), + ("Test Diagnostic Sensor", False, EntityCategory.DIAGNOSTIC), + ("Test Switch", True, EntityCategory.CONFIG), + ("Test Binary Sensor", False, EntityCategory.CONFIG), + ("Test Number", False, EntityCategory.DIAGNOSTIC), + ] + + # Test entities that should NOT be visible via API (internal) + internal_entities = [ + "Test Internal Sensor", + "Test Mixed Flags Sensor", + "Test All Flags Sensor", + "Test Select", + ] + + # Verify visible entities + for entity_name, expected_disabled, expected_category in visible_test_cases: + assert entity_name in entity_map, ( + f"Entity '{entity_name}' not found - it should be visible via API" + ) + entity = entity_map[entity_name] + + # Check disabled_by_default flag + assert entity.disabled_by_default == expected_disabled, ( + f"{entity_name}: disabled_by_default flag mismatch - " + f"expected {expected_disabled}, got {entity.disabled_by_default}" + ) + + # Check entity_category + assert entity.entity_category == expected_category, ( + f"{entity_name}: entity_category mismatch - " + f"expected {expected_category}, got {entity.entity_category}" + ) + + # Verify internal entities are NOT visible + for entity_name in internal_entities: + assert entity_name not in entity_map, ( + f"Entity '{entity_name}' found in API response - " + f"internal entities should not be exposed via API" + ) + + # Subscribe to states to verify has_state flag works + states: dict[int, EntityState] = {} + state_received = asyncio.Event() + + def on_state(state: EntityState) -> None: + states[state.key] = state + state_received.set() + + client.subscribe_states(on_state) + + # Wait for at least one state + try: + await asyncio.wait_for(state_received.wait(), timeout=5.0) + except asyncio.TimeoutError: + pytest.fail("No states received within 5 seconds") + + # Verify we received states (which means has_state flag is working) + assert len(states) > 0, "No states received - has_state flag may not be working" diff --git a/tests/integration/test_host_mode_fan_preset.py b/tests/integration/test_host_mode_fan_preset.py new file mode 100644 index 0000000000..1d956a7290 --- /dev/null +++ b/tests/integration/test_host_mode_fan_preset.py @@ -0,0 +1,152 @@ +"""Integration test for fan preset mode behavior.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import FanInfo, FanState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_fan_preset( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test fan preset mode behavior according to Home Assistant guidelines.""" + # Write, compile and run the ESPHome device, then connect to API + async with run_compiled(yaml_config), api_client_connected() as client: + # Get all fan entities + entities = await client.list_entities_services() + fans: list[FanInfo] = [] + for entity_list in entities: + for entity in entity_list: + if isinstance(entity, FanInfo): + fans.append(entity) + + # Create a map of fan names to entity info + fan_map = {fan.name: fan for fan in fans} + + # Verify we have our test fans + assert "Test Fan with Presets" in fan_map + assert "Test Fan Simple" in fan_map + assert "Test Fan No Speed" in fan_map + + # Get fan with presets + fan_presets = fan_map["Test Fan with Presets"] + assert fan_presets.supports_speed is True + assert fan_presets.supported_speed_count == 5 + assert fan_presets.supports_oscillation is True + assert fan_presets.supports_direction is True + assert set(fan_presets.supported_preset_modes) == {"Eco", "Sleep", "Turbo"} + + # Subscribe to states + states: dict[int, FanState] = {} + state_event = asyncio.Event() + + def on_state(state: FanState) -> None: + if isinstance(state, FanState): + states[state.key] = state + state_event.set() + + client.subscribe_states(on_state) + + # Test 1: Turn on fan without speed or preset - should set speed to 100% + state_event.clear() + client.fan_command( + key=fan_presets.key, + state=True, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_presets.key] + assert fan_state.state is True + assert fan_state.speed_level == 5 # Should be max speed (100%) + assert fan_state.preset_mode == "" + + # Turn off + state_event.clear() + client.fan_command( + key=fan_presets.key, + state=False, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + # Test 2: Turn on fan with preset mode - should NOT set speed to 100% + state_event.clear() + client.fan_command( + key=fan_presets.key, + state=True, + preset_mode="Eco", + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_presets.key] + assert fan_state.state is True + assert fan_state.preset_mode == "Eco" + # Speed should be whatever the preset sets, not forced to 100% + + # Test 3: Setting speed should clear preset mode + state_event.clear() + client.fan_command( + key=fan_presets.key, + speed_level=3, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_presets.key] + assert fan_state.state is True + assert fan_state.speed_level == 3 + assert fan_state.preset_mode == "" # Preset mode should be cleared + + # Test 4: Setting preset mode should work when fan is already on + state_event.clear() + client.fan_command( + key=fan_presets.key, + preset_mode="Sleep", + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_presets.key] + assert fan_state.state is True + assert fan_state.preset_mode == "Sleep" + + # Turn off + state_event.clear() + client.fan_command( + key=fan_presets.key, + state=False, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + # Test 5: Turn on fan with specific speed + state_event.clear() + client.fan_command( + key=fan_presets.key, + state=True, + speed_level=2, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_presets.key] + assert fan_state.state is True + assert fan_state.speed_level == 2 + assert fan_state.preset_mode == "" + + # Test 6: Test fan with no speed support + fan_no_speed = fan_map["Test Fan No Speed"] + assert fan_no_speed.supports_speed is False + + state_event.clear() + client.fan_command( + key=fan_no_speed.key, + state=True, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_no_speed.key] + assert fan_state.state is True + # No speed should be set for fans that don't support speed diff --git a/tests/integration/test_host_mode_many_entities.py b/tests/integration/test_host_mode_many_entities.py new file mode 100644 index 0000000000..d5622e6fa4 --- /dev/null +++ b/tests/integration/test_host_mode_many_entities.py @@ -0,0 +1,57 @@ +"""Integration test for many entities to test API batching.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_many_entities( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test API batching with many entities of different types.""" + # Write, compile and run the ESPHome device, then connect to API + loop = asyncio.get_running_loop() + async with run_compiled(yaml_config), api_client_connected() as client: + # Subscribe to state changes + states: dict[int, EntityState] = {} + entity_count_future: asyncio.Future[int] = loop.create_future() + + def on_state(state: EntityState) -> None: + states[state.key] = state + # When we have received states from a good number of entities, resolve the future + if len(states) >= 50 and not entity_count_future.done(): + entity_count_future.set_result(len(states)) + + client.subscribe_states(on_state) + + # Wait for states from at least 50 entities with timeout + try: + entity_count = await asyncio.wait_for(entity_count_future, timeout=10.0) + except asyncio.TimeoutError: + pytest.fail( + f"Did not receive states from at least 50 entities within 10 seconds. " + f"Received {len(states)} states: {list(states.keys())}" + ) + + # Verify we received a good number of entity states + assert entity_count >= 50, f"Expected at least 50 entities, got {entity_count}" + assert len(states) >= 50, f"Expected at least 50 states, got {len(states)}" + + # Verify we have different entity types by checking some expected values + sensor_states = [ + s + for s in states.values() + if hasattr(s, "state") and isinstance(s.state, float) + ] + + assert len(sensor_states) >= 50, ( + f"Expected at least 50 sensor states, got {len(sensor_states)}" + ) diff --git a/tests/integration/test_host_mode_many_entities_multiple_connections.py b/tests/integration/test_host_mode_many_entities_multiple_connections.py new file mode 100644 index 0000000000..a4e5f8a45c --- /dev/null +++ b/tests/integration/test_host_mode_many_entities_multiple_connections.py @@ -0,0 +1,71 @@ +"""Integration test for shared buffer optimization with multiple API connections.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_many_entities_multiple_connections( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test shared buffer optimization with multiple API connections.""" + # Write, compile and run the ESPHome device + loop = asyncio.get_running_loop() + async with ( + run_compiled(yaml_config), + api_client_connected() as client1, + api_client_connected() as client2, + ): + # Subscribe both clients to state changes + states1: dict[int, EntityState] = {} + states2: dict[int, EntityState] = {} + + client1_ready = loop.create_future() + client2_ready = loop.create_future() + + def on_state1(state: EntityState) -> None: + states1[state.key] = state + if len(states1) >= 20 and not client1_ready.done(): + client1_ready.set_result(len(states1)) + + def on_state2(state: EntityState) -> None: + states2[state.key] = state + if len(states2) >= 20 and not client2_ready.done(): + client2_ready.set_result(len(states2)) + + client1.subscribe_states(on_state1) + client2.subscribe_states(on_state2) + + # Wait for both clients to receive states + try: + count1, count2 = await asyncio.gather( + asyncio.wait_for(client1_ready, timeout=10.0), + asyncio.wait_for(client2_ready, timeout=10.0), + ) + except asyncio.TimeoutError: + pytest.fail( + f"One or both clients did not receive enough states within 10 seconds. " + f"Client1: {len(states1)}, Client2: {len(states2)}" + ) + + # Verify both clients received states successfully + assert count1 >= 20, ( + f"Client 1 should have received at least 20 states, got {count1}" + ) + assert count2 >= 20, ( + f"Client 2 should have received at least 20 states, got {count2}" + ) + + # Verify both clients received the same entity keys (same device state) + common_keys = set(states1.keys()) & set(states2.keys()) + assert len(common_keys) >= 20, ( + f"Expected at least 20 common entity keys, got {len(common_keys)}" + ) diff --git a/tests/integration/test_large_message_batching.py b/tests/integration/test_large_message_batching.py new file mode 100644 index 0000000000..399fd39dd3 --- /dev/null +++ b/tests/integration/test_large_message_batching.py @@ -0,0 +1,59 @@ +"""Integration test for API handling of large messages exceeding batch size.""" + +from __future__ import annotations + +from aioesphomeapi import SelectInfo +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_large_message_batching( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test API can handle large messages (>1390 bytes) in batches.""" + # Write, compile and run the ESPHome device, then connect to API + async with run_compiled(yaml_config), api_client_connected() as client: + # Verify we can get device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "large-message-test" + + # List entities - this will include our select with many options + entity_info, services = await client.list_entities_services() + + # Find our large select entity + large_select = None + for entity in entity_info: + if isinstance(entity, SelectInfo) and entity.object_id == "large_select": + large_select = entity + break + + assert large_select is not None, "Could not find large_select entity" + + # Verify the select has all its options + # We created 100 options with long names + assert len(large_select.options) == 100, ( + f"Expected 100 options, got {len(large_select.options)}" + ) + + # Verify all options are present and correct + for i in range(100): + expected_option = f"Option {i:03d} - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + assert expected_option in large_select.options, ( + f"Missing option: {expected_option}" + ) + + # Also verify we can still receive other entities in the same batch + # Count total entities - should have at least our select plus some sensors + entity_count = len(entity_info) + assert entity_count >= 4, f"Expected at least 4 entities, got {entity_count}" + + # Verify we have different entity types (not just selects) + entity_types = {type(entity).__name__ for entity in entity_info} + assert len(entity_types) >= 2, ( + f"Expected multiple entity types, got {entity_types}" + ) diff --git a/tests/integration/test_loop_disable_enable.py b/tests/integration/test_loop_disable_enable.py new file mode 100644 index 0000000000..d5f868aa93 --- /dev/null +++ b/tests/integration/test_loop_disable_enable.py @@ -0,0 +1,207 @@ +"""Integration test for loop disable/enable functionality.""" + +from __future__ import annotations + +import asyncio +from pathlib import Path +import re + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_loop_disable_enable( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that components can disable and enable their loop() method.""" + # Get the absolute path to the external components directory + external_components_path = str( + Path(__file__).parent / "fixtures" / "external_components" + ) + + # Replace the placeholder in the YAML config with the actual path + yaml_config = yaml_config.replace( + "EXTERNAL_COMPONENT_PATH", external_components_path + ) + + # Track log messages and events + log_messages: list[str] = [] + + # Event fired when self_disable_10 component disables itself after 10 loops + self_disable_10_disabled = asyncio.Event() + # Event fired when normal_component reaches 10 loops + normal_component_10_loops = asyncio.Event() + # Event fired when redundant_enable component tests enabling when already enabled + redundant_enable_tested = asyncio.Event() + # Event fired when redundant_disable component tests disabling when already disabled + redundant_disable_tested = asyncio.Event() + # Event fired when self_disable_10 component is re-enabled and runs again (count > 10) + self_disable_10_re_enabled = asyncio.Event() + # Events for ISR component testing + isr_component_disabled = asyncio.Event() + isr_component_re_enabled = asyncio.Event() + isr_component_pure_re_enabled = asyncio.Event() + + # Track loop counts for components + self_disable_10_counts: list[int] = [] + normal_component_counts: list[int] = [] + isr_component_counts: list[int] = [] + + def on_log_line(line: str) -> None: + """Process each log line from the process output.""" + # Strip ANSI color codes + clean_line = re.sub(r"\x1b\[[0-9;]*m", "", line) + + if ( + "loop_test_component" not in clean_line + and "loop_test_isr_component" not in clean_line + ): + return + + log_messages.append(clean_line) + + # Track specific events using the cleaned line + if "[self_disable_10]" in clean_line: + if "Loop count:" in clean_line: + # Extract loop count + try: + count = int(clean_line.split("Loop count: ")[1]) + self_disable_10_counts.append(count) + # Check if component was re-enabled (count > 10) + if count > 10: + self_disable_10_re_enabled.set() + except (IndexError, ValueError): + pass + elif "Disabling self after 10 loops" in clean_line: + self_disable_10_disabled.set() + + elif "[normal_component]" in clean_line and "Loop count:" in clean_line: + try: + count = int(clean_line.split("Loop count: ")[1]) + normal_component_counts.append(count) + if count >= 10: + normal_component_10_loops.set() + except (IndexError, ValueError): + pass + + elif ( + "[redundant_enable]" in clean_line + and "Testing enable when already enabled" in clean_line + ): + redundant_enable_tested.set() + + elif ( + "[redundant_disable]" in clean_line + and "Testing disable when will be disabled" in clean_line + ): + redundant_disable_tested.set() + + # ISR component events + elif "[isr_test]" in clean_line: + if "ISR component loop count:" in clean_line: + count = int(clean_line.split("ISR component loop count: ")[1]) + isr_component_counts.append(count) + elif "Disabling after 5 loops" in clean_line: + isr_component_disabled.set() + elif "Running after ISR re-enable!" in clean_line: + isr_component_re_enabled.set() + elif "Running after pure ISR re-enable!" in clean_line: + isr_component_pure_re_enabled.set() + + # Write, compile and run the ESPHome device with log callback + async with ( + run_compiled(yaml_config, line_callback=on_log_line), + api_client_connected() as client, + ): + # Verify we can connect and get device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "loop-test" + + # Wait for self_disable_10 to disable itself + try: + await asyncio.wait_for(self_disable_10_disabled.wait(), timeout=10.0) + except asyncio.TimeoutError: + pytest.fail("self_disable_10 did not disable itself within 10 seconds") + + # Verify it ran at least 10 times before disabling + assert len([c for c in self_disable_10_counts if c <= 10]) == 10, ( + f"Expected exactly 10 loops before disable, got {[c for c in self_disable_10_counts if c <= 10]}" + ) + assert self_disable_10_counts[:10] == list(range(1, 11)), ( + f"Expected first 10 counts to be 1-10, got {self_disable_10_counts[:10]}" + ) + + # Wait for normal_component to run at least 10 times + try: + await asyncio.wait_for(normal_component_10_loops.wait(), timeout=10.0) + except asyncio.TimeoutError: + pytest.fail( + f"normal_component did not reach 10 loops within timeout, got {len(normal_component_counts)}" + ) + + # Wait for redundant operation tests + try: + await asyncio.wait_for(redundant_enable_tested.wait(), timeout=10.0) + except asyncio.TimeoutError: + pytest.fail("redundant_enable did not test enabling when already enabled") + + try: + await asyncio.wait_for(redundant_disable_tested.wait(), timeout=10.0) + except asyncio.TimeoutError: + pytest.fail( + "redundant_disable did not test disabling when will be disabled" + ) + + # Wait to see if self_disable_10 gets re-enabled + try: + await asyncio.wait_for(self_disable_10_re_enabled.wait(), timeout=5.0) + except asyncio.TimeoutError: + pytest.fail("self_disable_10 was not re-enabled within 5 seconds") + + # Component was re-enabled - verify it ran more times + later_self_disable_counts = [c for c in self_disable_10_counts if c > 10] + assert later_self_disable_counts, ( + "self_disable_10 was re-enabled but did not run additional times" + ) + + # Test ISR component functionality + # Wait for ISR component to disable itself after 5 loops + try: + await asyncio.wait_for(isr_component_disabled.wait(), timeout=3.0) + except asyncio.TimeoutError: + pytest.fail("ISR component did not disable itself within 3 seconds") + + # Verify it ran exactly 5 times before disabling + first_run_counts = [c for c in isr_component_counts if c <= 5] + assert len(first_run_counts) == 5, ( + f"Expected 5 loops before disable, got {first_run_counts}" + ) + + # Wait for component to be re-enabled by periodic ISR simulation and run again + try: + await asyncio.wait_for(isr_component_re_enabled.wait(), timeout=2.0) + except asyncio.TimeoutError: + pytest.fail("ISR component was not re-enabled after ISR call") + + # Verify it's running again after ISR enable + count_after_isr = len(isr_component_counts) + assert count_after_isr > 5, ( + f"Component didn't run after ISR enable: got {count_after_isr} counts total" + ) + + # Wait for pure ISR enable (no main loop enable) to work + try: + await asyncio.wait_for(isr_component_pure_re_enabled.wait(), timeout=2.0) + except asyncio.TimeoutError: + pytest.fail("ISR component was not re-enabled by pure ISR call") + + # Verify it ran after pure ISR enable + final_count = len(isr_component_counts) + assert final_count > 10, ( + f"Component didn't run after pure ISR enable: got {final_count} counts total" + ) diff --git a/tests/integration/types.py b/tests/integration/types.py index 6fc3e9435e..5e4bfaa29d 100644 --- a/tests/integration/types.py +++ b/tests/integration/types.py @@ -13,7 +13,19 @@ from aioesphomeapi import APIClient ConfigWriter = Callable[[str, str | None], Awaitable[Path]] CompileFunction = Callable[[Path], Awaitable[Path]] RunFunction = Callable[[Path], Awaitable[asyncio.subprocess.Process]] -RunCompiledFunction = Callable[[str, str | None], AbstractAsyncContextManager[None]] + + +class RunCompiledFunction(Protocol): + """Protocol for run_compiled function with optional line callback.""" + + def __call__( # noqa: E704 + self, + yaml_content: str, + filename: str | None = None, + line_callback: Callable[[str], None] | None = None, + ) -> AbstractAsyncContextManager[None]: ... + + WaitFunction = Callable[[APIClient, float], Awaitable[bool]] diff --git a/tests/test_build_components/build_components_base.esp32-p4-idf.yaml b/tests/test_build_components/build_components_base.esp32-p4-idf.yaml new file mode 100644 index 0000000000..e2b975f643 --- /dev/null +++ b/tests/test_build_components/build_components_base.esp32-p4-idf.yaml @@ -0,0 +1,18 @@ +esphome: + name: componenttestesp32p4idf + friendly_name: $component_name + +esp32: + board: esp32-p4-evboard + framework: + type: esp-idf + +logger: + level: VERY_VERBOSE + +packages: + component_under_test: !include + file: $component_test_file + vars: + component_test_file: $component_test_file +