mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 00:51:49 +00:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
			2022.4.0b1
			...
			oral-b-bru
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a85b7b3f84 | ||
| 
						 | 
					a207ed08a9 | ||
| 
						 | 
					90c3cb62b3 | 
							
								
								
									
										10
									
								
								.clang-tidy
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								.clang-tidy
									
									
									
									
									
								
							@@ -68,6 +68,8 @@ Checks: >-
 | 
			
		||||
  -modernize-use-nodiscard,
 | 
			
		||||
  -mpi-*,
 | 
			
		||||
  -objc-*,
 | 
			
		||||
  -readability-braces-around-statements,
 | 
			
		||||
  -readability-const-return-type,
 | 
			
		||||
  -readability-convert-member-functions-to-static,
 | 
			
		||||
  -readability-else-after-return,
 | 
			
		||||
  -readability-function-cognitive-complexity,
 | 
			
		||||
@@ -75,6 +77,10 @@ Checks: >-
 | 
			
		||||
  -readability-isolate-declaration,
 | 
			
		||||
  -readability-magic-numbers,
 | 
			
		||||
  -readability-make-member-function-const,
 | 
			
		||||
  -readability-named-parameter,
 | 
			
		||||
  -readability-qualified-auto,
 | 
			
		||||
  -readability-redundant-access-specifiers,
 | 
			
		||||
  -readability-redundant-member-init,
 | 
			
		||||
  -readability-redundant-string-init,
 | 
			
		||||
  -readability-uppercase-literal-suffix,
 | 
			
		||||
  -readability-use-anyofallof,
 | 
			
		||||
@@ -108,8 +114,6 @@ CheckOptions:
 | 
			
		||||
    value:           'make_unique'
 | 
			
		||||
  - key:             modernize-make-unique.MakeSmartPtrFunctionHeader
 | 
			
		||||
    value:           'esphome/core/helpers.h'
 | 
			
		||||
  - key:             readability-braces-around-statements.ShortStatementLines
 | 
			
		||||
    value:           2
 | 
			
		||||
  - key:             readability-identifier-naming.LocalVariableCase
 | 
			
		||||
    value:           'lower_case'
 | 
			
		||||
  - key:             readability-identifier-naming.ClassCase
 | 
			
		||||
@@ -156,5 +160,3 @@ CheckOptions:
 | 
			
		||||
    value:           'lower_case'
 | 
			
		||||
  - key:             readability-identifier-naming.VirtualMethodSuffix
 | 
			
		||||
    value:           ''
 | 
			
		||||
  - key:             readability-qualified-auto.AddConstToQualified
 | 
			
		||||
    value:           0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							@@ -9,3 +9,4 @@ contact_links:
 | 
			
		||||
  - name: Frequently Asked Question
 | 
			
		||||
    url: https://esphome.io/guides/faq.html
 | 
			
		||||
    about: Please view the FAQ for common questions and what to include in a bug report.
 | 
			
		||||
    
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# What does this implement/fix?
 | 
			
		||||
# What does this implement/fix? 
 | 
			
		||||
 | 
			
		||||
Quick description and explanation of changes
 | 
			
		||||
 | 
			
		||||
@@ -35,6 +35,6 @@ Quick description and explanation of changes
 | 
			
		||||
## Checklist:
 | 
			
		||||
  - [ ] The code change is tested and works locally.
 | 
			
		||||
  - [ ] Tests have been added to verify that the new code works (under `tests/` folder).
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
If user exposed functionality or configuration variables are added/changed:
 | 
			
		||||
  - [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -51,26 +51,26 @@ jobs:
 | 
			
		||||
            name: Run script/clang-format
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP8266
 | 
			
		||||
            options: --environment esp8266-arduino-tidy --grep USE_ESP8266
 | 
			
		||||
            options: --environment esp8266-tidy --grep USE_ESP8266
 | 
			
		||||
            pio_cache_key: tidyesp8266
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 Arduino 1/4
 | 
			
		||||
            options: --environment esp32-arduino-tidy --split-num 4 --split-at 1
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 1/4
 | 
			
		||||
            options: --environment esp32-tidy --split-num 4 --split-at 1
 | 
			
		||||
            pio_cache_key: tidyesp32
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 Arduino 2/4
 | 
			
		||||
            options: --environment esp32-arduino-tidy --split-num 4 --split-at 2
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 2/4
 | 
			
		||||
            options: --environment esp32-tidy --split-num 4 --split-at 2
 | 
			
		||||
            pio_cache_key: tidyesp32
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 Arduino 3/4
 | 
			
		||||
            options: --environment esp32-arduino-tidy --split-num 4 --split-at 3
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 3/4
 | 
			
		||||
            options: --environment esp32-tidy --split-num 4 --split-at 3
 | 
			
		||||
            pio_cache_key: tidyesp32
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 Arduino 4/4
 | 
			
		||||
            options: --environment esp32-arduino-tidy --split-num 4 --split-at 4
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 4/4
 | 
			
		||||
            options: --environment esp32-tidy --split-num 4 --split-at 4
 | 
			
		||||
            pio_cache_key: tidyesp32
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 IDF
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 esp-idf
 | 
			
		||||
            options: --environment esp32-idf-tidy --grep USE_ESP_IDF
 | 
			
		||||
            pio_cache_key: tidyesp32-idf
 | 
			
		||||
 | 
			
		||||
@@ -80,7 +80,7 @@ jobs:
 | 
			
		||||
        uses: actions/setup-python@v2
 | 
			
		||||
        id: python
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: '3.8'
 | 
			
		||||
          python-version: '3.7'
 | 
			
		||||
 | 
			
		||||
      - name: Cache virtualenv
 | 
			
		||||
        uses: actions/cache@v2
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -137,18 +137,18 @@ jobs:
 | 
			
		||||
          --build-type "${{ matrix.build_type }}" \
 | 
			
		||||
          manifest
 | 
			
		||||
 | 
			
		||||
  deploy-ha-addon-repo:
 | 
			
		||||
  deploy-hassio-repo:
 | 
			
		||||
    if: github.repository == 'esphome/esphome' && github.event_name == 'release'
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: [deploy-docker]
 | 
			
		||||
    steps:
 | 
			
		||||
      - env:
 | 
			
		||||
          TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
 | 
			
		||||
          TOKEN: ${{ secrets.DEPLOY_HASSIO_TOKEN }}
 | 
			
		||||
        run: |
 | 
			
		||||
          TAG="${GITHUB_REF#refs/tags/}"
 | 
			
		||||
          curl \
 | 
			
		||||
            -u ":$TOKEN" \
 | 
			
		||||
            -X POST \
 | 
			
		||||
            -H "Accept: application/vnd.github.v3+json" \
 | 
			
		||||
            https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
 | 
			
		||||
            https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \
 | 
			
		||||
            -d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -77,7 +77,6 @@ venv/
 | 
			
		||||
ENV/
 | 
			
		||||
env.bak/
 | 
			
		||||
venv.bak/
 | 
			
		||||
venv-*/
 | 
			
		||||
 | 
			
		||||
# mypy
 | 
			
		||||
.mypy_cache/
 | 
			
		||||
 
 | 
			
		||||
@@ -3,4 +3,4 @@ ports:
 | 
			
		||||
  onOpen: open-preview
 | 
			
		||||
tasks:
 | 
			
		||||
- before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup
 | 
			
		||||
  command: python -m esphome dashboard config
 | 
			
		||||
  command: python -m esphome config dashboard
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
# See https://pre-commit.com/hooks.html for more hooks
 | 
			
		||||
repos:
 | 
			
		||||
  - repo: https://github.com/ambv/black
 | 
			
		||||
    rev: 22.3.0
 | 
			
		||||
    rev: 20.8b1
 | 
			
		||||
    hooks:
 | 
			
		||||
    - id: black
 | 
			
		||||
      args:
 | 
			
		||||
@@ -10,7 +10,7 @@ repos:
 | 
			
		||||
        - --quiet
 | 
			
		||||
      files: ^((esphome|script|tests)/.+)?[^/]+\.py$
 | 
			
		||||
  - repo: https://gitlab.com/pycqa/flake8
 | 
			
		||||
    rev: 4.0.1
 | 
			
		||||
    rev: 3.8.4
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: flake8
 | 
			
		||||
        additional_dependencies:
 | 
			
		||||
@@ -25,8 +25,3 @@ repos:
 | 
			
		||||
          - --branch=dev
 | 
			
		||||
          - --branch=release
 | 
			
		||||
          - --branch=beta
 | 
			
		||||
  - repo: https://github.com/asottile/pyupgrade
 | 
			
		||||
    rev: v2.31.1
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: pyupgrade
 | 
			
		||||
        args: [--py38-plus]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										38
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								CODEOWNERS
									
									
									
									
									
								
							@@ -19,7 +19,6 @@ esphome/components/airthings_wave_mini/* @ncareau
 | 
			
		||||
esphome/components/airthings_wave_plus/* @jeromelaban
 | 
			
		||||
esphome/components/am43/* @buxtronix
 | 
			
		||||
esphome/components/am43/cover/* @buxtronix
 | 
			
		||||
esphome/components/analog_threshold/* @ianchi
 | 
			
		||||
esphome/components/animation/* @syndlex
 | 
			
		||||
esphome/components/anova/* @buxtronix
 | 
			
		||||
esphome/components/api/* @OttoWinter
 | 
			
		||||
@@ -28,23 +27,18 @@ esphome/components/atc_mithermometer/* @ahpohl
 | 
			
		||||
esphome/components/b_parasite/* @rbaron
 | 
			
		||||
esphome/components/ballu/* @bazuchan
 | 
			
		||||
esphome/components/bang_bang/* @OttoWinter
 | 
			
		||||
esphome/components/bh1750/* @OttoWinter
 | 
			
		||||
esphome/components/binary_sensor/* @esphome/core
 | 
			
		||||
esphome/components/bl0940/* @tobias-
 | 
			
		||||
esphome/components/ble_client/* @buxtronix
 | 
			
		||||
esphome/components/bme680_bsec/* @trvrnrth
 | 
			
		||||
esphome/components/bmp3xx/* @martgras
 | 
			
		||||
esphome/components/button/* @esphome/core
 | 
			
		||||
esphome/components/canbus/* @danielschramm @mvturnho
 | 
			
		||||
esphome/components/cap1188/* @MrEditor97
 | 
			
		||||
esphome/components/captive_portal/* @OttoWinter
 | 
			
		||||
esphome/components/ccs811/* @habbie
 | 
			
		||||
esphome/components/cd74hc4067/* @asoehlke
 | 
			
		||||
esphome/components/climate/* @esphome/core
 | 
			
		||||
esphome/components/climate_ir/* @glmnet
 | 
			
		||||
esphome/components/color_temperature/* @jesserockz
 | 
			
		||||
esphome/components/coolix/* @glmnet
 | 
			
		||||
esphome/components/copy/* @OttoWinter
 | 
			
		||||
esphome/components/cover/* @esphome/core
 | 
			
		||||
esphome/components/cs5460a/* @balrog-kun
 | 
			
		||||
esphome/components/cse7761/* @berfenger
 | 
			
		||||
@@ -57,12 +51,10 @@ esphome/components/dfplayer/* @glmnet
 | 
			
		||||
esphome/components/dht/* @OttoWinter
 | 
			
		||||
esphome/components/ds1307/* @badbadc0ffee
 | 
			
		||||
esphome/components/dsmr/* @glmnet @zuidwijk
 | 
			
		||||
esphome/components/ektf2232/* @jesserockz
 | 
			
		||||
esphome/components/esp32/* @esphome/core
 | 
			
		||||
esphome/components/esp32_ble/* @jesserockz
 | 
			
		||||
esphome/components/esp32_ble_server/* @jesserockz
 | 
			
		||||
esphome/components/esp32_camera_web_server/* @ayufan
 | 
			
		||||
esphome/components/esp32_can/* @Sympatron
 | 
			
		||||
esphome/components/esp32_improv/* @jesserockz
 | 
			
		||||
esphome/components/esp8266/* @esphome/core
 | 
			
		||||
esphome/components/exposure_notifications/* @OttoWinter
 | 
			
		||||
@@ -80,27 +72,19 @@ esphome/components/hbridge/light/* @DotNetDann
 | 
			
		||||
esphome/components/heatpumpir/* @rob-deutsch
 | 
			
		||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
 | 
			
		||||
esphome/components/homeassistant/* @OttoWinter
 | 
			
		||||
esphome/components/honeywellabp/* @RubyBailey
 | 
			
		||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
 | 
			
		||||
esphome/components/hydreon_rgxx/* @functionpointer
 | 
			
		||||
esphome/components/i2c/* @esphome/core
 | 
			
		||||
esphome/components/improv_serial/* @esphome/core
 | 
			
		||||
esphome/components/ina260/* @MrEditor97
 | 
			
		||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
 | 
			
		||||
esphome/components/inkplate6/* @jesserockz
 | 
			
		||||
esphome/components/integration/* @OttoWinter
 | 
			
		||||
esphome/components/interval/* @esphome/core
 | 
			
		||||
esphome/components/json/* @OttoWinter
 | 
			
		||||
esphome/components/kalman_combinator/* @Cat-Ion
 | 
			
		||||
esphome/components/ledc/* @OttoWinter
 | 
			
		||||
esphome/components/light/* @esphome/core
 | 
			
		||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
 | 
			
		||||
esphome/components/lock/* @esphome/core
 | 
			
		||||
esphome/components/logger/* @esphome/core
 | 
			
		||||
esphome/components/ltr390/* @sjtrny
 | 
			
		||||
esphome/components/max44009/* @berfenger
 | 
			
		||||
esphome/components/max7219digit/* @rspaargaren
 | 
			
		||||
esphome/components/max9611/* @mckaymatthew
 | 
			
		||||
esphome/components/mcp23008/* @jesserockz
 | 
			
		||||
esphome/components/mcp23017/* @jesserockz
 | 
			
		||||
esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz
 | 
			
		||||
@@ -109,27 +93,18 @@ esphome/components/mcp23x08_base/* @jesserockz
 | 
			
		||||
esphome/components/mcp23x17_base/* @jesserockz
 | 
			
		||||
esphome/components/mcp23xxx_base/* @jesserockz
 | 
			
		||||
esphome/components/mcp2515/* @danielschramm @mvturnho
 | 
			
		||||
esphome/components/mcp3204/* @rsumner
 | 
			
		||||
esphome/components/mcp4728/* @berfenger
 | 
			
		||||
esphome/components/mcp47a1/* @jesserockz
 | 
			
		||||
esphome/components/mcp9808/* @k7hpn
 | 
			
		||||
esphome/components/md5/* @esphome/core
 | 
			
		||||
esphome/components/mdns/* @esphome/core
 | 
			
		||||
esphome/components/midea/* @dudanov
 | 
			
		||||
esphome/components/midea_ir/* @dudanov
 | 
			
		||||
esphome/components/mitsubishi/* @RubyBailey
 | 
			
		||||
esphome/components/mlx90393/* @functionpointer
 | 
			
		||||
esphome/components/modbus_controller/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/binary_sensor/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/number/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/output/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/select/* @martgras @stegm
 | 
			
		||||
esphome/components/modbus_controller/sensor/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/switch/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/text_sensor/* @martgras
 | 
			
		||||
esphome/components/mopeka_ble/* @spbrogan
 | 
			
		||||
esphome/components/mopeka_pro_check/* @spbrogan
 | 
			
		||||
esphome/components/mpu6886/* @fabaff
 | 
			
		||||
esphome/components/network/* @esphome/core
 | 
			
		||||
esphome/components/nextion/* @senexcrenshaw
 | 
			
		||||
esphome/components/nextion/binary_sensor/* @senexcrenshaw
 | 
			
		||||
@@ -149,13 +124,8 @@ esphome/components/pn532_i2c/* @OttoWinter @jesserockz
 | 
			
		||||
esphome/components/pn532_spi/* @OttoWinter @jesserockz
 | 
			
		||||
esphome/components/power_supply/* @esphome/core
 | 
			
		||||
esphome/components/preferences/* @esphome/core
 | 
			
		||||
esphome/components/psram/* @esphome/core
 | 
			
		||||
esphome/components/pulse_meter/* @cstaahl @stevebaxter
 | 
			
		||||
esphome/components/pulse_meter/* @stevebaxter
 | 
			
		||||
esphome/components/pvvx_mithermometer/* @pasiz
 | 
			
		||||
esphome/components/qmp6988/* @andrewpc
 | 
			
		||||
esphome/components/qr_code/* @wjtje
 | 
			
		||||
esphome/components/radon_eye_ble/* @jeffeb3
 | 
			
		||||
esphome/components/radon_eye_rd200/* @jeffeb3
 | 
			
		||||
esphome/components/rc522/* @glmnet
 | 
			
		||||
esphome/components/rc522_i2c/* @glmnet
 | 
			
		||||
esphome/components/rc522_spi/* @glmnet
 | 
			
		||||
@@ -170,7 +140,6 @@ esphome/components/sdm_meter/* @jesserockz @polyfaces
 | 
			
		||||
esphome/components/sdp3x/* @Azimath
 | 
			
		||||
esphome/components/selec_meter/* @sourabhjaiswal
 | 
			
		||||
esphome/components/select/* @esphome/core
 | 
			
		||||
esphome/components/sensirion_common/* @martgras
 | 
			
		||||
esphome/components/sensor/* @esphome/core
 | 
			
		||||
esphome/components/sgp40/* @SenexCrenshaw
 | 
			
		||||
esphome/components/sht4x/* @sjtrny
 | 
			
		||||
@@ -178,7 +147,6 @@ esphome/components/shutdown/* @esphome/core @jsuanet
 | 
			
		||||
esphome/components/sim800l/* @glmnet
 | 
			
		||||
esphome/components/sm2135/* @BoukeHaarsma23
 | 
			
		||||
esphome/components/socket/* @esphome/core
 | 
			
		||||
esphome/components/sonoff_d1/* @anatoly-savchenkov
 | 
			
		||||
esphome/components/spi/* @esphome/core
 | 
			
		||||
esphome/components/ssd1322_base/* @kbx81
 | 
			
		||||
esphome/components/ssd1322_spi/* @kbx81
 | 
			
		||||
@@ -209,7 +177,6 @@ esphome/components/tmp102/* @timsavage
 | 
			
		||||
esphome/components/tmp117/* @Azimath
 | 
			
		||||
esphome/components/tof10120/* @wstrzalka
 | 
			
		||||
esphome/components/toshiba/* @kbx81
 | 
			
		||||
esphome/components/touchscreen/* @jesserockz
 | 
			
		||||
esphome/components/tsl2591/* @wjcarpenter
 | 
			
		||||
esphome/components/tuya/binary_sensor/* @jesserockz
 | 
			
		||||
esphome/components/tuya/climate/* @jesserockz
 | 
			
		||||
@@ -220,11 +187,8 @@ esphome/components/tuya/text_sensor/* @dentra
 | 
			
		||||
esphome/components/uart/* @esphome/core
 | 
			
		||||
esphome/components/ultrasonic/* @OttoWinter
 | 
			
		||||
esphome/components/version/* @esphome/core
 | 
			
		||||
esphome/components/wake_on_lan/* @willwill2will54
 | 
			
		||||
esphome/components/web_server_base/* @OttoWinter
 | 
			
		||||
esphome/components/whirlpool/* @glmnet
 | 
			
		||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
 | 
			
		||||
esphome/components/xiaomi_mhoc303/* @drug123
 | 
			
		||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
 | 
			
		||||
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
 | 
			
		||||
esphome/components/xpt2046/* @numo68
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ For a detailed guide, please see https://esphome.io/guides/contributing.html#con
 | 
			
		||||
Things to note when contributing:
 | 
			
		||||
 | 
			
		||||
 - Please test your changes :)
 | 
			
		||||
 - If a new feature is added or an existing user-facing feature is changed, you should also
 | 
			
		||||
 - If a new feature is added or an existing user-facing feature is changed, you should also 
 | 
			
		||||
   update the [docs](https://github.com/esphome/esphome-docs). See [contributing to esphome-docs](https://esphome.io/guides/contributing.html#contributing-to-esphomedocs)
 | 
			
		||||
   for more information.
 | 
			
		||||
 - Please also update the tests in the `tests/` folder. You can do so by just adding a line in one of the YAML files
 | 
			
		||||
 
 | 
			
		||||
@@ -4,5 +4,4 @@ include requirements.txt
 | 
			
		||||
include esphome/dashboard/templates/*.html
 | 
			
		||||
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
 | 
			
		||||
recursive-include esphome *.cpp *.h *.tcc
 | 
			
		||||
recursive-include esphome *.py.script
 | 
			
		||||
recursive-include esphome LICENSE.txt
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,12 @@
 | 
			
		||||
# One of "docker", "hassio"
 | 
			
		||||
ARG BASEIMGTYPE=docker
 | 
			
		||||
 | 
			
		||||
# https://github.com/hassio-addons/addon-debian-base/releases
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/amd64:5.3.0 AS base-hassio-amd64
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/aarch64:5.3.0 AS base-hassio-arm64
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/armv7:5.3.0 AS base-hassio-armv7
 | 
			
		||||
# https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye
 | 
			
		||||
FROM debian:bullseye-20220328-slim AS base-docker-amd64
 | 
			
		||||
FROM debian:bullseye-20220328-slim AS base-docker-arm64
 | 
			
		||||
FROM debian:bullseye-20220328-slim AS base-docker-armv7
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/amd64:5.1.1 AS base-hassio-amd64
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/aarch64:5.1.1 AS base-hassio-arm64
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/armv7:5.1.1 AS base-hassio-armv7
 | 
			
		||||
FROM debian:bullseye-20211011-slim AS base-docker-amd64
 | 
			
		||||
FROM debian:bullseye-20211011-slim AS base-docker-arm64
 | 
			
		||||
FROM debian:bullseye-20211011-slim AS base-docker-armv7
 | 
			
		||||
 | 
			
		||||
# Use TARGETARCH/TARGETVARIANT defined by docker
 | 
			
		||||
# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
 | 
			
		||||
@@ -23,13 +21,13 @@ RUN \
 | 
			
		||||
    # Use pinned versions so that we get updates with build caching
 | 
			
		||||
    && apt-get install -y --no-install-recommends \
 | 
			
		||||
        python3=3.9.2-3 \
 | 
			
		||||
        python3-pip=20.3.4-4+deb11u1 \
 | 
			
		||||
        python3-pip=20.3.4-4 \
 | 
			
		||||
        python3-setuptools=52.0.0-4 \
 | 
			
		||||
        python3-pil=8.1.2+dfsg-0.3+deb11u1 \
 | 
			
		||||
        python3-pil=8.1.2+dfsg-0.3 \
 | 
			
		||||
        python3-cryptography=3.3.2-1 \
 | 
			
		||||
        iputils-ping=3:20210202-1 \
 | 
			
		||||
        git=1:2.30.2-1 \
 | 
			
		||||
        curl=7.74.0-1.3+deb11u1 \
 | 
			
		||||
        curl=7.74.0-1.3+b1 \
 | 
			
		||||
    && rm -rf \
 | 
			
		||||
        /tmp/* \
 | 
			
		||||
        /var/{cache,log}/* \
 | 
			
		||||
@@ -44,8 +42,8 @@ ENV \
 | 
			
		||||
RUN \
 | 
			
		||||
    # Ubuntu python3-pip is missing wheel
 | 
			
		||||
    pip3 install --no-cache-dir \
 | 
			
		||||
        wheel==0.37.1 \
 | 
			
		||||
        platformio==5.2.5 \
 | 
			
		||||
        wheel==0.36.2 \
 | 
			
		||||
        platformio==5.2.2 \
 | 
			
		||||
    # Change some platformio settings
 | 
			
		||||
    && platformio settings set enable_telemetry No \
 | 
			
		||||
    && platformio settings set check_libraries_interval 1000000 \
 | 
			
		||||
@@ -54,19 +52,19 @@ RUN \
 | 
			
		||||
    && mkdir -p /piolibs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ======================= docker-type image =======================
 | 
			
		||||
FROM base AS docker
 | 
			
		||||
 | 
			
		||||
# First install requirements to leverage caching when requirements don't change
 | 
			
		||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
 | 
			
		||||
RUN \
 | 
			
		||||
    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
 | 
			
		||||
    && /platformio_install_deps.py /platformio.ini
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ======================= docker-type image =======================
 | 
			
		||||
FROM base AS docker
 | 
			
		||||
 | 
			
		||||
# Copy esphome and install
 | 
			
		||||
COPY . /esphome
 | 
			
		||||
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
 | 
			
		||||
RUN pip3 install --no-cache-dir -e /esphome
 | 
			
		||||
 | 
			
		||||
# Settings for dashboard
 | 
			
		||||
ENV USERNAME="" PASSWORD=""
 | 
			
		||||
@@ -95,7 +93,7 @@ RUN \
 | 
			
		||||
    apt-get update \
 | 
			
		||||
    # Use pinned versions so that we get updates with build caching
 | 
			
		||||
    && apt-get install -y --no-install-recommends \
 | 
			
		||||
        nginx-light=1.18.0-6.1 \
 | 
			
		||||
        nginx=1.18.0-6.1 \
 | 
			
		||||
    && rm -rf \
 | 
			
		||||
        /tmp/* \
 | 
			
		||||
        /var/{cache,log}/* \
 | 
			
		||||
@@ -104,11 +102,17 @@ RUN \
 | 
			
		||||
ARG BUILD_VERSION=dev
 | 
			
		||||
 | 
			
		||||
# Copy root filesystem
 | 
			
		||||
COPY docker/ha-addon-rootfs/ /
 | 
			
		||||
COPY docker/hassio-rootfs/ /
 | 
			
		||||
 | 
			
		||||
# First install requirements to leverage caching when requirements don't change
 | 
			
		||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
 | 
			
		||||
RUN \
 | 
			
		||||
    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
 | 
			
		||||
    && /platformio_install_deps.py /platformio.ini
 | 
			
		||||
 | 
			
		||||
# Copy esphome and install
 | 
			
		||||
COPY . /esphome
 | 
			
		||||
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
 | 
			
		||||
RUN pip3 install --no-cache-dir -e /esphome
 | 
			
		||||
 | 
			
		||||
# Labels
 | 
			
		||||
LABEL \
 | 
			
		||||
@@ -143,8 +147,10 @@ RUN \
 | 
			
		||||
        /var/{cache,log}/* \
 | 
			
		||||
        /var/lib/apt/lists/*
 | 
			
		||||
 | 
			
		||||
COPY requirements_test.txt /
 | 
			
		||||
RUN pip3 install --no-cache-dir -r /requirements_test.txt
 | 
			
		||||
COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
 | 
			
		||||
RUN \
 | 
			
		||||
    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
 | 
			
		||||
    && /platformio_install_deps.py /platformio.ini
 | 
			
		||||
 | 
			
		||||
VOLUME ["/esphome"]
 | 
			
		||||
WORKDIR /esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,6 @@ parser.add_argument("--dry-run", action="store_true", help="Don't run any comman
 | 
			
		||||
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
 | 
			
		||||
build_parser = subparsers.add_parser("build", help="Build the image")
 | 
			
		||||
build_parser.add_argument("--push", help="Also push the images", action="store_true")
 | 
			
		||||
build_parser.add_argument("--load", help="Load the docker image locally", action="store_true")
 | 
			
		||||
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -133,8 +132,6 @@ def main():
 | 
			
		||||
            cmd += ["--tag", img]
 | 
			
		||||
        if args.push:
 | 
			
		||||
            cmd += ["--push", "--cache-to", f"type=registry,ref={cache_img},mode=max"]
 | 
			
		||||
        if args.load:
 | 
			
		||||
            cmd += ["--load"]
 | 
			
		||||
 | 
			
		||||
        run_command(*cmd, ".")
 | 
			
		||||
    elif args.command == "manifest":
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,12 @@
 | 
			
		||||
# Check SSL requirements, if enabled
 | 
			
		||||
if bashio::config.true 'ssl'; then
 | 
			
		||||
    if ! bashio::config.has_value 'certfile'; then
 | 
			
		||||
        bashio::log.fatal 'SSL is enabled, but no certfile was specified.'
 | 
			
		||||
        bashio::fatal 'SSL is enabled, but no certfile was specified.'
 | 
			
		||||
        bashio::exit.nok
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! bashio::config.has_value 'keyfile'; then
 | 
			
		||||
        bashio::log.fatal 'SSL is enabled, but no keyfile was specified'
 | 
			
		||||
        bashio::fatal 'SSL is enabled, but no keyfile was specified'
 | 
			
		||||
        bashio::exit.nok
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
@@ -10,7 +10,7 @@ server {
 | 
			
		||||
    ssl_certificate_key /ssl/%%keyfile%%;
 | 
			
		||||
 | 
			
		||||
    # Clear Hass.io Ingress header
 | 
			
		||||
    proxy_set_header X-HA-Ingress "";
 | 
			
		||||
    proxy_set_header X-Hassio-Ingress "";
 | 
			
		||||
 | 
			
		||||
    # Redirect http requests to https on the same port.
 | 
			
		||||
    # https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/
 | 
			
		||||
@@ -4,7 +4,7 @@ server {
 | 
			
		||||
    include /etc/nginx/includes/server_params.conf;
 | 
			
		||||
    include /etc/nginx/includes/proxy_params.conf;
 | 
			
		||||
    # Clear Hass.io Ingress header
 | 
			
		||||
    proxy_set_header X-HA-Ingress "";
 | 
			
		||||
    proxy_set_header X-Hassio-Ingress "";
 | 
			
		||||
 | 
			
		||||
    location / {
 | 
			
		||||
        proxy_pass http://esphome;
 | 
			
		||||
@@ -3,8 +3,8 @@ server {
 | 
			
		||||
 | 
			
		||||
    include /etc/nginx/includes/server_params.conf;
 | 
			
		||||
    include /etc/nginx/includes/proxy_params.conf;
 | 
			
		||||
    # Set Home Assistant Ingress header
 | 
			
		||||
    proxy_set_header X-HA-Ingress "YES";
 | 
			
		||||
    # Set Hass.io Ingress header
 | 
			
		||||
    proxy_set_header X-Hassio-Ingress "YES";
 | 
			
		||||
 | 
			
		||||
    location / {
 | 
			
		||||
        # Only allow from Hass.io supervisor
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
# Runs the ESPHome dashboard
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
export ESPHOME_IS_HA_ADDON=true
 | 
			
		||||
export ESPHOME_IS_HASSIO=true
 | 
			
		||||
 | 
			
		||||
if bashio::config.true 'leave_front_door_open'; then
 | 
			
		||||
    export DISABLE_HA_AUTHENTICATION=true
 | 
			
		||||
@@ -32,4 +32,4 @@ export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
 | 
			
		||||
export PLATFORMIO_GLOBALLIB_DIR=/piolibs
 | 
			
		||||
 | 
			
		||||
bashio::log.info "Starting ESPHome dashboard..."
 | 
			
		||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon
 | 
			
		||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio
 | 
			
		||||
@@ -661,7 +661,7 @@ def parse_args(argv):
 | 
			
		||||
        "--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
 | 
			
		||||
    )
 | 
			
		||||
    parser_dashboard.add_argument(
 | 
			
		||||
        "--ha-addon", help=argparse.SUPPRESS, action="store_true"
 | 
			
		||||
        "--hassio", help=argparse.SUPPRESS, action="store_true"
 | 
			
		||||
    )
 | 
			
		||||
    parser_dashboard.add_argument(
 | 
			
		||||
        "--socket", help="Make the dashboard serve under a unix socket", type=str
 | 
			
		||||
@@ -778,10 +778,10 @@ def run_esphome(argv):
 | 
			
		||||
        _LOGGER.warning("Please instead use:")
 | 
			
		||||
        _LOGGER.warning("   esphome %s", " ".join(args.deprecated_argv_suggestion))
 | 
			
		||||
 | 
			
		||||
    if sys.version_info < (3, 8, 0):
 | 
			
		||||
    if sys.version_info < (3, 7, 0):
 | 
			
		||||
        _LOGGER.error(
 | 
			
		||||
            "You're running ESPHome with Python <3.8. ESPHome is no longer compatible "
 | 
			
		||||
            "with this Python version. Please reinstall ESPHome with Python 3.8+"
 | 
			
		||||
            "You're running ESPHome with Python <3.7. ESPHome is no longer compatible "
 | 
			
		||||
            "with this Python version. Please reinstall ESPHome with Python 3.7+"
 | 
			
		||||
        )
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -262,16 +262,21 @@ async def repeat_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_validate_wait_until = cv.maybe_simple_value(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
			
		||||
        cv.Optional(CONF_TIMEOUT): cv.templatable(cv.positive_time_period_milliseconds),
 | 
			
		||||
    },
 | 
			
		||||
    key=CONF_CONDITION,
 | 
			
		||||
)
 | 
			
		||||
def validate_wait_until(value):
 | 
			
		||||
    schema = cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
			
		||||
            cv.Optional(CONF_TIMEOUT): cv.templatable(
 | 
			
		||||
                cv.positive_time_period_milliseconds
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    if isinstance(value, dict) and CONF_CONDITION in value:
 | 
			
		||||
        return schema(value)
 | 
			
		||||
    return validate_wait_until({CONF_CONDITION: value})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action("wait_until", WaitUntilAction, _validate_wait_until)
 | 
			
		||||
@register_action("wait_until", WaitUntilAction, validate_wait_until)
 | 
			
		||||
async def wait_until_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, conditions)
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,6 @@ from esphome.cpp_types import (  # noqa
 | 
			
		||||
    uint32,
 | 
			
		||||
    uint64,
 | 
			
		||||
    int32,
 | 
			
		||||
    int64,
 | 
			
		||||
    const_char_ptr,
 | 
			
		||||
    NAN,
 | 
			
		||||
    esphome_ns,
 | 
			
		||||
@@ -76,11 +75,11 @@ from esphome.cpp_types import (  # noqa
 | 
			
		||||
    optional,
 | 
			
		||||
    arduino_json_ns,
 | 
			
		||||
    JsonObject,
 | 
			
		||||
    JsonObjectConst,
 | 
			
		||||
    JsonObjectRef,
 | 
			
		||||
    JsonObjectConstRef,
 | 
			
		||||
    Controller,
 | 
			
		||||
    GPIOPin,
 | 
			
		||||
    InternalGPIOPin,
 | 
			
		||||
    gpio_Flags,
 | 
			
		||||
    EntityCategory,
 | 
			
		||||
    Parented,
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -52,10 +52,10 @@ uint32_t IRAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
 | 
			
		||||
    this->gate_pin.digital_write(false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (time_since_zc < this->enable_time_us) {
 | 
			
		||||
  if (time_since_zc < this->enable_time_us)
 | 
			
		||||
    // Next event is enable, return time until that event
 | 
			
		||||
    return this->enable_time_us - time_since_zc;
 | 
			
		||||
  } else if (time_since_zc < disable_time_us) {
 | 
			
		||||
  else if (time_since_zc < disable_time_us) {
 | 
			
		||||
    // Next event is disable, return time until that event
 | 
			
		||||
    return this->disable_time_us - time_since_zc;
 | 
			
		||||
  }
 | 
			
		||||
@@ -74,10 +74,9 @@ uint32_t IRAM_ATTR HOT timer_interrupt() {
 | 
			
		||||
  uint32_t min_dt_us = 1000;
 | 
			
		||||
  uint32_t now = micros();
 | 
			
		||||
  for (auto *dimmer : all_dimmers) {
 | 
			
		||||
    if (dimmer == nullptr) {
 | 
			
		||||
    if (dimmer == nullptr)
 | 
			
		||||
      // no more dimmers
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    uint32_t res = dimmer->timer_intr(now);
 | 
			
		||||
    if (res != 0 && res < min_dt_us)
 | 
			
		||||
      min_dt_us = res;
 | 
			
		||||
@@ -213,13 +212,12 @@ void AcDimmer::dump_config() {
 | 
			
		||||
  LOG_PIN("  Zero-Cross Pin: ", this->zero_cross_pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "   Min Power: %.1f%%", this->store_.min_power / 10.0f);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "   Init with half cycle: %s", YESNO(this->init_with_half_cycle_));
 | 
			
		||||
  if (method_ == DIM_METHOD_LEADING_PULSE) {
 | 
			
		||||
  if (method_ == DIM_METHOD_LEADING_PULSE)
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "   Method: leading pulse");
 | 
			
		||||
  } else if (method_ == DIM_METHOD_LEADING) {
 | 
			
		||||
  else if (method_ == DIM_METHOD_LEADING)
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "   Method: leading");
 | 
			
		||||
  } else {
 | 
			
		||||
  else
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "   Method: trailing");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOG_FLOAT_OUTPUT(this);
 | 
			
		||||
  ESP_LOGV(TAG, "  Estimated Frequency: %.3fHz", 1e6f / this->store_.cycle_time_us / 2);
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
 | 
			
		||||
 public:
 | 
			
		||||
  AdalightLightEffect(const std::string &name);
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  void start() override;
 | 
			
		||||
  void stop() override;
 | 
			
		||||
  void apply(light::AddressableLight &it, const Color ¤t_color) override;
 | 
			
		||||
@@ -29,6 +30,7 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
 | 
			
		||||
  void blank_all_leds_(light::AddressableLight &it);
 | 
			
		||||
  Frame parse_frame_(light::AddressableLight &it);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint32_t last_ack_{0};
 | 
			
		||||
  uint32_t last_byte_{0};
 | 
			
		||||
  uint32_t last_reset_{0};
 | 
			
		||||
 
 | 
			
		||||
@@ -15,11 +15,6 @@ namespace esphome {
 | 
			
		||||
namespace adc {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "adc";
 | 
			
		||||
// 13 bits for S3 / 12 bit for all other esp32 variants
 | 
			
		||||
// create a const to avoid the repated cast to enum
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void ADCSensor::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
 | 
			
		||||
@@ -28,14 +23,14 @@ void ADCSensor::setup() {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
 | 
			
		||||
  adc1_config_width(ADC_WIDTH_BIT_12);
 | 
			
		||||
  if (!autorange_) {
 | 
			
		||||
    adc1_config_channel_atten(channel_, attenuation_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // load characteristics for each attenuation
 | 
			
		||||
  for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) {
 | 
			
		||||
    auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
 | 
			
		||||
    auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_BIT_12,
 | 
			
		||||
                                              1100,  // default vref
 | 
			
		||||
                                              &cal_characteristics_[i]);
 | 
			
		||||
    switch (cal_value) {
 | 
			
		||||
@@ -70,9 +65,9 @@ void ADCSensor::dump_config() {
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  LOG_PIN("  Pin: ", pin_);
 | 
			
		||||
  if (autorange_) {
 | 
			
		||||
  if (autorange_)
 | 
			
		||||
    ESP_LOGCONFIG(TAG, " Attenuation: auto");
 | 
			
		||||
  } else {
 | 
			
		||||
  else
 | 
			
		||||
    switch (this->attenuation_) {
 | 
			
		||||
      case ADC_ATTEN_DB_0:
 | 
			
		||||
        ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
 | 
			
		||||
@@ -89,7 +84,6 @@ void ADCSensor::dump_config() {
 | 
			
		||||
      default:  // This is to satisfy the unused ADC_ATTEN_MAX
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -133,7 +133,6 @@ ADCSensor = adc_ns.class_(
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        ADCSensor,
 | 
			
		||||
        unit_of_measurement=UNIT_VOLT,
 | 
			
		||||
        accuracy_decimals=2,
 | 
			
		||||
        device_class=DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
@@ -141,6 +140,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(ADCSensor),
 | 
			
		||||
            cv.Required(CONF_PIN): validate_adc_pin,
 | 
			
		||||
            cv.Optional(CONF_RAW, default=False): cv.boolean,
 | 
			
		||||
            cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,6 @@ ADS1115Sensor = ads1115_ns.class_(
 | 
			
		||||
CONF_ADS1115_ID = "ads1115_id"
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        ADS1115Sensor,
 | 
			
		||||
        unit_of_measurement=UNIT_VOLT,
 | 
			
		||||
        accuracy_decimals=3,
 | 
			
		||||
        device_class=DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
@@ -60,6 +59,7 @@ CONFIG_SCHEMA = (
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(ADS1115Sensor),
 | 
			
		||||
            cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
 | 
			
		||||
            cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
 | 
			
		||||
            cv.Required(CONF_GAIN): validate_gain,
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
 | 
			
		||||
 | 
			
		||||
    case ESP_GATTC_SEARCH_CMPL_EVT: {
 | 
			
		||||
      this->handle_ = 0;
 | 
			
		||||
      auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
 | 
			
		||||
      auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
 | 
			
		||||
      if (chr == nullptr) {
 | 
			
		||||
        ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
 | 
			
		||||
                 sensors_data_characteristic_uuid_.to_string().c_str());
 | 
			
		||||
@@ -56,7 +56,7 @@ void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
 | 
			
		||||
  auto *value = (WaveMiniReadings *) raw_value;
 | 
			
		||||
  auto value = (WaveMiniReadings *) raw_value;
 | 
			
		||||
 | 
			
		||||
  if (sizeof(WaveMiniReadings) <= value_len) {
 | 
			
		||||
    this->humidity_sensor_->publish_state(value->humidity / 100.0f);
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
 | 
			
		||||
 | 
			
		||||
    case ESP_GATTC_SEARCH_CMPL_EVT: {
 | 
			
		||||
      this->handle_ = 0;
 | 
			
		||||
      auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
 | 
			
		||||
      auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
 | 
			
		||||
      if (chr == nullptr) {
 | 
			
		||||
        ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
 | 
			
		||||
                 sensors_data_characteristic_uuid_.to_string().c_str());
 | 
			
		||||
@@ -56,7 +56,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
 | 
			
		||||
  auto *value = (WavePlusReadings *) raw_value;
 | 
			
		||||
  auto value = (WavePlusReadings *) raw_value;
 | 
			
		||||
 | 
			
		||||
  if (sizeof(WavePlusReadings) <= value_len) {
 | 
			
		||||
    ESP_LOGD(TAG, "version = %d", value->version);
 | 
			
		||||
 
 | 
			
		||||
@@ -19,14 +19,12 @@ uint16_t crc_16(uint8_t *ptr, uint8_t length) {
 | 
			
		||||
  //------------------------------
 | 
			
		||||
  while (length--) {
 | 
			
		||||
    crc ^= *ptr++;
 | 
			
		||||
    for (i = 0; i < 8; i++) {
 | 
			
		||||
    for (i = 0; i < 8; i++)
 | 
			
		||||
      if ((crc & 0x01) != 0) {
 | 
			
		||||
        crc >>= 1;
 | 
			
		||||
        crc ^= 0xA001;
 | 
			
		||||
      } else {
 | 
			
		||||
      } else
 | 
			
		||||
        crc >>= 1;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return crc;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_SEARCH_CMPL_EVT: {
 | 
			
		||||
      auto *chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
 | 
			
		||||
      auto chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
 | 
			
		||||
      if (chr == nullptr) {
 | 
			
		||||
        if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
 | 
			
		||||
          ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.",
 | 
			
		||||
@@ -75,14 +75,13 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
 | 
			
		||||
 | 
			
		||||
      if (this->current_sensor_ > 0) {
 | 
			
		||||
        if (this->illuminance_ != nullptr) {
 | 
			
		||||
          auto *packet = this->encoder_->get_light_level_request();
 | 
			
		||||
          auto packet = this->encoder_->get_light_level_request();
 | 
			
		||||
          auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
 | 
			
		||||
                                                 packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
 | 
			
		||||
                                                 ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
          if (status) {
 | 
			
		||||
          if (status)
 | 
			
		||||
            ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
 | 
			
		||||
                     status);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        this->current_sensor_ = 0;
 | 
			
		||||
      }
 | 
			
		||||
@@ -100,7 +99,7 @@ void Am43::update() {
 | 
			
		||||
  }
 | 
			
		||||
  if (this->current_sensor_ == 0) {
 | 
			
		||||
    if (this->battery_ != nullptr) {
 | 
			
		||||
      auto *packet = this->encoder_->get_battery_level_request();
 | 
			
		||||
      auto packet = this->encoder_->get_battery_level_request();
 | 
			
		||||
      auto status =
 | 
			
		||||
          esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
 | 
			
		||||
                                   packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ from esphome.const import CONF_ID, CONF_PIN
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@buxtronix"]
 | 
			
		||||
DEPENDENCIES = ["ble_client"]
 | 
			
		||||
AUTO_LOAD = ["am43", "sensor"]
 | 
			
		||||
AUTO_LOAD = ["am43"]
 | 
			
		||||
 | 
			
		||||
CONF_INVERT_POSITION = "invert_position"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,16 +25,15 @@ void Am43Component::setup() {
 | 
			
		||||
 | 
			
		||||
void Am43Component::loop() {
 | 
			
		||||
  if (this->node_state == espbt::ClientState::ESTABLISHED && !this->logged_in_) {
 | 
			
		||||
    auto *packet = this->encoder_->get_send_pin_request(this->pin_);
 | 
			
		||||
    auto packet = this->encoder_->get_send_pin_request(this->pin_);
 | 
			
		||||
    auto status =
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
 | 
			
		||||
                                 packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
    ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str());
 | 
			
		||||
    if (status) {
 | 
			
		||||
    if (status)
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status);
 | 
			
		||||
    } else {
 | 
			
		||||
    else
 | 
			
		||||
      this->logged_in_ = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -52,7 +51,7 @@ void Am43Component::control(const CoverCall &call) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (call.get_stop()) {
 | 
			
		||||
    auto *packet = this->encoder_->get_stop_request();
 | 
			
		||||
    auto packet = this->encoder_->get_stop_request();
 | 
			
		||||
    auto status =
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
 | 
			
		||||
                                 packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
@@ -64,7 +63,7 @@ void Am43Component::control(const CoverCall &call) {
 | 
			
		||||
 | 
			
		||||
    if (this->invert_position_)
 | 
			
		||||
      pos = 1 - pos;
 | 
			
		||||
    auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100));
 | 
			
		||||
    auto packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100));
 | 
			
		||||
    auto status =
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
 | 
			
		||||
                                 packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
@@ -81,7 +80,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_SEARCH_CMPL_EVT: {
 | 
			
		||||
      auto *chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
 | 
			
		||||
      auto chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
 | 
			
		||||
      if (chr == nullptr) {
 | 
			
		||||
        if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
 | 
			
		||||
          ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", this->get_name().c_str());
 | 
			
		||||
@@ -121,7 +120,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
 | 
			
		||||
      if (this->decoder_->has_pin_response()) {
 | 
			
		||||
        if (this->decoder_->pin_ok_) {
 | 
			
		||||
          ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str());
 | 
			
		||||
          auto *packet = this->encoder_->get_position_request();
 | 
			
		||||
          auto packet = this->encoder_->get_position_request();
 | 
			
		||||
          auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
 | 
			
		||||
                                                 packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
 | 
			
		||||
                                                 ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
CODEOWNERS = ["@ianchi"]
 | 
			
		||||
@@ -1,40 +0,0 @@
 | 
			
		||||
#include "analog_threshold_binary_sensor.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace analog_threshold {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "analog_threshold.binary_sensor";
 | 
			
		||||
 | 
			
		||||
void AnalogThresholdBinarySensor::setup() {
 | 
			
		||||
  float sensor_value = this->sensor_->get_state();
 | 
			
		||||
 | 
			
		||||
  // TRUE state is defined to be when sensor is >= threshold
 | 
			
		||||
  // so when undefined sensor value initialize to FALSE
 | 
			
		||||
  if (std::isnan(sensor_value)) {
 | 
			
		||||
    this->publish_initial_state(false);
 | 
			
		||||
  } else {
 | 
			
		||||
    this->publish_initial_state(sensor_value >= (this->lower_threshold_ + this->upper_threshold_) / 2.0f);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
 | 
			
		||||
  this->sensor_ = analog_sensor;
 | 
			
		||||
 | 
			
		||||
  this->sensor_->add_on_state_callback([this](float sensor_value) {
 | 
			
		||||
    // if there is an invalid sensor reading, ignore the change and keep the current state
 | 
			
		||||
    if (!std::isnan(sensor_value)) {
 | 
			
		||||
      this->publish_state(sensor_value >= (this->state ? this->lower_threshold_ : this->upper_threshold_));
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AnalogThresholdBinarySensor::dump_config() {
 | 
			
		||||
  LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this);
 | 
			
		||||
  LOG_SENSOR("  ", "Sensor", this->sensor_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Upper threshold: %.11f", this->upper_threshold_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Lower threshold: %.11f", this->lower_threshold_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace analog_threshold
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace analog_threshold {
 | 
			
		||||
 | 
			
		||||
class AnalogThresholdBinarySensor : public Component, public binary_sensor::BinarySensor {
 | 
			
		||||
 public:
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void setup() override;
 | 
			
		||||
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
  void set_sensor(sensor::Sensor *analog_sensor);
 | 
			
		||||
  void set_upper_threshold(float threshold) { this->upper_threshold_ = threshold; }
 | 
			
		||||
  void set_lower_threshold(float threshold) { this->lower_threshold_ = threshold; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  sensor::Sensor *sensor_{nullptr};
 | 
			
		||||
 | 
			
		||||
  float upper_threshold_;
 | 
			
		||||
  float lower_threshold_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace analog_threshold
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import binary_sensor, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_SENSOR_ID,
 | 
			
		||||
    CONF_THRESHOLD,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
analog_threshold_ns = cg.esphome_ns.namespace("analog_threshold")
 | 
			
		||||
 | 
			
		||||
AnalogThresholdBinarySensor = analog_threshold_ns.class_(
 | 
			
		||||
    "AnalogThresholdBinarySensor", binary_sensor.BinarySensor, cg.Component
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONF_UPPER = "upper"
 | 
			
		||||
CONF_LOWER = "lower"
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(AnalogThresholdBinarySensor),
 | 
			
		||||
        cv.Required(CONF_SENSOR_ID): cv.use_id(sensor.Sensor),
 | 
			
		||||
        cv.Required(CONF_THRESHOLD): cv.Any(
 | 
			
		||||
            cv.float_,
 | 
			
		||||
            cv.Schema(
 | 
			
		||||
                {cv.Required(CONF_UPPER): cv.float_, cv.Required(CONF_LOWER): cv.float_}
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = await binary_sensor.new_binary_sensor(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    sens = await cg.get_variable(config[CONF_SENSOR_ID])
 | 
			
		||||
    cg.add(var.set_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if isinstance(config[CONF_THRESHOLD], float):
 | 
			
		||||
        cg.add(var.set_upper_threshold(config[CONF_THRESHOLD]))
 | 
			
		||||
        cg.add(var.set_lower_threshold(config[CONF_THRESHOLD]))
 | 
			
		||||
    else:
 | 
			
		||||
        cg.add(var.set_upper_threshold(config[CONF_THRESHOLD][CONF_UPPER]))
 | 
			
		||||
        cg.add(var.set_lower_threshold(config[CONF_THRESHOLD][CONF_LOWER]))
 | 
			
		||||
@@ -76,8 +76,6 @@ async def to_code(config):
 | 
			
		||||
        pos = 0
 | 
			
		||||
        for frameIndex in range(frames):
 | 
			
		||||
            image.seek(frameIndex)
 | 
			
		||||
            if CONF_RESIZE in config:
 | 
			
		||||
                image.thumbnail(config[CONF_RESIZE])
 | 
			
		||||
            frame = image.convert("RGB")
 | 
			
		||||
            if CONF_RESIZE in config:
 | 
			
		||||
                frame = frame.resize([width, height])
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ void Anova::control(const ClimateCall &call) {
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
 | 
			
		||||
  }
 | 
			
		||||
  if (call.get_target_temperature().has_value()) {
 | 
			
		||||
    auto *pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature());
 | 
			
		||||
    auto pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature());
 | 
			
		||||
    auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
 | 
			
		||||
                                           pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
    if (status)
 | 
			
		||||
@@ -57,7 +57,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_SEARCH_CMPL_EVT: {
 | 
			
		||||
      auto *chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
 | 
			
		||||
      auto chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
 | 
			
		||||
      if (chr == nullptr) {
 | 
			
		||||
        ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
 | 
			
		||||
        ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
 | 
			
		||||
@@ -114,10 +114,9 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
 | 
			
		||||
          auto status =
 | 
			
		||||
              esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length,
 | 
			
		||||
                                       pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
          if (status) {
 | 
			
		||||
          if (status)
 | 
			
		||||
            ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
 | 
			
		||||
                     status);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
@@ -134,7 +133,7 @@ void Anova::update() {
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (this->current_request_ < 2) {
 | 
			
		||||
    auto *pkt = this->codec_->get_read_device_status_request();
 | 
			
		||||
    auto pkt = this->codec_->get_read_device_status_request();
 | 
			
		||||
    if (this->current_request_ == 0)
 | 
			
		||||
      this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
 | 
			
		||||
    auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
 | 
			
		||||
    traits.set_visual_temperature_step(0.1);
 | 
			
		||||
    return traits;
 | 
			
		||||
  }
 | 
			
		||||
  void set_unit_of_measurement(const char *unit);
 | 
			
		||||
  void set_unit_of_measurement(const char *);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::unique_ptr<AnovaCodec> codec_;
 | 
			
		||||
 
 | 
			
		||||
@@ -225,10 +225,9 @@ void APDS9960::read_gesture_data_() {
 | 
			
		||||
 | 
			
		||||
  uint8_t fifo_level;
 | 
			
		||||
  APDS9960_WARNING_CHECK(this->read_byte(0xAE, &fifo_level), "Reading FIFO level failed.");
 | 
			
		||||
  if (fifo_level == 0) {
 | 
			
		||||
  if (fifo_level == 0)
 | 
			
		||||
    // no data to process
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  APDS9960_WARNING_CHECK(fifo_level <= 32, "FIFO level has invalid value.")
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import binary_sensor
 | 
			
		||||
from esphome.const import CONF_DIRECTION, DEVICE_CLASS_MOVING
 | 
			
		||||
from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING
 | 
			
		||||
from . import APDS9960, CONF_APDS9960_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["apds9960"]
 | 
			
		||||
@@ -13,12 +13,13 @@ DIRECTIONS = {
 | 
			
		||||
    "RIGHT": "set_right_direction",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(
 | 
			
		||||
    device_class=DEVICE_CLASS_MOVING
 | 
			
		||||
).extend(
 | 
			
		||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
 | 
			
		||||
        cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
 | 
			
		||||
        cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
 | 
			
		||||
        cv.Optional(
 | 
			
		||||
            CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING
 | 
			
		||||
        ): binary_sensor.device_class,
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,6 @@ service APIConnection {
 | 
			
		||||
  rpc number_command (NumberCommandRequest) returns (void) {}
 | 
			
		||||
  rpc select_command (SelectCommandRequest) returns (void) {}
 | 
			
		||||
  rpc button_command (ButtonCommandRequest) returns (void) {}
 | 
			
		||||
  rpc lock_command (LockCommandRequest) returns (void) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -96,9 +95,6 @@ message HelloResponse {
 | 
			
		||||
  // and only exists for debugging/logging purposes.
 | 
			
		||||
  // For example "ESPHome v1.10.0 on ESP8266"
 | 
			
		||||
  string server_info = 3;
 | 
			
		||||
 | 
			
		||||
  // The name of the server (App.get_name())
 | 
			
		||||
  string name = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Message sent at the beginning of each connection to authenticate the client
 | 
			
		||||
@@ -529,7 +525,6 @@ message ListEntitiesSwitchResponse {
 | 
			
		||||
  bool assumed_state = 6;
 | 
			
		||||
  bool disabled_by_default = 7;
 | 
			
		||||
  EntityCategory entity_category = 8;
 | 
			
		||||
  string device_class = 9;
 | 
			
		||||
}
 | 
			
		||||
message SwitchStateResponse {
 | 
			
		||||
  option (id) = 26;
 | 
			
		||||
@@ -958,63 +953,6 @@ message SelectCommandRequest {
 | 
			
		||||
  string state = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// ==================== LOCK ====================
 | 
			
		||||
enum LockState {
 | 
			
		||||
  LOCK_STATE_NONE = 0;
 | 
			
		||||
  LOCK_STATE_LOCKED = 1;
 | 
			
		||||
  LOCK_STATE_UNLOCKED = 2;
 | 
			
		||||
  LOCK_STATE_JAMMED = 3;
 | 
			
		||||
  LOCK_STATE_LOCKING = 4;
 | 
			
		||||
  LOCK_STATE_UNLOCKING = 5;
 | 
			
		||||
}
 | 
			
		||||
enum LockCommand  {
 | 
			
		||||
  LOCK_UNLOCK = 0;
 | 
			
		||||
  LOCK_LOCK = 1;
 | 
			
		||||
  LOCK_OPEN = 2;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesLockResponse {
 | 
			
		||||
  option (id) = 58;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_LOCK";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  string icon = 5;
 | 
			
		||||
  bool disabled_by_default = 6;
 | 
			
		||||
  EntityCategory entity_category = 7;
 | 
			
		||||
  bool assumed_state = 8;
 | 
			
		||||
 | 
			
		||||
  bool supports_open = 9;
 | 
			
		||||
  bool requires_code = 10;
 | 
			
		||||
 | 
			
		||||
  # Not yet implemented:
 | 
			
		||||
  string code_format = 11;
 | 
			
		||||
}
 | 
			
		||||
message LockStateResponse {
 | 
			
		||||
  option (id) = 59;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_LOCK";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  LockState state = 2;
 | 
			
		||||
}
 | 
			
		||||
message LockCommandRequest {
 | 
			
		||||
  option (id) = 60;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_LOCK";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  LockCommand command = 2;
 | 
			
		||||
 | 
			
		||||
  # Not yet implemented:
 | 
			
		||||
  bool has_code = 3;
 | 
			
		||||
  string code = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== BUTTON ====================
 | 
			
		||||
message ListEntitiesButtonResponse {
 | 
			
		||||
  option (id) = 61;
 | 
			
		||||
@@ -1039,4 +977,3 @@ message ButtonCommandRequest {
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,10 +20,9 @@ namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "api.connection";
 | 
			
		||||
static const int ESP32_CAMERA_STOP_STREAM = 5000;
 | 
			
		||||
 | 
			
		||||
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
 | 
			
		||||
    : parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
 | 
			
		||||
    : parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
 | 
			
		||||
  this->proto_write_buffer_.reserve(64);
 | 
			
		||||
 | 
			
		||||
#if defined(USE_API_PLAINTEXT)
 | 
			
		||||
@@ -105,7 +104,6 @@ void APIConnection::loop() {
 | 
			
		||||
      ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str());
 | 
			
		||||
    }
 | 
			
		||||
  } else if (now - this->last_traffic_ > keepalive) {
 | 
			
		||||
    ESP_LOGVV(TAG, "Sending keepalive PING...");
 | 
			
		||||
    this->sent_ping_ = true;
 | 
			
		||||
    this->send_ping_request(PingRequest());
 | 
			
		||||
  }
 | 
			
		||||
@@ -256,7 +254,7 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
 | 
			
		||||
// Shut-up about usage of deprecated speed_level_to_enum/speed_enum_to_level functions for a bit.
 | 
			
		||||
#pragma GCC diagnostic push
 | 
			
		||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 | 
			
		||||
bool APIConnection::send_fan_state(fan::Fan *fan) {
 | 
			
		||||
bool APIConnection::send_fan_state(fan::FanState *fan) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
@@ -274,7 +272,7 @@ bool APIConnection::send_fan_state(fan::Fan *fan) {
 | 
			
		||||
    resp.direction = static_cast<enums::FanDirection>(fan->direction);
 | 
			
		||||
  return this->send_fan_state_response(resp);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_fan_info(fan::Fan *fan) {
 | 
			
		||||
bool APIConnection::send_fan_info(fan::FanState *fan) {
 | 
			
		||||
  auto traits = fan->get_traits();
 | 
			
		||||
  ListEntitiesFanResponse msg;
 | 
			
		||||
  msg.key = fan->get_object_id_hash();
 | 
			
		||||
@@ -291,7 +289,7 @@ bool APIConnection::send_fan_info(fan::Fan *fan) {
 | 
			
		||||
  return this->send_list_entities_fan_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
 | 
			
		||||
  fan::Fan *fan = App.get_fan_by_key(msg.key);
 | 
			
		||||
  fan::FanState *fan = App.get_fan_by_key(msg.key);
 | 
			
		||||
  if (fan == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
@@ -463,7 +461,6 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
 | 
			
		||||
  msg.assumed_state = a_switch->assumed_state();
 | 
			
		||||
  msg.disabled_by_default = a_switch->is_disabled_by_default();
 | 
			
		||||
  msg.entity_category = static_cast<enums::EntityCategory>(a_switch->get_entity_category());
 | 
			
		||||
  msg.device_class = a_switch->get_device_class();
 | 
			
		||||
  return this->send_list_entities_switch_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
 | 
			
		||||
@@ -471,11 +468,10 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) {
 | 
			
		||||
  if (a_switch == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (msg.state) {
 | 
			
		||||
  if (msg.state)
 | 
			
		||||
    a_switch->turn_on();
 | 
			
		||||
  } else {
 | 
			
		||||
  else
 | 
			
		||||
    a_switch->turn_off();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -702,58 +698,13 @@ void APIConnection::button_command(const ButtonCommandRequest &msg) {
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  LockStateResponse resp{};
 | 
			
		||||
  resp.key = a_lock->get_object_id_hash();
 | 
			
		||||
  resp.state = static_cast<enums::LockState>(state);
 | 
			
		||||
  return this->send_lock_state_response(resp);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_lock_info(lock::Lock *a_lock) {
 | 
			
		||||
  ListEntitiesLockResponse msg;
 | 
			
		||||
  msg.key = a_lock->get_object_id_hash();
 | 
			
		||||
  msg.object_id = a_lock->get_object_id();
 | 
			
		||||
  msg.name = a_lock->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("lock", a_lock);
 | 
			
		||||
  msg.icon = a_lock->get_icon();
 | 
			
		||||
  msg.assumed_state = a_lock->traits.get_assumed_state();
 | 
			
		||||
  msg.disabled_by_default = a_lock->is_disabled_by_default();
 | 
			
		||||
  msg.entity_category = static_cast<enums::EntityCategory>(a_lock->get_entity_category());
 | 
			
		||||
  msg.supports_open = a_lock->traits.get_supports_open();
 | 
			
		||||
  msg.requires_code = a_lock->traits.get_requires_code();
 | 
			
		||||
  return this->send_list_entities_lock_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::lock_command(const LockCommandRequest &msg) {
 | 
			
		||||
  lock::Lock *a_lock = App.get_lock_by_key(msg.key);
 | 
			
		||||
  if (a_lock == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  switch (msg.command) {
 | 
			
		||||
    case enums::LOCK_UNLOCK:
 | 
			
		||||
      a_lock->unlock();
 | 
			
		||||
      break;
 | 
			
		||||
    case enums::LOCK_LOCK:
 | 
			
		||||
      a_lock->lock();
 | 
			
		||||
      break;
 | 
			
		||||
    case enums::LOCK_OPEN:
 | 
			
		||||
      a_lock->open();
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return;
 | 
			
		||||
  if (this->image_reader_.available())
 | 
			
		||||
    return;
 | 
			
		||||
  if (image->was_requested_by(esphome::esp32_camera::API_REQUESTER) ||
 | 
			
		||||
      image->was_requested_by(esphome::esp32_camera::IDLE))
 | 
			
		||||
    this->image_reader_.set_image(std::move(image));
 | 
			
		||||
  this->image_reader_.set_image(std::move(image));
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
 | 
			
		||||
  ListEntitiesCameraResponse msg;
 | 
			
		||||
@@ -771,14 +722,9 @@ void APIConnection::camera_image(const CameraImageRequest &msg) {
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (msg.single)
 | 
			
		||||
    esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::API_REQUESTER);
 | 
			
		||||
  if (msg.stream) {
 | 
			
		||||
    esp32_camera::global_esp32_camera->start_stream(esphome::esp32_camera::API_REQUESTER);
 | 
			
		||||
 | 
			
		||||
    App.scheduler.set_timeout(this->parent_, "api_esp32_camera_stop_stream", ESP32_CAMERA_STOP_STREAM, []() {
 | 
			
		||||
      esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_camera::API_REQUESTER);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
    esp32_camera::global_esp32_camera->request_image();
 | 
			
		||||
  if (msg.stream)
 | 
			
		||||
    esp32_camera::global_esp32_camera->request_stream();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -812,8 +758,6 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
 | 
			
		||||
  resp.api_version_major = 1;
 | 
			
		||||
  resp.api_version_minor = 6;
 | 
			
		||||
  resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
 | 
			
		||||
  resp.name = App.get_name();
 | 
			
		||||
 | 
			
		||||
  this->connection_state_ = ConnectionState::CONNECTED;
 | 
			
		||||
  return resp;
 | 
			
		||||
}
 | 
			
		||||
@@ -851,16 +795,15 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
 | 
			
		||||
  resp.project_version = ESPHOME_PROJECT_VERSION;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_WEBSERVER
 | 
			
		||||
  resp.webserver_port = USE_WEBSERVER_PORT;
 | 
			
		||||
  resp.webserver_port = WEBSERVER_PORT;
 | 
			
		||||
#endif
 | 
			
		||||
  return resp;
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
 | 
			
		||||
  for (auto &it : this->parent_->get_state_subs()) {
 | 
			
		||||
  for (auto &it : this->parent_->get_state_subs())
 | 
			
		||||
    if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
 | 
			
		||||
      it.callback(msg.state);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
 | 
			
		||||
  bool found = false;
 | 
			
		||||
@@ -909,7 +852,7 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  // Do not set last_traffic_ on send
 | 
			
		||||
  this->last_traffic_ = millis();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_unauthenticated_access() {
 | 
			
		||||
 
 | 
			
		||||
@@ -32,8 +32,8 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  void cover_command(const CoverCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool send_fan_state(fan::Fan *fan);
 | 
			
		||||
  bool send_fan_info(fan::Fan *fan);
 | 
			
		||||
  bool send_fan_state(fan::FanState *fan);
 | 
			
		||||
  bool send_fan_info(fan::FanState *fan);
 | 
			
		||||
  void fan_command(const FanCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
@@ -77,11 +77,6 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  bool send_button_info(button::Button *button);
 | 
			
		||||
  void button_command(const ButtonCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
 | 
			
		||||
  bool send_lock_info(lock::Lock *a_lock);
 | 
			
		||||
  void lock_command(const LockCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
  bool send_log_message(int level, const char *tag, const char *line);
 | 
			
		||||
  void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,7 @@
 | 
			
		||||
#include "api_frame_helper.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "proto.h"
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
@@ -303,16 +301,9 @@ APIError APINoiseFrameHelper::state_action_() {
 | 
			
		||||
  }
 | 
			
		||||
  if (state_ == State::SERVER_HELLO) {
 | 
			
		||||
    // send server hello
 | 
			
		||||
    std::vector<uint8_t> msg;
 | 
			
		||||
    // chosen proto
 | 
			
		||||
    msg.push_back(0x01);
 | 
			
		||||
 | 
			
		||||
    // node name, terminated by null byte
 | 
			
		||||
    const std::string &name = App.get_name();
 | 
			
		||||
    const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
 | 
			
		||||
    msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
 | 
			
		||||
 | 
			
		||||
    aerr = write_frame_(msg.data(), msg.size());
 | 
			
		||||
    uint8_t msg[1];
 | 
			
		||||
    msg[0] = 0x01;  // chosen proto
 | 
			
		||||
    aerr = write_frame_(msg, 1);
 | 
			
		||||
    if (aerr != APIError::OK)
 | 
			
		||||
      return aerr;
 | 
			
		||||
 | 
			
		||||
@@ -730,12 +721,7 @@ APIError APINoiseFrameHelper::shutdown(int how) {
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
 | 
			
		||||
void noise_rand_bytes(void *output, size_t len) {
 | 
			
		||||
  if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!");
 | 
			
		||||
    arch_restart();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void noise_rand_bytes(void *output, size_t len) { esphome::fill_random(reinterpret_cast<uint8_t *>(output), len); }
 | 
			
		||||
}
 | 
			
		||||
#endif  // USE_API_NOISE
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -278,36 +278,6 @@ template<> const char *proto_enum_to_string<enums::NumberMode>(enums::NumberMode
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::LockState>(enums::LockState value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::LOCK_STATE_NONE:
 | 
			
		||||
      return "LOCK_STATE_NONE";
 | 
			
		||||
    case enums::LOCK_STATE_LOCKED:
 | 
			
		||||
      return "LOCK_STATE_LOCKED";
 | 
			
		||||
    case enums::LOCK_STATE_UNLOCKED:
 | 
			
		||||
      return "LOCK_STATE_UNLOCKED";
 | 
			
		||||
    case enums::LOCK_STATE_JAMMED:
 | 
			
		||||
      return "LOCK_STATE_JAMMED";
 | 
			
		||||
    case enums::LOCK_STATE_LOCKING:
 | 
			
		||||
      return "LOCK_STATE_LOCKING";
 | 
			
		||||
    case enums::LOCK_STATE_UNLOCKING:
 | 
			
		||||
      return "LOCK_STATE_UNLOCKING";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::LockCommand>(enums::LockCommand value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::LOCK_UNLOCK:
 | 
			
		||||
      return "LOCK_UNLOCK";
 | 
			
		||||
    case enums::LOCK_LOCK:
 | 
			
		||||
      return "LOCK_LOCK";
 | 
			
		||||
    case enums::LOCK_OPEN:
 | 
			
		||||
      return "LOCK_OPEN";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
@@ -349,10 +319,6 @@ bool HelloResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value)
 | 
			
		||||
      this->server_info = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 4: {
 | 
			
		||||
      this->name = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -361,7 +327,6 @@ void HelloResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_uint32(1, this->api_version_major);
 | 
			
		||||
  buffer.encode_uint32(2, this->api_version_minor);
 | 
			
		||||
  buffer.encode_string(3, this->server_info);
 | 
			
		||||
  buffer.encode_string(4, this->name);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void HelloResponse::dump_to(std::string &out) const {
 | 
			
		||||
@@ -380,10 +345,6 @@ void HelloResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  server_info: ");
 | 
			
		||||
  out.append("'").append(this->server_info).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  name: ");
 | 
			
		||||
  out.append("'").append(this->name).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -2177,10 +2138,6 @@ bool ListEntitiesSwitchResponse::decode_length(uint32_t field_id, ProtoLengthDel
 | 
			
		||||
      this->icon = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 9: {
 | 
			
		||||
      this->device_class = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -2204,7 +2161,6 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_bool(6, this->assumed_state);
 | 
			
		||||
  buffer.encode_bool(7, this->disabled_by_default);
 | 
			
		||||
  buffer.encode_enum<enums::EntityCategory>(8, this->entity_category);
 | 
			
		||||
  buffer.encode_string(9, this->device_class);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
 | 
			
		||||
@@ -2242,10 +2198,6 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  entity_category: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  device_class: ");
 | 
			
		||||
  out.append("'").append(this->device_class).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -4225,234 +4177,6 @@ void SelectCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool ListEntitiesLockResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 6: {
 | 
			
		||||
      this->disabled_by_default = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 7: {
 | 
			
		||||
      this->entity_category = value.as_enum<enums::EntityCategory>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 8: {
 | 
			
		||||
      this->assumed_state = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 9: {
 | 
			
		||||
      this->supports_open = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 10: {
 | 
			
		||||
      this->requires_code = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesLockResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->object_id = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->name = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 4: {
 | 
			
		||||
      this->unique_id = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 5: {
 | 
			
		||||
      this->icon = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 11: {
 | 
			
		||||
      this->code_format = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesLockResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->key = value.as_fixed32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void ListEntitiesLockResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_string(1, this->object_id);
 | 
			
		||||
  buffer.encode_fixed32(2, this->key);
 | 
			
		||||
  buffer.encode_string(3, this->name);
 | 
			
		||||
  buffer.encode_string(4, this->unique_id);
 | 
			
		||||
  buffer.encode_string(5, this->icon);
 | 
			
		||||
  buffer.encode_bool(6, this->disabled_by_default);
 | 
			
		||||
  buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
 | 
			
		||||
  buffer.encode_bool(8, this->assumed_state);
 | 
			
		||||
  buffer.encode_bool(9, this->supports_open);
 | 
			
		||||
  buffer.encode_bool(10, this->requires_code);
 | 
			
		||||
  buffer.encode_string(11, this->code_format);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesLockResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesLockResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  name: ");
 | 
			
		||||
  out.append("'").append(this->name).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  unique_id: ");
 | 
			
		||||
  out.append("'").append(this->unique_id).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  icon: ");
 | 
			
		||||
  out.append("'").append(this->icon).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  disabled_by_default: ");
 | 
			
		||||
  out.append(YESNO(this->disabled_by_default));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  entity_category: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  assumed_state: ");
 | 
			
		||||
  out.append(YESNO(this->assumed_state));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  supports_open: ");
 | 
			
		||||
  out.append(YESNO(this->supports_open));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  requires_code: ");
 | 
			
		||||
  out.append(YESNO(this->requires_code));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  code_format: ");
 | 
			
		||||
  out.append("'").append(this->code_format).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool LockStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->state = value.as_enum<enums::LockState>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool LockStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->key = value.as_fixed32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void LockStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_fixed32(1, this->key);
 | 
			
		||||
  buffer.encode_enum<enums::LockState>(2, this->state);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void LockStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("LockStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  state: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::LockState>(this->state));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool LockCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->command = value.as_enum<enums::LockCommand>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->has_code = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool LockCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 4: {
 | 
			
		||||
      this->code = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->key = value.as_fixed32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void LockCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_fixed32(1, this->key);
 | 
			
		||||
  buffer.encode_enum<enums::LockCommand>(2, this->command);
 | 
			
		||||
  buffer.encode_bool(3, this->has_code);
 | 
			
		||||
  buffer.encode_string(4, this->code);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void LockCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("LockCommandRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  command: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::LockCommand>(this->command));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  has_code: ");
 | 
			
		||||
  out.append(YESNO(this->has_code));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  code: ");
 | 
			
		||||
  out.append("'").append(this->code).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 6: {
 | 
			
		||||
@@ -4515,7 +4239,7 @@ void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesButtonResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesButtonResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -4565,7 +4289,7 @@ bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
 | 
			
		||||
void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); }
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ButtonCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  out.append("ButtonCommandRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
 
 | 
			
		||||
@@ -128,19 +128,6 @@ enum NumberMode : uint32_t {
 | 
			
		||||
  NUMBER_MODE_BOX = 1,
 | 
			
		||||
  NUMBER_MODE_SLIDER = 2,
 | 
			
		||||
};
 | 
			
		||||
enum LockState : uint32_t {
 | 
			
		||||
  LOCK_STATE_NONE = 0,
 | 
			
		||||
  LOCK_STATE_LOCKED = 1,
 | 
			
		||||
  LOCK_STATE_UNLOCKED = 2,
 | 
			
		||||
  LOCK_STATE_JAMMED = 3,
 | 
			
		||||
  LOCK_STATE_LOCKING = 4,
 | 
			
		||||
  LOCK_STATE_UNLOCKING = 5,
 | 
			
		||||
};
 | 
			
		||||
enum LockCommand : uint32_t {
 | 
			
		||||
  LOCK_UNLOCK = 0,
 | 
			
		||||
  LOCK_LOCK = 1,
 | 
			
		||||
  LOCK_OPEN = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace enums
 | 
			
		||||
 | 
			
		||||
@@ -160,7 +147,6 @@ class HelloResponse : public ProtoMessage {
 | 
			
		||||
  uint32_t api_version_major{0};
 | 
			
		||||
  uint32_t api_version_minor{0};
 | 
			
		||||
  std::string server_info{};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
@@ -580,7 +566,6 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
 | 
			
		||||
  bool assumed_state{false};
 | 
			
		||||
  bool disabled_by_default{false};
 | 
			
		||||
  enums::EntityCategory entity_category{};
 | 
			
		||||
  std::string device_class{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
@@ -1063,58 +1048,6 @@ class SelectCommandRequest : public ProtoMessage {
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesLockResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  std::string icon{};
 | 
			
		||||
  bool disabled_by_default{false};
 | 
			
		||||
  enums::EntityCategory entity_category{};
 | 
			
		||||
  bool assumed_state{false};
 | 
			
		||||
  bool supports_open{false};
 | 
			
		||||
  bool requires_code{false};
 | 
			
		||||
  std::string code_format{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class LockStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  enums::LockState state{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class LockCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  enums::LockCommand command{};
 | 
			
		||||
  bool has_code{false};
 | 
			
		||||
  std::string code{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesButtonResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
 
 | 
			
		||||
@@ -282,24 +282,6 @@ bool APIServerConnectionBase::send_select_state_response(const SelectStateRespon
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_lock_response(const ListEntitiesLockResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_lock_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesLockResponse>(msg, 58);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
bool APIServerConnectionBase::send_lock_state_response(const LockStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_lock_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<LockStateResponse>(msg, 59);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
@@ -541,17 +523,6 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
			
		||||
      ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_select_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 60: {
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
      LockCommandRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_lock_command_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_lock_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -800,19 +771,6 @@ void APIServerConnection::on_button_command_request(const ButtonCommandRequest &
 | 
			
		||||
  this->button_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->lock_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -130,15 +130,6 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  virtual void on_select_command_request(const SelectCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  bool send_list_entities_lock_response(const ListEntitiesLockResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  bool send_lock_state_response(const LockStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  virtual void on_lock_command_request(const LockCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -189,9 +180,6 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  virtual void button_command(const ButtonCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  virtual void lock_command(const LockCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  void on_hello_request(const HelloRequest &msg) override;
 | 
			
		||||
@@ -233,9 +221,6 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  void on_button_command_request(const ButtonCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  void on_lock_command_request(const LockCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ static const char *const TAG = "api";
 | 
			
		||||
void APIServer::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
 | 
			
		||||
  this->setup_controller();
 | 
			
		||||
  socket_ = socket::socket_ip(SOCK_STREAM, 0);
 | 
			
		||||
  socket_ = socket::socket(AF_INET, SOCK_STREAM, 0);
 | 
			
		||||
  if (socket_ == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "Could not create socket.");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
@@ -43,16 +43,13 @@ void APIServer::setup() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  struct sockaddr_storage server;
 | 
			
		||||
  struct sockaddr_in server;
 | 
			
		||||
  memset(&server, 0, sizeof(server));
 | 
			
		||||
  server.sin_family = AF_INET;
 | 
			
		||||
  server.sin_addr.s_addr = ESPHOME_INADDR_ANY;
 | 
			
		||||
  server.sin_port = htons(this->port_);
 | 
			
		||||
 | 
			
		||||
  socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), htons(this->port_));
 | 
			
		||||
  if (sl == 0) {
 | 
			
		||||
    ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  err = socket_->bind((struct sockaddr *) &server, sl);
 | 
			
		||||
  err = socket_->bind((struct sockaddr *) &server, sizeof(server));
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
@@ -83,10 +80,9 @@ void APIServer::setup() {
 | 
			
		||||
  if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) {
 | 
			
		||||
    esp32_camera::global_esp32_camera->add_image_callback(
 | 
			
		||||
        [this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
 | 
			
		||||
          for (auto &c : this->clients_) {
 | 
			
		||||
          for (auto &c : this->clients_)
 | 
			
		||||
            if (!c->remove_)
 | 
			
		||||
              c->send_camera_state(image);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
@@ -192,7 +188,7 @@ void APIServer::on_cover_update(cover::Cover *obj) {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
void APIServer::on_fan_update(fan::Fan *obj) {
 | 
			
		||||
void APIServer::on_fan_update(fan::FanState *obj) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto &c : this->clients_)
 | 
			
		||||
@@ -263,15 +259,6 @@ void APIServer::on_select_update(select::Select *obj, const std::string &state)
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
void APIServer::on_lock_update(lock::Lock *obj) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto &c : this->clients_)
 | 
			
		||||
    c->send_lock_state(obj, obj->state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
 | 
			
		||||
void APIServer::set_port(uint16_t port) { this->port_ = port; }
 | 
			
		||||
APIServer *global_api_server = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
#include "esphome/components/socket/socket.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "api_pb2_service.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "list_entities.h"
 | 
			
		||||
#include "subscribe_state.h"
 | 
			
		||||
#include "user_services.h"
 | 
			
		||||
@@ -43,7 +44,7 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
  void on_cover_update(cover::Cover *obj) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  void on_fan_update(fan::Fan *obj) override;
 | 
			
		||||
  void on_fan_update(fan::FanState *obj) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  void on_light_update(light::LightState *obj) override;
 | 
			
		||||
@@ -65,9 +66,6 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  void on_select_update(select::Select *obj, const std::string &state) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  void on_lock_update(lock::Lock *obj) override;
 | 
			
		||||
#endif
 | 
			
		||||
  void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
 | 
			
		||||
  void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ async def async_run_logs(config, address):
 | 
			
		||||
    if CONF_ENCRYPTION in conf:
 | 
			
		||||
        noise_psk = conf[CONF_ENCRYPTION][CONF_KEY]
 | 
			
		||||
    _LOGGER.info("Starting log output from %s using esphome API", address)
 | 
			
		||||
    zc = zeroconf.Zeroconf()
 | 
			
		||||
    cli = APIClient(
 | 
			
		||||
        address,
 | 
			
		||||
        port,
 | 
			
		||||
 
 | 
			
		||||
@@ -12,10 +12,10 @@ template<typename... X> class TemplatableStringValue : public TemplatableValue<s
 | 
			
		||||
 public:
 | 
			
		||||
  TemplatableStringValue() : TemplatableValue<std::string, X...>() {}
 | 
			
		||||
 | 
			
		||||
  template<typename F, enable_if_t<!is_invocable<F, X...>::value, int> = 0>
 | 
			
		||||
  template<typename F, enable_if_t<!is_callable<F, X...>::value, int> = 0>
 | 
			
		||||
  TemplatableStringValue(F value) : TemplatableValue<std::string, X...>(value) {}
 | 
			
		||||
 | 
			
		||||
  template<typename F, enable_if_t<is_invocable<F, X...>::value, int> = 0>
 | 
			
		||||
  template<typename F, enable_if_t<is_callable<F, X...>::value, int> = 0>
 | 
			
		||||
  TemplatableStringValue(F f)
 | 
			
		||||
      : TemplatableValue<std::string, X...>([f](X... x) -> std::string { return to_string(f(x...)); }) {}
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_
 | 
			
		||||
bool ListEntitiesIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_info(cover); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
bool ListEntitiesIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_info(fan); }
 | 
			
		||||
bool ListEntitiesIterator::on_fan(fan::FanState *fan) { return this->client_->send_fan_info(fan); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
bool ListEntitiesIterator::on_light(light::LightState *light) { return this->client_->send_light_info(light); }
 | 
			
		||||
@@ -35,12 +35,10 @@ bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor)
 | 
			
		||||
  return this->client_->send_text_sensor_info(text_sensor);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_info(a_lock); }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
 | 
			
		||||
ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {}
 | 
			
		||||
ListEntitiesIterator::ListEntitiesIterator(APIServer *server, APIConnection *client)
 | 
			
		||||
    : ComponentIterator(server), client_(client) {}
 | 
			
		||||
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
 | 
			
		||||
  auto resp = service->encode_list_service_response();
 | 
			
		||||
  return this->client_->send_list_entities_services_response(resp);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/component_iterator.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
@@ -11,7 +11,7 @@ class APIConnection;
 | 
			
		||||
 | 
			
		||||
class ListEntitiesIterator : public ComponentIterator {
 | 
			
		||||
 public:
 | 
			
		||||
  ListEntitiesIterator(APIConnection *client);
 | 
			
		||||
  ListEntitiesIterator(APIServer *server, APIConnection *client);
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -19,7 +19,7 @@ class ListEntitiesIterator : public ComponentIterator {
 | 
			
		||||
  bool on_cover(cover::Cover *cover) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool on_fan(fan::Fan *fan) override;
 | 
			
		||||
  bool on_fan(fan::FanState *fan) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  bool on_light(light::LightState *light) override;
 | 
			
		||||
@@ -48,9 +48,6 @@ class ListEntitiesIterator : public ComponentIterator {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  bool on_select(select::Select *select) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  bool on_lock(lock::Lock *a_lock) override;
 | 
			
		||||
#endif
 | 
			
		||||
  bool on_end() override;
 | 
			
		||||
 | 
			
		||||
@@ -60,3 +57,5 @@ class ListEntitiesIterator : public ComponentIterator {
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "proto.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
 
 | 
			
		||||
@@ -55,19 +55,17 @@ class ProtoVarInt {
 | 
			
		||||
  }
 | 
			
		||||
  int32_t as_sint32() const {
 | 
			
		||||
    // with ZigZag encoding
 | 
			
		||||
    if (this->value_ & 1) {
 | 
			
		||||
    if (this->value_ & 1)
 | 
			
		||||
      return static_cast<int32_t>(~(this->value_ >> 1));
 | 
			
		||||
    } else {
 | 
			
		||||
    else
 | 
			
		||||
      return static_cast<int32_t>(this->value_ >> 1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  int64_t as_sint64() const {
 | 
			
		||||
    // with ZigZag encoding
 | 
			
		||||
    if (this->value_ & 1) {
 | 
			
		||||
    if (this->value_ & 1)
 | 
			
		||||
      return static_cast<int64_t>(~(this->value_ >> 1));
 | 
			
		||||
    } else {
 | 
			
		||||
    else
 | 
			
		||||
      return static_cast<int64_t>(this->value_ >> 1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  void encode(std::vector<uint8_t> &out) {
 | 
			
		||||
    uint32_t val = this->value_;
 | 
			
		||||
@@ -195,20 +193,6 @@ class ProtoWriteBuffer {
 | 
			
		||||
    this->write((value >> 16) & 0xFF);
 | 
			
		||||
    this->write((value >> 24) & 0xFF);
 | 
			
		||||
  }
 | 
			
		||||
  void encode_fixed64(uint32_t field_id, uint64_t value, bool force = false) {
 | 
			
		||||
    if (value == 0 && !force)
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    this->encode_field_raw(field_id, 5);
 | 
			
		||||
    this->write((value >> 0) & 0xFF);
 | 
			
		||||
    this->write((value >> 8) & 0xFF);
 | 
			
		||||
    this->write((value >> 16) & 0xFF);
 | 
			
		||||
    this->write((value >> 24) & 0xFF);
 | 
			
		||||
    this->write((value >> 32) & 0xFF);
 | 
			
		||||
    this->write((value >> 40) & 0xFF);
 | 
			
		||||
    this->write((value >> 48) & 0xFF);
 | 
			
		||||
    this->write((value >> 56) & 0xFF);
 | 
			
		||||
  }
 | 
			
		||||
  template<typename T> void encode_enum(uint32_t field_id, T value, bool force = false) {
 | 
			
		||||
    this->encode_uint32(field_id, static_cast<uint32_t>(value), force);
 | 
			
		||||
  }
 | 
			
		||||
@@ -236,22 +220,12 @@ class ProtoWriteBuffer {
 | 
			
		||||
  }
 | 
			
		||||
  void encode_sint32(uint32_t field_id, int32_t value, bool force = false) {
 | 
			
		||||
    uint32_t uvalue;
 | 
			
		||||
    if (value < 0) {
 | 
			
		||||
    if (value < 0)
 | 
			
		||||
      uvalue = ~(value << 1);
 | 
			
		||||
    } else {
 | 
			
		||||
    else
 | 
			
		||||
      uvalue = value << 1;
 | 
			
		||||
    }
 | 
			
		||||
    this->encode_uint32(field_id, uvalue, force);
 | 
			
		||||
  }
 | 
			
		||||
  void encode_sint64(uint32_t field_id, int64_t value, bool force = false) {
 | 
			
		||||
    uint64_t uvalue;
 | 
			
		||||
    if (value < 0) {
 | 
			
		||||
      uvalue = ~(value << 1);
 | 
			
		||||
    } else {
 | 
			
		||||
      uvalue = value << 1;
 | 
			
		||||
    }
 | 
			
		||||
    this->encode_uint64(field_id, uvalue, force);
 | 
			
		||||
  }
 | 
			
		||||
  template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) {
 | 
			
		||||
    this->encode_field_raw(field_id, 2);
 | 
			
		||||
    size_t begin = this->buffer_->size();
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_
 | 
			
		||||
bool InitialStateIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_state(cover); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_state(fan); }
 | 
			
		||||
bool InitialStateIterator::on_fan(fan::FanState *fan) { return this->client_->send_fan_state(fan); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); }
 | 
			
		||||
@@ -47,10 +47,8 @@ bool InitialStateIterator::on_select(select::Select *select) {
 | 
			
		||||
  return this->client_->send_select_state(select, select->state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); }
 | 
			
		||||
#endif
 | 
			
		||||
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
 | 
			
		||||
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
 | 
			
		||||
    : ComponentIterator(server), client_(client) {}
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/component_iterator.h"
 | 
			
		||||
#include "esphome/core/controller.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
@@ -12,7 +12,7 @@ class APIConnection;
 | 
			
		||||
 | 
			
		||||
class InitialStateIterator : public ComponentIterator {
 | 
			
		||||
 public:
 | 
			
		||||
  InitialStateIterator(APIConnection *client);
 | 
			
		||||
  InitialStateIterator(APIServer *server, APIConnection *client);
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -20,7 +20,7 @@ class InitialStateIterator : public ComponentIterator {
 | 
			
		||||
  bool on_cover(cover::Cover *cover) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool on_fan(fan::Fan *fan) override;
 | 
			
		||||
  bool on_fan(fan::FanState *fan) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  bool on_light(light::LightState *light) override;
 | 
			
		||||
@@ -45,9 +45,6 @@ class InitialStateIterator : public ComponentIterator {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  bool on_select(select::Select *select) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  bool on_lock(lock::Lock *a_lock) override;
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  APIConnection *client_;
 | 
			
		||||
@@ -55,3 +52,5 @@ class InitialStateIterator : public ComponentIterator {
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  virtual void execute(Ts... x) = 0;
 | 
			
		||||
  template<int... S> void execute_(std::vector<ExecuteServiceArgument> args, seq<S...> type) {
 | 
			
		||||
  template<int... S> void execute_(std::vector<ExecuteServiceArgument> args, seq<S...>) {
 | 
			
		||||
    this->execute((get_execute_arg_value<Ts>(args[S]))...);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,16 @@
 | 
			
		||||
#include "component_iterator.h"
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#include "user_services.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "esphome/components/api/api_server.h"
 | 
			
		||||
#include "esphome/components/api/user_services.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
void ComponentIterator::begin(bool include_internal) {
 | 
			
		||||
ComponentIterator::ComponentIterator(APIServer *server) : server_(server) {}
 | 
			
		||||
void ComponentIterator::begin() {
 | 
			
		||||
  this->state_ = IteratorState::BEGIN;
 | 
			
		||||
  this->at_ = 0;
 | 
			
		||||
  this->include_internal_ = include_internal;
 | 
			
		||||
}
 | 
			
		||||
void ComponentIterator::advance() {
 | 
			
		||||
  bool advance_platform = false;
 | 
			
		||||
@@ -34,7 +32,7 @@ void ComponentIterator::advance() {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *binary_sensor = App.get_binary_sensors()[this->at_];
 | 
			
		||||
        if (binary_sensor->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (binary_sensor->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -49,7 +47,7 @@ void ComponentIterator::advance() {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *cover = App.get_covers()[this->at_];
 | 
			
		||||
        if (cover->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (cover->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -64,7 +62,7 @@ void ComponentIterator::advance() {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *fan = App.get_fans()[this->at_];
 | 
			
		||||
        if (fan->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (fan->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -79,7 +77,7 @@ void ComponentIterator::advance() {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *light = App.get_lights()[this->at_];
 | 
			
		||||
        if (light->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (light->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -94,7 +92,7 @@ void ComponentIterator::advance() {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *sensor = App.get_sensors()[this->at_];
 | 
			
		||||
        if (sensor->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (sensor->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -109,7 +107,7 @@ void ComponentIterator::advance() {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *a_switch = App.get_switches()[this->at_];
 | 
			
		||||
        if (a_switch->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (a_switch->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -124,7 +122,7 @@ void ComponentIterator::advance() {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *button = App.get_buttons()[this->at_];
 | 
			
		||||
        if (button->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (button->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -139,7 +137,7 @@ void ComponentIterator::advance() {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *text_sensor = App.get_text_sensors()[this->at_];
 | 
			
		||||
        if (text_sensor->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (text_sensor->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -148,22 +146,20 @@ void ComponentIterator::advance() {
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
    case IteratorState ::SERVICE:
 | 
			
		||||
      if (this->at_ >= api::global_api_server->get_user_services().size()) {
 | 
			
		||||
      if (this->at_ >= this->server_->get_user_services().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *service = api::global_api_server->get_user_services()[this->at_];
 | 
			
		||||
        auto *service = this->server_->get_user_services()[this->at_];
 | 
			
		||||
        success = this->on_service(service);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
    case IteratorState::CAMERA:
 | 
			
		||||
      if (esp32_camera::global_esp32_camera == nullptr) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        if (esp32_camera::global_esp32_camera->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (esp32_camera::global_esp32_camera->is_internal()) {
 | 
			
		||||
          advance_platform = success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -178,7 +174,7 @@ void ComponentIterator::advance() {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *climate = App.get_climates()[this->at_];
 | 
			
		||||
        if (climate->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (climate->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -193,7 +189,7 @@ void ComponentIterator::advance() {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *number = App.get_numbers()[this->at_];
 | 
			
		||||
        if (number->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (number->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -208,7 +204,7 @@ void ComponentIterator::advance() {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *select = App.get_selects()[this->at_];
 | 
			
		||||
        if (select->is_internal() && !this->include_internal_) {
 | 
			
		||||
        if (select->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -216,21 +212,6 @@ void ComponentIterator::advance() {
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
    case IteratorState::LOCK:
 | 
			
		||||
      if (this->at_ >= App.get_locks().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *a_lock = App.get_locks()[this->at_];
 | 
			
		||||
        if (a_lock->is_internal() && !this->include_internal_) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
          success = this->on_lock(a_lock);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    case IteratorState::MAX:
 | 
			
		||||
      if (this->on_end()) {
 | 
			
		||||
@@ -248,10 +229,10 @@ void ComponentIterator::advance() {
 | 
			
		||||
}
 | 
			
		||||
bool ComponentIterator::on_end() { return true; }
 | 
			
		||||
bool ComponentIterator::on_begin() { return true; }
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
bool ComponentIterator::on_service(api::UserServiceDescriptor *service) { return true; }
 | 
			
		||||
#endif
 | 
			
		||||
bool ComponentIterator::on_service(UserServiceDescriptor *service) { return true; }
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
bool ComponentIterator::on_camera(esp32_camera::ESP32Camera *camera) { return true; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,24 +1,23 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/controller.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
#include "esphome/components/esp32_camera/esp32_camera.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class APIServer;
 | 
			
		||||
class UserServiceDescriptor;
 | 
			
		||||
}  // namespace api
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class ComponentIterator {
 | 
			
		||||
 public:
 | 
			
		||||
  void begin(bool include_internal = false);
 | 
			
		||||
  ComponentIterator(APIServer *server);
 | 
			
		||||
 | 
			
		||||
  void begin();
 | 
			
		||||
  void advance();
 | 
			
		||||
  virtual bool on_begin();
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
@@ -28,7 +27,7 @@ class ComponentIterator {
 | 
			
		||||
  virtual bool on_cover(cover::Cover *cover) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  virtual bool on_fan(fan::Fan *fan) = 0;
 | 
			
		||||
  virtual bool on_fan(fan::FanState *fan) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  virtual bool on_light(light::LightState *light) = 0;
 | 
			
		||||
@@ -45,9 +44,7 @@ class ComponentIterator {
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
  virtual bool on_service(api::UserServiceDescriptor *service);
 | 
			
		||||
#endif
 | 
			
		||||
  virtual bool on_service(UserServiceDescriptor *service);
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  virtual bool on_camera(esp32_camera::ESP32Camera *camera);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -59,9 +56,6 @@ class ComponentIterator {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  virtual bool on_select(select::Select *select) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  virtual bool on_lock(lock::Lock *a_lock) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
  virtual bool on_end();
 | 
			
		||||
 | 
			
		||||
@@ -93,9 +87,7 @@ class ComponentIterator {
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
    TEXT_SENSOR,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
    SERVICE,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
    CAMERA,
 | 
			
		||||
#endif
 | 
			
		||||
@@ -107,14 +99,13 @@ class ComponentIterator {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
    SELECT,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
    LOCK,
 | 
			
		||||
#endif
 | 
			
		||||
    MAX,
 | 
			
		||||
  } state_{IteratorState::NONE};
 | 
			
		||||
  size_t at_{0};
 | 
			
		||||
  bool include_internal_{false};
 | 
			
		||||
 | 
			
		||||
  APIServer *server_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -58,11 +58,10 @@ void AS3935Component::loop() {
 | 
			
		||||
 | 
			
		||||
void AS3935Component::write_indoor(bool indoor) {
 | 
			
		||||
  ESP_LOGV(TAG, "Setting indoor to %d", indoor);
 | 
			
		||||
  if (indoor) {
 | 
			
		||||
  if (indoor)
 | 
			
		||||
    this->write_register(AFE_GAIN, GAIN_MASK, INDOOR, 1);
 | 
			
		||||
  } else {
 | 
			
		||||
  else
 | 
			
		||||
    this->write_register(AFE_GAIN, GAIN_MASK, OUTDOOR, 1);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// REG0x01, bits[3:0], manufacturer default: 0010 (2).
 | 
			
		||||
// This setting determines the threshold for events that trigger the
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ from . import AS3935, CONF_AS3935_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["as3935"]
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend(
 | 
			
		||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ from esphome.components import sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_DISTANCE,
 | 
			
		||||
    CONF_LIGHTNING_ENERGY,
 | 
			
		||||
    STATE_CLASS_NONE,
 | 
			
		||||
    UNIT_KILOMETER,
 | 
			
		||||
    ICON_SIGNAL_DISTANCE_VARIANT,
 | 
			
		||||
    ICON_FLASH,
 | 
			
		||||
@@ -19,10 +20,12 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
            unit_of_measurement=UNIT_KILOMETER,
 | 
			
		||||
            icon=ICON_SIGNAL_DISTANCE_VARIANT,
 | 
			
		||||
            accuracy_decimals=1,
 | 
			
		||||
            state_class=STATE_CLASS_NONE,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
 | 
			
		||||
            icon=ICON_FLASH,
 | 
			
		||||
            accuracy_decimals=1,
 | 
			
		||||
            state_class=STATE_CLASS_NONE,
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 
 | 
			
		||||
@@ -45,8 +45,6 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device
 | 
			
		||||
      this->battery_voltage_->publish_state(*res->battery_voltage);
 | 
			
		||||
    success = true;
 | 
			
		||||
  }
 | 
			
		||||
  if (this->signal_strength_ != nullptr)
 | 
			
		||||
    this->signal_strength_->publish_state(device.get_rssi());
 | 
			
		||||
 | 
			
		||||
  return success;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,6 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
 | 
			
		||||
  void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
 | 
			
		||||
  void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; }
 | 
			
		||||
  void set_battery_voltage(sensor::Sensor *battery_voltage) { battery_voltage_ = battery_voltage; }
 | 
			
		||||
  void set_signal_strength(sensor::Sensor *signal_strength) { signal_strength_ = signal_strength; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint64_t address_;
 | 
			
		||||
@@ -36,7 +35,6 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
 | 
			
		||||
  sensor::Sensor *humidity_{nullptr};
 | 
			
		||||
  sensor::Sensor *battery_level_{nullptr};
 | 
			
		||||
  sensor::Sensor *battery_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *signal_strength_{nullptr};
 | 
			
		||||
 | 
			
		||||
  optional<ParseResult> parse_header_(const esp32_ble_tracker::ServiceData &service_data);
 | 
			
		||||
  bool parse_message_(const std::vector<uint8_t> &message, ParseResult &result);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,18 +6,15 @@ from esphome.const import (
 | 
			
		||||
    CONF_BATTERY_VOLTAGE,
 | 
			
		||||
    CONF_MAC_ADDRESS,
 | 
			
		||||
    CONF_HUMIDITY,
 | 
			
		||||
    CONF_SIGNAL_STRENGTH,
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    DEVICE_CLASS_BATTERY,
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
    DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    ENTITY_CATEGORY_DIAGNOSTIC,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_DECIBEL_MILLIWATT,
 | 
			
		||||
    UNIT_PERCENT,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
)
 | 
			
		||||
@@ -62,13 +59,6 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
                entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_SIGNAL_STRENGTH): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_DECIBEL_MILLIWATT,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
                entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
 | 
			
		||||
@@ -95,6 +85,3 @@ async def to_code(config):
 | 
			
		||||
    if CONF_BATTERY_VOLTAGE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
 | 
			
		||||
        cg.add(var.set_battery_voltage(sens))
 | 
			
		||||
    if CONF_SIGNAL_STRENGTH in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_SIGNAL_STRENGTH])
 | 
			
		||||
        cg.add(var.set_signal_strength(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
 | 
			
		||||
  const auto &data = service_data.data;
 | 
			
		||||
 | 
			
		||||
  const uint8_t protocol_version = data[0] >> 4;
 | 
			
		||||
  if (protocol_version != 1 && protocol_version != 2) {
 | 
			
		||||
  if (protocol_version != 1) {
 | 
			
		||||
    ESP_LOGE(TAG, "Unsupported protocol version: %u", protocol_version);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -57,15 +57,9 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
 | 
			
		||||
  uint16_t battery_millivolt = data[2] << 8 | data[3];
 | 
			
		||||
  float battery_voltage = battery_millivolt / 1000.0f;
 | 
			
		||||
 | 
			
		||||
  // Temperature in 1000 * Celsius (protocol v1) or 100 * Celsius (protocol v2).
 | 
			
		||||
  float temp_celsius;
 | 
			
		||||
  if (protocol_version == 1) {
 | 
			
		||||
    uint16_t temp_millicelsius = data[4] << 8 | data[5];
 | 
			
		||||
    temp_celsius = temp_millicelsius / 1000.0f;
 | 
			
		||||
  } else {
 | 
			
		||||
    int16_t temp_centicelsius = data[4] << 8 | data[5];
 | 
			
		||||
    temp_celsius = temp_centicelsius / 100.0f;
 | 
			
		||||
  }
 | 
			
		||||
  // Temperature in 1000 * Celsius.
 | 
			
		||||
  uint16_t temp_millicelcius = data[4] << 8 | data[5];
 | 
			
		||||
  float temp_celcius = temp_millicelcius / 1000.0f;
 | 
			
		||||
 | 
			
		||||
  // Relative air humidity in the range [0, 2^16).
 | 
			
		||||
  uint16_t humidity = data[6] << 8 | data[7];
 | 
			
		||||
@@ -82,7 +76,7 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
 | 
			
		||||
    battery_voltage_->publish_state(battery_voltage);
 | 
			
		||||
  }
 | 
			
		||||
  if (temperature_ != nullptr) {
 | 
			
		||||
    temperature_->publish_state(temp_celsius);
 | 
			
		||||
    temperature_->publish_state(temp_celcius);
 | 
			
		||||
  }
 | 
			
		||||
  if (humidity_ != nullptr) {
 | 
			
		||||
    humidity_->publish_state(humidity_percent);
 | 
			
		||||
 
 | 
			
		||||
@@ -97,7 +97,7 @@ void BalluClimate::transmit_state() {
 | 
			
		||||
 | 
			
		||||
  // Send code
 | 
			
		||||
  auto transmit = this->transmitter_->transmit();
 | 
			
		||||
  auto *data = transmit.get_data();
 | 
			
		||||
  auto data = transmit.get_data();
 | 
			
		||||
 | 
			
		||||
  data->set_carrier_frequency(38000);
 | 
			
		||||
 | 
			
		||||
@@ -130,10 +130,10 @@ bool BalluClimate::on_receive(remote_base::RemoteReceiveData data) {
 | 
			
		||||
  for (int i = 0; i < BALLU_STATE_LENGTH; i++) {
 | 
			
		||||
    // Read bit
 | 
			
		||||
    for (int j = 0; j < 8; j++) {
 | 
			
		||||
      if (data.expect_item(BALLU_BIT_MARK, BALLU_ONE_SPACE)) {
 | 
			
		||||
      if (data.expect_item(BALLU_BIT_MARK, BALLU_ONE_SPACE))
 | 
			
		||||
        remote_state[i] |= 1 << j;
 | 
			
		||||
 | 
			
		||||
      } else if (!data.expect_item(BALLU_BIT_MARK, BALLU_ZERO_SPACE)) {
 | 
			
		||||
      else if (!data.expect_item(BALLU_BIT_MARK, BALLU_ZERO_SPACE)) {
 | 
			
		||||
        ESP_LOGV(TAG, "Byte %d bit %d fail", i, j);
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -21,13 +21,12 @@ void BangBangClimate::setup() {
 | 
			
		||||
    restore->to_call(this).perform();
 | 
			
		||||
  } else {
 | 
			
		||||
    // restore from defaults, change_away handles those for us
 | 
			
		||||
    if (supports_cool_ && supports_heat_) {
 | 
			
		||||
    if (supports_cool_ && supports_heat_)
 | 
			
		||||
      this->mode = climate::CLIMATE_MODE_HEAT_COOL;
 | 
			
		||||
    } else if (supports_cool_) {
 | 
			
		||||
    else if (supports_cool_)
 | 
			
		||||
      this->mode = climate::CLIMATE_MODE_COOL;
 | 
			
		||||
    } else if (supports_heat_) {
 | 
			
		||||
    else if (supports_heat_)
 | 
			
		||||
      this->mode = climate::CLIMATE_MODE_HEAT;
 | 
			
		||||
    }
 | 
			
		||||
    this->change_away_(false);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -57,12 +56,11 @@ climate::ClimateTraits BangBangClimate::traits() {
 | 
			
		||||
  if (supports_cool_ && supports_heat_)
 | 
			
		||||
    traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL);
 | 
			
		||||
  traits.set_supports_two_point_target_temperature(true);
 | 
			
		||||
  if (supports_away_) {
 | 
			
		||||
  if (supports_away_)
 | 
			
		||||
    traits.set_supported_presets({
 | 
			
		||||
        climate::CLIMATE_PRESET_HOME,
 | 
			
		||||
        climate::CLIMATE_PRESET_AWAY,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  traits.set_supports_action(true);
 | 
			
		||||
  return traits;
 | 
			
		||||
}
 | 
			
		||||
@@ -82,25 +80,21 @@ void BangBangClimate::compute_state_() {
 | 
			
		||||
 | 
			
		||||
  climate::ClimateAction target_action;
 | 
			
		||||
  if (too_cold) {
 | 
			
		||||
    // too cold -> enable heating if possible and enabled, else idle
 | 
			
		||||
    if (this->supports_heat_ &&
 | 
			
		||||
        (this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_HEAT)) {
 | 
			
		||||
    // too cold -> enable heating if possible, else idle
 | 
			
		||||
    if (this->supports_heat_)
 | 
			
		||||
      target_action = climate::CLIMATE_ACTION_HEATING;
 | 
			
		||||
    } else {
 | 
			
		||||
    else
 | 
			
		||||
      target_action = climate::CLIMATE_ACTION_IDLE;
 | 
			
		||||
    }
 | 
			
		||||
  } else if (too_hot) {
 | 
			
		||||
    // too hot -> enable cooling if possible and enabled, else idle
 | 
			
		||||
    if (this->supports_cool_ &&
 | 
			
		||||
        (this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_COOL)) {
 | 
			
		||||
    // too hot -> enable cooling if possible, else idle
 | 
			
		||||
    if (this->supports_cool_)
 | 
			
		||||
      target_action = climate::CLIMATE_ACTION_COOLING;
 | 
			
		||||
    } else {
 | 
			
		||||
    else
 | 
			
		||||
      target_action = climate::CLIMATE_ACTION_IDLE;
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // neither too hot nor too cold -> in range
 | 
			
		||||
    if (this->supports_cool_ && this->supports_heat_ && this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
 | 
			
		||||
      // if supports both ends and both cooling and heating enabled, go to idle action
 | 
			
		||||
    if (this->supports_cool_ && this->supports_heat_) {
 | 
			
		||||
      // if supports both ends, go to idle action
 | 
			
		||||
      target_action = climate::CLIMATE_ACTION_IDLE;
 | 
			
		||||
    } else {
 | 
			
		||||
      // else use current mode and don't change (hysteresis)
 | 
			
		||||
@@ -111,10 +105,9 @@ void BangBangClimate::compute_state_() {
 | 
			
		||||
  this->switch_to_action_(target_action);
 | 
			
		||||
}
 | 
			
		||||
void BangBangClimate::switch_to_action_(climate::ClimateAction action) {
 | 
			
		||||
  if (action == this->action) {
 | 
			
		||||
  if (action == this->action)
 | 
			
		||||
    // already in target mode
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((action == climate::CLIMATE_ACTION_OFF && this->action == climate::CLIMATE_ACTION_IDLE) ||
 | 
			
		||||
      (action == climate::CLIMATE_ACTION_IDLE && this->action == climate::CLIMATE_ACTION_OFF)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,109 +9,18 @@ static const char *const TAG = "bh1750.sensor";
 | 
			
		||||
static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001;
 | 
			
		||||
static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000;  // last 3 bits
 | 
			
		||||
static const uint8_t BH1750_COMMAND_MT_REG_LO = 0b01100000;  // last 5 bits
 | 
			
		||||
static const uint8_t BH1750_COMMAND_ONE_TIME_L = 0b00100011;
 | 
			
		||||
static const uint8_t BH1750_COMMAND_ONE_TIME_H = 0b00100000;
 | 
			
		||||
static const uint8_t BH1750_COMMAND_ONE_TIME_H2 = 0b00100001;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
bh1750 properties:
 | 
			
		||||
 | 
			
		||||
L-resolution mode:
 | 
			
		||||
- resolution 4lx (@ mtreg=69)
 | 
			
		||||
- measurement time: typ=16ms, max=24ms, scaled by MTreg value divided by 69
 | 
			
		||||
- formula: counts / 1.2 * (69 / MTreg) lx
 | 
			
		||||
H-resolution mode:
 | 
			
		||||
- resolution 1lx (@ mtreg=69)
 | 
			
		||||
- measurement time: typ=120ms, max=180ms, scaled by MTreg value divided by 69
 | 
			
		||||
- formula: counts / 1.2 * (69 / MTreg) lx
 | 
			
		||||
H-resolution mode2:
 | 
			
		||||
- resolution 0.5lx (@ mtreg=69)
 | 
			
		||||
- measurement time: typ=120ms, max=180ms, scaled by MTreg value divided by 69
 | 
			
		||||
- formula: counts / 1.2 * (69 / MTreg) / 2 lx
 | 
			
		||||
 | 
			
		||||
MTreg:
 | 
			
		||||
- min=31, default=69, max=254
 | 
			
		||||
 | 
			
		||||
-> only reason to use l-resolution is faster, but offers no higher range
 | 
			
		||||
-> below ~7000lx, makes sense to use H-resolution2 @ MTreg=254
 | 
			
		||||
-> try to maximize MTreg to get lowest noise level
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
void BH1750Sensor::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up BH1750 '%s'...", this->name_.c_str());
 | 
			
		||||
  uint8_t turn_on = BH1750_COMMAND_POWER_ON;
 | 
			
		||||
  if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
 | 
			
		||||
  if (!this->write_bytes(BH1750_COMMAND_POWER_ON, nullptr, 0)) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f) {
 | 
			
		||||
  // turn on (after one-shot sensor automatically powers down)
 | 
			
		||||
  uint8_t turn_on = BH1750_COMMAND_POWER_ON;
 | 
			
		||||
  if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "Turning on BH1750 failed");
 | 
			
		||||
    f(NAN);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (active_mtreg_ != mtreg) {
 | 
			
		||||
    // set mtreg
 | 
			
		||||
    uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111);
 | 
			
		||||
    uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111);
 | 
			
		||||
    if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
 | 
			
		||||
      ESP_LOGW(TAG, "Setting measurement time for BH1750 failed");
 | 
			
		||||
      active_mtreg_ = 0;
 | 
			
		||||
      f(NAN);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    active_mtreg_ = mtreg;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t cmd;
 | 
			
		||||
  uint16_t meas_time;
 | 
			
		||||
  switch (mode) {
 | 
			
		||||
    case BH1750_MODE_L:
 | 
			
		||||
      cmd = BH1750_COMMAND_ONE_TIME_L;
 | 
			
		||||
      meas_time = 24 * mtreg / 69;
 | 
			
		||||
      break;
 | 
			
		||||
    case BH1750_MODE_H:
 | 
			
		||||
      cmd = BH1750_COMMAND_ONE_TIME_H;
 | 
			
		||||
      meas_time = 180 * mtreg / 69;
 | 
			
		||||
      break;
 | 
			
		||||
    case BH1750_MODE_H2:
 | 
			
		||||
      cmd = BH1750_COMMAND_ONE_TIME_H2;
 | 
			
		||||
      meas_time = 180 * mtreg / 69;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      f(NAN);
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
  if (this->write(&cmd, 1) != i2c::ERROR_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "Starting measurement for BH1750 failed");
 | 
			
		||||
    f(NAN);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // probably not needed, but adjust for rounding
 | 
			
		||||
  meas_time++;
 | 
			
		||||
 | 
			
		||||
  this->set_timeout("read", meas_time, [this, mode, mtreg, f]() {
 | 
			
		||||
    uint16_t raw_value;
 | 
			
		||||
    if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
 | 
			
		||||
      ESP_LOGW(TAG, "Reading BH1750 data failed");
 | 
			
		||||
      f(NAN);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    raw_value = i2c::i2ctohs(raw_value);
 | 
			
		||||
 | 
			
		||||
    float lx = float(raw_value) / 1.2f;
 | 
			
		||||
    lx *= 69.0f / mtreg;
 | 
			
		||||
    if (mode == BH1750_MODE_H2)
 | 
			
		||||
      lx /= 2.0f;
 | 
			
		||||
 | 
			
		||||
    f(lx);
 | 
			
		||||
  });
 | 
			
		||||
  uint8_t mtreg_hi = (this->measurement_duration_ >> 5) & 0b111;
 | 
			
		||||
  uint8_t mtreg_lo = (this->measurement_duration_ >> 0) & 0b11111;
 | 
			
		||||
  this->write_bytes(BH1750_COMMAND_MT_REG_HI | mtreg_hi, nullptr, 0);
 | 
			
		||||
  this->write_bytes(BH1750_COMMAND_MT_REG_LO | mtreg_lo, nullptr, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BH1750Sensor::dump_config() {
 | 
			
		||||
@@ -121,49 +30,64 @@ void BH1750Sensor::dump_config() {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with BH1750 failed!");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const char *resolution_s;
 | 
			
		||||
  switch (this->resolution_) {
 | 
			
		||||
    case BH1750_RESOLUTION_0P5_LX:
 | 
			
		||||
      resolution_s = "0.5";
 | 
			
		||||
      break;
 | 
			
		||||
    case BH1750_RESOLUTION_1P0_LX:
 | 
			
		||||
      resolution_s = "1";
 | 
			
		||||
      break;
 | 
			
		||||
    case BH1750_RESOLUTION_4P0_LX:
 | 
			
		||||
      resolution_s = "4";
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      resolution_s = "Unknown";
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Resolution: %s", resolution_s);
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BH1750Sensor::update() {
 | 
			
		||||
  // first do a quick measurement in L-mode with full range
 | 
			
		||||
  // to find right range
 | 
			
		||||
  this->read_lx_(BH1750_MODE_L, 31, [this](float val) {
 | 
			
		||||
    if (std::isnan(val)) {
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
      this->publish_state(NAN);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  if (!this->write_bytes(this->resolution_, nullptr, 0))
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
    BH1750Mode use_mode;
 | 
			
		||||
    uint8_t use_mtreg;
 | 
			
		||||
    if (val <= 7000) {
 | 
			
		||||
      use_mode = BH1750_MODE_H2;
 | 
			
		||||
      use_mtreg = 254;
 | 
			
		||||
    } else {
 | 
			
		||||
      use_mode = BH1750_MODE_H;
 | 
			
		||||
      // lx = counts / 1.2 * (69 / mtreg)
 | 
			
		||||
      // -> mtreg = counts / 1.2 * (69 / lx)
 | 
			
		||||
      // calculate for counts=50000 (allow some range to not saturate, but maximize mtreg)
 | 
			
		||||
      // -> mtreg = 50000*(10/12)*(69/lx)
 | 
			
		||||
      int ideal_mtreg = 50000 * 10 * 69 / (12 * (int) val);
 | 
			
		||||
      use_mtreg = std::min(254, std::max(31, ideal_mtreg));
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGV(TAG, "L result: %f -> Calculated mode=%d, mtreg=%d", val, (int) use_mode, use_mtreg);
 | 
			
		||||
  uint32_t wait = 0;
 | 
			
		||||
  // use max conversion times
 | 
			
		||||
  switch (this->resolution_) {
 | 
			
		||||
    case BH1750_RESOLUTION_0P5_LX:
 | 
			
		||||
    case BH1750_RESOLUTION_1P0_LX:
 | 
			
		||||
      wait = 180;
 | 
			
		||||
      break;
 | 
			
		||||
    case BH1750_RESOLUTION_4P0_LX:
 | 
			
		||||
      wait = 24;
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    this->read_lx_(use_mode, use_mtreg, [this](float val) {
 | 
			
		||||
      if (std::isnan(val)) {
 | 
			
		||||
        this->status_set_warning();
 | 
			
		||||
        this->publish_state(NAN);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), val);
 | 
			
		||||
      this->status_clear_warning();
 | 
			
		||||
      this->publish_state(val);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  this->set_timeout("illuminance", wait, [this]() { this->read_data_(); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
void BH1750Sensor::read_data_() {
 | 
			
		||||
  uint16_t raw_value;
 | 
			
		||||
  if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  raw_value = i2c::i2ctohs(raw_value);
 | 
			
		||||
 | 
			
		||||
  float lx = float(raw_value) / 1.2f;
 | 
			
		||||
  lx *= 69.0f / this->measurement_duration_;
 | 
			
		||||
  if (this->resolution_ == BH1750_RESOLUTION_0P5_LX) {
 | 
			
		||||
    lx /= 2.0f;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), lx);
 | 
			
		||||
  this->publish_state(lx);
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BH1750Sensor::set_resolution(BH1750Resolution resolution) { this->resolution_ = resolution; }
 | 
			
		||||
 | 
			
		||||
}  // namespace bh1750
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -7,15 +7,29 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bh1750 {
 | 
			
		||||
 | 
			
		||||
enum BH1750Mode {
 | 
			
		||||
  BH1750_MODE_L,
 | 
			
		||||
  BH1750_MODE_H,
 | 
			
		||||
  BH1750_MODE_H2,
 | 
			
		||||
/// Enum listing all resolutions that can be used with the BH1750
 | 
			
		||||
enum BH1750Resolution {
 | 
			
		||||
  BH1750_RESOLUTION_4P0_LX = 0b00100011,  // one-time low resolution mode
 | 
			
		||||
  BH1750_RESOLUTION_1P0_LX = 0b00100000,  // one-time high resolution mode 1
 | 
			
		||||
  BH1750_RESOLUTION_0P5_LX = 0b00100001,  // one-time high resolution mode 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// This class implements support for the i2c-based BH1750 ambient light sensor.
 | 
			
		||||
class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  /** Set the resolution of this sensor.
 | 
			
		||||
   *
 | 
			
		||||
   * Possible values are:
 | 
			
		||||
   *
 | 
			
		||||
   *  - `BH1750_RESOLUTION_4P0_LX`
 | 
			
		||||
   *  - `BH1750_RESOLUTION_1P0_LX`
 | 
			
		||||
   *  - `BH1750_RESOLUTION_0P5_LX` (default)
 | 
			
		||||
   *
 | 
			
		||||
   * @param resolution The new resolution of the sensor.
 | 
			
		||||
   */
 | 
			
		||||
  void set_resolution(BH1750Resolution resolution);
 | 
			
		||||
  void set_measurement_duration(uint8_t measurement_duration) { measurement_duration_ = measurement_duration; }
 | 
			
		||||
 | 
			
		||||
  // ========== INTERNAL METHODS ==========
 | 
			
		||||
  // (In most use cases you won't need these)
 | 
			
		||||
  void setup() override;
 | 
			
		||||
@@ -24,9 +38,10 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f);
 | 
			
		||||
  void read_data_();
 | 
			
		||||
 | 
			
		||||
  uint8_t active_mtreg_{0};
 | 
			
		||||
  BH1750Resolution resolution_{BH1750_RESOLUTION_0P5_LX};
 | 
			
		||||
  uint8_t measurement_duration_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace bh1750
 | 
			
		||||
 
 | 
			
		||||
@@ -2,23 +2,31 @@ import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_RESOLUTION,
 | 
			
		||||
    DEVICE_CLASS_ILLUMINANCE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_LUX,
 | 
			
		||||
    CONF_MEASUREMENT_DURATION,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
CODEOWNERS = ["@OttoWinter"]
 | 
			
		||||
 | 
			
		||||
bh1750_ns = cg.esphome_ns.namespace("bh1750")
 | 
			
		||||
BH1750Resolution = bh1750_ns.enum("BH1750Resolution")
 | 
			
		||||
BH1750_RESOLUTIONS = {
 | 
			
		||||
    4.0: BH1750Resolution.BH1750_RESOLUTION_4P0_LX,
 | 
			
		||||
    1.0: BH1750Resolution.BH1750_RESOLUTION_1P0_LX,
 | 
			
		||||
    0.5: BH1750Resolution.BH1750_RESOLUTION_0P5_LX,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BH1750Sensor = bh1750_ns.class_(
 | 
			
		||||
    "BH1750Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONF_MEASUREMENT_TIME = "measurement_time"
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        BH1750Sensor,
 | 
			
		||||
        unit_of_measurement=UNIT_LUX,
 | 
			
		||||
        accuracy_decimals=1,
 | 
			
		||||
        device_class=DEVICE_CLASS_ILLUMINANCE,
 | 
			
		||||
@@ -26,11 +34,15 @@ CONFIG_SCHEMA = (
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Optional("resolution"): cv.invalid(
 | 
			
		||||
                "The 'resolution' option has been removed. The optimal value is now dynamically calculated."
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BH1750Sensor),
 | 
			
		||||
            cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(
 | 
			
		||||
                BH1750_RESOLUTIONS, float=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional("measurement_duration"): cv.invalid(
 | 
			
		||||
                "The 'measurement_duration' option has been removed. The optimal value is now dynamically calculated."
 | 
			
		||||
            cv.Optional(CONF_MEASUREMENT_DURATION, default=69): cv.int_range(
 | 
			
		||||
                min=31, max=254
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_MEASUREMENT_TIME): cv.invalid(
 | 
			
		||||
                "The 'measurement_time' option has been replaced with 'measurement_duration' in 1.18.0"
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
@@ -40,6 +52,10 @@ CONFIG_SCHEMA = (
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = await sensor.new_sensor(config)
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await sensor.register_sensor(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_resolution(config[CONF_RESOLUTION]))
 | 
			
		||||
    cg.add(var.set_measurement_duration(config[CONF_MEASUREMENT_DURATION]))
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ from esphome.const import (
 | 
			
		||||
)
 | 
			
		||||
from .. import binary_ns
 | 
			
		||||
 | 
			
		||||
BinaryFan = binary_ns.class_("BinaryFan", fan.Fan, cg.Component)
 | 
			
		||||
BinaryFan = binary_ns.class_("BinaryFan", cg.Component)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
@@ -24,8 +24,9 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await fan.register_fan(var, config)
 | 
			
		||||
 | 
			
		||||
    fan_ = await fan.create_fan_state(config)
 | 
			
		||||
    cg.add(var.set_fan(fan_))
 | 
			
		||||
    output_ = await cg.get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    cg.add(var.set_output(output_))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,35 +6,59 @@ namespace binary {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "binary.fan";
 | 
			
		||||
 | 
			
		||||
void BinaryFan::setup() {
 | 
			
		||||
  auto restore = this->restore_state_();
 | 
			
		||||
  if (restore.has_value()) {
 | 
			
		||||
    restore->apply(*this);
 | 
			
		||||
    this->write_state_();
 | 
			
		||||
void binary::BinaryFan::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str());
 | 
			
		||||
  if (this->fan_->get_traits().supports_oscillation()) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Oscillation: YES");
 | 
			
		||||
  }
 | 
			
		||||
  if (this->fan_->get_traits().supports_direction()) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Direction: YES");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void BinaryFan::dump_config() { LOG_FAN("", "Binary Fan", this); }
 | 
			
		||||
fan::FanTraits BinaryFan::get_traits() {
 | 
			
		||||
  return fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr, 0);
 | 
			
		||||
void BinaryFan::setup() {
 | 
			
		||||
  auto traits = fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr, 0);
 | 
			
		||||
  this->fan_->set_traits(traits);
 | 
			
		||||
  this->fan_->add_on_state_callback([this]() { this->next_update_ = true; });
 | 
			
		||||
}
 | 
			
		||||
void BinaryFan::control(const fan::FanCall &call) {
 | 
			
		||||
  if (call.get_state().has_value())
 | 
			
		||||
    this->state = *call.get_state();
 | 
			
		||||
  if (call.get_oscillating().has_value())
 | 
			
		||||
    this->oscillating = *call.get_oscillating();
 | 
			
		||||
  if (call.get_direction().has_value())
 | 
			
		||||
    this->direction = *call.get_direction();
 | 
			
		||||
void BinaryFan::loop() {
 | 
			
		||||
  if (!this->next_update_) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->next_update_ = false;
 | 
			
		||||
 | 
			
		||||
  this->write_state_();
 | 
			
		||||
  this->publish_state();
 | 
			
		||||
}
 | 
			
		||||
void BinaryFan::write_state_() {
 | 
			
		||||
  this->output_->set_state(this->state);
 | 
			
		||||
  if (this->oscillating_ != nullptr)
 | 
			
		||||
    this->oscillating_->set_state(this->oscillating);
 | 
			
		||||
  if (this->direction_ != nullptr)
 | 
			
		||||
    this->direction_->set_state(this->direction == fan::FanDirection::REVERSE);
 | 
			
		||||
  {
 | 
			
		||||
    bool enable = this->fan_->state;
 | 
			
		||||
    if (enable)
 | 
			
		||||
      this->output_->turn_on();
 | 
			
		||||
    else
 | 
			
		||||
      this->output_->turn_off();
 | 
			
		||||
    ESP_LOGD(TAG, "Setting binary state: %s", ONOFF(enable));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->oscillating_ != nullptr) {
 | 
			
		||||
    bool enable = this->fan_->oscillating;
 | 
			
		||||
    if (enable) {
 | 
			
		||||
      this->oscillating_->turn_on();
 | 
			
		||||
    } else {
 | 
			
		||||
      this->oscillating_->turn_off();
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGD(TAG, "Setting oscillation: %s", ONOFF(enable));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->direction_ != nullptr) {
 | 
			
		||||
    bool enable = this->fan_->direction == fan::FAN_DIRECTION_REVERSE;
 | 
			
		||||
    if (enable) {
 | 
			
		||||
      this->direction_->turn_on();
 | 
			
		||||
    } else {
 | 
			
		||||
      this->direction_->turn_off();
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGD(TAG, "Setting reverse direction: %s", ONOFF(enable));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// We need a higher priority than the FanState component to make sure that the traits are set
 | 
			
		||||
// when that component sets itself up.
 | 
			
		||||
float BinaryFan::get_setup_priority() const { return fan_->get_setup_priority() + 1.0f; }
 | 
			
		||||
 | 
			
		||||
}  // namespace binary
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -2,29 +2,28 @@
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/output/binary_output.h"
 | 
			
		||||
#include "esphome/components/fan/fan.h"
 | 
			
		||||
#include "esphome/components/fan/fan_state.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace binary {
 | 
			
		||||
 | 
			
		||||
class BinaryFan : public Component, public fan::Fan {
 | 
			
		||||
class BinaryFan : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_fan(fan::FanState *fan) { fan_ = fan; }
 | 
			
		||||
  void set_output(output::BinaryOutput *output) { output_ = output; }
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  void set_output(output::BinaryOutput *output) { this->output_ = output; }
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; }
 | 
			
		||||
  void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; }
 | 
			
		||||
 | 
			
		||||
  fan::FanTraits get_traits() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void control(const fan::FanCall &call) override;
 | 
			
		||||
  void write_state_();
 | 
			
		||||
 | 
			
		||||
  fan::FanState *fan_;
 | 
			
		||||
  output::BinaryOutput *output_;
 | 
			
		||||
  output::BinaryOutput *oscillating_{nullptr};
 | 
			
		||||
  output::BinaryOutput *direction_{nullptr};
 | 
			
		||||
  bool next_update_{true};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace binary
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.cpp_generator import MockObjClass
 | 
			
		||||
from esphome.cpp_helpers import setup_entity
 | 
			
		||||
from esphome import automation, core
 | 
			
		||||
from esphome.automation import Condition, maybe_simple_id
 | 
			
		||||
@@ -8,9 +7,7 @@ from esphome.components import mqtt
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_DELAY,
 | 
			
		||||
    CONF_DEVICE_CLASS,
 | 
			
		||||
    CONF_ENTITY_CATEGORY,
 | 
			
		||||
    CONF_FILTERS,
 | 
			
		||||
    CONF_ICON,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_INVALID_COOLDOWN,
 | 
			
		||||
    CONF_INVERTED,
 | 
			
		||||
@@ -25,6 +22,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_STATE,
 | 
			
		||||
    CONF_TIMING,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_NAME,
 | 
			
		||||
    CONF_MQTT_ID,
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
    DEVICE_CLASS_BATTERY,
 | 
			
		||||
@@ -317,7 +315,7 @@ def validate_multi_click_timing(value):
 | 
			
		||||
    return timings
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
 | 
			
		||||
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
 | 
			
		||||
@@ -326,7 +324,7 @@ BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).ex
 | 
			
		||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
 | 
			
		||||
            mqtt.MQTTBinarySensorComponent
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
			
		||||
        cv.Optional(CONF_DEVICE_CLASS): device_class,
 | 
			
		||||
        cv.Optional(CONF_FILTERS): validate_filters,
 | 
			
		||||
        cv.Optional(CONF_ON_PRESS): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
@@ -379,39 +377,6 @@ BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).ex
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
_UNDEF = object()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def binary_sensor_schema(
 | 
			
		||||
    class_: MockObjClass = _UNDEF,
 | 
			
		||||
    *,
 | 
			
		||||
    icon: str = _UNDEF,
 | 
			
		||||
    entity_category: str = _UNDEF,
 | 
			
		||||
    device_class: str = _UNDEF,
 | 
			
		||||
) -> cv.Schema:
 | 
			
		||||
    schema = BINARY_SENSOR_SCHEMA
 | 
			
		||||
    if class_ is not _UNDEF:
 | 
			
		||||
        schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)})
 | 
			
		||||
    if icon is not _UNDEF:
 | 
			
		||||
        schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
 | 
			
		||||
    if entity_category is not _UNDEF:
 | 
			
		||||
        schema = schema.extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Optional(
 | 
			
		||||
                    CONF_ENTITY_CATEGORY, default=entity_category
 | 
			
		||||
                ): cv.entity_category
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    if device_class is not _UNDEF:
 | 
			
		||||
        schema = schema.extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Optional(
 | 
			
		||||
                    CONF_DEVICE_CLASS, default=device_class
 | 
			
		||||
                ): validate_device_class
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    return schema
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def setup_binary_sensor_core_(var, config):
 | 
			
		||||
    await setup_entity(var, config)
 | 
			
		||||
@@ -478,7 +443,7 @@ async def register_binary_sensor(var, config):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def new_binary_sensor(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME])
 | 
			
		||||
    await register_binary_sensor(var, config)
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -42,15 +42,13 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
std::string BinarySensor::device_class() { return ""; }
 | 
			
		||||
BinarySensor::BinarySensor() : state(false) {}
 | 
			
		||||
BinarySensor::BinarySensor(const std::string &name) : EntityBase(name), state(false) {}
 | 
			
		||||
BinarySensor::BinarySensor() : BinarySensor("") {}
 | 
			
		||||
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
 | 
			
		||||
std::string BinarySensor::get_device_class() {
 | 
			
		||||
  if (this->device_class_.has_value())
 | 
			
		||||
    return *this->device_class_;
 | 
			
		||||
#pragma GCC diagnostic push
 | 
			
		||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 | 
			
		||||
  return this->device_class();
 | 
			
		||||
#pragma GCC diagnostic pop
 | 
			
		||||
}
 | 
			
		||||
void BinarySensor::add_filter(Filter *filter) {
 | 
			
		||||
  filter->parent_ = this;
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,11 @@ namespace binary_sensor {
 | 
			
		||||
class BinarySensor : public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit BinarySensor();
 | 
			
		||||
  /** Construct a binary sensor with the specified name
 | 
			
		||||
   *
 | 
			
		||||
   * @param name Name of this binary sensor.
 | 
			
		||||
   */
 | 
			
		||||
  explicit BinarySensor(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /** Add a callback to be notified of state changes.
 | 
			
		||||
   *
 | 
			
		||||
@@ -69,10 +74,7 @@ class BinarySensor : public EntityBase {
 | 
			
		||||
 | 
			
		||||
  // ========== OVERRIDE METHODS ==========
 | 
			
		||||
  // (You'll only need this when creating your own custom binary sensor)
 | 
			
		||||
  /** Override this to set the default device class.
 | 
			
		||||
   *
 | 
			
		||||
   * @deprecated This method is deprecated, set the property during config validation instead. (2022.1)
 | 
			
		||||
   */
 | 
			
		||||
  /// Get the default device class for this sensor, or empty string for no default.
 | 
			
		||||
  virtual std::string device_class();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,14 @@ import esphome.config_validation as cv
 | 
			
		||||
 | 
			
		||||
from esphome.components import sensor, binary_sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_CHANNELS,
 | 
			
		||||
    CONF_VALUE,
 | 
			
		||||
    CONF_TYPE,
 | 
			
		||||
    ICON_CHECK_CIRCLE_OUTLINE,
 | 
			
		||||
    CONF_BINARY_SENSOR,
 | 
			
		||||
    CONF_GROUP,
 | 
			
		||||
    STATE_CLASS_NONE,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["binary_sensor"]
 | 
			
		||||
@@ -31,11 +33,12 @@ entry = {
 | 
			
		||||
CONFIG_SCHEMA = cv.typed_schema(
 | 
			
		||||
    {
 | 
			
		||||
        CONF_GROUP: sensor.sensor_schema(
 | 
			
		||||
            BinarySensorMap,
 | 
			
		||||
            icon=ICON_CHECK_CIRCLE_OUTLINE,
 | 
			
		||||
            accuracy_decimals=0,
 | 
			
		||||
            state_class=STATE_CLASS_NONE,
 | 
			
		||||
        ).extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(): cv.declare_id(BinarySensorMap),
 | 
			
		||||
                cv.Required(CONF_CHANNELS): cv.All(
 | 
			
		||||
                    cv.ensure_list(entry), cv.Length(min=1)
 | 
			
		||||
                ),
 | 
			
		||||
@@ -47,8 +50,9 @@ CONFIG_SCHEMA = cv.typed_schema(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = await sensor.new_sensor(config)
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await sensor.register_sensor(var, config)
 | 
			
		||||
 | 
			
		||||
    constant = SENSOR_MAP_TYPES[config[CONF_TYPE]]
 | 
			
		||||
    cg.add(var.set_sensor_type(constant))
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
CODEOWNERS = ["@tobias-"]
 | 
			
		||||
@@ -1,137 +0,0 @@
 | 
			
		||||
#include "bl0940.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bl0940 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "bl0940";
 | 
			
		||||
 | 
			
		||||
static const uint8_t BL0940_READ_COMMAND = 0x50;  // 0x58 according to documentation
 | 
			
		||||
static const uint8_t BL0940_FULL_PACKET = 0xAA;
 | 
			
		||||
static const uint8_t BL0940_PACKET_HEADER = 0x55;  // 0x58 according to documentation
 | 
			
		||||
 | 
			
		||||
static const uint8_t BL0940_WRITE_COMMAND = 0xA0;  // 0xA8 according to documentation
 | 
			
		||||
static const uint8_t BL0940_REG_I_FAST_RMS_CTRL = 0x10;
 | 
			
		||||
static const uint8_t BL0940_REG_MODE = 0x18;
 | 
			
		||||
static const uint8_t BL0940_REG_SOFT_RESET = 0x19;
 | 
			
		||||
static const uint8_t BL0940_REG_USR_WRPROT = 0x1A;
 | 
			
		||||
static const uint8_t BL0940_REG_TPS_CTRL = 0x1B;
 | 
			
		||||
 | 
			
		||||
const uint8_t BL0940_INIT[5][6] = {
 | 
			
		||||
    // Reset to default
 | 
			
		||||
    {BL0940_WRITE_COMMAND, BL0940_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38},
 | 
			
		||||
    // Enable User Operation Write
 | 
			
		||||
    {BL0940_WRITE_COMMAND, BL0940_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0},
 | 
			
		||||
    // 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS
 | 
			
		||||
    {BL0940_WRITE_COMMAND, BL0940_REG_MODE, 0x00, 0x10, 0x00, 0x37},
 | 
			
		||||
    // 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS
 | 
			
		||||
    {BL0940_WRITE_COMMAND, BL0940_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE},
 | 
			
		||||
    // 0x181C = Half cycle, Fast RMS threshold 6172
 | 
			
		||||
    {BL0940_WRITE_COMMAND, BL0940_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}};
 | 
			
		||||
 | 
			
		||||
void BL0940::loop() {
 | 
			
		||||
  DataPacket buffer;
 | 
			
		||||
  if (!this->available()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (read_array((uint8_t *) &buffer, sizeof(buffer))) {
 | 
			
		||||
    if (validate_checksum(&buffer)) {
 | 
			
		||||
      received_package_(&buffer);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGW(TAG, "Junk on wire. Throwing away partial message");
 | 
			
		||||
    while (read() >= 0)
 | 
			
		||||
      ;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BL0940::validate_checksum(const DataPacket *data) {
 | 
			
		||||
  uint8_t checksum = BL0940_READ_COMMAND;
 | 
			
		||||
  // Whole package but checksum
 | 
			
		||||
  for (uint32_t i = 0; i < sizeof(data->raw) - 1; i++) {
 | 
			
		||||
    checksum += data->raw[i];
 | 
			
		||||
  }
 | 
			
		||||
  checksum ^= 0xFF;
 | 
			
		||||
  if (checksum != data->checksum) {
 | 
			
		||||
    ESP_LOGW(TAG, "BL0940 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
 | 
			
		||||
  }
 | 
			
		||||
  return checksum == data->checksum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0940::update() {
 | 
			
		||||
  this->flush();
 | 
			
		||||
  this->write_byte(BL0940_READ_COMMAND);
 | 
			
		||||
  this->write_byte(BL0940_FULL_PACKET);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0940::setup() {
 | 
			
		||||
  for (auto *i : BL0940_INIT) {
 | 
			
		||||
    this->write_array(i, 6);
 | 
			
		||||
    delay(1);
 | 
			
		||||
  }
 | 
			
		||||
  this->flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float BL0940::update_temp_(sensor::Sensor *sensor, ube16_t temperature) const {
 | 
			
		||||
  auto tb = (float) (temperature.h << 8 | temperature.l);
 | 
			
		||||
  float converted_temp = ((float) 170 / 448) * (tb / 2 - 32) - 45;
 | 
			
		||||
  if (sensor != nullptr) {
 | 
			
		||||
    if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) {
 | 
			
		||||
      ESP_LOGD("bl0940", "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f",
 | 
			
		||||
               sensor->get_name().c_str(), sensor->get_state(), converted_temp);
 | 
			
		||||
      return 0.0f;
 | 
			
		||||
    }
 | 
			
		||||
    sensor->publish_state(converted_temp);
 | 
			
		||||
  }
 | 
			
		||||
  return converted_temp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0940::received_package_(const DataPacket *data) const {
 | 
			
		||||
  // Bad header
 | 
			
		||||
  if (data->frame_header != BL0940_PACKET_HEADER) {
 | 
			
		||||
    ESP_LOGI("bl0940", "Invalid data. Header mismatch: %d", data->frame_header);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  float v_rms = (float) to_uint32_t(data->v_rms) / voltage_reference_;
 | 
			
		||||
  float i_rms = (float) to_uint32_t(data->i_rms) / current_reference_;
 | 
			
		||||
  float watt = (float) to_int32_t(data->watt) / power_reference_;
 | 
			
		||||
  uint32_t cf_cnt = to_uint32_t(data->cf_cnt);
 | 
			
		||||
  float total_energy_consumption = (float) cf_cnt / energy_reference_;
 | 
			
		||||
 | 
			
		||||
  float tps1 = update_temp_(internal_temperature_sensor_, data->tps1);
 | 
			
		||||
  float tps2 = update_temp_(external_temperature_sensor_, data->tps2);
 | 
			
		||||
 | 
			
		||||
  if (voltage_sensor_ != nullptr) {
 | 
			
		||||
    voltage_sensor_->publish_state(v_rms);
 | 
			
		||||
  }
 | 
			
		||||
  if (current_sensor_ != nullptr) {
 | 
			
		||||
    current_sensor_->publish_state(i_rms);
 | 
			
		||||
  }
 | 
			
		||||
  if (power_sensor_ != nullptr) {
 | 
			
		||||
    power_sensor_->publish_state(watt);
 | 
			
		||||
  }
 | 
			
		||||
  if (energy_sensor_ != nullptr) {
 | 
			
		||||
    energy_sensor_->publish_state(total_energy_consumption);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV("bl0940", "BL0940: U %fV, I %fA, P %fW, Cnt %d, ∫P %fkWh, T1 %f°C, T2 %f°C", v_rms, i_rms, watt, cf_cnt,
 | 
			
		||||
           total_energy_consumption, tps1, tps2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0940::dump_config() {  // NOLINT(readability-function-cognitive-complexity)
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BL0940:");
 | 
			
		||||
  LOG_SENSOR("", "Voltage", this->voltage_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Current", this->current_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Power", this->power_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Energy", this->energy_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Internal temperature", this->internal_temperature_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "External temperature", this->external_temperature_sensor_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t BL0940::to_uint32_t(ube24_t input) { return input.h << 16 | input.m << 8 | input.l; }
 | 
			
		||||
 | 
			
		||||
int32_t BL0940::to_int32_t(sbe24_t input) { return input.h << 16 | input.m << 8 | input.l; }
 | 
			
		||||
 | 
			
		||||
}  // namespace bl0940
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,109 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/uart/uart.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bl0940 {
 | 
			
		||||
 | 
			
		||||
static const float BL0940_PREF = 1430;
 | 
			
		||||
static const float BL0940_UREF = 33000;
 | 
			
		||||
static const float BL0940_IREF = 275000;  // 2750 from tasmota. Seems to generate values 100 times too high
 | 
			
		||||
 | 
			
		||||
// Measured to 297J  per click according to power consumption of 5 minutes
 | 
			
		||||
// Converted to kWh (3.6MJ per kwH). Used to be 256 * 1638.4
 | 
			
		||||
static const float BL0940_EREF = 3.6e6 / 297;
 | 
			
		||||
 | 
			
		||||
struct ube24_t {  // NOLINT(readability-identifier-naming,altera-struct-pack-align)
 | 
			
		||||
  uint8_t l;
 | 
			
		||||
  uint8_t m;
 | 
			
		||||
  uint8_t h;
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
struct ube16_t {  // NOLINT(readability-identifier-naming,altera-struct-pack-align)
 | 
			
		||||
  uint8_t l;
 | 
			
		||||
  uint8_t h;
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
struct sbe24_t {  // NOLINT(readability-identifier-naming,altera-struct-pack-align)
 | 
			
		||||
  uint8_t l;
 | 
			
		||||
  uint8_t m;
 | 
			
		||||
  int8_t h;
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
// Caveat: All these values are big endian (low - middle - high)
 | 
			
		||||
 | 
			
		||||
union DataPacket {  // NOLINT(altera-struct-pack-align)
 | 
			
		||||
  uint8_t raw[35];
 | 
			
		||||
  struct {
 | 
			
		||||
    uint8_t frame_header;  // value of 0x58 according to docs. 0x55 according to Tasmota real world tests. Reality wins.
 | 
			
		||||
    ube24_t i_fast_rms;    // 0x00
 | 
			
		||||
    ube24_t i_rms;         // 0x04
 | 
			
		||||
    ube24_t RESERVED0;     // reserved
 | 
			
		||||
    ube24_t v_rms;         // 0x06
 | 
			
		||||
    ube24_t RESERVED1;     // reserved
 | 
			
		||||
    sbe24_t watt;          // 0x08
 | 
			
		||||
    ube24_t RESERVED2;     // reserved
 | 
			
		||||
    ube24_t cf_cnt;        // 0x0A
 | 
			
		||||
    ube24_t RESERVED3;     // reserved
 | 
			
		||||
    ube16_t tps1;          // 0x0c
 | 
			
		||||
    uint8_t RESERVED4;     // value of 0x00
 | 
			
		||||
    ube16_t tps2;          // 0x0c
 | 
			
		||||
    uint8_t RESERVED5;     // value of 0x00
 | 
			
		||||
    uint8_t checksum;      // checksum
 | 
			
		||||
  };
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
class BL0940 : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  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_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
 | 
			
		||||
  void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
 | 
			
		||||
  void set_internal_temperature_sensor(sensor::Sensor *internal_temperature_sensor) {
 | 
			
		||||
    internal_temperature_sensor_ = internal_temperature_sensor;
 | 
			
		||||
  }
 | 
			
		||||
  void set_external_temperature_sensor(sensor::Sensor *external_temperature_sensor) {
 | 
			
		||||
    external_temperature_sensor_ = external_temperature_sensor;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  sensor::Sensor *voltage_sensor_;
 | 
			
		||||
  sensor::Sensor *current_sensor_;
 | 
			
		||||
  // NB This may be negative as the circuits is seemingly able to measure
 | 
			
		||||
  // power in both directions
 | 
			
		||||
  sensor::Sensor *power_sensor_;
 | 
			
		||||
  sensor::Sensor *energy_sensor_;
 | 
			
		||||
  sensor::Sensor *internal_temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *external_temperature_sensor_;
 | 
			
		||||
 | 
			
		||||
  // Max difference between two measurements of the temperature. Used to avoid noise.
 | 
			
		||||
  float max_temperature_diff_{0};
 | 
			
		||||
  // Divide by this to turn into Watt
 | 
			
		||||
  float power_reference_ = BL0940_PREF;
 | 
			
		||||
  // Divide by this to turn into Volt
 | 
			
		||||
  float voltage_reference_ = BL0940_UREF;
 | 
			
		||||
  // Divide by this to turn into Ampere
 | 
			
		||||
  float current_reference_ = BL0940_IREF;
 | 
			
		||||
  // Divide by this to turn into kWh
 | 
			
		||||
  float energy_reference_ = BL0940_EREF;
 | 
			
		||||
 | 
			
		||||
  float update_temp_(sensor::Sensor *sensor, ube16_t packed_temperature) const;
 | 
			
		||||
 | 
			
		||||
  static uint32_t to_uint32_t(ube24_t input);
 | 
			
		||||
 | 
			
		||||
  static int32_t to_int32_t(sbe24_t input);
 | 
			
		||||
 | 
			
		||||
  static bool validate_checksum(const DataPacket *data);
 | 
			
		||||
 | 
			
		||||
  void received_package_(const DataPacket *data) const;
 | 
			
		||||
};
 | 
			
		||||
}  // namespace bl0940
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,105 +0,0 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, uart
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CURRENT,
 | 
			
		||||
    CONF_ENERGY,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_POWER,
 | 
			
		||||
    CONF_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_CURRENT,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_POWER,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_AMPERE,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_KILOWATT_HOURS,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    UNIT_WATT,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["uart"]
 | 
			
		||||
 | 
			
		||||
CONF_INTERNAL_TEMPERATURE = "internal_temperature"
 | 
			
		||||
CONF_EXTERNAL_TEMPERATURE = "external_temperature"
 | 
			
		||||
 | 
			
		||||
bl0940_ns = cg.esphome_ns.namespace("bl0940")
 | 
			
		||||
BL0940 = bl0940_ns.class_("BL0940", cg.PollingComponent, uart.UARTDevice)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BL0940),
 | 
			
		||||
            cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_VOLT,
 | 
			
		||||
                accuracy_decimals=1,
 | 
			
		||||
                device_class=DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_CURRENT): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_AMPERE,
 | 
			
		||||
                accuracy_decimals=2,
 | 
			
		||||
                device_class=DEVICE_CLASS_CURRENT,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_POWER): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_WATT,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_POWER,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ENERGY): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_KILOWATT_HOURS,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_ENERGY,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_INTERNAL_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_CELSIUS,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_EXTERNAL_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_CELSIUS,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(uart.UART_DEVICE_SCHEMA)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    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)
 | 
			
		||||
        cg.add(var.set_voltage_sensor(sens))
 | 
			
		||||
    if CONF_CURRENT in config:
 | 
			
		||||
        conf = config[CONF_CURRENT]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_current_sensor(sens))
 | 
			
		||||
    if CONF_POWER in config:
 | 
			
		||||
        conf = config[CONF_POWER]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_power_sensor(sens))
 | 
			
		||||
    if CONF_ENERGY in config:
 | 
			
		||||
        conf = config[CONF_ENERGY]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_energy_sensor(sens))
 | 
			
		||||
    if CONF_INTERNAL_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_INTERNAL_TEMPERATURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        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)
 | 
			
		||||
        cg.add(var.set_external_temperature_sensor(sens))
 | 
			
		||||
@@ -118,21 +118,16 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
 | 
			
		||||
        this->set_states_(espbt::ClientState::IDLE);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_CONNECT_EVT: {
 | 
			
		||||
      ESP_LOGV(TAG, "[%s] ESP_GATTC_CONNECT_EVT", this->address_str().c_str());
 | 
			
		||||
      this->conn_id = param->connect.conn_id;
 | 
			
		||||
      auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if, param->connect.conn_id);
 | 
			
		||||
      this->conn_id = param->open.conn_id;
 | 
			
		||||
      auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if, param->open.conn_id);
 | 
			
		||||
      if (ret) {
 | 
			
		||||
        ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%x", ret);
 | 
			
		||||
        ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%d", ret);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_CFG_MTU_EVT: {
 | 
			
		||||
      if (param->cfg_mtu.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "cfg_mtu to %s failed, mtu %d, status %d", this->address_str().c_str(), param->cfg_mtu.mtu,
 | 
			
		||||
                 param->cfg_mtu.status);
 | 
			
		||||
        ESP_LOGW(TAG, "cfg_mtu to %s failed, status %d", this->address_str().c_str(), param->cfg_mtu.status);
 | 
			
		||||
        this->set_states_(espbt::ClientState::IDLE);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
@@ -144,7 +139,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
 | 
			
		||||
      if (memcmp(param->disconnect.remote_bda, this->remote_bda, 6) != 0) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->address_str().c_str(), param->disconnect.reason);
 | 
			
		||||
      ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT", this->address_str().c_str());
 | 
			
		||||
      for (auto &svc : this->services_)
 | 
			
		||||
        delete svc;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
      this->services_.clear();
 | 
			
		||||
@@ -172,7 +167,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
 | 
			
		||||
      auto *descr = this->get_config_descriptor(param->reg_for_notify.handle);
 | 
			
		||||
      auto descr = this->get_config_descriptor(param->reg_for_notify.handle);
 | 
			
		||||
      if (descr == nullptr) {
 | 
			
		||||
        ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", param->reg_for_notify.handle);
 | 
			
		||||
        break;
 | 
			
		||||
@@ -206,32 +201,6 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEClient::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
 | 
			
		||||
  switch (event) {
 | 
			
		||||
    // This event is sent by the server when it requests security
 | 
			
		||||
    case ESP_GAP_BLE_SEC_REQ_EVT:
 | 
			
		||||
      ESP_LOGV(TAG, "ESP_GAP_BLE_SEC_REQ_EVT %x", event);
 | 
			
		||||
      esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
 | 
			
		||||
      break;
 | 
			
		||||
    // This event is sent once authentication has completed
 | 
			
		||||
    case ESP_GAP_BLE_AUTH_CMPL_EVT:
 | 
			
		||||
      esp_bd_addr_t bd_addr;
 | 
			
		||||
      memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
 | 
			
		||||
      ESP_LOGI(TAG, "auth complete. remote BD_ADDR: %s", format_hex(bd_addr, 6).c_str());
 | 
			
		||||
      if (!param->ble_security.auth_cmpl.success) {
 | 
			
		||||
        ESP_LOGE(TAG, "auth fail reason = 0x%x", param->ble_security.auth_cmpl.fail_reason);
 | 
			
		||||
      } else {
 | 
			
		||||
        ESP_LOGV(TAG, "auth success. address type = %d auth mode = %d", param->ble_security.auth_cmpl.addr_type,
 | 
			
		||||
                 param->ble_security.auth_cmpl.auth_mode);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    // There are other events we'll want to implement at some point to support things like pass key
 | 
			
		||||
    // https://github.com/espressif/esp-idf/blob/cba69dd088344ed9d26739f04736ae7a37541b3a/examples/bluetooth/bluedroid/ble/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse GATT values into a float for a sensor.
 | 
			
		||||
// Ref: https://www.bluetooth.com/specifications/assigned-numbers/format-types/
 | 
			
		||||
float BLEClient::parse_char_value(uint8_t *value, uint16_t length) {
 | 
			
		||||
@@ -283,17 +252,16 @@ float BLEClient::parse_char_value(uint8_t *value, uint16_t length) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEService *BLEClient::get_service(espbt::ESPBTUUID uuid) {
 | 
			
		||||
  for (auto *svc : this->services_) {
 | 
			
		||||
  for (auto svc : this->services_)
 | 
			
		||||
    if (svc->uuid == uuid)
 | 
			
		||||
      return svc;
 | 
			
		||||
  }
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEService *BLEClient::get_service(uint16_t uuid) { return this->get_service(espbt::ESPBTUUID::from_uint16(uuid)); }
 | 
			
		||||
 | 
			
		||||
BLECharacteristic *BLEClient::get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr) {
 | 
			
		||||
  auto *svc = this->get_service(service);
 | 
			
		||||
  auto svc = this->get_service(service);
 | 
			
		||||
  if (svc == nullptr)
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  return svc->get_characteristic(chr);
 | 
			
		||||
@@ -304,24 +272,19 @@ BLECharacteristic *BLEClient::get_characteristic(uint16_t service, uint16_t chr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEDescriptor *BLEClient::get_config_descriptor(uint16_t handle) {
 | 
			
		||||
  for (auto &svc : this->services_) {
 | 
			
		||||
    for (auto &chr : svc->characteristics) {
 | 
			
		||||
      if (chr->handle == handle) {
 | 
			
		||||
        for (auto &desc : chr->descriptors) {
 | 
			
		||||
  for (auto &svc : this->services_)
 | 
			
		||||
    for (auto &chr : svc->characteristics)
 | 
			
		||||
      if (chr->handle == handle)
 | 
			
		||||
        for (auto &desc : chr->descriptors)
 | 
			
		||||
          if (desc->uuid == espbt::ESPBTUUID::from_uint16(0x2902))
 | 
			
		||||
            return desc;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLECharacteristic *BLEService::get_characteristic(espbt::ESPBTUUID uuid) {
 | 
			
		||||
  for (auto &chr : this->characteristics) {
 | 
			
		||||
  for (auto &chr : this->characteristics)
 | 
			
		||||
    if (chr->uuid == uuid)
 | 
			
		||||
      return chr;
 | 
			
		||||
  }
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -330,10 +293,10 @@ BLECharacteristic *BLEService::get_characteristic(uint16_t uuid) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEDescriptor *BLEClient::get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr) {
 | 
			
		||||
  auto *svc = this->get_service(service);
 | 
			
		||||
  auto svc = this->get_service(service);
 | 
			
		||||
  if (svc == nullptr)
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  auto *ch = svc->get_characteristic(chr);
 | 
			
		||||
  auto ch = svc->get_characteristic(chr);
 | 
			
		||||
  if (ch == nullptr)
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  return ch->get_descriptor(descr);
 | 
			
		||||
@@ -416,29 +379,24 @@ void BLECharacteristic::parse_descriptors() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) {
 | 
			
		||||
  for (auto &desc : this->descriptors) {
 | 
			
		||||
  for (auto &desc : this->descriptors)
 | 
			
		||||
    if (desc->uuid == uuid)
 | 
			
		||||
      return desc;
 | 
			
		||||
  }
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) {
 | 
			
		||||
  return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type) {
 | 
			
		||||
  auto *client = this->service->client;
 | 
			
		||||
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) {
 | 
			
		||||
  auto client = this->service->client;
 | 
			
		||||
  auto status = esp_ble_gattc_write_char(client->gattc_if, client->conn_id, this->handle, new_val_size, new_val,
 | 
			
		||||
                                         write_type, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
                                         ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  if (status) {
 | 
			
		||||
    ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) {
 | 
			
		||||
  write_value(new_val, new_val_size, ESP_GATT_WRITE_TYPE_NO_RSP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ble_client
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@
 | 
			
		||||
#include <esp_gap_ble_api.h>
 | 
			
		||||
#include <esp_gattc_api.h>
 | 
			
		||||
#include <esp_bt_defs.h>
 | 
			
		||||
#include <esp_gatt_common_api.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ble_client {
 | 
			
		||||
@@ -61,7 +60,6 @@ class BLECharacteristic {
 | 
			
		||||
  BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid);
 | 
			
		||||
  BLEDescriptor *get_descriptor(uint16_t uuid);
 | 
			
		||||
  void write_value(uint8_t *new_val, int16_t new_val_size);
 | 
			
		||||
  void write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type);
 | 
			
		||||
  BLEService *service;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -87,7 +85,6 @@ class BLEClient : public espbt::ESPBTClient, public Component {
 | 
			
		||||
 | 
			
		||||
  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                           esp_ble_gattc_cb_param_t *param) override;
 | 
			
		||||
  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
 | 
			
		||||
  bool parse_device(const espbt::ESPBTDevice &device) override;
 | 
			
		||||
  void on_scan_end() override {}
 | 
			
		||||
  void connect() override;
 | 
			
		||||
@@ -129,10 +126,9 @@ class BLEClient : public espbt::ESPBTClient, public Component {
 | 
			
		||||
  bool all_nodes_established_() {
 | 
			
		||||
    if (this->state() != espbt::ClientState::ESTABLISHED)
 | 
			
		||||
      return false;
 | 
			
		||||
    for (auto &node : nodes_) {
 | 
			
		||||
    for (auto &node : nodes_)
 | 
			
		||||
      if (node->node_state != espbt::ClientState::ESTABLISHED)
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,13 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import ble_client, esp32_ble_tracker, output
 | 
			
		||||
from esphome.components import output, ble_client, esp32_ble_tracker
 | 
			
		||||
from esphome.const import CONF_ID, CONF_SERVICE_UUID
 | 
			
		||||
 | 
			
		||||
from .. import ble_client_ns
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["ble_client"]
 | 
			
		||||
 | 
			
		||||
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
 | 
			
		||||
CONF_REQUIRE_RESPONSE = "require_response"
 | 
			
		||||
 | 
			
		||||
BLEBinaryOutput = ble_client_ns.class_(
 | 
			
		||||
    "BLEBinaryOutput", output.BinaryOutput, ble_client.BLEClientNode, cg.Component
 | 
			
		||||
@@ -20,7 +19,6 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Required(CONF_ID): cv.declare_id(BLEBinaryOutput),
 | 
			
		||||
            cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Optional(CONF_REQUIRE_RESPONSE, default=False): cv.boolean,
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
@@ -63,7 +61,7 @@ def to_code(config):
 | 
			
		||||
            config[CONF_CHARACTERISTIC_UUID]
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_char_uuid128(uuid128))
 | 
			
		||||
    cg.add(var.set_require_response(config[CONF_REQUIRE_RESPONSE]))
 | 
			
		||||
 | 
			
		||||
    yield output.register_output(var, config)
 | 
			
		||||
    yield ble_client.register_ble_node(var, config)
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ void BLEBinaryOutput::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
 | 
			
		||||
      auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
 | 
			
		||||
      if (chr == nullptr) {
 | 
			
		||||
        ESP_LOGW(TAG, "[%s] Characteristic not found.", this->char_uuid_.to_string().c_str());
 | 
			
		||||
        break;
 | 
			
		||||
@@ -54,7 +54,7 @@ void BLEBinaryOutput::write_state(bool state) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
 | 
			
		||||
  auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
 | 
			
		||||
  if (chr == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%s] Characteristic not found.  State update can not be written.",
 | 
			
		||||
             this->char_uuid_.to_string().c_str());
 | 
			
		||||
@@ -63,11 +63,7 @@ void BLEBinaryOutput::write_state(bool state) {
 | 
			
		||||
 | 
			
		||||
  uint8_t state_as_uint = (uint8_t) state;
 | 
			
		||||
  ESP_LOGV(TAG, "[%s] Write State: %d", this->char_uuid_.to_string().c_str(), state_as_uint);
 | 
			
		||||
  if (this->require_response_) {
 | 
			
		||||
    chr->write_value(&state_as_uint, sizeof(state_as_uint), ESP_GATT_WRITE_TYPE_RSP);
 | 
			
		||||
  } else {
 | 
			
		||||
    chr->write_value(&state_as_uint, sizeof(state_as_uint), ESP_GATT_WRITE_TYPE_NO_RSP);
 | 
			
		||||
  }
 | 
			
		||||
  chr->write_value(&state_as_uint, sizeof(state_as_uint));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ble_client
 | 
			
		||||
 
 | 
			
		||||
@@ -25,11 +25,9 @@ class BLEBinaryOutput : public output::BinaryOutput, public BLEClientNode, publi
 | 
			
		||||
  void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
 | 
			
		||||
  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                           esp_ble_gattc_cb_param_t *param) override;
 | 
			
		||||
  void set_require_response(bool response) { this->require_response_ = response; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void write_state(bool state) override;
 | 
			
		||||
  bool require_response_;
 | 
			
		||||
  espbt::ESPBTUUID service_uuid_;
 | 
			
		||||
  espbt::ESPBTUUID char_uuid_;
 | 
			
		||||
  espbt::ClientState client_state_;
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user