esphome 2025.7.4__py3-none-any.whl → 2025.8.0b1__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.
Potentially problematic release.
This version of esphome might be problematic. Click here for more details.
- esphome/__main__.py +189 -82
- 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 +112 -56
- 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 +488 -118
- esphome/components/atm90e32/atm90e32.h +44 -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 +387 -82
- 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 +199 -58
- 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 +40 -2
- esphome/components/esp32_ble/ble.cpp +12 -8
- 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 +2 -4
- 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/font/__init__.py +9 -1
- 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 +46 -19
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +4 -3
- 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/lvgl/widgets/tileview.py +15 -7
- 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 +0 -1
- 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/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 +2 -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/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 +187 -352
- 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 +8 -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 +8 -15
- esphome/const.py +26 -1
- esphome/core/__init__.py +88 -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 +41 -6
- esphome/core/helpers.cpp +8 -15
- esphome/core/helpers.h +60 -6
- esphome/core/lock_free_queue.h +1 -1
- esphome/core/scheduler.cpp +277 -74
- esphome/core/scheduler.h +89 -27
- 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 +5 -107
- esphome/yaml_util.py +7 -5
- {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/METADATA +12 -13
- {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/RECORD +753 -673
- 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.4.dist-info → esphome-2025.8.0b1.dist-info}/WHEEL +0 -0
- {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/entry_points.txt +0 -0
- {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
#include "api_frame_helper_noise.h"
|
|
2
|
+
#ifdef USE_API
|
|
3
|
+
#ifdef USE_API_NOISE
|
|
4
|
+
#include "api_connection.h" // For ClientInfo struct
|
|
5
|
+
#include "esphome/core/application.h"
|
|
6
|
+
#include "esphome/core/hal.h"
|
|
7
|
+
#include "esphome/core/helpers.h"
|
|
8
|
+
#include "esphome/core/log.h"
|
|
9
|
+
#include "proto.h"
|
|
10
|
+
#include <cstring>
|
|
11
|
+
#include <cinttypes>
|
|
12
|
+
|
|
13
|
+
namespace esphome::api {
|
|
14
|
+
|
|
15
|
+
static const char *const TAG = "api.noise";
|
|
16
|
+
static const char *const PROLOGUE_INIT = "NoiseAPIInit";
|
|
17
|
+
static constexpr size_t PROLOGUE_INIT_LEN = 12; // strlen("NoiseAPIInit")
|
|
18
|
+
|
|
19
|
+
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->client_info_->get_combined_info().c_str(), ##__VA_ARGS__)
|
|
20
|
+
|
|
21
|
+
#ifdef HELPER_LOG_PACKETS
|
|
22
|
+
#define LOG_PACKET_RECEIVED(buffer) ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(buffer).c_str())
|
|
23
|
+
#define LOG_PACKET_SENDING(data, len) ESP_LOGVV(TAG, "Sending raw: %s", format_hex_pretty(data, len).c_str())
|
|
24
|
+
#else
|
|
25
|
+
#define LOG_PACKET_RECEIVED(buffer) ((void) 0)
|
|
26
|
+
#define LOG_PACKET_SENDING(data, len) ((void) 0)
|
|
27
|
+
#endif
|
|
28
|
+
|
|
29
|
+
/// Convert a noise error code to a readable error
|
|
30
|
+
std::string noise_err_to_str(int err) {
|
|
31
|
+
if (err == NOISE_ERROR_NO_MEMORY)
|
|
32
|
+
return "NO_MEMORY";
|
|
33
|
+
if (err == NOISE_ERROR_UNKNOWN_ID)
|
|
34
|
+
return "UNKNOWN_ID";
|
|
35
|
+
if (err == NOISE_ERROR_UNKNOWN_NAME)
|
|
36
|
+
return "UNKNOWN_NAME";
|
|
37
|
+
if (err == NOISE_ERROR_MAC_FAILURE)
|
|
38
|
+
return "MAC_FAILURE";
|
|
39
|
+
if (err == NOISE_ERROR_NOT_APPLICABLE)
|
|
40
|
+
return "NOT_APPLICABLE";
|
|
41
|
+
if (err == NOISE_ERROR_SYSTEM)
|
|
42
|
+
return "SYSTEM";
|
|
43
|
+
if (err == NOISE_ERROR_REMOTE_KEY_REQUIRED)
|
|
44
|
+
return "REMOTE_KEY_REQUIRED";
|
|
45
|
+
if (err == NOISE_ERROR_LOCAL_KEY_REQUIRED)
|
|
46
|
+
return "LOCAL_KEY_REQUIRED";
|
|
47
|
+
if (err == NOISE_ERROR_PSK_REQUIRED)
|
|
48
|
+
return "PSK_REQUIRED";
|
|
49
|
+
if (err == NOISE_ERROR_INVALID_LENGTH)
|
|
50
|
+
return "INVALID_LENGTH";
|
|
51
|
+
if (err == NOISE_ERROR_INVALID_PARAM)
|
|
52
|
+
return "INVALID_PARAM";
|
|
53
|
+
if (err == NOISE_ERROR_INVALID_STATE)
|
|
54
|
+
return "INVALID_STATE";
|
|
55
|
+
if (err == NOISE_ERROR_INVALID_NONCE)
|
|
56
|
+
return "INVALID_NONCE";
|
|
57
|
+
if (err == NOISE_ERROR_INVALID_PRIVATE_KEY)
|
|
58
|
+
return "INVALID_PRIVATE_KEY";
|
|
59
|
+
if (err == NOISE_ERROR_INVALID_PUBLIC_KEY)
|
|
60
|
+
return "INVALID_PUBLIC_KEY";
|
|
61
|
+
if (err == NOISE_ERROR_INVALID_FORMAT)
|
|
62
|
+
return "INVALID_FORMAT";
|
|
63
|
+
if (err == NOISE_ERROR_INVALID_SIGNATURE)
|
|
64
|
+
return "INVALID_SIGNATURE";
|
|
65
|
+
return to_string(err);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/// Initialize the frame helper, returns OK if successful.
|
|
69
|
+
APIError APINoiseFrameHelper::init() {
|
|
70
|
+
APIError err = init_common_();
|
|
71
|
+
if (err != APIError::OK) {
|
|
72
|
+
return err;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// init prologue
|
|
76
|
+
size_t old_size = prologue_.size();
|
|
77
|
+
prologue_.resize(old_size + PROLOGUE_INIT_LEN);
|
|
78
|
+
std::memcpy(prologue_.data() + old_size, PROLOGUE_INIT, PROLOGUE_INIT_LEN);
|
|
79
|
+
|
|
80
|
+
state_ = State::CLIENT_HELLO;
|
|
81
|
+
return APIError::OK;
|
|
82
|
+
}
|
|
83
|
+
// Helper for handling handshake frame errors
|
|
84
|
+
APIError APINoiseFrameHelper::handle_handshake_frame_error_(APIError aerr) {
|
|
85
|
+
if (aerr == APIError::BAD_INDICATOR) {
|
|
86
|
+
send_explicit_handshake_reject_("Bad indicator byte");
|
|
87
|
+
} else if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
|
|
88
|
+
send_explicit_handshake_reject_("Bad handshake packet len");
|
|
89
|
+
}
|
|
90
|
+
return aerr;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Helper for handling noise library errors
|
|
94
|
+
APIError APINoiseFrameHelper::handle_noise_error_(int err, const char *func_name, APIError api_err) {
|
|
95
|
+
if (err != 0) {
|
|
96
|
+
state_ = State::FAILED;
|
|
97
|
+
HELPER_LOG("%s failed: %s", func_name, noise_err_to_str(err).c_str());
|
|
98
|
+
return api_err;
|
|
99
|
+
}
|
|
100
|
+
return APIError::OK;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// Run through handshake messages (if in that phase)
|
|
104
|
+
APIError APINoiseFrameHelper::loop() {
|
|
105
|
+
// During handshake phase, process as many actions as possible until we can't progress
|
|
106
|
+
// socket_->ready() stays true until next main loop, but state_action() will return
|
|
107
|
+
// WOULD_BLOCK when no more data is available to read
|
|
108
|
+
while (state_ != State::DATA && this->socket_->ready()) {
|
|
109
|
+
APIError err = state_action_();
|
|
110
|
+
if (err == APIError::WOULD_BLOCK) {
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
if (err != APIError::OK) {
|
|
114
|
+
return err;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Use base class implementation for buffer sending
|
|
119
|
+
return APIFrameHelper::loop();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
|
123
|
+
*
|
|
124
|
+
* @param frame: The struct to hold the frame information in.
|
|
125
|
+
* msg_start: points to the start of the payload - this pointer is only valid until the next
|
|
126
|
+
* try_receive_raw_ call
|
|
127
|
+
*
|
|
128
|
+
* @return 0 if a full packet is in rx_buf_
|
|
129
|
+
* @return -1 if error, check errno.
|
|
130
|
+
*
|
|
131
|
+
* errno EWOULDBLOCK: Packet could not be read without blocking. Try again later.
|
|
132
|
+
* errno ENOMEM: Not enough memory for reading packet.
|
|
133
|
+
* errno API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
|
134
|
+
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
|
|
135
|
+
*/
|
|
136
|
+
APIError APINoiseFrameHelper::try_read_frame_(std::vector<uint8_t> *frame) {
|
|
137
|
+
if (frame == nullptr) {
|
|
138
|
+
HELPER_LOG("Bad argument for try_read_frame_");
|
|
139
|
+
return APIError::BAD_ARG;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// read header
|
|
143
|
+
if (rx_header_buf_len_ < 3) {
|
|
144
|
+
// no header information yet
|
|
145
|
+
uint8_t to_read = 3 - rx_header_buf_len_;
|
|
146
|
+
ssize_t received = this->socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
|
|
147
|
+
APIError err = handle_socket_read_result_(received);
|
|
148
|
+
if (err != APIError::OK) {
|
|
149
|
+
return err;
|
|
150
|
+
}
|
|
151
|
+
rx_header_buf_len_ += static_cast<uint8_t>(received);
|
|
152
|
+
if (static_cast<uint8_t>(received) != to_read) {
|
|
153
|
+
// not a full read
|
|
154
|
+
return APIError::WOULD_BLOCK;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (rx_header_buf_[0] != 0x01) {
|
|
158
|
+
state_ = State::FAILED;
|
|
159
|
+
HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
|
|
160
|
+
return APIError::BAD_INDICATOR;
|
|
161
|
+
}
|
|
162
|
+
// header reading done
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// read body
|
|
166
|
+
uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
|
|
167
|
+
|
|
168
|
+
if (state_ != State::DATA && msg_size > 128) {
|
|
169
|
+
// for handshake message only permit up to 128 bytes
|
|
170
|
+
state_ = State::FAILED;
|
|
171
|
+
HELPER_LOG("Bad packet len for handshake: %d", msg_size);
|
|
172
|
+
return APIError::BAD_HANDSHAKE_PACKET_LEN;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// reserve space for body
|
|
176
|
+
if (rx_buf_.size() != msg_size) {
|
|
177
|
+
rx_buf_.resize(msg_size);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (rx_buf_len_ < msg_size) {
|
|
181
|
+
// more data to read
|
|
182
|
+
uint16_t to_read = msg_size - rx_buf_len_;
|
|
183
|
+
ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
|
184
|
+
APIError err = handle_socket_read_result_(received);
|
|
185
|
+
if (err != APIError::OK) {
|
|
186
|
+
return err;
|
|
187
|
+
}
|
|
188
|
+
rx_buf_len_ += static_cast<uint16_t>(received);
|
|
189
|
+
if (static_cast<uint16_t>(received) != to_read) {
|
|
190
|
+
// not all read
|
|
191
|
+
return APIError::WOULD_BLOCK;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
LOG_PACKET_RECEIVED(rx_buf_);
|
|
196
|
+
*frame = std::move(rx_buf_);
|
|
197
|
+
// consume msg
|
|
198
|
+
rx_buf_ = {};
|
|
199
|
+
rx_buf_len_ = 0;
|
|
200
|
+
rx_header_buf_len_ = 0;
|
|
201
|
+
return APIError::OK;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** To be called from read/write methods.
|
|
205
|
+
*
|
|
206
|
+
* This method runs through the internal handshake methods, if in that state.
|
|
207
|
+
*
|
|
208
|
+
* If the handshake is still active when this method returns and a read/write can't take place at
|
|
209
|
+
* the moment, returns WOULD_BLOCK.
|
|
210
|
+
* If an error occurred, returns that error. Only returns OK if the transport is ready for data
|
|
211
|
+
* traffic.
|
|
212
|
+
*/
|
|
213
|
+
APIError APINoiseFrameHelper::state_action_() {
|
|
214
|
+
int err;
|
|
215
|
+
APIError aerr;
|
|
216
|
+
if (state_ == State::INITIALIZE) {
|
|
217
|
+
HELPER_LOG("Bad state for method: %d", (int) state_);
|
|
218
|
+
return APIError::BAD_STATE;
|
|
219
|
+
}
|
|
220
|
+
if (state_ == State::CLIENT_HELLO) {
|
|
221
|
+
// waiting for client hello
|
|
222
|
+
std::vector<uint8_t> frame;
|
|
223
|
+
aerr = try_read_frame_(&frame);
|
|
224
|
+
if (aerr != APIError::OK) {
|
|
225
|
+
return handle_handshake_frame_error_(aerr);
|
|
226
|
+
}
|
|
227
|
+
// ignore contents, may be used in future for flags
|
|
228
|
+
// Resize for: existing prologue + 2 size bytes + frame data
|
|
229
|
+
size_t old_size = prologue_.size();
|
|
230
|
+
prologue_.resize(old_size + 2 + frame.size());
|
|
231
|
+
prologue_[old_size] = (uint8_t) (frame.size() >> 8);
|
|
232
|
+
prologue_[old_size + 1] = (uint8_t) frame.size();
|
|
233
|
+
std::memcpy(prologue_.data() + old_size + 2, frame.data(), frame.size());
|
|
234
|
+
|
|
235
|
+
state_ = State::SERVER_HELLO;
|
|
236
|
+
}
|
|
237
|
+
if (state_ == State::SERVER_HELLO) {
|
|
238
|
+
// send server hello
|
|
239
|
+
const std::string &name = App.get_name();
|
|
240
|
+
const std::string &mac = get_mac_address();
|
|
241
|
+
|
|
242
|
+
std::vector<uint8_t> msg;
|
|
243
|
+
// Calculate positions and sizes
|
|
244
|
+
size_t name_len = name.size() + 1; // including null terminator
|
|
245
|
+
size_t mac_len = mac.size() + 1; // including null terminator
|
|
246
|
+
size_t name_offset = 1;
|
|
247
|
+
size_t mac_offset = name_offset + name_len;
|
|
248
|
+
size_t total_size = 1 + name_len + mac_len;
|
|
249
|
+
|
|
250
|
+
msg.resize(total_size);
|
|
251
|
+
|
|
252
|
+
// chosen proto
|
|
253
|
+
msg[0] = 0x01;
|
|
254
|
+
|
|
255
|
+
// node name, terminated by null byte
|
|
256
|
+
std::memcpy(msg.data() + name_offset, name.c_str(), name_len);
|
|
257
|
+
// node mac, terminated by null byte
|
|
258
|
+
std::memcpy(msg.data() + mac_offset, mac.c_str(), mac_len);
|
|
259
|
+
|
|
260
|
+
aerr = write_frame_(msg.data(), msg.size());
|
|
261
|
+
if (aerr != APIError::OK)
|
|
262
|
+
return aerr;
|
|
263
|
+
|
|
264
|
+
// start handshake
|
|
265
|
+
aerr = init_handshake_();
|
|
266
|
+
if (aerr != APIError::OK)
|
|
267
|
+
return aerr;
|
|
268
|
+
|
|
269
|
+
state_ = State::HANDSHAKE;
|
|
270
|
+
}
|
|
271
|
+
if (state_ == State::HANDSHAKE) {
|
|
272
|
+
int action = noise_handshakestate_get_action(handshake_);
|
|
273
|
+
if (action == NOISE_ACTION_READ_MESSAGE) {
|
|
274
|
+
// waiting for handshake msg
|
|
275
|
+
std::vector<uint8_t> frame;
|
|
276
|
+
aerr = try_read_frame_(&frame);
|
|
277
|
+
if (aerr != APIError::OK) {
|
|
278
|
+
return handle_handshake_frame_error_(aerr);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (frame.empty()) {
|
|
282
|
+
send_explicit_handshake_reject_("Empty handshake message");
|
|
283
|
+
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
|
|
284
|
+
} else if (frame[0] != 0x00) {
|
|
285
|
+
HELPER_LOG("Bad handshake error byte: %u", frame[0]);
|
|
286
|
+
send_explicit_handshake_reject_("Bad handshake error byte");
|
|
287
|
+
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
NoiseBuffer mbuf;
|
|
291
|
+
noise_buffer_init(mbuf);
|
|
292
|
+
noise_buffer_set_input(mbuf, frame.data() + 1, frame.size() - 1);
|
|
293
|
+
err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr);
|
|
294
|
+
if (err != 0) {
|
|
295
|
+
// Special handling for MAC failure
|
|
296
|
+
send_explicit_handshake_reject_(err == NOISE_ERROR_MAC_FAILURE ? "Handshake MAC failure" : "Handshake error");
|
|
297
|
+
return handle_noise_error_(err, "noise_handshakestate_read_message", APIError::HANDSHAKESTATE_READ_FAILED);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
aerr = check_handshake_finished_();
|
|
301
|
+
if (aerr != APIError::OK)
|
|
302
|
+
return aerr;
|
|
303
|
+
} else if (action == NOISE_ACTION_WRITE_MESSAGE) {
|
|
304
|
+
uint8_t buffer[65];
|
|
305
|
+
NoiseBuffer mbuf;
|
|
306
|
+
noise_buffer_init(mbuf);
|
|
307
|
+
noise_buffer_set_output(mbuf, buffer + 1, sizeof(buffer) - 1);
|
|
308
|
+
|
|
309
|
+
err = noise_handshakestate_write_message(handshake_, &mbuf, nullptr);
|
|
310
|
+
APIError aerr_write =
|
|
311
|
+
handle_noise_error_(err, "noise_handshakestate_write_message", APIError::HANDSHAKESTATE_WRITE_FAILED);
|
|
312
|
+
if (aerr_write != APIError::OK)
|
|
313
|
+
return aerr_write;
|
|
314
|
+
buffer[0] = 0x00; // success
|
|
315
|
+
|
|
316
|
+
aerr = write_frame_(buffer, mbuf.size + 1);
|
|
317
|
+
if (aerr != APIError::OK)
|
|
318
|
+
return aerr;
|
|
319
|
+
aerr = check_handshake_finished_();
|
|
320
|
+
if (aerr != APIError::OK)
|
|
321
|
+
return aerr;
|
|
322
|
+
} else {
|
|
323
|
+
// bad state for action
|
|
324
|
+
state_ = State::FAILED;
|
|
325
|
+
HELPER_LOG("Bad action for handshake: %d", action);
|
|
326
|
+
return APIError::HANDSHAKESTATE_BAD_STATE;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (state_ == State::CLOSED || state_ == State::FAILED) {
|
|
330
|
+
return APIError::BAD_STATE;
|
|
331
|
+
}
|
|
332
|
+
return APIError::OK;
|
|
333
|
+
}
|
|
334
|
+
void APINoiseFrameHelper::send_explicit_handshake_reject_(const std::string &reason) {
|
|
335
|
+
std::vector<uint8_t> data;
|
|
336
|
+
data.resize(reason.length() + 1);
|
|
337
|
+
data[0] = 0x01; // failure
|
|
338
|
+
|
|
339
|
+
// Copy error message in bulk
|
|
340
|
+
if (!reason.empty()) {
|
|
341
|
+
std::memcpy(data.data() + 1, reason.c_str(), reason.length());
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// temporarily remove failed state
|
|
345
|
+
auto orig_state = state_;
|
|
346
|
+
state_ = State::EXPLICIT_REJECT;
|
|
347
|
+
write_frame_(data.data(), data.size());
|
|
348
|
+
state_ = orig_state;
|
|
349
|
+
}
|
|
350
|
+
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|
351
|
+
int err;
|
|
352
|
+
APIError aerr;
|
|
353
|
+
aerr = state_action_();
|
|
354
|
+
if (aerr != APIError::OK) {
|
|
355
|
+
return aerr;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (state_ != State::DATA) {
|
|
359
|
+
return APIError::WOULD_BLOCK;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
std::vector<uint8_t> frame;
|
|
363
|
+
aerr = try_read_frame_(&frame);
|
|
364
|
+
if (aerr != APIError::OK)
|
|
365
|
+
return aerr;
|
|
366
|
+
|
|
367
|
+
NoiseBuffer mbuf;
|
|
368
|
+
noise_buffer_init(mbuf);
|
|
369
|
+
noise_buffer_set_inout(mbuf, frame.data(), frame.size(), frame.size());
|
|
370
|
+
err = noise_cipherstate_decrypt(recv_cipher_, &mbuf);
|
|
371
|
+
APIError decrypt_err = handle_noise_error_(err, "noise_cipherstate_decrypt", APIError::CIPHERSTATE_DECRYPT_FAILED);
|
|
372
|
+
if (decrypt_err != APIError::OK)
|
|
373
|
+
return decrypt_err;
|
|
374
|
+
|
|
375
|
+
uint16_t msg_size = mbuf.size;
|
|
376
|
+
uint8_t *msg_data = frame.data();
|
|
377
|
+
if (msg_size < 4) {
|
|
378
|
+
state_ = State::FAILED;
|
|
379
|
+
HELPER_LOG("Bad data packet: size %d too short", msg_size);
|
|
380
|
+
return APIError::BAD_DATA_PACKET;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
|
|
384
|
+
uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
|
|
385
|
+
if (data_len > msg_size - 4) {
|
|
386
|
+
state_ = State::FAILED;
|
|
387
|
+
HELPER_LOG("Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
|
|
388
|
+
return APIError::BAD_DATA_PACKET;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
buffer->container = std::move(frame);
|
|
392
|
+
buffer->data_offset = 4;
|
|
393
|
+
buffer->data_len = data_len;
|
|
394
|
+
buffer->type = type;
|
|
395
|
+
return APIError::OK;
|
|
396
|
+
}
|
|
397
|
+
APIError APINoiseFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
|
|
398
|
+
// Resize to include MAC space (required for Noise encryption)
|
|
399
|
+
buffer.get_buffer()->resize(buffer.get_buffer()->size() + frame_footer_size_);
|
|
400
|
+
PacketInfo packet{type, 0,
|
|
401
|
+
static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_ - frame_footer_size_)};
|
|
402
|
+
return write_protobuf_packets(buffer, std::span<const PacketInfo>(&packet, 1));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) {
|
|
406
|
+
APIError aerr = state_action_();
|
|
407
|
+
if (aerr != APIError::OK) {
|
|
408
|
+
return aerr;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (state_ != State::DATA) {
|
|
412
|
+
return APIError::WOULD_BLOCK;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (packets.empty()) {
|
|
416
|
+
return APIError::OK;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
|
420
|
+
uint8_t *buffer_data = raw_buffer->data(); // Cache buffer pointer
|
|
421
|
+
|
|
422
|
+
this->reusable_iovs_.clear();
|
|
423
|
+
this->reusable_iovs_.reserve(packets.size());
|
|
424
|
+
uint16_t total_write_len = 0;
|
|
425
|
+
|
|
426
|
+
// We need to encrypt each packet in place
|
|
427
|
+
for (const auto &packet : packets) {
|
|
428
|
+
// The buffer already has padding at offset
|
|
429
|
+
uint8_t *buf_start = buffer_data + packet.offset;
|
|
430
|
+
|
|
431
|
+
// Write noise header
|
|
432
|
+
buf_start[0] = 0x01; // indicator
|
|
433
|
+
// buf_start[1], buf_start[2] to be set after encryption
|
|
434
|
+
|
|
435
|
+
// Write message header (to be encrypted)
|
|
436
|
+
const uint8_t msg_offset = 3;
|
|
437
|
+
buf_start[msg_offset] = static_cast<uint8_t>(packet.message_type >> 8); // type high byte
|
|
438
|
+
buf_start[msg_offset + 1] = static_cast<uint8_t>(packet.message_type); // type low byte
|
|
439
|
+
buf_start[msg_offset + 2] = static_cast<uint8_t>(packet.payload_size >> 8); // data_len high byte
|
|
440
|
+
buf_start[msg_offset + 3] = static_cast<uint8_t>(packet.payload_size); // data_len low byte
|
|
441
|
+
// payload data is already in the buffer starting at offset + 7
|
|
442
|
+
|
|
443
|
+
// Make sure we have space for MAC
|
|
444
|
+
// The buffer should already have been sized appropriately
|
|
445
|
+
|
|
446
|
+
// Encrypt the message in place
|
|
447
|
+
NoiseBuffer mbuf;
|
|
448
|
+
noise_buffer_init(mbuf);
|
|
449
|
+
noise_buffer_set_inout(mbuf, buf_start + msg_offset, 4 + packet.payload_size,
|
|
450
|
+
4 + packet.payload_size + frame_footer_size_);
|
|
451
|
+
|
|
452
|
+
int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
|
|
453
|
+
APIError aerr = handle_noise_error_(err, "noise_cipherstate_encrypt", APIError::CIPHERSTATE_ENCRYPT_FAILED);
|
|
454
|
+
if (aerr != APIError::OK)
|
|
455
|
+
return aerr;
|
|
456
|
+
|
|
457
|
+
// Fill in the encrypted size
|
|
458
|
+
buf_start[1] = static_cast<uint8_t>(mbuf.size >> 8);
|
|
459
|
+
buf_start[2] = static_cast<uint8_t>(mbuf.size);
|
|
460
|
+
|
|
461
|
+
// Add iovec for this encrypted packet
|
|
462
|
+
size_t packet_len = static_cast<size_t>(3 + mbuf.size); // indicator + size + encrypted data
|
|
463
|
+
this->reusable_iovs_.push_back({buf_start, packet_len});
|
|
464
|
+
total_write_len += packet_len;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Send all encrypted packets in one writev call
|
|
468
|
+
return this->write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size(), total_write_len);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {
|
|
472
|
+
uint8_t header[3];
|
|
473
|
+
header[0] = 0x01; // indicator
|
|
474
|
+
header[1] = (uint8_t) (len >> 8);
|
|
475
|
+
header[2] = (uint8_t) len;
|
|
476
|
+
|
|
477
|
+
struct iovec iov[2];
|
|
478
|
+
iov[0].iov_base = header;
|
|
479
|
+
iov[0].iov_len = 3;
|
|
480
|
+
if (len == 0) {
|
|
481
|
+
return this->write_raw_(iov, 1, 3); // Just header
|
|
482
|
+
}
|
|
483
|
+
iov[1].iov_base = const_cast<uint8_t *>(data);
|
|
484
|
+
iov[1].iov_len = len;
|
|
485
|
+
|
|
486
|
+
return this->write_raw_(iov, 2, 3 + len); // Header + data
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** Initiate the data structures for the handshake.
|
|
490
|
+
*
|
|
491
|
+
* @return 0 on success, -1 on error (check errno)
|
|
492
|
+
*/
|
|
493
|
+
APIError APINoiseFrameHelper::init_handshake_() {
|
|
494
|
+
int err;
|
|
495
|
+
memset(&nid_, 0, sizeof(nid_));
|
|
496
|
+
// const char *proto = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
|
|
497
|
+
// err = noise_protocol_name_to_id(&nid_, proto, strlen(proto));
|
|
498
|
+
nid_.pattern_id = NOISE_PATTERN_NN;
|
|
499
|
+
nid_.cipher_id = NOISE_CIPHER_CHACHAPOLY;
|
|
500
|
+
nid_.dh_id = NOISE_DH_CURVE25519;
|
|
501
|
+
nid_.prefix_id = NOISE_PREFIX_STANDARD;
|
|
502
|
+
nid_.hybrid_id = NOISE_DH_NONE;
|
|
503
|
+
nid_.hash_id = NOISE_HASH_SHA256;
|
|
504
|
+
nid_.modifier_ids[0] = NOISE_MODIFIER_PSK0;
|
|
505
|
+
|
|
506
|
+
err = noise_handshakestate_new_by_id(&handshake_, &nid_, NOISE_ROLE_RESPONDER);
|
|
507
|
+
APIError aerr = handle_noise_error_(err, "noise_handshakestate_new_by_id", APIError::HANDSHAKESTATE_SETUP_FAILED);
|
|
508
|
+
if (aerr != APIError::OK)
|
|
509
|
+
return aerr;
|
|
510
|
+
|
|
511
|
+
const auto &psk = ctx_->get_psk();
|
|
512
|
+
err = noise_handshakestate_set_pre_shared_key(handshake_, psk.data(), psk.size());
|
|
513
|
+
aerr = handle_noise_error_(err, "noise_handshakestate_set_pre_shared_key", APIError::HANDSHAKESTATE_SETUP_FAILED);
|
|
514
|
+
if (aerr != APIError::OK)
|
|
515
|
+
return aerr;
|
|
516
|
+
|
|
517
|
+
err = noise_handshakestate_set_prologue(handshake_, prologue_.data(), prologue_.size());
|
|
518
|
+
aerr = handle_noise_error_(err, "noise_handshakestate_set_prologue", APIError::HANDSHAKESTATE_SETUP_FAILED);
|
|
519
|
+
if (aerr != APIError::OK)
|
|
520
|
+
return aerr;
|
|
521
|
+
// set_prologue copies it into handshakestate, so we can get rid of it now
|
|
522
|
+
prologue_ = {};
|
|
523
|
+
|
|
524
|
+
err = noise_handshakestate_start(handshake_);
|
|
525
|
+
aerr = handle_noise_error_(err, "noise_handshakestate_start", APIError::HANDSHAKESTATE_SETUP_FAILED);
|
|
526
|
+
if (aerr != APIError::OK)
|
|
527
|
+
return aerr;
|
|
528
|
+
return APIError::OK;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
APIError APINoiseFrameHelper::check_handshake_finished_() {
|
|
532
|
+
assert(state_ == State::HANDSHAKE);
|
|
533
|
+
|
|
534
|
+
int action = noise_handshakestate_get_action(handshake_);
|
|
535
|
+
if (action == NOISE_ACTION_READ_MESSAGE || action == NOISE_ACTION_WRITE_MESSAGE)
|
|
536
|
+
return APIError::OK;
|
|
537
|
+
if (action != NOISE_ACTION_SPLIT) {
|
|
538
|
+
state_ = State::FAILED;
|
|
539
|
+
HELPER_LOG("Bad action for handshake: %d", action);
|
|
540
|
+
return APIError::HANDSHAKESTATE_BAD_STATE;
|
|
541
|
+
}
|
|
542
|
+
int err = noise_handshakestate_split(handshake_, &send_cipher_, &recv_cipher_);
|
|
543
|
+
APIError aerr = handle_noise_error_(err, "noise_handshakestate_split", APIError::HANDSHAKESTATE_SPLIT_FAILED);
|
|
544
|
+
if (aerr != APIError::OK)
|
|
545
|
+
return aerr;
|
|
546
|
+
|
|
547
|
+
frame_footer_size_ = noise_cipherstate_get_mac_length(send_cipher_);
|
|
548
|
+
|
|
549
|
+
HELPER_LOG("Handshake complete!");
|
|
550
|
+
noise_handshakestate_free(handshake_);
|
|
551
|
+
handshake_ = nullptr;
|
|
552
|
+
state_ = State::DATA;
|
|
553
|
+
return APIError::OK;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
APINoiseFrameHelper::~APINoiseFrameHelper() {
|
|
557
|
+
if (handshake_ != nullptr) {
|
|
558
|
+
noise_handshakestate_free(handshake_);
|
|
559
|
+
handshake_ = nullptr;
|
|
560
|
+
}
|
|
561
|
+
if (send_cipher_ != nullptr) {
|
|
562
|
+
noise_cipherstate_free(send_cipher_);
|
|
563
|
+
send_cipher_ = nullptr;
|
|
564
|
+
}
|
|
565
|
+
if (recv_cipher_ != nullptr) {
|
|
566
|
+
noise_cipherstate_free(recv_cipher_);
|
|
567
|
+
recv_cipher_ = nullptr;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
extern "C" {
|
|
572
|
+
// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
|
|
573
|
+
void noise_rand_bytes(void *output, size_t len) {
|
|
574
|
+
if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
|
|
575
|
+
ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting");
|
|
576
|
+
arch_restart();
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
} // namespace esphome::api
|
|
582
|
+
#endif // USE_API_NOISE
|
|
583
|
+
#endif // USE_API
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "api_frame_helper.h"
|
|
3
|
+
#ifdef USE_API
|
|
4
|
+
#ifdef USE_API_NOISE
|
|
5
|
+
#include "noise/protocol.h"
|
|
6
|
+
#include "api_noise_context.h"
|
|
7
|
+
|
|
8
|
+
namespace esphome::api {
|
|
9
|
+
|
|
10
|
+
class APINoiseFrameHelper : public APIFrameHelper {
|
|
11
|
+
public:
|
|
12
|
+
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx,
|
|
13
|
+
const ClientInfo *client_info)
|
|
14
|
+
: APIFrameHelper(std::move(socket), client_info), ctx_(std::move(ctx)) {
|
|
15
|
+
// Noise header structure:
|
|
16
|
+
// Pos 0: indicator (0x01)
|
|
17
|
+
// Pos 1-2: encrypted payload size (16-bit big-endian)
|
|
18
|
+
// Pos 3-6: encrypted type (16-bit) + data_len (16-bit)
|
|
19
|
+
// Pos 7+: actual payload data
|
|
20
|
+
frame_header_padding_ = 7;
|
|
21
|
+
}
|
|
22
|
+
~APINoiseFrameHelper() override;
|
|
23
|
+
APIError init() override;
|
|
24
|
+
APIError loop() override;
|
|
25
|
+
APIError read_packet(ReadPacketBuffer *buffer) override;
|
|
26
|
+
APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override;
|
|
27
|
+
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
|
|
28
|
+
// Get the frame header padding required by this protocol
|
|
29
|
+
uint8_t frame_header_padding() override { return frame_header_padding_; }
|
|
30
|
+
// Get the frame footer size required by this protocol
|
|
31
|
+
uint8_t frame_footer_size() override { return frame_footer_size_; }
|
|
32
|
+
|
|
33
|
+
protected:
|
|
34
|
+
APIError state_action_();
|
|
35
|
+
APIError try_read_frame_(std::vector<uint8_t> *frame);
|
|
36
|
+
APIError write_frame_(const uint8_t *data, uint16_t len);
|
|
37
|
+
APIError init_handshake_();
|
|
38
|
+
APIError check_handshake_finished_();
|
|
39
|
+
void send_explicit_handshake_reject_(const std::string &reason);
|
|
40
|
+
APIError handle_handshake_frame_error_(APIError aerr);
|
|
41
|
+
APIError handle_noise_error_(int err, const char *func_name, APIError api_err);
|
|
42
|
+
|
|
43
|
+
// Pointers first (4 bytes each)
|
|
44
|
+
NoiseHandshakeState *handshake_{nullptr};
|
|
45
|
+
NoiseCipherState *send_cipher_{nullptr};
|
|
46
|
+
NoiseCipherState *recv_cipher_{nullptr};
|
|
47
|
+
|
|
48
|
+
// Shared pointer (8 bytes on 32-bit = 4 bytes control block pointer + 4 bytes object pointer)
|
|
49
|
+
std::shared_ptr<APINoiseContext> ctx_;
|
|
50
|
+
|
|
51
|
+
// Vector (12 bytes on 32-bit)
|
|
52
|
+
std::vector<uint8_t> prologue_;
|
|
53
|
+
|
|
54
|
+
// NoiseProtocolId (size depends on implementation)
|
|
55
|
+
NoiseProtocolId nid_;
|
|
56
|
+
|
|
57
|
+
// Group small types together
|
|
58
|
+
// Fixed-size header buffer for noise protocol:
|
|
59
|
+
// 1 byte for indicator + 2 bytes for message size (16-bit value, not varint)
|
|
60
|
+
// Note: Maximum message size is UINT16_MAX (65535), with a limit of 128 bytes during handshake phase
|
|
61
|
+
uint8_t rx_header_buf_[3];
|
|
62
|
+
uint8_t rx_header_buf_len_ = 0;
|
|
63
|
+
// 4 bytes total, no padding
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
} // namespace esphome::api
|
|
67
|
+
#endif // USE_API_NOISE
|
|
68
|
+
#endif // USE_API
|