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: |         with: | ||||||
|           python-version: "3.10" |           python-version: "3.10" | ||||||
|       - name: Set up Docker Buildx |       - name: Set up Docker Buildx | ||||||
|         uses: docker/setup-buildx-action@v3.10.0 |         uses: docker/setup-buildx-action@v3.11.1 | ||||||
|  |  | ||||||
|       - name: Set TAG |       - name: Set TAG | ||||||
|         run: | |         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 |             name: Run script/clang-tidy for ZEPHYR | ||||||
|             options: --environment nrf52-tidy --grep USE_ZEPHYR |             options: --environment nrf52-tidy --grep USE_ZEPHYR | ||||||
|             pio_cache_key: tidy-zephyr |             pio_cache_key: tidy-zephyr | ||||||
|             ignore_errors: true |             ignore_errors: false | ||||||
|  |  | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - 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" |           python-version: "3.10" | ||||||
|  |  | ||||||
|       - name: Set up Docker Buildx |       - 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 |       - name: Log in to docker hub | ||||||
|         uses: docker/login-action@v3.4.0 |         uses: docker/login-action@v3.4.0 | ||||||
| @@ -178,7 +178,7 @@ jobs: | |||||||
|           merge-multiple: true |           merge-multiple: true | ||||||
|  |  | ||||||
|       - name: Set up Docker Buildx |       - 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 |       - name: Log in to docker hub | ||||||
|         if: matrix.registry == 'dockerhub' |         if: matrix.registry == 'dockerhub' | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ | |||||||
| repos: | repos: | ||||||
|   - repo: https://github.com/astral-sh/ruff-pre-commit |   - repo: https://github.com/astral-sh/ruff-pre-commit | ||||||
|     # Ruff version. |     # Ruff version. | ||||||
|     rev: v0.11.10 |     rev: v0.12.0 | ||||||
|     hooks: |     hooks: | ||||||
|       # Run the linter. |       # Run the linter. | ||||||
|       - id: ruff |       - id: ruff | ||||||
|   | |||||||
| @@ -150,6 +150,7 @@ esphome/components/esp32_improv/* @jesserockz | |||||||
| esphome/components/esp32_rmt/* @jesserockz | esphome/components/esp32_rmt/* @jesserockz | ||||||
| esphome/components/esp32_rmt_led_strip/* @jesserockz | esphome/components/esp32_rmt_led_strip/* @jesserockz | ||||||
| esphome/components/esp8266/* @esphome/core | esphome/components/esp8266/* @esphome/core | ||||||
|  | esphome/components/esp_ldo/* @clydebarrow | ||||||
| esphome/components/ethernet_info/* @gtjadsonsantos | esphome/components/ethernet_info/* @gtjadsonsantos | ||||||
| esphome/components/event/* @nohat | esphome/components/event/* @nohat | ||||||
| esphome/components/event_emitter/* @Rapsssito | esphome/components/event_emitter/* @Rapsssito | ||||||
| @@ -235,6 +236,7 @@ esphome/components/kamstrup_kmp/* @cfeenstra1024 | |||||||
| esphome/components/key_collector/* @ssieb | esphome/components/key_collector/* @ssieb | ||||||
| esphome/components/key_provider/* @ssieb | esphome/components/key_provider/* @ssieb | ||||||
| esphome/components/kuntze/* @ssieb | esphome/components/kuntze/* @ssieb | ||||||
|  | esphome/components/lc709203f/* @ilikecake | ||||||
| esphome/components/lcd_menu/* @numo68 | esphome/components/lcd_menu/* @numo68 | ||||||
| esphome/components/ld2410/* @regevbr @sebcaps | esphome/components/ld2410/* @regevbr @sebcaps | ||||||
| esphome/components/ld2420/* @descipher | esphome/components/ld2420/* @descipher | ||||||
| @@ -320,6 +322,7 @@ esphome/components/number/* @esphome/core | |||||||
| esphome/components/one_wire/* @ssieb | esphome/components/one_wire/* @ssieb | ||||||
| esphome/components/online_image/* @clydebarrow @guillempages | esphome/components/online_image/* @clydebarrow @guillempages | ||||||
| esphome/components/opentherm/* @olegtarasov | esphome/components/opentherm/* @olegtarasov | ||||||
|  | esphome/components/openthread/* @mrene | ||||||
| esphome/components/ota/* @esphome/core | esphome/components/ota/* @esphome/core | ||||||
| esphome/components/output/* @esphome/core | esphome/components/output/* @esphome/core | ||||||
| esphome/components/packet_transport/* @clydebarrow | esphome/components/packet_transport/* @clydebarrow | ||||||
| @@ -517,6 +520,7 @@ esphome/components/xiaomi_lywsd03mmc/* @ahpohl | |||||||
| esphome/components/xiaomi_mhoc303/* @drug123 | esphome/components/xiaomi_mhoc303/* @drug123 | ||||||
| esphome/components/xiaomi_mhoc401/* @vevsvevs | esphome/components/xiaomi_mhoc401/* @vevsvevs | ||||||
| esphome/components/xiaomi_rtcgq02lm/* @jesserockz | esphome/components/xiaomi_rtcgq02lm/* @jesserockz | ||||||
|  | esphome/components/xiaomi_xmwsdj04mmc/* @medusalix | ||||||
| esphome/components/xl9535/* @mreditor97 | esphome/components/xl9535/* @mreditor97 | ||||||
| esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68 | esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68 | ||||||
| esphome/components/xxtea/* @clydebarrow | 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 | # could be handy for archiving the generated documentation or if some version | ||||||
| # control system is used. | # 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 | # 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 | # 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): | def run_miniterm(config, port, args): | ||||||
|  |     from aioesphomeapi import LogParser | ||||||
|     import serial |     import serial | ||||||
|  |  | ||||||
|     from esphome import platformio_api |     from esphome import platformio_api | ||||||
| @@ -158,6 +159,7 @@ def run_miniterm(config, port, args): | |||||||
|         ser.dtr = False |         ser.dtr = False | ||||||
|         ser.rts = False |         ser.rts = False | ||||||
|  |  | ||||||
|  |     parser = LogParser() | ||||||
|     tries = 0 |     tries = 0 | ||||||
|     while tries < 5: |     while tries < 5: | ||||||
|         try: |         try: | ||||||
| @@ -174,8 +176,7 @@ def run_miniterm(config, port, args): | |||||||
|                         .decode("utf8", "backslashreplace") |                         .decode("utf8", "backslashreplace") | ||||||
|                     ) |                     ) | ||||||
|                     time_str = datetime.now().time().strftime("[%H:%M:%S]") |                     time_str = datetime.now().time().strftime("[%H:%M:%S]") | ||||||
|                     message = time_str + line |                     safe_print(parser.parse_line(line, time_str)) | ||||||
|                     safe_print(message) |  | ||||||
|  |  | ||||||
|                     backtrace_state = platformio_api.process_stacktrace( |                     backtrace_state = platformio_api.process_stacktrace( | ||||||
|                         config, line, backtrace_state=backtrace_state |                         config, line, backtrace_state=backtrace_state | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ from esphome.cpp_generator import (  # noqa: F401 | |||||||
|     TemplateArguments, |     TemplateArguments, | ||||||
|     add, |     add, | ||||||
|     add_build_flag, |     add_build_flag, | ||||||
|  |     add_build_unflag, | ||||||
|     add_define, |     add_define, | ||||||
|     add_global, |     add_global, | ||||||
|     add_library, |     add_library, | ||||||
| @@ -34,6 +35,7 @@ from esphome.cpp_generator import (  # noqa: F401 | |||||||
|     process_lambda, |     process_lambda, | ||||||
|     progmem_array, |     progmem_array, | ||||||
|     safe_exp, |     safe_exp, | ||||||
|  |     set_cpp_standard, | ||||||
|     statement, |     statement, | ||||||
|     static_const_array, |     static_const_array, | ||||||
|     templatable, |     templatable, | ||||||
|   | |||||||
| @@ -40,9 +40,11 @@ void AbsoluteHumidityComponent::dump_config() { | |||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGCONFIG(TAG, "Sources"); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Temperature: '%s'", this->temperature_sensor_->get_name().c_str()); |                 "Sources\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Relative Humidity: '%s'", this->humidity_sensor_->get_name().c_str()); |                 "  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; } | float AbsoluteHumidityComponent::get_setup_priority() const { return setup_priority::DATA; } | ||||||
|   | |||||||
| @@ -193,14 +193,13 @@ void AcDimmer::setup() { | |||||||
|   setTimer1Callback(&timer_interrupt); |   setTimer1Callback(&timer_interrupt); | ||||||
| #endif | #endif | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
|   // 80 Divider -> 1 count=1µs |   // timer frequency of 1mhz | ||||||
|   dimmer_timer = timerBegin(0, 80, true); |   dimmer_timer = timerBegin(1000000); | ||||||
|   timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true); |   timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr); | ||||||
|   // For ESP32, we can't use dynamic interval calculation because the timerX functions |   // For ESP32, we can't use dynamic interval calculation because the timerX functions | ||||||
|   // are not callable from ISR (placed in flash storage). |   // are not callable from ISR (placed in flash storage). | ||||||
|   // Here we just use an interrupt firing every 50 µs. |   // Here we just use an interrupt firing every 50 µs. | ||||||
|   timerAlarmWrite(dimmer_timer, 50, true); |   timerAlarm(dimmer_timer, 50, true, 0); | ||||||
|   timerAlarmEnable(dimmer_timer); |  | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| void AcDimmer::write_state(float state) { | void AcDimmer::write_state(float state) { | ||||||
| @@ -214,8 +213,10 @@ void AcDimmer::dump_config() { | |||||||
|   ESP_LOGCONFIG(TAG, "AcDimmer:"); |   ESP_LOGCONFIG(TAG, "AcDimmer:"); | ||||||
|   LOG_PIN("  Output Pin: ", this->gate_pin_); |   LOG_PIN("  Output Pin: ", this->gate_pin_); | ||||||
|   LOG_PIN("  Zero-Cross Pin: ", this->zero_cross_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, | ||||||
|   ESP_LOGCONFIG(TAG, "   Init with half cycle: %s", YESNO(this->init_with_half_cycle_)); |                 "   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) { |   if (method_ == DIM_METHOD_LEADING_PULSE) { | ||||||
|     ESP_LOGCONFIG(TAG, "   Method: leading pulse"); |     ESP_LOGCONFIG(TAG, "   Method: leading pulse"); | ||||||
|   } else if (method_ == DIM_METHOD_LEADING) { |   } 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; | static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; | ||||||
|  |  | ||||||
| void ADCSensor::setup() { | 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) { |   if (this->channel1_ != ADC1_CHANNEL_MAX) { | ||||||
|     adc1_config_width(ADC_WIDTH_MAX_SOC_BITS); |     adc1_config_width(ADC_WIDTH_MAX_SOC_BITS); | ||||||
| @@ -77,8 +77,10 @@ void ADCSensor::dump_config() { | |||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   ESP_LOGCONFIG(TAG, "  Samples: %i", this->sample_count_); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); |                 "  Samples: %i\n" | ||||||
|  |                 "  Sampling mode: %s", | ||||||
|  |                 this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); | ||||||
|   LOG_UPDATE_INTERVAL(this); |   LOG_UPDATE_INTERVAL(this); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ namespace adc { | |||||||
| static const char *const TAG = "adc.esp8266"; | static const char *const TAG = "adc.esp8266"; | ||||||
|  |  | ||||||
| void ADCSensor::setup() { | 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 | #ifndef USE_ADC_SENSOR_VCC | ||||||
|   this->pin_->setup(); |   this->pin_->setup(); | ||||||
| #endif | #endif | ||||||
| @@ -30,8 +30,10 @@ void ADCSensor::dump_config() { | |||||||
| #else | #else | ||||||
|   LOG_PIN("  Pin: ", this->pin_); |   LOG_PIN("  Pin: ", this->pin_); | ||||||
| #endif  // USE_ADC_SENSOR_VCC | #endif  // USE_ADC_SENSOR_VCC | ||||||
|   ESP_LOGCONFIG(TAG, "  Samples: %i", this->sample_count_); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); |                 "  Samples: %i\n" | ||||||
|  |                 "  Sampling mode: %s", | ||||||
|  |                 this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); | ||||||
|   LOG_UPDATE_INTERVAL(this); |   LOG_UPDATE_INTERVAL(this); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ namespace adc { | |||||||
| static const char *const TAG = "adc.libretiny"; | static const char *const TAG = "adc.libretiny"; | ||||||
|  |  | ||||||
| void ADCSensor::setup() { | 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 | #ifndef USE_ADC_SENSOR_VCC | ||||||
|   this->pin_->setup(); |   this->pin_->setup(); | ||||||
| #endif  // !USE_ADC_SENSOR_VCC | #endif  // !USE_ADC_SENSOR_VCC | ||||||
| @@ -22,8 +22,10 @@ void ADCSensor::dump_config() { | |||||||
| #else   // USE_ADC_SENSOR_VCC | #else   // USE_ADC_SENSOR_VCC | ||||||
|   LOG_PIN("  Pin: ", this->pin_); |   LOG_PIN("  Pin: ", this->pin_); | ||||||
| #endif  // USE_ADC_SENSOR_VCC | #endif  // USE_ADC_SENSOR_VCC | ||||||
|   ESP_LOGCONFIG(TAG, "  Samples: %i", this->sample_count_); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); |                 "  Samples: %i\n" | ||||||
|  |                 "  Sampling mode: %s", | ||||||
|  |                 this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); | ||||||
|   LOG_UPDATE_INTERVAL(this); |   LOG_UPDATE_INTERVAL(this); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ namespace adc { | |||||||
| static const char *const TAG = "adc.rp2040"; | static const char *const TAG = "adc.rp2040"; | ||||||
|  |  | ||||||
| void ADCSensor::setup() { | 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; |   static bool initialized = false; | ||||||
|   if (!initialized) { |   if (!initialized) { | ||||||
|     adc_init(); |     adc_init(); | ||||||
| @@ -33,8 +33,10 @@ void ADCSensor::dump_config() { | |||||||
|     LOG_PIN("  Pin: ", this->pin_); |     LOG_PIN("  Pin: ", this->pin_); | ||||||
| #endif  // USE_ADC_SENSOR_VCC | #endif  // USE_ADC_SENSOR_VCC | ||||||
|   } |   } | ||||||
|   ESP_LOGCONFIG(TAG, "  Samples: %i", this->sample_count_); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); |                 "  Samples: %i\n" | ||||||
|  |                 "  Sampling mode: %s", | ||||||
|  |                 this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); | ||||||
|   LOG_UPDATE_INTERVAL(this); |   LOG_UPDATE_INTERVAL(this); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -177,11 +177,14 @@ void ADE7880::dump_config() { | |||||||
|     LOG_SENSOR("    ", "Power Factor", this->channel_a_->power_factor); |     LOG_SENSOR("    ", "Power Factor", this->channel_a_->power_factor); | ||||||
|     LOG_SENSOR("    ", "Forward Active Energy", this->channel_a_->forward_active_energy); |     LOG_SENSOR("    ", "Forward Active Energy", this->channel_a_->forward_active_energy); | ||||||
|     LOG_SENSOR("    ", "Reverse Active Energy", this->channel_a_->reverse_active_energy); |     LOG_SENSOR("    ", "Reverse Active Energy", this->channel_a_->reverse_active_energy); | ||||||
|     ESP_LOGCONFIG(TAG, "    Calibration:"); |     ESP_LOGCONFIG(TAG, | ||||||
|     ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_a_->current_gain_calibration); |                   "    Calibration:\n" | ||||||
|     ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_a_->voltage_gain_calibration); |                   "     Current: %" PRId32 "\n" | ||||||
|     ESP_LOGCONFIG(TAG, "     Power: %" PRId32, this->channel_a_->power_gain_calibration); |                   "     Voltage: %" PRId32 "\n" | ||||||
|     ESP_LOGCONFIG(TAG, "     Phase Angle: %u", this->channel_a_->phase_angle_calibration); |                   "     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) { |   if (this->channel_b_ != nullptr) { | ||||||
| @@ -193,11 +196,14 @@ void ADE7880::dump_config() { | |||||||
|     LOG_SENSOR("    ", "Power Factor", this->channel_b_->power_factor); |     LOG_SENSOR("    ", "Power Factor", this->channel_b_->power_factor); | ||||||
|     LOG_SENSOR("    ", "Forward Active Energy", this->channel_b_->forward_active_energy); |     LOG_SENSOR("    ", "Forward Active Energy", this->channel_b_->forward_active_energy); | ||||||
|     LOG_SENSOR("    ", "Reverse Active Energy", this->channel_b_->reverse_active_energy); |     LOG_SENSOR("    ", "Reverse Active Energy", this->channel_b_->reverse_active_energy); | ||||||
|     ESP_LOGCONFIG(TAG, "    Calibration:"); |     ESP_LOGCONFIG(TAG, | ||||||
|     ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_b_->current_gain_calibration); |                   "    Calibration:\n" | ||||||
|     ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_b_->voltage_gain_calibration); |                   "     Current: %" PRId32 "\n" | ||||||
|     ESP_LOGCONFIG(TAG, "     Power: %" PRId32, this->channel_b_->power_gain_calibration); |                   "     Voltage: %" PRId32 "\n" | ||||||
|     ESP_LOGCONFIG(TAG, "     Phase Angle: %u", this->channel_b_->phase_angle_calibration); |                   "     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) { |   if (this->channel_c_ != nullptr) { | ||||||
| @@ -209,18 +215,23 @@ void ADE7880::dump_config() { | |||||||
|     LOG_SENSOR("    ", "Power Factor", this->channel_c_->power_factor); |     LOG_SENSOR("    ", "Power Factor", this->channel_c_->power_factor); | ||||||
|     LOG_SENSOR("    ", "Forward Active Energy", this->channel_c_->forward_active_energy); |     LOG_SENSOR("    ", "Forward Active Energy", this->channel_c_->forward_active_energy); | ||||||
|     LOG_SENSOR("    ", "Reverse Active Energy", this->channel_c_->reverse_active_energy); |     LOG_SENSOR("    ", "Reverse Active Energy", this->channel_c_->reverse_active_energy); | ||||||
|     ESP_LOGCONFIG(TAG, "    Calibration:"); |     ESP_LOGCONFIG(TAG, | ||||||
|     ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_c_->current_gain_calibration); |                   "    Calibration:\n" | ||||||
|     ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_c_->voltage_gain_calibration); |                   "     Current: %" PRId32 "\n" | ||||||
|     ESP_LOGCONFIG(TAG, "     Power: %" PRId32, this->channel_c_->power_gain_calibration); |                   "     Voltage: %" PRId32 "\n" | ||||||
|     ESP_LOGCONFIG(TAG, "     Phase Angle: %u", this->channel_c_->phase_angle_calibration); |                   "     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) { |   if (this->channel_n_ != nullptr) { | ||||||
|     ESP_LOGCONFIG(TAG, "  Neutral:"); |     ESP_LOGCONFIG(TAG, "  Neutral:"); | ||||||
|     LOG_SENSOR("    ", "Current", this->channel_n_->current); |     LOG_SENSOR("    ", "Current", this->channel_n_->current); | ||||||
|     ESP_LOGCONFIG(TAG, "    Calibration:"); |     ESP_LOGCONFIG(TAG, | ||||||
|     ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_n_->current_gain_calibration); |                   "    Calibration:\n" | ||||||
|  |                   "     Current: %" PRId32, | ||||||
|  |                   this->channel_n_->current_gain_calibration); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   LOG_I2C_DEVICE(this); |   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("  ", "Active Power B Sensor", this->active_power_b_sensor_); | ||||||
|   LOG_SENSOR("  ", "Rective Power A Sensor", this->reactive_power_a_sensor_); |   LOG_SENSOR("  ", "Rective Power A Sensor", this->reactive_power_a_sensor_); | ||||||
|   LOG_SENSOR("  ", "Reactive Power B Sensor", this->reactive_power_b_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, | ||||||
|   ESP_LOGCONFIG(TAG, "  PGA_V_8: 0x%X", pga_v_); |                 "  USE_ACC_ENERGY_REGS: %d\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  PGA_IA_8: 0x%X", pga_ia_); |                 "  PGA_V_8: 0x%X\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  PGA_IB_8: 0x%X", pga_ib_); |                 "  PGA_IA_8: 0x%X\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  VGAIN_32: 0x%08jX", (uintmax_t) vgain_); |                 "  PGA_IB_8: 0x%X\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  AIGAIN_32: 0x%08jX", (uintmax_t) aigain_); |                 "  VGAIN_32: 0x%08jX\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  BIGAIN_32: 0x%08jX", (uintmax_t) bigain_); |                 "  AIGAIN_32: 0x%08jX\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  AWGAIN_32: 0x%08jX", (uintmax_t) awgain_); |                 "  BIGAIN_32: 0x%08jX\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  BWGAIN_32: 0x%08jX", (uintmax_t) bwgain_); |                 "  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) \ | #define ADE_PUBLISH_(name, val, factor) \ | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| #include "ade7953_i2c.h" | #include "ade7953_i2c.h" | ||||||
| #include "esphome/core/log.h" |  | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace ade7953_i2c { | namespace ade7953_i2c { | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| #include "ade7953_spi.h" | #include "ade7953_spi.h" | ||||||
| #include "esphome/core/log.h" |  | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace ade7953_spi { | namespace ade7953_spi { | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| #include "ads1118.h" | #include "ads1118.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| #include "ags10.h" | #include "ags10.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
| #include <cinttypes> | #include <cinttypes> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,9 +13,9 @@ | |||||||
| // results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time. | // results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time. | ||||||
|  |  | ||||||
| #include "aht10.h" | #include "aht10.h" | ||||||
| #include "esphome/core/log.h" |  | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace aht10 { | namespace aht10 { | ||||||
| @@ -115,7 +115,7 @@ void AHT10Component::read_data_() { | |||||||
|     if (this->humidity_sensor_ == nullptr) { |     if (this->humidity_sensor_ == nullptr) { | ||||||
|       ESP_LOGV(TAG, "Invalid humidity (reading not required)"); |       ESP_LOGV(TAG, "Invalid humidity (reading not required)"); | ||||||
|     } else { |     } 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) { |       if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) { | ||||||
|         this->status_set_warning(ESP_LOG_MSG_COMM_FAIL); |         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]): |     if not CORE.has_id(config[CONF_ID]): | ||||||
|         var = cg.Pvariable(config[CONF_ID], var) |         var = cg.Pvariable(config[CONF_ID], var) | ||||||
|     cg.add(cg.App.register_alarm_control_panel(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) |     await setup_alarm_control_panel_core_(var, config) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "esphome/core/log.h" |  | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace am43 { | namespace am43 { | ||||||
|   | |||||||
| @@ -12,8 +12,10 @@ using namespace esphome::cover; | |||||||
|  |  | ||||||
| void Am43Component::dump_config() { | void Am43Component::dump_config() { | ||||||
|   LOG_COVER("", "AM43 Cover", this); |   LOG_COVER("", "AM43 Cover", this); | ||||||
|   ESP_LOGCONFIG(TAG, "  Device Pin: %d", this->pin_); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Invert Position: %d", (int) this->invert_position_); |                 "  Device Pin: %d\n" | ||||||
|  |                 "  Invert Position: %d", | ||||||
|  |                 this->pin_, (int) this->invert_position_); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Am43Component::setup() { | void Am43Component::setup() { | ||||||
|   | |||||||
| @@ -34,8 +34,10 @@ void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) { | |||||||
| void AnalogThresholdBinarySensor::dump_config() { | void AnalogThresholdBinarySensor::dump_config() { | ||||||
|   LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this); |   LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this); | ||||||
|   LOG_SENSOR("  ", "Sensor", this->sensor_); |   LOG_SENSOR("  ", "Sensor", this->sensor_); | ||||||
|   ESP_LOGCONFIG(TAG, "  Upper threshold: %.11f", this->upper_threshold_.value()); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Lower threshold: %.11f", this->lower_threshold_.value()); |                 "  Upper threshold: %.11f\n" | ||||||
|  |                 "  Lower threshold: %.11f", | ||||||
|  |                 this->upper_threshold_.value(), this->lower_threshold_.value()); | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace analog_threshold | }  // namespace analog_threshold | ||||||
|   | |||||||
| @@ -17,7 +17,11 @@ void Anova::setup() { | |||||||
|   this->current_request_ = 0; |   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) { | void Anova::control(const ClimateCall &call) { | ||||||
|   if (call.get_mode().has_value()) { |   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, | ||||||
|   ESP_LOGCONFIG(TAG, "  Measurement rate: %u", MEASUREMENT_RATE_VALUES[this->measurement_rate_]); |                 "  Gain: %u\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Measurement Resolution/Bit width: %d", MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]); |                 "  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); |   LOG_UPDATE_INTERVAL(this); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -49,6 +49,7 @@ SERVICE_ARG_NATIVE_TYPES = { | |||||||
|     "string[]": cg.std_vector.template(cg.std_string), |     "string[]": cg.std_vector.template(cg.std_string), | ||||||
| } | } | ||||||
| CONF_ENCRYPTION = "encryption" | CONF_ENCRYPTION = "encryption" | ||||||
|  | CONF_BATCH_DELAY = "batch_delay" | ||||||
|  |  | ||||||
|  |  | ||||||
| def validate_encryption_key(value): | def validate_encryption_key(value): | ||||||
| @@ -109,6 +110,9 @@ CONFIG_SCHEMA = cv.All( | |||||||
|             ): ACTIONS_SCHEMA, |             ): ACTIONS_SCHEMA, | ||||||
|             cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA, |             cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA, | ||||||
|             cv.Optional(CONF_ENCRYPTION): _encryption_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( |             cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation( | ||||||
|                 single=True |                 single=True | ||||||
|             ), |             ), | ||||||
| @@ -129,6 +133,7 @@ async def to_code(config): | |||||||
|     cg.add(var.set_port(config[CONF_PORT])) |     cg.add(var.set_port(config[CONF_PORT])) | ||||||
|     cg.add(var.set_password(config[CONF_PASSWORD])) |     cg.add(var.set_password(config[CONF_PASSWORD])) | ||||||
|     cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) |     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, []): |     for conf in config.get(CONF_ACTIONS, []): | ||||||
|         template_args = [] |         template_args = [] | ||||||
|   | |||||||
| @@ -274,6 +274,7 @@ enum EntityCategory { | |||||||
| // ==================== BINARY SENSOR ==================== | // ==================== BINARY SENSOR ==================== | ||||||
| message ListEntitiesBinarySensorResponse { | message ListEntitiesBinarySensorResponse { | ||||||
|   option (id) = 12; |   option (id) = 12; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_BINARY_SENSOR"; |   option (ifdef) = "USE_BINARY_SENSOR"; | ||||||
|  |  | ||||||
| @@ -291,6 +292,7 @@ message ListEntitiesBinarySensorResponse { | |||||||
| } | } | ||||||
| message BinarySensorStateResponse { | message BinarySensorStateResponse { | ||||||
|   option (id) = 21; |   option (id) = 21; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_BINARY_SENSOR"; |   option (ifdef) = "USE_BINARY_SENSOR"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -305,6 +307,7 @@ message BinarySensorStateResponse { | |||||||
| // ==================== COVER ==================== | // ==================== COVER ==================== | ||||||
| message ListEntitiesCoverResponse { | message ListEntitiesCoverResponse { | ||||||
|   option (id) = 13; |   option (id) = 13; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_COVER"; |   option (ifdef) = "USE_COVER"; | ||||||
|  |  | ||||||
| @@ -335,6 +338,7 @@ enum CoverOperation { | |||||||
| } | } | ||||||
| message CoverStateResponse { | message CoverStateResponse { | ||||||
|   option (id) = 22; |   option (id) = 22; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_COVER"; |   option (ifdef) = "USE_COVER"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -377,6 +381,7 @@ message CoverCommandRequest { | |||||||
| // ==================== FAN ==================== | // ==================== FAN ==================== | ||||||
| message ListEntitiesFanResponse { | message ListEntitiesFanResponse { | ||||||
|   option (id) = 14; |   option (id) = 14; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_FAN"; |   option (ifdef) = "USE_FAN"; | ||||||
|  |  | ||||||
| @@ -406,6 +411,7 @@ enum FanDirection { | |||||||
| } | } | ||||||
| message FanStateResponse { | message FanStateResponse { | ||||||
|   option (id) = 23; |   option (id) = 23; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_FAN"; |   option (ifdef) = "USE_FAN"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -455,6 +461,7 @@ enum ColorMode { | |||||||
| } | } | ||||||
| message ListEntitiesLightResponse { | message ListEntitiesLightResponse { | ||||||
|   option (id) = 15; |   option (id) = 15; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_LIGHT"; |   option (ifdef) = "USE_LIGHT"; | ||||||
|  |  | ||||||
| @@ -479,6 +486,7 @@ message ListEntitiesLightResponse { | |||||||
| } | } | ||||||
| message LightStateResponse { | message LightStateResponse { | ||||||
|   option (id) = 24; |   option (id) = 24; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_LIGHT"; |   option (ifdef) = "USE_LIGHT"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -548,6 +556,7 @@ enum SensorLastResetType { | |||||||
|  |  | ||||||
| message ListEntitiesSensorResponse { | message ListEntitiesSensorResponse { | ||||||
|   option (id) = 16; |   option (id) = 16; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_SENSOR"; |   option (ifdef) = "USE_SENSOR"; | ||||||
|  |  | ||||||
| @@ -570,6 +579,7 @@ message ListEntitiesSensorResponse { | |||||||
| } | } | ||||||
| message SensorStateResponse { | message SensorStateResponse { | ||||||
|   option (id) = 25; |   option (id) = 25; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_SENSOR"; |   option (ifdef) = "USE_SENSOR"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -584,6 +594,7 @@ message SensorStateResponse { | |||||||
| // ==================== SWITCH ==================== | // ==================== SWITCH ==================== | ||||||
| message ListEntitiesSwitchResponse { | message ListEntitiesSwitchResponse { | ||||||
|   option (id) = 17; |   option (id) = 17; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_SWITCH"; |   option (ifdef) = "USE_SWITCH"; | ||||||
|  |  | ||||||
| @@ -601,6 +612,7 @@ message ListEntitiesSwitchResponse { | |||||||
| } | } | ||||||
| message SwitchStateResponse { | message SwitchStateResponse { | ||||||
|   option (id) = 26; |   option (id) = 26; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_SWITCH"; |   option (ifdef) = "USE_SWITCH"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -621,6 +633,7 @@ message SwitchCommandRequest { | |||||||
| // ==================== TEXT SENSOR ==================== | // ==================== TEXT SENSOR ==================== | ||||||
| message ListEntitiesTextSensorResponse { | message ListEntitiesTextSensorResponse { | ||||||
|   option (id) = 18; |   option (id) = 18; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_TEXT_SENSOR"; |   option (ifdef) = "USE_TEXT_SENSOR"; | ||||||
|  |  | ||||||
| @@ -637,6 +650,7 @@ message ListEntitiesTextSensorResponse { | |||||||
| } | } | ||||||
| message TextSensorStateResponse { | message TextSensorStateResponse { | ||||||
|   option (id) = 27; |   option (id) = 27; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_TEXT_SENSOR"; |   option (ifdef) = "USE_TEXT_SENSOR"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -804,6 +818,7 @@ message ExecuteServiceRequest { | |||||||
| // ==================== CAMERA ==================== | // ==================== CAMERA ==================== | ||||||
| message ListEntitiesCameraResponse { | message ListEntitiesCameraResponse { | ||||||
|   option (id) = 43; |   option (id) = 43; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_ESP32_CAMERA"; |   option (ifdef) = "USE_ESP32_CAMERA"; | ||||||
|  |  | ||||||
| @@ -885,6 +900,7 @@ enum ClimatePreset { | |||||||
| } | } | ||||||
| message ListEntitiesClimateResponse { | message ListEntitiesClimateResponse { | ||||||
|   option (id) = 46; |   option (id) = 46; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_CLIMATE"; |   option (ifdef) = "USE_CLIMATE"; | ||||||
|  |  | ||||||
| @@ -920,6 +936,7 @@ message ListEntitiesClimateResponse { | |||||||
| } | } | ||||||
| message ClimateStateResponse { | message ClimateStateResponse { | ||||||
|   option (id) = 47; |   option (id) = 47; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_CLIMATE"; |   option (ifdef) = "USE_CLIMATE"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -981,6 +998,7 @@ enum NumberMode { | |||||||
| } | } | ||||||
| message ListEntitiesNumberResponse { | message ListEntitiesNumberResponse { | ||||||
|   option (id) = 49; |   option (id) = 49; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_NUMBER"; |   option (ifdef) = "USE_NUMBER"; | ||||||
|  |  | ||||||
| @@ -1002,6 +1020,7 @@ message ListEntitiesNumberResponse { | |||||||
| } | } | ||||||
| message NumberStateResponse { | message NumberStateResponse { | ||||||
|   option (id) = 50; |   option (id) = 50; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_NUMBER"; |   option (ifdef) = "USE_NUMBER"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -1025,6 +1044,7 @@ message NumberCommandRequest { | |||||||
| // ==================== SELECT ==================== | // ==================== SELECT ==================== | ||||||
| message ListEntitiesSelectResponse { | message ListEntitiesSelectResponse { | ||||||
|   option (id) = 52; |   option (id) = 52; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_SELECT"; |   option (ifdef) = "USE_SELECT"; | ||||||
|  |  | ||||||
| @@ -1041,6 +1061,7 @@ message ListEntitiesSelectResponse { | |||||||
| } | } | ||||||
| message SelectStateResponse { | message SelectStateResponse { | ||||||
|   option (id) = 53; |   option (id) = 53; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_SELECT"; |   option (ifdef) = "USE_SELECT"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -1064,6 +1085,7 @@ message SelectCommandRequest { | |||||||
| // ==================== SIREN ==================== | // ==================== SIREN ==================== | ||||||
| message ListEntitiesSirenResponse { | message ListEntitiesSirenResponse { | ||||||
|   option (id) = 55; |   option (id) = 55; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_SIREN"; |   option (ifdef) = "USE_SIREN"; | ||||||
|  |  | ||||||
| @@ -1081,6 +1103,7 @@ message ListEntitiesSirenResponse { | |||||||
| } | } | ||||||
| message SirenStateResponse { | message SirenStateResponse { | ||||||
|   option (id) = 56; |   option (id) = 56; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_SIREN"; |   option (ifdef) = "USE_SIREN"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -1121,6 +1144,7 @@ enum LockCommand  { | |||||||
| } | } | ||||||
| message ListEntitiesLockResponse { | message ListEntitiesLockResponse { | ||||||
|   option (id) = 58; |   option (id) = 58; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_LOCK"; |   option (ifdef) = "USE_LOCK"; | ||||||
|  |  | ||||||
| @@ -1143,6 +1167,7 @@ message ListEntitiesLockResponse { | |||||||
| } | } | ||||||
| message LockStateResponse { | message LockStateResponse { | ||||||
|   option (id) = 59; |   option (id) = 59; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_LOCK"; |   option (ifdef) = "USE_LOCK"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -1165,6 +1190,7 @@ message LockCommandRequest { | |||||||
| // ==================== BUTTON ==================== | // ==================== BUTTON ==================== | ||||||
| message ListEntitiesButtonResponse { | message ListEntitiesButtonResponse { | ||||||
|   option (id) = 61; |   option (id) = 61; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_BUTTON"; |   option (ifdef) = "USE_BUTTON"; | ||||||
|  |  | ||||||
| @@ -1217,6 +1243,7 @@ message MediaPlayerSupportedFormat { | |||||||
| } | } | ||||||
| message ListEntitiesMediaPlayerResponse { | message ListEntitiesMediaPlayerResponse { | ||||||
|   option (id) = 63; |   option (id) = 63; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_MEDIA_PLAYER"; |   option (ifdef) = "USE_MEDIA_PLAYER"; | ||||||
|  |  | ||||||
| @@ -1237,6 +1264,7 @@ message ListEntitiesMediaPlayerResponse { | |||||||
| } | } | ||||||
| message MediaPlayerStateResponse { | message MediaPlayerStateResponse { | ||||||
|   option (id) = 64; |   option (id) = 64; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_MEDIA_PLAYER"; |   option (ifdef) = "USE_MEDIA_PLAYER"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -1638,6 +1666,7 @@ enum VoiceAssistantEvent { | |||||||
|   VOICE_ASSISTANT_STT_VAD_END = 12; |   VOICE_ASSISTANT_STT_VAD_END = 12; | ||||||
|   VOICE_ASSISTANT_TTS_STREAM_START = 98; |   VOICE_ASSISTANT_TTS_STREAM_START = 98; | ||||||
|   VOICE_ASSISTANT_TTS_STREAM_END = 99; |   VOICE_ASSISTANT_TTS_STREAM_END = 99; | ||||||
|  |   VOICE_ASSISTANT_INTENT_PROGRESS = 100; | ||||||
| } | } | ||||||
|  |  | ||||||
| message VoiceAssistantEventData { | message VoiceAssistantEventData { | ||||||
| @@ -1758,6 +1787,7 @@ enum AlarmControlPanelStateCommand { | |||||||
|  |  | ||||||
| message ListEntitiesAlarmControlPanelResponse { | message ListEntitiesAlarmControlPanelResponse { | ||||||
|   option (id) = 94; |   option (id) = 94; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_ALARM_CONTROL_PANEL"; |   option (ifdef) = "USE_ALARM_CONTROL_PANEL"; | ||||||
|  |  | ||||||
| @@ -1776,6 +1806,7 @@ message ListEntitiesAlarmControlPanelResponse { | |||||||
|  |  | ||||||
| message AlarmControlPanelStateResponse { | message AlarmControlPanelStateResponse { | ||||||
|   option (id) = 95; |   option (id) = 95; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_ALARM_CONTROL_PANEL"; |   option (ifdef) = "USE_ALARM_CONTROL_PANEL"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -1800,6 +1831,7 @@ enum TextMode { | |||||||
| } | } | ||||||
| message ListEntitiesTextResponse { | message ListEntitiesTextResponse { | ||||||
|   option (id) = 97; |   option (id) = 97; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_TEXT"; |   option (ifdef) = "USE_TEXT"; | ||||||
|  |  | ||||||
| @@ -1819,6 +1851,7 @@ message ListEntitiesTextResponse { | |||||||
| } | } | ||||||
| message TextStateResponse { | message TextStateResponse { | ||||||
|   option (id) = 98; |   option (id) = 98; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_TEXT"; |   option (ifdef) = "USE_TEXT"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -1843,6 +1876,7 @@ message TextCommandRequest { | |||||||
| // ==================== DATETIME DATE ==================== | // ==================== DATETIME DATE ==================== | ||||||
| message ListEntitiesDateResponse { | message ListEntitiesDateResponse { | ||||||
|   option (id) = 100; |   option (id) = 100; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_DATETIME_DATE"; |   option (ifdef) = "USE_DATETIME_DATE"; | ||||||
|  |  | ||||||
| @@ -1858,6 +1892,7 @@ message ListEntitiesDateResponse { | |||||||
| } | } | ||||||
| message DateStateResponse { | message DateStateResponse { | ||||||
|   option (id) = 101; |   option (id) = 101; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_DATETIME_DATE"; |   option (ifdef) = "USE_DATETIME_DATE"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -1885,6 +1920,7 @@ message DateCommandRequest { | |||||||
| // ==================== DATETIME TIME ==================== | // ==================== DATETIME TIME ==================== | ||||||
| message ListEntitiesTimeResponse { | message ListEntitiesTimeResponse { | ||||||
|   option (id) = 103; |   option (id) = 103; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_DATETIME_TIME"; |   option (ifdef) = "USE_DATETIME_TIME"; | ||||||
|  |  | ||||||
| @@ -1900,6 +1936,7 @@ message ListEntitiesTimeResponse { | |||||||
| } | } | ||||||
| message TimeStateResponse { | message TimeStateResponse { | ||||||
|   option (id) = 104; |   option (id) = 104; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_DATETIME_TIME"; |   option (ifdef) = "USE_DATETIME_TIME"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -1927,6 +1964,7 @@ message TimeCommandRequest { | |||||||
| // ==================== EVENT ==================== | // ==================== EVENT ==================== | ||||||
| message ListEntitiesEventResponse { | message ListEntitiesEventResponse { | ||||||
|   option (id) = 107; |   option (id) = 107; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_EVENT"; |   option (ifdef) = "USE_EVENT"; | ||||||
|  |  | ||||||
| @@ -1945,6 +1983,7 @@ message ListEntitiesEventResponse { | |||||||
| } | } | ||||||
| message EventResponse { | message EventResponse { | ||||||
|   option (id) = 108; |   option (id) = 108; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_EVENT"; |   option (ifdef) = "USE_EVENT"; | ||||||
|  |  | ||||||
| @@ -1955,6 +1994,7 @@ message EventResponse { | |||||||
| // ==================== VALVE ==================== | // ==================== VALVE ==================== | ||||||
| message ListEntitiesValveResponse { | message ListEntitiesValveResponse { | ||||||
|   option (id) = 109; |   option (id) = 109; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_VALVE"; |   option (ifdef) = "USE_VALVE"; | ||||||
|  |  | ||||||
| @@ -1981,6 +2021,7 @@ enum ValveOperation { | |||||||
| } | } | ||||||
| message ValveStateResponse { | message ValveStateResponse { | ||||||
|   option (id) = 110; |   option (id) = 110; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_VALVE"; |   option (ifdef) = "USE_VALVE"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -2005,6 +2046,7 @@ message ValveCommandRequest { | |||||||
| // ==================== DATETIME DATETIME ==================== | // ==================== DATETIME DATETIME ==================== | ||||||
| message ListEntitiesDateTimeResponse { | message ListEntitiesDateTimeResponse { | ||||||
|   option (id) = 112; |   option (id) = 112; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_DATETIME_DATETIME"; |   option (ifdef) = "USE_DATETIME_DATETIME"; | ||||||
|  |  | ||||||
| @@ -2020,6 +2062,7 @@ message ListEntitiesDateTimeResponse { | |||||||
| } | } | ||||||
| message DateTimeStateResponse { | message DateTimeStateResponse { | ||||||
|   option (id) = 113; |   option (id) = 113; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_DATETIME_DATETIME"; |   option (ifdef) = "USE_DATETIME_DATETIME"; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
| @@ -2043,6 +2086,7 @@ message DateTimeCommandRequest { | |||||||
| // ==================== UPDATE ==================== | // ==================== UPDATE ==================== | ||||||
| message ListEntitiesUpdateResponse { | message ListEntitiesUpdateResponse { | ||||||
|   option (id) = 116; |   option (id) = 116; | ||||||
|  |   option (base_class) = "InfoResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_UPDATE"; |   option (ifdef) = "USE_UPDATE"; | ||||||
|  |  | ||||||
| @@ -2059,6 +2103,7 @@ message ListEntitiesUpdateResponse { | |||||||
| } | } | ||||||
| message UpdateStateResponse { | message UpdateStateResponse { | ||||||
|   option (id) = 117; |   option (id) = 117; | ||||||
|  |   option (base_class) = "StateResponseProtoMessage"; | ||||||
|   option (source) = SOURCE_SERVER; |   option (source) = SOURCE_SERVER; | ||||||
|   option (ifdef) = "USE_UPDATE"; |   option (ifdef) = "USE_UPDATE"; | ||||||
|   option (no_delay) = true; |   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 "esphome/core/entity_base.h" | ||||||
|  |  | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include <functional> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace api { | namespace api { | ||||||
| @@ -18,49 +19,9 @@ namespace api { | |||||||
| // Keepalive timeout in milliseconds | // Keepalive timeout in milliseconds | ||||||
| static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000; | 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 { | class APIConnection : public APIServerConnection { | ||||||
|  public: |  public: | ||||||
|  |   friend class APIServer; | ||||||
|   APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent); |   APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent); | ||||||
|   virtual ~APIConnection(); |   virtual ~APIConnection(); | ||||||
|  |  | ||||||
| @@ -68,225 +29,105 @@ class APIConnection : public APIServerConnection { | |||||||
|   void loop(); |   void loop(); | ||||||
|  |  | ||||||
|   bool send_list_info_done() { |   bool send_list_info_done() { | ||||||
|     ListEntitiesDoneResponse resp; |     return this->schedule_message_(nullptr, &APIConnection::try_send_list_info_done, | ||||||
|     return this->send_list_entities_done_response(resp); |                                    ListEntitiesDoneResponse::MESSAGE_TYPE); | ||||||
|   } |   } | ||||||
| #ifdef USE_BINARY_SENSOR | #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); |   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 | #endif | ||||||
| #ifdef USE_COVER | #ifdef USE_COVER | ||||||
|   bool send_cover_state(cover::Cover *cover); |   bool send_cover_state(cover::Cover *cover); | ||||||
|   void send_cover_info(cover::Cover *cover); |   void send_cover_info(cover::Cover *cover); | ||||||
|   void cover_command(const CoverCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_FAN | #ifdef USE_FAN | ||||||
|   bool send_fan_state(fan::Fan *fan); |   bool send_fan_state(fan::Fan *fan); | ||||||
|   void send_fan_info(fan::Fan *fan); |   void send_fan_info(fan::Fan *fan); | ||||||
|   void fan_command(const FanCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_LIGHT | #ifdef USE_LIGHT | ||||||
|   bool send_light_state(light::LightState *light); |   bool send_light_state(light::LightState *light); | ||||||
|   void send_light_info(light::LightState *light); |   void send_light_info(light::LightState *light); | ||||||
|   void light_command(const LightCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_SENSOR | #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); |   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 | #endif | ||||||
| #ifdef USE_SWITCH | #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 send_switch_info(switch_::Switch *a_switch); | ||||||
|   void switch_command(const SwitchCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_TEXT_SENSOR | #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); |   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 | #endif | ||||||
| #ifdef USE_ESP32_CAMERA | #ifdef USE_ESP32_CAMERA | ||||||
|   void set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image); |   void set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image); | ||||||
|   void send_camera_info(esp32_camera::ESP32Camera *camera); |   void send_camera_info(esp32_camera::ESP32Camera *camera); | ||||||
|   void camera_image(const CameraImageRequest &msg) override; |   void camera_image(const CameraImageRequest &msg) override; | ||||||
|  |  | ||||||
|  protected: |  | ||||||
|   bool try_send_camera_info_(esp32_camera::ESP32Camera *camera); |  | ||||||
|  |  | ||||||
|  public: |  | ||||||
| #endif | #endif | ||||||
| #ifdef USE_CLIMATE | #ifdef USE_CLIMATE | ||||||
|   bool send_climate_state(climate::Climate *climate); |   bool send_climate_state(climate::Climate *climate); | ||||||
|   void send_climate_info(climate::Climate *climate); |   void send_climate_info(climate::Climate *climate); | ||||||
|   void climate_command(const ClimateCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_NUMBER | #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 send_number_info(number::Number *number); | ||||||
|   void number_command(const NumberCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_DATETIME_DATE | #ifdef USE_DATETIME_DATE | ||||||
|   bool send_date_state(datetime::DateEntity *date); |   bool send_date_state(datetime::DateEntity *date); | ||||||
|   void send_date_info(datetime::DateEntity *date); |   void send_date_info(datetime::DateEntity *date); | ||||||
|   void date_command(const DateCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_DATETIME_TIME | #ifdef USE_DATETIME_TIME | ||||||
|   bool send_time_state(datetime::TimeEntity *time); |   bool send_time_state(datetime::TimeEntity *time); | ||||||
|   void send_time_info(datetime::TimeEntity *time); |   void send_time_info(datetime::TimeEntity *time); | ||||||
|   void time_command(const TimeCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_DATETIME_DATETIME | #ifdef USE_DATETIME_DATETIME | ||||||
|   bool send_datetime_state(datetime::DateTimeEntity *datetime); |   bool send_datetime_state(datetime::DateTimeEntity *datetime); | ||||||
|   void send_datetime_info(datetime::DateTimeEntity *datetime); |   void send_datetime_info(datetime::DateTimeEntity *datetime); | ||||||
|   void datetime_command(const DateTimeCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_TEXT | #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 send_text_info(text::Text *text); | ||||||
|   void text_command(const TextCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_SELECT | #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 send_select_info(select::Select *select); | ||||||
|   void select_command(const SelectCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_BUTTON | #ifdef USE_BUTTON | ||||||
|   void send_button_info(button::Button *button); |   void send_button_info(button::Button *button); | ||||||
|   void button_command(const ButtonCommandRequest &msg) override; |   void button_command(const ButtonCommandRequest &msg) override; | ||||||
|  |  | ||||||
|  protected: |  | ||||||
|   bool try_send_button_info_(button::Button *button); |  | ||||||
|  |  | ||||||
|  public: |  | ||||||
| #endif | #endif | ||||||
| #ifdef USE_LOCK | #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 send_lock_info(lock::Lock *a_lock); | ||||||
|   void lock_command(const LockCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_VALVE | #ifdef USE_VALVE | ||||||
|   bool send_valve_state(valve::Valve *valve); |   bool send_valve_state(valve::Valve *valve); | ||||||
|   void send_valve_info(valve::Valve *valve); |   void send_valve_info(valve::Valve *valve); | ||||||
|   void valve_command(const ValveCommandRequest &msg) override; |   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 | #endif | ||||||
| #ifdef USE_MEDIA_PLAYER | #ifdef USE_MEDIA_PLAYER | ||||||
|   bool send_media_player_state(media_player::MediaPlayer *media_player); |   bool send_media_player_state(media_player::MediaPlayer *media_player); | ||||||
|   void send_media_player_info(media_player::MediaPlayer *media_player); |   void send_media_player_info(media_player::MediaPlayer *media_player); | ||||||
|   void media_player_command(const MediaPlayerCommandRequest &msg) override; |   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 | #endif | ||||||
|   bool try_send_log_message(int level, const char *tag, const char *line); |   bool try_send_log_message(int level, const char *tag, const char *line); | ||||||
|   void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { |   void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { | ||||||
|     if (!this->service_call_subscription_) |     if (!this->service_call_subscription_) | ||||||
|       return; |       return; | ||||||
|     this->send_homeassistant_service_response(call); |     this->send_message(call); | ||||||
|   } |   } | ||||||
| #ifdef USE_BLUETOOTH_PROXY | #ifdef USE_BLUETOOTH_PROXY | ||||||
|   void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; |   void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; | ||||||
| @@ -308,7 +149,7 @@ class APIConnection : public APIServerConnection { | |||||||
| #ifdef USE_HOMEASSISTANT_TIME | #ifdef USE_HOMEASSISTANT_TIME | ||||||
|   void send_time_request() { |   void send_time_request() { | ||||||
|     GetTimeRequest req; |     GetTimeRequest req; | ||||||
|     this->send_get_time_request(req); |     this->send_message(req); | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -328,36 +169,17 @@ class APIConnection : public APIServerConnection { | |||||||
|   bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); |   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 send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); | ||||||
|   void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override; |   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 | #endif | ||||||
|  |  | ||||||
| #ifdef USE_EVENT | #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); |   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 | #endif | ||||||
|  |  | ||||||
| #ifdef USE_UPDATE | #ifdef USE_UPDATE | ||||||
|   bool send_update_state(update::UpdateEntity *update); |   bool send_update_state(update::UpdateEntity *update); | ||||||
|   void send_update_info(update::UpdateEntity *update); |   void send_update_info(update::UpdateEntity *update); | ||||||
|   void update_command(const UpdateCommandRequest &msg) override; |   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 | #endif | ||||||
|  |  | ||||||
|   void on_disconnect_response(const DisconnectResponse &value) override; |   void on_disconnect_response(const DisconnectResponse &value) override; | ||||||
| @@ -407,102 +229,67 @@ class APIConnection : public APIServerConnection { | |||||||
|   void on_no_setup_connection() override; |   void on_no_setup_connection() override; | ||||||
|   ProtoWriteBuffer create_buffer(uint32_t reserve_size) override { |   ProtoWriteBuffer create_buffer(uint32_t reserve_size) override { | ||||||
|     // FIXME: ensure no recursive writes can happen |     // FIXME: ensure no recursive writes can happen | ||||||
|     this->proto_write_buffer_.clear(); |  | ||||||
|     // Get header padding size - used for both reserve and insert |     // Get header padding size - used for both reserve and insert | ||||||
|     uint8_t header_padding = this->helper_->frame_header_padding(); |     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 |     // Reserve space for header padding + message + footer | ||||||
|     // - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext) |     // - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext) | ||||||
|     // - Footer: space for MAC (16 bytes for Noise, 0 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()); |     shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size()); | ||||||
|     // Insert header padding bytes so message encoding starts at the correct position |     // Resize to add header padding so message encoding starts at the correct position | ||||||
|     this->proto_write_buffer_.insert(this->proto_write_buffer_.begin(), header_padding, 0); |     shared_buf.resize(header_padding); | ||||||
|     return {&this->proto_write_buffer_}; |     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: |  protected: | ||||||
|   friend APIServer; |   // Helper function to fill common entity info fields | ||||||
|  |   static void fill_entity_info_base(esphome::EntityBase *entity, InfoResponseProtoMessage &response) { | ||||||
|   /** |  | ||||||
|    * 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 &)) { |  | ||||||
|     // Set common fields that are shared by all entity types |     // Set common fields that are shared by all entity types | ||||||
|     response.key = entity->get_object_id_hash(); |     response.key = entity->get_object_id_hash(); | ||||||
|     response.object_id = entity->get_object_id(); |     response.object_id = entity->get_object_id(); | ||||||
| @@ -514,48 +301,332 @@ class APIConnection : public APIServerConnection { | |||||||
|     response.icon = entity->get_icon(); |     response.icon = entity->get_icon(); | ||||||
|     response.disabled_by_default = entity->is_disabled_by_default(); |     response.disabled_by_default = entity->is_disabled_by_default(); | ||||||
|     response.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category()); |     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, |     WAITING_FOR_HELLO, | ||||||
|     CONNECTED, |     CONNECTED, | ||||||
|     AUTHENTICATED, |     AUTHENTICATED, | ||||||
|   } connection_state_{ConnectionState::WAITING_FOR_HELLO}; |   } connection_state_{ConnectionState::WAITING_FOR_HELLO}; | ||||||
|  |   uint8_t log_subscription_{ESPHOME_LOG_LEVEL_NONE}; | ||||||
|   bool remove_{false}; |   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 |   // Larger objects at the end | ||||||
|   // Re-use to prevent allocations |   InitialStateIterator initial_state_iterator_; | ||||||
|   std::vector<uint8_t> proto_write_buffer_; |   ListEntitiesIterator list_entities_iterator_; | ||||||
|   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}; |  | ||||||
| #ifdef USE_ESP32_CAMERA | #ifdef USE_ESP32_CAMERA | ||||||
|   esp32_camera::CameraImageReader image_reader_; |   esp32_camera::CameraImageReader image_reader_; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   bool state_subscription_{false}; |   // Function pointer type for message encoding | ||||||
|   int log_subscription_{ESPHOME_LOG_LEVEL_NONE}; |   using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single); | ||||||
|   uint32_t last_traffic_; |  | ||||||
|   uint32_t next_ping_retry_{0}; |   // Optimized MessageCreator class using union dispatch | ||||||
|   uint8_t ping_retries_{0}; |   class MessageCreator { | ||||||
|   bool sent_ping_{false}; |    public: | ||||||
|   bool service_call_subscription_{false}; |     // Constructor for function pointer (message_type = 0) | ||||||
|   bool next_close_ = false; |     MessageCreator(MessageCreatorPtr ptr) : message_type_(0) { data_.ptr = ptr; } | ||||||
|   APIServer *parent_; |  | ||||||
|   DeferredMessageQueue deferred_message_queue_; |     // Constructor for string state capture | ||||||
|   InitialStateIterator initial_state_iterator_; |     MessageCreator(const std::string &value, uint16_t msg_type) : message_type_(msg_type) { | ||||||
|   ListEntitiesIterator list_entities_iterator_; |       data_.string_ptr = new std::string(value); | ||||||
|   int state_subs_at_ = -1; |     } | ||||||
|  |  | ||||||
|  |     // 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 | }  // namespace api | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| #include "api_frame_helper.h" | #include "api_frame_helper.h" | ||||||
| #ifdef USE_API | #ifdef USE_API | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/application.h" | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
| #include "esphome/core/application.h" | #include "esphome/core/log.h" | ||||||
| #include "proto.h" | #include "proto.h" | ||||||
| #include "api_pb2_size.h" | #include "api_pb2_size.h" | ||||||
| #include <cstring> | #include <cstring> | ||||||
| @@ -605,9 +605,21 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) { | |||||||
|   return APIError::OK; |   return APIError::OK; | ||||||
| } | } | ||||||
| APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) { | APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) { | ||||||
|   int err; |   std::vector<uint8_t> *raw_buffer = buffer.get_buffer(); | ||||||
|   APIError aerr; |   uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_); | ||||||
|   aerr = state_action_(); |  | ||||||
|  |   // 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) { |   if (aerr != APIError::OK) { | ||||||
|     return aerr; |     return aerr; | ||||||
|   } |   } | ||||||
| @@ -616,56 +628,66 @@ APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuf | |||||||
|     return APIError::WOULD_BLOCK; |     return APIError::WOULD_BLOCK; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (packets.empty()) { | ||||||
|  |     return APIError::OK; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   std::vector<uint8_t> *raw_buffer = buffer.get_buffer(); |   std::vector<uint8_t> *raw_buffer = buffer.get_buffer(); | ||||||
|   // Message data starts after padding |   this->reusable_iovs_.clear(); | ||||||
|   uint16_t payload_len = raw_buffer->size() - frame_header_padding_; |   this->reusable_iovs_.reserve(packets.size()); | ||||||
|   uint16_t padding = 0; |  | ||||||
|   uint16_t msg_len = 4 + payload_len + padding; |  | ||||||
|  |  | ||||||
|   // We need to resize to include MAC space, but we already reserved it in create_buffer |   // We need to encrypt each packet in place | ||||||
|   raw_buffer->resize(raw_buffer->size() + frame_footer_size_); |   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 |     // The buffer already has padding at offset | ||||||
|   // Buffer layout: |     uint8_t *buf_start = raw_buffer->data() + offset; | ||||||
|   // [0]    - 0x01 indicator byte |  | ||||||
|   // [1-2]  - Size of encrypted payload (filled after encryption) |     // Write noise header | ||||||
|   // [3-4]  - Message type (encrypted) |  | ||||||
|   // [5-6]  - Payload length (encrypted) |  | ||||||
|   // [7...] - Actual payload data (encrypted) |  | ||||||
|   uint8_t *buf_start = raw_buffer->data(); |  | ||||||
|     buf_start[0] = 0x01;  // indicator |     buf_start[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; |     const uint8_t msg_offset = 3; | ||||||
|     buf_start[msg_offset + 0] = (uint8_t) (type >> 8);         // type high byte |     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 + 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 + 2] = (uint8_t) (payload_len >> 8);  // data_len high byte | ||||||
|     buf_start[msg_offset + 3] = (uint8_t) payload_len;         // data_len low 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; |     NoiseBuffer mbuf; | ||||||
|     noise_buffer_init(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_); |     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) { |     if (err != 0) { | ||||||
|       state_ = State::FAILED; |       state_ = State::FAILED; | ||||||
|       HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str()); |       HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str()); | ||||||
|       return APIError::CIPHERSTATE_ENCRYPT_FAILED; |       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[1] = (uint8_t) (mbuf.size >> 8); | ||||||
|     buf_start[2] = (uint8_t) mbuf.size; |     buf_start[2] = (uint8_t) mbuf.size; | ||||||
|  |  | ||||||
|  |     // Add iovec for this encrypted packet | ||||||
|     struct iovec iov; |     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_base = buf_start; | ||||||
|   iov.iov_len = total_len; |     iov.iov_len = 3 + mbuf.size;  // indicator + size + encrypted data | ||||||
|  |     this->reusable_iovs_.push_back(iov); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // write raw to not have two packets sent if NAGLE disabled |   // Send all encrypted packets in one writev call | ||||||
|   return this->write_raw_(&iov, 1); |   return this->write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size()); | ||||||
| } | } | ||||||
|  |  | ||||||
| APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) { | APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) { | ||||||
|   uint8_t header[3]; |   uint8_t header[3]; | ||||||
|   header[0] = 0x01;  // indicator |   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) | // 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) { | void noise_rand_bytes(void *output, size_t len) { | ||||||
|   if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), 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(); |     arch_restart(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -1004,24 +1026,40 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) { | |||||||
|   return APIError::OK; |   return APIError::OK; | ||||||
| } | } | ||||||
| APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) { | 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) { |   if (state_ != State::DATA) { | ||||||
|     return APIError::BAD_STATE; |     return APIError::BAD_STATE; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   std::vector<uint8_t> *raw_buffer = buffer.get_buffer(); |   if (packets.empty()) { | ||||||
|   // Message data starts after padding (frame_header_padding_ = 6) |     return APIError::OK; | ||||||
|   uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_); |   } | ||||||
|  |  | ||||||
|   // 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 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 type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(type)); | ||||||
|     uint8_t total_header_len = 1 + size_varint_len + type_varint_len; |     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 |     // Calculate where to start writing the header | ||||||
|     // The header starts at the latest possible position to minimize unused padding |     // 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) |     // [1-3]  - Payload size varint (3 bytes, for sizes 16384-2097151) | ||||||
|     // [4-5]  - Message type varint (2 bytes, for types 128-32767) |     // [4-5]  - Message type varint (2 bytes, for types 128-32767) | ||||||
|     // [6...] - Actual payload data |     // [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 |     // Write the plaintext header | ||||||
|     buf_start[header_offset] = 0x00;  // indicator |     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 |     // Encode type varint directly into buffer | ||||||
|     ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len); |     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; |     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_base = buf_start + header_offset; | ||||||
|     iov.iov_len = total_header_len + payload_len; |     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 | #endif  // USE_API_PLAINTEXT | ||||||
|   | |||||||
| @@ -27,6 +27,17 @@ struct ReadPacketBuffer { | |||||||
|   uint16_t data_len; |   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 { | enum class APIError : int { | ||||||
|   OK = 0, |   OK = 0, | ||||||
|   WOULD_BLOCK = 1001, |   WOULD_BLOCK = 1001, | ||||||
| @@ -87,6 +98,10 @@ class APIFrameHelper { | |||||||
|   // Give this helper a name for logging |   // Give this helper a name for logging | ||||||
|   void set_log_info(std::string info) { info_ = std::move(info); } |   void set_log_info(std::string info) { info_ = std::move(info); } | ||||||
|   virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) = 0; |   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 |   // Get the frame header padding required by this protocol | ||||||
|   virtual uint8_t frame_header_padding() = 0; |   virtual uint8_t frame_header_padding() = 0; | ||||||
|   // Get the frame footer size required by this protocol |   // 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; } |     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 |   // Common implementation for writing raw data to socket | ||||||
|   APIError write_raw_(const struct iovec *iov, int iovcnt); |   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, |   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); |                       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_header_padding_{0}; | ||||||
|   uint8_t frame_footer_size_{0}; |   uint8_t frame_footer_size_{0}; | ||||||
|  |   // 5 bytes total, 3 bytes padding | ||||||
|   // Receive buffer for reading frame data |  | ||||||
|   std::vector<uint8_t> rx_buf_; |  | ||||||
|   uint16_t rx_buf_len_ = 0; |  | ||||||
|  |  | ||||||
|   // Common initialization for both plaintext and noise protocols |   // Common initialization for both plaintext and noise protocols | ||||||
|   APIError init_common_(); |   APIError init_common_(); | ||||||
| @@ -182,6 +194,7 @@ class APINoiseFrameHelper : public APIFrameHelper { | |||||||
|   APIError loop() override; |   APIError loop() override; | ||||||
|   APIError read_packet(ReadPacketBuffer *buffer) override; |   APIError read_packet(ReadPacketBuffer *buffer) override; | ||||||
|   APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer 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 |   // Get the frame header padding required by this protocol | ||||||
|   uint8_t frame_header_padding() override { return frame_header_padding_; } |   uint8_t frame_header_padding() override { return frame_header_padding_; } | ||||||
|   // Get the frame footer size required by this protocol |   // Get the frame footer size required by this protocol | ||||||
| @@ -194,19 +207,28 @@ class APINoiseFrameHelper : public APIFrameHelper { | |||||||
|   APIError init_handshake_(); |   APIError init_handshake_(); | ||||||
|   APIError check_handshake_finished_(); |   APIError check_handshake_finished_(); | ||||||
|   void send_explicit_handshake_reject_(const std::string &reason); |   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: |   // Fixed-size header buffer for noise protocol: | ||||||
|   // 1 byte for indicator + 2 bytes for message size (16-bit value, not varint) |   // 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 |   // 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_[3]; | ||||||
|   uint8_t rx_header_buf_len_ = 0; |   uint8_t rx_header_buf_len_ = 0; | ||||||
|  |   // 4 bytes total, no padding | ||||||
|   std::vector<uint8_t> prologue_; |  | ||||||
|  |  | ||||||
|   std::shared_ptr<APINoiseContext> ctx_; |  | ||||||
|   NoiseHandshakeState *handshake_{nullptr}; |  | ||||||
|   NoiseCipherState *send_cipher_{nullptr}; |  | ||||||
|   NoiseCipherState *recv_cipher_{nullptr}; |  | ||||||
|   NoiseProtocolId nid_; |  | ||||||
| }; | }; | ||||||
| #endif  // USE_API_NOISE | #endif  // USE_API_NOISE | ||||||
|  |  | ||||||
| @@ -226,12 +248,19 @@ class APIPlaintextFrameHelper : public APIFrameHelper { | |||||||
|   APIError loop() override; |   APIError loop() override; | ||||||
|   APIError read_packet(ReadPacketBuffer *buffer) override; |   APIError read_packet(ReadPacketBuffer *buffer) override; | ||||||
|   APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer 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_; } |   uint8_t frame_header_padding() override { return frame_header_padding_; } | ||||||
|   // Get the frame footer size required by this protocol |   // Get the frame footer size required by this protocol | ||||||
|   uint8_t frame_footer_size() override { return frame_footer_size_; } |   uint8_t frame_footer_size() override { return frame_footer_size_; } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   APIError try_read_frame_(ParsedFrame *frame); |   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: |   // Fixed-size header buffer for plaintext protocol: | ||||||
|   // We now store the indicator byte + the two varints. |   // We now store the indicator byte + the two varints. | ||||||
|   // To match noise protocol's maximum message size (UINT16_MAX = 65535), we need: |   // 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_[6];  // 1 byte indicator + 5 bytes for varints (3 for size + 2 for type) | ||||||
|   uint8_t rx_header_buf_pos_ = 0; |   uint8_t rx_header_buf_pos_ = 0; | ||||||
|   bool rx_header_parsed_ = false; |   bool rx_header_parsed_ = false; | ||||||
|   uint16_t rx_header_parsed_type_ = 0; |   // 8 bytes total, no padding needed | ||||||
|   uint16_t rx_header_parsed_len_ = 0; |  | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,4 +21,5 @@ extend google.protobuf.MessageOptions { | |||||||
|     optional string ifdef = 1038; |     optional string ifdef = 1038; | ||||||
|     optional bool log = 1039 [default=true]; |     optional bool log = 1039 [default=true]; | ||||||
|     optional bool no_delay = 1040 [default=false]; |     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"; |       return "VOICE_ASSISTANT_TTS_STREAM_START"; | ||||||
|     case enums::VOICE_ASSISTANT_TTS_STREAM_END: |     case enums::VOICE_ASSISTANT_TTS_STREAM_END: | ||||||
|       return "VOICE_ASSISTANT_TTS_STREAM_END"; |       return "VOICE_ASSISTANT_TTS_STREAM_END"; | ||||||
|  |     case enums::VOICE_ASSISTANT_INTENT_PROGRESS: | ||||||
|  |       return "VOICE_ASSISTANT_INTENT_PROGRESS"; | ||||||
|     default: |     default: | ||||||
|       return "UNKNOWN"; |       return "UNKNOWN"; | ||||||
|   } |   } | ||||||
| @@ -628,6 +630,7 @@ template<> const char *proto_enum_to_string<enums::UpdateCommand>(enums::UpdateC | |||||||
|   } |   } | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||||
|   switch (field_id) { |   switch (field_id) { | ||||||
|     case 2: { |     case 2: { | ||||||
| @@ -794,28 +797,18 @@ void ConnectResponse::dump_to(std::string &out) const { | |||||||
|   out.append("}"); |   out.append("}"); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| void DisconnectRequest::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void DisconnectRequest::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void DisconnectRequest::dump_to(std::string &out) const { out.append("DisconnectRequest {}"); } | void DisconnectRequest::dump_to(std::string &out) const { out.append("DisconnectRequest {}"); } | ||||||
| #endif | #endif | ||||||
| void DisconnectResponse::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void DisconnectResponse::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void DisconnectResponse::dump_to(std::string &out) const { out.append("DisconnectResponse {}"); } | void DisconnectResponse::dump_to(std::string &out) const { out.append("DisconnectResponse {}"); } | ||||||
| #endif | #endif | ||||||
| void PingRequest::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void PingRequest::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void PingRequest::dump_to(std::string &out) const { out.append("PingRequest {}"); } | void PingRequest::dump_to(std::string &out) const { out.append("PingRequest {}"); } | ||||||
| #endif | #endif | ||||||
| void PingResponse::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void PingResponse::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {}"); } | void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {}"); } | ||||||
| #endif | #endif | ||||||
| void DeviceInfoRequest::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void DeviceInfoRequest::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); } | void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); } | ||||||
| #endif | #endif | ||||||
| @@ -1103,18 +1096,12 @@ void DeviceInfoResponse::dump_to(std::string &out) const { | |||||||
|   out.append("}"); |   out.append("}"); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void ListEntitiesRequest::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void ListEntitiesRequest::dump_to(std::string &out) const { out.append("ListEntitiesRequest {}"); } | void ListEntitiesRequest::dump_to(std::string &out) const { out.append("ListEntitiesRequest {}"); } | ||||||
| #endif | #endif | ||||||
| void ListEntitiesDoneResponse::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void ListEntitiesDoneResponse::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void ListEntitiesDoneResponse::dump_to(std::string &out) const { out.append("ListEntitiesDoneResponse {}"); } | void ListEntitiesDoneResponse::dump_to(std::string &out) const { out.append("ListEntitiesDoneResponse {}"); } | ||||||
| #endif | #endif | ||||||
| void SubscribeStatesRequest::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void SubscribeStatesRequest::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void SubscribeStatesRequest::dump_to(std::string &out) const { out.append("SubscribeStatesRequest {}"); } | void SubscribeStatesRequest::dump_to(std::string &out) const { out.append("SubscribeStatesRequest {}"); } | ||||||
| #endif | #endif | ||||||
| @@ -3512,8 +3499,6 @@ void NoiseEncryptionSetKeyResponse::dump_to(std::string &out) const { | |||||||
|   out.append("}"); |   out.append("}"); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| void SubscribeHomeassistantServicesRequest::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void SubscribeHomeassistantServicesRequest::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const { | void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const { | ||||||
|   out.append("SubscribeHomeassistantServicesRequest {}"); |   out.append("SubscribeHomeassistantServicesRequest {}"); | ||||||
| @@ -3639,8 +3624,6 @@ void HomeassistantServiceResponse::dump_to(std::string &out) const { | |||||||
|   out.append("}"); |   out.append("}"); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| void SubscribeHomeAssistantStatesRequest::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void SubscribeHomeAssistantStatesRequest::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const { | void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const { | ||||||
|   out.append("SubscribeHomeAssistantStatesRequest {}"); |   out.append("SubscribeHomeAssistantStatesRequest {}"); | ||||||
| @@ -3744,8 +3727,6 @@ void HomeAssistantStateResponse::dump_to(std::string &out) const { | |||||||
|   out.append("}"); |   out.append("}"); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void GetTimeRequest::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); } | void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); } | ||||||
| #endif | #endif | ||||||
| @@ -7717,8 +7698,6 @@ void BluetoothGATTNotifyDataResponse::dump_to(std::string &out) const { | |||||||
|   out.append("}"); |   out.append("}"); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| void SubscribeBluetoothConnectionsFreeRequest::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void SubscribeBluetoothConnectionsFreeRequest::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void SubscribeBluetoothConnectionsFreeRequest::dump_to(std::string &out) const { | void SubscribeBluetoothConnectionsFreeRequest::dump_to(std::string &out) const { | ||||||
|   out.append("SubscribeBluetoothConnectionsFreeRequest {}"); |   out.append("SubscribeBluetoothConnectionsFreeRequest {}"); | ||||||
| @@ -8002,8 +7981,6 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const { | |||||||
|   out.append("}"); |   out.append("}"); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| void UnsubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void UnsubscribeBluetoothLEAdvertisementsRequest::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const { | void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const { | ||||||
|   out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}"); |   out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}"); | ||||||
| @@ -8669,8 +8646,6 @@ void VoiceAssistantWakeWord::dump_to(std::string &out) const { | |||||||
|   out.append("}"); |   out.append("}"); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| void VoiceAssistantConfigurationRequest::encode(ProtoWriteBuffer buffer) const {} |  | ||||||
| void VoiceAssistantConfigurationRequest::calculate_size(uint32_t &total_size) const {} |  | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const { | void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const { | ||||||
|   out.append("VoiceAssistantConfigurationRequest {}"); |   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 { | class APIServerConnectionBase : public ProtoService { | ||||||
|  public: |  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){}; |   virtual void on_hello_request(const HelloRequest &value){}; | ||||||
|   bool send_hello_response(const HelloResponse &msg); |  | ||||||
|   virtual void on_connect_request(const ConnectRequest &value){}; |   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){}; |   virtual void on_disconnect_request(const DisconnectRequest &value){}; | ||||||
|   bool send_disconnect_response(const DisconnectResponse &msg); |  | ||||||
|   virtual void on_disconnect_response(const DisconnectResponse &value){}; |   virtual void on_disconnect_response(const DisconnectResponse &value){}; | ||||||
|   bool send_ping_request(const PingRequest &msg); |  | ||||||
|   virtual void on_ping_request(const PingRequest &value){}; |   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_ping_response(const PingResponse &value){}; | ||||||
|   virtual void on_device_info_request(const DeviceInfoRequest &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){}; |   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){}; |   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 | #ifdef USE_COVER | ||||||
|   virtual void on_cover_command_request(const CoverCommandRequest &value){}; |   virtual void on_cover_command_request(const CoverCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_FAN | ||||||
|   virtual void on_fan_command_request(const FanCommandRequest &value){}; |   virtual void on_fan_command_request(const FanCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_LIGHT | ||||||
|   virtual void on_light_command_request(const LightCommandRequest &value){}; |   virtual void on_light_command_request(const LightCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_SWITCH | ||||||
|   virtual void on_switch_command_request(const SwitchCommandRequest &value){}; |   virtual void on_switch_command_request(const SwitchCommandRequest &value){}; | ||||||
| #endif | #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){}; |   virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){}; | ||||||
|   bool send_subscribe_logs_response(const SubscribeLogsResponse &msg); |  | ||||||
| #ifdef USE_API_NOISE | #ifdef USE_API_NOISE | ||||||
|   virtual void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &value){}; |   virtual void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &value){}; | ||||||
| #endif | #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){}; |   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){}; |   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){}; |   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){}; |   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){}; |   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){}; |   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 | #ifdef USE_ESP32_CAMERA | ||||||
|   virtual void on_camera_image_request(const CameraImageRequest &value){}; |   virtual void on_camera_image_request(const CameraImageRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_CLIMATE | ||||||
|   virtual void on_climate_command_request(const ClimateCommandRequest &value){}; |   virtual void on_climate_command_request(const ClimateCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_NUMBER | ||||||
|   virtual void on_number_command_request(const NumberCommandRequest &value){}; |   virtual void on_number_command_request(const NumberCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_SELECT | ||||||
|   virtual void on_select_command_request(const SelectCommandRequest &value){}; |   virtual void on_select_command_request(const SelectCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_SIREN | ||||||
|   virtual void on_siren_command_request(const SirenCommandRequest &value){}; |   virtual void on_siren_command_request(const SirenCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_LOCK | ||||||
|   virtual void on_lock_command_request(const LockCommandRequest &value){}; |   virtual void on_lock_command_request(const LockCommandRequest &value){}; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_BUTTON |  | ||||||
|   bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg); |  | ||||||
| #endif |  | ||||||
| #ifdef USE_BUTTON | #ifdef USE_BUTTON | ||||||
|   virtual void on_button_command_request(const ButtonCommandRequest &value){}; |   virtual void on_button_command_request(const ButtonCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_MEDIA_PLAYER | ||||||
|   virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){}; |   virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){}; | ||||||
| #endif | #endif | ||||||
| @@ -173,33 +105,19 @@ class APIServerConnectionBase : public ProtoService { | |||||||
|   virtual void on_subscribe_bluetooth_le_advertisements_request( |   virtual void on_subscribe_bluetooth_le_advertisements_request( | ||||||
|       const SubscribeBluetoothLEAdvertisementsRequest &value){}; |       const SubscribeBluetoothLEAdvertisementsRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_BLUETOOTH_PROXY | ||||||
|   virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){}; |   virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){}; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_BLUETOOTH_PROXY |  | ||||||
|   bool send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg); |  | ||||||
| #endif |  | ||||||
| #ifdef USE_BLUETOOTH_PROXY | #ifdef USE_BLUETOOTH_PROXY | ||||||
|   virtual void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &value){}; |   virtual void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_BLUETOOTH_PROXY | ||||||
|   virtual void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &value){}; |   virtual void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &value){}; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_BLUETOOTH_PROXY |  | ||||||
|   bool send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg); |  | ||||||
| #endif |  | ||||||
| #ifdef USE_BLUETOOTH_PROXY | #ifdef USE_BLUETOOTH_PROXY | ||||||
|   virtual void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &value){}; |   virtual void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &value){}; | ||||||
| #endif | #endif | ||||||
| @@ -212,49 +130,23 @@ class APIServerConnectionBase : public ProtoService { | |||||||
| #ifdef USE_BLUETOOTH_PROXY | #ifdef USE_BLUETOOTH_PROXY | ||||||
|   virtual void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &value){}; |   virtual void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &value){}; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_BLUETOOTH_PROXY |  | ||||||
|   bool send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg); |  | ||||||
| #endif |  | ||||||
| #ifdef USE_BLUETOOTH_PROXY | #ifdef USE_BLUETOOTH_PROXY | ||||||
|   virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){}; |   virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_BLUETOOTH_PROXY | ||||||
|   virtual void on_unsubscribe_bluetooth_le_advertisements_request( |   virtual void on_unsubscribe_bluetooth_le_advertisements_request( | ||||||
|       const UnsubscribeBluetoothLEAdvertisementsRequest &value){}; |       const UnsubscribeBluetoothLEAdvertisementsRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_BLUETOOTH_PROXY | ||||||
|   virtual void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &value){}; |   virtual void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &value){}; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_VOICE_ASSISTANT | #ifdef USE_VOICE_ASSISTANT | ||||||
|   virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){}; |   virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){}; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_VOICE_ASSISTANT |  | ||||||
|   bool send_voice_assistant_request(const VoiceAssistantRequest &msg); |  | ||||||
| #endif |  | ||||||
| #ifdef USE_VOICE_ASSISTANT | #ifdef USE_VOICE_ASSISTANT | ||||||
|   virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){}; |   virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){}; | ||||||
| #endif | #endif | ||||||
| @@ -262,7 +154,6 @@ class APIServerConnectionBase : public ProtoService { | |||||||
|   virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){}; |   virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){}; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_VOICE_ASSISTANT | #ifdef USE_VOICE_ASSISTANT | ||||||
|   bool send_voice_assistant_audio(const VoiceAssistantAudio &msg); |  | ||||||
|   virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){}; |   virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){}; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_VOICE_ASSISTANT | #ifdef USE_VOICE_ASSISTANT | ||||||
| @@ -271,84 +162,39 @@ class APIServerConnectionBase : public ProtoService { | |||||||
| #ifdef USE_VOICE_ASSISTANT | #ifdef USE_VOICE_ASSISTANT | ||||||
|   virtual void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &value){}; |   virtual void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &value){}; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_VOICE_ASSISTANT |  | ||||||
|   bool send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg); |  | ||||||
| #endif |  | ||||||
| #ifdef USE_VOICE_ASSISTANT | #ifdef USE_VOICE_ASSISTANT | ||||||
|   virtual void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &value){}; |   virtual void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &value){}; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_VOICE_ASSISTANT |  | ||||||
|   bool send_voice_assistant_configuration_response(const VoiceAssistantConfigurationResponse &msg); |  | ||||||
| #endif |  | ||||||
| #ifdef USE_VOICE_ASSISTANT | #ifdef USE_VOICE_ASSISTANT | ||||||
|   virtual void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &value){}; |   virtual void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &value){}; | ||||||
| #endif | #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 | #ifdef USE_ALARM_CONTROL_PANEL | ||||||
|   virtual void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &value){}; |   virtual void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_TEXT | ||||||
|   virtual void on_text_command_request(const TextCommandRequest &value){}; |   virtual void on_text_command_request(const TextCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_DATETIME_DATE | ||||||
|   virtual void on_date_command_request(const DateCommandRequest &value){}; |   virtual void on_date_command_request(const DateCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_DATETIME_TIME | ||||||
|   virtual void on_time_command_request(const TimeCommandRequest &value){}; |   virtual void on_time_command_request(const TimeCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_VALVE | ||||||
|   virtual void on_valve_command_request(const ValveCommandRequest &value){}; |   virtual void on_valve_command_request(const ValveCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_DATETIME_DATETIME | ||||||
|   virtual void on_date_time_command_request(const DateTimeCommandRequest &value){}; |   virtual void on_date_time_command_request(const DateTimeCommandRequest &value){}; | ||||||
| #endif | #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 | #ifdef USE_UPDATE | ||||||
|   virtual void on_update_command_request(const UpdateCommandRequest &value){}; |   virtual void on_update_command_request(const UpdateCommandRequest &value){}; | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -24,7 +24,11 @@ static const char *const TAG = "api"; | |||||||
| // APIServer | // APIServer | ||||||
| APIServer *global_api_server = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | 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() { | void APIServer::setup() { | ||||||
|   ESP_LOGCONFIG(TAG, "Running setup"); |   ESP_LOGCONFIG(TAG, "Running setup"); | ||||||
| @@ -88,6 +92,12 @@ void APIServer::setup() { | |||||||
| #ifdef USE_LOGGER | #ifdef USE_LOGGER | ||||||
|   if (logger::global_logger != nullptr) { |   if (logger::global_logger != nullptr) { | ||||||
|     logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) { |     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_) { |       for (auto &c : this->clients_) { | ||||||
|         if (!c->remove_) |         if (!c->remove_) | ||||||
|           c->try_send_log_message(level, tag, message); |           c->try_send_log_message(level, tag, message); | ||||||
| @@ -96,7 +106,7 @@ void APIServer::setup() { | |||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   this->last_connected_ = millis(); |   this->last_connected_ = App.get_loop_component_start_time(); | ||||||
|  |  | ||||||
| #ifdef USE_ESP32_CAMERA | #ifdef USE_ESP32_CAMERA | ||||||
|   if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) { |   if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) { | ||||||
| @@ -112,8 +122,8 @@ void APIServer::setup() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void APIServer::loop() { | void APIServer::loop() { | ||||||
|   // Accept new clients only if the socket has incoming connections |   // Accept new clients only if the socket exists and has incoming connections | ||||||
|   if (this->socket_->ready()) { |   if (this->socket_ && this->socket_->ready()) { | ||||||
|     while (true) { |     while (true) { | ||||||
|       struct sockaddr_storage source_addr; |       struct sockaddr_storage source_addr; | ||||||
|       socklen_t addr_len = sizeof(source_addr); |       socklen_t addr_len = sizeof(source_addr); | ||||||
| @@ -154,7 +164,7 @@ void APIServer::loop() { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (this->reboot_timeout_ != 0) { |   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 (!this->is_connected()) { | ||||||
|       if (now - this->last_connected_ > this->reboot_timeout_) { |       if (now - this->last_connected_ > this->reboot_timeout_) { | ||||||
|         ESP_LOGE(TAG, "No client connected; rebooting"); |         ESP_LOGE(TAG, "No client connected; rebooting"); | ||||||
| @@ -169,8 +179,10 @@ void APIServer::loop() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void APIServer::dump_config() { | void APIServer::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "API Server:"); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Address: %s:%u", network::get_use_address().c_str(), this->port_); |                 "API Server:\n" | ||||||
|  |                 "  Address: %s:%u", | ||||||
|  |                 network::get_use_address().c_str(), this->port_); | ||||||
| #ifdef USE_API_NOISE | #ifdef USE_API_NOISE | ||||||
|   ESP_LOGCONFIG(TAG, "  Using noise encryption: %s", YESNO(this->noise_ctx_->has_psk())); |   ESP_LOGCONFIG(TAG, "  Using noise encryption: %s", YESNO(this->noise_ctx_->has_psk())); | ||||||
|   if (!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) {} | void APIServer::handle_disconnect(APIConnection *conn) {} | ||||||
|  |  | ||||||
| #ifdef USE_BINARY_SENSOR | #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()) |   if (obj->is_internal()) | ||||||
|     return; |     return; | ||||||
|   for (auto &c : this->clients_) |   for (auto &c : this->clients_) | ||||||
|     c->send_binary_sensor_state(obj, state); |     c->send_binary_sensor_state(obj); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -255,7 +267,7 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) { | |||||||
|   if (obj->is_internal()) |   if (obj->is_internal()) | ||||||
|     return; |     return; | ||||||
|   for (auto &c : this->clients_) |   for (auto &c : this->clients_) | ||||||
|     c->send_sensor_state(obj, state); |     c->send_sensor_state(obj); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -264,7 +276,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) { | |||||||
|   if (obj->is_internal()) |   if (obj->is_internal()) | ||||||
|     return; |     return; | ||||||
|   for (auto &c : this->clients_) |   for (auto &c : this->clients_) | ||||||
|     c->send_switch_state(obj, state); |     c->send_switch_state(obj); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -273,7 +285,7 @@ void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s | |||||||
|   if (obj->is_internal()) |   if (obj->is_internal()) | ||||||
|     return; |     return; | ||||||
|   for (auto &c : this->clients_) |   for (auto &c : this->clients_) | ||||||
|     c->send_text_sensor_state(obj, state); |     c->send_text_sensor_state(obj); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -291,7 +303,7 @@ void APIServer::on_number_update(number::Number *obj, float state) { | |||||||
|   if (obj->is_internal()) |   if (obj->is_internal()) | ||||||
|     return; |     return; | ||||||
|   for (auto &c : this->clients_) |   for (auto &c : this->clients_) | ||||||
|     c->send_number_state(obj, state); |     c->send_number_state(obj); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -327,7 +339,7 @@ void APIServer::on_text_update(text::Text *obj, const std::string &state) { | |||||||
|   if (obj->is_internal()) |   if (obj->is_internal()) | ||||||
|     return; |     return; | ||||||
|   for (auto &c : this->clients_) |   for (auto &c : this->clients_) | ||||||
|     c->send_text_state(obj, state); |     c->send_text_state(obj); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -336,7 +348,7 @@ void APIServer::on_select_update(select::Select *obj, const std::string &state, | |||||||
|   if (obj->is_internal()) |   if (obj->is_internal()) | ||||||
|     return; |     return; | ||||||
|   for (auto &c : this->clients_) |   for (auto &c : this->clients_) | ||||||
|     c->send_select_state(obj, state); |     c->send_select_state(obj); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -345,7 +357,7 @@ void APIServer::on_lock_update(lock::Lock *obj) { | |||||||
|   if (obj->is_internal()) |   if (obj->is_internal()) | ||||||
|     return; |     return; | ||||||
|   for (auto &c : this->clients_) |   for (auto &c : this->clients_) | ||||||
|     c->send_lock_state(obj, obj->state); |     c->send_lock_state(obj); | ||||||
| } | } | ||||||
| #endif | #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_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) { | void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) { | ||||||
|   for (auto &client : this->clients_) { |   for (auto &client : this->clients_) { | ||||||
|     client->send_homeassistant_service_call(call); |     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"); |       ESP_LOGW(TAG, "Disconnecting all clients to reset connections"); | ||||||
|       this->set_noise_psk(psk); |       this->set_noise_psk(psk); | ||||||
|       for (auto &c : this->clients_) { |       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(); } | bool APIServer::is_connected() const { return !this->clients_.empty(); } | ||||||
|  |  | ||||||
| void APIServer::on_shutdown() { | void APIServer::on_shutdown() { | ||||||
|   for (auto &c : this->clients_) { |   this->shutting_down_ = true; | ||||||
|     c->send_disconnect_request(DisconnectRequest()); |  | ||||||
|  |   // 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 | }  // namespace api | ||||||
|   | |||||||
| @@ -34,11 +34,17 @@ class APIServer : public Component, public Controller { | |||||||
|   void loop() override; |   void loop() override; | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
|   void on_shutdown() override; |   void on_shutdown() override; | ||||||
|  |   bool teardown() override; | ||||||
|   bool check_password(const std::string &password) const; |   bool check_password(const std::string &password) const; | ||||||
|   bool uses_password() const; |   bool uses_password() const; | ||||||
|   void set_port(uint16_t port); |   void set_port(uint16_t port); | ||||||
|   void set_password(const std::string &password); |   void set_password(const std::string &password); | ||||||
|   void set_reboot_timeout(uint32_t reboot_timeout); |   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 | #ifdef USE_API_NOISE | ||||||
|   bool save_noise_psk(psk_t psk, bool make_active = true); |   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); |   void handle_disconnect(APIConnection *conn); | ||||||
| #ifdef USE_BINARY_SENSOR | #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 | #endif | ||||||
| #ifdef USE_COVER | #ifdef USE_COVER | ||||||
|   void on_cover_update(cover::Cover *obj) override; |   void on_cover_update(cover::Cover *obj) override; | ||||||
| @@ -136,17 +142,28 @@ class APIServer : public Component, public Controller { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|  |   // Pointers and pointer-like types first (4 bytes each) | ||||||
|   std::unique_ptr<socket::Socket> socket_ = nullptr; |   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_connected_trigger_ = new Trigger<std::string, std::string>(); | ||||||
|   Trigger<std::string, std::string> *client_disconnected_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 | #ifdef USE_API_NOISE | ||||||
|   std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>(); |   std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>(); | ||||||
|   ESPPreferenceObject noise_pref_; |   ESPPreferenceObject noise_pref_; | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ from datetime import datetime | |||||||
| import logging | import logging | ||||||
| from typing import TYPE_CHECKING, Any | 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 aioesphomeapi.log_runner import async_run | ||||||
|  |  | ||||||
| from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__ | 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() |         time_ = datetime.now() | ||||||
|         message: bytes = msg.message |         message: bytes = msg.message | ||||||
|         text = message.decode("utf8", "backslashreplace") |         text = message.decode("utf8", "backslashreplace") | ||||||
|         if dashboard: |         for parsed_msg in parse_log_message( | ||||||
|             text = text.replace("\033", "\\033") |             text, f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]" | ||||||
|         print(f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]{text}") |         ): | ||||||
|  |             print(parsed_msg.replace("\033", "\\033") if dashboard else parsed_msg) | ||||||
|  |  | ||||||
|     stop = await async_run(cli, on_log, name=name) |     stop = await async_run(cli, on_log, name=name) | ||||||
|     try: |     try: | ||||||
|   | |||||||
| @@ -3,8 +3,8 @@ | |||||||
| #include "api_server.h" | #include "api_server.h" | ||||||
| #ifdef USE_API | #ifdef USE_API | ||||||
| #include "api_pb2.h" | #include "api_pb2.h" | ||||||
| #include "esphome/core/helpers.h" |  | ||||||
| #include "esphome/core/automation.h" | #include "esphome/core/automation.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
|   | |||||||
| @@ -73,7 +73,7 @@ bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done( | |||||||
| ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {} | ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {} | ||||||
| bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) { | bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) { | ||||||
|   auto resp = service->encode_list_service_response(); |   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 | #ifdef USE_ESP32_CAMERA | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #include "proto.h" | #include "proto.h" | ||||||
| #include <cinttypes> | #include <cinttypes> | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/log.h" |  | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| @@ -216,7 +216,7 @@ class ProtoWriteBuffer { | |||||||
|     this->buffer_->insert(this->buffer_->end(), data, data + len); |     this->buffer_->insert(this->buffer_->end(), data, data + len); | ||||||
|   } |   } | ||||||
|   void encode_string(uint32_t field_id, const std::string &value, bool force = false) { |   void encode_string(uint32_t field_id, const std::string &value, bool force = false) { | ||||||
|     this->encode_string(field_id, value.data(), value.size()); |     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) { |   void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) { | ||||||
|     this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force); |     this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force); | ||||||
| @@ -327,9 +327,11 @@ class ProtoWriteBuffer { | |||||||
| class ProtoMessage { | class ProtoMessage { | ||||||
|  public: |  public: | ||||||
|   virtual ~ProtoMessage() = default; |   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); |   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 | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|   std::string dump() const; |   std::string dump() const; | ||||||
|   virtual void dump_to(std::string &out) const = 0; |   virtual void dump_to(std::string &out) const = 0; | ||||||
| @@ -360,11 +362,11 @@ class ProtoService { | |||||||
|    * @return A ProtoWriteBuffer object with the reserved size. |    * @return A ProtoWriteBuffer object with the reserved size. | ||||||
|    */ |    */ | ||||||
|   virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0; |   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; |   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 |   // 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; |     uint32_t msg_size = 0; | ||||||
|     msg.calculate_size(msg_size); |     msg.calculate_size(msg_size); | ||||||
|  |  | ||||||
| @@ -377,6 +379,26 @@ class ProtoService { | |||||||
|     // Send the buffer |     // Send the buffer | ||||||
|     return this->send_buffer(buffer, message_type); |     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 | }  // namespace api | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ namespace api { | |||||||
|  |  | ||||||
| #ifdef USE_BINARY_SENSOR | #ifdef USE_BINARY_SENSOR | ||||||
| bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *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 | #endif | ||||||
| #ifdef USE_COVER | #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); } | bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); } | ||||||
| #endif | #endif | ||||||
| #ifdef USE_SENSOR | #ifdef USE_SENSOR | ||||||
| bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { | bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_state(sensor); } | ||||||
|   return this->client_->send_sensor_state(sensor, sensor->state); |  | ||||||
| } |  | ||||||
| #endif | #endif | ||||||
| #ifdef USE_SWITCH | #ifdef USE_SWITCH | ||||||
| bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { | bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_state(a_switch); } | ||||||
|   return this->client_->send_switch_state(a_switch, a_switch->state); |  | ||||||
| } |  | ||||||
| #endif | #endif | ||||||
| #ifdef USE_TEXT_SENSOR | #ifdef USE_TEXT_SENSOR | ||||||
| bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *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 | #endif | ||||||
| #ifdef USE_CLIMATE | #ifdef USE_CLIMATE | ||||||
| bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); } | bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); } | ||||||
| #endif | #endif | ||||||
| #ifdef USE_NUMBER | #ifdef USE_NUMBER | ||||||
| bool InitialStateIterator::on_number(number::Number *number) { | bool InitialStateIterator::on_number(number::Number *number) { return this->client_->send_number_state(number); } | ||||||
|   return this->client_->send_number_state(number, number->state); |  | ||||||
| } |  | ||||||
| #endif | #endif | ||||||
| #ifdef USE_DATETIME_DATE | #ifdef USE_DATETIME_DATE | ||||||
| bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(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 | #endif | ||||||
| #ifdef USE_TEXT | #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 | #endif | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
| bool InitialStateIterator::on_select(select::Select *select) { | bool InitialStateIterator::on_select(select::Select *select) { return this->client_->send_select_state(select); } | ||||||
|   return this->client_->send_select_state(select, select->state); |  | ||||||
| } |  | ||||||
| #endif | #endif | ||||||
| #ifdef USE_LOCK | #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 | #endif | ||||||
| #ifdef USE_VALVE | #ifdef USE_VALVE | ||||||
| bool InitialStateIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_state(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 | // based on the resonance frequency of the antenna and so it should be trimmed | ||||||
| // before the calibration is done. | // before the calibration is done. | ||||||
| bool AS3935Component::calibrate_oscillator() { | 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->write_register(CALIB_RCO, WIPE_ALL, DIRECT_COMMAND, 0);  // Send command to calibrate the oscillators | ||||||
|  |  | ||||||
|   this->display_oscillator(true, 2); |   this->display_oscillator(true, 2); | ||||||
| @@ -307,7 +307,7 @@ bool AS3935Component::calibrate_oscillator() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void AS3935Component::tune_antenna() { | 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 div_ratio = this->read_div_ratio(); | ||||||
|   uint8_t tune_val = this->read_capacitance(); |   uint8_t tune_val = this->read_capacitance(); | ||||||
|   ESP_LOGI(TAG, "Division Ratio is set to: %d", div_ratio); |   ESP_LOGI(TAG, "Division Ratio is set to: %d", div_ratio); | ||||||
|   | |||||||
| @@ -95,11 +95,13 @@ void AS5600Component::dump_config() { | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGCONFIG(TAG, "  Watchdog: %d", this->watchdog_); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Fast Filter: %d", this->fast_filter_); |                 "  Watchdog: %d\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Slow Filter: %d", this->slow_filter_); |                 "  Fast Filter: %d\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Hysteresis: %d", this->hysteresis_); |                 "  Slow Filter: %d\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Start Position: %d", this->start_position_); |                 "  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) { |   if (this->end_mode_ == END_MODE_POSITION) { | ||||||
|     ESP_LOGCONFIG(TAG, "  End Position: %d", this->end_position_); |     ESP_LOGCONFIG(TAG, "  End Position: %d", this->end_position_); | ||||||
|   } else { |   } else { | ||||||
|   | |||||||
| @@ -41,9 +41,11 @@ void AS7341Component::dump_config() { | |||||||
|     ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); |     ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); | ||||||
|   } |   } | ||||||
|   LOG_UPDATE_INTERVAL(this); |   LOG_UPDATE_INTERVAL(this); | ||||||
|   ESP_LOGCONFIG(TAG, "  Gain: %u", get_gain()); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  ATIME: %u", get_atime()); |                 "  Gain: %u\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  ASTEP: %u", get_astep()); |                 "  ATIME: %u\n" | ||||||
|  |                 "  ASTEP: %u", | ||||||
|  |                 get_gain(), get_atime(), get_astep()); | ||||||
|  |  | ||||||
|   LOG_SENSOR("  ", "F1", this->f1_); |   LOG_SENSOR("  ", "F1", this->f1_); | ||||||
|   LOG_SENSOR("  ", "F2", this->f2_); |   LOG_SENSOR("  ", "F2", this->f2_); | ||||||
|   | |||||||
| @@ -21,8 +21,8 @@ CONFIG_SCHEMA = cv.All( | |||||||
| @coroutine_with_priority(200.0) | @coroutine_with_priority(200.0) | ||||||
| async def to_code(config): | async def to_code(config): | ||||||
|     if CORE.is_esp32 or CORE.is_libretiny: |     if CORE.is_esp32 or CORE.is_libretiny: | ||||||
|         # https://github.com/esphome/AsyncTCP/blob/master/library.json |         # https://github.com/ESP32Async/AsyncTCP | ||||||
|         cg.add_library("esphome/AsyncTCP-esphome", "2.1.4") |         cg.add_library("ESP32Async/AsyncTCP", "3.4.4") | ||||||
|     elif CORE.is_esp8266: |     elif CORE.is_esp8266: | ||||||
|         # https://github.com/esphome/ESPAsyncTCP |         # https://github.com/ESP32Async/ESPAsyncTCP | ||||||
|         cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0") |         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); } | void AT581XComponent::dump_config() { LOG_I2C_DEVICE(this); } | ||||||
| #define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) | #define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) | ||||||
| bool AT581XComponent::i2c_write_config() { | bool AT581XComponent::i2c_write_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "Writing new config for AT581X..."); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "Frequency: %dMHz", this->freq_); |                 "Writing new config for AT581X\n" | ||||||
|   ESP_LOGCONFIG(TAG, "Sensing distance: %d", this->delta_); |                 "Frequency: %dMHz\n" | ||||||
|   ESP_LOGCONFIG(TAG, "Power: %dµA", this->power_); |                 "Sensing distance: %d\n" | ||||||
|   ESP_LOGCONFIG(TAG, "Gain: %d", this->gain_); |                 "Power: %dµA\n" | ||||||
|   ESP_LOGCONFIG(TAG, "Trigger base time: %dms", this->trigger_base_time_ms_); |                 "Gain: %d\n" | ||||||
|   ESP_LOGCONFIG(TAG, "Trigger keep time: %dms", this->trigger_keep_time_ms_); |                 "Trigger base time: %dms\n" | ||||||
|   ESP_LOGCONFIG(TAG, "Protect time: %dms", this->protect_time_ms_); |                 "Trigger keep time: %dms\n" | ||||||
|   ESP_LOGCONFIG(TAG, "Self check time: %dms", this->self_check_time_ms_); |                 "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 |   // Set frequency point | ||||||
|   if (!this->i2c_write_reg(FREQ_ADDR, GAIN61_VALUE)) { |   if (!this->i2c_write_reg(FREQ_ADDR, GAIN61_VALUE)) { | ||||||
|   | |||||||
| @@ -686,7 +686,7 @@ void ATM90E32Component::restore_power_offset_calibrations_() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void ATM90E32Component::clear_gain_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++) { |   for (int phase = 0; phase < 3; phase++) { | ||||||
|     gain_phase_[phase].voltage_gain = this->phase_[phase].voltage_gain_; |     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) { | bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) { | ||||||
|   this->buffer_size_ = 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_); |   this->buffer_ = allocator.allocate(this->buffer_size_); | ||||||
|   if (this->buffer_ == nullptr) { |   if (this->buffer_ == nullptr) { | ||||||
| @@ -101,7 +101,7 @@ bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) { | |||||||
|  |  | ||||||
| void AudioTransferBuffer::deallocate_buffer_() { | void AudioTransferBuffer::deallocate_buffer_() { | ||||||
|   if (this->buffer_ != nullptr) { |   if (this->buffer_ != nullptr) { | ||||||
|     RAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); |     RAMAllocator<uint8_t> allocator; | ||||||
|     allocator.deallocate(this->buffer_, this->buffer_size_); |     allocator.deallocate(this->buffer_, this->buffer_size_); | ||||||
|     this->buffer_ = nullptr; |     this->buffer_ = nullptr; | ||||||
|     this->data_start_ = nullptr; |     this->data_start_ = nullptr; | ||||||
|   | |||||||
| @@ -60,8 +60,10 @@ void AXS15231Touchscreen::dump_config() { | |||||||
|   LOG_I2C_DEVICE(this); |   LOG_I2C_DEVICE(this); | ||||||
|   LOG_PIN("  Interrupt Pin: ", this->interrupt_pin_); |   LOG_PIN("  Interrupt Pin: ", this->interrupt_pin_); | ||||||
|   LOG_PIN("  Reset Pin: ", this->reset_pin_); |   LOG_PIN("  Reset Pin: ", this->reset_pin_); | ||||||
|   ESP_LOGCONFIG(TAG, "  Width: %d", this->x_raw_max_); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Height: %d", this->y_raw_max_); |                 "  Width: %d\n" | ||||||
|  |                 "  Height: %d", | ||||||
|  |                 this->x_raw_max_, this->y_raw_max_); | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace axs15231 | }  // 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::set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; } | ||||||
| void BangBangClimate::dump_config() { | void BangBangClimate::dump_config() { | ||||||
|   LOG_CLIMATE("", "Bang Bang Climate", this); |   LOG_CLIMATE("", "Bang Bang Climate", this); | ||||||
|   ESP_LOGCONFIG(TAG, "  Supports HEAT: %s", YESNO(this->supports_heat_)); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Supports COOL: %s", YESNO(this->supports_cool_)); |                 "  Supports HEAT: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Supports AWAY mode: %s", YESNO(this->supports_away_)); |                 "  Supports COOL: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Default Target Temperature Low: %.2f°C", this->normal_config_.default_temperature_low); |                 "  Supports AWAY mode: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Default Target Temperature High: %.2f°C", this->normal_config_.default_temperature_high); |                 "  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; | BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig() = default; | ||||||
|   | |||||||
| @@ -480,13 +480,19 @@ void BedJetHub::set_clock(uint8_t hour, uint8_t minute) { | |||||||
|  |  | ||||||
| /* Internal */ | /* 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::update() { this->dispatch_status_(); } | ||||||
|  |  | ||||||
| void BedJetHub::dump_config() { | void BedJetHub::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "BedJet Hub '%s'", this->get_name().c_str()); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  ble_client.app_id: %d", this->parent()->app_id); |                 "BedJet Hub '%s'\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  ble_client.conn_id: %d", this->parent()->get_conn_id()); |                 "  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) |   LOG_UPDATE_INTERVAL(this) | ||||||
|   ESP_LOGCONFIG(TAG, "  Child components (%d):", this->children_.size()); |   ESP_LOGCONFIG(TAG, "  Child components (%d):", this->children_.size()); | ||||||
|   for (auto *child : this->children_) { |   for (auto *child : this->children_) { | ||||||
| @@ -527,7 +533,7 @@ void BedJetHub::dispatch_status_() { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (this->timeout_ > 0 && diff > this->timeout_ && this->parent()->enabled) { |     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. |       // set_enabled(false) will only close the connection if state != IDLE. | ||||||
|       this->parent()->set_state(espbt::ClientState::CONNECTING); |       this->parent()->set_state(espbt::ClientState::CONNECTING); | ||||||
|       this->parent()->set_enabled(false); |       this->parent()->set_enabled(false); | ||||||
|   | |||||||
| @@ -83,7 +83,11 @@ void BedJetClimate::reset_state_() { | |||||||
|   this->publish_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) { | void BedJetClimate::control(const ClimateCall &call) { | ||||||
|   ESP_LOGD(TAG, "Received BedJetClimate::control"); |   ESP_LOGD(TAG, "Received BedJetClimate::control"); | ||||||
|   | |||||||
| @@ -7,11 +7,13 @@ | |||||||
|  |  | ||||||
| extern "C" { | extern "C" { | ||||||
| #include "rtos_pub.h" | #include "rtos_pub.h" | ||||||
| #include "spi.h" | // rtos_pub.h must be included before the rest of the includes | ||||||
|  |  | ||||||
| #include "arm_arch.h" | #include "arm_arch.h" | ||||||
| #include "general_dma_pub.h" | #include "general_dma_pub.h" | ||||||
| #include "gpio_pub.h" | #include "gpio_pub.h" | ||||||
| #include "icu_pub.h" | #include "icu_pub.h" | ||||||
|  | #include "spi.h" | ||||||
| #undef SPI_DAT | #undef SPI_DAT | ||||||
| #undef SPI_BASE | #undef SPI_BASE | ||||||
| }; | }; | ||||||
| @@ -124,7 +126,7 @@ void BekenSPILEDStripLightOutput::setup() { | |||||||
|   size_t buffer_size = this->get_buffer_size_(); |   size_t buffer_size = this->get_buffer_size_(); | ||||||
|   size_t dma_buffer_size = (buffer_size * 8) + (2 * 64); |   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); |   this->buf_ = allocator.allocate(buffer_size); | ||||||
|   if (this->buf_ == nullptr) { |   if (this->buf_ == nullptr) { | ||||||
|     ESP_LOGE(TAG, "Cannot allocate LED buffer!"); |     ESP_LOGE(TAG, "Cannot allocate LED buffer!"); | ||||||
| @@ -256,7 +258,7 @@ void BekenSPILEDStripLightOutput::write_state(light::LightState *state) { | |||||||
|   this->last_refresh_ = now; |   this->last_refresh_ = now; | ||||||
|   this->mark_shown_(); |   this->mark_shown_(); | ||||||
|  |  | ||||||
|   ESP_LOGVV(TAG, "Writing RGB values to bus..."); |   ESP_LOGVV(TAG, "Writing RGB values to bus"); | ||||||
|  |  | ||||||
|   if (spi_data == nullptr) { |   if (spi_data == nullptr) { | ||||||
|     ESP_LOGE(TAG, "SPI not initialized"); |     ESP_LOGE(TAG, "SPI not initialized"); | ||||||
| @@ -345,8 +347,10 @@ light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index | |||||||
| } | } | ||||||
|  |  | ||||||
| void BekenSPILEDStripLightOutput::dump_config() { | void BekenSPILEDStripLightOutput::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:"); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Pin: %u", this->pin_); |                 "Beken SPI LED Strip:\n" | ||||||
|  |                 "  Pin: %u", | ||||||
|  |                 this->pin_); | ||||||
|   const char *rgb_order; |   const char *rgb_order; | ||||||
|   switch (this->rgb_order_) { |   switch (this->rgb_order_) { | ||||||
|     case ORDER_RGB: |     case ORDER_RGB: | ||||||
| @@ -371,9 +375,11 @@ void BekenSPILEDStripLightOutput::dump_config() { | |||||||
|       rgb_order = "UNKNOWN"; |       rgb_order = "UNKNOWN"; | ||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|   ESP_LOGCONFIG(TAG, "  RGB Order: %s", rgb_order); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Max refresh rate: %" PRIu32, *this->max_refresh_rate_); |                 "  RGB Order: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Number of LEDs: %u", this->num_leds_); |                 "  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; } | 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) |   // turn on (after one-shot sensor automatically powers down) | ||||||
|   uint8_t turn_on = BH1750_COMMAND_POWER_ON; |   uint8_t turn_on = BH1750_COMMAND_POWER_ON; | ||||||
|   if (this->write(&turn_on, 1) != i2c::ERROR_OK) { |   if (this->write(&turn_on, 1) != i2c::ERROR_OK) { | ||||||
|     ESP_LOGW(TAG, "Turning on BH1750 failed"); |     ESP_LOGW(TAG, "Power on failed"); | ||||||
|     f(NAN); |     f(NAN); | ||||||
|     return; |     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_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111); | ||||||
|     uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111); |     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) { |     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; |       active_mtreg_ = 0; | ||||||
|       f(NAN); |       f(NAN); | ||||||
|       return; |       return; | ||||||
| @@ -88,7 +88,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function< | |||||||
|       return; |       return; | ||||||
|   } |   } | ||||||
|   if (this->write(&cmd, 1) != i2c::ERROR_OK) { |   if (this->write(&cmd, 1) != i2c::ERROR_OK) { | ||||||
|     ESP_LOGW(TAG, "Starting measurement for BH1750 failed"); |     ESP_LOGW(TAG, "Start measurement failed"); | ||||||
|     f(NAN); |     f(NAN); | ||||||
|     return; |     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]() { |   this->set_timeout("read", meas_time, [this, mode, mtreg, f]() { | ||||||
|     uint16_t raw_value; |     uint16_t raw_value; | ||||||
|     if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) { |     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); |       f(NAN); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| @@ -156,7 +156,7 @@ void BH1750Sensor::update() { | |||||||
|         this->publish_state(NAN); |         this->publish_state(NAN); | ||||||
|         return; |         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->status_clear_warning(); | ||||||
|       this->publish_state(val); |       this->publish_state(val); | ||||||
|     }); |     }); | ||||||
|   | |||||||
| @@ -1,7 +1,10 @@ | |||||||
|  | from logging import getLogger | ||||||
|  |  | ||||||
| from esphome import automation, core | from esphome import automation, core | ||||||
| from esphome.automation import Condition, maybe_simple_id | from esphome.automation import Condition, maybe_simple_id | ||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| from esphome.components import mqtt, web_server | from esphome.components import mqtt, web_server | ||||||
|  | from esphome.components.const import CONF_ON_STATE_CHANGE | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome.const import ( | from esphome.const import ( | ||||||
|     CONF_DELAY, |     CONF_DELAY, | ||||||
| @@ -98,6 +101,7 @@ IS_PLATFORM_COMPONENT = True | |||||||
|  |  | ||||||
| CONF_TIME_OFF = "time_off" | CONF_TIME_OFF = "time_off" | ||||||
| CONF_TIME_ON = "time_on" | CONF_TIME_ON = "time_on" | ||||||
|  | CONF_TRIGGER_ON_INITIAL_STATE = "trigger_on_initial_state" | ||||||
|  |  | ||||||
| DEFAULT_DELAY = "1s" | DEFAULT_DELAY = "1s" | ||||||
| DEFAULT_TIME_OFF = "100ms" | DEFAULT_TIME_OFF = "100ms" | ||||||
| @@ -127,9 +131,17 @@ MultiClickTriggerEvent = binary_sensor_ns.struct("MultiClickTriggerEvent") | |||||||
| StateTrigger = binary_sensor_ns.class_( | StateTrigger = binary_sensor_ns.class_( | ||||||
|     "StateTrigger", automation.Trigger.template(bool) |     "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 = binary_sensor_ns.class_( | ||||||
|     "BinarySensorPublishAction", automation.Action |     "BinarySensorPublishAction", automation.Action | ||||||
| ) | ) | ||||||
|  | BinarySensorInvalidateAction = binary_sensor_ns.class_( | ||||||
|  |     "BinarySensorInvalidateAction", automation.Action | ||||||
|  | ) | ||||||
|  |  | ||||||
| # Condition | # Condition | ||||||
| BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", 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) | LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter) | ||||||
| SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component) | SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component) | ||||||
|  |  | ||||||
|  | _LOGGER = getLogger(__name__) | ||||||
|  |  | ||||||
| FILTER_REGISTRY = Registry() | FILTER_REGISTRY = Registry() | ||||||
| validate_filters = cv.validate_registry("filter", FILTER_REGISTRY) | validate_filters = cv.validate_registry("filter", FILTER_REGISTRY) | ||||||
|  |  | ||||||
| @@ -386,6 +400,14 @@ def validate_click_timing(value): | |||||||
|     return 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 = ( | _BINARY_SENSOR_SCHEMA = ( | ||||||
|     cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) |     cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) | ||||||
|     .extend(cv.MQTT_COMPONENT_SCHEMA) |     .extend(cv.MQTT_COMPONENT_SCHEMA) | ||||||
| @@ -395,7 +417,12 @@ _BINARY_SENSOR_SCHEMA = ( | |||||||
|             cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id( |             cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id( | ||||||
|                 mqtt.MQTTBinarySensorComponent |                 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_DEVICE_CLASS): validate_device_class, | ||||||
|             cv.Optional(CONF_FILTERS): validate_filters, |             cv.Optional(CONF_FILTERS): validate_filters, | ||||||
|             cv.Optional(CONF_ON_PRESS): automation.validate_automation( |             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.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: |     if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: | ||||||
|         cg.add(var.set_device_class(device_class)) |         cg.add(var.set_device_class(device_class)) | ||||||
|     if publish_initial_state := config.get(CONF_PUBLISH_INITIAL_STATE): |     trigger = config.get(CONF_TRIGGER_ON_INITIAL_STATE, False) or config.get( | ||||||
|         cg.add(var.set_publish_initial_state(publish_initial_state)) |         CONF_PUBLISH_INITIAL_STATE, False | ||||||
|  |     ) | ||||||
|  |     cg.add(var.set_trigger_on_initial_state(trigger)) | ||||||
|     if inverted := config.get(CONF_INVERTED): |     if inverted := config.get(CONF_INVERTED): | ||||||
|         cg.add(var.set_inverted(inverted)) |         cg.add(var.set_inverted(inverted)) | ||||||
|     if filters_config := config.get(CONF_FILTERS): |     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) |         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||||
|         await automation.build_automation(trigger, [(bool, "x")], conf) |         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): |     if mqtt_id := config.get(CONF_MQTT_ID): | ||||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) |         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||||
|         await mqtt.register_mqtt_component(mqtt_, config) |         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]): |     if not CORE.has_id(config[CONF_ID]): | ||||||
|         var = cg.Pvariable(config[CONF_ID], var) |         var = cg.Pvariable(config[CONF_ID], var) | ||||||
|     cg.add(cg.App.register_binary_sensor(var)) |     cg.add(cg.App.register_binary_sensor(var)) | ||||||
|  |     CORE.register_platform_component("binary_sensor", var) | ||||||
|     await setup_binary_sensor_core_(var, config) |     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): | async def to_code(config): | ||||||
|     cg.add_define("USE_BINARY_SENSOR") |     cg.add_define("USE_BINARY_SENSOR") | ||||||
|     cg.add_global(binary_sensor_ns.using) |     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; |   *this->at_index_ = *this->at_index_ + 1; | ||||||
| } | } | ||||||
| void binary_sensor::MultiClickTrigger::schedule_cooldown_() { | void binary_sensor::MultiClickTrigger::schedule_cooldown_() { | ||||||
|   ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms...", |   ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms", this->invalid_cooldown_); | ||||||
|            this->invalid_cooldown_); |  | ||||||
|   this->is_in_cooldown_ = true; |   this->is_in_cooldown_ = true; | ||||||
|   this->set_timeout("cooldown", this->invalid_cooldown_, [this]() { |   this->set_timeout("cooldown", this->invalid_cooldown_, [this]() { | ||||||
|     ESP_LOGV(TAG, "Multi Click: Cooldown ended, matching is now enabled again."); |     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)) {} |       : parent_(parent), timing_(std::move(timing)) {} | ||||||
|  |  | ||||||
|   void setup() override { |   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); |     auto f = std::bind(&MultiClickTrigger::on_state_, this, std::placeholders::_1); | ||||||
|     this->parent_->add_on_state_callback(f); |     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...> { | template<typename... Ts> class BinarySensorCondition : public Condition<Ts...> { | ||||||
|  public: |  public: | ||||||
|   BinarySensorCondition(BinarySensor *parent, bool state) : parent_(parent), state_(state) {} |   BinarySensorCondition(BinarySensor *parent, bool state) : parent_(parent), state_(state) {} | ||||||
| @@ -154,5 +162,15 @@ template<typename... Ts> class BinarySensorPublishAction : public Action<Ts...> | |||||||
|   BinarySensor *sensor_; |   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 binary_sensor | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -7,42 +7,25 @@ namespace binary_sensor { | |||||||
|  |  | ||||||
| static const char *const TAG = "binary_sensor"; | static const char *const TAG = "binary_sensor"; | ||||||
|  |  | ||||||
| void BinarySensor::add_on_state_callback(std::function<void(bool)> &&callback) { | void BinarySensor::publish_state(bool new_state) { | ||||||
|   this->state_callback_.add(std::move(callback)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void BinarySensor::publish_state(bool state) { |  | ||||||
|   if (!this->publish_dedup_.next(state)) |  | ||||||
|     return; |  | ||||||
|   if (this->filter_list_ == nullptr) { |   if (this->filter_list_ == nullptr) { | ||||||
|     this->send_state_internal(state, false); |     this->send_state_internal(new_state); | ||||||
|   } else { |   } else { | ||||||
|     this->filter_list_->input(state, false); |     this->filter_list_->input(new_state); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| void BinarySensor::publish_initial_state(bool state) { | void BinarySensor::publish_initial_state(bool new_state) { | ||||||
|   if (!this->publish_dedup_.next(state)) |   this->invalidate_state(); | ||||||
|     return; |   this->publish_state(new_state); | ||||||
|   if (this->filter_list_ == nullptr) { | } | ||||||
|     this->send_state_internal(state, true); | void BinarySensor::send_state_internal(bool new_state) { | ||||||
|   } else { |   // copy the new state to the visible property for backwards compatibility, before any callbacks | ||||||
|     this->filter_list_->input(state, true); |   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) { | void BinarySensor::add_filter(Filter *filter) { | ||||||
|   filter->parent_ = this; |   filter->parent_ = this; | ||||||
| @@ -60,7 +43,6 @@ void BinarySensor::add_filters(const std::vector<Filter *> &filters) { | |||||||
|     this->add_filter(filter); |     this->add_filter(filter); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| bool BinarySensor::has_state() const { return this->has_state_; } |  | ||||||
| bool BinarySensor::is_status_binary_sensor() const { return false; } | bool BinarySensor::is_status_binary_sensor() const { return false; } | ||||||
|  |  | ||||||
| }  // namespace binary_sensor | }  // namespace binary_sensor | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" |  | ||||||
| #include "esphome/core/entity_base.h" | #include "esphome/core/entity_base.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
| #include "esphome/components/binary_sensor/filter.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 |  * The sub classes should notify the front-end of new states via the publish_state() method which | ||||||
|  * handles inverted inputs for you. |  * handles inverted inputs for you. | ||||||
|  */ |  */ | ||||||
| class BinarySensor : public EntityBase, public EntityBase_DeviceClass { | class BinarySensor : public StatefulEntityBase<bool>, public EntityBase_DeviceClass { | ||||||
|  public: |  public: | ||||||
|   explicit BinarySensor(); |   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); |  | ||||||
|  |  | ||||||
|   /** Publish a new state to the front-end. |   /** 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 |   /** Publish the initial state, this will not make the callback manager send callbacks | ||||||
|    * and is meant only for the initial state on boot. |    * 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); |   void publish_initial_state(bool new_state); | ||||||
|  |  | ||||||
|   /// The current reported state of the binary sensor. |  | ||||||
|   bool state{false}; |  | ||||||
|  |  | ||||||
|   void add_filter(Filter *filter); |   void add_filter(Filter *filter); | ||||||
|   void add_filters(const std::vector<Filter *> &filters); |   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 ========== |   // ========== INTERNAL METHODS ========== | ||||||
|   // (In most use cases you won't need these) |   // (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. |   /// Return whether this binary sensor has outputted a state. | ||||||
|   virtual bool has_state() const; |  | ||||||
|  |  | ||||||
|   virtual bool is_status_binary_sensor() const; |   virtual bool is_status_binary_sensor() const; | ||||||
|  |  | ||||||
|  |   // For backward compatibility, provide an accessible property | ||||||
|  |  | ||||||
|  |   bool state{}; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   CallbackManager<void(bool)> state_callback_{}; |  | ||||||
|   Filter *filter_list_{nullptr}; |   Filter *filter_list_{nullptr}; | ||||||
|   bool has_state_{false}; |  | ||||||
|   bool publish_initial_state_{false}; |  | ||||||
|   Deduplicator<bool> publish_dedup_; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class BinarySensorInitiallyOff : public BinarySensor { | class BinarySensorInitiallyOff : public BinarySensor { | ||||||
|   | |||||||
| @@ -9,37 +9,36 @@ namespace binary_sensor { | |||||||
|  |  | ||||||
| static const char *const TAG = "sensor.filter"; | 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)) |   if (!this->dedup_.next(value)) | ||||||
|     return; |     return; | ||||||
|  |   auto b = this->new_value(value); | ||||||
|   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); |  | ||||||
|   if (b.has_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) { |   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 { |   } 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 {}; |   return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
| float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; } | 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) { |   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 {}; |     return {}; | ||||||
|   } else { |   } else { | ||||||
|     this->cancel_timeout("ON"); |     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; } | 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) { |   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 {}; |     return {}; | ||||||
|   } else { |   } else { | ||||||
|     this->cancel_timeout("OFF"); |     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; } | 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)) {} | 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) { |   if (value) { | ||||||
|     // Ignore if already running |     // Ignore if already running | ||||||
|     if (this->active_timing_ != 0) |     if (this->active_timing_ != 0) | ||||||
| @@ -101,7 +100,7 @@ void AutorepeatFilter::next_timing_() { | |||||||
|  |  | ||||||
| void AutorepeatFilter::next_value_(bool val) { | void AutorepeatFilter::next_value_(bool val) { | ||||||
|   const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2]; |   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); }); |   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)) {} | 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_) { |   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->steady_ = true; | ||||||
|       this->output(value, is_initial); |       this->output(value); | ||||||
|     }); |     }); | ||||||
|     return {}; |     return {}; | ||||||
|   } else { |   } else { | ||||||
|     this->steady_ = false; |     this->steady_ = false; | ||||||
|     this->output(value, is_initial); |     this->output(value); | ||||||
|     this->set_timeout("SETTLE", this->delay_.value(), [this]() { this->steady_ = true; }); |     this->set_timeout("SETTLE", this->delay_.value(), [this]() { this->steady_ = true; }); | ||||||
|     return value; |     return value; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -14,11 +14,11 @@ class BinarySensor; | |||||||
|  |  | ||||||
| class Filter { | class Filter { | ||||||
|  public: |  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: |  protected: | ||||||
|   friend BinarySensor; |   friend BinarySensor; | ||||||
| @@ -30,7 +30,7 @@ class Filter { | |||||||
|  |  | ||||||
| class DelayedOnOffFilter : public Filter, public Component { | class DelayedOnOffFilter : public Filter, public Component { | ||||||
|  public: |  public: | ||||||
|   optional<bool> new_value(bool value, bool is_initial) override; |   optional<bool> new_value(bool value) override; | ||||||
|  |  | ||||||
|   float get_setup_priority() const override; |   float get_setup_priority() const override; | ||||||
|  |  | ||||||
| @@ -44,7 +44,7 @@ class DelayedOnOffFilter : public Filter, public Component { | |||||||
|  |  | ||||||
| class DelayedOnFilter : public Filter, public Component { | class DelayedOnFilter : public Filter, public Component { | ||||||
|  public: |  public: | ||||||
|   optional<bool> new_value(bool value, bool is_initial) override; |   optional<bool> new_value(bool value) override; | ||||||
|  |  | ||||||
|   float get_setup_priority() const override; |   float get_setup_priority() const override; | ||||||
|  |  | ||||||
| @@ -56,7 +56,7 @@ class DelayedOnFilter : public Filter, public Component { | |||||||
|  |  | ||||||
| class DelayedOffFilter : public Filter, public Component { | class DelayedOffFilter : public Filter, public Component { | ||||||
|  public: |  public: | ||||||
|   optional<bool> new_value(bool value, bool is_initial) override; |   optional<bool> new_value(bool value) override; | ||||||
|  |  | ||||||
|   float get_setup_priority() const override; |   float get_setup_priority() const override; | ||||||
|  |  | ||||||
| @@ -68,7 +68,7 @@ class DelayedOffFilter : public Filter, public Component { | |||||||
|  |  | ||||||
| class InvertFilter : public Filter { | class InvertFilter : public Filter { | ||||||
|  public: |  public: | ||||||
|   optional<bool> new_value(bool value, bool is_initial) override; |   optional<bool> new_value(bool value) override; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct AutorepeatFilterTiming { | struct AutorepeatFilterTiming { | ||||||
| @@ -86,7 +86,7 @@ class AutorepeatFilter : public Filter, public Component { | |||||||
|  public: |  public: | ||||||
|   explicit AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings); |   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; |   float get_setup_priority() const override; | ||||||
|  |  | ||||||
| @@ -102,7 +102,7 @@ class LambdaFilter : public Filter { | |||||||
|  public: |  public: | ||||||
|   explicit LambdaFilter(std::function<optional<bool>(bool)> f); |   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: |  protected: | ||||||
|   std::function<optional<bool>(bool)> f_; |   std::function<optional<bool>(bool)> f_; | ||||||
| @@ -110,7 +110,7 @@ class LambdaFilter : public Filter { | |||||||
|  |  | ||||||
| class SettleFilter : public Filter, public Component { | class SettleFilter : public Filter, public Component { | ||||||
|  public: |  public: | ||||||
|   optional<bool> new_value(bool value, bool is_initial) override; |   optional<bool> new_value(bool value) override; | ||||||
|  |  | ||||||
|   float get_setup_priority() const 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++) { |   for (int i = 0; i < this->action_queue_.size(); i++) { | ||||||
|     ptr_func = this->action_queue_[i]; |     ptr_func = this->action_queue_[i]; | ||||||
|     if (ptr_func) { |     if (ptr_func) { | ||||||
|       ESP_LOGI(TAG, "HandleActionCallback[%d]...", i); |       ESP_LOGI(TAG, "HandleActionCallback[%d]", i); | ||||||
|       (this->*ptr_func)(); |       (this->*ptr_func)(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -196,14 +196,17 @@ void BL0942::received_package_(DataPacket *data) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void BL0942::dump_config() {  // NOLINT(readability-function-cognitive-complexity) | void BL0942::dump_config() {  // NOLINT(readability-function-cognitive-complexity) | ||||||
|   ESP_LOGCONFIG(TAG, "BL0942:"); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Reset: %s", TRUEFALSE(this->reset_)); |                 "BL0942:\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Address: %d", this->address_); |                 "  Reset: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Nominal line frequency: %d Hz", this->line_freq_); |                 "  Address: %d\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Current reference: %f", this->current_reference_); |                 "  Nominal line frequency: %d Hz\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Energy reference: %f", this->energy_reference_); |                 "  Current reference: %f\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Power reference: %f", this->power_reference_); |                 "  Energy reference: %f\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Voltage reference: %f", this->voltage_reference_); |                 "  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("", "Voltage", this->voltage_sensor_); | ||||||
|   LOG_SENSOR("", "Current", this->current_sensor_); |   LOG_SENSOR("", "Current", this->current_sensor_); | ||||||
|   LOG_SENSOR("", "Power", this->power_sensor_); |   LOG_SENSOR("", "Power", this->power_sensor_); | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| from esphome import automation | from esphome import automation | ||||||
| from esphome.automation import maybe_simple_id | from esphome.automation import maybe_simple_id | ||||||
| import esphome.codegen as cg | 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 | import esphome.config_validation as cv | ||||||
| from esphome.const import ( | from esphome.const import ( | ||||||
|     CONF_CHARACTERISTIC_UUID, |     CONF_CHARACTERISTIC_UUID, | ||||||
| @@ -287,6 +288,9 @@ async def remove_bond_to_code(config, action_id, template_arg, args): | |||||||
|  |  | ||||||
|  |  | ||||||
| async def to_code(config): | 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]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     await cg.register_component(var, config) |     await cg.register_component(var, config) | ||||||
|     await esp32_ble_tracker.register_client(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() { | void BLEBinaryOutput::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "BLE Binary Output:"); |   ESP_LOGCONFIG(TAG, "BLE Binary Output:"); | ||||||
|   ESP_LOGCONFIG(TAG, "  MAC address        : %s", this->parent_->address_str().c_str()); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Service UUID       : %s", this->service_uuid_.to_string().c_str()); |                 "  MAC address        : %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Characteristic UUID: %s", this->char_uuid_.to_string().c_str()); |                 "  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); |   LOG_BINARY_OUTPUT(this); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,11 @@ namespace ble_client { | |||||||
|  |  | ||||||
| static const char *const TAG = "ble_rssi_sensor"; | 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() { | void BLEClientRSSISensor::dump_config() { | ||||||
|   LOG_SENSOR("", "BLE Client RSSI Sensor", this); |   LOG_SENSOR("", "BLE Client RSSI Sensor", this); | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| #include "ble_sensor.h" | #include "ble_sensor.h" | ||||||
| #include "esphome/core/log.h" |  | ||||||
| #include "esphome/core/application.h" | #include "esphome/core/application.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
| #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | ||||||
|  |  | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
| @@ -11,15 +11,22 @@ namespace ble_client { | |||||||
|  |  | ||||||
| static const char *const TAG = "ble_sensor"; | 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() { | void BLESensor::dump_config() { | ||||||
|   LOG_SENSOR("", "BLE Sensor", this); |   LOG_SENSOR("", "BLE Sensor", this); | ||||||
|   ESP_LOGCONFIG(TAG, "  MAC address        : %s", this->parent()->address_str().c_str()); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Service UUID       : %s", this->service_uuid_.to_string().c_str()); |                 "  MAC address        : %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Characteristic UUID: %s", this->char_uuid_.to_string().c_str()); |                 "  Service UUID       : %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Descriptor UUID    : %s", this->descr_uuid_.to_string().c_str()); |                 "  Characteristic UUID: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Notifications      : %s", YESNO(this->notify_)); |                 "  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); |   LOG_UPDATE_INTERVAL(this); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,15 +14,22 @@ static const char *const TAG = "ble_text_sensor"; | |||||||
|  |  | ||||||
| static const std::string EMPTY = ""; | 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() { | void BLETextSensor::dump_config() { | ||||||
|   LOG_TEXT_SENSOR("", "BLE Text Sensor", this); |   LOG_TEXT_SENSOR("", "BLE Text Sensor", this); | ||||||
|   ESP_LOGCONFIG(TAG, "  MAC address        : %s", this->parent()->address_str().c_str()); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Service UUID       : %s", this->service_uuid_.to_string().c_str()); |                 "  MAC address        : %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Characteristic UUID: %s", this->char_uuid_.to_string().c_str()); |                 "  Service UUID       : %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Descriptor UUID    : %s", this->descr_uuid_.to_string().c_str()); |                 "  Characteristic UUID: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Notifications      : %s", YESNO(this->notify_)); |                 "  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); |   LOG_UPDATE_INTERVAL(this); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import esphome.codegen as cg | 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 import add_idf_sdkconfig_option | ||||||
|  | from esphome.components.esp32_ble import BTLoggers | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome.const import CONF_ACTIVE, CONF_ID | from esphome.const import CONF_ACTIVE, CONF_ID | ||||||
|  |  | ||||||
| @@ -77,6 +78,9 @@ CONFIG_SCHEMA = cv.All( | |||||||
|  |  | ||||||
|  |  | ||||||
| async def to_code(config): | 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]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     await cg.register_component(var, config) |     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); |       resp.data.reserve(param->read.value_len); | ||||||
|       // Use bulk insert instead of individual push_backs |       // Use bulk insert instead of individual push_backs | ||||||
|       resp.data.insert(resp.data.end(), param->read.value, param->read.value + param->read.value_len); |       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; |       break; | ||||||
|     } |     } | ||||||
|     case ESP_GATTC_WRITE_CHAR_EVT: |     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; |       api::BluetoothGATTWriteResponse resp; | ||||||
|       resp.address = this->address_; |       resp.address = this->address_; | ||||||
|       resp.handle = param->write.handle; |       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; |       break; | ||||||
|     } |     } | ||||||
|     case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { |     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; |       api::BluetoothGATTNotifyResponse resp; | ||||||
|       resp.address = this->address_; |       resp.address = this->address_; | ||||||
|       resp.handle = param->unreg_for_notify.handle; |       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; |       break; | ||||||
|     } |     } | ||||||
|     case ESP_GATTC_REG_FOR_NOTIFY_EVT: { |     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; |       api::BluetoothGATTNotifyResponse resp; | ||||||
|       resp.address = this->address_; |       resp.address = this->address_; | ||||||
|       resp.handle = param->reg_for_notify.handle; |       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; |       break; | ||||||
|     } |     } | ||||||
|     case ESP_GATTC_NOTIFY_EVT: { |     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); |       resp.data.reserve(param->notify.value_len); | ||||||
|       // Use bulk insert instead of individual push_backs |       // Use bulk insert instead of individual push_backs | ||||||
|       resp.data.insert(resp.data.end(), param->notify.value, param->notify.value + param->notify.value_len); |       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; |       break; | ||||||
|     } |     } | ||||||
|     default: |     default: | ||||||
|   | |||||||
| @@ -26,10 +26,17 @@ class BluetoothConnection : public esp32_ble_client::BLEClientBase { | |||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   friend class BluetoothProxy; |   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_; |   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 | }  // 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.state = static_cast<api::enums::BluetoothScannerState>(state); | ||||||
|   resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE |   resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE | ||||||
|                                                : api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE; |                                                : 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) { | 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; |   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_) |   if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_) | ||||||
|     return false; |     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 |   // Add new advertisements to the batch buffer | ||||||
|   for (size_t i = 0; i < count; i++) { |   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; |     uint8_t length = result.adv_data_len + result.scan_rsp_len; | ||||||
|  |  | ||||||
|     batch_buffer.emplace_back(); |     batch_buffer.emplace_back(); | ||||||
| @@ -103,7 +103,7 @@ void BluetoothProxy::flush_pending_advertisements() { | |||||||
|  |  | ||||||
|   api::BluetoothLERawAdvertisementsResponse resp; |   api::BluetoothLERawAdvertisementsResponse resp; | ||||||
|   resp.advertisements.swap(batch_buffer); |   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) { | 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()); |     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() { | void BluetoothProxy::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); |   ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); | ||||||
|   ESP_LOGCONFIG(TAG, "  Active: %s", YESNO(this->active_)); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Connections: %d", this->connections_.size()); |                 "  Active: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Raw advertisements: %s", YESNO(this->raw_advertisements_)); |                 "  Connections: %d\n" | ||||||
|  |                 "  Raw advertisements: %s", | ||||||
|  |                 YESNO(this->active_), this->connections_.size(), YESNO(this->raw_advertisements_)); | ||||||
| } | } | ||||||
|  |  | ||||||
| int BluetoothProxy::get_bluetooth_connections_free() { | int BluetoothProxy::get_bluetooth_connections_free() { | ||||||
| @@ -300,7 +302,7 @@ void BluetoothProxy::loop() { | |||||||
|         service_resp.characteristics.push_back(std::move(characteristic_resp)); |         service_resp.characteristics.push_back(std::move(characteristic_resp)); | ||||||
|       } |       } | ||||||
|       resp.services.push_back(std::move(service_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.success = ret == ESP_OK; | ||||||
|       call.error = ret; |       call.error = ret; | ||||||
|  |  | ||||||
|       this->api_connection_->send_bluetooth_device_clear_cache_response(call); |       this->api_connection_->send_message(call); | ||||||
|  |  | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
| @@ -577,7 +579,7 @@ void BluetoothProxy::send_device_connection(uint64_t address, bool connected, ui | |||||||
|   call.connected = connected; |   call.connected = connected; | ||||||
|   call.mtu = mtu; |   call.mtu = mtu; | ||||||
|   call.error = error; |   call.error = error; | ||||||
|   this->api_connection_->send_bluetooth_device_connection_response(call); |   this->api_connection_->send_message(call); | ||||||
| } | } | ||||||
| void BluetoothProxy::send_connections_free() { | void BluetoothProxy::send_connections_free() { | ||||||
|   if (this->api_connection_ == nullptr) |   if (this->api_connection_ == nullptr) | ||||||
| @@ -590,7 +592,7 @@ void BluetoothProxy::send_connections_free() { | |||||||
|       call.allocated.push_back(connection->address_); |       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) { | void BluetoothProxy::send_gatt_services_done(uint64_t address) { | ||||||
| @@ -598,7 +600,7 @@ void BluetoothProxy::send_gatt_services_done(uint64_t address) { | |||||||
|     return; |     return; | ||||||
|   api::BluetoothGATTGetServicesDoneResponse call; |   api::BluetoothGATTGetServicesDoneResponse call; | ||||||
|   call.address = address; |   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) { | 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.address = address; | ||||||
|   call.handle = handle; |   call.handle = handle; | ||||||
|   call.error = error; |   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) { | 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.paired = paired; | ||||||
|   call.error = error; |   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) { | 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.success = success; | ||||||
|   call.error = error; |   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) { | void BluetoothProxy::bluetooth_scanner_set_mode(bool active) { | ||||||
|   | |||||||
| @@ -52,7 +52,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com | |||||||
|  public: |  public: | ||||||
|   BluetoothProxy(); |   BluetoothProxy(); | ||||||
|   bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; |   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 dump_config() override; | ||||||
|   void setup() override; |   void setup() override; | ||||||
|   void loop() 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); |   BluetoothConnection *get_connection_(uint64_t address, bool reserve); | ||||||
|  |  | ||||||
|   bool active_; |   // Memory optimized layout for 32-bit systems | ||||||
|  |   // Group 1: Pointers (4 bytes each, naturally aligned) | ||||||
|   std::vector<BluetoothConnection *> connections_{}; |  | ||||||
|   api::APIConnection *api_connection_{nullptr}; |   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}; |   bool raw_advertisements_{false}; | ||||||
|  |   // 2 bytes used, 2 bytes padding | ||||||
| }; | }; | ||||||
|  |  | ||||||
| extern BluetoothProxy *global_bluetooth_proxy;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | 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 |   // 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. |   // 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) { |   if (this->is_failed()) { | ||||||
|     this->component_state_ &= ~COMPONENT_STATE_MASK; |     this->reset_to_construction_state(); | ||||||
|     this->component_state_ |= COMPONENT_STATE_CONSTRUCTION; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) { |   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() { | void BME280Component::update() { | ||||||
|   // Enable sensor |   // Enable sensor | ||||||
|   ESP_LOGV(TAG, "Sending conversion request..."); |   ESP_LOGV(TAG, "Sending conversion request"); | ||||||
|   uint8_t meas_value = 0; |   uint8_t meas_value = 0; | ||||||
|   meas_value |= (this->temperature_oversampling_ & 0b111) << 5; |   meas_value |= (this->temperature_oversampling_ & 0b111) << 5; | ||||||
|   meas_value |= (this->pressure_oversampling_ & 0b111) << 2; |   meas_value |= (this->pressure_oversampling_ & 0b111) << 2; | ||||||
|   | |||||||
| @@ -12,8 +12,8 @@ from esphome.const import ( | |||||||
|     CONF_OVERSAMPLING, |     CONF_OVERSAMPLING, | ||||||
|     CONF_PRESSURE, |     CONF_PRESSURE, | ||||||
|     CONF_TEMPERATURE, |     CONF_TEMPERATURE, | ||||||
|  |     DEVICE_CLASS_ATMOSPHERIC_PRESSURE, | ||||||
|     DEVICE_CLASS_HUMIDITY, |     DEVICE_CLASS_HUMIDITY, | ||||||
|     DEVICE_CLASS_PRESSURE, |  | ||||||
|     DEVICE_CLASS_TEMPERATURE, |     DEVICE_CLASS_TEMPERATURE, | ||||||
|     ICON_GAS_CYLINDER, |     ICON_GAS_CYLINDER, | ||||||
|     STATE_CLASS_MEASUREMENT, |     STATE_CLASS_MEASUREMENT, | ||||||
| @@ -71,7 +71,7 @@ CONFIG_SCHEMA = ( | |||||||
|             cv.Optional(CONF_PRESSURE): sensor.sensor_schema( |             cv.Optional(CONF_PRESSURE): sensor.sensor_schema( | ||||||
|                 unit_of_measurement=UNIT_HECTOPASCAL, |                 unit_of_measurement=UNIT_HECTOPASCAL, | ||||||
|                 accuracy_decimals=1, |                 accuracy_decimals=1, | ||||||
|                 device_class=DEVICE_CLASS_PRESSURE, |                 device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE, | ||||||
|                 state_class=STATE_CLASS_MEASUREMENT, |                 state_class=STATE_CLASS_MEASUREMENT, | ||||||
|             ).extend( |             ).extend( | ||||||
|                 { |                 { | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| #include "bme680_bsec.h" | #include "bme680_bsec.h" | ||||||
| #include "esphome/core/log.h" |  | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| @@ -159,11 +159,15 @@ void BME680BSECComponent::dump_config() { | |||||||
|              this->bme680_status_); |              this->bme680_status_); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGCONFIG(TAG, "  Temperature Offset: %.2f", this->temperature_offset_); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile"); |                 "  Temperature Offset: %.2f\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Supply Voltage: %sV", this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8"); |                 "  IAQ Mode: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_)); |                 "  Supply Voltage: %sV\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  State Save Interval: %ims", this->state_save_interval_ms_); |                 "  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_); |   LOG_SENSOR("  ", "Temperature", this->temperature_sensor_); | ||||||
|   ESP_LOGCONFIG(TAG, "    Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->temperature_sample_rate_)); |   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, |     DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, | ||||||
|     ICON_GAS_CYLINDER, |     ICON_GAS_CYLINDER, | ||||||
|     ICON_GAUGE, |     ICON_GAUGE, | ||||||
|  |     ICON_THERMOMETER, | ||||||
|  |     ICON_WATER_PERCENT, | ||||||
|     STATE_CLASS_MEASUREMENT, |     STATE_CLASS_MEASUREMENT, | ||||||
|     UNIT_CELSIUS, |     UNIT_CELSIUS, | ||||||
|     UNIT_HECTOPASCAL, |     UNIT_HECTOPASCAL, | ||||||
| @@ -27,11 +29,11 @@ from . import CONF_BME680_BSEC_ID, SAMPLE_RATE_OPTIONS, BME680BSECComponent | |||||||
|  |  | ||||||
| DEPENDENCIES = ["bme680_bsec"] | DEPENDENCIES = ["bme680_bsec"] | ||||||
|  |  | ||||||
| CONF_IAQ = "iaq" |  | ||||||
| CONF_CO2_EQUIVALENT = "co2_equivalent" |  | ||||||
| CONF_BREATH_VOC_EQUIVALENT = "breath_voc_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" | ICON_ACCURACY = "mdi:checkbox-marked-circle-outline" | ||||||
|  | UNIT_IAQ = "IAQ" | ||||||
|  |  | ||||||
| TYPES = [ | TYPES = [ | ||||||
|     CONF_TEMPERATURE, |     CONF_TEMPERATURE, | ||||||
| @@ -49,6 +51,7 @@ CONFIG_SCHEMA = cv.Schema( | |||||||
|         cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent), |         cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent), | ||||||
|         cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( |         cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( | ||||||
|             unit_of_measurement=UNIT_CELSIUS, |             unit_of_measurement=UNIT_CELSIUS, | ||||||
|  |             icon=ICON_THERMOMETER, | ||||||
|             accuracy_decimals=1, |             accuracy_decimals=1, | ||||||
|             device_class=DEVICE_CLASS_TEMPERATURE, |             device_class=DEVICE_CLASS_TEMPERATURE, | ||||||
|             state_class=STATE_CLASS_MEASUREMENT, |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
| @@ -65,6 +68,7 @@ CONFIG_SCHEMA = cv.Schema( | |||||||
|         ), |         ), | ||||||
|         cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( |         cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( | ||||||
|             unit_of_measurement=UNIT_PERCENT, |             unit_of_measurement=UNIT_PERCENT, | ||||||
|  |             icon=ICON_WATER_PERCENT, | ||||||
|             accuracy_decimals=1, |             accuracy_decimals=1, | ||||||
|             device_class=DEVICE_CLASS_HUMIDITY, |             device_class=DEVICE_CLASS_HUMIDITY, | ||||||
|             state_class=STATE_CLASS_MEASUREMENT, |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|   | |||||||
| @@ -58,13 +58,13 @@ void BME68xBSEC2Component::setup() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void BME68xBSEC2Component::dump_config() { | void BME68xBSEC2Component::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "BME68X via BSEC2:"); |   ESP_LOGCONFIG(TAG, | ||||||
|  |                 "BME68X via BSEC2:\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  BSEC2 version: %d.%d.%d.%d", this->version_.major, this->version_.minor, |                 "  BSEC2 version: %d.%d.%d.%d\n" | ||||||
|                 this->version_.major_bugfix, this->version_.minor_bugfix); |                 "  BSEC2 configuration blob:\n" | ||||||
|  |                 "    Configured: %s", | ||||||
|   ESP_LOGCONFIG(TAG, "  BSEC2 configuration blob:"); |                 this->version_.major, this->version_.minor, this->version_.major_bugfix, this->version_.minor_bugfix, | ||||||
|   ESP_LOGCONFIG(TAG, "    Configured: %s", YESNO(this->bsec2_blob_configured_)); |                 YESNO(this->bsec2_blob_configured_)); | ||||||
|   if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) { |   if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) { | ||||||
|     ESP_LOGCONFIG(TAG, "    Size: %" PRIu32, 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) { |   if (this->algorithm_output_ != ALGORITHM_OUTPUT_IAQ) { | ||||||
|     ESP_LOGCONFIG(TAG, "  Algorithm output: %s", BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(this->algorithm_output_)); |     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, | ||||||
|   ESP_LOGCONFIG(TAG, "  Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_)); |                 "  Operating age: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Voltage: %s", BME68X_BSEC2_VOLTAGE_LOG(this->voltage_)); |                 "  Sample rate: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  State save interval: %ims", this->state_save_interval_ms_); |                 "  Voltage: %s\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Temperature offset: %.2f", this->temperature_offset_); |                 "  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 | #ifdef USE_SENSOR | ||||||
|   LOG_SENSOR("  ", "Temperature", this->temperature_sensor_); |   LOG_SENSOR("  ", "Temperature", this->temperature_sensor_); | ||||||
|   | |||||||
| @@ -9,8 +9,10 @@ from esphome.const import ( | |||||||
|     CONF_SAMPLE_RATE, |     CONF_SAMPLE_RATE, | ||||||
|     CONF_TEMPERATURE, |     CONF_TEMPERATURE, | ||||||
|     DEVICE_CLASS_ATMOSPHERIC_PRESSURE, |     DEVICE_CLASS_ATMOSPHERIC_PRESSURE, | ||||||
|  |     DEVICE_CLASS_CARBON_DIOXIDE, | ||||||
|     DEVICE_CLASS_HUMIDITY, |     DEVICE_CLASS_HUMIDITY, | ||||||
|     DEVICE_CLASS_TEMPERATURE, |     DEVICE_CLASS_TEMPERATURE, | ||||||
|  |     DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, | ||||||
|     ICON_GAS_CYLINDER, |     ICON_GAS_CYLINDER, | ||||||
|     ICON_GAUGE, |     ICON_GAUGE, | ||||||
|     ICON_THERMOMETER, |     ICON_THERMOMETER, | ||||||
| @@ -32,7 +34,6 @@ CONF_CO2_EQUIVALENT = "co2_equivalent" | |||||||
| CONF_IAQ = "iaq" | CONF_IAQ = "iaq" | ||||||
| CONF_IAQ_STATIC = "iaq_static" | CONF_IAQ_STATIC = "iaq_static" | ||||||
| ICON_ACCURACY = "mdi:checkbox-marked-circle-outline" | ICON_ACCURACY = "mdi:checkbox-marked-circle-outline" | ||||||
| ICON_TEST_TUBE = "mdi:test-tube" |  | ||||||
| UNIT_IAQ = "IAQ" | UNIT_IAQ = "IAQ" | ||||||
|  |  | ||||||
| TYPES = [ | TYPES = [ | ||||||
| @@ -61,7 +62,6 @@ CONFIG_SCHEMA = cv.Schema( | |||||||
|         ), |         ), | ||||||
|         cv.Optional(CONF_PRESSURE): sensor.sensor_schema( |         cv.Optional(CONF_PRESSURE): sensor.sensor_schema( | ||||||
|             unit_of_measurement=UNIT_HECTOPASCAL, |             unit_of_measurement=UNIT_HECTOPASCAL, | ||||||
|             icon=ICON_GAUGE, |  | ||||||
|             accuracy_decimals=1, |             accuracy_decimals=1, | ||||||
|             device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE, |             device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE, | ||||||
|             state_class=STATE_CLASS_MEASUREMENT, |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
| @@ -102,14 +102,14 @@ CONFIG_SCHEMA = cv.Schema( | |||||||
|         ), |         ), | ||||||
|         cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema( |         cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema( | ||||||
|             unit_of_measurement=UNIT_PARTS_PER_MILLION, |             unit_of_measurement=UNIT_PARTS_PER_MILLION, | ||||||
|             icon=ICON_TEST_TUBE, |  | ||||||
|             accuracy_decimals=1, |             accuracy_decimals=1, | ||||||
|  |             device_class=DEVICE_CLASS_CARBON_DIOXIDE, | ||||||
|             state_class=STATE_CLASS_MEASUREMENT, |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|         ), |         ), | ||||||
|         cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema( |         cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema( | ||||||
|             unit_of_measurement=UNIT_PARTS_PER_MILLION, |             unit_of_measurement=UNIT_PARTS_PER_MILLION, | ||||||
|             icon=ICON_TEST_TUBE, |  | ||||||
|             accuracy_decimals=1, |             accuracy_decimals=1, | ||||||
|  |             device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, | ||||||
|             state_class=STATE_CLASS_MEASUREMENT, |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|         ), |         ), | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -126,37 +126,37 @@ void BMI160Component::internal_setup_(int stage) { | |||||||
|         return; |         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)) { |       if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::ACCL_SET_PMU_MODE | (uint8_t) AcclPmuMode::NORMAL)) { | ||||||
|         this->mark_failed(); |         this->mark_failed(); | ||||||
|         return; |         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 |       // need to wait (max delay in datasheet) because we can't send commands while another is in progress | ||||||
|       // min 5ms, 10ms |       // min 5ms, 10ms | ||||||
|       this->set_timeout(10, [this]() { this->internal_setup_(1); }); |       this->set_timeout(10, [this]() { this->internal_setup_(1); }); | ||||||
|       break; |       break; | ||||||
|  |  | ||||||
|     case 1: |     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)) { |       if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::GYRO_SET_PMU_MODE | (uint8_t) GyroPmuMode::NORMAL)) { | ||||||
|         this->mark_failed(); |         this->mark_failed(); | ||||||
|         return; |         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 |       // wait between 51 & 81ms, doing 100 to be safe | ||||||
|       this->set_timeout(10, [this]() { this->internal_setup_(2); }); |       this->set_timeout(10, [this]() { this->internal_setup_(2); }); | ||||||
|       break; |       break; | ||||||
|  |  | ||||||
|     case 2: |     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; |       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)); |       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)) { |       if (!this->write_byte(BMI160_REGISTER_GYRO_CONFIG, gyro_config)) { | ||||||
|         this->mark_failed(); |         this->mark_failed(); | ||||||
|         return; |         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; |       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)); |       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)) { |       if (!this->write_byte(BMI160_REGISTER_GYRO_RANGE, gyro_range)) { | ||||||
| @@ -164,7 +164,7 @@ void BMI160Component::internal_setup_(int stage) { | |||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       ESP_LOGV(TAG, "  Setting up Accel Config..."); |       ESP_LOGV(TAG, "  Setting up Accel Config"); | ||||||
|       uint8_t accel_config = |       uint8_t accel_config = | ||||||
|           (uint8_t) AcclFilterMode::PERF | (uint8_t) AcclBandwidth::RES_AVG16 | (uint8_t) AccelOutputDataRate::HZ_25; |           (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)); |       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(); |         this->mark_failed(); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|       ESP_LOGV(TAG, "  Setting up Accel Range..."); |       ESP_LOGV(TAG, "  Setting up Accel Range"); | ||||||
|       uint8_t accel_range = (uint8_t) AccelRange::RANGE_16G; |       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)); |       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)) { |       if (!this->write_byte(BMI160_REGISTER_ACCEL_RANGE, accel_range)) { | ||||||
| @@ -219,7 +219,7 @@ void BMI160Component::update() { | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGV(TAG, "    Updating BMI160..."); |   ESP_LOGV(TAG, "    Updating BMI160"); | ||||||
|   int16_t data[6]; |   int16_t data[6]; | ||||||
|   if (this->read_le_int16_(BMI160_REGISTER_DATA_GYRO_X_LSB, data, 6) != i2c::ERROR_OK) { |   if (this->read_le_int16_(BMI160_REGISTER_DATA_GYRO_X_LSB, data, 6) != i2c::ERROR_OK) { | ||||||
|     this->status_set_warning(); |     this->status_set_warning(); | ||||||
|   | |||||||
| @@ -129,7 +129,7 @@ void BMP085Component::read_pressure_() { | |||||||
|   this->status_clear_warning(); |   this->status_clear_warning(); | ||||||
| } | } | ||||||
| bool BMP085Component::set_mode_(uint8_t mode) { | 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); |   return this->write_byte(BMP085_REGISTER_CONTROL, mode); | ||||||
| } | } | ||||||
| float BMP085Component::get_setup_priority() const { return setup_priority::DATA; } | 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() { | void BMP280Component::update() { | ||||||
|   // Enable sensor |   // Enable sensor | ||||||
|   ESP_LOGV(TAG, "Sending conversion request..."); |   ESP_LOGV(TAG, "Sending conversion request"); | ||||||
|   uint8_t meas_value = 0; |   uint8_t meas_value = 0; | ||||||
|   meas_value |= (this->temperature_oversampling_ & 0b111) << 5; |   meas_value |= (this->temperature_oversampling_ & 0b111) << 5; | ||||||
|   meas_value |= (this->pressure_oversampling_ & 0b111) << 2; |   meas_value |= (this->pressure_oversampling_ & 0b111) << 2; | ||||||
|   | |||||||
| @@ -73,7 +73,7 @@ void BMP3XXComponent::setup() { | |||||||
|   ESP_LOGCONFIG(TAG, "Running setup"); |   ESP_LOGCONFIG(TAG, "Running setup"); | ||||||
|   // Call the Device base class "initialise" function |   // Call the Device base class "initialise" function | ||||||
|   if (!reset()) { |   if (!reset()) { | ||||||
|     ESP_LOGE(TAG, "Failed to reset BMP3XX..."); |     ESP_LOGE(TAG, "Failed to reset"); | ||||||
|     this->error_code_ = ERROR_SENSOR_RESET; |     this->error_code_ = ERROR_SENSOR_RESET; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|   } |   } | ||||||
| @@ -148,8 +148,10 @@ void BMP3XXComponent::setup() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void BMP3XXComponent::dump_config() { | void BMP3XXComponent::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "BMP3XX:"); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg); |                 "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_) { |   switch (this->error_code_) { | ||||||
|     case NONE: |     case NONE: | ||||||
|       break; |       break; | ||||||
| @@ -157,16 +159,14 @@ void BMP3XXComponent::dump_config() { | |||||||
|       ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); |       ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); | ||||||
|       break; |       break; | ||||||
|     case ERROR_WRONG_CHIP_ID: |     case ERROR_WRONG_CHIP_ID: | ||||||
|       ESP_LOGE( |       ESP_LOGE(TAG, "Wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390", | ||||||
|           TAG, |  | ||||||
|           "BMP3XX has wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390", |  | ||||||
|                this->chip_id_.reg); |                this->chip_id_.reg); | ||||||
|       break; |       break; | ||||||
|     case ERROR_SENSOR_RESET: |     case ERROR_SENSOR_RESET: | ||||||
|       ESP_LOGE(TAG, "BMP3XX failed to reset"); |       ESP_LOGE(TAG, "Failed to reset"); | ||||||
|       break; |       break; | ||||||
|     default: |     default: | ||||||
|       ESP_LOGE(TAG, "BMP3XX error code %d", (int) this->error_code_); |       ESP_LOGE(TAG, "Error code %d", (int) this->error_code_); | ||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|   ESP_LOGCONFIG(TAG, "  IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_filter_))); |   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() { | void BMP3XXComponent::update() { | ||||||
|   // Enable sensor |   // Enable sensor | ||||||
|   ESP_LOGV(TAG, "Sending conversion request..."); |   ESP_LOGV(TAG, "Sending conversion request"); | ||||||
|   float meas_time = 1.0f; |   float meas_time = 1.0f; | ||||||
|   // Ref: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf 3.9.2 |   // 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; |   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) { | bool BMP3XXComponent::get_measurements(float &temperature, float &pressure) { | ||||||
|   // Check if a measurement is ready |   // Check if a measurement is ready | ||||||
|   if (!data_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; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -72,22 +72,22 @@ void BMP581Component::dump_config() { | |||||||
|     case NONE: |     case NONE: | ||||||
|       break; |       break; | ||||||
|     case ERROR_COMMUNICATION_FAILED: |     case ERROR_COMMUNICATION_FAILED: | ||||||
|       ESP_LOGE(TAG, "  Communication with BMP581 failed!"); |       ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); | ||||||
|       break; |       break; | ||||||
|     case ERROR_WRONG_CHIP_ID: |     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; |       break; | ||||||
|     case ERROR_SENSOR_RESET: |     case ERROR_SENSOR_RESET: | ||||||
|       ESP_LOGE(TAG, "  BMP581 failed to reset"); |       ESP_LOGE(TAG, "Reset failed"); | ||||||
|       break; |       break; | ||||||
|     case ERROR_SENSOR_STATUS: |     case ERROR_SENSOR_STATUS: | ||||||
|       ESP_LOGE(TAG, "  BMP581 sensor status failed, there were NVM problems"); |       ESP_LOGE(TAG, "Get status failed"); | ||||||
|       break; |       break; | ||||||
|     case ERROR_PRIME_IIR_FAILED: |     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; |       break; | ||||||
|     default: |     default: | ||||||
|       ESP_LOGE(TAG, "  BMP581 error code %d", (int) this->error_code_); |       ESP_LOGE(TAG, "Error %d", (int) this->error_code_); | ||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -98,14 +98,20 @@ void BMP581Component::dump_config() { | |||||||
|  |  | ||||||
|   if (this->temperature_sensor_) { |   if (this->temperature_sensor_) { | ||||||
|     LOG_SENSOR("  ", "Temperature", 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, | ||||||
|     ESP_LOGCONFIG(TAG, "    Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_))); |                   "    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_) { |   if (this->pressure_sensor_) { | ||||||
|     LOG_SENSOR("  ", "Pressure", 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, | ||||||
|     ESP_LOGCONFIG(TAG, "    Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_))); |                   "    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 |   // Power-On-Reboot bit is asserted if sensor successfully reset | ||||||
|   if (!this->reset_()) { |   if (!this->reset_()) { | ||||||
|     ESP_LOGE(TAG, "BMP581 failed to reset"); |     ESP_LOGE(TAG, "Reset failed"); | ||||||
|  |  | ||||||
|     this->error_code_ = ERROR_SENSOR_RESET; |     this->error_code_ = ERROR_SENSOR_RESET; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
| @@ -146,7 +152,7 @@ void BMP581Component::setup() { | |||||||
|  |  | ||||||
|   // read chip id from sensor |   // read chip id from sensor | ||||||
|   if (!this->read_byte(BMP581_CHIP_ID, &chip_id)) { |   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->error_code_ = ERROR_COMMUNICATION_FAILED; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
| @@ -156,7 +162,7 @@ void BMP581Component::setup() { | |||||||
|  |  | ||||||
|   // verify id |   // verify id | ||||||
|   if (chip_id != BMP581_ASIC_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->error_code_ = ERROR_WRONG_CHIP_ID; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
| @@ -179,7 +185,7 @@ void BMP581Component::setup() { | |||||||
|  |  | ||||||
|   // verify status_nvm_rdy bit (it is asserted if boot was successful) |   // verify status_nvm_rdy bit (it is asserted if boot was successful) | ||||||
|   if (!(this->status_.bit.status_nvm_rdy)) { |   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->error_code_ = ERROR_SENSOR_STATUS; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
| @@ -189,7 +195,7 @@ void BMP581Component::setup() { | |||||||
|  |  | ||||||
|   // verify status_nvm_err bit (it is asserted if an error is detected) |   // verify status_nvm_err bit (it is asserted if an error is detected) | ||||||
|   if (this->status_.bit.status_nvm_err) { |   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->error_code_ = ERROR_SENSOR_STATUS; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
| @@ -254,7 +260,7 @@ void BMP581Component::setup() { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!this->prime_iir_filter_()) { |     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->error_code_ = ERROR_PRIME_IIR_FAILED; | ||||||
|       this->mark_failed(); |       this->mark_failed(); | ||||||
| @@ -286,10 +292,10 @@ void BMP581Component::update() { | |||||||
|   // 1) Request a measurement // |   // 1) Request a measurement // | ||||||
|   ////////////////////////////// |   ////////////////////////////// | ||||||
|  |  | ||||||
|   ESP_LOGVV(TAG, "Requesting a measurement from sensor"); |   ESP_LOGVV(TAG, "Requesting measurement"); | ||||||
|  |  | ||||||
|   if (!this->start_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(); |     this->status_set_warning(); | ||||||
|  |  | ||||||
|     return; |     return; | ||||||
| @@ -299,7 +305,7 @@ void BMP581Component::update() { | |||||||
|   // 2) Wait for measurement to finish (based on oversampling rates) // |   // 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]() { |   this->set_timeout("measurement", this->conversion_time_, [this]() { | ||||||
|     float temperature = 0.0; |     float temperature = 0.0; | ||||||
| @@ -311,14 +317,14 @@ void BMP581Component::update() { | |||||||
|  |  | ||||||
|     if (this->pressure_sensor_) { |     if (this->pressure_sensor_) { | ||||||
|       if (!this->read_temperature_and_pressure_(temperature, pressure)) { |       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(); |         this->status_set_warning(); | ||||||
|  |  | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       if (!this->read_temperature_(temperature)) { |       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(); |         this->status_set_warning(); | ||||||
|  |  | ||||||
|         return; |         return; | ||||||
| @@ -349,7 +355,7 @@ bool BMP581Component::check_data_readiness_() { | |||||||
|   //   - returns data readiness state |   //   - returns data readiness state | ||||||
|  |  | ||||||
|   if (this->odr_config_.bit.pwr_mode == STANDBY_MODE) { |   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; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -443,7 +449,7 @@ bool BMP581Component::read_temperature_(float &temperature) { | |||||||
|   //    - the measured temperature (in degrees Celsius) |   //    - the measured temperature (in degrees Celsius) | ||||||
|  |  | ||||||
|   if (!this->check_data_readiness_()) { |   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(); |     this->status_set_warning(); | ||||||
|  |  | ||||||
|     return false; |     return false; | ||||||
| @@ -451,7 +457,7 @@ bool BMP581Component::read_temperature_(float &temperature) { | |||||||
|  |  | ||||||
|   uint8_t data[3]; |   uint8_t data[3]; | ||||||
|   if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 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(); |     this->status_set_warning(); | ||||||
|  |  | ||||||
|     return false; |     return false; | ||||||
| @@ -472,7 +478,7 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float & | |||||||
|   //    - the measured pressure (in Pa) |   //    - the measured pressure (in Pa) | ||||||
|  |  | ||||||
|   if (!this->check_data_readiness_()) { |   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(); |     this->status_set_warning(); | ||||||
|  |  | ||||||
|     return false; |     return false; | ||||||
| @@ -480,7 +486,7 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float & | |||||||
|  |  | ||||||
|   uint8_t data[6]; |   uint8_t data[6]; | ||||||
|   if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 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(); |     this->status_set_warning(); | ||||||
|  |  | ||||||
|     return false; |     return false; | ||||||
|   | |||||||
| @@ -26,8 +26,10 @@ void BP1658CJ::dump_config() { | |||||||
|   ESP_LOGCONFIG(TAG, "BP1658CJ:"); |   ESP_LOGCONFIG(TAG, "BP1658CJ:"); | ||||||
|   LOG_PIN("  Data Pin: ", this->data_pin_); |   LOG_PIN("  Data Pin: ", this->data_pin_); | ||||||
|   LOG_PIN("  Clock Pin: ", this->clock_pin_); |   LOG_PIN("  Clock Pin: ", this->clock_pin_); | ||||||
|   ESP_LOGCONFIG(TAG, "  Color Channels Max Power: %u", this->max_power_color_channels_); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  White Channels Max Power: %u", this->max_power_white_channels_); |                 "  Color Channels Max Power: %u\n" | ||||||
|  |                 "  White Channels Max Power: %u", | ||||||
|  |                 this->max_power_color_channels_, this->max_power_white_channels_); | ||||||
| } | } | ||||||
|  |  | ||||||
| void BP1658CJ::loop() { | void BP1658CJ::loop() { | ||||||
|   | |||||||
| @@ -108,6 +108,7 @@ async def register_button(var, config): | |||||||
|     if not CORE.has_id(config[CONF_ID]): |     if not CORE.has_id(config[CONF_ID]): | ||||||
|         var = cg.Pvariable(config[CONF_ID], var) |         var = cg.Pvariable(config[CONF_ID], var) | ||||||
|     cg.add(cg.App.register_button(var)) |     cg.add(cg.App.register_button(var)) | ||||||
|  |     CORE.register_platform_component("button", var) | ||||||
|     await setup_button_core_(var, config) |     await setup_button_core_(var, config) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import re | import re | ||||||
|  |  | ||||||
| from esphome import automation | from esphome import automation | ||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
|   | |||||||
| @@ -52,9 +52,11 @@ void CAP1188Component::dump_config() { | |||||||
|   ESP_LOGCONFIG(TAG, "CAP1188:"); |   ESP_LOGCONFIG(TAG, "CAP1188:"); | ||||||
|   LOG_I2C_DEVICE(this); |   LOG_I2C_DEVICE(this); | ||||||
|   LOG_PIN("  Reset Pin: ", this->reset_pin_); |   LOG_PIN("  Reset Pin: ", this->reset_pin_); | ||||||
|   ESP_LOGCONFIG(TAG, "  Product ID: 0x%x", this->cap1188_product_id_); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  Manufacture ID: 0x%x", this->cap1188_manufacture_id_); |                 "  Product ID: 0x%x\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  Revision ID: 0x%x", this->cap1188_revision_); |                 "  Manufacture ID: 0x%x\n" | ||||||
|  |                 "  Revision ID: 0x%x", | ||||||
|  |                 this->cap1188_product_id_, this->cap1188_manufacture_id_, this->cap1188_revision_); | ||||||
|  |  | ||||||
|   switch (this->error_code_) { |   switch (this->error_code_) { | ||||||
|     case COMMUNICATION_FAILED: |     case COMMUNICATION_FAILED: | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ async def to_code(config): | |||||||
|  |  | ||||||
|     if CORE.using_arduino: |     if CORE.using_arduino: | ||||||
|         if CORE.is_esp32: |         if CORE.is_esp32: | ||||||
|  |             cg.add_library("ESP32 Async UDP", None) | ||||||
|             cg.add_library("DNSServer", None) |             cg.add_library("DNSServer", None) | ||||||
|             cg.add_library("WiFi", None) |             cg.add_library("WiFi", None) | ||||||
|         if CORE.is_esp8266: |         if CORE.is_esp8266: | ||||||
|   | |||||||
| @@ -37,7 +37,12 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) { | |||||||
|   request->redirect("/?save"); |   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() { | void CaptivePortal::start() { | ||||||
|   this->base_->init(); |   this->base_->init(); | ||||||
|   if (!this->initialized_) { |   if (!this->initialized_) { | ||||||
| @@ -50,6 +55,8 @@ void CaptivePortal::start() { | |||||||
|   this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError); |   this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError); | ||||||
|   network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); |   network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); | ||||||
|   this->dns_server_->start(53, "*", ip); |   this->dns_server_->start(53, "*", ip); | ||||||
|  |   // Re-enable loop() when DNS server is started | ||||||
|  |   this->enable_loop(); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) { |   this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) { | ||||||
| @@ -68,7 +75,11 @@ void CaptivePortal::start() { | |||||||
|  |  | ||||||
| void CaptivePortal::handleRequest(AsyncWebServerRequest *req) { | void CaptivePortal::handleRequest(AsyncWebServerRequest *req) { | ||||||
|   if (req->url() == "/") { |   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)); |     auto *response = req->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ)); | ||||||
|  | #endif | ||||||
|     response->addHeader("Content-Encoding", "gzip"); |     response->addHeader("Content-Encoding", "gzip"); | ||||||
|     req->send(response); |     req->send(response); | ||||||
|     return; |     return; | ||||||
|   | |||||||
| @@ -21,8 +21,11 @@ class CaptivePortal : public AsyncWebHandler, public Component { | |||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
| #ifdef USE_ARDUINO | #ifdef USE_ARDUINO | ||||||
|   void loop() override { |   void loop() override { | ||||||
|     if (this->dns_server_ != nullptr) |     if (this->dns_server_ != nullptr) { | ||||||
|       this->dns_server_->processNextRequest(); |       this->dns_server_->processNextRequest(); | ||||||
|  |     } else { | ||||||
|  |       this->disable_loop(); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|   float get_setup_priority() const override; |   float get_setup_priority() const override; | ||||||
| @@ -37,7 +40,7 @@ class CaptivePortal : public AsyncWebHandler, public Component { | |||||||
| #endif | #endif | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool canHandle(AsyncWebServerRequest *request) override { |   bool canHandle(AsyncWebServerRequest *request) const override { | ||||||
|     if (!this->active_) |     if (!this->active_) | ||||||
|       return false; |       return false; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| #include "ccs811.h" | #include "ccs811.h" | ||||||
| #include "esphome/core/log.h" |  | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace ccs811 { | namespace ccs811 { | ||||||
|   | |||||||
| @@ -38,9 +38,11 @@ void CHSC6XTouchscreen::dump_config() { | |||||||
|   ESP_LOGCONFIG(TAG, "CHSC6X Touchscreen:"); |   ESP_LOGCONFIG(TAG, "CHSC6X Touchscreen:"); | ||||||
|   LOG_I2C_DEVICE(this); |   LOG_I2C_DEVICE(this); | ||||||
|   LOG_PIN("  Interrupt Pin: ", this->interrupt_pin_); |   LOG_PIN("  Interrupt Pin: ", this->interrupt_pin_); | ||||||
|   ESP_LOGCONFIG(TAG, "  Touch timeout: %d", this->touch_timeout_); |   ESP_LOGCONFIG(TAG, | ||||||
|   ESP_LOGCONFIG(TAG, "  x_raw_max_: %d", this->x_raw_max_); |                 "  Touch timeout: %d\n" | ||||||
|   ESP_LOGCONFIG(TAG, "  y_raw_max_: %d", this->y_raw_max_); |                 "  x_raw_max_: %d\n" | ||||||
|  |                 "  y_raw_max_: %d", | ||||||
|  |                 this->touch_timeout_, this->x_raw_max_, this->y_raw_max_); | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace chsc6x | }  // namespace chsc6x | ||||||
|   | |||||||
| @@ -443,6 +443,7 @@ async def register_climate(var, config): | |||||||
|     if not CORE.has_id(config[CONF_ID]): |     if not CORE.has_id(config[CONF_ID]): | ||||||
|         var = cg.Pvariable(config[CONF_ID], var) |         var = cg.Pvariable(config[CONF_ID], var) | ||||||
|     cg.add(cg.App.register_climate(var)) |     cg.add(cg.App.register_climate(var)) | ||||||
|  |     CORE.register_platform_component("climate", var) | ||||||
|     await setup_climate_core_(var, config) |     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) { | void Climate::dump_traits_(const char *tag) { | ||||||
|   auto traits = this->get_traits(); |   auto traits = this->get_traits(); | ||||||
|   ESP_LOGCONFIG(tag, "ClimateTraits:"); |   ESP_LOGCONFIG(tag, "ClimateTraits:"); | ||||||
|   ESP_LOGCONFIG(tag, "  [x] Visual settings:"); |   ESP_LOGCONFIG(tag, | ||||||
|   ESP_LOGCONFIG(tag, "      - Min temperature: %.1f", traits.get_visual_min_temperature()); |                 "  [x] Visual settings:\n" | ||||||
|   ESP_LOGCONFIG(tag, "      - Max temperature: %.1f", traits.get_visual_max_temperature()); |                 "      - Min temperature: %.1f\n" | ||||||
|   ESP_LOGCONFIG(tag, "      - Temperature step:"); |                 "      - Max temperature: %.1f\n" | ||||||
|   ESP_LOGCONFIG(tag, "          Target: %.1f", traits.get_visual_target_temperature_step()); |                 "      - 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()) { |   if (traits.get_supports_current_temperature()) { | ||||||
|     ESP_LOGCONFIG(tag, "          Current: %.1f", traits.get_visual_current_temperature_step()); |     ESP_LOGCONFIG(tag, "          Current: %.1f", traits.get_visual_current_temperature_step()); | ||||||
|   } |   } | ||||||
|   if (traits.get_supports_target_humidity() || traits.get_supports_current_humidity()) { |   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, | ||||||
|     ESP_LOGCONFIG(tag, "      - Max humidity: %.0f", traits.get_visual_max_humidity()); |                   "      - 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()) { |   if (traits.get_supports_two_point_target_temperature()) { | ||||||
|     ESP_LOGCONFIG(tag, "  [x] 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