diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e09fef069..3baaf02506 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -177,7 +177,7 @@ jobs: - name: Run yamllint if: matrix.id == 'yamllint' - uses: frenck/action-yamllint@v1.3.0 + uses: frenck/action-yamllint@v1.3.1 - name: Suggested changes run: script/ci-suggest-changes diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 1cf82895f3..e762512ff6 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -18,7 +18,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v3 + - uses: dessant/lock-threads@v4 with: pr-inactive-days: "1" pr-lock-reason: "" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4a9d9f5cc3..b8c0a22e81 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - --quiet files: ^((esphome|script|tests)/.+)?[^/]+\.py$ - repo: https://github.com/PyCQA/flake8 - rev: 5.0.4 + rev: 6.0.0 hooks: - id: flake8 additional_dependencies: @@ -27,7 +27,7 @@ repos: - --branch=release - --branch=beta - repo: https://github.com/asottile/pyupgrade - rev: v3.2.0 + rev: v3.3.0 hooks: - id: pyupgrade args: [--py39-plus] diff --git a/CODEOWNERS b/CODEOWNERS index 1da55891fd..d30060fe3b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -208,6 +208,7 @@ esphome/components/sim800l/* @glmnet esphome/components/sm2135/* @BoukeHaarsma23 esphome/components/sml/* @alengwenus esphome/components/smt100/* @piechade +esphome/components/sn74hc165/* @jesserockz esphome/components/socket/* @esphome/core esphome/components/sonoff_d1/* @anatoly-savchenkov esphome/components/spi/* @esphome/core diff --git a/docker/Dockerfile b/docker/Dockerfile index 102ef46461..a49ad5a9ef 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -6,17 +6,11 @@ ARG BASEIMGTYPE=docker # https://github.com/hassio-addons/addon-debian-base/releases -FROM ghcr.io/hassio-addons/debian-base/amd64:5.3.0 AS base-hassio-amd64 -FROM ghcr.io/hassio-addons/debian-base/aarch64:5.3.0 AS base-hassio-arm64 -FROM ghcr.io/hassio-addons/debian-base/armv7:5.3.0 AS base-hassio-armv7 +FROM ghcr.io/hassio-addons/debian-base:6.1.3 AS base-hassio # https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye -FROM debian:bullseye-20220328-slim AS base-docker-amd64 -FROM debian:bullseye-20220328-slim AS base-docker-arm64 -FROM debian:bullseye-20220328-slim AS base-docker-armv7 +FROM debian:bullseye-20221024-slim AS base-docker -# Use TARGETARCH/TARGETVARIANT defined by docker -# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope -FROM base-${BASEIMGTYPE}-${TARGETARCH}${TARGETVARIANT} AS base +FROM base-${BASEIMGTYPE} AS base RUN \ apt-get update \ @@ -29,8 +23,8 @@ RUN \ python3-cryptography=3.3.2-1 \ iputils-ping=3:20210202-1 \ git=1:2.30.2-1 \ - curl=7.74.0-1.3+deb11u1 \ - openssh-client=1:8.4p1-5 \ + curl=7.74.0-1.3+deb11u3 \ + openssh-client=1:8.4p1-5+deb11u1 \ && rm -rf \ /tmp/* \ /var/{cache,log}/* \ diff --git a/docker/ha-addon-rootfs/etc/cont-init.d/30-dirs.sh b/docker/ha-addon-rootfs/etc/cont-init.d/30-dirs.sh old mode 100644 new mode 100755 diff --git a/docker/ha-addon-rootfs/etc/services.d/esphome/finish b/docker/ha-addon-rootfs/etc/services.d/esphome/finish index 789a65e90c..fed449ce61 100755 --- a/docker/ha-addon-rootfs/etc/services.d/esphome/finish +++ b/docker/ha-addon-rootfs/etc/services.d/esphome/finish @@ -3,7 +3,13 @@ # Community Hass.io Add-ons: ESPHome # Take down the S6 supervision tree when ESPHome fails # ============================================================================== -if -n { s6-test $# -ne 0 } -if -n { s6-test ${1} -eq 256 } -s6-svscanctl -t /var/run/s6/services +declare APP_EXIT_CODE=${1} + +if [[ "${APP_EXIT_CODE}" -ne 0 ]] && [[ "${APP_EXIT_CODE}" -ne 256 ]]; then + bashio::log.warning "Halt add-on with exit code ${APP_EXIT_CODE}" + echo "${APP_EXIT_CODE}" > /run/s6-linux-init-container-results/exitcode + exec /run/s6/basedir/bin/halt +fi + +bashio::log.info "Service restart after closing" diff --git a/docker/ha-addon-rootfs/etc/services.d/esphome/run b/docker/ha-addon-rootfs/etc/services.d/esphome/run index 2c821bf4ee..747c64728e 100755 --- a/docker/ha-addon-rootfs/etc/services.d/esphome/run +++ b/docker/ha-addon-rootfs/etc/services.d/esphome/run @@ -22,6 +22,14 @@ if bashio::config.has_value 'relative_url'; then export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url') fi +if bashio::config.has_value 'default_compile_process_limit'; then + export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=$(bashio::config 'default_compile_process_limit') +else + if grep -q 'Raspberry Pi 3' /proc/cpuinfo; then + export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=1; + fi +fi + pio_cache_base=/data/cache/platformio # we can't set core_dir, because the settings file is stored in `core_dir/appstate.json` # setting `core_dir` would therefore prevent pio from accessing diff --git a/docker/ha-addon-rootfs/etc/services.d/nginx/finish b/docker/ha-addon-rootfs/etc/services.d/nginx/finish index 953f3771e0..8030841ec8 100755 --- a/docker/ha-addon-rootfs/etc/services.d/nginx/finish +++ b/docker/ha-addon-rootfs/etc/services.d/nginx/finish @@ -3,7 +3,13 @@ # Community Hass.io Add-ons: ESPHome # Take down the S6 supervision tree when NGINX fails # ============================================================================== -if -n { s6-test $# -ne 0 } -if -n { s6-test ${1} -eq 256 } -s6-svscanctl -t /var/run/s6/services +declare APP_EXIT_CODE=${1} + +if [[ "${APP_EXIT_CODE}" -ne 0 ]] && [[ "${APP_EXIT_CODE}" -ne 256 ]]; then + bashio::log.warning "Halt add-on with exit code ${APP_EXIT_CODE}" + echo "${APP_EXIT_CODE}" > /run/s6-linux-init-container-results/exitcode + exec /run/s6/basedir/bin/halt +fi + +bashio::log.info "Service restart after closing" diff --git a/esphome/__main__.py b/esphome/__main__.py index cf2d161d04..9b6043ef50 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -254,8 +254,7 @@ def upload_using_esptool(config, port): if os.environ.get("ESPHOME_USE_SUBPROCESS") is None: import esptool - # pylint: disable=protected-access - return run_external_command(esptool._main, *cmd) + return run_external_command(esptool.main, *cmd) # pylint: disable=no-member return run_external_process(*cmd) @@ -298,6 +297,8 @@ def upload_program(config, args, host): ota_conf = config[CONF_OTA] remote_port = ota_conf[CONF_PORT] password = ota_conf.get(CONF_PASSWORD, "") + if getattr(args, "file", None) is not None: + return espota2.run_ota(host, remote_port, password, args.file) return espota2.run_ota(host, remote_port, password, CORE.firmware_bin) @@ -687,6 +688,10 @@ def parse_args(argv): "--device", help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.", ) + parser_upload.add_argument( + "--file", + help="Manually specify the binary file to upload.", + ) parser_logs = subparsers.add_parser( "logs", diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor.cpp index 1e8d740062..9bfe0f5eed 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor.cpp @@ -117,10 +117,11 @@ void ADCSensor::dump_config() { } #endif // USE_ESP32 #ifdef USE_RP2040 - if (this->is_temperature_) + if (this->is_temperature_) { ESP_LOGCONFIG(TAG, " Pin: Temperature"); - else + } else { LOG_PIN(" Pin: ", pin_); + } #endif LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/addressable_light/addressable_light_display.h b/esphome/components/addressable_light/addressable_light_display.h index dfafe9d98d..8893c39be6 100644 --- a/esphome/components/addressable_light/addressable_light_display.h +++ b/esphome/components/addressable_light/addressable_light_display.h @@ -5,6 +5,8 @@ #include "esphome/components/display/display_buffer.h" #include "esphome/components/light/addressable_light.h" +#include + namespace esphome { namespace addressable_light { diff --git a/esphome/components/ade7953/ade7953.h b/esphome/components/ade7953/ade7953.h index 418ad1c9e7..c0c1cc4db8 100644 --- a/esphome/components/ade7953/ade7953.h +++ b/esphome/components/ade7953/ade7953.h @@ -5,6 +5,8 @@ #include "esphome/components/i2c/i2c.h" #include "esphome/components/sensor/sensor.h" +#include + namespace esphome { namespace ade7953 { diff --git a/esphome/components/ads1115/ads1115.h b/esphome/components/ads1115/ads1115.h index 059436d142..17d5a910d8 100644 --- a/esphome/components/ads1115/ads1115.h +++ b/esphome/components/ads1115/ads1115.h @@ -5,6 +5,8 @@ #include "esphome/components/i2c/i2c.h" #include "esphome/components/voltage_sampler/voltage_sampler.h" +#include + namespace esphome { namespace ads1115 { diff --git a/esphome/components/aht10/aht10.cpp b/esphome/components/aht10/aht10.cpp index 3c690c39b5..ad1a68498b 100644 --- a/esphome/components/aht10/aht10.cpp +++ b/esphome/components/aht10/aht10.cpp @@ -122,8 +122,9 @@ void AHT10Component::update() { this->temperature_sensor_->publish_state(temperature); } if (this->humidity_sensor_ != nullptr) { - if (std::isnan(humidity)) + if (std::isnan(humidity)) { ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum"); + } this->humidity_sensor_->publish_state(humidity); } this->status_clear_warning(); diff --git a/esphome/components/am43/am43.cpp b/esphome/components/am43/am43.cpp index 9a0e5999d2..09723496d9 100644 --- a/esphome/components/am43/am43.cpp +++ b/esphome/components/am43/am43.cpp @@ -104,8 +104,9 @@ void Am43::update() { auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); - if (status) + if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); + } } this->current_sensor_++; } diff --git a/esphome/components/am43/cover/am43_cover.cpp b/esphome/components/am43/cover/am43_cover.cpp index ba8e350732..b09f5ab767 100644 --- a/esphome/components/am43/cover/am43_cover.cpp +++ b/esphome/components/am43/cover/am43_cover.cpp @@ -56,8 +56,9 @@ void Am43Component::control(const CoverCall &call) { auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); - if (status) + if (status) { ESP_LOGW(TAG, "[%s] Error writing stop command to device, error = %d", this->get_name().c_str(), status); + } } if (call.get_position().has_value()) { auto pos = *call.get_position(); @@ -68,8 +69,9 @@ void Am43Component::control(const CoverCall &call) { auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); - if (status) + if (status) { ESP_LOGW(TAG, "[%s] Error writing set_position command to device, error = %d", this->get_name().c_str(), status); + } } } @@ -126,18 +128,21 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); - if (status) + if (status) { ESP_LOGW(TAG, "[%s] Error writing set_position to device, error = %d", this->get_name().c_str(), status); + } } else { ESP_LOGW(TAG, "[%s] AM43 pin rejected!", this->get_name().c_str()); } } - if (this->decoder_->has_set_position_response() && !this->decoder_->set_position_ok_) + if (this->decoder_->has_set_position_response() && !this->decoder_->set_position_ok_) { ESP_LOGW(TAG, "[%s] Got nack after sending set_position. Bad pin?", this->get_name().c_str()); + } - if (this->decoder_->has_set_state_response() && !this->decoder_->set_state_ok_) + if (this->decoder_->has_set_state_response() && !this->decoder_->set_state_ok_) { ESP_LOGW(TAG, "[%s] Got nack after sending set_state. Bad pin?", this->get_name().c_str()); + } break; } default: diff --git a/esphome/components/anova/anova.cpp b/esphome/components/anova/anova.cpp index cafd30149d..ebf6c1d037 100644 --- a/esphome/components/anova/anova.cpp +++ b/esphome/components/anova/anova.cpp @@ -37,16 +37,18 @@ void Anova::control(const ClimateCall &call) { auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); - if (status) + if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); + } } if (call.get_target_temperature().has_value()) { auto *pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature()); auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); - if (status) + if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); + } } } @@ -143,8 +145,9 @@ void Anova::update() { auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_, pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); - if (status) + if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); + } this->current_request_++; } } diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 5df35f7978..e1bc7b0a57 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -915,6 +915,7 @@ message ListEntitiesNumberResponse { EntityCategory entity_category = 10; string unit_of_measurement = 11; NumberMode mode = 12; + string device_class = 13; } message NumberStateResponse { option (id) = 50; @@ -1140,6 +1141,8 @@ message BluetoothLEAdvertisementResponse { repeated string service_uuids = 4; repeated BluetoothServiceData service_data = 5; repeated BluetoothServiceData manufacturer_data = 6; + + uint32 address_type = 7; } enum BluetoothDeviceRequestType { @@ -1147,6 +1150,8 @@ enum BluetoothDeviceRequestType { BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1; BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2; BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3; + BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4; + BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5; } message BluetoothDeviceRequest { @@ -1156,6 +1161,8 @@ message BluetoothDeviceRequest { uint64 address = 1; BluetoothDeviceRequestType request_type = 2; + bool has_address_type = 3; + uint32 address_type = 4; } message BluetoothDeviceConnectionResponse { diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 5227750cc0..aac58587d1 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -616,6 +616,7 @@ bool APIConnection::send_number_info(number::Number *number) { msg.entity_category = static_cast(number->get_entity_category()); msg.unit_of_measurement = number->traits.get_unit_of_measurement(); msg.mode = static_cast(number->traits.get_mode()); + msg.device_class = number->traits.get_device_class(); msg.min_value = number->traits.get_min_value(); msg.max_value = number->traits.get_max_value(); @@ -949,7 +950,7 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { resp.webserver_port = USE_WEBSERVER_PORT; #endif #ifdef USE_BLUETOOTH_PROXY - resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 2 : 1; + resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 3 : 1; #endif return resp; } diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 028fb80175..b2076635b0 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -7,6 +7,8 @@ #include "esphome/core/application.h" #include "esphome/core/component.h" +#include + namespace esphome { namespace api { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 03d09a0913..f108d38e8f 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -6,6 +6,7 @@ namespace esphome { namespace api { +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::EntityCategory value) { switch (value) { case enums::ENTITY_CATEGORY_NONE: @@ -18,6 +19,8 @@ template<> const char *proto_enum_to_string(enums::Entity return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::LegacyCoverState value) { switch (value) { case enums::LEGACY_COVER_STATE_OPEN: @@ -28,6 +31,8 @@ template<> const char *proto_enum_to_string(enums::Lega return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::CoverOperation value) { switch (value) { case enums::COVER_OPERATION_IDLE: @@ -40,6 +45,8 @@ template<> const char *proto_enum_to_string(enums::CoverO return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::LegacyCoverCommand value) { switch (value) { case enums::LEGACY_COVER_COMMAND_OPEN: @@ -52,6 +59,8 @@ template<> const char *proto_enum_to_string(enums::Le return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::FanSpeed value) { switch (value) { case enums::FAN_SPEED_LOW: @@ -64,6 +73,8 @@ template<> const char *proto_enum_to_string(enums::FanSpeed val return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::FanDirection value) { switch (value) { case enums::FAN_DIRECTION_FORWARD: @@ -74,6 +85,8 @@ template<> const char *proto_enum_to_string(enums::FanDirec return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::ColorMode value) { switch (value) { case enums::COLOR_MODE_UNKNOWN: @@ -100,6 +113,8 @@ template<> const char *proto_enum_to_string(enums::ColorMode v return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::SensorStateClass value) { switch (value) { case enums::STATE_CLASS_NONE: @@ -114,6 +129,8 @@ template<> const char *proto_enum_to_string(enums::Sens return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::SensorLastResetType value) { switch (value) { case enums::LAST_RESET_NONE: @@ -126,6 +143,8 @@ template<> const char *proto_enum_to_string(enums::S return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::LogLevel value) { switch (value) { case enums::LOG_LEVEL_NONE: @@ -148,6 +167,8 @@ template<> const char *proto_enum_to_string(enums::LogLevel val return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::ServiceArgType value) { switch (value) { case enums::SERVICE_ARG_TYPE_BOOL: @@ -170,6 +191,8 @@ template<> const char *proto_enum_to_string(enums::Servic return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::ClimateMode value) { switch (value) { case enums::CLIMATE_MODE_OFF: @@ -190,6 +213,8 @@ template<> const char *proto_enum_to_string(enums::ClimateMo return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::ClimateFanMode value) { switch (value) { case enums::CLIMATE_FAN_ON: @@ -214,6 +239,8 @@ template<> const char *proto_enum_to_string(enums::Climat return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::ClimateSwingMode value) { switch (value) { case enums::CLIMATE_SWING_OFF: @@ -228,6 +255,8 @@ template<> const char *proto_enum_to_string(enums::Clim return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::ClimateAction value) { switch (value) { case enums::CLIMATE_ACTION_OFF: @@ -246,6 +275,8 @@ template<> const char *proto_enum_to_string(enums::Climate return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::ClimatePreset value) { switch (value) { case enums::CLIMATE_PRESET_NONE: @@ -268,6 +299,8 @@ template<> const char *proto_enum_to_string(enums::Climate return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::NumberMode value) { switch (value) { case enums::NUMBER_MODE_AUTO: @@ -280,6 +313,8 @@ template<> const char *proto_enum_to_string(enums::NumberMode return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::LockState value) { switch (value) { case enums::LOCK_STATE_NONE: @@ -298,6 +333,8 @@ template<> const char *proto_enum_to_string(enums::LockState v return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::LockCommand value) { switch (value) { case enums::LOCK_UNLOCK: @@ -310,6 +347,8 @@ template<> const char *proto_enum_to_string(enums::LockComma return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::MediaPlayerState value) { switch (value) { case enums::MEDIA_PLAYER_STATE_NONE: @@ -324,6 +363,8 @@ template<> const char *proto_enum_to_string(enums::Medi return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::MediaPlayerCommand value) { switch (value) { case enums::MEDIA_PLAYER_COMMAND_PLAY: @@ -340,6 +381,8 @@ template<> const char *proto_enum_to_string(enums::Me return "UNKNOWN"; } } +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::BluetoothDeviceRequestType value) { switch (value) { @@ -351,10 +394,15 @@ const char *proto_enum_to_string(enums::Bluet return "BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR"; case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR: return "BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR"; + case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE: + return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE"; + case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE: + return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE"; default: return "UNKNOWN"; } } +#endif bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { @@ -3942,6 +3990,10 @@ bool ListEntitiesNumberResponse::decode_length(uint32_t field_id, ProtoLengthDel this->unit_of_measurement = value.as_string(); return true; } + case 13: { + this->device_class = value.as_string(); + return true; + } default: return false; } @@ -3981,6 +4033,7 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_enum(10, this->entity_category); buffer.encode_string(11, this->unit_of_measurement); buffer.encode_enum(12, this->mode); + buffer.encode_string(13, this->device_class); } #ifdef HAS_PROTO_MESSAGE_DUMP void ListEntitiesNumberResponse::dump_to(std::string &out) const { @@ -4037,6 +4090,10 @@ void ListEntitiesNumberResponse::dump_to(std::string &out) const { out.append(" mode: "); out.append(proto_enum_to_string(this->mode)); out.append("\n"); + + out.append(" device_class: "); + out.append("'").append(this->device_class).append("'"); + out.append("\n"); out.append("}"); } #endif @@ -4984,6 +5041,10 @@ bool BluetoothLEAdvertisementResponse::decode_varint(uint32_t field_id, ProtoVar this->rssi = value.as_sint32(); return true; } + case 7: { + this->address_type = value.as_uint32(); + return true; + } default: return false; } @@ -5023,6 +5084,7 @@ void BluetoothLEAdvertisementResponse::encode(ProtoWriteBuffer buffer) const { for (auto &it : this->manufacturer_data) { buffer.encode_message(6, it, true); } + buffer.encode_uint32(7, this->address_type); } #ifdef HAS_PROTO_MESSAGE_DUMP void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const { @@ -5059,6 +5121,11 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const { it.dump_to(out); out.append("\n"); } + + out.append(" address_type: "); + sprintf(buffer, "%u", this->address_type); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -5072,6 +5139,14 @@ bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) this->request_type = value.as_enum(); return true; } + case 3: { + this->has_address_type = value.as_bool(); + return true; + } + case 4: { + this->address_type = value.as_uint32(); + return true; + } default: return false; } @@ -5079,6 +5154,8 @@ bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) void BluetoothDeviceRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_enum(2, this->request_type); + buffer.encode_bool(3, this->has_address_type); + buffer.encode_uint32(4, this->address_type); } #ifdef HAS_PROTO_MESSAGE_DUMP void BluetoothDeviceRequest::dump_to(std::string &out) const { @@ -5092,6 +5169,15 @@ void BluetoothDeviceRequest::dump_to(std::string &out) const { out.append(" request_type: "); out.append(proto_enum_to_string(this->request_type)); out.append("\n"); + + out.append(" has_address_type: "); + out.append(YESNO(this->has_address_type)); + out.append("\n"); + + out.append(" address_type: "); + sprintf(buffer, "%u", this->address_type); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 2ca0853951..8a78f1ad03 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -160,6 +160,8 @@ enum BluetoothDeviceRequestType : uint32_t { BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1, BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2, BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3, + BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4, + BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5, }; } // namespace enums @@ -1004,6 +1006,7 @@ class ListEntitiesNumberResponse : public ProtoMessage { enums::EntityCategory entity_category{}; std::string unit_of_measurement{}; enums::NumberMode mode{}; + std::string device_class{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -1256,6 +1259,7 @@ class BluetoothLEAdvertisementResponse : public ProtoMessage { std::vector service_uuids{}; std::vector service_data{}; std::vector manufacturer_data{}; + uint32_t address_type{0}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -1269,6 +1273,8 @@ class BluetoothDeviceRequest : public ProtoMessage { public: uint64_t address{0}; enums::BluetoothDeviceRequestType request_type{}; + bool has_address_type{false}; + uint32_t address_type{0}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 1aefa31400..8e69a77475 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -12,6 +12,8 @@ #include "user_services.h" #include "api_noise_context.h" +#include + namespace esphome { namespace api { diff --git a/esphome/components/api/homeassistant_service.h b/esphome/components/api/homeassistant_service.h index 90cfe751b6..f04181e5b2 100644 --- a/esphome/components/api/homeassistant_service.h +++ b/esphome/components/api/homeassistant_service.h @@ -5,6 +5,8 @@ #include "api_pb2.h" #include "api_server.h" +#include + namespace esphome { namespace api { diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 11cd4330ce..fea219ecb9 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -4,6 +4,8 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include + #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE #define HAS_PROTO_MESSAGE_DUMP #endif diff --git a/esphome/components/api/user_services.h b/esphome/components/api/user_services.h index df6f6924aa..673bcf5693 100644 --- a/esphome/components/api/user_services.h +++ b/esphome/components/api/user_services.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "esphome/core/component.h" #include "esphome/core/automation.h" diff --git a/esphome/components/atc_mithermometer/atc_mithermometer.h b/esphome/components/atc_mithermometer/atc_mithermometer.h index 9398c02bcf..31fb77ac7f 100644 --- a/esphome/components/atc_mithermometer/atc_mithermometer.h +++ b/esphome/components/atc_mithermometer/atc_mithermometer.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include + #ifdef USE_ESP32 namespace esphome { diff --git a/esphome/components/bedjet/bedjet_hub.h b/esphome/components/bedjet/bedjet_hub.h index f1479710a7..fb757dfdf8 100644 --- a/esphome/components/bedjet/bedjet_hub.h +++ b/esphome/components/bedjet/bedjet_hub.h @@ -8,6 +8,8 @@ #include "bedjet_child.h" #include "bedjet_codec.h" +#include + #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" #endif diff --git a/esphome/components/binary_sensor/automation.h b/esphome/components/binary_sensor/automation.h index 31bf1a5565..de527d1070 100644 --- a/esphome/components/binary_sensor/automation.h +++ b/esphome/components/binary_sensor/automation.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "esphome/core/component.h" #include "esphome/core/automation.h" diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index b0c39a2c9b..730ded3f94 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -5,6 +5,8 @@ #include "esphome/core/helpers.h" #include "esphome/components/binary_sensor/filter.h" +#include + namespace esphome { namespace binary_sensor { diff --git a/esphome/components/binary_sensor/filter.h b/esphome/components/binary_sensor/filter.h index 59068634af..64a33f6e34 100644 --- a/esphome/components/binary_sensor/filter.h +++ b/esphome/components/binary_sensor/filter.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" +#include + namespace esphome { namespace binary_sensor { diff --git a/esphome/components/binary_sensor_map/binary_sensor_map.h b/esphome/components/binary_sensor_map/binary_sensor_map.h index a880be9623..a1d6f95009 100644 --- a/esphome/components/binary_sensor_map/binary_sensor_map.h +++ b/esphome/components/binary_sensor_map/binary_sensor_map.h @@ -4,6 +4,8 @@ #include "esphome/components/binary_sensor/binary_sensor.h" #include "esphome/components/sensor/sensor.h" +#include + namespace esphome { namespace binary_sensor_map { diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index 44812d29b5..03e8f0b0b2 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -66,7 +66,7 @@ CONF_BLE_CLIENT_ID = "ble_client_id" BLE_CLIENT_SCHEMA = cv.Schema( { - cv.Required(CONF_BLE_CLIENT_ID): cv.use_id(BLEClient), + cv.GenerateID(CONF_BLE_CLIENT_ID): cv.use_id(BLEClient), } ) @@ -78,7 +78,7 @@ async def register_ble_node(var, config): BLE_WRITE_ACTION_SCHEMA = cv.Schema( { - cv.Required(CONF_ID): cv.use_id(BLEClient), + cv.GenerateID(CONF_ID): cv.use_id(BLEClient), cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid, cv.Required(CONF_VALUE): cv.templatable(cv.ensure_list(cv.hex_uint8_t)), diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index 98c19dedf8..ef38333698 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "esphome/core/automation.h" #include "esphome/components/ble_client/ble_client.h" diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index 54f5f298fc..ceca94c86a 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -7,12 +7,13 @@ #ifdef USE_ESP32 -#include -#include -#include -#include #include +#include #include +#include +#include +#include +#include namespace esphome { namespace ble_client { diff --git a/esphome/components/ble_client/sensor/ble_sensor.h b/esphome/components/ble_client/sensor/ble_sensor.h index d3d037572a..b11a010ee4 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.h +++ b/esphome/components/ble_client/sensor/ble_sensor.h @@ -5,6 +5,8 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/sensor/sensor.h" +#include + #ifdef USE_ESP32 #include diff --git a/esphome/components/bluetooth_proxy/__init__.py b/esphome/components/bluetooth_proxy/__init__.py index 9c566c56a5..bec1579d8e 100644 --- a/esphome/components/bluetooth_proxy/__init__.py +++ b/esphome/components/bluetooth_proxy/__init__.py @@ -2,11 +2,13 @@ from esphome.components import esp32_ble_tracker, esp32_ble_client import esphome.config_validation as cv import esphome.codegen as cg from esphome.const import CONF_ACTIVE, CONF_ID +from esphome.components.esp32 import add_idf_sdkconfig_option AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"] DEPENDENCIES = ["api", "esp32"] CODEOWNERS = ["@jesserockz"] +CONF_CACHE_SERVICES = "cache_services" CONF_CONNECTIONS = "connections" MAX_CONNECTIONS = 3 @@ -47,6 +49,9 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(BluetoothProxy), cv.Optional(CONF_ACTIVE, default=False): cv.boolean, + cv.SplitDefault(CONF_CACHE_SERVICES, esp32_idf=True): cv.All( + cv.only_with_esp_idf, cv.boolean + ), cv.Optional(CONF_CONNECTIONS): cv.All( cv.ensure_list(CONNECTION_SCHEMA), cv.Length(min=1, max=MAX_CONNECTIONS), @@ -72,4 +77,7 @@ async def to_code(config): cg.add(var.register_connection(connection_var)) await esp32_ble_tracker.register_client(connection_var, connection_conf) + if config.get(CONF_CACHE_SERVICES): + add_idf_sdkconfig_option("CONFIG_BT_GATTC_CACHE_NVS_FLASH", True) + cg.add_define("USE_BLUETOOTH_PROXY") diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp index 1911701632..09611b6174 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp @@ -34,12 +34,39 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga this->set_address(0); api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), this->proxy_->get_bluetooth_connections_limit()); + } else if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) { + api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_); + api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), + this->proxy_->get_bluetooth_connections_limit()); } + this->seen_mtu_or_services_ = false; + break; + } + case ESP_GATTC_CFG_MTU_EVT: { + if (param->cfg_mtu.conn_id != this->conn_id_) + break; + if (!this->seen_mtu_or_services_) { + // We don't know if we will get the MTU or the services first, so + // only send the device connection true if we have already received + // the services. + this->seen_mtu_or_services_ = true; + break; + } + api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_); + api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), + this->proxy_->get_bluetooth_connections_limit()); break; } case ESP_GATTC_SEARCH_CMPL_EVT: { if (param->search_cmpl.conn_id != this->conn_id_) break; + if (!this->seen_mtu_or_services_) { + // We don't know if we will get the MTU or the services first, so + // only send the device connection true if we have already received + // the mtu. + this->seen_mtu_or_services_ = true; + break; + } api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_); api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), this->proxy_->get_bluetooth_connections_limit()); @@ -82,8 +109,6 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga break; } case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { - if (this->get_characteristic(param->unreg_for_notify.handle) == nullptr) // No conn_id in this event - break; if (param->unreg_for_notify.status != ESP_GATT_OK) { ESP_LOGW(TAG, "[%d] [%s] Error unregistering notifications for handle 0x%2X, status=%d", this->connection_index_, this->address_str_.c_str(), param->unreg_for_notify.handle, @@ -99,8 +124,6 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga break; } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - if (this->get_characteristic(param->reg_for_notify.handle) == nullptr) // No conn_id in this event - break; if (param->reg_for_notify.status != ESP_GATT_OK) { ESP_LOGW(TAG, "[%d] [%s] Error registering notifications for handle 0x%2X, status=%d", this->connection_index_, this->address_str_.c_str(), param->reg_for_notify.handle, param->reg_for_notify.status); @@ -141,18 +164,11 @@ esp_err_t BluetoothConnection::read_characteristic(uint16_t handle) { this->address_str_.c_str()); return ESP_GATT_NOT_CONNECTED; } - auto *characteristic = this->get_characteristic(handle); - if (characteristic == nullptr) { - ESP_LOGW(TAG, "[%d] [%s] Cannot read GATT characteristic, not found.", this->connection_index_, - this->address_str_.c_str()); - return ESP_GATT_INVALID_HANDLE; - } - ESP_LOGV(TAG, "[%d] [%s] Reading GATT characteristic %s", this->connection_index_, this->address_str_.c_str(), - characteristic->uuid.to_string().c_str()); + ESP_LOGV(TAG, "[%d] [%s] Reading GATT characteristic handle %d", this->connection_index_, this->address_str_.c_str(), + handle); - esp_err_t err = - esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, characteristic->handle, ESP_GATT_AUTH_REQ_NONE); + esp_err_t err = esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, handle, ESP_GATT_AUTH_REQ_NONE); if (err != ERR_OK) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_read_char error, err=%d", this->connection_index_, this->address_str_.c_str(), err); @@ -167,18 +183,12 @@ esp_err_t BluetoothConnection::write_characteristic(uint16_t handle, const std:: this->address_str_.c_str()); return ESP_GATT_NOT_CONNECTED; } - auto *characteristic = this->get_characteristic(handle); - if (characteristic == nullptr) { - ESP_LOGW(TAG, "[%d] [%s] Cannot write GATT characteristic, not found.", this->connection_index_, - this->address_str_.c_str()); - return ESP_GATT_INVALID_HANDLE; - } + ESP_LOGV(TAG, "[%d] [%s] Writing GATT characteristic handle %d", this->connection_index_, this->address_str_.c_str(), + handle); - ESP_LOGV(TAG, "[%d] [%s] Writing GATT characteristic %s", this->connection_index_, this->address_str_.c_str(), - characteristic->uuid.to_string().c_str()); - - auto err = characteristic->write_value((uint8_t *) data.data(), data.size(), - response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP); + esp_err_t err = + esp_ble_gattc_write_char(this->gattc_if_, this->conn_id_, handle, data.size(), (uint8_t *) data.data(), + response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (err != ERR_OK) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_write_char error, err=%d", this->connection_index_, this->address_str_.c_str(), err); @@ -193,18 +203,10 @@ esp_err_t BluetoothConnection::read_descriptor(uint16_t handle) { this->address_str_.c_str()); return ESP_GATT_NOT_CONNECTED; } - auto *descriptor = this->get_descriptor(handle); - if (descriptor == nullptr) { - ESP_LOGW(TAG, "[%d] [%s] Cannot read GATT descriptor, not found.", this->connection_index_, - this->address_str_.c_str()); - return ESP_GATT_INVALID_HANDLE; - } + ESP_LOGV(TAG, "[%d] [%s] Reading GATT descriptor handle %d", this->connection_index_, this->address_str_.c_str(), + handle); - ESP_LOGV(TAG, "[%d] [%s] Reading GATT descriptor %s", this->connection_index_, this->address_str_.c_str(), - descriptor->uuid.to_string().c_str()); - - esp_err_t err = - esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, ESP_GATT_AUTH_REQ_NONE); + esp_err_t err = esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, handle, ESP_GATT_AUTH_REQ_NONE); if (err != ERR_OK) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_read_char_descr error, err=%d", this->connection_index_, this->address_str_.c_str(), err); @@ -213,25 +215,18 @@ esp_err_t BluetoothConnection::read_descriptor(uint16_t handle) { return ESP_OK; } -esp_err_t BluetoothConnection::write_descriptor(uint16_t handle, const std::string &data) { +esp_err_t BluetoothConnection::write_descriptor(uint16_t handle, const std::string &data, bool response) { if (!this->connected()) { ESP_LOGW(TAG, "[%d] [%s] Cannot write GATT descriptor, not connected.", this->connection_index_, this->address_str_.c_str()); return ESP_GATT_NOT_CONNECTED; } - auto *descriptor = this->get_descriptor(handle); - if (descriptor == nullptr) { - ESP_LOGW(TAG, "[%d] [%s] Cannot write GATT descriptor, not found.", this->connection_index_, - this->address_str_.c_str()); - return ESP_GATT_INVALID_HANDLE; - } + ESP_LOGV(TAG, "[%d] [%s] Writing GATT descriptor handle %d", this->connection_index_, this->address_str_.c_str(), + handle); - ESP_LOGV(TAG, "[%d] [%s] Writing GATT descriptor %s", this->connection_index_, this->address_str_.c_str(), - descriptor->uuid.to_string().c_str()); - - auto err = - esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, data.size(), - (uint8_t *) data.data(), ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + esp_err_t err = esp_ble_gattc_write_char_descr( + this->gattc_if_, this->conn_id_, handle, data.size(), (uint8_t *) data.data(), + response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (err != ERR_OK) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_write_char_descr error, err=%d", this->connection_index_, this->address_str_.c_str(), err); @@ -246,26 +241,20 @@ esp_err_t BluetoothConnection::notify_characteristic(uint16_t handle, bool enabl this->address_str_.c_str()); return ESP_GATT_NOT_CONNECTED; } - auto *characteristic = this->get_characteristic(handle); - if (characteristic == nullptr) { - ESP_LOGW(TAG, "[%d] [%s] Cannot notify GATT characteristic, not found.", this->connection_index_, - this->address_str_.c_str()); - return ESP_GATT_INVALID_HANDLE; - } if (enable) { - ESP_LOGV(TAG, "[%d] [%s] Registering for GATT characteristic notifications %s", this->connection_index_, - this->address_str_.c_str(), characteristic->uuid.to_string().c_str()); - esp_err_t err = esp_ble_gattc_register_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle); + ESP_LOGV(TAG, "[%d] [%s] Registering for GATT characteristic notifications handle %d", this->connection_index_, + this->address_str_.c_str(), handle); + esp_err_t err = esp_ble_gattc_register_for_notify(this->gattc_if_, this->remote_bda_, handle); if (err != ESP_OK) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_register_for_notify failed, err=%d", this->connection_index_, this->address_str_.c_str(), err); return err; } } else { - ESP_LOGV(TAG, "[%d] [%s] Unregistering for GATT characteristic notifications %s", this->connection_index_, - this->address_str_.c_str(), characteristic->uuid.to_string().c_str()); - esp_err_t err = esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle); + ESP_LOGV(TAG, "[%d] [%s] Unregistering for GATT characteristic notifications handle %d", this->connection_index_, + this->address_str_.c_str(), handle); + esp_err_t err = esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, handle); if (err != ESP_OK) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_unregister_for_notify failed, err=%d", this->connection_index_, this->address_str_.c_str(), err); diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.h b/esphome/components/bluetooth_proxy/bluetooth_connection.h index 1f759c3485..fde074d17f 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.h +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.h @@ -17,14 +17,15 @@ class BluetoothConnection : public esp32_ble_client::BLEClientBase { esp_err_t read_characteristic(uint16_t handle); esp_err_t write_characteristic(uint16_t handle, const std::string &data, bool response); esp_err_t read_descriptor(uint16_t handle); - esp_err_t write_descriptor(uint16_t handle, const std::string &data); + esp_err_t write_descriptor(uint16_t handle, const std::string &data, bool response); esp_err_t notify_characteristic(uint16_t handle, bool enable); protected: friend class BluetoothProxy; + bool seen_mtu_or_services_{false}; - int16_t send_service_{-1}; + int16_t send_service_{-2}; BluetoothProxy *proxy_; }; diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 7798bb1e85..017e1bf83f 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -10,6 +10,19 @@ namespace esphome { namespace bluetooth_proxy { static const char *const TAG = "bluetooth_proxy"; +static const int DONE_SENDING_SERVICES = -2; + +std::vector get_128bit_uuid_vec(esp_bt_uuid_t uuid_source) { + esp_bt_uuid_t uuid = espbt::ESPBTUUID::from_uuid(uuid_source).as_128bit().get_uuid(); + return std::vector{((uint64_t) uuid.uuid.uuid128[15] << 56) | ((uint64_t) uuid.uuid.uuid128[14] << 48) | + ((uint64_t) uuid.uuid.uuid128[13] << 40) | ((uint64_t) uuid.uuid.uuid128[12] << 32) | + ((uint64_t) uuid.uuid.uuid128[11] << 24) | ((uint64_t) uuid.uuid.uuid128[10] << 16) | + ((uint64_t) uuid.uuid.uuid128[9] << 8) | ((uint64_t) uuid.uuid.uuid128[8]), + ((uint64_t) uuid.uuid.uuid128[7] << 56) | ((uint64_t) uuid.uuid.uuid128[6] << 48) | + ((uint64_t) uuid.uuid.uuid128[5] << 40) | ((uint64_t) uuid.uuid.uuid128[4] << 32) | + ((uint64_t) uuid.uuid.uuid128[3] << 24) | ((uint64_t) uuid.uuid.uuid128[2] << 16) | + ((uint64_t) uuid.uuid.uuid128[1] << 8) | ((uint64_t) uuid.uuid.uuid128[0])}; +} BluetoothProxy::BluetoothProxy() { global_bluetooth_proxy = this; } @@ -26,6 +39,7 @@ bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) { api::BluetoothLEAdvertisementResponse resp; resp.address = device.address_uint64(); + resp.address_type = device.get_address_type(); if (!device.get_name().empty()) resp.name = device.get_name(); resp.rssi = device.get_rssi(); @@ -52,6 +66,20 @@ void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, " Active: %s", YESNO(this->active_)); } +int BluetoothProxy::get_bluetooth_connections_free() { + int free = 0; + for (auto *connection : this->connections_) { + if (connection->address_ == 0) { + free++; + ESP_LOGV(TAG, "[%d] Free connection", connection->get_connection_index()); + } else { + ESP_LOGV(TAG, "[%d] Used connection by [%s]", connection->get_connection_index(), + connection->address_str().c_str()); + } + } + return free; +} + void BluetoothProxy::loop() { if (!api::global_api_server->is_connected()) { for (auto *connection : this->connections_) { @@ -62,32 +90,87 @@ void BluetoothProxy::loop() { return; } for (auto *connection : this->connections_) { - if (connection->send_service_ == connection->services_.size()) { - connection->send_service_ = -1; + if (connection->send_service_ == connection->service_count_) { + connection->send_service_ = DONE_SENDING_SERVICES; api::global_api_server->send_bluetooth_gatt_services_done(connection->get_address()); + if (connection->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE || + connection->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) { + connection->release_services(); + } } else if (connection->send_service_ >= 0) { - auto &service = connection->services_[connection->send_service_]; + esp_gattc_service_elem_t service_result; + uint16_t service_count = 1; + esp_gatt_status_t service_status = + esp_ble_gattc_get_service(connection->get_gattc_if(), connection->get_conn_id(), nullptr, &service_result, + &service_count, connection->send_service_); + connection->send_service_++; + if (service_status != ESP_GATT_OK) { + ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_service error at offset=%d, status=%d", + connection->get_connection_index(), connection->address_str().c_str(), connection->send_service_ - 1, + service_status); + continue; + } + if (service_count == 0) { + ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_service missing, service_count=%d", + connection->get_connection_index(), connection->address_str().c_str(), service_count); + continue; + } api::BluetoothGATTGetServicesResponse resp; resp.address = connection->get_address(); api::BluetoothGATTService service_resp; - service_resp.uuid = {service->uuid.get_128bit_high(), service->uuid.get_128bit_low()}; - service_resp.handle = service->start_handle; - for (auto &characteristic : service->characteristics) { + service_resp.uuid = get_128bit_uuid_vec(service_result.uuid); + service_resp.handle = service_result.start_handle; + uint16_t char_offset = 0; + esp_gattc_char_elem_t char_result; + while (true) { // characteristics + uint16_t char_count = 1; + esp_gatt_status_t char_status = esp_ble_gattc_get_all_char( + connection->get_gattc_if(), connection->get_conn_id(), service_result.start_handle, + service_result.end_handle, &char_result, &char_count, char_offset); + if (char_status == ESP_GATT_INVALID_OFFSET || char_status == ESP_GATT_NOT_FOUND) { + break; + } + if (char_status != ESP_GATT_OK) { + ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_all_char error, status=%d", connection->get_connection_index(), + connection->address_str().c_str(), char_status); + break; + } + if (char_count == 0) { + break; + } api::BluetoothGATTCharacteristic characteristic_resp; - characteristic_resp.uuid = {characteristic->uuid.get_128bit_high(), characteristic->uuid.get_128bit_low()}; - characteristic_resp.handle = characteristic->handle; - characteristic_resp.properties = characteristic->properties; - for (auto &descriptor : characteristic->descriptors) { + characteristic_resp.uuid = get_128bit_uuid_vec(char_result.uuid); + characteristic_resp.handle = char_result.char_handle; + characteristic_resp.properties = char_result.properties; + char_offset++; + uint16_t desc_offset = 0; + esp_gattc_descr_elem_t desc_result; + while (true) { // descriptors + uint16_t desc_count = 1; + esp_gatt_status_t desc_status = + esp_ble_gattc_get_all_descr(connection->get_gattc_if(), connection->get_conn_id(), + char_result.char_handle, &desc_result, &desc_count, desc_offset); + if (desc_status == ESP_GATT_INVALID_OFFSET || desc_status == ESP_GATT_NOT_FOUND) { + break; + } + if (desc_status != ESP_GATT_OK) { + ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_all_descr error, status=%d", connection->get_connection_index(), + connection->address_str().c_str(), desc_status); + break; + } + if (desc_count == 0) { + break; + } api::BluetoothGATTDescriptor descriptor_resp; - descriptor_resp.uuid = {descriptor->uuid.get_128bit_high(), descriptor->uuid.get_128bit_low()}; - descriptor_resp.handle = descriptor->handle; + descriptor_resp.uuid = get_128bit_uuid_vec(desc_result.uuid); + descriptor_resp.handle = desc_result.handle; characteristic_resp.descriptors.push_back(std::move(descriptor_resp)); + desc_offset++; } service_resp.characteristics.push_back(std::move(characteristic_resp)); } resp.services.push_back(std::move(service_resp)); api::global_api_server->send_bluetooth_gatt_services(resp); - connection->send_service_++; } } } @@ -103,7 +186,13 @@ BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool rese for (auto *connection : this->connections_) { if (connection->get_address() == 0) { + connection->send_service_ = DONE_SENDING_SERVICES; connection->set_address(address); + // All connections must start at INIT + // We only set the state if we allocate the connection + // to avoid a race where multiple connection attempts + // are made. + connection->set_state(espbt::ClientState::INIT); return connection; } } @@ -113,6 +202,8 @@ BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool rese void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest &msg) { switch (msg.request_type) { + case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE: + case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE: case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT: { auto *connection = this->get_connection_(msg.address, true); if (connection == nullptr) { @@ -120,9 +211,63 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest api::global_api_server->send_bluetooth_device_connection(msg.address, false); return; } - ESP_LOGV(TAG, "[%d] [%s] Searching to connect", connection->get_connection_index(), - connection->address_str().c_str()); - connection->set_state(espbt::ClientState::SEARCHING); + if (connection->state() == espbt::ClientState::CONNECTED || + connection->state() == espbt::ClientState::ESTABLISHED) { + ESP_LOGW(TAG, "[%d] [%s] Connection already established", connection->get_connection_index(), + connection->address_str().c_str()); + api::global_api_server->send_bluetooth_device_connection(msg.address, true); + api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), + this->get_bluetooth_connections_limit()); + return; + } else if (connection->state() == espbt::ClientState::SEARCHING) { + ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, already searching for device", + connection->get_connection_index(), connection->address_str().c_str()); + return; + } else if (connection->state() == espbt::ClientState::DISCOVERED) { + ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, device already discovered", + connection->get_connection_index(), connection->address_str().c_str()); + return; + } else if (connection->state() == espbt::ClientState::READY_TO_CONNECT) { + ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, waiting in line to connect", + connection->get_connection_index(), connection->address_str().c_str()); + return; + } else if (connection->state() == espbt::ClientState::CONNECTING) { + ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, already connecting", connection->get_connection_index(), + connection->address_str().c_str()); + return; + } else if (connection->state() == espbt::ClientState::DISCONNECTING) { + ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, device is disconnecting", + connection->get_connection_index(), connection->address_str().c_str()); + return; + } else if (connection->state() != espbt::ClientState::INIT) { + ESP_LOGW(TAG, "[%d] [%s] Connection already in progress", connection->get_connection_index(), + connection->address_str().c_str()); + return; + } + if (msg.request_type == api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE) { + connection->set_connection_type(espbt::ConnectionType::V3_WITH_CACHE); + ESP_LOGI(TAG, "[%d] [%s] Connecting v3 with cache", connection->get_connection_index(), + connection->address_str().c_str()); + } else if (msg.request_type == api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE) { + connection->set_connection_type(espbt::ConnectionType::V3_WITHOUT_CACHE); + ESP_LOGI(TAG, "[%d] [%s] Connecting v3 without cache", connection->get_connection_index(), + connection->address_str().c_str()); + } else { + connection->set_connection_type(espbt::ConnectionType::V1); + ESP_LOGI(TAG, "[%d] [%s] Connecting v1", connection->get_connection_index(), connection->address_str().c_str()); + } + if (msg.has_address_type) { + connection->remote_bda_[0] = (msg.address >> 40) & 0xFF; + connection->remote_bda_[1] = (msg.address >> 32) & 0xFF; + connection->remote_bda_[2] = (msg.address >> 24) & 0xFF; + connection->remote_bda_[3] = (msg.address >> 16) & 0xFF; + connection->remote_bda_[4] = (msg.address >> 8) & 0xFF; + connection->remote_bda_[5] = (msg.address >> 0) & 0xFF; + connection->set_remote_addr_type(static_cast(msg.address_type)); + connection->set_state(espbt::ClientState::DISCOVERED); + } else { + connection->set_state(espbt::ClientState::SEARCHING); + } api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), this->get_bluetooth_connections_limit()); break; @@ -201,7 +346,7 @@ void BluetoothProxy::bluetooth_gatt_write_descriptor(const api::BluetoothGATTWri return; } - auto err = connection->write_descriptor(msg.handle, msg.data); + auto err = connection->write_descriptor(msg.handle, msg.data, true); if (err != ESP_OK) { api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); } @@ -214,12 +359,13 @@ void BluetoothProxy::bluetooth_gatt_send_services(const api::BluetoothGATTGetSer api::global_api_server->send_bluetooth_gatt_error(msg.address, 0, ESP_GATT_NOT_CONNECTED); return; } - if (connection->services_.empty()) { + if (!connection->service_count_) { ESP_LOGW(TAG, "[%d] [%s] No GATT services found", connection->connection_index_, connection->address_str().c_str()); api::global_api_server->send_bluetooth_gatt_services_done(msg.address); return; } - if (connection->send_service_ == -1) // Don't start sending services again if we're already sending them + if (connection->send_service_ == + DONE_SENDING_SERVICES) // Only start sending services if we're not already sending them connection->send_service_ = 0; } diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index 129b042277..5d3b385bec 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -3,6 +3,7 @@ #ifdef USE_ESP32 #include +#include #include "esphome/components/api/api_pb2.h" #include "esphome/components/esp32_ble_client/ble_client_base.h" @@ -40,15 +41,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg); void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg); - int get_bluetooth_connections_free() { - int free = 0; - for (auto *connection : this->connections_) { - if (connection->address_ == 0) { - free++; - } - } - return free; - } + int get_bluetooth_connections_free(); int get_bluetooth_connections_limit() { return this->connections_.size(); } void set_active(bool active) { this->active_ = active; } @@ -59,7 +52,6 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com BluetoothConnection *get_connection_(uint64_t address, bool reserve); - int16_t send_service_{-1}; bool active_; std::vector connections_{}; diff --git a/esphome/components/canbus/canbus.h b/esphome/components/canbus/canbus.h index 06b15c0db5..4a12742627 100644 --- a/esphome/components/canbus/canbus.h +++ b/esphome/components/canbus/canbus.h @@ -4,6 +4,8 @@ #include "esphome/core/component.h" #include "esphome/core/optional.h" +#include + namespace esphome { namespace canbus { diff --git a/esphome/components/cap1188/cap1188.h b/esphome/components/cap1188/cap1188.h index a1433deb0f..fa0ed622fa 100644 --- a/esphome/components/cap1188/cap1188.h +++ b/esphome/components/cap1188/cap1188.h @@ -6,6 +6,8 @@ #include "esphome/components/output/binary_output.h" #include "esphome/components/binary_sensor/binary_sensor.h" +#include + namespace esphome { namespace cap1188 { diff --git a/esphome/components/cd74hc4067/__init__.py b/esphome/components/cd74hc4067/__init__.py index 4fb15d1bf3..d57061b710 100644 --- a/esphome/components/cd74hc4067/__init__.py +++ b/esphome/components/cd74hc4067/__init__.py @@ -27,10 +27,10 @@ DEFAULT_DELAY = "2ms" CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(CD74HC4067Component), - cv.Required(CONF_PIN_S0): pins.internal_gpio_output_pin_schema, - cv.Required(CONF_PIN_S1): pins.internal_gpio_output_pin_schema, - cv.Required(CONF_PIN_S2): pins.internal_gpio_output_pin_schema, - cv.Required(CONF_PIN_S3): pins.internal_gpio_output_pin_schema, + cv.Required(CONF_PIN_S0): pins.gpio_output_pin_schema, + cv.Required(CONF_PIN_S1): pins.gpio_output_pin_schema, + cv.Required(CONF_PIN_S2): pins.gpio_output_pin_schema, + cv.Required(CONF_PIN_S3): pins.gpio_output_pin_schema, cv.Optional( CONF_DELAY, default=DEFAULT_DELAY ): cv.positive_time_period_milliseconds, diff --git a/esphome/components/cd74hc4067/cd74hc4067.h b/esphome/components/cd74hc4067/cd74hc4067.h index 4a5c2e4e35..6193513575 100644 --- a/esphome/components/cd74hc4067/cd74hc4067.h +++ b/esphome/components/cd74hc4067/cd74hc4067.h @@ -19,22 +19,22 @@ class CD74HC4067Component : public Component { void activate_pin(uint8_t pin); /// set the pin connected to multiplexer control pin 0 - void set_pin_s0(InternalGPIOPin *pin) { this->pin_s0_ = pin; } + void set_pin_s0(GPIOPin *pin) { this->pin_s0_ = pin; } /// set the pin connected to multiplexer control pin 1 - void set_pin_s1(InternalGPIOPin *pin) { this->pin_s1_ = pin; } + void set_pin_s1(GPIOPin *pin) { this->pin_s1_ = pin; } /// set the pin connected to multiplexer control pin 2 - void set_pin_s2(InternalGPIOPin *pin) { this->pin_s2_ = pin; } + void set_pin_s2(GPIOPin *pin) { this->pin_s2_ = pin; } /// set the pin connected to multiplexer control pin 3 - void set_pin_s3(InternalGPIOPin *pin) { this->pin_s3_ = pin; } + void set_pin_s3(GPIOPin *pin) { this->pin_s3_ = pin; } /// set the delay needed after an input switch void set_switch_delay(uint32_t switch_delay) { this->switch_delay_ = switch_delay; } private: - InternalGPIOPin *pin_s0_; - InternalGPIOPin *pin_s1_; - InternalGPIOPin *pin_s2_; - InternalGPIOPin *pin_s3_; + GPIOPin *pin_s0_; + GPIOPin *pin_s1_; + GPIOPin *pin_s2_; + GPIOPin *pin_s3_; /// the currently active pin uint8_t active_pin_; uint32_t switch_delay_; diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 776a54f59d..512dbdd6dd 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -540,12 +540,15 @@ void Climate::dump_traits_(const char *tag) { ESP_LOGCONFIG(tag, " - Min: %.1f", traits.get_visual_min_temperature()); ESP_LOGCONFIG(tag, " - Max: %.1f", traits.get_visual_max_temperature()); ESP_LOGCONFIG(tag, " - Step: %.1f", traits.get_visual_temperature_step()); - if (traits.get_supports_current_temperature()) + if (traits.get_supports_current_temperature()) { ESP_LOGCONFIG(tag, " [x] Supports current temperature"); - if (traits.get_supports_two_point_target_temperature()) + } + if (traits.get_supports_two_point_target_temperature()) { ESP_LOGCONFIG(tag, " [x] Supports two-point target temperature"); - if (traits.get_supports_action()) + } + if (traits.get_supports_action()) { ESP_LOGCONFIG(tag, " [x] Supports action"); + } if (!traits.get_supported_modes().empty()) { ESP_LOGCONFIG(tag, " [x] Supported modes:"); for (ClimateMode m : traits.get_supported_modes()) diff --git a/esphome/components/custom/binary_sensor/custom_binary_sensor.h b/esphome/components/custom/binary_sensor/custom_binary_sensor.h index 314b9b0832..b7d5458d9e 100644 --- a/esphome/components/custom/binary_sensor/custom_binary_sensor.h +++ b/esphome/components/custom/binary_sensor/custom_binary_sensor.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/components/binary_sensor/binary_sensor.h" +#include + namespace esphome { namespace custom { diff --git a/esphome/components/custom/climate/custom_climate.h b/esphome/components/custom/climate/custom_climate.h index 250d83f69f..37876f7115 100644 --- a/esphome/components/custom/climate/custom_climate.h +++ b/esphome/components/custom/climate/custom_climate.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/components/climate/climate.h" +#include + namespace esphome { namespace custom { diff --git a/esphome/components/custom/cover/custom_cover.h b/esphome/components/custom/cover/custom_cover.h index 71f271bc86..58330b9d54 100644 --- a/esphome/components/custom/cover/custom_cover.h +++ b/esphome/components/custom/cover/custom_cover.h @@ -2,6 +2,8 @@ #include "esphome/components/cover/cover.h" +#include + namespace esphome { namespace custom { diff --git a/esphome/components/custom/light/custom_light_output.h b/esphome/components/custom/light/custom_light_output.h index 744e99b889..c2c83ebe97 100644 --- a/esphome/components/custom/light/custom_light_output.h +++ b/esphome/components/custom/light/custom_light_output.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/components/light/light_output.h" +#include + namespace esphome { namespace custom { diff --git a/esphome/components/custom/output/custom_output.h b/esphome/components/custom/output/custom_output.h index 1b55d51e29..4624642420 100644 --- a/esphome/components/custom/output/custom_output.h +++ b/esphome/components/custom/output/custom_output.h @@ -4,6 +4,8 @@ #include "esphome/components/output/binary_output.h" #include "esphome/components/output/float_output.h" +#include + namespace esphome { namespace custom { diff --git a/esphome/components/custom/sensor/custom_sensor.h b/esphome/components/custom/sensor/custom_sensor.h index 5ef3658e5d..d8f4fbc109 100644 --- a/esphome/components/custom/sensor/custom_sensor.h +++ b/esphome/components/custom/sensor/custom_sensor.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" +#include + namespace esphome { namespace custom { diff --git a/esphome/components/custom/switch/custom_switch.h b/esphome/components/custom/switch/custom_switch.h index 186e7473fe..9657e4b44d 100644 --- a/esphome/components/custom/switch/custom_switch.h +++ b/esphome/components/custom/switch/custom_switch.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/components/switch/switch.h" +#include + namespace esphome { namespace custom { diff --git a/esphome/components/custom/text_sensor/custom_text_sensor.h b/esphome/components/custom/text_sensor/custom_text_sensor.h index f1e9c7665e..13732c00b6 100644 --- a/esphome/components/custom/text_sensor/custom_text_sensor.h +++ b/esphome/components/custom/text_sensor/custom_text_sensor.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/components/text_sensor/text_sensor.h" +#include + namespace esphome { namespace custom { diff --git a/esphome/components/custom_component/custom_component.h b/esphome/components/custom_component/custom_component.h index 3f5760e4cf..3b34019a7a 100644 --- a/esphome/components/custom_component/custom_component.h +++ b/esphome/components/custom_component/custom_component.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/core/application.h" +#include + namespace esphome { namespace custom_component { diff --git a/esphome/components/dac7678/dac7678_output.cpp b/esphome/components/dac7678/dac7678_output.cpp index bfb18e4a4e..b6de615b30 100644 --- a/esphome/components/dac7678/dac7678_output.cpp +++ b/esphome/components/dac7678/dac7678_output.cpp @@ -29,8 +29,9 @@ void DAC7678Output::setup() { ESP_LOGE(TAG, "Reset failed"); this->mark_failed(); return; - } else + } else { ESP_LOGV(TAG, "Reset succeeded"); + } delayMicroseconds(1000); @@ -40,16 +41,18 @@ void DAC7678Output::setup() { ESP_LOGE(TAG, "Set internal reference failed"); this->mark_failed(); return; - } else + } else { ESP_LOGV(TAG, "Internal reference enabled"); + } } } void DAC7678Output::dump_config() { if (this->is_failed()) { ESP_LOGE(TAG, "Setting up DAC7678 failed!"); - } else + } else { ESP_LOGCONFIG(TAG, "DAC7678 initialised"); + } } void DAC7678Output::register_channel(DAC7678Channel *channel) { diff --git a/esphome/components/dallas/dallas_component.h b/esphome/components/dallas/dallas_component.h index 37c098283a..b21bc02e54 100644 --- a/esphome/components/dallas/dallas_component.h +++ b/esphome/components/dallas/dallas_component.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esp_one_wire.h" +#include + namespace esphome { namespace dallas { diff --git a/esphome/components/dallas/esp_one_wire.cpp b/esphome/components/dallas/esp_one_wire.cpp index 5bd0f42855..32ddf07fb6 100644 --- a/esphome/components/dallas/esp_one_wire.cpp +++ b/esphome/components/dallas/esp_one_wire.cpp @@ -85,9 +85,7 @@ bool HOT IRAM_ATTR ESPOneWire::read_bit() { // whereas on esp-idf it already happens during the pin_mode(OUTPUT) // manually correct for this with these constants. -#ifdef USE_ESP32_FRAMEWORK_ARDUINO - uint32_t timing_constant = 14; -#elif defined(USE_ESP32_FRAMEWORK_ESP_IDF) +#ifdef USE_ESP32 uint32_t timing_constant = 12; #else uint32_t timing_constant = 14; diff --git a/esphome/components/daly_bms/daly_bms.h b/esphome/components/daly_bms/daly_bms.h index 44915368ee..d4fe84fe46 100644 --- a/esphome/components/daly_bms/daly_bms.h +++ b/esphome/components/daly_bms/daly_bms.h @@ -6,6 +6,8 @@ #include "esphome/components/binary_sensor/binary_sensor.h" #include "esphome/components/uart/uart.h" +#include + namespace esphome { namespace daly_bms { diff --git a/esphome/components/dashboard_import/__init__.py b/esphome/components/dashboard_import/__init__.py index b795c85b12..4742c77785 100644 --- a/esphome/components/dashboard_import/__init__.py +++ b/esphome/components/dashboard_import/__init__.py @@ -1,4 +1,5 @@ from pathlib import Path +import requests import esphome.codegen as cg import esphome.config_validation as cv @@ -6,6 +7,7 @@ from esphome.components.packages import validate_source_shorthand from esphome.const import CONF_WIFI from esphome.wizard import wizard_file from esphome.yaml_util import dump +from esphome import git dashboard_import_ns = cg.esphome_ns.namespace("dashboard_import") @@ -25,9 +27,12 @@ def validate_import_url(value): CONF_PACKAGE_IMPORT_URL = "package_import_url" +CONF_IMPORT_FULL_CONFIG = "import_full_config" + CONFIG_SCHEMA = cv.Schema( { cv.Required(CONF_PACKAGE_IMPORT_URL): validate_import_url, + cv.Optional(CONF_IMPORT_FULL_CONFIG, default=False): cv.boolean, } ) @@ -41,6 +46,9 @@ wifi: async def to_code(config): cg.add_define("USE_DASHBOARD_IMPORT") + url = config[CONF_PACKAGE_IMPORT_URL] + if config[CONF_IMPORT_FULL_CONFIG]: + url += "?full_config" cg.add(dashboard_import_ns.set_package_import_url(config[CONF_PACKAGE_IMPORT_URL])) @@ -64,17 +72,30 @@ def import_config( encoding="utf8", ) else: - config = { - "substitutions": {"name": name}, - "packages": {project_name: import_url}, - "esphome": { - "name": "${name}", - "name_add_mac_suffix": False, - }, - } - output = dump(config) + git_file = git.GitFile.from_shorthand(import_url) - if network == CONF_WIFI: - output += WIFI_CONFIG + if git_file.query and "full_config" in git_file.query: + url = git_file.raw_url + try: + req = requests.get(url, timeout=30) + req.raise_for_status() + except requests.exceptions.RequestException as e: + raise ValueError(f"Error while fetching {url}: {e}") from e - p.write_text(output, encoding="utf8") + p.write_text(req.text, encoding="utf8") + + else: + config = { + "substitutions": {"name": name}, + "packages": {project_name: import_url}, + "esphome": { + "name": "${name}", + "name_add_mac_suffix": False, + }, + } + output = dump(config) + + if network == CONF_WIFI: + output += WIFI_CONFIG + + p.write_text(output, encoding="utf8") diff --git a/esphome/components/debug/__init__.py b/esphome/components/debug/__init__.py index ff9b9c5314..223c3c8df1 100644 --- a/esphome/components/debug/__init__.py +++ b/esphome/components/debug/__init__.py @@ -1,11 +1,15 @@ -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +import esphome.final_validate as fv +from esphome.components import logger from esphome.const import ( - CONF_ID, - CONF_DEVICE, - CONF_FREE, - CONF_FRAGMENTATION, CONF_BLOCK, + CONF_DEVICE, + CONF_FRAGMENTATION, + CONF_FREE, + CONF_ID, + CONF_LEVEL, + CONF_LOGGER, CONF_LOOP_TIME, ) @@ -39,6 +43,18 @@ CONFIG_SCHEMA = cv.Schema( ).extend(cv.polling_component_schema("60s")) +def _final_validate(_): + logger_conf = fv.full_config.get()[CONF_LOGGER] + severity = logger.LOG_LEVEL_SEVERITY.index(logger_conf[CONF_LEVEL]) + if severity < logger.LOG_LEVEL_SEVERITY.index("DEBUG"): + raise cv.Invalid( + "The debug component requires the logger to be at least at DEBUG level" + ) + + +FINAL_VALIDATE_SCHEMA = _final_validate + + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index 97d5aeb8a8..c1ede684e6 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -38,14 +38,9 @@ static uint32_t get_free_heap() { void DebugComponent::dump_config() { std::string device_info; + std::string reset_reason; device_info.reserve(256); -#ifndef ESPHOME_LOG_HAS_DEBUG - ESP_LOGE(TAG, "Debug Component requires debug log level!"); - this->status_set_error(); - return; -#endif - ESP_LOGCONFIG(TAG, "Debug component:"); #ifdef USE_TEXT_SENSOR LOG_TEXT_SENSOR(" ", "Device info", this->device_info_); @@ -146,7 +141,6 @@ void DebugComponent::dump_config() { device_info += "|EFuse MAC: "; device_info += mac; - const char *reset_reason; switch (rtc_get_reset_reason(0)) { case POWERON_RESET: reset_reason = "Power On Reset"; @@ -196,7 +190,7 @@ void DebugComponent::dump_config() { default: reset_reason = "Unknown Reset Reason"; } - ESP_LOGD(TAG, "Reset Reason: %s", reset_reason); + ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str()); device_info += "|Reset: "; device_info += reset_reason; @@ -270,6 +264,8 @@ void DebugComponent::dump_config() { device_info += ESP.getResetReason().c_str(); device_info += "|"; device_info += ESP.getResetInfo().c_str(); + + reset_reason = ESP.getResetReason().c_str(); #endif #ifdef USE_TEXT_SENSOR @@ -278,6 +274,9 @@ void DebugComponent::dump_config() { device_info.resize(255); this->device_info_->publish_state(device_info); } + if (this->reset_reason_ != nullptr) { + this->reset_reason_->publish_state(reset_reason); + } #endif // USE_TEXT_SENSOR } diff --git a/esphome/components/debug/debug_component.h b/esphome/components/debug/debug_component.h index 4dc1659616..b80fda55eb 100644 --- a/esphome/components/debug/debug_component.h +++ b/esphome/components/debug/debug_component.h @@ -24,6 +24,7 @@ class DebugComponent : public PollingComponent { #ifdef USE_TEXT_SENSOR void set_device_info_sensor(text_sensor::TextSensor *device_info) { device_info_ = device_info; } + void set_reset_reason_sensor(text_sensor::TextSensor *reset_reason) { reset_reason_ = reset_reason; } #endif // USE_TEXT_SENSOR #ifdef USE_SENSOR void set_free_sensor(sensor::Sensor *free_sensor) { free_sensor_ = free_sensor; } @@ -50,6 +51,7 @@ class DebugComponent : public PollingComponent { #ifdef USE_TEXT_SENSOR text_sensor::TextSensor *device_info_{nullptr}; + text_sensor::TextSensor *reset_reason_{nullptr}; #endif // USE_TEXT_SENSOR }; diff --git a/esphome/components/debug/text_sensor.py b/esphome/components/debug/text_sensor.py index 11e6354f57..24f938a0e2 100644 --- a/esphome/components/debug/text_sensor.py +++ b/esphome/components/debug/text_sensor.py @@ -1,18 +1,29 @@ from esphome.components import text_sensor import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_DEVICE, ENTITY_CATEGORY_DIAGNOSTIC +from esphome.const import ( + CONF_DEVICE, + ENTITY_CATEGORY_DIAGNOSTIC, + ICON_CHIP, + ICON_RESTART, +) from . import CONF_DEBUG_ID, DebugComponent DEPENDENCIES = ["debug"] +CONF_RESET_REASON = "reset_reason" CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(CONF_DEBUG_ID): cv.use_id(DebugComponent), cv.Optional(CONF_DEVICE): text_sensor.text_sensor_schema( - entity_category=ENTITY_CATEGORY_DIAGNOSTIC + icon=ICON_CHIP, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_RESET_REASON): text_sensor.text_sensor_schema( + icon=ICON_RESTART, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, ), } ) @@ -24,3 +35,6 @@ async def to_code(config): if CONF_DEVICE in config: sens = await text_sensor.new_text_sensor(config[CONF_DEVICE]) cg.add(debug_component.set_device_info_sensor(sens)) + if CONF_RESET_REASON in config: + sens = await text_sensor.new_text_sensor(config[CONF_RESET_REASON]) + cg.add(debug_component.set_reset_reason_sensor(sens)) diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index 8db100f236..f6472a123c 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -114,9 +114,9 @@ void DeepSleepComponent::begin_sleep(bool manual) { #endif ESP_LOGI(TAG, "Beginning Deep Sleep"); - if (this->sleep_duration_.has_value()) + if (this->sleep_duration_.has_value()) { ESP_LOGI(TAG, "Sleeping for %" PRId64 "us", *this->sleep_duration_); - + } App.run_safe_shutdown_hooks(); #if defined(USE_ESP32) @@ -147,7 +147,7 @@ void DeepSleepComponent::begin_sleep(bool manual) { if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) { level = !level; } - esp_deep_sleep_enable_gpio_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), + esp_deep_sleep_enable_gpio_wakeup(1 << this->wakeup_pin_->get_pin(), static_cast(level)); } #endif diff --git a/esphome/components/dfplayer/dfplayer.cpp b/esphome/components/dfplayer/dfplayer.cpp index 7df551f5d2..dcba95e20c 100644 --- a/esphome/components/dfplayer/dfplayer.cpp +++ b/esphome/components/dfplayer/dfplayer.cpp @@ -77,14 +77,16 @@ void DFPlayer::loop() { case 0x3A: if (argument == 1) { ESP_LOGI(TAG, "USB loaded"); - } else if (argument == 2) + } else if (argument == 2) { ESP_LOGI(TAG, "TF Card loaded"); + } break; case 0x3B: if (argument == 1) { ESP_LOGI(TAG, "USB unloaded"); - } else if (argument == 2) + } else if (argument == 2) { ESP_LOGI(TAG, "TF Card unloaded"); + } break; case 0x3F: if (argument == 1) { diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index d2d3f2ed77..41052b3ffd 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -4,7 +4,9 @@ #include "esphome/core/defines.h" #include "esphome/core/automation.h" #include "display_color_utils.h" + #include +#include #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" diff --git a/esphome/components/dsmr/dsmr.h b/esphome/components/dsmr/dsmr.h index 76f79ee55c..6621d02cae 100644 --- a/esphome/components/dsmr/dsmr.h +++ b/esphome/components/dsmr/dsmr.h @@ -13,6 +13,8 @@ #include #include +#include + namespace esphome { namespace dsmr { diff --git a/esphome/components/ektf2232/ektf2232.cpp b/esphome/components/ektf2232/ektf2232.cpp index 8df25fce24..80f5f8a8e2 100644 --- a/esphome/components/ektf2232/ektf2232.cpp +++ b/esphome/components/ektf2232/ektf2232.cpp @@ -2,6 +2,8 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include + namespace esphome { namespace ektf2232 { diff --git a/esphome/components/ektf2232/touchscreen.py b/esphome/components/ektf2232/touchscreen.py index b3513b2670..d937265e7a 100644 --- a/esphome/components/ektf2232/touchscreen.py +++ b/esphome/components/ektf2232/touchscreen.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import i2c, touchscreen -from esphome.const import CONF_ID +from esphome.const import CONF_ID, CONF_INTERRUPT_PIN CODEOWNERS = ["@jesserockz"] DEPENDENCIES = ["i2c"] @@ -17,10 +17,8 @@ EKTF2232Touchscreen = ektf2232_ns.class_( ) CONF_EKTF2232_ID = "ektf2232_id" -CONF_INTERRUPT_PIN = "interrupt_pin" CONF_RTS_PIN = "rts_pin" - CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend( cv.Schema( { diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 478c9701b8..3989b62842 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -33,7 +33,7 @@ from .const import ( # noqa VARIANT_FRIENDLY, VARIANTS, ) -from .boards import BOARD_TO_VARIANT +from .boards import BOARDS # force import gpio to register pin schema from .gpio import esp32_pin_to_code # noqa @@ -129,27 +129,27 @@ def _format_framework_espidf_version(ver: cv.Version) -> str: # The default/recommended arduino framework version # - https://github.com/espressif/arduino-esp32/releases # - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32 -RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(1, 0, 6) +RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 0, 5) # The platformio/espressif32 version to use for arduino frameworks # - https://github.com/platformio/platform-espressif32/releases # - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 -ARDUINO_PLATFORM_VERSION = cv.Version(3, 5, 0) +ARDUINO_PLATFORM_VERSION = cv.Version(5, 2, 0) # The default/recommended esp-idf framework version # - https://github.com/espressif/esp-idf/releases # - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf -RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 3, 2) +RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 2) # The platformio/espressif32 version to use for esp-idf frameworks # - https://github.com/platformio/platform-espressif32/releases # - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 -ESP_IDF_PLATFORM_VERSION = cv.Version(3, 5, 0) +ESP_IDF_PLATFORM_VERSION = cv.Version(5, 2, 0) def _arduino_check_versions(value): value = value.copy() lookups = { - "dev": (cv.Version(2, 0, 0), "https://github.com/espressif/arduino-esp32.git"), - "latest": (cv.Version(1, 0, 6), None), + "dev": (cv.Version(2, 0, 5), "https://github.com/espressif/arduino-esp32.git"), + "latest": (cv.Version(2, 0, 5), None), "recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None), } @@ -184,7 +184,7 @@ def _esp_idf_check_versions(value): value = value.copy() lookups = { "dev": (cv.Version(5, 0, 0), "https://github.com/espressif/esp-idf.git"), - "latest": (cv.Version(4, 3, 2), None), + "latest": (cv.Version(4, 4, 2), None), "recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None), } @@ -230,14 +230,14 @@ def _parse_platform_version(value): def _detect_variant(value): if CONF_VARIANT not in value: board = value[CONF_BOARD] - if board not in BOARD_TO_VARIANT: + if board not in BOARDS: raise cv.Invalid( "This board is unknown, please set the variant manually", path=[CONF_BOARD], ) value = value.copy() - value[CONF_VARIANT] = BOARD_TO_VARIANT[board] + value[CONF_VARIANT] = BOARDS[board][KEY_VARIANT] return value @@ -327,6 +327,11 @@ async def to_code(config): "platform_packages", [f"platformio/framework-espidf @ {conf[CONF_SOURCE]}"], ) + # platformio/toolchain-esp32ulp does not support linux_aarch64 yet and has not been updated for over 2 years + # This is espressif's own published version which is more up to date. + cg.add_platformio_option( + "platform_packages", ["espressif/toolchain-esp32ulp @ 2.35.0-20220830"] + ) add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False) add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True) add_idf_sdkconfig_option( @@ -393,11 +398,11 @@ spiffs, data, spiffs, 0x391000, 0x00F000 IDF_PARTITIONS_CSV = """\ # Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, , 0x4000, otadata, data, ota, , 0x2000, phy_init, data, phy, , 0x1000, app0, app, ota_0, , 0x1C0000, app1, app, ota_1, , 0x1C0000, +nvs, data, nvs, , 0x6d000, """ diff --git a/esphome/components/esp32/boards.py b/esphome/components/esp32/boards.py index f14aeefea2..e4fdaec0aa 100644 --- a/esphome/components/esp32/boards.py +++ b/esphome/components/esp32/boards.py @@ -1,4 +1,4 @@ -from .const import VARIANT_ESP32, VARIANT_ESP32S2, VARIANT_ESP32C3 +from .const import VARIANT_ESP32, VARIANT_ESP32S2, VARIANT_ESP32C3, VARIANT_ESP32S3 ESP32_BASE_PINS = { "TX": 1, @@ -42,6 +42,64 @@ ESP32_BASE_PINS = { } ESP32_BOARD_PINS = { + "adafruit_qtpy_esp32c3": { + "A0": 4, + "A1": 3, + "A2": 1, + "A3": 0, + "SDA": 5, + "SCL": 6, + "MOSI": 7, + "MISO": 8, + "SCK": 10, + "RX": 20, + "TX": 21, + "NEOPIXEL": 2, + "PIN_NEOPIXEL": 2, + "BUTTON": 9, + "SWITCH": 9, + }, + "adafruit_qtpy_esp32s2": { + "A0": 18, + "A1": 17, + "A2": 9, + "A3": 8, + "SDA": 7, + "SCL": 6, + "MOSI": 35, + "MISO": 37, + "SCK": 36, + "RX": 16, + "TX": 5, + "SDA1": 41, + "SCL1": 40, + "NEOPIXEL": 39, + "PIN_NEOPIXEL": 39, + "NEOPIXEL_POWER": 38, + "D0": 0, + "BUTTON": 0, + "SWITCH": 0, + }, + "adafruit_qtpy_esp32": { + "A0": 26, + "A1": 25, + "A2": 27, + "A3": 15, + "SDA": 4, + "SCL": 33, + "MOSI": 13, + "MISO": 12, + "SCK": 14, + "RX": 7, + "TX": 32, + "SDA1": 22, + "SCL1": 19, + "NEOPIXEL": 5, + "PIN_NEOPIXEL": 5, + "NEOPIXEL_POWER": 8, + "BUTTON": 0, + "SWITCH": 0, + }, "alksesp32": { "A0": 32, "A1": 33, @@ -550,6 +608,25 @@ ESP32_BOARD_PINS = { }, "lolin_d32": {"LED": 5, "_VBAT": 35}, "lolin_d32_pro": {"LED": 5, "_VBAT": 35}, + "lolin_s2_mini": { + "TX": 43, + "RX": 44, + "SPICS1": 29, + "SPIHD": 31, + "SPIWP": 32, + "SPICS0": 33, + "SPICLK": 34, + "SPIQ": 35, + "SPID": 36, + "MISO": 9, + "MOSI": 11, + "SCK": 7, + "SCL": 35, + "SDA": 33, + "DAC1": 17, + "DAC2": 18, + "LED": 15, + }, "lopy": { "A1": 37, "A2": 38, @@ -984,127 +1061,743 @@ ESP32_BOARD_PINS = { "D13": 2, }, "xinabox_cw02": {"LED": 27}, + "upesy_wroom": {"LED": 2}, + "upesy_wrover": {"LED": 2}, } """ -BOARD_TO_VARIANT generated with: +BOARDS generated with: git clone https://github.com/platformio/platform-espressif32 for x in platform-espressif32/boards/*.json; do mcu=$(jq -r .build.mcu <"$x"); + name=$(jq -r .name <"$x"); fname=$(basename "$x") board="${fname%.*}" variant=$(echo "$mcu" | tr '[:lower:]' '[:upper:]') - echo " \"$board\": VARIANT_${variant}," + echo " \"$board\": {\"name\": \"$name\", \"variant\": VARIANT_${variant},}," done | sort """ -BOARD_TO_VARIANT = { - "alksesp32": VARIANT_ESP32, - "az-delivery-devkit-v4": VARIANT_ESP32, - "bpi-bit": VARIANT_ESP32, - "briki_abc_esp32": VARIANT_ESP32, - "briki_mbc-wb_esp32": VARIANT_ESP32, - "d-duino-32": VARIANT_ESP32, - "esp320": VARIANT_ESP32, - "esp32-c3-devkitm-1": VARIANT_ESP32C3, - "esp32cam": VARIANT_ESP32, - "esp32-devkitlipo": VARIANT_ESP32, - "esp32dev": VARIANT_ESP32, - "esp32doit-devkit-v1": VARIANT_ESP32, - "esp32doit-espduino": VARIANT_ESP32, - "esp32-evb": VARIANT_ESP32, - "esp32-gateway": VARIANT_ESP32, - "esp32-poe-iso": VARIANT_ESP32, - "esp32-poe": VARIANT_ESP32, - "esp32-pro": VARIANT_ESP32, - "esp32-s2-kaluga-1": VARIANT_ESP32S2, - "esp32-s2-saola-1": VARIANT_ESP32S2, - "esp32thing_plus": VARIANT_ESP32, - "esp32thing": VARIANT_ESP32, - "esp32vn-iot-uno": VARIANT_ESP32, - "espea32": VARIANT_ESP32, - "espectro32": VARIANT_ESP32, - "espino32": VARIANT_ESP32, - "esp-wrover-kit": VARIANT_ESP32, - "etboard": VARIANT_ESP32, - "featheresp32-s2": VARIANT_ESP32S2, - "featheresp32": VARIANT_ESP32, - "firebeetle32": VARIANT_ESP32, - "fm-devkit": VARIANT_ESP32, - "frogboard": VARIANT_ESP32, - "healthypi4": VARIANT_ESP32, - "heltec_wifi_kit_32_v2": VARIANT_ESP32, - "heltec_wifi_kit_32": VARIANT_ESP32, - "heltec_wifi_lora_32_V2": VARIANT_ESP32, - "heltec_wifi_lora_32": VARIANT_ESP32, - "heltec_wireless_stick_lite": VARIANT_ESP32, - "heltec_wireless_stick": VARIANT_ESP32, - "honeylemon": VARIANT_ESP32, - "hornbill32dev": VARIANT_ESP32, - "hornbill32minima": VARIANT_ESP32, - "imbrios-logsens-v1p1": VARIANT_ESP32, - "inex_openkb": VARIANT_ESP32, - "intorobot": VARIANT_ESP32, - "iotaap_magnolia": VARIANT_ESP32, - "iotbusio": VARIANT_ESP32, - "iotbusproteus": VARIANT_ESP32, - "kits-edu": VARIANT_ESP32, - "labplus_mpython": VARIANT_ESP32, - "lolin32_lite": VARIANT_ESP32, - "lolin32": VARIANT_ESP32, - "lolin_c3_mini": VARIANT_ESP32C3, - "lolin_d32_pro": VARIANT_ESP32, - "lolin_d32": VARIANT_ESP32, - "lopy4": VARIANT_ESP32, - "lopy": VARIANT_ESP32, - "m5stack-atom": VARIANT_ESP32, - "m5stack-core2": VARIANT_ESP32, - "m5stack-core-esp32": VARIANT_ESP32, - "m5stack-coreink": VARIANT_ESP32, - "m5stack-fire": VARIANT_ESP32, - "m5stack-grey": VARIANT_ESP32, - "m5stack-timer-cam": VARIANT_ESP32, - "m5stick-c": VARIANT_ESP32, - "magicbit": VARIANT_ESP32, - "mgbot-iotik32a": VARIANT_ESP32, - "mgbot-iotik32b": VARIANT_ESP32, - "mhetesp32devkit": VARIANT_ESP32, - "mhetesp32minikit": VARIANT_ESP32, - "microduino-core-esp32": VARIANT_ESP32, - "nano32": VARIANT_ESP32, - "nina_w10": VARIANT_ESP32, - "node32s": VARIANT_ESP32, - "nodemcu-32s": VARIANT_ESP32, - "nscreen-32": VARIANT_ESP32, - "odroid_esp32": VARIANT_ESP32, - "onehorse32dev": VARIANT_ESP32, - "oroca_edubot": VARIANT_ESP32, - "pico32": VARIANT_ESP32, - "piranha_esp32": VARIANT_ESP32, - "pocket_32": VARIANT_ESP32, - "pycom_gpy": VARIANT_ESP32, - "qchip": VARIANT_ESP32, - "quantum": VARIANT_ESP32, - "sensesiot_weizen": VARIANT_ESP32, - "sg-o_airMon": VARIANT_ESP32, - "s_odi_ultra": VARIANT_ESP32, - "sparkfun_lora_gateway_1-channel": VARIANT_ESP32, - "tinypico": VARIANT_ESP32, - "ttgo-lora32-v1": VARIANT_ESP32, - "ttgo-lora32-v21": VARIANT_ESP32, - "ttgo-lora32-v2": VARIANT_ESP32, - "ttgo-t1": VARIANT_ESP32, - "ttgo-t7-v13-mini32": VARIANT_ESP32, - "ttgo-t7-v14-mini32": VARIANT_ESP32, - "ttgo-t-beam": VARIANT_ESP32, - "ttgo-t-watch": VARIANT_ESP32, - "turta_iot_node": VARIANT_ESP32, - "vintlabs-devkit-v1": VARIANT_ESP32, - "wemosbat": VARIANT_ESP32, - "wemos_d1_mini32": VARIANT_ESP32, - "wesp32": VARIANT_ESP32, - "widora-air": VARIANT_ESP32, - "wifiduino32": VARIANT_ESP32, - "xinabox_cw02": VARIANT_ESP32, +BOARDS = { + "adafruit_feather_esp32s2_tft": { + "name": "Adafruit Feather ESP32-S2 TFT", + "variant": VARIANT_ESP32S2, + }, + "adafruit_feather_esp32s3": { + "name": "Adafruit Feather ESP32-S3 2MB PSRAM", + "variant": VARIANT_ESP32S3, + }, + "adafruit_feather_esp32s3_nopsram": { + "name": "Adafruit Feather ESP32-S3 No PSRAM", + "variant": VARIANT_ESP32S3, + }, + "adafruit_feather_esp32s3_tft": { + "name": "Adafruit Feather ESP32-S3 TFT", + "variant": VARIANT_ESP32S3, + }, + "adafruit_feather_esp32_v2": { + "name": "Adafruit Feather ESP32 V2", + "variant": VARIANT_ESP32, + }, + "adafruit_funhouse_esp32s2": { + "name": "Adafruit FunHouse", + "variant": VARIANT_ESP32S2, + }, + "adafruit_itsybitsy_esp32": { + "name": "Adafruit ItsyBitsy ESP32", + "variant": VARIANT_ESP32, + }, + "adafruit_magtag29_esp32s2": { + "name": "Adafruit MagTag 2.9", + "variant": VARIANT_ESP32S2, + }, + "adafruit_metro_esp32s2": { + "name": "Adafruit Metro ESP32-S2", + "variant": VARIANT_ESP32S2, + }, + "adafruit_qtpy_esp32c3": { + "name": "Adafruit QT Py ESP32-C3", + "variant": VARIANT_ESP32C3, + }, + "adafruit_qtpy_esp32": { + "name": "Adafruit QT Py ESP32", + "variant": VARIANT_ESP32, + }, + "adafruit_qtpy_esp32s2": { + "name": "Adafruit QT Py ESP32-S2", + "variant": VARIANT_ESP32S2, + }, + "adafruit_qtpy_esp32s3_nopsram": { + "name": "Adafruit QT Py ESP32-S3 No PSRAM", + "variant": VARIANT_ESP32S3, + }, + "airm2m_core_esp32c3": { + "name": "AirM2M CORE ESP32C3", + "variant": VARIANT_ESP32C3, + }, + "alksesp32": { + "name": "ALKS ESP32", + "variant": VARIANT_ESP32, + }, + "atmegazero_esp32s2": { + "name": "EspinalLab ATMegaZero ESP32-S2", + "variant": VARIANT_ESP32S2, + }, + "az-delivery-devkit-v4": { + "name": "AZ-Delivery ESP-32 Dev Kit C V4", + "variant": VARIANT_ESP32, + }, + "bee_motion_mini": { + "name": "Smart Bee Motion Mini", + "variant": VARIANT_ESP32C3, + }, + "bee_motion": { + "name": "Smart Bee Motion", + "variant": VARIANT_ESP32S2, + }, + "bee_motion_s3": { + "name": "Smart Bee Motion S3", + "variant": VARIANT_ESP32S3, + }, + "bee_s3": { + "name": "Smart Bee S3", + "variant": VARIANT_ESP32S3, + }, + "bpi-bit": { + "name": "BPI-Bit", + "variant": VARIANT_ESP32, + }, + "briki_abc_esp32": { + "name": "Briki ABC (MBC-WB) - ESP32", + "variant": VARIANT_ESP32, + }, + "briki_mbc-wb_esp32": { + "name": "Briki MBC-WB - ESP32", + "variant": VARIANT_ESP32, + }, + "cnrs_aw2eth": { + "name": "CNRS AW2ETH", + "variant": VARIANT_ESP32, + }, + "connaxio_espoir": { + "name": "Connaxio's Espoir", + "variant": VARIANT_ESP32, + }, + "d-duino-32": { + "name": "D-duino-32", + "variant": VARIANT_ESP32, + }, + "deneyapkart1A": { + "name": "Deneyap Kart 1A", + "variant": VARIANT_ESP32, + }, + "deneyapkartg": { + "name": "Deneyap Kart G", + "variant": VARIANT_ESP32C3, + }, + "deneyapkart": { + "name": "Deneyap Kart", + "variant": VARIANT_ESP32, + }, + "deneyapmini": { + "name": "Deneyap Mini", + "variant": VARIANT_ESP32S2, + }, + "denky32": { + "name": "Denky32 (WROOM32)", + "variant": VARIANT_ESP32, + }, + "denky_d4": { + "name": "Denky D4 (PICO-V3-02)", + "variant": VARIANT_ESP32, + }, + "dfrobot_beetle_esp32c3": { + "name": "DFRobot Beetle ESP32-C3", + "variant": VARIANT_ESP32C3, + }, + "dfrobot_firebeetle2_esp32s3": { + "name": "DFRobot Firebeetle 2 ESP32-S3", + "variant": VARIANT_ESP32S3, + }, + "dpu_esp32": { + "name": "TAMC DPU ESP32", + "variant": VARIANT_ESP32, + }, + "esp320": { + "name": "Electronic SweetPeas ESP320", + "variant": VARIANT_ESP32, + }, + "esp32-c3-devkitm-1": { + "name": "Espressif ESP32-C3-DevKitM-1", + "variant": VARIANT_ESP32C3, + }, + "esp32cam": { + "name": "AI Thinker ESP32-CAM", + "variant": VARIANT_ESP32, + }, + "esp32-devkitlipo": { + "name": "OLIMEX ESP32-DevKit-LiPo", + "variant": VARIANT_ESP32, + }, + "esp32dev": { + "name": "Espressif ESP32 Dev Module", + "variant": VARIANT_ESP32, + }, + "esp32doit-devkit-v1": { + "name": "DOIT ESP32 DEVKIT V1", + "variant": VARIANT_ESP32, + }, + "esp32doit-espduino": { + "name": "DOIT ESPduino32", + "variant": VARIANT_ESP32, + }, + "esp32-evb": { + "name": "OLIMEX ESP32-EVB", + "variant": VARIANT_ESP32, + }, + "esp32-gateway": { + "name": "OLIMEX ESP32-GATEWAY", + "variant": VARIANT_ESP32, + }, + "esp32-poe-iso": { + "name": "OLIMEX ESP32-PoE-ISO", + "variant": VARIANT_ESP32, + }, + "esp32-poe": { + "name": "OLIMEX ESP32-PoE", + "variant": VARIANT_ESP32, + }, + "esp32-pro": { + "name": "OLIMEX ESP32-PRO", + "variant": VARIANT_ESP32, + }, + "esp32-s2-franzininho": { + "name": "Franzininho WiFi Board", + "variant": VARIANT_ESP32S2, + }, + "esp32-s2-kaluga-1": { + "name": "Espressif ESP32-S2-Kaluga-1 Kit", + "variant": VARIANT_ESP32S2, + }, + "esp32-s2-saola-1": { + "name": "Espressif ESP32-S2-Saola-1", + "variant": VARIANT_ESP32S2, + }, + "esp32s3box": { + "name": "Espressif ESP32-S3-Box", + "variant": VARIANT_ESP32S3, + }, + "esp32s3camlcd": { + "name": "ESP32S3 CAM LCD", + "variant": VARIANT_ESP32S3, + }, + "esp32-s3-devkitc-1": { + "name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)", + "variant": VARIANT_ESP32S3, + }, + "esp32thing": { + "name": "SparkFun ESP32 Thing", + "variant": VARIANT_ESP32, + }, + "esp32thing_plus": { + "name": "SparkFun ESP32 Thing Plus", + "variant": VARIANT_ESP32, + }, + "esp32vn-iot-uno": { + "name": "ESP32vn IoT Uno", + "variant": VARIANT_ESP32, + }, + "espea32": { + "name": "April Brother ESPea32", + "variant": VARIANT_ESP32, + }, + "espectro32": { + "name": "ESPectro32", + "variant": VARIANT_ESP32, + }, + "espino32": { + "name": "ESPino32", + "variant": VARIANT_ESP32, + }, + "esp-wrover-kit": { + "name": "Espressif ESP-WROVER-KIT", + "variant": VARIANT_ESP32, + }, + "etboard": { + "name": "ETBoard", + "variant": VARIANT_ESP32, + }, + "featheresp32": { + "name": "Adafruit ESP32 Feather", + "variant": VARIANT_ESP32, + }, + "featheresp32-s2": { + "name": "Adafruit ESP32-S2 Feather Development Board", + "variant": VARIANT_ESP32S2, + }, + "firebeetle32": { + "name": "FireBeetle-ESP32", + "variant": VARIANT_ESP32, + }, + "fm-devkit": { + "name": "ESP32 FM DevKit", + "variant": VARIANT_ESP32, + }, + "franzininho_wifi_esp32s2": { + "name": "Franzininho WiFi", + "variant": VARIANT_ESP32S2, + }, + "franzininho_wifi_msc_esp32s2": { + "name": "Franzininho WiFi MSC", + "variant": VARIANT_ESP32S2, + }, + "frogboard": { + "name": "Frog Board ESP32", + "variant": VARIANT_ESP32, + }, + "healthypi4": { + "name": "ProtoCentral HealthyPi 4", + "variant": VARIANT_ESP32, + }, + "heltec_wifi_kit_32": { + "name": "Heltec WiFi Kit 32", + "variant": VARIANT_ESP32, + }, + "heltec_wifi_kit_32_v2": { + "name": "Heltec WiFi Kit 32 (V2)", + "variant": VARIANT_ESP32, + }, + "heltec_wifi_lora_32": { + "name": "Heltec WiFi LoRa 32", + "variant": VARIANT_ESP32, + }, + "heltec_wifi_lora_32_V2": { + "name": "Heltec WiFi LoRa 32 (V2)", + "variant": VARIANT_ESP32, + }, + "heltec_wireless_stick_lite": { + "name": "Heltec Wireless Stick Lite", + "variant": VARIANT_ESP32, + }, + "heltec_wireless_stick": { + "name": "Heltec Wireless Stick", + "variant": VARIANT_ESP32, + }, + "honeylemon": { + "name": "HONEYLemon", + "variant": VARIANT_ESP32, + }, + "hornbill32dev": { + "name": "Hornbill ESP32 Dev", + "variant": VARIANT_ESP32, + }, + "hornbill32minima": { + "name": "Hornbill ESP32 Minima", + "variant": VARIANT_ESP32, + }, + "imbrios-logsens-v1p1": { + "name": "Imbrios LogSens V1P1", + "variant": VARIANT_ESP32, + }, + "inex_openkb": { + "name": "INEX OpenKB", + "variant": VARIANT_ESP32, + }, + "intorobot": { + "name": "IntoRobot Fig", + "variant": VARIANT_ESP32, + }, + "iotaap_magnolia": { + "name": "IoTaaP Magnolia", + "variant": VARIANT_ESP32, + }, + "iotbusio": { + "name": "oddWires IoT-Bus Io", + "variant": VARIANT_ESP32, + }, + "iotbusproteus": { + "name": "oddWires IoT-Bus Proteus", + "variant": VARIANT_ESP32, + }, + "kb32-ft": { + "name": "MakerAsia KB32-FT", + "variant": VARIANT_ESP32, + }, + "kits-edu": { + "name": "KITS ESP32 EDU", + "variant": VARIANT_ESP32, + }, + "labplus_mpython": { + "name": "Labplus mPython", + "variant": VARIANT_ESP32, + }, + "lionbit": { + "name": "Lion:Bit Dev Board", + "variant": VARIANT_ESP32, + }, + "lolin32_lite": { + "name": "WEMOS LOLIN32 Lite", + "variant": VARIANT_ESP32, + }, + "lolin32": { + "name": "WEMOS LOLIN32", + "variant": VARIANT_ESP32, + }, + "lolin_c3_mini": { + "name": "WEMOS LOLIN C3 Mini", + "variant": VARIANT_ESP32C3, + }, + "lolin_d32": { + "name": "WEMOS LOLIN D32", + "variant": VARIANT_ESP32, + }, + "lolin_d32_pro": { + "name": "WEMOS LOLIN D32 PRO", + "variant": VARIANT_ESP32, + }, + "lolin_s2_mini": { + "name": "WEMOS LOLIN S2 Mini", + "variant": VARIANT_ESP32S2, + }, + "lolin_s2_pico": { + "name": "WEMOS LOLIN S2 PICO", + "variant": VARIANT_ESP32S2, + }, + "lolin_s3": { + "name": "WEMOS LOLIN S3", + "variant": VARIANT_ESP32S3, + }, + "lopy4": { + "name": "Pycom LoPy4", + "variant": VARIANT_ESP32, + }, + "lopy": { + "name": "Pycom LoPy", + "variant": VARIANT_ESP32, + }, + "m5stack-atom": { + "name": "M5Stack-ATOM", + "variant": VARIANT_ESP32, + }, + "m5stack-core2": { + "name": "M5Stack Core2", + "variant": VARIANT_ESP32, + }, + "m5stack-core-esp32": { + "name": "M5Stack Core ESP32", + "variant": VARIANT_ESP32, + }, + "m5stack-coreink": { + "name": "M5Stack-Core Ink", + "variant": VARIANT_ESP32, + }, + "m5stack-fire": { + "name": "M5Stack FIRE", + "variant": VARIANT_ESP32, + }, + "m5stack-grey": { + "name": "M5Stack GREY ESP32", + "variant": VARIANT_ESP32, + }, + "m5stack-station": { + "name": "M5Stack Station", + "variant": VARIANT_ESP32, + }, + "m5stack-timer-cam": { + "name": "M5Stack Timer CAM", + "variant": VARIANT_ESP32, + }, + "m5stick-c": { + "name": "M5Stick-C", + "variant": VARIANT_ESP32, + }, + "magicbit": { + "name": "MagicBit", + "variant": VARIANT_ESP32, + }, + "mgbot-iotik32a": { + "name": "MGBOT IOTIK 32A", + "variant": VARIANT_ESP32, + }, + "mgbot-iotik32b": { + "name": "MGBOT IOTIK 32B", + "variant": VARIANT_ESP32, + }, + "mhetesp32devkit": { + "name": "MH ET LIVE ESP32DevKIT", + "variant": VARIANT_ESP32, + }, + "mhetesp32minikit": { + "name": "MH ET LIVE ESP32MiniKit", + "variant": VARIANT_ESP32, + }, + "microduino-core-esp32": { + "name": "Microduino Core ESP32", + "variant": VARIANT_ESP32, + }, + "micros2": { + "name": "microS2", + "variant": VARIANT_ESP32S2, + }, + "minimain_esp32s2": { + "name": "Deparment of Alchemy MiniMain ESP32-S2", + "variant": VARIANT_ESP32S2, + }, + "nano32": { + "name": "MakerAsia Nano32", + "variant": VARIANT_ESP32, + }, + "nina_w10": { + "name": "u-blox NINA-W10 series", + "variant": VARIANT_ESP32, + }, + "node32s": { + "name": "Node32s", + "variant": VARIANT_ESP32, + }, + "nodemcu-32s2": { + "name": "Ai-Thinker NodeMCU-32S2 (ESP-12K)", + "variant": VARIANT_ESP32S2, + }, + "nodemcu-32s": { + "name": "NodeMCU-32S", + "variant": VARIANT_ESP32, + }, + "nscreen-32": { + "name": "YeaCreate NSCREEN-32", + "variant": VARIANT_ESP32, + }, + "odroid_esp32": { + "name": "ODROID-GO", + "variant": VARIANT_ESP32, + }, + "onehorse32dev": { + "name": "Onehorse ESP32 Dev Module", + "variant": VARIANT_ESP32, + }, + "oroca_edubot": { + "name": "OROCA EduBot", + "variant": VARIANT_ESP32, + }, + "pico32": { + "name": "ESP32 Pico Kit", + "variant": VARIANT_ESP32, + }, + "piranha_esp32": { + "name": "Fishino Piranha ESP-32", + "variant": VARIANT_ESP32, + }, + "pocket_32": { + "name": "Dongsen Tech Pocket 32", + "variant": VARIANT_ESP32, + }, + "pycom_gpy": { + "name": "Pycom GPy", + "variant": VARIANT_ESP32, + }, + "qchip": { + "name": "Qchip", + "variant": VARIANT_ESP32, + }, + "quantum": { + "name": "Noduino Quantum", + "variant": VARIANT_ESP32, + }, + "seeed_xiao_esp32c3": { + "name": "Seeed Studio XIAO ESP32C3", + "variant": VARIANT_ESP32C3, + }, + "sensesiot_weizen": { + "name": "LOGISENSES Senses Weizen", + "variant": VARIANT_ESP32, + }, + "sg-o_airMon": { + "name": "SG-O AirMon", + "variant": VARIANT_ESP32, + }, + "s_odi_ultra": { + "name": "S.ODI Ultra v1", + "variant": VARIANT_ESP32, + }, + "sparkfun_esp32_iot_redboard": { + "name": "SparkFun ESP32 IoT RedBoard", + "variant": VARIANT_ESP32, + }, + "sparkfun_esp32micromod": { + "name": "SparkFun ESP32 MicroMod", + "variant": VARIANT_ESP32, + }, + "sparkfun_esp32s2_thing_plus_c": { + "name": "SparkFun ESP32 Thing Plus C", + "variant": VARIANT_ESP32, + }, + "sparkfun_esp32s2_thing_plus": { + "name": "SparkFun ESP32-S2 Thing Plus", + "variant": VARIANT_ESP32S2, + }, + "sparkfun_lora_gateway_1-channel": { + "name": "SparkFun LoRa Gateway 1-Channel", + "variant": VARIANT_ESP32, + }, + "tamc_termod_s3": { + "name": "TAMC Termod S3", + "variant": VARIANT_ESP32S3, + }, + "tinypico": { + "name": "Unexpected Maker TinyPICO", + "variant": VARIANT_ESP32, + }, + "trueverit-iot-driver-mk2": { + "name": "Trueverit ESP32 Universal IoT Driver MK II", + "variant": VARIANT_ESP32, + }, + "trueverit-iot-driver-mk3": { + "name": "Trueverit ESP32 Universal IoT Driver MK III", + "variant": VARIANT_ESP32, + }, + "trueverit-iot-driver": { + "name": "Trueverit ESP32 Universal IoT Driver", + "variant": VARIANT_ESP32, + }, + "ttgo-lora32-v1": { + "name": "TTGO LoRa32-OLED V1", + "variant": VARIANT_ESP32, + }, + "ttgo-lora32-v21": { + "name": "TTGO LoRa32-OLED v2.1.6", + "variant": VARIANT_ESP32, + }, + "ttgo-lora32-v2": { + "name": "TTGO LoRa32-OLED V2", + "variant": VARIANT_ESP32, + }, + "ttgo-t1": { + "name": "TTGO T1", + "variant": VARIANT_ESP32, + }, + "ttgo-t7-v13-mini32": { + "name": "TTGO T7 V1.3 Mini32", + "variant": VARIANT_ESP32, + }, + "ttgo-t7-v14-mini32": { + "name": "TTGO T7 V1.4 Mini32", + "variant": VARIANT_ESP32, + }, + "ttgo-t-beam": { + "name": "TTGO T-Beam", + "variant": VARIANT_ESP32, + }, + "ttgo-t-oi-plus": { + "name": "TTGO T-OI PLUS RISC-V ESP32-C3", + "variant": VARIANT_ESP32C3, + }, + "ttgo-t-watch": { + "name": "TTGO T-Watch", + "variant": VARIANT_ESP32, + }, + "turta_iot_node": { + "name": "Turta IoT Node", + "variant": VARIANT_ESP32, + }, + "um_feathers2": { + "name": "Unexpected Maker FeatherS2", + "variant": VARIANT_ESP32S2, + }, + "um_feathers2_neo": { + "name": "Unexpected Maker FeatherS2 Neo", + "variant": VARIANT_ESP32S2, + }, + "um_feathers3": { + "name": "Unexpected Maker FeatherS3", + "variant": VARIANT_ESP32S3, + }, + "um_pros3": { + "name": "Unexpected Maker PROS3", + "variant": VARIANT_ESP32S3, + }, + "um_rmp": { + "name": "Unexpected Maker RMP", + "variant": VARIANT_ESP32S2, + }, + "um_tinys2": { + "name": "Unexpected Maker TinyS2", + "variant": VARIANT_ESP32S2, + }, + "um_tinys3": { + "name": "Unexpected Maker TinyS3", + "variant": VARIANT_ESP32S3, + }, + "unphone7": { + "name": "unPhone 7", + "variant": VARIANT_ESP32, + }, + "unphone8": { + "name": "unPhone 8", + "variant": VARIANT_ESP32S3, + }, + "unphone9": { + "name": "unPhone 9", + "variant": VARIANT_ESP32S3, + }, + "upesy_wroom": { + "name": "uPesy ESP32 Wroom DevKit", + "variant": VARIANT_ESP32, + }, + "upesy_wrover": { + "name": "uPesy ESP32 Wrover DevKit", + "variant": VARIANT_ESP32, + }, + "vintlabs-devkit-v1": { + "name": "VintLabs ESP32 Devkit", + "variant": VARIANT_ESP32, + }, + "watchy": { + "name": "SQFMI Watchy v2.0", + "variant": VARIANT_ESP32, + }, + "wemosbat": { + "name": "WeMos WiFi and Bluetooth Battery", + "variant": VARIANT_ESP32, + }, + "wemos_d1_mini32": { + "name": "WEMOS D1 MINI ESP32", + "variant": VARIANT_ESP32, + }, + "wemos_d1_uno32": { + "name": "WEMOS D1 R32", + "variant": VARIANT_ESP32, + }, + "wesp32": { + "name": "Silicognition wESP32", + "variant": VARIANT_ESP32, + }, + "widora-air": { + "name": "Widora AIR", + "variant": VARIANT_ESP32, + }, + "wifiduino32c3": { + "name": "Blinker WiFiduinoV2 (ESP32-C3)", + "variant": VARIANT_ESP32C3, + }, + "wifiduino32": { + "name": "Blinker WiFiduino32", + "variant": VARIANT_ESP32, + }, + "wifiduino32s3": { + "name": "Blinker WiFiduino32S3", + "variant": VARIANT_ESP32S3, + }, + "wipy3": { + "name": "Pycom WiPy3", + "variant": VARIANT_ESP32, + }, + "wt32-eth01": { + "name": "Wireless-Tag WT32-ETH01 Ethernet Module", + "variant": VARIANT_ESP32, + }, + "xinabox_cw02": { + "name": "XinaBox CW02", + "variant": VARIANT_ESP32, + }, } diff --git a/esphome/components/esp32/gpio_idf.cpp b/esphome/components/esp32/gpio.cpp similarity index 80% rename from esphome/components/esp32/gpio_idf.cpp rename to esphome/components/esp32/gpio.cpp index 498843ebff..d0805e4dd2 100644 --- a/esphome/components/esp32/gpio_idf.cpp +++ b/esphome/components/esp32/gpio.cpp @@ -1,6 +1,6 @@ -#ifdef USE_ESP32_FRAMEWORK_ESP_IDF +#ifdef USE_ESP32 -#include "gpio_idf.h" +#include "gpio.h" #include "esphome/core/log.h" namespace esphome { @@ -8,13 +8,11 @@ namespace esp32 { static const char *const TAG = "esp32"; -bool IDFInternalGPIOPin::isr_service_installed = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +bool ESP32InternalGPIOPin::isr_service_installed = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static gpio_mode_t IRAM_ATTR flags_to_mode(gpio::Flags flags) { flags = (gpio::Flags)(flags & ~(gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN)); - if (flags == gpio::FLAG_NONE) { - return GPIO_MODE_DISABLE; - } else if (flags == gpio::FLAG_INPUT) { + if (flags == gpio::FLAG_INPUT) { return GPIO_MODE_INPUT; } else if (flags == gpio::FLAG_OUTPUT) { return GPIO_MODE_OUTPUT; @@ -25,7 +23,7 @@ static gpio_mode_t IRAM_ATTR flags_to_mode(gpio::Flags flags) { } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT)) { return GPIO_MODE_INPUT_OUTPUT; } else { - // unsupported + // unsupported or gpio::FLAG_NONE return GPIO_MODE_DISABLE; } } @@ -35,14 +33,14 @@ struct ISRPinArg { bool inverted; }; -ISRInternalGPIOPin IDFInternalGPIOPin::to_isr() const { +ISRInternalGPIOPin ESP32InternalGPIOPin::to_isr() const { auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory) arg->pin = pin_; arg->inverted = inverted_; return ISRInternalGPIOPin((void *) arg); } -void IDFInternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const { +void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const { gpio_int_type_t idf_type = GPIO_INTR_ANYEDGE; switch (type) { case gpio::INTERRUPT_RISING_EDGE: @@ -74,13 +72,13 @@ void IDFInternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio: gpio_isr_handler_add(pin_, func, arg); } -std::string IDFInternalGPIOPin::dump_summary() const { +std::string ESP32InternalGPIOPin::dump_summary() const { char buffer[32]; snprintf(buffer, sizeof(buffer), "GPIO%u", static_cast(pin_)); return buffer; } -void IDFInternalGPIOPin::setup() { +void ESP32InternalGPIOPin::setup() { gpio_config_t conf{}; conf.pin_bit_mask = 1ULL << static_cast(pin_); conf.mode = flags_to_mode(flags_); @@ -88,10 +86,12 @@ void IDFInternalGPIOPin::setup() { conf.pull_down_en = flags_ & gpio::FLAG_PULLDOWN ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE; conf.intr_type = GPIO_INTR_DISABLE; gpio_config(&conf); - gpio_set_drive_capability(pin_, drive_strength_); + if (flags_ & gpio::FLAG_OUTPUT) { + gpio_set_drive_capability(pin_, drive_strength_); + } } -void IDFInternalGPIOPin::pin_mode(gpio::Flags flags) { +void ESP32InternalGPIOPin::pin_mode(gpio::Flags flags) { // can't call gpio_config here because that logs in esp-idf which may cause issues gpio_set_direction(pin_, flags_to_mode(flags)); gpio_pull_mode_t pull_mode = GPIO_FLOATING; @@ -105,9 +105,9 @@ void IDFInternalGPIOPin::pin_mode(gpio::Flags flags) { gpio_set_pull_mode(pin_, pull_mode); } -bool IDFInternalGPIOPin::digital_read() { return bool(gpio_get_level(pin_)) != inverted_; } -void IDFInternalGPIOPin::digital_write(bool value) { gpio_set_level(pin_, value != inverted_ ? 1 : 0); } -void IDFInternalGPIOPin::detach_interrupt() const { gpio_intr_disable(pin_); } +bool ESP32InternalGPIOPin::digital_read() { return bool(gpio_get_level(pin_)) != inverted_; } +void ESP32InternalGPIOPin::digital_write(bool value) { gpio_set_level(pin_, value != inverted_ ? 1 : 0); } +void ESP32InternalGPIOPin::detach_interrupt() const { gpio_intr_disable(pin_); } } // namespace esp32 @@ -140,4 +140,4 @@ void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) { } // namespace esphome -#endif // USE_ESP32_FRAMEWORK_ESP_IDF +#endif // USE_ESP32 diff --git a/esphome/components/esp32/gpio_idf.h b/esphome/components/esp32/gpio.h similarity index 90% rename from esphome/components/esp32/gpio_idf.h rename to esphome/components/esp32/gpio.h index a07d11378a..23b723e0b4 100644 --- a/esphome/components/esp32/gpio_idf.h +++ b/esphome/components/esp32/gpio.h @@ -1,13 +1,13 @@ #pragma once -#ifdef USE_ESP32_FRAMEWORK_ESP_IDF +#ifdef USE_ESP32 #include "esphome/core/hal.h" #include namespace esphome { namespace esp32 { -class IDFInternalGPIOPin : public InternalGPIOPin { +class ESP32InternalGPIOPin : public InternalGPIOPin { public: void set_pin(gpio_num_t pin) { pin_ = pin; } void set_inverted(bool inverted) { inverted_ = inverted; } @@ -37,4 +37,4 @@ class IDFInternalGPIOPin : public InternalGPIOPin { } // namespace esp32 } // namespace esphome -#endif // USE_ESP32_FRAMEWORK_ESP_IDF +#endif // USE_ESP32 diff --git a/esphome/components/esp32/gpio.py b/esphome/components/esp32/gpio.py index 0c46bc9801..b97a5e0457 100644 --- a/esphome/components/esp32/gpio.py +++ b/esphome/components/esp32/gpio.py @@ -38,8 +38,7 @@ from .gpio_esp32_s3 import esp32_s3_validate_gpio_pin, esp32_s3_validate_support from .gpio_esp32_h2 import esp32_h2_validate_gpio_pin, esp32_h2_validate_supports -IDFInternalGPIOPin = esp32_ns.class_("IDFInternalGPIOPin", cg.InternalGPIOPin) -ArduinoInternalGPIOPin = esp32_ns.class_("ArduinoInternalGPIOPin", cg.InternalGPIOPin) +ESP32InternalGPIOPin = esp32_ns.class_("ESP32InternalGPIOPin", cg.InternalGPIOPin) def _lookup_pin(value): @@ -173,18 +172,10 @@ DRIVE_STRENGTHS = { gpio_num_t = cg.global_ns.enum("gpio_num_t") -def _choose_pin_declaration(value): - if CORE.using_esp_idf: - return cv.declare_id(IDFInternalGPIOPin)(value) - if CORE.using_arduino: - return cv.declare_id(ArduinoInternalGPIOPin)(value) - raise NotImplementedError - - CONF_DRIVE_STRENGTH = "drive_strength" ESP32_PIN_SCHEMA = cv.All( { - cv.GenerateID(): _choose_pin_declaration, + cv.GenerateID(): cv.declare_id(ESP32InternalGPIOPin), cv.Required(CONF_NUMBER): validate_gpio_pin, cv.Optional(CONF_MODE, default={}): cv.Schema( { @@ -196,8 +187,7 @@ ESP32_PIN_SCHEMA = cv.All( } ), cv.Optional(CONF_INVERTED, default=False): cv.boolean, - cv.SplitDefault(CONF_DRIVE_STRENGTH, esp32_idf="20mA"): cv.All( - cv.only_with_esp_idf, + cv.Optional(CONF_DRIVE_STRENGTH, default="20mA"): cv.All( cv.float_with_unit("current", "mA", optional_unit=True), cv.enum(DRIVE_STRENGTHS), ), @@ -210,10 +200,7 @@ ESP32_PIN_SCHEMA = cv.All( async def esp32_pin_to_code(config): var = cg.new_Pvariable(config[CONF_ID]) num = config[CONF_NUMBER] - if CORE.using_esp_idf: - cg.add(var.set_pin(getattr(gpio_num_t, f"GPIO_NUM_{num}"))) - else: - cg.add(var.set_pin(num)) + cg.add(var.set_pin(getattr(gpio_num_t, f"GPIO_NUM_{num}"))) cg.add(var.set_inverted(config[CONF_INVERTED])) if CONF_DRIVE_STRENGTH in config: cg.add(var.set_drive_strength(config[CONF_DRIVE_STRENGTH])) diff --git a/esphome/components/esp32/gpio_arduino.cpp b/esphome/components/esp32/gpio_arduino.cpp deleted file mode 100644 index ba92894f97..0000000000 --- a/esphome/components/esp32/gpio_arduino.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#ifdef USE_ESP32_FRAMEWORK_ARDUINO - -#include "gpio_arduino.h" -#include "esphome/core/log.h" -#include - -namespace esphome { -namespace esp32 { - -static const char *const TAG = "esp32"; - -static int IRAM_ATTR flags_to_mode(gpio::Flags flags) { - if (flags == gpio::FLAG_INPUT) { - return INPUT; - } else if (flags == gpio::FLAG_OUTPUT) { - return OUTPUT; - } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { - return INPUT_PULLUP; - } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLDOWN)) { - return INPUT_PULLDOWN; - } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { - return OUTPUT_OPEN_DRAIN; - } else { - return 0; - } -} - -struct ISRPinArg { - uint8_t pin; - bool inverted; -}; - -ISRInternalGPIOPin ArduinoInternalGPIOPin::to_isr() const { - auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory) - arg->pin = pin_; - arg->inverted = inverted_; - return ISRInternalGPIOPin((void *) arg); -} - -void ArduinoInternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const { - uint8_t arduino_mode = DISABLED; - switch (type) { - case gpio::INTERRUPT_RISING_EDGE: - arduino_mode = inverted_ ? FALLING : RISING; - break; - case gpio::INTERRUPT_FALLING_EDGE: - arduino_mode = inverted_ ? RISING : FALLING; - break; - case gpio::INTERRUPT_ANY_EDGE: - arduino_mode = CHANGE; - break; - case gpio::INTERRUPT_LOW_LEVEL: - arduino_mode = inverted_ ? ONHIGH : ONLOW; - break; - case gpio::INTERRUPT_HIGH_LEVEL: - arduino_mode = inverted_ ? ONLOW : ONHIGH; - break; - } - - attachInterruptArg(pin_, func, arg, arduino_mode); -} - -void ArduinoInternalGPIOPin::pin_mode(gpio::Flags flags) { - pinMode(pin_, flags_to_mode(flags)); // NOLINT -} - -std::string ArduinoInternalGPIOPin::dump_summary() const { - char buffer[32]; - snprintf(buffer, sizeof(buffer), "GPIO%u", pin_); - return buffer; -} - -bool ArduinoInternalGPIOPin::digital_read() { - return bool(digitalRead(pin_)) != inverted_; // NOLINT -} -void ArduinoInternalGPIOPin::digital_write(bool value) { - digitalWrite(pin_, value != inverted_ ? 1 : 0); // NOLINT -} -void ArduinoInternalGPIOPin::detach_interrupt() const { - detachInterrupt(pin_); // NOLINT -} - -} // namespace esp32 - -using namespace esp32; - -bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { - auto *arg = reinterpret_cast(arg_); - return bool(digitalRead(arg->pin)) != arg->inverted; // NOLINT -} -void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { - auto *arg = reinterpret_cast(arg_); - digitalWrite(arg->pin, value != arg->inverted ? 1 : 0); // NOLINT -} -void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() { - auto *arg = reinterpret_cast(arg_); -#ifdef CONFIG_IDF_TARGET_ESP32C3 - GPIO.status_w1tc.val = 1UL << arg->pin; -#else - if (arg->pin < 32) { - GPIO.status_w1tc = 1UL << arg->pin; - } else { - GPIO.status1_w1tc.intr_st = 1UL << (arg->pin - 32); - } -#endif -} -void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) { - auto *arg = reinterpret_cast(arg_); - pinMode(arg->pin, flags_to_mode(flags)); // NOLINT -} - -} // namespace esphome - -#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/esp32/gpio_arduino.h b/esphome/components/esp32/gpio_arduino.h deleted file mode 100644 index e88d39b1a8..0000000000 --- a/esphome/components/esp32/gpio_arduino.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#ifdef USE_ESP32_FRAMEWORK_ARDUINO -#include "esphome/core/hal.h" - -namespace esphome { -namespace esp32 { - -class ArduinoInternalGPIOPin : public InternalGPIOPin { - public: - void set_pin(uint8_t pin) { pin_ = pin; } - void set_inverted(bool inverted) { inverted_ = inverted; } - void set_flags(gpio::Flags flags) { flags_ = flags; } - - void setup() override { pin_mode(flags_); } - void pin_mode(gpio::Flags flags) override; - bool digital_read() override; - void digital_write(bool value) override; - std::string dump_summary() const override; - void detach_interrupt() const override; - ISRInternalGPIOPin to_isr() const override; - uint8_t get_pin() const override { return pin_; } - bool is_inverted() const override { return inverted_; } - - protected: - void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override; - - uint8_t pin_; - bool inverted_; - gpio::Flags flags_; -}; - -} // namespace esp32 -} // namespace esphome - -#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/esp32_ble_client/ble_characteristic.cpp b/esphome/components/esp32_ble_client/ble_characteristic.cpp index fbc9ba16dc..2fd7fe9871 100644 --- a/esphome/components/esp32_ble_client/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_client/ble_characteristic.cpp @@ -16,7 +16,15 @@ BLECharacteristic::~BLECharacteristic() { delete desc; // NOLINT(cppcoreguidelines-owning-memory) } +void BLECharacteristic::release_descriptors() { + this->parsed = false; + for (auto &desc : this->descriptors) + delete desc; // NOLINT(cppcoreguidelines-owning-memory) + this->descriptors.clear(); +} + void BLECharacteristic::parse_descriptors() { + this->parsed = true; uint16_t offset = 0; esp_gattc_descr_elem_t result; @@ -49,6 +57,8 @@ void BLECharacteristic::parse_descriptors() { } BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) { + if (!this->parsed) + this->parse_descriptors(); for (auto &desc : this->descriptors) { if (desc->uuid == uuid) return desc; @@ -59,6 +69,8 @@ BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) { return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid)); } BLEDescriptor *BLECharacteristic::get_descriptor_by_handle(uint16_t handle) { + if (!this->parsed) + this->parse_descriptors(); for (auto &desc : this->descriptors) { if (desc->handle == handle) return desc; diff --git a/esphome/components/esp32_ble_client/ble_characteristic.h b/esphome/components/esp32_ble_client/ble_characteristic.h index d290b282fc..a014788e65 100644 --- a/esphome/components/esp32_ble_client/ble_characteristic.h +++ b/esphome/components/esp32_ble_client/ble_characteristic.h @@ -6,6 +6,8 @@ #include "ble_descriptor.h" +#include + namespace esphome { namespace esp32_ble_client { @@ -16,11 +18,13 @@ class BLEService; class BLECharacteristic { public: ~BLECharacteristic(); + bool parsed = false; espbt::ESPBTUUID uuid; uint16_t handle; esp_gatt_char_prop_t properties; std::vector descriptors; void parse_descriptors(); + void release_descriptors(); BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid); BLEDescriptor *get_descriptor(uint16_t uuid); BLEDescriptor *get_descriptor_by_handle(uint16_t handle); diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 964ff29771..017d65573d 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -9,6 +9,13 @@ namespace esphome { namespace esp32_ble_client { static const char *const TAG = "esp32_ble_client"; +static const esp_bt_uuid_t NOTIFY_DESC_UUID = { + .len = ESP_UUID_LEN_16, + .uuid = + { + .uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG, + }, +}; void BLEClientBase::setup() { static uint8_t connection_index = 0; @@ -23,7 +30,9 @@ void BLEClientBase::setup() { } void BLEClientBase::loop() { - if (this->state_ == espbt::ClientState::DISCOVERED) { + // READY_TO_CONNECT means we have discovered the device + // and the scanner has been stopped by the tracker. + if (this->state_ == espbt::ClientState::READY_TO_CONNECT) { this->connect(); } } @@ -51,7 +60,8 @@ bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) { } void BLEClientBase::connect() { - ESP_LOGI(TAG, "[%d] [%s] Attempting BLE connection", this->connection_index_, this->address_str_.c_str()); + ESP_LOGI(TAG, "[%d] [%s] 0x%02x Attempting BLE connection", this->connection_index_, this->address_str_.c_str(), + this->remote_addr_type_); auto ret = esp_ble_gattc_open(this->gattc_if_, this->remote_bda_, this->remote_addr_type_, true); if (ret) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_open error, status=%d", this->connection_index_, this->address_str_.c_str(), @@ -63,6 +73,8 @@ void BLEClientBase::connect() { } void BLEClientBase::disconnect() { + if (this->state_ == espbt::ClientState::IDLE || this->state_ == espbt::ClientState::DISCONNECTING) + return; ESP_LOGI(TAG, "[%d] [%s] Disconnecting.", this->connection_index_, this->address_str_.c_str()); auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_); if (err != ESP_OK) { @@ -70,12 +82,24 @@ void BLEClientBase::disconnect() { err); } - if (this->state_ == espbt::ClientState::SEARCHING) { + if (this->state_ == espbt::ClientState::SEARCHING || this->state_ == espbt::ClientState::READY_TO_CONNECT || + this->state_ == espbt::ClientState::DISCOVERED) { this->set_address(0); this->set_state(espbt::ClientState::IDLE); + } else { + this->set_state(espbt::ClientState::DISCONNECTING); } } +void BLEClientBase::release_services() { + for (auto &svc : this->services_) + delete svc; // NOLINT(cppcoreguidelines-owning-memory) + this->services_.clear(); +#ifndef CONFIG_BT_GATTC_CACHE_NVS_FLASH + esp_ble_gattc_cache_clean(this->remote_bda_); +#endif +} + bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, esp_ble_gattc_cb_param_t *param) { if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id) @@ -101,17 +125,24 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ case ESP_GATTC_OPEN_EVT: { ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_OPEN_EVT", this->connection_index_, this->address_str_.c_str()); this->conn_id_ = param->open.conn_id; + this->service_count_ = 0; if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) { ESP_LOGW(TAG, "[%d] [%s] Connection failed, status=%d", this->connection_index_, this->address_str_.c_str(), param->open.status); this->set_state(espbt::ClientState::IDLE); break; } + if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) { + this->set_state(espbt::ClientState::CONNECTED); + this->state_ = espbt::ClientState::ESTABLISHED; + break; + } auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if_, param->open.conn_id); if (ret) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_send_mtu_req failed, status=%x", this->connection_index_, this->address_str_.c_str(), ret); } + esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr); break; } case ESP_GATTC_CFG_MTU_EVT: { @@ -124,7 +155,6 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ ESP_LOGV(TAG, "[%d] [%s] cfg_mtu status %d, mtu %d", this->connection_index_, this->address_str_.c_str(), param->cfg_mtu.status, param->cfg_mtu.mtu); this->mtu_ = param->cfg_mtu.mtu; - esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr); break; } case ESP_GATTC_DISCONNECT_EVT: { @@ -132,13 +162,17 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ return false; ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->connection_index_, this->address_str_.c_str(), param->disconnect.reason); - for (auto &svc : this->services_) - delete svc; // NOLINT(cppcoreguidelines-owning-memory) - this->services_.clear(); + this->release_services(); this->set_state(espbt::ClientState::IDLE); break; } case ESP_GATTC_SEARCH_RES_EVT: { + this->service_count_++; + if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) { + // V3 clients don't need services initialized since + // they only request by handle after receiving the services. + break; + } BLEService *ble_service = new BLEService(); // NOLINT(cppcoreguidelines-owning-memory) ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid); ble_service->start_handle = param->search_res.start_handle; @@ -150,27 +184,49 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ case ESP_GATTC_SEARCH_CMPL_EVT: { ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_SEARCH_CMPL_EVT", this->connection_index_, this->address_str_.c_str()); for (auto &svc : this->services_) { - ESP_LOGI(TAG, "[%d] [%s] Service UUID: %s", this->connection_index_, this->address_str_.c_str(), + ESP_LOGV(TAG, "[%d] [%s] Service UUID: %s", this->connection_index_, this->address_str_.c_str(), svc->uuid.to_string().c_str()); - ESP_LOGI(TAG, "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", this->connection_index_, + ESP_LOGV(TAG, "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", this->connection_index_, this->address_str_.c_str(), svc->start_handle, svc->end_handle); - svc->parse_characteristics(); } this->set_state(espbt::ClientState::CONNECTED); this->state_ = espbt::ClientState::ESTABLISHED; break; } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - auto *descr = this->get_config_descriptor(param->reg_for_notify.handle); - if (descr->uuid.get_uuid().len != ESP_UUID_LEN_16 || - descr->uuid.get_uuid().uuid.uuid16 != ESP_GATT_UUID_CHAR_CLIENT_CONFIG) { - ESP_LOGW(TAG, "[%d] [%s] Handle 0x%x (uuid %s) is not a client config char uuid", this->connection_index_, - this->address_str_.c_str(), param->reg_for_notify.handle, descr->uuid.to_string().c_str()); + if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE || + this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) { + // Client is responsible for flipping the descriptor value + // when using the cache break; } - uint16_t notify_en = 1; - auto status = - esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, descr->handle, sizeof(notify_en), + esp_gattc_descr_elem_t desc_result; + uint16_t count = 1; + esp_gatt_status_t descr_status = + esp_ble_gattc_get_descr_by_char_handle(this->gattc_if_, this->connection_index_, param->reg_for_notify.handle, + NOTIFY_DESC_UUID, &desc_result, &count); + if (descr_status != ESP_GATT_OK) { + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_get_descr_by_char_handle error, status=%d", this->connection_index_, + this->address_str_.c_str(), descr_status); + break; + } + esp_gattc_char_elem_t char_result; + esp_gatt_status_t char_status = + esp_ble_gattc_get_all_char(this->gattc_if_, this->connection_index_, param->reg_for_notify.handle, + param->reg_for_notify.handle, &char_result, &count, 0); + if (char_status != ESP_GATT_OK) { + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_get_all_char error, status=%d", this->connection_index_, + this->address_str_.c_str(), char_status); + break; + } + + /* + 1 = notify + 2 = indicate + */ + uint16_t notify_en = char_result.properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY ? 1 : 2; + esp_err_t status = + esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, desc_result.handle, sizeof(notify_en), (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_write_char_descr error, status=%d", this->connection_index_, @@ -234,14 +290,17 @@ float BLEClientBase::parse_char_value(uint8_t *value, uint16_t length) { if (length > 2) { return (float) encode_uint16(value[1], value[2]); } + // fall through case 0x7: // uint24. if (length > 3) { return (float) encode_uint24(value[1], value[2], value[3]); } + // fall through case 0x8: // uint32. if (length > 4) { return (float) encode_uint32(value[1], value[2], value[3], value[4]); } + // fall through case 0xC: // int8. return (float) ((int8_t) value[1]); case 0xD: // int12. @@ -249,10 +308,12 @@ float BLEClientBase::parse_char_value(uint8_t *value, uint16_t length) { if (length > 2) { return (float) ((int16_t)(value[1] << 8) + (int16_t) value[2]); } + // fall through case 0xF: // int24. if (length > 3) { return (float) ((int32_t)(value[1] << 16) + (int32_t)(value[2] << 8) + (int32_t)(value[3])); } + // fall through case 0x10: // int32. if (length > 4) { return (float) ((int32_t)(value[1] << 24) + (int32_t)(value[2] << 16) + (int32_t)(value[3] << 8) + @@ -287,6 +348,8 @@ BLECharacteristic *BLEClientBase::get_characteristic(uint16_t service, uint16_t BLECharacteristic *BLEClientBase::get_characteristic(uint16_t handle) { for (auto *svc : this->services_) { + if (!svc->parsed) + svc->parse_characteristics(); for (auto *chr : svc->characteristics) { if (chr->handle == handle) return chr; @@ -298,8 +361,10 @@ BLECharacteristic *BLEClientBase::get_characteristic(uint16_t handle) { BLEDescriptor *BLEClientBase::get_config_descriptor(uint16_t handle) { auto *chr = this->get_characteristic(handle); if (chr != nullptr) { + if (!chr->parsed) + chr->parse_descriptors(); for (auto &desc : chr->descriptors) { - if (desc->uuid.get_uuid().uuid.uuid16 == 0x2902) + if (desc->uuid.get_uuid().uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG) return desc; } } @@ -323,7 +388,11 @@ BLEDescriptor *BLEClientBase::get_descriptor(uint16_t service, uint16_t chr, uin BLEDescriptor *BLEClientBase::get_descriptor(uint16_t handle) { for (auto *svc : this->services_) { + if (!svc->parsed) + svc->parse_characteristics(); for (auto *chr : svc->characteristics) { + if (!chr->parsed) + chr->parse_descriptors(); for (auto *desc : chr->descriptors) { if (desc->handle == handle) return desc; diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index 90cd8dbddc..9adf239512 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -33,6 +34,7 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; void connect() override; void disconnect(); + void release_services(); bool connected() { return this->state_ == espbt::ClientState::ESTABLISHED; } @@ -42,7 +44,7 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { memset(this->remote_bda_, 0, sizeof(this->remote_bda_)); this->address_str_ = ""; } else { - this->address_str_ = str_snprintf("%02x:%02x:%02x:%02x:%02x:%02x", 17, (uint8_t)(this->address_ >> 40) & 0xff, + this->address_str_ = str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, (uint8_t)(this->address_ >> 40) & 0xff, (uint8_t)(this->address_ >> 32) & 0xff, (uint8_t)(this->address_ >> 24) & 0xff, (uint8_t)(this->address_ >> 16) & 0xff, (uint8_t)(this->address_ >> 8) & 0xff, (uint8_t)(this->address_ >> 0) & 0xff); @@ -66,11 +68,14 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { int get_gattc_if() const { return this->gattc_if_; } uint8_t *get_remote_bda() { return this->remote_bda_; } esp_ble_addr_type_t get_remote_addr_type() const { return this->remote_addr_type_; } + void set_remote_addr_type(esp_ble_addr_type_t address_type) { this->remote_addr_type_ = address_type; } uint16_t get_conn_id() const { return this->conn_id_; } uint64_t get_address() const { return this->address_; } uint8_t get_connection_index() const { return this->connection_index_; } + virtual void set_connection_type(espbt::ConnectionType ct) { this->connection_type_ = ct; } + protected: int gattc_if_; esp_bd_addr_t remote_bda_; @@ -79,7 +84,9 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { uint64_t address_{0}; std::string address_str_{}; uint8_t connection_index_; + int16_t service_count_{0}; uint16_t mtu_{23}; + espbt::ConnectionType connection_type_{espbt::ConnectionType::V1}; std::vector services_; }; diff --git a/esphome/components/esp32_ble_client/ble_service.cpp b/esphome/components/esp32_ble_client/ble_service.cpp index 2f8a55f928..b22d2a1788 100644 --- a/esphome/components/esp32_ble_client/ble_service.cpp +++ b/esphome/components/esp32_ble_client/ble_service.cpp @@ -11,6 +11,8 @@ namespace esp32_ble_client { static const char *const TAG = "esp32_ble_client"; BLECharacteristic *BLEService::get_characteristic(espbt::ESPBTUUID uuid) { + if (!this->parsed) + this->parse_characteristics(); for (auto &chr : this->characteristics) { if (chr->uuid == uuid) return chr; @@ -27,7 +29,15 @@ BLEService::~BLEService() { delete chr; // NOLINT(cppcoreguidelines-owning-memory) } +void BLEService::release_characteristics() { + this->parsed = false; + for (auto &chr : this->characteristics) + delete chr; // NOLINT(cppcoreguidelines-owning-memory) + this->characteristics.clear(); +} + void BLEService::parse_characteristics() { + this->parsed = true; uint16_t offset = 0; esp_gattc_char_elem_t result; @@ -54,10 +64,9 @@ void BLEService::parse_characteristics() { characteristic->handle = result.char_handle; characteristic->service = this; this->characteristics.push_back(characteristic); - ESP_LOGI(TAG, "[%d] [%s] characteristic %s, handle 0x%x, properties 0x%x", this->client->get_connection_index(), + ESP_LOGV(TAG, "[%d] [%s] characteristic %s, handle 0x%x, properties 0x%x", this->client->get_connection_index(), this->client->address_str().c_str(), characteristic->uuid.to_string().c_str(), characteristic->handle, characteristic->properties); - characteristic->parse_descriptors(); offset++; } } diff --git a/esphome/components/esp32_ble_client/ble_service.h b/esphome/components/esp32_ble_client/ble_service.h index 04f2212e0e..41fc3e838b 100644 --- a/esphome/components/esp32_ble_client/ble_service.h +++ b/esphome/components/esp32_ble_client/ble_service.h @@ -6,6 +6,8 @@ #include "ble_characteristic.h" +#include + namespace esphome { namespace esp32_ble_client { @@ -16,12 +18,14 @@ class BLEClientBase; class BLEService { public: ~BLEService(); + bool parsed = false; espbt::ESPBTUUID uuid; uint16_t start_handle; uint16_t end_handle; std::vector characteristics; BLEClientBase *client; void parse_characteristics(); + void release_characteristics(); BLECharacteristic *get_characteristic(espbt::ESPBTUUID uuid); BLECharacteristic *get_characteristic(uint16_t uuid); }; diff --git a/esphome/components/esp32_ble_server/ble_server.h b/esphome/components/esp32_ble_server/ble_server.h index d275eeab01..f82e854090 100644 --- a/esphome/components/esp32_ble_server/ble_server.h +++ b/esphome/components/esp32_ble_server/ble_server.h @@ -12,6 +12,7 @@ #include #include +#include #ifdef USE_ESP32 diff --git a/esphome/components/esp32_ble_server/ble_service.h b/esphome/components/esp32_ble_server/ble_service.h index 16cc897238..2766c931a7 100644 --- a/esphome/components/esp32_ble_server/ble_service.h +++ b/esphome/components/esp32_ble_server/ble_service.h @@ -3,6 +3,8 @@ #include "ble_characteristic.h" #include "esphome/components/esp32_ble/ble_uuid.h" +#include + #ifdef USE_ESP32 #include diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 0d7abe32f9..a3938faff2 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -238,6 +238,11 @@ async def to_code(config): if CORE.using_esp_idf: add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) + # https://github.com/espressif/esp-idf/issues/4101 + # https://github.com/espressif/esp-idf/issues/2503 + # Match arduino CONFIG_BTU_TASK_STACK_SIZE + # https://github.com/espressif/arduino-esp32/blob/fd72cf46ad6fc1a6de99c1d83ba8eba17d80a4ee/tools/sdk/esp32/sdkconfig#L1866 + add_idf_sdkconfig_option("CONFIG_BTU_TASK_STACK_SIZE", 8192) cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 223034578b..fb377e2be2 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -66,7 +66,11 @@ void ESP32BLETracker::setup() { #endif if (this->scan_continuous_) { - this->start_scan_(true); + if (xSemaphoreTake(this->scan_end_lock_, 0L)) { + this->start_scan_(true); + } else { + ESP_LOGW(TAG, "Cannot start scan!"); + } } } @@ -83,86 +87,144 @@ void ESP32BLETracker::loop() { ble_event = this->ble_events_.pop(); } - if (this->scanner_idle_) { - return; - } - - bool connecting = false; + int connecting = 0; + int discovered = 0; + int searching = 0; + int disconnecting = 0; for (auto *client : this->clients_) { - if (client->state() == ClientState::CONNECTING || client->state() == ClientState::DISCOVERED) - connecting = true; - } - - if (!connecting && xSemaphoreTake(this->scan_end_lock_, 0L)) { - xSemaphoreGive(this->scan_end_lock_); - if (this->scan_continuous_) { - this->start_scan_(false); - } else if (xSemaphoreTake(this->scan_end_lock_, 0L) && !this->scanner_idle_) { - xSemaphoreGive(this->scan_end_lock_); - this->end_of_scan_(); - return; + switch (client->state()) { + case ClientState::DISCONNECTING: + disconnecting++; + break; + case ClientState::DISCOVERED: + discovered++; + break; + case ClientState::SEARCHING: + searching++; + break; + case ClientState::CONNECTING: + case ClientState::READY_TO_CONNECT: + connecting++; + break; + default: + break; } } + bool promote_to_connecting = discovered && !searching && !connecting; - if (xSemaphoreTake(this->scan_result_lock_, 5L / portTICK_PERIOD_MS)) { - uint32_t index = this->scan_result_index_; - xSemaphoreGive(this->scan_result_lock_); + if (!this->scanner_idle_) { + if (this->scan_result_index_ && // if it looks like we have a scan result we will take the lock + xSemaphoreTake(this->scan_result_lock_, 5L / portTICK_PERIOD_MS)) { + uint32_t index = this->scan_result_index_; + if (index) { + if (index >= 16) { + ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up."); + } + for (size_t i = 0; i < index; i++) { + ESPBTDevice device; + device.parse_scan_rst(this->scan_result_buffer_[i]); - if (index >= 16) { - ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up."); - } - for (size_t i = 0; i < index; i++) { - ESPBTDevice device; - device.parse_scan_rst(this->scan_result_buffer_[i]); + bool found = false; + for (auto *listener : this->listeners_) { + if (listener->parse_device(device)) + found = true; + } - bool found = false; - for (auto *listener : this->listeners_) { - if (listener->parse_device(device)) - found = true; - } - - for (auto *client : this->clients_) { - if (client->parse_device(device)) { - found = true; - if (client->state() == ClientState::DISCOVERED) { - esp_ble_gap_stop_scanning(); -#ifdef USE_ARDUINO - constexpr TickType_t block_time = 10L / portTICK_PERIOD_MS; -#else - constexpr TickType_t block_time = 0L; // PR #3594 -#endif - if (xSemaphoreTake(this->scan_end_lock_, block_time)) { - xSemaphoreGive(this->scan_end_lock_); + for (auto *client : this->clients_) { + if (client->parse_device(device)) { + found = true; + if (!connecting && client->state() == ClientState::DISCOVERED) { + promote_to_connecting = true; + } } } + + if (!found && !this->scan_continuous_) { + this->print_bt_device_info(device); + } } + this->scan_result_index_ = 0; } - - if (!found) { - this->print_bt_device_info(device); - } - } - - if (xSemaphoreTake(this->scan_result_lock_, 10L / portTICK_PERIOD_MS)) { - this->scan_result_index_ = 0; xSemaphoreGive(this->scan_result_lock_); } + + /* + + Avoid starting the scanner if: + - we are already scanning + - we are connecting to a device + - we are disconnecting from a device + + Otherwise the scanner could fail to ever start again + and our only way to recover is to reboot. + + https://github.com/espressif/esp-idf/issues/6688 + + */ + if (!connecting && !disconnecting && xSemaphoreTake(this->scan_end_lock_, 0L)) { + if (this->scan_continuous_) { + if (!promote_to_connecting && !this->scan_start_failed_ && !this->scan_set_param_failed_) { + this->start_scan_(false); + } else { + // We didn't start the scan, so we need to release the lock + xSemaphoreGive(this->scan_end_lock_); + } + } else if (!this->scanner_idle_) { + this->end_of_scan_(); + return; + } + } + + if (this->scan_start_failed_ || this->scan_set_param_failed_) { + if (this->scan_start_fail_count_ == 255) { + ESP_LOGE(TAG, "ESP-IDF BLE scan could not restart after 255 attempts, rebooting to restore BLE stack..."); + App.reboot(); + } + if (xSemaphoreTake(this->scan_end_lock_, 0L)) { + xSemaphoreGive(this->scan_end_lock_); + } else { + ESP_LOGD(TAG, "Stopping scan after failure..."); + esp_ble_gap_stop_scanning(); + this->cancel_timeout("scan"); + } + if (this->scan_start_failed_) { + ESP_LOGE(TAG, "Scan start failed: %d", this->scan_start_failed_); + this->scan_start_failed_ = ESP_BT_STATUS_SUCCESS; + } + if (this->scan_set_param_failed_) { + ESP_LOGE(TAG, "Scan set param failed: %d", this->scan_set_param_failed_); + this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS; + } + } } - if (this->scan_set_param_failed_) { - ESP_LOGE(TAG, "Scan set param failed: %d", this->scan_set_param_failed_); - this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS; - } - - if (this->scan_start_failed_) { - ESP_LOGE(TAG, "Scan start failed: %d", this->scan_start_failed_); - this->scan_start_failed_ = ESP_BT_STATUS_SUCCESS; + // If there is a discovered client and no connecting + // clients and no clients using the scanner to search for + // devices, then stop scanning and promote the discovered + // client to ready to connect. + if (promote_to_connecting) { + for (auto *client : this->clients_) { + if (client->state() == ClientState::DISCOVERED) { + if (xSemaphoreTake(this->scan_end_lock_, 0L)) { + // Scanner is not running since we got the + // lock, so we can promote the client. + xSemaphoreGive(this->scan_end_lock_); + // We only want to promote one client at a time. + // once the scanner is fully stopped. + client->set_state(ClientState::READY_TO_CONNECT); + } else { + ESP_LOGD(TAG, "Pausing scan to make connection..."); + esp_ble_gap_stop_scanning(); + this->cancel_timeout("scan"); + } + break; + } + } } } void ESP32BLETracker::start_scan() { if (xSemaphoreTake(this->scan_end_lock_, 0L)) { - xSemaphoreGive(this->scan_end_lock_); this->start_scan_(true); } else { ESP_LOGW(TAG, "Scan requested when a scan is already in progress. Ignoring."); @@ -256,8 +318,9 @@ bool ESP32BLETracker::ble_setup() { } void ESP32BLETracker::start_scan_(bool first) { - if (!xSemaphoreTake(this->scan_end_lock_, 0L)) { - ESP_LOGW(TAG, "Cannot start scan!"); + // The lock must be held when calling this function. + if (xSemaphoreTake(this->scan_end_lock_, 0L)) { + ESP_LOGE(TAG, "start_scan called without holding scan_end_lock_"); return; } @@ -278,14 +341,15 @@ void ESP32BLETracker::start_scan_(bool first) { esp_ble_gap_start_scanning(this->scan_duration_); this->set_timeout("scan", this->scan_duration_ * 2000, []() { - ESP_LOGW(TAG, "ESP-IDF BLE scan never terminated, rebooting to restore BLE stack..."); + ESP_LOGE(TAG, "ESP-IDF BLE scan never terminated, rebooting to restore BLE stack..."); App.reboot(); }); } void ESP32BLETracker::end_of_scan_() { - if (!xSemaphoreTake(this->scan_end_lock_, 0L)) { - ESP_LOGW(TAG, "Cannot clean up end of scan!"); + // The lock must be held when calling this function. + if (xSemaphoreTake(this->scan_end_lock_, 0L)) { + ESP_LOGE(TAG, "end_of_scan_ called without holding the scan_end_lock_"); return; } @@ -337,6 +401,12 @@ void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t: void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param ¶m) { this->scan_start_failed_ = param.status; + if (param.status == ESP_BT_STATUS_SUCCESS) { + this->scan_start_fail_count_ = 0; + } else { + this->scan_start_fail_count_++; + xSemaphoreGive(this->scan_end_lock_); + } } void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m) { @@ -635,8 +705,13 @@ void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_p while (offset + 2 < len) { const uint8_t field_length = payload[offset++]; // First byte is length of adv record - if (field_length == 0) + if (field_length == 0) { + if (offset < param.adv_data_len && param.scan_rsp_len > 0) { // Zero padded advertisement data + offset = param.adv_data_len; + continue; + } break; + } // first byte of adv record is adv record type const uint8_t record_type = payload[offset++]; @@ -777,6 +852,9 @@ void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_p this->service_datas_.push_back(data); break; } + case ESP_BLE_AD_TYPE_INT_RANGE: + // Avoid logging this as it's very verbose + break; default: { ESP_LOGV(TAG, "Unhandled type: advType: 0x%02x", record_type); break; @@ -831,8 +909,9 @@ void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) { } ESP_LOGD(TAG, " Address Type: %s", address_type_s); - if (!device.get_name().empty()) + if (!device.get_name().empty()) { ESP_LOGD(TAG, " Name: '%s'", device.get_name().c_str()); + } for (auto &tx_power : device.get_tx_powers()) { ESP_LOGD(TAG, " TX Power: %d", tx_power); } diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 4fbf7b12e1..e6f7829353 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -5,10 +5,12 @@ #include "esphome/core/helpers.h" #include "queue.h" +#include +#include +#include + #ifdef USE_ESP32 -#include -#include #include #include #include @@ -143,12 +145,18 @@ class ESPBTDeviceListener { }; enum class ClientState { + // Connection is allocated + INIT, + // Client is disconnecting + DISCONNECTING, // Connection is idle, no device detected. IDLE, // Searching for device. SEARCHING, // Device advertisement found. DISCOVERED, + // Device is discovered and the scanner is stopped + READY_TO_CONNECT, // Connection in progress. CONNECTING, // Initial connection established. @@ -157,6 +165,18 @@ enum class ClientState { ESTABLISHED, }; +enum class ConnectionType { + // The default connection type, we hold all the services in ram + // for the duration of the connection. + V1, + // The client has a cache of the services and mtu so we should not + // fetch them again + V3_WITH_CACHE, + // The client does not need the services and mtu once we send them + // so we should wipe them from memory as soon as we send them + V3_WITHOUT_CACHE +}; + class ESPBTClient : public ESPBTDeviceListener { public: virtual bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, @@ -233,6 +253,7 @@ class ESP32BLETracker : public Component { uint32_t scan_duration_; uint32_t scan_interval_; uint32_t scan_window_; + uint8_t scan_start_fail_count_; bool scan_continuous_; bool scan_active_; bool scanner_idle_; diff --git a/esphome/components/esp32_ble_tracker/queue.h b/esphome/components/esp32_ble_tracker/queue.h index f3a2b3cb3c..f1dcc337e8 100644 --- a/esphome/components/esp32_ble_tracker/queue.h +++ b/esphome/components/esp32_ble_tracker/queue.h @@ -4,9 +4,10 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include -#include #include +#include +#include +#include #include #include diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index 7e23c9dc6e..b1bf1d8532 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -54,7 +54,11 @@ void ESP32Camera::dump_config() { ESP_LOGCONFIG(TAG, " HREF Pin: %d", conf.pin_href); ESP_LOGCONFIG(TAG, " Pixel Clock Pin: %d", conf.pin_pclk); ESP_LOGCONFIG(TAG, " External Clock: Pin:%d Frequency:%u", conf.pin_xclk, conf.xclk_freq_hz); +#ifdef USE_ESP_IDF // Temporary until the espressif/esp32-camera library is updated ESP_LOGCONFIG(TAG, " I2C Pins: SDA:%d SCL:%d", conf.pin_sscb_sda, conf.pin_sscb_scl); +#else + ESP_LOGCONFIG(TAG, " I2C Pins: SDA:%d SCL:%d", conf.pin_sccb_sda, conf.pin_sccb_scl); +#endif ESP_LOGCONFIG(TAG, " Reset Pin: %d", conf.pin_reset); switch (this->config_.frame_size) { case FRAMESIZE_QQVGA: @@ -209,8 +213,13 @@ void ESP32Camera::set_external_clock(uint8_t pin, uint32_t frequency) { this->config_.xclk_freq_hz = frequency; } void ESP32Camera::set_i2c_pins(uint8_t sda, uint8_t scl) { +#ifdef USE_ESP_IDF // Temporary until the espressif/esp32-camera library is updated this->config_.pin_sscb_sda = sda; this->config_.pin_sscb_scl = scl; +#else + this->config_.pin_sccb_sda = sda; + this->config_.pin_sccb_scl = scl; +#endif } void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; } void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; } diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 956934abc1..5ff4b0827d 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -177,8 +177,9 @@ void ESP32ImprovComponent::set_state_(improv::State state) { } void ESP32ImprovComponent::set_error_(improv::Error error) { - if (error != improv::ERROR_NONE) + if (error != improv::ERROR_NONE) { ESP_LOGE(TAG, "Error: %d", error); + } if (this->error_->get_value().empty() || this->error_->get_value()[0] != error) { uint8_t data[1]{error}; this->error_->set_value(data, 1); diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index 45639f2f63..1a142c94b6 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -9,6 +9,8 @@ #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" +#include + #ifdef USE_ESP32 #include diff --git a/esphome/components/esp32_touch/esp32_touch.h b/esphome/components/esp32_touch/esp32_touch.h index d49e4703a7..c954a14654 100644 --- a/esphome/components/esp32_touch/esp32_touch.h +++ b/esphome/components/esp32_touch/esp32_touch.h @@ -6,6 +6,8 @@ #include "esphome/components/binary_sensor/binary_sensor.h" #include +#include + #if ESP_IDF_VERSION_MAJOR >= 4 #include #else diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 41d7688d44..8715a3b4e6 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -22,10 +22,11 @@ from .const import ( CONF_EARLY_PIN_INIT, KEY_BOARD, KEY_ESP8266, + KEY_FLASH_SIZE, KEY_PIN_INITIAL_STATES, esp8266_ns, ) -from .boards import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS +from .boards import BOARDS, ESP8266_LD_SCRIPTS from .gpio import PinInitialState, add_pin_initial_states_array @@ -218,8 +219,8 @@ async def to_code(config): cg.RawExpression(f"VERSION_CODE({ver.major}, {ver.minor}, {ver.patch})"), ) - if config[CONF_BOARD] in ESP8266_FLASH_SIZES: - flash_size = ESP8266_FLASH_SIZES[config[CONF_BOARD]] + if config[CONF_BOARD] in BOARDS: + flash_size = BOARDS[config[CONF_BOARD]][KEY_FLASH_SIZE] ld_scripts = ESP8266_LD_SCRIPTS[flash_size] if ver <= cv.Version(2, 3, 0): diff --git a/esphome/components/esp8266/boards.py b/esphome/components/esp8266/boards.py index 8b0a23a00f..02bfa9e662 100644 --- a/esphome/components/esp8266/boards.py +++ b/esphome/components/esp8266/boards.py @@ -4,50 +4,6 @@ FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB -ESP8266_FLASH_SIZES = { - "d1": FLASH_SIZE_4_MB, - "d1_mini": FLASH_SIZE_4_MB, - "d1_mini_lite": FLASH_SIZE_1_MB, - "d1_mini_pro": FLASH_SIZE_16_MB, - "esp01": FLASH_SIZE_512_KB, - "esp01_1m": FLASH_SIZE_1_MB, - "esp07": FLASH_SIZE_4_MB, - "esp12e": FLASH_SIZE_4_MB, - "esp210": FLASH_SIZE_4_MB, - "esp8285": FLASH_SIZE_1_MB, - "esp_wroom_02": FLASH_SIZE_2_MB, - "espduino": FLASH_SIZE_4_MB, - "espectro": FLASH_SIZE_4_MB, - "espino": FLASH_SIZE_4_MB, - "espinotee": FLASH_SIZE_4_MB, - "espmxdevkit": FLASH_SIZE_1_MB, - "espresso_lite_v1": FLASH_SIZE_4_MB, - "espresso_lite_v2": FLASH_SIZE_4_MB, - "gen4iod": FLASH_SIZE_512_KB, - "heltec_wifi_kit_8": FLASH_SIZE_4_MB, - "huzzah": FLASH_SIZE_4_MB, - "inventone": FLASH_SIZE_4_MB, - "modwifi": FLASH_SIZE_2_MB, - "nodemcu": FLASH_SIZE_4_MB, - "nodemcuv2": FLASH_SIZE_4_MB, - "oak": FLASH_SIZE_4_MB, - "phoenix_v1": FLASH_SIZE_4_MB, - "phoenix_v2": FLASH_SIZE_4_MB, - "sonoff_basic": FLASH_SIZE_1_MB, - "sonoff_s20": FLASH_SIZE_1_MB, - "sonoff_sv": FLASH_SIZE_1_MB, - "sonoff_th": FLASH_SIZE_1_MB, - "sparkfunBlynk": FLASH_SIZE_4_MB, - "thing": FLASH_SIZE_512_KB, - "thingdev": FLASH_SIZE_512_KB, - "wifi_slot": FLASH_SIZE_1_MB, - "wifiduino": FLASH_SIZE_4_MB, - "wifinfo": FLASH_SIZE_1_MB, - "wio_link": FLASH_SIZE_4_MB, - "wio_node": FLASH_SIZE_4_MB, - "xinabox_cw01": FLASH_SIZE_4_MB, -} - ESP8266_LD_SCRIPTS = { FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"), FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"), @@ -206,3 +162,201 @@ ESP8266_BOARD_PINS = { "wio_node": {"LED": 2, "GROVE": 15, "D0": 3, "D1": 5, "BUTTON": 0}, "xinabox_cw01": {"SDA": 2, "SCL": 14, "LED": 5, "LED_RED": 12, "LED_GREEN": 13}, } + +""" +BOARDS generate with: + +git clone https://github.com/platformio/platform-espressif8266 +for x in platform-espressif8266/boards/*.json; do + max_size=$(jq -r .upload.maximum_size <"$x") + name=$(jq -r .name <"$x") + fname=$(basename "$x") + board="${fname%.*}" + size_mb=$((max_size / (1024 * 1024))) + if [[ $size_mb -gt 0 ]]; then + size="${size_mb}_MB" + else + size="${$((max_size / 1024))}_KB" + fi + echo " \"$board\": {\"name\": \"$name\", \"flash_size\": FLASH_SIZE_$size,}," +done | sort +""" + +BOARDS = { + "agruminolemon": { + "name": "Lifely Agrumino Lemon v4", + "flash_size": FLASH_SIZE_4_MB, + }, + "d1_mini_lite": { + "name": "WeMos D1 mini Lite", + "flash_size": FLASH_SIZE_1_MB, + }, + "d1_mini": { + "name": "WeMos D1 R2 and mini", + "flash_size": FLASH_SIZE_4_MB, + }, + "d1_mini_pro": { + "name": "WeMos D1 mini Pro", + "flash_size": FLASH_SIZE_16_MB, + }, + "d1": { + "name": "WEMOS D1 R1", + "flash_size": FLASH_SIZE_4_MB, + }, + "eduinowifi": { + "name": "Schirmilabs Eduino WiFi", + "flash_size": FLASH_SIZE_4_MB, + }, + "esp01_1m": { + "name": "Espressif Generic ESP8266 ESP-01 1M", + "flash_size": FLASH_SIZE_1_MB, + }, + "esp01": { + "name": "Espressif Generic ESP8266 ESP-01 512k", + "flash_size": FLASH_SIZE_512_KB, + }, + "esp07": { + "name": "Espressif Generic ESP8266 ESP-07 1MB", + "flash_size": FLASH_SIZE_1_MB, + }, + "esp07s": { + "name": "Espressif Generic ESP8266 ESP-07S", + "flash_size": FLASH_SIZE_4_MB, + }, + "esp12e": { + "name": "Espressif ESP8266 ESP-12E", + "flash_size": FLASH_SIZE_4_MB, + }, + "esp210": { + "name": "SweetPea ESP-210", + "flash_size": FLASH_SIZE_4_MB, + }, + "esp8285": { + "name": "Generic ESP8285 Module", + "flash_size": FLASH_SIZE_1_MB, + }, + "espduino": { + "name": "ESPDuino (ESP-13 Module)", + "flash_size": FLASH_SIZE_4_MB, + }, + "espectro": { + "name": "ESPectro Core", + "flash_size": FLASH_SIZE_4_MB, + }, + "espino": { + "name": "ESPino", + "flash_size": FLASH_SIZE_4_MB, + }, + "espinotee": { + "name": "ThaiEasyElec ESPino", + "flash_size": FLASH_SIZE_4_MB, + }, + "espmxdevkit": { + "name": "ESP-Mx DevKit (ESP8285)", + "flash_size": FLASH_SIZE_1_MB, + }, + "espresso_lite_v1": { + "name": "ESPresso Lite 1.0", + "flash_size": FLASH_SIZE_4_MB, + }, + "espresso_lite_v2": { + "name": "ESPresso Lite 2.0", + "flash_size": FLASH_SIZE_4_MB, + }, + "esp_wroom_02": { + "name": "ESP-WROOM-02", + "flash_size": FLASH_SIZE_2_MB, + }, + "gen4iod": { + "name": "4D Systems gen4 IoD Range", + "flash_size": FLASH_SIZE_512_KB, + }, + "heltec_wifi_kit_8": { + "name": "Heltec Wifi kit 8", + "flash_size": FLASH_SIZE_4_MB, + }, + "huzzah": { + "name": "Adafruit HUZZAH ESP8266", + "flash_size": FLASH_SIZE_4_MB, + }, + "inventone": { + "name": "Invent One", + "flash_size": FLASH_SIZE_4_MB, + }, + "modwifi": { + "name": "Olimex MOD-WIFI-ESP8266(-DEV)", + "flash_size": FLASH_SIZE_2_MB, + }, + "nodemcu": { + "name": "NodeMCU 0.9 (ESP-12 Module)", + "flash_size": FLASH_SIZE_4_MB, + }, + "nodemcuv2": { + "name": "NodeMCU 1.0 (ESP-12E Module)", + "flash_size": FLASH_SIZE_4_MB, + }, + "oak": { + "name": "DigiStump Oak", + "flash_size": FLASH_SIZE_4_MB, + }, + "phoenix_v1": { + "name": "Phoenix 1.0", + "flash_size": FLASH_SIZE_4_MB, + }, + "phoenix_v2": { + "name": "Phoenix 2.0", + "flash_size": FLASH_SIZE_4_MB, + }, + "sonoff_basic": { + "name": "Sonoff Basic", + "flash_size": FLASH_SIZE_1_MB, + }, + "sonoff_s20": { + "name": "Sonoff S20", + "flash_size": FLASH_SIZE_1_MB, + }, + "sonoff_sv": { + "name": "Sonoff SV", + "flash_size": FLASH_SIZE_1_MB, + }, + "sonoff_th": { + "name": "Sonoff TH", + "flash_size": FLASH_SIZE_1_MB, + }, + "sparkfunBlynk": { + "name": "SparkFun Blynk Board", + "flash_size": FLASH_SIZE_4_MB, + }, + "thingdev": { + "name": "SparkFun ESP8266 Thing Dev", + "flash_size": FLASH_SIZE_512_KB, + }, + "thing": { + "name": "SparkFun ESP8266 Thing", + "flash_size": FLASH_SIZE_512_KB, + }, + "wifiduino": { + "name": "WiFiduino", + "flash_size": FLASH_SIZE_4_MB, + }, + "wifinfo": { + "name": "WifInfo", + "flash_size": FLASH_SIZE_1_MB, + }, + "wifi_slot": { + "name": "WiFi Slot", + "flash_size": FLASH_SIZE_4_MB, + }, + "wio_link": { + "name": "Wio Link", + "flash_size": FLASH_SIZE_4_MB, + }, + "wio_node": { + "name": "Wio Node", + "flash_size": FLASH_SIZE_4_MB, + }, + "xinabox_cw01": { + "name": "XinaBox CW01", + "flash_size": FLASH_SIZE_4_MB, + }, +} diff --git a/esphome/components/esp8266/const.py b/esphome/components/esp8266/const.py index 7740a97ff4..b718306b01 100644 --- a/esphome/components/esp8266/const.py +++ b/esphome/components/esp8266/const.py @@ -5,6 +5,7 @@ KEY_BOARD = "board" KEY_PIN_INITIAL_STATES = "pin_initial_states" CONF_RESTORE_FROM_FLASH = "restore_from_flash" CONF_EARLY_PIN_INIT = "early_pin_init" +KEY_FLASH_SIZE = "flash_size" # esp8266 namespace is already defined by arduino, manually prefix esphome esp8266_ns = cg.global_ns.namespace("esphome").namespace("esp8266") diff --git a/esphome/components/esp8266/preferences.cpp b/esphome/components/esp8266/preferences.cpp index 1bd20f16ae..8ee5a8225a 100644 --- a/esphome/components/esp8266/preferences.cpp +++ b/esphome/components/esp8266/preferences.cpp @@ -5,12 +5,14 @@ extern "C" { #include "spi_flash.h" } -#include "preferences.h" -#include -#include "esphome/core/preferences.h" +#include "esphome/core/defines.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/defines.h" +#include "esphome/core/preferences.h" +#include "preferences.h" + +#include +#include namespace esphome { namespace esp8266 { diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 696cc62e75..b3614d8fcf 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -30,16 +30,17 @@ CONF_POWER_PIN = "power_pin" EthernetType = ethernet_ns.enum("EthernetType") ETHERNET_TYPES = { "LAN8720": EthernetType.ETHERNET_TYPE_LAN8720, - "TLK110": EthernetType.ETHERNET_TYPE_TLK110, + "RTL8201": EthernetType.ETHERNET_TYPE_RTL8201, + "DP83848": EthernetType.ETHERNET_TYPE_DP83848, "IP101": EthernetType.ETHERNET_TYPE_IP101, } -eth_clock_mode_t = cg.global_ns.enum("eth_clock_mode_t") +emac_rmii_clock_gpio_t = cg.global_ns.enum("emac_rmii_clock_gpio_t") CLK_MODES = { - "GPIO0_IN": eth_clock_mode_t.ETH_CLOCK_GPIO0_IN, - "GPIO0_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO0_OUT, - "GPIO16_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO16_OUT, - "GPIO17_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO17_OUT, + "GPIO0_IN": emac_rmii_clock_gpio_t.EMAC_CLK_IN_GPIO, + "GPIO0_OUT": emac_rmii_clock_gpio_t.EMAC_APPL_CLK_OUT_GPIO, + "GPIO16_OUT": emac_rmii_clock_gpio_t.EMAC_CLK_OUT_GPIO, + "GPIO17_OUT": emac_rmii_clock_gpio_t.EMAC_CLK_OUT_180_GPIO, } @@ -78,7 +79,7 @@ CONFIG_SCHEMA = cv.All( CLK_MODES, upper=True, space="_" ), cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31), - cv.Optional(CONF_POWER_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_POWER_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA, cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, cv.Optional(CONF_USE_ADDRESS): cv.string_strict, @@ -89,7 +90,6 @@ CONFIG_SCHEMA = cv.All( } ).extend(cv.COMPONENT_SCHEMA), _validate, - cv.only_with_arduino, ) @@ -117,13 +117,12 @@ async def to_code(config): cg.add(var.set_use_address(config[CONF_USE_ADDRESS])) if CONF_POWER_PIN in config: - pin = await cg.gpio_pin_expression(config[CONF_POWER_PIN]) - cg.add(var.set_power_pin(pin)) + cg.add(var.set_power_pin(config[CONF_POWER_PIN])) if CONF_MANUAL_IP in config: cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP]))) cg.add_define("USE_ETHERNET") - if CORE.is_esp32: + if CORE.using_arduino: cg.add_library("WiFi", None) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index d768964017..fa66d824be 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -3,21 +3,10 @@ #include "esphome/core/util.h" #include "esphome/core/application.h" -#ifdef USE_ESP32_FRAMEWORK_ARDUINO +#ifdef USE_ESP32 -#include -#include -#include #include - -/// Macro for IDF version comparison -#ifndef ESP_IDF_VERSION_VAL -#define ESP_IDF_VERSION_VAL(major, minor, patch) (((major) << 16) | ((minor) << 8) | (patch)) -#endif - -// Defined in WiFiGeneric.cpp, sets global initialized flag, starts network event task queue and calls -// tcpip_adapter_init() -extern void tcpipInit(); // NOLINT(readability-identifier-naming) +#include "esp_event.h" namespace esphome { namespace ethernet { @@ -37,25 +26,49 @@ EthernetComponent::EthernetComponent() { global_eth_component = this; } void EthernetComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Ethernet..."); + // Delay here to allow power to stabilise before Ethernet is initialised. + delay(300); // NOLINT - auto f = std::bind(&EthernetComponent::on_wifi_event_, this, std::placeholders::_1, std::placeholders::_2); - WiFi.onEvent(f); + esp_err_t err; + err = esp_netif_init(); + ESPHL_ERROR_CHECK(err, "ETH netif init error"); + err = esp_event_loop_create_default(); + ESPHL_ERROR_CHECK(err, "ETH event loop error"); - if (this->power_pin_ != nullptr) { - this->power_pin_->setup(); - } + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); + this->eth_netif_ = esp_netif_new(&cfg); + // Init MAC and PHY configs to default + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + + phy_config.phy_addr = this->phy_addr_; + if (this->power_pin_ != -1) + phy_config.reset_gpio_num = this->power_pin_; + + mac_config.smi_mdc_gpio_num = this->mdc_pin_; + mac_config.smi_mdio_gpio_num = this->mdio_pin_; + mac_config.clock_config.rmii.clock_mode = this->clk_mode_ == EMAC_CLK_IN_GPIO ? EMAC_CLK_EXT_IN : EMAC_CLK_OUT; + mac_config.clock_config.rmii.clock_gpio = this->clk_mode_; + + esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); + + esp_eth_phy_t *phy; switch (this->type_) { case ETHERNET_TYPE_LAN8720: { - memcpy(&this->eth_config_, &phy_lan8720_default_ethernet_config, sizeof(eth_config_t)); + phy = esp_eth_phy_new_lan87xx(&phy_config); break; } - case ETHERNET_TYPE_TLK110: { - memcpy(&this->eth_config_, &phy_tlk110_default_ethernet_config, sizeof(eth_config_t)); + case ETHERNET_TYPE_RTL8201: { + phy = esp_eth_phy_new_rtl8201(&phy_config); + break; + } + case ETHERNET_TYPE_DP83848: { + phy = esp_eth_phy_new_dp83848(&phy_config); break; } case ETHERNET_TYPE_IP101: { - memcpy(&this->eth_config_, &phy_ip101_default_ethernet_config, sizeof(eth_config_t)); + phy = esp_eth_phy_new_ip101(&phy_config); break; } default: { @@ -64,23 +77,23 @@ void EthernetComponent::setup() { } } - this->eth_config_.phy_addr = static_cast(this->phy_addr_); - this->eth_config_.clock_mode = this->clk_mode_; - this->eth_config_.gpio_config = EthernetComponent::eth_phy_config_gpio; - this->eth_config_.tcpip_input = tcpip_adapter_eth_input; + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); + this->eth_handle_ = nullptr; + err = esp_eth_driver_install(ð_config, &this->eth_handle_); + ESPHL_ERROR_CHECK(err, "ETH driver install error"); + /* attach Ethernet driver to TCP/IP stack */ + err = esp_netif_attach(this->eth_netif_, esp_eth_new_netif_glue(this->eth_handle_)); + ESPHL_ERROR_CHECK(err, "ETH netif attach error"); - if (this->power_pin_ != nullptr) { - this->orig_power_enable_fun_ = this->eth_config_.phy_power_enable; - this->eth_config_.phy_power_enable = EthernetComponent::eth_phy_power_enable; - } + // Register user defined event handers + err = esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &EthernetComponent::eth_event_handler, nullptr); + ESPHL_ERROR_CHECK(err, "ETH event handler register error"); + err = esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &EthernetComponent::got_ip_event_handler, nullptr); + ESPHL_ERROR_CHECK(err, "GOT IP event handler register error"); - tcpipInit(); - - esp_err_t err; - err = esp_eth_init(&this->eth_config_); - ESPHL_ERROR_CHECK(err, "ETH init error"); - err = esp_eth_enable(); - ESPHL_ERROR_CHECK(err, "ETH enable error"); + /* start Ethernet driver state machine */ + err = esp_eth_start(this->eth_handle_); + ESPHL_ERROR_CHECK(err, "ETH start error"); } void EthernetComponent::loop() { @@ -130,8 +143,12 @@ void EthernetComponent::dump_config() { eth_type = "LAN8720"; break; - case ETHERNET_TYPE_TLK110: - eth_type = "TLK110"; + case ETHERNET_TYPE_RTL8201: + eth_type = "RTL8201"; + break; + + case ETHERNET_TYPE_DP83848: + eth_type = "DP83848"; break; case ETHERNET_TYPE_IP101: @@ -145,7 +162,9 @@ void EthernetComponent::dump_config() { ESP_LOGCONFIG(TAG, "Ethernet:"); this->dump_connect_params_(); - LOG_PIN(" Power Pin: ", this->power_pin_); + if (this->power_pin_ != -1) { + ESP_LOGCONFIG(TAG, " Power Pin: %u", this->power_pin_); + } ESP_LOGCONFIG(TAG, " MDC Pin: %u", this->mdc_pin_); ESP_LOGCONFIG(TAG, " MDIO Pin: %u", this->mdio_pin_); ESP_LOGCONFIG(TAG, " Type: %s", eth_type.c_str()); @@ -156,34 +175,30 @@ float EthernetComponent::get_setup_priority() const { return setup_priority::WIF bool EthernetComponent::can_proceed() { return this->is_connected(); } network::IPAddress EthernetComponent::get_ip_address() { - tcpip_adapter_ip_info_t ip; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip); + esp_netif_ip_info_t ip; + esp_netif_get_ip_info(this->eth_netif_, &ip); return {ip.ip.addr}; } -void EthernetComponent::on_wifi_event_(system_event_id_t event, system_event_info_t info) { +void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) { const char *event_name; switch (event) { - case SYSTEM_EVENT_ETH_START: + case ETHERNET_EVENT_START: event_name = "ETH started"; - this->started_ = true; + global_eth_component->started_ = true; break; - case SYSTEM_EVENT_ETH_STOP: + case ETHERNET_EVENT_STOP: event_name = "ETH stopped"; - this->started_ = false; - this->connected_ = false; + global_eth_component->started_ = false; + global_eth_component->connected_ = false; break; - case SYSTEM_EVENT_ETH_CONNECTED: + case ETHERNET_EVENT_CONNECTED: event_name = "ETH connected"; break; - case SYSTEM_EVENT_ETH_DISCONNECTED: + case ETHERNET_EVENT_DISCONNECTED: event_name = "ETH disconnected"; - this->connected_ = false; - break; - case SYSTEM_EVENT_ETH_GOT_IP: - event_name = "ETH Got IP"; - this->connected_ = true; + global_eth_component->connected_ = false; break; default: return; @@ -192,17 +207,23 @@ void EthernetComponent::on_wifi_event_(system_event_id_t event, system_event_inf ESP_LOGV(TAG, "[Ethernet event] %s (num=%d)", event_name, event); } +void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, + void *event_data) { + global_eth_component->connected_ = true; + ESP_LOGV(TAG, "[Ethernet event] ETH Got IP (num=%d)", event_id); +} + void EthernetComponent::start_connect_() { this->connect_begin_ = millis(); this->status_set_warning(); esp_err_t err; - err = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_ETH, App.get_name().c_str()); + err = esp_netif_set_hostname(this->eth_netif_, App.get_name().c_str()); if (err != ERR_OK) { - ESP_LOGW(TAG, "tcpip_adapter_set_hostname failed: %s", esp_err_to_name(err)); + ESP_LOGW(TAG, "esp_netif_set_hostname failed: %s", esp_err_to_name(err)); } - tcpip_adapter_ip_info_t info; + esp_netif_ip_info_t info; if (this->manual_ip_.has_value()) { info.ip.addr = static_cast(this->manual_ip_->static_ip); info.gw.addr = static_cast(this->manual_ip_->gateway); @@ -213,11 +234,19 @@ void EthernetComponent::start_connect_() { info.netmask.addr = 0; } - err = tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_ETH); - if (err != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) { + esp_netif_dhcp_status_t status = ESP_NETIF_DHCP_INIT; + + err = esp_netif_dhcpc_get_status(this->eth_netif_, &status); + ESPHL_ERROR_CHECK(err, "DHCPC Get Status Failed!"); + + ESP_LOGV(TAG, "DHCP Client Status: %d", status); + + err = esp_netif_dhcpc_stop(this->eth_netif_); + if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) { ESPHL_ERROR_CHECK(err, "DHCPC stop error"); } - err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_ETH, &info); + + err = esp_netif_set_ip_info(this->eth_netif_, &info); ESPHL_ERROR_CHECK(err, "DHCPC set IP info error"); if (this->manual_ip_.has_value()) { @@ -234,8 +263,8 @@ void EthernetComponent::start_connect_() { dns_setserver(1, &d); } } else { - err = tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_ETH); - if (err != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STARTED) { + err = esp_netif_dhcpc_start(this->eth_netif_); + if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) { ESPHL_ERROR_CHECK(err, "DHCPC start error"); } } @@ -244,53 +273,46 @@ void EthernetComponent::start_connect_() { this->status_set_warning(); } -void EthernetComponent::eth_phy_config_gpio() { - phy_rmii_configure_data_interface_pins(); - phy_rmii_smi_configure_pins(global_eth_component->mdc_pin_, global_eth_component->mdio_pin_); -} - -void EthernetComponent::eth_phy_power_enable(bool enable) { - global_eth_component->power_pin_->digital_write(enable); - // power up takes some time, datasheet says max 300µs - delay(1); - global_eth_component->orig_power_enable_fun_(enable); -} - bool EthernetComponent::is_connected() { return this->state_ == EthernetComponentState::CONNECTED; } void EthernetComponent::dump_connect_params_() { - tcpip_adapter_ip_info_t ip; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip); + esp_netif_ip_info_t ip; + esp_netif_get_ip_info(this->eth_netif_, &ip); ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(ip.ip.addr).str().c_str()); ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(ip.netmask.addr).str().c_str()); ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(ip.gw.addr).str().c_str()); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 4) const ip_addr_t *dns_ip1 = dns_getserver(0); const ip_addr_t *dns_ip2 = dns_getserver(1); -#else - ip_addr_t tmp_ip1 = dns_getserver(0); - const ip_addr_t *dns_ip1 = &tmp_ip1; - ip_addr_t tmp_ip2 = dns_getserver(1); - const ip_addr_t *dns_ip2 = &tmp_ip2; -#endif + ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->u_addr.ip4.addr).str().c_str()); ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->u_addr.ip4.addr).str().c_str()); + + esp_err_t err; + uint8_t mac[6]; - esp_eth_get_mac(mac); + err = esp_eth_ioctl(this->eth_handle_, ETH_CMD_G_MAC_ADDR, &mac); + ESPHL_ERROR_CHECK(err, "ETH_CMD_G_MAC error"); ESP_LOGCONFIG(TAG, " MAC Address: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - ESP_LOGCONFIG(TAG, " Is Full Duplex: %s", YESNO(this->eth_config_.phy_get_duplex_mode())); - ESP_LOGCONFIG(TAG, " Link Up: %s", YESNO(this->eth_config_.phy_check_link())); - ESP_LOGCONFIG(TAG, " Link Speed: %u", this->eth_config_.phy_get_speed_mode() ? 100 : 10); + + eth_duplex_t duplex_mode; + err = esp_eth_ioctl(this->eth_handle_, ETH_CMD_G_DUPLEX_MODE, &duplex_mode); + ESPHL_ERROR_CHECK(err, "ETH_CMD_G_DUPLEX_MODE error"); + ESP_LOGCONFIG(TAG, " Is Full Duplex: %s", YESNO(duplex_mode == ETH_DUPLEX_FULL)); + + eth_speed_t speed; + err = esp_eth_ioctl(this->eth_handle_, ETH_CMD_G_SPEED, &speed); + ESPHL_ERROR_CHECK(err, "ETH_CMD_G_SPEED error"); + ESP_LOGCONFIG(TAG, " Link Speed: %u", speed == ETH_SPEED_100M ? 100 : 10); } void EthernetComponent::set_phy_addr(uint8_t phy_addr) { this->phy_addr_ = phy_addr; } -void EthernetComponent::set_power_pin(GPIOPin *power_pin) { this->power_pin_ = power_pin; } +void EthernetComponent::set_power_pin(int power_pin) { this->power_pin_ = power_pin; } void EthernetComponent::set_mdc_pin(uint8_t mdc_pin) { this->mdc_pin_ = mdc_pin; } void EthernetComponent::set_mdio_pin(uint8_t mdio_pin) { this->mdio_pin_ = mdio_pin; } void EthernetComponent::set_type(EthernetType type) { this->type_ = type; } -void EthernetComponent::set_clk_mode(eth_clock_mode_t clk_mode) { this->clk_mode_ = clk_mode; } +void EthernetComponent::set_clk_mode(emac_rmii_clock_gpio_t clk_mode) { this->clk_mode_ = clk_mode; } void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; } std::string EthernetComponent::get_use_address() const { @@ -305,4 +327,4 @@ void EthernetComponent::set_use_address(const std::string &use_address) { this-> } // namespace ethernet } // namespace esphome -#endif // USE_ESP32_FRAMEWORK_ARDUINO +#endif // USE_ESP32 diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 0924405521..ed784e1bc0 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -1,22 +1,22 @@ #pragma once -#ifdef USE_ESP32_FRAMEWORK_ARDUINO - #include "esphome/core/component.h" #include "esphome/core/hal.h" #include "esphome/components/network/ip_address.h" +#ifdef USE_ESP32 + #include "esp_eth.h" -#include -#include -#include +#include "esp_eth_mac.h" +#include "esp_netif.h" namespace esphome { namespace ethernet { enum EthernetType { ETHERNET_TYPE_LAN8720 = 0, - ETHERNET_TYPE_TLK110, + ETHERNET_TYPE_RTL8201, + ETHERNET_TYPE_DP83848, ETHERNET_TYPE_IP101, }; @@ -45,11 +45,11 @@ class EthernetComponent : public Component { bool is_connected(); void set_phy_addr(uint8_t phy_addr); - void set_power_pin(GPIOPin *power_pin); + void set_power_pin(int power_pin); void set_mdc_pin(uint8_t mdc_pin); void set_mdio_pin(uint8_t mdio_pin); void set_type(EthernetType type); - void set_clk_mode(eth_clock_mode_t clk_mode); + void set_clk_mode(emac_rmii_clock_gpio_t clk_mode); void set_manual_ip(const ManualIP &manual_ip); network::IPAddress get_ip_address(); @@ -57,28 +57,27 @@ class EthernetComponent : public Component { void set_use_address(const std::string &use_address); protected: - void on_wifi_event_(system_event_id_t event, system_event_info_t info); + static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + void start_connect_(); void dump_connect_params_(); - static void eth_phy_config_gpio(); - static void eth_phy_power_enable(bool enable); - std::string use_address_; uint8_t phy_addr_{0}; - GPIOPin *power_pin_{nullptr}; + int power_pin_{-1}; uint8_t mdc_pin_{23}; uint8_t mdio_pin_{18}; EthernetType type_{ETHERNET_TYPE_LAN8720}; - eth_clock_mode_t clk_mode_{ETH_CLOCK_GPIO0_IN}; + emac_rmii_clock_gpio_t clk_mode_{EMAC_CLK_IN_GPIO}; optional manual_ip_{}; bool started_{false}; bool connected_{false}; EthernetComponentState state_{EthernetComponentState::STOPPED}; uint32_t connect_begin_; - eth_config_t eth_config_; - eth_phy_power_enable_func orig_power_enable_fun_; + esp_netif_t *eth_netif_{nullptr}; + esp_eth_handle_t eth_handle_; }; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) @@ -87,4 +86,4 @@ extern EthernetComponent *global_eth_component; } // namespace ethernet } // namespace esphome -#endif // USE_ESP32_FRAMEWORK_ARDUINO +#endif // USE_ESP32 diff --git a/esphome/components/ethernet_info/ethernet_info_text_sensor.h b/esphome/components/ethernet_info/ethernet_info_text_sensor.h index c683b68080..aad8f362b5 100644 --- a/esphome/components/ethernet_info/ethernet_info_text_sensor.h +++ b/esphome/components/ethernet_info/ethernet_info_text_sensor.h @@ -12,9 +12,7 @@ namespace ethernet_info { class IPAddressEthernetInfo : public PollingComponent, public text_sensor::TextSensor { public: void update() override { - tcpip_adapter_ip_info_t tcpip; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &tcpip); - auto ip = tcpip.ip.addr; + auto ip = ethernet::global_eth_component->get_ip_address(); if (ip != this->last_ip_) { this->last_ip_ = ip; this->publish_state(network::IPAddress(ip).str()); diff --git a/esphome/components/ezo/ezo.cpp b/esphome/components/ezo/ezo.cpp index 2efdc1b984..9d4343e004 100644 --- a/esphome/components/ezo/ezo.cpp +++ b/esphome/components/ezo/ezo.cpp @@ -14,8 +14,9 @@ static const char *const EZO_CALIBRATION_TYPE_STRINGS[] = {"LOW", "MID", "HIGH"} void EZOSensor::dump_config() { LOG_SENSOR("", "EZO", this); LOG_I2C_DEVICE(this); - if (this->is_failed()) + if (this->is_failed()) { ESP_LOGE(TAG, "Communication with EZO circuit failed!"); + } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/ezo_pmp/ezo_pmp.cpp b/esphome/components/ezo_pmp/ezo_pmp.cpp index 9c96ce2af0..6e5779a12e 100644 --- a/esphome/components/ezo_pmp/ezo_pmp.cpp +++ b/esphome/components/ezo_pmp/ezo_pmp.cpp @@ -40,8 +40,9 @@ static const std::string DOSING_MODE_CONTINUOUS = "Continuous"; void EzoPMP::dump_config() { LOG_I2C_DEVICE(this); - if (this->is_failed()) + if (this->is_failed()) { ESP_LOGE(TAG, "Communication with EZO-PMP circuit failed!"); + } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index 961ed6142a..c8a3064f6c 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -20,14 +20,18 @@ const LogString *fan_direction_to_string(FanDirection direction) { void FanCall::perform() { ESP_LOGD(TAG, "'%s' - Setting:", this->parent_.get_name().c_str()); this->validate_(); - if (this->binary_state_.has_value()) + if (this->binary_state_.has_value()) { ESP_LOGD(TAG, " State: %s", ONOFF(*this->binary_state_)); - if (this->oscillating_.has_value()) + } + if (this->oscillating_.has_value()) { ESP_LOGD(TAG, " Oscillating: %s", YESNO(*this->oscillating_)); - if (this->speed_.has_value()) + } + if (this->speed_.has_value()) { ESP_LOGD(TAG, " Speed: %d", *this->speed_); - if (this->direction_.has_value()) + } + if (this->direction_.has_value()) { ESP_LOGD(TAG, " Direction: %s", LOG_STR_ARG(fan_direction_to_string(*this->direction_))); + } this->parent_.control(*this); } @@ -90,12 +94,15 @@ void Fan::publish_state() { ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str()); ESP_LOGD(TAG, " State: %s", ONOFF(this->state)); - if (traits.supports_speed()) + if (traits.supports_speed()) { ESP_LOGD(TAG, " Speed: %d", this->speed); - if (traits.supports_oscillation()) + } + if (traits.supports_oscillation()) { ESP_LOGD(TAG, " Oscillating: %s", YESNO(this->oscillating)); - if (traits.supports_direction()) + } + if (traits.supports_direction()) { ESP_LOGD(TAG, " Direction: %s", LOG_STR_ARG(fan_direction_to_string(this->direction))); + } this->state_callback_.call(); this->save_state_(); @@ -147,10 +154,12 @@ void Fan::dump_traits_(const char *tag, const char *prefix) { ESP_LOGCONFIG(tag, "%s Speed: YES", prefix); ESP_LOGCONFIG(tag, "%s Speed count: %d", prefix, this->get_traits().supported_speed_count()); } - if (this->get_traits().supports_oscillation()) + if (this->get_traits().supports_oscillation()) { ESP_LOGCONFIG(tag, "%s Oscillation: YES", prefix); - if (this->get_traits().supports_direction()) + } + if (this->get_traits().supports_direction()) { ESP_LOGCONFIG(tag, "%s Direction: YES", prefix); + } } } // namespace fan diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.h b/esphome/components/fingerprint_grow/fingerprint_grow.h index e50418a768..96d02a1e8c 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.h +++ b/esphome/components/fingerprint_grow/fingerprint_grow.h @@ -6,6 +6,8 @@ #include "esphome/components/binary_sensor/binary_sensor.h" #include "esphome/components/uart/uart.h" +#include + namespace esphome { namespace fingerprint_grow { diff --git a/esphome/components/gpio/switch/__init__.py b/esphome/components/gpio/switch/__init__.py index f81cd5b6fb..9da6870a46 100644 --- a/esphome/components/gpio/switch/__init__.py +++ b/esphome/components/gpio/switch/__init__.py @@ -2,20 +2,10 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import switch -from esphome.const import CONF_INTERLOCK, CONF_PIN, CONF_RESTORE_MODE +from esphome.const import CONF_INTERLOCK, CONF_PIN from .. import gpio_ns GPIOSwitch = gpio_ns.class_("GPIOSwitch", switch.Switch, cg.Component) -GPIOSwitchRestoreMode = gpio_ns.enum("GPIOSwitchRestoreMode") - -RESTORE_MODES = { - "RESTORE_DEFAULT_OFF": GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_OFF, - "RESTORE_DEFAULT_ON": GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_ON, - "ALWAYS_OFF": GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_OFF, - "ALWAYS_ON": GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_ON, - "RESTORE_INVERTED_DEFAULT_OFF": GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_INVERTED_DEFAULT_OFF, - "RESTORE_INVERTED_DEFAULT_ON": GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_INVERTED_DEFAULT_ON, -} CONF_INTERLOCK_WAIT_TIME = "interlock_wait_time" CONFIG_SCHEMA = ( @@ -23,9 +13,6 @@ CONFIG_SCHEMA = ( .extend( { cv.Required(CONF_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum( - RESTORE_MODES, upper=True, space="_" - ), cv.Optional(CONF_INTERLOCK): cv.ensure_list(cv.use_id(switch.Switch)), cv.Optional( CONF_INTERLOCK_WAIT_TIME, default="0ms" @@ -43,8 +30,6 @@ async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_PIN]) cg.add(var.set_pin(pin)) - cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) - if CONF_INTERLOCK in config: interlock = [] for it in config[CONF_INTERLOCK]: diff --git a/esphome/components/gpio/switch/gpio_switch.cpp b/esphome/components/gpio/switch/gpio_switch.cpp index 714f2ea6d8..5033315b5e 100644 --- a/esphome/components/gpio/switch/gpio_switch.cpp +++ b/esphome/components/gpio/switch/gpio_switch.cpp @@ -10,27 +10,7 @@ float GPIOSwitch::get_setup_priority() const { return setup_priority::HARDWARE; void GPIOSwitch::setup() { ESP_LOGCONFIG(TAG, "Setting up GPIO Switch '%s'...", this->name_.c_str()); - bool initial_state = false; - switch (this->restore_mode_) { - case GPIO_SWITCH_RESTORE_DEFAULT_OFF: - initial_state = this->get_initial_state().value_or(false); - break; - case GPIO_SWITCH_RESTORE_DEFAULT_ON: - initial_state = this->get_initial_state().value_or(true); - break; - case GPIO_SWITCH_RESTORE_INVERTED_DEFAULT_OFF: - initial_state = !this->get_initial_state().value_or(true); - break; - case GPIO_SWITCH_RESTORE_INVERTED_DEFAULT_ON: - initial_state = !this->get_initial_state().value_or(false); - break; - case GPIO_SWITCH_ALWAYS_OFF: - initial_state = false; - break; - case GPIO_SWITCH_ALWAYS_ON: - initial_state = true; - break; - } + bool initial_state = this->get_initial_state_with_restore_mode().value_or(false); // write state before setup if (initial_state) { @@ -49,28 +29,6 @@ void GPIOSwitch::setup() { void GPIOSwitch::dump_config() { LOG_SWITCH("", "GPIO Switch", this); LOG_PIN(" Pin: ", this->pin_); - const LogString *restore_mode = LOG_STR(""); - switch (this->restore_mode_) { - case GPIO_SWITCH_RESTORE_DEFAULT_OFF: - restore_mode = LOG_STR("Restore (Defaults to OFF)"); - break; - case GPIO_SWITCH_RESTORE_DEFAULT_ON: - restore_mode = LOG_STR("Restore (Defaults to ON)"); - break; - case GPIO_SWITCH_RESTORE_INVERTED_DEFAULT_ON: - restore_mode = LOG_STR("Restore inverted (Defaults to ON)"); - break; - case GPIO_SWITCH_RESTORE_INVERTED_DEFAULT_OFF: - restore_mode = LOG_STR("Restore inverted (Defaults to OFF)"); - break; - case GPIO_SWITCH_ALWAYS_OFF: - restore_mode = LOG_STR("Always OFF"); - break; - case GPIO_SWITCH_ALWAYS_ON: - restore_mode = LOG_STR("Always ON"); - break; - } - ESP_LOGCONFIG(TAG, " Restore Mode: %s", LOG_STR_ARG(restore_mode)); if (!this->interlock_.empty()) { ESP_LOGCONFIG(TAG, " Interlocks:"); for (auto *lock : this->interlock_) { @@ -111,7 +69,6 @@ void GPIOSwitch::write_state(bool state) { this->pin_->digital_write(state); this->publish_state(state); } -void GPIOSwitch::set_restore_mode(GPIOSwitchRestoreMode restore_mode) { this->restore_mode_ = restore_mode; } void GPIOSwitch::set_interlock(const std::vector &interlock) { this->interlock_ = interlock; } } // namespace gpio diff --git a/esphome/components/gpio/switch/gpio_switch.h b/esphome/components/gpio/switch/gpio_switch.h index 99f8060efa..94d49745b5 100644 --- a/esphome/components/gpio/switch/gpio_switch.h +++ b/esphome/components/gpio/switch/gpio_switch.h @@ -4,24 +4,15 @@ #include "esphome/core/hal.h" #include "esphome/components/switch/switch.h" +#include + namespace esphome { namespace gpio { -enum GPIOSwitchRestoreMode { - GPIO_SWITCH_RESTORE_DEFAULT_OFF, - GPIO_SWITCH_RESTORE_DEFAULT_ON, - GPIO_SWITCH_ALWAYS_OFF, - GPIO_SWITCH_ALWAYS_ON, - GPIO_SWITCH_RESTORE_INVERTED_DEFAULT_OFF, - GPIO_SWITCH_RESTORE_INVERTED_DEFAULT_ON, -}; - class GPIOSwitch : public switch_::Switch, public Component { public: void set_pin(GPIOPin *pin) { pin_ = pin; } - void set_restore_mode(GPIOSwitchRestoreMode restore_mode); - // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) float get_setup_priority() const override; @@ -35,7 +26,6 @@ class GPIOSwitch : public switch_::Switch, public Component { void write_state(bool state) override; GPIOPin *pin_; - GPIOSwitchRestoreMode restore_mode_{GPIO_SWITCH_RESTORE_DEFAULT_OFF}; std::vector interlock_; uint32_t interlock_wait_time_{0}; }; diff --git a/esphome/components/gps/gps.h b/esphome/components/gps/gps.h index 40cda145ca..0626fb0b0e 100644 --- a/esphome/components/gps/gps.h +++ b/esphome/components/gps/gps.h @@ -7,6 +7,8 @@ #include "esphome/components/sensor/sensor.h" #include +#include + namespace esphome { namespace gps { diff --git a/esphome/components/graph/graph.h b/esphome/components/graph/graph.h index 15d2d1c7c4..69c1167f54 100644 --- a/esphome/components/graph/graph.h +++ b/esphome/components/graph/graph.h @@ -1,9 +1,10 @@ #pragma once +#include +#include +#include #include "esphome/components/sensor/sensor.h" #include "esphome/core/color.h" #include "esphome/core/component.h" -#include -#include namespace esphome { diff --git a/esphome/components/growatt_solar/growatt_solar.h b/esphome/components/growatt_solar/growatt_solar.h index 0067998133..d1b08b534a 100644 --- a/esphome/components/growatt_solar/growatt_solar.h +++ b/esphome/components/growatt_solar/growatt_solar.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" +#include + namespace esphome { namespace growatt_solar { diff --git a/esphome/components/havells_solar/havells_solar.h b/esphome/components/havells_solar/havells_solar.h index 2ccc8be3d4..f3ac8fafcf 100644 --- a/esphome/components/havells_solar/havells_solar.h +++ b/esphome/components/havells_solar/havells_solar.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" +#include + namespace esphome { namespace havells_solar { diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 4590163f2c..eab3045fdc 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -6,10 +6,12 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/defines.h" + #include #include -#include #include +#include +#include #ifdef USE_ESP32 #include diff --git a/esphome/components/i2s_audio/media_player.py b/esphome/components/i2s_audio/media_player.py index e2bdbe2d88..e80780b28f 100644 --- a/esphome/components/i2s_audio/media_player.py +++ b/esphome/components/i2s_audio/media_player.py @@ -90,5 +90,5 @@ async def to_code(config): if CORE.is_esp32: cg.add_library("WiFiClientSecure", None) cg.add_library("HTTPClient", None) - cg.add_library("esphome/ESP32-audioI2S", "2.1.0") + cg.add_library("esphome/ESP32-audioI2S", "2.0.6") cg.add_build_flag("-DAUDIO_NO_SD_FS") diff --git a/esphome/components/improv_serial/improv_serial_component.h b/esphome/components/improv_serial/improv_serial_component.h index 6be5704b71..3d8478252d 100644 --- a/esphome/components/improv_serial/improv_serial_component.h +++ b/esphome/components/improv_serial/improv_serial_component.h @@ -6,6 +6,7 @@ #include "esphome/core/helpers.h" #include +#include #ifdef USE_ARDUINO #include diff --git a/esphome/components/lcd_base/lcd_display.h b/esphome/components/lcd_base/lcd_display.h index c8ba39f0d4..c1dc54a9ed 100644 --- a/esphome/components/lcd_base/lcd_display.h +++ b/esphome/components/lcd_base/lcd_display.h @@ -8,6 +8,7 @@ #endif #include +#include namespace esphome { namespace lcd_base { diff --git a/esphome/components/light/addressable_light_effect.h b/esphome/components/light/addressable_light_effect.h index d404898edf..0482cf53b9 100644 --- a/esphome/components/light/addressable_light_effect.h +++ b/esphome/components/light/addressable_light_effect.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "esphome/core/component.h" #include "esphome/components/light/light_state.h" diff --git a/esphome/components/light/base_light_effects.h b/esphome/components/light/base_light_effects.h index a6ab299308..6291aa0610 100644 --- a/esphome/components/light/base_light_effects.h +++ b/esphome/components/light/base_light_effects.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "light_effect.h" #include "esphome/core/automation.h" diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index 2834836c0e..81f8be7207 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -10,6 +10,8 @@ #include "light_traits.h" #include "light_transformer.h" +#include + namespace esphome { namespace light { diff --git a/esphome/components/lilygo_t5_47/touchscreen/__init__.py b/esphome/components/lilygo_t5_47/touchscreen/__init__.py index 9ec6c925ee..fe94120644 100644 --- a/esphome/components/lilygo_t5_47/touchscreen/__init__.py +++ b/esphome/components/lilygo_t5_47/touchscreen/__init__.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import i2c, touchscreen -from esphome.const import CONF_ID +from esphome.const import CONF_ID, CONF_INTERRUPT_PIN from .. import lilygo_t5_47_ns @@ -18,8 +18,6 @@ LilygoT547Touchscreen = lilygo_t5_47_ns.class_( ) CONF_LILYGO_T5_47_TOUCHSCREEN_ID = "lilygo_t5_47_touchscreen_id" -CONF_INTERRUPT_PIN = "interrupt_pin" - CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend( cv.Schema( diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index 0e12acacd8..88d5e8ee97 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -1,10 +1,11 @@ #pragma once +#include +#include #include "esphome/core/automation.h" #include "esphome/core/component.h" -#include "esphome/core/helpers.h" #include "esphome/core/defines.h" -#include +#include "esphome/core/helpers.h" #ifdef USE_ARDUINO #if defined(USE_ESP8266) || defined(USE_ESP32) diff --git a/esphome/components/ltr390/ltr390.h b/esphome/components/ltr390/ltr390.h index d607a3e55f..1bb7a8fa22 100644 --- a/esphome/components/ltr390/ltr390.h +++ b/esphome/components/ltr390/ltr390.h @@ -1,10 +1,11 @@ #pragma once +#include +#include +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/sensor/sensor.h" #include "esphome/core/component.h" #include "esphome/core/optional.h" -#include "esphome/components/sensor/sensor.h" -#include "esphome/components/i2c/i2c.h" -#include namespace esphome { namespace ltr390 { diff --git a/esphome/components/max7219digit/max7219digit.h b/esphome/components/max7219digit/max7219digit.h index 3552c6b9d5..3619478697 100644 --- a/esphome/components/max7219digit/max7219digit.h +++ b/esphome/components/max7219digit/max7219digit.h @@ -5,6 +5,8 @@ #include "esphome/components/display/display_buffer.h" #include "esphome/components/spi/spi.h" +#include + #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" #endif diff --git a/esphome/components/modbus/modbus.h b/esphome/components/modbus/modbus.h index 629ab6dcce..95414ba090 100644 --- a/esphome/components/modbus/modbus.h +++ b/esphome/components/modbus/modbus.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/components/uart/uart.h" +#include + namespace esphome { namespace modbus { diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 41beb67e1c..46bb2c4233 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -24,7 +24,6 @@ AUTO_LOAD = ["modbus"] MULTI_CONF = True -# pylint: disable=invalid-name modbus_controller_ns = cg.esphome_ns.namespace("modbus_controller") ModbusController = modbus_controller_ns.class_( "ModbusController", cg.PollingComponent, modbus.ModbusDevice diff --git a/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h index 3a8e175c26..3782416d4f 100644 --- a/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h +++ b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h @@ -4,6 +4,8 @@ #include "esphome/components/modbus_controller/modbus_controller.h" #include "esphome/core/component.h" +#include + namespace esphome { namespace modbus_controller { diff --git a/esphome/components/modbus_controller/number/modbus_number.h b/esphome/components/modbus_controller/number/modbus_number.h index aa5c8d1500..9b447d831c 100644 --- a/esphome/components/modbus_controller/number/modbus_number.h +++ b/esphome/components/modbus_controller/number/modbus_number.h @@ -4,6 +4,8 @@ #include "esphome/components/modbus_controller/modbus_controller.h" #include "esphome/core/component.h" +#include + namespace esphome { namespace modbus_controller { diff --git a/esphome/components/modbus_controller/output/modbus_output.cpp b/esphome/components/modbus_controller/output/modbus_output.cpp index b647312f52..79cd2d49c2 100644 --- a/esphome/components/modbus_controller/output/modbus_output.cpp +++ b/esphome/components/modbus_controller/output/modbus_output.cpp @@ -1,7 +1,6 @@ -#include #include "modbus_output.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace modbus_controller { diff --git a/esphome/components/modbus_controller/output/modbus_output.h b/esphome/components/modbus_controller/output/modbus_output.h index f089775c0c..f424671cd1 100644 --- a/esphome/components/modbus_controller/output/modbus_output.h +++ b/esphome/components/modbus_controller/output/modbus_output.h @@ -4,6 +4,8 @@ #include "esphome/components/modbus_controller/modbus_controller.h" #include "esphome/core/component.h" +#include + namespace esphome { namespace modbus_controller { diff --git a/esphome/components/modbus_controller/select/modbus_select.h b/esphome/components/modbus_controller/select/modbus_select.h index 2a31dfd7cc..ffbbba390b 100644 --- a/esphome/components/modbus_controller/select/modbus_select.h +++ b/esphome/components/modbus_controller/select/modbus_select.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "esphome/components/modbus_controller/modbus_controller.h" #include "esphome/components/select/select.h" diff --git a/esphome/components/modbus_controller/sensor/modbus_sensor.h b/esphome/components/modbus_controller/sensor/modbus_sensor.h index ababcc33d2..848d5f63de 100644 --- a/esphome/components/modbus_controller/sensor/modbus_sensor.h +++ b/esphome/components/modbus_controller/sensor/modbus_sensor.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/core/component.h" +#include + namespace esphome { namespace modbus_controller { diff --git a/esphome/components/modbus_controller/switch/__init__.py b/esphome/components/modbus_controller/switch/__init__.py index 9673a066e3..9490325968 100644 --- a/esphome/components/modbus_controller/switch/__init__.py +++ b/esphome/components/modbus_controller/switch/__init__.py @@ -32,7 +32,7 @@ ModbusSwitch = modbus_controller_ns.class_( ) CONFIG_SCHEMA = cv.All( - switch.switch_schema(ModbusSwitch) + switch.switch_schema(ModbusSwitch, default_restore_mode="DISABLED") .extend(cv.COMPONENT_SCHEMA) .extend(ModbusItemBaseSchema) .extend( diff --git a/esphome/components/modbus_controller/switch/modbus_switch.cpp b/esphome/components/modbus_controller/switch/modbus_switch.cpp index ca8d0be720..3a679fbeb8 100644 --- a/esphome/components/modbus_controller/switch/modbus_switch.cpp +++ b/esphome/components/modbus_controller/switch/modbus_switch.cpp @@ -7,9 +7,15 @@ namespace modbus_controller { static const char *const TAG = "modbus_controller.switch"; void ModbusSwitch::setup() { - // value isn't required - // without it we crash on save - this->get_initial_state(); + optional initial_state = Switch::get_initial_state_with_restore_mode(); + if (initial_state.has_value()) { + // if it has a value, restore_mode is not "DISABLED", therefore act on the switch: + if (initial_state.value()) { + this->turn_on(); + } else { + this->turn_off(); + } + } } void ModbusSwitch::dump_config() { LOG_SWITCH(TAG, "Modbus Controller Switch", this); } diff --git a/esphome/components/modbus_controller/switch/modbus_switch.h b/esphome/components/modbus_controller/switch/modbus_switch.h index eccfc3b64a..0f2d8f6e59 100644 --- a/esphome/components/modbus_controller/switch/modbus_switch.h +++ b/esphome/components/modbus_controller/switch/modbus_switch.h @@ -4,6 +4,8 @@ #include "esphome/components/switch/switch.h" #include "esphome/core/component.h" +#include + namespace esphome { namespace modbus_controller { diff --git a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h index c52c6cd072..2e3be72034 100644 --- a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h +++ b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h @@ -4,6 +4,8 @@ #include "esphome/components/text_sensor/text_sensor.h" #include "esphome/core/component.h" +#include + namespace esphome { namespace modbus_controller { diff --git a/esphome/components/mopeka_ble/mopeka_ble.h b/esphome/components/mopeka_ble/mopeka_ble.h index 7b797a3bbe..f88bad4f3a 100644 --- a/esphome/components/mopeka_ble/mopeka_ble.h +++ b/esphome/components/mopeka_ble/mopeka_ble.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include + #ifdef USE_ESP32 namespace esphome { diff --git a/esphome/components/mopeka_pro_check/mopeka_pro_check.h b/esphome/components/mopeka_pro_check/mopeka_pro_check.h index dfdce9353e..e6cc1fd6f1 100644 --- a/esphome/components/mopeka_pro_check/mopeka_pro_check.h +++ b/esphome/components/mopeka_pro_check/mopeka_pro_check.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include + #ifdef USE_ESP32 namespace esphome { diff --git a/esphome/components/mpr121/mpr121.h b/esphome/components/mpr121/mpr121.h index 3388f04926..8b7735fa28 100644 --- a/esphome/components/mpr121/mpr121.h +++ b/esphome/components/mpr121/mpr121.h @@ -4,6 +4,8 @@ #include "esphome/components/i2c/i2c.h" #include "esphome/components/binary_sensor/binary_sensor.h" +#include + namespace esphome { namespace mpr121 { diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 0310655146..188a027b91 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -16,6 +16,8 @@ #endif #include "lwip/ip_addr.h" +#include + namespace esphome { namespace mqtt { diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp index 7018792283..3a6ea97967 100644 --- a/esphome/components/mqtt/mqtt_number.cpp +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -55,6 +55,8 @@ void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon root[MQTT_MODE] = "slider"; break; } + if (!this->number_->traits.get_device_class().empty()) + root[MQTT_DEVICE_CLASS] = this->number_->traits.get_device_class(); config.command_topic = true; } diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 5dfff4327c..f8beaeab78 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -256,8 +256,9 @@ void Nextion::loop() { bool Nextion::remove_from_q_(bool report_empty) { if (this->nextion_queue_.empty()) { - if (report_empty) + if (report_empty) { ESP_LOGE(TAG, "Nextion queue is empty!"); + } return false; } diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index ad696d0e83..86610ef564 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -1,6 +1,8 @@ #pragma once #include +#include + #include "esphome/core/defines.h" #include "esphome/components/uart/uart.h" #include "nextion_base.h" diff --git a/esphome/components/nextion/nextion_component_base.h b/esphome/components/nextion/nextion_component_base.h index 71ad803bc4..e0ef8f93bc 100644 --- a/esphome/components/nextion/nextion_component_base.h +++ b/esphome/components/nextion/nextion_component_base.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include "esphome/core/defines.h" namespace esphome { diff --git a/esphome/components/nfc/ndef_message.h b/esphome/components/nfc/ndef_message.h index 5e44a06011..2e81fb216c 100644 --- a/esphome/components/nfc/ndef_message.h +++ b/esphome/components/nfc/ndef_message.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "esphome/core/helpers.h" #include "esphome/core/log.h" diff --git a/esphome/components/nfc/ndef_record.h b/esphome/components/nfc/ndef_record.h index 4fab1c03e4..27083fdaca 100644 --- a/esphome/components/nfc/ndef_record.h +++ b/esphome/components/nfc/ndef_record.h @@ -3,6 +3,8 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include + namespace esphome { namespace nfc { diff --git a/esphome/components/nfc/ndef_record_text.h b/esphome/components/nfc/ndef_record_text.h index 94375cc860..aa8f13bb4b 100644 --- a/esphome/components/nfc/ndef_record_text.h +++ b/esphome/components/nfc/ndef_record_text.h @@ -4,6 +4,8 @@ #include "esphome/core/helpers.h" #include "ndef_record.h" +#include + namespace esphome { namespace nfc { diff --git a/esphome/components/nfc/ndef_record_uri.h b/esphome/components/nfc/ndef_record_uri.h index 4c21724c5c..fc8f2d9a73 100644 --- a/esphome/components/nfc/ndef_record_uri.h +++ b/esphome/components/nfc/ndef_record_uri.h @@ -4,6 +4,8 @@ #include "esphome/core/helpers.h" #include "ndef_record.h" +#include + namespace esphome { namespace nfc { diff --git a/esphome/components/nfc/nfc.h b/esphome/components/nfc/nfc.h index d482131057..1a4e8f1c1d 100644 --- a/esphome/components/nfc/nfc.h +++ b/esphome/components/nfc/nfc.h @@ -6,6 +6,8 @@ #include "ndef_message.h" #include "nfc_tag.h" +#include + namespace esphome { namespace nfc { diff --git a/esphome/components/nfc/nfc_tag.h b/esphome/components/nfc/nfc_tag.h index 2dfc431428..58875a744d 100644 --- a/esphome/components/nfc/nfc_tag.h +++ b/esphome/components/nfc/nfc_tag.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "esphome/core/log.h" #include "esphome/core/helpers.h" diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index f809fff529..ec4dead42e 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -6,6 +6,7 @@ from esphome.components import mqtt from esphome.const import ( CONF_ABOVE, CONF_BELOW, + CONF_DEVICE_CLASS, CONF_ID, CONF_MODE, CONF_ON_VALUE, @@ -16,11 +17,87 @@ from esphome.const import ( CONF_VALUE, CONF_OPERATION, CONF_CYCLE, + DEVICE_CLASS_DISTANCE, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_APPARENT_POWER, + DEVICE_CLASS_AQI, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_CARBON_DIOXIDE, + DEVICE_CLASS_CARBON_MONOXIDE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_ENERGY, + DEVICE_CLASS_FREQUENCY, + DEVICE_CLASS_GAS, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_ILLUMINANCE, + DEVICE_CLASS_MOISTURE, + DEVICE_CLASS_MONETARY, + DEVICE_CLASS_NITROGEN_DIOXIDE, + DEVICE_CLASS_NITROGEN_MONOXIDE, + DEVICE_CLASS_NITROUS_OXIDE, + DEVICE_CLASS_OZONE, + DEVICE_CLASS_PM1, + DEVICE_CLASS_PM10, + DEVICE_CLASS_PM25, + DEVICE_CLASS_POWER, + DEVICE_CLASS_POWER_FACTOR, + DEVICE_CLASS_PRECIPITATION_INTENSITY, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_REACTIVE_POWER, + DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_SPEED, + DEVICE_CLASS_SULPHUR_DIOXIDE, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + DEVICE_CLASS_VOLTAGE, + DEVICE_CLASS_VOLUME, + DEVICE_CLASS_WATER, + DEVICE_CLASS_WIND_SPEED, + DEVICE_CLASS_WEIGHT, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_helpers import setup_entity CODEOWNERS = ["@esphome/core"] +DEVICE_CLASSES = [ + DEVICE_CLASS_APPARENT_POWER, + DEVICE_CLASS_AQI, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_CARBON_DIOXIDE, + DEVICE_CLASS_CARBON_MONOXIDE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_DISTANCE, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_ENERGY, + DEVICE_CLASS_FREQUENCY, + DEVICE_CLASS_GAS, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_ILLUMINANCE, + DEVICE_CLASS_MOISTURE, + DEVICE_CLASS_MONETARY, + DEVICE_CLASS_NITROGEN_DIOXIDE, + DEVICE_CLASS_NITROGEN_MONOXIDE, + DEVICE_CLASS_NITROUS_OXIDE, + DEVICE_CLASS_OZONE, + DEVICE_CLASS_PM1, + DEVICE_CLASS_PM10, + DEVICE_CLASS_PM25, + DEVICE_CLASS_POWER_FACTOR, + DEVICE_CLASS_POWER, + DEVICE_CLASS_PRECIPITATION_INTENSITY, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_REACTIVE_POWER, + DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_SPEED, + DEVICE_CLASS_SULPHUR_DIOXIDE, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + DEVICE_CLASS_VOLTAGE, + DEVICE_CLASS_VOLUME, + DEVICE_CLASS_WATER, + DEVICE_CLASS_WEIGHT, + DEVICE_CLASS_WIND_SPEED, +] IS_PLATFORM_COMPONENT = True number_ns = cg.esphome_ns.namespace("number") @@ -62,6 +139,7 @@ NUMBER_OPERATION_OPTIONS = { } icon = cv.icon +validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") NUMBER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { @@ -82,6 +160,7 @@ NUMBER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).e ), cv.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string_strict, cv.Optional(CONF_MODE, default="AUTO"): cv.enum(NUMBER_MODES, upper=True), + cv.Optional(CONF_DEVICE_CLASS): validate_device_class, } ) @@ -117,6 +196,8 @@ async def setup_number_core_( if CONF_MQTT_ID in config: mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) await mqtt.register_mqtt_component(mqtt_, config) + if CONF_DEVICE_CLASS in config: + cg.add(var.traits.set_device_class(config[CONF_DEVICE_CLASS])) async def register_number( diff --git a/esphome/components/number/number.h b/esphome/components/number/number.h index 7f360cad7b..4f63e0480c 100644 --- a/esphome/components/number/number.h +++ b/esphome/components/number/number.h @@ -18,6 +18,9 @@ namespace number { if (!(obj)->traits.get_unit_of_measurement().empty()) { \ ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->traits.get_unit_of_measurement().c_str()); \ } \ + if (!(obj)->traits.get_device_class().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->traits.get_device_class().c_str()); \ + } \ } class Number; diff --git a/esphome/components/number/number_traits.cpp b/esphome/components/number/number_traits.cpp index dcd05daa2a..1554f8d9c9 100644 --- a/esphome/components/number/number_traits.cpp +++ b/esphome/components/number/number_traits.cpp @@ -16,5 +16,13 @@ std::string NumberTraits::get_unit_of_measurement() { return ""; } +void NumberTraits::set_device_class(const std::string &device_class) { this->device_class_ = device_class; } + +std::string NumberTraits::get_device_class() { + if (this->device_class_.has_value()) + return *this->device_class_; + return ""; +} + } // namespace number } // namespace esphome diff --git a/esphome/components/number/number_traits.h b/esphome/components/number/number_traits.h index 47756ff66f..ee10b0010c 100644 --- a/esphome/components/number/number_traits.h +++ b/esphome/components/number/number_traits.h @@ -32,12 +32,17 @@ class NumberTraits { void set_mode(NumberMode mode) { this->mode_ = mode; } NumberMode get_mode() const { return this->mode_; } + // Set/get the device class. + void set_device_class(const std::string &device_class); + std::string get_device_class(); + protected: float min_value_ = NAN; float max_value_ = NAN; float step_ = NAN; optional unit_of_measurement_; ///< Unit of measurement override NumberMode mode_{NUMBER_MODE_AUTO}; + optional device_class_; }; } // namespace number diff --git a/esphome/components/ota/ota_backend_esp_idf.cpp b/esphome/components/ota/ota_backend_esp_idf.cpp index 167f8c059b..2fdc00c54d 100644 --- a/esphome/components/ota/ota_backend_esp_idf.cpp +++ b/esphome/components/ota/ota_backend_esp_idf.cpp @@ -1,6 +1,8 @@ #include "esphome/core/defines.h" #ifdef USE_ESP_IDF +#include + #include "ota_backend_esp_idf.h" #include "ota_component.h" #include @@ -14,7 +16,9 @@ OTAResponseTypes IDFOTABackend::begin(size_t image_size) { if (this->partition_ == nullptr) { return OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION; } + esp_task_wdt_init(15, false); // The following function takes longer than the 5 seconds timeout of WDT esp_err_t err = esp_ota_begin(this->partition_, image_size, &this->update_handle_); + esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false); // Set the WDT back to the configured timeout if (err != ESP_OK) { esp_ota_abort(this->update_handle_); this->update_handle_ = 0; diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 1f1ecd9867..0195cb4616 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -472,8 +472,9 @@ bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_ if (this->safe_mode_rtc_value_ >= num_attempts || is_manual_safe_mode) { this->clean_rtc(); - if (!is_manual_safe_mode) + if (!is_manual_safe_mode) { ESP_LOGE(TAG, "Boot loop detected. Proceeding to safe mode."); + } this->status_set_error(); this->set_timeout(enable_time, []() { diff --git a/esphome/components/output/switch/__init__.py b/esphome/components/output/switch/__init__.py index 3a23c1e33f..f5325643a7 100644 --- a/esphome/components/output/switch/__init__.py +++ b/esphome/components/output/switch/__init__.py @@ -1,29 +1,19 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import output, switch -from esphome.const import CONF_OUTPUT, CONF_RESTORE_MODE +from esphome.const import CONF_OUTPUT from .. import output_ns OutputSwitch = output_ns.class_("OutputSwitch", switch.Switch, cg.Component) OutputSwitchRestoreMode = output_ns.enum("OutputSwitchRestoreMode") -RESTORE_MODES = { - "RESTORE_DEFAULT_OFF": OutputSwitchRestoreMode.OUTPUT_SWITCH_RESTORE_DEFAULT_OFF, - "RESTORE_DEFAULT_ON": OutputSwitchRestoreMode.OUTPUT_SWITCH_RESTORE_DEFAULT_ON, - "ALWAYS_OFF": OutputSwitchRestoreMode.OUTPUT_SWITCH_ALWAYS_OFF, - "ALWAYS_ON": OutputSwitchRestoreMode.OUTPUT_SWITCH_ALWAYS_ON, - "RESTORE_INVERTED_DEFAULT_OFF": OutputSwitchRestoreMode.OUTPUT_SWITCH_RESTORE_INVERTED_DEFAULT_OFF, - "RESTORE_INVERTED_DEFAULT_ON": OutputSwitchRestoreMode.OUTPUT_SWITCH_RESTORE_INVERTED_DEFAULT_ON, -} + CONFIG_SCHEMA = ( switch.switch_schema(OutputSwitch) .extend( { cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), - cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum( - RESTORE_MODES, upper=True, space="_" - ), } ) .extend(cv.COMPONENT_SCHEMA) @@ -36,5 +26,3 @@ async def to_code(config): output_ = await cg.get_variable(config[CONF_OUTPUT]) cg.add(var.set_output(output_)) - - cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) diff --git a/esphome/components/output/switch/output_switch.cpp b/esphome/components/output/switch/output_switch.cpp index ec9c8afc01..0e045d18b4 100644 --- a/esphome/components/output/switch/output_switch.cpp +++ b/esphome/components/output/switch/output_switch.cpp @@ -8,27 +8,9 @@ static const char *const TAG = "output.switch"; void OutputSwitch::dump_config() { LOG_SWITCH("", "Output Switch", this); } void OutputSwitch::setup() { - bool initial_state = false; - switch (this->restore_mode_) { - case OUTPUT_SWITCH_RESTORE_DEFAULT_OFF: - initial_state = this->get_initial_state().value_or(false); - break; - case OUTPUT_SWITCH_RESTORE_DEFAULT_ON: - initial_state = this->get_initial_state().value_or(true); - break; - case OUTPUT_SWITCH_RESTORE_INVERTED_DEFAULT_OFF: - initial_state = !this->get_initial_state().value_or(true); - break; - case OUTPUT_SWITCH_RESTORE_INVERTED_DEFAULT_ON: - initial_state = !this->get_initial_state().value_or(false); - break; - case OUTPUT_SWITCH_ALWAYS_OFF: - initial_state = false; - break; - case OUTPUT_SWITCH_ALWAYS_ON: - initial_state = true; - break; - } + ESP_LOGCONFIG(TAG, "Setting up Output Switch '%s'...", this->name_.c_str()); + + bool initial_state = this->get_initial_state_with_restore_mode().value_or(false); if (initial_state) { this->turn_on(); diff --git a/esphome/components/output/switch/output_switch.h b/esphome/components/output/switch/output_switch.h index fc2c110662..a184a342fe 100644 --- a/esphome/components/output/switch/output_switch.h +++ b/esphome/components/output/switch/output_switch.h @@ -7,21 +7,10 @@ namespace esphome { namespace output { -enum OutputSwitchRestoreMode { - OUTPUT_SWITCH_RESTORE_DEFAULT_OFF, - OUTPUT_SWITCH_RESTORE_DEFAULT_ON, - OUTPUT_SWITCH_ALWAYS_OFF, - OUTPUT_SWITCH_ALWAYS_ON, - OUTPUT_SWITCH_RESTORE_INVERTED_DEFAULT_OFF, - OUTPUT_SWITCH_RESTORE_INVERTED_DEFAULT_ON, -}; - class OutputSwitch : public switch_::Switch, public Component { public: void set_output(BinaryOutput *output) { output_ = output; } - void set_restore_mode(OutputSwitchRestoreMode restore_mode) { restore_mode_ = restore_mode; } - void setup() override; float get_setup_priority() const override { return setup_priority::HARDWARE - 1.0f; } void dump_config() override; @@ -30,7 +19,6 @@ class OutputSwitch : public switch_::Switch, public Component { void write_state(bool state) override; output::BinaryOutput *output_; - OutputSwitchRestoreMode restore_mode_; }; } // namespace output diff --git a/esphome/components/packages/__init__.py b/esphome/components/packages/__init__.py index 3b5a6a5908..8392008222 100644 --- a/esphome/components/packages/__init__.py +++ b/esphome/components/packages/__init__.py @@ -1,23 +1,22 @@ -import re from pathlib import Path -from esphome.core import EsphomeError -from esphome.config_helpers import merge_config +import esphome.config_validation as cv from esphome import git, yaml_util +from esphome.config_helpers import merge_config from esphome.const import ( CONF_ESPHOME, CONF_FILE, CONF_FILES, CONF_MIN_VERSION, CONF_PACKAGES, + CONF_PASSWORD, CONF_REF, CONF_REFRESH, CONF_URL, CONF_USERNAME, - CONF_PASSWORD, - __version__ as ESPHOME_VERSION, ) -import esphome.config_validation as cv +from esphome.const import __version__ as ESPHOME_VERSION +from esphome.core import EsphomeError DOMAIN = CONF_PACKAGES @@ -55,23 +54,15 @@ def validate_source_shorthand(value): if not isinstance(value, str): raise cv.Invalid("Shorthand only for strings") - m = re.match( - r"github://([a-zA-Z0-9\-]+)/([a-zA-Z0-9\-\._]+)/([a-zA-Z0-9\-_.\./]+)(?:@([a-zA-Z0-9\-_.\./]+))?", - value, - ) - if m is None: - raise cv.Invalid( - "Source is not a file system path or in expected github://username/name/[sub-folder/]file-path.yml[@branch-or-tag] format!" - ) + git_file = git.GitFile.from_shorthand(value) conf = { - CONF_URL: f"https://github.com/{m.group(1)}/{m.group(2)}.git", - CONF_FILE: m.group(3), + CONF_URL: git_file.git_url, + CONF_FILE: git_file.filename, } - if m.group(4): - conf[CONF_REF] = m.group(4) + if git_file.ref: + conf[CONF_REF] = git_file.ref - # print(conf) return BASE_SCHEMA(conf) diff --git a/esphome/components/partition/light_partition.h b/esphome/components/partition/light_partition.h index 5790fd2b39..bd90b4c4f1 100644 --- a/esphome/components/partition/light_partition.h +++ b/esphome/components/partition/light_partition.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "esphome/core/component.h" #include "esphome/components/light/addressable_light.h" diff --git a/esphome/components/pid/climate.py b/esphome/components/pid/climate.py index ffc6392ec2..7cd414f912 100644 --- a/esphome/components/pid/climate.py +++ b/esphome/components/pid/climate.py @@ -18,6 +18,7 @@ CONF_DEFAULT_TARGET_TEMPERATURE = "default_target_temperature" CONF_KP = "kp" CONF_KI = "ki" +CONF_STARTING_INTEGRAL_TERM = "starting_integral_term" CONF_KD = "kd" CONF_CONTROL_PARAMETERS = "control_parameters" CONF_COOL_OUTPUT = "cool_output" @@ -27,6 +28,17 @@ CONF_POSITIVE_OUTPUT = "positive_output" CONF_NEGATIVE_OUTPUT = "negative_output" CONF_MIN_INTEGRAL = "min_integral" CONF_MAX_INTEGRAL = "max_integral" +CONF_OUTPUT_AVERAGING_SAMPLES = "output_averaging_samples" +CONF_DERIVATIVE_AVERAGING_SAMPLES = "derivative_averaging_samples" + +# Deadband parameters +CONF_DEADBAND_PARAMETERS = "deadband_parameters" +CONF_THRESHOLD_HIGH = "threshold_high" +CONF_THRESHOLD_LOW = "threshold_low" +CONF_DEADBAND_OUTPUT_AVERAGING_SAMPLES = "deadband_output_averaging_samples" +CONF_KP_MULTIPLIER = "kp_multiplier" +CONF_KI_MULTIPLIER = "ki_multiplier" +CONF_KD_MULTIPLIER = "kd_multiplier" CONFIG_SCHEMA = cv.All( climate.CLIMATE_SCHEMA.extend( @@ -36,13 +48,28 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE): cv.temperature, cv.Optional(CONF_COOL_OUTPUT): cv.use_id(output.FloatOutput), cv.Optional(CONF_HEAT_OUTPUT): cv.use_id(output.FloatOutput), + cv.Optional(CONF_DEADBAND_PARAMETERS): cv.Schema( + { + cv.Required(CONF_THRESHOLD_HIGH): cv.temperature, + cv.Required(CONF_THRESHOLD_LOW): cv.temperature, + cv.Optional(CONF_KP_MULTIPLIER, default=0.1): cv.float_, + cv.Optional(CONF_KI_MULTIPLIER, default=0.0): cv.float_, + cv.Optional(CONF_KD_MULTIPLIER, default=0.0): cv.float_, + cv.Optional( + CONF_DEADBAND_OUTPUT_AVERAGING_SAMPLES, default=1 + ): cv.int_, + } + ), cv.Required(CONF_CONTROL_PARAMETERS): cv.Schema( { cv.Required(CONF_KP): cv.float_, cv.Optional(CONF_KI, default=0.0): cv.float_, cv.Optional(CONF_KD, default=0.0): cv.float_, + cv.Optional(CONF_STARTING_INTEGRAL_TERM, default=0.0): cv.float_, cv.Optional(CONF_MIN_INTEGRAL, default=-1): cv.float_, cv.Optional(CONF_MAX_INTEGRAL, default=1): cv.float_, + cv.Optional(CONF_DERIVATIVE_AVERAGING_SAMPLES, default=1): cv.int_, + cv.Optional(CONF_OUTPUT_AVERAGING_SAMPLES, default=1): cv.int_, } ), } @@ -69,11 +96,29 @@ async def to_code(config): cg.add(var.set_kp(params[CONF_KP])) cg.add(var.set_ki(params[CONF_KI])) cg.add(var.set_kd(params[CONF_KD])) + cg.add(var.set_starting_integral_term(params[CONF_STARTING_INTEGRAL_TERM])) + cg.add(var.set_derivative_samples(params[CONF_DERIVATIVE_AVERAGING_SAMPLES])) + + cg.add(var.set_output_samples(params[CONF_OUTPUT_AVERAGING_SAMPLES])) + if CONF_MIN_INTEGRAL in params: cg.add(var.set_min_integral(params[CONF_MIN_INTEGRAL])) if CONF_MAX_INTEGRAL in params: cg.add(var.set_max_integral(params[CONF_MAX_INTEGRAL])) + if CONF_DEADBAND_PARAMETERS in config: + params = config[CONF_DEADBAND_PARAMETERS] + cg.add(var.set_threshold_low(params[CONF_THRESHOLD_LOW])) + cg.add(var.set_threshold_high(params[CONF_THRESHOLD_HIGH])) + cg.add(var.set_kp_multiplier(params[CONF_KP_MULTIPLIER])) + cg.add(var.set_ki_multiplier(params[CONF_KI_MULTIPLIER])) + cg.add(var.set_kd_multiplier(params[CONF_KD_MULTIPLIER])) + cg.add( + var.set_deadband_output_samples( + params[CONF_DEADBAND_OUTPUT_AVERAGING_SAMPLES] + ) + ) + cg.add(var.set_default_target_temperature(config[CONF_DEFAULT_TARGET_TEMPERATURE])) @@ -140,4 +185,5 @@ async def set_control_parameters(config, action_id, template_arg, args): kd_template_ = await cg.templatable(config[CONF_KD], args, float) cg.add(var.set_kd(kd_template_)) + return var diff --git a/esphome/components/pid/pid_autotuner.h b/esphome/components/pid/pid_autotuner.h index 7dfe0c056d..88716d2b89 100644 --- a/esphome/components/pid/pid_autotuner.h +++ b/esphome/components/pid/pid_autotuner.h @@ -5,6 +5,8 @@ #include "pid_controller.h" #include "pid_simulator.h" +#include + namespace esphome { namespace pid { diff --git a/esphome/components/pid/pid_climate.cpp b/esphome/components/pid/pid_climate.cpp index 3f8377c720..81c3e1f12e 100644 --- a/esphome/components/pid/pid_climate.cpp +++ b/esphome/components/pid/pid_climate.cpp @@ -61,7 +61,17 @@ climate::ClimateTraits PIDClimate::traits() { void PIDClimate::dump_config() { LOG_CLIMATE("", "PID Climate", this); ESP_LOGCONFIG(TAG, " Control Parameters:"); - ESP_LOGCONFIG(TAG, " kp: %.5f, ki: %.5f, kd: %.5f", controller_.kp, controller_.ki, controller_.kd); + ESP_LOGCONFIG(TAG, " kp: %.5f, ki: %.5f, kd: %.5f, output samples: %d", controller_.kp_, controller_.ki_, + controller_.kd_, controller_.output_samples_); + + if (controller_.threshold_low_ == 0 && controller_.threshold_high_ == 0) { + ESP_LOGCONFIG(TAG, " Deadband disabled."); + } else { + ESP_LOGCONFIG(TAG, " Deadband Parameters:"); + ESP_LOGCONFIG(TAG, " threshold: %0.5f to %0.5f, multipliers(kp: %.5f, ki: %.5f, kd: %.5f), output samples: %d", + controller_.threshold_low_, controller_.threshold_high_, controller_.kp_multiplier_, + controller_.ki_multiplier_, controller_.kd_multiplier_, controller_.deadband_output_samples_); + } if (this->autotuner_ != nullptr) { this->autotuner_->dump_config(); @@ -114,9 +124,9 @@ void PIDClimate::update_pid_() { if (this->autotuner_ != nullptr && !this->autotuner_->is_finished()) { auto res = this->autotuner_->update(this->target_temperature, this->current_temperature); if (res.result_params.has_value()) { - this->controller_.kp = res.result_params->kp; - this->controller_.ki = res.result_params->ki; - this->controller_.kd = res.result_params->kd; + this->controller_.kp_ = res.result_params->kp; + this->controller_.ki_ = res.result_params->ki; + this->controller_.kd_ = res.result_params->kd; // keep autotuner instance so that subsequent dump_configs will print the long result message. } else { value = res.output; diff --git a/esphome/components/pid/pid_climate.h b/esphome/components/pid/pid_climate.h index 095c00eb49..da57209a7e 100644 --- a/esphome/components/pid/pid_climate.h +++ b/esphome/components/pid/pid_climate.h @@ -21,20 +21,46 @@ class PIDClimate : public climate::Climate, public Component { void set_sensor(sensor::Sensor *sensor) { sensor_ = sensor; } void set_cool_output(output::FloatOutput *cool_output) { cool_output_ = cool_output; } void set_heat_output(output::FloatOutput *heat_output) { heat_output_ = heat_output; } - void set_kp(float kp) { controller_.kp = kp; } - void set_ki(float ki) { controller_.ki = ki; } - void set_kd(float kd) { controller_.kd = kd; } - void set_min_integral(float min_integral) { controller_.min_integral = min_integral; } - void set_max_integral(float max_integral) { controller_.max_integral = max_integral; } + void set_kp(float kp) { controller_.kp_ = kp; } + void set_ki(float ki) { controller_.ki_ = ki; } + void set_kd(float kd) { controller_.kd_ = kd; } + void set_min_integral(float min_integral) { controller_.min_integral_ = min_integral; } + void set_max_integral(float max_integral) { controller_.max_integral_ = max_integral; } + void set_output_samples(int in) { controller_.output_samples_ = in; } + void set_derivative_samples(int in) { controller_.derivative_samples_ = in; } + + void set_threshold_low(float in) { controller_.threshold_low_ = in; } + void set_threshold_high(float in) { controller_.threshold_high_ = in; } + void set_kp_multiplier(float in) { controller_.kp_multiplier_ = in; } + void set_ki_multiplier(float in) { controller_.ki_multiplier_ = in; } + void set_kd_multiplier(float in) { controller_.kd_multiplier_ = in; } + void set_starting_integral_term(float in) { controller_.set_starting_integral_term(in); } + + void set_deadband_output_samples(int in) { controller_.deadband_output_samples_ = in; } float get_output_value() const { return output_value_; } - float get_error_value() const { return controller_.error; } - float get_kp() { return controller_.kp; } - float get_ki() { return controller_.ki; } - float get_kd() { return controller_.kd; } - float get_proportional_term() const { return controller_.proportional_term; } - float get_integral_term() const { return controller_.integral_term; } - float get_derivative_term() const { return controller_.derivative_term; } + float get_error_value() const { return controller_.error_; } + float get_kp() { return controller_.kp_; } + float get_ki() { return controller_.ki_; } + float get_kd() { return controller_.kd_; } + float get_proportional_term() const { return controller_.proportional_term_; } + float get_integral_term() const { return controller_.integral_term_; } + float get_derivative_term() const { return controller_.derivative_term_; } + int get_output_samples() { return controller_.output_samples_; } + int get_derivative_samples() { return controller_.derivative_samples_; } + + float get_threshold_low() { return controller_.threshold_low_; } + float get_threshold_high() { return controller_.threshold_high_; } + float get_kp_multiplier() { return controller_.kp_multiplier_; } + float get_ki_multiplier() { return controller_.ki_multiplier_; } + float get_kd_multiplier() { return controller_.kd_multiplier_; } + int get_deadband_output_samples() { return controller_.deadband_output_samples_; } + bool in_deadband() { return controller_.in_deadband(); } + + // int get_derivative_samples() const { return controller_.derivative_samples; } + // float get_deadband() const { return controller_.deadband; } + // float get_proportional_deadband_multiplier() const { return controller_.proportional_deadband_multiplier; } + void add_on_pid_computed_callback(std::function &&callback) { pid_computed_callback_.add(std::move(callback)); } diff --git a/esphome/components/pid/pid_controller.cpp b/esphome/components/pid/pid_controller.cpp new file mode 100644 index 0000000000..afc2d91000 --- /dev/null +++ b/esphome/components/pid/pid_controller.cpp @@ -0,0 +1,124 @@ +#include "pid_controller.h" + +namespace esphome { +namespace pid { + +float PIDController::update(float setpoint, float process_value) { + // e(t) ... error at timestamp t + // r(t) ... setpoint + // y(t) ... process value (sensor reading) + // u(t) ... output value + + dt_ = calculate_relative_time_(); + + // e(t) := r(t) - y(t) + error_ = setpoint - process_value; + + calculate_proportional_term_(); + calculate_integral_term_(); + calculate_derivative_term_(); + + // u(t) := p(t) + i(t) + d(t) + float output = proportional_term_ + integral_term_ + derivative_term_; + + // smooth/sample the output + int samples = in_deadband() ? deadband_output_samples_ : output_samples_; + return weighted_average_(output_list_, output, samples); +} + +bool PIDController::in_deadband() { + // return (fabs(error) < deadband_threshold); + float err = -error_; + return ((err > 0 && err < threshold_high_) || (err < 0 && err > threshold_low_)); +} + +void PIDController::calculate_proportional_term_() { + // p(t) := K_p * e(t) + proportional_term_ = kp_ * error_; + + // set dead-zone to -X to +X + if (in_deadband()) { + // shallow the proportional_term in the deadband by the pdm + proportional_term_ *= kp_multiplier_; + + } else { + // pdm_offset prevents a jump when leaving the deadband + float threshold = (error_ < 0) ? threshold_high_ : threshold_low_; + float pdm_offset = (threshold - (kp_multiplier_ * threshold)) * kp_; + proportional_term_ += pdm_offset; + } +} + +void PIDController::calculate_integral_term_() { + // i(t) := K_i * \int_{0}^{t} e(t) dt + float new_integral = error_ * dt_ * ki_; + + if (in_deadband()) { + // shallow the integral when in the deadband + accumulated_integral_ += new_integral * ki_multiplier_; + } else { + accumulated_integral_ += new_integral; + } + + // constrain accumulated integral value + if (!std::isnan(min_integral_) && accumulated_integral_ < min_integral_) + accumulated_integral_ = min_integral_; + if (!std::isnan(max_integral_) && accumulated_integral_ > max_integral_) + accumulated_integral_ = max_integral_; + + integral_term_ = accumulated_integral_; +} + +void PIDController::calculate_derivative_term_() { + // derivative_term_ + // d(t) := K_d * de(t)/dt + float derivative = 0.0f; + if (dt_ != 0.0f) + derivative = (error_ - previous_error_) / dt_; + previous_error_ = error_; + + // smooth the derivative samples + derivative = weighted_average_(derivative_list_, derivative, derivative_samples_); + + derivative_term_ = kd_ * derivative; + + if (in_deadband()) { + // shallow the derivative when in the deadband + derivative_term_ *= kd_multiplier_; + } +} + +float PIDController::weighted_average_(std::deque &list, float new_value, int samples) { + // if only 1 sample needed, clear the list and return + if (samples == 1) { + list.clear(); + return new_value; + } + + // add the new item to the list + list.push_front(new_value); + + // keep only 'samples' readings, by popping off the back of the list + while (list.size() > samples) + list.pop_back(); + + // calculate and return the average of all values in the list + float sum = 0; + for (auto &elem : list) + sum += elem; + return sum / list.size(); +} + +float PIDController::calculate_relative_time_() { + uint32_t now = millis(); + uint32_t dt = now - this->last_time_; + if (last_time_ == 0) { + last_time_ = now; + return 0.0f; + } + last_time_ = now; + return dt / 1000.0f; +} + +} // namespace pid +} // namespace esphome diff --git a/esphome/components/pid/pid_controller.h b/esphome/components/pid/pid_controller.h index 35e3eb9fc0..05ce5f9224 100644 --- a/esphome/components/pid/pid_controller.h +++ b/esphome/components/pid/pid_controller.h @@ -1,81 +1,70 @@ #pragma once - #include "esphome/core/hal.h" +#include +#include namespace esphome { namespace pid { struct PIDController { - float update(float setpoint, float process_value) { - // e(t) ... error at timestamp t - // r(t) ... setpoint - // y(t) ... process value (sensor reading) - // u(t) ... output value - - float dt = calculate_relative_time_(); - - // e(t) := r(t) - y(t) - error = setpoint - process_value; - - // p(t) := K_p * e(t) - proportional_term = kp * error; - - // i(t) := K_i * \int_{0}^{t} e(t) dt - accumulated_integral_ += error * dt * ki; - // constrain accumulated integral value - if (!std::isnan(min_integral) && accumulated_integral_ < min_integral) - accumulated_integral_ = min_integral; - if (!std::isnan(max_integral) && accumulated_integral_ > max_integral) - accumulated_integral_ = max_integral; - integral_term = accumulated_integral_; - - // d(t) := K_d * de(t)/dt - float derivative = 0.0f; - if (dt != 0.0f) - derivative = (error - previous_error_) / dt; - previous_error_ = error; - derivative_term = kd * derivative; - - // u(t) := p(t) + i(t) + d(t) - return proportional_term + integral_term + derivative_term; - } + float update(float setpoint, float process_value); void reset_accumulated_integral() { accumulated_integral_ = 0; } + void set_starting_integral_term(float in) { accumulated_integral_ = in; } + bool in_deadband(); + + friend class PIDClimate; + + private: /// Proportional gain K_p. - float kp = 0; + float kp_ = 0; /// Integral gain K_i. - float ki = 0; + float ki_ = 0; /// Differential gain K_d. - float kd = 0; + float kd_ = 0; - float min_integral = NAN; - float max_integral = NAN; + // smooth the derivative value using a weighted average over X samples + int derivative_samples_ = 8; + + /// smooth the output value using a weighted average over X values + int output_samples_ = 1; + + float threshold_low_ = 0.0f; + float threshold_high_ = 0.0f; + float kp_multiplier_ = 0.0f; + float ki_multiplier_ = 0.0f; + float kd_multiplier_ = 0.0f; + int deadband_output_samples_ = 1; + + float min_integral_ = NAN; + float max_integral_ = NAN; // Store computed values in struct so that values can be monitored through sensors - float error; - float proportional_term; - float integral_term; - float derivative_term; + float error_; + float dt_; + float proportional_term_; + float integral_term_; + float derivative_term_; - protected: - float calculate_relative_time_() { - uint32_t now = millis(); - uint32_t dt = now - this->last_time_; - if (last_time_ == 0) { - last_time_ = now; - return 0.0f; - } - last_time_ = now; - return dt / 1000.0f; - } + void calculate_proportional_term_(); + void calculate_integral_term_(); + void calculate_derivative_term_(); + float weighted_average_(std::deque &list, float new_value, int samples); + float calculate_relative_time_(); /// Error from previous update used for derivative term float previous_error_ = 0; /// Accumulated integral value float accumulated_integral_ = 0; uint32_t last_time_ = 0; -}; + // this is a list of derivative values for smoothing. + std::deque derivative_list_; + + // this is a list of output values for smoothing. + std::deque output_list_; + +}; // Struct PID Controller } // namespace pid } // namespace esphome diff --git a/esphome/components/pid/pid_simulator.h b/esphome/components/pid/pid_simulator.h index fe145b7330..30222f2f7a 100644 --- a/esphome/components/pid/pid_simulator.h +++ b/esphome/components/pid/pid_simulator.h @@ -5,6 +5,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/output/float_output.h" +#include + namespace esphome { namespace pid { diff --git a/esphome/components/pipsolar/output/pipsolar_output.h b/esphome/components/pipsolar/output/pipsolar_output.h index fe783cf034..29b2d116f2 100644 --- a/esphome/components/pipsolar/output/pipsolar_output.h +++ b/esphome/components/pipsolar/output/pipsolar_output.h @@ -4,6 +4,8 @@ #include "esphome/components/output/float_output.h" #include "esphome/core/component.h" +#include + namespace esphome { namespace pipsolar { diff --git a/esphome/components/pn532/pn532.h b/esphome/components/pn532/pn532.h index 4f688dacc2..fee94a29b8 100644 --- a/esphome/components/pn532/pn532.h +++ b/esphome/components/pn532/pn532.h @@ -7,6 +7,8 @@ #include "esphome/components/nfc/nfc.h" #include "esphome/components/nfc/automation.h" +#include + namespace esphome { namespace pn532 { diff --git a/esphome/components/pn532/pn532_mifare_classic.cpp b/esphome/components/pn532/pn532_mifare_classic.cpp index 81d135d8e6..17d7aa123c 100644 --- a/esphome/components/pn532/pn532_mifare_classic.cpp +++ b/esphome/components/pn532/pn532_mifare_classic.cpp @@ -166,18 +166,23 @@ bool PN532::format_mifare_classic_ndef_(std::vector &uid) { return false; } if (block == 4) { - if (!this->write_mifare_classic_block_(block, empty_ndef_message)) + if (!this->write_mifare_classic_block_(block, empty_ndef_message)) { ESP_LOGE(TAG, "Unable to write block %d", block); + } } else { - if (!this->write_mifare_classic_block_(block, blank_block)) + if (!this->write_mifare_classic_block_(block, blank_block)) { ESP_LOGE(TAG, "Unable to write block %d", block); + } } - if (!this->write_mifare_classic_block_(block + 1, blank_block)) + if (!this->write_mifare_classic_block_(block + 1, blank_block)) { ESP_LOGE(TAG, "Unable to write block %d", block + 1); - if (!this->write_mifare_classic_block_(block + 2, blank_block)) + } + if (!this->write_mifare_classic_block_(block + 2, blank_block)) { ESP_LOGE(TAG, "Unable to write block %d", block + 2); - if (!this->write_mifare_classic_block_(block + 3, ndef_trailer)) + } + if (!this->write_mifare_classic_block_(block + 3, ndef_trailer)) { ESP_LOGE(TAG, "Unable to write trailer block %d", block + 3); + } } return true; } diff --git a/esphome/components/pn532_i2c/pn532_i2c.h b/esphome/components/pn532_i2c/pn532_i2c.h index 296d73e042..95cf8eeb36 100644 --- a/esphome/components/pn532_i2c/pn532_i2c.h +++ b/esphome/components/pn532_i2c/pn532_i2c.h @@ -4,6 +4,8 @@ #include "esphome/components/pn532/pn532.h" #include "esphome/components/i2c/i2c.h" +#include + namespace esphome { namespace pn532_i2c { diff --git a/esphome/components/pn532_spi/pn532_spi.h b/esphome/components/pn532_spi/pn532_spi.h index d98bd447c8..2d8312813d 100644 --- a/esphome/components/pn532_spi/pn532_spi.h +++ b/esphome/components/pn532_spi/pn532_spi.h @@ -4,6 +4,8 @@ #include "esphome/components/pn532/pn532.h" #include "esphome/components/spi/spi.h" +#include + namespace esphome { namespace pn532_spi { diff --git a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h index 9b8e2d9cfe..99455a1663 100644 --- a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h +++ b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include + #ifdef USE_ESP32 namespace esphome { diff --git a/esphome/components/pzemac/pzemac.h b/esphome/components/pzemac/pzemac.h index 8f2cf1460d..7a229b49ce 100644 --- a/esphome/components/pzemac/pzemac.h +++ b/esphome/components/pzemac/pzemac.h @@ -5,6 +5,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" +#include + namespace esphome { namespace pzemac { diff --git a/esphome/components/pzemdc/pzemdc.h b/esphome/components/pzemdc/pzemdc.h index a78a48a6fb..dff904476b 100644 --- a/esphome/components/pzemdc/pzemdc.h +++ b/esphome/components/pzemdc/pzemdc.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" +#include + namespace esphome { namespace pzemdc { diff --git a/esphome/components/rc522/rc522.h b/esphome/components/rc522/rc522.h index d853d2f5ff..5eea3c665e 100644 --- a/esphome/components/rc522/rc522.h +++ b/esphome/components/rc522/rc522.h @@ -5,6 +5,8 @@ #include "esphome/core/automation.h" #include "esphome/components/binary_sensor/binary_sensor.h" +#include + namespace esphome { namespace rc522 { diff --git a/esphome/components/rdm6300/rdm6300.h b/esphome/components/rdm6300/rdm6300.h index 13df400754..0aeabef2bc 100644 --- a/esphome/components/rdm6300/rdm6300.h +++ b/esphome/components/rdm6300/rdm6300.h @@ -5,6 +5,8 @@ #include "esphome/components/binary_sensor/binary_sensor.h" #include "esphome/components/uart/uart.h" +#include + namespace esphome { namespace rdm6300 { diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index e4d1e115e7..b979a050db 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1308,9 +1308,11 @@ MideaData, MideaBinarySensor, MideaTrigger, MideaAction, MideaDumper = declare_p MideaAction = ns.class_("MideaAction", RemoteTransmitterActionBase) MIDEA_SCHEMA = cv.Schema( { - cv.Required(CONF_CODE): cv.All( - [cv.Any(cv.hex_uint8_t, cv.uint8_t)], - cv.Length(min=5, max=5), + cv.Required(CONF_CODE): cv.templatable( + cv.All( + [cv.Any(cv.hex_uint8_t, cv.uint8_t)], + cv.Length(min=5, max=5), + ) ), } ) @@ -1337,7 +1339,12 @@ def midea_dumper(var, config): MIDEA_SCHEMA, ) async def midea_action(var, config, args): - cg.add(var.set_code(config[CONF_CODE])) + code_ = config[CONF_CODE] + if cg.is_template(code_): + template_ = await cg.templatable(code_, args, cg.std_vector.template(cg.uint8)) + cg.add(var.set_code_template(template_)) + else: + cg.add(var.set_code_static(code_)) # AEHA diff --git a/esphome/components/remote_base/aeha_protocol.h b/esphome/components/remote_base/aeha_protocol.h index 6cb4706506..c41f3f8df1 100644 --- a/esphome/components/remote_base/aeha_protocol.h +++ b/esphome/components/remote_base/aeha_protocol.h @@ -2,6 +2,8 @@ #include "remote_base.h" +#include + namespace esphome { namespace remote_base { diff --git a/esphome/components/remote_base/midea_protocol.h b/esphome/components/remote_base/midea_protocol.h index 135a93b36d..a7f5636b06 100644 --- a/esphome/components/remote_base/midea_protocol.h +++ b/esphome/components/remote_base/midea_protocol.h @@ -1,9 +1,11 @@ #pragma once -#include #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "remote_base.h" +#include +#include +#include namespace esphome { namespace remote_base { @@ -84,12 +86,23 @@ using MideaDumper = RemoteReceiverDumper; template class MideaAction : public RemoteTransmitterActionBase { TEMPLATABLE_VALUE(std::vector, code) - void set_code(const std::vector &code) { code_ = code; } + void set_code_static(std::vector code) { code_static_ = std::move(code); } + void set_code_template(std::function(Ts...)> func) { this->code_func_ = func; } + void encode(RemoteTransmitData *dst, Ts... x) override { - MideaData data = this->code_.value(x...); + MideaData data; + if (!this->code_static_.empty()) { + data = MideaData(this->code_static_); + } else { + data = MideaData(this->code_func_(x...)); + } data.finalize(); MideaProtocol().encode(dst, data); } + + protected: + std::function(Ts...)> code_func_{}; + std::vector code_static_{}; }; } // namespace remote_base diff --git a/esphome/components/remote_base/pronto_protocol.cpp b/esphome/components/remote_base/pronto_protocol.cpp index d8798d4ab9..4951b12bb1 100644 --- a/esphome/components/remote_base/pronto_protocol.cpp +++ b/esphome/components/remote_base/pronto_protocol.cpp @@ -236,8 +236,9 @@ void ProntoProtocol::dump(const ProntoData &data) { rest = data.data.substr(230); } ESP_LOGD(TAG, "Received Pronto: data=%s", first.c_str()); - if (!rest.empty()) + if (!rest.empty()) { ESP_LOGD(TAG, "%s", rest.c_str()); + } } } // namespace remote_base diff --git a/esphome/components/remote_base/pronto_protocol.h b/esphome/components/remote_base/pronto_protocol.h index 291bb8a99b..8c491257d3 100644 --- a/esphome/components/remote_base/pronto_protocol.h +++ b/esphome/components/remote_base/pronto_protocol.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "remote_base.h" +#include + namespace esphome { namespace remote_base { diff --git a/esphome/components/remote_base/raw_protocol.h b/esphome/components/remote_base/raw_protocol.h index 054f02ff7c..dc22282d1c 100644 --- a/esphome/components/remote_base/raw_protocol.h +++ b/esphome/components/remote_base/raw_protocol.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "remote_base.h" +#include + namespace esphome { namespace remote_base { diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index 3c76da84e3..fdb6d45e5f 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -1,4 +1,5 @@ #include +#include #pragma once diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index a4235e875f..560d83802e 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/components/remote_base/remote_base.h" +#include + namespace esphome { namespace remote_transmitter { diff --git a/esphome/components/rf_bridge/rf_bridge.cpp b/esphome/components/rf_bridge/rf_bridge.cpp index d8c8047496..c34b3d2dc4 100644 --- a/esphome/components/rf_bridge/rf_bridge.cpp +++ b/esphome/components/rf_bridge/rf_bridge.cpp @@ -49,8 +49,9 @@ bool RFBridgeComponent::parse_bridge_byte_(uint8_t byte) { data.high = (raw[6] << 8) | raw[7]; data.code = (raw[8] << 16) | (raw[9] << 8) | raw[10]; - if (action == RF_CODE_LEARN_OK) + if (action == RF_CODE_LEARN_OK) { ESP_LOGD(TAG, "Learning success"); + } ESP_LOGI(TAG, "Received RFBridge Code: sync=0x%04X low=0x%04X high=0x%04X code=0x%06X", data.sync, data.low, data.high, data.code); diff --git a/esphome/components/rf_bridge/rf_bridge.h b/esphome/components/rf_bridge/rf_bridge.h index 9156d995bc..fe6dd96b38 100644 --- a/esphome/components/rf_bridge/rf_bridge.h +++ b/esphome/components/rf_bridge/rf_bridge.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "esphome/core/component.h" #include "esphome/components/uart/uart.h" diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index c759576e4d..c6fbcf8deb 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -56,7 +56,7 @@ def _format_framework_arduino_version(ver: cv.Version) -> str: # The default/recommended arduino framework version # - https://github.com/earlephilhower/arduino-pico/releases # - https://api.registry.platformio.org/v3/packages/earlephilhower/tool/framework-arduinopico -RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 6, 2) +RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 6, 4) # The platformio/raspberrypi version to use for arduino frameworks # - https://github.com/platformio/platform-raspberrypi/releases @@ -67,8 +67,8 @@ ARDUINO_PLATFORM_VERSION = cv.Version(1, 7, 0) def _arduino_check_versions(value): value = value.copy() lookups = { - "dev": (cv.Version(2, 6, 2), "https://github.com/earlephilhower/arduino-pico"), - "latest": (cv.Version(2, 6, 2), None), + "dev": (cv.Version(2, 6, 4), "https://github.com/earlephilhower/arduino-pico"), + "latest": (cv.Version(2, 6, 4), None), "recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None), } diff --git a/esphome/components/rp2040/boards.py b/esphome/components/rp2040/boards.py index 6063b6d77d..c761efba58 100644 --- a/esphome/components/rp2040/boards.py +++ b/esphome/components/rp2040/boards.py @@ -17,3 +17,12 @@ RP2040_BOARD_PINS = { "SCL1": 27, }, } + +BOARDS = { + "rpipico": { + "name": "Raspberry Pi Pico", + }, + "rpipicow": { + "name": "Raspberry Pi Pico W", + }, +} diff --git a/esphome/components/rp2040/preferences.cpp b/esphome/components/rp2040/preferences.cpp index c98344541c..e7aa9ab28d 100644 --- a/esphome/components/rp2040/preferences.cpp +++ b/esphome/components/rp2040/preferences.cpp @@ -8,6 +8,8 @@ #include "preferences.h" #include +#include + #include "esphome/core/helpers.h" #include "esphome/core/log.h" #include "esphome/core/preferences.h" diff --git a/esphome/components/sdm_meter/sdm_meter.h b/esphome/components/sdm_meter/sdm_meter.h index 66f0fb8c5e..f8a3014a89 100644 --- a/esphome/components/sdm_meter/sdm_meter.h +++ b/esphome/components/sdm_meter/sdm_meter.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" +#include + namespace esphome { namespace sdm_meter { diff --git a/esphome/components/selec_meter/selec_meter.h b/esphome/components/selec_meter/selec_meter.h index 0477cd2a62..730791c91b 100644 --- a/esphome/components/selec_meter/selec_meter.h +++ b/esphome/components/selec_meter/selec_meter.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" +#include + namespace esphome { namespace selec_meter { diff --git a/esphome/components/sensirion_common/i2c_sensirion.h b/esphome/components/sensirion_common/i2c_sensirion.h index 3f0282a5d4..24b706cf36 100644 --- a/esphome/components/sensirion_common/i2c_sensirion.h +++ b/esphome/components/sensirion_common/i2c_sensirion.h @@ -1,6 +1,8 @@ #pragma once #include "esphome/components/i2c/i2c.h" +#include + namespace esphome { namespace sensirion_common { diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 6344d34661..c17d14583b 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -1,9 +1,10 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/core/helpers.h" #include #include +#include +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" namespace esphome { namespace sensor { diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index ba9edd68d0..958230cb3c 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -6,6 +6,8 @@ #include "esphome/core/helpers.h" #include "esphome/components/sensor/filter.h" +#include + namespace esphome { namespace sensor { diff --git a/esphome/components/shelly_dimmer/dev_table.h b/esphome/components/shelly_dimmer/dev_table.h index f4bf7778f2..e73cd1271c 100644 --- a/esphome/components/shelly_dimmer/dev_table.h +++ b/esphome/components/shelly_dimmer/dev_table.h @@ -155,4 +155,5 @@ constexpr stm32_dev_t DEVICES[] = { } // namespace shelly_dimmer } // namespace esphome -#endif + +#endif // USE_SHD_FIRMWARE_DATA diff --git a/esphome/components/shelly_dimmer/light.py b/esphome/components/shelly_dimmer/light.py index 3978d37c0b..9050a872b1 100644 --- a/esphome/components/shelly_dimmer/light.py +++ b/esphome/components/shelly_dimmer/light.py @@ -26,7 +26,7 @@ from esphome.const import ( from esphome.core import HexInt, CORE DOMAIN = "shelly_dimmer" -DEPENDENCIES = ["sensor", "uart"] +DEPENDENCIES = ["sensor", "uart", "esp8266"] shelly_dimmer_ns = cg.esphome_ns.namespace("shelly_dimmer") ShellyDimmer = shelly_dimmer_ns.class_( diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.cpp b/esphome/components/shelly_dimmer/shelly_dimmer.cpp index 94fe836742..144236bfe1 100644 --- a/esphome/components/shelly_dimmer/shelly_dimmer.cpp +++ b/esphome/components/shelly_dimmer/shelly_dimmer.cpp @@ -1,6 +1,8 @@ #include "esphome/core/defines.h" #include "esphome/core/helpers.h" +#ifdef USE_ESP8266 + #include "shelly_dimmer.h" #ifdef USE_SHD_FIRMWARE_DATA #include "stm32flash.h" @@ -521,3 +523,5 @@ void ShellyDimmer::reset_dfu_boot_() { } // namespace shelly_dimmer } // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.h b/esphome/components/shelly_dimmer/shelly_dimmer.h index b7d476279e..4701f3a32a 100644 --- a/esphome/components/shelly_dimmer/shelly_dimmer.h +++ b/esphome/components/shelly_dimmer/shelly_dimmer.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ESP8266 + #include "esphome/core/component.h" #include "esphome/core/log.h" #include "esphome/components/light/light_output.h" @@ -115,3 +117,5 @@ class ShellyDimmer : public PollingComponent, public light::LightOutput, public } // namespace shelly_dimmer } // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/shelly_dimmer/stm32flash.cpp b/esphome/components/shelly_dimmer/stm32flash.cpp index e688f2de36..3871d89a2f 100644 --- a/esphome/components/shelly_dimmer/stm32flash.cpp +++ b/esphome/components/shelly_dimmer/stm32flash.cpp @@ -613,8 +613,9 @@ stm32_unique_ptr stm32_init(uart::UARTDevice *stream, const uint8_t flags, const } } } - if (new_cmds) + if (new_cmds) { ESP_LOGD(TAG, ")"); + } if (stm32_get_ack(stm) != STM32_ERR_OK) { return make_stm32_with_deletor(nullptr); } @@ -1061,4 +1062,5 @@ stm32_err_t stm32_crc_wrapper(const stm32_unique_ptr &stm, uint32_t address, uin } // namespace shelly_dimmer } // namespace esphome -#endif + +#endif // USE_SHD_FIRMWARE_DATA diff --git a/esphome/components/sim800l/sim800l.cpp b/esphome/components/sim800l/sim800l.cpp index 3ae5de491a..7015933e73 100644 --- a/esphome/components/sim800l/sim800l.cpp +++ b/esphome/components/sim800l/sim800l.cpp @@ -196,8 +196,9 @@ void Sim800LComponent::parse_cmd_(std::string message) { // "+CREG: -,-" means not registered ok bool registered = message.compare(0, 6, "+CREG:") == 0 && (message[9] == '1' || message[9] == '5'); if (registered) { - if (!this->registered_) + if (!this->registered_) { ESP_LOGD(TAG, "Registered OK"); + } this->state_ = STATE_CSQ; this->expect_ack_ = true; } else { diff --git a/esphome/components/slow_pwm/slow_pwm_output.cpp b/esphome/components/slow_pwm/slow_pwm_output.cpp index 6af0283483..81c721a866 100644 --- a/esphome/components/slow_pwm/slow_pwm_output.cpp +++ b/esphome/components/slow_pwm/slow_pwm_output.cpp @@ -53,12 +53,15 @@ void SlowPWMOutput::loop() { void SlowPWMOutput::dump_config() { ESP_LOGCONFIG(TAG, "Slow PWM Output:"); LOG_PIN(" Pin: ", this->pin_); - if (this->state_change_trigger_) + if (this->state_change_trigger_) { ESP_LOGCONFIG(TAG, " State change automation configured"); - if (this->turn_on_trigger_) + } + if (this->turn_on_trigger_) { ESP_LOGCONFIG(TAG, " Turn on automation configured"); - if (this->turn_off_trigger_) + } + if (this->turn_off_trigger_) { ESP_LOGCONFIG(TAG, " Turn off automation configured"); + } ESP_LOGCONFIG(TAG, " Period: %d ms", this->period_); ESP_LOGCONFIG(TAG, " Restart cycle on state change: %s", YESNO(this->restart_cycle_on_state_change_)); LOG_FLOAT_OUTPUT(this); diff --git a/esphome/components/sn74hc165/__init__.py b/esphome/components/sn74hc165/__init__.py new file mode 100644 index 0000000000..85d0220a88 --- /dev/null +++ b/esphome/components/sn74hc165/__init__.py @@ -0,0 +1,87 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.const import ( + CONF_ID, + CONF_MODE, + CONF_NUMBER, + CONF_INVERTED, + CONF_DATA_PIN, + CONF_CLOCK_PIN, + CONF_INPUT, +) + +CODEOWNERS = ["@jesserockz"] +DEPENDENCIES = [] +MULTI_CONF = True + +sn74hc165_ns = cg.esphome_ns.namespace("sn74hc165") + +SN74HC165Component = sn74hc165_ns.class_("SN74HC165Component", cg.Component) +SN74HC165GPIOPin = sn74hc165_ns.class_( + "SN74HC165GPIOPin", cg.GPIOPin, cg.Parented.template(SN74HC165Component) +) + +CONF_SN74HC165 = "sn74hc165" +CONF_LOAD_PIN = "load_pin" +CONF_CLOCK_INHIBIT_PIN = "clock_inhibit_pin" +CONF_SR_COUNT = "sr_count" +CONFIG_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(SN74HC165Component), + cv.Required(CONF_DATA_PIN): pins.gpio_input_pin_schema, + cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_LOAD_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_CLOCK_INHIBIT_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_SR_COUNT, default=1): cv.int_range(min=1, max=256), + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + data_pin = await cg.gpio_pin_expression(config[CONF_DATA_PIN]) + cg.add(var.set_data_pin(data_pin)) + clock_pin = await cg.gpio_pin_expression(config[CONF_CLOCK_PIN]) + cg.add(var.set_clock_pin(clock_pin)) + load_pin = await cg.gpio_pin_expression(config[CONF_LOAD_PIN]) + cg.add(var.set_load_pin(load_pin)) + if CONF_CLOCK_INHIBIT_PIN in config: + clock_inhibit_pin = await cg.gpio_pin_expression(config[CONF_CLOCK_INHIBIT_PIN]) + cg.add(var.set_clock_inhibit_pin(clock_inhibit_pin)) + + cg.add(var.set_sr_count(config[CONF_SR_COUNT])) + + +def _validate_input_mode(value): + if value is not True: + raise cv.Invalid("Only input mode is supported") + return value + + +SN74HC165_PIN_SCHEMA = cv.All( + { + cv.GenerateID(): cv.declare_id(SN74HC165GPIOPin), + cv.Required(CONF_SN74HC165): cv.use_id(SN74HC165Component), + cv.Required(CONF_NUMBER): cv.int_range(min=0, max=2048, max_included=False), + cv.Optional(CONF_MODE, default={}): cv.All( + { + cv.Optional(CONF_INPUT, default=True): cv.All( + cv.boolean, _validate_input_mode + ), + }, + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) + + +@pins.PIN_SCHEMA_REGISTRY.register(CONF_SN74HC165, SN74HC165_PIN_SCHEMA) +async def sn74hc165_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_parented(var, config[CONF_SN74HC165]) + + cg.add(var.set_pin(config[CONF_NUMBER])) + cg.add(var.set_inverted(config[CONF_INVERTED])) + return var diff --git a/esphome/components/sn74hc165/sn74hc165.cpp b/esphome/components/sn74hc165/sn74hc165.cpp new file mode 100644 index 0000000000..6c89544db4 --- /dev/null +++ b/esphome/components/sn74hc165/sn74hc165.cpp @@ -0,0 +1,67 @@ +#include "sn74hc165.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace sn74hc165 { + +static const char *const TAG = "sn74hc165"; + +void SN74HC165Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up SN74HC165..."); + + // initialize pins + this->clock_pin_->setup(); + this->data_pin_->setup(); + this->load_pin_->setup(); + this->clock_pin_->digital_write(false); + this->load_pin_->digital_write(false); + + if (this->clock_inhibit_pin_ != nullptr) { + this->clock_inhibit_pin_->setup(); + this->clock_inhibit_pin_->digital_write(true); + } + + // read state from shift register + this->read_gpio_(); +} + +void SN74HC165Component::loop() { this->read_gpio_(); } + +void SN74HC165Component::dump_config() { ESP_LOGCONFIG(TAG, "SN74HC165:"); } + +bool SN74HC165Component::digital_read_(uint16_t pin) { + if (pin >= this->sr_count_ * 8) { + ESP_LOGE(TAG, "Pin %u is out of range! Maximum pin number with %u chips in series is %u", pin, this->sr_count_, + (this->sr_count_ * 8) - 1); + return false; + } + return this->input_bits_[pin]; +} + +void SN74HC165Component::read_gpio_() { + this->load_pin_->digital_write(false); + delayMicroseconds(5); + this->load_pin_->digital_write(true); + delayMicroseconds(5); + + if (this->clock_inhibit_pin_ != nullptr) + this->clock_inhibit_pin_->digital_write(false); + + for (int16_t i = (this->sr_count_ * 8) - 1; i >= 0; i--) { + this->input_bits_[i] = this->data_pin_->digital_read(); + this->clock_pin_->digital_write(true); + this->clock_pin_->digital_write(false); + } + + if (this->clock_inhibit_pin_ != nullptr) + this->clock_inhibit_pin_->digital_write(true); +} + +float SN74HC165Component::get_setup_priority() const { return setup_priority::IO; } + +bool SN74HC165GPIOPin::digital_read() { return this->parent_->digital_read_(this->pin_); } + +std::string SN74HC165GPIOPin::dump_summary() const { return str_snprintf("%u via SN74HC165", 18, pin_); } + +} // namespace sn74hc165 +} // namespace esphome diff --git a/esphome/components/sn74hc165/sn74hc165.h b/esphome/components/sn74hc165/sn74hc165.h new file mode 100644 index 0000000000..c349d079ae --- /dev/null +++ b/esphome/components/sn74hc165/sn74hc165.h @@ -0,0 +1,61 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" + +#include + +namespace esphome { +namespace sn74hc165 { + +class SN74HC165Component : public Component { + public: + SN74HC165Component() = default; + + void setup() override; + void loop() override; + float get_setup_priority() const override; + void dump_config() override; + + void set_data_pin(GPIOPin *pin) { this->data_pin_ = pin; } + void set_clock_pin(GPIOPin *pin) { this->clock_pin_ = pin; } + void set_load_pin(GPIOPin *pin) { this->load_pin_ = pin; } + void set_clock_inhibit_pin(GPIOPin *pin) { this->clock_inhibit_pin_ = pin; } + void set_sr_count(uint8_t count) { + this->sr_count_ = count; + this->input_bits_.resize(count * 8); + } + + protected: + friend class SN74HC165GPIOPin; + bool digital_read_(uint16_t pin); + void read_gpio_(); + + GPIOPin *data_pin_; + GPIOPin *clock_pin_; + GPIOPin *load_pin_; + GPIOPin *clock_inhibit_pin_; + uint8_t sr_count_; + std::vector input_bits_; +}; + +/// Helper class to expose a SC74HC165 pin as an internal input GPIO pin. +class SN74HC165GPIOPin : public GPIOPin, public Parented { + public: + void setup() override {} + void pin_mode(gpio::Flags flags) override {} + bool digital_read() override; + void digital_write(bool value) override{}; + std::string dump_summary() const override; + + void set_pin(uint16_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + + protected: + uint16_t pin_; + bool inverted_; +}; + +} // namespace sn74hc165 +} // namespace esphome diff --git a/esphome/components/sn74hc595/__init__.py b/esphome/components/sn74hc595/__init__.py index 630abc8bca..92b6d8d0e5 100644 --- a/esphome/components/sn74hc595/__init__.py +++ b/esphome/components/sn74hc595/__init__.py @@ -17,7 +17,9 @@ MULTI_CONF = True sn74hc595_ns = cg.esphome_ns.namespace("sn74hc595") SN74HC595Component = sn74hc595_ns.class_("SN74HC595Component", cg.Component) -SN74HC595GPIOPin = sn74hc595_ns.class_("SN74HC595GPIOPin", cg.GPIOPin) +SN74HC595GPIOPin = sn74hc595_ns.class_( + "SN74HC595GPIOPin", cg.GPIOPin, cg.Parented.template(SN74HC595Component) +) CONF_SN74HC595 = "sn74hc595" CONF_LATCH_PIN = "latch_pin" @@ -30,7 +32,7 @@ CONFIG_SCHEMA = cv.Schema( cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_LATCH_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_OE_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_SR_COUNT, default=1): cv.int_range(1, 4), + cv.Optional(CONF_SR_COUNT, default=1): cv.int_range(min=1, max=256), } ).extend(cv.COMPONENT_SCHEMA) @@ -60,7 +62,7 @@ SN74HC595_PIN_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(SN74HC595GPIOPin), cv.Required(CONF_SN74HC595): cv.use_id(SN74HC595Component), - cv.Required(CONF_NUMBER): cv.int_range(min=0, max=31), + cv.Required(CONF_NUMBER): cv.int_range(min=0, max=2048, max_included=False), cv.Optional(CONF_MODE, default={}): cv.All( { cv.Optional(CONF_OUTPUT, default=True): cv.All( @@ -76,10 +78,8 @@ SN74HC595_PIN_SCHEMA = cv.All( @pins.PIN_SCHEMA_REGISTRY.register(CONF_SN74HC595, SN74HC595_PIN_SCHEMA) async def sn74hc595_pin_to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - parent = await cg.get_variable(config[CONF_SN74HC595]) - cg.add(var.set_parent(parent)) + await cg.register_parented(var, config[CONF_SN74HC595]) - num = config[CONF_NUMBER] - cg.add(var.set_pin(num)) + cg.add(var.set_pin(config[CONF_NUMBER])) cg.add(var.set_inverted(config[CONF_INVERTED])) return var diff --git a/esphome/components/sn74hc595/sn74hc595.cpp b/esphome/components/sn74hc595/sn74hc595.cpp index 5ebf50e5cb..1895b1d5a6 100644 --- a/esphome/components/sn74hc595/sn74hc595.cpp +++ b/esphome/components/sn74hc595/sn74hc595.cpp @@ -28,24 +28,21 @@ void SN74HC595Component::setup() { void SN74HC595Component::dump_config() { ESP_LOGCONFIG(TAG, "SN74HC595:"); } -bool SN74HC595Component::digital_read_(uint8_t pin) { return this->output_bits_ >> pin; } - -void SN74HC595Component::digital_write_(uint8_t pin, bool value) { - uint32_t mask = 1UL << pin; - this->output_bits_ &= ~mask; - if (value) - this->output_bits_ |= mask; +void SN74HC595Component::digital_write_(uint16_t pin, bool value) { + if (pin >= this->sr_count_ * 8) { + ESP_LOGE(TAG, "Pin %u is out of range! Maximum pin number with %u chips in series is %u", pin, this->sr_count_, + (this->sr_count_ * 8) - 1); + return; + } + this->output_bits_[pin] = value; this->write_gpio_(); } -bool SN74HC595Component::write_gpio_() { - for (int i = this->sr_count_ - 1; i >= 0; i--) { - uint8_t data = (uint8_t)(this->output_bits_ >> (8 * i) & 0xff); - for (int j = 0; j < 8; j++) { - this->data_pin_->digital_write(data & (1 << (7 - j))); - this->clock_pin_->digital_write(true); - this->clock_pin_->digital_write(false); - } +void SN74HC595Component::write_gpio_() { + for (auto bit = this->output_bits_.rbegin(); bit != this->output_bits_.rend(); bit++) { + this->data_pin_->digital_write(*bit); + this->clock_pin_->digital_write(true); + this->clock_pin_->digital_write(false); } // pulse latch to activate new values @@ -56,8 +53,6 @@ bool SN74HC595Component::write_gpio_() { if (this->have_oe_pin_) { this->oe_pin_->digital_write(false); } - - return true; } float SN74HC595Component::get_setup_priority() const { return setup_priority::IO; } diff --git a/esphome/components/sn74hc595/sn74hc595.h b/esphome/components/sn74hc595/sn74hc595.h index 784019c3a6..64bf06d881 100644 --- a/esphome/components/sn74hc595/sn74hc595.h +++ b/esphome/components/sn74hc595/sn74hc595.h @@ -2,6 +2,9 @@ #include "esphome/core/component.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" + +#include namespace esphome { namespace sn74hc595 { @@ -21,13 +24,15 @@ class SN74HC595Component : public Component { oe_pin_ = pin; have_oe_pin_ = true; } - void set_sr_count(uint8_t count) { sr_count_ = count; } + void set_sr_count(uint8_t count) { + sr_count_ = count; + this->output_bits_.resize(count * 8); + } protected: friend class SN74HC595GPIOPin; - bool digital_read_(uint8_t pin); - void digital_write_(uint8_t pin, bool value); - bool write_gpio_(); + void digital_write_(uint16_t pin, bool value); + void write_gpio_(); GPIOPin *data_pin_; GPIOPin *clock_pin_; @@ -35,11 +40,11 @@ class SN74HC595Component : public Component { GPIOPin *oe_pin_; uint8_t sr_count_; bool have_oe_pin_{false}; - uint32_t output_bits_{0x00}; + std::vector output_bits_; }; /// Helper class to expose a SC74HC595 pin as an internal output GPIO pin. -class SN74HC595GPIOPin : public GPIOPin { +class SN74HC595GPIOPin : public GPIOPin, public Parented { public: void setup() override {} void pin_mode(gpio::Flags flags) override {} @@ -47,13 +52,11 @@ class SN74HC595GPIOPin : public GPIOPin { void digital_write(bool value) override; std::string dump_summary() const override; - void set_parent(SN74HC595Component *parent) { parent_ = parent; } - void set_pin(uint8_t pin) { pin_ = pin; } + void set_pin(uint16_t pin) { pin_ = pin; } void set_inverted(bool inverted) { inverted_ = inverted; } protected: - SN74HC595Component *parent_; - uint8_t pin_; + uint16_t pin_; bool inverted_; }; diff --git a/esphome/components/sonoff_d1/sonoff_d1.cpp b/esphome/components/sonoff_d1/sonoff_d1.cpp index dc6b719f1b..6ae80296fd 100644 --- a/esphome/components/sonoff_d1/sonoff_d1.cpp +++ b/esphome/components/sonoff_d1/sonoff_d1.cpp @@ -71,8 +71,9 @@ void SonoffD1Output::skip_command_() { } // Warn about unexpected bytes in the protocol with UART dimmer - if (garbage) + if (garbage) { ESP_LOGW(TAG, "[%04d] Skip %d bytes from the dimmer", this->write_count_, garbage); + } } // This assumes some data is already available diff --git a/esphome/components/sprinkler/__init__.py b/esphome/components/sprinkler/__init__.py index 4e80cfa021..52de290c85 100644 --- a/esphome/components/sprinkler/__init__.py +++ b/esphome/components/sprinkler/__init__.py @@ -8,6 +8,7 @@ from esphome.const import ( CONF_NAME, CONF_REPEAT, CONF_RUN_DURATION, + ENTITY_CATEGORY_CONFIG, ) AUTO_LOAD = ["switch"] @@ -223,7 +224,9 @@ SPRINKLER_ACTION_QUEUE_VALVE_SCHEMA = cv.Schema( SPRINKLER_VALVE_SCHEMA = cv.Schema( { cv.Optional(CONF_ENABLE_SWITCH): cv.maybe_simple_value( - switch.switch_schema(SprinklerControllerSwitch), + switch.switch_schema( + SprinklerControllerSwitch, entity_category=ENTITY_CATEGORY_CONFIG + ), key=CONF_NAME, ), cv.Optional(CONF_PUMP_OFF_SWITCH_ID): cv.use_id(switch.Switch), @@ -244,7 +247,9 @@ SPRINKLER_CONTROLLER_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(Sprinkler), cv.Optional(CONF_AUTO_ADVANCE_SWITCH): cv.maybe_simple_value( - switch.switch_schema(SprinklerControllerSwitch), + switch.switch_schema( + SprinklerControllerSwitch, entity_category=ENTITY_CATEGORY_CONFIG + ), key=CONF_NAME, ), cv.Optional(CONF_MAIN_SWITCH): cv.maybe_simple_value( @@ -252,11 +257,15 @@ SPRINKLER_CONTROLLER_SCHEMA = cv.Schema( key=CONF_NAME, ), cv.Optional(CONF_QUEUE_ENABLE_SWITCH): cv.maybe_simple_value( - switch.switch_schema(SprinklerControllerSwitch), + switch.switch_schema( + SprinklerControllerSwitch, entity_category=ENTITY_CATEGORY_CONFIG + ), key=CONF_NAME, ), cv.Optional(CONF_REVERSE_SWITCH): cv.maybe_simple_value( - switch.switch_schema(SprinklerControllerSwitch), + switch.switch_schema( + SprinklerControllerSwitch, entity_category=ENTITY_CATEGORY_CONFIG + ), key=CONF_NAME, ), cv.Optional(CONF_MANUAL_SELECTION_DELAY): cv.positive_time_period_seconds, diff --git a/esphome/components/sprinkler/sprinkler.h b/esphome/components/sprinkler/sprinkler.h index acd168d791..625118d9e5 100644 --- a/esphome/components/sprinkler/sprinkler.h +++ b/esphome/components/sprinkler/sprinkler.h @@ -5,6 +5,8 @@ #include "esphome/core/hal.h" #include "esphome/components/switch/switch.h" +#include + namespace esphome { namespace sprinkler { diff --git a/esphome/components/stepper/__init__.py b/esphome/components/stepper/__init__.py index 54f6aa4205..f2367fd62a 100644 --- a/esphome/components/stepper/__init__.py +++ b/esphome/components/stepper/__init__.py @@ -14,7 +14,6 @@ from esphome.core import CORE, coroutine_with_priority IS_PLATFORM_COMPONENT = True -# pylint: disable=invalid-name stepper_ns = cg.esphome_ns.namespace("stepper") Stepper = stepper_ns.class_("Stepper") diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index 336c7d38d6..c9bf81982a 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -12,6 +12,7 @@ from esphome.const import ( CONF_MQTT_ID, CONF_ON_TURN_OFF, CONF_ON_TURN_ON, + CONF_RESTORE_MODE, CONF_TRIGGER_ID, DEVICE_CLASS_EMPTY, DEVICE_CLASS_OUTLET, @@ -33,6 +34,19 @@ switch_ns = cg.esphome_ns.namespace("switch_") Switch = switch_ns.class_("Switch", cg.EntityBase) SwitchPtr = Switch.operator("ptr") +SwitchRestoreMode = switch_ns.enum("SwitchRestoreMode") + +RESTORE_MODES = { + "RESTORE_DEFAULT_OFF": SwitchRestoreMode.SWITCH_RESTORE_DEFAULT_OFF, + "RESTORE_DEFAULT_ON": SwitchRestoreMode.SWITCH_RESTORE_DEFAULT_ON, + "ALWAYS_OFF": SwitchRestoreMode.SWITCH_ALWAYS_OFF, + "ALWAYS_ON": SwitchRestoreMode.SWITCH_ALWAYS_ON, + "RESTORE_INVERTED_DEFAULT_OFF": SwitchRestoreMode.SWITCH_RESTORE_INVERTED_DEFAULT_OFF, + "RESTORE_INVERTED_DEFAULT_ON": SwitchRestoreMode.SWITCH_RESTORE_INVERTED_DEFAULT_ON, + "DISABLED": SwitchRestoreMode.SWITCH_RESTORE_DISABLED, +} + + ToggleAction = switch_ns.class_("ToggleAction", automation.Action) TurnOffAction = switch_ns.class_("TurnOffAction", automation.Action) TurnOnAction = switch_ns.class_("TurnOnAction", automation.Action) @@ -50,7 +64,7 @@ SwitchTurnOffTrigger = switch_ns.class_( validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True) -SWITCH_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( +_SWITCH_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSwitchComponent), cv.Optional(CONF_INVERTED): cv.boolean, @@ -78,8 +92,15 @@ def switch_schema( device_class: str = _UNDEF, icon: str = _UNDEF, block_inverted: bool = False, + default_restore_mode: str = "RESTORE_DEFAULT_OFF", ): - schema = SWITCH_SCHEMA + schema = _SWITCH_SCHEMA.extend( + { + cv.Optional(CONF_RESTORE_MODE, default=default_restore_mode): cv.enum( + RESTORE_MODES, upper=True, space="_" + ), + } + ) if class_ is not _UNDEF: schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)}) if entity_category is not _UNDEF: @@ -111,6 +132,9 @@ def switch_schema( return schema +SWITCH_SCHEMA = switch_schema() # for compatibility + + async def setup_switch_core_(var, config): await setup_entity(var, config) @@ -130,6 +154,8 @@ async def setup_switch_core_(var, config): if CONF_DEVICE_CLASS in config: cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) + cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) + async def register_switch(var, config): if not CORE.has_id(config[CONF_ID]): diff --git a/esphome/components/switch/switch.cpp b/esphome/components/switch/switch.cpp index 099bd4819b..caa072b1ea 100644 --- a/esphome/components/switch/switch.cpp +++ b/esphome/components/switch/switch.cpp @@ -22,18 +22,37 @@ void Switch::toggle() { this->write_state(this->inverted_ == this->state); } optional Switch::get_initial_state() { + if (!(restore_mode & RESTORE_MODE_PERSISTENT_MASK)) + return {}; + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); bool initial_state; if (!this->rtc_.load(&initial_state)) return {}; return initial_state; } +optional Switch::get_initial_state_with_restore_mode() { + if (restore_mode & RESTORE_MODE_DISABLED_MASK) { + return {}; + } + bool initial_state = restore_mode & RESTORE_MODE_ON_MASK; // default value *_OFF or *_ON + if (restore_mode & RESTORE_MODE_PERSISTENT_MASK) { // For RESTORE_* + optional restored_state = this->get_initial_state(); + if (restored_state.has_value()) { + // Invert value if any of the *_INVERTED_* modes + initial_state = restore_mode & RESTORE_MODE_INVERTED_MASK ? !restored_state.value() : restored_state.value(); + } + } + return initial_state; +} void Switch::publish_state(bool state) { if (!this->publish_dedup_.next(state)) return; this->state = state != this->inverted_; - this->rtc_.save(&this->state); + if (restore_mode & RESTORE_MODE_PERSISTENT_MASK) + this->rtc_.save(&this->state); + ESP_LOGD(TAG, "'%s': Sending state %s", this->name_.c_str(), ONOFF(this->state)); this->state_callback_.call(this->state); } @@ -52,5 +71,34 @@ std::string Switch::get_device_class() { } void Switch::set_device_class(const std::string &device_class) { this->device_class_ = device_class; } +void log_switch(const char *tag, const char *prefix, const char *type, Switch *obj) { + if (obj != nullptr) { + ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str()); + if (!obj->get_icon().empty()) { + ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon().c_str()); + } + if (obj->assumed_state()) { + ESP_LOGCONFIG(tag, "%s Assumed State: YES", prefix); + } + if (obj->is_inverted()) { + ESP_LOGCONFIG(tag, "%s Inverted: YES", prefix); + } + if (!obj->get_device_class().empty()) { + ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); + } + const LogString *onoff = LOG_STR(""), *inverted = onoff, *restore; + if (obj->restore_mode & RESTORE_MODE_DISABLED_MASK) { + restore = LOG_STR("disabled"); + } else { + onoff = obj->restore_mode & RESTORE_MODE_ON_MASK ? LOG_STR("ON") : LOG_STR("OFF"); + inverted = obj->restore_mode & RESTORE_MODE_INVERTED_MASK ? LOG_STR("inverted ") : LOG_STR(""); + restore = obj->restore_mode & RESTORE_MODE_PERSISTENT_MASK ? LOG_STR("restore defaults to") : LOG_STR("always"); + } + + ESP_LOGCONFIG(tag, "%s Restore Mode: %s%s %s", prefix, LOG_STR_ARG(inverted), LOG_STR_ARG(restore), + LOG_STR_ARG(onoff)); + } +} + } // namespace switch_ } // namespace esphome diff --git a/esphome/components/switch/switch.h b/esphome/components/switch/switch.h index c521c4024b..b89d8db6a5 100644 --- a/esphome/components/switch/switch.h +++ b/esphome/components/switch/switch.h @@ -8,22 +8,21 @@ namespace esphome { namespace switch_ { -#define LOG_SWITCH(prefix, type, obj) \ - if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ - if (!(obj)->get_icon().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ - } \ - if ((obj)->assumed_state()) { \ - ESP_LOGCONFIG(TAG, "%s Assumed State: YES", prefix); \ - } \ - if ((obj)->is_inverted()) { \ - ESP_LOGCONFIG(TAG, "%s Inverted: YES", prefix); \ - } \ - if (!(obj)->get_device_class().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ - } \ - } +// bit0: on/off. bit1: persistent. bit2: inverted. bit3: disabled +const int RESTORE_MODE_ON_MASK = 0x01; +const int RESTORE_MODE_PERSISTENT_MASK = 0x02; +const int RESTORE_MODE_INVERTED_MASK = 0x04; +const int RESTORE_MODE_DISABLED_MASK = 0x08; + +enum SwitchRestoreMode { + SWITCH_ALWAYS_OFF = !RESTORE_MODE_ON_MASK, + SWITCH_ALWAYS_ON = RESTORE_MODE_ON_MASK, + SWITCH_RESTORE_DEFAULT_OFF = RESTORE_MODE_PERSISTENT_MASK, + SWITCH_RESTORE_DEFAULT_ON = RESTORE_MODE_PERSISTENT_MASK | RESTORE_MODE_ON_MASK, + SWITCH_RESTORE_INVERTED_DEFAULT_OFF = RESTORE_MODE_PERSISTENT_MASK | RESTORE_MODE_INVERTED_MASK, + SWITCH_RESTORE_INVERTED_DEFAULT_ON = RESTORE_MODE_PERSISTENT_MASK | RESTORE_MODE_INVERTED_MASK | RESTORE_MODE_ON_MASK, + SWITCH_RESTORE_DISABLED = RESTORE_MODE_DISABLED_MASK, +}; /** Base class for all switches. * @@ -47,6 +46,9 @@ class Switch : public EntityBase { /// The current reported state of the binary sensor. bool state; + /// Indicates whether or not state is to be retrieved from flash and how + SwitchRestoreMode restore_mode{SWITCH_RESTORE_DEFAULT_OFF}; + /** Turn this switch on. This is called by the front-end. * * For implementing switches, please override write_state. @@ -80,8 +82,19 @@ class Switch : public EntityBase { */ void add_on_state_callback(std::function &&callback); + /** Returns the initial state of the switch, as persisted previously, + or empty if never persisted. + */ optional get_initial_state(); + /** Returns the initial state of the switch, after applying restore mode rules. + * If restore mode is disabled, this function will return an optional with no value + * (.has_value() is false), leaving it up to the component to decide the state. + * For example, the component could read the state from hardware and determine the current + * state. + */ + optional get_initial_state_with_restore_mode(); + /** Return whether this switch uses an assumed state - i.e. if both the ON/OFF actions should be displayed in Home * Assistant because the real state is unknown. * @@ -95,6 +108,7 @@ class Switch : public EntityBase { std::string get_device_class(); /// Set the Home Assistant device class for this switch. void set_device_class(const std::string &device_class); + void set_restore_mode(SwitchRestoreMode restore_mode) { this->restore_mode = restore_mode; } protected: /** Write the given state to hardware. You should implement this @@ -114,5 +128,8 @@ class Switch : public EntityBase { optional device_class_; }; +#define LOG_SWITCH(prefix, type, obj) log_switch((TAG), (prefix), LOG_STR_LITERAL(type), (obj)) +void log_switch(const char *tag, const char *prefix, const char *type, Switch *obj); + } // namespace switch_ } // namespace esphome diff --git a/esphome/components/sx1509/sx1509.h b/esphome/components/sx1509/sx1509.h index 5f0697b534..50230b1086 100644 --- a/esphome/components/sx1509/sx1509.h +++ b/esphome/components/sx1509/sx1509.h @@ -6,6 +6,8 @@ #include "sx1509_gpio_pin.h" #include "sx1509_registers.h" +#include + namespace esphome { namespace sx1509 { diff --git a/esphome/components/teleinfo/teleinfo.h b/esphome/components/teleinfo/teleinfo.h index 2be34cfb78..0c6217853e 100644 --- a/esphome/components/teleinfo/teleinfo.h +++ b/esphome/components/teleinfo/teleinfo.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/components/uart/uart.h" +#include + namespace esphome { namespace teleinfo { /* diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index dd3dbddc43..3ed6b72d8f 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -23,7 +23,6 @@ from esphome.util import Registry IS_PLATFORM_COMPONENT = True -# pylint: disable=invalid-name text_sensor_ns = cg.esphome_ns.namespace("text_sensor") TextSensor = text_sensor_ns.class_("TextSensor", cg.EntityBase) TextSensorPtr = TextSensor.operator("ptr") diff --git a/esphome/components/text_sensor/filter.h b/esphome/components/text_sensor/filter.h index 38f35e6172..4e36532945 100644 --- a/esphome/components/text_sensor/filter.h +++ b/esphome/components/text_sensor/filter.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace esphome { namespace text_sensor { diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h index 340f7ff9ed..a7673ed9ff 100644 --- a/esphome/components/text_sensor/text_sensor.h +++ b/esphome/components/text_sensor/text_sensor.h @@ -5,6 +5,8 @@ #include "esphome/core/helpers.h" #include "esphome/components/text_sensor/filter.h" +#include + namespace esphome { namespace text_sensor { diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index 61e279d4a6..1fbbbe022c 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -1307,10 +1307,12 @@ void ThermostatClimate::dump_config() { ESP_LOGCONFIG(TAG, " Supports FAN_ONLY_ACTION_USES_FAN_MODE_TIMER: %s", YESNO(this->supports_fan_only_action_uses_fan_mode_timer_)); ESP_LOGCONFIG(TAG, " Supports FAN_ONLY_COOLING: %s", YESNO(this->supports_fan_only_cooling_)); - if (this->supports_cool_) + if (this->supports_cool_) { ESP_LOGCONFIG(TAG, " Supports FAN_WITH_COOLING: %s", YESNO(this->supports_fan_with_cooling_)); - if (this->supports_heat_) + } + if (this->supports_heat_) { ESP_LOGCONFIG(TAG, " Supports FAN_WITH_HEATING: %s", YESNO(this->supports_fan_with_heating_)); + } ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_)); ESP_LOGCONFIG(TAG, " Supports FAN MODE ON: %s", YESNO(this->supports_fan_mode_on_)); ESP_LOGCONFIG(TAG, " Supports FAN MODE OFF: %s", YESNO(this->supports_fan_mode_off_)); diff --git a/esphome/components/thermostat/thermostat_climate.h b/esphome/components/thermostat/thermostat_climate.h index 68cbb17e34..124f95de33 100644 --- a/esphome/components/thermostat/thermostat_climate.h +++ b/esphome/components/thermostat/thermostat_climate.h @@ -4,7 +4,9 @@ #include "esphome/core/automation.h" #include "esphome/components/climate/climate.h" #include "esphome/components/sensor/sensor.h" + #include +#include namespace esphome { namespace thermostat { diff --git a/esphome/components/time/automation.h b/esphome/components/time/automation.h index 6167aac4f7..e97413e420 100644 --- a/esphome/components/time/automation.h +++ b/esphome/components/time/automation.h @@ -4,6 +4,8 @@ #include "esphome/core/automation.h" #include "real_time_clock.h" +#include + namespace esphome { namespace time { diff --git a/esphome/components/tm1637/tm1637.h b/esphome/components/tm1637/tm1637.h index 0a77acaabe..2fb572bc55 100644 --- a/esphome/components/tm1637/tm1637.h +++ b/esphome/components/tm1637/tm1637.h @@ -4,6 +4,8 @@ #include "esphome/core/defines.h" #include "esphome/core/hal.h" +#include + #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" #endif diff --git a/esphome/components/tm1638/tm1638.h b/esphome/components/tm1638/tm1638.h index 44160ad227..2e1ac6fad3 100644 --- a/esphome/components/tm1638/tm1638.h +++ b/esphome/components/tm1638/tm1638.h @@ -5,6 +5,8 @@ #include "esphome/core/automation.h" #include "esphome/core/hal.h" +#include + #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" #endif diff --git a/esphome/components/toshiba/toshiba.cpp b/esphome/components/toshiba/toshiba.cpp index c7a5b72852..104e885aab 100644 --- a/esphome/components/toshiba/toshiba.cpp +++ b/esphome/components/toshiba/toshiba.cpp @@ -1,5 +1,7 @@ #include "toshiba.h" +#include + namespace esphome { namespace toshiba { diff --git a/esphome/components/total_daily_energy/total_daily_energy.h b/esphome/components/total_daily_energy/total_daily_energy.h index a40c56a7db..1a9d5d1a49 100644 --- a/esphome/components/total_daily_energy/total_daily_energy.h +++ b/esphome/components/total_daily_energy/total_daily_energy.h @@ -37,7 +37,6 @@ class TotalDailyEnergy : public sensor::Sensor, public Component { TotalDailyEnergyMethod method_; uint16_t last_day_of_year_{}; uint32_t last_update_{0}; - uint32_t last_save_{0}; bool restore_; float total_energy_{0.0f}; float last_power_state_{0.0f}; diff --git a/esphome/components/ttp229_bsf/ttp229_bsf.h b/esphome/components/ttp229_bsf/ttp229_bsf.h index 59749a4fa7..2663afcec9 100644 --- a/esphome/components/ttp229_bsf/ttp229_bsf.h +++ b/esphome/components/ttp229_bsf/ttp229_bsf.h @@ -4,6 +4,8 @@ #include "esphome/core/hal.h" #include "esphome/components/binary_sensor/binary_sensor.h" +#include + namespace esphome { namespace ttp229_bsf { diff --git a/esphome/components/ttp229_lsf/ttp229_lsf.h b/esphome/components/ttp229_lsf/ttp229_lsf.h index 2064d9b654..f8775a17f0 100644 --- a/esphome/components/ttp229_lsf/ttp229_lsf.h +++ b/esphome/components/ttp229_lsf/ttp229_lsf.h @@ -4,6 +4,8 @@ #include "esphome/components/binary_sensor/binary_sensor.h" #include "esphome/components/i2c/i2c.h" +#include + namespace esphome { namespace ttp229_lsf { diff --git a/esphome/components/tuya/automation.h b/esphome/components/tuya/automation.h index d7706e1d60..8d91cfdfbf 100644 --- a/esphome/components/tuya/automation.h +++ b/esphome/components/tuya/automation.h @@ -4,6 +4,8 @@ #include "esphome/core/automation.h" #include "tuya.h" +#include + namespace esphome { namespace tuya { diff --git a/esphome/components/tuya/climate/__init__.py b/esphome/components/tuya/climate/__init__.py index 7d4b37ad22..199c2eabeb 100644 --- a/esphome/components/tuya/climate/__init__.py +++ b/esphome/components/tuya/climate/__init__.py @@ -25,6 +25,7 @@ CONF_CURRENT_TEMPERATURE_MULTIPLIER = "current_temperature_multiplier" CONF_TARGET_TEMPERATURE_MULTIPLIER = "target_temperature_multiplier" CONF_ECO_DATAPOINT = "eco_datapoint" CONF_ECO_TEMPERATURE = "eco_temperature" +CONF_REPORTS_FAHRENHEIT = "reports_fahrenheit" TuyaClimate = tuya_ns.class_("TuyaClimate", climate.Climate, cg.Component) @@ -110,6 +111,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_TARGET_TEMPERATURE_MULTIPLIER): cv.positive_float, cv.Optional(CONF_ECO_DATAPOINT): cv.uint8_t, cv.Optional(CONF_ECO_TEMPERATURE): cv.temperature, + cv.Optional(CONF_REPORTS_FAHRENHEIT, default=False): cv.boolean, } ).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT), @@ -186,3 +188,6 @@ async def to_code(config): cg.add(var.set_eco_id(config[CONF_ECO_DATAPOINT])) if CONF_ECO_TEMPERATURE in config: cg.add(var.set_eco_temperature(config[CONF_ECO_TEMPERATURE])) + + if config[CONF_REPORTS_FAHRENHEIT]: + cg.add(var.set_reports_fahrenheit()) diff --git a/esphome/components/tuya/climate/tuya_climate.cpp b/esphome/components/tuya/climate/tuya_climate.cpp index 39d4203684..687764e30f 100644 --- a/esphome/components/tuya/climate/tuya_climate.cpp +++ b/esphome/components/tuya/climate/tuya_climate.cpp @@ -1,5 +1,5 @@ -#include "esphome/core/log.h" #include "tuya_climate.h" +#include "esphome/core/log.h" namespace esphome { namespace tuya { @@ -44,6 +44,10 @@ void TuyaClimate::setup() { if (this->target_temperature_id_.has_value()) { this->parent_->register_listener(*this->target_temperature_id_, [this](const TuyaDatapoint &datapoint) { this->manual_temperature_ = datapoint.value_int * this->target_temperature_multiplier_; + if (this->reports_fahrenheit_) { + this->manual_temperature_ = (this->manual_temperature_ - 32) * 5 / 9; + } + ESP_LOGV(TAG, "MCU reported manual target temperature is: %.1f", this->manual_temperature_); this->compute_target_temperature_(); this->compute_state_(); @@ -53,6 +57,10 @@ void TuyaClimate::setup() { if (this->current_temperature_id_.has_value()) { this->parent_->register_listener(*this->current_temperature_id_, [this](const TuyaDatapoint &datapoint) { this->current_temperature = datapoint.value_int * this->current_temperature_multiplier_; + if (this->reports_fahrenheit_) { + this->current_temperature = (this->current_temperature - 32) * 5 / 9; + } + ESP_LOGV(TAG, "MCU reported current temperature is: %.1f", this->current_temperature); this->compute_state_(); this->publish_state(); @@ -105,7 +113,10 @@ void TuyaClimate::control(const climate::ClimateCall &call) { } if (call.get_target_temperature().has_value()) { - const float target_temperature = *call.get_target_temperature(); + float target_temperature = *call.get_target_temperature(); + if (this->reports_fahrenheit_) + target_temperature = (target_temperature * 9 / 5) + 32; + ESP_LOGV(TAG, "Setting target temperature: %.1f", target_temperature); this->parent_->set_integer_datapoint_value(*this->target_temperature_id_, (int) (target_temperature / this->target_temperature_multiplier_)); @@ -138,18 +149,23 @@ climate::ClimateTraits TuyaClimate::traits() { void TuyaClimate::dump_config() { LOG_CLIMATE("", "Tuya Climate", this); - if (this->switch_id_.has_value()) + if (this->switch_id_.has_value()) { ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_); - if (this->active_state_id_.has_value()) + } + if (this->active_state_id_.has_value()) { ESP_LOGCONFIG(TAG, " Active state has datapoint ID %u", *this->active_state_id_); - if (this->target_temperature_id_.has_value()) + } + if (this->target_temperature_id_.has_value()) { ESP_LOGCONFIG(TAG, " Target Temperature has datapoint ID %u", *this->target_temperature_id_); - if (this->current_temperature_id_.has_value()) + } + if (this->current_temperature_id_.has_value()) { ESP_LOGCONFIG(TAG, " Current Temperature has datapoint ID %u", *this->current_temperature_id_); + } LOG_PIN(" Heating State Pin: ", this->heating_state_pin_); LOG_PIN(" Cooling State Pin: ", this->cooling_state_pin_); - if (this->eco_id_.has_value()) + if (this->eco_id_.has_value()) { ESP_LOGCONFIG(TAG, " Eco has datapoint ID %u", *this->eco_id_); + } } void TuyaClimate::compute_preset_() { diff --git a/esphome/components/tuya/climate/tuya_climate.h b/esphome/components/tuya/climate/tuya_climate.h index ec19d05308..7c18625c4e 100644 --- a/esphome/components/tuya/climate/tuya_climate.h +++ b/esphome/components/tuya/climate/tuya_climate.h @@ -35,6 +35,8 @@ class TuyaClimate : public climate::Climate, public Component { void set_eco_id(uint8_t eco_id) { this->eco_id_ = eco_id; } void set_eco_temperature(float eco_temperature) { this->eco_temperature_ = eco_temperature; } + void set_reports_fahrenheit() { this->reports_fahrenheit_ = true; } + void set_tuya_parent(Tuya *parent) { this->parent_ = parent; } protected: @@ -77,6 +79,7 @@ class TuyaClimate : public climate::Climate, public Component { bool cooling_state_{false}; float manual_temperature_; bool eco_; + bool reports_fahrenheit_{false}; }; } // namespace tuya diff --git a/esphome/components/tuya/cover/tuya_cover.cpp b/esphome/components/tuya/cover/tuya_cover.cpp index b55873c3c1..11a458449f 100644 --- a/esphome/components/tuya/cover/tuya_cover.cpp +++ b/esphome/components/tuya/cover/tuya_cover.cpp @@ -112,14 +112,18 @@ void TuyaCover::dump_config() { ESP_LOGCONFIG(TAG, " Configured as Inverted, but direction_datapoint isn't configured"); } } - if (this->control_id_.has_value()) + if (this->control_id_.has_value()) { ESP_LOGCONFIG(TAG, " Control has datapoint ID %u", *this->control_id_); - if (this->direction_id_.has_value()) + } + if (this->direction_id_.has_value()) { ESP_LOGCONFIG(TAG, " Direction has datapoint ID %u", *this->direction_id_); - if (this->position_id_.has_value()) + } + if (this->position_id_.has_value()) { ESP_LOGCONFIG(TAG, " Position has datapoint ID %u", *this->position_id_); - if (this->position_report_id_.has_value()) + } + if (this->position_report_id_.has_value()) { ESP_LOGCONFIG(TAG, " Position Report has datapoint ID %u", *this->position_report_id_); + } } cover::CoverTraits TuyaCover::get_traits() { diff --git a/esphome/components/tuya/fan/tuya_fan.cpp b/esphome/components/tuya/fan/tuya_fan.cpp index 813aee4aa0..1b03ea50fa 100644 --- a/esphome/components/tuya/fan/tuya_fan.cpp +++ b/esphome/components/tuya/fan/tuya_fan.cpp @@ -49,14 +49,18 @@ void TuyaFan::setup() { void TuyaFan::dump_config() { LOG_FAN("", "Tuya Fan", this); - if (this->speed_id_.has_value()) + if (this->speed_id_.has_value()) { ESP_LOGCONFIG(TAG, " Speed has datapoint ID %u", *this->speed_id_); - if (this->switch_id_.has_value()) + } + if (this->switch_id_.has_value()) { ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_); - if (this->oscillation_id_.has_value()) + } + if (this->oscillation_id_.has_value()) { ESP_LOGCONFIG(TAG, " Oscillation has datapoint ID %u", *this->oscillation_id_); - if (this->direction_id_.has_value()) + } + if (this->direction_id_.has_value()) { ESP_LOGCONFIG(TAG, " Direction has datapoint ID %u", *this->direction_id_); + } } fan::FanTraits TuyaFan::get_traits() { diff --git a/esphome/components/tuya/light/tuya_light.cpp b/esphome/components/tuya/light/tuya_light.cpp index 4d8bc9e37b..b75e85bc14 100644 --- a/esphome/components/tuya/light/tuya_light.cpp +++ b/esphome/components/tuya/light/tuya_light.cpp @@ -92,10 +92,12 @@ void TuyaLight::setup() { void TuyaLight::dump_config() { ESP_LOGCONFIG(TAG, "Tuya Dimmer:"); - if (this->dimmer_id_.has_value()) + if (this->dimmer_id_.has_value()) { ESP_LOGCONFIG(TAG, " Dimmer has datapoint ID %u", *this->dimmer_id_); - if (this->switch_id_.has_value()) + } + if (this->switch_id_.has_value()) { ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_); + } if (this->rgb_id_.has_value()) { ESP_LOGCONFIG(TAG, " RGB has datapoint ID %u", *this->rgb_id_); } else if (this->hsv_id_.has_value()) { diff --git a/esphome/components/tuya/select/tuya_select.h b/esphome/components/tuya/select/tuya_select.h index ab233dc501..6a7e5c7ed0 100644 --- a/esphome/components/tuya/select/tuya_select.h +++ b/esphome/components/tuya/select/tuya_select.h @@ -4,6 +4,8 @@ #include "esphome/components/tuya/tuya.h" #include "esphome/components/select/select.h" +#include + namespace esphome { namespace tuya { diff --git a/esphome/components/tuya/tuya.h b/esphome/components/tuya/tuya.h index 1f21b09c0c..5839dfbec0 100644 --- a/esphome/components/tuya/tuya.h +++ b/esphome/components/tuya/tuya.h @@ -9,6 +9,8 @@ #include "esphome/components/time/real_time_clock.h" #endif +#include + namespace esphome { namespace tuya { diff --git a/esphome/components/tx20/tx20.cpp b/esphome/components/tx20/tx20.cpp index fefcc8f4d5..a2a3baa46e 100644 --- a/esphome/components/tx20/tx20.cpp +++ b/esphome/components/tx20/tx20.cpp @@ -2,6 +2,8 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include + namespace esphome { namespace tx20 { diff --git a/esphome/components/uart/automation.h b/esphome/components/uart/automation.h index 9686f94413..b6a50ea22d 100644 --- a/esphome/components/uart/automation.h +++ b/esphome/components/uart/automation.h @@ -3,6 +3,8 @@ #include "uart.h" #include "esphome/core/automation.h" +#include + namespace esphome { namespace uart { diff --git a/esphome/components/uart/switch/uart_switch.h b/esphome/components/uart/switch/uart_switch.h index 4c82d5680a..4f24d76d0c 100644 --- a/esphome/components/uart/switch/uart_switch.h +++ b/esphome/components/uart/switch/uart_switch.h @@ -4,6 +4,8 @@ #include "esphome/components/uart/uart.h" #include "esphome/components/switch/switch.h" +#include + namespace esphome { namespace uart { diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index a9d8350404..46da3f6fb4 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -88,13 +88,17 @@ MODELS = { } -def validate_full_update_every_only_type_a(value): +def validate_full_update_every_only_types_ac(value): if CONF_FULL_UPDATE_EVERY not in value: return value if MODELS[value[CONF_MODEL]][0] == "b": + full_models = [] + for key, val in sorted(MODELS.items()): + if val[0] != "b": + full_models.append(key) raise cv.Invalid( "The 'full_update_every' option is only available for models " - "'1.54in', '1.54inV2', '2.13in', '2.90in', and '2.90inV2'." + + ", ".join(full_models) ) return value @@ -116,7 +120,7 @@ CONFIG_SCHEMA = cv.All( ) .extend(cv.polling_component_schema("1s")) .extend(spi.spi_device_schema()), - validate_full_update_every_only_type_a, + validate_full_update_every_only_types_ac, cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), ) diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index cdabe66d44..f6d3a84f89 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -4,6 +4,8 @@ #include #include +#include + #include "esphome/core/component.h" #include diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index dba0d48724..544cb3dc61 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -5,7 +5,9 @@ #include "esphome/core/automation.h" #include "esphome/core/helpers.h" #include "esphome/components/network/ip_address.h" + #include +#include #ifdef USE_ESP32_FRAMEWORK_ARDUINO #include diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index e893712287..54ed7638d2 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -266,8 +266,7 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed! %d", err); } } - esp_wpa2_config_t wpa2_config = WPA2_CONFIG_INIT_DEFAULT(); - err = esp_wifi_sta_wpa2_ent_enable(&wpa2_config); + err = esp_wifi_sta_wpa2_ent_enable(); if (err != ESP_OK) { ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_enable failed! %d", err); } diff --git a/esphome/components/wifi/wifi_component_pico_w.cpp b/esphome/components/wifi/wifi_component_pico_w.cpp index 794ab34219..8e64878f8e 100644 --- a/esphome/components/wifi/wifi_component_pico_w.cpp +++ b/esphome/components/wifi/wifi_component_pico_w.cpp @@ -156,7 +156,7 @@ bool WiFiComponent::wifi_disconnect_() { int err = cyw43_wifi_leave(&cyw43_state, CYW43_ITF_STA); return err == 0; } -// NOTE: The driver does not provide an interface to get this + bssid_t WiFiComponent::wifi_bssid() { bssid_t bssid{}; uint8_t raw_bssid[6]; @@ -165,12 +165,10 @@ bssid_t WiFiComponent::wifi_bssid() { bssid[i] = raw_bssid[i]; return bssid; } -// NOTE: The driver does not provide an interface to get this -std::string WiFiComponent::wifi_ssid() { return WiFi.SSID(); } -// NOTE: The driver does not provide an interface to get this +std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } -// NOTE: The driver does not provide an interface to get this -int32_t WiFiComponent::wifi_channel_() { return 0; } +int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } + network::IPAddress WiFiComponent::wifi_sta_ip() { return {WiFi.localIP()}; } network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask()}; } network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; } diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.h b/esphome/components/xiaomi_ble/xiaomi_ble.h index 399bef83b8..c1086605d1 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.h +++ b/esphome/components/xiaomi_ble/xiaomi_ble.h @@ -3,6 +3,8 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/core/component.h" +#include + #ifdef USE_ESP32 namespace esphome { diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.h b/esphome/components/xiaomi_miscale/xiaomi_miscale.h index cff61f153b..4523bbc82b 100644 --- a/esphome/components/xiaomi_miscale/xiaomi_miscale.h +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include + #ifdef USE_ESP32 namespace esphome { diff --git a/esphome/components/xpt2046/touchscreen.py b/esphome/components/xpt2046/touchscreen.py index 868525ccb1..e45b723179 100644 --- a/esphome/components/xpt2046/touchscreen.py +++ b/esphome/components/xpt2046/touchscreen.py @@ -3,18 +3,19 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import spi, touchscreen -from esphome.const import CONF_ID, CONF_THRESHOLD +from esphome.const import CONF_ID, CONF_THRESHOLD, CONF_INTERRUPT_PIN CODEOWNERS = ["@numo68", "@nielsnl68"] DEPENDENCIES = ["spi"] XPT2046_ns = cg.esphome_ns.namespace("xpt2046") XPT2046Component = XPT2046_ns.class_( - "XPT2046Component", touchscreen.Touchscreen, cg.PollingComponent, spi.SPIDevice + "XPT2046Component", + touchscreen.Touchscreen, + cg.PollingComponent, + spi.SPIDevice, ) -CONF_INTERRUPT_PIN = "interrupt_pin" - CONF_REPORT_INTERVAL = "report_interval" CONF_CALIBRATION_X_MIN = "calibration_x_min" CONF_CALIBRATION_X_MAX = "calibration_x_max" diff --git a/esphome/config.py b/esphome/config.py index 0bf4ec8df3..e63cddf347 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -4,7 +4,8 @@ import heapq import logging import re -# pylint: disable=unused-import, wrong-import-order +from typing import Optional, Union + from contextlib import contextmanager import voluptuous as vol @@ -23,7 +24,6 @@ from esphome.core import CORE, EsphomeError from esphome.helpers import indent from esphome.util import safe_print, OrderedDict -from typing import Optional, Union from esphome.loader import get_component, get_platform, ComponentManifest from esphome.yaml_util import is_secret, ESPHomeDataBase, ESPForceValue from esphome.voluptuous_schema import ExtraKeysInvalid diff --git a/esphome/config_helpers.py b/esphome/config_helpers.py index bfb0f65417..d36a2f1e7f 100644 --- a/esphome/config_helpers.py +++ b/esphome/config_helpers.py @@ -26,7 +26,6 @@ def read_config_file(path: str) -> str: def merge_config(full_old, full_new): def merge(old, new): - # pylint: disable=no-else-return if isinstance(new, dict): if not isinstance(old, dict): return new @@ -34,11 +33,11 @@ def merge_config(full_old, full_new): for k, v in new.items(): res[k] = merge(old[k], v) if k in old else v return res - elif isinstance(new, list): + if isinstance(new, list): if not isinstance(old, list): return new return old + new - elif new is None: + if new is None: return old return new diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 7498f7c505..054ee194c1 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1053,9 +1053,8 @@ def mqtt_qos(value): def requires_component(comp): """Validate that this option can only be specified when the component `comp` is loaded.""" - # pylint: disable=unsupported-membership-test + def validator(value): - # pylint: disable=unsupported-membership-test if comp not in CORE.loaded_integrations: raise Invalid(f"This option requires component {comp}") return value @@ -1482,7 +1481,6 @@ class OnlyWith(Optional): @property def default(self): - # pylint: disable=unsupported-membership-test if self._component in CORE.loaded_integrations: return self._default return vol.UNDEFINED diff --git a/esphome/const.py b/esphome/const.py index 0dc5af2894..60752d0578 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.11.5" +__version__ = "2022.12.0b6" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" @@ -322,6 +322,7 @@ CONF_INTERNAL = "internal" CONF_INTERNAL_FILTER = "internal_filter" CONF_INTERNAL_FILTER_MODE = "internal_filter_mode" CONF_INTERRUPT = "interrupt" +CONF_INTERRUPT_PIN = "interrupt_pin" CONF_INTERVAL = "interval" CONF_INVALID_COOLDOWN = "invalid_cooldown" CONF_INVERT = "invert" @@ -803,6 +804,7 @@ ICON_BRIGHTNESS_6 = "mdi:brightness-6" ICON_BUG = "mdi:bug" ICON_CHECK_CIRCLE_OUTLINE = "mdi:check-circle-outline" ICON_CHEMICAL_WEAPON = "mdi:chemical-weapon" +ICON_CHIP = "mdi:chip" ICON_COUNTER = "mdi:counter" ICON_CURRENT_AC = "mdi:current-ac" ICON_DATABASE = "mdi:database" @@ -818,6 +820,7 @@ ICON_GRAIN = "mdi:grain" ICON_KEY_PLUS = "mdi:key-plus" ICON_LIGHTBULB = "mdi:lightbulb" ICON_MAGNET = "mdi:magnet" +ICON_MEMORY = "mdi:memory" ICON_MOLECULE_CO2 = "mdi:molecule-co2" ICON_MOTION_SENSOR = "mdi:motion-sensor" ICON_NEW_BOX = "mdi:new-box" @@ -989,6 +992,7 @@ KEY_CORE = "core" KEY_TARGET_PLATFORM = "target_platform" KEY_TARGET_FRAMEWORK = "target_framework" KEY_FRAMEWORK_VERSION = "framework_version" +KEY_NAME = "name" # Entity categories ENTITY_CATEGORY_NONE = "" diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index ef44b31fa3..09115c2791 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -443,7 +443,7 @@ class Library: return NotImplemented -# pylint: disable=too-many-instance-attributes,too-many-public-methods +# pylint: disable=too-many-public-methods class EsphomeCore: def __init__(self): # True if command is run from dashboard @@ -553,7 +553,6 @@ class EsphomeCore: return os.path.basename(self.config_path) def relative_config_path(self, *path): - # pylint: disable=no-value-for-parameter path_ = os.path.expanduser(os.path.join(*path)) return os.path.join(self.config_dir, path_) @@ -561,7 +560,6 @@ class EsphomeCore: return self.relative_config_path(".esphome", *path) def relative_build_path(self, *path): - # pylint: disable=no-value-for-parameter path_ = os.path.expanduser(os.path.join(*path)) return os.path.join(self.build_path, path_) diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index 6ac6e596c1..b36a64b82a 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -3,6 +3,8 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" +#include + namespace esphome { template class AndCondition : public Condition { diff --git a/esphome/core/config.py b/esphome/core/config.py index a40542c662..bf0beea2ef 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -110,6 +110,15 @@ def validate_version(value: str): return value +if "ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT" in os.environ: + _compile_process_limit_default = min( + int(os.environ["ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT"]), + multiprocessing.cpu_count(), + ) +else: + _compile_process_limit_default = cv.UNDEFINED + + CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash" CONFIG_SCHEMA = cv.All( cv.Schema( @@ -153,9 +162,9 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_MIN_VERSION, default=ESPHOME_VERSION): cv.All( cv.version_number, validate_version ), - cv.Optional(CONF_COMPILE_PROCESS_LIMIT): cv.int_range( - min=1, max=multiprocessing.cpu_count() - ), + cv.Optional( + CONF_COMPILE_PROCESS_LIMIT, default=_compile_process_limit_default + ): cv.int_range(min=1, max=multiprocessing.cpu_count()), } ), validate_hostname, diff --git a/esphome/core/defines.h b/esphome/core/defines.h index cea53b8545..01fc16cc02 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -68,19 +68,18 @@ #define USE_ESP32_BLE_CLIENT #define USE_ESP32_BLE_SERVER #define USE_ESP32_CAMERA -#define USE_ESP32_IGNORE_EFUSE_MAC_CRC #define USE_IMPROV #define USE_SOCKET_IMPL_BSD_SOCKETS #define USE_WIFI_11KV_SUPPORT #define USE_BLUETOOTH_PROXY #ifdef USE_ARDUINO -#define USE_ARDUINO_VERSION_CODE VERSION_CODE(1, 0, 6) +#define USE_ARDUINO_VERSION_CODE VERSION_CODE(2, 0, 5) #define USE_ETHERNET #endif #ifdef USE_ESP_IDF -#define USE_ARDUINO_VERSION_CODE VERSION_CODE(4, 3, 0) +#define USE_ESP_IDF_VERSION_CODE VERSION_CODE(4, 4, 2) #endif #endif @@ -91,15 +90,16 @@ #define USE_ESP8266_PREFERENCES_FLASH #define USE_HTTP_REQUEST_ESP8266_HTTPS #define USE_SOCKET_IMPL_LWIP_TCP -#endif - -// Disabled feature flags -//#define USE_BSEC // Requires a library with proprietary license. - -#define USE_DASHBOARD_IMPORT // Dummy firmware payload for shelly_dimmer #define USE_SHD_FIRMWARE_MAJOR_VERSION 56 #define USE_SHD_FIRMWARE_MINOR_VERSION 5 #define USE_SHD_FIRMWARE_DATA \ {} + +#endif + +// Disabled feature flags +//#define USE_BSEC // Requires a library with proprietary license. + +#define USE_DASHBOARD_IMPORT diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 9f9a1f10b1..7ec5d9d23c 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -422,7 +422,7 @@ void HighFrequencyLoopRequester::stop() { } bool HighFrequencyLoopRequester::is_high_frequency() { return num_requests > 0; } -void get_mac_address_raw(uint8_t *mac) { +void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) #if defined(USE_ESP32) #if defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC) // On some devices, the MAC address that is burnt into EFuse does not diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 6f3b5805cf..477dadc187 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -568,7 +568,7 @@ class HighFrequencyLoopRequester { }; /// Get the device MAC address as raw bytes, written into the provided byte array (6 bytes). -void get_mac_address_raw(uint8_t *mac); +void get_mac_address_raw(uint8_t *mac); // NOLINT(readability-non-const-parameter) /// Get the device MAC address as a string, in lowercase hex notation. std::string get_mac_address(); diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index a0f60e1ff8..a423554d10 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -2,34 +2,26 @@ import abc import inspect import math import re -from esphome.yaml_util import ESPHomeDataBase - -# pylint: disable=unused-import, wrong-import-order -from typing import ( - Any, - Callable, - Optional, - Union, -) from collections.abc import Generator, Sequence +from typing import Any, Callable, Optional, Union -from esphome.core import ( # noqa +from esphome.core import ( CORE, - HexInt, ID, + Define, + EnumValue, + HexInt, Lambda, + Library, TimePeriod, TimePeriodMicroseconds, TimePeriodMilliseconds, TimePeriodMinutes, TimePeriodSeconds, - coroutine, - Library, - Define, - EnumValue, ) from esphome.helpers import cpp_string_escape, indent_all_but_first_and_last from esphome.util import OrderedDict +from esphome.yaml_util import ESPHomeDataBase class Expression(abc.ABC): diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index 0ab4b75a16..02d339441f 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -11,7 +11,6 @@ from esphome.const import ( CONF_TYPE_ID, ) -# pylint: disable=unused-import from esphome.core import coroutine, ID, CORE from esphome.types import ConfigType, ConfigFragmentType from esphome.cpp_generator import add, get_variable diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 2153f15a35..2cf826dea9 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -1,5 +1,3 @@ -# pylint: disable=wrong-import-position - import base64 import codecs import collections @@ -10,11 +8,12 @@ import json import logging import multiprocessing import os -from pathlib import Path import secrets import shutil import subprocess import threading +from pathlib import Path +from typing import Optional import tornado import tornado.concurrent @@ -22,15 +21,15 @@ import tornado.gen import tornado.httpserver import tornado.ioloop import tornado.iostream -from tornado.log import access_log import tornado.netutil import tornado.process import tornado.web import tornado.websocket +from tornado.log import access_log from esphome import const, platformio_api, util, yaml_util from esphome.core import EsphomeError -from esphome.helpers import mkdir_p, get_bool_env, run_system_command +from esphome.helpers import get_bool_env, mkdir_p, run_system_command from esphome.storage_json import ( EsphomeStorageJSON, StorageJSON, @@ -38,14 +37,11 @@ from esphome.storage_json import ( ext_storage_path, trash_storage_path, ) -from esphome.util import shlex_quote, get_serial_ports -from .util import password_hash - -# pylint: disable=unused-import, wrong-import-order -from typing import Optional # noqa - +from esphome.util import get_serial_ports, shlex_quote from esphome.zeroconf import DashboardImportDiscovery, DashboardStatus, EsphomeZeroconf +from .util import password_hash + _LOGGER = logging.getLogger(__name__) ENV_DEV = "ESPHOME_DASHBOARD_DEV" @@ -190,7 +186,6 @@ def websocket_method(name): return wrap -# pylint: disable=abstract-method, arguments-differ @websocket_class class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler): def __init__(self, application, request, **kwargs): @@ -421,6 +416,10 @@ class ImportRequestHandler(BaseHandler): self.set_status(500) self.write("File already exists") return + except ValueError: + self.set_status(422) + self.write("Invalid package url") + return self.set_status(200) self.finish() @@ -694,6 +693,24 @@ class PrometheusServiceDiscoveryHandler(BaseHandler): self.write(json.dumps(sd)) +class BoardsRequestHandler(BaseHandler): + @authenticated + def get(self): + from esphome.components.esp32.boards import BOARDS as ESP32_BOARDS + from esphome.components.esp8266.boards import BOARDS as ESP8266_BOARDS + from esphome.components.rp2040.boards import BOARDS as RP2040_BOARDS + + boards = { + "esp32": {key: val[const.KEY_NAME] for key, val in ESP32_BOARDS.items()}, + "esp8266": { + key: val[const.KEY_NAME] for key, val in ESP8266_BOARDS.items() + }, + "rp2040": {key: val[const.KEY_NAME] for key, val in RP2040_BOARDS.items()}, + } + self.set_header("content-type", "application/json") + self.write(json.dumps(boards)) + + class MDNSStatusThread(threading.Thread): def run(self): global IMPORT_RESULT @@ -797,7 +814,6 @@ class EditRequestHandler(BaseHandler): filename = settings.rel_path(configuration) content = "" if os.path.isfile(filename): - # pylint: disable=no-value-for-parameter with open(file=filename, encoding="utf-8") as f: content = f.read() self.write(content) @@ -805,7 +821,6 @@ class EditRequestHandler(BaseHandler): @authenticated @bind_config def post(self, configuration=None): - # pylint: disable=no-value-for-parameter with open(file=settings.rel_path(configuration), mode="wb") as f: f.write(self.request.body) self.set_status(200) @@ -1066,6 +1081,7 @@ def make_app(debug=get_bool_env(ENV_DEV)): (f"{rel}json-config", JsonConfigRequestHandler), (f"{rel}rename", EsphomeRenameHandler), (f"{rel}prometheus-sd", PrometheusServiceDiscoveryHandler), + (f"{rel}boards", BoardsRequestHandler), ], **app_settings, ) diff --git a/esphome/git.py b/esphome/git.py index 54fedc035f..d3d5996fe3 100644 --- a/esphome/git.py +++ b/esphome/git.py @@ -1,14 +1,15 @@ -from pathlib import Path -import subprocess import hashlib import logging -from typing import Callable, Optional +import re +import subprocess import urllib.parse - +from dataclasses import dataclass from datetime import datetime +from pathlib import Path +from typing import Callable, Optional -from esphome.core import CORE, TimePeriodSeconds import esphome.config_validation as cv +from esphome.core import CORE, TimePeriodSeconds _LOGGER = logging.getLogger(__name__) @@ -103,3 +104,57 @@ def clone_or_update( return repo_dir, revert return repo_dir, None + + +GIT_DOMAINS = { + "github": "github.com", + "gitlab": "gitlab.com", +} + + +@dataclass(frozen=True) +class GitFile: + domain: str + owner: str + repo: str + filename: str + ref: str = None + query: str = None + + @property + def git_url(self) -> str: + return f"https://{self.domain}/{self.owner}/{self.repo}.git" + + @property + def raw_url(self) -> str: + if self.ref is None: + raise ValueError("URL has no ref") + if self.domain == "github": + return f"https://raw.githubusercontent.com/{self.owner}/{self.repo}/{self.ref}/{self.filename}" + if self.domain == "gitlab": + return f"https://gitlab.com/{self.owner}/{self.repo}/-/raw/{self.ref}/{self.filename}" + raise NotImplementedError(f"Git domain {self.domain} not supported") + + @classmethod + def from_shorthand(cls, shorthand): + """Parse a git shorthand URL into its components.""" + if not isinstance(shorthand, str): + raise ValueError("Git shorthand must be a string") + m = re.match( + r"(?P[a-zA-Z0-9\-]+)://(?P[a-zA-Z0-9\-]+)/(?P[a-zA-Z0-9\-\._]+)/(?P[a-zA-Z0-9\-_.\./]+)(?:@(?P[a-zA-Z0-9\-_.\./]+))?(?:\?(?P[a-zA-Z0-9\-_.\./]+))?", + shorthand, + ) + if m is None: + raise ValueError( + "URL is not in expected github://username/name/[sub-folder/]file-path.yml[@branch-or-tag] format!" + ) + if m.group("domain") not in GIT_DOMAINS: + raise ValueError(f"Unknown git domain {m.group('domain')}") + return cls( + domain=GIT_DOMAINS[m.group("domain")], + owner=m.group("owner"), + repo=m.group("repo"), + filename=m.group("filename"), + ref=m.group("ref"), + query=m.group("query"), + ) diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index 70576bec11..ef62e62ce7 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -37,7 +37,6 @@ def patch_structhash(): if not isdir(build_dir): makedirs(build_dir) - # pylint: disable=protected-access helpers.clean_build_dir = patched_clean_build_dir cli.clean_build_dir = patched_clean_build_dir diff --git a/esphome/storage_json.py b/esphome/storage_json.py index 1cc2180747..c2c8b91a36 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -31,7 +31,6 @@ def trash_storage_path(base_path: str) -> str: return os.path.join(base_path, ".esphome", "trash") -# pylint: disable=too-many-instance-attributes class StorageJSON: def __init__( self, diff --git a/esphome/voluptuous_schema.py b/esphome/voluptuous_schema.py index f4ed2fe03b..281ef10964 100644 --- a/esphome/voluptuous_schema.py +++ b/esphome/voluptuous_schema.py @@ -204,7 +204,6 @@ class _Schema(vol.Schema): return self @schema_extractor_extended - # pylint: disable=signature-differs def extend(self, *schemas, **kwargs): extra = kwargs.pop("extra", None) if kwargs: diff --git a/esphome/vscode.py b/esphome/vscode.py index 32a6b524f6..cb2f51976f 100644 --- a/esphome/vscode.py +++ b/esphome/vscode.py @@ -1,14 +1,12 @@ import json import os -# pylint: disable=unused-import +from typing import Optional + from esphome.config import load_config, _format_vol_invalid, Config from esphome.core import CORE, DocumentRange import esphome.config_validation as cv -# pylint: disable=unused-import, wrong-import-order -from typing import Optional - def _get_invalid_range(res: Config, invalid: cv.Invalid) -> Optional[DocumentRange]: return res.get_deepest_document_range_for_path( diff --git a/esphome/wizard.py b/esphome/wizard.py index 6273eec25d..0fcccfc3f6 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -9,7 +9,6 @@ import esphome.config_validation as cv from esphome.helpers import get_bool_env, write_file from esphome.log import color, Fore -# pylint: disable=anomalous-backslash-in-string from esphome.storage_json import StorageJSON, ext_storage_path from esphome.util import safe_print from esphome.const import ALLOWED_NAME_CHARS, ENV_QUICKWIZARD diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index 75aec0edc8..2689ce9245 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -88,7 +88,7 @@ def _add_data_ref(fn): return wrapped -class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors +class ESPHomeLoader(yaml.SafeLoader): """Loader class that keeps track of line numbers.""" @_add_data_ref @@ -419,7 +419,7 @@ def is_secret(value): return None -class ESPHomeDumper(yaml.SafeDumper): # pylint: disable=too-many-ancestors +class ESPHomeDumper(yaml.SafeDumper): def represent_mapping(self, tag, mapping, flow_style=None): value = [] node = yaml.MappingNode(tag, value, flow_style=flow_style) diff --git a/platformio.ini b/platformio.ini index ae0578de05..b92a1407ed 100644 --- a/platformio.ini +++ b/platformio.ini @@ -103,9 +103,9 @@ extra_scripts = post:esphome/components/esp8266/post_build.py.script ; This are common settings for the ESP32 (all variants) using Arduino. [common:esp32-arduino] extends = common:arduino -platform = platformio/espressif32 @ 3.5.0 +platform = platformio/espressif32 @ 5.2.0 platform_packages = - platformio/framework-arduinoespressif32 @ ~3.10006.0 + platformio/framework-arduinoespressif32 @ ~3.20005.0 framework = arduino board = nodemcu-32s @@ -121,7 +121,7 @@ lib_deps = HTTPClient ; http_request,nextion (Arduino built-in) ESPmDNS ; mdns (Arduino built-in) DNSServer ; captive_portal (Arduino built-in) - esphome/ESP32-audioI2S@2.1.0 ; i2s_audio + esphome/ESP32-audioI2S@2.0.6 ; i2s_audio crankyoldgit/IRremoteESP8266@2.7.12 ; heatpumpir build_flags = ${common:arduino.build_flags} @@ -133,9 +133,9 @@ extra_scripts = post:esphome/components/esp32/post_build.py.script ; This are common settings for the ESP32 (all variants) using IDF. [common:esp32-idf] extends = common:idf -platform = platformio/espressif32 @ 3.5.0 +platform = platformio/espressif32 @ 5.2.0 platform_packages = - platformio/framework-espidf @ ~3.40302.0 + platformio/framework-espidf @ ~3.40402.0 framework = espidf lib_deps = diff --git a/requirements.txt b/requirements.txt index 55da7ec57a..9c12253160 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,16 @@ voluptuous==0.13.1 PyYAML==6.0 paho-mqtt==1.6.1 -colorama==0.4.5 +colorama==0.4.6 tornado==6.2 tzlocal==4.2 # from time tzdata>=2021.1 # from time pyserial==3.5 platformio==6.1.5 # When updating platformio, also update Dockerfile -esptool==3.3.1 +esptool==4.4 click==8.1.3 -esphome-dashboard==20221109.0 -aioesphomeapi==11.4.3 +esphome-dashboard==20221213.0 +aioesphomeapi==13.0.1 zeroconf==0.39.4 # esp-idf requires this, but doesn't bundle it by default diff --git a/requirements_test.txt b/requirements_test.txt index 28112521ac..fbc8f63613 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,13 +1,13 @@ -pylint==2.15.5 -flake8==5.0.4 # also change in .pre-commit-config.yaml when updating +pylint==2.15.8 +flake8==6.0.0 # also change in .pre-commit-config.yaml when updating black==22.10.0 # also change in .pre-commit-config.yaml when updating -pyupgrade==3.2.0 # also change in .pre-commit-config.yaml when updating +pyupgrade==3.3.0 # also change in .pre-commit-config.yaml when updating pre-commit # Unit tests pytest==7.2.0 pytest-cov==4.0.0 pytest-mock==3.10.0 -pytest-asyncio==0.20.1 +pytest-asyncio==0.20.2 asyncmock==0.4.2 hypothesis==5.49.0 diff --git a/script/api_protobuf/api_options_pb2.py b/script/api_protobuf/api_options_pb2.py deleted file mode 100644 index f5297c062c..0000000000 --- a/script/api_protobuf/api_options_pb2.py +++ /dev/null @@ -1,251 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: api_options.proto - -import sys - -_b = sys.version_info[0] < 3 and (lambda x: x) or (lambda x: x.encode("latin1")) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name="api_options.proto", - package="", - syntax="proto2", - serialized_options=None, - serialized_pb=_b( - '\n\x11\x61pi_options.proto\x1a google/protobuf/descriptor.proto"\x06\n\x04void*F\n\rAPISourceType\x12\x0f\n\x0bSOURCE_BOTH\x10\x00\x12\x11\n\rSOURCE_SERVER\x10\x01\x12\x11\n\rSOURCE_CLIENT\x10\x02:E\n\x16needs_setup_connection\x12\x1e.google.protobuf.MethodOptions\x18\x8e\x08 \x01(\x08:\x04true:C\n\x14needs_authentication\x12\x1e.google.protobuf.MethodOptions\x18\x8f\x08 \x01(\x08:\x04true:/\n\x02id\x12\x1f.google.protobuf.MessageOptions\x18\x8c\x08 \x01(\r:\x01\x30:M\n\x06source\x12\x1f.google.protobuf.MessageOptions\x18\x8d\x08 \x01(\x0e\x32\x0e.APISourceType:\x0bSOURCE_BOTH:/\n\x05ifdef\x12\x1f.google.protobuf.MessageOptions\x18\x8e\x08 \x01(\t:3\n\x03log\x12\x1f.google.protobuf.MessageOptions\x18\x8f\x08 \x01(\x08:\x04true:9\n\x08no_delay\x12\x1f.google.protobuf.MessageOptions\x18\x90\x08 \x01(\x08:\x05\x66\x61lse' - ), - dependencies=[ - google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR, - ], -) - -_APISOURCETYPE = _descriptor.EnumDescriptor( - name="APISourceType", - full_name="APISourceType", - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name="SOURCE_BOTH", index=0, number=0, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="SOURCE_SERVER", index=1, number=1, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="SOURCE_CLIENT", index=2, number=2, serialized_options=None, type=None - ), - ], - containing_type=None, - serialized_options=None, - serialized_start=63, - serialized_end=133, -) -_sym_db.RegisterEnumDescriptor(_APISOURCETYPE) - -APISourceType = enum_type_wrapper.EnumTypeWrapper(_APISOURCETYPE) -SOURCE_BOTH = 0 -SOURCE_SERVER = 1 -SOURCE_CLIENT = 2 - -NEEDS_SETUP_CONNECTION_FIELD_NUMBER = 1038 -needs_setup_connection = _descriptor.FieldDescriptor( - name="needs_setup_connection", - full_name="needs_setup_connection", - index=0, - number=1038, - type=8, - cpp_type=7, - label=1, - has_default_value=True, - default_value=True, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=True, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, -) -NEEDS_AUTHENTICATION_FIELD_NUMBER = 1039 -needs_authentication = _descriptor.FieldDescriptor( - name="needs_authentication", - full_name="needs_authentication", - index=1, - number=1039, - type=8, - cpp_type=7, - label=1, - has_default_value=True, - default_value=True, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=True, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, -) -ID_FIELD_NUMBER = 1036 -id = _descriptor.FieldDescriptor( - name="id", - full_name="id", - index=2, - number=1036, - type=13, - cpp_type=3, - label=1, - has_default_value=True, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=True, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, -) -SOURCE_FIELD_NUMBER = 1037 -source = _descriptor.FieldDescriptor( - name="source", - full_name="source", - index=3, - number=1037, - type=14, - cpp_type=8, - label=1, - has_default_value=True, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=True, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, -) -IFDEF_FIELD_NUMBER = 1038 -ifdef = _descriptor.FieldDescriptor( - name="ifdef", - full_name="ifdef", - index=4, - number=1038, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=True, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, -) -LOG_FIELD_NUMBER = 1039 -log = _descriptor.FieldDescriptor( - name="log", - full_name="log", - index=5, - number=1039, - type=8, - cpp_type=7, - label=1, - has_default_value=True, - default_value=True, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=True, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, -) -NO_DELAY_FIELD_NUMBER = 1040 -no_delay = _descriptor.FieldDescriptor( - name="no_delay", - full_name="no_delay", - index=6, - number=1040, - type=8, - cpp_type=7, - label=1, - has_default_value=True, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=True, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, -) - - -_VOID = _descriptor.Descriptor( - name="void", - full_name="void", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto2", - extension_ranges=[], - oneofs=[], - serialized_start=55, - serialized_end=61, -) - -DESCRIPTOR.message_types_by_name["void"] = _VOID -DESCRIPTOR.enum_types_by_name["APISourceType"] = _APISOURCETYPE -DESCRIPTOR.extensions_by_name["needs_setup_connection"] = needs_setup_connection -DESCRIPTOR.extensions_by_name["needs_authentication"] = needs_authentication -DESCRIPTOR.extensions_by_name["id"] = id -DESCRIPTOR.extensions_by_name["source"] = source -DESCRIPTOR.extensions_by_name["ifdef"] = ifdef -DESCRIPTOR.extensions_by_name["log"] = log -DESCRIPTOR.extensions_by_name["no_delay"] = no_delay -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -void = _reflection.GeneratedProtocolMessageType( - "void", - (_message.Message,), - dict( - DESCRIPTOR=_VOID, - __module__="api_options_pb2" - # @@protoc_insertion_point(class_scope:void) - ), -) -_sym_db.RegisterMessage(void) - -google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension( - needs_setup_connection -) -google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension( - needs_authentication -) -google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(id) -source.enum_type = _APISOURCETYPE -google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(source) -google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(ifdef) -google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(log) -google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(no_delay) - -# @@protoc_insertion_point(module_scope) diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 26bf8647af..dba6f47d43 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -25,7 +25,7 @@ from subprocess import call # Generate with # protoc --python_out=script/api_protobuf -I esphome/components/api/ api_options.proto -import api_options_pb2 as pb +import aioesphomeapi.api_options_pb2 as pb import google.protobuf.descriptor_pb2 as descriptor file_header = "// This file was automatically generated with a tool.\n" @@ -546,7 +546,8 @@ def build_enum_type(desc): out += f" {v.name} = {v.number},\n" out += "};\n" - cpp = f"template<> const char *proto_enum_to_string(enums::{name} value) {{\n" + cpp = f"#ifdef HAS_PROTO_MESSAGE_DUMP\n" + cpp += f"template<> const char *proto_enum_to_string(enums::{name} value) {{\n" cpp += f" switch (value) {{\n" for v in desc.value: cpp += f" case enums::{v.name}:\n" @@ -555,6 +556,7 @@ def build_enum_type(desc): cpp += f' return "UNKNOWN";\n' cpp += f" }}\n" cpp += f"}}\n" + cpp += f"#endif\n" return out, cpp diff --git a/script/build_language_schema.py b/script/build_language_schema.py index 0b3cdf976d..4c8639a1b3 100644 --- a/script/build_language_schema.py +++ b/script/build_language_schema.py @@ -245,7 +245,7 @@ def do_esp32(): setEnum( output["esp32"]["schemas"]["CONFIG_SCHEMA"]["schema"]["config_vars"]["board"], - list(esp32_boards.BOARD_TO_VARIANT.keys()), + list(esp32_boards.BOARDS.keys()), ) @@ -283,6 +283,32 @@ def fix_script(): config_schema["is_list"] = True +def fix_menu(): + # # Menu has a recursive schema which is not kept properly + schemas = output["display_menu_base"][S_SCHEMAS] + # 1. Move items to a new schema + schemas["MENU_TYPES"] = { + S_TYPE: S_SCHEMA, + S_SCHEMA: { + S_CONFIG_VARS: { + "items": schemas["DISPLAY_MENU_BASE_SCHEMA"][S_SCHEMA][S_CONFIG_VARS][ + "items" + ] + } + }, + } + # 2. Remove items from the base schema + schemas["DISPLAY_MENU_BASE_SCHEMA"][S_SCHEMA][S_CONFIG_VARS].pop("items") + # 3. Add extends to this + schemas["DISPLAY_MENU_BASE_SCHEMA"][S_SCHEMA][S_EXTENDS].append( + "display_menu_base.MENU_TYPES" + ) + # 4. Configure menu items inside as recursive + menu = schemas["MENU_TYPES"][S_SCHEMA][S_CONFIG_VARS]["items"]["types"]["menu"] + menu[S_CONFIG_VARS].pop("items") + menu[S_EXTENDS] = ["display_menu_base.MENU_TYPES"] + + def get_logger_tags(): pattern = re.compile(r'^static const char \*const TAG = "(\w.*)";', re.MULTILINE) # tags not in components dir @@ -565,6 +591,7 @@ def build_schema(): fix_script() add_logger_tags() shrink() + fix_menu() # aggregate components, so all component info is in same file, otherwise we have dallas.json, dallas.sensor.json, etc. data = {} diff --git a/script/clang-tidy b/script/clang-tidy index 327b593008..5d2cba6eb5 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -82,6 +82,7 @@ def clang_options(idedata): "-mtext-section-literals", "-mfix-esp32-psram-cache-issue", "-mfix-esp32-psram-cache-strategy=memw", + "-fno-tree-switch-conversion", ) ) diff --git a/tests/test1.yaml b/tests/test1.yaml index eacaf9b38c..88cdb43fca 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1823,18 +1823,6 @@ light: to: 25 - single_light_id: ${roomname}_lights - - platform: shelly_dimmer - name: Shelly Dimmer Light - power: - name: Shelly Dimmer Power - voltage: - name: Shelly Dimmer Voltage - current: - name: Shelly Dimmer Current - max_brightness: 500 - firmware: "51.6" - uart_id: uart0 - remote_transmitter: - pin: 32 carrier_duty_percent: 100% diff --git a/tests/test3.yaml b/tests/test3.yaml index da704d90a3..9b4e530fbd 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -279,6 +279,10 @@ uart: tx_pin: GPIO4 rx_pin: GPIO5 baud_rate: 9600 + - id: uart11 + tx_pin: GPIO4 + rx_pin: GPIO5 + baud_rate: 9600 modbus: uart_id: uart1 @@ -1158,6 +1162,18 @@ climate: kp: 0.0 ki: 0.0 kd: 0.0 + max_integral: 0.0 + output_averaging_samples: 1 + derivative_averaging_samples: 1 + deadband_parameters: + threshold_high: 0.4 + threshold_low: -2.0 + kp_multiplier: 0.0 + ki_multiplier: 0.0 + kd_multiplier: 0.0 + deadband_output_averaging_samples: 1 + + sprinkler: - id: yard_sprinkler_ctrlr @@ -1358,6 +1374,17 @@ light: name: Sonoff D1 Dimmer id: d1_light restore_mode: RESTORE_DEFAULT_OFF + - platform: shelly_dimmer + name: "Shelly Dimmer Light" + power: + name: "Shelly Dimmer Power" + voltage: + name: "Shelly Dimmer Voltage" + current: + name: "Shelly Dimmer Current" + max_brightness: 500 + firmware: "51.6" + uart_id: uart11 servo: id: my_servo diff --git a/tests/test4.yaml b/tests/test4.yaml index a8077c625f..db7a123dbc 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -381,6 +381,7 @@ climate: target_temperature_datapoint: 3 current_temperature_multiplier: 0.5 target_temperature_multiplier: 0.5 + reports_fahrenheit: true switch: - platform: tuya diff --git a/tests/test5.yaml b/tests/test5.yaml index 131d2f5b7f..596ba5de4e 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -162,6 +162,11 @@ binary_sensor: then: - output.turn_off: Led7 + - platform: gpio + id: sn74hc165_pin_0 + pin: + sn74hc165: sn74hc165_hub + number: 0 @@ -240,6 +245,7 @@ number: step: 5 unit_of_measurement: "%" mode: slider + device_class: humidity on_value: - logger.log: format: Number changed to %f @@ -541,3 +547,11 @@ text_sensor: - ezo_pmp.arbitrary_command: id: hcl_pump command: D,? + +sn74hc165: + id: sn74hc165_hub + data_pin: GPIO12 + clock_pin: GPIO14 + load_pin: GPIO27 + clock_inhibit_pin: GPIO26 + sr_count: 4