1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-03 17:28:16 +00:00

Merge branch 'dev' into nvds-new-espnow

This commit is contained in:
NP v/d Spek 2024-12-12 19:53:40 +01:00 committed by GitHub
commit bd88263a88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
201 changed files with 3405 additions and 1250 deletions

View File

@ -7,28 +7,39 @@ Checks: >-
-boost-*, -boost-*,
-bugprone-easily-swappable-parameters, -bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result, -bugprone-implicit-widening-of-multiplication-result,
-bugprone-multi-level-implicit-pointer-conversion,
-bugprone-narrowing-conversions, -bugprone-narrowing-conversions,
-bugprone-signed-char-misuse, -bugprone-signed-char-misuse,
-bugprone-switch-missing-default-case,
-cert-dcl50-cpp, -cert-dcl50-cpp,
-cert-err33-c, -cert-err33-c,
-cert-err58-cpp, -cert-err58-cpp,
-cert-oop57-cpp, -cert-oop57-cpp,
-cert-str34-c, -cert-str34-c,
-clang-analyzer-optin.core.EnumCastOutOfRange,
-clang-analyzer-optin.cplusplus.UninitializedObject, -clang-analyzer-optin.cplusplus.UninitializedObject,
-clang-analyzer-osx.*, -clang-analyzer-osx.*,
-clang-diagnostic-delete-abstract-non-virtual-dtor, -clang-diagnostic-delete-abstract-non-virtual-dtor,
-clang-diagnostic-delete-non-abstract-non-virtual-dtor, -clang-diagnostic-delete-non-abstract-non-virtual-dtor,
-clang-diagnostic-deprecated-declarations,
-clang-diagnostic-ignored-optimization-argument, -clang-diagnostic-ignored-optimization-argument,
-clang-diagnostic-missing-field-initializers,
-clang-diagnostic-shadow-field, -clang-diagnostic-shadow-field,
-clang-diagnostic-unused-const-variable, -clang-diagnostic-unused-const-variable,
-clang-diagnostic-unused-parameter, -clang-diagnostic-unused-parameter,
-clang-diagnostic-vla-cxx-extension,
-concurrency-*, -concurrency-*,
-cppcoreguidelines-avoid-c-arrays, -cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-init-variables, -cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-to-enum,
-cppcoreguidelines-macro-usage, -cppcoreguidelines-macro-usage,
-cppcoreguidelines-missing-std-forward,
-cppcoreguidelines-narrowing-conversions, -cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-non-private-member-variables-in-classes, -cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-prefer-member-initializer, -cppcoreguidelines-prefer-member-initializer,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-bounds-constant-array-index,
@ -40,7 +51,9 @@ Checks: >-
-cppcoreguidelines-pro-type-static-cast-downcast, -cppcoreguidelines-pro-type-static-cast-downcast,
-cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg, -cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-rvalue-reference-param-not-moved,
-cppcoreguidelines-special-member-functions, -cppcoreguidelines-special-member-functions,
-cppcoreguidelines-use-default-member-init,
-cppcoreguidelines-virtual-class-destructor, -cppcoreguidelines-virtual-class-destructor,
-fuchsia-multiple-inheritance, -fuchsia-multiple-inheritance,
-fuchsia-overloaded-operator, -fuchsia-overloaded-operator,
@ -60,20 +73,32 @@ Checks: >-
-llvm-include-order, -llvm-include-order,
-llvm-qualified-auto, -llvm-qualified-auto,
-llvmlibc-*, -llvmlibc-*,
-misc-non-private-member-variables-in-classes, -misc-const-correctness,
-misc-include-cleaner,
-misc-no-recursion, -misc-no-recursion,
-misc-non-private-member-variables-in-classes,
-misc-unused-parameters, -misc-unused-parameters,
-modernize-avoid-c-arrays, -misc-use-anonymous-namespace,
-modernize-avoid-bind, -modernize-avoid-bind,
-modernize-avoid-c-arrays,
-modernize-concat-nested-namespaces, -modernize-concat-nested-namespaces,
-modernize-macro-to-enum,
-modernize-return-braced-init-list, -modernize-return-braced-init-list,
-modernize-type-traits,
-modernize-use-auto, -modernize-use-auto,
-modernize-use-constraints,
-modernize-use-default-member-init, -modernize-use-default-member-init,
-modernize-use-equals-default, -modernize-use-equals-default,
-modernize-use-trailing-return-type,
-modernize-use-nodiscard, -modernize-use-nodiscard,
-modernize-use-nullptr,
-modernize-use-nodiscard,
-modernize-use-nullptr,
-modernize-use-trailing-return-type,
-mpi-*, -mpi-*,
-objc-*, -objc-*,
-performance-enum-size,
-readability-avoid-nested-conditional-operator,
-readability-container-contains,
-readability-container-data-pointer, -readability-container-data-pointer,
-readability-convert-member-functions-to-static, -readability-convert-member-functions-to-static,
-readability-else-after-return, -readability-else-after-return,
@ -82,11 +107,14 @@ Checks: >-
-readability-isolate-declaration, -readability-isolate-declaration,
-readability-magic-numbers, -readability-magic-numbers,
-readability-make-member-function-const, -readability-make-member-function-const,
-readability-named-parameter,
-readability-redundant-casting,
-readability-redundant-inline-specifier,
-readability-redundant-member-init,
-readability-redundant-string-init, -readability-redundant-string-init,
-readability-uppercase-literal-suffix, -readability-uppercase-literal-suffix,
-readability-use-anyofallof, -readability-use-anyofallof,
WarningsAsErrors: '*' WarningsAsErrors: '*'
AnalyzeTemporaryDtors: false
FormatStyle: google FormatStyle: google
CheckOptions: CheckOptions:
- key: google-readability-function-size.StatementThreshold - key: google-readability-function-size.StatementThreshold

View File

@ -46,7 +46,7 @@ runs:
- name: Build and push to ghcr by digest - name: Build and push to ghcr by digest
id: build-ghcr id: build-ghcr
uses: docker/build-push-action@v6.9.0 uses: docker/build-push-action@v6.10.0
env: env:
DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false DOCKER_BUILD_RECORD_UPLOAD: false
@ -72,7 +72,7 @@ runs:
- name: Build and push to dockerhub by digest - name: Build and push to dockerhub by digest
id: build-dockerhub id: build-dockerhub
uses: docker/build-push-action@v6.9.0 uses: docker/build-push-action@v6.10.0
env: env:
DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false DOCKER_BUILD_RECORD_UPLOAD: false

View File

@ -22,7 +22,7 @@ runs:
python-version: ${{ inputs.python-version }} python-version: ${{ inputs.python-version }}
- name: Restore Python virtual environment - name: Restore Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache/restore@v4.1.2 uses: actions/cache/restore@v4.2.0
with: with:
path: venv path: venv
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length

View File

@ -30,7 +30,7 @@ concurrency:
jobs: jobs:
common: common:
name: Create common environment name: Create common environment
runs-on: ubuntu-latest runs-on: ubuntu-24.04
outputs: outputs:
cache-key: ${{ steps.cache-key.outputs.key }} cache-key: ${{ steps.cache-key.outputs.key }}
steps: steps:
@ -46,7 +46,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment - name: Restore Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v4.1.2 uses: actions/cache@v4.2.0
with: with:
path: venv path: venv
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length
@ -62,7 +62,7 @@ jobs:
black: black:
name: Check black name: Check black
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -83,7 +83,7 @@ jobs:
flake8: flake8:
name: Check flake8 name: Check flake8
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -104,7 +104,7 @@ jobs:
pylint: pylint:
name: Check pylint name: Check pylint
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -125,7 +125,7 @@ jobs:
pyupgrade: pyupgrade:
name: Check pyupgrade name: Check pyupgrade
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -146,7 +146,7 @@ jobs:
ci-custom: ci-custom:
name: Run script/ci-custom name: Run script/ci-custom
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -225,7 +225,7 @@ jobs:
clang-format: clang-format:
name: Check clang-format name: Check clang-format
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -251,7 +251,7 @@ jobs:
clang-tidy: clang-tidy:
name: ${{ matrix.name }} name: ${{ matrix.name }}
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
- black - black
@ -302,23 +302,18 @@ jobs:
- name: Cache platformio - name: Cache platformio
if: github.ref == 'refs/heads/dev' if: github.ref == 'refs/heads/dev'
uses: actions/cache@v4.1.2 uses: actions/cache@v4.2.0
with: with:
path: ~/.platformio path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }} key: platformio-${{ matrix.pio_cache_key }}
- name: Cache platformio - name: Cache platformio
if: github.ref != 'refs/heads/dev' if: github.ref != 'refs/heads/dev'
uses: actions/cache/restore@v4.1.2 uses: actions/cache/restore@v4.2.0
with: with:
path: ~/.platformio path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }} key: platformio-${{ matrix.pio_cache_key }}
- name: Install clang-tidy
run: |
sudo apt-get update
sudo apt-get install clang-tidy-14
- name: Register problem matchers - name: Register problem matchers
run: | run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json" echo "::add-matcher::.github/workflows/matchers/gcc.json"
@ -345,7 +340,7 @@ jobs:
if: always() if: always()
list-components: list-components:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
@ -387,7 +382,7 @@ jobs:
test-build-components: test-build-components:
name: Component test ${{ matrix.file }} name: Component test ${{ matrix.file }}
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
- list-components - list-components
@ -421,7 +416,7 @@ jobs:
test-build-components-splitter: test-build-components-splitter:
name: Split components for testing into 20 groups maximum name: Split components for testing into 20 groups maximum
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
- list-components - list-components
@ -439,7 +434,7 @@ jobs:
test-build-components-split: test-build-components-split:
name: Test split components name: Test split components
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
- list-components - list-components
@ -483,7 +478,7 @@ jobs:
ci-status: ci-status:
name: CI Status name: CI Status
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
- black - black

View File

@ -65,7 +65,7 @@ jobs:
pip3 install build pip3 install build
python3 -m build python3 -m build
- name: Publish - name: Publish
uses: pypa/gh-action-pypi-publish@v1.12.2 uses: pypa/gh-action-pypi-publish@v1.12.3
deploy-docker: deploy-docker:
name: Build ESPHome ${{ matrix.platform }} name: Build ESPHome ${{ matrix.platform }}

View File

@ -180,6 +180,7 @@ esphome/components/haier/text_sensor/* @paveldn
esphome/components/havells_solar/* @sourabhjaiswal esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/hbridge/fan/* @WeekendWarrior esphome/components/hbridge/fan/* @WeekendWarrior
esphome/components/hbridge/light/* @DotNetDann esphome/components/hbridge/light/* @DotNetDann
esphome/components/hbridge/switch/* @dwmw2
esphome/components/he60r/* @clydebarrow esphome/components/he60r/* @clydebarrow
esphome/components/heatpumpir/* @rob-deutsch esphome/components/heatpumpir/* @rob-deutsch
esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/hitachi_ac424/* @sourabhjaiswal
@ -355,6 +356,8 @@ esphome/components/sdl/* @clydebarrow
esphome/components/sdm_meter/* @jesserockz @polyfaces esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sdp3x/* @Azimath esphome/components/sdp3x/* @Azimath
esphome/components/seeed_mr24hpc1/* @limengdu esphome/components/seeed_mr24hpc1/* @limengdu
esphome/components/seeed_mr60bha2/* @limengdu
esphome/components/seeed_mr60fda2/* @limengdu
esphome/components/selec_meter/* @sourabhjaiswal esphome/components/selec_meter/* @sourabhjaiswal
esphome/components/select/* @esphome/core esphome/components/select/* @esphome/core
esphome/components/sen0321/* @notjj esphome/components/sen0321/* @notjj

View File

@ -163,6 +163,18 @@ ENTRYPOINT ["/entrypoint.sh"]
CMD ["dashboard", "/config"] CMD ["dashboard", "/config"]
ARG BUILD_VERSION=dev
# Labels
LABEL \
org.opencontainers.image.authors="The ESPHome Authors" \
org.opencontainers.image.title="ESPHome" \
org.opencontainers.image.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \
org.opencontainers.image.url="https://esphome.io/" \
org.opencontainers.image.documentation="https://esphome.io/" \
org.opencontainers.image.source="https://github.com/esphome/esphome" \
org.opencontainers.image.licenses="ESPHome" \
org.opencontainers.image.version=${BUILD_VERSION}
# ======================= hassio-type image ======================= # ======================= hassio-type image =======================
@ -194,7 +206,7 @@ RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
# Labels # Labels
LABEL \ LABEL \
io.hass.name="ESPHome" \ io.hass.name="ESPHome" \
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ io.hass.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \
io.hass.type="addon" \ io.hass.type="addon" \
io.hass.version="${BUILD_VERSION}" io.hass.version="${BUILD_VERSION}"
# io.hass.arch is inherited from addon-debian-base # io.hass.arch is inherited from addon-debian-base
@ -209,17 +221,22 @@ ENV \
PLATFORMIO_CORE_DIR=/esphome/.temp/platformio PLATFORMIO_CORE_DIR=/esphome/.temp/platformio
RUN \ RUN \
apt-get update \ curl -L https://apt.llvm.org/llvm-snapshot.gpg.key -o /etc/apt/trusted.gpg.d/apt.llvm.org.asc \
&& echo "deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-18 main" > /etc/apt/sources.list.d/llvm.sources.list \
&& apt-get update \
# Use pinned versions so that we get updates with build caching # Use pinned versions so that we get updates with build caching
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
clang-format-13=1:13.0.1-11+b2 \ clang-format-13=1:13.0.1-11+b2 \
clang-tidy-14=1:14.0.6-12 \
patch=2.7.6-7 \ patch=2.7.6-7 \
software-properties-common=0.99.30-4.1~deb12u1 \ software-properties-common=0.99.30-4.1~deb12u1 \
nano=7.2-1+deb12u1 \ nano=7.2-1+deb12u1 \
build-essential=12.9 \ build-essential=12.9 \
python3-dev=3.11.2-1+b1 \ python3-dev=3.11.2-1+b1 \
&& rm -rf \ && if [ "$TARGETARCH$TARGETVARIANT" != "armv7" ]; then \
# move this up after armv7 is retired
apt-get install -y --no-install-recommends clang-tidy-18=1:18.1.8~++20240731024826+3b5b5c1ec4a3-1~exp1~20240731144843.145 ; \
fi; \
rm -rf \
/tmp/* \ /tmp/* \
/var/{cache,log}/* \ /var/{cache,log}/* \
/var/lib/apt/lists/* /var/lib/apt/lists/*

View File

@ -363,7 +363,7 @@ def upload_program(config, args, host):
from esphome import espota2 from esphome import espota2
remote_port = ota_conf[CONF_PORT] remote_port = int(ota_conf[CONF_PORT])
password = ota_conf.get(CONF_PASSWORD, "") password = ota_conf.get(CONF_PASSWORD, "")
if ( if (

View File

@ -3,13 +3,12 @@
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h" #include "esphome/components/voltage_sampler/voltage_sampler.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <esp_adc_cal.h> #include <esp_adc_cal.h>
#include "driver/adc.h" #include "driver/adc.h"
#endif #endif // USE_ESP32
namespace esphome { namespace esphome {
namespace adc { namespace adc {
@ -43,7 +42,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
this->channel1_ = ADC1_CHANNEL_MAX; this->channel1_ = ADC1_CHANNEL_MAX;
} }
void set_autorange(bool autorange) { this->autorange_ = autorange; } void set_autorange(bool autorange) { this->autorange_ = autorange; }
#endif #endif // USE_ESP32
/// Update ADC values /// Update ADC values
void update() override; void update() override;
@ -59,11 +58,11 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#ifdef USE_ESP8266 #ifdef USE_ESP8266
std::string unique_id() override; std::string unique_id() override;
#endif #endif // USE_ESP8266
#ifdef USE_RP2040 #ifdef USE_RP2040
void set_is_temperature() { this->is_temperature_ = true; } void set_is_temperature() { this->is_temperature_ = true; }
#endif #endif // USE_RP2040
protected: protected:
InternalGPIOPin *pin_; InternalGPIOPin *pin_;
@ -72,7 +71,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#ifdef USE_RP2040 #ifdef USE_RP2040
bool is_temperature_{false}; bool is_temperature_{false};
#endif #endif // USE_RP2040
#ifdef USE_ESP32 #ifdef USE_ESP32
adc_atten_t attenuation_{ADC_ATTEN_DB_0}; adc_atten_t attenuation_{ADC_ATTEN_DB_0};
@ -83,8 +82,8 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {}; esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {};
#else #else
esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {}; esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {};
#endif #endif // ESP_IDF_VERSION_MAJOR
#endif #endif // USE_ESP32
}; };
} // namespace adc } // namespace adc

View File

@ -0,0 +1,24 @@
#include "adc_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace adc {
static const char *const TAG = "adc.common";
void ADCSensor::update() {
float value_v = this->sample();
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
this->publish_state(value_v);
}
void ADCSensor::set_sample_count(uint8_t sample_count) {
if (sample_count != 0) {
this->sample_count_ = sample_count;
}
}
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
} // namespace adc
} // namespace esphome

View File

@ -1,30 +1,13 @@
#ifdef USE_ESP32
#include "adc_sensor.h" #include "adc_sensor.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#ifdef USE_ESP8266
#ifdef USE_ADC_SENSOR_VCC
#include <Esp.h>
ADC_MODE(ADC_VCC)
#else
#include <Arduino.h>
#endif
#endif
#ifdef USE_RP2040
#ifdef CYW43_USES_VSYS_PIN
#include "pico/cyw43_arch.h"
#endif
#include <hardware/adc.h>
#endif
namespace esphome { namespace esphome {
namespace adc { namespace adc {
static const char *const TAG = "adc"; static const char *const TAG = "adc.esp32";
// 13-bit for S2, 12-bit for all other ESP32 variants
#ifdef USE_ESP32
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1); static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
#ifndef SOC_ADC_RTC_MAX_BITWIDTH #ifndef SOC_ADC_RTC_MAX_BITWIDTH
@ -32,24 +15,15 @@ static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_widt
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13; static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13;
#else #else
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12; static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
#endif #endif // USE_ESP32_VARIANT_ESP32S2
#endif #endif // SOC_ADC_RTC_MAX_BITWIDTH
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; // 4095 (12 bit) or 8191 (13 bit) static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1;
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit) static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1;
#endif
#ifdef USE_RP2040 void ADCSensor::setup() {
extern "C"
#endif
void
ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040)
this->pin_->setup();
#endif
#ifdef USE_ESP32
if (this->channel1_ != ADC1_CHANNEL_MAX) { if (this->channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS); adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
if (!this->autorange_) { if (!this->autorange_) {
@ -61,7 +35,6 @@ extern "C"
} }
} }
// load characteristics for each attenuation
for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) { for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) {
auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2; auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS, auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
@ -79,31 +52,10 @@ extern "C"
break; break;
} }
} }
#endif // USE_ESP32
#ifdef USE_RP2040
static bool initialized = false;
if (!initialized) {
adc_init();
initialized = true;
}
#endif
ESP_LOGCONFIG(TAG, "ADC '%s' setup finished!", this->get_name().c_str());
} }
void ADCSensor::dump_config() { void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this); LOG_SENSOR("", "ADC Sensor", this);
#if defined(USE_ESP8266) || defined(USE_LIBRETINY)
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
LOG_PIN(" Pin: ", this->pin_);
#endif
#endif // USE_ESP8266 || USE_LIBRETINY
#ifdef USE_ESP32
LOG_PIN(" Pin: ", this->pin_); LOG_PIN(" Pin: ", this->pin_);
if (this->autorange_) { if (this->autorange_) {
ESP_LOGCONFIG(TAG, " Attenuation: auto"); ESP_LOGCONFIG(TAG, " Attenuation: auto");
@ -125,55 +77,10 @@ void ADCSensor::dump_config() {
break; break;
} }
} }
#endif // USE_ESP32
#ifdef USE_RP2040
if (this->is_temperature_) {
ESP_LOGCONFIG(TAG, " Pin: Temperature");
} else {
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
}
#endif // USE_RP2040
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
} }
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
void ADCSensor::update() {
float value_v = this->sample();
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
this->publish_state(value_v);
}
void ADCSensor::set_sample_count(uint8_t sample_count) {
if (sample_count != 0) {
this->sample_count_ = sample_count;
}
}
#ifdef USE_ESP8266
float ADCSensor::sample() {
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
#ifdef USE_ADC_SENSOR_VCC
raw += ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
#else
raw += analogRead(this->pin_->get_pin()); // NOLINT
#endif
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
if (this->output_raw_) {
return raw;
}
return raw / 1024.0f;
}
#endif
#ifdef USE_ESP32
float ADCSensor::sample() { float ADCSensor::sample() {
if (!this->autorange_) { if (!this->autorange_) {
uint32_t sum = 0; uint32_t sum = 0;
@ -240,93 +147,17 @@ float ADCSensor::sample() {
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]); uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]); uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
// Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
uint32_t c12 = std::min(raw12, ADC_HALF); uint32_t c12 = std::min(raw12, ADC_HALF);
uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF); uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF); uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF); uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
// max theoretical csum value is 4096*4 = 16384
uint32_t csum = c12 + c6 + c2 + c0; uint32_t csum = c12 + c6 + c2 + c0;
// each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32
uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0); uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
return mv_scaled / (float) (csum * 1000U); return mv_scaled / (float) (csum * 1000U);
} }
#endif // USE_ESP32
#ifdef USE_RP2040
float ADCSensor::sample() {
if (this->is_temperature_) {
adc_set_temp_sensor_enabled(true);
delay(1);
adc_select_input(4);
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += adc_read();
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
adc_set_temp_sensor_enabled(false);
if (this->output_raw_) {
return raw;
}
return raw * 3.3f / 4096.0f;
} else {
uint8_t pin = this->pin_->get_pin();
#ifdef CYW43_USES_VSYS_PIN
if (pin == PICO_VSYS_PIN) {
// Measuring VSYS on Raspberry Pico W needs to be wrapped with
// `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in
// https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and
// VSYS ADC both share GPIO29
cyw43_thread_enter();
}
#endif // CYW43_USES_VSYS_PIN
adc_gpio_init(pin);
adc_select_input(pin - 26);
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += adc_read();
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
#ifdef CYW43_USES_VSYS_PIN
if (pin == PICO_VSYS_PIN) {
cyw43_thread_exit();
}
#endif // CYW43_USES_VSYS_PIN
if (this->output_raw_) {
return raw;
}
float coeff = pin == PICO_VSYS_PIN ? 3.0 : 1.0;
return raw * 3.3f / 4096.0f * coeff;
}
}
#endif
#ifdef USE_LIBRETINY
float ADCSensor::sample() {
uint32_t raw = 0;
if (this->output_raw_) {
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += analogRead(this->pin_->get_pin()); // NOLINT
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
return raw;
}
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += analogReadVoltage(this->pin_->get_pin()); // NOLINT
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
return raw / 1000.0f;
}
#endif // USE_LIBRETINY
#ifdef USE_ESP8266
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
#endif
} // namespace adc } // namespace adc
} // namespace esphome } // namespace esphome
#endif // USE_ESP32

View File

@ -0,0 +1,58 @@
#ifdef USE_ESP8266
#include "adc_sensor.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#ifdef USE_ADC_SENSOR_VCC
#include <Esp.h>
ADC_MODE(ADC_VCC)
#else
#include <Arduino.h>
#endif // USE_ADC_SENSOR_VCC
namespace esphome {
namespace adc {
static const char *const TAG = "adc.esp8266";
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#ifndef USE_ADC_SENSOR_VCC
this->pin_->setup();
#endif
}
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
LOG_UPDATE_INTERVAL(this);
}
float ADCSensor::sample() {
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
#ifdef USE_ADC_SENSOR_VCC
raw += ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
#else
raw += analogRead(this->pin_->get_pin()); // NOLINT
#endif // USE_ADC_SENSOR_VCC
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
if (this->output_raw_) {
return raw;
}
return raw / 1024.0f;
}
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
} // namespace adc
} // namespace esphome
#endif // USE_ESP8266

View File

@ -0,0 +1,93 @@
#ifdef USE_RP2040
#include "adc_sensor.h"
#include "esphome/core/log.h"
#ifdef CYW43_USES_VSYS_PIN
#include "pico/cyw43_arch.h"
#endif // CYW43_USES_VSYS_PIN
#include <hardware/adc.h>
namespace esphome {
namespace adc {
static const char *const TAG = "adc.rp2040";
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
static bool initialized = false;
if (!initialized) {
adc_init();
initialized = true;
}
}
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
if (this->is_temperature_) {
ESP_LOGCONFIG(TAG, " Pin: Temperature");
} else {
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
}
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
LOG_UPDATE_INTERVAL(this);
}
float ADCSensor::sample() {
if (this->is_temperature_) {
adc_set_temp_sensor_enabled(true);
delay(1);
adc_select_input(4);
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += adc_read();
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
adc_set_temp_sensor_enabled(false);
if (this->output_raw_) {
return raw;
}
return raw * 3.3f / 4096.0f;
}
uint8_t pin = this->pin_->get_pin();
#ifdef CYW43_USES_VSYS_PIN
if (pin == PICO_VSYS_PIN) {
// Measuring VSYS on Raspberry Pico W needs to be wrapped with
// `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in
// https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and
// VSYS ADC both share GPIO29
cyw43_thread_enter();
}
#endif // CYW43_USES_VSYS_PIN
adc_gpio_init(pin);
adc_select_input(pin - 26);
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += adc_read();
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
#ifdef CYW43_USES_VSYS_PIN
if (pin == PICO_VSYS_PIN) {
cyw43_thread_exit();
}
#endif // CYW43_USES_VSYS_PIN
if (this->output_raw_) {
return raw;
}
float coeff = pin == PICO_VSYS_PIN ? 3.0f : 1.0f;
return raw * 3.3f / 4096.0f * coeff;
}
} // namespace adc
} // namespace esphome
#endif // USE_RP2040

View File

@ -72,10 +72,9 @@ void AlarmControlPanelCall::validate_() {
this->state_.reset(); this->state_.reset();
return; return;
} }
if (state == ACP_STATE_DISARMED && if (state == ACP_STATE_DISARMED && !this->parent_->is_state_armed(this->parent_->get_state()) &&
!(this->parent_->is_state_armed(this->parent_->get_state()) || this->parent_->get_state() != ACP_STATE_PENDING && this->parent_->get_state() != ACP_STATE_ARMING &&
this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_ARMING || this->parent_->get_state() != ACP_STATE_TRIGGERED) {
this->parent_->get_state() == ACP_STATE_TRIGGERED)) {
ESP_LOGW(TAG, "Cannot disarm when not armed"); ESP_LOGW(TAG, "Cannot disarm when not armed");
this->state_.reset(); this->state_.reset();
return; return;

View File

@ -2,7 +2,6 @@ import logging
from esphome import automation, core from esphome import automation, core
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import font
import esphome.components.image as espImage import esphome.components.image as espImage
from esphome.components.image import ( from esphome.components.image import (
CONF_USE_TRANSPARENCY, CONF_USE_TRANSPARENCY,
@ -131,7 +130,7 @@ ANIMATION_SCHEMA = cv.Schema(
) )
) )
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA) CONFIG_SCHEMA = ANIMATION_SCHEMA
NEXT_FRAME_SCHEMA = automation.maybe_simple_id( NEXT_FRAME_SCHEMA = automation.maybe_simple_id(
{ {

View File

@ -122,7 +122,8 @@ void APDS9306::update() {
this->status_clear_warning(); this->status_clear_warning();
if (!(status &= 0b00001000)) { // No new data status &= 0b00001000;
if (!status) { // No new data
return; return;
} }

View File

@ -15,7 +15,7 @@ from esphome.components.libretiny.const import (
) )
from esphome.core import CORE from esphome.core import CORE
from .boards import BK72XX_BOARDS, BK72XX_BOARD_PINS from .boards import BK72XX_BOARD_PINS, BK72XX_BOARDS
CODEOWNERS = ["@kuba2k2"] CODEOWNERS = ["@kuba2k2"]
AUTO_LOAD = ["libretiny"] AUTO_LOAD = ["libretiny"]

View File

@ -43,7 +43,7 @@ bool CSE7766Component::check_byte_() {
uint8_t index = this->raw_data_index_; uint8_t index = this->raw_data_index_;
uint8_t byte = this->raw_data_[index]; uint8_t byte = this->raw_data_[index];
if (index == 0) { if (index == 0) {
return !((byte != 0x55) && ((byte & 0xF0) != 0xF0) && (byte != 0xAA)); return (byte == 0x55) || ((byte & 0xF0) == 0xF0) || (byte == 0xAA);
} }
if (index == 1) { if (index == 1) {

View File

@ -60,9 +60,7 @@ ESPTime DateTimeEntity::state_as_esptime() const {
obj.hour = this->hour_; obj.hour = this->hour_;
obj.minute = this->minute_; obj.minute = this->minute_;
obj.second = this->second_; obj.second = this->second_;
obj.day_of_week = 1; // Required to be valid for recalc_timestamp_local but not used. obj.recalc_timestamp_local();
obj.day_of_year = 1; // Required to be valid for recalc_timestamp_local but not used.
obj.recalc_timestamp_local(false);
return obj; return obj;
} }

View File

@ -52,11 +52,11 @@ void DeepSleepComponent::dump_config_platform_() {
bool DeepSleepComponent::prepare_to_sleep_() { bool DeepSleepComponent::prepare_to_sleep_() {
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr && if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr &&
!this->sleep_duration_.has_value() && this->wakeup_pin_->digital_read()) { this->wakeup_pin_->digital_read()) {
// Defer deep sleep until inactive // Defer deep sleep until inactive
if (!this->next_enter_deep_sleep_) { if (!this->next_enter_deep_sleep_) {
this->status_set_warning(); this->status_set_warning();
ESP_LOGW(TAG, "Waiting for pin_ to switch state to enter deep sleep..."); ESP_LOGW(TAG, "Waiting wakeup pin state change to enter deep sleep...");
} }
this->next_enter_deep_sleep_ = true; this->next_enter_deep_sleep_ = true;
return false; return false;

View File

@ -135,7 +135,8 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
// Wait for falling edge // Wait for falling edge
while (this->pin_->digital_read()) { while (this->pin_->digital_read()) {
if ((end_time = micros()) - start_time > 90) { end_time = micros();
if (end_time - start_time > 90) {
if (i < 0) { if (i < 0) {
error_code = 3; error_code = 3;
} else { } else {

View File

@ -1,6 +1,6 @@
#include "display.h" #include "display.h"
#include "display_color_utils.h"
#include <utility> #include <utility>
#include "display_color_utils.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
@ -662,20 +662,24 @@ void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) {
if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to)) if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
this->trigger(from, to); this->trigger(from, to);
} }
void Display::strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time) { void Display::strftime(int x, int y, BaseFont *font, Color color, Color background, TextAlign align, const char *format,
ESPTime time) {
char buffer[64]; char buffer[64];
size_t ret = time.strftime(buffer, sizeof(buffer), format); size_t ret = time.strftime(buffer, sizeof(buffer), format);
if (ret > 0) if (ret > 0)
this->print(x, y, font, color, align, buffer); this->print(x, y, font, color, align, buffer, background);
}
void Display::strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time) {
this->strftime(x, y, font, color, COLOR_OFF, align, format, time);
} }
void Display::strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time) { void Display::strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time) {
this->strftime(x, y, font, color, TextAlign::TOP_LEFT, format, time); this->strftime(x, y, font, color, COLOR_OFF, TextAlign::TOP_LEFT, format, time);
} }
void Display::strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time) { void Display::strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time) {
this->strftime(x, y, font, COLOR_ON, align, format, time); this->strftime(x, y, font, COLOR_ON, COLOR_OFF, align, format, time);
} }
void Display::strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) { void Display::strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) {
this->strftime(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, time); this->strftime(x, y, font, COLOR_ON, COLOR_OFF, TextAlign::TOP_LEFT, format, time);
} }
void Display::start_clipping(Rect rect) { void Display::start_clipping(Rect rect) {

View File

@ -437,6 +437,20 @@ class Display : public PollingComponent {
*/ */
void printf(int x, int y, BaseFont *font, const char *format, ...) __attribute__((format(printf, 5, 6))); void printf(int x, int y, BaseFont *font, const char *format, ...) __attribute__((format(printf, 5, 6)));
/** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`.
*
* @param x The x coordinate of the text alignment anchor point.
* @param y The y coordinate of the text alignment anchor point.
* @param font The font to draw the text with.
* @param color The color to draw the text with.
* @param background The background color to draw the text with.
* @param align The alignment of the text.
* @param format The format to use.
* @param ... The arguments to use for the text formatting.
*/
void strftime(int x, int y, BaseFont *font, Color color, Color background, TextAlign align, const char *format,
ESPTime time) __attribute__((format(strftime, 8, 0)));
/** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`. /** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`.
* *
* @param x The x coordinate of the text alignment anchor point. * @param x The x coordinate of the text alignment anchor point.

View File

@ -280,7 +280,7 @@ bool DisplayMenuComponent::cursor_down_() {
bool DisplayMenuComponent::enter_menu_() { bool DisplayMenuComponent::enter_menu_() {
this->displayed_item_->on_leave(); this->displayed_item_->on_leave();
this->displayed_item_ = static_cast<MenuItemMenu *>(this->get_selected_item_()); this->displayed_item_ = static_cast<MenuItemMenu *>(this->get_selected_item_());
this->selection_stack_.push_front({this->top_index_, this->cursor_index_}); this->selection_stack_.emplace_front(this->top_index_, this->cursor_index_);
this->cursor_index_ = this->top_index_ = 0; this->cursor_index_ = this->top_index_ = 0;
this->displayed_item_->on_enter(); this->displayed_item_->on_enter();

View File

@ -296,7 +296,7 @@ void Dsmr::dump_config() {
} }
void Dsmr::set_decryption_key(const std::string &decryption_key) { void Dsmr::set_decryption_key(const std::string &decryption_key) {
if (decryption_key.length() == 0) { if (decryption_key.empty()) {
ESP_LOGI(TAG, "Disabling decryption"); ESP_LOGI(TAG, "Disabling decryption");
this->decryption_key_.clear(); this->decryption_key_.clear();
if (this->crypt_telegram_ != nullptr) { if (this->crypt_telegram_ != nullptr) {

View File

@ -65,6 +65,8 @@ _LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
AUTO_LOAD = ["preferences"] AUTO_LOAD = ["preferences"]
CONF_RELEASE = "release"
def set_core_data(config): def set_core_data(config):
CORE.data[KEY_ESP32] = {} CORE.data[KEY_ESP32] = {}
@ -216,11 +218,17 @@ def _format_framework_arduino_version(ver: cv.Version) -> str:
return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
def _format_framework_espidf_version(ver: cv.Version) -> str: def _format_framework_espidf_version(
ver: cv.Version, release: str, for_platformio: bool
) -> str:
# format the given arduino (https://github.com/espressif/esp-idf/releases) version to # format the given arduino (https://github.com/espressif/esp-idf/releases) version to
# a PIO platformio/framework-espidf value # a PIO platformio/framework-espidf value
# List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf # List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf
return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" if for_platformio:
return f"platformio/framework-espidf@~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
if release:
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}.{release}/esp-idf-v{str(ver)}.zip"
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}/esp-idf-v{str(ver)}.zip"
# NOTE: Keep this in mind when updating the recommended version: # NOTE: Keep this in mind when updating the recommended version:
@ -241,11 +249,33 @@ ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0)
# The default/recommended esp-idf framework version # The default/recommended esp-idf framework version
# - https://github.com/espressif/esp-idf/releases # - https://github.com/espressif/esp-idf/releases
# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf # - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 8) RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(5, 1, 5)
# The platformio/espressif32 version to use for esp-idf frameworks # The platformio/espressif32 version to use for esp-idf frameworks
# - https://github.com/platformio/platform-espressif32/releases # - https://github.com/platformio/platform-espressif32/releases
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 # - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
ESP_IDF_PLATFORM_VERSION = cv.Version(5, 4, 0) ESP_IDF_PLATFORM_VERSION = cv.Version(51, 3, 7)
# List based on https://registry.platformio.org/tools/platformio/framework-espidf/versions
SUPPORTED_PLATFORMIO_ESP_IDF_5X = [
cv.Version(5, 3, 1),
cv.Version(5, 3, 0),
cv.Version(5, 2, 2),
cv.Version(5, 2, 1),
cv.Version(5, 1, 2),
cv.Version(5, 1, 1),
cv.Version(5, 1, 0),
cv.Version(5, 0, 2),
cv.Version(5, 0, 1),
cv.Version(5, 0, 0),
]
# pioarduino versions that don't require a release number
# List based on https://github.com/pioarduino/esp-idf/releases
SUPPORTED_PIOARDUINO_ESP_IDF_5X = [
cv.Version(5, 3, 1),
cv.Version(5, 3, 0),
cv.Version(5, 1, 5),
]
def _arduino_check_versions(value): def _arduino_check_versions(value):
@ -286,8 +316,8 @@ def _arduino_check_versions(value):
def _esp_idf_check_versions(value): def _esp_idf_check_versions(value):
value = value.copy() value = value.copy()
lookups = { lookups = {
"dev": (cv.Version(5, 1, 2), "https://github.com/espressif/esp-idf.git"), "dev": (cv.Version(5, 1, 5), "https://github.com/espressif/esp-idf.git"),
"latest": (cv.Version(5, 1, 2), None), "latest": (cv.Version(5, 1, 5), None),
"recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None), "recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None),
} }
@ -305,13 +335,51 @@ def _esp_idf_check_versions(value):
if version < cv.Version(4, 0, 0): if version < cv.Version(4, 0, 0):
raise cv.Invalid("Only ESP-IDF 4.0+ is supported.") raise cv.Invalid("Only ESP-IDF 4.0+ is supported.")
value[CONF_VERSION] = str(version) # flag this for later *before* we set value[CONF_PLATFORM_VERSION] below
value[CONF_SOURCE] = source or _format_framework_espidf_version(version) has_platform_ver = CONF_PLATFORM_VERSION in value
value[CONF_PLATFORM_VERSION] = value.get( value[CONF_PLATFORM_VERSION] = value.get(
CONF_PLATFORM_VERSION, _parse_platform_version(str(ESP_IDF_PLATFORM_VERSION)) CONF_PLATFORM_VERSION, _parse_platform_version(str(ESP_IDF_PLATFORM_VERSION))
) )
if (
(is_platformio := _platform_is_platformio(value[CONF_PLATFORM_VERSION]))
and version.major >= 5
and version not in SUPPORTED_PLATFORMIO_ESP_IDF_5X
):
raise cv.Invalid(
f"ESP-IDF {str(version)} not supported by platformio/espressif32"
)
if (
version.major < 5
or (
version in SUPPORTED_PLATFORMIO_ESP_IDF_5X
and version not in SUPPORTED_PIOARDUINO_ESP_IDF_5X
)
) and not has_platform_ver:
raise cv.Invalid(
f"ESP-IDF {value[CONF_VERSION]} may be supported by platformio/espressif32; please specify '{CONF_PLATFORM_VERSION}'"
)
if (
not is_platformio
and CONF_RELEASE not in value
and version not in SUPPORTED_PIOARDUINO_ESP_IDF_5X
):
raise cv.Invalid(
f"ESP-IDF {value[CONF_VERSION]} is not available with pioarduino; you may need to specify '{CONF_RELEASE}'"
)
value[CONF_VERSION] = str(version)
value[CONF_SOURCE] = source or _format_framework_espidf_version(
version, value.get(CONF_RELEASE, None), is_platformio
)
if value[CONF_SOURCE].startswith("http"):
# prefix is necessary or platformio will complain with a cryptic error
value[CONF_SOURCE] = f"framework-espidf@{value[CONF_SOURCE]}"
if version != RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION: if version != RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION:
_LOGGER.warning( _LOGGER.warning(
"The selected ESP-IDF framework version is not the recommended one. " "The selected ESP-IDF framework version is not the recommended one. "
@ -323,6 +391,12 @@ def _esp_idf_check_versions(value):
def _parse_platform_version(value): def _parse_platform_version(value):
try: try:
ver = cv.Version.parse(cv.version_number(value))
if ver.major >= 50: # a pioarduino version
if "-" in value:
# maybe a release candidate?...definitely not our default, just use it as-is...
return f"https://github.com/pioarduino/platform-espressif32.git#{value}"
return f"https://github.com/pioarduino/platform-espressif32.git#{ver.major}.{ver.minor:02d}.{ver.patch:02d}"
# if platform version is a valid version constraint, prefix the default package # if platform version is a valid version constraint, prefix the default package
cv.platformio_version_constraint(value) cv.platformio_version_constraint(value)
return f"platformio/espressif32@{value}" return f"platformio/espressif32@{value}"
@ -330,6 +404,14 @@ def _parse_platform_version(value):
return value return value
def _platform_is_platformio(value):
try:
ver = cv.Version.parse(cv.version_number(value))
return ver.major < 50
except cv.Invalid:
return "platformio" in value
def _detect_variant(value): def _detect_variant(value):
board = value[CONF_BOARD] board = value[CONF_BOARD]
if board in BOARDS: if board in BOARDS:
@ -355,24 +437,20 @@ def _detect_variant(value):
def final_validate(config): def final_validate(config):
if CONF_PLATFORMIO_OPTIONS not in fv.full_config.get()[CONF_ESPHOME]: if not (
pio_options := fv.full_config.get()[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS)
):
# Not specified or empty
return config return config
pio_flash_size_key = "board_upload.flash_size" pio_flash_size_key = "board_upload.flash_size"
pio_partitions_key = "board_build.partitions" pio_partitions_key = "board_build.partitions"
if ( if CONF_PARTITIONS in config and pio_partitions_key in pio_options:
CONF_PARTITIONS in config
and pio_partitions_key
in fv.full_config.get()[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS]
):
raise cv.Invalid( raise cv.Invalid(
f"Do not specify '{pio_partitions_key}' in '{CONF_PLATFORMIO_OPTIONS}' with '{CONF_PARTITIONS}' in esp32" f"Do not specify '{pio_partitions_key}' in '{CONF_PLATFORMIO_OPTIONS}' with '{CONF_PARTITIONS}' in esp32"
) )
if ( if pio_flash_size_key in pio_options:
pio_flash_size_key
in fv.full_config.get()[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS]
):
raise cv.Invalid( raise cv.Invalid(
f"Please specify {CONF_FLASH_SIZE} within esp32 configuration only" f"Please specify {CONF_FLASH_SIZE} within esp32 configuration only"
) )
@ -412,6 +490,7 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
cv.Schema( cv.Schema(
{ {
cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict,
cv.Optional(CONF_RELEASE): cv.string_strict,
cv.Optional(CONF_SOURCE): cv.string_strict, cv.Optional(CONF_SOURCE): cv.string_strict,
cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version, cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version,
cv.Optional(CONF_SDKCONFIG_OPTIONS, default={}): { cv.Optional(CONF_SDKCONFIG_OPTIONS, default={}): {
@ -515,10 +594,9 @@ async def to_code(config):
cg.add_build_flag("-DUSE_ESP_IDF") cg.add_build_flag("-DUSE_ESP_IDF")
cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ESP_IDF") cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ESP_IDF")
cg.add_build_flag("-Wno-nonnull-compare") cg.add_build_flag("-Wno-nonnull-compare")
cg.add_platformio_option(
"platform_packages", cg.add_platformio_option("platform_packages", [conf[CONF_SOURCE]])
[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 # 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. # This is espressif's own published version which is more up to date.
cg.add_platformio_option( cg.add_platformio_option(

View File

@ -1,4 +1,12 @@
from .const import VARIANT_ESP32, VARIANT_ESP32C3, VARIANT_ESP32S2, VARIANT_ESP32S3 from .const import (
VARIANT_ESP32,
VARIANT_ESP32C2,
VARIANT_ESP32C3,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
)
ESP32_BASE_PINS = { ESP32_BASE_PINS = {
"TX": 1, "TX": 1,
@ -1344,6 +1352,26 @@ done | sort
""" """
BOARDS = { BOARDS = {
"4d_systems_esp32s3_gen4_r8n16": {
"name": "4D Systems GEN4-ESP32 16MB (ESP32S3-R8N16)",
"variant": VARIANT_ESP32S3,
},
"adafruit_camera_esp32s3": {
"name": "Adafruit pyCamera S3",
"variant": VARIANT_ESP32S3,
},
"adafruit_feather_esp32c6": {
"name": "Adafruit Feather ESP32-C6",
"variant": VARIANT_ESP32C6,
},
"adafruit_feather_esp32s2": {
"name": "Adafruit Feather ESP32-S2",
"variant": VARIANT_ESP32S2,
},
"adafruit_feather_esp32s2_reversetft": {
"name": "Adafruit Feather ESP32-S2 Reverse TFT",
"variant": VARIANT_ESP32S2,
},
"adafruit_feather_esp32s2_tft": { "adafruit_feather_esp32s2_tft": {
"name": "Adafruit Feather ESP32-S2 TFT", "name": "Adafruit Feather ESP32-S2 TFT",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
@ -1356,6 +1384,10 @@ BOARDS = {
"name": "Adafruit Feather ESP32-S3 No PSRAM", "name": "Adafruit Feather ESP32-S3 No PSRAM",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"adafruit_feather_esp32s3_reversetft": {
"name": "Adafruit Feather ESP32-S3 Reverse TFT",
"variant": VARIANT_ESP32S3,
},
"adafruit_feather_esp32s3_tft": { "adafruit_feather_esp32s3_tft": {
"name": "Adafruit Feather ESP32-S3 TFT", "name": "Adafruit Feather ESP32-S3 TFT",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
@ -1376,10 +1408,18 @@ BOARDS = {
"name": "Adafruit MagTag 2.9", "name": "Adafruit MagTag 2.9",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
}, },
"adafruit_matrixportal_esp32s3": {
"name": "Adafruit MatrixPortal ESP32-S3",
"variant": VARIANT_ESP32S3,
},
"adafruit_metro_esp32s2": { "adafruit_metro_esp32s2": {
"name": "Adafruit Metro ESP32-S2", "name": "Adafruit Metro ESP32-S2",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
}, },
"adafruit_metro_esp32s3": {
"name": "Adafruit Metro ESP32-S3",
"variant": VARIANT_ESP32S3,
},
"adafruit_qtpy_esp32c3": { "adafruit_qtpy_esp32c3": {
"name": "Adafruit QT Py ESP32-C3", "name": "Adafruit QT Py ESP32-C3",
"variant": VARIANT_ESP32C3, "variant": VARIANT_ESP32C3,
@ -1392,10 +1432,18 @@ BOARDS = {
"name": "Adafruit QT Py ESP32-S2", "name": "Adafruit QT Py ESP32-S2",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
}, },
"adafruit_qtpy_esp32s3_n4r2": {
"name": "Adafruit QT Py ESP32-S3 (4M Flash 2M PSRAM)",
"variant": VARIANT_ESP32S3,
},
"adafruit_qtpy_esp32s3_nopsram": { "adafruit_qtpy_esp32s3_nopsram": {
"name": "Adafruit QT Py ESP32-S3 No PSRAM", "name": "Adafruit QT Py ESP32-S3 No PSRAM",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"adafruit_qualia_s3_rgb666": {
"name": "Adafruit Qualia ESP32-S3 RGB666",
"variant": VARIANT_ESP32S3,
},
"airm2m_core_esp32c3": { "airm2m_core_esp32c3": {
"name": "AirM2M CORE ESP32C3", "name": "AirM2M CORE ESP32C3",
"variant": VARIANT_ESP32C3, "variant": VARIANT_ESP32C3,
@ -1404,14 +1452,30 @@ BOARDS = {
"name": "ALKS ESP32", "name": "ALKS ESP32",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"arduino_nano_esp32": {
"name": "Arduino Nano ESP32",
"variant": VARIANT_ESP32S3,
},
"atd147_s3": {
"name": "ArtronShop ATD1.47-S3",
"variant": VARIANT_ESP32S3,
},
"atmegazero_esp32s2": { "atmegazero_esp32s2": {
"name": "EspinalLab ATMegaZero ESP32-S2", "name": "EspinalLab ATMegaZero ESP32-S2",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
}, },
"aventen_s3_sync": {
"name": "Aventen S3 Sync",
"variant": VARIANT_ESP32S3,
},
"az-delivery-devkit-v4": { "az-delivery-devkit-v4": {
"name": "AZ-Delivery ESP-32 Dev Kit C V4", "name": "AZ-Delivery ESP-32 Dev Kit C V4",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"bee_data_logger": {
"name": "Smart Bee Data Logger",
"variant": VARIANT_ESP32S3,
},
"bee_motion_mini": { "bee_motion_mini": {
"name": "Smart Bee Motion Mini", "name": "Smart Bee Motion Mini",
"variant": VARIANT_ESP32C3, "variant": VARIANT_ESP32C3,
@ -1436,14 +1500,6 @@ BOARDS = {
"name": "BPI-Leaf-S3", "name": "BPI-Leaf-S3",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"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": { "cnrs_aw2eth": {
"name": "CNRS AW2ETH", "name": "CNRS AW2ETH",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1496,18 +1552,38 @@ BOARDS = {
"name": "DFRobot Beetle ESP32-C3", "name": "DFRobot Beetle ESP32-C3",
"variant": VARIANT_ESP32C3, "variant": VARIANT_ESP32C3,
}, },
"dfrobot_firebeetle2_esp32e": {
"name": "DFRobot Firebeetle 2 ESP32-E",
"variant": VARIANT_ESP32,
},
"dfrobot_firebeetle2_esp32s3": { "dfrobot_firebeetle2_esp32s3": {
"name": "DFRobot Firebeetle 2 ESP32-S3", "name": "DFRobot Firebeetle 2 ESP32-S3",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"dfrobot_romeo_esp32s3": {
"name": "DFRobot Romeo ESP32-S3",
"variant": VARIANT_ESP32S3,
},
"dpu_esp32": { "dpu_esp32": {
"name": "TAMC DPU ESP32", "name": "TAMC DPU ESP32",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"edgebox-esp-100": {
"name": "Seeed Studio Edgebox-ESP-100",
"variant": VARIANT_ESP32S3,
},
"esp320": { "esp320": {
"name": "Electronic SweetPeas ESP320", "name": "Electronic SweetPeas ESP320",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"esp32-c2-devkitm-1": {
"name": "Espressif ESP32-C2-DevKitM-1",
"variant": VARIANT_ESP32C2,
},
"esp32-c3-devkitc-02": {
"name": "Espressif ESP32-C3-DevKitC-02",
"variant": VARIANT_ESP32C3,
},
"esp32-c3-devkitm-1": { "esp32-c3-devkitm-1": {
"name": "Espressif ESP32-C3-DevKitM-1", "name": "Espressif ESP32-C3-DevKitM-1",
"variant": VARIANT_ESP32C3, "variant": VARIANT_ESP32C3,
@ -1516,6 +1592,14 @@ BOARDS = {
"name": "Ai-Thinker ESP-C3-M1-I-Kit", "name": "Ai-Thinker ESP-C3-M1-I-Kit",
"variant": VARIANT_ESP32C3, "variant": VARIANT_ESP32C3,
}, },
"esp32-c6-devkitc-1": {
"name": "Espressif ESP32-C6-DevKitC-1",
"variant": VARIANT_ESP32C6,
},
"esp32-c6-devkitm-1": {
"name": "Espressif ESP32-C6-DevKitM-1",
"variant": VARIANT_ESP32C6,
},
"esp32cam": { "esp32cam": {
"name": "AI Thinker ESP32-CAM", "name": "AI Thinker ESP32-CAM",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1544,6 +1628,14 @@ BOARDS = {
"name": "OLIMEX ESP32-GATEWAY", "name": "OLIMEX ESP32-GATEWAY",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"esp32-h2-devkitm-1": {
"name": "Espressif ESP32-H2-DevKit",
"variant": VARIANT_ESP32H2,
},
"esp32-pico-devkitm-2": {
"name": "Espressif ESP32-PICO-DevKitM-2",
"variant": VARIANT_ESP32,
},
"esp32-poe-iso": { "esp32-poe-iso": {
"name": "OLIMEX ESP32-PoE-ISO", "name": "OLIMEX ESP32-PoE-ISO",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1580,10 +1672,22 @@ BOARDS = {
"name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)", "name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"esp32-s3-korvo-2": { "esp32-s3-devkitm-1": {
"name": "Espressif ESP32-S3-Korvo-2", "name": "Espressif ESP32-S3-DevKitM-1",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"esp32s3_powerfeather": {
"name": "ESP32-S3 PowerFeather",
"variant": VARIANT_ESP32S3,
},
"esp32s3usbotg": {
"name": "Espressif ESP32-S3-USB-OTG",
"variant": VARIANT_ESP32S3,
},
"esp32-solo1": {
"name": "Espressif Generic ESP32-solo1 4M Flash",
"variant": VARIANT_ESP32,
},
"esp32thing": { "esp32thing": {
"name": "SparkFun ESP32 Thing", "name": "SparkFun ESP32 Thing",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1652,9 +1756,9 @@ BOARDS = {
"name": "Heltec WiFi Kit 32", "name": "Heltec WiFi Kit 32",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"heltec_wifi_kit_32_v2": { "heltec_wifi_kit_32_V3": {
"name": "Heltec WiFi Kit 32 (V2)", "name": "Heltec WiFi Kit 32 (V3)",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32S3,
}, },
"heltec_wifi_lora_32": { "heltec_wifi_lora_32": {
"name": "Heltec WiFi LoRa 32", "name": "Heltec WiFi LoRa 32",
@ -1664,6 +1768,10 @@ BOARDS = {
"name": "Heltec WiFi LoRa 32 (V2)", "name": "Heltec WiFi LoRa 32 (V2)",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"heltec_wifi_lora_32_V3": {
"name": "Heltec WiFi LoRa 32 (V3)",
"variant": VARIANT_ESP32S3,
},
"heltec_wireless_stick_lite": { "heltec_wireless_stick_lite": {
"name": "Heltec Wireless Stick Lite", "name": "Heltec Wireless Stick Lite",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1708,6 +1816,14 @@ BOARDS = {
"name": "oddWires IoT-Bus Proteus", "name": "oddWires IoT-Bus Proteus",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"ioxesp32": {
"name": "ArtronShop IOXESP32",
"variant": VARIANT_ESP32,
},
"ioxesp32ps": {
"name": "ArtronShop IOXESP32PS",
"variant": VARIANT_ESP32,
},
"kb32-ft": { "kb32-ft": {
"name": "MakerAsia KB32-FT", "name": "MakerAsia KB32-FT",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1720,10 +1836,26 @@ BOARDS = {
"name": "Labplus mPython", "name": "Labplus mPython",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"lilka_v2": {
"name": "Lilka v2",
"variant": VARIANT_ESP32S3,
},
"lilygo-t-display": {
"name": "LilyGo T-Display",
"variant": VARIANT_ESP32,
},
"lilygo-t-display-s3": {
"name": "LilyGo T-Display-S3",
"variant": VARIANT_ESP32S3,
},
"lionbit": { "lionbit": {
"name": "Lion:Bit Dev Board", "name": "Lion:Bit Dev Board",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"lionbits3": {
"name": "Lion:Bit S3 STEM Dev Board",
"variant": VARIANT_ESP32S3,
},
"lolin32_lite": { "lolin32_lite": {
"name": "WEMOS LOLIN32 Lite", "name": "WEMOS LOLIN32 Lite",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1752,10 +1884,18 @@ BOARDS = {
"name": "WEMOS LOLIN S2 PICO", "name": "WEMOS LOLIN S2 PICO",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
}, },
"lolin_s3_mini": {
"name": "WEMOS LOLIN S3 Mini",
"variant": VARIANT_ESP32S3,
},
"lolin_s3": { "lolin_s3": {
"name": "WEMOS LOLIN S3", "name": "WEMOS LOLIN S3",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"lolin_s3_pro": {
"name": "WEMOS LOLIN S3 PRO",
"variant": VARIANT_ESP32S3,
},
"lopy4": { "lopy4": {
"name": "Pycom LoPy4", "name": "Pycom LoPy4",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1768,10 +1908,18 @@ BOARDS = {
"name": "M5Stack-ATOM", "name": "M5Stack-ATOM",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"m5stack-atoms3": {
"name": "M5Stack AtomS3",
"variant": VARIANT_ESP32S3,
},
"m5stack-core2": { "m5stack-core2": {
"name": "M5Stack Core2", "name": "M5Stack Core2",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"m5stack-core-esp32-16M": {
"name": "M5Stack Core ESP32 16M",
"variant": VARIANT_ESP32,
},
"m5stack-core-esp32": { "m5stack-core-esp32": {
"name": "M5Stack Core ESP32", "name": "M5Stack Core ESP32",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1780,6 +1928,10 @@ BOARDS = {
"name": "M5Stack-Core Ink", "name": "M5Stack-Core Ink",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"m5stack-cores3": {
"name": "M5Stack CoreS3",
"variant": VARIANT_ESP32S3,
},
"m5stack-fire": { "m5stack-fire": {
"name": "M5Stack FIRE", "name": "M5Stack FIRE",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1788,6 +1940,14 @@ BOARDS = {
"name": "M5Stack GREY ESP32", "name": "M5Stack GREY ESP32",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"m5stack_paper": {
"name": "M5Stack Paper",
"variant": VARIANT_ESP32,
},
"m5stack-stamps3": {
"name": "M5Stack StampS3",
"variant": VARIANT_ESP32S3,
},
"m5stack-station": { "m5stack-station": {
"name": "M5Stack Station", "name": "M5Stack Station",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1796,6 +1956,10 @@ BOARDS = {
"name": "M5Stack Timer CAM", "name": "M5Stack Timer CAM",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"m5stamp-pico": {
"name": "M5Stamp-Pico",
"variant": VARIANT_ESP32,
},
"m5stick-c": { "m5stick-c": {
"name": "M5Stick-C", "name": "M5Stick-C",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1832,10 +1996,26 @@ BOARDS = {
"name": "Deparment of Alchemy MiniMain ESP32-S2", "name": "Deparment of Alchemy MiniMain ESP32-S2",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
}, },
"motorgo_mini_1": {
"name": "MotorGo Mini 1 (ESP32-S3)",
"variant": VARIANT_ESP32S3,
},
"namino_arancio": {
"name": "Namino Arancio",
"variant": VARIANT_ESP32S3,
},
"namino_rosso": {
"name": "Namino Rosso",
"variant": VARIANT_ESP32S3,
},
"nano32": { "nano32": {
"name": "MakerAsia Nano32", "name": "MakerAsia Nano32",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"nebulas3": {
"name": "Kinetic Dynamics Nebula S3",
"variant": VARIANT_ESP32S3,
},
"nina_w10": { "nina_w10": {
"name": "u-blox NINA-W10 series", "name": "u-blox NINA-W10 series",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1896,10 +2076,22 @@ BOARDS = {
"name": "Munich Labs RedPill ESP32-S3", "name": "Munich Labs RedPill ESP32-S3",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"roboheart_hercules": {
"name": "RoboHeart Hercules",
"variant": VARIANT_ESP32,
},
"seeed_xiao_esp32c3": { "seeed_xiao_esp32c3": {
"name": "Seeed Studio XIAO ESP32C3", "name": "Seeed Studio XIAO ESP32C3",
"variant": VARIANT_ESP32C3, "variant": VARIANT_ESP32C3,
}, },
"seeed_xiao_esp32s3": {
"name": "Seeed Studio XIAO ESP32S3",
"variant": VARIANT_ESP32S3,
},
"sensebox_mcu_esp32s2": {
"name": "senseBox MCU-S2 ESP32-S2",
"variant": VARIANT_ESP32S2,
},
"sensesiot_weizen": { "sensesiot_weizen": {
"name": "LOGISENSES Senses Weizen", "name": "LOGISENSES Senses Weizen",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1912,6 +2104,10 @@ BOARDS = {
"name": "S.ODI Ultra v1", "name": "S.ODI Ultra v1",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"sparkfun_esp32c6_thing_plus": {
"name": "Sparkfun ESP32-C6 Thing Plus",
"variant": VARIANT_ESP32C6,
},
"sparkfun_esp32_iot_redboard": { "sparkfun_esp32_iot_redboard": {
"name": "SparkFun ESP32 IoT RedBoard", "name": "SparkFun ESP32 IoT RedBoard",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -2004,6 +2200,10 @@ BOARDS = {
"name": "Unexpected Maker FeatherS3", "name": "Unexpected Maker FeatherS3",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"um_nanos3": {
"name": "Unexpected Maker NanoS3",
"variant": VARIANT_ESP32S3,
},
"um_pros3": { "um_pros3": {
"name": "Unexpected Maker PROS3", "name": "Unexpected Maker PROS3",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
@ -2040,6 +2240,14 @@ BOARDS = {
"name": "uPesy ESP32 Wrover DevKit", "name": "uPesy ESP32 Wrover DevKit",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"valtrack_v4_mfw_esp32_c3": {
"name": "Valetron Systems VALTRACK-V4MVF",
"variant": VARIANT_ESP32C3,
},
"valtrack_v4_vts_esp32_c3": {
"name": "Valetron Systems VALTRACK-V4VTS",
"variant": VARIANT_ESP32C3,
},
"vintlabs-devkit-v1": { "vintlabs-devkit-v1": {
"name": "VintLabs ESP32 Devkit", "name": "VintLabs ESP32 Devkit",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,

View File

@ -2,8 +2,10 @@ from esphome import automation
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ID from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ESPHOME, CONF_ID, CONF_NAME
from esphome.core import CORE from esphome.core import CORE
from esphome.core.config import CONF_NAME_ADD_MAC_SUFFIX
import esphome.final_validate as fv
DEPENDENCIES = ["esp32"] DEPENDENCIES = ["esp32"]
CODEOWNERS = ["@jesserockz", "@Rapsssito"] CODEOWNERS = ["@jesserockz", "@Rapsssito"]
@ -50,6 +52,7 @@ TX_POWER_LEVELS = {
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(ESP32BLE), cv.GenerateID(): cv.declare_id(ESP32BLE),
cv.Optional(CONF_NAME): cv.All(cv.string, cv.Length(max=20)),
cv.Optional(CONF_IO_CAPABILITY, default="none"): cv.enum( cv.Optional(CONF_IO_CAPABILITY, default="none"): cv.enum(
IO_CAPABILITY, lower=True IO_CAPABILITY, lower=True
), ),
@ -67,7 +70,22 @@ def validate_variant(_):
raise cv.Invalid(f"{variant} does not support Bluetooth") raise cv.Invalid(f"{variant} does not support Bluetooth")
FINAL_VALIDATE_SCHEMA = validate_variant def final_validation(config):
validate_variant(config)
if (name := config.get(CONF_NAME)) is not None:
full_config = fv.full_config.get()
max_length = 20
if full_config[CONF_ESPHOME][CONF_NAME_ADD_MAC_SUFFIX]:
max_length -= 7 # "-AABBCC" is appended when add mac suffix option is used
if len(name) > max_length:
raise cv.Invalid(
f"Name '{name}' is too long, maximum length is {max_length} characters"
)
return config
FINAL_VALIDATE_SCHEMA = final_validation
async def to_code(config): async def to_code(config):
@ -75,6 +93,8 @@ async def to_code(config):
cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT])) cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT]))
cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY])) cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY]))
cg.add(var.set_advertising_cycle_time(config[CONF_ADVERTISING_CYCLE_TIME])) cg.add(var.set_advertising_cycle_time(config[CONF_ADVERTISING_CYCLE_TIME]))
if (name := config.get(CONF_NAME)) is not None:
cg.add(var.set_name(name))
await cg.register_component(var, config) await cg.register_component(var, config)
if CORE.using_esp_idf: if CORE.using_esp_idf:

View File

@ -188,7 +188,14 @@ bool ESP32BLE::ble_setup_() {
} }
} }
std::string name = App.get_name(); std::string name;
if (this->name_.has_value()) {
name = this->name_.value();
if (App.is_name_add_mac_suffix_enabled()) {
name += "-" + get_mac_address().substr(6);
}
} else {
name = App.get_name();
if (name.length() > 20) { if (name.length() > 20) {
if (App.is_name_add_mac_suffix_enabled()) { if (App.is_name_add_mac_suffix_enabled()) {
name.erase(name.begin() + 13, name.end() - 7); // Remove characters between 13 and the mac address name.erase(name.begin() + 13, name.end() - 7); // Remove characters between 13 and the mac address
@ -196,6 +203,7 @@ bool ESP32BLE::ble_setup_() {
name = name.substr(0, 20); name = name.substr(0, 20);
} }
} }
}
err = esp_ble_gap_set_device_name(name.c_str()); err = esp_ble_gap_set_device_name(name.c_str());
if (err != ESP_OK) { if (err != ESP_OK) {

View File

@ -90,6 +90,7 @@ class ESP32BLE : public Component {
void loop() override; void loop() override;
void dump_config() override; void dump_config() override;
float get_setup_priority() const override; float get_setup_priority() const override;
void set_name(const std::string &name) { this->name_ = name; }
void advertising_start(); void advertising_start();
void advertising_set_service_data(const std::vector<uint8_t> &data); void advertising_set_service_data(const std::vector<uint8_t> &data);
@ -131,6 +132,7 @@ class ESP32BLE : public Component {
esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE};
uint32_t advertising_cycle_time_; uint32_t advertising_cycle_time_;
bool enable_on_boot_; bool enable_on_boot_;
optional<std::string> name_;
}; };
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)

View File

@ -83,7 +83,7 @@ esp_err_t BLEAdvertising::services_advertisement_() {
esp_err_t err; esp_err_t err;
this->advertising_data_.set_scan_rsp = false; this->advertising_data_.set_scan_rsp = false;
this->advertising_data_.include_name = !this->scan_response_; this->advertising_data_.include_name = true;
this->advertising_data_.include_txpower = !this->scan_response_; this->advertising_data_.include_txpower = !this->scan_response_;
err = esp_ble_gap_config_adv_data(&this->advertising_data_); err = esp_ble_gap_config_adv_data(&this->advertising_data_);
if (err != ESP_OK) { if (err != ESP_OK) {

View File

@ -34,7 +34,7 @@ ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) {
ESPBTUUID ESPBTUUID::from_raw_reversed(const uint8_t *data) { ESPBTUUID ESPBTUUID::from_raw_reversed(const uint8_t *data) {
ESPBTUUID ret; ESPBTUUID ret;
ret.uuid_.len = ESP_UUID_LEN_128; ret.uuid_.len = ESP_UUID_LEN_128;
for (int i = 0; i < ESP_UUID_LEN_128; i++) for (uint8_t i = 0; i < ESP_UUID_LEN_128; i++)
ret.uuid_.uuid.uuid128[ESP_UUID_LEN_128 - 1 - i] = data[i]; ret.uuid_.uuid.uuid128[ESP_UUID_LEN_128 - 1 - i] = data[i];
return ret; return ret;
} }
@ -43,30 +43,30 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
if (data.length() == 4) { if (data.length() == 4) {
ret.uuid_.len = ESP_UUID_LEN_16; ret.uuid_.len = ESP_UUID_LEN_16;
ret.uuid_.uuid.uuid16 = 0; ret.uuid_.uuid.uuid16 = 0;
for (int i = 0; i < data.length();) { for (uint i = 0; i < data.length(); i += 2) {
uint8_t msb = data.c_str()[i]; uint8_t msb = data.c_str()[i];
uint8_t lsb = data.c_str()[i + 1]; uint8_t lsb = data.c_str()[i + 1];
uint8_t lsb_shift = i <= 2 ? (2 - i) * 4 : 0;
if (msb > '9') if (msb > '9')
msb -= 7; msb -= 7;
if (lsb > '9') if (lsb > '9')
lsb -= 7; lsb -= 7;
ret.uuid_.uuid.uuid16 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (2 - i) * 4; ret.uuid_.uuid.uuid16 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << lsb_shift;
i += 2;
} }
} else if (data.length() == 8) { } else if (data.length() == 8) {
ret.uuid_.len = ESP_UUID_LEN_32; ret.uuid_.len = ESP_UUID_LEN_32;
ret.uuid_.uuid.uuid32 = 0; ret.uuid_.uuid.uuid32 = 0;
for (int i = 0; i < data.length();) { for (uint i = 0; i < data.length(); i += 2) {
uint8_t msb = data.c_str()[i]; uint8_t msb = data.c_str()[i];
uint8_t lsb = data.c_str()[i + 1]; uint8_t lsb = data.c_str()[i + 1];
uint8_t lsb_shift = i <= 6 ? (6 - i) * 4 : 0;
if (msb > '9') if (msb > '9')
msb -= 7; msb -= 7;
if (lsb > '9') if (lsb > '9')
lsb -= 7; lsb -= 7;
ret.uuid_.uuid.uuid32 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (6 - i) * 4; ret.uuid_.uuid.uuid32 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << lsb_shift;
i += 2;
} }
} else if (data.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be } else if (data.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be
// investigated (lack of time) // investigated (lack of time)
@ -77,7 +77,7 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
// UUID format. // UUID format.
ret.uuid_.len = ESP_UUID_LEN_128; ret.uuid_.len = ESP_UUID_LEN_128;
int n = 0; int n = 0;
for (int i = 0; i < data.length();) { for (uint i = 0; i < data.length(); i += 2) {
if (data.c_str()[i] == '-') if (data.c_str()[i] == '-')
i++; i++;
uint8_t msb = data.c_str()[i]; uint8_t msb = data.c_str()[i];
@ -88,7 +88,6 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
if (lsb > '9') if (lsb > '9')
lsb -= 7; lsb -= 7;
ret.uuid_.uuid.uuid128[15 - n++] = ((msb & 0x0F) << 4) | (lsb & 0x0F); ret.uuid_.uuid.uuid128[15 - n++] = ((msb & 0x0F) << 4) | (lsb & 0x0F);
i += 2;
} }
} else { } else {
ESP_LOGE(TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes - %s", data.c_str()); ESP_LOGE(TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes - %s", data.c_str());
@ -155,7 +154,7 @@ bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const {
} }
break; break;
case ESP_UUID_LEN_128: case ESP_UUID_LEN_128:
for (int i = 0; i < ESP_UUID_LEN_128; i++) { for (uint8_t i = 0; i < ESP_UUID_LEN_128; i++) {
if (uuid.uuid_.uuid.uuid128[i] != this->uuid_.uuid.uuid128[i]) { if (uuid.uuid_.uuid.uuid128[i] != this->uuid_.uuid.uuid128[i]) {
return false; return false;
} }

View File

@ -432,7 +432,7 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
ESP_LOGVV(TAG, "Parse Result:"); ESP_LOGVV(TAG, "Parse Result:");
const char *address_type = ""; const char *address_type;
switch (this->address_type_) { switch (this->address_type_) {
case BLE_ADDR_TYPE_PUBLIC: case BLE_ADDR_TYPE_PUBLIC:
address_type = "PUBLIC"; address_type = "PUBLIC";
@ -446,6 +446,9 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
case BLE_ADDR_TYPE_RPA_RANDOM: case BLE_ADDR_TYPE_RPA_RANDOM:
address_type = "RPA_RANDOM"; address_type = "RPA_RANDOM";
break; break;
default:
address_type = "UNKNOWN";
break;
} }
ESP_LOGVV(TAG, " Address: %02X:%02X:%02X:%02X:%02X:%02X (%s)", this->address_[0], this->address_[1], ESP_LOGVV(TAG, " Address: %02X:%02X:%02X:%02X:%02X:%02X (%s)", this->address_[0], this->address_[1],
this->address_[2], this->address_[3], this->address_[4], this->address_[5], address_type); this->address_[2], this->address_[3], this->address_[4], this->address_[5], address_type);

View File

@ -11,7 +11,7 @@
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/preferences.h" #include "esphome/core/preferences.h"
struct httpd_req; struct httpd_req; // NOLINT(readability-identifier-naming)
namespace esphome { namespace esphome {
namespace esp32_camera_web_server { namespace esp32_camera_web_server {

View File

@ -1,9 +1,9 @@
from dataclasses import dataclass from dataclasses import dataclass
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins from esphome import pins
import esphome.codegen as cg
from esphome.components import esp32_rmt, light from esphome.components import esp32_rmt, light
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_CHIPSET, CONF_CHIPSET,
CONF_IS_RGBW, CONF_IS_RGBW,
@ -103,7 +103,7 @@ CONFIG_SCHEMA = cv.All(
default="0 us", default="0 us",
): cv.positive_time_period_nanoseconds, ): cv.positive_time_period_nanoseconds,
} }
), ).extend(cv.COMPONENT_SCHEMA),
cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH), cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH),
) )

View File

@ -1,10 +1,13 @@
import logging import logging
import os import os
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_BOARD, CONF_BOARD,
CONF_BOARD_FLASH_MODE, CONF_BOARD_FLASH_MODE,
CONF_FRAMEWORK, CONF_FRAMEWORK,
CONF_PLATFORM_VERSION,
CONF_SOURCE, CONF_SOURCE,
CONF_VERSION, CONF_VERSION,
KEY_CORE, KEY_CORE,
@ -12,27 +15,22 @@ from esphome.const import (
KEY_TARGET_FRAMEWORK, KEY_TARGET_FRAMEWORK,
KEY_TARGET_PLATFORM, KEY_TARGET_PLATFORM,
PLATFORM_ESP8266, PLATFORM_ESP8266,
CONF_PLATFORM_VERSION,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.helpers import copy_file_if_changed from esphome.helpers import copy_file_if_changed
from .boards import BOARDS, ESP8266_LD_SCRIPTS
from .const import ( from .const import (
CONF_RESTORE_FROM_FLASH,
CONF_EARLY_PIN_INIT, CONF_EARLY_PIN_INIT,
CONF_RESTORE_FROM_FLASH,
KEY_BOARD, KEY_BOARD,
KEY_ESP8266, KEY_ESP8266,
KEY_FLASH_SIZE, KEY_FLASH_SIZE,
KEY_PIN_INITIAL_STATES, KEY_PIN_INITIAL_STATES,
esp8266_ns, esp8266_ns,
) )
from .boards import BOARDS, ESP8266_LD_SCRIPTS
from .gpio import PinInitialState, add_pin_initial_states_array from .gpio import PinInitialState, add_pin_initial_states_array
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
AUTO_LOAD = ["preferences"] AUTO_LOAD = ["preferences"]

View File

@ -111,11 +111,11 @@ void EZOSensor::loop() {
if (buf[0] == 1) { if (buf[0] == 1) {
std::string payload = reinterpret_cast<char *>(&buf[1]); std::string payload = reinterpret_cast<char *>(&buf[1]);
if (!payload.empty()) { if (!payload.empty()) {
auto start_location = payload.find(',');
switch (to_run->command_type) { switch (to_run->command_type) {
case EzoCommandType::EZO_READ: { case EzoCommandType::EZO_READ: {
// some sensors return multiple comma-separated values, terminate string after first one // some sensors return multiple comma-separated values, terminate string after first one
int start_location = 0; if (start_location != std::string::npos) {
if ((start_location = payload.find(',')) != std::string::npos) {
payload.erase(start_location); payload.erase(start_location);
} }
auto val = parse_number<float>(payload); auto val = parse_number<float>(payload);
@ -126,49 +126,37 @@ void EZOSensor::loop() {
} }
break; break;
} }
case EzoCommandType::EZO_LED: { case EzoCommandType::EZO_LED:
this->led_callback_.call(payload.back() == '1'); this->led_callback_.call(payload.back() == '1');
break; break;
} case EzoCommandType::EZO_DEVICE_INFORMATION:
case EzoCommandType::EZO_DEVICE_INFORMATION: { if (start_location != std::string::npos) {
int start_location = 0;
if ((start_location = payload.find(',')) != std::string::npos) {
this->device_infomation_callback_.call(payload.substr(start_location + 1)); this->device_infomation_callback_.call(payload.substr(start_location + 1));
} }
break; break;
} case EzoCommandType::EZO_SLOPE:
case EzoCommandType::EZO_SLOPE: { if (start_location != std::string::npos) {
int start_location = 0;
if ((start_location = payload.find(',')) != std::string::npos) {
this->slope_callback_.call(payload.substr(start_location + 1)); this->slope_callback_.call(payload.substr(start_location + 1));
} }
break; break;
} case EzoCommandType::EZO_CALIBRATION:
case EzoCommandType::EZO_CALIBRATION: { if (start_location != std::string::npos) {
int start_location = 0;
if ((start_location = payload.find(',')) != std::string::npos) {
this->calibration_callback_.call(payload.substr(start_location + 1)); this->calibration_callback_.call(payload.substr(start_location + 1));
} }
break; break;
} case EzoCommandType::EZO_T:
case EzoCommandType::EZO_T: { if (start_location != std::string::npos) {
int start_location = 0;
if ((start_location = payload.find(',')) != std::string::npos) {
this->t_callback_.call(payload.substr(start_location + 1)); this->t_callback_.call(payload.substr(start_location + 1));
} }
break; break;
} case EzoCommandType::EZO_CUSTOM:
case EzoCommandType::EZO_CUSTOM: {
this->custom_callback_.call(payload); this->custom_callback_.call(payload);
break; break;
} default:
default: {
break; break;
} }
} }
} }
}
this->commands_.pop_front(); this->commands_.pop_front();
} }
@ -178,7 +166,7 @@ void EZOSensor::add_command_(const std::string &command, EzoCommandType command_
ezo_command->command_type = command_type; ezo_command->command_type = command_type;
ezo_command->delay_ms = delay_ms; ezo_command->delay_ms = delay_ms;
this->commands_.push_back(std::move(ezo_command)); this->commands_.push_back(std::move(ezo_command));
}; }
void EZOSensor::set_calibration_point_(EzoCalibrationType type, float value) { void EZOSensor::set_calibration_point_(EzoCalibrationType type, float value) {
std::string payload = str_sprintf("Cal,%s,%0.2f", EZO_CALIBRATION_TYPE_STRINGS[type], value); std::string payload = str_sprintf("Cal,%s,%0.2f", EZO_CALIBRATION_TYPE_STRINGS[type], value);

View File

@ -1,4 +1,3 @@
from collections.abc import Iterable
import functools import functools
import hashlib import hashlib
import logging import logging
@ -8,7 +7,6 @@ import re
import freetype import freetype
import glyphsets import glyphsets
from packaging import version
import requests import requests
from esphome import core, external_files from esphome import core, external_files
@ -88,7 +86,7 @@ def flatten(lists) -> list:
return list(chain.from_iterable(lists)) return list(chain.from_iterable(lists))
def check_missing_glyphs(file, codepoints: Iterable, warning: bool = False): def check_missing_glyphs(file, codepoints, warning: bool = False):
""" """
Check that the given font file actually contains the requested glyphs Check that the given font file actually contains the requested glyphs
:param file: A Truetype font file :param file: A Truetype font file
@ -177,24 +175,6 @@ def validate_glyphs(config):
return config return config
def validate_pillow_installed(value):
try:
import PIL
except ImportError as err:
raise cv.Invalid(
"Please install the pillow python package to use this feature. "
'(pip install "pillow==10.4.0")'
) from err
if version.parse(PIL.__version__) != version.parse("10.4.0"):
raise cv.Invalid(
"Please update your pillow installation to 10.4.0. "
'(pip install "pillow==10.4.0")'
)
return value
FONT_EXTENSIONS = (".ttf", ".woff", ".otf") FONT_EXTENSIONS = (".ttf", ".woff", ".otf")
@ -393,7 +373,9 @@ def font_file_schema(value):
# Default if no glyphs or glyphsets are provided # Default if no glyphs or glyphsets are provided
DEFAULT_GLYPHSET = "GF_Latin_Kernel" DEFAULT_GLYPHSET = "GF_Latin_Kernel"
# default for bitmap fonts # default for bitmap fonts
DEFAULT_GLYPHS = ' !"%()+=,-.:/?0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz<C2><B0>' DEFAULT_GLYPHS = (
' !"%()+=,-.:/?0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°'
)
CONF_RAW_GLYPH_ID = "raw_glyph_id" CONF_RAW_GLYPH_ID = "raw_glyph_id"
@ -421,7 +403,7 @@ FONT_SCHEMA = cv.Schema(
}, },
) )
CONFIG_SCHEMA = cv.All(validate_pillow_installed, FONT_SCHEMA, validate_glyphs) CONFIG_SCHEMA = cv.All(FONT_SCHEMA, validate_glyphs)
# PIL doesn't provide a consistent interface for both TrueType and bitmap # PIL doesn't provide a consistent interface for both TrueType and bitmap

View File

@ -133,9 +133,11 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo
auto diff_r = (float) color.r - (float) background.r; auto diff_r = (float) color.r - (float) background.r;
auto diff_g = (float) color.g - (float) background.g; auto diff_g = (float) color.g - (float) background.g;
auto diff_b = (float) color.b - (float) background.b; auto diff_b = (float) color.b - (float) background.b;
auto diff_w = (float) color.w - (float) background.w;
auto b_r = (float) background.r; auto b_r = (float) background.r;
auto b_g = (float) background.g; auto b_g = (float) background.g;
auto b_b = (float) background.g; auto b_b = (float) background.b;
auto b_w = (float) background.w;
for (int glyph_y = y_start + scan_y1; glyph_y != max_y; glyph_y++) { for (int glyph_y = y_start + scan_y1; glyph_y != max_y; glyph_y++) {
for (int glyph_x = x_at + scan_x1; glyph_x != max_x; glyph_x++) { for (int glyph_x = x_at + scan_x1; glyph_x != max_x; glyph_x++) {
uint8_t pixel = 0; uint8_t pixel = 0;
@ -153,8 +155,8 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo
display->draw_pixel_at(glyph_x, glyph_y, color); display->draw_pixel_at(glyph_x, glyph_y, color);
} else if (pixel != 0) { } else if (pixel != 0) {
auto on = (float) pixel / (float) bpp_max; auto on = (float) pixel / (float) bpp_max;
auto blended = auto blended = Color((uint8_t) (diff_r * on + b_r), (uint8_t) (diff_g * on + b_g),
Color((uint8_t) (diff_r * on + b_r), (uint8_t) (diff_g * on + b_g), (uint8_t) (diff_b * on + b_b)); (uint8_t) (diff_b * on + b_b), (uint8_t) (diff_w * on + b_w));
display->draw_pixel_at(glyph_x, glyph_y, blended); display->draw_pixel_at(glyph_x, glyph_y, blended);
} }
} }

View File

@ -35,7 +35,9 @@ void HonClimate::set_beeper_state(bool state) {
if (state != this->settings_.beeper_state) { if (state != this->settings_.beeper_state) {
this->settings_.beeper_state = state; this->settings_.beeper_state = state;
#ifdef USE_SWITCH #ifdef USE_SWITCH
if (this->beeper_switch_ != nullptr) {
this->beeper_switch_->publish_state(state); this->beeper_switch_->publish_state(state);
}
#endif #endif
this->hon_rtc_.save(&this->settings_); this->hon_rtc_.save(&this->settings_);
} }
@ -45,10 +47,17 @@ bool HonClimate::get_beeper_state() const { return this->settings_.beeper_state;
void HonClimate::set_quiet_mode_state(bool state) { void HonClimate::set_quiet_mode_state(bool state) {
if (state != this->get_quiet_mode_state()) { if (state != this->get_quiet_mode_state()) {
if ((this->mode != ClimateMode::CLIMATE_MODE_OFF) && (this->mode != ClimateMode::CLIMATE_MODE_FAN_ONLY)) {
this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
this->force_send_control_ = true;
} else {
this->quiet_mode_state_ = state ? SwitchState::ON : SwitchState::OFF;
}
this->settings_.quiet_mode_state = state; this->settings_.quiet_mode_state = state;
#ifdef USE_SWITCH #ifdef USE_SWITCH
if (this->quiet_mode_switch_ != nullptr) {
this->quiet_mode_switch_->publish_state(state); this->quiet_mode_switch_->publish_state(state);
}
#endif #endif
this->hon_rtc_.save(&this->settings_); this->hon_rtc_.save(&this->settings_);
} }
@ -509,7 +518,7 @@ void HonClimate::initialization() {
} }
this->current_vertical_swing_ = this->settings_.last_vertiacal_swing; this->current_vertical_swing_ = this->settings_.last_vertiacal_swing;
this->current_horizontal_swing_ = this->settings_.last_horizontal_swing; this->current_horizontal_swing_ = this->settings_.last_horizontal_swing;
this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::ON : SwitchState::OFF;
} }
haier_protocol::HaierMessage HonClimate::get_control_message() { haier_protocol::HaierMessage HonClimate::get_control_message() {
@ -932,7 +941,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
if (this->mode == CLIMATE_MODE_OFF) { if (this->mode == CLIMATE_MODE_OFF) {
// AC just turned on from remote need to turn off display // AC just turned on from remote need to turn off display
this->force_send_control_ = true; this->force_send_control_ = true;
} else if ((((uint8_t) this->health_mode_) & 0b10) == 0) { } else if ((((uint8_t) this->display_status_) & 0b10) == 0) {
this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF; this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF;
} }
} }
@ -1004,6 +1013,11 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
if (new_quiet_mode != this->get_quiet_mode_state()) { if (new_quiet_mode != this->get_quiet_mode_state()) {
this->quiet_mode_state_ = new_quiet_mode ? SwitchState::ON : SwitchState::OFF; this->quiet_mode_state_ = new_quiet_mode ? SwitchState::ON : SwitchState::OFF;
this->settings_.quiet_mode_state = new_quiet_mode; this->settings_.quiet_mode_state = new_quiet_mode;
#ifdef USE_SWITCH
if (this->quiet_mode_switch_ != nullptr) {
this->quiet_mode_switch_->publish_state(new_quiet_mode);
}
#endif // USE_SWITCH
this->hon_rtc_.save(&this->settings_); this->hon_rtc_.save(&this->settings_);
} }
} }
@ -1069,19 +1083,17 @@ void HonClimate::fill_control_messages_queue_() {
climate_control = this->current_hvac_settings_; climate_control = this->current_hvac_settings_;
// Beeper command // Beeper command
{ {
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::BEEPER_STATUS, (uint8_t) hon_protocol::DataParameters::BEEPER_STATUS,
this->get_beeper_state() ? ZERO_BUF : ONE_BUF, 2)); this->get_beeper_state() ? ZERO_BUF : ONE_BUF, 2);
} }
// Health mode // Health mode
{ {
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::HEALTH_MODE, (uint8_t) hon_protocol::DataParameters::HEALTH_MODE,
this->get_health_mode() ? ONE_BUF : ZERO_BUF, 2)); this->get_health_mode() ? ONE_BUF : ZERO_BUF, 2);
this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01); this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
} }
// Climate mode // Climate mode
@ -1099,51 +1111,46 @@ void HonClimate::fill_control_messages_queue_() {
case CLIMATE_MODE_HEAT_COOL: case CLIMATE_MODE_HEAT_COOL:
new_power = true; new_power = true;
buffer[1] = (uint8_t) hon_protocol::ConditioningMode::AUTO; buffer[1] = (uint8_t) hon_protocol::ConditioningMode::AUTO;
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::AC_MODE, (uint8_t) hon_protocol::DataParameters::AC_MODE,
buffer, 2)); buffer, 2);
fan_mode_buf[1] = this->other_modes_fan_speed_; fan_mode_buf[1] = this->other_modes_fan_speed_;
break; break;
case CLIMATE_MODE_HEAT: case CLIMATE_MODE_HEAT:
new_power = true; new_power = true;
buffer[1] = (uint8_t) hon_protocol::ConditioningMode::HEAT; buffer[1] = (uint8_t) hon_protocol::ConditioningMode::HEAT;
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::AC_MODE, (uint8_t) hon_protocol::DataParameters::AC_MODE,
buffer, 2)); buffer, 2);
fan_mode_buf[1] = this->other_modes_fan_speed_; fan_mode_buf[1] = this->other_modes_fan_speed_;
break; break;
case CLIMATE_MODE_DRY: case CLIMATE_MODE_DRY:
new_power = true; new_power = true;
buffer[1] = (uint8_t) hon_protocol::ConditioningMode::DRY; buffer[1] = (uint8_t) hon_protocol::ConditioningMode::DRY;
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::AC_MODE, (uint8_t) hon_protocol::DataParameters::AC_MODE,
buffer, 2)); buffer, 2);
fan_mode_buf[1] = this->other_modes_fan_speed_; fan_mode_buf[1] = this->other_modes_fan_speed_;
break; break;
case CLIMATE_MODE_FAN_ONLY: case CLIMATE_MODE_FAN_ONLY:
new_power = true; new_power = true;
buffer[1] = (uint8_t) hon_protocol::ConditioningMode::FAN; buffer[1] = (uint8_t) hon_protocol::ConditioningMode::FAN;
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::AC_MODE, (uint8_t) hon_protocol::DataParameters::AC_MODE,
buffer, 2)); buffer, 2);
fan_mode_buf[1] = this->other_modes_fan_speed_; // Auto doesn't work in fan only mode fan_mode_buf[1] = this->other_modes_fan_speed_; // Auto doesn't work in fan only mode
break; break;
case CLIMATE_MODE_COOL: case CLIMATE_MODE_COOL:
new_power = true; new_power = true;
buffer[1] = (uint8_t) hon_protocol::ConditioningMode::COOL; buffer[1] = (uint8_t) hon_protocol::ConditioningMode::COOL;
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::AC_MODE, (uint8_t) hon_protocol::DataParameters::AC_MODE,
buffer, 2)); buffer, 2);
fan_mode_buf[1] = this->other_modes_fan_speed_; fan_mode_buf[1] = this->other_modes_fan_speed_;
break; break;
default: default:
@ -1153,11 +1160,10 @@ void HonClimate::fill_control_messages_queue_() {
} }
// Climate power // Climate power
{ {
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::AC_POWER, (uint8_t) hon_protocol::DataParameters::AC_POWER,
new_power ? ONE_BUF : ZERO_BUF, 2)); new_power ? ONE_BUF : ZERO_BUF, 2);
} }
// CLimate preset // CLimate preset
{ {
@ -1199,36 +1205,32 @@ void HonClimate::fill_control_messages_queue_() {
} }
auto presets = this->traits_.get_supported_presets(); auto presets = this->traits_.get_supported_presets();
if (quiet_mode_buf[1] != 0xFF) { if (quiet_mode_buf[1] != 0xFF) {
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::QUIET_MODE, (uint8_t) hon_protocol::DataParameters::QUIET_MODE,
quiet_mode_buf, 2)); quiet_mode_buf, 2);
} }
if ((fast_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_BOOST) != presets.end()))) { if ((fast_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_BOOST) != presets.end()))) {
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::FAST_MODE, (uint8_t) hon_protocol::DataParameters::FAST_MODE,
fast_mode_buf, 2)); fast_mode_buf, 2);
} }
if ((away_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_AWAY) != presets.end()))) { if ((away_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_AWAY) != presets.end()))) {
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::TEN_DEGREE, (uint8_t) hon_protocol::DataParameters::TEN_DEGREE,
away_mode_buf, 2)); away_mode_buf, 2);
} }
} }
// Target temperature // Target temperature
if (climate_control.target_temperature.has_value() && (this->mode != ClimateMode::CLIMATE_MODE_FAN_ONLY)) { if (climate_control.target_temperature.has_value() && (this->mode != ClimateMode::CLIMATE_MODE_FAN_ONLY)) {
uint8_t buffer[2] = {0x00, 0x00}; uint8_t buffer[2] = {0x00, 0x00};
buffer[1] = ((uint8_t) climate_control.target_temperature.value()) - 16; buffer[1] = ((uint8_t) climate_control.target_temperature.value()) - 16;
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::SET_POINT, (uint8_t) hon_protocol::DataParameters::SET_POINT,
buffer, 2)); buffer, 2);
} }
// Vertical swing mode // Vertical swing mode
if (climate_control.swing_mode.has_value()) { if (climate_control.swing_mode.has_value()) {
@ -1248,16 +1250,14 @@ void HonClimate::fill_control_messages_queue_() {
case CLIMATE_SWING_BOTH: case CLIMATE_SWING_BOTH:
break; break;
} }
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::HORIZONTAL_SWING_MODE, (uint8_t) hon_protocol::DataParameters::HORIZONTAL_SWING_MODE,
horizontal_swing_buf, 2)); horizontal_swing_buf, 2);
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::VERTICAL_SWING_MODE, (uint8_t) hon_protocol::DataParameters::VERTICAL_SWING_MODE,
vertical_swing_buf, 2)); vertical_swing_buf, 2);
} }
// Fan mode // Fan mode
if (climate_control.fan_mode.has_value()) { if (climate_control.fan_mode.has_value()) {
@ -1280,11 +1280,10 @@ void HonClimate::fill_control_messages_queue_() {
break; break;
} }
if (fan_mode_buf[1] != 0xFF) { if (fan_mode_buf[1] != 0xFF) {
this->control_messages_queue_.push( this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::FAN_MODE, (uint8_t) hon_protocol::DataParameters::FAN_MODE,
fan_mode_buf, 2)); fan_mode_buf, 2);
} }
} }
} }

View File

@ -0,0 +1,44 @@
from esphome import pins
import esphome.codegen as cg
from esphome.components import switch
import esphome.config_validation as cv
from esphome.const import CONF_OPTIMISTIC, CONF_PULSE_LENGTH, CONF_WAIT_TIME
from .. import hbridge_ns
HBridgeSwitch = hbridge_ns.class_("HBridgeSwitch", switch.Switch, cg.Component)
CODEOWNERS = ["@dwmw2"]
CONF_OFF_PIN = "off_pin"
CONF_ON_PIN = "on_pin"
CONFIG_SCHEMA = (
switch.switch_schema(HBridgeSwitch)
.extend(
{
cv.Required(CONF_ON_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_OFF_PIN): pins.gpio_output_pin_schema,
cv.Optional(
CONF_PULSE_LENGTH, default="100ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_WAIT_TIME): cv.positive_time_period_milliseconds,
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
}
)
.extend(cv.COMPONENT_SCHEMA)
)
async def to_code(config):
var = await switch.new_switch(config)
await cg.register_component(var, config)
on_pin = await cg.gpio_pin_expression(config[CONF_ON_PIN])
cg.add(var.set_on_pin(on_pin))
off_pin = await cg.gpio_pin_expression(config[CONF_OFF_PIN])
cg.add(var.set_off_pin(off_pin))
cg.add(var.set_pulse_length(config[CONF_PULSE_LENGTH]))
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
if wait_time := config.get(CONF_WAIT_TIME):
cg.add(var.set_wait_time(wait_time))

View File

@ -0,0 +1,95 @@
#include "hbridge_switch.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace hbridge {
static const char *const TAG = "switch.hbridge";
float HBridgeSwitch::get_setup_priority() const { return setup_priority::HARDWARE; }
void HBridgeSwitch::setup() {
ESP_LOGCONFIG(TAG, "Setting up H-Bridge Switch '%s'...", this->name_.c_str());
optional<bool> initial_state = this->get_initial_state_with_restore_mode().value_or(false);
// Like GPIOSwitch does, set the pin state both before and after pin setup()
this->on_pin_->digital_write(false);
this->on_pin_->setup();
this->on_pin_->digital_write(false);
this->off_pin_->digital_write(false);
this->off_pin_->setup();
this->off_pin_->digital_write(false);
if (initial_state.has_value())
this->write_state(initial_state);
}
void HBridgeSwitch::dump_config() {
LOG_SWITCH("", "H-Bridge Switch", this);
LOG_PIN(" On Pin: ", this->on_pin_);
LOG_PIN(" Off Pin: ", this->off_pin_);
ESP_LOGCONFIG(TAG, " Pulse length: %" PRId32 " ms", this->pulse_length_);
if (this->wait_time_)
ESP_LOGCONFIG(TAG, " Wait time %" PRId32 " ms", this->wait_time_);
}
void HBridgeSwitch::write_state(bool state) {
this->desired_state_ = state;
if (!this->timer_running_)
this->timer_fn_();
}
void HBridgeSwitch::timer_fn_() {
uint32_t next_timeout = 0;
while ((uint8_t) this->desired_state_ != this->relay_state_) {
switch (this->relay_state_) {
case RELAY_STATE_ON:
case RELAY_STATE_OFF:
case RELAY_STATE_UNKNOWN:
if (this->desired_state_) {
this->on_pin_->digital_write(true);
this->relay_state_ = RELAY_STATE_SWITCHING_ON;
} else {
this->off_pin_->digital_write(true);
this->relay_state_ = RELAY_STATE_SWITCHING_OFF;
}
next_timeout = this->pulse_length_;
if (!this->optimistic_)
this->publish_state(this->desired_state_);
break;
case RELAY_STATE_SWITCHING_ON:
this->on_pin_->digital_write(false);
this->relay_state_ = RELAY_STATE_ON;
if (this->optimistic_)
this->publish_state(true);
next_timeout = this->wait_time_;
break;
case RELAY_STATE_SWITCHING_OFF:
this->off_pin_->digital_write(false);
this->relay_state_ = RELAY_STATE_OFF;
if (this->optimistic_)
this->publish_state(false);
next_timeout = this->wait_time_;
break;
}
if (next_timeout) {
this->timer_running_ = true;
this->set_timeout(next_timeout, [this]() { this->timer_fn_(); });
return;
}
// In the case where ON/OFF state has been reached but we need to
// immediately change back again to reach desired_state_, we loop.
}
this->timer_running_ = false;
}
} // namespace hbridge
} // namespace esphome

View File

@ -0,0 +1,50 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/switch/switch.h"
#include <vector>
namespace esphome {
namespace hbridge {
enum RelayState : uint8_t {
RELAY_STATE_OFF = 0,
RELAY_STATE_ON = 1,
RELAY_STATE_SWITCHING_ON = 2,
RELAY_STATE_SWITCHING_OFF = 3,
RELAY_STATE_UNKNOWN = 4,
};
class HBridgeSwitch : public switch_::Switch, public Component {
public:
void set_on_pin(GPIOPin *pin) { this->on_pin_ = pin; }
void set_off_pin(GPIOPin *pin) { this->off_pin_ = pin; }
void set_pulse_length(uint32_t pulse_length) { this->pulse_length_ = pulse_length; }
void set_wait_time(uint32_t wait_time) { this->wait_time_ = wait_time; }
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
float get_setup_priority() const override;
void setup() override;
void dump_config() override;
protected:
void write_state(bool state) override;
void timer_fn_();
bool timer_running_{false};
bool desired_state_{false};
RelayState relay_state_{RELAY_STATE_UNKNOWN};
GPIOPin *on_pin_{nullptr};
GPIOPin *off_pin_{nullptr};
uint32_t pulse_length_{0};
uint32_t wait_time_{0};
bool optimistic_{false};
};
} // namespace hbridge
} // namespace esphome

View File

@ -133,8 +133,10 @@ bool HitachiClimate::get_swing_v_() {
} }
void HitachiClimate::set_swing_h_(uint8_t position) { void HitachiClimate::set_swing_h_(uint8_t position) {
if (position > HITACHI_AC344_SWINGH_LEFT_MAX) if (position > HITACHI_AC344_SWINGH_LEFT_MAX) {
return set_swing_h_(HITACHI_AC344_SWINGH_MIDDLE); set_swing_h_(HITACHI_AC344_SWINGH_MIDDLE);
return;
}
set_bits(&remote_state_[HITACHI_AC344_SWINGH_BYTE], HITACHI_AC344_SWINGH_OFFSET, HITACHI_AC344_SWINGH_SIZE, position); set_bits(&remote_state_[HITACHI_AC344_SWINGH_BYTE], HITACHI_AC344_SWINGH_OFFSET, HITACHI_AC344_SWINGH_SIZE, position);
set_button_(HITACHI_AC344_BUTTON_SWINGH); set_button_(HITACHI_AC344_BUTTON_SWINGH);
} }

View File

@ -133,8 +133,10 @@ bool HitachiClimate::get_swing_v_() {
} }
void HitachiClimate::set_swing_h_(uint8_t position) { void HitachiClimate::set_swing_h_(uint8_t position) {
if (position > HITACHI_AC424_SWINGH_LEFT_MAX) if (position > HITACHI_AC424_SWINGH_LEFT_MAX) {
return set_swing_h_(HITACHI_AC424_SWINGH_MIDDLE); set_swing_h_(HITACHI_AC424_SWINGH_MIDDLE);
return;
}
set_bits(&remote_state_[HITACHI_AC424_SWINGH_BYTE], HITACHI_AC424_SWINGH_OFFSET, HITACHI_AC424_SWINGH_SIZE, position); set_bits(&remote_state_[HITACHI_AC424_SWINGH_BYTE], HITACHI_AC424_SWINGH_OFFSET, HITACHI_AC424_SWINGH_SIZE, position);
set_button_(HITACHI_AC424_BUTTON_SWINGH); set_button_(HITACHI_AC424_BUTTON_SWINGH);
} }

View File

@ -27,6 +27,7 @@ void HomeassistantNumber::min_retrieved_(const std::string &min) {
auto min_value = parse_number<float>(min); auto min_value = parse_number<float>(min);
if (!min_value.has_value()) { if (!min_value.has_value()) {
ESP_LOGE(TAG, "'%s': Can't convert 'min' value '%s' to number!", this->entity_id_.c_str(), min.c_str()); ESP_LOGE(TAG, "'%s': Can't convert 'min' value '%s' to number!", this->entity_id_.c_str(), min.c_str());
return;
} }
ESP_LOGD(TAG, "'%s': Min retrieved: %s", get_name().c_str(), min.c_str()); ESP_LOGD(TAG, "'%s': Min retrieved: %s", get_name().c_str(), min.c_str());
this->traits.set_min_value(min_value.value()); this->traits.set_min_value(min_value.value());
@ -36,6 +37,7 @@ void HomeassistantNumber::max_retrieved_(const std::string &max) {
auto max_value = parse_number<float>(max); auto max_value = parse_number<float>(max);
if (!max_value.has_value()) { if (!max_value.has_value()) {
ESP_LOGE(TAG, "'%s': Can't convert 'max' value '%s' to number!", this->entity_id_.c_str(), max.c_str()); ESP_LOGE(TAG, "'%s': Can't convert 'max' value '%s' to number!", this->entity_id_.c_str(), max.c_str());
return;
} }
ESP_LOGD(TAG, "'%s': Max retrieved: %s", get_name().c_str(), max.c_str()); ESP_LOGD(TAG, "'%s': Max retrieved: %s", get_name().c_str(), max.c_str());
this->traits.set_max_value(max_value.value()); this->traits.set_max_value(max_value.value());
@ -45,6 +47,7 @@ void HomeassistantNumber::step_retrieved_(const std::string &step) {
auto step_value = parse_number<float>(step); auto step_value = parse_number<float>(step);
if (!step_value.has_value()) { if (!step_value.has_value()) {
ESP_LOGE(TAG, "'%s': Can't convert 'step' value '%s' to number!", this->entity_id_.c_str(), step.c_str()); ESP_LOGE(TAG, "'%s': Can't convert 'step' value '%s' to number!", this->entity_id_.c_str(), step.c_str());
return;
} }
ESP_LOGD(TAG, "'%s': Step Retrieved %s", get_name().c_str(), step.c_str()); ESP_LOGD(TAG, "'%s': Step Retrieved %s", get_name().c_str(), step.c_str());
this->traits.set_step(step_value.value()); this->traits.set_step(step_value.value());

View File

@ -15,7 +15,7 @@ static const char *const TAG = "honeywellabp2";
void HONEYWELLABP2Sensor::read_sensor_data() { void HONEYWELLABP2Sensor::read_sensor_data() {
if (this->read(raw_data_, 7) != i2c::ERROR_OK) { if (this->read(raw_data_, 7) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with ABP2 failed!"); ESP_LOGE(TAG, "Communication with ABP2 failed!");
this->mark_failed(); this->status_set_warning("couldn't read sensor data");
return; return;
} }
float press_counts = encode_uint24(raw_data_[1], raw_data_[2], raw_data_[3]); // calculate digital pressure counts float press_counts = encode_uint24(raw_data_[1], raw_data_[2], raw_data_[3]); // calculate digital pressure counts
@ -25,12 +25,13 @@ void HONEYWELLABP2Sensor::read_sensor_data() {
(this->max_pressure_ - this->min_pressure_)) + (this->max_pressure_ - this->min_pressure_)) +
this->min_pressure_; this->min_pressure_;
this->last_temperature_ = (temp_counts * 200 / 16777215) - 50; this->last_temperature_ = (temp_counts * 200 / 16777215) - 50;
this->status_clear_warning();
} }
void HONEYWELLABP2Sensor::start_measurement() { void HONEYWELLABP2Sensor::start_measurement() {
if (this->write(i2c_cmd_, 3) != i2c::ERROR_OK) { if (this->write(i2c_cmd_, 3) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with ABP2 failed!"); ESP_LOGE(TAG, "Communication with ABP2 failed!");
this->mark_failed(); this->status_set_warning("couldn't start measurement");
return; return;
} }
this->measurement_running_ = true; this->measurement_running_ = true;
@ -39,7 +40,7 @@ void HONEYWELLABP2Sensor::start_measurement() {
bool HONEYWELLABP2Sensor::is_measurement_ready() { bool HONEYWELLABP2Sensor::is_measurement_ready() {
if (this->read(raw_data_, 1) != i2c::ERROR_OK) { if (this->read(raw_data_, 1) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with ABP2 failed!"); ESP_LOGE(TAG, "Communication with ABP2 failed!");
this->mark_failed(); this->status_set_warning("couldn't check measurement");
return false; return false;
} }
if ((raw_data_[0] & (0x1 << STATUS_BIT_BUSY)) > 0) { if ((raw_data_[0] & (0x1 << STATUS_BIT_BUSY)) > 0) {
@ -52,7 +53,7 @@ bool HONEYWELLABP2Sensor::is_measurement_ready() {
void HONEYWELLABP2Sensor::measurement_timeout() { void HONEYWELLABP2Sensor::measurement_timeout() {
ESP_LOGE(TAG, "Timeout!"); ESP_LOGE(TAG, "Timeout!");
this->measurement_running_ = false; this->measurement_running_ = false;
this->mark_failed(); this->status_set_warning("measurement timed out");
} }
float HONEYWELLABP2Sensor::get_pressure() { return this->last_pressure_; } float HONEYWELLABP2Sensor::get_pressure() { return this->last_pressure_; }
@ -79,7 +80,7 @@ void HONEYWELLABP2Sensor::update() {
ESP_LOGV(TAG, "Update Honeywell ABP2 Sensor"); ESP_LOGV(TAG, "Update Honeywell ABP2 Sensor");
this->start_measurement(); this->start_measurement();
this->set_timeout("meas_timeout", 50, [this] { this->measurement_timeout(); }); this->set_timeout("meas_timeout", 100, [this] { this->measurement_timeout(); });
} }
void HONEYWELLABP2Sensor::dump_config() { void HONEYWELLABP2Sensor::dump_config() {

View File

@ -53,7 +53,7 @@ bool HX711Sensor::read_sensor_(uint32_t *result) {
} }
// Cycle clock pin for gain setting // Cycle clock pin for gain setting
for (uint8_t i = 0; i < this->gain_; i++) { for (uint8_t i = 0; i < static_cast<uint8_t>(this->gain_); i++) {
this->sck_pin_->digital_write(true); this->sck_pin_->digital_write(true);
delayMicroseconds(1); delayMicroseconds(1);
this->sck_pin_->digital_write(false); this->sck_pin_->digital_write(false);

View File

@ -9,7 +9,7 @@
namespace esphome { namespace esphome {
namespace hx711 { namespace hx711 {
enum HX711Gain { enum HX711Gain : uint8_t {
HX711_GAIN_128 = 1, HX711_GAIN_128 = 1,
HX711_GAIN_32 = 2, HX711_GAIN_32 = 2,
HX711_GAIN_64 = 3, HX711_GAIN_64 = 3,

View File

@ -17,14 +17,14 @@ void IDFI2CBus::setup() {
ESP_LOGCONFIG(TAG, "Setting up I2C bus..."); ESP_LOGCONFIG(TAG, "Setting up I2C bus...");
static i2c_port_t next_port = I2C_NUM_0; static i2c_port_t next_port = I2C_NUM_0;
port_ = next_port; port_ = next_port;
#if I2C_NUM_MAX > 1 #if SOC_I2C_NUM > 1
next_port = (next_port == I2C_NUM_0) ? I2C_NUM_1 : I2C_NUM_MAX; next_port = (next_port == I2C_NUM_0) ? I2C_NUM_1 : I2C_NUM_MAX;
#else #else
next_port = I2C_NUM_MAX; next_port = I2C_NUM_MAX;
#endif #endif
if (port_ == I2C_NUM_MAX) { if (port_ == I2C_NUM_MAX) {
ESP_LOGE(TAG, "Too many I2C buses configured"); ESP_LOGE(TAG, "Too many I2C buses configured. Max %u supported.", SOC_I2C_NUM);
this->mark_failed(); this->mark_failed();
return; return;
} }

View File

@ -33,14 +33,15 @@ enum SpeakerEventGroupBits : uint32_t {
STATE_RUNNING = (1 << 11), STATE_RUNNING = (1 << 11),
STATE_STOPPING = (1 << 12), STATE_STOPPING = (1 << 12),
STATE_STOPPED = (1 << 13), STATE_STOPPED = (1 << 13),
ERR_INVALID_FORMAT = (1 << 14), ERR_TASK_FAILED_TO_START = (1 << 14),
ERR_TASK_FAILED_TO_START = (1 << 15), ERR_ESP_INVALID_STATE = (1 << 15),
ERR_ESP_INVALID_STATE = (1 << 16), ERR_ESP_NOT_SUPPORTED = (1 << 16),
ERR_ESP_INVALID_ARG = (1 << 17), ERR_ESP_INVALID_ARG = (1 << 17),
ERR_ESP_INVALID_SIZE = (1 << 18), ERR_ESP_INVALID_SIZE = (1 << 18),
ERR_ESP_NO_MEM = (1 << 19), ERR_ESP_NO_MEM = (1 << 19),
ERR_ESP_FAIL = (1 << 20), ERR_ESP_FAIL = (1 << 20),
ALL_ERR_ESP_BITS = ERR_ESP_INVALID_STATE | ERR_ESP_INVALID_ARG | ERR_ESP_INVALID_SIZE | ERR_ESP_NO_MEM | ERR_ESP_FAIL, ALL_ERR_ESP_BITS = ERR_ESP_INVALID_STATE | ERR_ESP_NOT_SUPPORTED | ERR_ESP_INVALID_ARG | ERR_ESP_INVALID_SIZE |
ERR_ESP_NO_MEM | ERR_ESP_FAIL,
ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
}; };
@ -55,6 +56,8 @@ static esp_err_t err_bit_to_esp_err(uint32_t bit) {
return ESP_ERR_INVALID_SIZE; return ESP_ERR_INVALID_SIZE;
case SpeakerEventGroupBits::ERR_ESP_NO_MEM: case SpeakerEventGroupBits::ERR_ESP_NO_MEM:
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
case SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED:
return ESP_ERR_NOT_SUPPORTED;
default: default:
return ESP_FAIL; return ESP_FAIL;
} }
@ -135,19 +138,19 @@ void I2SAudioSpeaker::loop() {
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START); xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
} }
if (event_group_bits & SpeakerEventGroupBits::ERR_INVALID_FORMAT) { if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
this->status_set_warning();
}
if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) {
this->status_set_error("Failed to adjust I2S bus to match the incoming audio"); this->status_set_error("Failed to adjust I2S bus to match the incoming audio");
ESP_LOGE(TAG, ESP_LOGE(TAG,
"Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8, "Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8,
this->audio_stream_info_.sample_rate, this->audio_stream_info_.channels, this->audio_stream_info_.sample_rate, this->audio_stream_info_.channels,
this->audio_stream_info_.bits_per_sample); this->audio_stream_info_.bits_per_sample);
} }
if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
this->status_set_warning();
}
} }
void I2SAudioSpeaker::set_volume(float volume) { void I2SAudioSpeaker::set_volume(float volume) {
@ -236,13 +239,15 @@ void I2SAudioSpeaker::speaker_task(void *params) {
xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STARTING); xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STARTING);
audio::AudioStreamInfo audio_stream_info = this_speaker->audio_stream_info_; audio::AudioStreamInfo audio_stream_info = this_speaker->audio_stream_info_;
const ssize_t bytes_per_sample = audio_stream_info.get_bytes_per_sample();
const uint8_t number_of_channels = audio_stream_info.channels;
const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * this_speaker->sample_rate_ / 1000 * const uint32_t bytes_per_ms =
bytes_per_sample * number_of_channels; audio_stream_info.channels * audio_stream_info.get_bytes_per_sample() * audio_stream_info.sample_rate / 1000;
const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * bytes_per_ms;
// Ensure ring buffer is at least as large as the total size of the DMA buffers
const size_t ring_buffer_size = const size_t ring_buffer_size =
this_speaker->buffer_duration_ms_ * this_speaker->sample_rate_ / 1000 * bytes_per_sample * number_of_channels; std::min((uint32_t) dma_buffers_size, this_speaker->buffer_duration_ms_ * bytes_per_ms);
if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) { if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) {
// Failed to allocate buffers // Failed to allocate buffers
@ -250,14 +255,7 @@ void I2SAudioSpeaker::speaker_task(void *params) {
this_speaker->delete_task_(dma_buffers_size); this_speaker->delete_task_(dma_buffers_size);
} }
if (this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_())) { if (!this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_(audio_stream_info))) {
// Failed to start I2S driver
this_speaker->delete_task_(dma_buffers_size);
}
if (!this_speaker->send_esp_err_to_event_group_(this_speaker->reconfigure_i2s_stream_info_(audio_stream_info))) {
// Successfully set the I2S stream info, ready to write audio data to the I2S port
xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_RUNNING); xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
bool stop_gracefully = false; bool stop_gracefully = false;
@ -275,6 +273,12 @@ void I2SAudioSpeaker::speaker_task(void *params) {
stop_gracefully = true; stop_gracefully = true;
} }
if (this_speaker->audio_stream_info_ != audio_stream_info) {
// Audio stream info has changed, stop the speaker task so it will restart with the proper settings.
break;
}
i2s_event_t i2s_event; i2s_event_t i2s_event;
while (xQueueReceive(this_speaker->i2s_event_queue_, &i2s_event, 0)) { while (xQueueReceive(this_speaker->i2s_event_queue_, &i2s_event, 0)) {
if (i2s_event.type == I2S_EVENT_TX_Q_OVF) { if (i2s_event.type == I2S_EVENT_TX_Q_OVF) {
@ -316,17 +320,14 @@ void I2SAudioSpeaker::speaker_task(void *params) {
} }
} }
} }
} else {
// Couldn't configure the I2S port to be compatible with the incoming audio
xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_INVALID_FORMAT);
}
i2s_zero_dma_buffer(this_speaker->parent_->get_port());
xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING); xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
i2s_driver_uninstall(this_speaker->parent_->get_port()); i2s_driver_uninstall(this_speaker->parent_->get_port());
this_speaker->parent_->unlock(); this_speaker->parent_->unlock();
}
this_speaker->delete_task_(dma_buffers_size); this_speaker->delete_task_(dma_buffers_size);
} }
@ -382,6 +383,9 @@ bool I2SAudioSpeaker::send_esp_err_to_event_group_(esp_err_t err) {
case ESP_ERR_NO_MEM: case ESP_ERR_NO_MEM:
xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM); xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
return true; return true;
case ESP_ERR_NOT_SUPPORTED:
xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED);
return true;
default: default:
xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_FAIL); xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_FAIL);
return true; return true;
@ -411,18 +415,40 @@ esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t rin
return ESP_OK; return ESP_OK;
} }
esp_err_t I2SAudioSpeaker::start_i2s_driver_() { esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info) {
if ((this->i2s_mode_ & I2S_MODE_SLAVE) && (this->sample_rate_ != audio_stream_info.sample_rate)) { // NOLINT
// Can't reconfigure I2S bus, so the sample rate must match the configured value
return ESP_ERR_NOT_SUPPORTED;
}
if ((i2s_bits_per_sample_t) audio_stream_info.bits_per_sample > this->bits_per_sample_) {
// Currently can't handle the case when the incoming audio has more bits per sample than the configured value
return ESP_ERR_NOT_SUPPORTED;
}
if (!this->parent_->try_lock()) { if (!this->parent_->try_lock()) {
return ESP_ERR_INVALID_STATE; return ESP_ERR_INVALID_STATE;
} }
i2s_channel_fmt_t channel = this->channel_;
if (audio_stream_info.channels == 1) {
if (this->channel_ == I2S_CHANNEL_FMT_ONLY_LEFT) {
channel = I2S_CHANNEL_FMT_ONLY_LEFT;
} else {
channel = I2S_CHANNEL_FMT_ONLY_RIGHT;
}
} else if (audio_stream_info.channels == 2) {
channel = I2S_CHANNEL_FMT_RIGHT_LEFT;
}
int dma_buffer_length = DMA_BUFFER_DURATION_MS * this->sample_rate_ / 1000; int dma_buffer_length = DMA_BUFFER_DURATION_MS * this->sample_rate_ / 1000;
i2s_driver_config_t config = { i2s_driver_config_t config = {
.mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_TX), .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_TX),
.sample_rate = this->sample_rate_, .sample_rate = audio_stream_info.sample_rate,
.bits_per_sample = this->bits_per_sample_, .bits_per_sample = this->bits_per_sample_,
.channel_format = this->channel_, .channel_format = channel,
.communication_format = this->i2s_comm_fmt_, .communication_format = this->i2s_comm_fmt_,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = DMA_BUFFERS_COUNT, .dma_buf_count = DMA_BUFFERS_COUNT,
@ -477,30 +503,6 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_() {
return err; return err;
} }
esp_err_t I2SAudioSpeaker::reconfigure_i2s_stream_info_(audio::AudioStreamInfo &audio_stream_info) {
if (this->i2s_mode_ & I2S_MODE_MASTER) {
// ESP controls for the the I2S bus, so adjust the sample rate and bits per sample to match the incoming audio
this->sample_rate_ = audio_stream_info.sample_rate;
this->bits_per_sample_ = (i2s_bits_per_sample_t) audio_stream_info.bits_per_sample;
} else if (this->sample_rate_ != audio_stream_info.sample_rate) {
// Can't reconfigure I2S bus, so the sample rate must match the configured value
return ESP_ERR_INVALID_ARG;
}
if ((i2s_bits_per_sample_t) audio_stream_info.bits_per_sample > this->bits_per_sample_) {
// Currently can't handle the case when the incoming audio has more bits per sample than the configured value
return ESP_ERR_INVALID_ARG;
}
if (audio_stream_info.channels == 1) {
return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_MONO);
} else if (audio_stream_info.channels == 2) {
return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_STEREO);
}
return ESP_ERR_INVALID_ARG;
}
void I2SAudioSpeaker::delete_task_(size_t buffer_size) { void I2SAudioSpeaker::delete_task_(size_t buffer_size) {
this->audio_ring_buffer_.reset(); // Releases onwership of the shared_ptr this->audio_ring_buffer_.reset(); // Releases onwership of the shared_ptr

View File

@ -91,24 +91,15 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
esp_err_t allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size); esp_err_t allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size);
/// @brief Starts the ESP32 I2S driver. /// @brief Starts the ESP32 I2S driver.
/// Attempts to lock the I2S port, starts the I2S driver, and sets the data out pin. If it fails, it will unlock /// Attempts to lock the I2S port, starts the I2S driver using the passed in stream information, and sets the data out
/// the I2S port and uninstall the driver, if necessary. /// pin. If it fails, it will unlock the I2S port and uninstall the driver, if necessary.
/// @return ESP_ERR_INVALID_STATE if the I2S port is already locked. /// @param audio_stream_info Stream information for the I2S driver.
/// ESP_ERR_INVALID_ARG if installing the driver or setting the data out pin fails due to a parameter error. /// @return ESP_ERR_NOT_ALLOWED if the I2S port can't play the incoming audio stream.
/// ESP_ERR_INVALID_STATE if the I2S port is already locked.
/// ESP_ERR_INVALID_ARG if nstalling the driver or setting the data outpin fails due to a parameter error.
/// ESP_ERR_NO_MEM if the driver fails to install due to a memory allocation error. /// ESP_ERR_NO_MEM if the driver fails to install due to a memory allocation error.
/// ESP_FAIL if setting the data out pin fails due to an IO error /// ESP_FAIL if setting the data out pin fails due to an IO error ESP_OK if successful
/// ESP_OK if successful esp_err_t start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info);
esp_err_t start_i2s_driver_();
/// @brief Adjusts the I2S driver configuration to match the incoming audio stream.
/// Modifies I2S driver's sample rate, bits per sample, and number of channel settings. If the I2S is in secondary
/// mode, it only modifies the number of channels.
/// @param audio_stream_info Describes the incoming audio stream
/// @return ESP_ERR_INVALID_ARG if there is a parameter error, if there is more than 2 channels in the stream, or if
/// the audio settings are incompatible with the configuration.
/// ESP_ERR_NO_MEM if the driver fails to reconfigure due to a memory allocation error.
/// ESP_OK if successful.
esp_err_t reconfigure_i2s_stream_info_(audio::AudioStreamInfo &audio_stream_info);
/// @brief Deletes the speaker's task. /// @brief Deletes the speaker's task.
/// Deallocates the data_buffer_ and audio_ring_buffer_, if necessary, and deletes the task. Should only be called by /// Deallocates the data_buffer_ and audio_ring_buffer_, if necessary, and deletes the task. Should only be called by

View File

@ -1,6 +1,6 @@
from esphome import core, pins from esphome import core, pins
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import display, font, spi from esphome.components import display, spi
from esphome.components.display import validate_rotation from esphome.components.display import validate_rotation
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
@ -147,7 +147,6 @@ def _validate(config):
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
font.validate_pillow_installed,
display.FULL_DISPLAY_SCHEMA.extend( display.FULL_DISPLAY_SCHEMA.extend(
{ {
cv.GenerateID(): cv.declare_id(ILI9XXXDisplay), cv.GenerateID(): cv.declare_id(ILI9XXXDisplay),

View File

@ -313,8 +313,9 @@ void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, cons
// do color conversion pixel-by-pixel into the buffer and draw it later. If this is happening the user has not // do color conversion pixel-by-pixel into the buffer and draw it later. If this is happening the user has not
// configured the renderer well. // configured the renderer well.
if (this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || bitness != display::COLOR_BITNESS_565 || !big_endian) { if (this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || bitness != display::COLOR_BITNESS_565 || !big_endian) {
return display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset,
x_pad); x_pad);
return;
} }
this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1); this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1);
// x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display. // x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display.

View File

@ -10,7 +10,6 @@ import puremagic
from esphome import core, external_files from esphome import core, external_files
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import font
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_DITHER, CONF_DITHER,
@ -233,7 +232,7 @@ IMAGE_SCHEMA = cv.Schema(
) )
) )
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA) CONFIG_SCHEMA = IMAGE_SCHEMA
def load_svg_image(file: bytes, resize: tuple[int, int]): def load_svg_image(file: bytes, resize: tuple[int, int]):

View File

@ -47,7 +47,7 @@ void Logger::write_header_(int level, const char *tag, int line) {
if (current_task == main_task_) { if (current_task == main_task_) {
this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line); this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line);
} else { } else {
const char *thread_name = ""; const char *thread_name = ""; // NOLINT(clang-analyzer-deadcode.DeadStores)
#if defined(USE_ESP32) #if defined(USE_ESP32)
thread_name = pcTaskGetName(current_task); thread_name = pcTaskGetName(current_task);
#elif defined(USE_LIBRETINY) #elif defined(USE_LIBRETINY)

View File

@ -23,7 +23,7 @@ bool operator==(const GainTimePair &lhs, const GainTimePair &rhs) {
} }
bool operator!=(const GainTimePair &lhs, const GainTimePair &rhs) { bool operator!=(const GainTimePair &lhs, const GainTimePair &rhs) {
return !(lhs.gain == rhs.gain && lhs.time == rhs.time); return lhs.gain != rhs.gain || lhs.time != rhs.time;
} }
template<typename T, size_t size> T get_next(const T (&array)[size], const T val) { template<typename T, size_t size> T get_next(const T (&array)[size], const T val) {

View File

@ -8,7 +8,7 @@ import logging
from esphome import codegen as cg, config_validation as cv from esphome import codegen as cg, config_validation as cv
from esphome.const import CONF_ITEMS from esphome.const import CONF_ITEMS
from esphome.core import Lambda from esphome.core import ID, Lambda
from esphome.cpp_generator import LambdaExpression, MockObj from esphome.cpp_generator import LambdaExpression, MockObj
from esphome.cpp_types import uint32 from esphome.cpp_types import uint32
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
@ -38,7 +38,7 @@ def literal(arg):
def call_lambda(lamb: LambdaExpression): def call_lambda(lamb: LambdaExpression):
expr = lamb.content.strip() expr = lamb.content.strip()
if expr.startswith("return") and expr.endswith(";"): if expr.startswith("return") and expr.endswith(";"):
return expr[7:][:-1] return expr[6:][:-1].strip()
return f"{lamb}()" return f"{lamb}()"
@ -72,6 +72,12 @@ class LValidator:
) )
if self.retmapper is not None: if self.retmapper is not None:
return self.retmapper(value) return self.retmapper(value)
if isinstance(value, ID):
return await cg.get_variable(value)
if isinstance(value, list):
value = [
await cg.get_variable(x) if isinstance(x, ID) else x for x in value
]
return cg.safe_exp(value) return cg.safe_exp(value)
@ -162,6 +168,7 @@ LV_EVENT_MAP = {
"READY": "READY", "READY": "READY",
"CANCEL": "CANCEL", "CANCEL": "CANCEL",
"ALL_EVENTS": "ALL", "ALL_EVENTS": "ALL",
"CHANGE": "VALUE_CHANGED",
} }
LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT_MAP) LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT_MAP)

View File

@ -1,6 +1,7 @@
from typing import Union from typing import Union
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import image
from esphome.components.color import CONF_HEX, ColorStruct, from_rgbw from esphome.components.color import CONF_HEX, ColorStruct, from_rgbw
from esphome.components.font import Font from esphome.components.font import Font
from esphome.components.image import Image_ from esphome.components.image import Image_
@ -31,7 +32,7 @@ from .defines import (
literal, literal,
) )
from .helpers import add_lv_use, esphome_fonts_used, lv_fonts_used, requires_component from .helpers import add_lv_use, esphome_fonts_used, lv_fonts_used, requires_component
from .types import lv_font_t, lv_gradient_t, lv_img_t from .types import lv_font_t, lv_gradient_t
opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER") opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER")
@ -332,8 +333,12 @@ def image_validator(value):
lv_image = LValidator( lv_image = LValidator(
image_validator, image_validator,
lv_img_t, image.Image_.operator("ptr"),
retmapper=lambda x: MockObj(x, "->").get_lv_img_dsc(), requires="image",
)
lv_image_list = LValidator(
cv.ensure_list(image_validator),
cg.std_vector.template(image.Image_.operator("ptr")),
requires="image", requires="image",
) )
lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal) lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal)

View File

@ -279,7 +279,7 @@ std::string LvSelectable::get_selected_text() {
static std::string join_string(std::vector<std::string> options) { static std::string join_string(std::vector<std::string> options) {
return std::accumulate( return std::accumulate(
options.begin(), options.end(), std::string(), options.begin(), options.end(), std::string(),
[](const std::string &a, const std::string &b) -> std::string { return a + (a.length() > 0 ? "\n" : "") + b; }); [](const std::string &a, const std::string &b) -> std::string { return a + (!a.empty() ? "\n" : "") + b; });
} }
void LvSelectable::set_selected_text(const std::string &text, lv_anim_enable_t anim) { void LvSelectable::set_selected_text(const std::string &text, lv_anim_enable_t anim) {

View File

@ -56,7 +56,26 @@ static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BIT
inline void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image) { inline void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image) {
lv_img_set_src(obj, image->get_lv_img_dsc()); lv_img_set_src(obj, image->get_lv_img_dsc());
} }
inline void lv_disp_set_bg_image(lv_disp_t *disp, esphome::image::Image *image) {
lv_disp_set_bg_image(disp, image->get_lv_img_dsc());
}
#endif // USE_LVGL_IMAGE #endif // USE_LVGL_IMAGE
#ifdef USE_LVGL_ANIMIMG
inline void lv_animimg_set_src(lv_obj_t *img, std::vector<image::Image *> images) {
auto *dsc = static_cast<std::vector<lv_img_dsc_t *> *>(lv_obj_get_user_data(img));
if (dsc == nullptr) {
// object will be lazily allocated but never freed.
dsc = new std::vector<lv_img_dsc_t *>(images.size()); // NOLINT
lv_obj_set_user_data(img, dsc);
}
dsc->clear();
for (auto &image : images) {
dsc->push_back(image->get_lv_img_dsc());
}
lv_animimg_set_src(img, (const void **) dsc->data(), dsc->size());
}
#endif // USE_LVGL_ANIMIMG
// Parent class for things that wrap an LVGL object // Parent class for things that wrap an LVGL object
class LvCompound { class LvCompound {

View File

@ -1,20 +1,18 @@
from esphome import automation from esphome import automation
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_DURATION, CONF_ID from esphome.const import CONF_DURATION, CONF_ID
from ..automation import action_to_code from ..automation import action_to_code
from ..defines import CONF_AUTO_START, CONF_MAIN, CONF_REPEAT_COUNT, CONF_SRC from ..defines import CONF_AUTO_START, CONF_MAIN, CONF_REPEAT_COUNT, CONF_SRC
from ..helpers import lvgl_components_required from ..helpers import lvgl_components_required
from ..lv_validation import lv_image, lv_milliseconds from ..lv_validation import lv_image_list, lv_milliseconds
from ..lvcode import lv from ..lvcode import lv
from ..types import LvType, ObjUpdateAction, void_ptr from ..types import LvType, ObjUpdateAction
from . import Widget, WidgetType, get_widgets from . import Widget, WidgetType, get_widgets
from .img import CONF_IMAGE from .img import CONF_IMAGE
from .label import CONF_LABEL from .label import CONF_LABEL
CONF_ANIMIMG = "animimg" CONF_ANIMIMG = "animimg"
CONF_SRC_LIST_ID = "src_list_id"
def lv_repeat_count(value): def lv_repeat_count(value):
@ -32,14 +30,14 @@ ANIMIMG_BASE_SCHEMA = cv.Schema(
ANIMIMG_SCHEMA = ANIMIMG_BASE_SCHEMA.extend( ANIMIMG_SCHEMA = ANIMIMG_BASE_SCHEMA.extend(
{ {
cv.Required(CONF_DURATION): lv_milliseconds, cv.Required(CONF_DURATION): lv_milliseconds,
cv.Required(CONF_SRC): cv.ensure_list(lv_image), cv.Required(CONF_SRC): lv_image_list,
cv.GenerateID(CONF_SRC_LIST_ID): cv.declare_id(void_ptr),
} }
) )
ANIMIMG_MODIFY_SCHEMA = ANIMIMG_BASE_SCHEMA.extend( ANIMIMG_MODIFY_SCHEMA = ANIMIMG_BASE_SCHEMA.extend(
{ {
cv.Optional(CONF_DURATION): lv_milliseconds, cv.Optional(CONF_DURATION): lv_milliseconds,
cv.Optional(CONF_SRC): lv_image_list,
} }
) )
@ -59,17 +57,14 @@ class AnimimgType(WidgetType):
async def to_code(self, w: Widget, config): async def to_code(self, w: Widget, config):
lvgl_components_required.add(CONF_IMAGE) lvgl_components_required.add(CONF_IMAGE)
lvgl_components_required.add(CONF_ANIMIMG) lvgl_components_required.add(CONF_ANIMIMG)
if CONF_SRC in config: if srcs := config.get(CONF_SRC):
srcs = [ srcs = await lv_image_list.process(srcs)
await lv_image.process(await cg.get_variable(x)) lv.animimg_set_src(w.obj, srcs)
for x in config[CONF_SRC] if repeat_count := config.get(CONF_REPEAT_COUNT):
] lv.animimg_set_repeat_count(w.obj, repeat_count)
src_id = cg.static_const_array(config[CONF_SRC_LIST_ID], srcs) if duration := config.get(CONF_DURATION):
count = len(config[CONF_SRC]) lv.animimg_set_duration(w.obj, duration)
lv.animimg_set_src(w.obj, src_id, count) if config[CONF_AUTO_START]:
lv.animimg_set_repeat_count(w.obj, config[CONF_REPEAT_COUNT])
lv.animimg_set_duration(w.obj, config[CONF_DURATION])
if config.get(CONF_AUTO_START):
lv.animimg_start(w.obj) lv.animimg_start(w.obj)
def get_uses(self): def get_uses(self):

View File

@ -1,4 +1,3 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_ANGLE, CONF_MODE from esphome.const import CONF_ANGLE, CONF_MODE
@ -65,7 +64,6 @@ class ImgType(WidgetType):
async def to_code(self, w: Widget, config): async def to_code(self, w: Widget, config):
if src := config.get(CONF_SRC): if src := config.get(CONF_SRC):
src = await cg.get_variable(src)
lv.img_set_src(w.obj, await lv_image.process(src)) lv.img_set_src(w.obj, await lv_image.process(src))
if (cf_angle := config.get(CONF_ANGLE)) is not None: if (cf_angle := config.get(CONF_ANGLE)) is not None:
pivot_x = config[CONF_PIVOT_X] pivot_x = config[CONF_PIVOT_X]
@ -81,7 +79,7 @@ class ImgType(WidgetType):
if CONF_ANTIALIAS in config: if CONF_ANTIALIAS in config:
lv.img_set_antialias(w.obj, config[CONF_ANTIALIAS]) lv.img_set_antialias(w.obj, config[CONF_ANTIALIAS])
if mode := config.get(CONF_MODE): if mode := config.get(CONF_MODE):
lv.img_set_mode(w.obj, mode) await w.set_property("size_mode", mode)
img_spec = ImgType() img_spec = ImgType()

View File

@ -35,6 +35,11 @@ LINE_SCHEMA = {
cv.GenerateID(CONF_POINT_LIST_ID): cv.declare_id(lv_point_t), cv.GenerateID(CONF_POINT_LIST_ID): cv.declare_id(lv_point_t),
} }
LINE_MODIFY_SCHEMA = {
cv.Optional(CONF_POINTS): cv_point_list,
cv.GenerateID(CONF_POINT_LIST_ID): cv.declare_id(lv_point_t),
}
class LineType(WidgetType): class LineType(WidgetType):
def __init__(self): def __init__(self):
@ -43,6 +48,7 @@ class LineType(WidgetType):
LvType("lv_line_t"), LvType("lv_line_t"),
(CONF_MAIN,), (CONF_MAIN,),
LINE_SCHEMA, LINE_SCHEMA,
modify_schema=LINE_MODIFY_SCHEMA,
) )
async def to_code(self, w: Widget, config): async def to_code(self, w: Widget, config):

View File

@ -29,7 +29,7 @@ from ..lvcode import (
) )
from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema, part_schema from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema, part_schema
from ..types import LV_EVENT, char_ptr, lv_obj_t from ..types import LV_EVENT, char_ptr, lv_obj_t
from . import Widget, set_obj_properties from . import Widget, add_widgets, set_obj_properties
from .button import button_spec from .button import button_spec
from .buttonmatrix import ( from .buttonmatrix import (
BUTTONMATRIX_BUTTON_SCHEMA, BUTTONMATRIX_BUTTON_SCHEMA,
@ -119,6 +119,7 @@ async def msgbox_to_code(top_layer, conf):
button_style = {CONF_ITEMS: button_style} button_style = {CONF_ITEMS: button_style}
await set_obj_properties(buttonmatrix_widget, button_style) await set_obj_properties(buttonmatrix_widget, button_style)
await set_obj_properties(msgbox_widget, conf) await set_obj_properties(msgbox_widget, conf)
await add_widgets(msgbox_widget, conf)
async with LambdaContext(EVENT_ARG, where=messagebox_id) as close_action: async with LambdaContext(EVENT_ARG, where=messagebox_id) as close_action:
outer_widget.add_flag("LV_OBJ_FLAG_HIDDEN") outer_widget.add_flag("LV_OBJ_FLAG_HIDDEN")
if close_button: if close_button:

View File

@ -1,8 +1,8 @@
from esphome import automation, pins
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import key_provider from esphome.components import key_provider
from esphome.const import CONF_ID, CONF_PIN import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_ON_KEY, CONF_PIN, CONF_TRIGGER_ID
CODEOWNERS = ["@ssieb"] CODEOWNERS = ["@ssieb"]
@ -14,6 +14,9 @@ matrix_keypad_ns = cg.esphome_ns.namespace("matrix_keypad")
MatrixKeypad = matrix_keypad_ns.class_( MatrixKeypad = matrix_keypad_ns.class_(
"MatrixKeypad", key_provider.KeyProvider, cg.Component "MatrixKeypad", key_provider.KeyProvider, cg.Component
) )
MatrixKeyTrigger = matrix_keypad_ns.class_(
"MatrixKeyTrigger", automation.Trigger.template(cg.uint8)
)
CONF_KEYPAD_ID = "keypad_id" CONF_KEYPAD_ID = "keypad_id"
CONF_ROWS = "rows" CONF_ROWS = "rows"
@ -47,6 +50,11 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_DEBOUNCE_TIME, default=1): cv.int_range(min=1, max=100), cv.Optional(CONF_DEBOUNCE_TIME, default=1): cv.int_range(min=1, max=100),
cv.Optional(CONF_HAS_DIODES): cv.boolean, cv.Optional(CONF_HAS_DIODES): cv.boolean,
cv.Optional(CONF_HAS_PULLDOWNS): cv.boolean, cv.Optional(CONF_HAS_PULLDOWNS): cv.boolean,
cv.Optional(CONF_ON_KEY): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MatrixKeyTrigger),
}
),
} }
), ),
check_keys, check_keys,
@ -73,3 +81,7 @@ async def to_code(config):
cg.add(var.set_has_diodes(config[CONF_HAS_DIODES])) cg.add(var.set_has_diodes(config[CONF_HAS_DIODES]))
if CONF_HAS_PULLDOWNS in config: if CONF_HAS_PULLDOWNS in config:
cg.add(var.set_has_pulldowns(config[CONF_HAS_PULLDOWNS])) cg.add(var.set_has_pulldowns(config[CONF_HAS_PULLDOWNS]))
for conf in config.get(CONF_ON_KEY, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
cg.add(var.register_key_trigger(trigger))
await automation.build_automation(trigger, [(cg.uint8, "x")], conf)

View File

@ -86,6 +86,8 @@ void MatrixKeypad::loop() {
if (!this->keys_.empty()) { if (!this->keys_.empty()) {
uint8_t keycode = this->keys_[key]; uint8_t keycode = this->keys_[key];
ESP_LOGD(TAG, "key '%c' pressed", keycode); ESP_LOGD(TAG, "key '%c' pressed", keycode);
for (auto &trigger : this->key_triggers_)
trigger->trigger(keycode);
for (auto &listener : this->listeners_) for (auto &listener : this->listeners_)
listener->key_pressed(keycode); listener->key_pressed(keycode);
this->send_key_(keycode); this->send_key_(keycode);
@ -107,5 +109,7 @@ void MatrixKeypad::dump_config() {
void MatrixKeypad::register_listener(MatrixKeypadListener *listener) { this->listeners_.push_back(listener); } void MatrixKeypad::register_listener(MatrixKeypadListener *listener) { this->listeners_.push_back(listener); }
void MatrixKeypad::register_key_trigger(MatrixKeyTrigger *trig) { this->key_triggers_.push_back(trig); }
} // namespace matrix_keypad } // namespace matrix_keypad
} // namespace esphome } // namespace esphome

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "esphome/components/key_provider/key_provider.h" #include "esphome/components/key_provider/key_provider.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
@ -18,6 +19,8 @@ class MatrixKeypadListener {
virtual void key_released(uint8_t key){}; virtual void key_released(uint8_t key){};
}; };
class MatrixKeyTrigger : public Trigger<uint8_t> {};
class MatrixKeypad : public key_provider::KeyProvider, public Component { class MatrixKeypad : public key_provider::KeyProvider, public Component {
public: public:
void setup() override; void setup() override;
@ -31,6 +34,7 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component {
void set_has_pulldowns(int has_pulldowns) { has_pulldowns_ = has_pulldowns; }; void set_has_pulldowns(int has_pulldowns) { has_pulldowns_ = has_pulldowns; };
void register_listener(MatrixKeypadListener *listener); void register_listener(MatrixKeypadListener *listener);
void register_key_trigger(MatrixKeyTrigger *trig);
protected: protected:
std::vector<GPIOPin *> rows_; std::vector<GPIOPin *> rows_;
@ -42,6 +46,7 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component {
int pressed_key_ = -1; int pressed_key_ = -1;
std::vector<MatrixKeypadListener *> listeners_{}; std::vector<MatrixKeypadListener *> listeners_{};
std::vector<MatrixKeyTrigger *> key_triggers_;
}; };
} // namespace matrix_keypad } // namespace matrix_keypad

View File

@ -106,7 +106,8 @@ void MAX31865Sensor::read_data_() {
// Check faults // Check faults
const uint8_t faults = this->read_register_(FAULT_STATUS_REG); const uint8_t faults = this->read_register_(FAULT_STATUS_REG);
if ((has_fault_ = faults & 0b00111100)) { has_fault_ = faults & 0b00111100;
if (has_fault_) {
if (faults & (1 << 2)) { if (faults & (1 << 2)) {
ESP_LOGE(TAG, "Overvoltage/undervoltage fault"); ESP_LOGE(TAG, "Overvoltage/undervoltage fault");
} }
@ -125,7 +126,8 @@ void MAX31865Sensor::read_data_() {
} else { } else {
this->status_clear_error(); this->status_clear_error();
} }
if ((has_warn_ = faults & 0b11000000)) { has_warn_ = faults & 0b11000000;
if (has_warn_) {
if (faults & (1 << 6)) { if (faults & (1 << 6)) {
ESP_LOGW(TAG, "RTD Low Threshold"); ESP_LOGW(TAG, "RTD Low Threshold");
} }

View File

@ -152,11 +152,11 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t
} }
SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const { SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const {
auto reg_it = find_if(begin(register_ranges_), end(register_ranges_), [=](RegisterRange const &r) { auto reg_it = std::find_if(
return (r.start_address == start_address && r.register_type == register_type); std::begin(this->register_ranges_), std::end(this->register_ranges_),
}); [=](RegisterRange const &r) { return (r.start_address == start_address && r.register_type == register_type); });
if (reg_it == register_ranges_.end()) { if (reg_it == this->register_ranges_.end()) {
ESP_LOGE(TAG, "No matching range for sensor found - start_address : 0x%X", start_address); ESP_LOGE(TAG, "No matching range for sensor found - start_address : 0x%X", start_address);
} else { } else {
return reg_it->sensors; return reg_it->sensors;
@ -240,18 +240,18 @@ void ModbusController::update() {
// walk through the sensors and determine the register ranges to read // walk through the sensors and determine the register ranges to read
size_t ModbusController::create_register_ranges_() { size_t ModbusController::create_register_ranges_() {
register_ranges_.clear(); this->register_ranges_.clear();
if (this->parent_->role == modbus::ModbusRole::CLIENT && sensorset_.empty()) { if (this->parent_->role == modbus::ModbusRole::CLIENT && this->sensorset_.empty()) {
ESP_LOGW(TAG, "No sensors registered"); ESP_LOGW(TAG, "No sensors registered");
return 0; return 0;
} }
// iterator is sorted see SensorItemsComparator for details // iterator is sorted see SensorItemsComparator for details
auto ix = sensorset_.begin(); auto ix = this->sensorset_.begin();
RegisterRange r = {}; RegisterRange r = {};
uint8_t buffer_offset = 0; uint8_t buffer_offset = 0;
SensorItem *prev = nullptr; SensorItem *prev = nullptr;
while (ix != sensorset_.end()) { while (ix != this->sensorset_.end()) {
SensorItem *curr = *ix; SensorItem *curr = *ix;
ESP_LOGV(TAG, "Register: 0x%X %d %d %d offset=%u skip=%u addr=%p", curr->start_address, curr->register_count, ESP_LOGV(TAG, "Register: 0x%X %d %d %d offset=%u skip=%u addr=%p", curr->start_address, curr->register_count,
@ -278,12 +278,12 @@ size_t ModbusController::create_register_ranges_() {
// this register can re-use the data from the previous register // this register can re-use the data from the previous register
// remove this sensore because start_address is changed (sort-order) // remove this sensore because start_address is changed (sort-order)
ix = sensorset_.erase(ix); ix = this->sensorset_.erase(ix);
curr->start_address = r.start_address; curr->start_address = r.start_address;
curr->offset += prev->offset; curr->offset += prev->offset;
sensorset_.insert(curr); this->sensorset_.insert(curr);
// move iterator backwards because it will be incremented later // move iterator backwards because it will be incremented later
ix--; ix--;
@ -293,14 +293,14 @@ size_t ModbusController::create_register_ranges_() {
// this register can extend the current range // this register can extend the current range
// remove this sensore because start_address is changed (sort-order) // remove this sensore because start_address is changed (sort-order)
ix = sensorset_.erase(ix); ix = this->sensorset_.erase(ix);
curr->start_address = r.start_address; curr->start_address = r.start_address;
curr->offset += buffer_offset; curr->offset += buffer_offset;
buffer_offset += curr->get_register_size(); buffer_offset += curr->get_register_size();
r.register_count += curr->register_count; r.register_count += curr->register_count;
sensorset_.insert(curr); this->sensorset_.insert(curr);
// move iterator backwards because it will be incremented later // move iterator backwards because it will be incremented later
ix--; ix--;
@ -327,7 +327,7 @@ size_t ModbusController::create_register_ranges_() {
ix++; ix++;
} else { } else {
ESP_LOGV(TAG, "Add range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates); ESP_LOGV(TAG, "Add range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
register_ranges_.push_back(r); this->register_ranges_.push_back(r);
r = {}; r = {};
buffer_offset = 0; buffer_offset = 0;
// do not increment the iterator here because the current sensor has to be re-evaluated // do not increment the iterator here because the current sensor has to be re-evaluated
@ -339,10 +339,10 @@ size_t ModbusController::create_register_ranges_() {
if (r.register_count > 0) { if (r.register_count > 0) {
// Add the last range // Add the last range
ESP_LOGV(TAG, "Add last range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates); ESP_LOGV(TAG, "Add last range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates);
register_ranges_.push_back(r); this->register_ranges_.push_back(r);
} }
return register_ranges_.size(); return this->register_ranges_.size();
} }
void ModbusController::dump_config() { void ModbusController::dump_config() {
@ -352,18 +352,18 @@ void ModbusController::dump_config() {
ESP_LOGCONFIG(TAG, " Offline Skip Updates: %d", this->offline_skip_updates_); ESP_LOGCONFIG(TAG, " Offline Skip Updates: %d", this->offline_skip_updates_);
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
ESP_LOGCONFIG(TAG, "sensormap"); ESP_LOGCONFIG(TAG, "sensormap");
for (auto &it : sensorset_) { for (auto &it : this->sensorset_) {
ESP_LOGCONFIG(TAG, " Sensor type=%zu start=0x%X offset=0x%X count=%d size=%d", ESP_LOGCONFIG(TAG, " Sensor type=%zu start=0x%X offset=0x%X count=%d size=%d",
static_cast<uint8_t>(it->register_type), it->start_address, it->offset, it->register_count, static_cast<uint8_t>(it->register_type), it->start_address, it->offset, it->register_count,
it->get_register_size()); it->get_register_size());
} }
ESP_LOGCONFIG(TAG, "ranges"); ESP_LOGCONFIG(TAG, "ranges");
for (auto &it : register_ranges_) { for (auto &it : this->register_ranges_) {
ESP_LOGCONFIG(TAG, " Range type=%zu start=0x%X count=%d skip_updates=%d", static_cast<uint8_t>(it.register_type), ESP_LOGCONFIG(TAG, " Range type=%zu start=0x%X count=%d skip_updates=%d", static_cast<uint8_t>(it.register_type),
it.start_address, it.register_count, it.skip_updates); it.start_address, it.register_count, it.skip_updates);
} }
ESP_LOGCONFIG(TAG, "server registers"); ESP_LOGCONFIG(TAG, "server registers");
for (auto &r : server_registers_) { for (auto &r : this->server_registers_) {
ESP_LOGCONFIG(TAG, " Address=0x%02X value_type=%zu register_count=%u", r->address, ESP_LOGCONFIG(TAG, " Address=0x%02X value_type=%zu register_count=%u", r->address,
static_cast<uint8_t>(r->value_type), r->register_count); static_cast<uint8_t>(r->value_type), r->register_count);
} }
@ -372,15 +372,15 @@ void ModbusController::dump_config() {
void ModbusController::loop() { void ModbusController::loop() {
// Incoming data to process? // Incoming data to process?
if (!incoming_queue_.empty()) { if (!this->incoming_queue_.empty()) {
auto &message = incoming_queue_.front(); auto &message = this->incoming_queue_.front();
if (message != nullptr) if (message != nullptr)
process_modbus_data_(message.get()); this->process_modbus_data_(message.get());
incoming_queue_.pop(); this->incoming_queue_.pop();
} else { } else {
// all messages processed send pending commands // all messages processed send pending commands
send_next_command_(); this->send_next_command_();
} }
} }
@ -391,7 +391,7 @@ void ModbusController::on_write_register_response(ModbusRegisterType register_ty
void ModbusController::dump_sensors_() { void ModbusController::dump_sensors_() {
ESP_LOGV(TAG, "sensors"); ESP_LOGV(TAG, "sensors");
for (auto &it : sensorset_) { for (auto &it : this->sensorset_) {
ESP_LOGV(TAG, " Sensor start=0x%X count=%d size=%d offset=%d", it->start_address, it->register_count, ESP_LOGV(TAG, " Sensor start=0x%X count=%d size=%d offset=%d", it->start_address, it->register_count,
it->get_register_size(), it->offset); it->get_register_size(), it->offset);
} }

View File

@ -240,14 +240,14 @@ class SensorItem {
} }
// Override register size for modbus devices not using 1 register for one dword // Override register size for modbus devices not using 1 register for one dword
void set_register_size(uint8_t register_size) { response_bytes = register_size; } void set_register_size(uint8_t register_size) { response_bytes = register_size; }
ModbusRegisterType register_type; ModbusRegisterType register_type{ModbusRegisterType::CUSTOM};
SensorValueType sensor_value_type; SensorValueType sensor_value_type{SensorValueType::RAW};
uint16_t start_address; uint16_t start_address{0};
uint32_t bitmask; uint32_t bitmask{0};
uint8_t offset; uint8_t offset{0};
uint8_t register_count; uint8_t register_count{0};
uint8_t response_bytes{0}; uint8_t response_bytes{0};
uint16_t skip_updates; uint16_t skip_updates{0};
std::vector<uint8_t> custom_data{}; std::vector<uint8_t> custom_data{};
bool force_new_range{false}; bool force_new_range{false};
}; };
@ -261,9 +261,9 @@ class ServerRegister {
this->register_count = register_count; this->register_count = register_count;
this->read_lambda = std::move(read_lambda); this->read_lambda = std::move(read_lambda);
} }
uint16_t address; uint16_t address{0};
SensorValueType value_type; SensorValueType value_type{SensorValueType::RAW};
uint8_t register_count; uint8_t register_count{0};
std::function<float()> read_lambda; std::function<float()> read_lambda;
}; };
@ -312,11 +312,11 @@ struct RegisterRange {
class ModbusCommandItem { class ModbusCommandItem {
public: public:
static const size_t MAX_PAYLOAD_BYTES = 240; static const size_t MAX_PAYLOAD_BYTES = 240;
ModbusController *modbusdevice; ModbusController *modbusdevice{nullptr};
uint16_t register_address; uint16_t register_address{0};
uint16_t register_count; uint16_t register_count{0};
ModbusFunctionCode function_code; ModbusFunctionCode function_code{ModbusFunctionCode::CUSTOM};
ModbusRegisterType register_type; ModbusRegisterType register_type{ModbusRegisterType::CUSTOM};
std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)> std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
on_data_func; on_data_func;
std::vector<uint8_t> payload = {}; std::vector<uint8_t> payload = {};
@ -493,23 +493,23 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice {
/// Collection of all sensors for this component /// Collection of all sensors for this component
SensorSet sensorset_; SensorSet sensorset_;
/// Collection of all server registers for this component /// Collection of all server registers for this component
std::vector<ServerRegister *> server_registers_; std::vector<ServerRegister *> server_registers_{};
/// Continuous range of modbus registers /// Continuous range of modbus registers
std::vector<RegisterRange> register_ranges_; std::vector<RegisterRange> register_ranges_{};
/// Hold the pending requests to be sent /// Hold the pending requests to be sent
std::list<std::unique_ptr<ModbusCommandItem>> command_queue_; std::list<std::unique_ptr<ModbusCommandItem>> command_queue_;
/// modbus response data waiting to get processed /// modbus response data waiting to get processed
std::queue<std::unique_ptr<ModbusCommandItem>> incoming_queue_; std::queue<std::unique_ptr<ModbusCommandItem>> incoming_queue_;
/// if duplicate commands can be sent /// if duplicate commands can be sent
bool allow_duplicate_commands_; bool allow_duplicate_commands_{false};
/// when was the last send operation /// when was the last send operation
uint32_t last_command_timestamp_; uint32_t last_command_timestamp_{0};
/// min time in ms between sending modbus commands /// min time in ms between sending modbus commands
uint16_t command_throttle_; uint16_t command_throttle_{0};
/// if module didn't respond the last command /// if module didn't respond the last command
bool module_offline_; bool module_offline_{false};
/// how many updates to skip if module is offline /// how many updates to skip if module is offline
uint16_t offline_skip_updates_; uint16_t offline_skip_updates_{0};
/// How many times we will retry a command if we get no response /// How many times we will retry a command if we get no response
uint8_t max_cmd_retries_{4}; uint8_t max_cmd_retries_{4};
/// Command sent callback /// Command sent callback

View File

@ -8,7 +8,7 @@ namespace modbus_controller {
static const char *const TAG = "modbus.number"; static const char *const TAG = "modbus.number";
void ModbusNumber::parse_and_publish(const std::vector<uint8_t> &data) { void ModbusNumber::parse_and_publish(const std::vector<uint8_t> &data) {
float result = payload_to_float(data, *this) / multiply_by_; float result = payload_to_float(data, *this) / this->multiply_by_;
// Is there a lambda registered // Is there a lambda registered
// call it with the pre converted value and the raw data array // call it with the pre converted value and the raw data array
@ -43,7 +43,7 @@ void ModbusNumber::control(float value) {
return; return;
} }
} else { } else {
write_value = multiply_by_ * write_value; write_value = this->multiply_by_ * write_value;
} }
if (!data.empty()) { if (!data.empty()) {
@ -63,21 +63,21 @@ void ModbusNumber::control(float value) {
// Create and send the write command // Create and send the write command
if (this->register_count == 1 && !this->use_write_multiple_) { if (this->register_count == 1 && !this->use_write_multiple_) {
// since offset is in bytes and a register is 16 bits we get the start by adding offset/2 // since offset is in bytes and a register is 16 bits we get the start by adding offset/2
write_cmd = write_cmd = ModbusCommandItem::create_write_single_command(this->parent_, this->start_address + this->offset / 2,
ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset / 2, data[0]); data[0]);
} else { } else {
write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset / 2, write_cmd = ModbusCommandItem::create_write_multiple_command(
this->register_count, data); this->parent_, this->start_address + this->offset / 2, this->register_count, data);
} }
// publish new value // publish new value
write_cmd.on_data_func = [this, write_cmd, value](ModbusRegisterType register_type, uint16_t start_address, write_cmd.on_data_func = [this, write_cmd, value](ModbusRegisterType register_type, uint16_t start_address,
const std::vector<uint8_t> &data) { const std::vector<uint8_t> &data) {
// gets called when the write command is ack'd from the device // gets called when the write command is ack'd from the device
parent_->on_write_register_response(write_cmd.register_type, start_address, data); this->parent_->on_write_register_response(write_cmd.register_type, start_address, data);
this->publish_state(value); this->publish_state(value);
}; };
} }
parent_->queue_command(write_cmd); this->parent_->queue_command(write_cmd);
this->publish_state(value); this->publish_state(value);
} }
void ModbusNumber::dump_config() { LOG_NUMBER(TAG, "Modbus Number", this); } void ModbusNumber::dump_config() { LOG_NUMBER(TAG, "Modbus Number", this); }

View File

@ -29,7 +29,7 @@ class ModbusNumber : public number::Number, public Component, public SensorItem
void parse_and_publish(const std::vector<uint8_t> &data) override; void parse_and_publish(const std::vector<uint8_t> &data) override;
float get_setup_priority() const override { return setup_priority::HARDWARE; } float get_setup_priority() const override { return setup_priority::HARDWARE; }
void set_parent(ModbusController *parent) { this->parent_ = parent; } void set_parent(ModbusController *parent) { this->parent_ = parent; }
void set_write_multiply(float factor) { multiply_by_ = factor; } void set_write_multiply(float factor) { this->multiply_by_ = factor; }
using transform_func_t = std::function<optional<float>(ModbusNumber *, float, const std::vector<uint8_t> &)>; using transform_func_t = std::function<optional<float>(ModbusNumber *, float, const std::vector<uint8_t> &)>;
using write_transform_func_t = std::function<optional<float>(ModbusNumber *, float, std::vector<uint16_t> &)>; using write_transform_func_t = std::function<optional<float>(ModbusNumber *, float, std::vector<uint16_t> &)>;
@ -39,9 +39,9 @@ class ModbusNumber : public number::Number, public Component, public SensorItem
protected: protected:
void control(float value) override; void control(float value) override;
optional<transform_func_t> transform_func_; optional<transform_func_t> transform_func_{nullopt};
optional<write_transform_func_t> write_transform_func_; optional<write_transform_func_t> write_transform_func_{nullopt};
ModbusController *parent_; ModbusController *parent_{nullptr};
float multiply_by_{1.0}; float multiply_by_{1.0};
bool use_write_multiple_{false}; bool use_write_multiple_{false};
}; };

View File

@ -27,7 +27,7 @@ void ModbusFloatOutput::write_state(float value) {
return; return;
} }
} else { } else {
value = multiply_by_ * value; value = this->multiply_by_ * value;
} }
// lambda didn't set payload // lambda didn't set payload
if (data.empty()) { if (data.empty()) {
@ -40,12 +40,13 @@ void ModbusFloatOutput::write_state(float value) {
// Create and send the write command // Create and send the write command
ModbusCommandItem write_cmd; ModbusCommandItem write_cmd;
if (this->register_count == 1 && !this->use_write_multiple_) { if (this->register_count == 1 && !this->use_write_multiple_) {
write_cmd = ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset, data[0]); write_cmd =
ModbusCommandItem::create_write_single_command(this->parent_, this->start_address + this->offset, data[0]);
} else { } else {
write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset, write_cmd = ModbusCommandItem::create_write_multiple_command(this->parent_, this->start_address + this->offset,
this->register_count, data); this->register_count, data);
} }
parent_->queue_command(write_cmd); this->parent_->queue_command(write_cmd);
} }
void ModbusFloatOutput::dump_config() { void ModbusFloatOutput::dump_config() {
@ -90,9 +91,9 @@ void ModbusBinaryOutput::write_state(bool state) {
// offset for coil and discrete inputs is the coil/register number not bytes // offset for coil and discrete inputs is the coil/register number not bytes
if (this->use_write_multiple_) { if (this->use_write_multiple_) {
std::vector<bool> states{state}; std::vector<bool> states{state};
cmd = ModbusCommandItem::create_write_multiple_coils(parent_, this->start_address + this->offset, states); cmd = ModbusCommandItem::create_write_multiple_coils(this->parent_, this->start_address + this->offset, states);
} else { } else {
cmd = ModbusCommandItem::create_write_single_coil(parent_, this->start_address + this->offset, state); cmd = ModbusCommandItem::create_write_single_coil(this->parent_, this->start_address + this->offset, state);
} }
} }
this->parent_->queue_command(cmd); this->parent_->queue_command(cmd);

View File

@ -25,7 +25,7 @@ class ModbusFloatOutput : public output::FloatOutput, public Component, public S
void dump_config() override; void dump_config() override;
void set_parent(ModbusController *parent) { this->parent_ = parent; } void set_parent(ModbusController *parent) { this->parent_ = parent; }
void set_write_multiply(float factor) { multiply_by_ = factor; } void set_write_multiply(float factor) { this->multiply_by_ = factor; }
// Do nothing // Do nothing
void parse_and_publish(const std::vector<uint8_t> &data) override{}; void parse_and_publish(const std::vector<uint8_t> &data) override{};
@ -37,9 +37,9 @@ class ModbusFloatOutput : public output::FloatOutput, public Component, public S
void write_state(float value) override; void write_state(float value) override;
optional<write_transform_func_t> write_transform_func_{nullopt}; optional<write_transform_func_t> write_transform_func_{nullopt};
ModbusController *parent_; ModbusController *parent_{nullptr};
float multiply_by_{1.0}; float multiply_by_{1.0};
bool use_write_multiple_; bool use_write_multiple_{false};
}; };
class ModbusBinaryOutput : public output::BinaryOutput, public Component, public SensorItem { class ModbusBinaryOutput : public output::BinaryOutput, public Component, public SensorItem {
@ -68,8 +68,8 @@ class ModbusBinaryOutput : public output::BinaryOutput, public Component, public
void write_state(bool state) override; void write_state(bool state) override;
optional<write_transform_func_t> write_transform_func_{nullopt}; optional<write_transform_func_t> write_transform_func_{nullopt};
ModbusController *parent_; ModbusController *parent_{nullptr};
bool use_write_multiple_; bool use_write_multiple_{false};
}; };
} // namespace modbus_controller } // namespace modbus_controller

View File

@ -74,12 +74,13 @@ void ModbusSelect::control(const std::string &value) {
const uint16_t write_address = this->start_address + this->offset / 2; const uint16_t write_address = this->start_address + this->offset / 2;
ModbusCommandItem write_cmd; ModbusCommandItem write_cmd;
if ((this->register_count == 1) && (!this->use_write_multiple_)) { if ((this->register_count == 1) && (!this->use_write_multiple_)) {
write_cmd = ModbusCommandItem::create_write_single_command(parent_, write_address, data[0]); write_cmd = ModbusCommandItem::create_write_single_command(this->parent_, write_address, data[0]);
} else { } else {
write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, write_address, this->register_count, data); write_cmd =
ModbusCommandItem::create_write_multiple_command(this->parent_, write_address, this->register_count, data);
} }
parent_->queue_command(write_cmd); this->parent_->queue_command(write_cmd);
if (this->optimistic_) if (this->optimistic_)
this->publish_state(value); this->publish_state(value);

View File

@ -42,12 +42,12 @@ class ModbusSelect : public Component, public select::Select, public SensorItem
void control(const std::string &value) override; void control(const std::string &value) override;
protected: protected:
std::vector<int64_t> mapping_; std::vector<int64_t> mapping_{};
ModbusController *parent_; ModbusController *parent_{nullptr};
bool use_write_multiple_{false}; bool use_write_multiple_{false};
bool optimistic_{false}; bool optimistic_{false};
optional<transform_func_t> transform_func_; optional<transform_func_t> transform_func_{nullopt};
optional<write_transform_func_t> write_transform_func_; optional<write_transform_func_t> write_transform_func_{nullopt};
}; };
} // namespace modbus_controller } // namespace modbus_controller

View File

@ -80,24 +80,24 @@ void ModbusSwitch::write_state(bool state) {
// offset for coil and discrete inputs is the coil/register number not bytes // offset for coil and discrete inputs is the coil/register number not bytes
if (this->use_write_multiple_) { if (this->use_write_multiple_) {
std::vector<bool> states{state}; std::vector<bool> states{state};
cmd = ModbusCommandItem::create_write_multiple_coils(parent_, this->start_address + this->offset, states); cmd = ModbusCommandItem::create_write_multiple_coils(this->parent_, this->start_address + this->offset, states);
} else { } else {
cmd = ModbusCommandItem::create_write_single_coil(parent_, this->start_address + this->offset, state); cmd = ModbusCommandItem::create_write_single_coil(this->parent_, this->start_address + this->offset, state);
} }
} else { } else {
// since offset is in bytes and a register is 16 bits we get the start by adding offset/2 // since offset is in bytes and a register is 16 bits we get the start by adding offset/2
if (this->use_write_multiple_) { if (this->use_write_multiple_) {
std::vector<uint16_t> bool_states(1, state ? (0xFFFF & this->bitmask) : 0); std::vector<uint16_t> bool_states(1, state ? (0xFFFF & this->bitmask) : 0);
cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset / 2, 1, cmd = ModbusCommandItem::create_write_multiple_command(this->parent_, this->start_address + this->offset / 2, 1,
bool_states); bool_states);
} else { } else {
cmd = ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset / 2, cmd = ModbusCommandItem::create_write_single_command(this->parent_, this->start_address + this->offset / 2,
state ? 0xFFFF & this->bitmask : 0u); state ? 0xFFFF & this->bitmask : 0u);
} }
} }
} }
this->parent_->queue_command(cmd); this->parent_->queue_command(cmd);
publish_state(state); this->publish_state(state);
} }
// ModbusSwitch end // ModbusSwitch end
} // namespace modbus_controller } // namespace modbus_controller

View File

@ -40,8 +40,8 @@ class ModbusSwitch : public Component, public switch_::Switch, public SensorItem
void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; } void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; }
protected: protected:
ModbusController *parent_; ModbusController *parent_{nullptr};
bool use_write_multiple_; bool use_write_multiple_{false};
optional<transform_func_t> publish_transform_func_{nullopt}; optional<transform_func_t> publish_transform_func_{nullopt};
optional<write_transform_func_t> write_transform_func_{nullopt}; optional<write_transform_func_t> write_transform_func_{nullopt};
}; };

View File

@ -49,6 +49,7 @@ from esphome.const import (
CONF_USE_ABBREVIATIONS, CONF_USE_ABBREVIATIONS,
CONF_USERNAME, CONF_USERNAME,
CONF_WILL_MESSAGE, CONF_WILL_MESSAGE,
CONF_PUBLISH_NAN_AS_NONE,
PLATFORM_BK72XX, PLATFORM_BK72XX,
PLATFORM_ESP32, PLATFORM_ESP32,
PLATFORM_ESP8266, PLATFORM_ESP8266,
@ -296,6 +297,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, cv.Optional(CONF_QOS, default=0): cv.mqtt_qos,
} }
), ),
cv.Optional(CONF_PUBLISH_NAN_AS_NONE, default=False): cv.boolean,
} }
), ),
validate_config, validate_config,
@ -449,6 +451,8 @@ async def to_code(config):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
cg.add(var.set_publish_nan_as_none(config[CONF_PUBLISH_NAN_AS_NONE]))
MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema( MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema(
{ {

View File

@ -80,8 +80,7 @@ const EntityBase *MQTTAlarmControlPanelComponent::get_entity() const { return th
bool MQTTAlarmControlPanelComponent::send_initial_state() { return this->publish_state(); } bool MQTTAlarmControlPanelComponent::send_initial_state() { return this->publish_state(); }
bool MQTTAlarmControlPanelComponent::publish_state() { bool MQTTAlarmControlPanelComponent::publish_state() {
bool success = true; const char *state_s;
const char *state_s = "";
switch (this->alarm_control_panel_->get_state()) { switch (this->alarm_control_panel_->get_state()) {
case ACP_STATE_DISARMED: case ACP_STATE_DISARMED:
state_s = "disarmed"; state_s = "disarmed";
@ -116,9 +115,7 @@ bool MQTTAlarmControlPanelComponent::publish_state() {
default: default:
state_s = "unknown"; state_s = "unknown";
} }
if (!this->publish(this->get_state_topic_(), state_s)) return this->publish(this->get_state_topic_(), state_s);
success = false;
return success;
} }
} // namespace mqtt } // namespace mqtt

View File

@ -151,11 +151,11 @@ void MQTTBackendESP32::mqtt_event_handler_(const Event &event) {
break; break;
case MQTT_EVENT_DATA: { case MQTT_EVENT_DATA: {
static std::string topic; static std::string topic;
if (event.topic.length() > 0) { if (!event.topic.empty()) {
topic = event.topic; topic = event.topic;
} }
ESP_LOGV(TAG, "MQTT_EVENT_DATA %s", topic.c_str()); ESP_LOGV(TAG, "MQTT_EVENT_DATA %s", topic.c_str());
this->on_message_.call(event.topic.length() > 0 ? topic.c_str() : nullptr, event.data.data(), event.data.size(), this->on_message_.call(!event.topic.empty() ? topic.c_str() : nullptr, event.data.data(), event.data.size(),
event.current_data_offset, event.total_data_len); event.current_data_offset, event.total_data_len);
} break; } break;
case MQTT_EVENT_ERROR: case MQTT_EVENT_ERROR:
@ -184,7 +184,7 @@ void MQTTBackendESP32::mqtt_event_handler(void *handler_args, esp_event_base_t b
// queue event to decouple processing // queue event to decouple processing
if (instance) { if (instance) {
auto event = *static_cast<esp_mqtt_event_t *>(event_data); auto event = *static_cast<esp_mqtt_event_t *>(event_data);
instance->mqtt_events_.push(Event(event)); instance->mqtt_events_.emplace(event);
} }
} }

View File

@ -608,6 +608,10 @@ void MQTTClientComponent::set_log_message_template(MQTTMessage &&message) { this
const MQTTDiscoveryInfo &MQTTClientComponent::get_discovery_info() const { return this->discovery_info_; } const MQTTDiscoveryInfo &MQTTClientComponent::get_discovery_info() const { return this->discovery_info_; }
void MQTTClientComponent::set_topic_prefix(const std::string &topic_prefix) { this->topic_prefix_ = topic_prefix; } void MQTTClientComponent::set_topic_prefix(const std::string &topic_prefix) { this->topic_prefix_ = topic_prefix; }
const std::string &MQTTClientComponent::get_topic_prefix() const { return this->topic_prefix_; } const std::string &MQTTClientComponent::get_topic_prefix() const { return this->topic_prefix_; }
void MQTTClientComponent::set_publish_nan_as_none(bool publish_nan_as_none) {
this->publish_nan_as_none_ = publish_nan_as_none;
}
bool MQTTClientComponent::is_publish_nan_as_none() const { return this->publish_nan_as_none_; }
void MQTTClientComponent::disable_birth_message() { void MQTTClientComponent::disable_birth_message() {
this->birth_message_.topic = ""; this->birth_message_.topic = "";
this->recalculate_availability_(); this->recalculate_availability_();

View File

@ -263,6 +263,10 @@ class MQTTClientComponent : public Component {
void set_on_connect(mqtt_on_connect_callback_t &&callback); void set_on_connect(mqtt_on_connect_callback_t &&callback);
void set_on_disconnect(mqtt_on_disconnect_callback_t &&callback); void set_on_disconnect(mqtt_on_disconnect_callback_t &&callback);
// Publish None state instead of NaN for Home Assistant
void set_publish_nan_as_none(bool publish_nan_as_none);
bool is_publish_nan_as_none() const;
protected: protected:
void send_device_info_(); void send_device_info_();
@ -328,6 +332,8 @@ class MQTTClientComponent : public Component {
uint32_t connect_begin_; uint32_t connect_begin_;
uint32_t last_connected_{0}; uint32_t last_connected_{0};
optional<MQTTClientDisconnectReason> disconnect_reason_{}; optional<MQTTClientDisconnectReason> disconnect_reason_{};
bool publish_nan_as_none_{false};
}; };
extern MQTTClientComponent *global_mqtt_client; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) extern MQTTClientComponent *global_mqtt_client; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View File

@ -257,7 +257,7 @@ const EntityBase *MQTTClimateComponent::get_entity() const { return this->device
bool MQTTClimateComponent::publish_state_() { bool MQTTClimateComponent::publish_state_() {
auto traits = this->device_->get_traits(); auto traits = this->device_->get_traits();
// mode // mode
const char *mode_s = ""; const char *mode_s;
switch (this->device_->mode) { switch (this->device_->mode) {
case CLIMATE_MODE_OFF: case CLIMATE_MODE_OFF:
mode_s = "off"; mode_s = "off";
@ -280,6 +280,8 @@ bool MQTTClimateComponent::publish_state_() {
case CLIMATE_MODE_HEAT_COOL: case CLIMATE_MODE_HEAT_COOL:
mode_s = "heat_cool"; mode_s = "heat_cool";
break; break;
default:
mode_s = "unknown";
} }
bool success = true; bool success = true;
if (!this->publish(this->get_mode_state_topic(), mode_s)) if (!this->publish(this->get_mode_state_topic(), mode_s))
@ -343,6 +345,8 @@ bool MQTTClimateComponent::publish_state_() {
case CLIMATE_PRESET_ACTIVITY: case CLIMATE_PRESET_ACTIVITY:
payload = "activity"; payload = "activity";
break; break;
default:
payload = "unknown";
} }
} }
if (this->device_->custom_preset.has_value()) if (this->device_->custom_preset.has_value())
@ -352,7 +356,7 @@ bool MQTTClimateComponent::publish_state_() {
} }
if (traits.get_supports_action()) { if (traits.get_supports_action()) {
const char *payload = "unknown"; const char *payload;
switch (this->device_->action) { switch (this->device_->action) {
case CLIMATE_ACTION_OFF: case CLIMATE_ACTION_OFF:
payload = "off"; payload = "off";
@ -372,6 +376,8 @@ bool MQTTClimateComponent::publish_state_() {
case CLIMATE_ACTION_FAN: case CLIMATE_ACTION_FAN:
payload = "fan"; payload = "fan";
break; break;
default:
payload = "unknown";
} }
if (!this->publish(this->get_action_state_topic(), payload)) if (!this->publish(this->get_action_state_topic(), payload))
success = false; success = false;
@ -411,6 +417,8 @@ bool MQTTClimateComponent::publish_state_() {
case CLIMATE_FAN_QUIET: case CLIMATE_FAN_QUIET:
payload = "quiet"; payload = "quiet";
break; break;
default:
payload = "unknown";
} }
} }
if (this->device_->custom_fan_mode.has_value()) if (this->device_->custom_fan_mode.has_value())
@ -420,7 +428,7 @@ bool MQTTClimateComponent::publish_state_() {
} }
if (traits.get_supports_swing_modes()) { if (traits.get_supports_swing_modes()) {
const char *payload = ""; const char *payload;
switch (this->device_->swing_mode) { switch (this->device_->swing_mode) {
case CLIMATE_SWING_OFF: case CLIMATE_SWING_OFF:
payload = "off"; payload = "off";
@ -434,6 +442,8 @@ bool MQTTClimateComponent::publish_state_() {
case CLIMATE_SWING_HORIZONTAL: case CLIMATE_SWING_HORIZONTAL:
payload = "horizontal"; payload = "horizontal";
break; break;
default:
payload = "unknown";
} }
if (!this->publish(this->get_swing_mode_state_topic(), payload)) if (!this->publish(this->get_swing_mode_state_topic(), payload))
success = false; success = false;

View File

@ -69,6 +69,8 @@ bool MQTTSensorComponent::send_initial_state() {
} }
} }
bool MQTTSensorComponent::publish_state(float value) { bool MQTTSensorComponent::publish_state(float value) {
if (mqtt::global_mqtt_client->is_publish_nan_as_none() && std::isnan(value))
return this->publish(this->get_state_topic_(), "None");
int8_t accuracy = this->sensor_->get_accuracy_decimals(); int8_t accuracy = this->sensor_->get_accuracy_decimals();
return this->publish(this->get_state_topic_(), value_accuracy_to_string(value, accuracy)); return this->publish(this->get_state_topic_(), value_accuracy_to_string(value, accuracy));
} }

View File

@ -116,7 +116,7 @@ struct IPAddress {
operator arduino_ns::IPAddress() const { return ip_addr_get_ip4_u32(&ip_addr_); } operator arduino_ns::IPAddress() const { return ip_addr_get_ip4_u32(&ip_addr_); }
#endif #endif
bool is_set() { return !ip_addr_isany(&ip_addr_); } bool is_set() { return !ip_addr_isany(&ip_addr_); } // NOLINT(readability-simplify-boolean-expr)
bool is_ip4() { return IP_IS_V4(&ip_addr_); } bool is_ip4() { return IP_IS_V4(&ip_addr_); }
bool is_ip6() { return IP_IS_V6(&ip_addr_); } bool is_ip6() { return IP_IS_V6(&ip_addr_); }
std::string str() const { return str_lower_case(ipaddr_ntoa(&ip_addr_)); } std::string str() const { return str_lower_case(ipaddr_ntoa(&ip_addr_)); }

View File

@ -343,7 +343,7 @@ void Nextion::process_serial_() {
} }
// nextion.tech/instruction-set/ // nextion.tech/instruction-set/
void Nextion::process_nextion_commands_() { void Nextion::process_nextion_commands_() {
if (this->command_data_.length() == 0) { if (this->command_data_.empty()) {
return; return;
} }
@ -563,13 +563,10 @@ void Nextion::process_nextion_commands_() {
break; break;
} }
int dataindex = 0;
int value = 0; int value = 0;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
value += to_process[i] << (8 * i); value += to_process[i] << (8 * i);
++dataindex;
} }
NextionQueue *nb = this->nextion_queue_.front(); NextionQueue *nb = this->nextion_queue_.front();

View File

@ -30,13 +30,13 @@ std::vector<uint8_t> NdefRecord::encode(bool first, bool last) {
data.push_back(payload_length & 0xFF); data.push_back(payload_length & 0xFF);
} }
if (this->id_.length()) { if (!this->id_.empty()) {
data.push_back(this->id_.length()); data.push_back(this->id_.length());
} }
data.insert(data.end(), this->type_.begin(), this->type_.end()); data.insert(data.end(), this->type_.begin(), this->type_.end());
if (this->id_.length()) { if (!this->id_.empty()) {
data.insert(data.end(), this->id_.begin(), this->id_.end()); data.insert(data.end(), this->id_.begin(), this->id_.end());
} }
@ -55,7 +55,7 @@ uint8_t NdefRecord::create_flag_byte(bool first, bool last, size_t payload_size)
if (payload_size <= 255) { if (payload_size <= 255) {
value = value | 0x10; // Set SR bit value = value | 0x10; // Set SR bit
} }
if (this->id_.length()) { if (!this->id_.empty()) {
value = value | 0x08; // Set IL bit value = value | 0x08; // Set IL bit
} }
return value; return value;

View File

@ -49,6 +49,10 @@ void PngDecoder::prepare(uint32_t download_size) {
} }
int HOT PngDecoder::decode(uint8_t *buffer, size_t size) { int HOT PngDecoder::decode(uint8_t *buffer, size_t size) {
if (!this->pngle_) {
ESP_LOGE(TAG, "PNG decoder engine not initialized!");
return -1;
}
if (size < 256 && size < this->download_size_ - this->decoded_bytes_) { if (size < 256 && size < this->download_size_ - this->decoded_bytes_) {
ESP_LOGD(TAG, "Waiting for data"); ESP_LOGD(TAG, "Waiting for data");
return 0; return 0;

View File

@ -138,7 +138,7 @@ OpenthermHub::OpenthermHub() : Component(), in_pin_{}, out_pin_{} {}
void OpenthermHub::process_response(OpenthermData &data) { void OpenthermHub::process_response(OpenthermData &data) {
ESP_LOGD(TAG, "Received OpenTherm response with id %d (%s)", data.id, ESP_LOGD(TAG, "Received OpenTherm response with id %d (%s)", data.id,
this->opentherm_->message_id_to_str((MessageId) data.id)); this->opentherm_->message_id_to_str((MessageId) data.id));
ESP_LOGD(TAG, "%s", this->opentherm_->debug_data(data).c_str()); this->opentherm_->debug_data(data);
switch (data.id) { switch (data.id) {
OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_RESPONSE_MESSAGE, OPENTHERM_MESSAGE_RESPONSE_ENTITY, , OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_RESPONSE_MESSAGE, OPENTHERM_MESSAGE_RESPONSE_ENTITY, ,
@ -315,7 +315,7 @@ void OpenthermHub::start_conversation_() {
ESP_LOGD(TAG, "Sending request with id %d (%s)", request.id, ESP_LOGD(TAG, "Sending request with id %d (%s)", request.id,
this->opentherm_->message_id_to_str((MessageId) request.id)); this->opentherm_->message_id_to_str((MessageId) request.id));
ESP_LOGD(TAG, "%s", this->opentherm_->debug_data(request).c_str()); this->opentherm_->debug_data(request);
// Send the request // Send the request
this->last_conversation_start_ = millis(); this->last_conversation_start_ = millis();
this->opentherm_->send(request); this->opentherm_->send(request);
@ -340,19 +340,18 @@ void OpenthermHub::stop_opentherm_() {
this->opentherm_->stop(); this->opentherm_->stop();
this->last_conversation_end_ = millis(); this->last_conversation_end_ = millis();
} }
void OpenthermHub::handle_protocol_write_error_() { void OpenthermHub::handle_protocol_write_error_() {
ESP_LOGW(TAG, "Error while sending request: %s", ESP_LOGW(TAG, "Error while sending request: %s",
this->opentherm_->operation_mode_to_str(this->opentherm_->get_mode())); this->opentherm_->operation_mode_to_str(this->opentherm_->get_mode()));
ESP_LOGW(TAG, "%s", this->opentherm_->debug_data(this->last_request_).c_str()); this->opentherm_->debug_data(this->last_request_);
} }
void OpenthermHub::handle_protocol_read_error_() { void OpenthermHub::handle_protocol_read_error_() {
OpenThermError error; OpenThermError error;
this->opentherm_->get_protocol_error(error); this->opentherm_->get_protocol_error(error);
ESP_LOGW(TAG, "Protocol error occured while receiving response: %s", this->opentherm_->debug_error(error).c_str()); ESP_LOGW(TAG, "Protocol error occured while receiving response: %s",
this->opentherm_->protocol_error_to_to_str(error.error_type));
this->opentherm_->debug_error(error);
} }
void OpenthermHub::handle_timeout_error_() { void OpenthermHub::handle_timeout_error_() {
ESP_LOGW(TAG, "Receive response timed out at a protocol level"); ESP_LOGW(TAG, "Receive response timed out at a protocol level");
this->stop_opentherm_(); this->stop_opentherm_();

View File

@ -15,15 +15,11 @@
#include "Arduino.h" #include "Arduino.h"
#endif #endif
#include <string> #include <string>
#include <sstream>
#include <bitset>
namespace esphome { namespace esphome {
namespace opentherm { namespace opentherm {
using std::string; using std::string;
using std::bitset;
using std::stringstream;
using std::to_string; using std::to_string;
static const char *const TAG = "opentherm"; static const char *const TAG = "opentherm";
@ -224,7 +220,7 @@ void IRAM_ATTR OpenTherm::bit_read_(uint8_t value) {
this->bit_pos_++; this->bit_pos_++;
} }
ProtocolErrorType OpenTherm::verify_stop_bit_(uint8_t value) { ProtocolErrorType IRAM_ATTR OpenTherm::verify_stop_bit_(uint8_t value) {
if (value) { // stop bit detected if (value) { // stop bit detected
return check_parity_(this->data_) ? ProtocolErrorType::NO_ERROR : ProtocolErrorType::PARITY_ERROR; return check_parity_(this->data_) ? ProtocolErrorType::NO_ERROR : ProtocolErrorType::PARITY_ERROR;
} else { // no stop bit detected, error } else { // no stop bit detected, error
@ -369,7 +365,7 @@ void IRAM_ATTR OpenTherm::stop_timer_() {
#ifdef ESP8266 #ifdef ESP8266
// 5 kHz timer_ // 5 kHz timer_
void OpenTherm::start_read_timer_() { void IRAM_ATTR OpenTherm::start_read_timer_() {
InterruptLock const lock; InterruptLock const lock;
timer1_attachInterrupt(OpenTherm::esp8266_timer_isr); timer1_attachInterrupt(OpenTherm::esp8266_timer_isr);
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max) timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max)
@ -377,14 +373,14 @@ void OpenTherm::start_read_timer_() {
} }
// 2 kHz timer_ // 2 kHz timer_
void OpenTherm::start_write_timer_() { void IRAM_ATTR OpenTherm::start_write_timer_() {
InterruptLock const lock; InterruptLock const lock;
timer1_attachInterrupt(OpenTherm::esp8266_timer_isr); timer1_attachInterrupt(OpenTherm::esp8266_timer_isr);
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max) timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max)
timer1_write(2500); // 2kHz timer1_write(2500); // 2kHz
} }
void OpenTherm::stop_timer_() { void IRAM_ATTR OpenTherm::stop_timer_() {
InterruptLock const lock; InterruptLock const lock;
timer1_disable(); timer1_disable();
timer1_detachInterrupt(); timer1_detachInterrupt();
@ -393,7 +389,7 @@ void OpenTherm::stop_timer_() {
#endif // END ESP8266 #endif // END ESP8266
// https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd // https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd
bool OpenTherm::check_parity_(uint32_t val) { bool IRAM_ATTR OpenTherm::check_parity_(uint32_t val) {
val ^= val >> 16; val ^= val >> 16;
val ^= val >> 8; val ^= val >> 8;
val ^= val >> 4; val ^= val >> 4;
@ -545,29 +541,17 @@ const char *OpenTherm::message_id_to_str(MessageId id) {
} }
} }
string OpenTherm::debug_data(OpenthermData &data) { void OpenTherm::debug_data(OpenthermData &data) {
stringstream result; ESP_LOGD(TAG, "%s %s %s %s", format_bin(data.type).c_str(), format_bin(data.id).c_str(),
result << bitset<8>(data.type) << " " << bitset<8>(data.id) << " " << bitset<8>(data.valueHB) << " " format_bin(data.valueHB).c_str(), format_bin(data.valueLB).c_str());
<< bitset<8>(data.valueLB) << "\n"; ESP_LOGD(TAG, "type: %s; id: %s; HB: %s; LB: %s; uint_16: %s; float: %s",
result << "type: " << this->message_type_to_str((MessageType) data.type) << "; "; this->message_type_to_str((MessageType) data.type), to_string(data.id).c_str(),
result << "id: " << to_string(data.id) << "; "; to_string(data.valueHB).c_str(), to_string(data.valueLB).c_str(), to_string(data.u16()).c_str(),
result << "HB: " << to_string(data.valueHB) << "; "; to_string(data.f88()).c_str());
result << "LB: " << to_string(data.valueLB) << "; ";
result << "uint_16: " << to_string(data.u16()) << "; ";
result << "float: " << to_string(data.f88());
return result.str();
} }
std::string OpenTherm::debug_error(OpenThermError &error) { void OpenTherm::debug_error(OpenThermError &error) const {
stringstream result; ESP_LOGD(TAG, "data: %s; clock: %s; capture: %s; bit_pos: %s", format_hex(error.data).c_str(),
result << "type: " << this->protocol_error_to_to_str(error.error_type) << "; "; to_string(clock_).c_str(), format_bin(error.capture).c_str(), to_string(error.bit_pos).c_str());
result << "data: ";
result << format_hex(error.data);
result << "; clock: " << to_string(clock_);
result << "; capture: " << bitset<32>(error.capture);
result << "; bit_pos: " << to_string(error.bit_pos);
return result.str();
} }
float OpenthermData::f88() { return ((float) this->s16()) / 256.0; } float OpenthermData::f88() { return ((float) this->s16()) / 256.0; }

View File

@ -8,10 +8,9 @@
#pragma once #pragma once
#include <string> #include <string>
#include <sstream>
#include <iomanip>
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#if defined(ESP32) || defined(USE_ESP_IDF) #if defined(ESP32) || defined(USE_ESP_IDF)
#include "driver/timer.h" #include "driver/timer.h"
@ -318,8 +317,8 @@ class OpenTherm {
OperationMode get_mode() { return mode_; } OperationMode get_mode() { return mode_; }
std::string debug_data(OpenthermData &data); void debug_data(OpenthermData &data);
std::string debug_error(OpenThermError &error); void debug_error(OpenThermError &error) const;
const char *protocol_error_to_to_str(ProtocolErrorType error_type); const char *protocol_error_to_to_str(ProtocolErrorType error_type);
const char *message_type_to_str(MessageType message_type); const char *message_type_to_str(MessageType message_type);
@ -371,7 +370,7 @@ class OpenTherm {
#ifdef ESP8266 #ifdef ESP8266
// ESP8266 timer can accept callback with no parameters, so we have this hack to save a static instance of OpenTherm // ESP8266 timer can accept callback with no parameters, so we have this hack to save a static instance of OpenTherm
static OpenTherm *instance; static OpenTherm *instance; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
#endif #endif
}; };

View File

@ -32,7 +32,7 @@ void FloatOutput::set_level(float state) {
} }
#endif #endif
if (!(state == 0.0f && this->zero_means_zero_)) // regardless of min_power_, 0.0 means off if (state != 0.0f || !this->zero_means_zero_) // regardless of min_power_, 0.0 means off
state = (state * (this->max_power_ - this->min_power_)) + this->min_power_; state = (state * (this->max_power_ - this->min_power_)) + this->min_power_;
if (this->is_inverted()) if (this->is_inverted())

View File

@ -34,7 +34,7 @@ void PCA6416AComponent::setup() {
} }
// Test to see if the device supports pull-up resistors // Test to see if the device supports pull-up resistors
if (this->read_register(PCAL6416A_PULL_EN0, &value, 1, true) == esphome::i2c::ERROR_OK) { if (this->read_register(PCAL6416A_PULL_EN0, &value, 1, true) == i2c::ERROR_OK) {
this->has_pullup_ = true; this->has_pullup_ = true;
} }
@ -106,7 +106,8 @@ bool PCA6416AComponent::read_register_(uint8_t reg, uint8_t *value) {
return false; return false;
} }
if ((this->last_error_ = this->read_register(reg, value, 1, true)) != esphome::i2c::ERROR_OK) { this->last_error_ = this->read_register(reg, value, 1, true);
if (this->last_error_ != i2c::ERROR_OK) {
this->status_set_warning(); this->status_set_warning();
ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_); ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_);
return false; return false;
@ -122,7 +123,8 @@ bool PCA6416AComponent::write_register_(uint8_t reg, uint8_t value) {
return false; return false;
} }
if ((this->last_error_ = this->write_register(reg, &value, 1, true)) != esphome::i2c::ERROR_OK) { this->last_error_ = this->write_register(reg, &value, 1, true);
if (this->last_error_ != i2c::ERROR_OK) {
this->status_set_warning(); this->status_set_warning();
ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_); ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_);
return false; return false;

View File

@ -95,8 +95,8 @@ bool PCA9554Component::read_inputs_() {
return false; return false;
} }
if ((this->last_error_ = this->read_register(INPUT_REG * this->reg_width_, inputs, this->reg_width_, true)) != this->last_error_ = this->read_register(INPUT_REG * this->reg_width_, inputs, this->reg_width_, true);
esphome::i2c::ERROR_OK) { if (this->last_error_ != i2c::ERROR_OK) {
this->status_set_warning(); this->status_set_warning();
ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_); ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_);
return false; return false;
@ -113,8 +113,8 @@ bool PCA9554Component::write_register_(uint8_t reg, uint16_t value) {
uint8_t outputs[2]; uint8_t outputs[2];
outputs[0] = (uint8_t) value; outputs[0] = (uint8_t) value;
outputs[1] = (uint8_t) (value >> 8); outputs[1] = (uint8_t) (value >> 8);
if ((this->last_error_ = this->write_register(reg * this->reg_width_, outputs, this->reg_width_, true)) != this->last_error_ = this->write_register(reg * this->reg_width_, outputs, this->reg_width_, true);
esphome::i2c::ERROR_OK) { if (this->last_error_ != i2c::ERROR_OK) {
this->status_set_warning(); this->status_set_warning();
ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_); ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_);
return false; return false;

View File

@ -790,7 +790,7 @@ uint8_t Pipsolar::check_incoming_crc_() {
// send next command used // send next command used
uint8_t Pipsolar::send_next_command_() { uint8_t Pipsolar::send_next_command_() {
uint16_t crc16; uint16_t crc16;
if (this->command_queue_[this->command_queue_position_].length() != 0) { if (!this->command_queue_[this->command_queue_position_].empty()) {
const char *command = this->command_queue_[this->command_queue_position_].c_str(); const char *command = this->command_queue_[this->command_queue_position_].c_str();
uint8_t byte_command[16]; uint8_t byte_command[16];
uint8_t length = this->command_queue_[this->command_queue_position_].length(); uint8_t length = this->command_queue_[this->command_queue_position_].length();
@ -846,7 +846,7 @@ void Pipsolar::queue_command_(const char *command, uint8_t length) {
uint8_t next_position = command_queue_position_; uint8_t next_position = command_queue_position_;
for (uint8_t i = 0; i < COMMAND_QUEUE_LENGTH; i++) { for (uint8_t i = 0; i < COMMAND_QUEUE_LENGTH; i++) {
uint8_t testposition = (next_position + i) % COMMAND_QUEUE_LENGTH; uint8_t testposition = (next_position + i) % COMMAND_QUEUE_LENGTH;
if (command_queue_[testposition].length() == 0) { if (command_queue_[testposition].empty()) {
command_queue_[testposition] = command; command_queue_[testposition] = command;
ESP_LOGD(TAG, "Command queued successfully: %s with length %u at position %d", command, ESP_LOGD(TAG, "Command queued successfully: %s with length %u at position %d", command,
command_queue_[testposition].length(), testposition); command_queue_[testposition].length(), testposition);

View File

@ -10,11 +10,11 @@ static const char *const TAG = "pipsolar.switch";
void PipsolarSwitch::dump_config() { LOG_SWITCH("", "Pipsolar Switch", this); } void PipsolarSwitch::dump_config() { LOG_SWITCH("", "Pipsolar Switch", this); }
void PipsolarSwitch::write_state(bool state) { void PipsolarSwitch::write_state(bool state) {
if (state) { if (state) {
if (this->on_command_.length() > 0) { if (!this->on_command_.empty()) {
this->parent_->switch_command(this->on_command_); this->parent_->switch_command(this->on_command_);
} }
} else { } else {
if (this->off_command_.length() > 0) { if (!this->off_command_.empty()) {
this->parent_->switch_command(this->off_command_); this->parent_->switch_command(this->off_command_);
} }
} }

View File

@ -80,8 +80,8 @@ bool PN532::is_mifare_ultralight_formatted_(const std::vector<uint8_t> &page_3_t
const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector
return (page_3_to_6.size() > p4_offset + 3) && return (page_3_to_6.size() > p4_offset + 3) &&
!((page_3_to_6[p4_offset + 0] == 0xFF) && (page_3_to_6[p4_offset + 1] == 0xFF) && ((page_3_to_6[p4_offset + 0] != 0xFF) || (page_3_to_6[p4_offset + 1] != 0xFF) ||
(page_3_to_6[p4_offset + 2] == 0xFF) && (page_3_to_6[p4_offset + 3] == 0xFF)); (page_3_to_6[p4_offset + 2] != 0xFF) || (page_3_to_6[p4_offset + 3] != 0xFF));
} }
uint16_t PN532::read_mifare_ultralight_capacity_() { uint16_t PN532::read_mifare_ultralight_capacity_() {

View File

@ -81,8 +81,8 @@ bool PN7150::is_mifare_ultralight_formatted_(const std::vector<uint8_t> &page_3_
const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector
return (page_3_to_6.size() > p4_offset + 3) && return (page_3_to_6.size() > p4_offset + 3) &&
!((page_3_to_6[p4_offset + 0] == 0xFF) && (page_3_to_6[p4_offset + 1] == 0xFF) && ((page_3_to_6[p4_offset + 0] != 0xFF) || (page_3_to_6[p4_offset + 1] != 0xFF) ||
(page_3_to_6[p4_offset + 2] == 0xFF) && (page_3_to_6[p4_offset + 3] == 0xFF)); (page_3_to_6[p4_offset + 2] != 0xFF) || (page_3_to_6[p4_offset + 3] != 0xFF));
} }
uint16_t PN7150::read_mifare_ultralight_capacity_() { uint16_t PN7150::read_mifare_ultralight_capacity_() {

Some files were not shown because too many files have changed in this diff Show More