esphome 2025.7.5__py3-none-any.whl → 2025.8.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 +190 -83
- esphome/automation.py +2 -4
- esphome/build_gen/__init__.py +0 -0
- esphome/build_gen/platformio.py +102 -0
- esphome/components/a4988/a4988.cpp +0 -1
- esphome/components/absolute_humidity/absolute_humidity.cpp +0 -2
- esphome/components/absolute_humidity/sensor.py +2 -2
- esphome/components/adc/__init__.py +123 -85
- esphome/components/adc/adc_sensor.h +98 -35
- esphome/components/adc/adc_sensor_common.cpp +10 -4
- esphome/components/adc/adc_sensor_esp32.cpp +291 -123
- esphome/components/adc/adc_sensor_esp8266.cpp +1 -4
- esphome/components/adc/adc_sensor_libretiny.cpp +1 -2
- esphome/components/adc/adc_sensor_rp2040.cpp +1 -2
- esphome/components/adc/adc_sensor_zephyr.cpp +207 -0
- esphome/components/adc/sensor.py +61 -27
- esphome/components/adc128s102/adc128s102.cpp +1 -4
- esphome/components/ade7880/sensor.py +75 -49
- esphome/components/ads1115/ads1115.cpp +0 -1
- esphome/components/ads1118/ads1118.cpp +0 -1
- esphome/components/ags10/ags10.cpp +0 -4
- esphome/components/aht10/aht10.cpp +0 -4
- esphome/components/aic3204/aic3204.cpp +0 -2
- esphome/components/airthings_wave_plus/__init__.py +1 -1
- esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +22 -4
- esphome/components/airthings_wave_plus/airthings_wave_plus.h +10 -1
- esphome/components/airthings_wave_plus/sensor.py +55 -28
- esphome/components/alarm_control_panel/__init__.py +4 -9
- esphome/components/am2315c/am2315c.cpp +0 -2
- esphome/components/am2320/am2320.cpp +0 -1
- esphome/components/animation/__init__.py +14 -11
- esphome/components/apds9306/apds9306.cpp +0 -4
- esphome/components/apds9960/apds9960.cpp +0 -1
- esphome/components/api/__init__.py +29 -4
- esphome/components/api/api_connection.cpp +378 -401
- esphome/components/api/api_connection.h +102 -52
- esphome/components/api/api_frame_helper.cpp +69 -896
- esphome/components/api/api_frame_helper.h +31 -126
- esphome/components/api/api_frame_helper_noise.cpp +583 -0
- esphome/components/api/api_frame_helper_noise.h +68 -0
- esphome/components/api/api_frame_helper_plaintext.cpp +290 -0
- esphome/components/api/api_frame_helper_plaintext.h +53 -0
- esphome/components/api/api_noise_context.h +2 -4
- esphome/components/api/api_pb2.cpp +1601 -1808
- esphome/components/api/api_pb2.h +367 -323
- esphome/components/api/api_pb2_dump.cpp +1137 -3466
- esphome/components/api/api_pb2_includes.h +34 -0
- esphome/components/api/api_pb2_service.cpp +94 -105
- esphome/components/api/api_pb2_service.h +27 -16
- esphome/components/api/api_server.cpp +18 -17
- esphome/components/api/api_server.h +8 -5
- esphome/components/api/client.py +16 -8
- esphome/components/api/custom_api_device.h +68 -14
- esphome/components/api/homeassistant_service.h +24 -19
- esphome/components/api/list_entities.cpp +3 -5
- esphome/components/api/list_entities.h +2 -4
- esphome/components/api/proto.cpp +3 -5
- esphome/components/api/proto.h +239 -274
- esphome/components/api/subscribe_state.cpp +2 -4
- esphome/components/api/subscribe_state.h +2 -4
- esphome/components/api/user_services.cpp +2 -4
- esphome/components/api/user_services.h +8 -8
- esphome/components/as3935/as3935.cpp +0 -2
- esphome/components/as3935_spi/as3935_spi.cpp +0 -2
- esphome/components/as5600/__init__.py +1 -1
- esphome/components/as5600/as5600.cpp +0 -2
- esphome/components/as5600/sensor/__init__.py +0 -1
- esphome/components/as7341/as7341.cpp +0 -1
- esphome/components/async_tcp/__init__.py +1 -1
- esphome/components/at581x/at581x.cpp +1 -1
- esphome/components/atm90e26/atm90e26.cpp +0 -1
- esphome/components/atm90e32/atm90e32.cpp +475 -116
- esphome/components/atm90e32/atm90e32.h +43 -5
- esphome/components/audio/audio.h +2 -2
- esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +0 -2
- esphome/components/beken_spi_led_strip/led_strip.cpp +0 -2
- esphome/components/bh1750/bh1750.cpp +0 -1
- esphome/components/binary_sensor/__init__.py +14 -12
- esphome/components/ble_client/__init__.py +4 -7
- esphome/components/bluetooth_proxy/__init__.py +40 -3
- esphome/components/bluetooth_proxy/bluetooth_connection.cpp +392 -81
- esphome/components/bluetooth_proxy/bluetooth_connection.h +16 -5
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +102 -311
- esphome/components/bluetooth_proxy/bluetooth_proxy.h +30 -14
- esphome/components/bme280_base/bme280_base.cpp +15 -16
- esphome/components/bme680/bme680.cpp +2 -3
- esphome/components/bme680_bsec/bme680_bsec.cpp +0 -2
- esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +0 -2
- esphome/components/bmi160/bmi160.cpp +0 -1
- esphome/components/bmp085/bmp085.cpp +0 -1
- esphome/components/bmp280_base/bmp280_base.cpp +13 -14
- esphome/components/bmp3xx_base/bmp3xx_base.cpp +0 -1
- esphome/components/bmp581/bmp581.cpp +0 -2
- esphome/components/bp1658cj/bp1658cj.cpp +0 -1
- esphome/components/bp5758d/bp5758d.cpp +0 -1
- esphome/components/button/__init__.py +0 -1
- esphome/components/canbus/__init__.py +2 -3
- esphome/components/canbus/canbus.cpp +0 -1
- esphome/components/cap1188/cap1188.cpp +0 -2
- esphome/components/captive_portal/__init__.py +1 -1
- esphome/components/cd74hc4067/cd74hc4067.cpp +0 -2
- esphome/components/ch422g/ch422g.cpp +0 -1
- esphome/components/chsc6x/chsc6x_touchscreen.cpp +0 -3
- esphome/components/climate/__init__.py +0 -1
- esphome/components/climate/climate_traits.h +24 -0
- esphome/components/cm1106/cm1106.cpp +0 -1
- esphome/components/const/__init__.py +6 -0
- esphome/components/cover/__init__.py +0 -1
- esphome/components/cover/cover.cpp +9 -13
- esphome/components/cs5460a/cs5460a.cpp +0 -2
- esphome/components/cse7761/cse7761.cpp +0 -1
- esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +0 -2
- esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +0 -2
- esphome/components/dac7678/dac7678_output.cpp +0 -2
- esphome/components/dallas_temp/dallas_temp.cpp +0 -1
- esphome/components/datetime/__init__.py +0 -2
- esphome/components/debug/__init__.py +15 -1
- esphome/components/debug/debug_zephyr.cpp +281 -0
- esphome/components/debug/sensor.py +2 -1
- esphome/components/deep_sleep/deep_sleep_component.cpp +0 -1
- esphome/components/deep_sleep/deep_sleep_esp32.cpp +20 -1
- esphome/components/dfrobot_sen0395/__init__.py +1 -2
- esphome/components/dht/dht.cpp +0 -1
- esphome/components/dht12/dht12.cpp +0 -1
- esphome/components/display/__init__.py +16 -3
- esphome/components/display_menu_base/__init__.py +1 -1
- esphome/components/dps310/dps310.cpp +0 -2
- esphome/components/ds1307/ds1307.cpp +0 -1
- esphome/components/ds2484/ds2484.cpp +0 -1
- esphome/components/duty_cycle/duty_cycle_sensor.cpp +0 -1
- esphome/components/ee895/ee895.cpp +0 -1
- esphome/components/ektf2232/touchscreen/ektf2232.cpp +0 -1
- esphome/components/emc2101/emc2101.cpp +0 -2
- esphome/components/ens160_base/ens160_base.cpp +0 -2
- esphome/components/ens210/ens210.cpp +0 -1
- esphome/components/es7210/es7210.cpp +0 -2
- esphome/components/es7243e/es7243e.cpp +0 -2
- esphome/components/es8156/es8156.cpp +0 -2
- esphome/components/es8311/es8311.cpp +0 -2
- esphome/components/es8388/es8388.cpp +0 -2
- esphome/components/esp32/__init__.py +203 -60
- esphome/components/esp32/boards.py +17 -0
- esphome/components/esp32/gpio.cpp +0 -1
- esphome/components/esp32/gpio.py +1 -2
- esphome/components/esp32/gpio_esp32_h2.py +2 -7
- esphome/components/esp32/gpio_esp32_p4.py +2 -7
- esphome/components/esp32/post_build.py.script +112 -61
- esphome/components/esp32_ble/__init__.py +41 -2
- esphome/components/esp32_ble/ble.cpp +14 -10
- esphome/components/esp32_ble/ble.h +18 -18
- esphome/components/esp32_ble/ble_advertising.cpp +5 -5
- esphome/components/esp32_ble/ble_advertising.h +7 -5
- esphome/components/esp32_ble/ble_event.h +139 -73
- esphome/components/esp32_ble/ble_scan_result.h +2 -4
- esphome/components/esp32_ble/ble_uuid.cpp +5 -5
- esphome/components/esp32_ble/ble_uuid.h +6 -5
- esphome/components/esp32_ble_beacon/__init__.py +4 -0
- esphome/components/esp32_ble_client/__init__.py +1 -1
- esphome/components/esp32_ble_client/ble_characteristic.cpp +4 -4
- esphome/components/esp32_ble_client/ble_characteristic.h +6 -4
- esphome/components/esp32_ble_client/ble_client_base.cpp +155 -104
- esphome/components/esp32_ble_client/ble_client_base.h +17 -6
- esphome/components/esp32_ble_client/ble_descriptor.h +6 -4
- esphome/components/esp32_ble_client/ble_service.cpp +4 -4
- esphome/components/esp32_ble_client/ble_service.h +6 -4
- esphome/components/esp32_ble_server/__init__.py +15 -12
- esphome/components/esp32_ble_tracker/__init__.py +79 -11
- esphome/components/esp32_ble_tracker/automation.h +4 -4
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +264 -261
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +103 -37
- esphome/components/esp32_camera/__init__.py +13 -1
- esphome/components/esp32_camera/esp32_camera.cpp +7 -2
- esphome/components/esp32_camera/esp32_camera.h +1 -0
- esphome/components/esp32_dac/esp32_dac.cpp +3 -19
- esphome/components/esp32_dac/esp32_dac.h +4 -8
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -6
- esphome/components/esp32_rmt_led_strip/light.py +1 -1
- esphome/components/esp32_touch/__init__.py +2 -3
- esphome/components/esp32_touch/esp32_touch.h +9 -6
- esphome/components/esp32_touch/esp32_touch_common.cpp +2 -0
- esphome/components/esp32_touch/esp32_touch_v1.cpp +7 -9
- esphome/components/esp32_touch/esp32_touch_v2.cpp +10 -6
- esphome/components/esp8266/__init__.py +3 -1
- esphome/components/esp8266_pwm/esp8266_pwm.cpp +0 -1
- esphome/components/esphome/ota/__init__.py +1 -2
- esphome/components/esphome/ota/ota_esphome.cpp +150 -77
- esphome/components/esphome/ota/ota_esphome.h +8 -1
- esphome/components/espnow/__init__.py +309 -0
- esphome/components/espnow/automation.h +175 -0
- esphome/components/espnow/espnow_component.cpp +468 -0
- esphome/components/espnow/espnow_component.h +183 -0
- esphome/components/espnow/espnow_err.h +19 -0
- esphome/components/espnow/espnow_packet.h +166 -0
- esphome/components/ethernet/__init__.py +7 -1
- esphome/components/ethernet/esp_eth_phy_jl1101.c +5 -0
- esphome/components/ethernet/ethernet_component.cpp +0 -1
- esphome/components/ethernet/ethernet_component.h +4 -0
- esphome/components/ethernet_info/ethernet_info_text_sensor.h +0 -3
- esphome/components/event/__init__.py +0 -1
- esphome/components/factory_reset/__init__.py +92 -0
- esphome/components/factory_reset/factory_reset.cpp +76 -0
- esphome/components/factory_reset/factory_reset.h +43 -0
- esphome/components/fan/__init__.py +0 -1
- esphome/components/fan/fan_traits.h +16 -0
- esphome/components/fastled_base/fastled_light.cpp +0 -1
- esphome/components/fastled_spi/light.py +1 -3
- esphome/components/fingerprint_grow/fingerprint_grow.cpp +0 -2
- esphome/components/fs3000/fs3000.cpp +0 -2
- esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp +0 -2
- esphome/components/ft63x6/ft63x6.cpp +0 -1
- esphome/components/gdk101/gdk101.cpp +0 -1
- esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +0 -1
- esphome/components/gl_r01_i2c/sensor.py +1 -1
- esphome/components/gpio/one_wire/gpio_one_wire.cpp +0 -1
- esphome/components/gpio/switch/gpio_switch.cpp +0 -2
- esphome/components/gpio_expander/cached_gpio.h +24 -15
- esphome/components/gps/__init__.py +6 -2
- esphome/components/gps/gps.cpp +50 -49
- esphome/components/gps/gps.h +4 -8
- esphome/components/gps/time/gps_time.cpp +3 -9
- esphome/components/gps/time/gps_time.h +4 -7
- esphome/components/graph/__init__.py +1 -1
- esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +0 -1
- esphome/components/grove_tb6612fng/grove_tb6612fng.cpp +0 -1
- esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +21 -12
- esphome/components/gt911/touchscreen/gt911_touchscreen.h +26 -2
- esphome/components/haier/climate.py +5 -10
- esphome/components/haier/haier_base.cpp +0 -1
- esphome/components/hbridge/switch/hbridge_switch.cpp +0 -2
- esphome/components/hdc1080/hdc1080.cpp +0 -2
- esphome/components/heatpumpir/climate.py +2 -2
- esphome/components/hlw8012/hlw8012.cpp +0 -1
- esphome/components/hm3301/hm3301.cpp +0 -1
- esphome/components/hmc5883l/hmc5883l.cpp +0 -1
- esphome/components/homeassistant/__init__.py +1 -0
- esphome/components/homeassistant/number/__init__.py +1 -0
- esphome/components/homeassistant/number/homeassistant_number.cpp +11 -7
- esphome/components/homeassistant/switch/__init__.py +1 -0
- esphome/components/homeassistant/switch/homeassistant_switch.cpp +9 -5
- esphome/components/honeywellabp/honeywellabp.cpp +1 -4
- esphome/components/host/__init__.py +2 -0
- esphome/components/hte501/hte501.cpp +0 -1
- esphome/components/http_request/__init__.py +2 -3
- esphome/components/http_request/http_request_idf.cpp +2 -2
- esphome/components/htu21d/htu21d.cpp +0 -2
- esphome/components/htu31d/htu31d.cpp +0 -2
- esphome/components/hx711/hx711.cpp +0 -1
- esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +0 -1
- esphome/components/hydreon_rgxx/sensor.py +4 -5
- esphome/components/i2c/i2c_bus.h +1 -1
- esphome/components/i2c/i2c_bus_arduino.cpp +1 -2
- esphome/components/i2c/i2c_bus_esp_idf.cpp +192 -17
- esphome/components/i2c/i2c_bus_esp_idf.h +11 -1
- esphome/components/i2s_audio/__init__.py +6 -5
- esphome/components/i2s_audio/i2s_audio.cpp +0 -2
- esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +1 -4
- esphome/components/i2s_audio/microphone/__init__.py +4 -6
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +0 -1
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +2 -2
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +273 -269
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +19 -34
- esphome/components/ili9xxx/display.py +4 -3
- esphome/components/ili9xxx/ili9xxx_display.cpp +0 -2
- esphome/components/image/__init__.py +123 -92
- esphome/components/improv_serial/__init__.py +7 -8
- esphome/components/ina219/ina219.cpp +0 -1
- esphome/components/ina226/ina226.cpp +0 -2
- esphome/components/ina260/ina260.cpp +0 -2
- esphome/components/ina2xx_base/__init__.py +2 -5
- esphome/components/ina2xx_base/ina2xx_base.cpp +0 -2
- esphome/components/ina3221/ina3221.cpp +0 -1
- esphome/components/internal_temperature/internal_temperature.cpp +0 -2
- esphome/components/interval/interval.h +5 -9
- esphome/components/json/__init__.py +1 -1
- esphome/components/kmeteriso/kmeteriso.cpp +0 -2
- esphome/components/lc709203f/lc709203f.cpp +0 -2
- esphome/components/lcd_gpio/display.py +1 -3
- esphome/components/lcd_gpio/gpio_lcd_display.cpp +0 -1
- esphome/components/lcd_pcf8574/pcf8574_display.cpp +0 -1
- esphome/components/ld2410/__init__.py +4 -6
- esphome/components/ld2410/binary_sensor.py +4 -0
- esphome/components/ld2410/ld2410.cpp +56 -100
- esphome/components/ld2410/ld2410.h +17 -15
- esphome/components/ld2410/sensor.py +24 -10
- esphome/components/ld2412/__init__.py +46 -0
- esphome/components/ld2412/binary_sensor.py +70 -0
- esphome/components/ld2412/button/__init__.py +74 -0
- esphome/components/ld2412/button/factory_reset_button.cpp +9 -0
- esphome/components/ld2412/button/factory_reset_button.h +18 -0
- esphome/components/ld2412/button/query_button.cpp +9 -0
- esphome/components/ld2412/button/query_button.h +18 -0
- esphome/components/ld2412/button/restart_button.cpp +9 -0
- esphome/components/ld2412/button/restart_button.h +18 -0
- esphome/components/ld2412/button/start_dynamic_background_correction_button.cpp +11 -0
- esphome/components/ld2412/button/start_dynamic_background_correction_button.h +18 -0
- esphome/components/ld2412/ld2412.cpp +861 -0
- esphome/components/ld2412/ld2412.h +141 -0
- esphome/components/ld2412/number/__init__.py +126 -0
- esphome/components/ld2412/number/gate_threshold_number.cpp +14 -0
- esphome/components/ld2412/number/gate_threshold_number.h +19 -0
- esphome/components/ld2412/number/light_threshold_number.cpp +12 -0
- esphome/components/ld2412/number/light_threshold_number.h +18 -0
- esphome/components/ld2412/number/max_distance_timeout_number.cpp +12 -0
- esphome/components/ld2412/number/max_distance_timeout_number.h +18 -0
- esphome/components/ld2412/select/__init__.py +82 -0
- esphome/components/ld2412/select/baud_rate_select.cpp +12 -0
- esphome/components/ld2412/select/baud_rate_select.h +18 -0
- esphome/components/ld2412/select/distance_resolution_select.cpp +12 -0
- esphome/components/ld2412/select/distance_resolution_select.h +18 -0
- esphome/components/ld2412/select/light_out_control_select.cpp +12 -0
- esphome/components/ld2412/select/light_out_control_select.h +18 -0
- esphome/components/ld2412/sensor.py +124 -0
- esphome/components/ld2412/switch/__init__.py +45 -0
- esphome/components/ld2412/switch/bluetooth_switch.cpp +12 -0
- esphome/components/ld2412/switch/bluetooth_switch.h +18 -0
- esphome/components/ld2412/switch/engineering_mode_switch.cpp +12 -0
- esphome/components/ld2412/switch/engineering_mode_switch.h +18 -0
- esphome/components/ld2412/text_sensor.py +34 -0
- esphome/components/ld2420/ld2420.cpp +0 -1
- esphome/components/ld2450/__init__.py +3 -4
- esphome/components/ld2450/binary_sensor.py +3 -0
- esphome/components/ld2450/ld2450.cpp +77 -165
- esphome/components/ld2450/ld2450.h +26 -54
- esphome/components/ld2450/sensor.py +120 -6
- esphome/components/ld2450/text_sensor.py +5 -4
- esphome/components/ld24xx/__init__.py +1 -0
- esphome/components/ld24xx/ld24xx.h +65 -0
- esphome/components/ledc/ledc_output.cpp +0 -1
- esphome/components/libretiny/__init__.py +2 -0
- esphome/components/light/__init__.py +0 -1
- esphome/components/light/effects.py +70 -45
- esphome/components/light/light_call.cpp +101 -66
- esphome/components/light/light_color_values.h +16 -11
- esphome/components/light/light_json_schema.cpp +46 -44
- esphome/components/light/light_state.cpp +8 -11
- esphome/components/light/light_traits.h +17 -0
- esphome/components/lightwaverf/lightwaverf.cpp +0 -2
- esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +0 -1
- esphome/components/lock/__init__.py +0 -1
- esphome/components/logger/__init__.py +31 -9
- esphome/components/logger/logger.cpp +12 -7
- esphome/components/logger/logger.h +25 -14
- esphome/components/logger/logger_esp32.cpp +2 -7
- esphome/components/logger/logger_esp8266.cpp +2 -4
- esphome/components/logger/logger_host.cpp +2 -4
- esphome/components/logger/logger_libretiny.cpp +2 -4
- esphome/components/logger/logger_rp2040.cpp +2 -4
- esphome/components/logger/logger_zephyr.cpp +86 -0
- esphome/components/logger/select/logger_level_select.cpp +2 -4
- esphome/components/logger/select/logger_level_select.h +2 -4
- esphome/components/logger/task_log_buffer.cpp +2 -4
- esphome/components/logger/task_log_buffer.h +2 -4
- esphome/components/lps22/sensor.py +5 -5
- esphome/components/ltr390/ltr390.cpp +0 -2
- esphome/components/ltr501/ltr501.cpp +0 -1
- esphome/components/ltr_als_ps/ltr_als_ps.cpp +0 -1
- esphome/components/lvgl/__init__.py +14 -13
- esphome/components/lvgl/automation.py +2 -4
- esphome/components/lvgl/defines.py +0 -2
- esphome/components/lvgl/helpers.py +1 -1
- esphome/components/lvgl/lv_validation.py +7 -4
- esphome/components/lvgl/lvgl_esphome.cpp +2 -3
- esphome/components/lvgl/styles.py +2 -2
- esphome/components/lvgl/types.py +1 -1
- esphome/components/lvgl/widgets/__init__.py +2 -2
- esphome/components/lvgl/widgets/arc.py +14 -11
- esphome/components/lvgl/widgets/buttonmatrix.py +1 -1
- esphome/components/lvgl/widgets/qrcode.py +7 -7
- esphome/components/lvgl/widgets/spinner.py +6 -6
- esphome/components/lvgl/widgets/switch.py +2 -2
- esphome/components/lvgl/widgets/tabview.py +3 -3
- esphome/components/m5stack_8angle/m5stack_8angle.cpp +0 -1
- esphome/components/matrix_keypad/__init__.py +4 -3
- esphome/components/max17043/max17043.cpp +0 -2
- esphome/components/max31855/max31855.cpp +1 -4
- esphome/components/max31856/max31856.cpp +0 -4
- esphome/components/max31865/max31865.cpp +0 -1
- esphome/components/max44009/max44009.cpp +0 -1
- esphome/components/max6675/max6675.cpp +1 -4
- esphome/components/max6956/max6956.cpp +0 -1
- esphome/components/max7219/max7219.cpp +0 -1
- esphome/components/max7219digit/display.py +1 -1
- esphome/components/max7219digit/max7219digit.cpp +0 -1
- esphome/components/max9611/max9611.cpp +0 -1
- esphome/components/mcp23008/__init__.py +1 -1
- esphome/components/mcp23008/mcp23008.cpp +0 -1
- esphome/components/mcp23016/mcp23016.cpp +0 -1
- esphome/components/mcp23017/__init__.py +1 -1
- esphome/components/mcp23017/mcp23017.cpp +0 -1
- esphome/components/mcp23s08/__init__.py +1 -1
- esphome/components/mcp23s08/mcp23s08.cpp +0 -1
- esphome/components/mcp23s17/__init__.py +1 -1
- esphome/components/mcp23s17/mcp23s17.cpp +0 -1
- esphome/components/mcp23x08_base/__init__.py +2 -0
- esphome/components/mcp23x08_base/mcp23x08_base.cpp +9 -7
- esphome/components/mcp23x08_base/mcp23x08_base.h +9 -4
- esphome/components/mcp23x17_base/__init__.py +2 -0
- esphome/components/mcp23x17_base/mcp23x17_base.cpp +20 -7
- esphome/components/mcp23x17_base/mcp23x17_base.h +9 -4
- esphome/components/mcp23xxx_base/__init__.py +11 -5
- esphome/components/mcp23xxx_base/mcp23xxx_base.cpp +15 -12
- esphome/components/mcp23xxx_base/mcp23xxx_base.h +8 -7
- esphome/components/mcp3008/mcp3008.cpp +1 -4
- esphome/components/mcp3204/mcp3204.cpp +1 -4
- esphome/components/mcp4461/mcp4461.cpp +0 -1
- esphome/components/mcp4725/mcp4725.cpp +0 -1
- esphome/components/mcp4728/mcp4728.cpp +0 -1
- esphome/components/mcp9600/mcp9600.cpp +0 -2
- esphome/components/mcp9808/mcp9808.cpp +0 -2
- esphome/components/mdns/__init__.py +3 -0
- esphome/components/mdns/mdns_component.cpp +2 -0
- esphome/components/mdns/mdns_component.h +4 -0
- esphome/components/media_player/__init__.py +40 -0
- esphome/components/media_player/automation.h +16 -0
- esphome/components/media_player/media_player.cpp +13 -0
- esphome/components/media_player/media_player.h +50 -3
- esphome/components/micro_wake_word/micro_wake_word.cpp +0 -3
- esphome/components/mics_4514/mics_4514.cpp +1 -6
- esphome/components/midea/ir_transmitter.h +4 -4
- esphome/components/mipi/__init__.py +416 -0
- esphome/components/mipi_dsi/__init__.py +5 -0
- esphome/components/mipi_dsi/display.py +233 -0
- esphome/components/mipi_dsi/mipi_dsi.cpp +379 -0
- esphome/components/mipi_dsi/mipi_dsi.h +123 -0
- esphome/components/mipi_dsi/models/__init__.py +0 -0
- esphome/components/mipi_dsi/models/guition.py +38 -0
- esphome/components/mipi_dsi/models/m5stack.py +57 -0
- esphome/components/mipi_dsi/models/waveshare.py +105 -0
- esphome/components/mipi_rgb/models/lilygo.py +0 -0
- esphome/components/mipi_spi/__init__.py +0 -9
- esphome/components/mipi_spi/display.py +220 -256
- esphome/components/mipi_spi/mipi_spi.cpp +1 -485
- esphome/components/mipi_spi/mipi_spi.h +556 -108
- esphome/components/mipi_spi/models/__init__.py +0 -65
- esphome/components/mipi_spi/models/adafruit.py +30 -0
- esphome/components/mipi_spi/models/amoled.py +41 -5
- esphome/components/mipi_spi/models/ili.py +5 -5
- esphome/components/mipi_spi/models/jc.py +1 -3
- esphome/components/mipi_spi/models/lilygo.py +1 -1
- esphome/components/mipi_spi/models/waveshare.py +16 -1
- esphome/components/mixer/speaker/__init__.py +4 -5
- esphome/components/mlx90393/sensor.py +7 -5
- esphome/components/mlx90393/sensor_mlx90393.cpp +0 -1
- esphome/components/mlx90614/mlx90614.cpp +0 -1
- esphome/components/mmc5603/mmc5603.cpp +0 -1
- esphome/components/mmc5983/mmc5983.cpp +0 -2
- esphome/components/mpl3115a2/mpl3115a2.cpp +0 -2
- esphome/components/mpr121/__init__.py +7 -6
- esphome/components/mpr121/mpr121.cpp +0 -1
- esphome/components/mpu6050/mpu6050.cpp +0 -1
- esphome/components/mpu6886/mpu6886.cpp +0 -1
- esphome/components/mqtt/__init__.py +1 -2
- esphome/components/mqtt/mqtt_button.cpp +1 -1
- esphome/components/mqtt/mqtt_client.cpp +0 -1
- esphome/components/mqtt/mqtt_component.cpp +8 -14
- esphome/components/mqtt/mqtt_component.h +0 -7
- esphome/components/mqtt/mqtt_sensor.cpp +0 -1
- esphome/components/mqtt/mqtt_sensor.h +0 -1
- esphome/components/mqtt/mqtt_text_sensor.cpp +0 -1
- esphome/components/mqtt/mqtt_text_sensor.h +0 -1
- esphome/components/ms5611/ms5611.cpp +0 -1
- esphome/components/ms8607/ms8607.cpp +0 -1
- esphome/components/msa3xx/msa3xx.cpp +0 -2
- esphome/components/my9231/my9231.cpp +0 -2
- esphome/components/nau7802/nau7802.cpp +0 -1
- esphome/components/neopixelbus/light.py +3 -0
- esphome/components/network/util.cpp +29 -0
- esphome/components/nextion/nextion.cpp +2 -2
- esphome/components/nfc/binary_sensor/{binary_sensor.cpp → nfc_binary_sensor.cpp} +1 -1
- esphome/components/npi19/npi19.cpp +0 -2
- esphome/components/nrf52/__init__.py +223 -0
- esphome/components/nrf52/boards.py +34 -0
- esphome/components/nrf52/const.py +18 -0
- esphome/components/nrf52/gpio.py +79 -0
- esphome/components/number/__init__.py +2 -1
- esphome/components/one_wire/__init__.py +1 -2
- esphome/components/one_wire/one_wire.cpp +0 -2
- esphome/components/one_wire/one_wire.h +0 -2
- esphome/components/opentherm/hub.cpp +0 -1
- esphome/components/opentherm/number/__init__.py +2 -2
- esphome/components/openthread/__init__.py +2 -3
- esphome/components/openthread/openthread.cpp +30 -13
- esphome/components/openthread/openthread.h +3 -0
- esphome/components/openthread/openthread_esp.cpp +3 -1
- esphome/components/opt3001/sensor.py +2 -6
- esphome/components/output/__init__.py +38 -0
- esphome/components/output/automation.h +24 -0
- esphome/components/output/switch/output_switch.cpp +0 -2
- esphome/components/packages/__init__.py +1 -2
- esphome/components/packet_transport/__init__.py +4 -3
- esphome/components/pca6416a/pca6416a.cpp +0 -1
- esphome/components/pca9554/pca9554.cpp +0 -1
- esphome/components/pca9685/pca9685_output.cpp +0 -2
- esphome/components/pcf85063/pcf85063.cpp +0 -1
- esphome/components/pcf8563/pcf8563.cpp +0 -1
- esphome/components/pcf8574/pcf8574.cpp +0 -1
- esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +0 -1
- esphome/components/pipsolar/pipsolar.cpp +54 -42
- esphome/components/pipsolar/pipsolar.h +5 -4
- esphome/components/pipsolar/sensor/__init__.py +1 -1
- esphome/components/pm2005/pm2005.cpp +0 -1
- esphome/components/pmsa003i/pmsa003i.cpp +0 -2
- esphome/components/pmwcs3/sensor.py +1 -2
- esphome/components/pn532/pn532.cpp +0 -2
- esphome/components/pn532_spi/pn532_spi.cpp +0 -2
- esphome/components/power_supply/power_supply.cpp +7 -10
- esphome/components/power_supply/power_supply.h +1 -1
- esphome/components/psram/__init__.py +6 -1
- esphome/components/pulse_counter/pulse_counter_sensor.cpp +0 -1
- esphome/components/pulse_counter/sensor.py +9 -6
- esphome/components/pylontech/pylontech.cpp +0 -1
- esphome/components/qmc5883l/qmc5883l.cpp +0 -1
- esphome/components/qmp6988/qmp6988.cpp +0 -2
- esphome/components/qspi_dbi/display.py +2 -3
- esphome/components/qspi_dbi/qspi_dbi.cpp +0 -2
- esphome/components/qwiic_pir/binary_sensor.py +2 -3
- esphome/components/qwiic_pir/qwiic_pir.cpp +0 -2
- esphome/components/rc522/rc522.cpp +9 -31
- esphome/components/rc522_spi/rc522_spi.cpp +0 -1
- esphome/components/remote_base/__init__.py +5 -6
- esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -1
- esphome/components/remote_receiver/remote_receiver_esp8266.cpp +0 -1
- esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -1
- esphome/components/remote_transmitter/__init__.py +26 -0
- esphome/components/remote_transmitter/automation.h +18 -0
- esphome/components/remote_transmitter/remote_transmitter.h +2 -1
- esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -1
- esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
- esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
- esphome/components/resampler/speaker/__init__.py +4 -5
- esphome/components/rf_bridge/__init__.py +4 -8
- esphome/components/rotary_encoder/rotary_encoder.cpp +0 -2
- esphome/components/rp2040/__init__.py +3 -1
- esphome/components/rp2040_pio_led_strip/led_strip.cpp +0 -2
- esphome/components/rp2040_pio_led_strip/light.py +1 -2
- esphome/components/rp2040_pwm/rp2040_pwm.cpp +1 -5
- esphome/components/rpi_dpi_rgb/display.py +13 -15
- esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -3
- esphome/components/runtime_stats/__init__.py +34 -0
- esphome/components/runtime_stats/runtime_stats.cpp +102 -0
- esphome/components/runtime_stats/runtime_stats.h +132 -0
- esphome/components/scd30/scd30.cpp +0 -2
- esphome/components/scd30/sensor.py +1 -2
- esphome/components/scd4x/scd4x.cpp +0 -1
- esphome/components/scd4x/sensor.py +1 -3
- esphome/components/sdl/display.py +3 -1
- esphome/components/sdl/sdl_esphome.cpp +0 -2
- esphome/components/sdp3x/sdp3x.cpp +0 -2
- esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp +0 -2
- esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +0 -3
- esphome/components/select/__init__.py +2 -3
- esphome/components/select/select_traits.cpp +1 -1
- esphome/components/select/select_traits.h +1 -1
- esphome/components/sen0321/sen0321.cpp +0 -1
- esphome/components/sen5x/sen5x.cpp +0 -2
- esphome/components/senseair/senseair.cpp +7 -3
- esphome/components/senseair/senseair.h +11 -0
- esphome/components/sensor/__init__.py +36 -4
- esphome/components/sensor/filter.cpp +49 -10
- esphome/components/sensor/filter.h +22 -7
- esphome/components/sensor/sensor.cpp +0 -1
- esphome/components/sensor/sensor.h +0 -9
- esphome/components/sfa30/sfa30.cpp +0 -4
- esphome/components/sgp30/sgp30.cpp +0 -2
- esphome/components/sgp4x/sensor.py +1 -1
- esphome/components/sgp4x/sgp4x.cpp +0 -2
- esphome/components/shelly_dimmer/shelly_dimmer.cpp +0 -2
- esphome/components/sht3xd/sht3xd.cpp +0 -2
- esphome/components/sht4x/sht4x.cpp +0 -2
- esphome/components/shtcx/shtcx.cpp +0 -1
- esphome/components/sim800l/__init__.py +2 -4
- esphome/components/sm16716/sm16716.cpp +0 -1
- esphome/components/sm2135/sm2135.cpp +0 -1
- esphome/components/sm2235/sm2235.cpp +0 -1
- esphome/components/sm2335/sm2335.cpp +0 -1
- esphome/components/sn74hc165/sn74hc165.cpp +0 -1
- esphome/components/sn74hc595/sn74hc595.cpp +0 -1
- esphome/components/sntp/sntp_component.cpp +0 -1
- esphome/components/sound_level/sensor.py +1 -1
- esphome/components/speaker/media_player/__init__.py +21 -33
- esphome/components/speaker/media_player/audio_pipeline.cpp +4 -7
- esphome/components/spi/__init__.py +29 -13
- esphome/components/spi/spi.cpp +0 -2
- esphome/components/spi_device/spi_device.cpp +1 -4
- esphome/components/sprinkler/__init__.py +4 -4
- esphome/components/sps30/sps30.cpp +0 -1
- esphome/components/ssd1306_base/__init__.py +11 -11
- esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
- esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +0 -1
- esphome/components/ssd1306_spi/ssd1306_spi.cpp +0 -1
- esphome/components/ssd1322_spi/ssd1322_spi.cpp +0 -1
- esphome/components/ssd1325_spi/ssd1325_spi.cpp +0 -1
- esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +0 -1
- esphome/components/ssd1327_spi/ssd1327_spi.cpp +0 -1
- esphome/components/ssd1331_spi/ssd1331_spi.cpp +0 -1
- esphome/components/ssd1351_spi/ssd1351_spi.cpp +0 -1
- esphome/components/st7567_i2c/st7567_i2c.cpp +0 -1
- esphome/components/st7567_spi/st7567_spi.cpp +0 -1
- esphome/components/st7701s/display.py +10 -14
- esphome/components/st7701s/st7701s.cpp +0 -3
- esphome/components/st7735/st7735.cpp +0 -1
- esphome/components/st7789v/st7789v.cpp +0 -1
- esphome/components/st7920/st7920.cpp +0 -1
- esphome/components/status_led/light/status_led_light.cpp +0 -2
- esphome/components/status_led/status_led.cpp +0 -1
- esphome/components/stepper/__init__.py +2 -4
- esphome/components/sts3x/sts3x.cpp +0 -1
- esphome/components/substitutions/__init__.py +10 -16
- esphome/components/substitutions/jinja.py +24 -1
- esphome/components/sun/__init__.py +2 -3
- esphome/components/switch/__init__.py +31 -1
- esphome/components/switch/automation.h +24 -0
- esphome/components/switch/switch.cpp +8 -0
- esphome/components/switch/switch.h +8 -0
- esphome/components/sx126x/sx126x.cpp +0 -2
- esphome/components/sx127x/sx127x.cpp +0 -2
- esphome/components/sx1509/__init__.py +7 -5
- esphome/components/sx1509/output/sx1509_float_output.cpp +1 -1
- esphome/components/sx1509/sx1509.cpp +0 -2
- esphome/components/syslog/esphome_syslog.cpp +1 -1
- esphome/components/tc74/tc74.cpp +0 -1
- esphome/components/tca9548a/tca9548a.cpp +0 -1
- esphome/components/tca9555/tca9555.cpp +0 -1
- esphome/components/tcs34725/tcs34725.cpp +0 -1
- esphome/components/tee501/tee501.cpp +0 -1
- esphome/components/tem3200/tem3200.cpp +0 -2
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +0 -1
- esphome/components/template/cover/template_cover.cpp +0 -1
- esphome/components/template/select/template_select.cpp +0 -1
- esphome/components/template/text/template_text.cpp +0 -2
- esphome/components/template/valve/template_valve.cpp +0 -1
- esphome/components/text/__init__.py +0 -1
- esphome/components/text/text_traits.h +2 -0
- esphome/components/text_sensor/__init__.py +2 -1
- esphome/components/text_sensor/text_sensor.cpp +0 -2
- esphome/components/text_sensor/text_sensor.h +0 -8
- esphome/components/thermostat/climate.py +4 -4
- esphome/components/time/__init__.py +7 -4
- esphome/components/time/real_time_clock.cpp +16 -3
- esphome/components/tlc59208f/tlc59208f_output.cpp +0 -2
- esphome/components/tlc5947/tlc5947.cpp +0 -2
- esphome/components/tlc5971/tlc5971.cpp +0 -2
- esphome/components/tm1621/tm1621.cpp +0 -2
- esphome/components/tm1637/tm1637.cpp +0 -2
- esphome/components/tm1638/tm1638.cpp +0 -2
- esphome/components/tm1651/__init__.py +45 -48
- esphome/components/tm1651/tm1651.cpp +213 -47
- esphome/components/tm1651/tm1651.h +37 -32
- esphome/components/tmp117/tmp117.cpp +0 -2
- esphome/components/tsl2561/tsl2561.cpp +0 -1
- esphome/components/tsl2591/tsl2591.cpp +0 -1
- esphome/components/tt21100/touchscreen/tt21100.cpp +0 -2
- esphome/components/ttp229_bsf/ttp229_bsf.cpp +0 -1
- esphome/components/ttp229_lsf/ttp229_lsf.cpp +0 -1
- esphome/components/tuya/climate/__init__.py +9 -10
- esphome/components/tuya/number/__init__.py +8 -6
- esphome/components/tx20/tx20.cpp +0 -1
- esphome/components/uart/uart_component_esp32_arduino.cpp +0 -1
- esphome/components/uart/uart_component_esp8266.cpp +0 -1
- esphome/components/uart/uart_component_esp_idf.cpp +0 -2
- esphome/components/uart/uart_component_libretiny.cpp +0 -2
- esphome/components/uart/uart_component_rp2040.cpp +0 -2
- esphome/components/udp/__init__.py +1 -1
- esphome/components/ufire_ec/sensor.py +1 -2
- esphome/components/ufire_ec/ufire_ec.cpp +0 -2
- esphome/components/ufire_ise/sensor.py +1 -2
- esphome/components/ufire_ise/ufire_ise.cpp +0 -2
- esphome/components/ultrasonic/ultrasonic_sensor.cpp +0 -1
- esphome/components/update/__init__.py +0 -1
- esphome/components/uptime/sensor/uptime_seconds_sensor.cpp +0 -1
- esphome/components/uptime/sensor/uptime_seconds_sensor.h +0 -2
- esphome/components/usb_host/usb_host_client.cpp +0 -1
- esphome/components/usb_host/usb_host_component.cpp +0 -1
- esphome/components/valve/__init__.py +0 -1
- esphome/components/veml3235/veml3235.cpp +0 -3
- esphome/components/veml7700/veml7700.cpp +0 -2
- esphome/components/version/version_text_sensor.cpp +0 -1
- esphome/components/version/version_text_sensor.h +0 -1
- esphome/components/vl53l0x/vl53l0x_sensor.cpp +0 -4
- esphome/components/voice_assistant/voice_assistant.cpp +9 -8
- esphome/components/web_server/__init__.py +13 -0
- esphome/components/web_server/web_server.cpp +188 -353
- esphome/components/web_server/web_server.h +61 -1
- esphome/components/web_server_base/__init__.py +1 -1
- esphome/components/web_server_base/web_server_base.cpp +2 -0
- esphome/components/web_server_base/web_server_base.h +6 -0
- esphome/components/web_server_idf/web_server_idf.cpp +10 -8
- esphome/components/web_server_idf/web_server_idf.h +2 -0
- esphome/components/weikai_i2c/weikai_i2c.cpp +1 -2
- esphome/components/weikai_spi/weikai_spi.cpp +1 -1
- esphome/components/wifi/__init__.py +17 -43
- esphome/components/wifi/wifi_component.cpp +100 -36
- esphome/components/wifi/wifi_component.h +5 -1
- esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -0
- esphome/components/wifi/wifi_component_esp_idf.cpp +30 -0
- esphome/components/wifi_info/wifi_info_text_sensor.h +0 -6
- esphome/components/wifi_signal/wifi_signal_sensor.h +0 -1
- esphome/components/wireguard/wireguard.cpp +0 -2
- esphome/components/x9c/x9c.cpp +0 -2
- esphome/components/xgzp68xx/xgzp68xx.cpp +0 -1
- esphome/components/xl9535/xl9535.cpp +0 -2
- esphome/components/zephyr/__init__.py +252 -0
- esphome/components/zephyr/const.py +16 -0
- esphome/components/zephyr/core.cpp +90 -0
- esphome/components/zephyr/gpio.cpp +120 -0
- esphome/components/zephyr/gpio.h +38 -0
- esphome/components/zephyr/pre_build.py.script +4 -0
- esphome/components/zephyr/preferences.cpp +156 -0
- esphome/components/zephyr/preferences.h +13 -0
- esphome/config.py +38 -16
- esphome/config_helpers.py +1 -2
- esphome/config_validation.py +12 -16
- esphome/const.py +26 -1
- esphome/core/__init__.py +92 -51
- esphome/core/application.cpp +75 -21
- esphome/core/application.h +106 -171
- esphome/core/color.h +10 -0
- esphome/core/component.cpp +41 -25
- esphome/core/component.h +9 -6
- esphome/core/component_iterator.cpp +61 -261
- esphome/core/component_iterator.h +15 -0
- esphome/core/config.py +26 -11
- esphome/core/defines.h +40 -2
- esphome/core/entity_base.h +18 -0
- esphome/core/entity_helpers.py +45 -10
- esphome/core/helpers.cpp +8 -15
- esphome/core/helpers.h +60 -6
- esphome/core/lock_free_queue.h +1 -1
- esphome/core/scheduler.cpp +311 -77
- esphome/core/scheduler.h +141 -28
- esphome/cpp_generator.py +2 -6
- esphome/cpp_helpers.py +1 -1
- esphome/dashboard/dashboard.py +2 -3
- esphome/dashboard/dns.py +2 -8
- esphome/dashboard/web_server.py +34 -19
- esphome/espota2.py +1 -4
- esphome/git.py +3 -1
- esphome/helpers.py +23 -4
- esphome/log.py +3 -1
- esphome/mqtt.py +3 -5
- esphome/platformio_api.py +7 -4
- esphome/types.py +12 -0
- esphome/util.py +20 -8
- esphome/voluptuous_schema.py +4 -3
- esphome/vscode.py +1 -2
- esphome/wizard.py +1 -4
- esphome/writer.py +16 -108
- esphome/yaml_util.py +7 -5
- {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/METADATA +13 -14
- {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/RECORD +755 -675
- esphome/components/mipi_spi/models/commands.py +0 -82
- /esphome/components/nfc/binary_sensor/{binary_sensor.h → nfc_binary_sensor.h} +0 -0
- {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/WHEEL +0 -0
- {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/entry_points.txt +0 -0
- {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#include "api_frame_helper.h"
|
|
2
2
|
#ifdef USE_API
|
|
3
|
+
#include "api_connection.h" // For ClientInfo struct
|
|
3
4
|
#include "esphome/core/application.h"
|
|
4
5
|
#include "esphome/core/hal.h"
|
|
5
6
|
#include "esphome/core/helpers.h"
|
|
@@ -8,10 +9,19 @@
|
|
|
8
9
|
#include <cstring>
|
|
9
10
|
#include <cinttypes>
|
|
10
11
|
|
|
11
|
-
namespace esphome {
|
|
12
|
-
namespace api {
|
|
12
|
+
namespace esphome::api {
|
|
13
13
|
|
|
14
|
-
static const char *const TAG = "api.
|
|
14
|
+
static const char *const TAG = "api.frame_helper";
|
|
15
|
+
|
|
16
|
+
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->client_info_->get_combined_info().c_str(), ##__VA_ARGS__)
|
|
17
|
+
|
|
18
|
+
#ifdef HELPER_LOG_PACKETS
|
|
19
|
+
#define LOG_PACKET_RECEIVED(buffer) ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(buffer).c_str())
|
|
20
|
+
#define LOG_PACKET_SENDING(data, len) ESP_LOGVV(TAG, "Sending raw: %s", format_hex_pretty(data, len).c_str())
|
|
21
|
+
#else
|
|
22
|
+
#define LOG_PACKET_RECEIVED(buffer) ((void) 0)
|
|
23
|
+
#define LOG_PACKET_SENDING(data, len) ((void) 0)
|
|
24
|
+
#endif
|
|
15
25
|
|
|
16
26
|
const char *api_error_to_str(APIError err) {
|
|
17
27
|
// not using switch to ensure compiler doesn't try to build a big table out of it
|
|
@@ -19,8 +29,6 @@ const char *api_error_to_str(APIError err) {
|
|
|
19
29
|
return "OK";
|
|
20
30
|
} else if (err == APIError::WOULD_BLOCK) {
|
|
21
31
|
return "WOULD_BLOCK";
|
|
22
|
-
} else if (err == APIError::BAD_HANDSHAKE_PACKET_LEN) {
|
|
23
|
-
return "BAD_HANDSHAKE_PACKET_LEN";
|
|
24
32
|
} else if (err == APIError::BAD_INDICATOR) {
|
|
25
33
|
return "BAD_INDICATOR";
|
|
26
34
|
} else if (err == APIError::BAD_DATA_PACKET) {
|
|
@@ -41,6 +49,14 @@ const char *api_error_to_str(APIError err) {
|
|
|
41
49
|
return "SOCKET_READ_FAILED";
|
|
42
50
|
} else if (err == APIError::SOCKET_WRITE_FAILED) {
|
|
43
51
|
return "SOCKET_WRITE_FAILED";
|
|
52
|
+
} else if (err == APIError::OUT_OF_MEMORY) {
|
|
53
|
+
return "OUT_OF_MEMORY";
|
|
54
|
+
} else if (err == APIError::CONNECTION_CLOSED) {
|
|
55
|
+
return "CONNECTION_CLOSED";
|
|
56
|
+
}
|
|
57
|
+
#ifdef USE_API_NOISE
|
|
58
|
+
else if (err == APIError::BAD_HANDSHAKE_PACKET_LEN) {
|
|
59
|
+
return "BAD_HANDSHAKE_PACKET_LEN";
|
|
44
60
|
} else if (err == APIError::HANDSHAKESTATE_READ_FAILED) {
|
|
45
61
|
return "HANDSHAKESTATE_READ_FAILED";
|
|
46
62
|
} else if (err == APIError::HANDSHAKESTATE_WRITE_FAILED) {
|
|
@@ -51,17 +67,14 @@ const char *api_error_to_str(APIError err) {
|
|
|
51
67
|
return "CIPHERSTATE_DECRYPT_FAILED";
|
|
52
68
|
} else if (err == APIError::CIPHERSTATE_ENCRYPT_FAILED) {
|
|
53
69
|
return "CIPHERSTATE_ENCRYPT_FAILED";
|
|
54
|
-
} else if (err == APIError::OUT_OF_MEMORY) {
|
|
55
|
-
return "OUT_OF_MEMORY";
|
|
56
70
|
} else if (err == APIError::HANDSHAKESTATE_SETUP_FAILED) {
|
|
57
71
|
return "HANDSHAKESTATE_SETUP_FAILED";
|
|
58
72
|
} else if (err == APIError::HANDSHAKESTATE_SPLIT_FAILED) {
|
|
59
73
|
return "HANDSHAKESTATE_SPLIT_FAILED";
|
|
60
74
|
} else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) {
|
|
61
75
|
return "BAD_HANDSHAKE_ERROR_BYTE";
|
|
62
|
-
} else if (err == APIError::CONNECTION_CLOSED) {
|
|
63
|
-
return "CONNECTION_CLOSED";
|
|
64
76
|
}
|
|
77
|
+
#endif
|
|
65
78
|
return "UNKNOWN";
|
|
66
79
|
}
|
|
67
80
|
|
|
@@ -76,33 +89,55 @@ APIError APIFrameHelper::loop() {
|
|
|
76
89
|
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
|
|
77
90
|
}
|
|
78
91
|
|
|
92
|
+
// Common socket write error handling
|
|
93
|
+
APIError APIFrameHelper::handle_socket_write_error_() {
|
|
94
|
+
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
|
95
|
+
return APIError::WOULD_BLOCK;
|
|
96
|
+
}
|
|
97
|
+
HELPER_LOG("Socket write failed with errno %d", errno);
|
|
98
|
+
this->state_ = State::FAILED;
|
|
99
|
+
return APIError::SOCKET_WRITE_FAILED;
|
|
100
|
+
}
|
|
101
|
+
|
|
79
102
|
// Helper method to buffer data from IOVs
|
|
80
|
-
void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len
|
|
103
|
+
void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len,
|
|
104
|
+
uint16_t offset) {
|
|
81
105
|
SendBuffer buffer;
|
|
82
|
-
buffer.
|
|
106
|
+
buffer.size = total_write_len - offset;
|
|
107
|
+
buffer.data = std::make_unique<uint8_t[]>(buffer.size);
|
|
108
|
+
|
|
109
|
+
uint16_t to_skip = offset;
|
|
110
|
+
uint16_t write_pos = 0;
|
|
111
|
+
|
|
83
112
|
for (int i = 0; i < iovcnt; i++) {
|
|
84
|
-
|
|
85
|
-
|
|
113
|
+
if (to_skip >= iov[i].iov_len) {
|
|
114
|
+
// Skip this entire segment
|
|
115
|
+
to_skip -= static_cast<uint16_t>(iov[i].iov_len);
|
|
116
|
+
} else {
|
|
117
|
+
// Include this segment (partially or fully)
|
|
118
|
+
const uint8_t *src = reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_skip;
|
|
119
|
+
uint16_t len = static_cast<uint16_t>(iov[i].iov_len) - to_skip;
|
|
120
|
+
std::memcpy(buffer.data.get() + write_pos, src, len);
|
|
121
|
+
write_pos += len;
|
|
122
|
+
to_skip = 0;
|
|
123
|
+
}
|
|
86
124
|
}
|
|
87
125
|
this->tx_buf_.push_back(std::move(buffer));
|
|
88
126
|
}
|
|
89
127
|
|
|
90
128
|
// This method writes data to socket or buffers it
|
|
91
|
-
APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
|
129
|
+
APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, uint16_t total_write_len) {
|
|
92
130
|
// Returns APIError::OK if successful (or would block, but data has been buffered)
|
|
93
131
|
// Returns APIError::SOCKET_WRITE_FAILED if socket write failed, and sets state to FAILED
|
|
94
132
|
|
|
95
133
|
if (iovcnt == 0)
|
|
96
134
|
return APIError::OK; // Nothing to do, success
|
|
97
135
|
|
|
98
|
-
uint16_t total_write_len = 0;
|
|
99
|
-
for (int i = 0; i < iovcnt; i++) {
|
|
100
136
|
#ifdef HELPER_LOG_PACKETS
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
#endif
|
|
104
|
-
total_write_len += static_cast<uint16_t>(iov[i].iov_len);
|
|
137
|
+
for (int i = 0; i < iovcnt; i++) {
|
|
138
|
+
LOG_PACKET_SENDING(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
|
|
105
139
|
}
|
|
140
|
+
#endif
|
|
106
141
|
|
|
107
142
|
// Try to send any existing buffered data first if there is any
|
|
108
143
|
if (!this->tx_buf_.empty()) {
|
|
@@ -115,46 +150,27 @@ APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
|
|
115
150
|
// If there is still data in the buffer, we can't send, buffer
|
|
116
151
|
// the new data and return
|
|
117
152
|
if (!this->tx_buf_.empty()) {
|
|
118
|
-
this->buffer_data_from_iov_(iov, iovcnt, total_write_len);
|
|
153
|
+
this->buffer_data_from_iov_(iov, iovcnt, total_write_len, 0);
|
|
119
154
|
return APIError::OK; // Success, data buffered
|
|
120
155
|
}
|
|
121
156
|
}
|
|
122
157
|
|
|
123
158
|
// Try to send directly if no buffered data
|
|
124
|
-
|
|
159
|
+
// Optimize for single iovec case (common for plaintext API)
|
|
160
|
+
ssize_t sent =
|
|
161
|
+
(iovcnt == 1) ? this->socket_->write(iov[0].iov_base, iov[0].iov_len) : this->socket_->writev(iov, iovcnt);
|
|
125
162
|
|
|
126
163
|
if (sent == -1) {
|
|
127
|
-
|
|
164
|
+
APIError err = this->handle_socket_write_error_();
|
|
165
|
+
if (err == APIError::WOULD_BLOCK) {
|
|
128
166
|
// Socket would block, buffer the data
|
|
129
|
-
this->buffer_data_from_iov_(iov, iovcnt, total_write_len);
|
|
167
|
+
this->buffer_data_from_iov_(iov, iovcnt, total_write_len, 0);
|
|
130
168
|
return APIError::OK; // Success, data buffered
|
|
131
169
|
}
|
|
132
|
-
// Socket
|
|
133
|
-
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
|
|
134
|
-
this->state_ = State::FAILED;
|
|
135
|
-
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
|
|
170
|
+
return err; // Socket write failed
|
|
136
171
|
} else if (static_cast<uint16_t>(sent) < total_write_len) {
|
|
137
172
|
// Partially sent, buffer the remaining data
|
|
138
|
-
|
|
139
|
-
uint16_t to_consume = static_cast<uint16_t>(sent);
|
|
140
|
-
uint16_t remaining = total_write_len - static_cast<uint16_t>(sent);
|
|
141
|
-
|
|
142
|
-
buffer.data.reserve(remaining);
|
|
143
|
-
|
|
144
|
-
for (int i = 0; i < iovcnt; i++) {
|
|
145
|
-
if (to_consume >= iov[i].iov_len) {
|
|
146
|
-
// This segment was fully sent
|
|
147
|
-
to_consume -= static_cast<uint16_t>(iov[i].iov_len);
|
|
148
|
-
} else {
|
|
149
|
-
// This segment was partially sent or not sent at all
|
|
150
|
-
const uint8_t *data = reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume;
|
|
151
|
-
uint16_t len = static_cast<uint16_t>(iov[i].iov_len) - to_consume;
|
|
152
|
-
buffer.data.insert(buffer.data.end(), data, data + len);
|
|
153
|
-
to_consume = 0;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
this->tx_buf_.push_back(std::move(buffer));
|
|
173
|
+
this->buffer_data_from_iov_(iov, iovcnt, total_write_len, static_cast<uint16_t>(sent));
|
|
158
174
|
}
|
|
159
175
|
|
|
160
176
|
return APIError::OK; // Success, all data sent or buffered
|
|
@@ -173,14 +189,7 @@ APIError APIFrameHelper::try_send_tx_buf_() {
|
|
|
173
189
|
ssize_t sent = this->socket_->write(front_buffer.current_data(), front_buffer.remaining());
|
|
174
190
|
|
|
175
191
|
if (sent == -1) {
|
|
176
|
-
|
|
177
|
-
// Real socket error (not just would block)
|
|
178
|
-
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
|
|
179
|
-
this->state_ = State::FAILED;
|
|
180
|
-
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
|
|
181
|
-
}
|
|
182
|
-
// Socket would block, we'll try again later
|
|
183
|
-
return APIError::WOULD_BLOCK;
|
|
192
|
+
return this->handle_socket_write_error_();
|
|
184
193
|
} else if (sent == 0) {
|
|
185
194
|
// Nothing sent but not an error
|
|
186
195
|
return APIError::WOULD_BLOCK;
|
|
@@ -203,13 +212,13 @@ APIError APIFrameHelper::try_send_tx_buf_() {
|
|
|
203
212
|
|
|
204
213
|
APIError APIFrameHelper::init_common_() {
|
|
205
214
|
if (state_ != State::INITIALIZE || this->socket_ == nullptr) {
|
|
206
|
-
|
|
215
|
+
HELPER_LOG("Bad state for init %d", (int) state_);
|
|
207
216
|
return APIError::BAD_STATE;
|
|
208
217
|
}
|
|
209
218
|
int err = this->socket_->setblocking(false);
|
|
210
219
|
if (err != 0) {
|
|
211
220
|
state_ = State::FAILED;
|
|
212
|
-
|
|
221
|
+
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
|
213
222
|
return APIError::TCP_NONBLOCKING_FAILED;
|
|
214
223
|
}
|
|
215
224
|
|
|
@@ -217,14 +226,12 @@ APIError APIFrameHelper::init_common_() {
|
|
|
217
226
|
err = this->socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
|
218
227
|
if (err != 0) {
|
|
219
228
|
state_ = State::FAILED;
|
|
220
|
-
|
|
229
|
+
HELPER_LOG("Setting nodelay failed with errno %d", errno);
|
|
221
230
|
return APIError::TCP_NODELAY_FAILED;
|
|
222
231
|
}
|
|
223
232
|
return APIError::OK;
|
|
224
233
|
}
|
|
225
234
|
|
|
226
|
-
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->info_.c_str(), ##__VA_ARGS__)
|
|
227
|
-
|
|
228
235
|
APIError APIFrameHelper::handle_socket_read_result_(ssize_t received) {
|
|
229
236
|
if (received == -1) {
|
|
230
237
|
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
|
@@ -240,840 +247,6 @@ APIError APIFrameHelper::handle_socket_read_result_(ssize_t received) {
|
|
|
240
247
|
}
|
|
241
248
|
return APIError::OK;
|
|
242
249
|
}
|
|
243
|
-
// uncomment to log raw packets
|
|
244
|
-
//#define HELPER_LOG_PACKETS
|
|
245
|
-
|
|
246
|
-
#ifdef USE_API_NOISE
|
|
247
|
-
static const char *const PROLOGUE_INIT = "NoiseAPIInit";
|
|
248
|
-
|
|
249
|
-
/// Convert a noise error code to a readable error
|
|
250
|
-
std::string noise_err_to_str(int err) {
|
|
251
|
-
if (err == NOISE_ERROR_NO_MEMORY)
|
|
252
|
-
return "NO_MEMORY";
|
|
253
|
-
if (err == NOISE_ERROR_UNKNOWN_ID)
|
|
254
|
-
return "UNKNOWN_ID";
|
|
255
|
-
if (err == NOISE_ERROR_UNKNOWN_NAME)
|
|
256
|
-
return "UNKNOWN_NAME";
|
|
257
|
-
if (err == NOISE_ERROR_MAC_FAILURE)
|
|
258
|
-
return "MAC_FAILURE";
|
|
259
|
-
if (err == NOISE_ERROR_NOT_APPLICABLE)
|
|
260
|
-
return "NOT_APPLICABLE";
|
|
261
|
-
if (err == NOISE_ERROR_SYSTEM)
|
|
262
|
-
return "SYSTEM";
|
|
263
|
-
if (err == NOISE_ERROR_REMOTE_KEY_REQUIRED)
|
|
264
|
-
return "REMOTE_KEY_REQUIRED";
|
|
265
|
-
if (err == NOISE_ERROR_LOCAL_KEY_REQUIRED)
|
|
266
|
-
return "LOCAL_KEY_REQUIRED";
|
|
267
|
-
if (err == NOISE_ERROR_PSK_REQUIRED)
|
|
268
|
-
return "PSK_REQUIRED";
|
|
269
|
-
if (err == NOISE_ERROR_INVALID_LENGTH)
|
|
270
|
-
return "INVALID_LENGTH";
|
|
271
|
-
if (err == NOISE_ERROR_INVALID_PARAM)
|
|
272
|
-
return "INVALID_PARAM";
|
|
273
|
-
if (err == NOISE_ERROR_INVALID_STATE)
|
|
274
|
-
return "INVALID_STATE";
|
|
275
|
-
if (err == NOISE_ERROR_INVALID_NONCE)
|
|
276
|
-
return "INVALID_NONCE";
|
|
277
|
-
if (err == NOISE_ERROR_INVALID_PRIVATE_KEY)
|
|
278
|
-
return "INVALID_PRIVATE_KEY";
|
|
279
|
-
if (err == NOISE_ERROR_INVALID_PUBLIC_KEY)
|
|
280
|
-
return "INVALID_PUBLIC_KEY";
|
|
281
|
-
if (err == NOISE_ERROR_INVALID_FORMAT)
|
|
282
|
-
return "INVALID_FORMAT";
|
|
283
|
-
if (err == NOISE_ERROR_INVALID_SIGNATURE)
|
|
284
|
-
return "INVALID_SIGNATURE";
|
|
285
|
-
return to_string(err);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/// Initialize the frame helper, returns OK if successful.
|
|
289
|
-
APIError APINoiseFrameHelper::init() {
|
|
290
|
-
APIError err = init_common_();
|
|
291
|
-
if (err != APIError::OK) {
|
|
292
|
-
return err;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// init prologue
|
|
296
|
-
prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
|
|
297
|
-
|
|
298
|
-
state_ = State::CLIENT_HELLO;
|
|
299
|
-
return APIError::OK;
|
|
300
|
-
}
|
|
301
|
-
/// Run through handshake messages (if in that phase)
|
|
302
|
-
APIError APINoiseFrameHelper::loop() {
|
|
303
|
-
// During handshake phase, process as many actions as possible until we can't progress
|
|
304
|
-
// socket_->ready() stays true until next main loop, but state_action() will return
|
|
305
|
-
// WOULD_BLOCK when no more data is available to read
|
|
306
|
-
while (state_ != State::DATA && this->socket_->ready()) {
|
|
307
|
-
APIError err = state_action_();
|
|
308
|
-
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
|
309
|
-
return err;
|
|
310
|
-
}
|
|
311
|
-
if (err == APIError::WOULD_BLOCK) {
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Use base class implementation for buffer sending
|
|
317
|
-
return APIFrameHelper::loop();
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
|
321
|
-
*
|
|
322
|
-
* @param frame: The struct to hold the frame information in.
|
|
323
|
-
* msg_start: points to the start of the payload - this pointer is only valid until the next
|
|
324
|
-
* try_receive_raw_ call
|
|
325
|
-
*
|
|
326
|
-
* @return 0 if a full packet is in rx_buf_
|
|
327
|
-
* @return -1 if error, check errno.
|
|
328
|
-
*
|
|
329
|
-
* errno EWOULDBLOCK: Packet could not be read without blocking. Try again later.
|
|
330
|
-
* errno ENOMEM: Not enough memory for reading packet.
|
|
331
|
-
* errno API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
|
332
|
-
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
|
|
333
|
-
*/
|
|
334
|
-
APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|
335
|
-
if (frame == nullptr) {
|
|
336
|
-
HELPER_LOG("Bad argument for try_read_frame_");
|
|
337
|
-
return APIError::BAD_ARG;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// read header
|
|
341
|
-
if (rx_header_buf_len_ < 3) {
|
|
342
|
-
// no header information yet
|
|
343
|
-
uint8_t to_read = 3 - rx_header_buf_len_;
|
|
344
|
-
ssize_t received = this->socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
|
|
345
|
-
APIError err = handle_socket_read_result_(received);
|
|
346
|
-
if (err != APIError::OK) {
|
|
347
|
-
return err;
|
|
348
|
-
}
|
|
349
|
-
rx_header_buf_len_ += static_cast<uint8_t>(received);
|
|
350
|
-
if (static_cast<uint8_t>(received) != to_read) {
|
|
351
|
-
// not a full read
|
|
352
|
-
return APIError::WOULD_BLOCK;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (rx_header_buf_[0] != 0x01) {
|
|
356
|
-
state_ = State::FAILED;
|
|
357
|
-
HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
|
|
358
|
-
return APIError::BAD_INDICATOR;
|
|
359
|
-
}
|
|
360
|
-
// header reading done
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// read body
|
|
364
|
-
uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
|
|
365
|
-
|
|
366
|
-
if (state_ != State::DATA && msg_size > 128) {
|
|
367
|
-
// for handshake message only permit up to 128 bytes
|
|
368
|
-
state_ = State::FAILED;
|
|
369
|
-
HELPER_LOG("Bad packet len for handshake: %d", msg_size);
|
|
370
|
-
return APIError::BAD_HANDSHAKE_PACKET_LEN;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// reserve space for body
|
|
374
|
-
if (rx_buf_.size() != msg_size) {
|
|
375
|
-
rx_buf_.resize(msg_size);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (rx_buf_len_ < msg_size) {
|
|
379
|
-
// more data to read
|
|
380
|
-
uint16_t to_read = msg_size - rx_buf_len_;
|
|
381
|
-
ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
|
382
|
-
APIError err = handle_socket_read_result_(received);
|
|
383
|
-
if (err != APIError::OK) {
|
|
384
|
-
return err;
|
|
385
|
-
}
|
|
386
|
-
rx_buf_len_ += static_cast<uint16_t>(received);
|
|
387
|
-
if (static_cast<uint16_t>(received) != to_read) {
|
|
388
|
-
// not all read
|
|
389
|
-
return APIError::WOULD_BLOCK;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// uncomment for even more debugging
|
|
394
|
-
#ifdef HELPER_LOG_PACKETS
|
|
395
|
-
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
|
|
396
|
-
#endif
|
|
397
|
-
frame->msg = std::move(rx_buf_);
|
|
398
|
-
// consume msg
|
|
399
|
-
rx_buf_ = {};
|
|
400
|
-
rx_buf_len_ = 0;
|
|
401
|
-
rx_header_buf_len_ = 0;
|
|
402
|
-
return APIError::OK;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/** To be called from read/write methods.
|
|
406
|
-
*
|
|
407
|
-
* This method runs through the internal handshake methods, if in that state.
|
|
408
|
-
*
|
|
409
|
-
* If the handshake is still active when this method returns and a read/write can't take place at
|
|
410
|
-
* the moment, returns WOULD_BLOCK.
|
|
411
|
-
* If an error occurred, returns that error. Only returns OK if the transport is ready for data
|
|
412
|
-
* traffic.
|
|
413
|
-
*/
|
|
414
|
-
APIError APINoiseFrameHelper::state_action_() {
|
|
415
|
-
int err;
|
|
416
|
-
APIError aerr;
|
|
417
|
-
if (state_ == State::INITIALIZE) {
|
|
418
|
-
HELPER_LOG("Bad state for method: %d", (int) state_);
|
|
419
|
-
return APIError::BAD_STATE;
|
|
420
|
-
}
|
|
421
|
-
if (state_ == State::CLIENT_HELLO) {
|
|
422
|
-
// waiting for client hello
|
|
423
|
-
ParsedFrame frame;
|
|
424
|
-
aerr = try_read_frame_(&frame);
|
|
425
|
-
if (aerr == APIError::BAD_INDICATOR) {
|
|
426
|
-
send_explicit_handshake_reject_("Bad indicator byte");
|
|
427
|
-
return aerr;
|
|
428
|
-
}
|
|
429
|
-
if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
|
|
430
|
-
send_explicit_handshake_reject_("Bad handshake packet len");
|
|
431
|
-
return aerr;
|
|
432
|
-
}
|
|
433
|
-
if (aerr != APIError::OK)
|
|
434
|
-
return aerr;
|
|
435
|
-
// ignore contents, may be used in future for flags
|
|
436
|
-
// Reserve space for: existing prologue + 2 size bytes + frame data
|
|
437
|
-
prologue_.reserve(prologue_.size() + 2 + frame.msg.size());
|
|
438
|
-
prologue_.push_back((uint8_t) (frame.msg.size() >> 8));
|
|
439
|
-
prologue_.push_back((uint8_t) frame.msg.size());
|
|
440
|
-
prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end());
|
|
441
|
-
|
|
442
|
-
state_ = State::SERVER_HELLO;
|
|
443
|
-
}
|
|
444
|
-
if (state_ == State::SERVER_HELLO) {
|
|
445
|
-
// send server hello
|
|
446
|
-
const std::string &name = App.get_name();
|
|
447
|
-
const std::string &mac = get_mac_address();
|
|
448
|
-
|
|
449
|
-
std::vector<uint8_t> msg;
|
|
450
|
-
// Reserve space for: 1 byte proto + name + null + mac + null
|
|
451
|
-
msg.reserve(1 + name.size() + 1 + mac.size() + 1);
|
|
452
|
-
|
|
453
|
-
// chosen proto
|
|
454
|
-
msg.push_back(0x01);
|
|
455
|
-
|
|
456
|
-
// node name, terminated by null byte
|
|
457
|
-
const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
|
|
458
|
-
msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
|
|
459
|
-
// node mac, terminated by null byte
|
|
460
|
-
const uint8_t *mac_ptr = reinterpret_cast<const uint8_t *>(mac.c_str());
|
|
461
|
-
msg.insert(msg.end(), mac_ptr, mac_ptr + mac.size() + 1);
|
|
462
|
-
|
|
463
|
-
aerr = write_frame_(msg.data(), msg.size());
|
|
464
|
-
if (aerr != APIError::OK)
|
|
465
|
-
return aerr;
|
|
466
|
-
|
|
467
|
-
// start handshake
|
|
468
|
-
aerr = init_handshake_();
|
|
469
|
-
if (aerr != APIError::OK)
|
|
470
|
-
return aerr;
|
|
471
|
-
|
|
472
|
-
state_ = State::HANDSHAKE;
|
|
473
|
-
}
|
|
474
|
-
if (state_ == State::HANDSHAKE) {
|
|
475
|
-
int action = noise_handshakestate_get_action(handshake_);
|
|
476
|
-
if (action == NOISE_ACTION_READ_MESSAGE) {
|
|
477
|
-
// waiting for handshake msg
|
|
478
|
-
ParsedFrame frame;
|
|
479
|
-
aerr = try_read_frame_(&frame);
|
|
480
|
-
if (aerr == APIError::BAD_INDICATOR) {
|
|
481
|
-
send_explicit_handshake_reject_("Bad indicator byte");
|
|
482
|
-
return aerr;
|
|
483
|
-
}
|
|
484
|
-
if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
|
|
485
|
-
send_explicit_handshake_reject_("Bad handshake packet len");
|
|
486
|
-
return aerr;
|
|
487
|
-
}
|
|
488
|
-
if (aerr != APIError::OK)
|
|
489
|
-
return aerr;
|
|
490
|
-
|
|
491
|
-
if (frame.msg.empty()) {
|
|
492
|
-
send_explicit_handshake_reject_("Empty handshake message");
|
|
493
|
-
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
|
|
494
|
-
} else if (frame.msg[0] != 0x00) {
|
|
495
|
-
HELPER_LOG("Bad handshake error byte: %u", frame.msg[0]);
|
|
496
|
-
send_explicit_handshake_reject_("Bad handshake error byte");
|
|
497
|
-
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
NoiseBuffer mbuf;
|
|
501
|
-
noise_buffer_init(mbuf);
|
|
502
|
-
noise_buffer_set_input(mbuf, frame.msg.data() + 1, frame.msg.size() - 1);
|
|
503
|
-
err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr);
|
|
504
|
-
if (err != 0) {
|
|
505
|
-
state_ = State::FAILED;
|
|
506
|
-
HELPER_LOG("noise_handshakestate_read_message failed: %s", noise_err_to_str(err).c_str());
|
|
507
|
-
if (err == NOISE_ERROR_MAC_FAILURE) {
|
|
508
|
-
send_explicit_handshake_reject_("Handshake MAC failure");
|
|
509
|
-
} else {
|
|
510
|
-
send_explicit_handshake_reject_("Handshake error");
|
|
511
|
-
}
|
|
512
|
-
return APIError::HANDSHAKESTATE_READ_FAILED;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
aerr = check_handshake_finished_();
|
|
516
|
-
if (aerr != APIError::OK)
|
|
517
|
-
return aerr;
|
|
518
|
-
} else if (action == NOISE_ACTION_WRITE_MESSAGE) {
|
|
519
|
-
uint8_t buffer[65];
|
|
520
|
-
NoiseBuffer mbuf;
|
|
521
|
-
noise_buffer_init(mbuf);
|
|
522
|
-
noise_buffer_set_output(mbuf, buffer + 1, sizeof(buffer) - 1);
|
|
523
|
-
|
|
524
|
-
err = noise_handshakestate_write_message(handshake_, &mbuf, nullptr);
|
|
525
|
-
if (err != 0) {
|
|
526
|
-
state_ = State::FAILED;
|
|
527
|
-
HELPER_LOG("noise_handshakestate_write_message failed: %s", noise_err_to_str(err).c_str());
|
|
528
|
-
return APIError::HANDSHAKESTATE_WRITE_FAILED;
|
|
529
|
-
}
|
|
530
|
-
buffer[0] = 0x00; // success
|
|
531
|
-
|
|
532
|
-
aerr = write_frame_(buffer, mbuf.size + 1);
|
|
533
|
-
if (aerr != APIError::OK)
|
|
534
|
-
return aerr;
|
|
535
|
-
aerr = check_handshake_finished_();
|
|
536
|
-
if (aerr != APIError::OK)
|
|
537
|
-
return aerr;
|
|
538
|
-
} else {
|
|
539
|
-
// bad state for action
|
|
540
|
-
state_ = State::FAILED;
|
|
541
|
-
HELPER_LOG("Bad action for handshake: %d", action);
|
|
542
|
-
return APIError::HANDSHAKESTATE_BAD_STATE;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
if (state_ == State::CLOSED || state_ == State::FAILED) {
|
|
546
|
-
return APIError::BAD_STATE;
|
|
547
|
-
}
|
|
548
|
-
return APIError::OK;
|
|
549
|
-
}
|
|
550
|
-
void APINoiseFrameHelper::send_explicit_handshake_reject_(const std::string &reason) {
|
|
551
|
-
std::vector<uint8_t> data;
|
|
552
|
-
data.resize(reason.length() + 1);
|
|
553
|
-
data[0] = 0x01; // failure
|
|
554
|
-
|
|
555
|
-
// Copy error message in bulk
|
|
556
|
-
if (!reason.empty()) {
|
|
557
|
-
std::memcpy(data.data() + 1, reason.c_str(), reason.length());
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// temporarily remove failed state
|
|
561
|
-
auto orig_state = state_;
|
|
562
|
-
state_ = State::EXPLICIT_REJECT;
|
|
563
|
-
write_frame_(data.data(), data.size());
|
|
564
|
-
state_ = orig_state;
|
|
565
|
-
}
|
|
566
|
-
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|
567
|
-
int err;
|
|
568
|
-
APIError aerr;
|
|
569
|
-
aerr = state_action_();
|
|
570
|
-
if (aerr != APIError::OK) {
|
|
571
|
-
return aerr;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
if (state_ != State::DATA) {
|
|
575
|
-
return APIError::WOULD_BLOCK;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
ParsedFrame frame;
|
|
579
|
-
aerr = try_read_frame_(&frame);
|
|
580
|
-
if (aerr != APIError::OK)
|
|
581
|
-
return aerr;
|
|
582
|
-
|
|
583
|
-
NoiseBuffer mbuf;
|
|
584
|
-
noise_buffer_init(mbuf);
|
|
585
|
-
noise_buffer_set_inout(mbuf, frame.msg.data(), frame.msg.size(), frame.msg.size());
|
|
586
|
-
err = noise_cipherstate_decrypt(recv_cipher_, &mbuf);
|
|
587
|
-
if (err != 0) {
|
|
588
|
-
state_ = State::FAILED;
|
|
589
|
-
HELPER_LOG("noise_cipherstate_decrypt failed: %s", noise_err_to_str(err).c_str());
|
|
590
|
-
return APIError::CIPHERSTATE_DECRYPT_FAILED;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
uint16_t msg_size = mbuf.size;
|
|
594
|
-
uint8_t *msg_data = frame.msg.data();
|
|
595
|
-
if (msg_size < 4) {
|
|
596
|
-
state_ = State::FAILED;
|
|
597
|
-
HELPER_LOG("Bad data packet: size %d too short", msg_size);
|
|
598
|
-
return APIError::BAD_DATA_PACKET;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
|
|
602
|
-
uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
|
|
603
|
-
if (data_len > msg_size - 4) {
|
|
604
|
-
state_ = State::FAILED;
|
|
605
|
-
HELPER_LOG("Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
|
|
606
|
-
return APIError::BAD_DATA_PACKET;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
buffer->container = std::move(frame.msg);
|
|
610
|
-
buffer->data_offset = 4;
|
|
611
|
-
buffer->data_len = data_len;
|
|
612
|
-
buffer->type = type;
|
|
613
|
-
return APIError::OK;
|
|
614
|
-
}
|
|
615
|
-
APIError APINoiseFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
|
|
616
|
-
// Resize to include MAC space (required for Noise encryption)
|
|
617
|
-
buffer.get_buffer()->resize(buffer.get_buffer()->size() + frame_footer_size_);
|
|
618
|
-
PacketInfo packet{type, 0,
|
|
619
|
-
static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_ - frame_footer_size_)};
|
|
620
|
-
return write_protobuf_packets(buffer, std::span<const PacketInfo>(&packet, 1));
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) {
|
|
624
|
-
APIError aerr = state_action_();
|
|
625
|
-
if (aerr != APIError::OK) {
|
|
626
|
-
return aerr;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
if (state_ != State::DATA) {
|
|
630
|
-
return APIError::WOULD_BLOCK;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
if (packets.empty()) {
|
|
634
|
-
return APIError::OK;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
|
638
|
-
uint8_t *buffer_data = raw_buffer->data(); // Cache buffer pointer
|
|
639
|
-
|
|
640
|
-
this->reusable_iovs_.clear();
|
|
641
|
-
this->reusable_iovs_.reserve(packets.size());
|
|
642
|
-
|
|
643
|
-
// We need to encrypt each packet in place
|
|
644
|
-
for (const auto &packet : packets) {
|
|
645
|
-
// The buffer already has padding at offset
|
|
646
|
-
uint8_t *buf_start = buffer_data + packet.offset;
|
|
647
|
-
|
|
648
|
-
// Write noise header
|
|
649
|
-
buf_start[0] = 0x01; // indicator
|
|
650
|
-
// buf_start[1], buf_start[2] to be set after encryption
|
|
651
|
-
|
|
652
|
-
// Write message header (to be encrypted)
|
|
653
|
-
const uint8_t msg_offset = 3;
|
|
654
|
-
buf_start[msg_offset] = static_cast<uint8_t>(packet.message_type >> 8); // type high byte
|
|
655
|
-
buf_start[msg_offset + 1] = static_cast<uint8_t>(packet.message_type); // type low byte
|
|
656
|
-
buf_start[msg_offset + 2] = static_cast<uint8_t>(packet.payload_size >> 8); // data_len high byte
|
|
657
|
-
buf_start[msg_offset + 3] = static_cast<uint8_t>(packet.payload_size); // data_len low byte
|
|
658
|
-
// payload data is already in the buffer starting at offset + 7
|
|
659
|
-
|
|
660
|
-
// Make sure we have space for MAC
|
|
661
|
-
// The buffer should already have been sized appropriately
|
|
662
|
-
|
|
663
|
-
// Encrypt the message in place
|
|
664
|
-
NoiseBuffer mbuf;
|
|
665
|
-
noise_buffer_init(mbuf);
|
|
666
|
-
noise_buffer_set_inout(mbuf, buf_start + msg_offset, 4 + packet.payload_size,
|
|
667
|
-
4 + packet.payload_size + frame_footer_size_);
|
|
668
|
-
|
|
669
|
-
int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
|
|
670
|
-
if (err != 0) {
|
|
671
|
-
state_ = State::FAILED;
|
|
672
|
-
HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
|
|
673
|
-
return APIError::CIPHERSTATE_ENCRYPT_FAILED;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
// Fill in the encrypted size
|
|
677
|
-
buf_start[1] = static_cast<uint8_t>(mbuf.size >> 8);
|
|
678
|
-
buf_start[2] = static_cast<uint8_t>(mbuf.size);
|
|
679
|
-
|
|
680
|
-
// Add iovec for this encrypted packet
|
|
681
|
-
this->reusable_iovs_.push_back(
|
|
682
|
-
{buf_start, static_cast<size_t>(3 + mbuf.size)}); // indicator + size + encrypted data
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// Send all encrypted packets in one writev call
|
|
686
|
-
return this->write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {
|
|
690
|
-
uint8_t header[3];
|
|
691
|
-
header[0] = 0x01; // indicator
|
|
692
|
-
header[1] = (uint8_t) (len >> 8);
|
|
693
|
-
header[2] = (uint8_t) len;
|
|
694
|
-
|
|
695
|
-
struct iovec iov[2];
|
|
696
|
-
iov[0].iov_base = header;
|
|
697
|
-
iov[0].iov_len = 3;
|
|
698
|
-
if (len == 0) {
|
|
699
|
-
return this->write_raw_(iov, 1);
|
|
700
|
-
}
|
|
701
|
-
iov[1].iov_base = const_cast<uint8_t *>(data);
|
|
702
|
-
iov[1].iov_len = len;
|
|
703
|
-
|
|
704
|
-
return this->write_raw_(iov, 2);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
/** Initiate the data structures for the handshake.
|
|
708
|
-
*
|
|
709
|
-
* @return 0 on success, -1 on error (check errno)
|
|
710
|
-
*/
|
|
711
|
-
APIError APINoiseFrameHelper::init_handshake_() {
|
|
712
|
-
int err;
|
|
713
|
-
memset(&nid_, 0, sizeof(nid_));
|
|
714
|
-
// const char *proto = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
|
|
715
|
-
// err = noise_protocol_name_to_id(&nid_, proto, strlen(proto));
|
|
716
|
-
nid_.pattern_id = NOISE_PATTERN_NN;
|
|
717
|
-
nid_.cipher_id = NOISE_CIPHER_CHACHAPOLY;
|
|
718
|
-
nid_.dh_id = NOISE_DH_CURVE25519;
|
|
719
|
-
nid_.prefix_id = NOISE_PREFIX_STANDARD;
|
|
720
|
-
nid_.hybrid_id = NOISE_DH_NONE;
|
|
721
|
-
nid_.hash_id = NOISE_HASH_SHA256;
|
|
722
|
-
nid_.modifier_ids[0] = NOISE_MODIFIER_PSK0;
|
|
723
|
-
|
|
724
|
-
err = noise_handshakestate_new_by_id(&handshake_, &nid_, NOISE_ROLE_RESPONDER);
|
|
725
|
-
if (err != 0) {
|
|
726
|
-
state_ = State::FAILED;
|
|
727
|
-
HELPER_LOG("noise_handshakestate_new_by_id failed: %s", noise_err_to_str(err).c_str());
|
|
728
|
-
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
const auto &psk = ctx_->get_psk();
|
|
732
|
-
err = noise_handshakestate_set_pre_shared_key(handshake_, psk.data(), psk.size());
|
|
733
|
-
if (err != 0) {
|
|
734
|
-
state_ = State::FAILED;
|
|
735
|
-
HELPER_LOG("noise_handshakestate_set_pre_shared_key failed: %s", noise_err_to_str(err).c_str());
|
|
736
|
-
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
err = noise_handshakestate_set_prologue(handshake_, prologue_.data(), prologue_.size());
|
|
740
|
-
if (err != 0) {
|
|
741
|
-
state_ = State::FAILED;
|
|
742
|
-
HELPER_LOG("noise_handshakestate_set_prologue failed: %s", noise_err_to_str(err).c_str());
|
|
743
|
-
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
|
744
|
-
}
|
|
745
|
-
// set_prologue copies it into handshakestate, so we can get rid of it now
|
|
746
|
-
prologue_ = {};
|
|
747
|
-
|
|
748
|
-
err = noise_handshakestate_start(handshake_);
|
|
749
|
-
if (err != 0) {
|
|
750
|
-
state_ = State::FAILED;
|
|
751
|
-
HELPER_LOG("noise_handshakestate_start failed: %s", noise_err_to_str(err).c_str());
|
|
752
|
-
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
|
753
|
-
}
|
|
754
|
-
return APIError::OK;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
APIError APINoiseFrameHelper::check_handshake_finished_() {
|
|
758
|
-
assert(state_ == State::HANDSHAKE);
|
|
759
|
-
|
|
760
|
-
int action = noise_handshakestate_get_action(handshake_);
|
|
761
|
-
if (action == NOISE_ACTION_READ_MESSAGE || action == NOISE_ACTION_WRITE_MESSAGE)
|
|
762
|
-
return APIError::OK;
|
|
763
|
-
if (action != NOISE_ACTION_SPLIT) {
|
|
764
|
-
state_ = State::FAILED;
|
|
765
|
-
HELPER_LOG("Bad action for handshake: %d", action);
|
|
766
|
-
return APIError::HANDSHAKESTATE_BAD_STATE;
|
|
767
|
-
}
|
|
768
|
-
int err = noise_handshakestate_split(handshake_, &send_cipher_, &recv_cipher_);
|
|
769
|
-
if (err != 0) {
|
|
770
|
-
state_ = State::FAILED;
|
|
771
|
-
HELPER_LOG("noise_handshakestate_split failed: %s", noise_err_to_str(err).c_str());
|
|
772
|
-
return APIError::HANDSHAKESTATE_SPLIT_FAILED;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
frame_footer_size_ = noise_cipherstate_get_mac_length(send_cipher_);
|
|
776
|
-
|
|
777
|
-
HELPER_LOG("Handshake complete!");
|
|
778
|
-
noise_handshakestate_free(handshake_);
|
|
779
|
-
handshake_ = nullptr;
|
|
780
|
-
state_ = State::DATA;
|
|
781
|
-
return APIError::OK;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
APINoiseFrameHelper::~APINoiseFrameHelper() {
|
|
785
|
-
if (handshake_ != nullptr) {
|
|
786
|
-
noise_handshakestate_free(handshake_);
|
|
787
|
-
handshake_ = nullptr;
|
|
788
|
-
}
|
|
789
|
-
if (send_cipher_ != nullptr) {
|
|
790
|
-
noise_cipherstate_free(send_cipher_);
|
|
791
|
-
send_cipher_ = nullptr;
|
|
792
|
-
}
|
|
793
|
-
if (recv_cipher_ != nullptr) {
|
|
794
|
-
noise_cipherstate_free(recv_cipher_);
|
|
795
|
-
recv_cipher_ = nullptr;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
extern "C" {
|
|
800
|
-
// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
|
|
801
|
-
void noise_rand_bytes(void *output, size_t len) {
|
|
802
|
-
if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
|
|
803
|
-
ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting");
|
|
804
|
-
arch_restart();
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
#endif // USE_API_NOISE
|
|
810
|
-
|
|
811
|
-
#ifdef USE_API_PLAINTEXT
|
|
812
|
-
|
|
813
|
-
/// Initialize the frame helper, returns OK if successful.
|
|
814
|
-
APIError APIPlaintextFrameHelper::init() {
|
|
815
|
-
APIError err = init_common_();
|
|
816
|
-
if (err != APIError::OK) {
|
|
817
|
-
return err;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
state_ = State::DATA;
|
|
821
|
-
return APIError::OK;
|
|
822
|
-
}
|
|
823
|
-
APIError APIPlaintextFrameHelper::loop() {
|
|
824
|
-
if (state_ != State::DATA) {
|
|
825
|
-
return APIError::BAD_STATE;
|
|
826
|
-
}
|
|
827
|
-
// Use base class implementation for buffer sending
|
|
828
|
-
return APIFrameHelper::loop();
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
|
832
|
-
*
|
|
833
|
-
* @param frame: The struct to hold the frame information in.
|
|
834
|
-
* msg: store the parsed frame in that struct
|
|
835
|
-
*
|
|
836
|
-
* @return See APIError
|
|
837
|
-
*
|
|
838
|
-
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
|
839
|
-
*/
|
|
840
|
-
APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|
841
|
-
if (frame == nullptr) {
|
|
842
|
-
HELPER_LOG("Bad argument for try_read_frame_");
|
|
843
|
-
return APIError::BAD_ARG;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// read header
|
|
847
|
-
while (!rx_header_parsed_) {
|
|
848
|
-
// Now that we know when the socket is ready, we can read up to 3 bytes
|
|
849
|
-
// into the rx_header_buf_ before we have to switch back to reading
|
|
850
|
-
// one byte at a time to ensure we don't read past the message and
|
|
851
|
-
// into the next one.
|
|
852
|
-
|
|
853
|
-
// Read directly into rx_header_buf_ at the current position
|
|
854
|
-
// Try to get to at least 3 bytes total (indicator + 2 varint bytes), then read one byte at a time
|
|
855
|
-
ssize_t received =
|
|
856
|
-
this->socket_->read(&rx_header_buf_[rx_header_buf_pos_], rx_header_buf_pos_ < 3 ? 3 - rx_header_buf_pos_ : 1);
|
|
857
|
-
APIError err = handle_socket_read_result_(received);
|
|
858
|
-
if (err != APIError::OK) {
|
|
859
|
-
return err;
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
// If this was the first read, validate the indicator byte
|
|
863
|
-
if (rx_header_buf_pos_ == 0 && received > 0) {
|
|
864
|
-
if (rx_header_buf_[0] != 0x00) {
|
|
865
|
-
state_ = State::FAILED;
|
|
866
|
-
HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
|
|
867
|
-
return APIError::BAD_INDICATOR;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
rx_header_buf_pos_ += received;
|
|
872
|
-
|
|
873
|
-
// Check for buffer overflow
|
|
874
|
-
if (rx_header_buf_pos_ >= sizeof(rx_header_buf_)) {
|
|
875
|
-
state_ = State::FAILED;
|
|
876
|
-
HELPER_LOG("Header buffer overflow");
|
|
877
|
-
return APIError::BAD_DATA_PACKET;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
// Need at least 3 bytes total (indicator + 2 varint bytes) before trying to parse
|
|
881
|
-
if (rx_header_buf_pos_ < 3) {
|
|
882
|
-
continue;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
// At this point, we have at least 3 bytes total:
|
|
886
|
-
// - Validated indicator byte (0x00) stored at position 0
|
|
887
|
-
// - At least 2 bytes in the buffer for the varints
|
|
888
|
-
// Buffer layout:
|
|
889
|
-
// [0]: indicator byte (0x00)
|
|
890
|
-
// [1-3]: Message size varint (variable length)
|
|
891
|
-
// - 2 bytes would only allow up to 16383, which is less than noise's UINT16_MAX (65535)
|
|
892
|
-
// - 3 bytes allows up to 2097151, ensuring we support at least as much as noise
|
|
893
|
-
// [2-5]: Message type varint (variable length)
|
|
894
|
-
// We now attempt to parse both varints. If either is incomplete,
|
|
895
|
-
// we'll continue reading more bytes.
|
|
896
|
-
|
|
897
|
-
// Skip indicator byte at position 0
|
|
898
|
-
uint8_t varint_pos = 1;
|
|
899
|
-
uint32_t consumed = 0;
|
|
900
|
-
|
|
901
|
-
auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
|
|
902
|
-
if (!msg_size_varint.has_value()) {
|
|
903
|
-
// not enough data there yet
|
|
904
|
-
continue;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
if (msg_size_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
|
|
908
|
-
state_ = State::FAILED;
|
|
909
|
-
HELPER_LOG("Bad packet: message size %" PRIu32 " exceeds maximum %u", msg_size_varint->as_uint32(),
|
|
910
|
-
std::numeric_limits<uint16_t>::max());
|
|
911
|
-
return APIError::BAD_DATA_PACKET;
|
|
912
|
-
}
|
|
913
|
-
rx_header_parsed_len_ = msg_size_varint->as_uint16();
|
|
914
|
-
|
|
915
|
-
// Move to next varint position
|
|
916
|
-
varint_pos += consumed;
|
|
917
|
-
|
|
918
|
-
auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
|
|
919
|
-
if (!msg_type_varint.has_value()) {
|
|
920
|
-
// not enough data there yet
|
|
921
|
-
continue;
|
|
922
|
-
}
|
|
923
|
-
if (msg_type_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
|
|
924
|
-
state_ = State::FAILED;
|
|
925
|
-
HELPER_LOG("Bad packet: message type %" PRIu32 " exceeds maximum %u", msg_type_varint->as_uint32(),
|
|
926
|
-
std::numeric_limits<uint16_t>::max());
|
|
927
|
-
return APIError::BAD_DATA_PACKET;
|
|
928
|
-
}
|
|
929
|
-
rx_header_parsed_type_ = msg_type_varint->as_uint16();
|
|
930
|
-
rx_header_parsed_ = true;
|
|
931
|
-
}
|
|
932
|
-
// header reading done
|
|
933
|
-
|
|
934
|
-
// reserve space for body
|
|
935
|
-
if (rx_buf_.size() != rx_header_parsed_len_) {
|
|
936
|
-
rx_buf_.resize(rx_header_parsed_len_);
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
if (rx_buf_len_ < rx_header_parsed_len_) {
|
|
940
|
-
// more data to read
|
|
941
|
-
uint16_t to_read = rx_header_parsed_len_ - rx_buf_len_;
|
|
942
|
-
ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
|
943
|
-
APIError err = handle_socket_read_result_(received);
|
|
944
|
-
if (err != APIError::OK) {
|
|
945
|
-
return err;
|
|
946
|
-
}
|
|
947
|
-
rx_buf_len_ += static_cast<uint16_t>(received);
|
|
948
|
-
if (static_cast<uint16_t>(received) != to_read) {
|
|
949
|
-
// not all read
|
|
950
|
-
return APIError::WOULD_BLOCK;
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
// uncomment for even more debugging
|
|
955
|
-
#ifdef HELPER_LOG_PACKETS
|
|
956
|
-
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
|
|
957
|
-
#endif
|
|
958
|
-
frame->msg = std::move(rx_buf_);
|
|
959
|
-
// consume msg
|
|
960
|
-
rx_buf_ = {};
|
|
961
|
-
rx_buf_len_ = 0;
|
|
962
|
-
rx_header_buf_pos_ = 0;
|
|
963
|
-
rx_header_parsed_ = false;
|
|
964
|
-
return APIError::OK;
|
|
965
|
-
}
|
|
966
|
-
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|
967
|
-
APIError aerr;
|
|
968
|
-
|
|
969
|
-
if (state_ != State::DATA) {
|
|
970
|
-
return APIError::WOULD_BLOCK;
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
ParsedFrame frame;
|
|
974
|
-
aerr = try_read_frame_(&frame);
|
|
975
|
-
if (aerr != APIError::OK) {
|
|
976
|
-
if (aerr == APIError::BAD_INDICATOR) {
|
|
977
|
-
// Make sure to tell the remote that we don't
|
|
978
|
-
// understand the indicator byte so it knows
|
|
979
|
-
// we do not support it.
|
|
980
|
-
struct iovec iov[1];
|
|
981
|
-
// The \x00 first byte is the marker for plaintext.
|
|
982
|
-
//
|
|
983
|
-
// The remote will know how to handle the indicator byte,
|
|
984
|
-
// but it likely won't understand the rest of the message.
|
|
985
|
-
//
|
|
986
|
-
// We must send at least 3 bytes to be read, so we add
|
|
987
|
-
// a message after the indicator byte to ensures its long
|
|
988
|
-
// enough and can aid in debugging.
|
|
989
|
-
const char msg[] = "\x00"
|
|
990
|
-
"Bad indicator byte";
|
|
991
|
-
iov[0].iov_base = (void *) msg;
|
|
992
|
-
iov[0].iov_len = 19;
|
|
993
|
-
this->write_raw_(iov, 1);
|
|
994
|
-
}
|
|
995
|
-
return aerr;
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
buffer->container = std::move(frame.msg);
|
|
999
|
-
buffer->data_offset = 0;
|
|
1000
|
-
buffer->data_len = rx_header_parsed_len_;
|
|
1001
|
-
buffer->type = rx_header_parsed_type_;
|
|
1002
|
-
return APIError::OK;
|
|
1003
|
-
}
|
|
1004
|
-
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
|
|
1005
|
-
PacketInfo packet{type, 0, static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_)};
|
|
1006
|
-
return write_protobuf_packets(buffer, std::span<const PacketInfo>(&packet, 1));
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) {
|
|
1010
|
-
if (state_ != State::DATA) {
|
|
1011
|
-
return APIError::BAD_STATE;
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
if (packets.empty()) {
|
|
1015
|
-
return APIError::OK;
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
|
1019
|
-
uint8_t *buffer_data = raw_buffer->data(); // Cache buffer pointer
|
|
1020
|
-
|
|
1021
|
-
this->reusable_iovs_.clear();
|
|
1022
|
-
this->reusable_iovs_.reserve(packets.size());
|
|
1023
|
-
|
|
1024
|
-
for (const auto &packet : packets) {
|
|
1025
|
-
// Calculate varint sizes for header layout
|
|
1026
|
-
uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.payload_size));
|
|
1027
|
-
uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.message_type));
|
|
1028
|
-
uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
|
|
1029
|
-
|
|
1030
|
-
// Calculate where to start writing the header
|
|
1031
|
-
// The header starts at the latest possible position to minimize unused padding
|
|
1032
|
-
//
|
|
1033
|
-
// Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3
|
|
1034
|
-
// [0-2] - Unused padding
|
|
1035
|
-
// [3] - 0x00 indicator byte
|
|
1036
|
-
// [4] - Payload size varint (1 byte, for sizes 0-127)
|
|
1037
|
-
// [5] - Message type varint (1 byte, for types 0-127)
|
|
1038
|
-
// [6...] - Actual payload data
|
|
1039
|
-
//
|
|
1040
|
-
// Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2
|
|
1041
|
-
// [0-1] - Unused padding
|
|
1042
|
-
// [2] - 0x00 indicator byte
|
|
1043
|
-
// [3-4] - Payload size varint (2 bytes, for sizes 128-16383)
|
|
1044
|
-
// [5] - Message type varint (1 byte, for types 0-127)
|
|
1045
|
-
// [6...] - Actual payload data
|
|
1046
|
-
//
|
|
1047
|
-
// Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0
|
|
1048
|
-
// [0] - 0x00 indicator byte
|
|
1049
|
-
// [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151)
|
|
1050
|
-
// [4-5] - Message type varint (2 bytes, for types 128-32767)
|
|
1051
|
-
// [6...] - Actual payload data
|
|
1052
|
-
//
|
|
1053
|
-
// The message starts at offset + frame_header_padding_
|
|
1054
|
-
// So we write the header starting at offset + frame_header_padding_ - total_header_len
|
|
1055
|
-
uint8_t *buf_start = buffer_data + packet.offset;
|
|
1056
|
-
uint32_t header_offset = frame_header_padding_ - total_header_len;
|
|
1057
|
-
|
|
1058
|
-
// Write the plaintext header
|
|
1059
|
-
buf_start[header_offset] = 0x00; // indicator
|
|
1060
|
-
|
|
1061
|
-
// Encode varints directly into buffer
|
|
1062
|
-
ProtoVarInt(packet.payload_size).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
|
|
1063
|
-
ProtoVarInt(packet.message_type)
|
|
1064
|
-
.encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
|
|
1065
|
-
|
|
1066
|
-
// Add iovec for this packet (header + payload)
|
|
1067
|
-
this->reusable_iovs_.push_back(
|
|
1068
|
-
{buf_start + header_offset, static_cast<size_t>(total_header_len + packet.payload_size)});
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
// Send all packets in one writev call
|
|
1072
|
-
return write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
#endif // USE_API_PLAINTEXT
|
|
1076
250
|
|
|
1077
|
-
} // namespace api
|
|
1078
|
-
} // namespace esphome
|
|
251
|
+
} // namespace esphome::api
|
|
1079
252
|
#endif
|