diff --git a/docker/Dockerfile b/docker/Dockerfile index ed6ce083a8..c2902a9dd1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -99,15 +99,17 @@ BUILD_DEPS=" libfreetype-dev=2.12.1+dfsg-5+deb12u3 libssl-dev=3.0.15-1~deb12u1 libffi-dev=3.4.4-1 - libopenjp2-7=2.5.0-2 - libtiff6=4.5.0-6+deb12u1 cargo=0.66.0+ds1-1 pkg-config=1.8.1-1 " +LIB_DEPS=" + libtiff6=4.5.0-6+deb12u1 + libopenjp2-7=2.5.0-2 +" if [ "$TARGETARCH$TARGETVARIANT" = "arm64" ] || [ "$TARGETARCH$TARGETVARIANT" = "armv7" ] then apt-get update - apt-get install -y --no-install-recommends $BUILD_DEPS + apt-get install -y --no-install-recommends $BUILD_DEPS $LIB_DEPS fi CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 301a472810..57cae9e2f5 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -58,7 +58,7 @@ class BinarySensor : public EntityBase, public EntityBase_DeviceClass { void publish_initial_state(bool state); /// The current reported state of the binary sensor. - bool state; + bool state{false}; void add_filter(Filter *filter); void add_filters(const std::vector &filters); diff --git a/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp b/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp index e2910032cc..d111723669 100644 --- a/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp +++ b/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp @@ -15,7 +15,7 @@ static const char *const TAG = "honeywellabp2"; void HONEYWELLABP2Sensor::read_sensor_data() { if (this->read(raw_data_, 7) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Communication with ABP2 failed!"); - this->mark_failed(); + this->status_set_warning("couldn't read sensor data"); return; } float press_counts = encode_uint24(raw_data_[1], raw_data_[2], raw_data_[3]); // calculate digital pressure counts @@ -25,12 +25,13 @@ void HONEYWELLABP2Sensor::read_sensor_data() { (this->max_pressure_ - this->min_pressure_)) + this->min_pressure_; this->last_temperature_ = (temp_counts * 200 / 16777215) - 50; + this->status_clear_warning(); } void HONEYWELLABP2Sensor::start_measurement() { if (this->write(i2c_cmd_, 3) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Communication with ABP2 failed!"); - this->mark_failed(); + this->status_set_warning("couldn't start measurement"); return; } this->measurement_running_ = true; @@ -39,7 +40,7 @@ void HONEYWELLABP2Sensor::start_measurement() { bool HONEYWELLABP2Sensor::is_measurement_ready() { if (this->read(raw_data_, 1) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Communication with ABP2 failed!"); - this->mark_failed(); + this->status_set_warning("couldn't check measurement"); return false; } if ((raw_data_[0] & (0x1 << STATUS_BIT_BUSY)) > 0) { @@ -52,7 +53,7 @@ bool HONEYWELLABP2Sensor::is_measurement_ready() { void HONEYWELLABP2Sensor::measurement_timeout() { ESP_LOGE(TAG, "Timeout!"); this->measurement_running_ = false; - this->mark_failed(); + this->status_set_warning("measurement timed out"); } float HONEYWELLABP2Sensor::get_pressure() { return this->last_pressure_; } @@ -79,7 +80,7 @@ void HONEYWELLABP2Sensor::update() { ESP_LOGV(TAG, "Update Honeywell ABP2 Sensor"); this->start_measurement(); - this->set_timeout("meas_timeout", 50, [this] { this->measurement_timeout(); }); + this->set_timeout("meas_timeout", 100, [this] { this->measurement_timeout(); }); } void HONEYWELLABP2Sensor::dump_config() { diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index d03adc9624..8fdd03f647 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -322,8 +322,8 @@ async def to_code(configs): await encoders_to_code(lv_component, config, default_group) await keypads_to_code(lv_component, config, default_group) await theme_to_code(config) - await styles_to_code(config) await gradients_to_code(config) + await styles_to_code(config) await set_obj_properties(lv_scr_act, config) await add_widgets(lv_scr_act, config) await add_pages(lv_component, config) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index b91b0905df..766c010244 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -30,7 +30,7 @@ from .defines import ( call_lambda, literal, ) -from .helpers import esphome_fonts_used, lv_fonts_used, requires_component +from .helpers import add_lv_use, esphome_fonts_used, lv_fonts_used, requires_component from .types import lv_font_t, lv_gradient_t, lv_img_t opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER") @@ -326,6 +326,7 @@ def image_validator(value): value = requires_component("image")(value) value = cv.use_id(Image_)(value) lv_images_used.add(value) + add_lv_use("img", "label") return value diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 516627708e..3f56b3345f 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -341,6 +341,7 @@ FLEX_OBJ_SCHEMA = { cv.Optional(df.CONF_FLEX_GROW): cv.int_, } + DISP_BG_SCHEMA = cv.Schema( { cv.Optional(df.CONF_DISP_BG_IMAGE): lv_image, diff --git a/esphome/components/lvgl/widgets/line.py b/esphome/components/lvgl/widgets/line.py index 4c6439fde4..548dfa8452 100644 --- a/esphome/components/lvgl/widgets/line.py +++ b/esphome/components/lvgl/widgets/line.py @@ -39,7 +39,10 @@ LINE_SCHEMA = { class LineType(WidgetType): def __init__(self): super().__init__( - CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA, modify_schema={} + CONF_LINE, + LvType("lv_line_t"), + (CONF_MAIN,), + LINE_SCHEMA, ) async def to_code(self, w: Widget, config): diff --git a/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h b/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h index d8a217f55e..2c1ce96f0a 100644 --- a/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h +++ b/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h @@ -6,7 +6,7 @@ namespace esphome { namespace matrix_keypad { -class MatrixKeypadBinarySensor : public MatrixKeypadListener, public binary_sensor::BinarySensor { +class MatrixKeypadBinarySensor : public MatrixKeypadListener, public binary_sensor::BinarySensorInitiallyOff { public: MatrixKeypadBinarySensor(uint8_t key) : has_key_(true), key_(key){}; MatrixKeypadBinarySensor(const char *key) : has_key_(true), key_((uint8_t) key[0]){}; diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index 8544b50261..47deea83e6 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -38,8 +38,9 @@ void Modbus::loop() { // stop blocking new send commands after sent_wait_time_ ms after response received if (now - this->last_send_ > send_wait_time_) { - if (waiting_for_response > 0) + if (waiting_for_response > 0) { ESP_LOGV(TAG, "Stop waiting for response from %d", waiting_for_response); + } waiting_for_response = 0; } } diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 5c407d6fff..2a08075831 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -163,7 +163,7 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional(CONF_ON_OFFLINE): automation.validate_automation( { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ModbusOnlineTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ModbusOfflineTrigger), } ), } diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index e1102516ca..f8b72af817 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -622,51 +622,87 @@ int64_t payload_to_number(const std::vector &data, SensorValueType sens uint32_t bitmask) { int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits + size_t size = data.size() - offset; + bool error = false; switch (sensor_value_type) { case SensorValueType::U_WORD: - value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); // default is 0xFFFF ; + if (size >= 2) { + value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); // default is 0xFFFF ; + } else { + error = true; + } break; case SensorValueType::U_DWORD: case SensorValueType::FP32: - value = get_data(data, offset); - value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + if (size >= 4) { + value = get_data(data, offset); + value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + } else { + error = true; + } break; case SensorValueType::U_DWORD_R: case SensorValueType::FP32_R: - value = get_data(data, offset); - value = static_cast(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16; - value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + if (size >= 4) { + value = get_data(data, offset); + value = static_cast(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16; + value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + } else { + error = true; + } break; case SensorValueType::S_WORD: - value = mask_and_shift_by_rightbit(get_data(data, offset), - bitmask); // default is 0xFFFF ; + if (size >= 2) { + value = mask_and_shift_by_rightbit(get_data(data, offset), + bitmask); // default is 0xFFFF ; + } else { + error = true; + } break; case SensorValueType::S_DWORD: - value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); + if (size >= 4) { + value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); + } else { + error = true; + } break; case SensorValueType::S_DWORD_R: { - value = get_data(data, offset); - // Currently the high word is at the low position - // the sign bit is therefore at low before the switch - uint32_t sign_bit = (value & 0x8000) << 16; - value = mask_and_shift_by_rightbit( - static_cast(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask); + if (size >= 4) { + value = get_data(data, offset); + // Currently the high word is at the low position + // the sign bit is therefore at low before the switch + uint32_t sign_bit = (value & 0x8000) << 16; + value = mask_and_shift_by_rightbit( + static_cast(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask); + } else { + error = true; + } } break; case SensorValueType::U_QWORD: case SensorValueType::S_QWORD: // Ignore bitmask for QWORD - value = get_data(data, offset); + if (size >= 8) { + value = get_data(data, offset); + } else { + error = true; + } break; case SensorValueType::U_QWORD_R: case SensorValueType::S_QWORD_R: { // Ignore bitmask for QWORD - uint64_t tmp = get_data(data, offset); - value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000); + if (size >= 8) { + uint64_t tmp = get_data(data, offset); + value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000); + } else { + error = true; + } } break; case SensorValueType::RAW: default: break; } + if (error) + ESP_LOGE(TAG, "not enough data for value"); return value; } diff --git a/esphome/components/qspi_dbi/models.py b/esphome/components/qspi_dbi/models.py index cbd9c4663f..c1fe434853 100644 --- a/esphome/components/qspi_dbi/models.py +++ b/esphome/components/qspi_dbi/models.py @@ -1,6 +1,7 @@ # Commands SW_RESET_CMD = 0x01 SLEEP_OUT = 0x11 +NORON = 0x13 INVERT_OFF = 0x20 INVERT_ON = 0x21 ALL_ON = 0x23 @@ -56,6 +57,7 @@ chip.cmd(0xC2, 0x00) chip.delay(10) chip.cmd(TEON, 0x00) chip.cmd(PIXFMT, 0x55) +chip.cmd(NORON) chip = DriverChip("AXS15231") chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5) diff --git a/esphome/components/qspi_dbi/qspi_dbi.cpp b/esphome/components/qspi_dbi/qspi_dbi.cpp index a649a25ea6..785885d4ec 100644 --- a/esphome/components/qspi_dbi/qspi_dbi.cpp +++ b/esphome/components/qspi_dbi/qspi_dbi.cpp @@ -111,7 +111,6 @@ void QspiDbi::reset_params_(bool ready) { mad |= MADCTL_MY; this->write_command_(MADCTL_CMD, mad); this->write_command_(BRIGHTNESS, this->brightness_); - this->write_command_(NORON); this->write_command_(DISPLAY_ON); } diff --git a/esphome/components/status/binary_sensor.py b/esphome/components/status/binary_sensor.py index 1f2b7c9d18..adc342ed4d 100644 --- a/esphome/components/status/binary_sensor.py +++ b/esphome/components/status/binary_sensor.py @@ -6,6 +6,8 @@ from esphome.const import ( ENTITY_CATEGORY_DIAGNOSTIC, ) +DEPENDENCIES = ["network"] + status_ns = cg.esphome_ns.namespace("status") StatusBinarySensor = status_ns.class_( "StatusBinarySensor", binary_sensor.BinarySensor, cg.Component diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index 88648093c6..ee00e2ac6c 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -137,8 +137,16 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.sta.ssid), sizeof(conf.sta.ssid), "%s", ap.get_ssid().c_str()); - snprintf(reinterpret_cast(conf.sta.password), sizeof(conf.sta.password), "%s", ap.get_password().c_str()); + if (ap.get_ssid().size() > sizeof(conf.sta.ssid)) { + ESP_LOGE(TAG, "SSID is too long"); + return false; + } + if (ap.get_password().size() > sizeof(conf.sta.password)) { + ESP_LOGE(TAG, "password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); + memcpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), ap.get_password().size()); // The weakest authmode to accept in the fast scan mode if (ap.get_password().empty()) { @@ -746,7 +754,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.ap.ssid), sizeof(conf.ap.ssid), "%s", ap.get_ssid().c_str()); + if (ap.get_ssid().size() > sizeof(conf.ap.ssid)) { + ESP_LOGE(TAG, "AP SSID is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); conf.ap.channel = ap.get_channel().value_or(1); conf.ap.ssid_hidden = ap.get_ssid().size(); conf.ap.max_connection = 5; @@ -757,7 +769,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.ap.password = 0; } else { conf.ap.authmode = WIFI_AUTH_WPA2_PSK; - snprintf(reinterpret_cast(conf.ap.password), sizeof(conf.ap.password), "%s", ap.get_password().c_str()); + if (ap.get_password().size() > sizeof(conf.ap.password)) { + ESP_LOGE(TAG, "AP password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), ap.get_password().size()); } // pairwise cipher of SoftAP, group cipher will be derived using this. diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 4568895950..14506f569c 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -236,8 +236,16 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { struct station_config conf {}; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.ssid), sizeof(conf.ssid), "%s", ap.get_ssid().c_str()); - snprintf(reinterpret_cast(conf.password), sizeof(conf.password), "%s", ap.get_password().c_str()); + if (ap.get_ssid().size() > sizeof(conf.ssid)) { + ESP_LOGE(TAG, "SSID is too long"); + return false; + } + if (ap.get_password().size() > sizeof(conf.password)) { + ESP_LOGE(TAG, "password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); + memcpy(reinterpret_cast(conf.password), ap.get_password().c_str(), ap.get_password().size()); if (ap.get_bssid().has_value()) { conf.bssid_set = 1; @@ -775,7 +783,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return false; struct softap_config conf {}; - snprintf(reinterpret_cast(conf.ssid), sizeof(conf.ssid), "%s", ap.get_ssid().c_str()); + if (ap.get_ssid().size() > sizeof(conf.ssid)) { + ESP_LOGE(TAG, "AP SSID is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); conf.ssid_len = static_cast(ap.get_ssid().size()); conf.channel = ap.get_channel().value_or(1); conf.ssid_hidden = ap.get_hidden(); @@ -787,7 +799,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.password = 0; } else { conf.authmode = AUTH_WPA2_PSK; - snprintf(reinterpret_cast(conf.password), sizeof(conf.password), "%s", ap.get_password().c_str()); + if (ap.get_password().size() > sizeof(conf.password)) { + ESP_LOGE(TAG, "AP password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.password), ap.get_password().c_str(), ap.get_password().size()); } ETS_UART_INTR_DISABLE(); diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 13870136d4..3074ffbe1b 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -289,8 +289,16 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.sta.ssid), sizeof(conf.sta.ssid), "%s", ap.get_ssid().c_str()); - snprintf(reinterpret_cast(conf.sta.password), sizeof(conf.sta.password), "%s", ap.get_password().c_str()); + if (ap.get_ssid().size() > sizeof(conf.sta.ssid)) { + ESP_LOGE(TAG, "SSID is too long"); + return false; + } + if (ap.get_password().size() > sizeof(conf.sta.password)) { + ESP_LOGE(TAG, "password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); + memcpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), ap.get_password().size()); // The weakest authmode to accept in the fast scan mode if (ap.get_password().empty()) { @@ -902,7 +910,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - strncpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid)); + if (ap.get_ssid().size() > sizeof(conf.ap.ssid)) { + ESP_LOGE(TAG, "AP SSID is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); conf.ap.channel = ap.get_channel().value_or(1); conf.ap.ssid_hidden = ap.get_ssid().size(); conf.ap.max_connection = 5; @@ -913,7 +925,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.ap.password = 0; } else { conf.ap.authmode = WIFI_AUTH_WPA2_PSK; - strncpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.password)); + if (ap.get_password().size() > sizeof(conf.ap.password)) { + ESP_LOGE(TAG, "AP password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), ap.get_password().size()); } // pairwise cipher of SoftAP, group cipher will be derived using this. diff --git a/esphome/const.py b/esphome/const.py index d14cdecb23..4b19e2865d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.11.1" +__version__ = "2024.11.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = (