esphome 2025.4.2__py3-none-any.whl → 2025.5.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 +16 -14
- esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
- esphome/components/adc/__init__.py +51 -34
- esphome/components/airthings_wave_base/__init__.py +1 -1
- esphome/components/alarm_control_panel/__init__.py +37 -2
- esphome/components/am43/cover/__init__.py +4 -5
- esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
- esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
- esphome/components/analog_threshold/binary_sensor.py +10 -8
- esphome/components/anova/climate.py +4 -5
- esphome/components/api/__init__.py +25 -8
- esphome/components/api/api_connection.cpp +416 -662
- esphome/components/api/api_connection.h +256 -57
- esphome/components/api/api_frame_helper.cpp +232 -177
- esphome/components/api/api_frame_helper.h +61 -8
- esphome/components/api/api_noise_context.h +13 -4
- esphome/components/api/api_pb2.cpp +1422 -1
- esphome/components/api/api_pb2.h +255 -1
- esphome/components/api/api_pb2_service.cpp +162 -49
- esphome/components/api/api_pb2_service.h +90 -51
- esphome/components/api/api_pb2_size.h +361 -0
- esphome/components/api/api_server.cpp +110 -34
- esphome/components/api/api_server.h +8 -0
- esphome/components/api/proto.h +86 -17
- esphome/components/as7341/as7341.h +1 -1
- esphome/components/at581x/at581x.h +4 -4
- esphome/components/atm90e32/__init__.py +1 -0
- esphome/components/atm90e32/atm90e32.cpp +576 -199
- esphome/components/atm90e32/atm90e32.h +128 -31
- esphome/components/atm90e32/atm90e32_reg.h +4 -2
- esphome/components/atm90e32/button/__init__.py +62 -10
- esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
- esphome/components/atm90e32/button/atm90e32_button.h +36 -4
- esphome/components/atm90e32/number/__init__.py +130 -0
- esphome/components/atm90e32/number/atm90e32_number.h +16 -0
- esphome/components/atm90e32/sensor.py +21 -4
- esphome/components/atm90e32/text_sensor/__init__.py +48 -0
- esphome/components/audio/__init__.py +96 -49
- esphome/components/audio/audio.h +48 -0
- esphome/components/audio/audio_decoder.cpp +1 -1
- esphome/components/audio/audio_resampler.cpp +2 -0
- esphome/components/audio/audio_resampler.h +1 -0
- esphome/components/ballu/climate.py +2 -9
- esphome/components/bang_bang/climate.py +5 -6
- esphome/components/bedjet/bedjet_hub.cpp +1 -0
- esphome/components/bedjet/climate/__init__.py +3 -8
- esphome/components/bedjet/fan/__init__.py +2 -11
- esphome/components/binary/fan/__init__.py +13 -16
- esphome/components/binary_sensor/__init__.py +13 -10
- esphome/components/bl0906/constants.h +16 -16
- esphome/components/ble_client/text_sensor/__init__.py +3 -5
- esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
- esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
- esphome/components/button/__init__.py +11 -8
- esphome/components/canbus/canbus.cpp +3 -0
- esphome/components/canbus/canbus.h +16 -0
- esphome/components/ccs811/sensor.py +9 -6
- esphome/components/climate/__init__.py +35 -2
- esphome/components/climate/climate_mode.h +1 -1
- esphome/components/climate/climate_traits.h +63 -57
- esphome/components/climate_ir/__init__.py +57 -17
- esphome/components/climate_ir_lg/climate.py +2 -5
- esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
- esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
- esphome/components/color/__init__.py +2 -0
- esphome/components/const/__init__.py +5 -0
- esphome/components/coolix/climate.py +2 -9
- esphome/components/copy/cover/__init__.py +10 -9
- esphome/components/copy/fan/__init__.py +11 -9
- esphome/components/copy/lock/__init__.py +11 -9
- esphome/components/copy/text/__init__.py +9 -6
- esphome/components/cover/__init__.py +37 -2
- esphome/components/cse7766/cse7766.cpp +2 -1
- esphome/components/cst226/binary_sensor/__init__.py +28 -0
- esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
- esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
- esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
- esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
- esphome/components/current_based/cover.py +37 -36
- esphome/components/current_based/current_based_cover.cpp +2 -1
- esphome/components/daikin/climate.py +2 -9
- esphome/components/daikin/daikin.cpp +15 -9
- esphome/components/daikin/daikin.h +5 -5
- esphome/components/daikin_arc/climate.py +2 -7
- esphome/components/daikin_brc/climate.py +3 -5
- esphome/components/dallas_temp/dallas_temp.cpp +17 -24
- esphome/components/dallas_temp/dallas_temp.h +0 -1
- esphome/components/daly_bms/daly_bms.cpp +2 -1
- esphome/components/debug/debug_component.cpp +6 -1
- esphome/components/debug/debug_component.h +8 -0
- esphome/components/debug/debug_esp32.cpp +109 -254
- esphome/components/debug/sensor.py +14 -0
- esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
- esphome/components/delonghi/climate.py +2 -9
- esphome/components/demo/__init__.py +18 -20
- esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
- esphome/components/dps310/sensor.py +6 -6
- esphome/components/ee895/sensor.py +9 -9
- esphome/components/emmeti/climate.py +2 -9
- esphome/components/endstop/cover.py +17 -16
- esphome/components/endstop/endstop_cover.cpp +2 -1
- esphome/components/ens160_base/__init__.py +12 -9
- esphome/components/esp32/__init__.py +60 -3
- esphome/components/esp32/core.cpp +11 -5
- esphome/components/esp32/gpio.cpp +86 -24
- esphome/components/esp32/gpio.py +15 -16
- esphome/components/esp32/gpio_esp32.py +1 -2
- esphome/components/esp32/gpio_esp32_c2.py +1 -1
- esphome/components/esp32/gpio_esp32_c3.py +1 -1
- esphome/components/esp32/gpio_esp32_c6.py +1 -1
- esphome/components/esp32/gpio_esp32_h2.py +1 -1
- esphome/components/esp32_ble/ble.cpp +1 -0
- esphome/components/esp32_ble/ble.h +5 -3
- esphome/components/esp32_ble/ble_advertising.cpp +2 -1
- esphome/components/esp32_ble/ble_advertising.h +1 -0
- esphome/components/esp32_ble_server/__init__.py +3 -0
- esphome/components/esp32_ble_tracker/__init__.py +7 -1
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
- esphome/components/esp32_camera/__init__.py +1 -1
- esphome/components/esp32_camera/esp32_camera.cpp +2 -10
- esphome/components/esp32_camera/esp32_camera.h +1 -1
- esphome/components/esp32_can/esp32_can.cpp +1 -1
- esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
- esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
- esphome/components/esp32_rmt_led_strip/light.py +9 -1
- esphome/components/esp32_touch/esp32_touch.cpp +1 -1
- esphome/components/esp8266/gpio.cpp +69 -8
- esphome/components/ethernet/ethernet_component.cpp +1 -1
- esphome/components/event/__init__.py +13 -10
- esphome/components/factory_reset/switch/__init__.py +7 -21
- esphome/components/fan/__init__.py +52 -5
- esphome/components/fastled_base/__init__.py +1 -4
- esphome/components/fastled_base/fastled_light.cpp +1 -1
- esphome/components/feedback/cover.py +38 -33
- esphome/components/feedback/feedback_cover.cpp +2 -1
- esphome/components/fujitsu_general/climate.py +2 -9
- esphome/components/gcja5/gcja5.cpp +2 -1
- esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
- esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
- esphome/components/gpio_expander/cached_gpio.h +22 -7
- esphome/components/gps/__init__.py +47 -17
- esphome/components/gps/gps.cpp +42 -23
- esphome/components/gps/gps.h +17 -13
- esphome/components/graph/__init__.py +1 -2
- esphome/components/gree/climate.py +4 -6
- esphome/components/gree/gree.cpp +16 -2
- esphome/components/gree/gree.h +2 -2
- esphome/components/growatt_solar/growatt_solar.cpp +2 -1
- esphome/components/haier/climate.py +37 -34
- esphome/components/hbridge/fan/__init__.py +19 -17
- esphome/components/he60r/cover.py +4 -5
- esphome/components/heatpumpir/climate.py +3 -6
- esphome/components/hitachi_ac344/climate.py +2 -9
- esphome/components/hitachi_ac424/climate.py +2 -9
- esphome/components/hm3301/hm3301.h +1 -1
- esphome/components/hte501/sensor.py +6 -6
- esphome/components/http_request/__init__.py +39 -6
- esphome/components/http_request/http_request.cpp +20 -0
- esphome/components/http_request/http_request.h +57 -15
- esphome/components/http_request/http_request_arduino.cpp +22 -6
- esphome/components/http_request/http_request_arduino.h +4 -3
- esphome/components/http_request/http_request_host.cpp +141 -0
- esphome/components/http_request/http_request_host.h +37 -0
- esphome/components/http_request/http_request_idf.cpp +35 -3
- esphome/components/http_request/http_request_idf.h +10 -3
- esphome/components/http_request/httplib.h +9691 -0
- esphome/components/http_request/update/__init__.py +11 -8
- esphome/components/hyt271/sensor.py +6 -6
- esphome/components/i2c/i2c.h +4 -0
- esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
- esphome/components/i2s_audio/__init__.py +131 -22
- esphome/components/i2s_audio/i2s_audio.h +44 -4
- esphome/components/i2s_audio/media_player/__init__.py +19 -9
- esphome/components/i2s_audio/microphone/__init__.py +63 -5
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
- esphome/components/i2s_audio/speaker/__init__.py +31 -5
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
- esphome/components/ili9xxx/ili9xxx_init.h +1 -1
- esphome/components/image/__init__.py +37 -17
- esphome/components/image/image.cpp +25 -8
- esphome/components/internal_temperature/internal_temperature.cpp +6 -4
- esphome/components/key_collector/__init__.py +35 -0
- esphome/components/key_collector/key_collector.cpp +8 -0
- esphome/components/key_collector/key_collector.h +10 -0
- esphome/components/kuntze/kuntze.cpp +2 -1
- esphome/components/ld2410/ld2410.h +1 -1
- esphome/components/ld2450/ld2450.h +1 -1
- esphome/components/light/__init__.py +57 -0
- esphome/components/lock/__init__.py +51 -4
- esphome/components/lock/automation.h +2 -13
- esphome/components/logger/__init__.py +22 -0
- esphome/components/logger/logger.cpp +154 -103
- esphome/components/logger/logger.h +211 -36
- esphome/components/logger/task_log_buffer.cpp +138 -0
- esphome/components/logger/task_log_buffer.h +69 -0
- esphome/components/lvgl/__init__.py +13 -5
- esphome/components/lvgl/automation.py +50 -1
- esphome/components/lvgl/defines.py +0 -1
- esphome/components/lvgl/lvgl_esphome.cpp +5 -1
- esphome/components/lvgl/text/__init__.py +1 -2
- esphome/components/mapping/__init__.py +134 -0
- esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
- esphome/components/max7219digit/max7219digit.cpp +28 -27
- esphome/components/mdns/__init__.py +11 -5
- esphome/components/mdns/mdns_component.cpp +11 -5
- esphome/components/mdns/mdns_component.h +3 -2
- esphome/components/mdns/mdns_esp32.cpp +4 -3
- esphome/components/mdns/mdns_esp8266.cpp +4 -2
- esphome/components/mdns/mdns_libretiny.cpp +4 -2
- esphome/components/mdns/mdns_rp2040.cpp +4 -2
- esphome/components/media_player/__init__.py +33 -1
- esphome/components/mhz19/sensor.py +11 -7
- esphome/components/micro_wake_word/__init__.py +99 -31
- esphome/components/micro_wake_word/automation.h +54 -0
- esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
- esphome/components/micro_wake_word/micro_wake_word.h +58 -105
- esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
- esphome/components/micro_wake_word/streaming_model.cpp +158 -41
- esphome/components/micro_wake_word/streaming_model.h +85 -13
- esphome/components/microphone/__init__.py +139 -9
- esphome/components/microphone/automation.h +14 -2
- esphome/components/microphone/microphone.cpp +21 -0
- esphome/components/microphone/microphone.h +14 -5
- esphome/components/microphone/microphone_source.cpp +95 -0
- esphome/components/microphone/microphone_source.h +80 -0
- esphome/components/mics_4514/sensor.py +25 -14
- esphome/components/midea/climate.py +3 -4
- esphome/components/midea_ir/climate.py +3 -5
- esphome/components/mipi_spi/__init__.py +15 -0
- esphome/components/mipi_spi/display.py +474 -0
- esphome/components/mipi_spi/mipi_spi.cpp +481 -0
- esphome/components/mipi_spi/mipi_spi.h +171 -0
- esphome/components/mipi_spi/models/__init__.py +65 -0
- esphome/components/mipi_spi/models/amoled.py +72 -0
- esphome/components/mipi_spi/models/commands.py +82 -0
- esphome/components/mipi_spi/models/cyd.py +10 -0
- esphome/components/mipi_spi/models/ili.py +749 -0
- esphome/components/mipi_spi/models/jc.py +260 -0
- esphome/components/mipi_spi/models/lanbon.py +15 -0
- esphome/components/mipi_spi/models/lilygo.py +60 -0
- esphome/components/mipi_spi/models/waveshare.py +139 -0
- esphome/components/mitsubishi/climate.py +2 -5
- esphome/components/mitsubishi/mitsubishi.cpp +9 -9
- esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
- esphome/components/mixer/speaker/mixer_speaker.h +1 -3
- esphome/components/mlx90393/sensor.py +5 -0
- esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
- esphome/components/mlx90393/sensor_mlx90393.h +21 -4
- esphome/components/modbus/modbus.cpp +2 -1
- esphome/components/mqtt/__init__.py +1 -1
- esphome/components/mqtt/mqtt_client.cpp +6 -2
- esphome/components/mqtt/mqtt_const.h +4 -0
- esphome/components/mqtt/mqtt_fan.cpp +39 -0
- esphome/components/mqtt/mqtt_fan.h +2 -0
- esphome/components/ms5611/sensor.py +6 -6
- esphome/components/ms8607/sensor.py +3 -3
- esphome/components/network/__init__.py +1 -1
- esphome/components/nextion/base_component.py +17 -16
- esphome/components/nextion/display.py +11 -2
- esphome/components/nextion/nextion.cpp +39 -1
- esphome/components/nextion/nextion.h +50 -0
- esphome/components/noblex/climate.py +2 -9
- esphome/components/number/__init__.py +12 -9
- esphome/components/one_wire/one_wire_bus.cpp +14 -10
- esphome/components/one_wire/one_wire_bus.h +14 -8
- esphome/components/online_image/bmp_image.cpp +48 -11
- esphome/components/online_image/bmp_image.h +2 -0
- esphome/components/opentherm/binary_sensor/__init__.py +2 -4
- esphome/components/opentherm/number/__init__.py +11 -20
- esphome/components/opentherm/sensor/__init__.py +3 -3
- esphome/components/opentherm/switch/__init__.py +3 -5
- esphome/components/output/lock/__init__.py +11 -9
- esphome/components/packages/__init__.py +33 -31
- esphome/components/packet_transport/__init__.py +201 -0
- esphome/components/packet_transport/binary_sensor.py +19 -0
- esphome/components/packet_transport/packet_transport.cpp +534 -0
- esphome/components/packet_transport/packet_transport.h +154 -0
- esphome/components/packet_transport/sensor.py +19 -0
- esphome/components/pca9685/pca9685_output.cpp +2 -1
- esphome/components/pid/climate.py +2 -4
- esphome/components/pm2005/__init__.py +1 -0
- esphome/components/pm2005/pm2005.cpp +123 -0
- esphome/components/pm2005/pm2005.h +46 -0
- esphome/components/pm2005/sensor.py +86 -0
- esphome/components/pmsa003i/pmsa003i.cpp +43 -16
- esphome/components/pmsa003i/pmsa003i.h +25 -25
- esphome/components/pmsx003/pmsx003.cpp +195 -230
- esphome/components/pmsx003/pmsx003.h +51 -33
- esphome/components/pmsx003/sensor.py +21 -11
- esphome/components/pn7150/pn7150.h +2 -2
- esphome/components/pn7160/pn7160.h +2 -2
- esphome/components/prometheus/prometheus_handler.cpp +174 -0
- esphome/components/prometheus/prometheus_handler.h +17 -0
- esphome/components/psram/__init__.py +7 -5
- esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
- esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
- esphome/components/pzem004t/pzem004t.cpp +2 -1
- esphome/components/qspi_dbi/__init__.py +0 -1
- esphome/components/qspi_dbi/display.py +2 -1
- esphome/components/qspi_dbi/models.py +1 -2
- esphome/components/remote_base/__init__.py +91 -0
- esphome/components/remote_base/beo4_protocol.cpp +153 -0
- esphome/components/remote_base/beo4_protocol.h +43 -0
- esphome/components/remote_base/gobox_protocol.cpp +131 -0
- esphome/components/remote_base/gobox_protocol.h +54 -0
- esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
- esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
- esphome/components/resampler/speaker/resampler_speaker.h +1 -1
- esphome/components/rf_bridge/rf_bridge.cpp +2 -1
- esphome/components/scd30/sensor.py +2 -3
- esphome/components/scd4x/sensor.py +4 -5
- esphome/components/sdp3x/sensor.py +2 -1
- esphome/components/sds011/sds011.cpp +2 -1
- esphome/components/select/__init__.py +19 -20
- esphome/components/sen5x/sen5x.cpp +55 -36
- esphome/components/sen5x/sensor.py +1 -1
- esphome/components/senseair/sensor.py +3 -3
- esphome/components/sensor/__init__.py +158 -14
- esphome/components/sensor/filter.cpp +23 -0
- esphome/components/sensor/filter.h +22 -0
- esphome/components/sgp30/sensor.py +14 -16
- esphome/components/sgp4x/sensor.py +1 -1
- esphome/components/sht4x/sht4x.cpp +43 -22
- esphome/components/sht4x/sht4x.h +1 -1
- esphome/components/shtcx/sensor.py +6 -6
- esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
- esphome/components/sml/text_sensor/__init__.py +4 -6
- esphome/components/sound_level/__init__.py +0 -0
- esphome/components/sound_level/sensor.py +97 -0
- esphome/components/sound_level/sound_level.cpp +194 -0
- esphome/components/sound_level/sound_level.h +73 -0
- esphome/components/speaker/media_player/__init__.py +4 -8
- esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
- esphome/components/speaker/media_player/speaker_media_player.h +0 -11
- esphome/components/speaker/speaker.h +4 -7
- esphome/components/speed/fan/__init__.py +17 -16
- esphome/components/spi/spi.h +11 -1
- esphome/components/sprinkler/__init__.py +18 -19
- esphome/components/sprinkler/sprinkler.cpp +6 -5
- esphome/components/switch/__init__.py +32 -42
- esphome/components/syslog/__init__.py +41 -0
- esphome/components/syslog/esphome_syslog.cpp +49 -0
- esphome/components/syslog/esphome_syslog.h +27 -0
- esphome/components/t6615/sensor.py +3 -3
- esphome/components/t6615/t6615.cpp +2 -1
- esphome/components/tca9555/tca9555.cpp +11 -6
- esphome/components/tcl112/climate.py +2 -9
- esphome/components/template/alarm_control_panel/__init__.py +7 -6
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
- esphome/components/template/cover/__init__.py +27 -21
- esphome/components/template/fan/__init__.py +14 -12
- esphome/components/template/lock/__init__.py +20 -25
- esphome/components/template/lock/automation.h +18 -0
- esphome/components/template/text/__init__.py +4 -3
- esphome/components/template/valve/__init__.py +32 -21
- esphome/components/template/valve/automation.h +24 -0
- esphome/components/text/__init__.py +32 -1
- esphome/components/text_sensor/__init__.py +24 -29
- esphome/components/thermostat/climate.py +5 -5
- esphome/components/time_based/cover.py +17 -16
- esphome/components/time_based/time_based_cover.cpp +2 -1
- esphome/components/tm1638/switch/__init__.py +10 -7
- esphome/components/tormatic/cover.py +4 -5
- esphome/components/toshiba/climate.py +3 -5
- esphome/components/touchscreen/touchscreen.cpp +3 -1
- esphome/components/tuya/climate/__init__.py +5 -6
- esphome/components/tuya/cover/__init__.py +6 -11
- esphome/components/tuya/select/__init__.py +15 -5
- esphome/components/tuya/select/tuya_select.cpp +6 -1
- esphome/components/tuya/select/tuya_select.h +5 -1
- esphome/components/uart/packet_transport/__init__.py +20 -0
- esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
- esphome/components/uart/packet_transport/uart_transport.h +41 -0
- esphome/components/uart/switch/uart_switch.cpp +2 -1
- esphome/components/udp/__init__.py +126 -128
- esphome/components/udp/automation.h +40 -0
- esphome/components/udp/binary_sensor.py +3 -25
- esphome/components/udp/packet_transport/__init__.py +29 -0
- esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
- esphome/components/udp/packet_transport/udp_transport.h +28 -0
- esphome/components/udp/sensor.py +3 -25
- esphome/components/udp/udp_component.cpp +26 -470
- esphome/components/udp/udp_component.h +21 -128
- esphome/components/update/__init__.py +31 -1
- esphome/components/uponor_smatrix/climate/__init__.py +4 -9
- esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
- esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
- esphome/components/uptime/text_sensor/__init__.py +47 -7
- esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
- esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
- esphome/components/valve/__init__.py +34 -3
- esphome/components/valve/automation.h +1 -19
- esphome/components/vl53l0x/sensor.py +11 -0
- esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
- esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
- esphome/components/voice_assistant/__init__.py +36 -10
- esphome/components/voice_assistant/voice_assistant.cpp +170 -144
- esphome/components/voice_assistant/voice_assistant.h +26 -25
- esphome/components/waveshare_epaper/display.py +6 -0
- esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
- esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
- esphome/components/weikai/weikai.cpp +0 -52
- esphome/components/whirlpool/climate.py +3 -5
- esphome/components/whynter/climate.py +3 -5
- esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
- esphome/components/yashima/climate.py +6 -6
- esphome/components/zhlt01/climate.py +2 -7
- esphome/config.py +13 -13
- esphome/config_validation.py +38 -58
- esphome/const.py +15 -1
- esphome/core/__init__.py +2 -0
- esphome/core/application.cpp +27 -10
- esphome/core/application.h +9 -1
- esphome/core/automation.h +4 -3
- esphome/core/component.cpp +28 -7
- esphome/core/component.h +10 -1
- esphome/core/defines.h +23 -17
- esphome/core/doxygen.h +13 -0
- esphome/core/macros.h +4 -0
- esphome/core/scheduler.cpp +7 -1
- esphome/cpp_generator.py +6 -2
- esphome/dashboard/web_server.py +3 -3
- esphome/helpers.py +39 -0
- esphome/loader.py +4 -0
- esphome/log.py +15 -19
- esphome/mqtt.py +23 -10
- esphome/platformio_api.py +1 -1
- esphome/schema_extractors.py +0 -1
- esphome/voluptuous_schema.py +3 -1
- esphome/vscode.py +15 -0
- esphome/wizard.py +47 -37
- esphome/zeroconf.py +7 -3
- {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/METADATA +10 -11
- {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/RECORD +444 -383
- {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/WHEEL +1 -1
- {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/entry_points.txt +0 -0
- {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/top_level.txt +0 -0
@@ -9,6 +9,8 @@ from esphome.const import (
|
|
9
9
|
CONF_ID,
|
10
10
|
CONF_METHANE,
|
11
11
|
CONF_NITROGEN_DIOXIDE,
|
12
|
+
DEVICE_CLASS_CARBON_MONOXIDE,
|
13
|
+
DEVICE_CLASS_EMPTY,
|
12
14
|
STATE_CLASS_MEASUREMENT,
|
13
15
|
UNIT_PARTS_PER_MILLION,
|
14
16
|
)
|
@@ -22,24 +24,33 @@ MICS4514Component = mics_4514_ns.class_(
|
|
22
24
|
"MICS4514Component", cg.PollingComponent, i2c.I2CDevice
|
23
25
|
)
|
24
26
|
|
25
|
-
SENSORS =
|
26
|
-
CONF_CARBON_MONOXIDE,
|
27
|
-
CONF_METHANE,
|
28
|
-
CONF_ETHANOL,
|
29
|
-
CONF_HYDROGEN,
|
30
|
-
CONF_AMMONIA,
|
31
|
-
CONF_NITROGEN_DIOXIDE,
|
32
|
-
|
27
|
+
SENSORS = {
|
28
|
+
CONF_CARBON_MONOXIDE: DEVICE_CLASS_CARBON_MONOXIDE,
|
29
|
+
CONF_METHANE: DEVICE_CLASS_EMPTY,
|
30
|
+
CONF_ETHANOL: DEVICE_CLASS_EMPTY,
|
31
|
+
CONF_HYDROGEN: DEVICE_CLASS_EMPTY,
|
32
|
+
CONF_AMMONIA: DEVICE_CLASS_EMPTY,
|
33
|
+
CONF_NITROGEN_DIOXIDE: DEVICE_CLASS_EMPTY,
|
34
|
+
}
|
35
|
+
|
36
|
+
|
37
|
+
def common_sensor_schema(*, device_class: str) -> cv.Schema:
|
38
|
+
return sensor.sensor_schema(
|
39
|
+
accuracy_decimals=2,
|
40
|
+
device_class=device_class,
|
41
|
+
state_class=STATE_CLASS_MEASUREMENT,
|
42
|
+
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
43
|
+
)
|
33
44
|
|
34
|
-
common_sensor_schema = sensor.sensor_schema(
|
35
|
-
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
36
|
-
state_class=STATE_CLASS_MEASUREMENT,
|
37
|
-
accuracy_decimals=2,
|
38
|
-
)
|
39
45
|
|
40
46
|
CONFIG_SCHEMA = (
|
41
47
|
cv.Schema({cv.GenerateID(): cv.declare_id(MICS4514Component)})
|
42
|
-
.extend(
|
48
|
+
.extend(
|
49
|
+
{
|
50
|
+
cv.Optional(sensor_type): common_sensor_schema(device_class=device_class)
|
51
|
+
for sensor_type, device_class in SENSORS.items()
|
52
|
+
}
|
53
|
+
)
|
43
54
|
.extend(i2c.i2c_device_schema(0x75))
|
44
55
|
.extend(cv.polling_component_schema("60s"))
|
45
56
|
)
|
@@ -104,9 +104,9 @@ validate_custom_fan_modes = cv.enum(CUSTOM_FAN_MODES, upper=True)
|
|
104
104
|
validate_custom_presets = cv.enum(CUSTOM_PRESETS, upper=True)
|
105
105
|
|
106
106
|
CONFIG_SCHEMA = cv.All(
|
107
|
-
climate.
|
107
|
+
climate.climate_schema(AirConditioner)
|
108
|
+
.extend(
|
108
109
|
{
|
109
|
-
cv.GenerateID(): cv.declare_id(AirConditioner),
|
110
110
|
cv.Optional(CONF_PERIOD, default="1s"): cv.time_period,
|
111
111
|
cv.Optional(CONF_TIMEOUT, default="2s"): cv.time_period,
|
112
112
|
cv.Optional(CONF_NUM_ATTEMPTS, default=3): cv.int_range(min=1, max=5),
|
@@ -259,10 +259,9 @@ async def power_inv_to_code(var, config, args):
|
|
259
259
|
|
260
260
|
|
261
261
|
async def to_code(config):
|
262
|
-
var =
|
262
|
+
var = await climate.new_climate(config)
|
263
263
|
await cg.register_component(var, config)
|
264
264
|
await uart.register_uart_device(var, config)
|
265
|
-
await climate.register_climate(var, config)
|
266
265
|
cg.add(var.set_period(config[CONF_PERIOD].total_milliseconds))
|
267
266
|
cg.add(var.set_response_timeout(config[CONF_TIMEOUT].total_milliseconds))
|
268
267
|
cg.add(var.set_request_attempts(config[CONF_NUM_ATTEMPTS]))
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import esphome.codegen as cg
|
2
2
|
from esphome.components import climate_ir
|
3
3
|
import esphome.config_validation as cv
|
4
|
-
from esphome.const import
|
4
|
+
from esphome.const import CONF_USE_FAHRENHEIT
|
5
5
|
|
6
6
|
AUTO_LOAD = ["climate_ir", "coolix"]
|
7
7
|
CODEOWNERS = ["@dudanov"]
|
@@ -10,15 +10,13 @@ midea_ir_ns = cg.esphome_ns.namespace("midea_ir")
|
|
10
10
|
MideaIR = midea_ir_ns.class_("MideaIR", climate_ir.ClimateIR)
|
11
11
|
|
12
12
|
|
13
|
-
CONFIG_SCHEMA = climate_ir.
|
13
|
+
CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(MideaIR).extend(
|
14
14
|
{
|
15
|
-
cv.GenerateID(): cv.declare_id(MideaIR),
|
16
15
|
cv.Optional(CONF_USE_FAHRENHEIT, default=False): cv.boolean,
|
17
16
|
}
|
18
17
|
)
|
19
18
|
|
20
19
|
|
21
20
|
async def to_code(config):
|
22
|
-
var =
|
23
|
-
await climate_ir.register_climate_ir(var, config)
|
21
|
+
var = await climate_ir.new_climate_ir(config)
|
24
22
|
cg.add(var.set_fahrenheit(config[CONF_USE_FAHRENHEIT]))
|
@@ -0,0 +1,15 @@
|
|
1
|
+
CODEOWNERS = ["@clydebarrow"]
|
2
|
+
|
3
|
+
DOMAIN = "mipi_spi"
|
4
|
+
|
5
|
+
CONF_DRAW_FROM_ORIGIN = "draw_from_origin"
|
6
|
+
CONF_SPI_16 = "spi_16"
|
7
|
+
CONF_PIXEL_MODE = "pixel_mode"
|
8
|
+
CONF_COLOR_DEPTH = "color_depth"
|
9
|
+
CONF_BUS_MODE = "bus_mode"
|
10
|
+
CONF_USE_AXIS_FLIPS = "use_axis_flips"
|
11
|
+
CONF_NATIVE_WIDTH = "native_width"
|
12
|
+
CONF_NATIVE_HEIGHT = "native_height"
|
13
|
+
|
14
|
+
MODE_RGB = "RGB"
|
15
|
+
MODE_BGR = "BGR"
|
@@ -0,0 +1,474 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from esphome import pins
|
4
|
+
import esphome.codegen as cg
|
5
|
+
from esphome.components import display, spi
|
6
|
+
from esphome.components.spi import TYPE_OCTAL, TYPE_QUAD, TYPE_SINGLE
|
7
|
+
import esphome.config_validation as cv
|
8
|
+
from esphome.config_validation import ALLOW_EXTRA
|
9
|
+
from esphome.const import (
|
10
|
+
CONF_BRIGHTNESS,
|
11
|
+
CONF_COLOR_ORDER,
|
12
|
+
CONF_CS_PIN,
|
13
|
+
CONF_DATA_RATE,
|
14
|
+
CONF_DC_PIN,
|
15
|
+
CONF_DIMENSIONS,
|
16
|
+
CONF_ENABLE_PIN,
|
17
|
+
CONF_HEIGHT,
|
18
|
+
CONF_ID,
|
19
|
+
CONF_INIT_SEQUENCE,
|
20
|
+
CONF_INVERT_COLORS,
|
21
|
+
CONF_LAMBDA,
|
22
|
+
CONF_MIRROR_X,
|
23
|
+
CONF_MIRROR_Y,
|
24
|
+
CONF_MODEL,
|
25
|
+
CONF_OFFSET_HEIGHT,
|
26
|
+
CONF_OFFSET_WIDTH,
|
27
|
+
CONF_RESET_PIN,
|
28
|
+
CONF_ROTATION,
|
29
|
+
CONF_SWAP_XY,
|
30
|
+
CONF_TRANSFORM,
|
31
|
+
CONF_WIDTH,
|
32
|
+
)
|
33
|
+
from esphome.core import TimePeriod
|
34
|
+
|
35
|
+
from ..const import CONF_DRAW_ROUNDING
|
36
|
+
from ..lvgl.defines import CONF_COLOR_DEPTH
|
37
|
+
from . import (
|
38
|
+
CONF_BUS_MODE,
|
39
|
+
CONF_DRAW_FROM_ORIGIN,
|
40
|
+
CONF_NATIVE_HEIGHT,
|
41
|
+
CONF_NATIVE_WIDTH,
|
42
|
+
CONF_PIXEL_MODE,
|
43
|
+
CONF_SPI_16,
|
44
|
+
CONF_USE_AXIS_FLIPS,
|
45
|
+
DOMAIN,
|
46
|
+
MODE_BGR,
|
47
|
+
MODE_RGB,
|
48
|
+
)
|
49
|
+
from .models import (
|
50
|
+
DELAY_FLAG,
|
51
|
+
MADCTL_BGR,
|
52
|
+
MADCTL_MV,
|
53
|
+
MADCTL_MX,
|
54
|
+
MADCTL_MY,
|
55
|
+
MADCTL_XFLIP,
|
56
|
+
MADCTL_YFLIP,
|
57
|
+
DriverChip,
|
58
|
+
amoled,
|
59
|
+
cyd,
|
60
|
+
ili,
|
61
|
+
jc,
|
62
|
+
lanbon,
|
63
|
+
lilygo,
|
64
|
+
waveshare,
|
65
|
+
)
|
66
|
+
from .models.commands import BRIGHTNESS, DISPON, INVOFF, INVON, MADCTL, PIXFMT, SLPOUT
|
67
|
+
|
68
|
+
DEPENDENCIES = ["spi"]
|
69
|
+
|
70
|
+
LOGGER = logging.getLogger(DOMAIN)
|
71
|
+
mipi_spi_ns = cg.esphome_ns.namespace("mipi_spi")
|
72
|
+
MipiSpi = mipi_spi_ns.class_(
|
73
|
+
"MipiSpi", display.Display, display.DisplayBuffer, cg.Component, spi.SPIDevice
|
74
|
+
)
|
75
|
+
ColorOrder = display.display_ns.enum("ColorMode")
|
76
|
+
ColorBitness = display.display_ns.enum("ColorBitness")
|
77
|
+
Model = mipi_spi_ns.enum("Model")
|
78
|
+
|
79
|
+
COLOR_ORDERS = {
|
80
|
+
MODE_RGB: ColorOrder.COLOR_ORDER_RGB,
|
81
|
+
MODE_BGR: ColorOrder.COLOR_ORDER_BGR,
|
82
|
+
}
|
83
|
+
|
84
|
+
COLOR_DEPTHS = {
|
85
|
+
8: ColorBitness.COLOR_BITNESS_332,
|
86
|
+
16: ColorBitness.COLOR_BITNESS_565,
|
87
|
+
}
|
88
|
+
DATA_PIN_SCHEMA = pins.internal_gpio_output_pin_schema
|
89
|
+
|
90
|
+
|
91
|
+
DriverChip("CUSTOM", initsequence={})
|
92
|
+
|
93
|
+
MODELS = DriverChip.models
|
94
|
+
# These statements are noops, but serve to suppress linting of side-effect-only imports
|
95
|
+
for _ in (ili, jc, amoled, lilygo, lanbon, cyd, waveshare):
|
96
|
+
pass
|
97
|
+
|
98
|
+
PixelMode = mipi_spi_ns.enum("PixelMode")
|
99
|
+
|
100
|
+
PIXEL_MODE_18BIT = "18bit"
|
101
|
+
PIXEL_MODE_16BIT = "16bit"
|
102
|
+
|
103
|
+
PIXEL_MODES = {
|
104
|
+
PIXEL_MODE_16BIT: 0x55,
|
105
|
+
PIXEL_MODE_18BIT: 0x66,
|
106
|
+
}
|
107
|
+
|
108
|
+
|
109
|
+
def validate_dimension(rounding):
|
110
|
+
def validator(value):
|
111
|
+
value = cv.positive_int(value)
|
112
|
+
if value % rounding != 0:
|
113
|
+
raise cv.Invalid(f"Dimensions and offsets must be divisible by {rounding}")
|
114
|
+
return value
|
115
|
+
|
116
|
+
return validator
|
117
|
+
|
118
|
+
|
119
|
+
def map_sequence(value):
|
120
|
+
"""
|
121
|
+
The format is a repeated sequence of [CMD, <data>] where <data> is s a sequence of bytes. The length is inferred
|
122
|
+
from the length of the sequence and should not be explicit.
|
123
|
+
A delay can be inserted by specifying "- delay N" where N is in ms
|
124
|
+
"""
|
125
|
+
if isinstance(value, str) and value.lower().startswith("delay "):
|
126
|
+
value = value.lower()[6:]
|
127
|
+
delay = cv.All(
|
128
|
+
cv.positive_time_period_milliseconds,
|
129
|
+
cv.Range(TimePeriod(milliseconds=1), TimePeriod(milliseconds=255)),
|
130
|
+
)(value)
|
131
|
+
return DELAY_FLAG, delay.total_milliseconds
|
132
|
+
if isinstance(value, int):
|
133
|
+
return (value,)
|
134
|
+
value = cv.All(cv.ensure_list(cv.int_range(0, 255)), cv.Length(1, 254))(value)
|
135
|
+
return tuple(value)
|
136
|
+
|
137
|
+
|
138
|
+
def power_of_two(value):
|
139
|
+
value = cv.int_range(1, 128)(value)
|
140
|
+
if value & (value - 1) != 0:
|
141
|
+
raise cv.Invalid("value must be a power of two")
|
142
|
+
return value
|
143
|
+
|
144
|
+
|
145
|
+
def dimension_schema(rounding):
|
146
|
+
return cv.Any(
|
147
|
+
cv.dimensions,
|
148
|
+
cv.Schema(
|
149
|
+
{
|
150
|
+
cv.Required(CONF_WIDTH): validate_dimension(rounding),
|
151
|
+
cv.Required(CONF_HEIGHT): validate_dimension(rounding),
|
152
|
+
cv.Optional(CONF_OFFSET_HEIGHT, default=0): validate_dimension(
|
153
|
+
rounding
|
154
|
+
),
|
155
|
+
cv.Optional(CONF_OFFSET_WIDTH, default=0): validate_dimension(rounding),
|
156
|
+
}
|
157
|
+
),
|
158
|
+
)
|
159
|
+
|
160
|
+
|
161
|
+
def model_schema(bus_mode, model: DriverChip, swapsies: bool):
|
162
|
+
transform = cv.Schema(
|
163
|
+
{
|
164
|
+
cv.Required(CONF_MIRROR_X): cv.boolean,
|
165
|
+
cv.Required(CONF_MIRROR_Y): cv.boolean,
|
166
|
+
}
|
167
|
+
)
|
168
|
+
if model.get_default(CONF_SWAP_XY, False) == cv.UNDEFINED:
|
169
|
+
transform = transform.extend(
|
170
|
+
{
|
171
|
+
cv.Optional(CONF_SWAP_XY): cv.invalid(
|
172
|
+
"Axis swapping not supported by this model"
|
173
|
+
)
|
174
|
+
}
|
175
|
+
)
|
176
|
+
else:
|
177
|
+
transform = transform.extend(
|
178
|
+
{
|
179
|
+
cv.Required(CONF_SWAP_XY): cv.boolean,
|
180
|
+
}
|
181
|
+
)
|
182
|
+
# CUSTOM model will need to provide a custom init sequence
|
183
|
+
iseqconf = (
|
184
|
+
cv.Required(CONF_INIT_SEQUENCE)
|
185
|
+
if model.initsequence is None
|
186
|
+
else cv.Optional(CONF_INIT_SEQUENCE)
|
187
|
+
)
|
188
|
+
# Dimensions are optional if the model has a default width and the transform is not overridden
|
189
|
+
cv_dimensions = (
|
190
|
+
cv.Optional if model.get_default(CONF_WIDTH) and not swapsies else cv.Required
|
191
|
+
)
|
192
|
+
pixel_modes = PIXEL_MODES if bus_mode == TYPE_SINGLE else (PIXEL_MODE_16BIT,)
|
193
|
+
color_depth = (
|
194
|
+
("16", "8", "16bit", "8bit") if bus_mode == TYPE_SINGLE else ("16", "16bit")
|
195
|
+
)
|
196
|
+
schema = (
|
197
|
+
display.FULL_DISPLAY_SCHEMA.extend(
|
198
|
+
spi.spi_device_schema(
|
199
|
+
cs_pin_required=False,
|
200
|
+
default_mode="MODE3" if bus_mode == TYPE_OCTAL else "MODE0",
|
201
|
+
default_data_rate=model.get_default(CONF_DATA_RATE, 10_000_000),
|
202
|
+
mode=bus_mode,
|
203
|
+
)
|
204
|
+
)
|
205
|
+
.extend(
|
206
|
+
{
|
207
|
+
model.option(pin, cv.UNDEFINED): pins.gpio_output_pin_schema
|
208
|
+
for pin in (CONF_RESET_PIN, CONF_CS_PIN, CONF_DC_PIN)
|
209
|
+
}
|
210
|
+
)
|
211
|
+
.extend(
|
212
|
+
{
|
213
|
+
cv.GenerateID(): cv.declare_id(MipiSpi),
|
214
|
+
cv_dimensions(CONF_DIMENSIONS): dimension_schema(
|
215
|
+
model.get_default(CONF_DRAW_ROUNDING, 1)
|
216
|
+
),
|
217
|
+
model.option(CONF_ENABLE_PIN, cv.UNDEFINED): cv.ensure_list(
|
218
|
+
pins.gpio_output_pin_schema
|
219
|
+
),
|
220
|
+
model.option(CONF_COLOR_ORDER, MODE_BGR): cv.enum(
|
221
|
+
COLOR_ORDERS, upper=True
|
222
|
+
),
|
223
|
+
model.option(CONF_COLOR_DEPTH, 16): cv.one_of(*color_depth, lower=True),
|
224
|
+
model.option(CONF_DRAW_ROUNDING, 2): power_of_two,
|
225
|
+
model.option(CONF_PIXEL_MODE, PIXEL_MODE_16BIT): cv.Any(
|
226
|
+
cv.one_of(*pixel_modes, lower=True),
|
227
|
+
cv.int_range(0, 255, min_included=True, max_included=True),
|
228
|
+
),
|
229
|
+
cv.Optional(CONF_TRANSFORM): transform,
|
230
|
+
cv.Optional(CONF_BUS_MODE, default=bus_mode): cv.one_of(
|
231
|
+
bus_mode, lower=True
|
232
|
+
),
|
233
|
+
cv.Required(CONF_MODEL): cv.one_of(model.name, upper=True),
|
234
|
+
iseqconf: cv.ensure_list(map_sequence),
|
235
|
+
}
|
236
|
+
)
|
237
|
+
.extend(
|
238
|
+
{
|
239
|
+
model.option(x): cv.boolean
|
240
|
+
for x in [
|
241
|
+
CONF_DRAW_FROM_ORIGIN,
|
242
|
+
CONF_SPI_16,
|
243
|
+
CONF_INVERT_COLORS,
|
244
|
+
CONF_USE_AXIS_FLIPS,
|
245
|
+
]
|
246
|
+
}
|
247
|
+
)
|
248
|
+
)
|
249
|
+
if brightness := model.get_default(CONF_BRIGHTNESS):
|
250
|
+
schema = schema.extend(
|
251
|
+
{
|
252
|
+
cv.Optional(CONF_BRIGHTNESS, default=brightness): cv.int_range(
|
253
|
+
0, 0xFF, min_included=True, max_included=True
|
254
|
+
),
|
255
|
+
}
|
256
|
+
)
|
257
|
+
if bus_mode != TYPE_SINGLE:
|
258
|
+
return cv.All(schema, cv.only_with_esp_idf)
|
259
|
+
return schema
|
260
|
+
|
261
|
+
|
262
|
+
def rotation_as_transform(model, config):
|
263
|
+
"""
|
264
|
+
Check if a rotation can be implemented in hardware using the MADCTL register.
|
265
|
+
A rotation of 180 is always possible, 90 and 270 are possible if the model supports swapping X and Y.
|
266
|
+
"""
|
267
|
+
rotation = config.get(CONF_ROTATION, 0)
|
268
|
+
return rotation and (
|
269
|
+
model.get_default(CONF_SWAP_XY) != cv.UNDEFINED or rotation == 180
|
270
|
+
)
|
271
|
+
|
272
|
+
|
273
|
+
def config_schema(config):
|
274
|
+
# First get the model and bus mode
|
275
|
+
config = cv.Schema(
|
276
|
+
{
|
277
|
+
cv.Required(CONF_MODEL): cv.one_of(*MODELS, upper=True),
|
278
|
+
},
|
279
|
+
extra=ALLOW_EXTRA,
|
280
|
+
)(config)
|
281
|
+
model = MODELS[config[CONF_MODEL]]
|
282
|
+
bus_modes = model.modes
|
283
|
+
config = cv.Schema(
|
284
|
+
{
|
285
|
+
model.option(CONF_BUS_MODE, TYPE_SINGLE): cv.one_of(*bus_modes, lower=True),
|
286
|
+
cv.Required(CONF_MODEL): cv.one_of(*MODELS, upper=True),
|
287
|
+
},
|
288
|
+
extra=ALLOW_EXTRA,
|
289
|
+
)(config)
|
290
|
+
bus_mode = config.get(CONF_BUS_MODE, model.modes[0])
|
291
|
+
swapsies = config.get(CONF_TRANSFORM, {}).get(CONF_SWAP_XY) is True
|
292
|
+
config = model_schema(bus_mode, model, swapsies)(config)
|
293
|
+
# Check for invalid combinations of MADCTL config
|
294
|
+
if init_sequence := config.get(CONF_INIT_SEQUENCE):
|
295
|
+
if MADCTL in [x[0] for x in init_sequence] and CONF_TRANSFORM in config:
|
296
|
+
raise cv.Invalid(
|
297
|
+
f"transform is not supported when MADCTL ({MADCTL:#X}) is in the init sequence"
|
298
|
+
)
|
299
|
+
|
300
|
+
if bus_mode == TYPE_QUAD and CONF_DC_PIN in config:
|
301
|
+
raise cv.Invalid("DC pin is not supported in quad mode")
|
302
|
+
if config[CONF_PIXEL_MODE] == PIXEL_MODE_18BIT and bus_mode != TYPE_SINGLE:
|
303
|
+
raise cv.Invalid("18-bit pixel mode is not supported on a quad or octal bus")
|
304
|
+
if bus_mode != TYPE_QUAD and CONF_DC_PIN not in config:
|
305
|
+
raise cv.Invalid(f"DC pin is required in {bus_mode} mode")
|
306
|
+
return config
|
307
|
+
|
308
|
+
|
309
|
+
CONFIG_SCHEMA = config_schema
|
310
|
+
|
311
|
+
|
312
|
+
def get_transform(model, config):
|
313
|
+
can_transform = rotation_as_transform(model, config)
|
314
|
+
transform = config.get(
|
315
|
+
CONF_TRANSFORM,
|
316
|
+
{
|
317
|
+
CONF_MIRROR_X: model.get_default(CONF_MIRROR_X, False),
|
318
|
+
CONF_MIRROR_Y: model.get_default(CONF_MIRROR_Y, False),
|
319
|
+
CONF_SWAP_XY: model.get_default(CONF_SWAP_XY, False),
|
320
|
+
},
|
321
|
+
)
|
322
|
+
|
323
|
+
# Can we use the MADCTL register to set the rotation?
|
324
|
+
if can_transform and CONF_TRANSFORM not in config:
|
325
|
+
rotation = config[CONF_ROTATION]
|
326
|
+
if rotation == 180:
|
327
|
+
transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X]
|
328
|
+
transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y]
|
329
|
+
elif rotation == 90:
|
330
|
+
transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY]
|
331
|
+
transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X]
|
332
|
+
else:
|
333
|
+
transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY]
|
334
|
+
transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y]
|
335
|
+
transform[CONF_TRANSFORM] = True
|
336
|
+
return transform
|
337
|
+
|
338
|
+
|
339
|
+
def get_sequence(model, config):
|
340
|
+
"""
|
341
|
+
Create the init sequence for the display.
|
342
|
+
Use the default sequence from the model, if any, and append any custom sequence provided in the config.
|
343
|
+
Append SLPOUT (if not already in the sequence) and DISPON to the end of the sequence
|
344
|
+
Pixel format, color order, and orientation will be set.
|
345
|
+
"""
|
346
|
+
sequence = list(model.initsequence)
|
347
|
+
custom_sequence = config.get(CONF_INIT_SEQUENCE, [])
|
348
|
+
sequence.extend(custom_sequence)
|
349
|
+
# Ensure each command is a tuple
|
350
|
+
sequence = [x if isinstance(x, tuple) else (x,) for x in sequence]
|
351
|
+
commands = [x[0] for x in sequence]
|
352
|
+
# Set pixel format if not already in the custom sequence
|
353
|
+
if PIXFMT not in commands:
|
354
|
+
pixel_mode = config[CONF_PIXEL_MODE]
|
355
|
+
if not isinstance(pixel_mode, int):
|
356
|
+
pixel_mode = PIXEL_MODES[pixel_mode]
|
357
|
+
sequence.append((PIXFMT, pixel_mode))
|
358
|
+
# Does the chip use the flipping bits for mirroring rather than the reverse order bits?
|
359
|
+
use_flip = config[CONF_USE_AXIS_FLIPS]
|
360
|
+
if MADCTL not in commands:
|
361
|
+
madctl = 0
|
362
|
+
transform = get_transform(model, config)
|
363
|
+
if transform.get(CONF_TRANSFORM):
|
364
|
+
LOGGER.info("Using hardware transform to implement rotation")
|
365
|
+
if transform.get(CONF_MIRROR_X):
|
366
|
+
madctl |= MADCTL_XFLIP if use_flip else MADCTL_MX
|
367
|
+
if transform.get(CONF_MIRROR_Y):
|
368
|
+
madctl |= MADCTL_YFLIP if use_flip else MADCTL_MY
|
369
|
+
if transform.get(CONF_SWAP_XY) is True: # Exclude Undefined
|
370
|
+
madctl |= MADCTL_MV
|
371
|
+
if config[CONF_COLOR_ORDER] == MODE_BGR:
|
372
|
+
madctl |= MADCTL_BGR
|
373
|
+
sequence.append((MADCTL, madctl))
|
374
|
+
if INVON not in commands and INVOFF not in commands:
|
375
|
+
if config[CONF_INVERT_COLORS]:
|
376
|
+
sequence.append((INVON,))
|
377
|
+
else:
|
378
|
+
sequence.append((INVOFF,))
|
379
|
+
if BRIGHTNESS not in commands:
|
380
|
+
if brightness := config.get(
|
381
|
+
CONF_BRIGHTNESS, model.get_default(CONF_BRIGHTNESS)
|
382
|
+
):
|
383
|
+
sequence.append((BRIGHTNESS, brightness))
|
384
|
+
if SLPOUT not in commands:
|
385
|
+
sequence.append((SLPOUT,))
|
386
|
+
sequence.append((DISPON,))
|
387
|
+
|
388
|
+
# Flatten the sequence into a list of bytes, with the length of each command
|
389
|
+
# or the delay flag inserted where needed
|
390
|
+
return sum(
|
391
|
+
tuple(
|
392
|
+
(x[1], 0xFF) if x[0] == DELAY_FLAG else (x[0], len(x) - 1) + x[1:]
|
393
|
+
for x in sequence
|
394
|
+
),
|
395
|
+
(),
|
396
|
+
)
|
397
|
+
|
398
|
+
|
399
|
+
async def to_code(config):
|
400
|
+
model = MODELS[config[CONF_MODEL]]
|
401
|
+
transform = get_transform(model, config)
|
402
|
+
if CONF_DIMENSIONS in config:
|
403
|
+
# Explicit dimensions, just use as is
|
404
|
+
dimensions = config[CONF_DIMENSIONS]
|
405
|
+
if isinstance(dimensions, dict):
|
406
|
+
width = dimensions[CONF_WIDTH]
|
407
|
+
height = dimensions[CONF_HEIGHT]
|
408
|
+
offset_width = dimensions[CONF_OFFSET_WIDTH]
|
409
|
+
offset_height = dimensions[CONF_OFFSET_HEIGHT]
|
410
|
+
else:
|
411
|
+
(width, height) = dimensions
|
412
|
+
offset_width = 0
|
413
|
+
offset_height = 0
|
414
|
+
else:
|
415
|
+
# Default dimensions, use model defaults and transform if needed
|
416
|
+
width = model.get_default(CONF_WIDTH)
|
417
|
+
height = model.get_default(CONF_HEIGHT)
|
418
|
+
offset_width = model.get_default(CONF_OFFSET_WIDTH, 0)
|
419
|
+
offset_height = model.get_default(CONF_OFFSET_HEIGHT, 0)
|
420
|
+
|
421
|
+
# if mirroring axes and there are offsets, also mirror the offsets to cater for situations where
|
422
|
+
# the offset is asymmetric
|
423
|
+
if transform[CONF_MIRROR_X]:
|
424
|
+
native_width = model.get_default(
|
425
|
+
CONF_NATIVE_WIDTH, width + offset_width * 2
|
426
|
+
)
|
427
|
+
offset_width = native_width - width - offset_width
|
428
|
+
if transform[CONF_MIRROR_Y]:
|
429
|
+
native_height = model.get_default(
|
430
|
+
CONF_NATIVE_HEIGHT, height + offset_height * 2
|
431
|
+
)
|
432
|
+
offset_height = native_height - height - offset_height
|
433
|
+
# Swap default dimensions if swap_xy is set
|
434
|
+
if transform[CONF_SWAP_XY] is True:
|
435
|
+
width, height = height, width
|
436
|
+
offset_height, offset_width = offset_width, offset_height
|
437
|
+
|
438
|
+
color_depth = config[CONF_COLOR_DEPTH]
|
439
|
+
if color_depth.endswith("bit"):
|
440
|
+
color_depth = color_depth[:-3]
|
441
|
+
color_depth = COLOR_DEPTHS[int(color_depth)]
|
442
|
+
|
443
|
+
var = cg.new_Pvariable(
|
444
|
+
config[CONF_ID], width, height, offset_width, offset_height, color_depth
|
445
|
+
)
|
446
|
+
cg.add(var.set_init_sequence(get_sequence(model, config)))
|
447
|
+
if rotation_as_transform(model, config):
|
448
|
+
if CONF_TRANSFORM in config:
|
449
|
+
LOGGER.warning("Use of 'transform' with 'rotation' is not recommended")
|
450
|
+
else:
|
451
|
+
config[CONF_ROTATION] = 0
|
452
|
+
cg.add(var.set_model(config[CONF_MODEL]))
|
453
|
+
cg.add(var.set_draw_from_origin(config[CONF_DRAW_FROM_ORIGIN]))
|
454
|
+
cg.add(var.set_draw_rounding(config[CONF_DRAW_ROUNDING]))
|
455
|
+
cg.add(var.set_spi_16(config[CONF_SPI_16]))
|
456
|
+
if enable_pin := config.get(CONF_ENABLE_PIN):
|
457
|
+
enable = [await cg.gpio_pin_expression(pin) for pin in enable_pin]
|
458
|
+
cg.add(var.set_enable_pins(enable))
|
459
|
+
|
460
|
+
if reset_pin := config.get(CONF_RESET_PIN):
|
461
|
+
reset = await cg.gpio_pin_expression(reset_pin)
|
462
|
+
cg.add(var.set_reset_pin(reset))
|
463
|
+
|
464
|
+
if dc_pin := config.get(CONF_DC_PIN):
|
465
|
+
dc_pin = await cg.gpio_pin_expression(dc_pin)
|
466
|
+
cg.add(var.set_dc_pin(dc_pin))
|
467
|
+
|
468
|
+
if lamb := config.get(CONF_LAMBDA):
|
469
|
+
lambda_ = await cg.process_lambda(
|
470
|
+
lamb, [(display.DisplayRef, "it")], return_type=cg.void
|
471
|
+
)
|
472
|
+
cg.add(var.set_writer(lambda_))
|
473
|
+
await display.register_display(var, config)
|
474
|
+
await spi.register_spi_device(var, config)
|