mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/dev' into multi_device
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							| @@ -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: | | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -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' | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										2
									
								
								Doxyfile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Doxyfile
									
									
									
									
									
								
							| @@ -48,7 +48,7 @@ PROJECT_NAME           = ESPHome | ||||
| # could be handy for archiving the generated documentation or if some version | ||||
| # control system is used. | ||||
|  | ||||
| PROJECT_NUMBER         = 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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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; } | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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) \ | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| #include "ads1118.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| #include "ags10.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| #include <cinttypes> | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|       } | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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() { | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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()) { | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|   | ||||
| @@ -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 = [] | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -11,6 +11,7 @@ | ||||
| #include "esphome/core/entity_base.h" | ||||
|  | ||||
| #include <vector> | ||||
| #include <functional> | ||||
|  | ||||
| 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<DeferredMessage> 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::Socket> 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<esp32_camera::CameraImage> 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<uint8_t> &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<uint8_t> &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<typename EntityT, typename StateT, typename... Args> | ||||
|   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<send_message_t>(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<typename ResponseT> | ||||
|   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<enums::EntityCategory>(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<APIFrameHelper> 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<uint8_t> proto_write_buffer_; | ||||
|   std::unique_ptr<APIFrameHelper> 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<BatchItem> 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 | ||||
|   | ||||
| @@ -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 <cstring> | ||||
| @@ -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<uint8_t> *raw_buffer = buffer.get_buffer(); | ||||
|   uint16_t payload_len = static_cast<uint16_t>(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<PacketInfo> packets; | ||||
|   packets.emplace_back(type, 0, payload_len); | ||||
|  | ||||
|   return write_protobuf_packets(buffer, packets); | ||||
| } | ||||
|  | ||||
| APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &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; | ||||
|   } | ||||
|  | ||||
|   if (packets.empty()) { | ||||
|     return APIError::OK; | ||||
|   } | ||||
|  | ||||
|   std::vector<uint8_t> *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; | ||||
|   this->reusable_iovs_.clear(); | ||||
|   this->reusable_iovs_.reserve(packets.size()); | ||||
|  | ||||
|   // 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_); | ||||
|   // 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 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(); | ||||
|     // 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 later after encryption | ||||
|     // 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 position 7 | ||||
|     // 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); | ||||
|   // 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); | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|   uint16_t total_len = 3 + mbuf.size; | ||||
|     // 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; | ||||
|   // 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; | ||||
|  | ||||
|   // write raw to not have two packets sent if NAGLE disabled | ||||
|   return this->write_raw_(&iov, 1); | ||||
|     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<uint8_t *>(output), len)) { | ||||
|     ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!"); | ||||
|     ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting"); | ||||
|     arch_restart(); | ||||
|   } | ||||
| } | ||||
| @@ -1004,24 +1026,40 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) { | ||||
|   return APIError::OK; | ||||
| } | ||||
| APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) { | ||||
|   std::vector<uint8_t> *raw_buffer = buffer.get_buffer(); | ||||
|   uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_); | ||||
|  | ||||
|   // Use write_protobuf_packets with a single packet | ||||
|   std::vector<PacketInfo> packets; | ||||
|   packets.emplace_back(type, 0, payload_len); | ||||
|  | ||||
|   return write_protobuf_packets(buffer, packets); | ||||
| } | ||||
|  | ||||
| APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, | ||||
|                                                          const std::vector<PacketInfo> &packets) { | ||||
|   if (state_ != State::DATA) { | ||||
|     return APIError::BAD_STATE; | ||||
|   } | ||||
|  | ||||
|   std::vector<uint8_t> *raw_buffer = buffer.get_buffer(); | ||||
|   // Message data starts after padding (frame_header_padding_ = 6) | ||||
|   uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_); | ||||
|   if (packets.empty()) { | ||||
|     return APIError::OK; | ||||
|   } | ||||
|  | ||||
|   // Calculate varint sizes for header components | ||||
|   std::vector<uint8_t> *raw_buffer = buffer.get_buffer(); | ||||
|   this->reusable_iovs_.clear(); | ||||
|   this->reusable_iovs_.reserve(packets.size()); | ||||
|  | ||||
|   for (const auto &packet : packets) { | ||||
|     uint16_t type = packet.message_type; | ||||
|     uint16_t offset = packet.offset; | ||||
|     uint16_t payload_len = packet.payload_size; | ||||
|  | ||||
|     // Calculate varint sizes for header layout | ||||
|     uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(payload_len)); | ||||
|     uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(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; | ||||
|   } | ||||
|  | ||||
|     // Calculate where to start writing the header | ||||
|     // The header starts at the latest possible position to minimize unused padding | ||||
|     // | ||||
| @@ -1044,8 +1082,11 @@ APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWrit | ||||
|     // [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; | ||||
|     // | ||||
|     // 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; | ||||
|  | ||||
|     // Write the plaintext header | ||||
|     buf_start[header_offset] = 0x00;  // indicator | ||||
| @@ -1056,13 +1097,15 @@ APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWrit | ||||
|     // 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; | ||||
|   // 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; | ||||
|     this->reusable_iovs_.push_back(iov); | ||||
|   } | ||||
|  | ||||
|   return write_raw_(&iov, 1); | ||||
|   // Send all packets in one writev call | ||||
|   return write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size()); | ||||
| } | ||||
|  | ||||
| #endif  // USE_API_PLAINTEXT | ||||
|   | ||||
| @@ -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<PacketInfo> &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<SendBuffer> 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::Socket> 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<uint8_t> &tx_buf, | ||||
|                       const std::string &info, StateEnum &state, StateEnum failed_state); | ||||
|  | ||||
|   // Pointers first (4 bytes each) | ||||
|   socket::Socket *socket_{nullptr}; | ||||
|   std::unique_ptr<socket::Socket> 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<SendBuffer> tx_buf_; | ||||
|   std::string info_; | ||||
|   std::vector<struct iovec> reusable_iovs_; | ||||
|   std::vector<uint8_t> 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<uint8_t> 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<PacketInfo> &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<APINoiseContext> ctx_; | ||||
|  | ||||
|   // Vector (12 bytes on 32-bit) | ||||
|   std::vector<uint8_t> 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<uint8_t> prologue_; | ||||
|  | ||||
|   std::shared_ptr<APINoiseContext> 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<PacketInfo> &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 | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -516,6 +516,8 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(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::UpdateCommand>(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 {}"); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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<typename T> 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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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<uint8_t> &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::Socket> socket_ = nullptr; | ||||
|   uint16_t port_{6053}; | ||||
|   uint32_t reboot_timeout_{300000}; | ||||
|   uint32_t last_connected_{0}; | ||||
|   std::vector<std::unique_ptr<APIConnection>> clients_; | ||||
|   std::string password_; | ||||
|   std::vector<HomeAssistantStateSubscription> state_subs_; | ||||
|   std::vector<UserServiceDescriptor *> user_services_; | ||||
|   Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>(); | ||||
|   Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>(); | ||||
|  | ||||
|   // 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<std::unique_ptr<APIConnection>> clients_; | ||||
|   std::string password_; | ||||
|   std::vector<uint8_t> shared_write_buffer_;  // Shared proto write buffer for all connections | ||||
|   std::vector<HomeAssistantStateSubscription> state_subs_; | ||||
|   std::vector<UserServiceDescriptor *> 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<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>(); | ||||
|   ESPPreferenceObject noise_pref_; | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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 <vector> | ||||
|  | ||||
| namespace esphome { | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #include "proto.h" | ||||
| #include <cinttypes> | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
|   | ||||
| @@ -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 <vector> | ||||
|  | ||||
| @@ -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<const char *>(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<class C> 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 | ||||
|   | ||||
| @@ -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); } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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_); | ||||
|   | ||||
| @@ -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") | ||||
|   | ||||
| @@ -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)) { | ||||
|   | ||||
| @@ -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_; | ||||
|   | ||||
| @@ -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<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||
|   RAMAllocator<uint8_t> 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<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||
|     RAMAllocator<uint8_t> allocator; | ||||
|     allocator.deallocate(this->buffer_, this->buffer_size_); | ||||
|     this->buffer_ = nullptr; | ||||
|     this->data_start_ = nullptr; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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"); | ||||
|   | ||||
| @@ -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<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||
|   RAMAllocator<uint8_t> 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; } | ||||
|   | ||||
| @@ -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<uint8_t *>(&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); | ||||
|     }); | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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."); | ||||
|   | ||||
| @@ -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<bool> { | ||||
|   } | ||||
| }; | ||||
|  | ||||
| class StateChangeTrigger : public Trigger<optional<bool>, optional<bool> > { | ||||
|  public: | ||||
|   explicit StateChangeTrigger(BinarySensor *parent) { | ||||
|     parent->add_full_state_callback( | ||||
|         [this](optional<bool> old_state, optional<bool> state) { this->trigger(old_state, state); }); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class BinarySensorCondition : public Condition<Ts...> { | ||||
|  public: | ||||
|   BinarySensorCondition(BinarySensor *parent, bool state) : parent_(parent), state_(state) {} | ||||
| @@ -154,5 +162,15 @@ template<typename... Ts> class BinarySensorPublishAction : public Action<Ts...> | ||||
|   BinarySensor *sensor_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class BinarySensorInvalidateAction : public Action<Ts...> { | ||||
|  public: | ||||
|   explicit BinarySensorInvalidateAction(BinarySensor *sensor) : sensor_(sensor) {} | ||||
|  | ||||
|   void play(Ts... x) override { this->sensor_->invalidate_state(); } | ||||
|  | ||||
|  protected: | ||||
|   BinarySensor *sensor_; | ||||
| }; | ||||
|  | ||||
| }  // namespace binary_sensor | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -7,42 +7,25 @@ namespace binary_sensor { | ||||
|  | ||||
| static const char *const TAG = "binary_sensor"; | ||||
|  | ||||
| void BinarySensor::add_on_state_callback(std::function<void(bool)> &&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<Filter *> &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 | ||||
|   | ||||
| @@ -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<bool>, 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<void(bool)> &&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<Filter *> &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<void(bool)> state_callback_{}; | ||||
|   Filter *filter_list_{nullptr}; | ||||
|   bool has_state_{false}; | ||||
|   bool publish_initial_state_{false}; | ||||
|   Deduplicator<bool> publish_dedup_; | ||||
| }; | ||||
|  | ||||
| class BinarySensorInitiallyOff : public BinarySensor { | ||||
|   | ||||
| @@ -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<bool> DelayedOnOffFilter::new_value(bool value, bool is_initial) { | ||||
| optional<bool> 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<bool> DelayedOnFilter::new_value(bool value, bool is_initial) { | ||||
| optional<bool> 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<bool> DelayedOnFilter::new_value(bool value, bool is_initial) { | ||||
|  | ||||
| float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||
|  | ||||
| optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) { | ||||
| optional<bool> 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<bool> DelayedOffFilter::new_value(bool value, bool is_initial) { | ||||
|  | ||||
| float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||
|  | ||||
| optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; } | ||||
| optional<bool> InvertFilter::new_value(bool value) { return !value; } | ||||
|  | ||||
| AutorepeatFilter::AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings) : timings_(std::move(timings)) {} | ||||
|  | ||||
| optional<bool> AutorepeatFilter::new_value(bool value, bool is_initial) { | ||||
| optional<bool> 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<optional<bool>(bool)> f) : f_(std::move(f)) {} | ||||
|  | ||||
| optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); } | ||||
| optional<bool> LambdaFilter::new_value(bool value) { return this->f_(value); } | ||||
|  | ||||
| optional<bool> SettleFilter::new_value(bool value, bool is_initial) { | ||||
| optional<bool> 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; | ||||
|   } | ||||
|   | ||||
| @@ -14,11 +14,11 @@ class BinarySensor; | ||||
|  | ||||
| class Filter { | ||||
|  public: | ||||
|   virtual optional<bool> new_value(bool value, bool is_initial) = 0; | ||||
|   virtual optional<bool> 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<bool> new_value(bool value, bool is_initial) override; | ||||
|   optional<bool> 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<bool> new_value(bool value, bool is_initial) override; | ||||
|   optional<bool> 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<bool> new_value(bool value, bool is_initial) override; | ||||
|   optional<bool> 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<bool> new_value(bool value, bool is_initial) override; | ||||
|   optional<bool> new_value(bool value) override; | ||||
| }; | ||||
|  | ||||
| struct AutorepeatFilterTiming { | ||||
| @@ -86,7 +86,7 @@ class AutorepeatFilter : public Filter, public Component { | ||||
|  public: | ||||
|   explicit AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings); | ||||
|  | ||||
|   optional<bool> new_value(bool value, bool is_initial) override; | ||||
|   optional<bool> new_value(bool value) override; | ||||
|  | ||||
|   float get_setup_priority() const override; | ||||
|  | ||||
| @@ -102,7 +102,7 @@ class LambdaFilter : public Filter { | ||||
|  public: | ||||
|   explicit LambdaFilter(std::function<optional<bool>(bool)> f); | ||||
|  | ||||
|   optional<bool> new_value(bool value, bool is_initial) override; | ||||
|   optional<bool> new_value(bool value) override; | ||||
|  | ||||
|  protected: | ||||
|   std::function<optional<bool>(bool)> f_; | ||||
| @@ -110,7 +110,7 @@ class LambdaFilter : public Filter { | ||||
|  | ||||
| class SettleFilter : public Filter, public Component { | ||||
|  public: | ||||
|   optional<bool> new_value(bool value, bool is_initial) override; | ||||
|   optional<bool> new_value(bool value) override; | ||||
|  | ||||
|   float get_setup_priority() const override; | ||||
|  | ||||
|   | ||||
| @@ -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)(); | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -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_); | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -39,7 +39,7 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta | ||||
|   resp.state = static_cast<api::enums::BluetoothScannerState>(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<api::BluetoothLERawAdvertisement> &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) { | ||||
|   | ||||
| @@ -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<BluetoothConnection *> 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<BluetoothConnection *> 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) | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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( | ||||
|                 { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #include "bme680_bsec.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include <string> | ||||
|  | ||||
| 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_)); | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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_); | ||||
|   | ||||
| @@ -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, | ||||
|         ), | ||||
|     } | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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; } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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", | ||||
|       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; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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() { | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import re | ||||
|  | ||||
| from esphome import automation | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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"); | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user