esphome 2025.5.2__py3-none-any.whl → 2025.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- esphome/__main__.py +20 -14
- esphome/components/a4988/a4988.cpp +1 -1
- esphome/components/absolute_humidity/absolute_humidity.cpp +6 -4
- esphome/components/ac_dimmer/ac_dimmer.cpp +4 -2
- esphome/components/adc/adc_sensor_esp32.cpp +5 -3
- esphome/components/adc/adc_sensor_esp8266.cpp +5 -3
- esphome/components/adc/adc_sensor_libretiny.cpp +5 -3
- esphome/components/adc/adc_sensor_rp2040.cpp +5 -3
- esphome/components/adc128s102/adc128s102.cpp +1 -1
- esphome/components/ade7880/ade7880.cpp +28 -17
- esphome/components/ade7953_base/ade7953_base.cpp +12 -9
- esphome/components/ade7953_i2c/ade7953_i2c.cpp +1 -1
- esphome/components/ade7953_spi/ade7953_spi.cpp +1 -1
- esphome/components/ads1115/ads1115.cpp +3 -5
- esphome/components/ads1118/ads1118.cpp +2 -1
- esphome/components/ags10/ags10.cpp +3 -2
- esphome/components/aht10/aht10.cpp +27 -29
- esphome/components/aic3204/aic3204.cpp +2 -2
- esphome/components/alarm_control_panel/__init__.py +1 -0
- esphome/components/am2315c/am2315c.cpp +2 -2
- esphome/components/am2320/am2320.cpp +2 -2
- esphome/components/am43/am43_base.h +1 -1
- esphome/components/am43/cover/am43_cover.cpp +4 -2
- esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +4 -2
- esphome/components/apds9306/apds9306.cpp +8 -5
- esphome/components/apds9960/apds9960.cpp +2 -2
- esphome/components/api/__init__.py +5 -0
- esphome/components/api/api_connection.cpp +753 -379
- esphome/components/api/api_connection.h +341 -283
- esphome/components/api/api_frame_helper.cpp +349 -344
- esphome/components/api/api_frame_helper.h +121 -94
- esphome/components/api/api_pb2.cpp +5 -0
- esphome/components/api/api_pb2.h +703 -227
- esphome/components/api/api_pb2_service.cpp +12 -688
- esphome/components/api/api_pb2_service.h +53 -207
- esphome/components/api/api_server.cpp +71 -29
- esphome/components/api/api_server.h +9 -0
- esphome/components/api/client.py +5 -4
- esphome/components/api/homeassistant_service.h +1 -1
- esphome/components/api/list_entities.cpp +1 -1
- esphome/components/api/proto.cpp +1 -0
- esphome/components/api/proto.h +5 -4
- esphome/components/api/subscribe_state.cpp +8 -16
- esphome/components/as3935/as3935.cpp +3 -3
- esphome/components/as5600/as5600.cpp +9 -7
- esphome/components/as7341/as7341.cpp +7 -5
- esphome/components/at581x/at581x.cpp +13 -10
- esphome/components/atm90e26/atm90e26.cpp +2 -2
- esphome/components/atm90e32/atm90e32.cpp +3 -3
- esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +5 -3
- esphome/components/bang_bang/bang_bang_climate.cpp +8 -5
- esphome/components/bedjet/bedjet_hub.cpp +6 -4
- esphome/components/beken_spi_led_strip/led_strip.cpp +11 -7
- esphome/components/bh1750/bh1750.cpp +2 -2
- esphome/components/binary_sensor/__init__.py +1 -0
- esphome/components/binary_sensor/automation.cpp +1 -2
- esphome/components/bl0906/bl0906.cpp +1 -1
- esphome/components/bl0942/bl0942.cpp +11 -8
- esphome/components/bl0942/sensor.py +1 -1
- esphome/components/ble_client/__init__.py +5 -1
- esphome/components/ble_client/output/ble_binary_output.cpp +6 -3
- esphome/components/ble_client/sensor/ble_sensor.cpp +9 -6
- esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +8 -5
- esphome/components/bluetooth_proxy/__init__.py +5 -1
- esphome/components/bluetooth_proxy/bluetooth_connection.cpp +5 -5
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +18 -16
- esphome/components/bluetooth_proxy/bluetooth_proxy.h +1 -1
- esphome/components/bme280_base/bme280_base.cpp +5 -6
- esphome/components/bme680/bme680.cpp +3 -3
- esphome/components/bme680/sensor.py +2 -2
- esphome/components/bme680_bsec/bme680_bsec.cpp +11 -7
- esphome/components/bme680_bsec/sensor.py +7 -3
- esphome/components/bme68x_bsec2/__init__.py +6 -5
- esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +17 -13
- esphome/components/bme68x_bsec2/sensor.py +4 -4
- esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp +1 -0
- esphome/components/bmi160/bmi160.cpp +11 -11
- esphome/components/bmp085/bmp085.cpp +2 -2
- esphome/components/bmp280_base/bmp280_base.cpp +3 -3
- esphome/components/bmp3xx_base/bmp3xx_base.cpp +13 -13
- esphome/components/bmp581/bmp581.cpp +33 -27
- esphome/components/bp1658cj/bp1658cj.cpp +5 -3
- esphome/components/bp5758d/bp5758d.cpp +1 -1
- esphome/components/button/__init__.py +1 -0
- esphome/components/canbus/canbus.cpp +1 -1
- esphome/components/cap1188/cap1188.cpp +6 -4
- esphome/components/captive_portal/captive_portal.cpp +1 -1
- esphome/components/ccs811/ccs811.cpp +3 -2
- esphome/components/cd74hc4067/cd74hc4067.cpp +1 -1
- esphome/components/ch422g/ch422g.cpp +2 -2
- esphome/components/chsc6x/chsc6x_touchscreen.cpp +6 -4
- esphome/components/climate/__init__.py +1 -0
- esphome/components/climate/climate.cpp +12 -7
- esphome/components/climate/climate.h +1 -1
- esphome/components/climate_ir/climate_ir.cpp +7 -4
- esphome/components/cm1106/__init__.py +1 -0
- esphome/components/cm1106/cm1106.cpp +112 -0
- esphome/components/cm1106/cm1106.h +40 -0
- esphome/components/cm1106/sensor.py +72 -0
- esphome/components/const/__init__.py +1 -0
- esphome/components/cover/__init__.py +1 -0
- esphome/components/cs5460a/cs5460a.cpp +16 -11
- esphome/components/cse7761/cse7761.cpp +2 -2
- esphome/components/cse7766/cse7766.cpp +0 -5
- esphome/components/cse7766/cse7766.h +5 -1
- esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +1 -1
- esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +6 -3
- esphome/components/current_based/current_based_cover.cpp +4 -2
- esphome/components/dac7678/dac7678_output.cpp +4 -4
- esphome/components/dallas_temp/dallas_temp.cpp +2 -3
- esphome/components/daly_bms/daly_bms.cpp +2 -1
- esphome/components/dashboard_import/__init__.py +1 -2
- esphome/components/datetime/__init__.py +3 -1
- esphome/components/datetime/date_entity.cpp +5 -5
- esphome/components/datetime/datetime_base.h +0 -5
- esphome/components/datetime/datetime_entity.cpp +8 -8
- esphome/components/datetime/time_entity.cpp +4 -4
- esphome/components/debug/debug_component.cpp +1 -1
- esphome/components/debug/debug_component.h +1 -1
- esphome/components/debug/debug_esp32.cpp +4 -2
- esphome/components/deep_sleep/deep_sleep_component.cpp +11 -5
- esphome/components/deep_sleep/deep_sleep_esp32.cpp +7 -5
- esphome/components/demo/__init__.py +206 -0
- esphome/components/demo/demo_alarm_control_panel.h +65 -0
- esphome/components/demo/demo_button.h +15 -0
- esphome/components/demo/demo_date.h +34 -0
- esphome/components/demo/demo_datetime.h +40 -0
- esphome/components/demo/demo_lock.h +17 -0
- esphome/components/demo/demo_select.h +15 -0
- esphome/components/demo/demo_text.h +18 -0
- esphome/components/demo/demo_time.h +34 -0
- esphome/components/demo/demo_valve.h +54 -0
- esphome/components/dfrobot_sen0395/commands.cpp +3 -3
- esphome/components/dht/dht.cpp +29 -50
- esphome/components/dht12/dht12.cpp +2 -2
- esphome/components/display/display.h +5 -3
- esphome/components/dps310/dps310.cpp +7 -5
- esphome/components/ds1307/ds1307.cpp +2 -2
- esphome/components/dsmr/dsmr.cpp +5 -3
- esphome/components/duty_cycle/duty_cycle_sensor.cpp +2 -2
- esphome/components/duty_time/duty_time_sensor.cpp +5 -3
- esphome/components/ee895/ee895.cpp +3 -3
- esphome/components/ektf2232/touchscreen/ektf2232.cpp +1 -1
- esphome/components/emc2101/emc2101.cpp +11 -9
- esphome/components/ens160_base/ens160_base.cpp +2 -2
- esphome/components/ens210/ens210.cpp +2 -2
- esphome/components/es7210/es7210.cpp +6 -4
- esphome/components/es7243e/es7243e.cpp +1 -1
- esphome/components/es8156/es8156.cpp +1 -1
- esphome/components/es8311/es8311.cpp +8 -6
- esphome/components/es8388/__init__.py +0 -0
- esphome/components/es8388/audio_dac.py +26 -0
- esphome/components/es8388/es8388.cpp +289 -0
- esphome/components/es8388/es8388.h +81 -0
- esphome/components/es8388/es8388_const.h +83 -0
- esphome/components/es8388/select/__init__.py +47 -0
- esphome/components/es8388/select/adc_input_mic_select.cpp +12 -0
- esphome/components/es8388/select/adc_input_mic_select.h +15 -0
- esphome/components/es8388/select/dac_output_select.cpp +12 -0
- esphome/components/es8388/select/dac_output_select.h +15 -0
- esphome/components/esp32/__init__.py +118 -23
- esphome/components/esp32/boards.py +208 -125
- esphome/components/esp32/const.py +6 -0
- esphome/components/esp32/gpio.py +14 -1
- esphome/components/esp32/gpio_esp32_c5.py +45 -0
- esphome/components/esp32/gpio_esp32_p4.py +43 -0
- esphome/components/esp32/preferences.cpp +7 -7
- esphome/components/esp32_ble/__init__.py +115 -0
- esphome/components/esp32_ble/ble.cpp +101 -54
- esphome/components/esp32_ble/ble.h +24 -5
- esphome/components/esp32_ble/ble_event.h +172 -32
- esphome/components/esp32_ble/ble_scan_result.h +24 -0
- esphome/components/esp32_ble/ble_uuid.h +1 -1
- esphome/components/esp32_ble/queue.h +53 -27
- esphome/components/esp32_ble_client/ble_client_base.cpp +4 -2
- esphome/components/esp32_ble_server/__init__.py +4 -1
- esphome/components/esp32_ble_server/ble_characteristic.cpp +1 -0
- esphome/components/esp32_ble_server/ble_server.h +0 -1
- esphome/components/esp32_ble_tracker/__init__.py +7 -2
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +107 -75
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +16 -16
- esphome/components/esp32_camera/esp32_camera.cpp +35 -27
- esphome/components/esp32_camera_web_server/camera_web_server.cpp +4 -2
- esphome/components/esp32_dac/esp32_dac.cpp +2 -2
- esphome/components/esp32_improv/__init__.py +5 -1
- esphome/components/esp32_improv/esp32_improv_component.cpp +2 -2
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +11 -7
- esphome/components/esp32_rmt_led_strip/light.py +5 -1
- esphome/components/esp32_touch/esp32_touch.cpp +12 -8
- esphome/components/esp8266/preferences.cpp +6 -6
- esphome/components/esp8266_pwm/esp8266_pwm.cpp +3 -3
- esphome/components/esp_ldo/__init__.py +91 -0
- esphome/components/esp_ldo/esp_ldo.cpp +43 -0
- esphome/components/esp_ldo/esp_ldo.h +43 -0
- esphome/components/esphome/ota/ota_esphome.cpp +15 -8
- esphome/components/ethernet/ethernet_component.cpp +38 -26
- esphome/components/ethernet/ethernet_component.h +1 -1
- esphome/components/event/__init__.py +1 -0
- esphome/components/exposure_notifications/exposure_notifications.cpp +1 -1
- esphome/components/ezo/ezo.cpp +1 -1
- esphome/components/ezo_pmp/ezo_pmp.cpp +1 -1
- esphome/components/factory_reset/button/factory_reset_button.cpp +1 -1
- esphome/components/factory_reset/switch/factory_reset_switch.cpp +1 -1
- esphome/components/fan/__init__.py +1 -0
- esphome/components/fan/fan.cpp +30 -19
- esphome/components/fastled_base/fastled_light.cpp +7 -5
- esphome/components/fingerprint_grow/fingerprint_grow.cpp +8 -6
- esphome/components/fs3000/fs3000.cpp +1 -1
- esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp +7 -4
- esphome/components/ft63x6/ft63x6.cpp +5 -3
- esphome/components/gcja5/gcja5.cpp +0 -12
- esphome/components/gcja5/gcja5.h +8 -3
- esphome/components/gdk101/gdk101.cpp +2 -2
- esphome/components/globals/globals_component.h +13 -10
- esphome/components/gp2y1010au0f/gp2y1010au0f.cpp +4 -2
- esphome/components/gp8403/gp8403.cpp +4 -2
- esphome/components/gp8403/output/gp8403_output.cpp +4 -2
- esphome/components/gpio/one_wire/gpio_one_wire.cpp +2 -2
- esphome/components/gpio/output/gpio_binary_output.cpp +1 -1
- esphome/components/gpio/switch/gpio_switch.cpp +1 -1
- esphome/components/graphical_display_menu/graphical_display_menu.cpp +12 -8
- esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +5 -6
- esphome/components/grove_tb6612fng/grove_tb6612fng.cpp +1 -1
- esphome/components/grove_tb6612fng/grove_tb6612fng.h +1 -1
- esphome/components/growatt_solar/growatt_solar.cpp +6 -3
- esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +1 -1
- esphome/components/haier/haier_base.cpp +2 -2
- esphome/components/haier/hon_climate.cpp +13 -6
- esphome/components/havells_solar/havells_solar.cpp +5 -2
- esphome/components/hbridge/switch/hbridge_switch.cpp +1 -1
- esphome/components/hdc1080/hdc1080.cpp +2 -2
- esphome/components/he60r/he60r.cpp +4 -2
- esphome/components/hlw8012/hlw8012.cpp +6 -4
- esphome/components/hm3301/hm3301.cpp +2 -2
- esphome/components/hmc5883l/hmc5883l.cpp +2 -2
- esphome/components/homeassistant/time/homeassistant_time.cpp +4 -2
- esphome/components/honeywell_hih_i2c/honeywell_hih.cpp +4 -4
- esphome/components/honeywellabp/honeywellabp.cpp +4 -2
- esphome/components/honeywellabp2_i2c/honeywellabp2.cpp +8 -6
- esphome/components/hte501/hte501.cpp +3 -2
- esphome/components/http_request/__init__.py +2 -2
- esphome/components/http_request/http_request.cpp +7 -5
- esphome/components/http_request/http_request_idf.cpp +4 -2
- esphome/components/http_request/ota/ota_http_request.cpp +1 -1
- esphome/components/htu21d/htu21d.cpp +2 -2
- esphome/components/htu31d/htu31d.cpp +2 -2
- esphome/components/hx711/hx711.cpp +2 -2
- esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +5 -3
- esphome/components/hyt271/hyt271.cpp +2 -2
- esphome/components/i2c/i2c_bus_arduino.cpp +14 -11
- esphome/components/i2c/i2c_bus_esp_idf.cpp +13 -11
- esphome/components/i2s_audio/__init__.py +2 -0
- esphome/components/i2s_audio/i2s_audio.cpp +3 -4
- esphome/components/i2s_audio/i2s_audio.h +1 -1
- esphome/components/i2s_audio/media_player/__init__.py +1 -1
- esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +5 -3
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +48 -30
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +2 -0
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +29 -11
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +1 -0
- esphome/components/iaqcore/iaqcore.cpp +3 -3
- esphome/components/ili9xxx/ili9xxx_display.cpp +12 -7
- esphome/components/ili9xxx/ili9xxx_display.h +1 -1
- esphome/components/image/image.cpp +1 -0
- esphome/components/ina219/ina219.cpp +2 -2
- esphome/components/ina226/ina226.cpp +8 -5
- esphome/components/ina260/ina260.cpp +1 -1
- esphome/components/ina2xx_base/ina2xx_base.cpp +13 -9
- esphome/components/ina3221/ina3221.cpp +2 -2
- esphome/components/inkplate6/display.py +12 -2
- esphome/components/inkplate6/inkplate.cpp +13 -9
- esphome/components/inkplate6/inkplate.h +7 -6
- esphome/components/integration/integration_sensor.cpp +1 -1
- esphome/components/internal_temperature/internal_temperature.cpp +4 -4
- esphome/components/json/json_util.cpp +4 -5
- esphome/components/kamstrup_kmp/kamstrup_kmp.cpp +1 -1
- esphome/components/key_collector/key_collector.cpp +4 -2
- esphome/components/kmeteriso/kmeteriso.cpp +4 -4
- esphome/components/kuntze/kuntze.cpp +4 -2
- esphome/components/lc709203f/__init__.py +1 -0
- esphome/components/lc709203f/lc709203f.cpp +299 -0
- esphome/components/lc709203f/lc709203f.h +55 -0
- esphome/components/lc709203f/sensor.py +93 -0
- esphome/components/lcd_base/lcd_display.cpp +2 -2
- esphome/components/lcd_gpio/gpio_lcd_display.cpp +5 -3
- esphome/components/lcd_menu/lcd_menu.cpp +6 -4
- esphome/components/lcd_pcf8574/pcf8574_display.cpp +6 -4
- esphome/components/ld2410/ld2410.cpp +6 -7
- esphome/components/ld2420/ld2420.cpp +9 -7
- esphome/components/ld2450/ld2450.cpp +6 -4
- esphome/components/ld2450/sensor.py +2 -2
- esphome/components/ledc/ledc_output.cpp +32 -28
- esphome/components/libretiny/const.py +1 -1
- esphome/components/libretiny/preferences.cpp +6 -7
- esphome/components/light/__init__.py +2 -1
- esphome/components/light/esp_hsv_color.h +1 -1
- esphome/components/light/light_state.cpp +9 -5
- esphome/components/light/light_state.h +15 -15
- esphome/components/light/light_transformer.h +1 -1
- esphome/components/lightwaverf/LwTx.cpp +1 -1
- esphome/components/lightwaverf/lightwaverf.cpp +1 -1
- esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +1 -1
- esphome/components/lock/__init__.py +1 -0
- esphome/components/lock/lock.h +1 -1
- esphome/components/logger/__init__.py +6 -0
- esphome/components/logger/logger.cpp +11 -20
- esphome/components/logger/logger.h +1 -2
- esphome/components/logger/logger_esp32.cpp +5 -5
- esphome/components/ltr390/ltr390.cpp +8 -5
- esphome/components/ltr501/ltr501.cpp +19 -14
- esphome/components/ltr_als_ps/ltr_als_ps.cpp +20 -13
- esphome/components/lvgl/__init__.py +2 -2
- esphome/components/lvgl/automation.py +5 -4
- esphome/components/lvgl/defines.py +0 -2
- esphome/components/lvgl/lv_validation.py +1 -3
- esphome/components/lvgl/lvcode.py +7 -8
- esphome/components/lvgl/lvgl_esphome.cpp +26 -10
- esphome/components/lvgl/schemas.py +22 -23
- esphome/components/lvgl/trigger.py +8 -3
- esphome/components/lvgl/widgets/__init__.py +2 -2
- esphome/components/lvgl/widgets/canvas.py +9 -3
- esphome/components/lvgl/widgets/line.py +2 -1
- esphome/components/lvgl/widgets/tabview.py +7 -0
- esphome/components/m5stack_8angle/m5stack_8angle.cpp +3 -3
- esphome/components/matrix_keypad/matrix_keypad.cpp +2 -2
- esphome/components/max17043/max17043.cpp +2 -2
- esphome/components/max31855/max31855.cpp +2 -1
- esphome/components/max31856/max31856.cpp +9 -11
- esphome/components/max31865/max31865.cpp +6 -4
- esphome/components/max44009/max44009.cpp +2 -2
- esphome/components/max6675/max6675.cpp +1 -1
- esphome/components/max6956/max6956.cpp +5 -3
- esphome/components/max7219/max7219.cpp +8 -6
- esphome/components/max7219digit/automation.h +52 -0
- esphome/components/max7219digit/display.py +93 -1
- esphome/components/max7219digit/max7219digit.cpp +16 -13
- esphome/components/max9611/max9611.cpp +9 -6
- esphome/components/mcp23008/mcp23008.cpp +1 -1
- esphome/components/mcp23016/mcp23016.cpp +1 -1
- esphome/components/mcp23017/mcp23017.cpp +1 -1
- esphome/components/mcp23s08/mcp23s08.cpp +1 -1
- esphome/components/mcp23s17/mcp23s17.cpp +1 -1
- esphome/components/mcp3008/mcp3008.cpp +1 -1
- esphome/components/mcp3008/sensor/mcp3008_sensor.cpp +5 -3
- esphome/components/mcp3204/mcp3204.cpp +1 -1
- esphome/components/mcp4461/mcp4461.cpp +2 -2
- esphome/components/mcp4725/mcp4725.cpp +2 -2
- esphome/components/mcp4728/mcp4728.cpp +2 -2
- esphome/components/mcp9600/mcp9600.cpp +1 -1
- esphome/components/mcp9808/mcp9808.cpp +4 -4
- esphome/components/mdns/mdns_component.cpp +8 -2
- esphome/components/mdns/mdns_component.h +3 -1
- esphome/components/mdns/mdns_esp32.cpp +2 -2
- esphome/components/media_player/__init__.py +1 -0
- esphome/components/micro_wake_word/micro_wake_word.cpp +1 -1
- esphome/components/micro_wake_word/streaming_model.cpp +10 -6
- esphome/components/micronova/micronova.h +2 -2
- esphome/components/mics_4514/mics_4514.cpp +2 -2
- esphome/components/midea/air_conditioner.cpp +7 -5
- esphome/components/midea_ir/midea_ir.cpp +1 -1
- esphome/components/mipi_spi/mipi_spi.cpp +24 -15
- esphome/components/mixer/speaker/mixer_speaker.cpp +8 -4
- esphome/components/mlx90393/sensor_mlx90393.cpp +2 -2
- esphome/components/mlx90614/mlx90614.cpp +4 -3
- esphome/components/mmc5603/mmc5603.cpp +2 -2
- esphome/components/mmc5983/mmc5983.cpp +1 -1
- esphome/components/modbus/modbus.cpp +7 -5
- esphome/components/modbus_controller/modbus_controller.cpp +6 -4
- esphome/components/modbus_controller/output/modbus_output.cpp +10 -6
- esphome/components/modbus_controller/switch/__init__.py +6 -2
- esphome/components/modbus_controller/switch/modbus_switch.cpp +4 -0
- esphome/components/modbus_controller/switch/modbus_switch.h +3 -0
- esphome/components/mpl3115a2/mpl3115a2.cpp +3 -2
- esphome/components/mpr121/mpr121.cpp +2 -2
- esphome/components/mpu6050/mpu6050.cpp +6 -6
- esphome/components/mpu6886/mpu6886.cpp +6 -6
- esphome/components/mqtt/mqtt_alarm_control_panel.cpp +7 -3
- esphome/components/mqtt/mqtt_backend_esp32.cpp +1 -1
- esphome/components/mqtt/mqtt_client.cpp +31 -24
- esphome/components/mqtt/mqtt_component.cpp +3 -3
- esphome/components/mqtt/mqtt_cover.cpp +8 -4
- esphome/components/mqtt/mqtt_fan.cpp +12 -6
- esphome/components/mqtt/mqtt_valve.cpp +4 -2
- esphome/components/ms5611/ms5611.cpp +2 -2
- esphome/components/ms8607/ms8607.cpp +2 -2
- esphome/components/msa3xx/msa3xx.cpp +16 -12
- esphome/components/my9231/my9231.cpp +7 -5
- esphome/components/nau7802/nau7802.cpp +6 -4
- esphome/components/neopixelbus/neopixelbus_light.h +2 -2
- esphome/components/network/ip_address.h +1 -1
- esphome/components/network/util.cpp +13 -0
- esphome/components/nextion/base_component.py +2 -0
- esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +3 -4
- esphome/components/nextion/display.py +34 -22
- esphome/components/nextion/nextion.cpp +182 -143
- esphome/components/nextion/nextion.h +36 -0
- esphome/components/nextion/nextion_commands.cpp +3 -3
- esphome/components/nextion/nextion_upload_arduino.cpp +69 -69
- esphome/components/nextion/nextion_upload_idf.cpp +79 -80
- esphome/components/nextion/sensor/nextion_sensor.cpp +5 -5
- esphome/components/nextion/switch/nextion_switch.cpp +2 -2
- esphome/components/nextion/text_sensor/nextion_textsensor.cpp +3 -3
- esphome/components/nfc/nci_message.h +1 -1
- esphome/components/nfc/ndef_record.h +1 -1
- esphome/components/nfc/ndef_record_text.h +1 -1
- esphome/components/nfc/ndef_record_uri.h +1 -1
- esphome/components/nfc/nfc.h +1 -1
- esphome/components/nfc/nfc_tag.h +1 -1
- esphome/components/noblex/noblex.cpp +1 -1
- esphome/components/npi19/npi19.cpp +5 -7
- esphome/components/number/__init__.py +11 -0
- esphome/components/number/number.cpp +1 -1
- esphome/components/number/number.h +0 -4
- esphome/components/online_image/__init__.py +13 -1
- esphome/components/online_image/online_image.cpp +26 -4
- esphome/components/online_image/online_image.h +21 -4
- esphome/components/opentherm/generate.py +3 -3
- esphome/components/opentherm/hub.cpp +11 -7
- esphome/components/opentherm/number/number.cpp +5 -3
- esphome/components/opentherm/opentherm.h +1 -1
- esphome/components/opentherm/schema.py +13 -13
- esphome/components/opentherm/validate.py +1 -1
- esphome/components/openthread/__init__.py +146 -0
- esphome/components/openthread/const.py +10 -0
- esphome/components/openthread/openthread.cpp +206 -0
- esphome/components/openthread/openthread.h +68 -0
- esphome/components/openthread/openthread_esp.cpp +164 -0
- esphome/components/openthread/tlv.py +58 -0
- esphome/components/openthread_info/__init__.py +0 -0
- esphome/components/openthread_info/openthread_info_text_sensor.cpp +24 -0
- esphome/components/openthread_info/openthread_info_text_sensor.h +218 -0
- esphome/components/openthread_info/text_sensor.py +105 -0
- esphome/components/output/float_output.cpp +1 -1
- esphome/components/output/switch/output_switch.cpp +1 -1
- esphome/components/packet_transport/packet_transport.cpp +6 -4
- esphome/components/pca6416a/pca6416a.cpp +2 -2
- esphome/components/pca9554/pca9554.cpp +6 -4
- esphome/components/pca9685/pca9685_output.cpp +12 -8
- esphome/components/pcd8544/pcd_8544.cpp +1 -1
- esphome/components/pcf85063/pcf85063.cpp +2 -2
- esphome/components/pcf8563/pcf8563.cpp +2 -2
- esphome/components/pcf8574/pcf8574.cpp +2 -2
- esphome/components/pid/pid_climate.cpp +8 -5
- esphome/components/pid/pid_climate.h +1 -1
- esphome/components/pid/sensor/pid_climate_sensor.cpp +1 -1
- esphome/components/pipsolar/output/pipsolar_output.cpp +1 -1
- esphome/components/pipsolar/pipsolar.cpp +3 -3
- esphome/components/pm1006/pm1006.cpp +3 -7
- esphome/components/pm1006/pm1006.h +4 -1
- esphome/components/pm2005/pm2005.cpp +12 -13
- esphome/components/pm2005/sensor.py +1 -1
- esphome/components/pmsa003i/pmsa003i.cpp +2 -2
- esphome/components/pmsx003/pmsx003.cpp +0 -4
- esphome/components/pmsx003/pmsx003.h +5 -2
- esphome/components/pmwcs3/pmwcs3.cpp +9 -13
- esphome/components/pmwcs3/pmwcs3.h +0 -1
- esphome/components/pn532/pn532.cpp +7 -7
- esphome/components/pn532/pn532.h +1 -1
- esphome/components/pn532_spi/pn532_spi.cpp +1 -1
- esphome/components/pn7150/pn7150.cpp +4 -4
- esphome/components/pn7160/pn7160.cpp +4 -4
- esphome/components/power_supply/power_supply.cpp +6 -4
- esphome/components/power_supply/power_supply.h +1 -1
- esphome/components/prometheus/__init__.py +0 -1
- esphome/components/psram/__init__.py +59 -35
- esphome/components/pulse_counter/pulse_counter_sensor.cpp +7 -4
- esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +8 -5
- esphome/components/pylontech/pylontech.cpp +2 -2
- esphome/components/pylontech/sensor/pylontech_sensor.cpp +4 -2
- esphome/components/pylontech/text_sensor/pylontech_text_sensor.cpp +4 -2
- esphome/components/pzemac/pzemac.cpp +4 -2
- esphome/components/pzemdc/pzemdc.cpp +4 -2
- esphome/components/qmc5883l/qmc5883l.cpp +2 -2
- esphome/components/qmp6988/qmp6988.cpp +2 -2
- esphome/components/qmp6988/qmp6988.h +1 -1
- esphome/components/qr_code/qr_code.cpp +5 -3
- esphome/components/qspi_dbi/qspi_dbi.cpp +1 -1
- esphome/components/qwiic_pir/qwiic_pir.cpp +26 -28
- esphome/components/rc522/rc522.cpp +5 -5
- esphome/components/rdm6300/rdm6300.cpp +1 -0
- esphome/components/remote_receiver/__init__.py +10 -2
- esphome/components/remote_receiver/remote_receiver_esp32.cpp +22 -12
- esphome/components/remote_receiver/remote_receiver_esp8266.cpp +10 -7
- esphome/components/remote_receiver/remote_receiver_libretiny.cpp +10 -7
- esphome/components/remote_transmitter/__init__.py +10 -2
- esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +10 -6
- esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +5 -3
- esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +5 -3
- esphome/components/resistance/resistance_sensor.cpp +6 -3
- esphome/components/restart/button/restart_button.cpp +1 -1
- esphome/components/restart/switch/restart_switch.cpp +1 -1
- esphome/components/rotary_encoder/rotary_encoder.cpp +2 -2
- esphome/components/rp2040/__init__.py +7 -0
- esphome/components/rp2040/core.cpp +8 -1
- esphome/components/rp2040/preferences.cpp +3 -3
- esphome/components/rp2040_pio_led_strip/led_strip.cpp +11 -8
- esphome/components/rp2040_pio_led_strip/light.py +1 -1
- esphome/components/rp2040_pwm/rp2040_pwm.cpp +1 -1
- esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +1 -1
- esphome/components/rtttl/rtttl.cpp +11 -9
- esphome/components/safe_mode/button/safe_mode_button.cpp +1 -1
- esphome/components/safe_mode/safe_mode.cpp +9 -8
- esphome/components/safe_mode/switch/safe_mode_switch.cpp +1 -1
- esphome/components/scd30/scd30.cpp +11 -8
- esphome/components/scd4x/scd4x.cpp +12 -8
- esphome/components/sdl/display.py +40 -0
- esphome/components/sdl/sdl_esphome.cpp +2 -2
- esphome/components/sdl/sdl_esphome.h +8 -0
- esphome/components/sdm_meter/sdm_meter.cpp +5 -2
- esphome/components/sdp3x/sdp3x.cpp +11 -11
- esphome/components/sds011/sds011.cpp +5 -6
- esphome/components/sds011/sds011.h +4 -1
- esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp +3 -2
- esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +1 -0
- esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +3 -2
- esphome/components/selec_meter/selec_meter.cpp +5 -2
- esphome/components/select/__init__.py +1 -0
- esphome/components/select/select.cpp +1 -1
- esphome/components/select/select.h +0 -4
- esphome/components/sen0321/sen0321.cpp +2 -2
- esphome/components/sen21231/sen21231.cpp +1 -1
- esphome/components/sen5x/sen5x.cpp +10 -7
- esphome/components/sensirion_common/i2c_sensirion.cpp +2 -1
- esphome/components/sensirion_common/i2c_sensirion.h +1 -0
- esphome/components/sensor/__init__.py +11 -1
- esphome/components/sensor/filter.h +1 -1
- esphome/components/sensor/sensor.cpp +8 -4
- esphome/components/sensor/sensor.h +12 -11
- esphome/components/servo/servo.cpp +12 -9
- esphome/components/servo/servo.h +1 -1
- esphome/components/sfa30/sfa30.cpp +1 -1
- esphome/components/sgp30/sgp30.cpp +10 -8
- esphome/components/sgp4x/sgp4x.cpp +49 -61
- esphome/components/sgp4x/sgp4x.h +0 -1
- esphome/components/shelly_dimmer/shelly_dimmer.cpp +11 -7
- esphome/components/sht3xd/sht3xd.cpp +1 -1
- esphome/components/sht4x/sht4x.cpp +2 -2
- esphome/components/shtcx/shtcx.cpp +13 -16
- esphome/components/shutdown/button/shutdown_button.cpp +1 -1
- esphome/components/shutdown/switch/shutdown_switch.cpp +1 -1
- esphome/components/sim800l/sim800l.cpp +2 -2
- esphome/components/slow_pwm/slow_pwm_output.cpp +4 -2
- esphome/components/sm16716/sm16716.cpp +1 -1
- esphome/components/sm2135/sm2135.cpp +1 -1
- esphome/components/sm2235/sm2235.cpp +5 -3
- esphome/components/sm2335/sm2335.cpp +5 -3
- esphome/components/sml/sml.cpp +1 -1
- esphome/components/sn74hc165/sn74hc165.cpp +1 -2
- esphome/components/sn74hc595/sn74hc595.cpp +1 -2
- esphome/components/sntp/sntp_component.cpp +1 -1
- esphome/components/socket/__init__.py +2 -0
- esphome/components/socket/bsd_sockets_impl.cpp +51 -7
- esphome/components/socket/lwip_raw_tcp_impl.cpp +5 -0
- esphome/components/socket/lwip_sockets_impl.cpp +51 -7
- esphome/components/socket/socket.cpp +31 -0
- esphome/components/socket/socket.h +27 -1
- esphome/components/sonoff_d1/sonoff_d1.cpp +7 -4
- esphome/components/sonoff_d1/sonoff_d1.h +2 -2
- esphome/components/sound_level/sound_level.cpp +4 -2
- esphome/components/speaker/media_player/__init__.py +3 -0
- esphome/components/speaker/media_player/speaker_media_player.cpp +1 -3
- esphome/components/speaker/media_player/speaker_media_player.h +6 -0
- esphome/components/spi/__init__.py +10 -2
- esphome/components/spi/spi.cpp +4 -4
- esphome/components/spi/spi_arduino.cpp +22 -9
- esphome/components/spi_device/spi_device.cpp +1 -2
- esphome/components/sprinkler/sprinkler.cpp +9 -6
- esphome/components/sps30/sps30.cpp +16 -15
- esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
- esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +11 -8
- esphome/components/ssd1306_spi/ssd1306_spi.cpp +10 -7
- esphome/components/ssd1322_base/ssd1322_base.cpp +1 -1
- esphome/components/ssd1322_spi/ssd1322_spi.cpp +1 -1
- esphome/components/ssd1325_base/ssd1325_base.cpp +1 -1
- esphome/components/ssd1325_spi/ssd1325_spi.cpp +1 -1
- esphome/components/ssd1327_base/ssd1327_base.cpp +1 -1
- esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +2 -2
- esphome/components/ssd1327_spi/ssd1327_spi.cpp +1 -1
- esphome/components/ssd1331_base/ssd1331_base.cpp +1 -1
- esphome/components/ssd1331_spi/ssd1331_spi.cpp +1 -1
- esphome/components/ssd1351_base/ssd1351_base.cpp +1 -1
- esphome/components/ssd1351_spi/ssd1351_spi.cpp +1 -1
- esphome/components/st7567_base/st7567_base.cpp +3 -3
- esphome/components/st7567_i2c/st7567_i2c.cpp +7 -5
- esphome/components/st7567_spi/st7567_spi.cpp +1 -1
- esphome/components/st7701s/st7701s.cpp +4 -2
- esphome/components/st7735/st7735.cpp +3 -3
- esphome/components/st7789v/st7789v.cpp +10 -7
- esphome/components/st7920/st7920.cpp +6 -4
- esphome/components/statsd/statsd.cpp +9 -5
- esphome/components/status_led/light/status_led_light.cpp +3 -3
- esphome/components/status_led/light/status_led_light.h +1 -1
- esphome/components/status_led/status_led.cpp +1 -1
- esphome/components/stepper/stepper.h +5 -3
- esphome/components/sts3x/sts3x.cpp +2 -2
- esphome/components/switch/__init__.py +1 -0
- esphome/components/switch/switch.cpp +18 -12
- esphome/components/switch/switch.h +14 -8
- esphome/components/sx1509/__init__.py +53 -20
- esphome/components/sx1509/sx1509.cpp +29 -5
- esphome/components/sx1509/sx1509.h +9 -1
- esphome/components/t6615/t6615.cpp +1 -0
- esphome/components/tc74/tc74.cpp +1 -1
- esphome/components/tca9548a/tca9548a.cpp +1 -1
- esphome/components/tca9555/tca9555.cpp +2 -2
- esphome/components/tcs34725/tcs34725.cpp +4 -4
- esphome/components/tee501/tee501.cpp +3 -2
- esphome/components/tem3200/tem3200.cpp +5 -6
- esphome/components/template/alarm_control_panel/__init__.py +6 -0
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +43 -12
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +4 -1
- esphome/components/template/cover/template_cover.cpp +1 -1
- esphome/components/template/select/template_select.cpp +6 -4
- esphome/components/template/text/template_text.cpp +2 -3
- esphome/components/template/valve/template_valve.cpp +5 -3
- esphome/components/text/__init__.py +10 -11
- esphome/components/text/text.cpp +1 -1
- esphome/components/text/text.h +0 -4
- esphome/components/text_sensor/__init__.py +1 -0
- esphome/components/text_sensor/text_sensor.cpp +8 -4
- esphome/components/text_sensor/text_sensor.h +6 -6
- esphome/components/thermostat/thermostat_climate.cpp +67 -43
- esphome/components/time/__init__.py +1 -2
- esphome/components/time_based/time_based_cover.cpp +4 -2
- esphome/components/tlc59208f/tlc59208f_output.cpp +8 -6
- esphome/components/tm1621/tm1621.cpp +3 -3
- esphome/components/tm1637/tm1637.cpp +9 -7
- esphome/components/tm1638/tm1638.cpp +7 -5
- esphome/components/tm1651/tm1651.cpp +2 -2
- esphome/components/tmp102/tmp102.cpp +1 -3
- esphome/components/tmp102/tmp102.h +0 -3
- esphome/components/tmp1075/tmp1075.cpp +12 -9
- esphome/components/tmp117/tmp117.cpp +2 -2
- esphome/components/tof10120/tof10120_sensor.cpp +2 -2
- esphome/components/tormatic/tormatic_cover.cpp +4 -2
- esphome/components/tsl2561/tsl2561.cpp +7 -5
- esphome/components/tsl2591/tsl2591.cpp +25 -27
- esphome/components/tt21100/touchscreen/tt21100.cpp +1 -1
- esphome/components/ttp229_bsf/ttp229_bsf.cpp +1 -1
- esphome/components/ttp229_lsf/ttp229_lsf.cpp +2 -2
- esphome/components/tuya/select/tuya_select.cpp +5 -3
- esphome/components/tx20/tx20.cpp +3 -3
- esphome/components/uart/__init__.py +4 -5
- esphome/components/uart/button/uart_button.cpp +1 -1
- esphome/components/uart/switch/uart_switch.cpp +2 -2
- esphome/components/uart/uart.cpp +2 -2
- esphome/components/uart/uart_component_esp32_arduino.cpp +8 -6
- esphome/components/uart/uart_component_esp8266.cpp +9 -7
- esphome/components/uart/uart_component_esp_idf.cpp +9 -7
- esphome/components/uart/uart_component_host.cpp +11 -8
- esphome/components/uart/uart_component_libretiny.cpp +8 -6
- esphome/components/uart/uart_component_rp2040.cpp +8 -6
- esphome/components/udp/udp_component.cpp +9 -5
- esphome/components/ufire_ec/ufire_ec.cpp +5 -3
- esphome/components/ufire_ise/ufire_ise.cpp +1 -1
- esphome/components/ultrasonic/ultrasonic_sensor.cpp +5 -3
- esphome/components/update/__init__.py +1 -0
- esphome/components/update/update_entity.cpp +1 -1
- esphome/components/update/update_entity.h +0 -3
- esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +1 -1
- esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +4 -2
- esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
- esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp +1 -1
- esphome/components/usb_host/__init__.py +64 -0
- esphome/components/usb_host/usb_host.h +116 -0
- esphome/components/usb_host/usb_host_client.cpp +394 -0
- esphome/components/usb_host/usb_host_component.cpp +35 -0
- esphome/components/usb_uart/__init__.py +134 -0
- esphome/components/usb_uart/ch34x.cpp +80 -0
- esphome/components/usb_uart/cp210x.cpp +126 -0
- esphome/components/usb_uart/usb_uart.cpp +328 -0
- esphome/components/usb_uart/usb_uart.h +151 -0
- esphome/components/valve/__init__.py +1 -0
- esphome/components/veml3235/veml3235.cpp +13 -9
- esphome/components/veml7700/veml7700.cpp +10 -6
- esphome/components/voice_assistant/voice_assistant.cpp +7 -7
- esphome/components/wake_on_lan/wake_on_lan.cpp +1 -1
- esphome/components/waveshare_epaper/waveshare_epaper.cpp +1 -1
- esphome/components/web_server/server_index_v2.h +632 -630
- esphome/components/web_server/server_index_v3.h +411 -409
- esphome/components/web_server/web_server.cpp +5 -3
- esphome/components/web_server_base/web_server_base.cpp +1 -1
- esphome/components/web_server_idf/__init__.py +0 -2
- esphome/components/web_server_idf/utils.cpp +1 -1
- esphome/components/web_server_idf/web_server_idf.cpp +7 -3
- esphome/components/web_server_idf/web_server_idf.h +7 -0
- esphome/components/weikai/__init__.py +2 -0
- esphome/components/weikai/weikai.cpp +24 -22
- esphome/components/weikai_i2c/weikai_i2c.cpp +14 -9
- esphome/components/weikai_spi/weikai_spi.cpp +11 -6
- esphome/components/wiegand/wiegand.cpp +1 -1
- esphome/components/wifi/wifi_component.cpp +50 -37
- esphome/components/wifi/wifi_component.h +7 -4
- esphome/components/wifi/wifi_component_esp32_arduino.cpp +2 -2
- esphome/components/wifi/wifi_component_esp8266.cpp +3 -3
- esphome/components/wifi/wifi_component_libretiny.cpp +4 -4
- esphome/components/wireguard/wireguard.cpp +21 -21
- esphome/components/wl_134/text_sensor.py +1 -2
- esphome/components/wled/wled_light_effect.cpp +1 -1
- esphome/components/x9c/x9c.cpp +5 -3
- esphome/components/xgzp68xx/xgzp68xx.cpp +8 -6
- esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp +4 -2
- esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp +4 -2
- esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp +4 -2
- esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp +1 -1
- esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp +4 -2
- esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp +4 -2
- esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp +4 -2
- esphome/components/xl9535/xl9535.cpp +2 -2
- esphome/components/xpt2046/touchscreen/xpt2046.cpp +12 -11
- esphome/components/xpt2046/touchscreen/xpt2046.h +2 -2
- esphome/config.py +3 -2
- esphome/config_validation.py +46 -17
- esphome/const.py +10 -1
- esphome/core/__init__.py +37 -18
- esphome/core/application.cpp +197 -8
- esphome/core/application.h +116 -6
- esphome/core/component.cpp +36 -15
- esphome/core/component.h +43 -13
- esphome/core/config.py +12 -0
- esphome/core/defines.h +10 -2
- esphome/core/entity_base.cpp +4 -16
- esphome/core/entity_base.h +27 -13
- esphome/core/helpers.cpp +1 -1
- esphome/core/helpers.h +5 -5
- esphome/core/log.h +2 -0
- esphome/core/log_const_en.h +4 -0
- esphome/core/scheduler.cpp +2 -2
- esphome/coroutine.py +3 -4
- esphome/cpp_generator.py +32 -32
- esphome/dashboard/core.py +2 -2
- esphome/dashboard/web_server.py +2 -2
- esphome/git.py +4 -4
- esphome/helpers.py +5 -6
- esphome/loader.py +8 -7
- esphome/log.py +7 -1
- esphome/platformio_api.py +2 -3
- esphome/storage_json.py +13 -5
- esphome/types.py +12 -13
- esphome/util.py +1 -2
- esphome/wizard.py +0 -16
- esphome/writer.py +5 -3
- esphome/yaml_util.py +6 -1
- esphome/zeroconf.py +1 -1
- {esphome-2025.5.2.dist-info → esphome-2025.6.0.dist-info}/METADATA +12 -11
- {esphome-2025.5.2.dist-info → esphome-2025.6.0.dist-info}/RECORD +750 -696
- {esphome-2025.5.2.dist-info → esphome-2025.6.0.dist-info}/WHEEL +1 -1
- {esphome-2025.5.2.dist-info → esphome-2025.6.0.dist-info}/entry_points.txt +0 -0
- {esphome-2025.5.2.dist-info → esphome-2025.6.0.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.5.2.dist-info → esphome-2025.6.0.dist-info}/top_level.txt +0 -0
@@ -1,26 +1,19 @@
|
|
1
1
|
#include "api_frame_helper.h"
|
2
2
|
#ifdef USE_API
|
3
|
-
#include "esphome/core/
|
3
|
+
#include "esphome/core/application.h"
|
4
4
|
#include "esphome/core/hal.h"
|
5
5
|
#include "esphome/core/helpers.h"
|
6
|
-
#include "esphome/core/
|
6
|
+
#include "esphome/core/log.h"
|
7
7
|
#include "proto.h"
|
8
8
|
#include "api_pb2_size.h"
|
9
9
|
#include <cstring>
|
10
|
+
#include <cinttypes>
|
10
11
|
|
11
12
|
namespace esphome {
|
12
13
|
namespace api {
|
13
14
|
|
14
15
|
static const char *const TAG = "api.socket";
|
15
16
|
|
16
|
-
/// Is the given return value (from write syscalls) a wouldblock error?
|
17
|
-
bool is_would_block(ssize_t ret) {
|
18
|
-
if (ret == -1) {
|
19
|
-
return errno == EWOULDBLOCK || errno == EAGAIN;
|
20
|
-
}
|
21
|
-
return ret == 0;
|
22
|
-
}
|
23
|
-
|
24
17
|
const char *api_error_to_str(APIError err) {
|
25
18
|
// not using switch to ensure compiler doesn't try to build a big table out of it
|
26
19
|
if (err == APIError::OK) {
|
@@ -73,92 +66,154 @@ const char *api_error_to_str(APIError err) {
|
|
73
66
|
return "UNKNOWN";
|
74
67
|
}
|
75
68
|
|
76
|
-
//
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
69
|
+
// Helper method to buffer data from IOVs
|
70
|
+
void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len) {
|
71
|
+
SendBuffer buffer;
|
72
|
+
buffer.data.reserve(total_write_len);
|
73
|
+
for (int i = 0; i < iovcnt; i++) {
|
74
|
+
const uint8_t *data = reinterpret_cast<uint8_t *>(iov[i].iov_base);
|
75
|
+
buffer.data.insert(buffer.data.end(), data, data + iov[i].iov_len);
|
76
|
+
}
|
77
|
+
this->tx_buf_.push_back(std::move(buffer));
|
78
|
+
}
|
79
|
+
|
80
|
+
// This method writes data to socket or buffers it
|
81
|
+
APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
82
82
|
// Returns APIError::OK if successful (or would block, but data has been buffered)
|
83
|
-
// Returns APIError::SOCKET_WRITE_FAILED if socket write failed, and sets state to
|
83
|
+
// Returns APIError::SOCKET_WRITE_FAILED if socket write failed, and sets state to FAILED
|
84
84
|
|
85
85
|
if (iovcnt == 0)
|
86
86
|
return APIError::OK; // Nothing to do, success
|
87
87
|
|
88
|
-
|
88
|
+
uint16_t total_write_len = 0;
|
89
89
|
for (int i = 0; i < iovcnt; i++) {
|
90
90
|
#ifdef HELPER_LOG_PACKETS
|
91
91
|
ESP_LOGVV(TAG, "Sending raw: %s",
|
92
92
|
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
93
93
|
#endif
|
94
|
-
total_write_len += iov[i].iov_len;
|
94
|
+
total_write_len += static_cast<uint16_t>(iov[i].iov_len);
|
95
95
|
}
|
96
96
|
|
97
|
-
if
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
} else if (sent == -1) {
|
104
|
-
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", info.c_str(), errno);
|
105
|
-
state = failed_state;
|
106
|
-
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
|
107
|
-
}
|
108
|
-
// TODO: inefficient if multiple packets in txbuf
|
109
|
-
// replace with deque of buffers
|
110
|
-
tx_buf.erase(tx_buf.begin(), tx_buf.begin() + sent);
|
97
|
+
// Try to send any existing buffered data first if there is any
|
98
|
+
if (!this->tx_buf_.empty()) {
|
99
|
+
APIError send_result = try_send_tx_buf_();
|
100
|
+
// If real error occurred (not just WOULD_BLOCK), return it
|
101
|
+
if (send_result != APIError::OK && send_result != APIError::WOULD_BLOCK) {
|
102
|
+
return send_result;
|
111
103
|
}
|
112
|
-
}
|
113
104
|
|
114
|
-
|
115
|
-
//
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
tx_buf.insert(tx_buf.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
|
120
|
-
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
105
|
+
// If there is still data in the buffer, we can't send, buffer
|
106
|
+
// the new data and return
|
107
|
+
if (!this->tx_buf_.empty()) {
|
108
|
+
this->buffer_data_from_iov_(iov, iovcnt, total_write_len);
|
109
|
+
return APIError::OK; // Success, data buffered
|
121
110
|
}
|
122
|
-
return APIError::OK; // Success, data buffered
|
123
111
|
}
|
124
112
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
113
|
+
// Try to send directly if no buffered data
|
114
|
+
ssize_t sent = this->socket_->writev(iov, iovcnt);
|
115
|
+
|
116
|
+
if (sent == -1) {
|
117
|
+
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
118
|
+
// Socket would block, buffer the data
|
119
|
+
this->buffer_data_from_iov_(iov, iovcnt, total_write_len);
|
120
|
+
return APIError::OK; // Success, data buffered
|
133
121
|
}
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", info.c_str(), errno);
|
138
|
-
state = failed_state;
|
122
|
+
// Socket error
|
123
|
+
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
|
124
|
+
this->state_ = State::FAILED;
|
139
125
|
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
|
140
|
-
} else if ((
|
141
|
-
//
|
142
|
-
|
143
|
-
|
144
|
-
|
126
|
+
} else if (static_cast<uint16_t>(sent) < total_write_len) {
|
127
|
+
// Partially sent, buffer the remaining data
|
128
|
+
SendBuffer buffer;
|
129
|
+
uint16_t to_consume = static_cast<uint16_t>(sent);
|
130
|
+
uint16_t remaining = total_write_len - static_cast<uint16_t>(sent);
|
131
|
+
|
132
|
+
buffer.data.reserve(remaining);
|
145
133
|
|
146
|
-
size_t to_consume = sent;
|
147
134
|
for (int i = 0; i < iovcnt; i++) {
|
148
135
|
if (to_consume >= iov[i].iov_len) {
|
149
|
-
|
136
|
+
// This segment was fully sent
|
137
|
+
to_consume -= static_cast<uint16_t>(iov[i].iov_len);
|
150
138
|
} else {
|
151
|
-
|
152
|
-
|
139
|
+
// This segment was partially sent or not sent at all
|
140
|
+
const uint8_t *data = reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume;
|
141
|
+
uint16_t len = static_cast<uint16_t>(iov[i].iov_len) - to_consume;
|
142
|
+
buffer.data.insert(buffer.data.end(), data, data + len);
|
153
143
|
to_consume = 0;
|
154
144
|
}
|
155
145
|
}
|
156
|
-
|
146
|
+
|
147
|
+
this->tx_buf_.push_back(std::move(buffer));
|
157
148
|
}
|
158
|
-
|
149
|
+
|
150
|
+
return APIError::OK; // Success, all data sent or buffered
|
159
151
|
}
|
160
152
|
|
161
|
-
|
153
|
+
// Common implementation for trying to send buffered data
|
154
|
+
// IMPORTANT: Caller MUST ensure tx_buf_ is not empty before calling this method
|
155
|
+
APIError APIFrameHelper::try_send_tx_buf_() {
|
156
|
+
// Try to send from tx_buf - we assume it's not empty as it's the caller's responsibility to check
|
157
|
+
bool tx_buf_empty = false;
|
158
|
+
while (!tx_buf_empty) {
|
159
|
+
// Get the first buffer in the queue
|
160
|
+
SendBuffer &front_buffer = this->tx_buf_.front();
|
161
|
+
|
162
|
+
// Try to send the remaining data in this buffer
|
163
|
+
ssize_t sent = this->socket_->write(front_buffer.current_data(), front_buffer.remaining());
|
164
|
+
|
165
|
+
if (sent == -1) {
|
166
|
+
if (errno != EWOULDBLOCK && errno != EAGAIN) {
|
167
|
+
// Real socket error (not just would block)
|
168
|
+
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
|
169
|
+
this->state_ = State::FAILED;
|
170
|
+
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
|
171
|
+
}
|
172
|
+
// Socket would block, we'll try again later
|
173
|
+
return APIError::WOULD_BLOCK;
|
174
|
+
} else if (sent == 0) {
|
175
|
+
// Nothing sent but not an error
|
176
|
+
return APIError::WOULD_BLOCK;
|
177
|
+
} else if (static_cast<uint16_t>(sent) < front_buffer.remaining()) {
|
178
|
+
// Partially sent, update offset
|
179
|
+
// Cast to ensure no overflow issues with uint16_t
|
180
|
+
front_buffer.offset += static_cast<uint16_t>(sent);
|
181
|
+
return APIError::WOULD_BLOCK; // Stop processing more buffers if we couldn't send a complete buffer
|
182
|
+
} else {
|
183
|
+
// Buffer completely sent, remove it from the queue
|
184
|
+
this->tx_buf_.pop_front();
|
185
|
+
// Update empty status for the loop condition
|
186
|
+
tx_buf_empty = this->tx_buf_.empty();
|
187
|
+
// Continue loop to try sending the next buffer
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
return APIError::OK; // All buffers sent successfully
|
192
|
+
}
|
193
|
+
|
194
|
+
APIError APIFrameHelper::init_common_() {
|
195
|
+
if (state_ != State::INITIALIZE || this->socket_ == nullptr) {
|
196
|
+
ESP_LOGVV(TAG, "%s: Bad state for init %d", this->info_.c_str(), (int) state_);
|
197
|
+
return APIError::BAD_STATE;
|
198
|
+
}
|
199
|
+
int err = this->socket_->setblocking(false);
|
200
|
+
if (err != 0) {
|
201
|
+
state_ = State::FAILED;
|
202
|
+
ESP_LOGVV(TAG, "%s: Setting nonblocking failed with errno %d", this->info_.c_str(), errno);
|
203
|
+
return APIError::TCP_NONBLOCKING_FAILED;
|
204
|
+
}
|
205
|
+
|
206
|
+
int enable = 1;
|
207
|
+
err = this->socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
208
|
+
if (err != 0) {
|
209
|
+
state_ = State::FAILED;
|
210
|
+
ESP_LOGVV(TAG, "%s: Setting nodelay failed with errno %d", this->info_.c_str(), errno);
|
211
|
+
return APIError::TCP_NODELAY_FAILED;
|
212
|
+
}
|
213
|
+
return APIError::OK;
|
214
|
+
}
|
215
|
+
|
216
|
+
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->info_.c_str(), ##__VA_ARGS__)
|
162
217
|
// uncomment to log raw packets
|
163
218
|
//#define HELPER_LOG_PACKETS
|
164
219
|
|
@@ -206,23 +261,9 @@ std::string noise_err_to_str(int err) {
|
|
206
261
|
|
207
262
|
/// Initialize the frame helper, returns OK if successful.
|
208
263
|
APIError APINoiseFrameHelper::init() {
|
209
|
-
|
210
|
-
|
211
|
-
return
|
212
|
-
}
|
213
|
-
int err = socket_->setblocking(false);
|
214
|
-
if (err != 0) {
|
215
|
-
state_ = State::FAILED;
|
216
|
-
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
217
|
-
return APIError::TCP_NONBLOCKING_FAILED;
|
218
|
-
}
|
219
|
-
|
220
|
-
int enable = 1;
|
221
|
-
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
222
|
-
if (err != 0) {
|
223
|
-
state_ = State::FAILED;
|
224
|
-
HELPER_LOG("Setting nodelay failed with errno %d", errno);
|
225
|
-
return APIError::TCP_NODELAY_FAILED;
|
264
|
+
APIError err = init_common_();
|
265
|
+
if (err != APIError::OK) {
|
266
|
+
return err;
|
226
267
|
}
|
227
268
|
|
228
269
|
// init prologue
|
@@ -234,17 +275,16 @@ APIError APINoiseFrameHelper::init() {
|
|
234
275
|
/// Run through handshake messages (if in that phase)
|
235
276
|
APIError APINoiseFrameHelper::loop() {
|
236
277
|
APIError err = state_action_();
|
237
|
-
if (err
|
238
|
-
return APIError::OK;
|
239
|
-
if (err != APIError::OK)
|
278
|
+
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
240
279
|
return err;
|
241
|
-
|
280
|
+
}
|
281
|
+
if (!this->tx_buf_.empty()) {
|
242
282
|
err = try_send_tx_buf_();
|
243
|
-
if (err != APIError::OK) {
|
283
|
+
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
244
284
|
return err;
|
245
285
|
}
|
246
286
|
}
|
247
|
-
return APIError::OK;
|
287
|
+
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
|
248
288
|
}
|
249
289
|
|
250
290
|
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
@@ -270,8 +310,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|
270
310
|
// read header
|
271
311
|
if (rx_header_buf_len_ < 3) {
|
272
312
|
// no header information yet
|
273
|
-
|
274
|
-
ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
|
313
|
+
uint8_t to_read = 3 - rx_header_buf_len_;
|
314
|
+
ssize_t received = this->socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
|
275
315
|
if (received == -1) {
|
276
316
|
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
277
317
|
return APIError::WOULD_BLOCK;
|
@@ -284,8 +324,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|
284
324
|
HELPER_LOG("Connection closed");
|
285
325
|
return APIError::CONNECTION_CLOSED;
|
286
326
|
}
|
287
|
-
rx_header_buf_len_ += received;
|
288
|
-
if ((
|
327
|
+
rx_header_buf_len_ += static_cast<uint8_t>(received);
|
328
|
+
if (static_cast<uint8_t>(received) != to_read) {
|
289
329
|
// not a full read
|
290
330
|
return APIError::WOULD_BLOCK;
|
291
331
|
}
|
@@ -317,8 +357,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|
317
357
|
|
318
358
|
if (rx_buf_len_ < msg_size) {
|
319
359
|
// more data to read
|
320
|
-
|
321
|
-
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
360
|
+
uint16_t to_read = msg_size - rx_buf_len_;
|
361
|
+
ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
322
362
|
if (received == -1) {
|
323
363
|
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
324
364
|
return APIError::WOULD_BLOCK;
|
@@ -331,8 +371,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|
331
371
|
HELPER_LOG("Connection closed");
|
332
372
|
return APIError::CONNECTION_CLOSED;
|
333
373
|
}
|
334
|
-
rx_buf_len_ += received;
|
335
|
-
if ((
|
374
|
+
rx_buf_len_ += static_cast<uint16_t>(received);
|
375
|
+
if (static_cast<uint16_t>(received) != to_read) {
|
336
376
|
// not all read
|
337
377
|
return APIError::WOULD_BLOCK;
|
338
378
|
}
|
@@ -381,6 +421,8 @@ APIError APINoiseFrameHelper::state_action_() {
|
|
381
421
|
if (aerr != APIError::OK)
|
382
422
|
return aerr;
|
383
423
|
// ignore contents, may be used in future for flags
|
424
|
+
// Reserve space for: existing prologue + 2 size bytes + frame data
|
425
|
+
prologue_.reserve(prologue_.size() + 2 + frame.msg.size());
|
384
426
|
prologue_.push_back((uint8_t) (frame.msg.size() >> 8));
|
385
427
|
prologue_.push_back((uint8_t) frame.msg.size());
|
386
428
|
prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end());
|
@@ -389,16 +431,20 @@ APIError APINoiseFrameHelper::state_action_() {
|
|
389
431
|
}
|
390
432
|
if (state_ == State::SERVER_HELLO) {
|
391
433
|
// send server hello
|
434
|
+
const std::string &name = App.get_name();
|
435
|
+
const std::string &mac = get_mac_address();
|
436
|
+
|
392
437
|
std::vector<uint8_t> msg;
|
438
|
+
// Reserve space for: 1 byte proto + name + null + mac + null
|
439
|
+
msg.reserve(1 + name.size() + 1 + mac.size() + 1);
|
440
|
+
|
393
441
|
// chosen proto
|
394
442
|
msg.push_back(0x01);
|
395
443
|
|
396
444
|
// node name, terminated by null byte
|
397
|
-
const std::string &name = App.get_name();
|
398
445
|
const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
|
399
446
|
msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
|
400
447
|
// node mac, terminated by null byte
|
401
|
-
const std::string &mac = get_mac_address();
|
402
448
|
const uint8_t *mac_ptr = reinterpret_cast<const uint8_t *>(mac.c_str());
|
403
449
|
msg.insert(msg.end(), mac_ptr, mac_ptr + mac.size() + 1);
|
404
450
|
|
@@ -505,7 +551,6 @@ void APINoiseFrameHelper::send_explicit_handshake_reject_(const std::string &rea
|
|
505
551
|
write_frame_(data.data(), data.size());
|
506
552
|
state_ = orig_state;
|
507
553
|
}
|
508
|
-
|
509
554
|
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
510
555
|
int err;
|
511
556
|
APIError aerr;
|
@@ -533,7 +578,7 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|
533
578
|
return APIError::CIPHERSTATE_DECRYPT_FAILED;
|
534
579
|
}
|
535
580
|
|
536
|
-
|
581
|
+
uint16_t msg_size = mbuf.size;
|
537
582
|
uint8_t *msg_data = frame.msg.data();
|
538
583
|
if (msg_size < 4) {
|
539
584
|
state_ = State::FAILED;
|
@@ -559,11 +604,22 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|
559
604
|
buffer->type = type;
|
560
605
|
return APIError::OK;
|
561
606
|
}
|
562
|
-
bool APINoiseFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
|
563
607
|
APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
|
564
|
-
|
565
|
-
|
566
|
-
|
608
|
+
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
609
|
+
uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_);
|
610
|
+
|
611
|
+
// Resize to include MAC space (required for Noise encryption)
|
612
|
+
raw_buffer->resize(raw_buffer->size() + frame_footer_size_);
|
613
|
+
|
614
|
+
// Use write_protobuf_packets with a single packet
|
615
|
+
std::vector<PacketInfo> packets;
|
616
|
+
packets.emplace_back(type, 0, payload_len);
|
617
|
+
|
618
|
+
return write_protobuf_packets(buffer, packets);
|
619
|
+
}
|
620
|
+
|
621
|
+
APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) {
|
622
|
+
APIError aerr = state_action_();
|
567
623
|
if (aerr != APIError::OK) {
|
568
624
|
return aerr;
|
569
625
|
}
|
@@ -572,77 +628,67 @@ APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuf
|
|
572
628
|
return APIError::WOULD_BLOCK;
|
573
629
|
}
|
574
630
|
|
575
|
-
|
576
|
-
|
577
|
-
size_t payload_len = raw_buffer->size() - frame_header_padding_;
|
578
|
-
size_t padding = 0;
|
579
|
-
size_t msg_len = 4 + payload_len + padding;
|
580
|
-
|
581
|
-
// We need to resize to include MAC space, but we already reserved it in create_buffer
|
582
|
-
raw_buffer->resize(raw_buffer->size() + frame_footer_size_);
|
583
|
-
|
584
|
-
// Write the noise header in the padded area
|
585
|
-
// Buffer layout:
|
586
|
-
// [0] - 0x01 indicator byte
|
587
|
-
// [1-2] - Size of encrypted payload (filled after encryption)
|
588
|
-
// [3-4] - Message type (encrypted)
|
589
|
-
// [5-6] - Payload length (encrypted)
|
590
|
-
// [7...] - Actual payload data (encrypted)
|
591
|
-
uint8_t *buf_start = raw_buffer->data();
|
592
|
-
buf_start[0] = 0x01; // indicator
|
593
|
-
// buf_start[1], buf_start[2] to be set later after encryption
|
594
|
-
const uint8_t msg_offset = 3;
|
595
|
-
buf_start[msg_offset + 0] = (uint8_t) (type >> 8); // type high byte
|
596
|
-
buf_start[msg_offset + 1] = (uint8_t) type; // type low byte
|
597
|
-
buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len high byte
|
598
|
-
buf_start[msg_offset + 3] = (uint8_t) payload_len; // data_len low byte
|
599
|
-
// payload data is already in the buffer starting at position 7
|
600
|
-
|
601
|
-
NoiseBuffer mbuf;
|
602
|
-
noise_buffer_init(mbuf);
|
603
|
-
// The capacity parameter should be msg_len + frame_footer_size_ (MAC length) to allow space for encryption
|
604
|
-
noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_);
|
605
|
-
err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
|
606
|
-
if (err != 0) {
|
607
|
-
state_ = State::FAILED;
|
608
|
-
HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
|
609
|
-
return APIError::CIPHERSTATE_ENCRYPT_FAILED;
|
631
|
+
if (packets.empty()) {
|
632
|
+
return APIError::OK;
|
610
633
|
}
|
611
634
|
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
635
|
+
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
636
|
+
this->reusable_iovs_.clear();
|
637
|
+
this->reusable_iovs_.reserve(packets.size());
|
638
|
+
|
639
|
+
// We need to encrypt each packet in place
|
640
|
+
for (const auto &packet : packets) {
|
641
|
+
uint16_t type = packet.message_type;
|
642
|
+
uint16_t offset = packet.offset;
|
643
|
+
uint16_t payload_len = packet.payload_size;
|
644
|
+
uint16_t msg_len = 4 + payload_len; // type(2) + data_len(2) + payload
|
645
|
+
|
646
|
+
// The buffer already has padding at offset
|
647
|
+
uint8_t *buf_start = raw_buffer->data() + offset;
|
648
|
+
|
649
|
+
// Write noise header
|
650
|
+
buf_start[0] = 0x01; // indicator
|
651
|
+
// buf_start[1], buf_start[2] to be set after encryption
|
652
|
+
|
653
|
+
// Write message header (to be encrypted)
|
654
|
+
const uint8_t msg_offset = 3;
|
655
|
+
buf_start[msg_offset + 0] = (uint8_t) (type >> 8); // type high byte
|
656
|
+
buf_start[msg_offset + 1] = (uint8_t) type; // type low byte
|
657
|
+
buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len high byte
|
658
|
+
buf_start[msg_offset + 3] = (uint8_t) payload_len; // data_len low byte
|
659
|
+
// payload data is already in the buffer starting at offset + 7
|
660
|
+
|
661
|
+
// Make sure we have space for MAC
|
662
|
+
// The buffer should already have been sized appropriately
|
663
|
+
|
664
|
+
// Encrypt the message in place
|
665
|
+
NoiseBuffer mbuf;
|
666
|
+
noise_buffer_init(mbuf);
|
667
|
+
noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_);
|
668
|
+
|
669
|
+
int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
|
670
|
+
if (err != 0) {
|
632
671
|
state_ = State::FAILED;
|
633
|
-
HELPER_LOG("
|
634
|
-
return APIError::
|
635
|
-
} else if (sent == 0) {
|
636
|
-
break;
|
672
|
+
HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
|
673
|
+
return APIError::CIPHERSTATE_ENCRYPT_FAILED;
|
637
674
|
}
|
638
|
-
|
639
|
-
//
|
640
|
-
|
675
|
+
|
676
|
+
// Fill in the encrypted size
|
677
|
+
buf_start[1] = (uint8_t) (mbuf.size >> 8);
|
678
|
+
buf_start[2] = (uint8_t) mbuf.size;
|
679
|
+
|
680
|
+
// Add iovec for this encrypted packet
|
681
|
+
struct iovec iov;
|
682
|
+
iov.iov_base = buf_start;
|
683
|
+
iov.iov_len = 3 + mbuf.size; // indicator + size + encrypted data
|
684
|
+
this->reusable_iovs_.push_back(iov);
|
641
685
|
}
|
642
686
|
|
643
|
-
|
687
|
+
// Send all encrypted packets in one writev call
|
688
|
+
return this->write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
|
644
689
|
}
|
645
|
-
|
690
|
+
|
691
|
+
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {
|
646
692
|
uint8_t header[3];
|
647
693
|
header[0] = 0x01; // indicator
|
648
694
|
header[1] = (uint8_t) (len >> 8);
|
@@ -652,12 +698,12 @@ APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
|
|
652
698
|
iov[0].iov_base = header;
|
653
699
|
iov[0].iov_len = 3;
|
654
700
|
if (len == 0) {
|
655
|
-
return write_raw_(iov, 1);
|
701
|
+
return this->write_raw_(iov, 1);
|
656
702
|
}
|
657
703
|
iov[1].iov_base = const_cast<uint8_t *>(data);
|
658
704
|
iov[1].iov_len = len;
|
659
705
|
|
660
|
-
return write_raw_(iov, 2);
|
706
|
+
return this->write_raw_(iov, 2);
|
661
707
|
}
|
662
708
|
|
663
709
|
/** Initiate the data structures for the handshake.
|
@@ -752,58 +798,25 @@ APINoiseFrameHelper::~APINoiseFrameHelper() {
|
|
752
798
|
}
|
753
799
|
}
|
754
800
|
|
755
|
-
APIError APINoiseFrameHelper::close() {
|
756
|
-
state_ = State::CLOSED;
|
757
|
-
int err = socket_->close();
|
758
|
-
if (err == -1)
|
759
|
-
return APIError::CLOSE_FAILED;
|
760
|
-
return APIError::OK;
|
761
|
-
}
|
762
|
-
APIError APINoiseFrameHelper::shutdown(int how) {
|
763
|
-
int err = socket_->shutdown(how);
|
764
|
-
if (err == -1)
|
765
|
-
return APIError::SHUTDOWN_FAILED;
|
766
|
-
if (how == SHUT_RDWR) {
|
767
|
-
state_ = State::CLOSED;
|
768
|
-
}
|
769
|
-
return APIError::OK;
|
770
|
-
}
|
771
801
|
extern "C" {
|
772
802
|
// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
|
773
803
|
void noise_rand_bytes(void *output, size_t len) {
|
774
804
|
if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
|
775
|
-
ESP_LOGE(TAG, "
|
805
|
+
ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting");
|
776
806
|
arch_restart();
|
777
807
|
}
|
778
808
|
}
|
779
809
|
}
|
780
810
|
|
781
|
-
// Explicit template instantiation for Noise
|
782
|
-
template APIError APIFrameHelper::write_raw_<APINoiseFrameHelper::State>(
|
783
|
-
const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf_, const std::string &info,
|
784
|
-
APINoiseFrameHelper::State &state, APINoiseFrameHelper::State failed_state);
|
785
811
|
#endif // USE_API_NOISE
|
786
812
|
|
787
813
|
#ifdef USE_API_PLAINTEXT
|
788
814
|
|
789
815
|
/// Initialize the frame helper, returns OK if successful.
|
790
816
|
APIError APIPlaintextFrameHelper::init() {
|
791
|
-
|
792
|
-
|
793
|
-
return
|
794
|
-
}
|
795
|
-
int err = socket_->setblocking(false);
|
796
|
-
if (err != 0) {
|
797
|
-
state_ = State::FAILED;
|
798
|
-
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
799
|
-
return APIError::TCP_NONBLOCKING_FAILED;
|
800
|
-
}
|
801
|
-
int enable = 1;
|
802
|
-
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
803
|
-
if (err != 0) {
|
804
|
-
state_ = State::FAILED;
|
805
|
-
HELPER_LOG("Setting nodelay failed with errno %d", errno);
|
806
|
-
return APIError::TCP_NODELAY_FAILED;
|
817
|
+
APIError err = init_common_();
|
818
|
+
if (err != APIError::OK) {
|
819
|
+
return err;
|
807
820
|
}
|
808
821
|
|
809
822
|
state_ = State::DATA;
|
@@ -814,14 +827,13 @@ APIError APIPlaintextFrameHelper::loop() {
|
|
814
827
|
if (state_ != State::DATA) {
|
815
828
|
return APIError::BAD_STATE;
|
816
829
|
}
|
817
|
-
|
818
|
-
if (!tx_buf_.empty()) {
|
830
|
+
if (!this->tx_buf_.empty()) {
|
819
831
|
APIError err = try_send_tx_buf_();
|
820
|
-
if (err != APIError::OK) {
|
832
|
+
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
821
833
|
return err;
|
822
834
|
}
|
823
835
|
}
|
824
|
-
return APIError::OK;
|
836
|
+
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
|
825
837
|
}
|
826
838
|
|
827
839
|
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
@@ -841,12 +853,15 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|
841
853
|
|
842
854
|
// read header
|
843
855
|
while (!rx_header_parsed_) {
|
844
|
-
|
845
|
-
//
|
846
|
-
//
|
847
|
-
//
|
848
|
-
|
849
|
-
|
856
|
+
// Now that we know when the socket is ready, we can read up to 3 bytes
|
857
|
+
// into the rx_header_buf_ before we have to switch back to reading
|
858
|
+
// one byte at a time to ensure we don't read past the message and
|
859
|
+
// into the next one.
|
860
|
+
|
861
|
+
// Read directly into rx_header_buf_ at the current position
|
862
|
+
// Try to get to at least 3 bytes total (indicator + 2 varint bytes), then read one byte at a time
|
863
|
+
ssize_t received =
|
864
|
+
this->socket_->read(&rx_header_buf_[rx_header_buf_pos_], rx_header_buf_pos_ < 3 ? 3 - rx_header_buf_pos_ : 1);
|
850
865
|
if (received == -1) {
|
851
866
|
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
852
867
|
return APIError::WOULD_BLOCK;
|
@@ -860,64 +875,74 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|
860
875
|
return APIError::CONNECTION_CLOSED;
|
861
876
|
}
|
862
877
|
|
863
|
-
//
|
864
|
-
|
865
|
-
|
866
|
-
if (rx_header_buf_pos_ == 0) { // Case 1: First byte (indicator byte)
|
867
|
-
if (data != 0x00) {
|
878
|
+
// If this was the first read, validate the indicator byte
|
879
|
+
if (rx_header_buf_pos_ == 0 && received > 0) {
|
880
|
+
if (rx_header_buf_[0] != 0x00) {
|
868
881
|
state_ = State::FAILED;
|
869
|
-
HELPER_LOG("Bad indicator byte %u",
|
882
|
+
HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
|
870
883
|
return APIError::BAD_INDICATOR;
|
871
884
|
}
|
872
|
-
// We don't store the indicator byte, just increment position
|
873
|
-
rx_header_buf_pos_ = 1; // Set to 1 directly
|
874
|
-
continue; // Need more bytes before we can parse
|
875
885
|
}
|
876
886
|
|
877
|
-
|
878
|
-
|
887
|
+
rx_header_buf_pos_ += received;
|
888
|
+
|
889
|
+
// Check for buffer overflow
|
890
|
+
if (rx_header_buf_pos_ >= sizeof(rx_header_buf_)) {
|
879
891
|
state_ = State::FAILED;
|
880
892
|
HELPER_LOG("Header buffer overflow");
|
881
893
|
return APIError::BAD_DATA_PACKET;
|
882
894
|
}
|
883
895
|
|
884
|
-
//
|
885
|
-
|
886
|
-
|
887
|
-
// Increment position after storing
|
888
|
-
rx_header_buf_pos_++;
|
889
|
-
|
890
|
-
// Case 3: If we only have one varint byte, we need more
|
891
|
-
if (rx_header_buf_pos_ == 2) { // Have read indicator + 1 byte
|
892
|
-
continue; // Need more bytes before we can parse
|
896
|
+
// Need at least 3 bytes total (indicator + 2 varint bytes) before trying to parse
|
897
|
+
if (rx_header_buf_pos_ < 3) {
|
898
|
+
continue;
|
893
899
|
}
|
894
900
|
|
895
901
|
// At this point, we have at least 3 bytes total:
|
896
|
-
// - Validated indicator byte (0x00)
|
902
|
+
// - Validated indicator byte (0x00) stored at position 0
|
897
903
|
// - At least 2 bytes in the buffer for the varints
|
898
904
|
// Buffer layout:
|
899
|
-
//
|
900
|
-
//
|
905
|
+
// [0]: indicator byte (0x00)
|
906
|
+
// [1-3]: Message size varint (variable length)
|
907
|
+
// - 2 bytes would only allow up to 16383, which is less than noise's UINT16_MAX (65535)
|
901
908
|
// - 3 bytes allows up to 2097151, ensuring we support at least as much as noise
|
902
|
-
//
|
909
|
+
// [2-5]: Message type varint (variable length)
|
903
910
|
// We now attempt to parse both varints. If either is incomplete,
|
904
911
|
// we'll continue reading more bytes.
|
905
912
|
|
913
|
+
// Skip indicator byte at position 0
|
914
|
+
uint8_t varint_pos = 1;
|
906
915
|
uint32_t consumed = 0;
|
907
|
-
|
916
|
+
|
917
|
+
auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
|
908
918
|
if (!msg_size_varint.has_value()) {
|
909
919
|
// not enough data there yet
|
910
920
|
continue;
|
911
921
|
}
|
912
922
|
|
913
|
-
|
923
|
+
if (msg_size_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
|
924
|
+
state_ = State::FAILED;
|
925
|
+
HELPER_LOG("Bad packet: message size %" PRIu32 " exceeds maximum %u", msg_size_varint->as_uint32(),
|
926
|
+
std::numeric_limits<uint16_t>::max());
|
927
|
+
return APIError::BAD_DATA_PACKET;
|
928
|
+
}
|
929
|
+
rx_header_parsed_len_ = msg_size_varint->as_uint16();
|
930
|
+
|
931
|
+
// Move to next varint position
|
932
|
+
varint_pos += consumed;
|
914
933
|
|
915
|
-
auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[
|
934
|
+
auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
|
916
935
|
if (!msg_type_varint.has_value()) {
|
917
936
|
// not enough data there yet
|
918
937
|
continue;
|
919
938
|
}
|
920
|
-
|
939
|
+
if (msg_type_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
|
940
|
+
state_ = State::FAILED;
|
941
|
+
HELPER_LOG("Bad packet: message type %" PRIu32 " exceeds maximum %u", msg_type_varint->as_uint32(),
|
942
|
+
std::numeric_limits<uint16_t>::max());
|
943
|
+
return APIError::BAD_DATA_PACKET;
|
944
|
+
}
|
945
|
+
rx_header_parsed_type_ = msg_type_varint->as_uint16();
|
921
946
|
rx_header_parsed_ = true;
|
922
947
|
}
|
923
948
|
// header reading done
|
@@ -929,8 +954,8 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|
929
954
|
|
930
955
|
if (rx_buf_len_ < rx_header_parsed_len_) {
|
931
956
|
// more data to read
|
932
|
-
|
933
|
-
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
957
|
+
uint16_t to_read = rx_header_parsed_len_ - rx_buf_len_;
|
958
|
+
ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
934
959
|
if (received == -1) {
|
935
960
|
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
936
961
|
return APIError::WOULD_BLOCK;
|
@@ -943,8 +968,8 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|
943
968
|
HELPER_LOG("Connection closed");
|
944
969
|
return APIError::CONNECTION_CLOSED;
|
945
970
|
}
|
946
|
-
rx_buf_len_ += received;
|
947
|
-
if ((
|
971
|
+
rx_buf_len_ += static_cast<uint16_t>(received);
|
972
|
+
if (static_cast<uint16_t>(received) != to_read) {
|
948
973
|
// not all read
|
949
974
|
return APIError::WOULD_BLOCK;
|
950
975
|
}
|
@@ -962,7 +987,6 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|
962
987
|
rx_header_parsed_ = false;
|
963
988
|
return APIError::OK;
|
964
989
|
}
|
965
|
-
|
966
990
|
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
967
991
|
APIError aerr;
|
968
992
|
|
@@ -990,7 +1014,7 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|
990
1014
|
"Bad indicator byte";
|
991
1015
|
iov[0].iov_base = (void *) msg;
|
992
1016
|
iov[0].iov_len = 19;
|
993
|
-
write_raw_(iov, 1);
|
1017
|
+
this->write_raw_(iov, 1);
|
994
1018
|
}
|
995
1019
|
return aerr;
|
996
1020
|
}
|
@@ -1001,108 +1025,89 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|
1001
1025
|
buffer->type = rx_header_parsed_type_;
|
1002
1026
|
return APIError::OK;
|
1003
1027
|
}
|
1004
|
-
bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
|
1005
1028
|
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
|
1006
|
-
if (state_ != State::DATA) {
|
1007
|
-
return APIError::BAD_STATE;
|
1008
|
-
}
|
1009
|
-
|
1010
1029
|
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
1011
|
-
|
1012
|
-
size_t payload_len = raw_buffer->size() - frame_header_padding_;
|
1013
|
-
|
1014
|
-
// Calculate varint sizes for header components
|
1015
|
-
size_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(payload_len));
|
1016
|
-
size_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(type));
|
1017
|
-
size_t total_header_len = 1 + size_varint_len + type_varint_len;
|
1030
|
+
uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_);
|
1018
1031
|
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
}
|
1032
|
+
// Use write_protobuf_packets with a single packet
|
1033
|
+
std::vector<PacketInfo> packets;
|
1034
|
+
packets.emplace_back(type, 0, payload_len);
|
1023
1035
|
|
1024
|
-
|
1025
|
-
// The header starts at the latest possible position to minimize unused padding
|
1026
|
-
//
|
1027
|
-
// Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3
|
1028
|
-
// [0-2] - Unused padding
|
1029
|
-
// [3] - 0x00 indicator byte
|
1030
|
-
// [4] - Payload size varint (1 byte, for sizes 0-127)
|
1031
|
-
// [5] - Message type varint (1 byte, for types 0-127)
|
1032
|
-
// [6...] - Actual payload data
|
1033
|
-
//
|
1034
|
-
// Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2
|
1035
|
-
// [0-1] - Unused padding
|
1036
|
-
// [2] - 0x00 indicator byte
|
1037
|
-
// [3-4] - Payload size varint (2 bytes, for sizes 128-16383)
|
1038
|
-
// [5] - Message type varint (1 byte, for types 0-127)
|
1039
|
-
// [6...] - Actual payload data
|
1040
|
-
//
|
1041
|
-
// Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0
|
1042
|
-
// [0] - 0x00 indicator byte
|
1043
|
-
// [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151)
|
1044
|
-
// [4-5] - Message type varint (2 bytes, for types 128-32767)
|
1045
|
-
// [6...] - Actual payload data
|
1046
|
-
uint8_t *buf_start = raw_buffer->data();
|
1047
|
-
size_t header_offset = frame_header_padding_ - total_header_len;
|
1048
|
-
|
1049
|
-
// Write the plaintext header
|
1050
|
-
buf_start[header_offset] = 0x00; // indicator
|
1051
|
-
|
1052
|
-
// Encode size varint directly into buffer
|
1053
|
-
ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
|
1054
|
-
|
1055
|
-
// Encode type varint directly into buffer
|
1056
|
-
ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
|
1057
|
-
|
1058
|
-
struct iovec iov;
|
1059
|
-
// Point iov_base to the beginning of our header (skip unused padding)
|
1060
|
-
// This ensures we only send the actual header and payload, not the empty padding bytes
|
1061
|
-
iov.iov_base = buf_start + header_offset;
|
1062
|
-
iov.iov_len = total_header_len + payload_len;
|
1063
|
-
|
1064
|
-
return write_raw_(&iov, 1);
|
1036
|
+
return write_protobuf_packets(buffer, packets);
|
1065
1037
|
}
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
break;
|
1072
|
-
} else if (sent == -1) {
|
1073
|
-
state_ = State::FAILED;
|
1074
|
-
HELPER_LOG("Socket write failed with errno %d", errno);
|
1075
|
-
return APIError::SOCKET_WRITE_FAILED;
|
1076
|
-
}
|
1077
|
-
// TODO: inefficient if multiple packets in txbuf
|
1078
|
-
// replace with deque of buffers
|
1079
|
-
tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
|
1038
|
+
|
1039
|
+
APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer,
|
1040
|
+
const std::vector<PacketInfo> &packets) {
|
1041
|
+
if (state_ != State::DATA) {
|
1042
|
+
return APIError::BAD_STATE;
|
1080
1043
|
}
|
1081
1044
|
|
1082
|
-
|
1083
|
-
|
1045
|
+
if (packets.empty()) {
|
1046
|
+
return APIError::OK;
|
1047
|
+
}
|
1084
1048
|
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1049
|
+
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
1050
|
+
this->reusable_iovs_.clear();
|
1051
|
+
this->reusable_iovs_.reserve(packets.size());
|
1052
|
+
|
1053
|
+
for (const auto &packet : packets) {
|
1054
|
+
uint16_t type = packet.message_type;
|
1055
|
+
uint16_t offset = packet.offset;
|
1056
|
+
uint16_t payload_len = packet.payload_size;
|
1057
|
+
|
1058
|
+
// Calculate varint sizes for header layout
|
1059
|
+
uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(payload_len));
|
1060
|
+
uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(type));
|
1061
|
+
uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
|
1062
|
+
|
1063
|
+
// Calculate where to start writing the header
|
1064
|
+
// The header starts at the latest possible position to minimize unused padding
|
1065
|
+
//
|
1066
|
+
// Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3
|
1067
|
+
// [0-2] - Unused padding
|
1068
|
+
// [3] - 0x00 indicator byte
|
1069
|
+
// [4] - Payload size varint (1 byte, for sizes 0-127)
|
1070
|
+
// [5] - Message type varint (1 byte, for types 0-127)
|
1071
|
+
// [6...] - Actual payload data
|
1072
|
+
//
|
1073
|
+
// Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2
|
1074
|
+
// [0-1] - Unused padding
|
1075
|
+
// [2] - 0x00 indicator byte
|
1076
|
+
// [3-4] - Payload size varint (2 bytes, for sizes 128-16383)
|
1077
|
+
// [5] - Message type varint (1 byte, for types 0-127)
|
1078
|
+
// [6...] - Actual payload data
|
1079
|
+
//
|
1080
|
+
// Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0
|
1081
|
+
// [0] - 0x00 indicator byte
|
1082
|
+
// [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151)
|
1083
|
+
// [4-5] - Message type varint (2 bytes, for types 128-32767)
|
1084
|
+
// [6...] - Actual payload data
|
1085
|
+
//
|
1086
|
+
// The message starts at offset + frame_header_padding_
|
1087
|
+
// So we write the header starting at offset + frame_header_padding_ - total_header_len
|
1088
|
+
uint8_t *buf_start = raw_buffer->data() + offset;
|
1089
|
+
uint32_t header_offset = frame_header_padding_ - total_header_len;
|
1090
|
+
|
1091
|
+
// Write the plaintext header
|
1092
|
+
buf_start[header_offset] = 0x00; // indicator
|
1093
|
+
|
1094
|
+
// Encode size varint directly into buffer
|
1095
|
+
ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
|
1096
|
+
|
1097
|
+
// Encode type varint directly into buffer
|
1098
|
+
ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
|
1099
|
+
|
1100
|
+
// Add iovec for this packet (header + payload)
|
1101
|
+
struct iovec iov;
|
1102
|
+
iov.iov_base = buf_start + header_offset;
|
1103
|
+
iov.iov_len = total_header_len + payload_len;
|
1104
|
+
this->reusable_iovs_.push_back(iov);
|
1098
1105
|
}
|
1099
|
-
|
1106
|
+
|
1107
|
+
// Send all packets in one writev call
|
1108
|
+
return write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
|
1100
1109
|
}
|
1101
1110
|
|
1102
|
-
// Explicit template instantiation for Plaintext
|
1103
|
-
template APIError APIFrameHelper::write_raw_<APIPlaintextFrameHelper::State>(
|
1104
|
-
const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf_, const std::string &info,
|
1105
|
-
APIPlaintextFrameHelper::State &state, APIPlaintextFrameHelper::State failed_state);
|
1106
1111
|
#endif // USE_API_PLAINTEXT
|
1107
1112
|
|
1108
1113
|
} // namespace api
|