mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-03 08:31:47 +00:00 
			
		
		
		
	Compare commits
	
		
			139 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					01ec414873 | ||
| 
						 | 
					150c9b5fa3 | ||
| 
						 | 
					55df88d7ae | ||
| 
						 | 
					619787e6d2 | ||
| 
						 | 
					3f8bad3ed1 | ||
| 
						 | 
					c146712b16 | ||
| 
						 | 
					2cabe59c22 | ||
| 
						 | 
					a67b92a04c | ||
| 
						 | 
					9fb8e9edef | ||
| 
						 | 
					d2bccbe8ac | ||
| 
						 | 
					e44a60e814 | ||
| 
						 | 
					02a71cb6a7 | ||
| 
						 | 
					e600784ebf | ||
| 
						 | 
					5e19a3b892 | ||
| 
						 | 
					8bf112669f | ||
| 
						 | 
					4278664208 | ||
| 
						 | 
					0789657fd5 | ||
| 
						 | 
					b566c78f00 | ||
| 
						 | 
					a35122231c | ||
| 
						 | 
					7e4ee32b54 | ||
| 
						 | 
					7df80eadcf | ||
| 
						 | 
					2aaba1d2b8 | ||
| 
						 | 
					7c129a4018 | ||
| 
						 | 
					cb66ce069e | ||
| 
						 | 
					a27e72362a | ||
| 
						 | 
					f44e5d3142 | ||
| 
						 | 
					532163738e | ||
| 
						 | 
					63fa922547 | ||
| 
						 | 
					48e4cb5ae2 | ||
| 
						 | 
					ff8a73c2d1 | ||
| 
						 | 
					afd26c6f1a | ||
| 
						 | 
					67b06a88b2 | ||
| 
						 | 
					265e019381 | ||
| 
						 | 
					560e36a65c | ||
| 
						 | 
					b05a3fbb55 | ||
| 
						 | 
					3a899e28dc | ||
| 
						 | 
					f26238e824 | ||
| 
						 | 
					3717e34bba | ||
| 
						 | 
					be6f95d43e | ||
| 
						 | 
					99a765dc06 | ||
| 
						 | 
					351e7ea16b | ||
| 
						 | 
					2fa79a2e2f | ||
| 
						 | 
					44a917929d | ||
| 
						 | 
					21ebc7f95b | ||
| 
						 | 
					72e72d7d4b | ||
| 
						 | 
					02ed2c0ebe | ||
| 
						 | 
					0f506ea8eb | ||
| 
						 | 
					8e7e8da4a3 | ||
| 
						 | 
					b56c606523 | ||
| 
						 | 
					f457269a68 | ||
| 
						 | 
					5b0b9da0b9 | ||
| 
						 | 
					0ed0bdc655 | ||
| 
						 | 
					a8fa4b56f9 | ||
| 
						 | 
					cd514b140e | ||
| 
						 | 
					f3329fdc8c | ||
| 
						 | 
					689bbf2419 | ||
| 
						 | 
					a6b89e4e8a | ||
| 
						 | 
					ffd2cb9814 | ||
| 
						 | 
					1d5f088740 | ||
| 
						 | 
					4e7011c25d | ||
| 
						 | 
					f4ac176d77 | ||
| 
						 | 
					e4cf7b86fa | ||
| 
						 | 
					9876d5276c | ||
| 
						 | 
					0b1b25191d | ||
| 
						 | 
					9980b9972f | ||
| 
						 | 
					93b7ca77ca | ||
| 
						 | 
					40697fea96 | ||
| 
						 | 
					c541fa1763 | ||
| 
						 | 
					fd08f1e23d | ||
| 
						 | 
					3a07121784 | ||
| 
						 | 
					1495fada90 | ||
| 
						 | 
					62fed4c1eb | ||
| 
						 | 
					00f9af70a9 | ||
| 
						 | 
					0ae3fcb0b7 | ||
| 
						 | 
					dfffa67c0f | ||
| 
						 | 
					f81c556b63 | ||
| 
						 | 
					ce8091c14e | ||
| 
						 | 
					581cb642ff | ||
| 
						 | 
					e02aaedc42 | ||
| 
						 | 
					8c66de2391 | ||
| 
						 | 
					cb8ca433d9 | ||
| 
						 | 
					b914d6e305 | ||
| 
						 | 
					956e19be7d | ||
| 
						 | 
					b3d5a4dfdb | ||
| 
						 | 
					c63cdae84f | ||
| 
						 | 
					dec044ad8b | ||
| 
						 | 
					2a12ec09fb | ||
| 
						 | 
					91e920c498 | ||
| 
						 | 
					9b19c45735 | ||
| 
						 | 
					3843d21dbf | ||
| 
						 | 
					73db164fb1 | ||
| 
						 | 
					17be6b106b | ||
| 
						 | 
					869981cfe4 | ||
| 
						 | 
					7dd56fb0fa | ||
| 
						 | 
					f0f09d3714 | ||
| 
						 | 
					9a66199904 | ||
| 
						 | 
					bf732f2a2b | ||
| 
						 | 
					c418eecf83 | ||
| 
						 | 
					98bf427600 | ||
| 
						 | 
					9aa5ee3372 | ||
| 
						 | 
					ccb3d3d308 | ||
| 
						 | 
					9ff0471274 | ||
| 
						 | 
					fdb20e4a30 | ||
| 
						 | 
					56630bb717 | ||
| 
						 | 
					08a41d9bd6 | ||
| 
						 | 
					cd46a69f2c | ||
| 
						 | 
					794a4bd9a1 | ||
| 
						 | 
					a120a455bf | ||
| 
						 | 
					cd72a2ed7e | ||
| 
						 | 
					3eff7e76aa | ||
| 
						 | 
					959d1944fd | ||
| 
						 | 
					b0966532bf | ||
| 
						 | 
					827b2def1e | ||
| 
						 | 
					80154b280e | ||
| 
						 | 
					efd0dd4c3d | ||
| 
						 | 
					c91b775b73 | ||
| 
						 | 
					1c237aef77 | ||
| 
						 | 
					89c5298bb9 | ||
| 
						 | 
					76c0d0912f | ||
| 
						 | 
					5eb12ac5fe | ||
| 
						 | 
					d238155640 | ||
| 
						 | 
					de626c0f5f | ||
| 
						 | 
					973e78355f | ||
| 
						 | 
					b82c7ad608 | ||
| 
						 | 
					417d45939f | ||
| 
						 | 
					837c749cd7 | ||
| 
						 | 
					b0e286972d | ||
| 
						 | 
					3cfe1e3083 | ||
| 
						 | 
					6738295475 | ||
| 
						 | 
					1617eba764 | ||
| 
						 | 
					899aa31df3 | ||
| 
						 | 
					ac81fae855 | ||
| 
						 | 
					8c6cddf1bb | ||
| 
						 | 
					508392db6e | ||
| 
						 | 
					3ac0165f00 | ||
| 
						 | 
					1691c13b47 | ||
| 
						 | 
					f8df694aa3 | ||
| 
						 | 
					ac05495781 | ||
| 
						 | 
					306ab0c56c | 
							
								
								
									
										15
									
								
								.clang-tidy
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								.clang-tidy
									
									
									
									
									
								
							@@ -5,9 +5,12 @@ Checks: >-
 | 
			
		||||
  -altera-*,
 | 
			
		||||
  -android-*,
 | 
			
		||||
  -boost-*,
 | 
			
		||||
  -bugprone-easily-swappable-parameters,
 | 
			
		||||
  -bugprone-implicit-widening-of-multiplication-result,
 | 
			
		||||
  -bugprone-narrowing-conversions,
 | 
			
		||||
  -bugprone-signed-char-misuse,
 | 
			
		||||
  -cert-dcl50-cpp,
 | 
			
		||||
  -cert-err33-c,
 | 
			
		||||
  -cert-err58-cpp,
 | 
			
		||||
  -cert-oop57-cpp,
 | 
			
		||||
  -cert-str34-c,
 | 
			
		||||
@@ -15,6 +18,7 @@ Checks: >-
 | 
			
		||||
  -clang-analyzer-osx.*,
 | 
			
		||||
  -clang-diagnostic-delete-abstract-non-virtual-dtor,
 | 
			
		||||
  -clang-diagnostic-delete-non-abstract-non-virtual-dtor,
 | 
			
		||||
  -clang-diagnostic-ignored-optimization-argument,
 | 
			
		||||
  -clang-diagnostic-shadow-field,
 | 
			
		||||
  -clang-diagnostic-unused-const-variable,
 | 
			
		||||
  -clang-diagnostic-unused-parameter,
 | 
			
		||||
@@ -25,6 +29,7 @@ Checks: >-
 | 
			
		||||
  -cppcoreguidelines-macro-usage,
 | 
			
		||||
  -cppcoreguidelines-narrowing-conversions,
 | 
			
		||||
  -cppcoreguidelines-non-private-member-variables-in-classes,
 | 
			
		||||
  -cppcoreguidelines-prefer-member-initializer,
 | 
			
		||||
  -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
 | 
			
		||||
  -cppcoreguidelines-pro-bounds-constant-array-index,
 | 
			
		||||
  -cppcoreguidelines-pro-bounds-pointer-arithmetic,
 | 
			
		||||
@@ -36,6 +41,7 @@ Checks: >-
 | 
			
		||||
  -cppcoreguidelines-pro-type-union-access,
 | 
			
		||||
  -cppcoreguidelines-pro-type-vararg,
 | 
			
		||||
  -cppcoreguidelines-special-member-functions,
 | 
			
		||||
  -cppcoreguidelines-virtual-class-destructor,
 | 
			
		||||
  -fuchsia-multiple-inheritance,
 | 
			
		||||
  -fuchsia-overloaded-operator,
 | 
			
		||||
  -fuchsia-statically-constructed-objects,
 | 
			
		||||
@@ -68,6 +74,7 @@ Checks: >-
 | 
			
		||||
  -modernize-use-nodiscard,
 | 
			
		||||
  -mpi-*,
 | 
			
		||||
  -objc-*,
 | 
			
		||||
  -readability-container-data-pointer,
 | 
			
		||||
  -readability-convert-member-functions-to-static,
 | 
			
		||||
  -readability-else-after-return,
 | 
			
		||||
  -readability-function-cognitive-complexity,
 | 
			
		||||
@@ -82,8 +89,6 @@ WarningsAsErrors: '*'
 | 
			
		||||
AnalyzeTemporaryDtors: false
 | 
			
		||||
FormatStyle:     google
 | 
			
		||||
CheckOptions:
 | 
			
		||||
  - key:             google-readability-braces-around-statements.ShortStatementLines
 | 
			
		||||
    value:           '1'
 | 
			
		||||
  - key:             google-readability-function-size.StatementThreshold
 | 
			
		||||
    value:           '800'
 | 
			
		||||
  - key:             google-runtime-int.TypeSuffix
 | 
			
		||||
@@ -158,3 +163,9 @@ CheckOptions:
 | 
			
		||||
    value:           ''
 | 
			
		||||
  - key:             readability-qualified-auto.AddConstToQualified
 | 
			
		||||
    value:           0
 | 
			
		||||
  - key:             readability-identifier-length.MinimumVariableNameLength
 | 
			
		||||
    value:           0
 | 
			
		||||
  - key:             readability-identifier-length.MinimumParameterNameLength
 | 
			
		||||
    value:           0
 | 
			
		||||
  - key:             readability-identifier-length.MinimumLoopCounterNameLength
 | 
			
		||||
    value:           0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -305,7 +305,7 @@ jobs:
 | 
			
		||||
          key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
 | 
			
		||||
 | 
			
		||||
      - name: Install clang-tidy
 | 
			
		||||
        run: sudo apt-get install clang-tidy-11
 | 
			
		||||
        run: sudo apt-get install clang-tidy-14
 | 
			
		||||
 | 
			
		||||
      - name: Register problem matchers
 | 
			
		||||
        run: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -129,4 +129,6 @@ tests/.esphome/
 | 
			
		||||
sdkconfig.*
 | 
			
		||||
!sdkconfig.defaults
 | 
			
		||||
 | 
			
		||||
.tests/
 | 
			
		||||
.tests/
 | 
			
		||||
 | 
			
		||||
/components
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
# See https://pre-commit.com/hooks.html for more hooks
 | 
			
		||||
repos:
 | 
			
		||||
  - repo: https://github.com/psf/black
 | 
			
		||||
    rev: 23.3.0
 | 
			
		||||
    rev: 23.7.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: black
 | 
			
		||||
        args:
 | 
			
		||||
@@ -27,7 +27,7 @@ repos:
 | 
			
		||||
          - --branch=release
 | 
			
		||||
          - --branch=beta
 | 
			
		||||
  - repo: https://github.com/asottile/pyupgrade
 | 
			
		||||
    rev: v3.7.0
 | 
			
		||||
    rev: v3.10.1
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: pyupgrade
 | 
			
		||||
        args: [--py39-plus]
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ esphome/*.py @esphome/core
 | 
			
		||||
esphome/core/* @esphome/core
 | 
			
		||||
 | 
			
		||||
# Integrations
 | 
			
		||||
esphome/components/a01nyub/* @MrSuicideParrot
 | 
			
		||||
esphome/components/absolute_humidity/* @DAVe3283
 | 
			
		||||
esphome/components/ac_dimmer/* @glmnet
 | 
			
		||||
esphome/components/adc/* @esphome/core
 | 
			
		||||
@@ -48,6 +49,7 @@ esphome/components/ble_client/* @buxtronix
 | 
			
		||||
esphome/components/bluetooth_proxy/* @jesserockz
 | 
			
		||||
esphome/components/bme680_bsec/* @trvrnrth
 | 
			
		||||
esphome/components/bmp3xx/* @martgras
 | 
			
		||||
esphome/components/bmp581/* @kahrendt
 | 
			
		||||
esphome/components/bp1658cj/* @Cossid
 | 
			
		||||
esphome/components/bp5758d/* @Cossid
 | 
			
		||||
esphome/components/button/* @esphome/core
 | 
			
		||||
@@ -100,6 +102,7 @@ esphome/components/fastled_base/* @OttoWinter
 | 
			
		||||
esphome/components/feedback/* @ianchi
 | 
			
		||||
esphome/components/fingerprint_grow/* @OnFreund @loongyh
 | 
			
		||||
esphome/components/fs3000/* @kahrendt
 | 
			
		||||
esphome/components/gcja5/* @gcormier
 | 
			
		||||
esphome/components/globals/* @esphome/core
 | 
			
		||||
esphome/components/gp8403/* @jesserockz
 | 
			
		||||
esphome/components/gpio/* @esphome/core
 | 
			
		||||
@@ -141,7 +144,7 @@ esphome/components/key_collector/* @ssieb
 | 
			
		||||
esphome/components/key_provider/* @ssieb
 | 
			
		||||
esphome/components/kuntze/* @ssieb
 | 
			
		||||
esphome/components/lcd_menu/* @numo68
 | 
			
		||||
esphome/components/ld2410/* @sebcaps
 | 
			
		||||
esphome/components/ld2410/* @regevbr @sebcaps
 | 
			
		||||
esphome/components/ledc/* @OttoWinter
 | 
			
		||||
esphome/components/light/* @esphome/core
 | 
			
		||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
 | 
			
		||||
 
 | 
			
		||||
@@ -22,16 +22,23 @@ RUN \
 | 
			
		||||
        python3=3.9.2-3 \
 | 
			
		||||
        python3-pip=20.3.4-4+deb11u1 \
 | 
			
		||||
        python3-setuptools=52.0.0-4 \
 | 
			
		||||
        python3-pil=8.1.2+dfsg-0.3+deb11u1 \
 | 
			
		||||
        python3-cryptography=3.3.2-1 \
 | 
			
		||||
        python3-venv=3.9.2-3 \
 | 
			
		||||
        iputils-ping=3:20210202-1 \
 | 
			
		||||
        git=1:2.30.2-1+deb11u2 \
 | 
			
		||||
        curl=7.74.0-1.3+deb11u7 \
 | 
			
		||||
        openssh-client=1:8.4p1-5+deb11u1 \
 | 
			
		||||
        libcairo2=1.16.0-5 \
 | 
			
		||||
        python3-cffi=1.14.5-1 \
 | 
			
		||||
    && rm -rf \
 | 
			
		||||
        libcairo2=1.16.0-5; \
 | 
			
		||||
    if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
 | 
			
		||||
        apt-get install -y --no-install-recommends \
 | 
			
		||||
          build-essential=12.9 \
 | 
			
		||||
          python3-dev=3.9.2-3 \
 | 
			
		||||
          zlib1g-dev=1:1.2.11.dfsg-2+deb11u2 \
 | 
			
		||||
          libjpeg-dev=1:2.0.6-4 \
 | 
			
		||||
          libfreetype-dev=2.10.4+dfsg-1+deb11u1; \
 | 
			
		||||
    fi; \
 | 
			
		||||
    rm -rf \
 | 
			
		||||
        /tmp/* \
 | 
			
		||||
        /var/{cache,log}/* \
 | 
			
		||||
        /var/lib/apt/lists/*
 | 
			
		||||
 
 | 
			
		||||
@@ -365,10 +365,16 @@ def command_wizard(args):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_config(args, config):
 | 
			
		||||
    _LOGGER.info("Configuration is valid!")
 | 
			
		||||
    if not CORE.verbose:
 | 
			
		||||
        config = strip_default_ids(config)
 | 
			
		||||
    safe_print(yaml_util.dump(config, args.show_secrets))
 | 
			
		||||
    output = yaml_util.dump(config, args.show_secrets)
 | 
			
		||||
    # add the console decoration so the front-end can hide the secrets
 | 
			
		||||
    if not args.show_secrets:
 | 
			
		||||
        output = re.sub(
 | 
			
		||||
            r"(password|key|psk|ssid)\: (.+)", r"\1: \\033[5m\2\\033[6m", output
 | 
			
		||||
        )
 | 
			
		||||
    safe_print(output)
 | 
			
		||||
    _LOGGER.info("Configuration is valid!")
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								esphome/components/a01nyub/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/a01nyub/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
CODEOWNERS = ["@MrSuicideParrot"]
 | 
			
		||||
							
								
								
									
										57
									
								
								esphome/components/a01nyub/a01nyub.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								esphome/components/a01nyub/a01nyub.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
// Datasheet https://wiki.dfrobot.com/A01NYUB%20Waterproof%20Ultrasonic%20Sensor%20SKU:%20SEN0313
 | 
			
		||||
 | 
			
		||||
#include "a01nyub.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace a01nyub {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "a01nyub.sensor";
 | 
			
		||||
static const uint8_t MAX_DATA_LENGTH_BYTES = 4;
 | 
			
		||||
 | 
			
		||||
void A01nyubComponent::loop() {
 | 
			
		||||
  uint8_t data;
 | 
			
		||||
  while (this->available() > 0) {
 | 
			
		||||
    if (this->read_byte(&data)) {
 | 
			
		||||
      buffer_.push_back(data);
 | 
			
		||||
      this->check_buffer_();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void A01nyubComponent::check_buffer_() {
 | 
			
		||||
  if (this->buffer_.size() >= MAX_DATA_LENGTH_BYTES) {
 | 
			
		||||
    size_t i;
 | 
			
		||||
    for (i = 0; i < this->buffer_.size(); i++) {
 | 
			
		||||
      // Look for the first packet
 | 
			
		||||
      if (this->buffer_[i] == 0xFF) {
 | 
			
		||||
        if (i + 1 + 3 < this->buffer_.size()) {  // Packet is not complete
 | 
			
		||||
          return;                                // Wait for completion
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint8_t checksum = (this->buffer_[i] + this->buffer_[i + 1] + this->buffer_[i + 2]) & 0xFF;
 | 
			
		||||
        if (this->buffer_[i + 3] == checksum) {
 | 
			
		||||
          float distance = (this->buffer_[i + 1] << 8) + this->buffer_[i + 2];
 | 
			
		||||
          if (distance > 280) {
 | 
			
		||||
            float meters = distance / 1000.0;
 | 
			
		||||
            ESP_LOGV(TAG, "Distance from sensor: %f mm, %f m", distance, meters);
 | 
			
		||||
            this->publish_state(meters);
 | 
			
		||||
          } else {
 | 
			
		||||
            ESP_LOGW(TAG, "Invalid data read from sensor: %s", format_hex_pretty(this->buffer_).c_str());
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    this->buffer_.clear();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void A01nyubComponent::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "A01nyub Sensor:");
 | 
			
		||||
  LOG_SENSOR("  ", "Distance", this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace a01nyub
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										27
									
								
								esphome/components/a01nyub/a01nyub.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/a01nyub/a01nyub.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/uart/uart.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace a01nyub {
 | 
			
		||||
 | 
			
		||||
class A01nyubComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  // Nothing really public.
 | 
			
		||||
 | 
			
		||||
  // ========== INTERNAL METHODS ==========
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void check_buffer_();
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> buffer_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace a01nyub
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										41
									
								
								esphome/components/a01nyub/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								esphome/components/a01nyub/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import sensor, uart
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_METER,
 | 
			
		||||
    ICON_ARROW_EXPAND_VERTICAL,
 | 
			
		||||
    DEVICE_CLASS_DISTANCE,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@MrSuicideParrot"]
 | 
			
		||||
DEPENDENCIES = ["uart"]
 | 
			
		||||
 | 
			
		||||
a01nyub_ns = cg.esphome_ns.namespace("a01nyub")
 | 
			
		||||
A01nyubComponent = a01nyub_ns.class_(
 | 
			
		||||
    "A01nyubComponent", sensor.Sensor, cg.Component, uart.UARTDevice
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = sensor.sensor_schema(
 | 
			
		||||
    A01nyubComponent,
 | 
			
		||||
    unit_of_measurement=UNIT_METER,
 | 
			
		||||
    icon=ICON_ARROW_EXPAND_VERTICAL,
 | 
			
		||||
    accuracy_decimals=3,
 | 
			
		||||
    state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    device_class=DEVICE_CLASS_DISTANCE,
 | 
			
		||||
).extend(uart.UART_DEVICE_SCHEMA)
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
 | 
			
		||||
    "a01nyub",
 | 
			
		||||
    baud_rate=9600,
 | 
			
		||||
    require_tx=False,
 | 
			
		||||
    require_rx=True,
 | 
			
		||||
    data_bits=8,
 | 
			
		||||
    parity=None,
 | 
			
		||||
    stop_bits=1,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = await sensor.new_sensor(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await uart.register_uart_device(var, config)
 | 
			
		||||
@@ -28,6 +28,6 @@ async def to_code(config):
 | 
			
		||||
    dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
 | 
			
		||||
    cg.add(var.set_dir_pin(dir_pin))
 | 
			
		||||
 | 
			
		||||
    if CONF_SLEEP_PIN in config:
 | 
			
		||||
        sleep_pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
 | 
			
		||||
    if sleep_pin_config := config.get(CONF_SLEEP_PIN):
 | 
			
		||||
        sleep_pin = await cg.gpio_pin_expression(sleep_pin_config)
 | 
			
		||||
        cg.add(var.set_sleep_pin(sleep_pin))
 | 
			
		||||
 
 | 
			
		||||
@@ -32,8 +32,8 @@ static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static const int32_t ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1;    // 4095 (12 bit) or 8191 (13 bit)
 | 
			
		||||
static const int32_t ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1;  // 2048 (12 bit) or 4096 (13 bit)
 | 
			
		||||
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1;    // 4095 (12 bit) or 8191 (13 bit)
 | 
			
		||||
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1;  // 2048 (12 bit) or 4096 (13 bit)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_RP2040
 | 
			
		||||
@@ -59,7 +59,7 @@ extern "C"
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // load characteristics for each attenuation
 | 
			
		||||
  for (int32_t i = 0; i < (int32_t) ADC_ATTEN_MAX; i++) {
 | 
			
		||||
  for (int32_t i = 0; i <= ADC_ATTEN_DB_11; i++) {
 | 
			
		||||
    auto adc_unit = 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,
 | 
			
		||||
                                              1100,  // default vref
 | 
			
		||||
@@ -157,7 +157,7 @@ float ADCSensor::sample() {
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
float ADCSensor::sample() {
 | 
			
		||||
  if (!autorange_) {
 | 
			
		||||
    int32_t raw = -1;
 | 
			
		||||
    int raw = -1;
 | 
			
		||||
    if (channel1_ != ADC1_CHANNEL_MAX) {
 | 
			
		||||
      raw = adc1_get_raw(channel1_);
 | 
			
		||||
    } else if (channel2_ != ADC2_CHANNEL_MAX) {
 | 
			
		||||
@@ -174,7 +174,7 @@ float ADCSensor::sample() {
 | 
			
		||||
    return mv / 1000.0f;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int32_t raw11 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
 | 
			
		||||
  int raw11 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
 | 
			
		||||
 | 
			
		||||
  if (channel1_ != ADC1_CHANNEL_MAX) {
 | 
			
		||||
    adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_11);
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
 | 
			
		||||
  adc1_channel_t channel1_{ADC1_CHANNEL_MAX};
 | 
			
		||||
  adc2_channel_t channel2_{ADC2_CHANNEL_MAX};
 | 
			
		||||
  bool autorange_{false};
 | 
			
		||||
  esp_adc_cal_characteristics_t cal_characteristics_[(int32_t) ADC_ATTEN_MAX] = {};
 | 
			
		||||
  esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {};
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -89,14 +89,13 @@ async def to_code(config):
 | 
			
		||||
        pin = await cg.gpio_pin_expression(config[CONF_PIN])
 | 
			
		||||
        cg.add(var.set_pin(pin))
 | 
			
		||||
 | 
			
		||||
    if CONF_RAW in config:
 | 
			
		||||
        cg.add(var.set_output_raw(config[CONF_RAW]))
 | 
			
		||||
    cg.add(var.set_output_raw(config[CONF_RAW]))
 | 
			
		||||
 | 
			
		||||
    if CONF_ATTENUATION in config:
 | 
			
		||||
        if config[CONF_ATTENUATION] == "auto":
 | 
			
		||||
    if attenuation := config.get(CONF_ATTENUATION):
 | 
			
		||||
        if attenuation == "auto":
 | 
			
		||||
            cg.add(var.set_autorange(cg.global_ns.true))
 | 
			
		||||
        else:
 | 
			
		||||
            cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
 | 
			
		||||
            cg.add(var.set_attenuation(attenuation))
 | 
			
		||||
 | 
			
		||||
    if CORE.is_esp32:
 | 
			
		||||
        variant = get_esp32_variant()
 | 
			
		||||
 
 | 
			
		||||
@@ -48,16 +48,16 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await display.register_display(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_PIXEL_MAPPER in config:
 | 
			
		||||
    if pixel_mapper := config.get(CONF_PIXEL_MAPPER):
 | 
			
		||||
        pixel_mapper_template_ = await cg.process_lambda(
 | 
			
		||||
            config[CONF_PIXEL_MAPPER],
 | 
			
		||||
            pixel_mapper,
 | 
			
		||||
            [(int, "x"), (int, "y")],
 | 
			
		||||
            return_type=cg.int_,
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_pixel_mapper(pixel_mapper_template_))
 | 
			
		||||
 | 
			
		||||
    if CONF_LAMBDA in config:
 | 
			
		||||
    if lambda_config := config.get(CONF_LAMBDA):
 | 
			
		||||
        lambda_ = await cg.process_lambda(
 | 
			
		||||
            config[CONF_LAMBDA], [(display.DisplayRef, "it")], return_type=cg.void
 | 
			
		||||
            lambda_config, [(display.DisplayRef, "it")], return_type=cg.void
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_writer(lambda_))
 | 
			
		||||
 
 | 
			
		||||
@@ -72,8 +72,8 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_IRQ_PIN in config:
 | 
			
		||||
        irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
 | 
			
		||||
    if irq_pin_config := config.get(CONF_IRQ_PIN):
 | 
			
		||||
        irq_pin = await cg.gpio_pin_expression(irq_pin_config)
 | 
			
		||||
        cg.add(var.set_irq_pin(irq_pin))
 | 
			
		||||
 | 
			
		||||
    for key in [
 | 
			
		||||
 
 | 
			
		||||
@@ -45,10 +45,10 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
    if temperature := config.get(CONF_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(temperature)
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_HUMIDITY])
 | 
			
		||||
    if humidity := config.get(CONF_HUMIDITY):
 | 
			
		||||
        sens = await sensor.new_sensor(humidity)
 | 
			
		||||
        cg.add(var.set_humidity_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#include "airthings_listener.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
@@ -19,7 +20,7 @@ bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &devic
 | 
			
		||||
      sn |= ((uint32_t) it.data[2] << 16);
 | 
			
		||||
      sn |= ((uint32_t) it.data[3] << 24);
 | 
			
		||||
 | 
			
		||||
      ESP_LOGD(TAG, "Found AirThings device Serial:%u (MAC: %s)", sn, device.address_str().c_str());
 | 
			
		||||
      ESP_LOGD(TAG, "Found AirThings device Serial:%" PRIu32 " (MAC: %s)", sn, device.address_str().c_str());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,12 @@ IS_PLATFORM_COMPONENT = True
 | 
			
		||||
 | 
			
		||||
CONF_ON_TRIGGERED = "on_triggered"
 | 
			
		||||
CONF_ON_CLEARED = "on_cleared"
 | 
			
		||||
CONF_ON_ARMING = "on_arming"
 | 
			
		||||
CONF_ON_PENDING = "on_pending"
 | 
			
		||||
CONF_ON_ARMED_HOME = "on_armed_home"
 | 
			
		||||
CONF_ON_ARMED_NIGHT = "on_armed_night"
 | 
			
		||||
CONF_ON_ARMED_AWAY = "on_armed_away"
 | 
			
		||||
CONF_ON_DISARMED = "on_disarmed"
 | 
			
		||||
 | 
			
		||||
alarm_control_panel_ns = cg.esphome_ns.namespace("alarm_control_panel")
 | 
			
		||||
AlarmControlPanel = alarm_control_panel_ns.class_("AlarmControlPanel", cg.EntityBase)
 | 
			
		||||
@@ -29,8 +35,27 @@ TriggeredTrigger = alarm_control_panel_ns.class_(
 | 
			
		||||
ClearedTrigger = alarm_control_panel_ns.class_(
 | 
			
		||||
    "ClearedTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
ArmingTrigger = alarm_control_panel_ns.class_(
 | 
			
		||||
    "ArmingTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
PendingTrigger = alarm_control_panel_ns.class_(
 | 
			
		||||
    "PendingTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
ArmedHomeTrigger = alarm_control_panel_ns.class_(
 | 
			
		||||
    "ArmedHomeTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
ArmedNightTrigger = alarm_control_panel_ns.class_(
 | 
			
		||||
    "ArmedNightTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
ArmedAwayTrigger = alarm_control_panel_ns.class_(
 | 
			
		||||
    "ArmedAwayTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
DisarmedTrigger = alarm_control_panel_ns.class_(
 | 
			
		||||
    "DisarmedTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
ArmAwayAction = alarm_control_panel_ns.class_("ArmAwayAction", automation.Action)
 | 
			
		||||
ArmHomeAction = alarm_control_panel_ns.class_("ArmHomeAction", automation.Action)
 | 
			
		||||
ArmNightAction = alarm_control_panel_ns.class_("ArmNightAction", automation.Action)
 | 
			
		||||
DisarmAction = alarm_control_panel_ns.class_("DisarmAction", automation.Action)
 | 
			
		||||
PendingAction = alarm_control_panel_ns.class_("PendingAction", automation.Action)
 | 
			
		||||
TriggeredAction = alarm_control_panel_ns.class_("TriggeredAction", automation.Action)
 | 
			
		||||
@@ -51,6 +76,36 @@ ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_ARMING): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmingTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_PENDING): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PendingTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedHomeTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedNightTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedAwayTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_DISARMED): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DisarmedTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_CLEARED): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger),
 | 
			
		||||
@@ -81,6 +136,24 @@ async def setup_alarm_control_panel_core_(var, config):
 | 
			
		||||
    for conf in config.get(CONF_ON_TRIGGERED, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_ARMING, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_PENDING, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_ARMED_HOME, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_ARMED_NIGHT, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_ARMED_AWAY, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_DISARMED, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_CLEARED, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
@@ -99,8 +172,8 @@ async def register_alarm_control_panel(var, config):
 | 
			
		||||
async def alarm_action_arm_away_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
    if CONF_CODE in config:
 | 
			
		||||
        templatable_ = await cg.templatable(config[CONF_CODE], args, cg.std_string)
 | 
			
		||||
    if code_config := config.get(CONF_CODE):
 | 
			
		||||
        templatable_ = await cg.templatable(code_config, args, cg.std_string)
 | 
			
		||||
        cg.add(var.set_code(templatable_))
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
@@ -109,6 +182,18 @@ async def alarm_action_arm_away_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    "alarm_control_panel.arm_home", ArmHomeAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
 | 
			
		||||
)
 | 
			
		||||
async def alarm_action_arm_home_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
    if code_config := config.get(CONF_CODE):
 | 
			
		||||
        templatable_ = await cg.templatable(code_config, args, cg.std_string)
 | 
			
		||||
        cg.add(var.set_code(templatable_))
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "alarm_control_panel.arm_night", ArmNightAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
 | 
			
		||||
)
 | 
			
		||||
async def alarm_action_arm_night_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
    if CONF_CODE in config:
 | 
			
		||||
@@ -123,8 +208,8 @@ async def alarm_action_arm_home_to_code(config, action_id, template_arg, args):
 | 
			
		||||
async def alarm_action_disarm_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
    if CONF_CODE in config:
 | 
			
		||||
        templatable_ = await cg.templatable(config[CONF_CODE], args, cg.std_string)
 | 
			
		||||
    if code_config := config.get(CONF_CODE):
 | 
			
		||||
        templatable_ = await cg.templatable(code_config, args, cg.std_string)
 | 
			
		||||
        cg.add(var.set_code(templatable_))
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,20 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
 | 
			
		||||
    this->state_callback_.call();
 | 
			
		||||
    if (state == ACP_STATE_TRIGGERED) {
 | 
			
		||||
      this->triggered_callback_.call();
 | 
			
		||||
    } else if (state == ACP_STATE_ARMING) {
 | 
			
		||||
      this->arming_callback_.call();
 | 
			
		||||
    } else if (state == ACP_STATE_PENDING) {
 | 
			
		||||
      this->pending_callback_.call();
 | 
			
		||||
    } else if (state == ACP_STATE_ARMED_HOME) {
 | 
			
		||||
      this->armed_home_callback_.call();
 | 
			
		||||
    } else if (state == ACP_STATE_ARMED_NIGHT) {
 | 
			
		||||
      this->armed_night_callback_.call();
 | 
			
		||||
    } else if (state == ACP_STATE_ARMED_AWAY) {
 | 
			
		||||
      this->armed_away_callback_.call();
 | 
			
		||||
    } else if (state == ACP_STATE_DISARMED) {
 | 
			
		||||
      this->disarmed_callback_.call();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (prev_state == ACP_STATE_TRIGGERED) {
 | 
			
		||||
      this->cleared_callback_.call();
 | 
			
		||||
    }
 | 
			
		||||
@@ -55,6 +68,30 @@ void AlarmControlPanel::add_on_triggered_callback(std::function<void()> &&callba
 | 
			
		||||
  this->triggered_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AlarmControlPanel::add_on_arming_callback(std::function<void()> &&callback) {
 | 
			
		||||
  this->arming_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AlarmControlPanel::add_on_armed_home_callback(std::function<void()> &&callback) {
 | 
			
		||||
  this->armed_home_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AlarmControlPanel::add_on_armed_night_callback(std::function<void()> &&callback) {
 | 
			
		||||
  this->armed_night_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AlarmControlPanel::add_on_armed_away_callback(std::function<void()> &&callback) {
 | 
			
		||||
  this->armed_away_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AlarmControlPanel::add_on_pending_callback(std::function<void()> &&callback) {
 | 
			
		||||
  this->pending_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AlarmControlPanel::add_on_disarmed_callback(std::function<void()> &&callback) {
 | 
			
		||||
  this->disarmed_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AlarmControlPanel::add_on_cleared_callback(std::function<void()> &&callback) {
 | 
			
		||||
  this->cleared_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,42 @@ class AlarmControlPanel : public EntityBase {
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_triggered_callback(std::function<void()> &&callback);
 | 
			
		||||
 | 
			
		||||
  /** Add a callback for when the state of the alarm_control_panel chanes to arming
 | 
			
		||||
   *
 | 
			
		||||
   * @param callback The callback function
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_arming_callback(std::function<void()> &&callback);
 | 
			
		||||
 | 
			
		||||
  /** Add a callback for when the state of the alarm_control_panel changes to pending
 | 
			
		||||
   *
 | 
			
		||||
   * @param callback The callback function
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_pending_callback(std::function<void()> &&callback);
 | 
			
		||||
 | 
			
		||||
  /** Add a callback for when the state of the alarm_control_panel changes to armed_home
 | 
			
		||||
   *
 | 
			
		||||
   * @param callback The callback function
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_armed_home_callback(std::function<void()> &&callback);
 | 
			
		||||
 | 
			
		||||
  /** Add a callback for when the state of the alarm_control_panel changes to armed_night
 | 
			
		||||
   *
 | 
			
		||||
   * @param callback The callback function
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_armed_night_callback(std::function<void()> &&callback);
 | 
			
		||||
 | 
			
		||||
  /** Add a callback for when the state of the alarm_control_panel changes to armed_away
 | 
			
		||||
   *
 | 
			
		||||
   * @param callback The callback function
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_armed_away_callback(std::function<void()> &&callback);
 | 
			
		||||
 | 
			
		||||
  /** Add a callback for when the state of the alarm_control_panel changes to disarmed
 | 
			
		||||
   *
 | 
			
		||||
   * @param callback The callback function
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_disarmed_callback(std::function<void()> &&callback);
 | 
			
		||||
 | 
			
		||||
  /** Add a callback for when the state of the alarm_control_panel clears from triggered
 | 
			
		||||
   *
 | 
			
		||||
   * @param callback The callback function
 | 
			
		||||
@@ -128,6 +164,18 @@ class AlarmControlPanel : public EntityBase {
 | 
			
		||||
  CallbackManager<void()> state_callback_{};
 | 
			
		||||
  // trigger callback
 | 
			
		||||
  CallbackManager<void()> triggered_callback_{};
 | 
			
		||||
  // arming callback
 | 
			
		||||
  CallbackManager<void()> arming_callback_{};
 | 
			
		||||
  // pending callback
 | 
			
		||||
  CallbackManager<void()> pending_callback_{};
 | 
			
		||||
  // armed_home callback
 | 
			
		||||
  CallbackManager<void()> armed_home_callback_{};
 | 
			
		||||
  // armed_night callback
 | 
			
		||||
  CallbackManager<void()> armed_night_callback_{};
 | 
			
		||||
  // armed_away callback
 | 
			
		||||
  CallbackManager<void()> armed_away_callback_{};
 | 
			
		||||
  // disarmed callback
 | 
			
		||||
  CallbackManager<void()> disarmed_callback_{};
 | 
			
		||||
  // clear callback
 | 
			
		||||
  CallbackManager<void()> cleared_callback_{};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -85,6 +85,11 @@ void AlarmControlPanelCall::validate_() {
 | 
			
		||||
      this->state_.reset();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (state == ACP_STATE_ARMED_NIGHT && (this->parent_->get_supported_features() & ACP_FEAT_ARM_NIGHT) == 0) {
 | 
			
		||||
      ESP_LOGW(TAG, "Cannot arm night when not supported");
 | 
			
		||||
      this->state_.reset();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState stat
 | 
			
		||||
    case ACP_STATE_ARMED_AWAY:
 | 
			
		||||
      return LOG_STR("ARMED_AWAY");
 | 
			
		||||
    case ACP_STATE_ARMED_NIGHT:
 | 
			
		||||
      return LOG_STR("NIGHT");
 | 
			
		||||
      return LOG_STR("ARMED_NIGHT");
 | 
			
		||||
    case ACP_STATE_ARMED_VACATION:
 | 
			
		||||
      return LOG_STR("ARMED_VACATION");
 | 
			
		||||
    case ACP_STATE_ARMED_CUSTOM_BYPASS:
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,48 @@ class TriggeredTrigger : public Trigger<> {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ArmingTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ArmingTrigger(AlarmControlPanel *alarm_control_panel) {
 | 
			
		||||
    alarm_control_panel->add_on_arming_callback([this]() { this->trigger(); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PendingTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit PendingTrigger(AlarmControlPanel *alarm_control_panel) {
 | 
			
		||||
    alarm_control_panel->add_on_pending_callback([this]() { this->trigger(); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ArmedHomeTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ArmedHomeTrigger(AlarmControlPanel *alarm_control_panel) {
 | 
			
		||||
    alarm_control_panel->add_on_armed_home_callback([this]() { this->trigger(); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ArmedNightTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ArmedNightTrigger(AlarmControlPanel *alarm_control_panel) {
 | 
			
		||||
    alarm_control_panel->add_on_armed_night_callback([this]() { this->trigger(); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ArmedAwayTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ArmedAwayTrigger(AlarmControlPanel *alarm_control_panel) {
 | 
			
		||||
    alarm_control_panel->add_on_armed_away_callback([this]() { this->trigger(); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DisarmedTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit DisarmedTrigger(AlarmControlPanel *alarm_control_panel) {
 | 
			
		||||
    alarm_control_panel->add_on_disarmed_callback([this]() { this->trigger(); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ClearedTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ClearedTrigger(AlarmControlPanel *alarm_control_panel) {
 | 
			
		||||
@@ -67,6 +109,26 @@ template<typename... Ts> class ArmHomeAction : public Action<Ts...> {
 | 
			
		||||
  AlarmControlPanel *alarm_control_panel_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class ArmNightAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ArmNightAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
 | 
			
		||||
 | 
			
		||||
  TEMPLATABLE_VALUE(std::string, code)
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override {
 | 
			
		||||
    auto call = this->alarm_control_panel_->make_call();
 | 
			
		||||
    auto code = this->code_.optional_value(x...);
 | 
			
		||||
    if (code.has_value()) {
 | 
			
		||||
      call.set_code(code.value());
 | 
			
		||||
    }
 | 
			
		||||
    call.arm_night();
 | 
			
		||||
    call.perform();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  AlarmControlPanel *alarm_control_panel_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class DisarmAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit DisarmAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
 | 
			
		||||
 
 | 
			
		||||
@@ -60,26 +60,26 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await ble_client.register_ble_node(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_FLOW in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_FLOW])
 | 
			
		||||
    if flow_config := config.get(CONF_FLOW):
 | 
			
		||||
        sens = await sensor.new_sensor(flow_config)
 | 
			
		||||
        cg.add(var.set_flow_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_HEAD in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_HEAD])
 | 
			
		||||
    if head_config := config.get(CONF_HEAD):
 | 
			
		||||
        sens = await sensor.new_sensor(head_config)
 | 
			
		||||
        cg.add(var.set_head_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_POWER in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_POWER])
 | 
			
		||||
    if power_config := config.get(CONF_POWER):
 | 
			
		||||
        sens = await sensor.new_sensor(power_config)
 | 
			
		||||
        cg.add(var.set_power_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_CURRENT in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_CURRENT])
 | 
			
		||||
    if current_config := config.get(CONF_CURRENT):
 | 
			
		||||
        sens = await sensor.new_sensor(current_config)
 | 
			
		||||
        cg.add(var.set_current_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_SPEED in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_SPEED])
 | 
			
		||||
    if speed_config := config.get(CONF_SPEED):
 | 
			
		||||
        sens = await sensor.new_sensor(speed_config)
 | 
			
		||||
        cg.add(var.set_speed_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_VOLTAGE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_VOLTAGE])
 | 
			
		||||
    if voltage_config := config.get(CONF_VOLTAGE):
 | 
			
		||||
        sens = await sensor.new_sensor(voltage_config)
 | 
			
		||||
        cg.add(var.set_voltage_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -47,10 +47,10 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
    if temperature_config := config.get(CONF_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(temperature_config)
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_HUMIDITY])
 | 
			
		||||
    if humidity_config := config.get(CONF_HUMIDITY):
 | 
			
		||||
        sens = await sensor.new_sensor(humidity_config)
 | 
			
		||||
        cg.add(var.set_humidity_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -44,10 +44,10 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await ble_client.register_ble_node(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_BATTERY_LEVEL in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
 | 
			
		||||
    if battery_level_config := config.get(CONF_BATTERY_LEVEL):
 | 
			
		||||
        sens = await sensor.new_sensor(battery_level_config)
 | 
			
		||||
        cg.add(var.set_battery(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_ILLUMINANCE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_ILLUMINANCE])
 | 
			
		||||
    if illuminance_config := config.get(CONF_ILLUMINANCE):
 | 
			
		||||
        sens = await sensor.new_sensor(illuminance_config)
 | 
			
		||||
        cg.add(var.set_illuminance(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -115,8 +115,8 @@ async def animation_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
 | 
			
		||||
    if CONF_FRAME in config:
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_FRAME], args, cg.uint16)
 | 
			
		||||
    if (frame := config.get(CONF_FRAME)) is not None:
 | 
			
		||||
        template_ = await cg.templatable(frame, args, cg.uint16)
 | 
			
		||||
        cg.add(var.set_frame(template_))
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
@@ -289,8 +289,8 @@ async def to_code(config):
 | 
			
		||||
        espImage.IMAGE_TYPE[config[CONF_TYPE]],
 | 
			
		||||
    )
 | 
			
		||||
    cg.add(var.set_transparency(transparent))
 | 
			
		||||
    if CONF_LOOP in config:
 | 
			
		||||
        start = config[CONF_LOOP][CONF_START_FRAME]
 | 
			
		||||
        end = config[CONF_LOOP].get(CONF_END_FRAME, frames)
 | 
			
		||||
        count = config[CONF_LOOP].get(CONF_REPEAT, -1)
 | 
			
		||||
    if loop_config := config.get(CONF_LOOP):
 | 
			
		||||
        start = loop_config[CONF_START_FRAME]
 | 
			
		||||
        end = loop_config.get(CONF_END_FRAME, frames)
 | 
			
		||||
        count = loop_config.get(CONF_REPEAT, -1)
 | 
			
		||||
        cg.add(var.set_loop(start, end, count))
 | 
			
		||||
 
 | 
			
		||||
@@ -116,9 +116,8 @@ async def to_code(config):
 | 
			
		||||
        cg.add(var.register_user_service(trigger))
 | 
			
		||||
        await automation.build_automation(trigger, func_args, conf)
 | 
			
		||||
 | 
			
		||||
    if CONF_ENCRYPTION in config:
 | 
			
		||||
        conf = config[CONF_ENCRYPTION]
 | 
			
		||||
        decoded = base64.b64decode(conf[CONF_KEY])
 | 
			
		||||
    if encryption_config := config.get(CONF_ENCRYPTION):
 | 
			
		||||
        decoded = base64.b64decode(encryption_config[CONF_KEY])
 | 
			
		||||
        cg.add(var.set_noise_psk(list(decoded)))
 | 
			
		||||
        cg.add_define("USE_API_NOISE")
 | 
			
		||||
        cg.add_library("esphome/noise-c", "0.1.4")
 | 
			
		||||
 
 | 
			
		||||
@@ -31,12 +31,10 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    hub = await cg.get_variable(config[CONF_AS3935_ID])
 | 
			
		||||
 | 
			
		||||
    if CONF_DISTANCE in config:
 | 
			
		||||
        conf = config[CONF_DISTANCE]
 | 
			
		||||
        distance_sensor = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(hub.set_distance_sensor(distance_sensor))
 | 
			
		||||
    if distance_config := config.get(CONF_DISTANCE):
 | 
			
		||||
        sens = await sensor.new_sensor(distance_config)
 | 
			
		||||
        cg.add(hub.set_distance_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_LIGHTNING_ENERGY in config:
 | 
			
		||||
        conf = config[CONF_LIGHTNING_ENERGY]
 | 
			
		||||
        lightning_energy_sensor = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(hub.set_energy_sensor(lightning_energy_sensor))
 | 
			
		||||
    if lightning_energy_config := config.get(CONF_LIGHTNING_ENERGY):
 | 
			
		||||
        sens = await sensor.new_sensor(lightning_energy_config)
 | 
			
		||||
        cg.add(hub.set_energy_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -107,6 +107,6 @@ async def to_code(config):
 | 
			
		||||
    cg.add(var.set_astep(config[CONF_ASTEP]))
 | 
			
		||||
 | 
			
		||||
    for conf_id, set_sensor_func in SENSORS.items():
 | 
			
		||||
        if conf_id in config:
 | 
			
		||||
            sens = await sensor.new_sensor(config[conf_id])
 | 
			
		||||
        if sens_config := config.get(conf_id):
 | 
			
		||||
            sens = await sensor.new_sensor(sens_config)
 | 
			
		||||
            cg.add(getattr(var, set_sensor_func)(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -83,18 +83,18 @@ async def to_code(config):
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
    if temperature_config := config.get(CONF_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(temperature_config)
 | 
			
		||||
        cg.add(var.set_temperature(sens))
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_HUMIDITY])
 | 
			
		||||
    if humidity_config := config.get(CONF_HUMIDITY):
 | 
			
		||||
        sens = await sensor.new_sensor(humidity_config)
 | 
			
		||||
        cg.add(var.set_humidity(sens))
 | 
			
		||||
    if CONF_BATTERY_LEVEL in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
 | 
			
		||||
    if battery_level_config := config.get(CONF_BATTERY_LEVEL):
 | 
			
		||||
        sens = await sensor.new_sensor(battery_level_config)
 | 
			
		||||
        cg.add(var.set_battery_level(sens))
 | 
			
		||||
    if CONF_BATTERY_VOLTAGE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
 | 
			
		||||
    if battery_voltage_config := config.get(CONF_BATTERY_VOLTAGE):
 | 
			
		||||
        sens = await sensor.new_sensor(battery_voltage_config)
 | 
			
		||||
        cg.add(var.set_battery_voltage(sens))
 | 
			
		||||
    if CONF_SIGNAL_STRENGTH in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_SIGNAL_STRENGTH])
 | 
			
		||||
    if signal_strength_config := config.get(CONF_SIGNAL_STRENGTH):
 | 
			
		||||
        sens = await sensor.new_sensor(signal_strength_config)
 | 
			
		||||
        cg.add(var.set_signal_strength(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -124,29 +124,29 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await spi.register_spi_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_VOLTAGE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_VOLTAGE])
 | 
			
		||||
    if voltage_config := config.get(CONF_VOLTAGE):
 | 
			
		||||
        sens = await sensor.new_sensor(voltage_config)
 | 
			
		||||
        cg.add(var.set_voltage_sensor(sens))
 | 
			
		||||
    if CONF_CURRENT in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_CURRENT])
 | 
			
		||||
    if current_config := config.get(CONF_CURRENT):
 | 
			
		||||
        sens = await sensor.new_sensor(current_config)
 | 
			
		||||
        cg.add(var.set_current_sensor(sens))
 | 
			
		||||
    if CONF_POWER in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_POWER])
 | 
			
		||||
    if power_config := config.get(CONF_POWER):
 | 
			
		||||
        sens = await sensor.new_sensor(power_config)
 | 
			
		||||
        cg.add(var.set_power_sensor(sens))
 | 
			
		||||
    if CONF_REACTIVE_POWER in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_REACTIVE_POWER])
 | 
			
		||||
    if reactive_power_config := config.get(CONF_REACTIVE_POWER):
 | 
			
		||||
        sens = await sensor.new_sensor(reactive_power_config)
 | 
			
		||||
        cg.add(var.set_reactive_power_sensor(sens))
 | 
			
		||||
    if CONF_POWER_FACTOR in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_POWER_FACTOR])
 | 
			
		||||
    if power_factor_config := config.get(CONF_POWER_FACTOR):
 | 
			
		||||
        sens = await sensor.new_sensor(power_factor_config)
 | 
			
		||||
        cg.add(var.set_power_factor_sensor(sens))
 | 
			
		||||
    if CONF_FORWARD_ACTIVE_ENERGY in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_FORWARD_ACTIVE_ENERGY])
 | 
			
		||||
    if forward_active_energy_config := config.get(CONF_FORWARD_ACTIVE_ENERGY):
 | 
			
		||||
        sens = await sensor.new_sensor(forward_active_energy_config)
 | 
			
		||||
        cg.add(var.set_forward_active_energy_sensor(sens))
 | 
			
		||||
    if CONF_REVERSE_ACTIVE_ENERGY in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_REVERSE_ACTIVE_ENERGY])
 | 
			
		||||
    if reverse_active_energy_config := config.get(CONF_REVERSE_ACTIVE_ENERGY):
 | 
			
		||||
        sens = await sensor.new_sensor(reverse_active_energy_config)
 | 
			
		||||
        cg.add(var.set_reverse_active_energy_sensor(sens))
 | 
			
		||||
    if CONF_FREQUENCY in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_FREQUENCY])
 | 
			
		||||
    if frequency_config := config.get(CONF_FREQUENCY):
 | 
			
		||||
        sens = await sensor.new_sensor(frequency_config)
 | 
			
		||||
        cg.add(var.set_freq_sensor(sens))
 | 
			
		||||
    cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
 | 
			
		||||
    cg.add(var.set_meter_constant(config[CONF_METER_CONSTANT]))
 | 
			
		||||
 
 | 
			
		||||
@@ -151,33 +151,35 @@ async def to_code(config):
 | 
			
		||||
        conf = config[phase]
 | 
			
		||||
        cg.add(var.set_volt_gain(i, conf[CONF_GAIN_VOLTAGE]))
 | 
			
		||||
        cg.add(var.set_ct_gain(i, conf[CONF_GAIN_CT]))
 | 
			
		||||
        if CONF_VOLTAGE in conf:
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_VOLTAGE])
 | 
			
		||||
        if voltage_config := conf.get(CONF_VOLTAGE):
 | 
			
		||||
            sens = await sensor.new_sensor(voltage_config)
 | 
			
		||||
            cg.add(var.set_voltage_sensor(i, sens))
 | 
			
		||||
        if CONF_CURRENT in conf:
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_CURRENT])
 | 
			
		||||
        if current_config := conf.get(CONF_CURRENT):
 | 
			
		||||
            sens = await sensor.new_sensor(current_config)
 | 
			
		||||
            cg.add(var.set_current_sensor(i, sens))
 | 
			
		||||
        if CONF_POWER in conf:
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_POWER])
 | 
			
		||||
        if power_config := conf.get(CONF_POWER):
 | 
			
		||||
            sens = await sensor.new_sensor(power_config)
 | 
			
		||||
            cg.add(var.set_power_sensor(i, sens))
 | 
			
		||||
        if CONF_REACTIVE_POWER in conf:
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_REACTIVE_POWER])
 | 
			
		||||
        if reactive_power_config := conf.get(CONF_REACTIVE_POWER):
 | 
			
		||||
            sens = await sensor.new_sensor(reactive_power_config)
 | 
			
		||||
            cg.add(var.set_reactive_power_sensor(i, sens))
 | 
			
		||||
        if CONF_POWER_FACTOR in conf:
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_POWER_FACTOR])
 | 
			
		||||
        if power_factor_config := conf.get(CONF_POWER_FACTOR):
 | 
			
		||||
            sens = await sensor.new_sensor(power_factor_config)
 | 
			
		||||
            cg.add(var.set_power_factor_sensor(i, sens))
 | 
			
		||||
        if CONF_FORWARD_ACTIVE_ENERGY in conf:
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_FORWARD_ACTIVE_ENERGY])
 | 
			
		||||
        if forward_active_energy_config := conf.get(CONF_FORWARD_ACTIVE_ENERGY):
 | 
			
		||||
            sens = await sensor.new_sensor(forward_active_energy_config)
 | 
			
		||||
            cg.add(var.set_forward_active_energy_sensor(i, sens))
 | 
			
		||||
        if CONF_REVERSE_ACTIVE_ENERGY in conf:
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_REVERSE_ACTIVE_ENERGY])
 | 
			
		||||
        if reverse_active_energy_config := conf.get(CONF_REVERSE_ACTIVE_ENERGY):
 | 
			
		||||
            sens = await sensor.new_sensor(reverse_active_energy_config)
 | 
			
		||||
            cg.add(var.set_reverse_active_energy_sensor(i, sens))
 | 
			
		||||
    if CONF_FREQUENCY in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_FREQUENCY])
 | 
			
		||||
 | 
			
		||||
    if frequency_config := config.get(CONF_FREQUENCY):
 | 
			
		||||
        sens = await sensor.new_sensor(frequency_config)
 | 
			
		||||
        cg.add(var.set_freq_sensor(sens))
 | 
			
		||||
    if CONF_CHIP_TEMPERATURE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_CHIP_TEMPERATURE])
 | 
			
		||||
    if chip_temperature_config := config.get(CONF_CHIP_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(chip_temperature_config)
 | 
			
		||||
        cg.add(var.set_chip_temperature_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
 | 
			
		||||
    cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
 | 
			
		||||
    cg.add(var.set_pga_gain(config[CONF_GAIN_PGA]))
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,6 @@ async def to_code(config):
 | 
			
		||||
        (CONF_MOISTURE, var.set_soil_moisture),
 | 
			
		||||
        (CONF_ILLUMINANCE, var.set_illuminance),
 | 
			
		||||
    ]:
 | 
			
		||||
        if config_key in config:
 | 
			
		||||
            sens = await sensor.new_sensor(config[config_key])
 | 
			
		||||
        if sensor_config := config.get(config_key):
 | 
			
		||||
            sens = await sensor.new_sensor(sensor_config)
 | 
			
		||||
            cg.add(setter(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -57,19 +57,18 @@ async def to_code(config):
 | 
			
		||||
        var.get_idle_trigger(), [], config[CONF_IDLE_ACTION]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if CONF_COOL_ACTION in config:
 | 
			
		||||
    if cool_action_config := config.get(CONF_COOL_ACTION):
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_cool_trigger(), [], config[CONF_COOL_ACTION]
 | 
			
		||||
            var.get_cool_trigger(), [], cool_action_config
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_supports_cool(True))
 | 
			
		||||
    if CONF_HEAT_ACTION in config:
 | 
			
		||||
    if heat_action_config := config.get(CONF_HEAT_ACTION):
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_heat_trigger(), [], config[CONF_HEAT_ACTION]
 | 
			
		||||
            var.get_heat_trigger(), [], heat_action_config
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_supports_heat(True))
 | 
			
		||||
 | 
			
		||||
    if CONF_AWAY_CONFIG in config:
 | 
			
		||||
        away = config[CONF_AWAY_CONFIG]
 | 
			
		||||
    if away := config.get(CONF_AWAY_CONFIG):
 | 
			
		||||
        away_config = BangBangClimateTargetTempConfig(
 | 
			
		||||
            away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
 | 
			
		||||
            away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH],
 | 
			
		||||
 
 | 
			
		||||
@@ -45,8 +45,8 @@ async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await ble_client.register_ble_node(var, config)
 | 
			
		||||
    if CONF_TIME_ID in config:
 | 
			
		||||
        time_ = await cg.get_variable(config[CONF_TIME_ID])
 | 
			
		||||
    if time_id := config.get(CONF_TIME_ID):
 | 
			
		||||
        time_ = await cg.get_variable(time_id)
 | 
			
		||||
        cg.add(var.set_time_id(time_))
 | 
			
		||||
    if CONF_RECEIVE_TIMEOUT in config:
 | 
			
		||||
        cg.add(var.set_status_timeout(config[CONF_RECEIVE_TIMEOUT]))
 | 
			
		||||
    if (receive_timeout := config.get(CONF_RECEIVE_TIMEOUT)) is not None:
 | 
			
		||||
        cg.add(var.set_status_timeout(receive_timeout))
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#include "bedjet_hub.h"
 | 
			
		||||
#include "bedjet_child.h"
 | 
			
		||||
#include "bedjet_const.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bedjet {
 | 
			
		||||
@@ -373,7 +374,7 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
 | 
			
		||||
      if (this->last_notify_ == 0 || delta > MIN_NOTIFY_THROTTLE || this->force_refresh_) {
 | 
			
		||||
        // Set reentrant flag to prevent processing multiple packets.
 | 
			
		||||
        this->processing_ = true;
 | 
			
		||||
        ESP_LOGVV(TAG, "[%s] Decoding packet: last=%d, delta=%d, force=%s", this->get_name().c_str(),
 | 
			
		||||
        ESP_LOGVV(TAG, "[%s] Decoding packet: last=%" PRId32 ", delta=%" PRId32 ", force=%s", this->get_name().c_str(),
 | 
			
		||||
                  this->last_notify_, delta, this->force_refresh_ ? "y" : "n");
 | 
			
		||||
        bool needs_extra = this->codec_->decode_notify(param->notify.value, param->notify.value_len);
 | 
			
		||||
 | 
			
		||||
@@ -523,11 +524,11 @@ void BedJetHub::dispatch_status_() {
 | 
			
		||||
 | 
			
		||||
      ESP_LOGI(TAG, "[%s] Still waiting for first GATT notify event.", this->get_name().c_str());
 | 
			
		||||
    } else if (diff > NOTIFY_WARN_THRESHOLD) {
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] Last GATT notify was %d seconds ago.", this->get_name().c_str(), diff / 1000);
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] Last GATT notify was %" PRId32 " seconds ago.", this->get_name().c_str(), diff / 1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this->timeout_ > 0 && diff > this->timeout_ && this->parent()->enabled) {
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] Timed out after %d sec. Retrying...", this->get_name().c_str(), this->timeout_);
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] Timed out after %" PRId32 " sec. Retrying...", this->get_name().c_str(), this->timeout_);
 | 
			
		||||
      // set_enabled(false) will only close the connection if state != IDLE.
 | 
			
		||||
      this->parent()->set_state(espbt::ClientState::CONNECTING);
 | 
			
		||||
      this->parent()->set_enabled(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,10 +29,10 @@ async def to_code(config):
 | 
			
		||||
    output_ = await cg.get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    cg.add(var.set_output(output_))
 | 
			
		||||
 | 
			
		||||
    if CONF_OSCILLATION_OUTPUT in config:
 | 
			
		||||
        oscillation_output = await cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
 | 
			
		||||
    if oscillation_output_id := config.get(CONF_OSCILLATION_OUTPUT):
 | 
			
		||||
        oscillation_output = await cg.get_variable(oscillation_output_id)
 | 
			
		||||
        cg.add(var.set_oscillating(oscillation_output))
 | 
			
		||||
 | 
			
		||||
    if CONF_DIRECTION_OUTPUT in config:
 | 
			
		||||
        direction_output = await cg.get_variable(config[CONF_DIRECTION_OUTPUT])
 | 
			
		||||
    if direction_output_id := config.get(CONF_DIRECTION_OUTPUT):
 | 
			
		||||
        direction_output = await cg.get_variable(direction_output_id)
 | 
			
		||||
        cg.add(var.set_direction(direction_output))
 | 
			
		||||
 
 | 
			
		||||
@@ -467,14 +467,14 @@ def binary_sensor_schema(
 | 
			
		||||
async def setup_binary_sensor_core_(var, config):
 | 
			
		||||
    await setup_entity(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_DEVICE_CLASS in config:
 | 
			
		||||
        cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
 | 
			
		||||
    if CONF_PUBLISH_INITIAL_STATE in config:
 | 
			
		||||
        cg.add(var.set_publish_initial_state(config[CONF_PUBLISH_INITIAL_STATE]))
 | 
			
		||||
    if CONF_INVERTED in config:
 | 
			
		||||
        cg.add(var.set_inverted(config[CONF_INVERTED]))
 | 
			
		||||
    if CONF_FILTERS in config:
 | 
			
		||||
        filters = await cg.build_registry_list(FILTER_REGISTRY, config[CONF_FILTERS])
 | 
			
		||||
    if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
 | 
			
		||||
        cg.add(var.set_device_class(device_class))
 | 
			
		||||
    if publish_initial_state := config.get(CONF_PUBLISH_INITIAL_STATE):
 | 
			
		||||
        cg.add(var.set_publish_initial_state(publish_initial_state))
 | 
			
		||||
    if inverted := config.get(CONF_INVERTED):
 | 
			
		||||
        cg.add(var.set_inverted(inverted))
 | 
			
		||||
    if filters_config := config.get(CONF_FILTERS):
 | 
			
		||||
        filters = await cg.build_registry_list(FILTER_REGISTRY, filters_config)
 | 
			
		||||
        cg.add(var.add_filters(filters))
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_PRESS, []):
 | 
			
		||||
@@ -518,8 +518,8 @@ async def setup_binary_sensor_core_(var, config):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [(bool, "x")], conf)
 | 
			
		||||
 | 
			
		||||
    if CONF_MQTT_ID in config:
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
 | 
			
		||||
    if mqtt_id := config.get(CONF_MQTT_ID):
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -93,35 +93,27 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await uart.register_uart_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_VOLTAGE in config:
 | 
			
		||||
        conf = config[CONF_VOLTAGE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if voltage_config := config.get(CONF_VOLTAGE):
 | 
			
		||||
        sens = await sensor.new_sensor(voltage_config)
 | 
			
		||||
        cg.add(var.set_voltage_sensor(sens))
 | 
			
		||||
    if CONF_CURRENT_1 in config:
 | 
			
		||||
        conf = config[CONF_CURRENT_1]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if current_1_config := config.get(CONF_CURRENT_1):
 | 
			
		||||
        sens = await sensor.new_sensor(current_1_config)
 | 
			
		||||
        cg.add(var.set_current_sensor_1(sens))
 | 
			
		||||
    if CONF_CURRENT_2 in config:
 | 
			
		||||
        conf = config[CONF_CURRENT_2]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if current_2_config := config.get(CONF_CURRENT_2):
 | 
			
		||||
        sens = await sensor.new_sensor(current_2_config)
 | 
			
		||||
        cg.add(var.set_current_sensor_2(sens))
 | 
			
		||||
    if CONF_ACTIVE_POWER_1 in config:
 | 
			
		||||
        conf = config[CONF_ACTIVE_POWER_1]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if active_power_1_config := config.get(CONF_ACTIVE_POWER_1):
 | 
			
		||||
        sens = await sensor.new_sensor(active_power_1_config)
 | 
			
		||||
        cg.add(var.set_power_sensor_1(sens))
 | 
			
		||||
    if CONF_ACTIVE_POWER_2 in config:
 | 
			
		||||
        conf = config[CONF_ACTIVE_POWER_2]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if active_power_2_config := config.get(CONF_ACTIVE_POWER_2):
 | 
			
		||||
        sens = await sensor.new_sensor(active_power_2_config)
 | 
			
		||||
        cg.add(var.set_power_sensor_2(sens))
 | 
			
		||||
    if CONF_ENERGY_1 in config:
 | 
			
		||||
        conf = config[CONF_ENERGY_1]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if energy_1_config := config.get(CONF_ENERGY_1):
 | 
			
		||||
        sens = await sensor.new_sensor(energy_1_config)
 | 
			
		||||
        cg.add(var.set_energy_sensor_1(sens))
 | 
			
		||||
    if CONF_ENERGY_2 in config:
 | 
			
		||||
        conf = config[CONF_ENERGY_2]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if energy_2_config := config.get(CONF_ENERGY_2):
 | 
			
		||||
        sens = await sensor.new_sensor(energy_2_config)
 | 
			
		||||
        cg.add(var.set_energy_sensor_2(sens))
 | 
			
		||||
    if CONF_ENERGY_TOTAL in config:
 | 
			
		||||
        conf = config[CONF_ENERGY_TOTAL]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if energy_total_config := config.get(CONF_ENERGY_TOTAL):
 | 
			
		||||
        sens = await sensor.new_sensor(energy_total_config)
 | 
			
		||||
        cg.add(var.set_energy_sensor_sum(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -79,27 +79,21 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await uart.register_uart_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_VOLTAGE in config:
 | 
			
		||||
        conf = config[CONF_VOLTAGE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if voltage_config := config.get(CONF_VOLTAGE):
 | 
			
		||||
        sens = await sensor.new_sensor(voltage_config)
 | 
			
		||||
        cg.add(var.set_voltage_sensor(sens))
 | 
			
		||||
    if CONF_CURRENT in config:
 | 
			
		||||
        conf = config[CONF_CURRENT]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if current_config := config.get(CONF_CURRENT):
 | 
			
		||||
        sens = await sensor.new_sensor(current_config)
 | 
			
		||||
        cg.add(var.set_current_sensor(sens))
 | 
			
		||||
    if CONF_POWER in config:
 | 
			
		||||
        conf = config[CONF_POWER]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if power_config := config.get(CONF_POWER):
 | 
			
		||||
        sens = await sensor.new_sensor(power_config)
 | 
			
		||||
        cg.add(var.set_power_sensor(sens))
 | 
			
		||||
    if CONF_ENERGY in config:
 | 
			
		||||
        conf = config[CONF_ENERGY]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if energy_config := config.get(CONF_ENERGY):
 | 
			
		||||
        sens = await sensor.new_sensor(energy_config)
 | 
			
		||||
        cg.add(var.set_energy_sensor(sens))
 | 
			
		||||
    if CONF_INTERNAL_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_INTERNAL_TEMPERATURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if internal_temperature_config := config.get(CONF_INTERNAL_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(internal_temperature_config)
 | 
			
		||||
        cg.add(var.set_internal_temperature_sensor(sens))
 | 
			
		||||
    if CONF_EXTERNAL_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_EXTERNAL_TEMPERATURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if external_temperature_config := config.get(CONF_EXTERNAL_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(external_temperature_config)
 | 
			
		||||
        cg.add(var.set_external_temperature_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -71,23 +71,18 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await uart.register_uart_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_VOLTAGE in config:
 | 
			
		||||
        conf = config[CONF_VOLTAGE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if voltage_config := config.get(CONF_VOLTAGE):
 | 
			
		||||
        sens = await sensor.new_sensor(voltage_config)
 | 
			
		||||
        cg.add(var.set_voltage_sensor(sens))
 | 
			
		||||
    if CONF_CURRENT in config:
 | 
			
		||||
        conf = config[CONF_CURRENT]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if current_config := config.get(CONF_CURRENT):
 | 
			
		||||
        sens = await sensor.new_sensor(current_config)
 | 
			
		||||
        cg.add(var.set_current_sensor(sens))
 | 
			
		||||
    if CONF_POWER in config:
 | 
			
		||||
        conf = config[CONF_POWER]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if power_config := config.get(CONF_POWER):
 | 
			
		||||
        sens = await sensor.new_sensor(power_config)
 | 
			
		||||
        cg.add(var.set_power_sensor(sens))
 | 
			
		||||
    if CONF_ENERGY in config:
 | 
			
		||||
        conf = config[CONF_ENERGY]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if energy_config := config.get(CONF_ENERGY):
 | 
			
		||||
        sens = await sensor.new_sensor(energy_config)
 | 
			
		||||
        cg.add(var.set_energy_sensor(sens))
 | 
			
		||||
    if CONF_FREQUENCY in config:
 | 
			
		||||
        conf = config[CONF_FREQUENCY]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if frequency_config := config.get(CONF_FREQUENCY):
 | 
			
		||||
        sens = await sensor.new_sensor(frequency_config)
 | 
			
		||||
        cg.add(var.set_frequency_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -129,32 +129,18 @@ async def characteristic_sensor_to_code(config):
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_char_uuid128(uuid128))
 | 
			
		||||
 | 
			
		||||
    if CONF_DESCRIPTOR_UUID in config:
 | 
			
		||||
        if len(config[CONF_DESCRIPTOR_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
            cg.add(
 | 
			
		||||
                var.set_descr_uuid16(
 | 
			
		||||
                    esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        elif len(config[CONF_DESCRIPTOR_UUID]) == len(
 | 
			
		||||
            esp32_ble_tracker.bt_uuid32_format
 | 
			
		||||
        ):
 | 
			
		||||
            cg.add(
 | 
			
		||||
                var.set_descr_uuid32(
 | 
			
		||||
                    esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        elif len(config[CONF_DESCRIPTOR_UUID]) == len(
 | 
			
		||||
            esp32_ble_tracker.bt_uuid128_format
 | 
			
		||||
        ):
 | 
			
		||||
            uuid128 = esp32_ble_tracker.as_reversed_hex_array(
 | 
			
		||||
                config[CONF_DESCRIPTOR_UUID]
 | 
			
		||||
            )
 | 
			
		||||
    if descriptor_uuid := config.get(CONF_DESCRIPTOR_UUID):
 | 
			
		||||
        if len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
            cg.add(var.set_descr_uuid16(esp32_ble_tracker.as_hex(descriptor_uuid)))
 | 
			
		||||
        elif len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid32_format):
 | 
			
		||||
            cg.add(var.set_descr_uuid32(esp32_ble_tracker.as_hex(descriptor_uuid)))
 | 
			
		||||
        elif len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid128_format):
 | 
			
		||||
            uuid128 = esp32_ble_tracker.as_reversed_hex_array(descriptor_uuid)
 | 
			
		||||
            cg.add(var.set_descr_uuid128(uuid128))
 | 
			
		||||
 | 
			
		||||
    if CONF_LAMBDA in config:
 | 
			
		||||
    if lambda_config := config.get(CONF_LAMBDA):
 | 
			
		||||
        lambda_ = await cg.process_lambda(
 | 
			
		||||
            config[CONF_LAMBDA], [(adv_data_t_const_ref, "x")], return_type=cg.float_
 | 
			
		||||
            lambda_config, [(adv_data_t_const_ref, "x")], return_type=cg.float_
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_data_to_value(lambda_))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -88,27 +88,13 @@ async def to_code(config):
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_char_uuid128(uuid128))
 | 
			
		||||
 | 
			
		||||
    if CONF_DESCRIPTOR_UUID in config:
 | 
			
		||||
        if len(config[CONF_DESCRIPTOR_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
            cg.add(
 | 
			
		||||
                var.set_descr_uuid16(
 | 
			
		||||
                    esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        elif len(config[CONF_DESCRIPTOR_UUID]) == len(
 | 
			
		||||
            esp32_ble_tracker.bt_uuid32_format
 | 
			
		||||
        ):
 | 
			
		||||
            cg.add(
 | 
			
		||||
                var.set_descr_uuid32(
 | 
			
		||||
                    esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        elif len(config[CONF_DESCRIPTOR_UUID]) == len(
 | 
			
		||||
            esp32_ble_tracker.bt_uuid128_format
 | 
			
		||||
        ):
 | 
			
		||||
            uuid128 = esp32_ble_tracker.as_reversed_hex_array(
 | 
			
		||||
                config[CONF_DESCRIPTOR_UUID]
 | 
			
		||||
            )
 | 
			
		||||
    if descriptor_uuid := config:
 | 
			
		||||
        if len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
            cg.add(var.set_descr_uuid16(esp32_ble_tracker.as_hex(descriptor_uuid)))
 | 
			
		||||
        elif len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid32_format):
 | 
			
		||||
            cg.add(var.set_descr_uuid32(esp32_ble_tracker.as_hex(descriptor_uuid)))
 | 
			
		||||
        elif len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid128_format):
 | 
			
		||||
            uuid128 = esp32_ble_tracker.as_reversed_hex_array(descriptor_uuid)
 | 
			
		||||
            cg.add(var.set_descr_uuid128(uuid128))
 | 
			
		||||
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t,
 | 
			
		||||
            cv.Optional(CONF_IBEACON_UUID): cv.uuid,
 | 
			
		||||
            cv.Optional(CONF_MIN_RSSI): cv.All(
 | 
			
		||||
                cv.decibel, cv.int_range(min=-90, max=-30)
 | 
			
		||||
                cv.decibel, cv.int_range(min=-100, max=-30)
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
@@ -55,35 +55,27 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_MIN_RSSI in config:
 | 
			
		||||
        cg.add(var.set_minimum_rssi(config[CONF_MIN_RSSI]))
 | 
			
		||||
    if min_rssi := config.get(CONF_MIN_RSSI):
 | 
			
		||||
        cg.add(var.set_minimum_rssi(min_rssi))
 | 
			
		||||
 | 
			
		||||
    if CONF_MAC_ADDRESS in config:
 | 
			
		||||
        cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
 | 
			
		||||
    if mac_address := config.get(CONF_MAC_ADDRESS):
 | 
			
		||||
        cg.add(var.set_address(mac_address.as_hex))
 | 
			
		||||
 | 
			
		||||
    if CONF_SERVICE_UUID in config:
 | 
			
		||||
        if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
            cg.add(
 | 
			
		||||
                var.set_service_uuid16(
 | 
			
		||||
                    esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
 | 
			
		||||
            cg.add(
 | 
			
		||||
                var.set_service_uuid32(
 | 
			
		||||
                    esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
 | 
			
		||||
            uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
 | 
			
		||||
    if service_uuid := config.get(CONF_SERVICE_UUID):
 | 
			
		||||
        if len(service_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
            cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(service_uuid)))
 | 
			
		||||
        elif len(service_uuid) == len(esp32_ble_tracker.bt_uuid32_format):
 | 
			
		||||
            cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(service_uuid)))
 | 
			
		||||
        elif len(service_uuid) == len(esp32_ble_tracker.bt_uuid128_format):
 | 
			
		||||
            uuid128 = esp32_ble_tracker.as_reversed_hex_array(service_uuid)
 | 
			
		||||
            cg.add(var.set_service_uuid128(uuid128))
 | 
			
		||||
 | 
			
		||||
    if CONF_IBEACON_UUID in config:
 | 
			
		||||
        ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(config[CONF_IBEACON_UUID]))
 | 
			
		||||
    if ibeacon_uuid := config.get(CONF_IBEACON_UUID):
 | 
			
		||||
        ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(ibeacon_uuid))
 | 
			
		||||
        cg.add(var.set_ibeacon_uuid(ibeacon_uuid))
 | 
			
		||||
 | 
			
		||||
        if CONF_IBEACON_MAJOR in config:
 | 
			
		||||
            cg.add(var.set_ibeacon_major(config[CONF_IBEACON_MAJOR]))
 | 
			
		||||
        if (ibeacon_major := config.get(CONF_IBEACON_MAJOR)) is not None:
 | 
			
		||||
            cg.add(var.set_ibeacon_major(ibeacon_major))
 | 
			
		||||
 | 
			
		||||
        if CONF_IBEACON_MINOR in config:
 | 
			
		||||
            cg.add(var.set_ibeacon_minor(config[CONF_IBEACON_MINOR]))
 | 
			
		||||
        if (ibeacon_minor := config.get(CONF_IBEACON_MINOR)) is not None:
 | 
			
		||||
            cg.add(var.set_ibeacon_minor(ibeacon_minor))
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
 | 
			
		||||
    this->found_ = false;
 | 
			
		||||
  }
 | 
			
		||||
  bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
 | 
			
		||||
    if (this->check_minimum_rssi_ && this->minimum_rssi_ <= device.get_rssi()) {
 | 
			
		||||
    if (this->check_minimum_rssi_ && this->minimum_rssi_ > device.get_rssi()) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    switch (this->match_by_) {
 | 
			
		||||
 
 | 
			
		||||
@@ -57,32 +57,24 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_MAC_ADDRESS in config:
 | 
			
		||||
        cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
 | 
			
		||||
    if mac_address := config.get(CONF_MAC_ADDRESS):
 | 
			
		||||
        cg.add(var.set_address(mac_address.as_hex))
 | 
			
		||||
 | 
			
		||||
    if CONF_SERVICE_UUID in config:
 | 
			
		||||
        if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
            cg.add(
 | 
			
		||||
                var.set_service_uuid16(
 | 
			
		||||
                    esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
 | 
			
		||||
            cg.add(
 | 
			
		||||
                var.set_service_uuid32(
 | 
			
		||||
                    esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
 | 
			
		||||
            uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
 | 
			
		||||
    if service_uuid := config.get(CONF_SERVICE_UUID):
 | 
			
		||||
        if len(service_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
            cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(service_uuid)))
 | 
			
		||||
        elif len(service_uuid) == len(esp32_ble_tracker.bt_uuid32_format):
 | 
			
		||||
            cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(service_uuid)))
 | 
			
		||||
        elif len(service_uuid) == len(esp32_ble_tracker.bt_uuid128_format):
 | 
			
		||||
            uuid128 = esp32_ble_tracker.as_reversed_hex_array(service_uuid)
 | 
			
		||||
            cg.add(var.set_service_uuid128(uuid128))
 | 
			
		||||
 | 
			
		||||
    if CONF_IBEACON_UUID in config:
 | 
			
		||||
        ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(config[CONF_IBEACON_UUID]))
 | 
			
		||||
    if ibeacon_uuid := config.get(CONF_IBEACON_UUID):
 | 
			
		||||
        ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(ibeacon_uuid))
 | 
			
		||||
        cg.add(var.set_ibeacon_uuid(ibeacon_uuid))
 | 
			
		||||
 | 
			
		||||
        if CONF_IBEACON_MAJOR in config:
 | 
			
		||||
            cg.add(var.set_ibeacon_major(config[CONF_IBEACON_MAJOR]))
 | 
			
		||||
        if (ibeacon_major := config.get(CONF_IBEACON_MAJOR)) is not None:
 | 
			
		||||
            cg.add(var.set_ibeacon_major(ibeacon_major))
 | 
			
		||||
 | 
			
		||||
        if CONF_IBEACON_MINOR in config:
 | 
			
		||||
            cg.add(var.set_ibeacon_minor(config[CONF_IBEACON_MINOR]))
 | 
			
		||||
        if (ibeacon_minor := config.get(CONF_IBEACON_MINOR)) is not None:
 | 
			
		||||
            cg.add(var.set_ibeacon_minor(ibeacon_minor))
 | 
			
		||||
 
 | 
			
		||||
@@ -98,22 +98,19 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_TEMPERATURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if temperature_config := config.get(CONF_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(temperature_config)
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
        cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
        cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_PRESSURE in config:
 | 
			
		||||
        conf = config[CONF_PRESSURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if pressure_config := config.get(CONF_PRESSURE):
 | 
			
		||||
        sens = await sensor.new_sensor(pressure_config)
 | 
			
		||||
        cg.add(var.set_pressure_sensor(sens))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        conf = config[CONF_HUMIDITY]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if humidity_config := config.get(CONF_HUMIDITY):
 | 
			
		||||
        sens = await sensor.new_sensor(humidity_config)
 | 
			
		||||
        cg.add(var.set_humidity_sensor(sens))
 | 
			
		||||
        cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
        cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_iir_filter(config[CONF_IIR_FILTER]))
 | 
			
		||||
 
 | 
			
		||||
@@ -130,27 +130,23 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_TEMPERATURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if temperature_config := config.get(CONF_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(temperature_config)
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
        cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
        cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_PRESSURE in config:
 | 
			
		||||
        conf = config[CONF_PRESSURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if pressure_config := config.get(CONF_PRESSURE):
 | 
			
		||||
        sens = await sensor.new_sensor(pressure_config)
 | 
			
		||||
        cg.add(var.set_pressure_sensor(sens))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        conf = config[CONF_HUMIDITY]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if humidity_config := config.get(CONF_HUMIDITY):
 | 
			
		||||
        sens = await sensor.new_sensor(humidity_config)
 | 
			
		||||
        cg.add(var.set_humidity_sensor(sens))
 | 
			
		||||
        cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
        cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_GAS_RESISTANCE in config:
 | 
			
		||||
        conf = config[CONF_GAS_RESISTANCE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if gas_resistance_config := config.get(CONF_GAS_RESISTANCE):
 | 
			
		||||
        sens = await sensor.new_sensor(gas_resistance_config)
 | 
			
		||||
        cg.add(var.set_gas_resistance_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_iir_filter(IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]))
 | 
			
		||||
 
 | 
			
		||||
@@ -108,12 +108,13 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def setup_conf(config, key, hub):
 | 
			
		||||
    if key in config:
 | 
			
		||||
        conf = config[key]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if sensor_config := config.get(key):
 | 
			
		||||
        sens = await sensor.new_sensor(sensor_config)
 | 
			
		||||
        cg.add(getattr(hub, f"set_{key}_sensor")(sens))
 | 
			
		||||
        if CONF_SAMPLE_RATE in conf:
 | 
			
		||||
            cg.add(getattr(hub, f"set_{key}_sample_rate")(conf[CONF_SAMPLE_RATE]))
 | 
			
		||||
        if CONF_SAMPLE_RATE in sensor_config:
 | 
			
		||||
            cg.add(
 | 
			
		||||
                getattr(hub, f"set_{key}_sample_rate")(sensor_config[CONF_SAMPLE_RATE])
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
 
 | 
			
		||||
@@ -21,9 +21,8 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def setup_conf(config, key, hub):
 | 
			
		||||
    if key in config:
 | 
			
		||||
        conf = config[key]
 | 
			
		||||
        sens = await text_sensor.new_text_sensor(conf)
 | 
			
		||||
    if sensor_config := config.get(key):
 | 
			
		||||
        sens = await text_sensor.new_text_sensor(sensor_config)
 | 
			
		||||
        cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -47,12 +47,10 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_TEMPERATURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if temperature_config := config.get(CONF_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(temperature_config)
 | 
			
		||||
        cg.add(var.set_temperature(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_PRESSURE in config:
 | 
			
		||||
        conf = config[CONF_PRESSURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if pressure_config := config.get(CONF_PRESSURE):
 | 
			
		||||
        sens = await sensor.new_sensor(pressure_config)
 | 
			
		||||
        cg.add(var.set_pressure(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -83,16 +83,14 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_TEMPERATURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if temperature_config := config.get(CONF_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(temperature_config)
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
        cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
        cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_PRESSURE in config:
 | 
			
		||||
        conf = config[CONF_PRESSURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if pressure_config := config.get(CONF_PRESSURE):
 | 
			
		||||
        sens = await sensor.new_sensor(pressure_config)
 | 
			
		||||
        cg.add(var.set_pressure_sensor(sens))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_iir_filter(config[CONF_IIR_FILTER]))
 | 
			
		||||
 
 | 
			
		||||
@@ -87,14 +87,16 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
    cg.add(var.set_iir_filter_config(config[CONF_IIR_FILTER]))
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_TEMPERATURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if temperature_config := config.get(CONF_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(temperature_config)
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
        cg.add(var.set_temperature_oversampling_config(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_temperature_oversampling_config(
 | 
			
		||||
                temperature_config[CONF_OVERSAMPLING]
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if CONF_PRESSURE in config:
 | 
			
		||||
        conf = config[CONF_PRESSURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if pressure_config := config.get(CONF_PRESSURE):
 | 
			
		||||
        sens = await sensor.new_sensor(pressure_config)
 | 
			
		||||
        cg.add(var.set_pressure_sensor(sens))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling_config(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling_config(pressure_config[CONF_OVERSAMPLING]))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/bmp581/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/bmp581/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										596
									
								
								esphome/components/bmp581/bmp581.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										596
									
								
								esphome/components/bmp581/bmp581.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,596 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Adds support for Bosch's BMP581 high accuracy pressure and temperature sensor
 | 
			
		||||
 *  - Component structure based on ESPHome's BMP3XX component (as of March, 2023)
 | 
			
		||||
 *    - Implementation is easier as the sensor itself automatically compensates pressure for the temperature
 | 
			
		||||
 *      - Temperature and pressure data is converted via simple divison operations in this component
 | 
			
		||||
 *    - IIR filter level can independently be applied to temperature and pressure measurements
 | 
			
		||||
 *  - Bosch's BMP5-Sensor-API was consulted to verify that sensor configuration is done correctly
 | 
			
		||||
 *    - Copyright (c) 2022 Bosch Sensortec Gmbh, SPDX-License-Identifier: BSD-3-Clause
 | 
			
		||||
 *  - This component uses forced power mode only so measurements are synchronized by the host
 | 
			
		||||
 *  - All datasheet page references refer to Bosch Document Number BST-BMP581-DS004-04 (revision number 1.4)
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "bmp581.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bmp581 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "bmp581";
 | 
			
		||||
 | 
			
		||||
static const LogString *oversampling_to_str(Oversampling oversampling) {
 | 
			
		||||
  switch (oversampling) {
 | 
			
		||||
    case Oversampling::OVERSAMPLING_NONE:
 | 
			
		||||
      return LOG_STR("None");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X2:
 | 
			
		||||
      return LOG_STR("2x");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X4:
 | 
			
		||||
      return LOG_STR("4x");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X8:
 | 
			
		||||
      return LOG_STR("8x");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X16:
 | 
			
		||||
      return LOG_STR("16x");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X32:
 | 
			
		||||
      return LOG_STR("32x");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X64:
 | 
			
		||||
      return LOG_STR("64x");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X128:
 | 
			
		||||
      return LOG_STR("128x");
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const LogString *iir_filter_to_str(IIRFilter filter) {
 | 
			
		||||
  switch (filter) {
 | 
			
		||||
    case IIRFilter::IIR_FILTER_OFF:
 | 
			
		||||
      return LOG_STR("OFF");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_2:
 | 
			
		||||
      return LOG_STR("2x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_4:
 | 
			
		||||
      return LOG_STR("4x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_8:
 | 
			
		||||
      return LOG_STR("8x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_16:
 | 
			
		||||
      return LOG_STR("16x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_32:
 | 
			
		||||
      return LOG_STR("32x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_64:
 | 
			
		||||
      return LOG_STR("64x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_128:
 | 
			
		||||
      return LOG_STR("128x");
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BMP581Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BMP581:");
 | 
			
		||||
 | 
			
		||||
  switch (this->error_code_) {
 | 
			
		||||
    case NONE:
 | 
			
		||||
      break;
 | 
			
		||||
    case ERROR_COMMUNICATION_FAILED:
 | 
			
		||||
      ESP_LOGE(TAG, "  Communication with BMP581 failed!");
 | 
			
		||||
      break;
 | 
			
		||||
    case ERROR_WRONG_CHIP_ID:
 | 
			
		||||
      ESP_LOGE(TAG, "  BMP581 has wrong chip ID - please verify you are using a BMP 581");
 | 
			
		||||
      break;
 | 
			
		||||
    case ERROR_SENSOR_RESET:
 | 
			
		||||
      ESP_LOGE(TAG, "  BMP581 failed to reset");
 | 
			
		||||
      break;
 | 
			
		||||
    case ERROR_SENSOR_STATUS:
 | 
			
		||||
      ESP_LOGE(TAG, "  BMP581 sensor status failed, there were NVM problems");
 | 
			
		||||
      break;
 | 
			
		||||
    case ERROR_PRIME_IIR_FAILED:
 | 
			
		||||
      ESP_LOGE(TAG, "  BMP581's IIR Filter failed to prime with an initial measurement");
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      ESP_LOGE(TAG, "  BMP581 error code %d", (int) this->error_code_);
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Measurement conversion time: %ums", this->conversion_time_);
 | 
			
		||||
 | 
			
		||||
  if (this->temperature_sensor_) {
 | 
			
		||||
    LOG_SENSOR("  ", "Temperature", this->temperature_sensor_);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_temperature_level_)));
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->pressure_sensor_) {
 | 
			
		||||
    LOG_SENSOR("  ", "Pressure", this->pressure_sensor_);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_pressure_level_)));
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BMP581Component::setup() {
 | 
			
		||||
  /*
 | 
			
		||||
   * Setup goes through several stages, which follows the post-power-up procedure (page 18 of datasheet) and then sets
 | 
			
		||||
   * configured options
 | 
			
		||||
   *  1) Soft reboot
 | 
			
		||||
   *  2) Verify ASIC chip ID matches BMP581
 | 
			
		||||
   *  3) Verify sensor status (check if NVM is okay)
 | 
			
		||||
   *  4) Enable data ready interrupt
 | 
			
		||||
   *  5) Write oversampling settings and set internal configuration values
 | 
			
		||||
   *  6) Configure and prime IIR Filter(s), if enabled
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  this->error_code_ = NONE;
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up BMP581...");
 | 
			
		||||
 | 
			
		||||
  ////////////////////
 | 
			
		||||
  // 1) Soft reboot //
 | 
			
		||||
  ////////////////////
 | 
			
		||||
 | 
			
		||||
  // Power-On-Reboot bit is asserted if sensor successfully reset
 | 
			
		||||
  if (!this->reset_()) {
 | 
			
		||||
    ESP_LOGE(TAG, "BMP581 failed to reset");
 | 
			
		||||
 | 
			
		||||
    this->error_code_ = ERROR_SENSOR_RESET;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ///////////////////////////////////////////
 | 
			
		||||
  // 2) Verify ASIC chip ID matches BMP581 //
 | 
			
		||||
  ///////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
  uint8_t chip_id;
 | 
			
		||||
 | 
			
		||||
  // read chip id from sensor
 | 
			
		||||
  if (!this->read_byte(BMP581_CHIP_ID, &chip_id)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to read chip id");
 | 
			
		||||
 | 
			
		||||
    this->error_code_ = ERROR_COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // verify id
 | 
			
		||||
  if (chip_id != BMP581_ASIC_ID) {
 | 
			
		||||
    ESP_LOGE(TAG, "Unknown chip ID, is this a BMP581?");
 | 
			
		||||
 | 
			
		||||
    this->error_code_ = ERROR_WRONG_CHIP_ID;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ////////////////////////////////////////////////////
 | 
			
		||||
  // 3) Verify sensor status (check if NVM is okay) //
 | 
			
		||||
  ////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
  if (!this->read_byte(BMP581_STATUS, &this->status_.reg)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to read status register");
 | 
			
		||||
 | 
			
		||||
    this->error_code_ = ERROR_COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // verify status_nvm_rdy bit (it is asserted if boot was successful)
 | 
			
		||||
  if (!(this->status_.bit.status_nvm_rdy)) {
 | 
			
		||||
    ESP_LOGE(TAG, "NVM not ready after boot");
 | 
			
		||||
 | 
			
		||||
    this->error_code_ = ERROR_SENSOR_STATUS;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // verify status_nvm_err bit (it is asserted if an error is detected)
 | 
			
		||||
  if (this->status_.bit.status_nvm_err) {
 | 
			
		||||
    ESP_LOGE(TAG, "NVM error detected on boot");
 | 
			
		||||
 | 
			
		||||
    this->error_code_ = ERROR_SENSOR_STATUS;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ////////////////////////////////////
 | 
			
		||||
  // 4) Enable data ready interrupt //
 | 
			
		||||
  ////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
  // enable the data ready interrupt source
 | 
			
		||||
  if (!this->write_interrupt_source_settings_(true)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to write interrupt source register");
 | 
			
		||||
 | 
			
		||||
    this->error_code_ = ERROR_COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //////////////////////////////////////////////////////////////////////////
 | 
			
		||||
  // 5) Write oversampling settings and set internal configuration values //
 | 
			
		||||
  //////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
  // configure pressure readings, if sensor is defined
 | 
			
		||||
  // otherwise, disable pressure oversampling
 | 
			
		||||
  if (this->pressure_sensor_) {
 | 
			
		||||
    this->osr_config_.bit.press_en = true;
 | 
			
		||||
  } else {
 | 
			
		||||
    this->pressure_oversampling_ = OVERSAMPLING_NONE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // write oversampling settings
 | 
			
		||||
  if (!this->write_oversampling_settings_(this->temperature_oversampling_, this->pressure_oversampling_)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to write oversampling register");
 | 
			
		||||
 | 
			
		||||
    this->error_code_ = ERROR_COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // set output data rate to 4 Hz=0x19 (page 65 of datasheet)
 | 
			
		||||
  //  - ?shouldn't? matter as this component only uses FORCED_MODE - datasheet is ambiguous
 | 
			
		||||
  //  - If in NORMAL_MODE or NONSTOP_MODE, then this would still allow deep standby to save power
 | 
			
		||||
  //  - will be written to BMP581 at next requested measurement
 | 
			
		||||
  this->odr_config_.bit.odr = 0x19;
 | 
			
		||||
 | 
			
		||||
  ///////////////////////////////////////////////////////
 | 
			
		||||
  /// 6) Configure and prime IIR Filter(s), if enabled //
 | 
			
		||||
  ///////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
  if ((this->iir_temperature_level_ != IIR_FILTER_OFF) || (this->iir_pressure_level_ != IIR_FILTER_OFF)) {
 | 
			
		||||
    if (!this->write_iir_settings_(this->iir_temperature_level_, this->iir_pressure_level_)) {
 | 
			
		||||
      ESP_LOGE(TAG, "Failed to write IIR configuration registers");
 | 
			
		||||
 | 
			
		||||
      this->error_code_ = ERROR_COMMUNICATION_FAILED;
 | 
			
		||||
      this->mark_failed();
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!this->prime_iir_filter_()) {
 | 
			
		||||
      ESP_LOGE(TAG, "Failed to prime the IIR filter with an intiial measurement");
 | 
			
		||||
 | 
			
		||||
      this->error_code_ = ERROR_PRIME_IIR_FAILED;
 | 
			
		||||
      this->mark_failed();
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BMP581Component::update() {
 | 
			
		||||
  /*
 | 
			
		||||
   * Each update goes through several stages
 | 
			
		||||
   *  0) Verify either a temperature or pressure sensor is defined before proceeding
 | 
			
		||||
   *  1) Request a measurement
 | 
			
		||||
   *  2) Wait for measurement to finish (based on oversampling rates)
 | 
			
		||||
   *  3) Read data registers for temperature and pressure, if applicable
 | 
			
		||||
   *  4) Publish measurements to sensor(s), if applicable
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  ////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
  // 0) Verify either a temperature or pressure sensor is defined before proceeding //
 | 
			
		||||
  ////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
  if ((!this->temperature_sensor_) && (!this->pressure_sensor_)) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //////////////////////////////
 | 
			
		||||
  // 1) Request a measurement //
 | 
			
		||||
  //////////////////////////////
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, "Requesting a measurement from sensor");
 | 
			
		||||
 | 
			
		||||
  if (!this->start_measurement_()) {
 | 
			
		||||
    ESP_LOGW(TAG, "Failed to request forced measurement of sensor");
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //////////////////////////////////////////////////////////////////////
 | 
			
		||||
  // 2) Wait for measurement to finish (based on oversampling rates) //
 | 
			
		||||
  //////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, "Measurement is expected to take %d ms to complete", this->conversion_time_);
 | 
			
		||||
 | 
			
		||||
  this->set_timeout("measurement", this->conversion_time_, [this]() {
 | 
			
		||||
    float temperature = 0.0;
 | 
			
		||||
    float pressure = 0.0;
 | 
			
		||||
 | 
			
		||||
    ////////////////////////////////////////////////////////////////////////
 | 
			
		||||
    // 3) Read data registers for temperature and pressure, if applicable //
 | 
			
		||||
    ////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    if (this->pressure_sensor_) {
 | 
			
		||||
      if (!this->read_temperature_and_pressure_(temperature, pressure)) {
 | 
			
		||||
        ESP_LOGW(TAG, "Failed to read temperature and pressure measurements, skipping update");
 | 
			
		||||
        this->status_set_warning();
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      if (!this->read_temperature_(temperature)) {
 | 
			
		||||
        ESP_LOGW(TAG, "Failed to read temperature measurement, skipping update");
 | 
			
		||||
        this->status_set_warning();
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /////////////////////////////////////////////////////////
 | 
			
		||||
    // 4) Publish measurements to sensor(s), if applicable //
 | 
			
		||||
    /////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    if (this->temperature_sensor_) {
 | 
			
		||||
      this->temperature_sensor_->publish_state(temperature);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this->pressure_sensor_) {
 | 
			
		||||
      this->pressure_sensor_->publish_state(pressure);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->status_clear_warning();
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BMP581Component::check_data_readiness_() {
 | 
			
		||||
  //   - verifies component is not internally in standby mode
 | 
			
		||||
  //   - reads interrupt status register
 | 
			
		||||
  //   - checks if data ready bit is asserted
 | 
			
		||||
  //      - If true, then internally sets component to standby mode if in forced mode
 | 
			
		||||
  //   - returns data readiness state
 | 
			
		||||
 | 
			
		||||
  if (this->odr_config_.bit.pwr_mode == STANDBY_MODE) {
 | 
			
		||||
    ESP_LOGD(TAG, "Data is not ready, sensor is in standby mode");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t status;
 | 
			
		||||
 | 
			
		||||
  if (!this->read_byte(BMP581_INT_STATUS, &status)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to read interrupt status register");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->int_status_.reg = status;
 | 
			
		||||
 | 
			
		||||
  if (this->int_status_.bit.drdy_data_reg) {
 | 
			
		||||
    // If in forced mode, then set internal record of the power mode to STANDBY_MODE
 | 
			
		||||
    //  - sensor automatically returns to standby mode after completing a forced measurement
 | 
			
		||||
    if (this->odr_config_.bit.pwr_mode == FORCED_MODE) {
 | 
			
		||||
      this->odr_config_.bit.pwr_mode = STANDBY_MODE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BMP581Component::prime_iir_filter_() {
 | 
			
		||||
  // - temporarily disables oversampling for a fast initial measurement; avoids slowing down ESPHome's startup process
 | 
			
		||||
  // - enables IIR filter flushing with forced measurements
 | 
			
		||||
  // - forces a measurement; flushing the IIR filter and priming it with a current value
 | 
			
		||||
  // - disables IIR filter flushing with forced measurements
 | 
			
		||||
  // - reverts to internally configured oversampling rates
 | 
			
		||||
  // - returns success of all register writes/priming
 | 
			
		||||
 | 
			
		||||
  // store current internal oversampling settings to revert to after priming
 | 
			
		||||
  Oversampling current_temperature_oversampling = (Oversampling) this->osr_config_.bit.osr_t;
 | 
			
		||||
  Oversampling current_pressure_oversampling = (Oversampling) this->osr_config_.bit.osr_p;
 | 
			
		||||
 | 
			
		||||
  // temporarily disables oversampling for temperature and pressure for a fast priming measurement
 | 
			
		||||
  if (!this->write_oversampling_settings_(OVERSAMPLING_NONE, OVERSAMPLING_NONE)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to write oversampling register");
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // flush the IIR filter with forced measurements (we will only flush once)
 | 
			
		||||
  this->dsp_config_.bit.iir_flush_forced_en = true;
 | 
			
		||||
  if (!this->write_byte(BMP581_DSP, this->dsp_config_.reg)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to write IIR source register");
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // forces an intial measurement
 | 
			
		||||
  //  - this measurements flushes the IIR filter reflecting written DSP settings
 | 
			
		||||
  //  - flushing with this initial reading avoids having the internal previous data aquisition being 0, which
 | 
			
		||||
  //    (I)nfinitely affects future values
 | 
			
		||||
  if (!this->start_measurement_()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to request a forced measurement");
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // wait for priming measurement to complete
 | 
			
		||||
  //  - with oversampling disabled, the conversion time for a single measurement for pressure and temperature is
 | 
			
		||||
  //    ceilf(1.05*(1.0+1.0)) = 3ms
 | 
			
		||||
  //  - see page 12 of datasheet for details
 | 
			
		||||
  delay(3);
 | 
			
		||||
 | 
			
		||||
  if (!this->check_data_readiness_()) {
 | 
			
		||||
    ESP_LOGE(TAG, "IIR priming measurement was not ready");
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // disable IIR filter flushings on future forced measurements
 | 
			
		||||
  this->dsp_config_.bit.iir_flush_forced_en = false;
 | 
			
		||||
  if (!this->write_byte(BMP581_DSP, this->dsp_config_.reg)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to write IIR source register");
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // revert oversampling rates to original settings
 | 
			
		||||
  return this->write_oversampling_settings_(current_temperature_oversampling, current_pressure_oversampling);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BMP581Component::read_temperature_(float &temperature) {
 | 
			
		||||
  // - verifies data is ready to be read
 | 
			
		||||
  // - reads in 3 bytes of temperature data
 | 
			
		||||
  // - returns whether successful, where the the variable parameter contains
 | 
			
		||||
  //    - the measured temperature (in degrees Celsius)
 | 
			
		||||
 | 
			
		||||
  if (!this->check_data_readiness_()) {
 | 
			
		||||
    ESP_LOGW(TAG, "Data from sensor isn't ready, skipping this update");
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t data[3];
 | 
			
		||||
  if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 3)) {
 | 
			
		||||
    ESP_LOGW(TAG, "Failed to read sensor's measurement data");
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // temperature MSB is in data[2], LSB is in data[1], XLSB in data[0]
 | 
			
		||||
  int32_t raw_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
 | 
			
		||||
  temperature = (float) (raw_temp / 65536.0);  // convert measurement to degrees Celsius (page 22 of datasheet)
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BMP581Component::read_temperature_and_pressure_(float &temperature, float &pressure) {
 | 
			
		||||
  // - verifies data is ready to be read
 | 
			
		||||
  // - reads in 6 bytes of temperature data (3 for temeperature, 3 for pressure)
 | 
			
		||||
  // - returns whether successful, where the variable parameters contain
 | 
			
		||||
  //    - the measured temperature (in degrees Celsius)
 | 
			
		||||
  //    - the measured pressure (in Pa)
 | 
			
		||||
 | 
			
		||||
  if (!this->check_data_readiness_()) {
 | 
			
		||||
    ESP_LOGW(TAG, "Data from sensor isn't ready, skipping this update");
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t data[6];
 | 
			
		||||
  if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 6)) {
 | 
			
		||||
    ESP_LOGW(TAG, "Failed to read sensor's measurement data");
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // temperature MSB is in data[2], LSB is in data[1], XLSB in data[0]
 | 
			
		||||
  int32_t raw_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
 | 
			
		||||
  temperature = (float) (raw_temp / 65536.0);  // convert measurement to degrees Celsius (page 22 of datasheet)
 | 
			
		||||
 | 
			
		||||
  // pressure MSB is in data[5], LSB is in data[4], XLSB in data[3]
 | 
			
		||||
  int32_t raw_press = (int32_t) data[5] << 16 | (int32_t) data[4] << 8 | (int32_t) data[3];
 | 
			
		||||
  pressure = (float) (raw_press / 64.0);  // Divide by 2^6=64 for Pa (page 22 of datasheet)
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BMP581Component::reset_() {
 | 
			
		||||
  // - writes reset command to the command register
 | 
			
		||||
  // - waits for sensor to complete reset
 | 
			
		||||
  // - returns the Power-On-Reboot interrupt status, which is asserted if successful
 | 
			
		||||
 | 
			
		||||
  // writes reset command to BMP's command register
 | 
			
		||||
  if (!this->write_byte(BMP581_COMMAND, RESET_COMMAND)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to write reset command");
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // t_{soft_res} = 2ms (page 11 of datasheet); time it takes to enter standby mode
 | 
			
		||||
  //  - round up to 3 ms
 | 
			
		||||
  delay(3);
 | 
			
		||||
 | 
			
		||||
  // read interrupt status register
 | 
			
		||||
  if (!this->read_byte(BMP581_INT_STATUS, &this->int_status_.reg)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to read interrupt status register");
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Power-On-Reboot bit is asserted if sensor successfully reset
 | 
			
		||||
  return this->int_status_.bit.por;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BMP581Component::start_measurement_() {
 | 
			
		||||
  // - only pushes the sensor into FORCED_MODE for a reading if already in STANDBY_MODE
 | 
			
		||||
  // - returns whether a measurement is in progress or has been initiated
 | 
			
		||||
 | 
			
		||||
  if (this->odr_config_.bit.pwr_mode == STANDBY_MODE) {
 | 
			
		||||
    return this->write_power_mode_(FORCED_MODE);
 | 
			
		||||
  } else {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BMP581Component::write_iir_settings_(IIRFilter temperature_iir, IIRFilter pressure_iir) {
 | 
			
		||||
  // - ensures data registers store filtered values
 | 
			
		||||
  // - sets IIR filter levels on sensor
 | 
			
		||||
  // - matches other default settings on sensor
 | 
			
		||||
  // - writes configuration to the two relevant registers
 | 
			
		||||
  // - returns success or failure of write to the registers
 | 
			
		||||
 | 
			
		||||
  // If the temperature/pressure IIR filter is configured, then ensure data registers store the filtered measurement
 | 
			
		||||
  this->dsp_config_.bit.shdw_sel_iir_t = (temperature_iir != IIR_FILTER_OFF);
 | 
			
		||||
  this->dsp_config_.bit.shdw_sel_iir_p = (pressure_iir != IIR_FILTER_OFF);
 | 
			
		||||
 | 
			
		||||
  // set temperature and pressure IIR filter level to configured values
 | 
			
		||||
  this->iir_config_.bit.set_iir_t = temperature_iir;
 | 
			
		||||
  this->iir_config_.bit.set_iir_p = pressure_iir;
 | 
			
		||||
 | 
			
		||||
  // enable pressure and temperature compensation (page 61 of datasheet)
 | 
			
		||||
  //  - ?only relevant if IIR filter is applied?; the datasheet is ambiguous
 | 
			
		||||
  //  - matches BMP's default setting
 | 
			
		||||
  this->dsp_config_.bit.comp_pt_en = 0x3;
 | 
			
		||||
 | 
			
		||||
  // BMP581_DSP register and BMP581_DSP_IIR registers are successive
 | 
			
		||||
  //  - allows us to write the IIR configuration with one command to both registers
 | 
			
		||||
  uint8_t register_data[2] = {this->dsp_config_.reg, this->iir_config_.reg};
 | 
			
		||||
  return this->write_bytes(BMP581_DSP, register_data, sizeof(register_data));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BMP581Component::write_interrupt_source_settings_(bool data_ready_enable) {
 | 
			
		||||
  // - updates component's internal setting
 | 
			
		||||
  // - returns success or failure of write to interrupt source register
 | 
			
		||||
 | 
			
		||||
  this->int_source_.bit.drdy_data_reg_en = data_ready_enable;
 | 
			
		||||
 | 
			
		||||
  // write interrupt source register
 | 
			
		||||
  return this->write_byte(BMP581_INT_SOURCE, this->int_source_.reg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BMP581Component::write_oversampling_settings_(Oversampling temperature_oversampling,
 | 
			
		||||
                                                   Oversampling pressure_oversampling) {
 | 
			
		||||
  // - updates component's internal setting
 | 
			
		||||
  // - returns success or failure of write to Over-Sampling Rate register
 | 
			
		||||
 | 
			
		||||
  this->osr_config_.bit.osr_t = temperature_oversampling;
 | 
			
		||||
  this->osr_config_.bit.osr_p = pressure_oversampling;
 | 
			
		||||
 | 
			
		||||
  return this->write_byte(BMP581_OSR, this->osr_config_.reg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BMP581Component::write_power_mode_(OperationMode mode) {
 | 
			
		||||
  // - updates the component's internal power mode
 | 
			
		||||
  // - returns success or failure of write to Output Data Rate register
 | 
			
		||||
 | 
			
		||||
  this->odr_config_.bit.pwr_mode = mode;
 | 
			
		||||
 | 
			
		||||
  // write odr register
 | 
			
		||||
  return this->write_byte(BMP581_ODR, this->odr_config_.reg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace bmp581
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										222
									
								
								esphome/components/bmp581/bmp581.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								esphome/components/bmp581/bmp581.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,222 @@
 | 
			
		||||
// All datasheet page references refer to Bosch Document Number BST-BMP581-DS004-04 (revision number 1.4)
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bmp581 {
 | 
			
		||||
 | 
			
		||||
static const uint8_t BMP581_ASIC_ID = 0x50;  // BMP581's ASIC chip ID (page 51 of datasheet)
 | 
			
		||||
static const uint8_t RESET_COMMAND = 0xB6;   // Soft reset command
 | 
			
		||||
 | 
			
		||||
// BMP581 Register Addresses
 | 
			
		||||
enum {
 | 
			
		||||
  BMP581_CHIP_ID = 0x01,     // read chip ID
 | 
			
		||||
  BMP581_INT_SOURCE = 0x15,  // write interrupt sources
 | 
			
		||||
  BMP581_MEASUREMENT_DATA =
 | 
			
		||||
      0x1D,  // read measurement registers, 0x1D-0x1F are temperature XLSB to MSB and 0x20-0x22 are pressure XLSB to MSB
 | 
			
		||||
  BMP581_INT_STATUS = 0x27,  // read interrupt statuses
 | 
			
		||||
  BMP581_STATUS = 0x28,      // read sensor status
 | 
			
		||||
  BMP581_DSP = 0x30,         // write sensor configuration
 | 
			
		||||
  BMP581_DSP_IIR = 0x31,     // write IIR filter configuration
 | 
			
		||||
  BMP581_OSR = 0x36,         // write oversampling configuration
 | 
			
		||||
  BMP581_ODR = 0x37,         // write data rate and power mode configuration
 | 
			
		||||
  BMP581_COMMAND = 0x7E      // write sensor command
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// BMP581 Power mode operations
 | 
			
		||||
enum OperationMode {
 | 
			
		||||
  STANDBY_MODE = 0x0,  // no active readings
 | 
			
		||||
  NORMAL_MODE = 0x1,   // read continuously at ODR configured rate and standby between
 | 
			
		||||
  FORCED_MODE = 0x2,   // read sensor once (only reading mode used by this component)
 | 
			
		||||
  NONSTOP_MODE = 0x3   // read continuously with no standby
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Temperature and pressure sensors can be oversampled to reduce noise
 | 
			
		||||
enum Oversampling {
 | 
			
		||||
  OVERSAMPLING_NONE = 0x0,
 | 
			
		||||
  OVERSAMPLING_X2 = 0x1,
 | 
			
		||||
  OVERSAMPLING_X4 = 0x2,
 | 
			
		||||
  OVERSAMPLING_X8 = 0x3,
 | 
			
		||||
  OVERSAMPLING_X16 = 0x4,
 | 
			
		||||
  OVERSAMPLING_X32 = 0x5,
 | 
			
		||||
  OVERSAMPLING_X64 = 0x6,
 | 
			
		||||
  OVERSAMPLING_X128 = 0x7
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Infinite Impulse Response filter reduces noise caused by ambient disturbances
 | 
			
		||||
enum IIRFilter {
 | 
			
		||||
  IIR_FILTER_OFF = 0x0,
 | 
			
		||||
  IIR_FILTER_2 = 0x1,
 | 
			
		||||
  IIR_FILTER_4 = 0x2,
 | 
			
		||||
  IIR_FILTER_8 = 0x3,
 | 
			
		||||
  IIR_FILTER_16 = 0x4,
 | 
			
		||||
  IIR_FILTER_32 = 0x5,
 | 
			
		||||
  IIR_FILTER_64 = 0x6,
 | 
			
		||||
  IIR_FILTER_128 = 0x7
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BMP581Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void update() override;
 | 
			
		||||
 | 
			
		||||
  void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
 | 
			
		||||
  void set_pressure_sensor(sensor::Sensor *pressure_sensor) { this->pressure_sensor_ = pressure_sensor; }
 | 
			
		||||
 | 
			
		||||
  void set_temperature_oversampling_config(Oversampling temperature_oversampling) {
 | 
			
		||||
    this->temperature_oversampling_ = temperature_oversampling;
 | 
			
		||||
  }
 | 
			
		||||
  void set_pressure_oversampling_config(Oversampling pressure_oversampling) {
 | 
			
		||||
    this->pressure_oversampling_ = pressure_oversampling;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_temperature_iir_filter_config(IIRFilter iir_temperature_level) {
 | 
			
		||||
    this->iir_temperature_level_ = iir_temperature_level;
 | 
			
		||||
  }
 | 
			
		||||
  void set_pressure_iir_filter_config(IIRFilter iir_pressure_level) { this->iir_pressure_level_ = iir_pressure_level; }
 | 
			
		||||
 | 
			
		||||
  void set_conversion_time(uint8_t conversion_time) { this->conversion_time_ = conversion_time; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  sensor::Sensor *temperature_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *pressure_sensor_{nullptr};
 | 
			
		||||
 | 
			
		||||
  Oversampling temperature_oversampling_;
 | 
			
		||||
  Oversampling pressure_oversampling_;
 | 
			
		||||
 | 
			
		||||
  IIRFilter iir_temperature_level_;
 | 
			
		||||
  IIRFilter iir_pressure_level_;
 | 
			
		||||
 | 
			
		||||
  // Stores the sensors conversion time needed for a measurement based on oversampling settings and datasheet (page 12)
 | 
			
		||||
  // Computed in Python during codegen
 | 
			
		||||
  uint8_t conversion_time_;
 | 
			
		||||
 | 
			
		||||
  // Checks if the BMP581 has measurement data ready by checking the sensor's interrupts
 | 
			
		||||
  bool check_data_readiness_();
 | 
			
		||||
 | 
			
		||||
  // Flushes the IIR filter and primes an initial reading
 | 
			
		||||
  bool prime_iir_filter_();
 | 
			
		||||
 | 
			
		||||
  // Reads temperature data from sensor and converts data to measurement in degrees Celsius
 | 
			
		||||
  bool read_temperature_(float &temperature);
 | 
			
		||||
  // Reads temperature and pressure data from sensor and converts data to measurements in degrees Celsius and Pa
 | 
			
		||||
  bool read_temperature_and_pressure_(float &temperature, float &pressure);
 | 
			
		||||
 | 
			
		||||
  // Soft resets the BMP581
 | 
			
		||||
  bool reset_();
 | 
			
		||||
 | 
			
		||||
  // Initiates a measurement on sensor by switching to FORCED_MODE
 | 
			
		||||
  bool start_measurement_();
 | 
			
		||||
 | 
			
		||||
  // Writes the IIR filter configuration to the DSP and DSP_IIR registers
 | 
			
		||||
  bool write_iir_settings_(IIRFilter temperature_iir, IIRFilter pressure_iir);
 | 
			
		||||
 | 
			
		||||
  // Writes whether to enable the data ready interrupt to the interrupt source register
 | 
			
		||||
  bool write_interrupt_source_settings_(bool data_ready_enable);
 | 
			
		||||
 | 
			
		||||
  // Writes the oversampling settings to the OSR register
 | 
			
		||||
  bool write_oversampling_settings_(Oversampling temperature_oversampling, Oversampling pressure_oversampling);
 | 
			
		||||
 | 
			
		||||
  // Sets the power mode on the BMP581 by writing to the ODR register
 | 
			
		||||
  bool write_power_mode_(OperationMode mode);
 | 
			
		||||
 | 
			
		||||
  enum ErrorCode {
 | 
			
		||||
    NONE = 0,
 | 
			
		||||
    ERROR_COMMUNICATION_FAILED,
 | 
			
		||||
    ERROR_WRONG_CHIP_ID,
 | 
			
		||||
    ERROR_SENSOR_STATUS,
 | 
			
		||||
    ERROR_SENSOR_RESET,
 | 
			
		||||
    ERROR_PRIME_IIR_FAILED
 | 
			
		||||
  } error_code_{NONE};
 | 
			
		||||
 | 
			
		||||
  // BMP581's interrupt source register (address 0x15) to configure which interrupts are enabled (page 54 of datasheet)
 | 
			
		||||
  union {
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t drdy_data_reg_en : 1;  // Data ready interrupt enable
 | 
			
		||||
      uint8_t fifo_full_en : 1;      // FIFO full interrupt enable
 | 
			
		||||
      uint8_t fifo_ths_en : 1;       // FIFO threshold/watermark interrupt enable
 | 
			
		||||
      uint8_t oor_p_en : 1;          // Pressure data out-of-range interrupt enable
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } int_source_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  // BMP581's interrupt status register (address 0x27) to determine ensor's current state (page 58 of datasheet)
 | 
			
		||||
  union {
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t drdy_data_reg : 1;  // Data ready
 | 
			
		||||
      uint8_t fifo_full : 1;      // FIFO full
 | 
			
		||||
      uint8_t fifo_ths : 1;       // FIFO fhreshold/watermark
 | 
			
		||||
      uint8_t oor_p : 1;          // Pressure data out-of-range
 | 
			
		||||
      uint8_t por : 1;            // Power-On-Reset complete
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } int_status_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  // BMP581's status register (address 0x28) to determine if sensor has setup correctly (page 58 of datasheet)
 | 
			
		||||
  union {
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t status_core_rdy : 1;
 | 
			
		||||
      uint8_t status_nvm_rdy : 1;             // asserted if NVM is ready of operations
 | 
			
		||||
      uint8_t status_nvm_err : 1;             // asserted if NVM error
 | 
			
		||||
      uint8_t status_nvm_cmd_err : 1;         // asserted if boot command error
 | 
			
		||||
      uint8_t status_boot_err_corrected : 1;  // asserted if a boot error has been corrected
 | 
			
		||||
      uint8_t : 2;
 | 
			
		||||
      uint8_t st_crack_pass : 1;  // asserted if crack check has executed without detecting a crack
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } status_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  // BMP581's dsp register (address 0x30) to configure data registers iir selection (page 61 of datasheet)
 | 
			
		||||
  union {
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t comp_pt_en : 2;           // enable temperature and pressure compensation
 | 
			
		||||
      uint8_t iir_flush_forced_en : 1;  // IIR filter is flushed in forced mode
 | 
			
		||||
      uint8_t shdw_sel_iir_t : 1;       // temperature data register value selected before or after iir
 | 
			
		||||
      uint8_t fifo_sel_iir_t : 1;       // FIFO temperature data register value secected before or after iir
 | 
			
		||||
      uint8_t shdw_sel_iir_p : 1;       // pressure data register value selected before or after iir
 | 
			
		||||
      uint8_t fifo_sel_iir_p : 1;       // FIFO pressure data register value selected before or after iir
 | 
			
		||||
      uint8_t oor_sel_iir_p : 1;        // pressure out-of-range value selected before or after iir
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } dsp_config_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  // BMP581's iir register (address 0x31) to configure iir filtering(page 62 of datasheet)
 | 
			
		||||
  union {
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t set_iir_t : 3;  // Temperature IIR filter coefficient
 | 
			
		||||
      uint8_t set_iir_p : 3;  // Pressure IIR filter coefficient
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } iir_config_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  // BMP581's OSR register (address 0x36) to configure Over-Sampling Rates (page 64 of datasheet)
 | 
			
		||||
  union {
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t osr_t : 3;     // Temperature oversampling
 | 
			
		||||
      uint8_t osr_p : 3;     // Pressure oversampling
 | 
			
		||||
      uint8_t press_en : 1;  // Enables pressure measurement
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } osr_config_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  // BMP581's odr register (address 0x37) to configure output data rate and power mode (page 64 of datasheet)
 | 
			
		||||
  union {
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t pwr_mode : 2;  // power mode of sensor
 | 
			
		||||
      uint8_t odr : 5;       // output data rate
 | 
			
		||||
      uint8_t deep_dis : 1;  // deep standby disabled if asserted
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } odr_config_ = {.reg = 0};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace bmp581
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										163
									
								
								esphome/components/bmp581/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								esphome/components/bmp581/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
			
		||||
import math
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_IIR_FILTER,
 | 
			
		||||
    CONF_OVERSAMPLING,
 | 
			
		||||
    CONF_PRESSURE,
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_PASCAL,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@kahrendt"]
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
bmp581_ns = cg.esphome_ns.namespace("bmp581")
 | 
			
		||||
 | 
			
		||||
Oversampling = bmp581_ns.enum("Oversampling")
 | 
			
		||||
OVERSAMPLING_OPTIONS = {
 | 
			
		||||
    "NONE": Oversampling.OVERSAMPLING_NONE,
 | 
			
		||||
    "2X": Oversampling.OVERSAMPLING_X2,
 | 
			
		||||
    "4X": Oversampling.OVERSAMPLING_X4,
 | 
			
		||||
    "8X": Oversampling.OVERSAMPLING_X8,
 | 
			
		||||
    "16X": Oversampling.OVERSAMPLING_X16,
 | 
			
		||||
    "32X": Oversampling.OVERSAMPLING_X32,
 | 
			
		||||
    "64X": Oversampling.OVERSAMPLING_X64,
 | 
			
		||||
    "128X": Oversampling.OVERSAMPLING_X128,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRFilter = bmp581_ns.enum("IIRFilter")
 | 
			
		||||
IIR_FILTER_OPTIONS = {
 | 
			
		||||
    "OFF": IIRFilter.IIR_FILTER_OFF,
 | 
			
		||||
    "2X": IIRFilter.IIR_FILTER_2,
 | 
			
		||||
    "4X": IIRFilter.IIR_FILTER_4,
 | 
			
		||||
    "8X": IIRFilter.IIR_FILTER_8,
 | 
			
		||||
    "16X": IIRFilter.IIR_FILTER_16,
 | 
			
		||||
    "32X": IIRFilter.IIR_FILTER_32,
 | 
			
		||||
    "64X": IIRFilter.IIR_FILTER_64,
 | 
			
		||||
    "128X": IIRFilter.IIR_FILTER_128,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BMP581Component = bmp581_ns.class_(
 | 
			
		||||
    "BMP581Component", cg.PollingComponent, i2c.I2CDevice
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def compute_measurement_conversion_time(config):
 | 
			
		||||
    # - adds up sensor conversion time based on temperature and pressure oversampling rates given in datasheet
 | 
			
		||||
    # - returns a rounded up time in ms
 | 
			
		||||
 | 
			
		||||
    # Page 12 of datasheet
 | 
			
		||||
    PRESSURE_OVERSAMPLING_CONVERSION_TIMES = {
 | 
			
		||||
        "NONE": 1.0,
 | 
			
		||||
        "2X": 1.7,
 | 
			
		||||
        "4X": 2.9,
 | 
			
		||||
        "8X": 5.4,
 | 
			
		||||
        "16X": 10.4,
 | 
			
		||||
        "32X": 20.4,
 | 
			
		||||
        "64X": 40.4,
 | 
			
		||||
        "128X": 80.4,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # Page 12 of datasheet
 | 
			
		||||
    TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES = {
 | 
			
		||||
        "NONE": 1.0,
 | 
			
		||||
        "2X": 1.1,
 | 
			
		||||
        "4X": 1.5,
 | 
			
		||||
        "8X": 2.1,
 | 
			
		||||
        "16X": 3.3,
 | 
			
		||||
        "32X": 5.8,
 | 
			
		||||
        "64X": 10.8,
 | 
			
		||||
        "128X": 20.8,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pressure_conversion_time = (
 | 
			
		||||
        0.0  # No conversion time necessary without a pressure sensor
 | 
			
		||||
    )
 | 
			
		||||
    if pressure_config := config.get(CONF_PRESSURE):
 | 
			
		||||
        pressure_conversion_time = PRESSURE_OVERSAMPLING_CONVERSION_TIMES[
 | 
			
		||||
            pressure_config.get(CONF_OVERSAMPLING)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    temperature_conversion_time = (
 | 
			
		||||
        1.0  # BMP581 always samples the temperature even if only reading pressure
 | 
			
		||||
    )
 | 
			
		||||
    if temperature_config := config.get(CONF_TEMPERATURE):
 | 
			
		||||
        temperature_conversion_time = TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES[
 | 
			
		||||
            temperature_config.get(CONF_OVERSAMPLING)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    # Datasheet indicates a 5% possible error in each conversion time listed
 | 
			
		||||
    return math.ceil(1.05 * (pressure_conversion_time + temperature_conversion_time))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BMP581Component),
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_CELSIUS,
 | 
			
		||||
                accuracy_decimals=1,
 | 
			
		||||
                device_class=DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="NONE"): cv.enum(
 | 
			
		||||
                        OVERSAMPLING_OPTIONS, upper=True
 | 
			
		||||
                    ),
 | 
			
		||||
                    cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
 | 
			
		||||
                        IIR_FILTER_OPTIONS, upper=True
 | 
			
		||||
                    ),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_PASCAL,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
 | 
			
		||||
                        OVERSAMPLING_OPTIONS, upper=True
 | 
			
		||||
                    ),
 | 
			
		||||
                    cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
 | 
			
		||||
                        IIR_FILTER_OPTIONS, upper=True
 | 
			
		||||
                    ),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(i2c.i2c_device_schema(0x46))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
    if temperature_config := config.get(CONF_TEMPERATURE):
 | 
			
		||||
        sens = await sensor.new_sensor(temperature_config)
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_temperature_oversampling_config(
 | 
			
		||||
                temperature_config[CONF_OVERSAMPLING]
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_temperature_iir_filter_config(temperature_config[CONF_IIR_FILTER])
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if pressure_config := config.get(CONF_PRESSURE):
 | 
			
		||||
        sens = await sensor.new_sensor(pressure_config)
 | 
			
		||||
        cg.add(var.set_pressure_sensor(sens))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling_config(pressure_config[CONF_OVERSAMPLING]))
 | 
			
		||||
        cg.add(var.set_pressure_iir_filter_config(pressure_config[CONF_IIR_FILTER]))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_conversion_time(compute_measurement_conversion_time(config)))
 | 
			
		||||
@@ -85,11 +85,11 @@ async def setup_button_core_(var, config):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
 | 
			
		||||
    if CONF_DEVICE_CLASS in config:
 | 
			
		||||
        cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
 | 
			
		||||
    if device_class := config.get(CONF_DEVICE_CLASS):
 | 
			
		||||
        cg.add(var.set_device_class(device_class))
 | 
			
		||||
 | 
			
		||||
    if CONF_MQTT_ID in config:
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
 | 
			
		||||
    if mqtt_id := config.get(CONF_MQTT_ID):
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,12 +17,11 @@ CONF_ON_FRAME = "on_frame"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_id(config):
 | 
			
		||||
    if CONF_CAN_ID in config:
 | 
			
		||||
        id_value = config[CONF_CAN_ID]
 | 
			
		||||
        id_ext = config[CONF_USE_EXTENDED_ID]
 | 
			
		||||
        if not id_ext:
 | 
			
		||||
            if id_value > 0x7FF:
 | 
			
		||||
                raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)")
 | 
			
		||||
    can_id = config[CONF_CAN_ID]
 | 
			
		||||
    id_ext = config[CONF_USE_EXTENDED_ID]
 | 
			
		||||
    if not id_ext:
 | 
			
		||||
        if can_id > 0x7FF:
 | 
			
		||||
            raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)")
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -145,8 +144,8 @@ async def canbus_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg)
 | 
			
		||||
    await cg.register_parented(var, config[CONF_CANBUS_ID])
 | 
			
		||||
 | 
			
		||||
    if CONF_CAN_ID in config:
 | 
			
		||||
        can_id = await cg.templatable(config[CONF_CAN_ID], args, cg.uint32)
 | 
			
		||||
    if can_id := config.get(CONF_CAN_ID):
 | 
			
		||||
        can_id = await cg.templatable(can_id, args, cg.uint32)
 | 
			
		||||
        cg.add(var.set_can_id(can_id))
 | 
			
		||||
    use_extended_id = await cg.templatable(
 | 
			
		||||
        config[CONF_USE_EXTENDED_ID], args, cg.uint32
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,8 @@ async def to_code(config):
 | 
			
		||||
    cg.add(var.set_touch_threshold(config[CONF_TOUCH_THRESHOLD]))
 | 
			
		||||
    cg.add(var.set_allow_multiple_touches(config[CONF_ALLOW_MULTIPLE_TOUCHES]))
 | 
			
		||||
 | 
			
		||||
    if CONF_RESET_PIN in config:
 | 
			
		||||
        pin = await cg.gpio_pin_expression(config[CONF_RESET_PIN])
 | 
			
		||||
    if reset_pin_config := config.get(CONF_RESET_PIN):
 | 
			
		||||
        pin = await cg.gpio_pin_expression(reset_pin_config)
 | 
			
		||||
        cg.add(var.set_reset_pin(pin))
 | 
			
		||||
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -69,16 +69,16 @@ async def to_code(config):
 | 
			
		||||
    sens = await sensor.new_sensor(config[CONF_TVOC])
 | 
			
		||||
    cg.add(var.set_tvoc(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_VERSION in config:
 | 
			
		||||
        sens = await text_sensor.new_text_sensor(config[CONF_VERSION])
 | 
			
		||||
    if version_config := config.get(CONF_VERSION):
 | 
			
		||||
        sens = await text_sensor.new_text_sensor(version_config)
 | 
			
		||||
        cg.add(var.set_version(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_BASELINE in config:
 | 
			
		||||
        cg.add(var.set_baseline(config[CONF_BASELINE]))
 | 
			
		||||
    if (baseline := config.get(CONF_BASELINE)) is not None:
 | 
			
		||||
        cg.add(var.set_baseline(baseline))
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        sens = await cg.get_variable(config[CONF_TEMPERATURE])
 | 
			
		||||
    if temperature_id := config.get(CONF_TEMPERATURE):
 | 
			
		||||
        sens = await cg.get_variable(temperature_id)
 | 
			
		||||
        cg.add(var.set_temperature(sens))
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        sens = await cg.get_variable(config[CONF_HUMIDITY])
 | 
			
		||||
    if humidity_id := config.get(CONF_HUMIDITY):
 | 
			
		||||
        sens = await cg.get_variable(humidity_id)
 | 
			
		||||
        cg.add(var.set_humidity(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -127,8 +127,12 @@ def single_visual_temperature(value):
 | 
			
		||||
 | 
			
		||||
# Actions
 | 
			
		||||
ControlAction = climate_ns.class_("ControlAction", automation.Action)
 | 
			
		||||
StateTrigger = climate_ns.class_("StateTrigger", automation.Trigger.template())
 | 
			
		||||
ControlTrigger = climate_ns.class_("ControlTrigger", automation.Trigger.template())
 | 
			
		||||
StateTrigger = climate_ns.class_(
 | 
			
		||||
    "StateTrigger", automation.Trigger.template(Climate.operator("ref"))
 | 
			
		||||
)
 | 
			
		||||
ControlTrigger = climate_ns.class_(
 | 
			
		||||
    "ControlTrigger", automation.Trigger.template(ClimateCall.operator("ref"))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Any(
 | 
			
		||||
    single_visual_temperature,
 | 
			
		||||
@@ -322,11 +326,15 @@ async def setup_climate_core_(var, config):
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_STATE, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            trigger, [(Climate.operator("ref"), "x")], conf
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_CONTROL, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            trigger, [(ClimateCall.operator("ref"), "x")], conf
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_climate(var, config):
 | 
			
		||||
 
 | 
			
		||||
@@ -42,17 +42,17 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
 | 
			
		||||
  Climate *climate_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ControlTrigger : public Trigger<> {
 | 
			
		||||
class ControlTrigger : public Trigger<ClimateCall &> {
 | 
			
		||||
 public:
 | 
			
		||||
  ControlTrigger(Climate *climate) {
 | 
			
		||||
    climate->add_on_control_callback([this]() { this->trigger(); });
 | 
			
		||||
    climate->add_on_control_callback([this](ClimateCall &x) { this->trigger(x); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class StateTrigger : public Trigger<> {
 | 
			
		||||
class StateTrigger : public Trigger<Climate &> {
 | 
			
		||||
 public:
 | 
			
		||||
  StateTrigger(Climate *climate) {
 | 
			
		||||
    climate->add_on_state_callback([this]() { this->trigger(); });
 | 
			
		||||
    climate->add_on_state_callback([this](Climate &x) { this->trigger(x); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ namespace climate {
 | 
			
		||||
static const char *const TAG = "climate";
 | 
			
		||||
 | 
			
		||||
void ClimateCall::perform() {
 | 
			
		||||
  this->parent_->control_callback_.call(*this);
 | 
			
		||||
  ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
 | 
			
		||||
  this->validate_();
 | 
			
		||||
  if (this->mode_.has_value()) {
 | 
			
		||||
@@ -44,7 +45,6 @@ void ClimateCall::perform() {
 | 
			
		||||
  if (this->target_temperature_high_.has_value()) {
 | 
			
		||||
    ESP_LOGD(TAG, "  Target Temperature High: %.2f", *this->target_temperature_high_);
 | 
			
		||||
  }
 | 
			
		||||
  this->parent_->control_callback_.call();
 | 
			
		||||
  this->parent_->control(*this);
 | 
			
		||||
}
 | 
			
		||||
void ClimateCall::validate_() {
 | 
			
		||||
@@ -300,11 +300,11 @@ ClimateCall &ClimateCall::set_swing_mode(optional<ClimateSwingMode> swing_mode)
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Climate::add_on_state_callback(std::function<void()> &&callback) {
 | 
			
		||||
void Climate::add_on_state_callback(std::function<void(Climate &)> &&callback) {
 | 
			
		||||
  this->state_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Climate::add_on_control_callback(std::function<void()> &&callback) {
 | 
			
		||||
void Climate::add_on_control_callback(std::function<void(ClimateCall &)> &&callback) {
 | 
			
		||||
  this->control_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -408,7 +408,7 @@ void Climate::publish_state() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Send state to frontend
 | 
			
		||||
  this->state_callback_.call();
 | 
			
		||||
  this->state_callback_.call(*this);
 | 
			
		||||
  // Save state
 | 
			
		||||
  this->save_state_();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -198,7 +198,7 @@ class Climate : public EntityBase {
 | 
			
		||||
   *
 | 
			
		||||
   * @param callback The callback to call.
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_state_callback(std::function<void()> &&callback);
 | 
			
		||||
  void add_on_state_callback(std::function<void(Climate &)> &&callback);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Add a callback for the climate device configuration; each time the configuration parameters of a climate device
 | 
			
		||||
@@ -206,7 +206,7 @@ class Climate : public EntityBase {
 | 
			
		||||
   *
 | 
			
		||||
   * @param callback The callback to call.
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_control_callback(std::function<void()> &&callback);
 | 
			
		||||
  void add_on_control_callback(std::function<void(ClimateCall &)> &&callback);
 | 
			
		||||
 | 
			
		||||
  /** Make a climate device control call, this is used to control the climate device, see the ClimateCall description
 | 
			
		||||
   * for more info.
 | 
			
		||||
@@ -273,8 +273,8 @@ class Climate : public EntityBase {
 | 
			
		||||
 | 
			
		||||
  void dump_traits_(const char *tag);
 | 
			
		||||
 | 
			
		||||
  CallbackManager<void()> state_callback_{};
 | 
			
		||||
  CallbackManager<void()> control_callback_{};
 | 
			
		||||
  CallbackManager<void(Climate &)> state_callback_{};
 | 
			
		||||
  CallbackManager<void(ClimateCall &)> control_callback_{};
 | 
			
		||||
  ESPPreferenceObject rtc_;
 | 
			
		||||
  optional<float> visual_min_temperature_override_{};
 | 
			
		||||
  optional<float> visual_max_temperature_override_{};
 | 
			
		||||
 
 | 
			
		||||
@@ -44,11 +44,11 @@ async def register_climate_ir(var, config):
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
 | 
			
		||||
    cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
 | 
			
		||||
    if CONF_SENSOR in config:
 | 
			
		||||
        sens = await cg.get_variable(config[CONF_SENSOR])
 | 
			
		||||
    if sensor_id := config.get(CONF_SENSOR):
 | 
			
		||||
        sens = await cg.get_variable(sensor_id)
 | 
			
		||||
        cg.add(var.set_sensor(sens))
 | 
			
		||||
    if CONF_RECEIVER_ID in config:
 | 
			
		||||
        receiver = await cg.get_variable(config[CONF_RECEIVER_ID])
 | 
			
		||||
    if receiver_id := config.get(CONF_RECEIVER_ID):
 | 
			
		||||
        receiver = await cg.get_variable(receiver_id)
 | 
			
		||||
        cg.add(receiver.register_listener(var))
 | 
			
		||||
 | 
			
		||||
    transmitter = await cg.get_variable(config[CONF_TRANSMITTER_ID])
 | 
			
		||||
 
 | 
			
		||||
@@ -82,5 +82,5 @@ async def to_code(config):
 | 
			
		||||
 | 
			
		||||
    cg.new_variable(
 | 
			
		||||
        config[CONF_ID],
 | 
			
		||||
        cg.StructInitializer(ColorStruct, ("r", r), ("g", g), ("b", b), ("w", w)),
 | 
			
		||||
        cg.ArrayInitializer(r, g, b, w),
 | 
			
		||||
    )
 | 
			
		||||
 
 | 
			
		||||
@@ -114,7 +114,7 @@ bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteRecei
 | 
			
		||||
  if (!decoded.has_value())
 | 
			
		||||
    return false;
 | 
			
		||||
  // Decoded remote state y 3 bytes long code.
 | 
			
		||||
  uint32_t remote_state = *decoded;
 | 
			
		||||
  uint32_t remote_state = (*decoded).second;
 | 
			
		||||
  ESP_LOGV(TAG, "Decoded 0x%06X", remote_state);
 | 
			
		||||
  if ((remote_state & 0xFF0000) != 0xB20000)
 | 
			
		||||
    return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -113,17 +113,14 @@ async def to_code(config):
 | 
			
		||||
    cg.add(var.set_hpf_enable(config[CONF_CURRENT_HPF], config[CONF_VOLTAGE_HPF]))
 | 
			
		||||
    cg.add(var.set_pulse_energy_wh(config[CONF_PULSE_ENERGY]))
 | 
			
		||||
 | 
			
		||||
    if CONF_VOLTAGE in config:
 | 
			
		||||
        conf = config[CONF_VOLTAGE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if voltage_config := config.get(CONF_VOLTAGE):
 | 
			
		||||
        sens = await sensor.new_sensor(voltage_config)
 | 
			
		||||
        cg.add(var.set_voltage_sensor(sens))
 | 
			
		||||
    if CONF_CURRENT in config:
 | 
			
		||||
        conf = config[CONF_CURRENT]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if current_config := config.get(CONF_CURRENT):
 | 
			
		||||
        sens = await sensor.new_sensor(current_config)
 | 
			
		||||
        cg.add(var.set_current_sensor(sens))
 | 
			
		||||
    if CONF_POWER in config:
 | 
			
		||||
        conf = config[CONF_POWER]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if power_config := config.get(CONF_POWER):
 | 
			
		||||
        sens = await sensor.new_sensor(power_config)
 | 
			
		||||
        cg.add(var.set_power_sensor(sens))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -69,19 +69,15 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await uart.register_uart_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_VOLTAGE in config:
 | 
			
		||||
        conf = config[CONF_VOLTAGE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if voltage_config := config.get(CONF_VOLTAGE):
 | 
			
		||||
        sens = await sensor.new_sensor(voltage_config)
 | 
			
		||||
        cg.add(var.set_voltage_sensor(sens))
 | 
			
		||||
    if CONF_CURRENT in config:
 | 
			
		||||
        conf = config[CONF_CURRENT]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if current_config := config.get(CONF_CURRENT):
 | 
			
		||||
        sens = await sensor.new_sensor(current_config)
 | 
			
		||||
        cg.add(var.set_current_sensor(sens))
 | 
			
		||||
    if CONF_POWER in config:
 | 
			
		||||
        conf = config[CONF_POWER]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if power_config := config.get(CONF_POWER):
 | 
			
		||||
        sens = await sensor.new_sensor(power_config)
 | 
			
		||||
        cg.add(var.set_power_sensor(sens))
 | 
			
		||||
    if CONF_ENERGY in config:
 | 
			
		||||
        conf = config[CONF_ENERGY]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if energy_config := config.get(CONF_ENERGY):
 | 
			
		||||
        sens = await sensor.new_sensor(energy_config)
 | 
			
		||||
        cg.add(var.set_energy_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -66,59 +66,63 @@ CONFIG_SCHEMA = cover.COVER_SCHEMA.extend(
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield cover.register_cover(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await cover.register_cover(var, config)
 | 
			
		||||
 | 
			
		||||
    yield automation.build_automation(
 | 
			
		||||
    await automation.build_automation(
 | 
			
		||||
        var.get_stop_trigger(), [], config[CONF_STOP_ACTION]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # OPEN
 | 
			
		||||
    bin = yield cg.get_variable(config[CONF_OPEN_SENSOR])
 | 
			
		||||
    bin = await cg.get_variable(config[CONF_OPEN_SENSOR])
 | 
			
		||||
    cg.add(var.set_open_sensor(bin))
 | 
			
		||||
    cg.add(
 | 
			
		||||
        var.set_open_moving_current_threshold(
 | 
			
		||||
            config[CONF_OPEN_MOVING_CURRENT_THRESHOLD]
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    if CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD in config:
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_open_obstacle_current_threshold(
 | 
			
		||||
                config[CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD]
 | 
			
		||||
            )
 | 
			
		||||
    if (
 | 
			
		||||
        open_obsticle_current_threshold := config.get(
 | 
			
		||||
            CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD
 | 
			
		||||
        )
 | 
			
		||||
    ) is not None:
 | 
			
		||||
        cg.add(var.set_open_obstacle_current_threshold(open_obsticle_current_threshold))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_open_duration(config[CONF_OPEN_DURATION]))
 | 
			
		||||
    yield automation.build_automation(
 | 
			
		||||
    await automation.build_automation(
 | 
			
		||||
        var.get_open_trigger(), [], config[CONF_OPEN_ACTION]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # CLOSE
 | 
			
		||||
    bin = yield cg.get_variable(config[CONF_CLOSE_SENSOR])
 | 
			
		||||
    bin = await cg.get_variable(config[CONF_CLOSE_SENSOR])
 | 
			
		||||
    cg.add(var.set_close_sensor(bin))
 | 
			
		||||
    cg.add(
 | 
			
		||||
        var.set_close_moving_current_threshold(
 | 
			
		||||
            config[CONF_CLOSE_MOVING_CURRENT_THRESHOLD]
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    if CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD in config:
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_close_obstacle_current_threshold(
 | 
			
		||||
                config[CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD]
 | 
			
		||||
            )
 | 
			
		||||
    if (
 | 
			
		||||
        close_obsticle_current_threshold := config.get(
 | 
			
		||||
            CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD
 | 
			
		||||
        )
 | 
			
		||||
    ) is not None:
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_close_obstacle_current_threshold(close_obsticle_current_threshold)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION]))
 | 
			
		||||
    yield automation.build_automation(
 | 
			
		||||
    await automation.build_automation(
 | 
			
		||||
        var.get_close_trigger(), [], config[CONF_CLOSE_ACTION]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_obstacle_rollback(config[CONF_OBSTACLE_ROLLBACK]))
 | 
			
		||||
    if CONF_MAX_DURATION in config:
 | 
			
		||||
        cg.add(var.set_max_duration(config[CONF_MAX_DURATION]))
 | 
			
		||||
    if (max_duration := config.get(CONF_MAX_DURATION)) is not None:
 | 
			
		||||
        cg.add(var.set_max_duration(max_duration))
 | 
			
		||||
    cg.add(var.set_malfunction_detection(config[CONF_MALFUNCTION_DETECTION]))
 | 
			
		||||
    if CONF_MALFUNCTION_ACTION in config:
 | 
			
		||||
        yield automation.build_automation(
 | 
			
		||||
            var.get_malfunction_trigger(), [], config[CONF_MALFUNCTION_ACTION]
 | 
			
		||||
    if malfunction_action := config.get(CONF_MALFUNCTION_ACTION):
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_malfunction_trigger(), [], malfunction_action
 | 
			
		||||
        )
 | 
			
		||||
    cg.add(var.set_start_sensing_delay(config[CONF_START_SENSING_DELAY]))
 | 
			
		||||
 
 | 
			
		||||
@@ -37,16 +37,12 @@ async def to_code(config):
 | 
			
		||||
 | 
			
		||||
    cwhite = await cg.get_variable(config[CONF_COLD_WHITE])
 | 
			
		||||
    cg.add(var.set_cold_white(cwhite))
 | 
			
		||||
    if CONF_COLD_WHITE_COLOR_TEMPERATURE in config:
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE])
 | 
			
		||||
        )
 | 
			
		||||
    if cold_white_color_temperature := config.get(CONF_COLD_WHITE_COLOR_TEMPERATURE):
 | 
			
		||||
        cg.add(var.set_cold_white_temperature(cold_white_color_temperature))
 | 
			
		||||
 | 
			
		||||
    wwhite = await cg.get_variable(config[CONF_WARM_WHITE])
 | 
			
		||||
    cg.add(var.set_warm_white(wwhite))
 | 
			
		||||
    if CONF_WARM_WHITE_COLOR_TEMPERATURE in config:
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_warm_white_temperature(config[CONF_WARM_WHITE_COLOR_TEMPERATURE])
 | 
			
		||||
        )
 | 
			
		||||
    if warm_white_color_temperature := config.get(CONF_WARM_WHITE_COLOR_TEMPERATURE):
 | 
			
		||||
        cg.add(var.set_warm_white_temperature(warm_white_color_temperature))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_constant_brightness(config[CONF_CONSTANT_BRIGHTNESS]))
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ from esphome.const import CONF_ID, CONF_ADDRESS
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@s1lvi0"]
 | 
			
		||||
DEPENDENCIES = ["uart"]
 | 
			
		||||
AUTO_LOAD = ["sensor", "text_sensor", "binary_sensor"]
 | 
			
		||||
 | 
			
		||||
CONF_BMS_DALY_ID = "bms_daly_id"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,9 +27,8 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def setup_conf(config, key, hub):
 | 
			
		||||
    if key in config:
 | 
			
		||||
        conf = config[key]
 | 
			
		||||
        var = await binary_sensor.new_binary_sensor(conf)
 | 
			
		||||
    if sensor_config := config.get(key):
 | 
			
		||||
        var = await binary_sensor.new_binary_sensor(sensor_config)
 | 
			
		||||
        cg.add(getattr(hub, f"set_{key}_binary_sensor")(var))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#include "daly_bms.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace daly_bms {
 | 
			
		||||
@@ -19,7 +19,7 @@ static const uint8_t DALY_REQUEST_STATUS = 0x94;
 | 
			
		||||
static const uint8_t DALY_REQUEST_CELL_VOLTAGE = 0x95;
 | 
			
		||||
static const uint8_t DALY_REQUEST_TEMPERATURE = 0x96;
 | 
			
		||||
 | 
			
		||||
void DalyBmsComponent::setup() {}
 | 
			
		||||
void DalyBmsComponent::setup() { this->next_request_ = 1; }
 | 
			
		||||
 | 
			
		||||
void DalyBmsComponent::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Daly BMS:");
 | 
			
		||||
@@ -27,20 +27,78 @@ void DalyBmsComponent::dump_config() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DalyBmsComponent::update() {
 | 
			
		||||
  this->request_data_(DALY_REQUEST_BATTERY_LEVEL);
 | 
			
		||||
  this->request_data_(DALY_REQUEST_MIN_MAX_VOLTAGE);
 | 
			
		||||
  this->request_data_(DALY_REQUEST_MIN_MAX_TEMPERATURE);
 | 
			
		||||
  this->request_data_(DALY_REQUEST_MOS);
 | 
			
		||||
  this->request_data_(DALY_REQUEST_STATUS);
 | 
			
		||||
  this->request_data_(DALY_REQUEST_CELL_VOLTAGE);
 | 
			
		||||
  this->request_data_(DALY_REQUEST_TEMPERATURE);
 | 
			
		||||
  this->trigger_next_ = true;
 | 
			
		||||
  this->next_request_ = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> get_battery_level_data;
 | 
			
		||||
  int available_data = this->available();
 | 
			
		||||
  if (available_data >= DALY_FRAME_SIZE) {
 | 
			
		||||
    get_battery_level_data.resize(available_data);
 | 
			
		||||
    this->read_array(get_battery_level_data.data(), available_data);
 | 
			
		||||
    this->decode_data_(get_battery_level_data);
 | 
			
		||||
void DalyBmsComponent::loop() {
 | 
			
		||||
  const uint32_t now = millis();
 | 
			
		||||
  if (this->receiving_ && (now - this->last_transmission_ >= 200)) {
 | 
			
		||||
    // last transmission too long ago. Reset RX index.
 | 
			
		||||
    ESP_LOGW(TAG, "Last transmission too long ago. Reset RX index.");
 | 
			
		||||
    this->data_.clear();
 | 
			
		||||
    this->receiving_ = false;
 | 
			
		||||
  }
 | 
			
		||||
  if ((now - this->last_transmission_ >= 250) && !this->trigger_next_) {
 | 
			
		||||
    // last transmittion longer than 0.25s ago -> trigger next request
 | 
			
		||||
    this->last_transmission_ = now;
 | 
			
		||||
    this->trigger_next_ = true;
 | 
			
		||||
  }
 | 
			
		||||
  if (available())
 | 
			
		||||
    this->last_transmission_ = now;
 | 
			
		||||
  while (available()) {
 | 
			
		||||
    uint8_t c;
 | 
			
		||||
    read_byte(&c);
 | 
			
		||||
    if (!this->receiving_) {
 | 
			
		||||
      if (c != 0xa5)
 | 
			
		||||
        continue;
 | 
			
		||||
      this->receiving_ = true;
 | 
			
		||||
    }
 | 
			
		||||
    this->data_.push_back(c);
 | 
			
		||||
    if (this->data_.size() == 4)
 | 
			
		||||
      this->data_count_ = c;
 | 
			
		||||
    if ((this->data_.size() > 4) and (data_.size() == this->data_count_ + 5)) {
 | 
			
		||||
      this->decode_data_(this->data_);
 | 
			
		||||
      this->data_.clear();
 | 
			
		||||
      this->receiving_ = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->trigger_next_) {
 | 
			
		||||
    this->trigger_next_ = false;
 | 
			
		||||
    switch (this->next_request_) {
 | 
			
		||||
      case 0:
 | 
			
		||||
        this->request_data_(DALY_REQUEST_BATTERY_LEVEL);
 | 
			
		||||
        this->next_request_ = 1;
 | 
			
		||||
        break;
 | 
			
		||||
      case 1:
 | 
			
		||||
        this->request_data_(DALY_REQUEST_MIN_MAX_VOLTAGE);
 | 
			
		||||
        this->next_request_ = 2;
 | 
			
		||||
        break;
 | 
			
		||||
      case 2:
 | 
			
		||||
        this->request_data_(DALY_REQUEST_MIN_MAX_TEMPERATURE);
 | 
			
		||||
        this->next_request_ = 3;
 | 
			
		||||
        break;
 | 
			
		||||
      case 3:
 | 
			
		||||
        this->request_data_(DALY_REQUEST_MOS);
 | 
			
		||||
        this->next_request_ = 4;
 | 
			
		||||
        break;
 | 
			
		||||
      case 4:
 | 
			
		||||
        this->request_data_(DALY_REQUEST_STATUS);
 | 
			
		||||
        this->next_request_ = 5;
 | 
			
		||||
        break;
 | 
			
		||||
      case 5:
 | 
			
		||||
        this->request_data_(DALY_REQUEST_CELL_VOLTAGE);
 | 
			
		||||
        this->next_request_ = 6;
 | 
			
		||||
        break;
 | 
			
		||||
      case 6:
 | 
			
		||||
        this->request_data_(DALY_REQUEST_TEMPERATURE);
 | 
			
		||||
        this->next_request_ = 7;
 | 
			
		||||
        break;
 | 
			
		||||
      case 7:
 | 
			
		||||
      default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -49,21 +107,23 @@ float DalyBmsComponent::get_setup_priority() const { return setup_priority::DATA
 | 
			
		||||
void DalyBmsComponent::request_data_(uint8_t data_id) {
 | 
			
		||||
  uint8_t request_message[DALY_FRAME_SIZE];
 | 
			
		||||
 | 
			
		||||
  request_message[0] = 0xA5;     // Start Flag
 | 
			
		||||
  request_message[1] = addr_;    // Communication Module Address
 | 
			
		||||
  request_message[2] = data_id;  // Data ID
 | 
			
		||||
  request_message[3] = 0x08;     // Data Length (Fixed)
 | 
			
		||||
  request_message[4] = 0x00;     // Empty Data
 | 
			
		||||
  request_message[5] = 0x00;     //     |
 | 
			
		||||
  request_message[6] = 0x00;     //     |
 | 
			
		||||
  request_message[7] = 0x00;     //     |
 | 
			
		||||
  request_message[8] = 0x00;     //     |
 | 
			
		||||
  request_message[9] = 0x00;     //     |
 | 
			
		||||
  request_message[10] = 0x00;    //     |
 | 
			
		||||
  request_message[11] = 0x00;    // Empty Data
 | 
			
		||||
  request_message[0] = 0xA5;         // Start Flag
 | 
			
		||||
  request_message[1] = this->addr_;  // Communication Module Address
 | 
			
		||||
  request_message[2] = data_id;      // Data ID
 | 
			
		||||
  request_message[3] = 0x08;         // Data Length (Fixed)
 | 
			
		||||
  request_message[4] = 0x00;         // Empty Data
 | 
			
		||||
  request_message[5] = 0x00;         //     |
 | 
			
		||||
  request_message[6] = 0x00;         //     |
 | 
			
		||||
  request_message[7] = 0x00;         //     |
 | 
			
		||||
  request_message[8] = 0x00;         //     |
 | 
			
		||||
  request_message[9] = 0x00;         //     |
 | 
			
		||||
  request_message[10] = 0x00;        //     |
 | 
			
		||||
  request_message[11] = 0x00;        // Empty Data
 | 
			
		||||
 | 
			
		||||
  request_message[12] = (uint8_t) (request_message[0] + request_message[1] + request_message[2] +
 | 
			
		||||
                                   request_message[3]);  // Checksum (Lower byte of the other bytes sum)
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Request datapacket Nr %x", data_id);
 | 
			
		||||
  this->write_array(request_message, sizeof(request_message));
 | 
			
		||||
  this->flush();
 | 
			
		||||
}
 | 
			
		||||
@@ -82,6 +142,7 @@ void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
 | 
			
		||||
 | 
			
		||||
      if (checksum == it[12]) {
 | 
			
		||||
        switch (it[2]) {
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
          case DALY_REQUEST_BATTERY_LEVEL:
 | 
			
		||||
            if (this->voltage_sensor_) {
 | 
			
		||||
              this->voltage_sensor_->publish_state((float) encode_uint16(it[4], it[5]) / 10);
 | 
			
		||||
@@ -95,36 +156,37 @@ void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
          case DALY_REQUEST_MIN_MAX_VOLTAGE:
 | 
			
		||||
            if (this->max_cell_voltage_) {
 | 
			
		||||
              this->max_cell_voltage_->publish_state((float) encode_uint16(it[4], it[5]) / 1000);
 | 
			
		||||
            if (this->max_cell_voltage_sensor_) {
 | 
			
		||||
              this->max_cell_voltage_sensor_->publish_state((float) encode_uint16(it[4], it[5]) / 1000);
 | 
			
		||||
            }
 | 
			
		||||
            if (this->max_cell_voltage_number_) {
 | 
			
		||||
              this->max_cell_voltage_number_->publish_state(it[6]);
 | 
			
		||||
            if (this->max_cell_voltage_number_sensor_) {
 | 
			
		||||
              this->max_cell_voltage_number_sensor_->publish_state(it[6]);
 | 
			
		||||
            }
 | 
			
		||||
            if (this->min_cell_voltage_) {
 | 
			
		||||
              this->min_cell_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
            if (this->min_cell_voltage_sensor_) {
 | 
			
		||||
              this->min_cell_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
            }
 | 
			
		||||
            if (this->min_cell_voltage_number_) {
 | 
			
		||||
              this->min_cell_voltage_number_->publish_state(it[9]);
 | 
			
		||||
            if (this->min_cell_voltage_number_sensor_) {
 | 
			
		||||
              this->min_cell_voltage_number_sensor_->publish_state(it[9]);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
          case DALY_REQUEST_MIN_MAX_TEMPERATURE:
 | 
			
		||||
            if (this->max_temperature_) {
 | 
			
		||||
              this->max_temperature_->publish_state(it[4] - DALY_TEMPERATURE_OFFSET);
 | 
			
		||||
            if (this->max_temperature_sensor_) {
 | 
			
		||||
              this->max_temperature_sensor_->publish_state(it[4] - DALY_TEMPERATURE_OFFSET);
 | 
			
		||||
            }
 | 
			
		||||
            if (this->max_temperature_probe_number_) {
 | 
			
		||||
              this->max_temperature_probe_number_->publish_state(it[5]);
 | 
			
		||||
            if (this->max_temperature_probe_number_sensor_) {
 | 
			
		||||
              this->max_temperature_probe_number_sensor_->publish_state(it[5]);
 | 
			
		||||
            }
 | 
			
		||||
            if (this->min_temperature_) {
 | 
			
		||||
              this->min_temperature_->publish_state(it[6] - DALY_TEMPERATURE_OFFSET);
 | 
			
		||||
            if (this->min_temperature_sensor_) {
 | 
			
		||||
              this->min_temperature_sensor_->publish_state(it[6] - DALY_TEMPERATURE_OFFSET);
 | 
			
		||||
            }
 | 
			
		||||
            if (this->min_temperature_probe_number_) {
 | 
			
		||||
              this->min_temperature_probe_number_->publish_state(it[7]);
 | 
			
		||||
            if (this->min_temperature_probe_number_sensor_) {
 | 
			
		||||
              this->min_temperature_probe_number_sensor_->publish_state(it[7]);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
          case DALY_REQUEST_MOS:
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
            if (this->status_text_sensor_ != nullptr) {
 | 
			
		||||
              switch (it[4]) {
 | 
			
		||||
                case 0:
 | 
			
		||||
@@ -140,20 +202,27 @@ void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
 | 
			
		||||
                  break;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            if (this->charging_mos_enabled_) {
 | 
			
		||||
              this->charging_mos_enabled_->publish_state(it[5]);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
            if (this->charging_mos_enabled_binary_sensor_) {
 | 
			
		||||
              this->charging_mos_enabled_binary_sensor_->publish_state(it[5]);
 | 
			
		||||
            }
 | 
			
		||||
            if (this->discharging_mos_enabled_) {
 | 
			
		||||
              this->discharging_mos_enabled_->publish_state(it[6]);
 | 
			
		||||
            if (this->discharging_mos_enabled_binary_sensor_) {
 | 
			
		||||
              this->discharging_mos_enabled_binary_sensor_->publish_state(it[6]);
 | 
			
		||||
            }
 | 
			
		||||
            if (this->remaining_capacity_) {
 | 
			
		||||
              this->remaining_capacity_->publish_state((float) encode_uint32(it[8], it[9], it[10], it[11]) / 1000);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
            if (this->remaining_capacity_sensor_) {
 | 
			
		||||
              this->remaining_capacity_sensor_->publish_state((float) encode_uint32(it[8], it[9], it[10], it[11]) /
 | 
			
		||||
                                                              1000);
 | 
			
		||||
            }
 | 
			
		||||
#endif
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
          case DALY_REQUEST_STATUS:
 | 
			
		||||
            if (this->cells_number_) {
 | 
			
		||||
              this->cells_number_->publish_state(it[4]);
 | 
			
		||||
            if (this->cells_number_sensor_) {
 | 
			
		||||
              this->cells_number_sensor_->publish_state(it[4]);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
@@ -171,71 +240,73 @@ void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
 | 
			
		||||
          case DALY_REQUEST_CELL_VOLTAGE:
 | 
			
		||||
            switch (it[4]) {
 | 
			
		||||
              case 1:
 | 
			
		||||
                if (this->cell_1_voltage_) {
 | 
			
		||||
                  this->cell_1_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                if (this->cell_1_voltage_sensor_) {
 | 
			
		||||
                  this->cell_1_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                if (this->cell_2_voltage_) {
 | 
			
		||||
                  this->cell_2_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
                if (this->cell_2_voltage_sensor_) {
 | 
			
		||||
                  this->cell_2_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                if (this->cell_3_voltage_) {
 | 
			
		||||
                  this->cell_3_voltage_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
 | 
			
		||||
                if (this->cell_3_voltage_sensor_) {
 | 
			
		||||
                  this->cell_3_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
              case 2:
 | 
			
		||||
                if (this->cell_4_voltage_) {
 | 
			
		||||
                  this->cell_4_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                if (this->cell_4_voltage_sensor_) {
 | 
			
		||||
                  this->cell_4_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                if (this->cell_5_voltage_) {
 | 
			
		||||
                  this->cell_5_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
                if (this->cell_5_voltage_sensor_) {
 | 
			
		||||
                  this->cell_5_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                if (this->cell_6_voltage_) {
 | 
			
		||||
                  this->cell_6_voltage_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
 | 
			
		||||
                if (this->cell_6_voltage_sensor_) {
 | 
			
		||||
                  this->cell_6_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
              case 3:
 | 
			
		||||
                if (this->cell_7_voltage_) {
 | 
			
		||||
                  this->cell_7_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                if (this->cell_7_voltage_sensor_) {
 | 
			
		||||
                  this->cell_7_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                if (this->cell_8_voltage_) {
 | 
			
		||||
                  this->cell_8_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
                if (this->cell_8_voltage_sensor_) {
 | 
			
		||||
                  this->cell_8_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                if (this->cell_9_voltage_) {
 | 
			
		||||
                  this->cell_9_voltage_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
 | 
			
		||||
                if (this->cell_9_voltage_sensor_) {
 | 
			
		||||
                  this->cell_9_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
              case 4:
 | 
			
		||||
                if (this->cell_10_voltage_) {
 | 
			
		||||
                  this->cell_10_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                if (this->cell_10_voltage_sensor_) {
 | 
			
		||||
                  this->cell_10_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                if (this->cell_11_voltage_) {
 | 
			
		||||
                  this->cell_11_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
                if (this->cell_11_voltage_sensor_) {
 | 
			
		||||
                  this->cell_11_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                if (this->cell_12_voltage_) {
 | 
			
		||||
                  this->cell_12_voltage_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
 | 
			
		||||
                if (this->cell_12_voltage_sensor_) {
 | 
			
		||||
                  this->cell_12_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
              case 5:
 | 
			
		||||
                if (this->cell_13_voltage_) {
 | 
			
		||||
                  this->cell_13_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                if (this->cell_13_voltage_sensor_) {
 | 
			
		||||
                  this->cell_13_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                if (this->cell_14_voltage_) {
 | 
			
		||||
                  this->cell_14_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
                if (this->cell_14_voltage_sensor_) {
 | 
			
		||||
                  this->cell_14_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                if (this->cell_15_voltage_) {
 | 
			
		||||
                  this->cell_15_voltage_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
 | 
			
		||||
                if (this->cell_15_voltage_sensor_) {
 | 
			
		||||
                  this->cell_15_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
              case 6:
 | 
			
		||||
                if (this->cell_16_voltage_) {
 | 
			
		||||
                  this->cell_16_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                if (this->cell_16_voltage_sensor_) {
 | 
			
		||||
                  this->cell_16_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
          default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        ESP_LOGW(TAG, "Checksum-Error on Packet %x", it[4]);
 | 
			
		||||
      }
 | 
			
		||||
      std::advance(it, DALY_FRAME_SIZE);
 | 
			
		||||
    } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,16 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
#include "esphome/components/text_sensor/text_sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "esphome/components/uart/uart.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
@@ -15,60 +22,53 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  DalyBmsComponent() = default;
 | 
			
		||||
 | 
			
		||||
  // SENSORS
 | 
			
		||||
  void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
 | 
			
		||||
  void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
 | 
			
		||||
  void set_battery_level_sensor(sensor::Sensor *battery_level_sensor) { battery_level_sensor_ = battery_level_sensor; }
 | 
			
		||||
  void set_max_cell_voltage_sensor(sensor::Sensor *max_cell_voltage) { max_cell_voltage_ = max_cell_voltage; }
 | 
			
		||||
  void set_max_cell_voltage_number_sensor(sensor::Sensor *max_cell_voltage_number) {
 | 
			
		||||
    max_cell_voltage_number_ = max_cell_voltage_number;
 | 
			
		||||
  }
 | 
			
		||||
  void set_min_cell_voltage_sensor(sensor::Sensor *min_cell_voltage) { min_cell_voltage_ = min_cell_voltage; }
 | 
			
		||||
  void set_min_cell_voltage_number_sensor(sensor::Sensor *min_cell_voltage_number) {
 | 
			
		||||
    min_cell_voltage_number_ = min_cell_voltage_number;
 | 
			
		||||
  }
 | 
			
		||||
  void set_max_temperature_sensor(sensor::Sensor *max_temperature) { max_temperature_ = max_temperature; }
 | 
			
		||||
  void set_max_temperature_probe_number_sensor(sensor::Sensor *max_temperature_probe_number) {
 | 
			
		||||
    max_temperature_probe_number_ = max_temperature_probe_number;
 | 
			
		||||
  }
 | 
			
		||||
  void set_min_temperature_sensor(sensor::Sensor *min_temperature) { min_temperature_ = min_temperature; }
 | 
			
		||||
  void set_min_temperature_probe_number_sensor(sensor::Sensor *min_temperature_probe_number) {
 | 
			
		||||
    min_temperature_probe_number_ = min_temperature_probe_number;
 | 
			
		||||
  }
 | 
			
		||||
  void set_remaining_capacity_sensor(sensor::Sensor *remaining_capacity) { remaining_capacity_ = remaining_capacity; }
 | 
			
		||||
  void set_cells_number_sensor(sensor::Sensor *cells_number) { cells_number_ = cells_number; }
 | 
			
		||||
  void set_temperature_1_sensor(sensor::Sensor *temperature_1_sensor) { temperature_1_sensor_ = temperature_1_sensor; }
 | 
			
		||||
  void set_temperature_2_sensor(sensor::Sensor *temperature_2_sensor) { temperature_2_sensor_ = temperature_2_sensor; }
 | 
			
		||||
  void set_cell_1_voltage_sensor(sensor::Sensor *cell_1_voltage) { cell_1_voltage_ = cell_1_voltage; }
 | 
			
		||||
  void set_cell_2_voltage_sensor(sensor::Sensor *cell_2_voltage) { cell_2_voltage_ = cell_2_voltage; }
 | 
			
		||||
  void set_cell_3_voltage_sensor(sensor::Sensor *cell_3_voltage) { cell_3_voltage_ = cell_3_voltage; }
 | 
			
		||||
  void set_cell_4_voltage_sensor(sensor::Sensor *cell_4_voltage) { cell_4_voltage_ = cell_4_voltage; }
 | 
			
		||||
  void set_cell_5_voltage_sensor(sensor::Sensor *cell_5_voltage) { cell_5_voltage_ = cell_5_voltage; }
 | 
			
		||||
  void set_cell_6_voltage_sensor(sensor::Sensor *cell_6_voltage) { cell_6_voltage_ = cell_6_voltage; }
 | 
			
		||||
  void set_cell_7_voltage_sensor(sensor::Sensor *cell_7_voltage) { cell_7_voltage_ = cell_7_voltage; }
 | 
			
		||||
  void set_cell_8_voltage_sensor(sensor::Sensor *cell_8_voltage) { cell_8_voltage_ = cell_8_voltage; }
 | 
			
		||||
  void set_cell_9_voltage_sensor(sensor::Sensor *cell_9_voltage) { cell_9_voltage_ = cell_9_voltage; }
 | 
			
		||||
  void set_cell_10_voltage_sensor(sensor::Sensor *cell_10_voltage) { cell_10_voltage_ = cell_10_voltage; }
 | 
			
		||||
  void set_cell_11_voltage_sensor(sensor::Sensor *cell_11_voltage) { cell_11_voltage_ = cell_11_voltage; }
 | 
			
		||||
  void set_cell_12_voltage_sensor(sensor::Sensor *cell_12_voltage) { cell_12_voltage_ = cell_12_voltage; }
 | 
			
		||||
  void set_cell_13_voltage_sensor(sensor::Sensor *cell_13_voltage) { cell_13_voltage_ = cell_13_voltage; }
 | 
			
		||||
  void set_cell_14_voltage_sensor(sensor::Sensor *cell_14_voltage) { cell_14_voltage_ = cell_14_voltage; }
 | 
			
		||||
  void set_cell_15_voltage_sensor(sensor::Sensor *cell_15_voltage) { cell_15_voltage_ = cell_15_voltage; }
 | 
			
		||||
  void set_cell_16_voltage_sensor(sensor::Sensor *cell_16_voltage) { cell_16_voltage_ = cell_16_voltage; }
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  SUB_SENSOR(voltage)
 | 
			
		||||
  SUB_SENSOR(current)
 | 
			
		||||
  SUB_SENSOR(battery_level)
 | 
			
		||||
  SUB_SENSOR(max_cell_voltage)
 | 
			
		||||
  SUB_SENSOR(max_cell_voltage_number)
 | 
			
		||||
  SUB_SENSOR(min_cell_voltage)
 | 
			
		||||
  SUB_SENSOR(min_cell_voltage_number)
 | 
			
		||||
  SUB_SENSOR(max_temperature)
 | 
			
		||||
  SUB_SENSOR(max_temperature_probe_number)
 | 
			
		||||
  SUB_SENSOR(min_temperature)
 | 
			
		||||
  SUB_SENSOR(min_temperature_probe_number)
 | 
			
		||||
  SUB_SENSOR(remaining_capacity)
 | 
			
		||||
  SUB_SENSOR(cells_number)
 | 
			
		||||
  SUB_SENSOR(temperature_1)
 | 
			
		||||
  SUB_SENSOR(temperature_2)
 | 
			
		||||
  SUB_SENSOR(cell_1_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_2_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_3_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_4_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_5_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_6_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_7_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_8_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_9_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_10_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_11_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_12_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_13_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_14_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_15_voltage)
 | 
			
		||||
  SUB_SENSOR(cell_16_voltage)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // TEXT_SENSORS
 | 
			
		||||
  void set_status_text_sensor(text_sensor::TextSensor *status_text_sensor) { status_text_sensor_ = status_text_sensor; }
 | 
			
		||||
  // BINARY_SENSORS
 | 
			
		||||
  void set_charging_mos_enabled_binary_sensor(binary_sensor::BinarySensor *charging_mos_enabled) {
 | 
			
		||||
    charging_mos_enabled_ = charging_mos_enabled;
 | 
			
		||||
  }
 | 
			
		||||
  void set_discharging_mos_enabled_binary_sensor(binary_sensor::BinarySensor *discharging_mos_enabled) {
 | 
			
		||||
    discharging_mos_enabled_ = discharging_mos_enabled;
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  SUB_TEXT_SENSOR(status)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  SUB_BINARY_SENSOR(charging_mos_enabled)
 | 
			
		||||
  SUB_BINARY_SENSOR(discharging_mos_enabled)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void set_address(uint8_t address) { this->addr_ = address; }
 | 
			
		||||
@@ -79,42 +79,12 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
 | 
			
		||||
  uint8_t addr_;
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *voltage_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *current_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *battery_level_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *max_cell_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *max_cell_voltage_number_{nullptr};
 | 
			
		||||
  sensor::Sensor *min_cell_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *min_cell_voltage_number_{nullptr};
 | 
			
		||||
  sensor::Sensor *max_temperature_{nullptr};
 | 
			
		||||
  sensor::Sensor *max_temperature_probe_number_{nullptr};
 | 
			
		||||
  sensor::Sensor *min_temperature_{nullptr};
 | 
			
		||||
  sensor::Sensor *min_temperature_probe_number_{nullptr};
 | 
			
		||||
  sensor::Sensor *remaining_capacity_{nullptr};
 | 
			
		||||
  sensor::Sensor *cells_number_{nullptr};
 | 
			
		||||
  sensor::Sensor *temperature_1_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *temperature_2_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_1_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_2_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_3_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_4_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_5_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_6_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_7_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_8_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_9_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_10_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_11_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_12_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_13_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_14_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_15_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *cell_16_voltage_{nullptr};
 | 
			
		||||
 | 
			
		||||
  text_sensor::TextSensor *status_text_sensor_{nullptr};
 | 
			
		||||
 | 
			
		||||
  binary_sensor::BinarySensor *charging_mos_enabled_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *discharging_mos_enabled_{nullptr};
 | 
			
		||||
  std::vector<uint8_t> data_;
 | 
			
		||||
  bool receiving_{false};
 | 
			
		||||
  uint8_t data_count_;
 | 
			
		||||
  uint32_t last_transmission_{0};
 | 
			
		||||
  bool trigger_next_;
 | 
			
		||||
  uint8_t next_request_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace daly_bms
 | 
			
		||||
 
 | 
			
		||||
@@ -218,9 +218,8 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def setup_conf(config, key, hub):
 | 
			
		||||
    if key in config:
 | 
			
		||||
        conf = config[key]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
    if sensor_config := config.get(key):
 | 
			
		||||
        sens = await sensor.new_sensor(sensor_config)
 | 
			
		||||
        cg.add(getattr(hub, f"set_{key}_sensor")(sens))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,8 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def setup_conf(config, key, hub):
 | 
			
		||||
    if key in config:
 | 
			
		||||
        conf = config[key]
 | 
			
		||||
        sens = await text_sensor.new_text_sensor(conf)
 | 
			
		||||
    if sensor_config := config.get(key):
 | 
			
		||||
        sens = await text_sensor.new_text_sensor(sensor_config)
 | 
			
		||||
        cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -269,10 +269,7 @@ void Display::do_update_() {
 | 
			
		||||
  } else if (this->writer_.has_value()) {
 | 
			
		||||
    (*this->writer_)(*this);
 | 
			
		||||
  }
 | 
			
		||||
  // remove all not ended clipping regions
 | 
			
		||||
  while (is_clipping()) {
 | 
			
		||||
    end_clipping();
 | 
			
		||||
  }
 | 
			
		||||
  this->clear_clipping_();
 | 
			
		||||
}
 | 
			
		||||
void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) {
 | 
			
		||||
  if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
 | 
			
		||||
@@ -322,13 +319,51 @@ void Display::shrink_clipping(Rect add_rect) {
 | 
			
		||||
    this->clipping_rectangle_.back().shrink(add_rect);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
Rect Display::get_clipping() {
 | 
			
		||||
Rect Display::get_clipping() const {
 | 
			
		||||
  if (this->clipping_rectangle_.empty()) {
 | 
			
		||||
    return Rect();
 | 
			
		||||
  } else {
 | 
			
		||||
    return this->clipping_rectangle_.back();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void Display::clear_clipping_() { this->clipping_rectangle_.clear(); }
 | 
			
		||||
bool Display::clip(int x, int y) {
 | 
			
		||||
  if (x < 0 || x >= this->get_width() || y < 0 || y >= this->get_height())
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!this->get_clipping().inside(x, y))
 | 
			
		||||
    return false;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
bool Display::clamp_x_(int x, int w, int &min_x, int &max_x) {
 | 
			
		||||
  min_x = std::max(x, 0);
 | 
			
		||||
  max_x = std::min(x + w, this->get_width());
 | 
			
		||||
 | 
			
		||||
  if (!this->clipping_rectangle_.empty()) {
 | 
			
		||||
    const auto &rect = this->clipping_rectangle_.back();
 | 
			
		||||
    if (!rect.is_set())
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    min_x = std::max(min_x, (int) rect.x);
 | 
			
		||||
    max_x = std::min(max_x, (int) rect.x2());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return min_x < max_x;
 | 
			
		||||
}
 | 
			
		||||
bool Display::clamp_y_(int y, int h, int &min_y, int &max_y) {
 | 
			
		||||
  min_y = std::max(y, 0);
 | 
			
		||||
  max_y = std::min(y + h, this->get_height());
 | 
			
		||||
 | 
			
		||||
  if (!this->clipping_rectangle_.empty()) {
 | 
			
		||||
    const auto &rect = this->clipping_rectangle_.back();
 | 
			
		||||
    if (!rect.is_set())
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    min_y = std::max(min_y, (int) rect.y);
 | 
			
		||||
    max_y = std::min(max_y, (int) rect.y2());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return min_y < max_y;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {}
 | 
			
		||||
void DisplayPage::show() { this->parent_->show_page(this); }
 | 
			
		||||
 
 | 
			
		||||
@@ -472,14 +472,21 @@ class Display {
 | 
			
		||||
   *
 | 
			
		||||
   * return rect for active clipping region
 | 
			
		||||
   */
 | 
			
		||||
  Rect get_clipping();
 | 
			
		||||
  Rect get_clipping() const;
 | 
			
		||||
 | 
			
		||||
  bool is_clipping() const { return !this->clipping_rectangle_.empty(); }
 | 
			
		||||
 | 
			
		||||
  /** Check if pixel is within region of display.
 | 
			
		||||
   */
 | 
			
		||||
  bool clip(int x, int y);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool clamp_x_(int x, int w, int &min_x, int &max_x);
 | 
			
		||||
  bool clamp_y_(int y, int h, int &min_y, int &max_y);
 | 
			
		||||
  void vprintf_(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, va_list arg);
 | 
			
		||||
 | 
			
		||||
  void do_update_();
 | 
			
		||||
  void clear_clipping_();
 | 
			
		||||
 | 
			
		||||
  DisplayRotation rotation_{DISPLAY_ROTATION_0_DEGREES};
 | 
			
		||||
  optional<display_writer_t> writer_{};
 | 
			
		||||
 
 | 
			
		||||
@@ -60,11 +60,11 @@ void Rect::shrink(Rect rect) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Rect::equal(Rect rect) {
 | 
			
		||||
bool Rect::equal(Rect rect) const {
 | 
			
		||||
  return (rect.x == this->x) && (rect.w == this->w) && (rect.y == this->y) && (rect.h == this->h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Rect::inside(int16_t test_x, int16_t test_y, bool absolute) {  // NOLINT
 | 
			
		||||
bool Rect::inside(int16_t test_x, int16_t test_y, bool absolute) const {  // NOLINT
 | 
			
		||||
  if (!this->is_set()) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
@@ -75,7 +75,7 @@ bool Rect::inside(int16_t test_x, int16_t test_y, bool absolute) {  // NOLINT
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Rect::inside(Rect rect, bool absolute) {
 | 
			
		||||
bool Rect::inside(Rect rect, bool absolute) const {
 | 
			
		||||
  if (!this->is_set() || !rect.is_set()) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -16,19 +16,19 @@ class Rect {
 | 
			
		||||
 | 
			
		||||
  Rect() : x(VALUE_NO_SET), y(VALUE_NO_SET), w(VALUE_NO_SET), h(VALUE_NO_SET) {}  // NOLINT
 | 
			
		||||
  inline Rect(int16_t x, int16_t y, int16_t w, int16_t h) ALWAYS_INLINE : x(x), y(y), w(w), h(h) {}
 | 
			
		||||
  inline int16_t x2() { return this->x + this->w; };  ///< X coordinate of corner
 | 
			
		||||
  inline int16_t y2() { return this->y + this->h; };  ///< Y coordinate of corner
 | 
			
		||||
  inline int16_t x2() const { return this->x + this->w; };  ///< X coordinate of corner
 | 
			
		||||
  inline int16_t y2() const { return this->y + this->h; };  ///< Y coordinate of corner
 | 
			
		||||
 | 
			
		||||
  inline bool is_set() ALWAYS_INLINE { return (this->h != VALUE_NO_SET) && (this->w != VALUE_NO_SET); }
 | 
			
		||||
  inline bool is_set() const ALWAYS_INLINE { return (this->h != VALUE_NO_SET) && (this->w != VALUE_NO_SET); }
 | 
			
		||||
 | 
			
		||||
  void expand(int16_t horizontal, int16_t vertical);
 | 
			
		||||
 | 
			
		||||
  void extend(Rect rect);
 | 
			
		||||
  void shrink(Rect rect);
 | 
			
		||||
 | 
			
		||||
  bool inside(Rect rect, bool absolute = true);
 | 
			
		||||
  bool inside(int16_t test_x, int16_t test_y, bool absolute = true);
 | 
			
		||||
  bool equal(Rect rect);
 | 
			
		||||
  bool inside(Rect rect, bool absolute = true) const;
 | 
			
		||||
  bool inside(int16_t test_x, int16_t test_y, bool absolute = true) const;
 | 
			
		||||
  bool equal(Rect rect) const;
 | 
			
		||||
  void info(const std::string &prefix = "rect info:");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,11 @@ namespace duty_time_sensor {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "duty_time_sensor";
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
void DutyTimeSensor::set_sensor(binary_sensor::BinarySensor *const sensor) {
 | 
			
		||||
  sensor->add_on_state_callback([this](bool state) { this->process_state_(state); });
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void DutyTimeSensor::start() {
 | 
			
		||||
  if (!this->last_state_)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,10 @@
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/preferences.h"
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace duty_time_sensor {
 | 
			
		||||
@@ -22,8 +24,10 @@ class DutyTimeSensor : public sensor::Sensor, public PollingComponent {
 | 
			
		||||
  bool is_running() const { return this->last_state_; }
 | 
			
		||||
  void reset() { this->set_value_(0); }
 | 
			
		||||
 | 
			
		||||
  void set_lambda(std::function<bool()> &&func) { this->func_ = func; }
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  void set_sensor(binary_sensor::BinarySensor *sensor);
 | 
			
		||||
#endif
 | 
			
		||||
  void set_lambda(std::function<bool()> &&func) { this->func_ = func; }
 | 
			
		||||
  void set_last_duty_time_sensor(sensor::Sensor *sensor) { this->last_duty_time_sensor_ = sensor; }
 | 
			
		||||
  void set_restore(bool restore) { this->restore_ = restore; }
 | 
			
		||||
 | 
			
		||||
@@ -43,44 +47,26 @@ class DutyTimeSensor : public sensor::Sensor, public PollingComponent {
 | 
			
		||||
  bool restore_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class StartAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit StartAction(DutyTimeSensor *parent) : parent_(parent) {}
 | 
			
		||||
template<typename... Ts> class BaseAction : public Action<Ts...>, public Parented<DutyTimeSensor> {};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class StartAction : public BaseAction<Ts...> {
 | 
			
		||||
  void play(Ts... x) override { this->parent_->start(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  DutyTimeSensor *parent_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class StopAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit StopAction(DutyTimeSensor *parent) : parent_(parent) {}
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class StopAction : public BaseAction<Ts...> {
 | 
			
		||||
  void play(Ts... x) override { this->parent_->stop(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  DutyTimeSensor *parent_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class ResetAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ResetAction(DutyTimeSensor *parent) : parent_(parent) {}
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class ResetAction : public BaseAction<Ts...> {
 | 
			
		||||
  void play(Ts... x) override { this->parent_->reset(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  DutyTimeSensor *parent_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class RunningCondition : public Condition<Ts...> {
 | 
			
		||||
template<typename... Ts> class RunningCondition : public Condition<Ts...>, public Parented<DutyTimeSensor> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit RunningCondition(DutyTimeSensor *parent, bool state) : parent_(parent), state_(state) {}
 | 
			
		||||
 | 
			
		||||
  bool check(Ts... x) override { return this->parent_->is_running() == this->state_; }
 | 
			
		||||
  explicit RunningCondition(DutyTimeSensor *parent, bool state) : Parented(parent), state_(state) {}
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  DutyTimeSensor *parent_;
 | 
			
		||||
  bool check(Ts... x) override { return this->parent_->is_running() == this->state_; }
 | 
			
		||||
  bool state_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,11 +26,14 @@ duty_time_sensor_ns = cg.esphome_ns.namespace("duty_time_sensor")
 | 
			
		||||
DutyTimeSensor = duty_time_sensor_ns.class_(
 | 
			
		||||
    "DutyTimeSensor", sensor.Sensor, cg.PollingComponent
 | 
			
		||||
)
 | 
			
		||||
StartAction = duty_time_sensor_ns.class_("StartAction", Action)
 | 
			
		||||
StopAction = duty_time_sensor_ns.class_("StopAction", Action)
 | 
			
		||||
ResetAction = duty_time_sensor_ns.class_("ResetAction", Action)
 | 
			
		||||
SetAction = duty_time_sensor_ns.class_("SetAction", Action)
 | 
			
		||||
RunningCondition = duty_time_sensor_ns.class_("RunningCondition", Condition)
 | 
			
		||||
BaseAction = duty_time_sensor_ns.class_("BaseAction", Action, cg.Parented)
 | 
			
		||||
StartAction = duty_time_sensor_ns.class_("StartAction", BaseAction)
 | 
			
		||||
StopAction = duty_time_sensor_ns.class_("StopAction", BaseAction)
 | 
			
		||||
ResetAction = duty_time_sensor_ns.class_("ResetAction", BaseAction)
 | 
			
		||||
SetAction = duty_time_sensor_ns.class_("SetAction", BaseAction)
 | 
			
		||||
RunningCondition = duty_time_sensor_ns.class_(
 | 
			
		||||
    "RunningCondition", Condition, cg.Parented
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
@@ -89,20 +92,23 @@ DUTY_TIME_ID_SCHEMA = maybe_simple_id(
 | 
			
		||||
 | 
			
		||||
@register_action("sensor.duty_time.start", StartAction, DUTY_TIME_ID_SCHEMA)
 | 
			
		||||
async def sensor_runtime_start_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg)
 | 
			
		||||
    await cg.register_parented(var, config[CONF_ID])
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action("sensor.duty_time.stop", StopAction, DUTY_TIME_ID_SCHEMA)
 | 
			
		||||
async def sensor_runtime_stop_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg)
 | 
			
		||||
    await cg.register_parented(var, config[CONF_ID])
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action("sensor.duty_time.reset", ResetAction, DUTY_TIME_ID_SCHEMA)
 | 
			
		||||
async def sensor_runtime_reset_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg)
 | 
			
		||||
    await cg.register_parented(var, config[CONF_ID])
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition(
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet
 | 
			
		||||
  if (universe < first_universe_ || universe > get_last_universe())
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  int output_offset = (universe - first_universe_) * get_lights_per_universe();
 | 
			
		||||
  int32_t output_offset = (universe - first_universe_) * get_lights_per_universe();
 | 
			
		||||
  // limit amount of lights per universe and received
 | 
			
		||||
  int output_end =
 | 
			
		||||
      std::min(it->size(), std::min(output_offset + get_lights_per_universe(), output_offset + packet.count - 1));
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
from typing import Union
 | 
			
		||||
from typing import Union, Optional
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
@@ -42,6 +42,7 @@ from .const import (  # noqa
 | 
			
		||||
    KEY_REFRESH,
 | 
			
		||||
    KEY_REPO,
 | 
			
		||||
    KEY_SDKCONFIG_OPTIONS,
 | 
			
		||||
    KEY_SUBMODULES,
 | 
			
		||||
    KEY_VARIANT,
 | 
			
		||||
    VARIANT_ESP32C3,
 | 
			
		||||
    VARIANT_FRIENDLY,
 | 
			
		||||
@@ -80,6 +81,10 @@ def get_esp32_variant(core_obj=None):
 | 
			
		||||
    return (core_obj or CORE).data[KEY_ESP32][KEY_VARIANT]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_board(core_obj=None):
 | 
			
		||||
    return (core_obj or CORE).data[KEY_ESP32][KEY_BOARD]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def only_on_variant(*, supported=None, unsupported=None):
 | 
			
		||||
    """Config validator for features only available on some ESP32 variants."""
 | 
			
		||||
    if supported is not None and not isinstance(supported, list):
 | 
			
		||||
@@ -120,17 +125,28 @@ def add_idf_sdkconfig_option(name: str, value: SdkconfigValueType):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_idf_component(
 | 
			
		||||
    name: str, repo: str, ref: str = None, path: str = None, refresh: TimePeriod = None
 | 
			
		||||
    *,
 | 
			
		||||
    name: str,
 | 
			
		||||
    repo: str,
 | 
			
		||||
    ref: str = None,
 | 
			
		||||
    path: str = None,
 | 
			
		||||
    refresh: TimePeriod = None,
 | 
			
		||||
    components: Optional[list[str]] = None,
 | 
			
		||||
    submodules: Optional[list[str]] = None,
 | 
			
		||||
):
 | 
			
		||||
    """Add an esp-idf component to the project."""
 | 
			
		||||
    if not CORE.using_esp_idf:
 | 
			
		||||
        raise ValueError("Not an esp-idf project")
 | 
			
		||||
    if components is None:
 | 
			
		||||
        components = []
 | 
			
		||||
    if name not in CORE.data[KEY_ESP32][KEY_COMPONENTS]:
 | 
			
		||||
        CORE.data[KEY_ESP32][KEY_COMPONENTS][name] = {
 | 
			
		||||
            KEY_REPO: repo,
 | 
			
		||||
            KEY_REF: ref,
 | 
			
		||||
            KEY_PATH: path,
 | 
			
		||||
            KEY_REFRESH: refresh,
 | 
			
		||||
            KEY_COMPONENTS: components,
 | 
			
		||||
            KEY_SUBMODULES: submodules,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -163,23 +179,23 @@ RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 0, 5)
 | 
			
		||||
# The platformio/espressif32 version to use for arduino frameworks
 | 
			
		||||
#  - https://github.com/platformio/platform-espressif32/releases
 | 
			
		||||
#  - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
 | 
			
		||||
ARDUINO_PLATFORM_VERSION = cv.Version(5, 3, 0)
 | 
			
		||||
ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0)
 | 
			
		||||
 | 
			
		||||
# The default/recommended esp-idf framework version
 | 
			
		||||
#  - https://github.com/espressif/esp-idf/releases
 | 
			
		||||
#  - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf
 | 
			
		||||
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 4)
 | 
			
		||||
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 5)
 | 
			
		||||
# The platformio/espressif32 version to use for esp-idf frameworks
 | 
			
		||||
#  - https://github.com/platformio/platform-espressif32/releases
 | 
			
		||||
#  - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
 | 
			
		||||
ESP_IDF_PLATFORM_VERSION = cv.Version(5, 3, 0)
 | 
			
		||||
ESP_IDF_PLATFORM_VERSION = cv.Version(5, 4, 0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _arduino_check_versions(value):
 | 
			
		||||
    value = value.copy()
 | 
			
		||||
    lookups = {
 | 
			
		||||
        "dev": (cv.Version(2, 1, 0), "https://github.com/espressif/arduino-esp32.git"),
 | 
			
		||||
        "latest": (cv.Version(2, 0, 7), None),
 | 
			
		||||
        "latest": (cv.Version(2, 0, 9), None),
 | 
			
		||||
        "recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -214,7 +230,7 @@ def _esp_idf_check_versions(value):
 | 
			
		||||
    value = value.copy()
 | 
			
		||||
    lookups = {
 | 
			
		||||
        "dev": (cv.Version(5, 1, 0), "https://github.com/espressif/esp-idf.git"),
 | 
			
		||||
        "latest": (cv.Version(5, 0, 1), None),
 | 
			
		||||
        "latest": (cv.Version(5, 1, 0), None),
 | 
			
		||||
        "recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -536,20 +552,41 @@ def copy_files():
 | 
			
		||||
                    ref=component[KEY_REF],
 | 
			
		||||
                    refresh=component[KEY_REFRESH],
 | 
			
		||||
                    domain="idf_components",
 | 
			
		||||
                    submodules=component[KEY_SUBMODULES],
 | 
			
		||||
                )
 | 
			
		||||
                mkdir_p(CORE.relative_build_path("components"))
 | 
			
		||||
                component_dir = repo_dir
 | 
			
		||||
                if component[KEY_PATH] is not None:
 | 
			
		||||
                    component_dir = component_dir / component[KEY_PATH]
 | 
			
		||||
 | 
			
		||||
                shutil.copytree(
 | 
			
		||||
                    component_dir,
 | 
			
		||||
                    CORE.relative_build_path(f"components/{name}"),
 | 
			
		||||
                    dirs_exist_ok=True,
 | 
			
		||||
                    ignore=shutil.ignore_patterns(".git", ".github"),
 | 
			
		||||
                    symlinks=True,
 | 
			
		||||
                    ignore_dangling_symlinks=True,
 | 
			
		||||
                )
 | 
			
		||||
                if component[KEY_COMPONENTS] == ["*"]:
 | 
			
		||||
                    shutil.copytree(
 | 
			
		||||
                        component_dir,
 | 
			
		||||
                        CORE.relative_build_path("components"),
 | 
			
		||||
                        dirs_exist_ok=True,
 | 
			
		||||
                        ignore=shutil.ignore_patterns(".git*"),
 | 
			
		||||
                        symlinks=True,
 | 
			
		||||
                        ignore_dangling_symlinks=True,
 | 
			
		||||
                    )
 | 
			
		||||
                elif len(component[KEY_COMPONENTS]) > 0:
 | 
			
		||||
                    for comp in component[KEY_COMPONENTS]:
 | 
			
		||||
                        shutil.copytree(
 | 
			
		||||
                            component_dir / comp,
 | 
			
		||||
                            CORE.relative_build_path(f"components/{comp}"),
 | 
			
		||||
                            dirs_exist_ok=True,
 | 
			
		||||
                            ignore=shutil.ignore_patterns(".git*"),
 | 
			
		||||
                            symlinks=True,
 | 
			
		||||
                            ignore_dangling_symlinks=True,
 | 
			
		||||
                        )
 | 
			
		||||
                else:
 | 
			
		||||
                    shutil.copytree(
 | 
			
		||||
                        component_dir,
 | 
			
		||||
                        CORE.relative_build_path(f"components/{name}"),
 | 
			
		||||
                        dirs_exist_ok=True,
 | 
			
		||||
                        ignore=shutil.ignore_patterns(".git*"),
 | 
			
		||||
                        symlinks=True,
 | 
			
		||||
                        ignore_dangling_symlinks=True,
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
    dir = os.path.dirname(__file__)
 | 
			
		||||
    post_build_file = os.path.join(dir, "post_build.py.script")
 | 
			
		||||
 
 | 
			
		||||
@@ -1201,6 +1201,10 @@ BOARDS = {
 | 
			
		||||
        "name": "BPI-Bit",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "bpi_leaf_s3": {
 | 
			
		||||
        "name": "BPI-Leaf-S3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "briki_abc_esp32": {
 | 
			
		||||
        "name": "Briki ABC (MBC-WB) - ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
@@ -1217,6 +1221,10 @@ BOARDS = {
 | 
			
		||||
        "name": "Connaxio's Espoir",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "cytron_maker_feather_aiot_s3": {
 | 
			
		||||
        "name": "Cytron Maker Feather AIoT S3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "d-duino-32": {
 | 
			
		||||
        "name": "D-duino-32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
@@ -1225,6 +1233,10 @@ BOARDS = {
 | 
			
		||||
        "name": "Deneyap Kart 1A",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "deneyapkart1Av2": {
 | 
			
		||||
        "name": "Deneyap Kart 1A v2",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "deneyapkartg": {
 | 
			
		||||
        "name": "Deneyap Kart G",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
@@ -1237,6 +1249,10 @@ BOARDS = {
 | 
			
		||||
        "name": "Deneyap Mini",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "deneyapminiv2": {
 | 
			
		||||
        "name": "Deneyap Mini v2",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "denky32": {
 | 
			
		||||
        "name": "Denky32 (WROOM32)",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
@@ -1265,6 +1281,10 @@ BOARDS = {
 | 
			
		||||
        "name": "Espressif ESP32-C3-DevKitM-1",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-c3-m1i-kit": {
 | 
			
		||||
        "name": "Ai-Thinker ESP-C3-M1-I-Kit",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32cam": {
 | 
			
		||||
        "name": "AI Thinker ESP32-CAM",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
@@ -1329,6 +1349,10 @@ BOARDS = {
 | 
			
		||||
        "name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-s3-korvo-2": {
 | 
			
		||||
        "name": "Espressif ESP32-S3-Korvo-2",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32thing": {
 | 
			
		||||
        "name": "SparkFun ESP32 Thing",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
@@ -1637,6 +1661,10 @@ BOARDS = {
 | 
			
		||||
        "name": "Noduino Quantum",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "redpill_esp32s3": {
 | 
			
		||||
        "name": "Munich Labs RedPill ESP32-S3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "seeed_xiao_esp32c3": {
 | 
			
		||||
        "name": "Seeed Studio XIAO ESP32C3",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ KEY_REPO = "repo"
 | 
			
		||||
KEY_REF = "ref"
 | 
			
		||||
KEY_REFRESH = "refresh"
 | 
			
		||||
KEY_PATH = "path"
 | 
			
		||||
KEY_SUBMODULES = "submodules"
 | 
			
		||||
 | 
			
		||||
VARIANT_ESP32 = "ESP32"
 | 
			
		||||
VARIANT_ESP32S2 = "ESP32S2"
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -166,7 +167,7 @@ std::string ESPBTUUID::to_string() const {
 | 
			
		||||
    case ESP_UUID_LEN_16:
 | 
			
		||||
      return str_snprintf("0x%02X%02X", 6, this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16 & 0xff);
 | 
			
		||||
    case ESP_UUID_LEN_32:
 | 
			
		||||
      return str_snprintf("0x%02X%02X%02X%02X", 10, this->uuid_.uuid.uuid32 >> 24,
 | 
			
		||||
      return str_snprintf("0x%02" PRIX32 "%02" PRIX32 "%02" PRIX32 "%02" PRIX32, 10, (this->uuid_.uuid.uuid32 >> 24),
 | 
			
		||||
                          (this->uuid_.uuid.uuid32 >> 16 & 0xff), (this->uuid_.uuid.uuid32 >> 8 & 0xff),
 | 
			
		||||
                          this->uuid_.uuid.uuid32 & 0xff);
 | 
			
		||||
    default:
 | 
			
		||||
 
 | 
			
		||||
@@ -72,3 +72,4 @@ async def to_code(config):
 | 
			
		||||
 | 
			
		||||
    if CORE.using_esp_idf:
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True)
 | 
			
		||||
 
 | 
			
		||||
@@ -263,6 +263,7 @@ async def to_code(config):
 | 
			
		||||
        # Match arduino CONFIG_BTU_TASK_STACK_SIZE
 | 
			
		||||
        # https://github.com/espressif/arduino-esp32/blob/fd72cf46ad6fc1a6de99c1d83ba8eba17d80a4ee/tools/sdk/esp32/sdkconfig#L1866
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_BTU_TASK_STACK_SIZE", 8192)
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_BT_ACL_CONNECTIONS", 9)
 | 
			
		||||
 | 
			
		||||
    cg.add_define("USE_OTA_STATE_CALLBACK")  # To be notified when an OTA update starts
 | 
			
		||||
    cg.add_define("USE_ESP32_BLE_CLIENT")
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
#include <freertos/FreeRTOSConfig.h>
 | 
			
		||||
#include <freertos/task.h>
 | 
			
		||||
#include <nvs_flash.h>
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
#ifdef USE_OTA
 | 
			
		||||
#include "esphome/components/ota/ota_component.h"
 | 
			
		||||
@@ -614,7 +615,7 @@ uint64_t ESPBTDevice::address_uint64() const { return esp32_ble::ble_addr_to_uin
 | 
			
		||||
 | 
			
		||||
void ESP32BLETracker::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BLE Tracker:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Scan Duration: %u s", this->scan_duration_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Scan Duration: %" PRIu32 " s", this->scan_duration_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Scan Interval: %.1f ms", this->scan_interval_ * 0.625f);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Scan Window: %.1f ms", this->scan_window_ * 0.625f);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Scan Type: %s", this->scan_active_ ? "ACTIVE" : "PASSIVE");
 | 
			
		||||
 
 | 
			
		||||
@@ -12,25 +12,112 @@ from esphome.const import (
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import TimePeriod
 | 
			
		||||
from esphome.components import esp32
 | 
			
		||||
from esphome.components.esp32 import get_esp32_variant, gpio
 | 
			
		||||
from esphome.components.esp32.const import (
 | 
			
		||||
    VARIANT_ESP32,
 | 
			
		||||
    VARIANT_ESP32S2,
 | 
			
		||||
    VARIANT_ESP32S3,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["binary_sensor"]
 | 
			
		||||
DEPENDENCIES = ["esp32"]
 | 
			
		||||
 | 
			
		||||
CONF_DEBOUNCE_COUNT = "debounce_count"
 | 
			
		||||
CONF_DENOISE_GRADE = "denoise_grade"
 | 
			
		||||
CONF_DENOISE_CAP_LEVEL = "denoise_cap_level"
 | 
			
		||||
CONF_FILTER_MODE = "filter_mode"
 | 
			
		||||
CONF_NOISE_THRESHOLD = "noise_threshold"
 | 
			
		||||
CONF_JITTER_STEP = "jitter_step"
 | 
			
		||||
CONF_SMOOTH_MODE = "smooth_mode"
 | 
			
		||||
CONF_WATERPROOF_GUARD_RING = "waterproof_guard_ring"
 | 
			
		||||
CONF_WATERPROOF_SHIELD_DRIVER = "waterproof_shield_driver"
 | 
			
		||||
 | 
			
		||||
esp32_touch_ns = cg.esphome_ns.namespace("esp32_touch")
 | 
			
		||||
ESP32TouchComponent = esp32_touch_ns.class_("ESP32TouchComponent", cg.Component)
 | 
			
		||||
 | 
			
		||||
TOUCH_PADS = {
 | 
			
		||||
    VARIANT_ESP32: {
 | 
			
		||||
        4: cg.global_ns.TOUCH_PAD_NUM0,
 | 
			
		||||
        0: cg.global_ns.TOUCH_PAD_NUM1,
 | 
			
		||||
        2: cg.global_ns.TOUCH_PAD_NUM2,
 | 
			
		||||
        15: cg.global_ns.TOUCH_PAD_NUM3,
 | 
			
		||||
        13: cg.global_ns.TOUCH_PAD_NUM4,
 | 
			
		||||
        12: cg.global_ns.TOUCH_PAD_NUM5,
 | 
			
		||||
        14: cg.global_ns.TOUCH_PAD_NUM6,
 | 
			
		||||
        27: cg.global_ns.TOUCH_PAD_NUM7,
 | 
			
		||||
        33: cg.global_ns.TOUCH_PAD_NUM8,
 | 
			
		||||
        32: cg.global_ns.TOUCH_PAD_NUM9,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32S2: {
 | 
			
		||||
        1: cg.global_ns.TOUCH_PAD_NUM1,
 | 
			
		||||
        2: cg.global_ns.TOUCH_PAD_NUM2,
 | 
			
		||||
        3: cg.global_ns.TOUCH_PAD_NUM3,
 | 
			
		||||
        4: cg.global_ns.TOUCH_PAD_NUM4,
 | 
			
		||||
        5: cg.global_ns.TOUCH_PAD_NUM5,
 | 
			
		||||
        6: cg.global_ns.TOUCH_PAD_NUM6,
 | 
			
		||||
        7: cg.global_ns.TOUCH_PAD_NUM7,
 | 
			
		||||
        8: cg.global_ns.TOUCH_PAD_NUM8,
 | 
			
		||||
        9: cg.global_ns.TOUCH_PAD_NUM9,
 | 
			
		||||
        10: cg.global_ns.TOUCH_PAD_NUM10,
 | 
			
		||||
        11: cg.global_ns.TOUCH_PAD_NUM11,
 | 
			
		||||
        12: cg.global_ns.TOUCH_PAD_NUM12,
 | 
			
		||||
        13: cg.global_ns.TOUCH_PAD_NUM13,
 | 
			
		||||
        14: cg.global_ns.TOUCH_PAD_NUM14,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32S3: {
 | 
			
		||||
        1: cg.global_ns.TOUCH_PAD_NUM1,
 | 
			
		||||
        2: cg.global_ns.TOUCH_PAD_NUM2,
 | 
			
		||||
        3: cg.global_ns.TOUCH_PAD_NUM3,
 | 
			
		||||
        4: cg.global_ns.TOUCH_PAD_NUM4,
 | 
			
		||||
        5: cg.global_ns.TOUCH_PAD_NUM5,
 | 
			
		||||
        6: cg.global_ns.TOUCH_PAD_NUM6,
 | 
			
		||||
        7: cg.global_ns.TOUCH_PAD_NUM7,
 | 
			
		||||
        8: cg.global_ns.TOUCH_PAD_NUM8,
 | 
			
		||||
        9: cg.global_ns.TOUCH_PAD_NUM9,
 | 
			
		||||
        10: cg.global_ns.TOUCH_PAD_NUM10,
 | 
			
		||||
        11: cg.global_ns.TOUCH_PAD_NUM11,
 | 
			
		||||
        12: cg.global_ns.TOUCH_PAD_NUM12,
 | 
			
		||||
        13: cg.global_ns.TOUCH_PAD_NUM13,
 | 
			
		||||
        14: cg.global_ns.TOUCH_PAD_NUM14,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def validate_voltage(values):
 | 
			
		||||
    def validator(value):
 | 
			
		||||
        if isinstance(value, float) and value.is_integer():
 | 
			
		||||
            value = int(value)
 | 
			
		||||
        value = cv.string(value)
 | 
			
		||||
        if not value.endswith("V"):
 | 
			
		||||
            value += "V"
 | 
			
		||||
        return cv.one_of(*values)(value)
 | 
			
		||||
 | 
			
		||||
    return validator
 | 
			
		||||
TOUCH_PAD_DENOISE_GRADE = {
 | 
			
		||||
    "BIT12": cg.global_ns.TOUCH_PAD_DENOISE_BIT12,
 | 
			
		||||
    "BIT10": cg.global_ns.TOUCH_PAD_DENOISE_BIT10,
 | 
			
		||||
    "BIT8": cg.global_ns.TOUCH_PAD_DENOISE_BIT8,
 | 
			
		||||
    "BIT4": cg.global_ns.TOUCH_PAD_DENOISE_BIT4,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TOUCH_PAD_DENOISE_CAP_LEVEL = {
 | 
			
		||||
    "L0": cg.global_ns.TOUCH_PAD_DENOISE_CAP_L0,
 | 
			
		||||
    "L1": cg.global_ns.TOUCH_PAD_DENOISE_CAP_L1,
 | 
			
		||||
    "L2": cg.global_ns.TOUCH_PAD_DENOISE_CAP_L2,
 | 
			
		||||
    "L3": cg.global_ns.TOUCH_PAD_DENOISE_CAP_L3,
 | 
			
		||||
    "L4": cg.global_ns.TOUCH_PAD_DENOISE_CAP_L4,
 | 
			
		||||
    "L5": cg.global_ns.TOUCH_PAD_DENOISE_CAP_L5,
 | 
			
		||||
    "L6": cg.global_ns.TOUCH_PAD_DENOISE_CAP_L6,
 | 
			
		||||
    "L7": cg.global_ns.TOUCH_PAD_DENOISE_CAP_L7,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TOUCH_PAD_FILTER_MODE = {
 | 
			
		||||
    "IIR_4": cg.global_ns.TOUCH_PAD_FILTER_IIR_4,
 | 
			
		||||
    "IIR_8": cg.global_ns.TOUCH_PAD_FILTER_IIR_8,
 | 
			
		||||
    "IIR_16": cg.global_ns.TOUCH_PAD_FILTER_IIR_16,
 | 
			
		||||
    "IIR_32": cg.global_ns.TOUCH_PAD_FILTER_IIR_32,
 | 
			
		||||
    "IIR_64": cg.global_ns.TOUCH_PAD_FILTER_IIR_64,
 | 
			
		||||
    "IIR_128": cg.global_ns.TOUCH_PAD_FILTER_IIR_128,
 | 
			
		||||
    "IIR_256": cg.global_ns.TOUCH_PAD_FILTER_IIR_256,
 | 
			
		||||
    "JITTER": cg.global_ns.TOUCH_PAD_FILTER_JITTER,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TOUCH_PAD_SMOOTH_MODE = {
 | 
			
		||||
    "OFF": cg.global_ns.TOUCH_PAD_SMOOTH_OFF,
 | 
			
		||||
    "IIR_2": cg.global_ns.TOUCH_PAD_SMOOTH_IIR_2,
 | 
			
		||||
    "IIR_4": cg.global_ns.TOUCH_PAD_SMOOTH_IIR_4,
 | 
			
		||||
    "IIR_8": cg.global_ns.TOUCH_PAD_SMOOTH_IIR_8,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LOW_VOLTAGE_REFERENCE = {
 | 
			
		||||
    "0.5V": cg.global_ns.TOUCH_LVOLT_0V5,
 | 
			
		||||
@@ -50,15 +137,74 @@ VOLTAGE_ATTENUATION = {
 | 
			
		||||
    "0.5V": cg.global_ns.TOUCH_HVOLT_ATTEN_0V5,
 | 
			
		||||
    "0V": cg.global_ns.TOUCH_HVOLT_ATTEN_0V,
 | 
			
		||||
}
 | 
			
		||||
TOUCH_PAD_WATERPROOF_SHIELD_DRIVER = {
 | 
			
		||||
    "L0": cg.global_ns.TOUCH_PAD_SHIELD_DRV_L0,
 | 
			
		||||
    "L1": cg.global_ns.TOUCH_PAD_SHIELD_DRV_L1,
 | 
			
		||||
    "L2": cg.global_ns.TOUCH_PAD_SHIELD_DRV_L2,
 | 
			
		||||
    "L3": cg.global_ns.TOUCH_PAD_SHIELD_DRV_L3,
 | 
			
		||||
    "L4": cg.global_ns.TOUCH_PAD_SHIELD_DRV_L4,
 | 
			
		||||
    "L5": cg.global_ns.TOUCH_PAD_SHIELD_DRV_L5,
 | 
			
		||||
    "L6": cg.global_ns.TOUCH_PAD_SHIELD_DRV_L6,
 | 
			
		||||
    "L7": cg.global_ns.TOUCH_PAD_SHIELD_DRV_L7,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_touch_pad(value):
 | 
			
		||||
    value = gpio.validate_gpio_pin(value)
 | 
			
		||||
    variant = get_esp32_variant()
 | 
			
		||||
    if variant not in TOUCH_PADS:
 | 
			
		||||
        raise cv.Invalid(f"ESP32 variant {variant} does not support touch pads.")
 | 
			
		||||
 | 
			
		||||
    pads = TOUCH_PADS[variant]
 | 
			
		||||
    if value not in pads:
 | 
			
		||||
        raise cv.Invalid(f"Pin {value} does not support touch pads.")
 | 
			
		||||
    return cv.enum(pads)(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_variant_vars(config):
 | 
			
		||||
    if get_esp32_variant() == VARIANT_ESP32:
 | 
			
		||||
        variant_vars = {
 | 
			
		||||
            CONF_DEBOUNCE_COUNT,
 | 
			
		||||
            CONF_DENOISE_GRADE,
 | 
			
		||||
            CONF_DENOISE_CAP_LEVEL,
 | 
			
		||||
            CONF_FILTER_MODE,
 | 
			
		||||
            CONF_NOISE_THRESHOLD,
 | 
			
		||||
            CONF_JITTER_STEP,
 | 
			
		||||
            CONF_SMOOTH_MODE,
 | 
			
		||||
            CONF_WATERPROOF_GUARD_RING,
 | 
			
		||||
            CONF_WATERPROOF_SHIELD_DRIVER,
 | 
			
		||||
        }
 | 
			
		||||
        for vvar in variant_vars:
 | 
			
		||||
            if vvar in config:
 | 
			
		||||
                raise cv.Invalid(f"{vvar} is not valid on {VARIANT_ESP32}")
 | 
			
		||||
    elif (
 | 
			
		||||
        get_esp32_variant() == VARIANT_ESP32S2 or get_esp32_variant() == VARIANT_ESP32S3
 | 
			
		||||
    ) and CONF_IIR_FILTER in config:
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            f"{CONF_IIR_FILTER} is not valid on {VARIANT_ESP32S2} or {VARIANT_ESP32S3}"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_voltage(values):
 | 
			
		||||
    def validator(value):
 | 
			
		||||
        if isinstance(value, float) and value.is_integer():
 | 
			
		||||
            value = int(value)
 | 
			
		||||
        value = cv.string(value)
 | 
			
		||||
        if not value.endswith("V"):
 | 
			
		||||
            value += "V"
 | 
			
		||||
        return cv.one_of(*values)(value)
 | 
			
		||||
 | 
			
		||||
    return validator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(ESP32TouchComponent),
 | 
			
		||||
            cv.Optional(CONF_SETUP_MODE, default=False): cv.boolean,
 | 
			
		||||
            cv.Optional(
 | 
			
		||||
                CONF_IIR_FILTER, default="0ms"
 | 
			
		||||
            ): cv.positive_time_period_milliseconds,
 | 
			
		||||
            # common options
 | 
			
		||||
            cv.Optional(CONF_SLEEP_DURATION, default="27306us"): cv.All(
 | 
			
		||||
                cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=436906))
 | 
			
		||||
            ),
 | 
			
		||||
@@ -74,13 +220,47 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Optional(CONF_VOLTAGE_ATTENUATION, default="0V"): validate_voltage(
 | 
			
		||||
                VOLTAGE_ATTENUATION
 | 
			
		||||
            ),
 | 
			
		||||
            # ESP32 only
 | 
			
		||||
            cv.Optional(CONF_IIR_FILTER): cv.positive_time_period_milliseconds,
 | 
			
		||||
            # ESP32-S2/S3 only
 | 
			
		||||
            cv.Optional(CONF_DEBOUNCE_COUNT): cv.int_range(min=0, max=7),
 | 
			
		||||
            cv.Optional(CONF_FILTER_MODE): cv.enum(
 | 
			
		||||
                TOUCH_PAD_FILTER_MODE, upper=True, space="_"
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_NOISE_THRESHOLD): cv.int_range(min=0, max=3),
 | 
			
		||||
            cv.Optional(CONF_JITTER_STEP): cv.int_range(min=0, max=15),
 | 
			
		||||
            cv.Optional(CONF_SMOOTH_MODE): cv.enum(
 | 
			
		||||
                TOUCH_PAD_SMOOTH_MODE, upper=True, space="_"
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_DENOISE_GRADE): cv.enum(
 | 
			
		||||
                TOUCH_PAD_DENOISE_GRADE, upper=True, space="_"
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_DENOISE_CAP_LEVEL): cv.enum(
 | 
			
		||||
                TOUCH_PAD_DENOISE_CAP_LEVEL, upper=True, space="_"
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_WATERPROOF_GUARD_RING): validate_touch_pad,
 | 
			
		||||
            cv.Optional(CONF_WATERPROOF_SHIELD_DRIVER): cv.enum(
 | 
			
		||||
                TOUCH_PAD_WATERPROOF_SHIELD_DRIVER, upper=True, space="_"
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    ).extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
    cv.has_none_or_all_keys(CONF_DENOISE_GRADE, CONF_DENOISE_CAP_LEVEL),
 | 
			
		||||
    cv.has_none_or_all_keys(
 | 
			
		||||
        CONF_DEBOUNCE_COUNT,
 | 
			
		||||
        CONF_FILTER_MODE,
 | 
			
		||||
        CONF_NOISE_THRESHOLD,
 | 
			
		||||
        CONF_JITTER_STEP,
 | 
			
		||||
        CONF_SMOOTH_MODE,
 | 
			
		||||
    ),
 | 
			
		||||
    cv.has_none_or_all_keys(CONF_WATERPROOF_GUARD_RING, CONF_WATERPROOF_SHIELD_DRIVER),
 | 
			
		||||
    esp32.only_on_variant(
 | 
			
		||||
        supported=[
 | 
			
		||||
            esp32.const.VARIANT_ESP32,
 | 
			
		||||
            esp32.const.VARIANT_ESP32S2,
 | 
			
		||||
            esp32.const.VARIANT_ESP32S3,
 | 
			
		||||
        ]
 | 
			
		||||
    ),
 | 
			
		||||
    validate_variant_vars,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -89,7 +269,6 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(touch, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(touch.set_setup_mode(config[CONF_SETUP_MODE]))
 | 
			
		||||
    cg.add(touch.set_iir_filter(config[CONF_IIR_FILTER]))
 | 
			
		||||
 | 
			
		||||
    sleep_duration = int(round(config[CONF_SLEEP_DURATION].total_microseconds * 0.15))
 | 
			
		||||
    cg.add(touch.set_sleep_duration(sleep_duration))
 | 
			
		||||
@@ -114,3 +293,33 @@ async def to_code(config):
 | 
			
		||||
            VOLTAGE_ATTENUATION[config[CONF_VOLTAGE_ATTENUATION]]
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if get_esp32_variant() == VARIANT_ESP32:
 | 
			
		||||
        if CONF_IIR_FILTER in config:
 | 
			
		||||
            cg.add(touch.set_iir_filter(config[CONF_IIR_FILTER]))
 | 
			
		||||
 | 
			
		||||
    if get_esp32_variant() == VARIANT_ESP32S2 or get_esp32_variant() == VARIANT_ESP32S3:
 | 
			
		||||
        if CONF_FILTER_MODE in config:
 | 
			
		||||
            cg.add(touch.set_filter_mode(config[CONF_FILTER_MODE]))
 | 
			
		||||
        if CONF_DEBOUNCE_COUNT in config:
 | 
			
		||||
            cg.add(touch.set_debounce_count(config[CONF_DEBOUNCE_COUNT]))
 | 
			
		||||
        if CONF_NOISE_THRESHOLD in config:
 | 
			
		||||
            cg.add(touch.set_noise_threshold(config[CONF_NOISE_THRESHOLD]))
 | 
			
		||||
        if CONF_JITTER_STEP in config:
 | 
			
		||||
            cg.add(touch.set_jitter_step(config[CONF_JITTER_STEP]))
 | 
			
		||||
        if CONF_SMOOTH_MODE in config:
 | 
			
		||||
            cg.add(touch.set_smooth_level(config[CONF_SMOOTH_MODE]))
 | 
			
		||||
        if CONF_DENOISE_GRADE in config:
 | 
			
		||||
            cg.add(touch.set_denoise_grade(config[CONF_DENOISE_GRADE]))
 | 
			
		||||
        if CONF_DENOISE_CAP_LEVEL in config:
 | 
			
		||||
            cg.add(touch.set_denoise_cap(config[CONF_DENOISE_CAP_LEVEL]))
 | 
			
		||||
        if CONF_WATERPROOF_GUARD_RING in config:
 | 
			
		||||
            cg.add(
 | 
			
		||||
                touch.set_waterproof_guard_ring_pad(config[CONF_WATERPROOF_GUARD_RING])
 | 
			
		||||
            )
 | 
			
		||||
        if CONF_WATERPROOF_SHIELD_DRIVER in config:
 | 
			
		||||
            cg.add(
 | 
			
		||||
                touch.set_waterproof_shield_driver(
 | 
			
		||||
                    config[CONF_WATERPROOF_SHIELD_DRIVER]
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
 
 | 
			
		||||
@@ -1,87 +1,18 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
from esphome.components import binary_sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_PIN,
 | 
			
		||||
    CONF_THRESHOLD,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
)
 | 
			
		||||
from esphome.components.esp32 import gpio
 | 
			
		||||
from esphome.components.esp32.const import (
 | 
			
		||||
    KEY_ESP32,
 | 
			
		||||
    KEY_VARIANT,
 | 
			
		||||
    VARIANT_ESP32,
 | 
			
		||||
    VARIANT_ESP32S2,
 | 
			
		||||
    VARIANT_ESP32S3,
 | 
			
		||||
)
 | 
			
		||||
from . import esp32_touch_ns, ESP32TouchComponent
 | 
			
		||||
from . import esp32_touch_ns, ESP32TouchComponent, validate_touch_pad
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["esp32_touch", "esp32"]
 | 
			
		||||
 | 
			
		||||
CONF_ESP32_TOUCH_ID = "esp32_touch_id"
 | 
			
		||||
CONF_WAKEUP_THRESHOLD = "wakeup_threshold"
 | 
			
		||||
 | 
			
		||||
TOUCH_PADS = {
 | 
			
		||||
    VARIANT_ESP32: {
 | 
			
		||||
        4: cg.global_ns.TOUCH_PAD_NUM0,
 | 
			
		||||
        0: cg.global_ns.TOUCH_PAD_NUM1,
 | 
			
		||||
        2: cg.global_ns.TOUCH_PAD_NUM2,
 | 
			
		||||
        15: cg.global_ns.TOUCH_PAD_NUM3,
 | 
			
		||||
        13: cg.global_ns.TOUCH_PAD_NUM4,
 | 
			
		||||
        12: cg.global_ns.TOUCH_PAD_NUM5,
 | 
			
		||||
        14: cg.global_ns.TOUCH_PAD_NUM6,
 | 
			
		||||
        27: cg.global_ns.TOUCH_PAD_NUM7,
 | 
			
		||||
        33: cg.global_ns.TOUCH_PAD_NUM8,
 | 
			
		||||
        32: cg.global_ns.TOUCH_PAD_NUM9,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32S2: {
 | 
			
		||||
        1: cg.global_ns.TOUCH_PAD_NUM1,
 | 
			
		||||
        2: cg.global_ns.TOUCH_PAD_NUM2,
 | 
			
		||||
        3: cg.global_ns.TOUCH_PAD_NUM3,
 | 
			
		||||
        4: cg.global_ns.TOUCH_PAD_NUM4,
 | 
			
		||||
        5: cg.global_ns.TOUCH_PAD_NUM5,
 | 
			
		||||
        6: cg.global_ns.TOUCH_PAD_NUM6,
 | 
			
		||||
        7: cg.global_ns.TOUCH_PAD_NUM7,
 | 
			
		||||
        8: cg.global_ns.TOUCH_PAD_NUM8,
 | 
			
		||||
        9: cg.global_ns.TOUCH_PAD_NUM9,
 | 
			
		||||
        10: cg.global_ns.TOUCH_PAD_NUM10,
 | 
			
		||||
        11: cg.global_ns.TOUCH_PAD_NUM11,
 | 
			
		||||
        12: cg.global_ns.TOUCH_PAD_NUM12,
 | 
			
		||||
        13: cg.global_ns.TOUCH_PAD_NUM13,
 | 
			
		||||
        14: cg.global_ns.TOUCH_PAD_NUM14,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32S3: {
 | 
			
		||||
        1: cg.global_ns.TOUCH_PAD_NUM1,
 | 
			
		||||
        2: cg.global_ns.TOUCH_PAD_NUM2,
 | 
			
		||||
        3: cg.global_ns.TOUCH_PAD_NUM3,
 | 
			
		||||
        4: cg.global_ns.TOUCH_PAD_NUM4,
 | 
			
		||||
        5: cg.global_ns.TOUCH_PAD_NUM5,
 | 
			
		||||
        6: cg.global_ns.TOUCH_PAD_NUM6,
 | 
			
		||||
        7: cg.global_ns.TOUCH_PAD_NUM7,
 | 
			
		||||
        8: cg.global_ns.TOUCH_PAD_NUM8,
 | 
			
		||||
        9: cg.global_ns.TOUCH_PAD_NUM9,
 | 
			
		||||
        10: cg.global_ns.TOUCH_PAD_NUM10,
 | 
			
		||||
        11: cg.global_ns.TOUCH_PAD_NUM11,
 | 
			
		||||
        12: cg.global_ns.TOUCH_PAD_NUM12,
 | 
			
		||||
        13: cg.global_ns.TOUCH_PAD_NUM13,
 | 
			
		||||
        14: cg.global_ns.TOUCH_PAD_NUM14,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_touch_pad(value):
 | 
			
		||||
    value = gpio.validate_gpio_pin(value)
 | 
			
		||||
    variant = CORE.data[KEY_ESP32][KEY_VARIANT]
 | 
			
		||||
    if variant not in TOUCH_PADS:
 | 
			
		||||
        raise cv.Invalid(f"ESP32 variant {variant} does not support touch pads.")
 | 
			
		||||
 | 
			
		||||
    pads = TOUCH_PADS[variant]
 | 
			
		||||
    if value not in pads:
 | 
			
		||||
        raise cv.Invalid(f"Pin {value} does not support touch pads.")
 | 
			
		||||
    return cv.enum(pads)(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ESP32TouchBinarySensor = esp32_touch_ns.class_(
 | 
			
		||||
    "ESP32TouchBinarySensor", binary_sensor.BinarySensor
 | 
			
		||||
)
 | 
			
		||||
@@ -90,8 +21,8 @@ CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(ESP32TouchBinarySensor).exten
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_id(ESP32TouchComponent),
 | 
			
		||||
        cv.Required(CONF_PIN): validate_touch_pad,
 | 
			
		||||
        cv.Required(CONF_THRESHOLD): cv.uint16_t,
 | 
			
		||||
        cv.Optional(CONF_WAKEUP_THRESHOLD, default=0): cv.uint16_t,
 | 
			
		||||
        cv.Required(CONF_THRESHOLD): cv.uint32_t,
 | 
			
		||||
        cv.Optional(CONF_WAKEUP_THRESHOLD, default=0): cv.uint32_t,
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32_touch {
 | 
			
		||||
 | 
			
		||||
@@ -13,18 +15,58 @@ static const char *const TAG = "esp32_touch";
 | 
			
		||||
void ESP32TouchComponent::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ESP32 Touch Hub...");
 | 
			
		||||
  touch_pad_init();
 | 
			
		||||
// set up and enable/start filtering based on ESP32 variant
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
  if (this->filter_configured_()) {
 | 
			
		||||
    touch_filter_config_t filter_info = {
 | 
			
		||||
        .mode = this->filter_mode_,
 | 
			
		||||
        .debounce_cnt = this->debounce_count_,
 | 
			
		||||
        .noise_thr = this->noise_threshold_,
 | 
			
		||||
        .jitter_step = this->jitter_step_,
 | 
			
		||||
        .smh_lvl = this->smooth_level_,
 | 
			
		||||
    };
 | 
			
		||||
    touch_pad_filter_set_config(&filter_info);
 | 
			
		||||
    touch_pad_filter_enable();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->denoise_configured_()) {
 | 
			
		||||
    touch_pad_denoise_t denoise = {
 | 
			
		||||
        .grade = this->grade_,
 | 
			
		||||
        .cap_level = this->cap_level_,
 | 
			
		||||
    };
 | 
			
		||||
    touch_pad_denoise_set_config(&denoise);
 | 
			
		||||
    touch_pad_denoise_enable();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->waterproof_configured_()) {
 | 
			
		||||
    touch_pad_waterproof_t waterproof = {
 | 
			
		||||
        .guard_ring_pad = this->waterproof_guard_ring_pad_,
 | 
			
		||||
        .shield_driver = this->waterproof_shield_driver_,
 | 
			
		||||
    };
 | 
			
		||||
    touch_pad_waterproof_set_config(&waterproof);
 | 
			
		||||
    touch_pad_waterproof_enable();
 | 
			
		||||
  }
 | 
			
		||||
#else
 | 
			
		||||
  if (this->iir_filter_enabled_()) {
 | 
			
		||||
    touch_pad_filter_start(this->iir_filter_);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  touch_pad_set_meas_time(this->sleep_cycle_, this->meas_cycle_);
 | 
			
		||||
  touch_pad_set_voltage(this->high_voltage_reference_, this->low_voltage_reference_, this->voltage_attenuation_);
 | 
			
		||||
 | 
			
		||||
  for (auto *child : this->children_) {
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    touch_pad_config(child->get_touch_pad());
 | 
			
		||||
#else
 | 
			
		||||
    // Disable interrupt threshold
 | 
			
		||||
    touch_pad_config(child->get_touch_pad(), 0);
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
  touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
 | 
			
		||||
  touch_pad_fsm_start();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32TouchComponent::dump_config() {
 | 
			
		||||
@@ -92,38 +134,168 @@ void ESP32TouchComponent::dump_config() {
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Voltage Attenuation: %s", atten_s);
 | 
			
		||||
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
  if (this->filter_configured_()) {
 | 
			
		||||
    const char *filter_mode_s;
 | 
			
		||||
    switch (this->filter_mode_) {
 | 
			
		||||
      case TOUCH_PAD_FILTER_IIR_4:
 | 
			
		||||
        filter_mode_s = "IIR_4";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_FILTER_IIR_8:
 | 
			
		||||
        filter_mode_s = "IIR_8";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_FILTER_IIR_16:
 | 
			
		||||
        filter_mode_s = "IIR_16";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_FILTER_IIR_32:
 | 
			
		||||
        filter_mode_s = "IIR_32";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_FILTER_IIR_64:
 | 
			
		||||
        filter_mode_s = "IIR_64";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_FILTER_IIR_128:
 | 
			
		||||
        filter_mode_s = "IIR_128";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_FILTER_IIR_256:
 | 
			
		||||
        filter_mode_s = "IIR_256";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_FILTER_JITTER:
 | 
			
		||||
        filter_mode_s = "JITTER";
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        filter_mode_s = "UNKNOWN";
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Filter mode: %s", filter_mode_s);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Debounce count: %" PRIu32, this->debounce_count_);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Noise threshold coefficient: %" PRIu32, this->noise_threshold_);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Jitter filter step size: %" PRIu32, this->jitter_step_);
 | 
			
		||||
    const char *smooth_level_s;
 | 
			
		||||
    switch (this->smooth_level_) {
 | 
			
		||||
      case TOUCH_PAD_SMOOTH_OFF:
 | 
			
		||||
        smooth_level_s = "OFF";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_SMOOTH_IIR_2:
 | 
			
		||||
        smooth_level_s = "IIR_2";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_SMOOTH_IIR_4:
 | 
			
		||||
        smooth_level_s = "IIR_4";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_SMOOTH_IIR_8:
 | 
			
		||||
        smooth_level_s = "IIR_8";
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        smooth_level_s = "UNKNOWN";
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Smooth level: %s", smooth_level_s);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->denoise_configured_()) {
 | 
			
		||||
    const char *grade_s;
 | 
			
		||||
    switch (this->grade_) {
 | 
			
		||||
      case TOUCH_PAD_DENOISE_BIT12:
 | 
			
		||||
        grade_s = "BIT12";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_DENOISE_BIT10:
 | 
			
		||||
        grade_s = "BIT10";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_DENOISE_BIT8:
 | 
			
		||||
        grade_s = "BIT8";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_DENOISE_BIT4:
 | 
			
		||||
        grade_s = "BIT4";
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        grade_s = "UNKNOWN";
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Denoise grade: %s", grade_s);
 | 
			
		||||
 | 
			
		||||
    const char *cap_level_s;
 | 
			
		||||
    switch (this->cap_level_) {
 | 
			
		||||
      case TOUCH_PAD_DENOISE_CAP_L0:
 | 
			
		||||
        cap_level_s = "L0";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_DENOISE_CAP_L1:
 | 
			
		||||
        cap_level_s = "L1";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_DENOISE_CAP_L2:
 | 
			
		||||
        cap_level_s = "L2";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_DENOISE_CAP_L3:
 | 
			
		||||
        cap_level_s = "L3";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_DENOISE_CAP_L4:
 | 
			
		||||
        cap_level_s = "L4";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_DENOISE_CAP_L5:
 | 
			
		||||
        cap_level_s = "L5";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_DENOISE_CAP_L6:
 | 
			
		||||
        cap_level_s = "L6";
 | 
			
		||||
        break;
 | 
			
		||||
      case TOUCH_PAD_DENOISE_CAP_L7:
 | 
			
		||||
        cap_level_s = "L7";
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        cap_level_s = "UNKNOWN";
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Denoise capacitance level: %s", cap_level_s);
 | 
			
		||||
  }
 | 
			
		||||
#else
 | 
			
		||||
  if (this->iir_filter_enabled_()) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    IIR Filter: %ums", this->iir_filter_);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    IIR Filter: %" PRIu32 "ms", this->iir_filter_);
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  IIR Filter DISABLED");
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  if (this->setup_mode_) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Setup Mode ENABLED!");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Setup Mode ENABLED");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (auto *child : this->children_) {
 | 
			
		||||
    LOG_BINARY_SENSOR("  ", "Touch Pad", child);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Pad: T%d", child->get_touch_pad());
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Threshold: %u", child->get_threshold());
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Pad: T%" PRIu32, (uint32_t) child->get_touch_pad());
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Threshold: %" PRIu32, child->get_threshold());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t ESP32TouchComponent::component_touch_pad_read(touch_pad_t tp) {
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
  uint32_t value = 0;
 | 
			
		||||
  if (this->filter_configured_()) {
 | 
			
		||||
    touch_pad_filter_read_smooth(tp, &value);
 | 
			
		||||
  } else {
 | 
			
		||||
    touch_pad_read_raw_data(tp, &value);
 | 
			
		||||
  }
 | 
			
		||||
#else
 | 
			
		||||
  uint16_t value = 0;
 | 
			
		||||
  if (this->iir_filter_enabled_()) {
 | 
			
		||||
    touch_pad_read_filtered(tp, &value);
 | 
			
		||||
  } else {
 | 
			
		||||
    touch_pad_read(tp, &value);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32TouchComponent::loop() {
 | 
			
		||||
  const uint32_t now = millis();
 | 
			
		||||
  bool should_print = this->setup_mode_ && now - this->setup_mode_last_log_print_ > 250;
 | 
			
		||||
  for (auto *child : this->children_) {
 | 
			
		||||
    uint16_t value;
 | 
			
		||||
    if (this->iir_filter_enabled_()) {
 | 
			
		||||
      touch_pad_read_filtered(child->get_touch_pad(), &value);
 | 
			
		||||
    } else {
 | 
			
		||||
      touch_pad_read(child->get_touch_pad(), &value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    child->value_ = value;
 | 
			
		||||
    child->publish_state(value < child->get_threshold());
 | 
			
		||||
    child->value_ = this->component_touch_pad_read(child->get_touch_pad());
 | 
			
		||||
#if !(defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3))
 | 
			
		||||
    child->publish_state(child->value_ < child->get_threshold());
 | 
			
		||||
#else
 | 
			
		||||
    child->publish_state(child->value_ > child->get_threshold());
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (should_print) {
 | 
			
		||||
      ESP_LOGD(TAG, "Touch Pad '%s' (T%u): %u", child->get_name().c_str(), child->get_touch_pad(), value);
 | 
			
		||||
      ESP_LOGD(TAG, "Touch Pad '%s' (T%" PRIu32 "): %" PRIu32, child->get_name().c_str(),
 | 
			
		||||
               (uint32_t) child->get_touch_pad(), child->value_);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    App.feed_wdt();
 | 
			
		||||
@@ -138,10 +310,12 @@ void ESP32TouchComponent::loop() {
 | 
			
		||||
void ESP32TouchComponent::on_shutdown() {
 | 
			
		||||
  bool is_wakeup_source = false;
 | 
			
		||||
 | 
			
		||||
#if !(defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3))
 | 
			
		||||
  if (this->iir_filter_enabled_()) {
 | 
			
		||||
    touch_pad_filter_stop();
 | 
			
		||||
    touch_pad_filter_delete();
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  for (auto *child : this->children_) {
 | 
			
		||||
    if (child->get_wakeup_threshold() != 0) {
 | 
			
		||||
@@ -151,8 +325,10 @@ void ESP32TouchComponent::on_shutdown() {
 | 
			
		||||
        touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
#if !(defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3))
 | 
			
		||||
      // No filter available when using as wake-up source.
 | 
			
		||||
      touch_pad_config(child->get_touch_pad(), child->get_wakeup_threshold());
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -161,7 +337,7 @@ void ESP32TouchComponent::on_shutdown() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ESP32TouchBinarySensor::ESP32TouchBinarySensor(touch_pad_t touch_pad, uint16_t threshold, uint16_t wakeup_threshold)
 | 
			
		||||
ESP32TouchBinarySensor::ESP32TouchBinarySensor(touch_pad_t touch_pad, uint32_t threshold, uint32_t wakeup_threshold)
 | 
			
		||||
    : touch_pad_(touch_pad), threshold_(threshold), wakeup_threshold_(wakeup_threshold) {}
 | 
			
		||||
 | 
			
		||||
}  // namespace esp32_touch
 | 
			
		||||
 
 | 
			
		||||
@@ -21,25 +21,37 @@ class ESP32TouchBinarySensor;
 | 
			
		||||
 | 
			
		||||
class ESP32TouchComponent : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void register_touch_pad(ESP32TouchBinarySensor *pad) { children_.push_back(pad); }
 | 
			
		||||
 | 
			
		||||
  void set_setup_mode(bool setup_mode) { setup_mode_ = setup_mode; }
 | 
			
		||||
 | 
			
		||||
  void set_iir_filter(uint32_t iir_filter) { iir_filter_ = iir_filter; }
 | 
			
		||||
 | 
			
		||||
  void set_sleep_duration(uint16_t sleep_duration) { sleep_cycle_ = sleep_duration; }
 | 
			
		||||
 | 
			
		||||
  void set_measurement_duration(uint16_t meas_cycle) { meas_cycle_ = meas_cycle; }
 | 
			
		||||
  void register_touch_pad(ESP32TouchBinarySensor *pad) { this->children_.push_back(pad); }
 | 
			
		||||
 | 
			
		||||
  void set_setup_mode(bool setup_mode) { this->setup_mode_ = setup_mode; }
 | 
			
		||||
  void set_sleep_duration(uint16_t sleep_duration) { this->sleep_cycle_ = sleep_duration; }
 | 
			
		||||
  void set_measurement_duration(uint16_t meas_cycle) { this->meas_cycle_ = meas_cycle; }
 | 
			
		||||
  void set_low_voltage_reference(touch_low_volt_t low_voltage_reference) {
 | 
			
		||||
    low_voltage_reference_ = low_voltage_reference;
 | 
			
		||||
    this->low_voltage_reference_ = low_voltage_reference;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_high_voltage_reference(touch_high_volt_t high_voltage_reference) {
 | 
			
		||||
    high_voltage_reference_ = high_voltage_reference;
 | 
			
		||||
    this->high_voltage_reference_ = high_voltage_reference;
 | 
			
		||||
  }
 | 
			
		||||
  void set_voltage_attenuation(touch_volt_atten_t voltage_attenuation) {
 | 
			
		||||
    this->voltage_attenuation_ = voltage_attenuation;
 | 
			
		||||
  }
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
  void set_filter_mode(touch_filter_mode_t filter_mode) { this->filter_mode_ = filter_mode; }
 | 
			
		||||
  void set_debounce_count(uint32_t debounce_count) { this->debounce_count_ = debounce_count; }
 | 
			
		||||
  void set_noise_threshold(uint32_t noise_threshold) { this->noise_threshold_ = noise_threshold; }
 | 
			
		||||
  void set_jitter_step(uint32_t jitter_step) { this->jitter_step_ = jitter_step; }
 | 
			
		||||
  void set_smooth_level(touch_smooth_mode_t smooth_level) { this->smooth_level_ = smooth_level; }
 | 
			
		||||
  void set_denoise_grade(touch_pad_denoise_grade_t denoise_grade) { this->grade_ = denoise_grade; }
 | 
			
		||||
  void set_denoise_cap(touch_pad_denoise_cap_t cap_level) { this->cap_level_ = cap_level; }
 | 
			
		||||
  void set_waterproof_guard_ring_pad(touch_pad_t pad) { this->waterproof_guard_ring_pad_ = pad; }
 | 
			
		||||
  void set_waterproof_shield_driver(touch_pad_shield_driver_t drive_capability) {
 | 
			
		||||
    this->waterproof_shield_driver_ = drive_capability;
 | 
			
		||||
  }
 | 
			
		||||
#else
 | 
			
		||||
  void set_iir_filter(uint32_t iir_filter) { this->iir_filter_ = iir_filter; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  void set_voltage_attenuation(touch_volt_atten_t voltage_attenuation) { voltage_attenuation_ = voltage_attenuation; }
 | 
			
		||||
  uint32_t component_touch_pad_read(touch_pad_t tp);
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
@@ -49,38 +61,63 @@ class ESP32TouchComponent : public Component {
 | 
			
		||||
  void on_shutdown() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  /// Is the IIR filter enabled?
 | 
			
		||||
  bool iir_filter_enabled_() const { return iir_filter_ > 0; }
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
  bool filter_configured_() const {
 | 
			
		||||
    return (this->filter_mode_ != TOUCH_PAD_FILTER_MAX) && (this->smooth_level_ != TOUCH_PAD_SMOOTH_MAX);
 | 
			
		||||
  }
 | 
			
		||||
  bool denoise_configured_() const {
 | 
			
		||||
    return (this->grade_ != TOUCH_PAD_DENOISE_MAX) && (this->cap_level_ != TOUCH_PAD_DENOISE_CAP_MAX);
 | 
			
		||||
  }
 | 
			
		||||
  bool waterproof_configured_() const {
 | 
			
		||||
    return (this->waterproof_guard_ring_pad_ != TOUCH_PAD_MAX) &&
 | 
			
		||||
           (this->waterproof_shield_driver_ != TOUCH_PAD_SHIELD_DRV_MAX);
 | 
			
		||||
  }
 | 
			
		||||
#else
 | 
			
		||||
  bool iir_filter_enabled_() const { return this->iir_filter_ > 0; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  uint16_t sleep_cycle_{};
 | 
			
		||||
  uint16_t meas_cycle_{65535};
 | 
			
		||||
  touch_low_volt_t low_voltage_reference_{};
 | 
			
		||||
  touch_high_volt_t high_voltage_reference_{};
 | 
			
		||||
  touch_volt_atten_t voltage_attenuation_{};
 | 
			
		||||
  std::vector<ESP32TouchBinarySensor *> children_;
 | 
			
		||||
  bool setup_mode_{false};
 | 
			
		||||
  uint32_t setup_mode_last_log_print_{};
 | 
			
		||||
  uint32_t setup_mode_last_log_print_{0};
 | 
			
		||||
  // common parameters
 | 
			
		||||
  uint16_t sleep_cycle_{4095};
 | 
			
		||||
  uint16_t meas_cycle_{65535};
 | 
			
		||||
  touch_low_volt_t low_voltage_reference_{TOUCH_LVOLT_0V5};
 | 
			
		||||
  touch_high_volt_t high_voltage_reference_{TOUCH_HVOLT_2V7};
 | 
			
		||||
  touch_volt_atten_t voltage_attenuation_{TOUCH_HVOLT_ATTEN_0V};
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
  touch_filter_mode_t filter_mode_{TOUCH_PAD_FILTER_MAX};
 | 
			
		||||
  uint32_t debounce_count_{0};
 | 
			
		||||
  uint32_t noise_threshold_{0};
 | 
			
		||||
  uint32_t jitter_step_{0};
 | 
			
		||||
  touch_smooth_mode_t smooth_level_{TOUCH_PAD_SMOOTH_MAX};
 | 
			
		||||
  touch_pad_denoise_grade_t grade_{TOUCH_PAD_DENOISE_MAX};
 | 
			
		||||
  touch_pad_denoise_cap_t cap_level_{TOUCH_PAD_DENOISE_CAP_MAX};
 | 
			
		||||
  touch_pad_t waterproof_guard_ring_pad_{TOUCH_PAD_MAX};
 | 
			
		||||
  touch_pad_shield_driver_t waterproof_shield_driver_{TOUCH_PAD_SHIELD_DRV_MAX};
 | 
			
		||||
#else
 | 
			
		||||
  uint32_t iir_filter_{0};
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Simple helper class to expose a touch pad value as a binary sensor.
 | 
			
		||||
class ESP32TouchBinarySensor : public binary_sensor::BinarySensor {
 | 
			
		||||
 public:
 | 
			
		||||
  ESP32TouchBinarySensor(touch_pad_t touch_pad, uint16_t threshold, uint16_t wakeup_threshold);
 | 
			
		||||
  ESP32TouchBinarySensor(touch_pad_t touch_pad, uint32_t threshold, uint32_t wakeup_threshold);
 | 
			
		||||
 | 
			
		||||
  touch_pad_t get_touch_pad() const { return touch_pad_; }
 | 
			
		||||
  uint16_t get_threshold() const { return threshold_; }
 | 
			
		||||
  void set_threshold(uint16_t threshold) { threshold_ = threshold; }
 | 
			
		||||
  uint16_t get_value() const { return value_; }
 | 
			
		||||
  uint16_t get_wakeup_threshold() const { return wakeup_threshold_; }
 | 
			
		||||
  touch_pad_t get_touch_pad() const { return this->touch_pad_; }
 | 
			
		||||
  uint32_t get_threshold() const { return this->threshold_; }
 | 
			
		||||
  void set_threshold(uint32_t threshold) { this->threshold_ = threshold; }
 | 
			
		||||
  uint32_t get_value() const { return this->value_; }
 | 
			
		||||
  uint32_t get_wakeup_threshold() const { return this->wakeup_threshold_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  friend ESP32TouchComponent;
 | 
			
		||||
 | 
			
		||||
  touch_pad_t touch_pad_;
 | 
			
		||||
  uint16_t threshold_;
 | 
			
		||||
  uint16_t value_;
 | 
			
		||||
  const uint16_t wakeup_threshold_;
 | 
			
		||||
  touch_pad_t touch_pad_{TOUCH_PAD_MAX};
 | 
			
		||||
  uint32_t threshold_{0};
 | 
			
		||||
  uint32_t value_{0};
 | 
			
		||||
  const uint32_t wakeup_threshold_{0};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace esp32_touch
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user