mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Compare commits
	
		
			143 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					59c192becc | ||
| 
						 | 
					a800816750 | ||
| 
						 | 
					99d9ab4e40 | ||
| 
						 | 
					f310ca1b74 | ||
| 
						 | 
					f763daa577 | ||
| 
						 | 
					970563e07b | ||
| 
						 | 
					e006045f59 | ||
| 
						 | 
					fff3645901 | ||
| 
						 | 
					a5383fd208 | ||
| 
						 | 
					9ce3a2059f | ||
| 
						 | 
					69e6cf2c0c | ||
| 
						 | 
					28635124f9 | ||
| 
						 | 
					035be87a83 | ||
| 
						 | 
					e8bdbc45a9 | ||
| 
						 | 
					429caccefa | ||
| 
						 | 
					744ca1af7c | ||
| 
						 | 
					106f0d611f | ||
| 
						 | 
					d826416684 | ||
| 
						 | 
					81959804df | ||
| 
						 | 
					75b524ddc4 | ||
| 
						 | 
					f599c36272 | ||
| 
						 | 
					9bb64315f3 | ||
| 
						 | 
					575badc690 | ||
| 
						 | 
					4b91cfb7f9 | ||
| 
						 | 
					a57a842f7b | ||
| 
						 | 
					a8c253a2a5 | ||
| 
						 | 
					8b737aabd9 | ||
| 
						 | 
					0db4815f3d | ||
| 
						 | 
					139db58a66 | ||
| 
						 | 
					c32fec7432 | ||
| 
						 | 
					8bd23dd457 | ||
| 
						 | 
					97a12c0169 | ||
| 
						 | 
					635916737b | ||
| 
						 | 
					65c50e4f01 | ||
| 
						 | 
					5cf18235e3 | ||
| 
						 | 
					b80f3fdec9 | ||
| 
						 | 
					0426be9280 | ||
| 
						 | 
					7c678659d4 | ||
| 
						 | 
					13fe9e83fa | ||
| 
						 | 
					4711f36a1f | ||
| 
						 | 
					01e2a51132 | ||
| 
						 | 
					a70a205ace | ||
| 
						 | 
					33625e2dd3 | ||
| 
						 | 
					0277218319 | ||
| 
						 | 
					18a8c727fa | ||
| 
						 | 
					80ad784a4e | ||
| 
						 | 
					ebadaa9660 | ||
| 
						 | 
					7bc51582f0 | ||
| 
						 | 
					11fb54c74e | ||
| 
						 | 
					913ac8b7e8 | ||
| 
						 | 
					2b9350ce76 | ||
| 
						 | 
					3b18f1b87f | ||
| 
						 | 
					c5c24c1989 | ||
| 
						 | 
					c3938d04f3 | ||
| 
						 | 
					f968713be8 | ||
| 
						 | 
					7b11284008 | ||
| 
						 | 
					f39c0d52ee | ||
| 
						 | 
					a3756a9600 | ||
| 
						 | 
					afa436fe8f | ||
| 
						 | 
					48b5ea9e59 | ||
| 
						 | 
					56974153f1 | ||
| 
						 | 
					9a2cd71571 | ||
| 
						 | 
					d1c6368283 | ||
| 
						 | 
					5c3268b8d4 | ||
| 
						 | 
					25af5ab7c6 | ||
| 
						 | 
					4d586b1446 | ||
| 
						 | 
					bb759d52c8 | ||
| 
						 | 
					9a2cf05c5f | ||
| 
						 | 
					c79d700d03 | ||
| 
						 | 
					482a3aebc9 | ||
| 
						 | 
					387f249363 | ||
| 
						 | 
					3d917d0b7e | ||
| 
						 | 
					824f3187ac | ||
| 
						 | 
					e3c27a483c | ||
| 
						 | 
					a33bb32874 | ||
| 
						 | 
					93d9d4b50a | ||
| 
						 | 
					2376a2c941 | ||
| 
						 | 
					b92702a312 | ||
| 
						 | 
					b11d5f6799 | ||
| 
						 | 
					072dce340e | ||
| 
						 | 
					cccb1a2c9e | ||
| 
						 | 
					063d9c47a4 | ||
| 
						 | 
					8d8d421286 | ||
| 
						 | 
					0ce57e5a39 | ||
| 
						 | 
					aebad04c0b | ||
| 
						 | 
					514d11d46f | ||
| 
						 | 
					96e46db272 | ||
| 
						 | 
					76f78877f6 | ||
| 
						 | 
					4ffa68b773 | ||
| 
						 | 
					8eaffee160 | ||
| 
						 | 
					557a622f71 | ||
| 
						 | 
					e9c6556296 | ||
| 
						 | 
					dce9d59dfe | ||
| 
						 | 
					d3e291b442 | ||
| 
						 | 
					d4686c0fb1 | ||
| 
						 | 
					ae9b247f47 | ||
| 
						 | 
					a59761d292 | ||
| 
						 | 
					030c87d142 | ||
| 
						 | 
					95ed3e9d46 | ||
| 
						 | 
					d0eaebe19f | ||
| 
						 | 
					9ecead2645 | ||
| 
						 | 
					98166dfa66 | ||
| 
						 | 
					5645be4e0f | ||
| 
						 | 
					9a7a205510 | ||
| 
						 | 
					7e3b8fd346 | ||
| 
						 | 
					3d6dcc9eee | ||
| 
						 | 
					4f6982fbc5 | ||
| 
						 | 
					00c144daeb | ||
| 
						 | 
					54660300e9 | ||
| 
						 | 
					5dc40049be | ||
| 
						 | 
					1e46b4073f | ||
| 
						 | 
					29fc4af0fc | ||
| 
						 | 
					4030a2e253 | ||
| 
						 | 
					ea80cb751b | ||
| 
						 | 
					6aa61dbce7 | ||
| 
						 | 
					cdc9c99d40 | ||
| 
						 | 
					a546677b08 | ||
| 
						 | 
					5c3af1d3f6 | ||
| 
						 | 
					66b0b6feeb | ||
| 
						 | 
					7665a220a0 | ||
| 
						 | 
					4250af4dd9 | ||
| 
						 | 
					73252ccd25 | ||
| 
						 | 
					33bf78c369 | ||
| 
						 | 
					96ded4e402 | ||
| 
						 | 
					076124eb71 | ||
| 
						 | 
					b660e5a7fa | ||
| 
						 | 
					3b4ea0ed0a | ||
| 
						 | 
					403b6e32e3 | ||
| 
						 | 
					b91a1aa027 | ||
| 
						 | 
					13dbdd9b16 | ||
| 
						 | 
					37bc0b3b5a | ||
| 
						 | 
					be70a96651 | ||
| 
						 | 
					d83d214497 | ||
| 
						 | 
					6ec0f80b76 | ||
| 
						 | 
					06f566346d | ||
| 
						 | 
					b680649113 | ||
| 
						 | 
					5ab2ef4079 | ||
| 
						 | 
					392ed64375 | ||
| 
						 | 
					566c129435 | ||
| 
						 | 
					c903eb2d01 | ||
| 
						 | 
					69c78651d5 | ||
| 
						 | 
					34487c9de4 | ||
| 
						 | 
					822377be8b | 
							
								
								
									
										38
									
								
								.clang-tidy
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								.clang-tidy
									
									
									
									
									
								
							@@ -4,14 +4,35 @@ Checks: >-
 | 
			
		||||
  -abseil-*,
 | 
			
		||||
  -android-*,
 | 
			
		||||
  -boost-*,
 | 
			
		||||
  -bugprone-branch-clone,
 | 
			
		||||
  -bugprone-macro-parentheses,
 | 
			
		||||
  -bugprone-narrowing-conversions,
 | 
			
		||||
  -bugprone-reserved-identifier,
 | 
			
		||||
  -bugprone-signed-char-misuse,
 | 
			
		||||
  -bugprone-suspicious-include,
 | 
			
		||||
  -bugprone-too-small-loop-variable,
 | 
			
		||||
  -bugprone-unhandled-self-assignment,
 | 
			
		||||
  -cert-dcl37-c,
 | 
			
		||||
  -cert-dcl50-cpp,
 | 
			
		||||
  -cert-dcl51-cpp,
 | 
			
		||||
  -cert-err58-cpp,
 | 
			
		||||
  -cert-oop54-cpp,
 | 
			
		||||
  -cert-oop57-cpp,
 | 
			
		||||
  -cert-str34-c,
 | 
			
		||||
  -clang-analyzer-core.CallAndMessage,
 | 
			
		||||
  -clang-analyzer-optin.*,
 | 
			
		||||
  -clang-analyzer-osx.*,
 | 
			
		||||
  -clang-analyzer-security.*,
 | 
			
		||||
  -clang-diagnostic-shadow-field,
 | 
			
		||||
  -cppcoreguidelines-avoid-c-arrays,
 | 
			
		||||
  -cppcoreguidelines-avoid-goto,
 | 
			
		||||
  -cppcoreguidelines-avoid-magic-numbers,
 | 
			
		||||
  -cppcoreguidelines-avoid-non-const-global-variables,
 | 
			
		||||
  -cppcoreguidelines-c-copy-assignment-signature,
 | 
			
		||||
  -cppcoreguidelines-init-variables,
 | 
			
		||||
  -cppcoreguidelines-macro-usage,
 | 
			
		||||
  -cppcoreguidelines-narrowing-conversions,
 | 
			
		||||
  -cppcoreguidelines-non-private-member-variables-in-classes,
 | 
			
		||||
  -cppcoreguidelines-owning-memory,
 | 
			
		||||
  -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
 | 
			
		||||
  -cppcoreguidelines-pro-bounds-constant-array-index,
 | 
			
		||||
@@ -37,10 +58,16 @@ Checks: >-
 | 
			
		||||
  -google-runtime-int,
 | 
			
		||||
  -google-runtime-references,
 | 
			
		||||
  -hicpp-*,
 | 
			
		||||
  -llvm-else-after-return,
 | 
			
		||||
  -llvm-header-guard,
 | 
			
		||||
  -llvm-include-order,
 | 
			
		||||
  -llvm-qualified-auto,
 | 
			
		||||
  -llvmlibc-*,
 | 
			
		||||
  -misc-non-private-member-variables-in-classes,
 | 
			
		||||
  -misc-no-recursion,
 | 
			
		||||
  -misc-unconventional-assign-operator,
 | 
			
		||||
  -misc-unused-parameters,
 | 
			
		||||
  -modernize-avoid-c-arrays,
 | 
			
		||||
  -modernize-deprecated-headers,
 | 
			
		||||
  -modernize-pass-by-value,
 | 
			
		||||
  -modernize-pass-by-value,
 | 
			
		||||
@@ -48,14 +75,25 @@ Checks: >-
 | 
			
		||||
  -modernize-use-auto,
 | 
			
		||||
  -modernize-use-default-member-init,
 | 
			
		||||
  -modernize-use-equals-default,
 | 
			
		||||
  -modernize-use-trailing-return-type,
 | 
			
		||||
  -mpi-*,
 | 
			
		||||
  -objc-*,
 | 
			
		||||
  -performance-unnecessary-value-param,
 | 
			
		||||
  -readability-braces-around-statements,
 | 
			
		||||
  -readability-const-return-type,
 | 
			
		||||
  -readability-convert-member-functions-to-static,
 | 
			
		||||
  -readability-else-after-return,
 | 
			
		||||
  -readability-implicit-bool-conversion,
 | 
			
		||||
  -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,
 | 
			
		||||
  -warnings-as-errors,
 | 
			
		||||
  -zircon-*
 | 
			
		||||
WarningsAsErrors: '*'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							@@ -1,25 +1,22 @@
 | 
			
		||||
# What does this implement/fix? 
 | 
			
		||||
 | 
			
		||||
Quick description 
 | 
			
		||||
Quick description and explanation of changes
 | 
			
		||||
 | 
			
		||||
## Types of changes
 | 
			
		||||
 | 
			
		||||
- [ ] Bugfix (non-breaking change which fixes an issue)
 | 
			
		||||
- [ ] New feature (non-breaking change which adds functionality)
 | 
			
		||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
 | 
			
		||||
- [ ] Configuration change (this will require users to update their yaml configuration files to keep working)
 | 
			
		||||
- [ ] Other
 | 
			
		||||
 | 
			
		||||
**Related issue or feature (if applicable):** fixes <link to issue>
 | 
			
		||||
 | 
			
		||||
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>
 | 
			
		||||
  
 | 
			
		||||
# Test Environment
 | 
			
		||||
 | 
			
		||||
## Test Environment
 | 
			
		||||
 | 
			
		||||
- [ ] ESP32
 | 
			
		||||
- [ ] ESP8266
 | 
			
		||||
- [ ] Windows
 | 
			
		||||
- [ ] Mac OS
 | 
			
		||||
- [ ] Linux
 | 
			
		||||
 | 
			
		||||
## Example entry for `config.yaml`:
 | 
			
		||||
<!--
 | 
			
		||||
@@ -34,11 +31,6 @@ Quick description
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# Explain your changes
 | 
			
		||||
 | 
			
		||||
Describe your changes here to communicate to the maintainers **why we should accept this pull request**.
 | 
			
		||||
Very important to fill if no issue linked
 | 
			
		||||
 | 
			
		||||
## Checklist:
 | 
			
		||||
  - [ ] The code change is tested and works locally.
 | 
			
		||||
  - [ ] Tests have been added to verify that the new code works (under `tests/` folder).
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							@@ -26,7 +26,7 @@ jobs:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: Set up env variables
 | 
			
		||||
        run: |
 | 
			
		||||
          base_version="3.0.0"
 | 
			
		||||
          base_version="3.4.0"
 | 
			
		||||
 | 
			
		||||
          if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
 | 
			
		||||
            build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -15,7 +15,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
			
		||||
    # doesn't have to be installed
 | 
			
		||||
    container: esphome/esphome-lint:latest
 | 
			
		||||
    container: esphome/esphome-lint:1.1
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      # Set up the pio project so that the cpp checks know how files are compiled
 | 
			
		||||
@@ -32,7 +32,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
			
		||||
    # doesn't have to be installed
 | 
			
		||||
    container: esphome/esphome-lint:latest
 | 
			
		||||
    container: esphome/esphome-lint:1.1
 | 
			
		||||
    # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
@@ -97,6 +97,7 @@ jobs:
 | 
			
		||||
          - test2
 | 
			
		||||
          - test3
 | 
			
		||||
          - test4
 | 
			
		||||
          - test5
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
@@ -126,7 +127,7 @@ jobs:
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
			
		||||
          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
			
		||||
      - run: esphome tests/${{ matrix.test }}.yaml compile
 | 
			
		||||
      - run: esphome compile tests/${{ matrix.test }}.yaml
 | 
			
		||||
 | 
			
		||||
  pytest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/docker-lint-build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/docker-lint-build.yml
									
									
									
									
										vendored
									
									
								
							@@ -7,6 +7,7 @@ on:
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'docker/Dockerfile.lint'
 | 
			
		||||
      - 'requirements.txt'
 | 
			
		||||
      - 'requirements_optional.txt'
 | 
			
		||||
      - 'requirements_test.txt'
 | 
			
		||||
      - 'platformio.ini'
 | 
			
		||||
      - '.github/workflows/docker-lint-build.yml'
 | 
			
		||||
@@ -17,6 +18,9 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: Set TAG
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "TAG=1.1" >> $GITHUB_ENV
 | 
			
		||||
      - name: Pull for cache
 | 
			
		||||
        run: |
 | 
			
		||||
          docker pull "esphome/esphome-lint:latest" || true
 | 
			
		||||
@@ -26,6 +30,7 @@ jobs:
 | 
			
		||||
            --cache-from "esphome/esphome-lint:latest" \
 | 
			
		||||
            --file "docker/Dockerfile.lint" \
 | 
			
		||||
            --tag "esphome/esphome-lint:latest" \
 | 
			
		||||
            --tag "esphome/esphome-lint:${TAG}" \
 | 
			
		||||
            .
 | 
			
		||||
      - name: Log in to docker hub
 | 
			
		||||
        env:
 | 
			
		||||
@@ -33,4 +38,5 @@ jobs:
 | 
			
		||||
          DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
 | 
			
		||||
        run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
 | 
			
		||||
      - run: |
 | 
			
		||||
          docker push "esphome/esphome-lint:${TAG}"
 | 
			
		||||
          docker push "esphome/esphome-lint:latest"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								.github/workflows/release-dev.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/release-dev.yml
									
									
									
									
										vendored
									
									
								
							@@ -12,7 +12,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
			
		||||
    # doesn't have to be installed
 | 
			
		||||
    container: esphome/esphome-lint:latest
 | 
			
		||||
    container: esphome/esphome-lint:1.1
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      # Set up the pio project so that the cpp checks know how files are compiled
 | 
			
		||||
@@ -29,7 +29,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
			
		||||
    # doesn't have to be installed
 | 
			
		||||
    container: esphome/esphome-lint:latest
 | 
			
		||||
    container: esphome/esphome-lint:1.1
 | 
			
		||||
    # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
@@ -94,6 +94,7 @@ jobs:
 | 
			
		||||
          - test2
 | 
			
		||||
          - test3
 | 
			
		||||
          - test4
 | 
			
		||||
          - test5
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
@@ -123,7 +124,7 @@ jobs:
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
			
		||||
          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
			
		||||
      - run: esphome tests/${{ matrix.test }}.yaml compile
 | 
			
		||||
      - run: esphome compile tests/${{ matrix.test }}.yaml
 | 
			
		||||
 | 
			
		||||
  pytest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
@@ -174,7 +175,7 @@ jobs:
 | 
			
		||||
          echo "TAG=${TAG}" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set up env variables
 | 
			
		||||
        run: |
 | 
			
		||||
          base_version="3.0.0"
 | 
			
		||||
          base_version="3.4.0"
 | 
			
		||||
 | 
			
		||||
          if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
 | 
			
		||||
            build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -11,7 +11,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
			
		||||
    # doesn't have to be installed
 | 
			
		||||
    container: esphome/esphome-lint:latest
 | 
			
		||||
    container: esphome/esphome-lint:1.1
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      # Set up the pio project so that the cpp checks know how files are compiled
 | 
			
		||||
@@ -28,7 +28,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
			
		||||
    # doesn't have to be installed
 | 
			
		||||
    container: esphome/esphome-lint:latest
 | 
			
		||||
    container: esphome/esphome-lint:1.1
 | 
			
		||||
    # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
@@ -93,6 +93,7 @@ jobs:
 | 
			
		||||
          - test2
 | 
			
		||||
          - test3
 | 
			
		||||
          - test4
 | 
			
		||||
          - test5
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
@@ -121,7 +122,7 @@ jobs:
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
			
		||||
          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
			
		||||
      - run: esphome tests/${{ matrix.test }}.yaml compile
 | 
			
		||||
      - run: esphome compile tests/${{ matrix.test }}.yaml
 | 
			
		||||
 | 
			
		||||
  pytest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
@@ -194,7 +195,7 @@ jobs:
 | 
			
		||||
          echo "TAG=${TAG}" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set up env variables
 | 
			
		||||
        run: |
 | 
			
		||||
          base_version="3.0.0"
 | 
			
		||||
          base_version="3.4.0"
 | 
			
		||||
 | 
			
		||||
          if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
 | 
			
		||||
            build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -100,6 +100,8 @@ CMakeLists.txt
 | 
			
		||||
 | 
			
		||||
# CMake
 | 
			
		||||
cmake-build-debug/
 | 
			
		||||
cmake-build-livingroom8266/
 | 
			
		||||
cmake-build-livingroom32/
 | 
			
		||||
cmake-build-release/
 | 
			
		||||
 | 
			
		||||
CMakeCache.txt
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
        {
 | 
			
		||||
            "label": "run",
 | 
			
		||||
            "type": "shell",
 | 
			
		||||
            "command": "python3 -m esphome config dashboard",
 | 
			
		||||
            "command": "python3 -m esphome dashboard config",
 | 
			
		||||
            "problemMatcher": []
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
@@ -29,11 +29,15 @@ esphome/components/climate/* @esphome/core
 | 
			
		||||
esphome/components/climate_ir/* @glmnet
 | 
			
		||||
esphome/components/coolix/* @glmnet
 | 
			
		||||
esphome/components/cover/* @esphome/core
 | 
			
		||||
esphome/components/cs5460a/* @balrog-kun
 | 
			
		||||
esphome/components/ct_clamp/* @jesserockz
 | 
			
		||||
esphome/components/debug/* @OttoWinter
 | 
			
		||||
esphome/components/dfplayer/* @glmnet
 | 
			
		||||
esphome/components/dht/* @OttoWinter
 | 
			
		||||
esphome/components/ds1307/* @badbadc0ffee
 | 
			
		||||
esphome/components/esp32_ble/* @jesserockz
 | 
			
		||||
esphome/components/esp32_ble_server/* @jesserockz
 | 
			
		||||
esphome/components/esp32_improv/* @jesserockz
 | 
			
		||||
esphome/components/exposure_notifications/* @OttoWinter
 | 
			
		||||
esphome/components/ezo/* @ssieb
 | 
			
		||||
esphome/components/fastled_base/* @OttoWinter
 | 
			
		||||
@@ -43,6 +47,7 @@ esphome/components/gpio/* @esphome/core
 | 
			
		||||
esphome/components/gps/* @coogle
 | 
			
		||||
esphome/components/homeassistant/* @OttoWinter
 | 
			
		||||
esphome/components/i2c/* @esphome/core
 | 
			
		||||
esphome/components/improv/* @jesserockz
 | 
			
		||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
 | 
			
		||||
esphome/components/inkplate6/* @jesserockz
 | 
			
		||||
esphome/components/integration/* @OttoWinter
 | 
			
		||||
@@ -63,6 +68,7 @@ esphome/components/mcp2515/* @danielschramm @mvturnho
 | 
			
		||||
esphome/components/mcp9808/* @k7hpn
 | 
			
		||||
esphome/components/midea_ac/* @dudanov
 | 
			
		||||
esphome/components/midea_dongle/* @dudanov
 | 
			
		||||
esphome/components/mitsubishi/* @RubyBailey
 | 
			
		||||
esphome/components/network/* @esphome/core
 | 
			
		||||
esphome/components/nfc/* @jesserockz
 | 
			
		||||
esphome/components/ota/* @esphome/core
 | 
			
		||||
@@ -80,6 +86,7 @@ esphome/components/restart/* @esphome/core
 | 
			
		||||
esphome/components/rf_bridge/* @jesserockz
 | 
			
		||||
esphome/components/rtttl/* @glmnet
 | 
			
		||||
esphome/components/script/* @esphome/core
 | 
			
		||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
 | 
			
		||||
esphome/components/sensor/* @esphome/core
 | 
			
		||||
esphome/components/sgp40/* @SenexCrenshaw
 | 
			
		||||
esphome/components/sht4x/* @sjtrny
 | 
			
		||||
@@ -122,3 +129,4 @@ esphome/components/web_server_base/* @OttoWinter
 | 
			
		||||
esphome/components/whirlpool/* @glmnet
 | 
			
		||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
 | 
			
		||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
 | 
			
		||||
esphome/components/xpt2046/* @numo68
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
ARG BUILD_FROM=esphome/esphome-base-amd64:3.0.0
 | 
			
		||||
ARG BUILD_FROM=esphome/esphome-base-amd64:3.4.0
 | 
			
		||||
FROM ${BUILD_FROM}
 | 
			
		||||
 | 
			
		||||
# First install requirements to leverage caching when requirements don't change
 | 
			
		||||
COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
 | 
			
		||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
 | 
			
		||||
RUN \
 | 
			
		||||
    pip3 install --no-cache-dir -r /requirements.txt \
 | 
			
		||||
    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
 | 
			
		||||
    && /platformio_install_deps.py /platformio.ini
 | 
			
		||||
 | 
			
		||||
# Then copy esphome and install
 | 
			
		||||
@@ -27,4 +27,4 @@ WORKDIR /config
 | 
			
		||||
# in every docker command twice
 | 
			
		||||
ENTRYPOINT ["esphome"]
 | 
			
		||||
# When no arguments given, start the dashboard in the workdir
 | 
			
		||||
CMD ["/config", "dashboard"]
 | 
			
		||||
CMD ["dashboard", "/config"]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
FROM esphome/esphome-base-amd64:3.0.0
 | 
			
		||||
FROM esphome/esphome-base-amd64:3.4.0
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,9 @@ ARG BUILD_FROM
 | 
			
		||||
FROM ${BUILD_FROM}
 | 
			
		||||
 | 
			
		||||
# First install requirements to leverage caching when requirements don't change
 | 
			
		||||
COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
 | 
			
		||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
 | 
			
		||||
RUN \
 | 
			
		||||
    pip3 install --no-cache-dir -r /requirements.txt \
 | 
			
		||||
    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
 | 
			
		||||
    && /platformio_install_deps.py /platformio.ini
 | 
			
		||||
 | 
			
		||||
# Copy root filesystem
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
FROM esphome/esphome-lint-base:3.0.0
 | 
			
		||||
FROM esphome/esphome-lint-base:3.4.0
 | 
			
		||||
 | 
			
		||||
COPY requirements.txt requirements_test.txt docker/platformio_install_deps.py  platformio.ini /
 | 
			
		||||
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_test.txt \
 | 
			
		||||
    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
 | 
			
		||||
    && /platformio_install_deps.py /platformio.ini
 | 
			
		||||
 | 
			
		||||
VOLUME ["/esphome"]
 | 
			
		||||
 
 | 
			
		||||
@@ -23,4 +23,4 @@ if bashio::config.has_value 'relative_url'; then
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
bashio::log.info "Starting ESPHome dashboard..."
 | 
			
		||||
exec esphome /config/esphome dashboard --socket /var/run/esphome.sock --hassio
 | 
			
		||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_ESPHOME,
 | 
			
		||||
    CONF_PLATFORMIO_OPTIONS,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
 | 
			
		||||
from esphome.core import CORE, EsphomeError, coroutine
 | 
			
		||||
from esphome.helpers import indent
 | 
			
		||||
from esphome.util import (
 | 
			
		||||
    run_external_command,
 | 
			
		||||
@@ -127,15 +127,16 @@ def wrap_to_code(name, comp):
 | 
			
		||||
    coro = coroutine(comp.to_code)
 | 
			
		||||
 | 
			
		||||
    @functools.wraps(comp.to_code)
 | 
			
		||||
    @coroutine_with_priority(coro.priority)
 | 
			
		||||
    def wrapped(conf):
 | 
			
		||||
    async def wrapped(conf):
 | 
			
		||||
        cg.add(cg.LineComment(f"{name}:"))
 | 
			
		||||
        if comp.config_schema is not None:
 | 
			
		||||
            conf_str = yaml_util.dump(conf)
 | 
			
		||||
            conf_str = conf_str.replace("//", "")
 | 
			
		||||
            cg.add(cg.LineComment(indent(conf_str)))
 | 
			
		||||
        yield coro(conf)
 | 
			
		||||
        await coro(conf)
 | 
			
		||||
 | 
			
		||||
    if hasattr(coro, "priority"):
 | 
			
		||||
        wrapped.priority = coro.priority
 | 
			
		||||
    return wrapped
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -267,7 +268,7 @@ def clean_mqtt(config, args):
 | 
			
		||||
def command_wizard(args):
 | 
			
		||||
    from esphome import wizard
 | 
			
		||||
 | 
			
		||||
    return wizard.wizard(args.configuration[0])
 | 
			
		||||
    return wizard.wizard(args.configuration)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_config(args, config):
 | 
			
		||||
@@ -283,7 +284,7 @@ def command_vscode(args):
 | 
			
		||||
 | 
			
		||||
    logging.disable(logging.INFO)
 | 
			
		||||
    logging.disable(logging.WARNING)
 | 
			
		||||
    CORE.config_path = args.configuration[0]
 | 
			
		||||
    CORE.config_path = args.configuration
 | 
			
		||||
    vscode.read_config(args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -303,7 +304,7 @@ def command_compile(args, config):
 | 
			
		||||
 | 
			
		||||
def command_upload(args, config):
 | 
			
		||||
    port = choose_upload_log_host(
 | 
			
		||||
        default=args.upload_port,
 | 
			
		||||
        default=args.device,
 | 
			
		||||
        check_default=None,
 | 
			
		||||
        show_ota=True,
 | 
			
		||||
        show_mqtt=False,
 | 
			
		||||
@@ -318,7 +319,7 @@ def command_upload(args, config):
 | 
			
		||||
 | 
			
		||||
def command_logs(args, config):
 | 
			
		||||
    port = choose_upload_log_host(
 | 
			
		||||
        default=args.serial_port,
 | 
			
		||||
        default=args.device,
 | 
			
		||||
        check_default=None,
 | 
			
		||||
        show_ota=False,
 | 
			
		||||
        show_mqtt=True,
 | 
			
		||||
@@ -336,7 +337,7 @@ def command_run(args, config):
 | 
			
		||||
        return exit_code
 | 
			
		||||
    _LOGGER.info("Successfully compiled program.")
 | 
			
		||||
    port = choose_upload_log_host(
 | 
			
		||||
        default=args.upload_port,
 | 
			
		||||
        default=args.device,
 | 
			
		||||
        check_default=None,
 | 
			
		||||
        show_ota=True,
 | 
			
		||||
        show_mqtt=False,
 | 
			
		||||
@@ -349,7 +350,7 @@ def command_run(args, config):
 | 
			
		||||
    if args.no_logs:
 | 
			
		||||
        return 0
 | 
			
		||||
    port = choose_upload_log_host(
 | 
			
		||||
        default=args.upload_port,
 | 
			
		||||
        default=args.device,
 | 
			
		||||
        check_default=port,
 | 
			
		||||
        show_ota=False,
 | 
			
		||||
        show_mqtt=True,
 | 
			
		||||
@@ -407,7 +408,7 @@ def command_update_all(args):
 | 
			
		||||
        print("-" * twidth)
 | 
			
		||||
        print()
 | 
			
		||||
        rc = run_external_process(
 | 
			
		||||
            "esphome", "--dashboard", f, "run", "--no-logs", "--upload-port", "OTA"
 | 
			
		||||
            "esphome", "--dashboard", "run", "--no-logs", "--device", "OTA", f
 | 
			
		||||
        )
 | 
			
		||||
        if rc == 0:
 | 
			
		||||
            print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
 | 
			
		||||
@@ -452,15 +453,17 @@ POST_CONFIG_ACTIONS = {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_args(argv):
 | 
			
		||||
    parser = argparse.ArgumentParser(description=f"ESPHome v{const.__version__}")
 | 
			
		||||
    parser.add_argument(
 | 
			
		||||
        "-v", "--verbose", help="Enable verbose esphome logs.", action="store_true"
 | 
			
		||||
    options_parser = argparse.ArgumentParser(add_help=False)
 | 
			
		||||
    options_parser.add_argument(
 | 
			
		||||
        "-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true"
 | 
			
		||||
    )
 | 
			
		||||
    parser.add_argument(
 | 
			
		||||
        "-q", "--quiet", help="Disable all esphome logs.", action="store_true"
 | 
			
		||||
    options_parser.add_argument(
 | 
			
		||||
        "-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
 | 
			
		||||
    )
 | 
			
		||||
    parser.add_argument("--dashboard", help=argparse.SUPPRESS, action="store_true")
 | 
			
		||||
    parser.add_argument(
 | 
			
		||||
    options_parser.add_argument(
 | 
			
		||||
        "--dashboard", help=argparse.SUPPRESS, action="store_true"
 | 
			
		||||
    )
 | 
			
		||||
    options_parser.add_argument(
 | 
			
		||||
        "-s",
 | 
			
		||||
        "--substitution",
 | 
			
		||||
        nargs=2,
 | 
			
		||||
@@ -468,17 +471,88 @@ def parse_args(argv):
 | 
			
		||||
        help="Add a substitution",
 | 
			
		||||
        metavar=("key", "value"),
 | 
			
		||||
    )
 | 
			
		||||
    parser.add_argument(
 | 
			
		||||
        "configuration", help="Your YAML configuration file.", nargs="*"
 | 
			
		||||
 | 
			
		||||
    # Keep backward compatibility with the old command line format of
 | 
			
		||||
    # esphome <config> <command>.
 | 
			
		||||
    #
 | 
			
		||||
    # Unfortunately this can't be done by adding another configuration argument to the
 | 
			
		||||
    # main config parser, as argparse is greedy when parsing arguments, so in regular
 | 
			
		||||
    # usage it'll eat the command as the configuration argument and error out out
 | 
			
		||||
    # because it can't parse the configuration as a command.
 | 
			
		||||
    #
 | 
			
		||||
    # Instead, construct an ad-hoc parser for the old format that doesn't actually
 | 
			
		||||
    # process the arguments, but parses them enough to let us figure out if the old
 | 
			
		||||
    # format is used. In that case, swap the command and configuration in the arguments
 | 
			
		||||
    # and continue on with the normal parser (after raising a deprecation warning).
 | 
			
		||||
    #
 | 
			
		||||
    # Disable argparse's built-in help option and add it manually to prevent this
 | 
			
		||||
    # parser from printing the help messagefor the old format when invoked with -h.
 | 
			
		||||
    compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
 | 
			
		||||
    compat_parser.add_argument("-h", "--help")
 | 
			
		||||
    compat_parser.add_argument("configuration", nargs="*")
 | 
			
		||||
    compat_parser.add_argument(
 | 
			
		||||
        "command",
 | 
			
		||||
        choices=[
 | 
			
		||||
            "config",
 | 
			
		||||
            "compile",
 | 
			
		||||
            "upload",
 | 
			
		||||
            "logs",
 | 
			
		||||
            "run",
 | 
			
		||||
            "clean-mqtt",
 | 
			
		||||
            "wizard",
 | 
			
		||||
            "mqtt-fingerprint",
 | 
			
		||||
            "version",
 | 
			
		||||
            "clean",
 | 
			
		||||
            "dashboard",
 | 
			
		||||
            "vscode",
 | 
			
		||||
        ],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    subparsers = parser.add_subparsers(help="Commands", dest="command")
 | 
			
		||||
    # on Python 3.9+ we can simply set exit_on_error=False in the constructor
 | 
			
		||||
    def _raise(x):
 | 
			
		||||
        raise argparse.ArgumentError(None, x)
 | 
			
		||||
 | 
			
		||||
    compat_parser.error = _raise
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        result, unparsed = compat_parser.parse_known_args(argv[1:])
 | 
			
		||||
        last_option = len(argv) - len(unparsed) - 1 - len(result.configuration)
 | 
			
		||||
        argv = argv[0:last_option] + [result.command] + result.configuration + unparsed
 | 
			
		||||
        deprecated_argv_suggestion = argv
 | 
			
		||||
    except argparse.ArgumentError:
 | 
			
		||||
        # This is not an old-style command line, so we don't have to do anything.
 | 
			
		||||
        deprecated_argv_suggestion = None
 | 
			
		||||
 | 
			
		||||
    # And continue on with regular parsing
 | 
			
		||||
    parser = argparse.ArgumentParser(
 | 
			
		||||
        description=f"ESPHome v{const.__version__}", parents=[options_parser]
 | 
			
		||||
    )
 | 
			
		||||
    parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
 | 
			
		||||
 | 
			
		||||
    mqtt_options = argparse.ArgumentParser(add_help=False)
 | 
			
		||||
    mqtt_options.add_argument("--topic", help="Manually set the MQTT topic.")
 | 
			
		||||
    mqtt_options.add_argument("--username", help="Manually set the MQTT username.")
 | 
			
		||||
    mqtt_options.add_argument("--password", help="Manually set the MQTT password.")
 | 
			
		||||
    mqtt_options.add_argument("--client-id", help="Manually set the MQTT client id.")
 | 
			
		||||
 | 
			
		||||
    subparsers = parser.add_subparsers(
 | 
			
		||||
        help="Command to run:", dest="command", metavar="command"
 | 
			
		||||
    )
 | 
			
		||||
    subparsers.required = True
 | 
			
		||||
    subparsers.add_parser("config", help="Validate the configuration and spit it out.")
 | 
			
		||||
 | 
			
		||||
    parser_config = subparsers.add_parser(
 | 
			
		||||
        "config", help="Validate the configuration and spit it out."
 | 
			
		||||
    )
 | 
			
		||||
    parser_config.add_argument(
 | 
			
		||||
        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    parser_compile = subparsers.add_parser(
 | 
			
		||||
        "compile", help="Read the configuration and compile a program."
 | 
			
		||||
    )
 | 
			
		||||
    parser_compile.add_argument(
 | 
			
		||||
        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
			
		||||
    )
 | 
			
		||||
    parser_compile.add_argument(
 | 
			
		||||
        "--only-generate",
 | 
			
		||||
        help="Only generate source code, do not compile.",
 | 
			
		||||
@@ -486,106 +560,124 @@ def parse_args(argv):
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    parser_upload = subparsers.add_parser(
 | 
			
		||||
        "upload", help="Validate the configuration " "and upload the latest binary."
 | 
			
		||||
        "upload", help="Validate the configuration and upload the latest binary."
 | 
			
		||||
    )
 | 
			
		||||
    parser_upload.add_argument(
 | 
			
		||||
        "--upload-port",
 | 
			
		||||
        help="Manually specify the upload port to use. "
 | 
			
		||||
        "For example /dev/cu.SLAB_USBtoUART.",
 | 
			
		||||
        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
			
		||||
    )
 | 
			
		||||
    parser_upload.add_argument(
 | 
			
		||||
        "--device",
 | 
			
		||||
        help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    parser_logs = subparsers.add_parser(
 | 
			
		||||
        "logs", help="Validate the configuration " "and show all MQTT logs."
 | 
			
		||||
        "logs",
 | 
			
		||||
        help="Validate the configuration and show all logs.",
 | 
			
		||||
        parents=[mqtt_options],
 | 
			
		||||
    )
 | 
			
		||||
    parser_logs.add_argument("--topic", help="Manually set the topic to subscribe to.")
 | 
			
		||||
    parser_logs.add_argument("--username", help="Manually set the username.")
 | 
			
		||||
    parser_logs.add_argument("--password", help="Manually set the password.")
 | 
			
		||||
    parser_logs.add_argument("--client-id", help="Manually set the client id.")
 | 
			
		||||
    parser_logs.add_argument(
 | 
			
		||||
        "--serial-port",
 | 
			
		||||
        help="Manually specify a serial port to use"
 | 
			
		||||
        "For example /dev/cu.SLAB_USBtoUART.",
 | 
			
		||||
        "configuration", help="Your YAML configuration file.", nargs=1
 | 
			
		||||
    )
 | 
			
		||||
    parser_logs.add_argument(
 | 
			
		||||
        "--device",
 | 
			
		||||
        help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    parser_run = subparsers.add_parser(
 | 
			
		||||
        "run",
 | 
			
		||||
        help="Validate the configuration, create a binary, "
 | 
			
		||||
        "upload it, and start MQTT logs.",
 | 
			
		||||
        help="Validate the configuration, create a binary, upload it, and start logs.",
 | 
			
		||||
        parents=[mqtt_options],
 | 
			
		||||
    )
 | 
			
		||||
    parser_run.add_argument(
 | 
			
		||||
        "--upload-port",
 | 
			
		||||
        help="Manually specify the upload port/ip to use. "
 | 
			
		||||
        "For example /dev/cu.SLAB_USBtoUART.",
 | 
			
		||||
        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
			
		||||
    )
 | 
			
		||||
    parser_run.add_argument(
 | 
			
		||||
        "--no-logs", help="Disable starting MQTT logs.", action="store_true"
 | 
			
		||||
        "--device",
 | 
			
		||||
        help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
 | 
			
		||||
    )
 | 
			
		||||
    parser_run.add_argument(
 | 
			
		||||
        "--topic", help="Manually set the topic to subscribe to for logs."
 | 
			
		||||
        "--no-logs", help="Disable starting logs.", action="store_true"
 | 
			
		||||
    )
 | 
			
		||||
    parser_run.add_argument(
 | 
			
		||||
        "--username", help="Manually set the MQTT username for logs."
 | 
			
		||||
    )
 | 
			
		||||
    parser_run.add_argument(
 | 
			
		||||
        "--password", help="Manually set the MQTT password for logs."
 | 
			
		||||
    )
 | 
			
		||||
    parser_run.add_argument("--client-id", help="Manually set the client id for logs.")
 | 
			
		||||
 | 
			
		||||
    parser_clean = subparsers.add_parser(
 | 
			
		||||
        "clean-mqtt", help="Helper to clear an MQTT topic from " "retain messages."
 | 
			
		||||
        "clean-mqtt",
 | 
			
		||||
        help="Helper to clear retained messages from an MQTT topic.",
 | 
			
		||||
        parents=[mqtt_options],
 | 
			
		||||
    )
 | 
			
		||||
    parser_clean.add_argument(
 | 
			
		||||
        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
			
		||||
    )
 | 
			
		||||
    parser_clean.add_argument("--topic", help="Manually set the topic to subscribe to.")
 | 
			
		||||
    parser_clean.add_argument("--username", help="Manually set the username.")
 | 
			
		||||
    parser_clean.add_argument("--password", help="Manually set the password.")
 | 
			
		||||
    parser_clean.add_argument("--client-id", help="Manually set the client id.")
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser(
 | 
			
		||||
    parser_wizard = subparsers.add_parser(
 | 
			
		||||
        "wizard",
 | 
			
		||||
        help="A helpful setup wizard that will guide "
 | 
			
		||||
        "you through setting up esphome.",
 | 
			
		||||
        help="A helpful setup wizard that will guide you through setting up ESPHome.",
 | 
			
		||||
    )
 | 
			
		||||
    parser_wizard.add_argument(
 | 
			
		||||
        "configuration",
 | 
			
		||||
        help="Your YAML configuration file.",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser(
 | 
			
		||||
    parser_fingerprint = subparsers.add_parser(
 | 
			
		||||
        "mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
 | 
			
		||||
    )
 | 
			
		||||
    parser_fingerprint.add_argument(
 | 
			
		||||
        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser("version", help="Print the esphome version and exit.")
 | 
			
		||||
    subparsers.add_parser("version", help="Print the ESPHome version and exit.")
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser("clean", help="Delete all temporary build files.")
 | 
			
		||||
    parser_clean = subparsers.add_parser(
 | 
			
		||||
        "clean", help="Delete all temporary build files."
 | 
			
		||||
    )
 | 
			
		||||
    parser_clean.add_argument(
 | 
			
		||||
        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    dashboard = subparsers.add_parser(
 | 
			
		||||
    parser_dashboard = subparsers.add_parser(
 | 
			
		||||
        "dashboard", help="Create a simple web server for a dashboard."
 | 
			
		||||
    )
 | 
			
		||||
    dashboard.add_argument(
 | 
			
		||||
    parser_dashboard.add_argument(
 | 
			
		||||
        "configuration",
 | 
			
		||||
        help="Your YAML configuration file directory.",
 | 
			
		||||
    )
 | 
			
		||||
    parser_dashboard.add_argument(
 | 
			
		||||
        "--port",
 | 
			
		||||
        help="The HTTP port to open connections on. Defaults to 6052.",
 | 
			
		||||
        type=int,
 | 
			
		||||
        default=6052,
 | 
			
		||||
    )
 | 
			
		||||
    dashboard.add_argument(
 | 
			
		||||
    parser_dashboard.add_argument(
 | 
			
		||||
        "--username",
 | 
			
		||||
        help="The optional username to require " "for authentication.",
 | 
			
		||||
        help="The optional username to require for authentication.",
 | 
			
		||||
        type=str,
 | 
			
		||||
        default="",
 | 
			
		||||
    )
 | 
			
		||||
    dashboard.add_argument(
 | 
			
		||||
    parser_dashboard.add_argument(
 | 
			
		||||
        "--password",
 | 
			
		||||
        help="The optional password to require " "for authentication.",
 | 
			
		||||
        help="The optional password to require for authentication.",
 | 
			
		||||
        type=str,
 | 
			
		||||
        default="",
 | 
			
		||||
    )
 | 
			
		||||
    dashboard.add_argument(
 | 
			
		||||
    parser_dashboard.add_argument(
 | 
			
		||||
        "--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
 | 
			
		||||
    )
 | 
			
		||||
    dashboard.add_argument("--hassio", help=argparse.SUPPRESS, action="store_true")
 | 
			
		||||
    dashboard.add_argument(
 | 
			
		||||
    parser_dashboard.add_argument(
 | 
			
		||||
        "--hassio", help=argparse.SUPPRESS, action="store_true"
 | 
			
		||||
    )
 | 
			
		||||
    parser_dashboard.add_argument(
 | 
			
		||||
        "--socket", help="Make the dashboard serve under a unix socket", type=str
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    vscode = subparsers.add_parser("vscode", help=argparse.SUPPRESS)
 | 
			
		||||
    vscode.add_argument("--ace", action="store_true")
 | 
			
		||||
    parser_vscode = subparsers.add_parser("vscode")
 | 
			
		||||
    parser_vscode.add_argument(
 | 
			
		||||
        "configuration", help="Your YAML configuration file.", nargs=1
 | 
			
		||||
    )
 | 
			
		||||
    parser_vscode.add_argument("--ace", action="store_true")
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser("update-all", help=argparse.SUPPRESS)
 | 
			
		||||
    parser_update = subparsers.add_parser("update-all")
 | 
			
		||||
    parser_update.add_argument(
 | 
			
		||||
        "configuration", help="Your YAML configuration file directory.", nargs=1
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    return parser.parse_args(argv[1:])
 | 
			
		||||
 | 
			
		||||
@@ -595,9 +687,13 @@ def run_esphome(argv):
 | 
			
		||||
    CORE.dashboard = args.dashboard
 | 
			
		||||
 | 
			
		||||
    setup_log(args.verbose, args.quiet)
 | 
			
		||||
    if args.command != "version" and not args.configuration:
 | 
			
		||||
        _LOGGER.error("Missing configuration parameter, see esphome --help.")
 | 
			
		||||
        return 1
 | 
			
		||||
    if args.deprecated_argv_suggestion is not None and args.command != "vscode":
 | 
			
		||||
        _LOGGER.warning(
 | 
			
		||||
            "Calling ESPHome with the configuration before the command is deprecated "
 | 
			
		||||
            "and will be removed in the future. "
 | 
			
		||||
        )
 | 
			
		||||
        _LOGGER.warning("Please instead use:")
 | 
			
		||||
        _LOGGER.warning("   esphome %s", " ".join(args.deprecated_argv_suggestion[1:]))
 | 
			
		||||
 | 
			
		||||
    if sys.version_info < (3, 7, 0):
 | 
			
		||||
        _LOGGER.error(
 | 
			
		||||
@@ -610,7 +706,7 @@ def run_esphome(argv):
 | 
			
		||||
        try:
 | 
			
		||||
            return PRE_CONFIG_ACTIONS[args.command](args)
 | 
			
		||||
        except EsphomeError as e:
 | 
			
		||||
            _LOGGER.error(e)
 | 
			
		||||
            _LOGGER.error(e, exc_info=args.verbose)
 | 
			
		||||
            return 1
 | 
			
		||||
 | 
			
		||||
    for conf_path in args.configuration:
 | 
			
		||||
@@ -628,7 +724,7 @@ def run_esphome(argv):
 | 
			
		||||
        try:
 | 
			
		||||
            rc = POST_CONFIG_ACTIONS[args.command](args, config)
 | 
			
		||||
        except EsphomeError as e:
 | 
			
		||||
            _LOGGER.error(e)
 | 
			
		||||
            _LOGGER.error(e, exc_info=args.verbose)
 | 
			
		||||
            return 1
 | 
			
		||||
        if rc != 0:
 | 
			
		||||
            return rc
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_TYPE_ID,
 | 
			
		||||
    CONF_TIME,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import coroutine
 | 
			
		||||
from esphome.jsonschema import jschema_extractor
 | 
			
		||||
from esphome.util import Registry
 | 
			
		||||
 | 
			
		||||
@@ -142,27 +141,27 @@ NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition("and", AndCondition, validate_condition_list)
 | 
			
		||||
def and_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    conditions = yield build_condition_list(config, template_arg, args)
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg, conditions)
 | 
			
		||||
async def and_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    conditions = await build_condition_list(config, template_arg, args)
 | 
			
		||||
    return cg.new_Pvariable(condition_id, template_arg, conditions)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition("or", OrCondition, validate_condition_list)
 | 
			
		||||
def or_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    conditions = yield build_condition_list(config, template_arg, args)
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg, conditions)
 | 
			
		||||
async def or_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    conditions = await build_condition_list(config, template_arg, args)
 | 
			
		||||
    return cg.new_Pvariable(condition_id, template_arg, conditions)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition("not", NotCondition, validate_potentially_and_condition)
 | 
			
		||||
def not_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    condition = yield build_condition(config, template_arg, args)
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg, condition)
 | 
			
		||||
async def not_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    condition = await build_condition(config, template_arg, args)
 | 
			
		||||
    return cg.new_Pvariable(condition_id, template_arg, condition)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition("lambda", LambdaCondition, cv.lambda_)
 | 
			
		||||
def lambda_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    lambda_ = yield cg.process_lambda(config, args, return_type=bool)
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg, lambda_)
 | 
			
		||||
@register_condition("lambda", LambdaCondition, cv.returning_lambda)
 | 
			
		||||
async def lambda_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    lambda_ = await cg.process_lambda(config, args, return_type=bool)
 | 
			
		||||
    return cg.new_Pvariable(condition_id, template_arg, lambda_)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition(
 | 
			
		||||
@@ -177,26 +176,26 @@ def lambda_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
        }
 | 
			
		||||
    ).extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
)
 | 
			
		||||
def for_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    condition = yield build_condition(
 | 
			
		||||
async def for_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    condition = await build_condition(
 | 
			
		||||
        config[CONF_CONDITION], cg.TemplateArguments(), []
 | 
			
		||||
    )
 | 
			
		||||
    var = cg.new_Pvariable(condition_id, template_arg, condition)
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    templ = await cg.templatable(config[CONF_TIME], args, cg.uint32)
 | 
			
		||||
    cg.add(var.set_time(templ))
 | 
			
		||||
    yield var
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action(
 | 
			
		||||
    "delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds)
 | 
			
		||||
)
 | 
			
		||||
def delay_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
async def delay_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg)
 | 
			
		||||
    yield cg.register_component(var, {})
 | 
			
		||||
    template_ = yield cg.templatable(config, args, cg.uint32)
 | 
			
		||||
    await cg.register_component(var, {})
 | 
			
		||||
    template_ = await cg.templatable(config, args, cg.uint32)
 | 
			
		||||
    cg.add(var.set_delay(template_))
 | 
			
		||||
    yield var
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action(
 | 
			
		||||
@@ -211,16 +210,16 @@ def delay_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
        cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
def if_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
 | 
			
		||||
async def if_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)
 | 
			
		||||
    if CONF_THEN in config:
 | 
			
		||||
        actions = yield build_action_list(config[CONF_THEN], template_arg, args)
 | 
			
		||||
        actions = await build_action_list(config[CONF_THEN], template_arg, args)
 | 
			
		||||
        cg.add(var.add_then(actions))
 | 
			
		||||
    if CONF_ELSE in config:
 | 
			
		||||
        actions = yield build_action_list(config[CONF_ELSE], template_arg, args)
 | 
			
		||||
        actions = await build_action_list(config[CONF_ELSE], template_arg, args)
 | 
			
		||||
        cg.add(var.add_else(actions))
 | 
			
		||||
    yield var
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action(
 | 
			
		||||
@@ -233,12 +232,12 @@ def if_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
def while_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
 | 
			
		||||
async def while_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)
 | 
			
		||||
    actions = yield build_action_list(config[CONF_THEN], template_arg, args)
 | 
			
		||||
    actions = await build_action_list(config[CONF_THEN], template_arg, args)
 | 
			
		||||
    cg.add(var.add_then(actions))
 | 
			
		||||
    yield var
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_wait_until(value):
 | 
			
		||||
@@ -253,17 +252,17 @@ def validate_wait_until(value):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action("wait_until", WaitUntilAction, validate_wait_until)
 | 
			
		||||
def wait_until_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
 | 
			
		||||
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)
 | 
			
		||||
    yield cg.register_component(var, {})
 | 
			
		||||
    yield var
 | 
			
		||||
    await cg.register_component(var, {})
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action("lambda", LambdaAction, cv.lambda_)
 | 
			
		||||
def lambda_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    lambda_ = yield cg.process_lambda(config, args, return_type=cg.void)
 | 
			
		||||
    yield cg.new_Pvariable(action_id, template_arg, lambda_)
 | 
			
		||||
async def lambda_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    lambda_ = await cg.process_lambda(config, args, return_type=cg.void)
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, lambda_)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action(
 | 
			
		||||
@@ -275,54 +274,51 @@ def lambda_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
def component_update_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    comp = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
    yield cg.new_Pvariable(action_id, template_arg, comp)
 | 
			
		||||
async def component_update_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    comp = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, comp)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def build_action(full_config, template_arg, args):
 | 
			
		||||
async def build_action(full_config, template_arg, args):
 | 
			
		||||
    registry_entry, config = cg.extract_registry_entry_config(
 | 
			
		||||
        ACTION_REGISTRY, full_config
 | 
			
		||||
    )
 | 
			
		||||
    action_id = full_config[CONF_TYPE_ID]
 | 
			
		||||
    builder = registry_entry.coroutine_fun
 | 
			
		||||
    yield builder(config, action_id, template_arg, args)
 | 
			
		||||
    ret = await builder(config, action_id, template_arg, args)
 | 
			
		||||
    return ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def build_action_list(config, templ, arg_type):
 | 
			
		||||
async def build_action_list(config, templ, arg_type):
 | 
			
		||||
    actions = []
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        action = yield build_action(conf, templ, arg_type)
 | 
			
		||||
        action = await build_action(conf, templ, arg_type)
 | 
			
		||||
        actions.append(action)
 | 
			
		||||
    yield actions
 | 
			
		||||
    return actions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def build_condition(full_config, template_arg, args):
 | 
			
		||||
async def build_condition(full_config, template_arg, args):
 | 
			
		||||
    registry_entry, config = cg.extract_registry_entry_config(
 | 
			
		||||
        CONDITION_REGISTRY, full_config
 | 
			
		||||
    )
 | 
			
		||||
    action_id = full_config[CONF_TYPE_ID]
 | 
			
		||||
    builder = registry_entry.coroutine_fun
 | 
			
		||||
    yield builder(config, action_id, template_arg, args)
 | 
			
		||||
    ret = await builder(config, action_id, template_arg, args)
 | 
			
		||||
    return ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def build_condition_list(config, templ, args):
 | 
			
		||||
async def build_condition_list(config, templ, args):
 | 
			
		||||
    conditions = []
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        condition = yield build_condition(conf, templ, args)
 | 
			
		||||
        condition = await build_condition(conf, templ, args)
 | 
			
		||||
        conditions.append(condition)
 | 
			
		||||
    yield conditions
 | 
			
		||||
    return conditions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def build_automation(trigger, args, config):
 | 
			
		||||
async def build_automation(trigger, args, config):
 | 
			
		||||
    arg_types = [arg[0] for arg in args]
 | 
			
		||||
    templ = cg.TemplateArguments(*arg_types)
 | 
			
		||||
    obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ, trigger)
 | 
			
		||||
    actions = yield build_action_list(config[CONF_THEN], templ, args)
 | 
			
		||||
    actions = await build_action_list(config[CONF_THEN], templ, args)
 | 
			
		||||
    cg.add(obj.add_actions(actions))
 | 
			
		||||
    yield obj
 | 
			
		||||
    return obj
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ from esphome.cpp_generator import (  # noqa
 | 
			
		||||
    Statement,
 | 
			
		||||
    LineComment,
 | 
			
		||||
    progmem_array,
 | 
			
		||||
    static_const_array,
 | 
			
		||||
    statement,
 | 
			
		||||
    variable,
 | 
			
		||||
    new_variable,
 | 
			
		||||
 
 | 
			
		||||
@@ -18,16 +18,16 @@ CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend(
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield stepper.register_stepper(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await stepper.register_stepper(var, config)
 | 
			
		||||
 | 
			
		||||
    step_pin = yield cg.gpio_pin_expression(config[CONF_STEP_PIN])
 | 
			
		||||
    step_pin = await cg.gpio_pin_expression(config[CONF_STEP_PIN])
 | 
			
		||||
    cg.add(var.set_step_pin(step_pin))
 | 
			
		||||
    dir_pin = yield cg.gpio_pin_expression(config[CONF_DIR_PIN])
 | 
			
		||||
    dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
 | 
			
		||||
    cg.add(var.set_dir_pin(dir_pin))
 | 
			
		||||
 | 
			
		||||
    if CONF_SLEEP_PIN in config:
 | 
			
		||||
        sleep_pin = yield cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
 | 
			
		||||
        sleep_pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
 | 
			
		||||
        cg.add(var.set_sleep_pin(sleep_pin))
 | 
			
		||||
 
 | 
			
		||||
@@ -32,18 +32,18 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    # override default min power to 10%
 | 
			
		||||
    if CONF_MIN_POWER not in config:
 | 
			
		||||
        config[CONF_MIN_POWER] = 0.1
 | 
			
		||||
    yield output.register_output(var, config)
 | 
			
		||||
    await output.register_output(var, config)
 | 
			
		||||
 | 
			
		||||
    pin = yield cg.gpio_pin_expression(config[CONF_GATE_PIN])
 | 
			
		||||
    pin = await cg.gpio_pin_expression(config[CONF_GATE_PIN])
 | 
			
		||||
    cg.add(var.set_gate_pin(pin))
 | 
			
		||||
    pin = yield cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
 | 
			
		||||
    pin = await cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
 | 
			
		||||
    cg.add(var.set_zero_cross_pin(pin))
 | 
			
		||||
    cg.add(var.set_init_with_half_cycle(config[CONF_INIT_WITH_HALF_CYCLE]))
 | 
			
		||||
    cg.add(var.set_method(config[CONF_METHOD]))
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,7 @@ CONFIG_SCHEMA = cv.Schema({})
 | 
			
		||||
    "Adalight",
 | 
			
		||||
    {cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)},
 | 
			
		||||
)
 | 
			
		||||
def adalight_light_effect_to_code(config, effect_id):
 | 
			
		||||
async def adalight_light_effect_to_code(config, effect_id):
 | 
			
		||||
    effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
 | 
			
		||||
    yield uart.register_uart_device(effect, config)
 | 
			
		||||
 | 
			
		||||
    yield effect
 | 
			
		||||
    await uart.register_uart_device(effect, config)
 | 
			
		||||
    return effect
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_PIN,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -35,7 +36,9 @@ ADCSensor = adc_ns.class_(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE)
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(ADCSensor),
 | 
			
		||||
@@ -49,10 +52,10 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield sensor.register_sensor(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await sensor.register_sensor(var, config)
 | 
			
		||||
 | 
			
		||||
    if config[CONF_PIN] == "VCC":
 | 
			
		||||
        cg.add_define("USE_ADC_SENSOR_VCC")
 | 
			
		||||
 
 | 
			
		||||
@@ -38,18 +38,18 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    wrapped_light = yield cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
 | 
			
		||||
    wrapped_light = await cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
 | 
			
		||||
    cg.add(var.set_width(config[CONF_WIDTH]))
 | 
			
		||||
    cg.add(var.set_height(config[CONF_HEIGHT]))
 | 
			
		||||
    cg.add(var.set_light(wrapped_light))
 | 
			
		||||
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield display.register_display(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await display.register_display(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_PIXEL_MAPPER in config:
 | 
			
		||||
        pixel_mapper_template_ = yield cg.process_lambda(
 | 
			
		||||
        pixel_mapper_template_ = await cg.process_lambda(
 | 
			
		||||
            config[CONF_PIXEL_MAPPER],
 | 
			
		||||
            [(int, "x"), (int, "y")],
 | 
			
		||||
            return_type=cg.int_,
 | 
			
		||||
@@ -57,7 +57,7 @@ def to_code(config):
 | 
			
		||||
        cg.add(var.set_pixel_mapper(pixel_mapper_template_))
 | 
			
		||||
 | 
			
		||||
    if CONF_LAMBDA in config:
 | 
			
		||||
        lambda_ = yield cg.process_lambda(
 | 
			
		||||
        lambda_ = await cg.process_lambda(
 | 
			
		||||
            config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_writer(lambda_))
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_POWER,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    UNIT_AMPERE,
 | 
			
		||||
    UNIT_WATT,
 | 
			
		||||
@@ -31,19 +32,27 @@ CONFIG_SCHEMA = (
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(ADE7953),
 | 
			
		||||
            cv.Optional(CONF_IRQ_PIN): pins.input_pin,
 | 
			
		||||
            cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE
 | 
			
		||||
                UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
 | 
			
		||||
                UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
 | 
			
		||||
                UNIT_AMPERE,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                2,
 | 
			
		||||
                DEVICE_CLASS_CURRENT,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
 | 
			
		||||
                UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
 | 
			
		||||
                UNIT_AMPERE,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                2,
 | 
			
		||||
                DEVICE_CLASS_CURRENT,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
 | 
			
		||||
                UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
 | 
			
		||||
                UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
 | 
			
		||||
                UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
 | 
			
		||||
                UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
@@ -52,10 +61,10 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_IRQ_PIN in config:
 | 
			
		||||
        cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
 | 
			
		||||
@@ -70,5 +79,5 @@ def to_code(config):
 | 
			
		||||
        if key not in config:
 | 
			
		||||
            continue
 | 
			
		||||
        conf = config[key]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(getattr(var, f"set_{key}_sensor")(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,9 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_MULTIPLEXER,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
)
 | 
			
		||||
@@ -51,7 +52,9 @@ ADS1115Sensor = ads1115_ns.class_(
 | 
			
		||||
 | 
			
		||||
CONF_ADS1115_ID = "ads1115_id"
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE)
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(ADS1115Sensor),
 | 
			
		||||
@@ -64,11 +67,11 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    paren = yield cg.get_variable(config[CONF_ADS1115_ID])
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ADS1115_ID])
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID], paren)
 | 
			
		||||
    yield sensor.register_sensor(var, config)
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    await sensor.register_sensor(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
 | 
			
		||||
    cg.add(var.set_gain(config[CONF_GAIN]))
 | 
			
		||||
 
 | 
			
		||||
@@ -60,6 +60,7 @@ void AHT10Component::update() {
 | 
			
		||||
    delay = AHT10_HUMIDITY_DELAY;
 | 
			
		||||
  for (int i = 0; i < AHT10_ATTEMPS; ++i) {
 | 
			
		||||
    ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis());
 | 
			
		||||
    delay_microseconds_accurate(4);
 | 
			
		||||
    if (!this->read_bytes(0, data, 6, delay)) {
 | 
			
		||||
      ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
 | 
			
		||||
    } else if ((data[0] & 0x80) == 0x80) {  // Bit[7] = 0b1, device is busy
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_PERCENT,
 | 
			
		||||
)
 | 
			
		||||
@@ -22,10 +23,18 @@ CONFIG_SCHEMA = (
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(AHT10Component),
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE
 | 
			
		||||
                UNIT_CELSIUS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                2,
 | 
			
		||||
                DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
			
		||||
                UNIT_PERCENT, ICON_EMPTY, 2, DEVICE_CLASS_HUMIDITY
 | 
			
		||||
                UNIT_PERCENT,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                2,
 | 
			
		||||
                DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
@@ -34,15 +43,15 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_HUMIDITY])
 | 
			
		||||
        cg.add(var.set_humidity_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    UNIT_PERCENT,
 | 
			
		||||
@@ -24,10 +25,18 @@ CONFIG_SCHEMA = (
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(AM2320Component),
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
 | 
			
		||||
                UNIT_CELSIUS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
			
		||||
                UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
 | 
			
		||||
                UNIT_PERCENT,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
@@ -36,15 +45,15 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_HUMIDITY])
 | 
			
		||||
        cg.add(var.set_humidity_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
 | 
			
		||||
CODEOWNERS = ["@syndlex"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    from PIL import Image
 | 
			
		||||
 | 
			
		||||
    path = CORE.relative_config_path(config[CONF_FILE])
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,8 @@ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = yield cg.get_variable(config[CONF_APDS9960_ID])
 | 
			
		||||
    var = yield binary_sensor.new_binary_sensor(config)
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    hub = await cg.get_variable(config[CONF_APDS9960_ID])
 | 
			
		||||
    var = await binary_sensor.new_binary_sensor(config)
 | 
			
		||||
    func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
 | 
			
		||||
    cg.add(func(var))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,13 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor
 | 
			
		||||
from esphome.const import CONF_TYPE, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_LIGHTBULB
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_TYPE,
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_PERCENT,
 | 
			
		||||
    ICON_LIGHTBULB,
 | 
			
		||||
)
 | 
			
		||||
from . import APDS9960, CONF_APDS9960_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["apds9960"]
 | 
			
		||||
@@ -15,7 +21,7 @@ TYPES = {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = sensor.sensor_schema(
 | 
			
		||||
    UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY
 | 
			
		||||
    UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
 | 
			
		||||
).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
 | 
			
		||||
@@ -24,8 +30,8 @@ CONFIG_SCHEMA = sensor.sensor_schema(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = yield cg.get_variable(config[CONF_APDS9960_ID])
 | 
			
		||||
    var = yield sensor.new_sensor(config)
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    hub = await cg.get_variable(config[CONF_APDS9960_ID])
 | 
			
		||||
    var = await sensor.new_sensor(config)
 | 
			
		||||
    func = getattr(hub, TYPES[config[CONF_TYPE]])
 | 
			
		||||
    cg.add(func(var))
 | 
			
		||||
 
 | 
			
		||||
@@ -68,9 +68,9 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(40.0)
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_port(config[CONF_PORT]))
 | 
			
		||||
    cg.add(var.set_password(config[CONF_PASSWORD]))
 | 
			
		||||
@@ -90,7 +90,7 @@ def to_code(config):
 | 
			
		||||
            conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.register_user_service(trigger))
 | 
			
		||||
        yield automation.build_automation(trigger, func_args, conf)
 | 
			
		||||
        await automation.build_automation(trigger, func_args, conf)
 | 
			
		||||
 | 
			
		||||
    cg.add_define("USE_API")
 | 
			
		||||
    cg.add_global(api_ns.using)
 | 
			
		||||
@@ -116,21 +116,21 @@ HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
    HomeAssistantServiceCallAction,
 | 
			
		||||
    HOMEASSISTANT_SERVICE_ACTION_SCHEMA,
 | 
			
		||||
)
 | 
			
		||||
def homeassistant_service_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    serv = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
async def homeassistant_service_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    serv = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, serv, False)
 | 
			
		||||
    templ = yield cg.templatable(config[CONF_SERVICE], args, None)
 | 
			
		||||
    templ = await cg.templatable(config[CONF_SERVICE], args, None)
 | 
			
		||||
    cg.add(var.set_service(templ))
 | 
			
		||||
    for key, value in config[CONF_DATA].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        templ = await cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_data(key, templ))
 | 
			
		||||
    for key, value in config[CONF_DATA_TEMPLATE].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        templ = await cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_data_template(key, templ))
 | 
			
		||||
    for key, value in config[CONF_VARIABLES].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        templ = await cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_variable(key, templ))
 | 
			
		||||
    yield var
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_homeassistant_event(value):
 | 
			
		||||
@@ -159,21 +159,21 @@ HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
    HomeAssistantServiceCallAction,
 | 
			
		||||
    HOMEASSISTANT_EVENT_ACTION_SCHEMA,
 | 
			
		||||
)
 | 
			
		||||
def homeassistant_event_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    serv = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
async def homeassistant_event_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    serv = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, serv, True)
 | 
			
		||||
    templ = yield cg.templatable(config[CONF_EVENT], args, None)
 | 
			
		||||
    templ = await cg.templatable(config[CONF_EVENT], args, None)
 | 
			
		||||
    cg.add(var.set_service(templ))
 | 
			
		||||
    for key, value in config[CONF_DATA].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        templ = await cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_data(key, templ))
 | 
			
		||||
    for key, value in config[CONF_DATA_TEMPLATE].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        templ = await cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_data_template(key, templ))
 | 
			
		||||
    for key, value in config[CONF_VARIABLES].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        templ = await cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_variable(key, templ))
 | 
			
		||||
    yield var
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
 | 
			
		||||
@@ -190,15 +190,15 @@ HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
 | 
			
		||||
    HomeAssistantServiceCallAction,
 | 
			
		||||
    HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA,
 | 
			
		||||
)
 | 
			
		||||
def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    serv = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    serv = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, serv, True)
 | 
			
		||||
    cg.add(var.set_service("esphome.tag_scanned"))
 | 
			
		||||
    templ = yield cg.templatable(config[CONF_TAG], args, cg.std_string)
 | 
			
		||||
    templ = await cg.templatable(config[CONF_TAG], args, cg.std_string)
 | 
			
		||||
    cg.add(var.add_data("tag_id", templ))
 | 
			
		||||
    yield var
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_condition("api.connected", APIConnectedCondition, {})
 | 
			
		||||
def api_connected_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg)
 | 
			
		||||
async def api_connected_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    return cg.new_Pvariable(condition_id, template_arg)
 | 
			
		||||
 
 | 
			
		||||
@@ -176,6 +176,10 @@ message DeviceInfoResponse {
 | 
			
		||||
  string model = 6;
 | 
			
		||||
 | 
			
		||||
  bool has_deep_sleep = 7;
 | 
			
		||||
 | 
			
		||||
  // The esphome project details if set
 | 
			
		||||
  string project_name = 8;
 | 
			
		||||
  string project_version = 9;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ListEntitiesRequest {
 | 
			
		||||
@@ -409,6 +413,11 @@ message LightCommandRequest {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== SENSOR ====================
 | 
			
		||||
enum SensorStateClass {
 | 
			
		||||
  STATE_CLASS_NONE = 0;
 | 
			
		||||
  STATE_CLASS_MEASUREMENT = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ListEntitiesSensorResponse {
 | 
			
		||||
  option (id) = 16;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
@@ -424,6 +433,7 @@ message ListEntitiesSensorResponse {
 | 
			
		||||
  int32 accuracy_decimals = 7;
 | 
			
		||||
  bool force_update = 8;
 | 
			
		||||
  string device_class = 9;
 | 
			
		||||
  SensorStateClass state_class = 10;
 | 
			
		||||
}
 | 
			
		||||
message SensorStateResponse {
 | 
			
		||||
  option (id) = 25;
 | 
			
		||||
@@ -561,6 +571,7 @@ message SubscribeHomeAssistantStateResponse {
 | 
			
		||||
  option (id) = 39;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  string entity_id = 1;
 | 
			
		||||
  string attribute = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message HomeAssistantStateResponse {
 | 
			
		||||
@@ -570,6 +581,7 @@ message HomeAssistantStateResponse {
 | 
			
		||||
 | 
			
		||||
  string entity_id = 1;
 | 
			
		||||
  string state = 2;
 | 
			
		||||
  string attribute = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== IMPORT TIME ====================
 | 
			
		||||
@@ -664,11 +676,12 @@ message CameraImageRequest {
 | 
			
		||||
// ==================== CLIMATE ====================
 | 
			
		||||
enum ClimateMode {
 | 
			
		||||
  CLIMATE_MODE_OFF = 0;
 | 
			
		||||
  CLIMATE_MODE_AUTO = 1;
 | 
			
		||||
  CLIMATE_MODE_HEAT_COOL = 1;
 | 
			
		||||
  CLIMATE_MODE_COOL = 2;
 | 
			
		||||
  CLIMATE_MODE_HEAT = 3;
 | 
			
		||||
  CLIMATE_MODE_FAN_ONLY = 4;
 | 
			
		||||
  CLIMATE_MODE_DRY = 5;
 | 
			
		||||
  CLIMATE_MODE_AUTO = 6;
 | 
			
		||||
}
 | 
			
		||||
enum ClimateFanMode {
 | 
			
		||||
  CLIMATE_FAN_ON = 0;
 | 
			
		||||
@@ -696,6 +709,15 @@ enum ClimateAction {
 | 
			
		||||
  CLIMATE_ACTION_DRYING = 5;
 | 
			
		||||
  CLIMATE_ACTION_FAN = 6;
 | 
			
		||||
}
 | 
			
		||||
enum ClimatePreset {
 | 
			
		||||
  CLIMATE_PRESET_ECO = 0;
 | 
			
		||||
  CLIMATE_PRESET_AWAY = 1;
 | 
			
		||||
  CLIMATE_PRESET_BOOST = 2;
 | 
			
		||||
  CLIMATE_PRESET_COMFORT = 3;
 | 
			
		||||
  CLIMATE_PRESET_HOME = 4;
 | 
			
		||||
  CLIMATE_PRESET_SLEEP = 5;
 | 
			
		||||
  CLIMATE_PRESET_ACTIVITY = 6;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesClimateResponse {
 | 
			
		||||
  option (id) = 46;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
@@ -716,6 +738,9 @@ message ListEntitiesClimateResponse {
 | 
			
		||||
  bool supports_action = 12;
 | 
			
		||||
  repeated ClimateFanMode supported_fan_modes = 13;
 | 
			
		||||
  repeated ClimateSwingMode supported_swing_modes = 14;
 | 
			
		||||
  repeated string supported_custom_fan_modes = 15;
 | 
			
		||||
  repeated ClimatePreset supported_presets = 16;
 | 
			
		||||
  repeated string supported_custom_presets = 17;
 | 
			
		||||
}
 | 
			
		||||
message ClimateStateResponse {
 | 
			
		||||
  option (id) = 47;
 | 
			
		||||
@@ -733,6 +758,9 @@ message ClimateStateResponse {
 | 
			
		||||
  ClimateAction action = 8;
 | 
			
		||||
  ClimateFanMode fan_mode = 9;
 | 
			
		||||
  ClimateSwingMode swing_mode = 10;
 | 
			
		||||
  string custom_fan_mode = 11;
 | 
			
		||||
  ClimatePreset preset = 12;
 | 
			
		||||
  string custom_preset = 13;
 | 
			
		||||
}
 | 
			
		||||
message ClimateCommandRequest {
 | 
			
		||||
  option (id) = 48;
 | 
			
		||||
@@ -755,4 +783,10 @@ message ClimateCommandRequest {
 | 
			
		||||
  ClimateFanMode fan_mode = 13;
 | 
			
		||||
  bool has_swing_mode = 14;
 | 
			
		||||
  ClimateSwingMode swing_mode = 15;
 | 
			
		||||
  bool has_custom_fan_mode = 16;
 | 
			
		||||
  string custom_fan_mode = 17;
 | 
			
		||||
  bool has_preset = 18;
 | 
			
		||||
  ClimatePreset preset = 19;
 | 
			
		||||
  bool has_custom_preset = 20;
 | 
			
		||||
  string custom_preset = 21;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -395,6 +395,8 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
 | 
			
		||||
  msg.accuracy_decimals = sensor->get_accuracy_decimals();
 | 
			
		||||
  msg.force_update = sensor->get_force_update();
 | 
			
		||||
  msg.device_class = sensor->get_device_class();
 | 
			
		||||
  msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
 | 
			
		||||
 | 
			
		||||
  return this->send_list_entities_sensor_response(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -475,8 +477,14 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_away())
 | 
			
		||||
    resp.away = climate->away;
 | 
			
		||||
  if (traits.get_supports_fan_modes())
 | 
			
		||||
    resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode);
 | 
			
		||||
  if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
 | 
			
		||||
    resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
 | 
			
		||||
  if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
 | 
			
		||||
    resp.custom_fan_mode = climate->custom_fan_mode.value();
 | 
			
		||||
  if (traits.get_supports_presets() && climate->preset.has_value())
 | 
			
		||||
    resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
 | 
			
		||||
  if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
 | 
			
		||||
    resp.custom_preset = climate->custom_preset.value();
 | 
			
		||||
  if (traits.get_supports_swing_modes())
 | 
			
		||||
    resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
 | 
			
		||||
  return this->send_climate_state_response(resp);
 | 
			
		||||
@@ -490,8 +498,9 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
 | 
			
		||||
  msg.unique_id = get_default_unique_id("climate", climate);
 | 
			
		||||
  msg.supports_current_temperature = traits.get_supports_current_temperature();
 | 
			
		||||
  msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
 | 
			
		||||
  for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL,
 | 
			
		||||
                    climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY}) {
 | 
			
		||||
  for (auto mode :
 | 
			
		||||
       {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, climate::CLIMATE_MODE_HEAT,
 | 
			
		||||
        climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY, climate::CLIMATE_MODE_HEAT_COOL}) {
 | 
			
		||||
    if (traits.supports_mode(mode))
 | 
			
		||||
      msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
 | 
			
		||||
  }
 | 
			
		||||
@@ -506,6 +515,18 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
 | 
			
		||||
    if (traits.supports_fan_mode(fan_mode))
 | 
			
		||||
      msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
 | 
			
		||||
  }
 | 
			
		||||
  for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes()) {
 | 
			
		||||
    msg.supported_custom_fan_modes.push_back(custom_fan_mode);
 | 
			
		||||
  }
 | 
			
		||||
  for (auto preset : {climate::CLIMATE_PRESET_ECO, climate::CLIMATE_PRESET_AWAY, climate::CLIMATE_PRESET_BOOST,
 | 
			
		||||
                      climate::CLIMATE_PRESET_COMFORT, climate::CLIMATE_PRESET_HOME, climate::CLIMATE_PRESET_SLEEP,
 | 
			
		||||
                      climate::CLIMATE_PRESET_ACTIVITY}) {
 | 
			
		||||
    if (traits.supports_preset(preset))
 | 
			
		||||
      msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
 | 
			
		||||
  }
 | 
			
		||||
  for (auto const &custom_preset : traits.get_supported_custom_presets()) {
 | 
			
		||||
    msg.supported_custom_presets.push_back(custom_preset);
 | 
			
		||||
  }
 | 
			
		||||
  for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL,
 | 
			
		||||
                          climate::CLIMATE_SWING_HORIZONTAL}) {
 | 
			
		||||
    if (traits.supports_swing_mode(swing_mode))
 | 
			
		||||
@@ -531,6 +552,12 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
 | 
			
		||||
    call.set_away(msg.away);
 | 
			
		||||
  if (msg.has_fan_mode)
 | 
			
		||||
    call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
 | 
			
		||||
  if (msg.has_custom_fan_mode)
 | 
			
		||||
    call.set_fan_mode(msg.custom_fan_mode);
 | 
			
		||||
  if (msg.has_preset)
 | 
			
		||||
    call.set_preset(static_cast<climate::ClimatePreset>(msg.preset));
 | 
			
		||||
  if (msg.has_custom_preset)
 | 
			
		||||
    call.set_preset(msg.custom_preset);
 | 
			
		||||
  if (msg.has_swing_mode)
 | 
			
		||||
    call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
 | 
			
		||||
  call.perform();
 | 
			
		||||
@@ -637,13 +664,18 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DEEP_SLEEP
 | 
			
		||||
  resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef ESPHOME_PROJECT_NAME
 | 
			
		||||
  resp.project_name = ESPHOME_PROJECT_NAME;
 | 
			
		||||
  resp.project_version = ESPHOME_PROJECT_VERSION;
 | 
			
		||||
#endif
 | 
			
		||||
  return resp;
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
 | 
			
		||||
  for (auto &it : this->parent_->get_state_subs())
 | 
			
		||||
    if (it.entity_id == msg.entity_id)
 | 
			
		||||
    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;
 | 
			
		||||
@@ -660,6 +692,7 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
 | 
			
		||||
  for (auto &it : this->parent_->get_state_subs()) {
 | 
			
		||||
    SubscribeHomeAssistantStateResponse resp;
 | 
			
		||||
    resp.entity_id = it.entity_id;
 | 
			
		||||
    resp.attribute = it.attribute.value();
 | 
			
		||||
    if (!this->send_subscribe_home_assistant_state_response(resp)) {
 | 
			
		||||
      this->on_fatal_error();
 | 
			
		||||
      return;
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,16 @@ template<> const char *proto_enum_to_string<enums::FanDirection>(enums::FanDirec
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::SensorStateClass>(enums::SensorStateClass value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::STATE_CLASS_NONE:
 | 
			
		||||
      return "STATE_CLASS_NONE";
 | 
			
		||||
    case enums::STATE_CLASS_MEASUREMENT:
 | 
			
		||||
      return "STATE_CLASS_MEASUREMENT";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::LOG_LEVEL_NONE:
 | 
			
		||||
@@ -108,8 +118,8 @@ template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMo
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::CLIMATE_MODE_OFF:
 | 
			
		||||
      return "CLIMATE_MODE_OFF";
 | 
			
		||||
    case enums::CLIMATE_MODE_AUTO:
 | 
			
		||||
      return "CLIMATE_MODE_AUTO";
 | 
			
		||||
    case enums::CLIMATE_MODE_HEAT_COOL:
 | 
			
		||||
      return "CLIMATE_MODE_HEAT_COOL";
 | 
			
		||||
    case enums::CLIMATE_MODE_COOL:
 | 
			
		||||
      return "CLIMATE_MODE_COOL";
 | 
			
		||||
    case enums::CLIMATE_MODE_HEAT:
 | 
			
		||||
@@ -118,6 +128,8 @@ template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMo
 | 
			
		||||
      return "CLIMATE_MODE_FAN_ONLY";
 | 
			
		||||
    case enums::CLIMATE_MODE_DRY:
 | 
			
		||||
      return "CLIMATE_MODE_DRY";
 | 
			
		||||
    case enums::CLIMATE_MODE_AUTO:
 | 
			
		||||
      return "CLIMATE_MODE_AUTO";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
@@ -178,6 +190,26 @@ template<> const char *proto_enum_to_string<enums::ClimateAction>(enums::Climate
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::ClimatePreset>(enums::ClimatePreset value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::CLIMATE_PRESET_ECO:
 | 
			
		||||
      return "CLIMATE_PRESET_ECO";
 | 
			
		||||
    case enums::CLIMATE_PRESET_AWAY:
 | 
			
		||||
      return "CLIMATE_PRESET_AWAY";
 | 
			
		||||
    case enums::CLIMATE_PRESET_BOOST:
 | 
			
		||||
      return "CLIMATE_PRESET_BOOST";
 | 
			
		||||
    case enums::CLIMATE_PRESET_COMFORT:
 | 
			
		||||
      return "CLIMATE_PRESET_COMFORT";
 | 
			
		||||
    case enums::CLIMATE_PRESET_HOME:
 | 
			
		||||
      return "CLIMATE_PRESET_HOME";
 | 
			
		||||
    case enums::CLIMATE_PRESET_SLEEP:
 | 
			
		||||
      return "CLIMATE_PRESET_SLEEP";
 | 
			
		||||
    case enums::CLIMATE_PRESET_ACTIVITY:
 | 
			
		||||
      return "CLIMATE_PRESET_ACTIVITY";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
@@ -328,6 +360,14 @@ bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited v
 | 
			
		||||
      this->model = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 8: {
 | 
			
		||||
      this->project_name = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 9: {
 | 
			
		||||
      this->project_version = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -340,6 +380,8 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_string(5, this->compilation_time);
 | 
			
		||||
  buffer.encode_string(6, this->model);
 | 
			
		||||
  buffer.encode_bool(7, this->has_deep_sleep);
 | 
			
		||||
  buffer.encode_string(8, this->project_name);
 | 
			
		||||
  buffer.encode_string(9, this->project_version);
 | 
			
		||||
}
 | 
			
		||||
void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
@@ -371,6 +413,14 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  has_deep_sleep: ");
 | 
			
		||||
  out.append(YESNO(this->has_deep_sleep));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  project_name: ");
 | 
			
		||||
  out.append("'").append(this->project_name).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  project_version: ");
 | 
			
		||||
  out.append("'").append(this->project_version).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {}
 | 
			
		||||
@@ -1507,6 +1557,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va
 | 
			
		||||
      this->force_update = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 10: {
 | 
			
		||||
      this->state_class = value.as_enum<enums::SensorStateClass>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -1561,6 +1615,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_int32(7, this->accuracy_decimals);
 | 
			
		||||
  buffer.encode_bool(8, this->force_update);
 | 
			
		||||
  buffer.encode_string(9, this->device_class);
 | 
			
		||||
  buffer.encode_enum<enums::SensorStateClass>(10, this->state_class);
 | 
			
		||||
}
 | 
			
		||||
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
@@ -1602,6 +1657,10 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  device_class: ");
 | 
			
		||||
  out.append("'").append(this->device_class).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  state_class: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::SensorStateClass>(this->state_class));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
@@ -2123,12 +2182,17 @@ bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, Proto
 | 
			
		||||
      this->entity_id = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->attribute = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_string(1, this->entity_id);
 | 
			
		||||
  buffer.encode_string(2, this->attribute);
 | 
			
		||||
}
 | 
			
		||||
void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
@@ -2136,6 +2200,10 @@ void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  entity_id: ");
 | 
			
		||||
  out.append("'").append(this->entity_id).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  attribute: ");
 | 
			
		||||
  out.append("'").append(this->attribute).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
@@ -2148,6 +2216,10 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
 | 
			
		||||
      this->state = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->attribute = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -2155,6 +2227,7 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
 | 
			
		||||
void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_string(1, this->entity_id);
 | 
			
		||||
  buffer.encode_string(2, this->state);
 | 
			
		||||
  buffer.encode_string(3, this->attribute);
 | 
			
		||||
}
 | 
			
		||||
void HomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
@@ -2166,6 +2239,10 @@ void HomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  state: ");
 | 
			
		||||
  out.append("'").append(this->state).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  attribute: ");
 | 
			
		||||
  out.append("'").append(this->attribute).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {}
 | 
			
		||||
@@ -2610,6 +2687,10 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
 | 
			
		||||
      this->supported_swing_modes.push_back(value.as_enum<enums::ClimateSwingMode>());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 16: {
 | 
			
		||||
      this->supported_presets.push_back(value.as_enum<enums::ClimatePreset>());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -2628,6 +2709,14 @@ bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDe
 | 
			
		||||
      this->unique_id = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 15: {
 | 
			
		||||
      this->supported_custom_fan_modes.push_back(value.as_string());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 17: {
 | 
			
		||||
      this->supported_custom_presets.push_back(value.as_string());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -2675,6 +2764,15 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  for (auto &it : this->supported_swing_modes) {
 | 
			
		||||
    buffer.encode_enum<enums::ClimateSwingMode>(14, it, true);
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &it : this->supported_custom_fan_modes) {
 | 
			
		||||
    buffer.encode_string(15, it, true);
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &it : this->supported_presets) {
 | 
			
		||||
    buffer.encode_enum<enums::ClimatePreset>(16, it, true);
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &it : this->supported_custom_presets) {
 | 
			
		||||
    buffer.encode_string(17, it, true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
@@ -2744,6 +2842,24 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
 | 
			
		||||
    out.append(proto_enum_to_string<enums::ClimateSwingMode>(it));
 | 
			
		||||
    out.append("\n");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const auto &it : this->supported_custom_fan_modes) {
 | 
			
		||||
    out.append("  supported_custom_fan_modes: ");
 | 
			
		||||
    out.append("'").append(it).append("'");
 | 
			
		||||
    out.append("\n");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const auto &it : this->supported_presets) {
 | 
			
		||||
    out.append("  supported_presets: ");
 | 
			
		||||
    out.append(proto_enum_to_string<enums::ClimatePreset>(it));
 | 
			
		||||
    out.append("\n");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const auto &it : this->supported_custom_presets) {
 | 
			
		||||
    out.append("  supported_custom_presets: ");
 | 
			
		||||
    out.append("'").append(it).append("'");
 | 
			
		||||
    out.append("\n");
 | 
			
		||||
  }
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
@@ -2768,6 +2884,24 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
      this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 12: {
 | 
			
		||||
      this->preset = value.as_enum<enums::ClimatePreset>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool ClimateStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 11: {
 | 
			
		||||
      this->custom_fan_mode = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 13: {
 | 
			
		||||
      this->custom_preset = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -2809,6 +2943,9 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_enum<enums::ClimateAction>(8, this->action);
 | 
			
		||||
  buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode);
 | 
			
		||||
  buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode);
 | 
			
		||||
  buffer.encode_string(11, this->custom_fan_mode);
 | 
			
		||||
  buffer.encode_enum<enums::ClimatePreset>(12, this->preset);
 | 
			
		||||
  buffer.encode_string(13, this->custom_preset);
 | 
			
		||||
}
 | 
			
		||||
void ClimateStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
@@ -2857,6 +2994,18 @@ void ClimateStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  swing_mode: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  custom_fan_mode: ");
 | 
			
		||||
  out.append("'").append(this->custom_fan_mode).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  preset: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::ClimatePreset>(this->preset));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  custom_preset: ");
 | 
			
		||||
  out.append("'").append(this->custom_preset).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
@@ -2905,6 +3054,36 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
 | 
			
		||||
      this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 16: {
 | 
			
		||||
      this->has_custom_fan_mode = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 18: {
 | 
			
		||||
      this->has_preset = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 19: {
 | 
			
		||||
      this->preset = value.as_enum<enums::ClimatePreset>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 20: {
 | 
			
		||||
      this->has_custom_preset = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool ClimateCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 17: {
 | 
			
		||||
      this->custom_fan_mode = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 21: {
 | 
			
		||||
      this->custom_preset = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -2947,6 +3126,12 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode);
 | 
			
		||||
  buffer.encode_bool(14, this->has_swing_mode);
 | 
			
		||||
  buffer.encode_enum<enums::ClimateSwingMode>(15, this->swing_mode);
 | 
			
		||||
  buffer.encode_bool(16, this->has_custom_fan_mode);
 | 
			
		||||
  buffer.encode_string(17, this->custom_fan_mode);
 | 
			
		||||
  buffer.encode_bool(18, this->has_preset);
 | 
			
		||||
  buffer.encode_enum<enums::ClimatePreset>(19, this->preset);
 | 
			
		||||
  buffer.encode_bool(20, this->has_custom_preset);
 | 
			
		||||
  buffer.encode_string(21, this->custom_preset);
 | 
			
		||||
}
 | 
			
		||||
void ClimateCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
@@ -3014,6 +3199,30 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  swing_mode: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  has_custom_fan_mode: ");
 | 
			
		||||
  out.append(YESNO(this->has_custom_fan_mode));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  custom_fan_mode: ");
 | 
			
		||||
  out.append("'").append(this->custom_fan_mode).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  has_preset: ");
 | 
			
		||||
  out.append(YESNO(this->has_preset));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  preset: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::ClimatePreset>(this->preset));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  has_custom_preset: ");
 | 
			
		||||
  out.append(YESNO(this->has_custom_preset));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  custom_preset: ");
 | 
			
		||||
  out.append("'").append(this->custom_preset).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,10 @@ enum FanDirection : uint32_t {
 | 
			
		||||
  FAN_DIRECTION_FORWARD = 0,
 | 
			
		||||
  FAN_DIRECTION_REVERSE = 1,
 | 
			
		||||
};
 | 
			
		||||
enum SensorStateClass : uint32_t {
 | 
			
		||||
  STATE_CLASS_NONE = 0,
 | 
			
		||||
  STATE_CLASS_MEASUREMENT = 1,
 | 
			
		||||
};
 | 
			
		||||
enum LogLevel : uint32_t {
 | 
			
		||||
  LOG_LEVEL_NONE = 0,
 | 
			
		||||
  LOG_LEVEL_ERROR = 1,
 | 
			
		||||
@@ -53,11 +57,12 @@ enum ServiceArgType : uint32_t {
 | 
			
		||||
};
 | 
			
		||||
enum ClimateMode : uint32_t {
 | 
			
		||||
  CLIMATE_MODE_OFF = 0,
 | 
			
		||||
  CLIMATE_MODE_AUTO = 1,
 | 
			
		||||
  CLIMATE_MODE_HEAT_COOL = 1,
 | 
			
		||||
  CLIMATE_MODE_COOL = 2,
 | 
			
		||||
  CLIMATE_MODE_HEAT = 3,
 | 
			
		||||
  CLIMATE_MODE_FAN_ONLY = 4,
 | 
			
		||||
  CLIMATE_MODE_DRY = 5,
 | 
			
		||||
  CLIMATE_MODE_AUTO = 6,
 | 
			
		||||
};
 | 
			
		||||
enum ClimateFanMode : uint32_t {
 | 
			
		||||
  CLIMATE_FAN_ON = 0,
 | 
			
		||||
@@ -84,12 +89,21 @@ enum ClimateAction : uint32_t {
 | 
			
		||||
  CLIMATE_ACTION_DRYING = 5,
 | 
			
		||||
  CLIMATE_ACTION_FAN = 6,
 | 
			
		||||
};
 | 
			
		||||
enum ClimatePreset : uint32_t {
 | 
			
		||||
  CLIMATE_PRESET_ECO = 0,
 | 
			
		||||
  CLIMATE_PRESET_AWAY = 1,
 | 
			
		||||
  CLIMATE_PRESET_BOOST = 2,
 | 
			
		||||
  CLIMATE_PRESET_COMFORT = 3,
 | 
			
		||||
  CLIMATE_PRESET_HOME = 4,
 | 
			
		||||
  CLIMATE_PRESET_SLEEP = 5,
 | 
			
		||||
  CLIMATE_PRESET_ACTIVITY = 6,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace enums
 | 
			
		||||
 | 
			
		||||
class HelloRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string client_info{};  // NOLINT
 | 
			
		||||
  std::string client_info{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -98,9 +112,9 @@ class HelloRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class HelloResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t api_version_major{0};  // NOLINT
 | 
			
		||||
  uint32_t api_version_minor{0};  // NOLINT
 | 
			
		||||
  std::string server_info{};      // NOLINT
 | 
			
		||||
  uint32_t api_version_major{0};
 | 
			
		||||
  uint32_t api_version_minor{0};
 | 
			
		||||
  std::string server_info{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -110,7 +124,7 @@ class HelloResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ConnectRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string password{};  // NOLINT
 | 
			
		||||
  std::string password{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -119,7 +133,7 @@ class ConnectRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ConnectResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool invalid_password{false};  // NOLINT
 | 
			
		||||
  bool invalid_password{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -163,13 +177,15 @@ class DeviceInfoRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class DeviceInfoResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool uses_password{false};       // NOLINT
 | 
			
		||||
  std::string name{};              // NOLINT
 | 
			
		||||
  std::string mac_address{};       // NOLINT
 | 
			
		||||
  std::string esphome_version{};   // NOLINT
 | 
			
		||||
  std::string compilation_time{};  // NOLINT
 | 
			
		||||
  std::string model{};             // NOLINT
 | 
			
		||||
  bool has_deep_sleep{false};      // NOLINT
 | 
			
		||||
  bool uses_password{false};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string mac_address{};
 | 
			
		||||
  std::string esphome_version{};
 | 
			
		||||
  std::string compilation_time{};
 | 
			
		||||
  std::string model{};
 | 
			
		||||
  bool has_deep_sleep{false};
 | 
			
		||||
  std::string project_name{};
 | 
			
		||||
  std::string project_version{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -200,12 +216,12 @@ class SubscribeStatesRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesBinarySensorResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};              // NOLINT
 | 
			
		||||
  uint32_t key{0};                      // NOLINT
 | 
			
		||||
  std::string name{};                   // NOLINT
 | 
			
		||||
  std::string unique_id{};              // NOLINT
 | 
			
		||||
  std::string device_class{};           // NOLINT
 | 
			
		||||
  bool is_status_binary_sensor{false};  // NOLINT
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  std::string device_class{};
 | 
			
		||||
  bool is_status_binary_sensor{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -216,9 +232,9 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class BinarySensorStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};            // NOLINT
 | 
			
		||||
  bool state{false};          // NOLINT
 | 
			
		||||
  bool missing_state{false};  // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  bool state{false};
 | 
			
		||||
  bool missing_state{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -228,14 +244,14 @@ class BinarySensorStateResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesCoverResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};        // NOLINT
 | 
			
		||||
  uint32_t key{0};                // NOLINT
 | 
			
		||||
  std::string name{};             // NOLINT
 | 
			
		||||
  std::string unique_id{};        // NOLINT
 | 
			
		||||
  bool assumed_state{false};      // NOLINT
 | 
			
		||||
  bool supports_position{false};  // NOLINT
 | 
			
		||||
  bool supports_tilt{false};      // NOLINT
 | 
			
		||||
  std::string device_class{};     // NOLINT
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  bool assumed_state{false};
 | 
			
		||||
  bool supports_position{false};
 | 
			
		||||
  bool supports_tilt{false};
 | 
			
		||||
  std::string device_class{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -246,11 +262,11 @@ class ListEntitiesCoverResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class CoverStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                            // NOLINT
 | 
			
		||||
  enums::LegacyCoverState legacy_state{};     // NOLINT
 | 
			
		||||
  float position{0.0f};                       // NOLINT
 | 
			
		||||
  float tilt{0.0f};                           // NOLINT
 | 
			
		||||
  enums::CoverOperation current_operation{};  // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  enums::LegacyCoverState legacy_state{};
 | 
			
		||||
  float position{0.0f};
 | 
			
		||||
  float tilt{0.0f};
 | 
			
		||||
  enums::CoverOperation current_operation{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -260,14 +276,14 @@ class CoverStateResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class CoverCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                             // NOLINT
 | 
			
		||||
  bool has_legacy_command{false};              // NOLINT
 | 
			
		||||
  enums::LegacyCoverCommand legacy_command{};  // NOLINT
 | 
			
		||||
  bool has_position{false};                    // NOLINT
 | 
			
		||||
  float position{0.0f};                        // NOLINT
 | 
			
		||||
  bool has_tilt{false};                        // NOLINT
 | 
			
		||||
  float tilt{0.0f};                            // NOLINT
 | 
			
		||||
  bool stop{false};                            // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  bool has_legacy_command{false};
 | 
			
		||||
  enums::LegacyCoverCommand legacy_command{};
 | 
			
		||||
  bool has_position{false};
 | 
			
		||||
  float position{0.0f};
 | 
			
		||||
  bool has_tilt{false};
 | 
			
		||||
  float tilt{0.0f};
 | 
			
		||||
  bool stop{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -277,14 +293,14 @@ class CoverCommandRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesFanResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};           // NOLINT
 | 
			
		||||
  uint32_t key{0};                   // NOLINT
 | 
			
		||||
  std::string name{};                // NOLINT
 | 
			
		||||
  std::string unique_id{};           // NOLINT
 | 
			
		||||
  bool supports_oscillation{false};  // NOLINT
 | 
			
		||||
  bool supports_speed{false};        // NOLINT
 | 
			
		||||
  bool supports_direction{false};    // NOLINT
 | 
			
		||||
  int32_t supported_speed_count{0};  // NOLINT
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  bool supports_oscillation{false};
 | 
			
		||||
  bool supports_speed{false};
 | 
			
		||||
  bool supports_direction{false};
 | 
			
		||||
  int32_t supported_speed_count{0};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -295,12 +311,12 @@ class ListEntitiesFanResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class FanStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                  // NOLINT
 | 
			
		||||
  bool state{false};                // NOLINT
 | 
			
		||||
  bool oscillating{false};          // NOLINT
 | 
			
		||||
  enums::FanSpeed speed{};          // NOLINT
 | 
			
		||||
  enums::FanDirection direction{};  // NOLINT
 | 
			
		||||
  int32_t speed_level{0};           // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  bool state{false};
 | 
			
		||||
  bool oscillating{false};
 | 
			
		||||
  enums::FanSpeed speed{};
 | 
			
		||||
  enums::FanDirection direction{};
 | 
			
		||||
  int32_t speed_level{0};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -310,17 +326,17 @@ class FanStateResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class FanCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                  // NOLINT
 | 
			
		||||
  bool has_state{false};            // NOLINT
 | 
			
		||||
  bool state{false};                // NOLINT
 | 
			
		||||
  bool has_speed{false};            // NOLINT
 | 
			
		||||
  enums::FanSpeed speed{};          // NOLINT
 | 
			
		||||
  bool has_oscillating{false};      // NOLINT
 | 
			
		||||
  bool oscillating{false};          // NOLINT
 | 
			
		||||
  bool has_direction{false};        // NOLINT
 | 
			
		||||
  enums::FanDirection direction{};  // NOLINT
 | 
			
		||||
  bool has_speed_level{false};      // NOLINT
 | 
			
		||||
  int32_t speed_level{0};           // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  bool has_state{false};
 | 
			
		||||
  bool state{false};
 | 
			
		||||
  bool has_speed{false};
 | 
			
		||||
  enums::FanSpeed speed{};
 | 
			
		||||
  bool has_oscillating{false};
 | 
			
		||||
  bool oscillating{false};
 | 
			
		||||
  bool has_direction{false};
 | 
			
		||||
  enums::FanDirection direction{};
 | 
			
		||||
  bool has_speed_level{false};
 | 
			
		||||
  int32_t speed_level{0};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -330,17 +346,17 @@ class FanCommandRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesLightResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};                 // NOLINT
 | 
			
		||||
  uint32_t key{0};                         // NOLINT
 | 
			
		||||
  std::string name{};                      // NOLINT
 | 
			
		||||
  std::string unique_id{};                 // NOLINT
 | 
			
		||||
  bool supports_brightness{false};         // NOLINT
 | 
			
		||||
  bool supports_rgb{false};                // NOLINT
 | 
			
		||||
  bool supports_white_value{false};        // NOLINT
 | 
			
		||||
  bool supports_color_temperature{false};  // NOLINT
 | 
			
		||||
  float min_mireds{0.0f};                  // NOLINT
 | 
			
		||||
  float max_mireds{0.0f};                  // NOLINT
 | 
			
		||||
  std::vector<std::string> effects{};      // NOLINT
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  bool supports_brightness{false};
 | 
			
		||||
  bool supports_rgb{false};
 | 
			
		||||
  bool supports_white_value{false};
 | 
			
		||||
  bool supports_color_temperature{false};
 | 
			
		||||
  float min_mireds{0.0f};
 | 
			
		||||
  float max_mireds{0.0f};
 | 
			
		||||
  std::vector<std::string> effects{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -351,15 +367,15 @@ class ListEntitiesLightResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class LightStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                // NOLINT
 | 
			
		||||
  bool state{false};              // NOLINT
 | 
			
		||||
  float brightness{0.0f};         // NOLINT
 | 
			
		||||
  float red{0.0f};                // NOLINT
 | 
			
		||||
  float green{0.0f};              // NOLINT
 | 
			
		||||
  float blue{0.0f};               // NOLINT
 | 
			
		||||
  float white{0.0f};              // NOLINT
 | 
			
		||||
  float color_temperature{0.0f};  // NOLINT
 | 
			
		||||
  std::string effect{};           // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  bool state{false};
 | 
			
		||||
  float brightness{0.0f};
 | 
			
		||||
  float red{0.0f};
 | 
			
		||||
  float green{0.0f};
 | 
			
		||||
  float blue{0.0f};
 | 
			
		||||
  float white{0.0f};
 | 
			
		||||
  float color_temperature{0.0f};
 | 
			
		||||
  std::string effect{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -370,25 +386,25 @@ class LightStateResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class LightCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                    // NOLINT
 | 
			
		||||
  bool has_state{false};              // NOLINT
 | 
			
		||||
  bool state{false};                  // NOLINT
 | 
			
		||||
  bool has_brightness{false};         // NOLINT
 | 
			
		||||
  float brightness{0.0f};             // NOLINT
 | 
			
		||||
  bool has_rgb{false};                // NOLINT
 | 
			
		||||
  float red{0.0f};                    // NOLINT
 | 
			
		||||
  float green{0.0f};                  // NOLINT
 | 
			
		||||
  float blue{0.0f};                   // NOLINT
 | 
			
		||||
  bool has_white{false};              // NOLINT
 | 
			
		||||
  float white{0.0f};                  // NOLINT
 | 
			
		||||
  bool has_color_temperature{false};  // NOLINT
 | 
			
		||||
  float color_temperature{0.0f};      // NOLINT
 | 
			
		||||
  bool has_transition_length{false};  // NOLINT
 | 
			
		||||
  uint32_t transition_length{0};      // NOLINT
 | 
			
		||||
  bool has_flash_length{false};       // NOLINT
 | 
			
		||||
  uint32_t flash_length{0};           // NOLINT
 | 
			
		||||
  bool has_effect{false};             // NOLINT
 | 
			
		||||
  std::string effect{};               // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  bool has_state{false};
 | 
			
		||||
  bool state{false};
 | 
			
		||||
  bool has_brightness{false};
 | 
			
		||||
  float brightness{0.0f};
 | 
			
		||||
  bool has_rgb{false};
 | 
			
		||||
  float red{0.0f};
 | 
			
		||||
  float green{0.0f};
 | 
			
		||||
  float blue{0.0f};
 | 
			
		||||
  bool has_white{false};
 | 
			
		||||
  float white{0.0f};
 | 
			
		||||
  bool has_color_temperature{false};
 | 
			
		||||
  float color_temperature{0.0f};
 | 
			
		||||
  bool has_transition_length{false};
 | 
			
		||||
  uint32_t transition_length{0};
 | 
			
		||||
  bool has_flash_length{false};
 | 
			
		||||
  uint32_t flash_length{0};
 | 
			
		||||
  bool has_effect{false};
 | 
			
		||||
  std::string effect{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -399,15 +415,16 @@ class LightCommandRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesSensorResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};            // NOLINT
 | 
			
		||||
  uint32_t key{0};                    // NOLINT
 | 
			
		||||
  std::string name{};                 // NOLINT
 | 
			
		||||
  std::string unique_id{};            // NOLINT
 | 
			
		||||
  std::string icon{};                 // NOLINT
 | 
			
		||||
  std::string unit_of_measurement{};  // NOLINT
 | 
			
		||||
  int32_t accuracy_decimals{0};       // NOLINT
 | 
			
		||||
  bool force_update{false};           // NOLINT
 | 
			
		||||
  std::string device_class{};         // NOLINT
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  std::string icon{};
 | 
			
		||||
  std::string unit_of_measurement{};
 | 
			
		||||
  int32_t accuracy_decimals{0};
 | 
			
		||||
  bool force_update{false};
 | 
			
		||||
  std::string device_class{};
 | 
			
		||||
  enums::SensorStateClass state_class{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -418,9 +435,9 @@ class ListEntitiesSensorResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class SensorStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};            // NOLINT
 | 
			
		||||
  float state{0.0f};          // NOLINT
 | 
			
		||||
  bool missing_state{false};  // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  float state{0.0f};
 | 
			
		||||
  bool missing_state{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -430,12 +447,12 @@ class SensorStateResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesSwitchResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};    // NOLINT
 | 
			
		||||
  uint32_t key{0};            // NOLINT
 | 
			
		||||
  std::string name{};         // NOLINT
 | 
			
		||||
  std::string unique_id{};    // NOLINT
 | 
			
		||||
  std::string icon{};         // NOLINT
 | 
			
		||||
  bool assumed_state{false};  // NOLINT
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  std::string icon{};
 | 
			
		||||
  bool assumed_state{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -446,8 +463,8 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class SwitchStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};    // NOLINT
 | 
			
		||||
  bool state{false};  // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  bool state{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -457,8 +474,8 @@ class SwitchStateResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class SwitchCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};    // NOLINT
 | 
			
		||||
  bool state{false};  // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  bool state{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -468,11 +485,11 @@ class SwitchCommandRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesTextSensorResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};  // NOLINT
 | 
			
		||||
  uint32_t key{0};          // NOLINT
 | 
			
		||||
  std::string name{};       // NOLINT
 | 
			
		||||
  std::string unique_id{};  // NOLINT
 | 
			
		||||
  std::string icon{};       // NOLINT
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  std::string icon{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -482,9 +499,9 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class TextSensorStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};            // NOLINT
 | 
			
		||||
  std::string state{};        // NOLINT
 | 
			
		||||
  bool missing_state{false};  // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string state{};
 | 
			
		||||
  bool missing_state{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -495,8 +512,8 @@ class TextSensorStateResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class SubscribeLogsRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  enums::LogLevel level{};  // NOLINT
 | 
			
		||||
  bool dump_config{false};  // NOLINT
 | 
			
		||||
  enums::LogLevel level{};
 | 
			
		||||
  bool dump_config{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -505,10 +522,10 @@ class SubscribeLogsRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class SubscribeLogsResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  enums::LogLevel level{};  // NOLINT
 | 
			
		||||
  std::string tag{};        // NOLINT
 | 
			
		||||
  std::string message{};    // NOLINT
 | 
			
		||||
  bool send_failed{false};  // NOLINT
 | 
			
		||||
  enums::LogLevel level{};
 | 
			
		||||
  std::string tag{};
 | 
			
		||||
  std::string message{};
 | 
			
		||||
  bool send_failed{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -525,8 +542,8 @@ class SubscribeHomeassistantServicesRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class HomeassistantServiceMap : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string key{};    // NOLINT
 | 
			
		||||
  std::string value{};  // NOLINT
 | 
			
		||||
  std::string key{};
 | 
			
		||||
  std::string value{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -535,11 +552,11 @@ class HomeassistantServiceMap : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class HomeassistantServiceResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string service{};                                 // NOLINT
 | 
			
		||||
  std::vector<HomeassistantServiceMap> data{};           // NOLINT
 | 
			
		||||
  std::vector<HomeassistantServiceMap> data_template{};  // NOLINT
 | 
			
		||||
  std::vector<HomeassistantServiceMap> variables{};      // NOLINT
 | 
			
		||||
  bool is_event{false};                                  // NOLINT
 | 
			
		||||
  std::string service{};
 | 
			
		||||
  std::vector<HomeassistantServiceMap> data{};
 | 
			
		||||
  std::vector<HomeassistantServiceMap> data_template{};
 | 
			
		||||
  std::vector<HomeassistantServiceMap> variables{};
 | 
			
		||||
  bool is_event{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -556,7 +573,8 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class SubscribeHomeAssistantStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string entity_id{};  // NOLINT
 | 
			
		||||
  std::string entity_id{};
 | 
			
		||||
  std::string attribute{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -565,8 +583,9 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class HomeAssistantStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string entity_id{};  // NOLINT
 | 
			
		||||
  std::string state{};      // NOLINT
 | 
			
		||||
  std::string entity_id{};
 | 
			
		||||
  std::string state{};
 | 
			
		||||
  std::string attribute{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -582,7 +601,7 @@ class GetTimeRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class GetTimeResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t epoch_seconds{0};  // NOLINT
 | 
			
		||||
  uint32_t epoch_seconds{0};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -591,8 +610,8 @@ class GetTimeResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesServicesArgument : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string name{};            // NOLINT
 | 
			
		||||
  enums::ServiceArgType type{};  // NOLINT
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  enums::ServiceArgType type{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -602,9 +621,9 @@ class ListEntitiesServicesArgument : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesServicesResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string name{};                                // NOLINT
 | 
			
		||||
  uint32_t key{0};                                   // NOLINT
 | 
			
		||||
  std::vector<ListEntitiesServicesArgument> args{};  // NOLINT
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::vector<ListEntitiesServicesArgument> args{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -614,15 +633,15 @@ class ListEntitiesServicesResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ExecuteServiceArgument : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool bool_{false};                        // NOLINT
 | 
			
		||||
  int32_t legacy_int{0};                    // NOLINT
 | 
			
		||||
  float float_{0.0f};                       // NOLINT
 | 
			
		||||
  std::string string_{};                    // NOLINT
 | 
			
		||||
  int32_t int_{0};                          // NOLINT
 | 
			
		||||
  std::vector<bool> bool_array{};           // NOLINT
 | 
			
		||||
  std::vector<int32_t> int_array{};         // NOLINT
 | 
			
		||||
  std::vector<float> float_array{};         // NOLINT
 | 
			
		||||
  std::vector<std::string> string_array{};  // NOLINT
 | 
			
		||||
  bool bool_{false};
 | 
			
		||||
  int32_t legacy_int{0};
 | 
			
		||||
  float float_{0.0f};
 | 
			
		||||
  std::string string_{};
 | 
			
		||||
  int32_t int_{0};
 | 
			
		||||
  std::vector<bool> bool_array{};
 | 
			
		||||
  std::vector<int32_t> int_array{};
 | 
			
		||||
  std::vector<float> float_array{};
 | 
			
		||||
  std::vector<std::string> string_array{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -633,8 +652,8 @@ class ExecuteServiceArgument : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ExecuteServiceRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                             // NOLINT
 | 
			
		||||
  std::vector<ExecuteServiceArgument> args{};  // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::vector<ExecuteServiceArgument> args{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -644,10 +663,10 @@ class ExecuteServiceRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesCameraResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};  // NOLINT
 | 
			
		||||
  uint32_t key{0};          // NOLINT
 | 
			
		||||
  std::string name{};       // NOLINT
 | 
			
		||||
  std::string unique_id{};  // NOLINT
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -657,9 +676,9 @@ class ListEntitiesCameraResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class CameraImageResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};     // NOLINT
 | 
			
		||||
  std::string data{};  // NOLINT
 | 
			
		||||
  bool done{false};    // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string data{};
 | 
			
		||||
  bool done{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -670,8 +689,8 @@ class CameraImageResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class CameraImageRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool single{false};  // NOLINT
 | 
			
		||||
  bool stream{false};  // NOLINT
 | 
			
		||||
  bool single{false};
 | 
			
		||||
  bool stream{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -680,20 +699,23 @@ class CameraImageRequest : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesClimateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};                                       // NOLINT
 | 
			
		||||
  uint32_t key{0};                                               // NOLINT
 | 
			
		||||
  std::string name{};                                            // NOLINT
 | 
			
		||||
  std::string unique_id{};                                       // NOLINT
 | 
			
		||||
  bool supports_current_temperature{false};                      // NOLINT
 | 
			
		||||
  bool supports_two_point_target_temperature{false};             // NOLINT
 | 
			
		||||
  std::vector<enums::ClimateMode> supported_modes{};             // NOLINT
 | 
			
		||||
  float visual_min_temperature{0.0f};                            // NOLINT
 | 
			
		||||
  float visual_max_temperature{0.0f};                            // NOLINT
 | 
			
		||||
  float visual_temperature_step{0.0f};                           // NOLINT
 | 
			
		||||
  bool supports_away{false};                                     // NOLINT
 | 
			
		||||
  bool supports_action{false};                                   // NOLINT
 | 
			
		||||
  std::vector<enums::ClimateFanMode> supported_fan_modes{};      // NOLINT
 | 
			
		||||
  std::vector<enums::ClimateSwingMode> supported_swing_modes{};  // NOLINT
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  bool supports_current_temperature{false};
 | 
			
		||||
  bool supports_two_point_target_temperature{false};
 | 
			
		||||
  std::vector<enums::ClimateMode> supported_modes{};
 | 
			
		||||
  float visual_min_temperature{0.0f};
 | 
			
		||||
  float visual_max_temperature{0.0f};
 | 
			
		||||
  float visual_temperature_step{0.0f};
 | 
			
		||||
  bool supports_away{false};
 | 
			
		||||
  bool supports_action{false};
 | 
			
		||||
  std::vector<enums::ClimateFanMode> supported_fan_modes{};
 | 
			
		||||
  std::vector<enums::ClimateSwingMode> supported_swing_modes{};
 | 
			
		||||
  std::vector<std::string> supported_custom_fan_modes{};
 | 
			
		||||
  std::vector<enums::ClimatePreset> supported_presets{};
 | 
			
		||||
  std::vector<std::string> supported_custom_presets{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
@@ -704,45 +726,56 @@ class ListEntitiesClimateResponse : public ProtoMessage {
 | 
			
		||||
};
 | 
			
		||||
class ClimateStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                       // NOLINT
 | 
			
		||||
  enums::ClimateMode mode{};             // NOLINT
 | 
			
		||||
  float current_temperature{0.0f};       // NOLINT
 | 
			
		||||
  float target_temperature{0.0f};        // NOLINT
 | 
			
		||||
  float target_temperature_low{0.0f};    // NOLINT
 | 
			
		||||
  float target_temperature_high{0.0f};   // NOLINT
 | 
			
		||||
  bool away{false};                      // NOLINT
 | 
			
		||||
  enums::ClimateAction action{};         // NOLINT
 | 
			
		||||
  enums::ClimateFanMode fan_mode{};      // NOLINT
 | 
			
		||||
  enums::ClimateSwingMode swing_mode{};  // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  enums::ClimateMode mode{};
 | 
			
		||||
  float current_temperature{0.0f};
 | 
			
		||||
  float target_temperature{0.0f};
 | 
			
		||||
  float target_temperature_low{0.0f};
 | 
			
		||||
  float target_temperature_high{0.0f};
 | 
			
		||||
  bool away{false};
 | 
			
		||||
  enums::ClimateAction action{};
 | 
			
		||||
  enums::ClimateFanMode fan_mode{};
 | 
			
		||||
  enums::ClimateSwingMode swing_mode{};
 | 
			
		||||
  std::string custom_fan_mode{};
 | 
			
		||||
  enums::ClimatePreset preset{};
 | 
			
		||||
  std::string custom_preset{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 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 ClimateCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                          // NOLINT
 | 
			
		||||
  bool has_mode{false};                     // NOLINT
 | 
			
		||||
  enums::ClimateMode mode{};                // NOLINT
 | 
			
		||||
  bool has_target_temperature{false};       // NOLINT
 | 
			
		||||
  float target_temperature{0.0f};           // NOLINT
 | 
			
		||||
  bool has_target_temperature_low{false};   // NOLINT
 | 
			
		||||
  float target_temperature_low{0.0f};       // NOLINT
 | 
			
		||||
  bool has_target_temperature_high{false};  // NOLINT
 | 
			
		||||
  float target_temperature_high{0.0f};      // NOLINT
 | 
			
		||||
  bool has_away{false};                     // NOLINT
 | 
			
		||||
  bool away{false};                         // NOLINT
 | 
			
		||||
  bool has_fan_mode{false};                 // NOLINT
 | 
			
		||||
  enums::ClimateFanMode fan_mode{};         // NOLINT
 | 
			
		||||
  bool has_swing_mode{false};               // NOLINT
 | 
			
		||||
  enums::ClimateSwingMode swing_mode{};     // NOLINT
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  bool has_mode{false};
 | 
			
		||||
  enums::ClimateMode mode{};
 | 
			
		||||
  bool has_target_temperature{false};
 | 
			
		||||
  float target_temperature{0.0f};
 | 
			
		||||
  bool has_target_temperature_low{false};
 | 
			
		||||
  float target_temperature_low{0.0f};
 | 
			
		||||
  bool has_target_temperature_high{false};
 | 
			
		||||
  float target_temperature_high{0.0f};
 | 
			
		||||
  bool has_away{false};
 | 
			
		||||
  bool away{false};
 | 
			
		||||
  bool has_fan_mode{false};
 | 
			
		||||
  enums::ClimateFanMode fan_mode{};
 | 
			
		||||
  bool has_swing_mode{false};
 | 
			
		||||
  enums::ClimateSwingMode swing_mode{};
 | 
			
		||||
  bool has_custom_fan_mode{false};
 | 
			
		||||
  std::string custom_fan_mode{};
 | 
			
		||||
  bool has_preset{false};
 | 
			
		||||
  enums::ClimatePreset preset{};
 | 
			
		||||
  bool has_custom_preset{false};
 | 
			
		||||
  std::string custom_preset{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -208,9 +208,11 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
APIServer::APIServer() { global_api_server = this; }
 | 
			
		||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f) {
 | 
			
		||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
 | 
			
		||||
                                               std::function<void(std::string)> f) {
 | 
			
		||||
  this->state_subs_.push_back(HomeAssistantStateSubscription{
 | 
			
		||||
      .entity_id = std::move(entity_id),
 | 
			
		||||
      .attribute = std::move(attribute),
 | 
			
		||||
      .callback = std::move(f),
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -71,10 +71,12 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
 | 
			
		||||
  struct HomeAssistantStateSubscription {
 | 
			
		||||
    std::string entity_id;
 | 
			
		||||
    optional<std::string> attribute;
 | 
			
		||||
    std::function<void(std::string)> callback;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  void subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f);
 | 
			
		||||
  void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
 | 
			
		||||
                                      std::function<void(std::string)> f);
 | 
			
		||||
  const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
 | 
			
		||||
  const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -76,13 +76,13 @@ class CustomAPIDevice {
 | 
			
		||||
    global_api_server->register_user_service(service);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Subscribe to the state of an entity from Home Assistant.
 | 
			
		||||
  /** Subscribe to the state (or attribute state) of an entity from Home Assistant.
 | 
			
		||||
   *
 | 
			
		||||
   * Usage:
 | 
			
		||||
   *
 | 
			
		||||
   * ```cpp
 | 
			
		||||
   * void setup() override {
 | 
			
		||||
   *   subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
 | 
			
		||||
   *   subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "climate.kitchen", "current_temperature");
 | 
			
		||||
   * }
 | 
			
		||||
   *
 | 
			
		||||
   * void on_state_changed(std::string state) {
 | 
			
		||||
@@ -93,17 +93,19 @@ class CustomAPIDevice {
 | 
			
		||||
   * @tparam T The class type creating the service, automatically deduced from the function pointer.
 | 
			
		||||
   * @param callback The member function to call when the entity state changes.
 | 
			
		||||
   * @param entity_id The entity_id to track.
 | 
			
		||||
   * @param attribute The entity state attribute to track.
 | 
			
		||||
   */
 | 
			
		||||
  template<typename T>
 | 
			
		||||
  void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id) {
 | 
			
		||||
  void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id,
 | 
			
		||||
                                     const std::string &attribute = "") {
 | 
			
		||||
    auto f = std::bind(callback, (T *) this, std::placeholders::_1);
 | 
			
		||||
    global_api_server->subscribe_home_assistant_state(entity_id, f);
 | 
			
		||||
    global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Subscribe to the state of an entity from Home Assistant.
 | 
			
		||||
  /** Subscribe to the state (or attribute state) of an entity from Home Assistant.
 | 
			
		||||
   *
 | 
			
		||||
   * Usage:
 | 
			
		||||
   *
 | 
			
		||||
   *å
 | 
			
		||||
   * ```cpp
 | 
			
		||||
   * void setup() override {
 | 
			
		||||
   *   subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
 | 
			
		||||
@@ -117,11 +119,13 @@ class CustomAPIDevice {
 | 
			
		||||
   * @tparam T The class type creating the service, automatically deduced from the function pointer.
 | 
			
		||||
   * @param callback The member function to call when the entity state changes.
 | 
			
		||||
   * @param entity_id The entity_id to track.
 | 
			
		||||
   * @param attribute The entity state attribute to track.
 | 
			
		||||
   */
 | 
			
		||||
  template<typename T>
 | 
			
		||||
  void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id) {
 | 
			
		||||
  void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id,
 | 
			
		||||
                                     const std::string &attribute = "") {
 | 
			
		||||
    auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
 | 
			
		||||
    global_api_server->subscribe_home_assistant_state(entity_id, f);
 | 
			
		||||
    global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Call a Home Assistant service from ESPHome.
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_DIV_RATIO,
 | 
			
		||||
    CONF_CAPACITANCE,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import coroutine
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["sensor", "binary_sensor"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
@@ -40,11 +39,10 @@ AS3935_SCHEMA = cv.Schema(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def setup_as3935(var, config):
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
async def setup_as3935(var, config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    irq_pin = yield cg.gpio_pin_expression(config[CONF_IRQ_PIN])
 | 
			
		||||
    irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
 | 
			
		||||
    cg.add(var.set_irq_pin(irq_pin))
 | 
			
		||||
    cg.add(var.set_indoor(config[CONF_INDOOR]))
 | 
			
		||||
    cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = yield cg.get_variable(config[CONF_AS3935_ID])
 | 
			
		||||
    var = yield binary_sensor.new_binary_sensor(config)
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    hub = await cg.get_variable(config[CONF_AS3935_ID])
 | 
			
		||||
    var = await binary_sensor.new_binary_sensor(config)
 | 
			
		||||
    cg.add(hub.set_thunder_alert_binary_sensor(var))
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_DISTANCE,
 | 
			
		||||
    CONF_LIGHTNING_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
    STATE_CLASS_NONE,
 | 
			
		||||
    UNIT_KILOMETER,
 | 
			
		||||
    UNIT_EMPTY,
 | 
			
		||||
    ICON_SIGNAL_DISTANCE_VARIANT,
 | 
			
		||||
@@ -18,24 +19,28 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
 | 
			
		||||
        cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
 | 
			
		||||
            UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1, DEVICE_CLASS_EMPTY
 | 
			
		||||
            UNIT_KILOMETER,
 | 
			
		||||
            ICON_SIGNAL_DISTANCE_VARIANT,
 | 
			
		||||
            1,
 | 
			
		||||
            DEVICE_CLASS_EMPTY,
 | 
			
		||||
            STATE_CLASS_NONE,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
 | 
			
		||||
            UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY
 | 
			
		||||
            UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = yield cg.get_variable(config[CONF_AS3935_ID])
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    hub = await cg.get_variable(config[CONF_AS3935_ID])
 | 
			
		||||
 | 
			
		||||
    if CONF_DISTANCE in config:
 | 
			
		||||
        conf = config[CONF_DISTANCE]
 | 
			
		||||
        distance_sensor = yield sensor.new_sensor(conf)
 | 
			
		||||
        distance_sensor = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(hub.set_distance_sensor(distance_sensor))
 | 
			
		||||
 | 
			
		||||
    if CONF_LIGHTNING_ENERGY in config:
 | 
			
		||||
        conf = config[CONF_LIGHTNING_ENERGY]
 | 
			
		||||
        lightning_energy_sensor = yield sensor.new_sensor(conf)
 | 
			
		||||
        lightning_energy_sensor = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(hub.set_energy_sensor(lightning_energy_sensor))
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield as3935.setup_as3935(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await as3935.setup_as3935(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield as3935.setup_as3935(var, config)
 | 
			
		||||
    yield spi.register_spi_device(var, config)
 | 
			
		||||
    await as3935.setup_as3935(var, config)
 | 
			
		||||
    await spi.register_spi_device(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ void SPIAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits,
 | 
			
		||||
uint8_t SPIAS3935Component::read_register(uint8_t reg) {
 | 
			
		||||
  uint8_t value = 0;
 | 
			
		||||
  this->enable();
 | 
			
		||||
  this->write_byte(reg |= SPI_READ_M);
 | 
			
		||||
  this->write_byte(reg | SPI_READ_M);
 | 
			
		||||
  value = this->read_byte();
 | 
			
		||||
  // According to datsheet, the chip select must be written HIGH, LOW, HIGH
 | 
			
		||||
  // to correctly end the READ command.
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ CODEOWNERS = ["@OttoWinter"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(200.0)
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    if CORE.is_esp32:
 | 
			
		||||
        # https://github.com/esphome/AsyncTCP/blob/master/library.json
 | 
			
		||||
        cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_PERCENT,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
@@ -33,16 +34,28 @@ CONFIG_SCHEMA = (
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(ATCMiThermometer),
 | 
			
		||||
            cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
 | 
			
		||||
                UNIT_CELSIUS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
			
		||||
                UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY
 | 
			
		||||
                UNIT_PERCENT,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                0,
 | 
			
		||||
                DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
 | 
			
		||||
                UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY
 | 
			
		||||
                UNIT_PERCENT,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                0,
 | 
			
		||||
                DEVICE_CLASS_BATTERY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE
 | 
			
		||||
                UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
@@ -51,22 +64,22 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
        cg.add(var.set_temperature(sens))
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_HUMIDITY])
 | 
			
		||||
        cg.add(var.set_humidity(sens))
 | 
			
		||||
    if CONF_BATTERY_LEVEL in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
 | 
			
		||||
        cg.add(var.set_battery_level(sens))
 | 
			
		||||
    if CONF_BATTERY_VOLTAGE in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
 | 
			
		||||
        cg.add(var.set_battery_voltage(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, spi
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_REACTIVE_POWER,
 | 
			
		||||
    CONF_VOLTAGE,
 | 
			
		||||
    CONF_CURRENT,
 | 
			
		||||
    CONF_POWER,
 | 
			
		||||
@@ -20,6 +21,7 @@ from esphome.const import (
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    ICON_LIGHTBULB,
 | 
			
		||||
    ICON_CURRENT_AC,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_HERTZ,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    UNIT_AMPERE,
 | 
			
		||||
@@ -34,7 +36,6 @@ CONF_PHASE_A = "phase_a"
 | 
			
		||||
CONF_PHASE_B = "phase_b"
 | 
			
		||||
CONF_PHASE_C = "phase_c"
 | 
			
		||||
 | 
			
		||||
CONF_REACTIVE_POWER = "reactive_power"
 | 
			
		||||
CONF_LINE_FREQUENCY = "line_frequency"
 | 
			
		||||
CONF_CHIP_TEMPERATURE = "chip_temperature"
 | 
			
		||||
CONF_GAIN_PGA = "gain_pga"
 | 
			
		||||
@@ -63,25 +64,37 @@ ATM90E32Component = atm90e32_ns.class_(
 | 
			
		||||
ATM90E32_PHASE_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
 | 
			
		||||
            UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE
 | 
			
		||||
            UNIT_VOLT,
 | 
			
		||||
            ICON_EMPTY,
 | 
			
		||||
            2,
 | 
			
		||||
            DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
            STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_CURRENT): sensor.sensor_schema(
 | 
			
		||||
            UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
 | 
			
		||||
            UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_POWER): sensor.sensor_schema(
 | 
			
		||||
            UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER
 | 
			
		||||
            UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
 | 
			
		||||
            UNIT_VOLT_AMPS_REACTIVE, ICON_LIGHTBULB, 2, DEVICE_CLASS_EMPTY
 | 
			
		||||
            UNIT_VOLT_AMPS_REACTIVE,
 | 
			
		||||
            ICON_LIGHTBULB,
 | 
			
		||||
            2,
 | 
			
		||||
            DEVICE_CLASS_EMPTY,
 | 
			
		||||
            STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
 | 
			
		||||
            UNIT_EMPTY, ICON_EMPTY, 2, DEVICE_CLASS_POWER_FACTOR
 | 
			
		||||
            UNIT_EMPTY,
 | 
			
		||||
            ICON_EMPTY,
 | 
			
		||||
            2,
 | 
			
		||||
            DEVICE_CLASS_POWER_FACTOR,
 | 
			
		||||
            STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema(
 | 
			
		||||
            UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY
 | 
			
		||||
            UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema(
 | 
			
		||||
            UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY
 | 
			
		||||
            UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
 | 
			
		||||
        cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,
 | 
			
		||||
@@ -96,10 +109,18 @@ CONFIG_SCHEMA = (
 | 
			
		||||
            cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA,
 | 
			
		||||
            cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA,
 | 
			
		||||
            cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
 | 
			
		||||
                UNIT_HERTZ, ICON_CURRENT_AC, 1, DEVICE_CLASS_EMPTY
 | 
			
		||||
                UNIT_HERTZ,
 | 
			
		||||
                ICON_CURRENT_AC,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_EMPTY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
 | 
			
		||||
                UNIT_CELSIUS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
 | 
			
		||||
            cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum(
 | 
			
		||||
@@ -113,10 +134,10 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield spi.register_spi_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await spi.register_spi_device(var, config)
 | 
			
		||||
 | 
			
		||||
    for i, phase in enumerate([CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]):
 | 
			
		||||
        if phase not in config:
 | 
			
		||||
@@ -125,31 +146,31 @@ def to_code(config):
 | 
			
		||||
        cg.add(var.set_volt_gain(i, conf[CONF_GAIN_VOLTAGE]))
 | 
			
		||||
        cg.add(var.set_ct_gain(i, conf[CONF_GAIN_CT]))
 | 
			
		||||
        if CONF_VOLTAGE in conf:
 | 
			
		||||
            sens = yield sensor.new_sensor(conf[CONF_VOLTAGE])
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_VOLTAGE])
 | 
			
		||||
            cg.add(var.set_voltage_sensor(i, sens))
 | 
			
		||||
        if CONF_CURRENT in conf:
 | 
			
		||||
            sens = yield sensor.new_sensor(conf[CONF_CURRENT])
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_CURRENT])
 | 
			
		||||
            cg.add(var.set_current_sensor(i, sens))
 | 
			
		||||
        if CONF_POWER in conf:
 | 
			
		||||
            sens = yield sensor.new_sensor(conf[CONF_POWER])
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_POWER])
 | 
			
		||||
            cg.add(var.set_power_sensor(i, sens))
 | 
			
		||||
        if CONF_REACTIVE_POWER in conf:
 | 
			
		||||
            sens = yield sensor.new_sensor(conf[CONF_REACTIVE_POWER])
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_REACTIVE_POWER])
 | 
			
		||||
            cg.add(var.set_reactive_power_sensor(i, sens))
 | 
			
		||||
        if CONF_POWER_FACTOR in conf:
 | 
			
		||||
            sens = yield sensor.new_sensor(conf[CONF_POWER_FACTOR])
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_POWER_FACTOR])
 | 
			
		||||
            cg.add(var.set_power_factor_sensor(i, sens))
 | 
			
		||||
        if CONF_FORWARD_ACTIVE_ENERGY in conf:
 | 
			
		||||
            sens = yield sensor.new_sensor(conf[CONF_FORWARD_ACTIVE_ENERGY])
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_FORWARD_ACTIVE_ENERGY])
 | 
			
		||||
            cg.add(var.set_forward_active_energy_sensor(i, sens))
 | 
			
		||||
        if CONF_REVERSE_ACTIVE_ENERGY in conf:
 | 
			
		||||
            sens = yield sensor.new_sensor(conf[CONF_REVERSE_ACTIVE_ENERGY])
 | 
			
		||||
            sens = await sensor.new_sensor(conf[CONF_REVERSE_ACTIVE_ENERGY])
 | 
			
		||||
            cg.add(var.set_reverse_active_energy_sensor(i, sens))
 | 
			
		||||
    if CONF_FREQUENCY in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_FREQUENCY])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_FREQUENCY])
 | 
			
		||||
        cg.add(var.set_freq_sensor(sens))
 | 
			
		||||
    if CONF_CHIP_TEMPERATURE in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_CHIP_TEMPERATURE])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_CHIP_TEMPERATURE])
 | 
			
		||||
        cg.add(var.set_chip_temperature_sensor(sens))
 | 
			
		||||
    cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
 | 
			
		||||
    cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_PERCENT,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
@@ -32,16 +33,28 @@ CONFIG_SCHEMA = (
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BParasite),
 | 
			
		||||
            cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
 | 
			
		||||
                UNIT_CELSIUS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
			
		||||
                UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
 | 
			
		||||
                UNIT_PERCENT,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE
 | 
			
		||||
                UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_MOISTURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
 | 
			
		||||
                UNIT_PERCENT,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
@@ -50,10 +63,10 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
 | 
			
		||||
 | 
			
		||||
@@ -64,5 +77,5 @@ def to_code(config):
 | 
			
		||||
        (CONF_MOISTURE, var.set_soil_moisture),
 | 
			
		||||
    ]:
 | 
			
		||||
        if config_key in config:
 | 
			
		||||
            sens = yield sensor.new_sensor(config[config_key])
 | 
			
		||||
            sens = await sensor.new_sensor(config[config_key])
 | 
			
		||||
            cg.add(setter(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -39,12 +39,12 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield climate.register_climate(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await climate.register_climate(var, config)
 | 
			
		||||
 | 
			
		||||
    sens = yield cg.get_variable(config[CONF_SENSOR])
 | 
			
		||||
    sens = await cg.get_variable(config[CONF_SENSOR])
 | 
			
		||||
    cg.add(var.set_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    normal_config = BangBangClimateTargetTempConfig(
 | 
			
		||||
@@ -53,17 +53,17 @@ def to_code(config):
 | 
			
		||||
    )
 | 
			
		||||
    cg.add(var.set_normal_config(normal_config))
 | 
			
		||||
 | 
			
		||||
    yield automation.build_automation(
 | 
			
		||||
    await automation.build_automation(
 | 
			
		||||
        var.get_idle_trigger(), [], config[CONF_IDLE_ACTION]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if CONF_COOL_ACTION in config:
 | 
			
		||||
        yield automation.build_automation(
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_cool_trigger(), [], config[CONF_COOL_ACTION]
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_supports_cool(True))
 | 
			
		||||
    if CONF_HEAT_ACTION in config:
 | 
			
		||||
        yield automation.build_automation(
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_heat_trigger(), [], config[CONF_HEAT_ACTION]
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_supports_heat(True))
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_RESOLUTION,
 | 
			
		||||
    DEVICE_CLASS_ILLUMINANCE,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_LUX,
 | 
			
		||||
    CONF_MEASUREMENT_DURATION,
 | 
			
		||||
)
 | 
			
		||||
@@ -26,7 +27,9 @@ BH1750Sensor = bh1750_ns.class_(
 | 
			
		||||
 | 
			
		||||
CONF_MEASUREMENT_TIME = "measurement_time"
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE)
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE, STATE_CLASS_MEASUREMENT
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BH1750Sensor),
 | 
			
		||||
@@ -46,11 +49,11 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield sensor.register_sensor(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    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]))
 | 
			
		||||
 
 | 
			
		||||
@@ -21,19 +21,19 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    fan_ = yield fan.create_fan_state(config)
 | 
			
		||||
    fan_ = await fan.create_fan_state(config)
 | 
			
		||||
    cg.add(var.set_fan(fan_))
 | 
			
		||||
    output_ = yield cg.get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    output_ = await cg.get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    cg.add(var.set_output(output_))
 | 
			
		||||
 | 
			
		||||
    if CONF_OSCILLATION_OUTPUT in config:
 | 
			
		||||
        oscillation_output = yield cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
 | 
			
		||||
        oscillation_output = await cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
 | 
			
		||||
        cg.add(var.set_oscillating(oscillation_output))
 | 
			
		||||
 | 
			
		||||
    if CONF_DIRECTION_OUTPUT in config:
 | 
			
		||||
        direction_output = yield cg.get_variable(config[CONF_DIRECTION_OUTPUT])
 | 
			
		||||
        direction_output = await cg.get_variable(config[CONF_DIRECTION_OUTPUT])
 | 
			
		||||
        cg.add(var.set_direction(direction_output))
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,9 @@ CONFIG_SCHEMA = light.BINARY_LIGHT_SCHEMA.extend(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
 | 
			
		||||
    yield light.register_light(var, config)
 | 
			
		||||
    await light.register_light(var, config)
 | 
			
		||||
 | 
			
		||||
    out = yield cg.get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    out = await cg.get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    cg.add(var.set_output(out))
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ from esphome import automation, core
 | 
			
		||||
from esphome.automation import Condition, maybe_simple_id
 | 
			
		||||
from esphome.components import mqtt
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_DELAY,
 | 
			
		||||
    CONF_DEVICE_CLASS,
 | 
			
		||||
    CONF_FILTERS,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
@@ -50,7 +51,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_VIBRATION,
 | 
			
		||||
    DEVICE_CLASS_WINDOW,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine, coroutine_with_priority
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
from esphome.util import Registry
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@esphome/core"]
 | 
			
		||||
@@ -120,6 +121,7 @@ DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Co
 | 
			
		||||
DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component)
 | 
			
		||||
DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component)
 | 
			
		||||
InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
 | 
			
		||||
AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component)
 | 
			
		||||
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
 | 
			
		||||
 | 
			
		||||
FILTER_REGISTRY = Registry()
 | 
			
		||||
@@ -127,43 +129,88 @@ validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@FILTER_REGISTRY.register("invert", InvertFilter, {})
 | 
			
		||||
def invert_filter_to_code(config, filter_id):
 | 
			
		||||
    yield cg.new_Pvariable(filter_id)
 | 
			
		||||
async def invert_filter_to_code(config, filter_id):
 | 
			
		||||
    return cg.new_Pvariable(filter_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@FILTER_REGISTRY.register(
 | 
			
		||||
    "delayed_on_off", DelayedOnOffFilter, cv.positive_time_period_milliseconds
 | 
			
		||||
)
 | 
			
		||||
def delayed_on_off_filter_to_code(config, filter_id):
 | 
			
		||||
async def delayed_on_off_filter_to_code(config, filter_id):
 | 
			
		||||
    var = cg.new_Pvariable(filter_id, config)
 | 
			
		||||
    yield cg.register_component(var, {})
 | 
			
		||||
    yield var
 | 
			
		||||
    await cg.register_component(var, {})
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@FILTER_REGISTRY.register(
 | 
			
		||||
    "delayed_on", DelayedOnFilter, cv.positive_time_period_milliseconds
 | 
			
		||||
)
 | 
			
		||||
def delayed_on_filter_to_code(config, filter_id):
 | 
			
		||||
async def delayed_on_filter_to_code(config, filter_id):
 | 
			
		||||
    var = cg.new_Pvariable(filter_id, config)
 | 
			
		||||
    yield cg.register_component(var, {})
 | 
			
		||||
    yield var
 | 
			
		||||
    await cg.register_component(var, {})
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@FILTER_REGISTRY.register(
 | 
			
		||||
    "delayed_off", DelayedOffFilter, cv.positive_time_period_milliseconds
 | 
			
		||||
)
 | 
			
		||||
def delayed_off_filter_to_code(config, filter_id):
 | 
			
		||||
async def delayed_off_filter_to_code(config, filter_id):
 | 
			
		||||
    var = cg.new_Pvariable(filter_id, config)
 | 
			
		||||
    yield cg.register_component(var, {})
 | 
			
		||||
    yield var
 | 
			
		||||
    await cg.register_component(var, {})
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONF_TIME_OFF = "time_off"
 | 
			
		||||
CONF_TIME_ON = "time_on"
 | 
			
		||||
 | 
			
		||||
DEFAULT_DELAY = "1s"
 | 
			
		||||
DEFAULT_TIME_OFF = "100ms"
 | 
			
		||||
DEFAULT_TIME_ON = "900ms"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@FILTER_REGISTRY.register(
 | 
			
		||||
    "autorepeat",
 | 
			
		||||
    AutorepeatFilter,
 | 
			
		||||
    cv.All(
 | 
			
		||||
        cv.ensure_list(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Optional(
 | 
			
		||||
                    CONF_DELAY, default=DEFAULT_DELAY
 | 
			
		||||
                ): cv.positive_time_period_milliseconds,
 | 
			
		||||
                cv.Optional(
 | 
			
		||||
                    CONF_TIME_OFF, default=DEFAULT_TIME_OFF
 | 
			
		||||
                ): cv.positive_time_period_milliseconds,
 | 
			
		||||
                cv.Optional(
 | 
			
		||||
                    CONF_TIME_ON, default=DEFAULT_TIME_ON
 | 
			
		||||
                ): cv.positive_time_period_milliseconds,
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
async def autorepeat_filter_to_code(config, filter_id):
 | 
			
		||||
    timings = []
 | 
			
		||||
    if len(config) > 0:
 | 
			
		||||
        for conf in config:
 | 
			
		||||
            timings.append((conf[CONF_DELAY], conf[CONF_TIME_OFF], conf[CONF_TIME_ON]))
 | 
			
		||||
    else:
 | 
			
		||||
        timings.append(
 | 
			
		||||
            (
 | 
			
		||||
                cv.time_period_str_unit(DEFAULT_DELAY).total_milliseconds,
 | 
			
		||||
                cv.time_period_str_unit(DEFAULT_TIME_OFF).total_milliseconds,
 | 
			
		||||
                cv.time_period_str_unit(DEFAULT_TIME_ON).total_milliseconds,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    var = cg.new_Pvariable(filter_id, timings)
 | 
			
		||||
    await cg.register_component(var, {})
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda)
 | 
			
		||||
def lambda_filter_to_code(config, filter_id):
 | 
			
		||||
    lambda_ = yield cg.process_lambda(
 | 
			
		||||
async def lambda_filter_to_code(config, filter_id):
 | 
			
		||||
    lambda_ = await cg.process_lambda(
 | 
			
		||||
        config, [(bool, "x")], return_type=cg.optional.template(bool)
 | 
			
		||||
    )
 | 
			
		||||
    yield cg.new_Pvariable(filter_id, lambda_)
 | 
			
		||||
    return cg.new_Pvariable(filter_id, lambda_)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MULTI_CLICK_TIMING_SCHEMA = cv.Schema(
 | 
			
		||||
@@ -334,8 +381,7 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def setup_binary_sensor_core_(var, config):
 | 
			
		||||
async def setup_binary_sensor_core_(var, config):
 | 
			
		||||
    cg.add(var.set_name(config[CONF_NAME]))
 | 
			
		||||
    if CONF_INTERNAL in config:
 | 
			
		||||
        cg.add(var.set_internal(config[CONF_INTERNAL]))
 | 
			
		||||
@@ -344,28 +390,28 @@ def setup_binary_sensor_core_(var, config):
 | 
			
		||||
    if CONF_INVERTED in config:
 | 
			
		||||
        cg.add(var.set_inverted(config[CONF_INVERTED]))
 | 
			
		||||
    if CONF_FILTERS in config:
 | 
			
		||||
        filters = yield cg.build_registry_list(FILTER_REGISTRY, config[CONF_FILTERS])
 | 
			
		||||
        filters = await cg.build_registry_list(FILTER_REGISTRY, config[CONF_FILTERS])
 | 
			
		||||
        cg.add(var.add_filters(filters))
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_PRESS, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        yield automation.build_automation(trigger, [], conf)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_RELEASE, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        yield automation.build_automation(trigger, [], conf)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_CLICK, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(
 | 
			
		||||
            conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]
 | 
			
		||||
        )
 | 
			
		||||
        yield automation.build_automation(trigger, [], conf)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_DOUBLE_CLICK, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(
 | 
			
		||||
            conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]
 | 
			
		||||
        )
 | 
			
		||||
        yield automation.build_automation(trigger, [], conf)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_MULTI_CLICK, []):
 | 
			
		||||
        timings = []
 | 
			
		||||
@@ -381,31 +427,29 @@ def setup_binary_sensor_core_(var, config):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, timings)
 | 
			
		||||
        if CONF_INVALID_COOLDOWN in conf:
 | 
			
		||||
            cg.add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN]))
 | 
			
		||||
        yield cg.register_component(trigger, conf)
 | 
			
		||||
        yield automation.build_automation(trigger, [], conf)
 | 
			
		||||
        await cg.register_component(trigger, conf)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_STATE, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        yield automation.build_automation(trigger, [(bool, "x")], conf)
 | 
			
		||||
        await automation.build_automation(trigger, [(bool, "x")], conf)
 | 
			
		||||
 | 
			
		||||
    if CONF_MQTT_ID in config:
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
 | 
			
		||||
        yield mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def register_binary_sensor(var, config):
 | 
			
		||||
async def register_binary_sensor(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
        var = cg.Pvariable(config[CONF_ID], var)
 | 
			
		||||
    cg.add(cg.App.register_binary_sensor(var))
 | 
			
		||||
    yield setup_binary_sensor_core_(var, config)
 | 
			
		||||
    await setup_binary_sensor_core_(var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def new_binary_sensor(config):
 | 
			
		||||
async def new_binary_sensor(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME])
 | 
			
		||||
    yield register_binary_sensor(var, config)
 | 
			
		||||
    yield var
 | 
			
		||||
    await register_binary_sensor(var, config)
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id(
 | 
			
		||||
@@ -422,20 +466,20 @@ BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id(
 | 
			
		||||
@automation.register_condition(
 | 
			
		||||
    "binary_sensor.is_on", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA
 | 
			
		||||
)
 | 
			
		||||
def binary_sensor_is_on_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    paren = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg, paren, True)
 | 
			
		||||
async def binary_sensor_is_on_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(condition_id, template_arg, paren, True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_condition(
 | 
			
		||||
    "binary_sensor.is_off", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA
 | 
			
		||||
)
 | 
			
		||||
def binary_sensor_is_off_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    paren = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg, paren, False)
 | 
			
		||||
async def binary_sensor_is_off_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(condition_id, template_arg, paren, False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(100.0)
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    cg.add_define("USE_BINARY_SENSOR")
 | 
			
		||||
    cg.add_global(binary_sensor_ns.using)
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,50 @@ float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARD
 | 
			
		||||
 | 
			
		||||
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
 | 
			
		||||
 | 
			
		||||
AutorepeatFilter::AutorepeatFilter(const std::vector<AutorepeatFilterTiming> &timings) : timings_(timings) {}
 | 
			
		||||
 | 
			
		||||
optional<bool> AutorepeatFilter::new_value(bool value, bool is_initial) {
 | 
			
		||||
  if (value) {
 | 
			
		||||
    // Ignore if already running
 | 
			
		||||
    if (this->active_timing_ != 0)
 | 
			
		||||
      return {};
 | 
			
		||||
 | 
			
		||||
    this->next_timing_();
 | 
			
		||||
    return true;
 | 
			
		||||
  } else {
 | 
			
		||||
    this->cancel_timeout("TIMING");
 | 
			
		||||
    this->cancel_timeout("ON_OFF");
 | 
			
		||||
    this->active_timing_ = 0;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AutorepeatFilter::next_timing_() {
 | 
			
		||||
  // Entering this method
 | 
			
		||||
  // 1st time: starts waiting the first delay
 | 
			
		||||
  // 2nd time: starts waiting the second delay and starts toggling with the first time_off / _on
 | 
			
		||||
  // last time: no delay to start but have to bump the index to reflect the last
 | 
			
		||||
  if (this->active_timing_ < this->timings_.size())
 | 
			
		||||
    this->set_timeout("TIMING", this->timings_[this->active_timing_].delay, [this]() { this->next_timing_(); });
 | 
			
		||||
 | 
			
		||||
  if (this->active_timing_ <= this->timings_.size()) {
 | 
			
		||||
    this->active_timing_++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->active_timing_ == 2)
 | 
			
		||||
    this->next_value_(false);
 | 
			
		||||
 | 
			
		||||
  // Leaving this method: if the toggling is started, it has to use [active_timing_ - 2] for the intervals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AutorepeatFilter::next_value_(bool val) {
 | 
			
		||||
  const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2];
 | 
			
		||||
  this->output(val, false);  // This is at least the second one so not initial
 | 
			
		||||
  this->set_timeout("ON_OFF", val ? timing.time_on : timing.time_off, [this, val]() { this->next_value_(!val); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
 | 
			
		||||
 | 
			
		||||
LambdaFilter::LambdaFilter(const std::function<optional<bool>(bool)> &f) : f_(f) {}
 | 
			
		||||
 | 
			
		||||
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }
 | 
			
		||||
 
 | 
			
		||||
@@ -66,6 +66,33 @@ class InvertFilter : public Filter {
 | 
			
		||||
  optional<bool> new_value(bool value, bool is_initial) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct AutorepeatFilterTiming {
 | 
			
		||||
  AutorepeatFilterTiming(uint32_t delay, uint32_t off, uint32_t on) {
 | 
			
		||||
    this->delay = delay;
 | 
			
		||||
    this->time_off = off;
 | 
			
		||||
    this->time_on = on;
 | 
			
		||||
  }
 | 
			
		||||
  uint32_t delay;
 | 
			
		||||
  uint32_t time_off;
 | 
			
		||||
  uint32_t time_on;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AutorepeatFilter : public Filter, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit AutorepeatFilter(const std::vector<AutorepeatFilterTiming> &timings);
 | 
			
		||||
 | 
			
		||||
  optional<bool> new_value(bool value, bool is_initial) override;
 | 
			
		||||
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void next_timing_();
 | 
			
		||||
  void next_value_(bool val);
 | 
			
		||||
 | 
			
		||||
  std::vector<AutorepeatFilterTiming> timings_;
 | 
			
		||||
  uint8_t active_timing_{0};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class LambdaFilter : public Filter {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit LambdaFilter(const std::function<optional<bool>(bool)> &f);
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ from esphome.const import (
 | 
			
		||||
    ICON_CHECK_CIRCLE_OUTLINE,
 | 
			
		||||
    CONF_BINARY_SENSOR,
 | 
			
		||||
    CONF_GROUP,
 | 
			
		||||
    STATE_CLASS_NONE,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["binary_sensor"]
 | 
			
		||||
@@ -34,7 +35,11 @@ entry = {
 | 
			
		||||
CONFIG_SCHEMA = cv.typed_schema(
 | 
			
		||||
    {
 | 
			
		||||
        CONF_GROUP: sensor.sensor_schema(
 | 
			
		||||
            UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, 0, DEVICE_CLASS_EMPTY
 | 
			
		||||
            UNIT_EMPTY,
 | 
			
		||||
            ICON_CHECK_CIRCLE_OUTLINE,
 | 
			
		||||
            0,
 | 
			
		||||
            DEVICE_CLASS_EMPTY,
 | 
			
		||||
            STATE_CLASS_NONE,
 | 
			
		||||
        ).extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(): cv.declare_id(BinarySensorMap),
 | 
			
		||||
@@ -48,14 +53,14 @@ CONFIG_SCHEMA = cv.typed_schema(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield sensor.register_sensor(var, config)
 | 
			
		||||
    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))
 | 
			
		||||
 | 
			
		||||
    for ch in config[CONF_CHANNELS]:
 | 
			
		||||
        input_var = yield cg.get_variable(ch[CONF_BINARY_SENSOR])
 | 
			
		||||
        input_var = await cg.get_variable(ch[CONF_BINARY_SENSOR])
 | 
			
		||||
        cg.add(var.add_channel(input_var, ch[CONF_VALUE]))
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_ON_DISCONNECT,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import coroutine
 | 
			
		||||
from esphome import automation
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@buxtronix"]
 | 
			
		||||
@@ -68,20 +67,19 @@ BLE_CLIENT_SCHEMA = cv.Schema(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def register_ble_node(var, config):
 | 
			
		||||
    parent = yield cg.get_variable(config[CONF_BLE_CLIENT_ID])
 | 
			
		||||
async def register_ble_node(var, config):
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_BLE_CLIENT_ID])
 | 
			
		||||
    cg.add(parent.register_ble_node(var))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield esp32_ble_tracker.register_client(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await esp32_ble_tracker.register_client(var, config)
 | 
			
		||||
    cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
 | 
			
		||||
    for conf in config.get(CONF_ON_CONNECT, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        yield automation.build_automation(trigger, [], conf)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_DISCONNECT, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        yield automation.build_automation(trigger, [], conf)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
 | 
			
		||||
                                    esp_ble_gattc_cb_param_t *param) {
 | 
			
		||||
  if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id)
 | 
			
		||||
    return;
 | 
			
		||||
  if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && gattc_if != this->gattc_if)
 | 
			
		||||
  if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  bool all_established = this->all_nodes_established();
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,8 @@ from esphome.components import sensor, ble_client, esp32_ble_tracker
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_LAMBDA,
 | 
			
		||||
    STATE_CLASS_NONE,
 | 
			
		||||
    UNIT_EMPTY,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
@@ -20,6 +22,9 @@ CONF_DESCRIPTOR_UUID = "descriptor_uuid"
 | 
			
		||||
CONF_NOTIFY = "notify"
 | 
			
		||||
CONF_ON_NOTIFY = "on_notify"
 | 
			
		||||
 | 
			
		||||
adv_data_t = cg.std_vector.template(cg.uint8)
 | 
			
		||||
adv_data_t_const_ref = adv_data_t.operator("ref").operator("const")
 | 
			
		||||
 | 
			
		||||
BLESensor = ble_client_ns.class_(
 | 
			
		||||
    "BLESensor", sensor.Sensor, cg.PollingComponent, ble_client.BLEClientNode
 | 
			
		||||
)
 | 
			
		||||
@@ -28,13 +33,16 @@ BLESensorNotifyTrigger = ble_client_ns.class_(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY)
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BLESensor),
 | 
			
		||||
            cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Optional(CONF_LAMBDA): cv.returning_lambda,
 | 
			
		||||
            cv.Optional(CONF_NOTIFY, default=False): cv.boolean,
 | 
			
		||||
            cv.Optional(CONF_ON_NOTIFY): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
@@ -50,7 +58,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
        cg.add(
 | 
			
		||||
@@ -105,11 +113,17 @@ def to_code(config):
 | 
			
		||||
            uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_DESCRIPTOR_UUID])
 | 
			
		||||
            cg.add(var.set_descr_uuid128(uuid128))
 | 
			
		||||
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield ble_client.register_ble_node(var, config)
 | 
			
		||||
    if CONF_LAMBDA in config:
 | 
			
		||||
        lambda_ = await cg.process_lambda(
 | 
			
		||||
            config[CONF_LAMBDA], [(adv_data_t_const_ref, "x")], return_type=cg.float_
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_data_to_value(lambda_))
 | 
			
		||||
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await ble_client.register_ble_node(var, config)
 | 
			
		||||
    cg.add(var.set_enable_notify(config[CONF_NOTIFY]))
 | 
			
		||||
    yield sensor.register_sensor(var, config)
 | 
			
		||||
    await sensor.register_sensor(var, config)
 | 
			
		||||
    for conf in config.get(CONF_ON_NOTIFY, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        yield ble_client.register_ble_node(trigger, config)
 | 
			
		||||
        yield automation.build_automation(trigger, [(float, "x")], conf)
 | 
			
		||||
        await ble_client.register_ble_node(trigger, config)
 | 
			
		||||
        await automation.build_automation(trigger, [(float, "x")], conf)
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
 | 
			
		||||
      }
 | 
			
		||||
      if (param->read.handle == this->handle) {
 | 
			
		||||
        this->status_clear_warning();
 | 
			
		||||
        this->publish_state((float) param->read.value[0]);
 | 
			
		||||
        this->publish_state(this->parse_data(param->read.value, param->read.value_len));
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -93,7 +93,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
 | 
			
		||||
        break;
 | 
			
		||||
      ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
 | 
			
		||||
               param->notify.handle, param->notify.value[0]);
 | 
			
		||||
      this->publish_state((float) param->notify.value[0]);
 | 
			
		||||
      this->publish_state(this->parse_data(param->notify.value, param->notify.value_len));
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
 | 
			
		||||
@@ -105,6 +105,15 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float BLESensor::parse_data(uint8_t *value, uint16_t value_len) {
 | 
			
		||||
  if (this->data_to_value_func_.has_value()) {
 | 
			
		||||
    std::vector<uint8_t> data(value, value + value_len);
 | 
			
		||||
    return (*this->data_to_value_func_)(data);
 | 
			
		||||
  } else {
 | 
			
		||||
    return value[0];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLESensor::update() {
 | 
			
		||||
  if (this->node_state != espbt::ClientState::Established) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@ namespace ble_client {
 | 
			
		||||
 | 
			
		||||
namespace espbt = esphome::esp32_ble_tracker;
 | 
			
		||||
 | 
			
		||||
using data_to_value_t = std::function<float(std::vector<uint8_t>)>;
 | 
			
		||||
 | 
			
		||||
class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClientNode {
 | 
			
		||||
 public:
 | 
			
		||||
  void loop() override;
 | 
			
		||||
@@ -30,11 +32,14 @@ class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClie
 | 
			
		||||
  void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
 | 
			
		||||
  void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
 | 
			
		||||
  void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
 | 
			
		||||
  void set_data_to_value(data_to_value_t &&lambda_) { this->data_to_value_func_ = lambda_; }
 | 
			
		||||
  void set_enable_notify(bool notify) { this->notify_ = notify; }
 | 
			
		||||
  uint16_t handle;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint32_t hash_base() override;
 | 
			
		||||
  float parse_data(uint8_t *value, uint16_t value_len);
 | 
			
		||||
  optional<data_to_value_t> data_to_value_func_{};
 | 
			
		||||
  bool notify_;
 | 
			
		||||
  espbt::ESPBTUUID service_uuid_;
 | 
			
		||||
  espbt::ESPBTUUID char_uuid_;
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,8 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield switch.register_switch(var, config)
 | 
			
		||||
    yield ble_client.register_ble_node(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await switch.register_switch(var, config)
 | 
			
		||||
    await ble_client.register_ble_node(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,11 +27,11 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
    yield binary_sensor.register_binary_sensor(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
    await binary_sensor.register_binary_sensor(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_MAC_ADDRESS in config:
 | 
			
		||||
        cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_MAC_ADDRESS,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_DECIBEL,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
)
 | 
			
		||||
@@ -18,7 +19,13 @@ BLERSSISensor = ble_rssi_ns.class_(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    sensor.sensor_schema(UNIT_DECIBEL, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH)
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        UNIT_DECIBEL,
 | 
			
		||||
        ICON_EMPTY,
 | 
			
		||||
        0,
 | 
			
		||||
        DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
			
		||||
        STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BLERSSISensor),
 | 
			
		||||
@@ -32,11 +39,11 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
    yield sensor.register_sensor(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
    await sensor.register_sensor(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_MAC_ADDRESS in config:
 | 
			
		||||
        cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,8 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
    yield text_sensor.register_text_sensor(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
    await text_sensor.register_text_sensor(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_PRESSURE,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_HECTOPASCAL,
 | 
			
		||||
    UNIT_PERCENT,
 | 
			
		||||
@@ -48,7 +49,11 @@ CONFIG_SCHEMA = (
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BME280Component),
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
 | 
			
		||||
                UNIT_CELSIUS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
 | 
			
		||||
@@ -57,7 +62,11 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
 | 
			
		||||
                UNIT_HECTOPASCAL,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_PRESSURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
 | 
			
		||||
@@ -66,7 +75,11 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
			
		||||
                UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
 | 
			
		||||
                UNIT_PERCENT,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
 | 
			
		||||
@@ -84,26 +97,26 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_TEMPERATURE]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
        cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_PRESSURE in config:
 | 
			
		||||
        conf = config[CONF_PRESSURE]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_pressure_sensor(sens))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        conf = config[CONF_HUMIDITY]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_humidity_sensor(sens))
 | 
			
		||||
        cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
    DEVICE_CLASS_PRESSURE,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_OHM,
 | 
			
		||||
    ICON_GAS_CYLINDER,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
@@ -58,7 +59,11 @@ CONFIG_SCHEMA = (
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BME680Component),
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
 | 
			
		||||
                UNIT_CELSIUS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
 | 
			
		||||
@@ -67,7 +72,11 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
 | 
			
		||||
                UNIT_HECTOPASCAL,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_PRESSURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
 | 
			
		||||
@@ -76,7 +85,11 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
			
		||||
                UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
 | 
			
		||||
                UNIT_PERCENT,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
 | 
			
		||||
@@ -85,7 +98,11 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_OHM, ICON_GAS_CYLINDER, 1, DEVICE_CLASS_EMPTY
 | 
			
		||||
                UNIT_OHM,
 | 
			
		||||
                ICON_GAS_CYLINDER,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_EMPTY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
 | 
			
		||||
                IIR_FILTER_OPTIONS, upper=True
 | 
			
		||||
@@ -114,32 +131,32 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_TEMPERATURE]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
        cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_PRESSURE in config:
 | 
			
		||||
        conf = config[CONF_PRESSURE]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_pressure_sensor(sens))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        conf = config[CONF_HUMIDITY]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_humidity_sensor(sens))
 | 
			
		||||
        cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_GAS_RESISTANCE in config:
 | 
			
		||||
        conf = config[CONF_GAS_RESISTANCE]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_gas_resistance_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_iir_filter(IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]))
 | 
			
		||||
 
 | 
			
		||||
@@ -48,10 +48,10 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
).extend(i2c.i2c_device_schema(0x76))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET]))
 | 
			
		||||
    cg.add(var.set_iaq_mode(config[CONF_IAQ_MODE]))
 | 
			
		||||
@@ -60,5 +60,5 @@ def to_code(config):
 | 
			
		||||
        var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    cg.add_define("USING_BSEC")
 | 
			
		||||
    cg.add_define("USE_BSEC")
 | 
			
		||||
    cg.add_library("BSEC Software Library", "1.6.1480")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "bme680_bsec.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
@@ -7,7 +5,7 @@
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bme680_bsec {
 | 
			
		||||
#ifdef USING_BSEC
 | 
			
		||||
#ifdef USE_BSEC
 | 
			
		||||
static const char *TAG = "bme680_bsec.sensor";
 | 
			
		||||
 | 
			
		||||
static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
 | 
			
		||||
@@ -30,7 +28,6 @@ void BME680BSECComponent::setup() {
 | 
			
		||||
  this->bme680_.write = BME680BSECComponent::write_bytes_wrapper;
 | 
			
		||||
  this->bme680_.delay_ms = BME680BSECComponent::delay_ms;
 | 
			
		||||
  this->bme680_.amb_temp = 25;
 | 
			
		||||
  this->bme680_.power_mode = BME680_FORCED_MODE;
 | 
			
		||||
 | 
			
		||||
  this->bme680_status_ = bme680_init(&this->bme680_);
 | 
			
		||||
  if (this->bme680_status_ != BME680_OK) {
 | 
			
		||||
@@ -43,14 +40,13 @@ void BME680BSECComponent::setup() {
 | 
			
		||||
#include "config/generic_33v_300s_28d/bsec_iaq.txt"
 | 
			
		||||
    };
 | 
			
		||||
    this->set_config_(bsec_config);
 | 
			
		||||
    this->update_subscription_(BSEC_SAMPLE_RATE_ULP);
 | 
			
		||||
  } else {
 | 
			
		||||
    const uint8_t bsec_config[] = {
 | 
			
		||||
#include "config/generic_33v_3s_28d/bsec_iaq.txt"
 | 
			
		||||
    };
 | 
			
		||||
    this->set_config_(bsec_config);
 | 
			
		||||
    this->update_subscription_(BSEC_SAMPLE_RATE_LP);
 | 
			
		||||
  }
 | 
			
		||||
  this->update_subscription_();
 | 
			
		||||
  if (this->bsec_status_ != BSEC_OK) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
@@ -64,50 +60,57 @@ void BME680BSECComponent::set_config_(const uint8_t *config) {
 | 
			
		||||
  this->bsec_status_ = bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, work_buffer, sizeof(work_buffer));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BME680BSECComponent::update_subscription_(float sample_rate) {
 | 
			
		||||
float BME680BSECComponent::calc_sensor_sample_rate_(SampleRate sample_rate) {
 | 
			
		||||
  if (sample_rate == SAMPLE_RATE_DEFAULT) {
 | 
			
		||||
    sample_rate = this->sample_rate_;
 | 
			
		||||
  }
 | 
			
		||||
  return sample_rate == SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BME680BSECComponent::update_subscription_() {
 | 
			
		||||
  bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
 | 
			
		||||
  int num_virtual_sensors = 0;
 | 
			
		||||
 | 
			
		||||
  if (this->iaq_sensor_) {
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sensor_id =
 | 
			
		||||
        this->iaq_mode_ == IAQ_MODE_STATIC ? BSEC_OUTPUT_STATIC_IAQ : BSEC_OUTPUT_IAQ;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = sample_rate;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
 | 
			
		||||
    num_virtual_sensors++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->co2_equivalent_sensor_) {
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = sample_rate;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
 | 
			
		||||
    num_virtual_sensors++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->breath_voc_equivalent_sensor_) {
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = sample_rate;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
 | 
			
		||||
    num_virtual_sensors++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->pressure_sensor_) {
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = sample_rate;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->pressure_sample_rate_);
 | 
			
		||||
    num_virtual_sensors++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->gas_resistance_sensor_) {
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = sample_rate;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
 | 
			
		||||
    num_virtual_sensors++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->temperature_sensor_) {
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = sample_rate;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->temperature_sample_rate_);
 | 
			
		||||
    num_virtual_sensors++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->humidity_sensor_) {
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = sample_rate;
 | 
			
		||||
    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->humidity_sample_rate_);
 | 
			
		||||
    num_virtual_sensors++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -134,12 +137,15 @@ void BME680BSECComponent::dump_config() {
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Temperature Offset: %.2f", this->temperature_offset_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Sample Rate: %s", this->sample_rate_ == SAMPLE_RATE_ULP ? "ULP" : "LP");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  State Save Interval: %ims", this->state_save_interval_ms_);
 | 
			
		||||
 | 
			
		||||
  LOG_SENSOR("  ", "Temperature", this->temperature_sensor_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "    Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->temperature_sample_rate_));
 | 
			
		||||
  LOG_SENSOR("  ", "Pressure", this->pressure_sensor_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "    Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->pressure_sample_rate_));
 | 
			
		||||
  LOG_SENSOR("  ", "Humidity", this->humidity_sensor_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "    Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->humidity_sample_rate_));
 | 
			
		||||
  LOG_SENSOR("  ", "Gas Resistance", this->gas_resistance_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "IAQ", this->iaq_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Numeric IAQ Accuracy", this->iaq_accuracy_sensor_);
 | 
			
		||||
@@ -181,34 +187,55 @@ void BME680BSECComponent::run_() {
 | 
			
		||||
  }
 | 
			
		||||
  this->next_call_ns_ = bme680_settings.next_call;
 | 
			
		||||
 | 
			
		||||
  this->bme680_.gas_sett.run_gas = bme680_settings.run_gas;
 | 
			
		||||
  this->bme680_.tph_sett.os_hum = bme680_settings.humidity_oversampling;
 | 
			
		||||
  this->bme680_.tph_sett.os_temp = bme680_settings.temperature_oversampling;
 | 
			
		||||
  this->bme680_.tph_sett.os_pres = bme680_settings.pressure_oversampling;
 | 
			
		||||
  this->bme680_.gas_sett.heatr_temp = bme680_settings.heater_temperature;
 | 
			
		||||
  this->bme680_.gas_sett.heatr_dur = bme680_settings.heating_duration;
 | 
			
		||||
  uint16_t desired_settings =
 | 
			
		||||
      BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_FILTER_SEL | BME680_GAS_SENSOR_SEL;
 | 
			
		||||
  this->bme680_status_ = bme680_set_sensor_settings(desired_settings, &this->bme680_);
 | 
			
		||||
  if (this->bme680_status_ != BME680_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "Failed to set sensor settings (BME680 Error Code %d)", this->bme680_status_);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (bme680_settings.trigger_measurement) {
 | 
			
		||||
    this->bme680_.tph_sett.os_temp = bme680_settings.temperature_oversampling;
 | 
			
		||||
    this->bme680_.tph_sett.os_pres = bme680_settings.pressure_oversampling;
 | 
			
		||||
    this->bme680_.tph_sett.os_hum = bme680_settings.humidity_oversampling;
 | 
			
		||||
    this->bme680_.gas_sett.run_gas = bme680_settings.run_gas;
 | 
			
		||||
    this->bme680_.gas_sett.heatr_temp = bme680_settings.heater_temperature;
 | 
			
		||||
    this->bme680_.gas_sett.heatr_dur = bme680_settings.heating_duration;
 | 
			
		||||
    this->bme680_.power_mode = BME680_FORCED_MODE;
 | 
			
		||||
    uint16_t desired_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;
 | 
			
		||||
    this->bme680_status_ = bme680_set_sensor_settings(desired_settings, &this->bme680_);
 | 
			
		||||
    if (this->bme680_status_ != BME680_OK) {
 | 
			
		||||
      ESP_LOGW(TAG, "Failed to set sensor settings (BME680 Error Code %d)", this->bme680_status_);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  this->bme680_status_ = bme680_set_sensor_mode(&this->bme680_);
 | 
			
		||||
  if (this->bme680_status_ != BME680_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "Failed to set sensor mode (BME680 Error Code %d)", this->bme680_status_);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
    this->bme680_status_ = bme680_set_sensor_mode(&this->bme680_);
 | 
			
		||||
    if (this->bme680_status_ != BME680_OK) {
 | 
			
		||||
      ESP_LOGW(TAG, "Failed to set sensor mode (BME680 Error Code %d)", this->bme680_status_);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  uint16_t meas_dur = 0;
 | 
			
		||||
  bme680_get_profile_dur(&meas_dur, &this->bme680_);
 | 
			
		||||
  ESP_LOGV(TAG, "Queueing read in %ums", meas_dur);
 | 
			
		||||
  this->set_timeout("read", meas_dur, [this, bme680_settings]() { this->read_(bme680_settings); });
 | 
			
		||||
    uint16_t meas_dur = 0;
 | 
			
		||||
    bme680_get_profile_dur(&meas_dur, &this->bme680_);
 | 
			
		||||
    ESP_LOGV(TAG, "Queueing read in %ums", meas_dur);
 | 
			
		||||
    this->set_timeout("read", meas_dur,
 | 
			
		||||
                      [this, curr_time_ns, bme680_settings]() { this->read_(curr_time_ns, bme680_settings); });
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGV(TAG, "Measurement not required");
 | 
			
		||||
    this->read_(curr_time_ns, bme680_settings);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BME680BSECComponent::read_(bsec_bme_settings_t bme680_settings) {
 | 
			
		||||
void BME680BSECComponent::read_(int64_t trigger_time_ns, bsec_bme_settings_t bme680_settings) {
 | 
			
		||||
  ESP_LOGV(TAG, "Reading data");
 | 
			
		||||
 | 
			
		||||
  if (bme680_settings.trigger_measurement) {
 | 
			
		||||
    while (this->bme680_.power_mode != BME680_SLEEP_MODE) {
 | 
			
		||||
      this->bme680_status_ = bme680_get_sensor_mode(&this->bme680_);
 | 
			
		||||
      if (this->bme680_status_ != BME680_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "Failed to get sensor mode (BME680 Error Code %d)", this->bme680_status_);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!bme680_settings.process_data) {
 | 
			
		||||
    ESP_LOGV(TAG, "Data processing not required");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  struct bme680_field_data data;
 | 
			
		||||
  this->bme680_status_ = bme680_get_sensor_data(&data, &this->bme680_);
 | 
			
		||||
 | 
			
		||||
@@ -223,37 +250,40 @@ void BME680BSECComponent::read_(bsec_bme_settings_t bme680_settings) {
 | 
			
		||||
 | 
			
		||||
  bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR];  // Temperature, Pressure, Humidity & Gas Resistance
 | 
			
		||||
  uint8_t num_inputs = 0;
 | 
			
		||||
  int64_t curr_time_ns = this->get_time_ns_();
 | 
			
		||||
 | 
			
		||||
  if (bme680_settings.process_data & BSEC_PROCESS_TEMPERATURE) {
 | 
			
		||||
    inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
 | 
			
		||||
    inputs[num_inputs].signal = data.temperature / 100.0f;
 | 
			
		||||
    inputs[num_inputs].time_stamp = curr_time_ns;
 | 
			
		||||
    inputs[num_inputs].time_stamp = trigger_time_ns;
 | 
			
		||||
    num_inputs++;
 | 
			
		||||
 | 
			
		||||
    // Temperature offset from the real temperature due to external heat sources
 | 
			
		||||
    inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
 | 
			
		||||
    inputs[num_inputs].signal = this->temperature_offset_;
 | 
			
		||||
    inputs[num_inputs].time_stamp = curr_time_ns;
 | 
			
		||||
    inputs[num_inputs].time_stamp = trigger_time_ns;
 | 
			
		||||
    num_inputs++;
 | 
			
		||||
  }
 | 
			
		||||
  if (bme680_settings.process_data & BSEC_PROCESS_HUMIDITY) {
 | 
			
		||||
    inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
 | 
			
		||||
    inputs[num_inputs].signal = data.humidity / 1000.0f;
 | 
			
		||||
    inputs[num_inputs].time_stamp = curr_time_ns;
 | 
			
		||||
    inputs[num_inputs].time_stamp = trigger_time_ns;
 | 
			
		||||
    num_inputs++;
 | 
			
		||||
  }
 | 
			
		||||
  if (bme680_settings.process_data & BSEC_PROCESS_PRESSURE) {
 | 
			
		||||
    inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
 | 
			
		||||
    inputs[num_inputs].signal = data.pressure;
 | 
			
		||||
    inputs[num_inputs].time_stamp = curr_time_ns;
 | 
			
		||||
    inputs[num_inputs].time_stamp = trigger_time_ns;
 | 
			
		||||
    num_inputs++;
 | 
			
		||||
  }
 | 
			
		||||
  if (bme680_settings.process_data & BSEC_PROCESS_GAS) {
 | 
			
		||||
    inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
 | 
			
		||||
    inputs[num_inputs].signal = data.gas_resistance;
 | 
			
		||||
    inputs[num_inputs].time_stamp = curr_time_ns;
 | 
			
		||||
    num_inputs++;
 | 
			
		||||
    if (data.status & BME680_GASM_VALID_MSK) {
 | 
			
		||||
      inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
 | 
			
		||||
      inputs[num_inputs].signal = data.gas_resistance;
 | 
			
		||||
      inputs[num_inputs].time_stamp = trigger_time_ns;
 | 
			
		||||
      num_inputs++;
 | 
			
		||||
    } else {
 | 
			
		||||
      ESP_LOGD(TAG, "BME680 did not report gas data");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (num_inputs < 1) {
 | 
			
		||||
    ESP_LOGD(TAG, "No signal inputs available for BSEC");
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
@@ -9,13 +7,13 @@
 | 
			
		||||
#include "esphome/core/preferences.h"
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#ifdef USING_BSEC
 | 
			
		||||
#ifdef USE_BSEC
 | 
			
		||||
#include <bsec.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bme680_bsec {
 | 
			
		||||
#ifdef USING_BSEC
 | 
			
		||||
#ifdef USE_BSEC
 | 
			
		||||
 | 
			
		||||
enum IAQMode {
 | 
			
		||||
  IAQ_MODE_STATIC = 0,
 | 
			
		||||
@@ -25,32 +23,31 @@ enum IAQMode {
 | 
			
		||||
enum SampleRate {
 | 
			
		||||
  SAMPLE_RATE_LP = 0,
 | 
			
		||||
  SAMPLE_RATE_ULP = 1,
 | 
			
		||||
  SAMPLE_RATE_DEFAULT = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define BME680_BSEC_SAMPLE_RATE_LOG(r) (r == SAMPLE_RATE_DEFAULT ? "Default" : (r == SAMPLE_RATE_ULP ? "ULP" : "LP"))
 | 
			
		||||
 | 
			
		||||
class BME680BSECComponent : public Component, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_temperature_offset(float offset) { this->temperature_offset_ = offset; }
 | 
			
		||||
  void set_iaq_mode(IAQMode iaq_mode) { this->iaq_mode_ = iaq_mode; }
 | 
			
		||||
  void set_sample_rate(SampleRate sample_rate) { this->sample_rate_ = sample_rate; }
 | 
			
		||||
  void set_state_save_interval(uint32_t interval) { this->state_save_interval_ms_ = interval; }
 | 
			
		||||
 | 
			
		||||
  void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
 | 
			
		||||
  void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
 | 
			
		||||
  void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; }
 | 
			
		||||
  void set_gas_resistance_sensor(sensor::Sensor *gas_resistance_sensor) {
 | 
			
		||||
    gas_resistance_sensor_ = gas_resistance_sensor;
 | 
			
		||||
  }
 | 
			
		||||
  void set_iaq_sensor(sensor::Sensor *iaq_sensor) { iaq_sensor_ = iaq_sensor; }
 | 
			
		||||
  void set_iaq_accuracy_text_sensor(text_sensor::TextSensor *iaq_accuracy_text_sensor) {
 | 
			
		||||
    iaq_accuracy_text_sensor_ = iaq_accuracy_text_sensor;
 | 
			
		||||
  }
 | 
			
		||||
  void set_iaq_accuracy_sensor(sensor::Sensor *iaq_accuracy_sensor) { iaq_accuracy_sensor_ = iaq_accuracy_sensor; }
 | 
			
		||||
  void set_co2_equivalent_sensor(sensor::Sensor *co2_equivalent_sensor) {
 | 
			
		||||
    co2_equivalent_sensor_ = co2_equivalent_sensor;
 | 
			
		||||
  }
 | 
			
		||||
  void set_breath_voc_equivalent_sensor(sensor::Sensor *breath_voc_equivalent_sensor) {
 | 
			
		||||
    breath_voc_equivalent_sensor_ = breath_voc_equivalent_sensor;
 | 
			
		||||
  }
 | 
			
		||||
  void set_sample_rate(SampleRate sample_rate) { this->sample_rate_ = sample_rate; }
 | 
			
		||||
  void set_temperature_sample_rate(SampleRate sample_rate) { this->temperature_sample_rate_ = sample_rate; }
 | 
			
		||||
  void set_pressure_sample_rate(SampleRate sample_rate) { this->pressure_sample_rate_ = sample_rate; }
 | 
			
		||||
  void set_humidity_sample_rate(SampleRate sample_rate) { this->humidity_sample_rate_ = sample_rate; }
 | 
			
		||||
 | 
			
		||||
  void set_temperature_sensor(sensor::Sensor *sensor) { this->temperature_sensor_ = sensor; }
 | 
			
		||||
  void set_pressure_sensor(sensor::Sensor *sensor) { this->pressure_sensor_ = sensor; }
 | 
			
		||||
  void set_humidity_sensor(sensor::Sensor *sensor) { this->humidity_sensor_ = sensor; }
 | 
			
		||||
  void set_gas_resistance_sensor(sensor::Sensor *sensor) { this->gas_resistance_sensor_ = sensor; }
 | 
			
		||||
  void set_iaq_sensor(sensor::Sensor *sensor) { this->iaq_sensor_ = sensor; }
 | 
			
		||||
  void set_iaq_accuracy_text_sensor(text_sensor::TextSensor *sensor) { this->iaq_accuracy_text_sensor_ = sensor; }
 | 
			
		||||
  void set_iaq_accuracy_sensor(sensor::Sensor *sensor) { this->iaq_accuracy_sensor_ = sensor; }
 | 
			
		||||
  void set_co2_equivalent_sensor(sensor::Sensor *sensor) { this->co2_equivalent_sensor_ = sensor; }
 | 
			
		||||
  void set_breath_voc_equivalent_sensor(sensor::Sensor *sensor) { this->breath_voc_equivalent_sensor_ = sensor; }
 | 
			
		||||
 | 
			
		||||
  static BME680BSECComponent *instance;
 | 
			
		||||
  static int8_t read_bytes_wrapper(uint8_t address, uint8_t a_register, uint8_t *data, uint16_t len);
 | 
			
		||||
@@ -64,10 +61,11 @@ class BME680BSECComponent : public Component, public i2c::I2CDevice {
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void set_config_(const uint8_t *config);
 | 
			
		||||
  void update_subscription_(float sample_rate);
 | 
			
		||||
  float calc_sensor_sample_rate_(SampleRate sample_rate);
 | 
			
		||||
  void update_subscription_();
 | 
			
		||||
 | 
			
		||||
  void run_();
 | 
			
		||||
  void read_(bsec_bme_settings_t bme680_settings);
 | 
			
		||||
  void read_(int64_t trigger_time_ns, bsec_bme_settings_t bme680_settings);
 | 
			
		||||
  void publish_(const bsec_output_t *outputs, uint8_t num_outputs);
 | 
			
		||||
  int64_t get_time_ns_();
 | 
			
		||||
 | 
			
		||||
@@ -91,7 +89,11 @@ class BME680BSECComponent : public Component, public i2c::I2CDevice {
 | 
			
		||||
 | 
			
		||||
  float temperature_offset_{0};
 | 
			
		||||
  IAQMode iaq_mode_{IAQ_MODE_STATIC};
 | 
			
		||||
  SampleRate sample_rate_{SAMPLE_RATE_LP};
 | 
			
		||||
 | 
			
		||||
  SampleRate sample_rate_{SAMPLE_RATE_LP};  // Core/gas sample rate
 | 
			
		||||
  SampleRate temperature_sample_rate_{SAMPLE_RATE_DEFAULT};
 | 
			
		||||
  SampleRate pressure_sample_rate_{SAMPLE_RATE_DEFAULT};
 | 
			
		||||
  SampleRate humidity_sample_rate_{SAMPLE_RATE_DEFAULT};
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *pressure_sensor_;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
    DEVICE_CLASS_PRESSURE,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_EMPTY,
 | 
			
		||||
    UNIT_HECTOPASCAL,
 | 
			
		||||
@@ -21,8 +22,12 @@ from esphome.const import (
 | 
			
		||||
    ICON_THERMOMETER,
 | 
			
		||||
    ICON_WATER_PERCENT,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import coroutine
 | 
			
		||||
from . import BME680BSECComponent, CONF_BME680_BSEC_ID
 | 
			
		||||
from . import (
 | 
			
		||||
    BME680BSECComponent,
 | 
			
		||||
    CONF_BME680_BSEC_ID,
 | 
			
		||||
    CONF_SAMPLE_RATE,
 | 
			
		||||
    SAMPLE_RATE_OPTIONS,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["bme680_bsec"]
 | 
			
		||||
 | 
			
		||||
@@ -34,58 +39,84 @@ UNIT_IAQ = "IAQ"
 | 
			
		||||
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
 | 
			
		||||
ICON_TEST_TUBE = "mdi:test-tube"
 | 
			
		||||
 | 
			
		||||
TYPES = {
 | 
			
		||||
    CONF_TEMPERATURE: "set_temperature_sensor",
 | 
			
		||||
    CONF_PRESSURE: "set_pressure_sensor",
 | 
			
		||||
    CONF_HUMIDITY: "set_humidity_sensor",
 | 
			
		||||
    CONF_GAS_RESISTANCE: "set_gas_resistance_sensor",
 | 
			
		||||
    CONF_IAQ: "set_iaq_sensor",
 | 
			
		||||
    CONF_IAQ_ACCURACY: "set_iaq_accuracy_sensor",
 | 
			
		||||
    CONF_CO2_EQUIVALENT: "set_co2_equivalent_sensor",
 | 
			
		||||
    CONF_BREATH_VOC_EQUIVALENT: "set_breath_voc_equivalent_sensor",
 | 
			
		||||
}
 | 
			
		||||
TYPES = [
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    CONF_PRESSURE,
 | 
			
		||||
    CONF_HUMIDITY,
 | 
			
		||||
    CONF_GAS_RESISTANCE,
 | 
			
		||||
    CONF_IAQ,
 | 
			
		||||
    CONF_IAQ_ACCURACY,
 | 
			
		||||
    CONF_CO2_EQUIVALENT,
 | 
			
		||||
    CONF_BREATH_VOC_EQUIVALENT,
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent),
 | 
			
		||||
        cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
            UNIT_CELSIUS, ICON_THERMOMETER, 1, DEVICE_CLASS_TEMPERATURE
 | 
			
		||||
            UNIT_CELSIUS,
 | 
			
		||||
            ICON_THERMOMETER,
 | 
			
		||||
            1,
 | 
			
		||||
            DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
            STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ).extend(
 | 
			
		||||
            {cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
 | 
			
		||||
            UNIT_HECTOPASCAL, ICON_GAUGE, 1, DEVICE_CLASS_PRESSURE
 | 
			
		||||
            UNIT_HECTOPASCAL,
 | 
			
		||||
            ICON_GAUGE,
 | 
			
		||||
            1,
 | 
			
		||||
            DEVICE_CLASS_PRESSURE,
 | 
			
		||||
            STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ).extend(
 | 
			
		||||
            {cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
			
		||||
            UNIT_PERCENT, ICON_WATER_PERCENT, 1, DEVICE_CLASS_HUMIDITY
 | 
			
		||||
            UNIT_PERCENT,
 | 
			
		||||
            ICON_WATER_PERCENT,
 | 
			
		||||
            1,
 | 
			
		||||
            DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
            STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ).extend(
 | 
			
		||||
            {cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema(
 | 
			
		||||
            UNIT_OHM, ICON_GAS_CYLINDER, 0, DEVICE_CLASS_EMPTY
 | 
			
		||||
            UNIT_OHM, ICON_GAS_CYLINDER, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_IAQ): sensor.sensor_schema(
 | 
			
		||||
            UNIT_IAQ, ICON_GAUGE, 0, DEVICE_CLASS_EMPTY
 | 
			
		||||
            UNIT_IAQ, ICON_GAUGE, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_IAQ_ACCURACY): sensor.sensor_schema(
 | 
			
		||||
            UNIT_EMPTY, ICON_ACCURACY, 0, DEVICE_CLASS_EMPTY
 | 
			
		||||
            UNIT_EMPTY, ICON_ACCURACY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema(
 | 
			
		||||
            UNIT_PARTS_PER_MILLION, ICON_TEST_TUBE, 1, DEVICE_CLASS_EMPTY
 | 
			
		||||
            UNIT_PARTS_PER_MILLION,
 | 
			
		||||
            ICON_TEST_TUBE,
 | 
			
		||||
            1,
 | 
			
		||||
            DEVICE_CLASS_EMPTY,
 | 
			
		||||
            STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema(
 | 
			
		||||
            UNIT_PARTS_PER_MILLION, ICON_TEST_TUBE, 1, DEVICE_CLASS_EMPTY
 | 
			
		||||
            UNIT_PARTS_PER_MILLION,
 | 
			
		||||
            ICON_TEST_TUBE,
 | 
			
		||||
            1,
 | 
			
		||||
            DEVICE_CLASS_EMPTY,
 | 
			
		||||
            STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def setup_conf(config, key, hub, funcName):
 | 
			
		||||
async def setup_conf(config, key, hub):
 | 
			
		||||
    if key in config:
 | 
			
		||||
        conf = config[key]
 | 
			
		||||
        var = yield sensor.new_sensor(conf)
 | 
			
		||||
        func = getattr(hub, funcName)
 | 
			
		||||
        cg.add(func(var))
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(getattr(hub, f"set_{key}_sensor")(sens))
 | 
			
		||||
        if CONF_SAMPLE_RATE in conf:
 | 
			
		||||
            cg.add(getattr(hub, f"set_{key}_sample_rate")(conf[CONF_SAMPLE_RATE]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = yield cg.get_variable(config[CONF_BME680_BSEC_ID])
 | 
			
		||||
    for key, funcName in TYPES.items():
 | 
			
		||||
        yield setup_conf(config, key, hub, funcName)
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    hub = await cg.get_variable(config[CONF_BME680_BSEC_ID])
 | 
			
		||||
    for key in TYPES:
 | 
			
		||||
        await setup_conf(config, key, hub)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import text_sensor
 | 
			
		||||
from esphome.const import CONF_ID, CONF_ICON
 | 
			
		||||
from esphome.core import coroutine
 | 
			
		||||
from . import BME680BSECComponent, CONF_BME680_BSEC_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["bme680_bsec"]
 | 
			
		||||
@@ -10,7 +9,7 @@ DEPENDENCIES = ["bme680_bsec"]
 | 
			
		||||
CONF_IAQ_ACCURACY = "iaq_accuracy"
 | 
			
		||||
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
 | 
			
		||||
 | 
			
		||||
TYPES = {CONF_IAQ_ACCURACY: "set_iaq_accuracy_text_sensor"}
 | 
			
		||||
TYPES = [CONF_IAQ_ACCURACY]
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
@@ -25,17 +24,15 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def setup_conf(config, key, hub, funcName):
 | 
			
		||||
async def setup_conf(config, key, hub):
 | 
			
		||||
    if key in config:
 | 
			
		||||
        conf = config[key]
 | 
			
		||||
        var = cg.new_Pvariable(conf[CONF_ID])
 | 
			
		||||
        yield text_sensor.register_text_sensor(var, conf)
 | 
			
		||||
        func = getattr(hub, funcName)
 | 
			
		||||
        cg.add(func(var))
 | 
			
		||||
        sens = cg.new_Pvariable(conf[CONF_ID])
 | 
			
		||||
        await text_sensor.register_text_sensor(sens, conf)
 | 
			
		||||
        cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = yield cg.get_variable(config[CONF_BME680_BSEC_ID])
 | 
			
		||||
    for key, funcName in TYPES.items():
 | 
			
		||||
        yield setup_conf(config, key, hub, funcName)
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    hub = await cg.get_variable(config[CONF_BME680_BSEC_ID])
 | 
			
		||||
    for key in TYPES:
 | 
			
		||||
        await setup_conf(config, key, hub)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_PRESSURE,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    UNIT_HECTOPASCAL,
 | 
			
		||||
@@ -24,10 +25,18 @@ CONFIG_SCHEMA = (
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BMP085Component),
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
 | 
			
		||||
                UNIT_CELSIUS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
 | 
			
		||||
                UNIT_HECTOPASCAL,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_PRESSURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
@@ -36,17 +45,17 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_TEMPERATURE]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_temperature(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_PRESSURE in config:
 | 
			
		||||
        conf = config[CONF_PRESSURE]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_pressure(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_PRESSURE,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    UNIT_HECTOPASCAL,
 | 
			
		||||
@@ -45,7 +46,11 @@ CONFIG_SCHEMA = (
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BMP280Component),
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
 | 
			
		||||
                UNIT_CELSIUS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
 | 
			
		||||
@@ -54,7 +59,11 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
 | 
			
		||||
                UNIT_HECTOPASCAL,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                1,
 | 
			
		||||
                DEVICE_CLASS_PRESSURE,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
 | 
			
		||||
@@ -72,19 +81,19 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_TEMPERATURE]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
        cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_PRESSURE in config:
 | 
			
		||||
        conf = config[CONF_PRESSURE]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_pressure_sensor(sens))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.core import CORE, coroutine
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_DATA
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@mvturnho", "@danielschramm"]
 | 
			
		||||
@@ -82,10 +82,9 @@ CANBUS_SCHEMA = cv.Schema(
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def setup_canbus_core_(var, config):
 | 
			
		||||
async def setup_canbus_core_(var, config):
 | 
			
		||||
    validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    cg.add(var.set_can_id([config[CONF_CAN_ID]]))
 | 
			
		||||
    cg.add(var.set_use_extended_id([config[CONF_USE_EXTENDED_ID]]))
 | 
			
		||||
    cg.add(var.set_bitrate(CAN_SPEEDS[config[CONF_BIT_RATE]]))
 | 
			
		||||
@@ -95,17 +94,16 @@ def setup_canbus_core_(var, config):
 | 
			
		||||
        ext_id = conf[CONF_USE_EXTENDED_ID]
 | 
			
		||||
        validate_id(can_id, ext_id)
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id)
 | 
			
		||||
        yield cg.register_component(trigger, conf)
 | 
			
		||||
        yield automation.build_automation(
 | 
			
		||||
        await cg.register_component(trigger, conf)
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            trigger, [(cg.std_vector.template(cg.uint8), "x")], conf
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def register_canbus(var, config):
 | 
			
		||||
async def register_canbus(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
        var = cg.new_Pvariable(config[CONF_ID], var)
 | 
			
		||||
    yield setup_canbus_core_(var, config)
 | 
			
		||||
    await setup_canbus_core_(var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Actions
 | 
			
		||||
@@ -122,16 +120,16 @@ def register_canbus(var, config):
 | 
			
		||||
        key=CONF_DATA,
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
def canbus_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
async def canbus_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg)
 | 
			
		||||
    yield cg.register_parented(var, config[CONF_CANBUS_ID])
 | 
			
		||||
    await cg.register_parented(var, config[CONF_CANBUS_ID])
 | 
			
		||||
 | 
			
		||||
    if CONF_CAN_ID in config:
 | 
			
		||||
        can_id = yield cg.templatable(config[CONF_CAN_ID], args, cg.uint32)
 | 
			
		||||
        can_id = await cg.templatable(config[CONF_CAN_ID], args, cg.uint32)
 | 
			
		||||
        cg.add(var.set_can_id(can_id))
 | 
			
		||||
 | 
			
		||||
    use_extended_id = yield cg.templatable(
 | 
			
		||||
    use_extended_id = await cg.templatable(
 | 
			
		||||
        config[CONF_USE_EXTENDED_ID], args, cg.uint32
 | 
			
		||||
    )
 | 
			
		||||
    cg.add(var.set_use_extended_id(use_extended_id))
 | 
			
		||||
@@ -140,8 +138,8 @@ def canbus_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    if isinstance(data, bytes):
 | 
			
		||||
        data = [int(x) for x in data]
 | 
			
		||||
    if cg.is_template(data):
 | 
			
		||||
        templ = yield cg.templatable(data, args, cg.std_vector.template(cg.uint8))
 | 
			
		||||
        templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8))
 | 
			
		||||
        cg.add(var.set_data_template(templ))
 | 
			
		||||
    else:
 | 
			
		||||
        cg.add(var.set_data_static(data))
 | 
			
		||||
    yield var
 | 
			
		||||
    return var
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,9 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(64.0)
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    paren = yield cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
 | 
			
		||||
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID], paren)
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    cg.add_define("USE_CAPTIVE_PORTAL")
 | 
			
		||||
 
 | 
			
		||||
@@ -64,32 +64,11 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
 | 
			
		||||
  ESP_LOGI(TAG, "Captive Portal Requested WiFi Settings Change:");
 | 
			
		||||
  ESP_LOGI(TAG, "  SSID='%s'", ssid.c_str());
 | 
			
		||||
  ESP_LOGI(TAG, "  Password=" LOG_SECRET("'%s'"), psk.c_str());
 | 
			
		||||
  this->override_sta_(ssid, psk);
 | 
			
		||||
  wifi::global_wifi_component->save_wifi_sta(ssid, psk);
 | 
			
		||||
  request->redirect("/?save=true");
 | 
			
		||||
}
 | 
			
		||||
void CaptivePortal::override_sta_(const std::string &ssid, const std::string &password) {
 | 
			
		||||
  CaptivePortalSettings save{};
 | 
			
		||||
  strcpy(save.ssid, ssid.c_str());
 | 
			
		||||
  strcpy(save.password, password.c_str());
 | 
			
		||||
  this->pref_.save(&save);
 | 
			
		||||
 | 
			
		||||
  wifi::WiFiAP sta{};
 | 
			
		||||
  sta.set_ssid(ssid);
 | 
			
		||||
  sta.set_password(password);
 | 
			
		||||
  wifi::global_wifi_component->set_sta(sta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CaptivePortal::setup() {
 | 
			
		||||
  // Hash with compilation time
 | 
			
		||||
  // This ensures the AP override is not applied for OTA
 | 
			
		||||
  uint32_t hash = fnv1_hash(App.get_compilation_time());
 | 
			
		||||
  this->pref_ = global_preferences.make_preference<CaptivePortalSettings>(hash, true);
 | 
			
		||||
 | 
			
		||||
  CaptivePortalSettings save{};
 | 
			
		||||
  if (this->pref_.load(&save)) {
 | 
			
		||||
    this->override_sta_(save.ssid, save.password);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void CaptivePortal::setup() {}
 | 
			
		||||
void CaptivePortal::start() {
 | 
			
		||||
  this->base_->init();
 | 
			
		||||
  if (!this->initialized_) {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,11 +10,6 @@ namespace esphome {
 | 
			
		||||
 | 
			
		||||
namespace captive_portal {
 | 
			
		||||
 | 
			
		||||
struct CaptivePortalSettings {
 | 
			
		||||
  char ssid[33];
 | 
			
		||||
  char password[65];
 | 
			
		||||
} PACKED;  // NOLINT
 | 
			
		||||
 | 
			
		||||
class CaptivePortal : public AsyncWebHandler, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  CaptivePortal(web_server_base::WebServerBase *base);
 | 
			
		||||
@@ -67,12 +62,9 @@ class CaptivePortal : public AsyncWebHandler, public Component {
 | 
			
		||||
  void handleRequest(AsyncWebServerRequest *req) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void override_sta_(const std::string &ssid, const std::string &password);
 | 
			
		||||
 | 
			
		||||
  web_server_base::WebServerBase *base_;
 | 
			
		||||
  bool initialized_{false};
 | 
			
		||||
  bool active_{false};
 | 
			
		||||
  ESPPreferenceObject pref_;
 | 
			
		||||
  DNSServer *dns_server_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
    ICON_RADIATOR,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_PARTS_PER_MILLION,
 | 
			
		||||
    UNIT_PARTS_PER_BILLION,
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
@@ -28,10 +29,18 @@ CONFIG_SCHEMA = (
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(CCS811Component),
 | 
			
		||||
            cv.Required(CONF_ECO2): sensor.sensor_schema(
 | 
			
		||||
                UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY
 | 
			
		||||
                UNIT_PARTS_PER_MILLION,
 | 
			
		||||
                ICON_MOLECULE_CO2,
 | 
			
		||||
                0,
 | 
			
		||||
                DEVICE_CLASS_EMPTY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Required(CONF_TVOC): sensor.sensor_schema(
 | 
			
		||||
                UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0, DEVICE_CLASS_EMPTY
 | 
			
		||||
                UNIT_PARTS_PER_BILLION,
 | 
			
		||||
                ICON_RADIATOR,
 | 
			
		||||
                0,
 | 
			
		||||
                DEVICE_CLASS_EMPTY,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_BASELINE): cv.hex_uint16_t,
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
 | 
			
		||||
@@ -43,22 +52,22 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    sens = yield sensor.new_sensor(config[CONF_ECO2])
 | 
			
		||||
    sens = await sensor.new_sensor(config[CONF_ECO2])
 | 
			
		||||
    cg.add(var.set_co2(sens))
 | 
			
		||||
    sens = yield sensor.new_sensor(config[CONF_TVOC])
 | 
			
		||||
    sens = await sensor.new_sensor(config[CONF_TVOC])
 | 
			
		||||
    cg.add(var.set_tvoc(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_BASELINE in config:
 | 
			
		||||
        cg.add(var.set_baseline(config[CONF_BASELINE]))
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        sens = yield cg.get_variable(config[CONF_TEMPERATURE])
 | 
			
		||||
        sens = await cg.get_variable(config[CONF_TEMPERATURE])
 | 
			
		||||
        cg.add(var.set_temperature(sens))
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        sens = yield cg.get_variable(config[CONF_HUMIDITY])
 | 
			
		||||
        sens = await cg.get_variable(config[CONF_HUMIDITY])
 | 
			
		||||
        cg.add(var.set_humidity(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,14 @@ from esphome import automation
 | 
			
		||||
from esphome.components import mqtt
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_AWAY,
 | 
			
		||||
    CONF_CUSTOM_FAN_MODE,
 | 
			
		||||
    CONF_CUSTOM_PRESET,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_INTERNAL,
 | 
			
		||||
    CONF_MAX_TEMPERATURE,
 | 
			
		||||
    CONF_MIN_TEMPERATURE,
 | 
			
		||||
    CONF_MODE,
 | 
			
		||||
    CONF_PRESET,
 | 
			
		||||
    CONF_TARGET_TEMPERATURE,
 | 
			
		||||
    CONF_TARGET_TEMPERATURE_HIGH,
 | 
			
		||||
    CONF_TARGET_TEMPERATURE_LOW,
 | 
			
		||||
@@ -19,7 +22,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_FAN_MODE,
 | 
			
		||||
    CONF_SWING_MODE,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine, coroutine_with_priority
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
 | 
			
		||||
IS_PLATFORM_COMPONENT = True
 | 
			
		||||
 | 
			
		||||
@@ -33,11 +36,12 @@ ClimateTraits = climate_ns.class_("ClimateTraits")
 | 
			
		||||
ClimateMode = climate_ns.enum("ClimateMode")
 | 
			
		||||
CLIMATE_MODES = {
 | 
			
		||||
    "OFF": ClimateMode.CLIMATE_MODE_OFF,
 | 
			
		||||
    "AUTO": ClimateMode.CLIMATE_MODE_AUTO,
 | 
			
		||||
    "HEAT_COOL": ClimateMode.CLIMATE_HEAT_COOL,
 | 
			
		||||
    "COOL": ClimateMode.CLIMATE_MODE_COOL,
 | 
			
		||||
    "HEAT": ClimateMode.CLIMATE_MODE_HEAT,
 | 
			
		||||
    "DRY": ClimateMode.CLIMATE_MODE_DRY,
 | 
			
		||||
    "FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY,
 | 
			
		||||
    "AUTO": ClimateMode.CLIMATE_MODE_AUTO,
 | 
			
		||||
}
 | 
			
		||||
validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
 | 
			
		||||
 | 
			
		||||
@@ -56,6 +60,19 @@ CLIMATE_FAN_MODES = {
 | 
			
		||||
 | 
			
		||||
validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True)
 | 
			
		||||
 | 
			
		||||
ClimatePreset = climate_ns.enum("ClimatePreset")
 | 
			
		||||
CLIMATE_PRESETS = {
 | 
			
		||||
    "ECO": ClimatePreset.CLIMATE_PRESET_ECO,
 | 
			
		||||
    "AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
 | 
			
		||||
    "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
 | 
			
		||||
    "COMFORT": ClimatePreset.CLIMATE_PRESET_COMFORT,
 | 
			
		||||
    "HOME": ClimatePreset.CLIMATE_PRESET_HOME,
 | 
			
		||||
    "SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
 | 
			
		||||
    "ACTIVITY": ClimatePreset.CLIMATE_PRESET_ACTIVITY,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
validate_climate_preset = cv.enum(CLIMATE_PRESETS, upper=True)
 | 
			
		||||
 | 
			
		||||
ClimateSwingMode = climate_ns.enum("ClimateSwingMode")
 | 
			
		||||
CLIMATE_SWING_MODES = {
 | 
			
		||||
    "OFF": ClimateSwingMode.CLIMATE_SWING_OFF,
 | 
			
		||||
@@ -85,8 +102,7 @@ CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def setup_climate_core_(var, config):
 | 
			
		||||
async def setup_climate_core_(var, config):
 | 
			
		||||
    cg.add(var.set_name(config[CONF_NAME]))
 | 
			
		||||
    if CONF_INTERNAL in config:
 | 
			
		||||
        cg.add(var.set_internal(config[CONF_INTERNAL]))
 | 
			
		||||
@@ -100,15 +116,14 @@ def setup_climate_core_(var, config):
 | 
			
		||||
 | 
			
		||||
    if CONF_MQTT_ID in config:
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
 | 
			
		||||
        yield mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def register_climate(var, config):
 | 
			
		||||
async def register_climate(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
        var = cg.Pvariable(config[CONF_ID], var)
 | 
			
		||||
    cg.add(cg.App.register_climate(var))
 | 
			
		||||
    yield setup_climate_core_(var, config)
 | 
			
		||||
    await setup_climate_core_(var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
@@ -119,7 +134,12 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
        cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
 | 
			
		||||
        cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature),
 | 
			
		||||
        cv.Optional(CONF_AWAY): cv.templatable(cv.boolean),
 | 
			
		||||
        cv.Optional(CONF_FAN_MODE): cv.templatable(validate_climate_fan_mode),
 | 
			
		||||
        cv.Exclusive(CONF_FAN_MODE, "fan_mode"): cv.templatable(
 | 
			
		||||
            validate_climate_fan_mode
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Exclusive(CONF_CUSTOM_FAN_MODE, "fan_mode"): cv.string_strict,
 | 
			
		||||
        cv.Exclusive(CONF_PRESET, "preset"): cv.templatable(validate_climate_preset),
 | 
			
		||||
        cv.Exclusive(CONF_CUSTOM_PRESET, "preset"): cv.string_strict,
 | 
			
		||||
        cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
@@ -128,40 +148,49 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "climate.control", ControlAction, CLIMATE_CONTROL_ACTION_SCHEMA
 | 
			
		||||
)
 | 
			
		||||
def climate_control_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
async def climate_control_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
    if CONF_MODE in config:
 | 
			
		||||
        template_ = yield cg.templatable(config[CONF_MODE], args, ClimateMode)
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_MODE], args, ClimateMode)
 | 
			
		||||
        cg.add(var.set_mode(template_))
 | 
			
		||||
    if CONF_TARGET_TEMPERATURE in config:
 | 
			
		||||
        template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float)
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float)
 | 
			
		||||
        cg.add(var.set_target_temperature(template_))
 | 
			
		||||
    if CONF_TARGET_TEMPERATURE_LOW in config:
 | 
			
		||||
        template_ = yield cg.templatable(
 | 
			
		||||
        template_ = await cg.templatable(
 | 
			
		||||
            config[CONF_TARGET_TEMPERATURE_LOW], args, float
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_target_temperature_low(template_))
 | 
			
		||||
    if CONF_TARGET_TEMPERATURE_HIGH in config:
 | 
			
		||||
        template_ = yield cg.templatable(
 | 
			
		||||
        template_ = await cg.templatable(
 | 
			
		||||
            config[CONF_TARGET_TEMPERATURE_HIGH], args, float
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_target_temperature_high(template_))
 | 
			
		||||
    if CONF_AWAY in config:
 | 
			
		||||
        template_ = yield cg.templatable(config[CONF_AWAY], args, bool)
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_AWAY], args, bool)
 | 
			
		||||
        cg.add(var.set_away(template_))
 | 
			
		||||
    if CONF_FAN_MODE in config:
 | 
			
		||||
        template_ = yield cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode)
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode)
 | 
			
		||||
        cg.add(var.set_fan_mode(template_))
 | 
			
		||||
    if CONF_CUSTOM_FAN_MODE in config:
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_CUSTOM_FAN_MODE], args, str)
 | 
			
		||||
        cg.add(var.set_custom_fan_mode(template_))
 | 
			
		||||
    if CONF_PRESET in config:
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_PRESET], args, ClimatePreset)
 | 
			
		||||
        cg.add(var.set_preset(template_))
 | 
			
		||||
    if CONF_CUSTOM_PRESET in config:
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_CUSTOM_PRESET], args, str)
 | 
			
		||||
        cg.add(var.set_custom_preset(template_))
 | 
			
		||||
    if CONF_SWING_MODE in config:
 | 
			
		||||
        template_ = yield cg.templatable(
 | 
			
		||||
        template_ = await cg.templatable(
 | 
			
		||||
            config[CONF_SWING_MODE], args, ClimateSwingMode
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_swing_mode(template_))
 | 
			
		||||
    yield var
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(100.0)
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    cg.add_define("USE_CLIMATE")
 | 
			
		||||
    cg.add_global(climate_ns.using)
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,9 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
 | 
			
		||||
  TEMPLATABLE_VALUE(float, target_temperature_high)
 | 
			
		||||
  TEMPLATABLE_VALUE(bool, away)
 | 
			
		||||
  TEMPLATABLE_VALUE(ClimateFanMode, fan_mode)
 | 
			
		||||
  TEMPLATABLE_VALUE(std::string, custom_fan_mode)
 | 
			
		||||
  TEMPLATABLE_VALUE(ClimatePreset, preset)
 | 
			
		||||
  TEMPLATABLE_VALUE(std::string, custom_preset)
 | 
			
		||||
  TEMPLATABLE_VALUE(ClimateSwingMode, swing_mode)
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override {
 | 
			
		||||
@@ -26,6 +29,9 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
 | 
			
		||||
    call.set_target_temperature_high(this->target_temperature_high_.optional_value(x...));
 | 
			
		||||
    call.set_away(this->away_.optional_value(x...));
 | 
			
		||||
    call.set_fan_mode(this->fan_mode_.optional_value(x...));
 | 
			
		||||
    call.set_fan_mode(this->custom_fan_mode_.optional_value(x...));
 | 
			
		||||
    call.set_preset(this->preset_.optional_value(x...));
 | 
			
		||||
    call.set_preset(this->custom_preset_.optional_value(x...));
 | 
			
		||||
    call.set_swing_mode(this->swing_mode_.optional_value(x...));
 | 
			
		||||
    call.perform();
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
#include "climate.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace climate {
 | 
			
		||||
@@ -13,10 +12,24 @@ void ClimateCall::perform() {
 | 
			
		||||
    const char *mode_s = climate_mode_to_string(*this->mode_);
 | 
			
		||||
    ESP_LOGD(TAG, "  Mode: %s", mode_s);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->custom_fan_mode_.has_value()) {
 | 
			
		||||
    this->fan_mode_.reset();
 | 
			
		||||
    ESP_LOGD(TAG, " Custom Fan: %s", this->custom_fan_mode_.value().c_str());
 | 
			
		||||
  }
 | 
			
		||||
  if (this->fan_mode_.has_value()) {
 | 
			
		||||
    this->custom_fan_mode_.reset();
 | 
			
		||||
    const char *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_);
 | 
			
		||||
    ESP_LOGD(TAG, "  Fan: %s", fan_mode_s);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->custom_preset_.has_value()) {
 | 
			
		||||
    this->preset_.reset();
 | 
			
		||||
    ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset_.value().c_str());
 | 
			
		||||
  }
 | 
			
		||||
  if (this->preset_.has_value()) {
 | 
			
		||||
    this->custom_preset_.reset();
 | 
			
		||||
    const char *preset_s = climate_preset_to_string(*this->preset_);
 | 
			
		||||
    ESP_LOGD(TAG, "  Preset: %s", preset_s);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->swing_mode_.has_value()) {
 | 
			
		||||
    const char *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_);
 | 
			
		||||
    ESP_LOGD(TAG, "  Swing: %s", swing_mode_s);
 | 
			
		||||
@@ -44,13 +57,32 @@ void ClimateCall::validate_() {
 | 
			
		||||
      this->mode_.reset();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (this->fan_mode_.has_value()) {
 | 
			
		||||
  if (this->custom_fan_mode_.has_value()) {
 | 
			
		||||
    auto custom_fan_mode = *this->custom_fan_mode_;
 | 
			
		||||
    if (!traits.supports_custom_fan_mode(custom_fan_mode)) {
 | 
			
		||||
      ESP_LOGW(TAG, "  Fan Mode %s is not supported by this device!", custom_fan_mode.c_str());
 | 
			
		||||
      this->custom_fan_mode_.reset();
 | 
			
		||||
    }
 | 
			
		||||
  } else if (this->fan_mode_.has_value()) {
 | 
			
		||||
    auto fan_mode = *this->fan_mode_;
 | 
			
		||||
    if (!traits.supports_fan_mode(fan_mode)) {
 | 
			
		||||
      ESP_LOGW(TAG, "  Fan Mode %s is not supported by this device!", climate_fan_mode_to_string(fan_mode));
 | 
			
		||||
      this->fan_mode_.reset();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (this->custom_preset_.has_value()) {
 | 
			
		||||
    auto custom_preset = *this->custom_preset_;
 | 
			
		||||
    if (!traits.supports_custom_preset(custom_preset)) {
 | 
			
		||||
      ESP_LOGW(TAG, "  Preset %s is not supported by this device!", custom_preset.c_str());
 | 
			
		||||
      this->custom_preset_.reset();
 | 
			
		||||
    }
 | 
			
		||||
  } else if (this->preset_.has_value()) {
 | 
			
		||||
    auto preset = *this->preset_;
 | 
			
		||||
    if (!traits.supports_preset(preset)) {
 | 
			
		||||
      ESP_LOGW(TAG, "  Preset %s is not supported by this device!", climate_preset_to_string(preset));
 | 
			
		||||
      this->preset_.reset();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (this->swing_mode_.has_value()) {
 | 
			
		||||
    auto swing_mode = *this->swing_mode_;
 | 
			
		||||
    if (!traits.supports_swing_mode(swing_mode)) {
 | 
			
		||||
@@ -117,6 +149,8 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) {
 | 
			
		||||
    this->set_mode(CLIMATE_MODE_FAN_ONLY);
 | 
			
		||||
  } else if (str_equals_case_insensitive(mode, "DRY")) {
 | 
			
		||||
    this->set_mode(CLIMATE_MODE_DRY);
 | 
			
		||||
  } else if (str_equals_case_insensitive(mode, "HEAT_COOL")) {
 | 
			
		||||
    this->set_mode(CLIMATE_MODE_HEAT_COOL);
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode.c_str());
 | 
			
		||||
  }
 | 
			
		||||
@@ -124,6 +158,7 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) {
 | 
			
		||||
}
 | 
			
		||||
ClimateCall &ClimateCall::set_fan_mode(ClimateFanMode fan_mode) {
 | 
			
		||||
  this->fan_mode_ = fan_mode;
 | 
			
		||||
  this->custom_fan_mode_.reset();
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
 | 
			
		||||
@@ -146,11 +181,59 @@ ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
 | 
			
		||||
  } else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) {
 | 
			
		||||
    this->set_fan_mode(CLIMATE_FAN_DIFFUSE);
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str());
 | 
			
		||||
    auto custom_fan_modes = this->parent_->get_traits().get_supported_custom_fan_modes();
 | 
			
		||||
    if (std::find(custom_fan_modes.begin(), custom_fan_modes.end(), fan_mode) != custom_fan_modes.end()) {
 | 
			
		||||
      this->custom_fan_mode_ = fan_mode;
 | 
			
		||||
      this->fan_mode_.reset();
 | 
			
		||||
    } else {
 | 
			
		||||
      ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
ClimateCall &ClimateCall::set_fan_mode(optional<std::string> fan_mode) {
 | 
			
		||||
  if (fan_mode.has_value()) {
 | 
			
		||||
    this->set_fan_mode(fan_mode.value());
 | 
			
		||||
  }
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
ClimateCall &ClimateCall::set_preset(ClimatePreset preset) {
 | 
			
		||||
  this->preset_ = preset;
 | 
			
		||||
  this->custom_preset_.reset();
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
ClimateCall &ClimateCall::set_preset(const std::string &preset) {
 | 
			
		||||
  if (str_equals_case_insensitive(preset, "ECO")) {
 | 
			
		||||
    this->set_preset(CLIMATE_PRESET_ECO);
 | 
			
		||||
  } else if (str_equals_case_insensitive(preset, "AWAY")) {
 | 
			
		||||
    this->set_preset(CLIMATE_PRESET_AWAY);
 | 
			
		||||
  } else if (str_equals_case_insensitive(preset, "BOOST")) {
 | 
			
		||||
    this->set_preset(CLIMATE_PRESET_BOOST);
 | 
			
		||||
  } else if (str_equals_case_insensitive(preset, "COMFORT")) {
 | 
			
		||||
    this->set_preset(CLIMATE_PRESET_COMFORT);
 | 
			
		||||
  } else if (str_equals_case_insensitive(preset, "HOME")) {
 | 
			
		||||
    this->set_preset(CLIMATE_PRESET_HOME);
 | 
			
		||||
  } else if (str_equals_case_insensitive(preset, "SLEEP")) {
 | 
			
		||||
    this->set_preset(CLIMATE_PRESET_SLEEP);
 | 
			
		||||
  } else if (str_equals_case_insensitive(preset, "ACTIVITY")) {
 | 
			
		||||
    this->set_preset(CLIMATE_PRESET_ACTIVITY);
 | 
			
		||||
  } else {
 | 
			
		||||
    auto custom_presets = this->parent_->get_traits().get_supported_custom_presets();
 | 
			
		||||
    if (std::find(custom_presets.begin(), custom_presets.end(), preset) != custom_presets.end()) {
 | 
			
		||||
      this->custom_preset_ = preset;
 | 
			
		||||
      this->preset_.reset();
 | 
			
		||||
    } else {
 | 
			
		||||
      ESP_LOGW(TAG, "'%s' - Unrecognized preset %s", this->parent_->get_name().c_str(), preset.c_str());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
ClimateCall &ClimateCall::set_preset(optional<std::string> preset) {
 | 
			
		||||
  if (preset.has_value()) {
 | 
			
		||||
    this->set_preset(preset.value());
 | 
			
		||||
  }
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ClimateCall &ClimateCall::set_swing_mode(ClimateSwingMode swing_mode) {
 | 
			
		||||
  this->swing_mode_ = swing_mode;
 | 
			
		||||
  return *this;
 | 
			
		||||
@@ -188,6 +271,9 @@ const optional<float> &ClimateCall::get_target_temperature_low() const { return
 | 
			
		||||
const optional<float> &ClimateCall::get_target_temperature_high() const { return this->target_temperature_high_; }
 | 
			
		||||
const optional<bool> &ClimateCall::get_away() const { return this->away_; }
 | 
			
		||||
const optional<ClimateFanMode> &ClimateCall::get_fan_mode() const { return this->fan_mode_; }
 | 
			
		||||
const optional<std::string> &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; }
 | 
			
		||||
const optional<ClimatePreset> &ClimateCall::get_preset() const { return this->preset_; }
 | 
			
		||||
const optional<std::string> &ClimateCall::get_custom_preset() const { return this->custom_preset_; }
 | 
			
		||||
const optional<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; }
 | 
			
		||||
ClimateCall &ClimateCall::set_away(bool away) {
 | 
			
		||||
  this->away_ = away;
 | 
			
		||||
@@ -215,6 +301,12 @@ ClimateCall &ClimateCall::set_mode(optional<ClimateMode> mode) {
 | 
			
		||||
}
 | 
			
		||||
ClimateCall &ClimateCall::set_fan_mode(optional<ClimateFanMode> fan_mode) {
 | 
			
		||||
  this->fan_mode_ = fan_mode;
 | 
			
		||||
  this->custom_fan_mode_.reset();
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
ClimateCall &ClimateCall::set_preset(optional<ClimatePreset> preset) {
 | 
			
		||||
  this->preset_ = preset;
 | 
			
		||||
  this->custom_preset_.reset();
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
ClimateCall &ClimateCall::set_swing_mode(optional<ClimateSwingMode> swing_mode) {
 | 
			
		||||
@@ -249,8 +341,31 @@ void Climate::save_state_() {
 | 
			
		||||
  if (traits.get_supports_away()) {
 | 
			
		||||
    state.away = this->away;
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_fan_modes()) {
 | 
			
		||||
    state.fan_mode = this->fan_mode;
 | 
			
		||||
  if (traits.get_supports_fan_modes() && fan_mode.has_value()) {
 | 
			
		||||
    state.uses_custom_fan_mode = false;
 | 
			
		||||
    state.fan_mode = this->fan_mode.value();
 | 
			
		||||
  }
 | 
			
		||||
  if (!traits.get_supported_custom_fan_modes().empty() && custom_fan_mode.has_value()) {
 | 
			
		||||
    state.uses_custom_fan_mode = true;
 | 
			
		||||
    auto &custom_fan_modes = traits.get_supported_custom_fan_modes();
 | 
			
		||||
    auto it = std::find(custom_fan_modes.begin(), custom_fan_modes.end(), this->custom_fan_mode.value());
 | 
			
		||||
    // only set custom fan mode if value exists, otherwise leave it as is
 | 
			
		||||
    if (it != custom_fan_modes.cend()) {
 | 
			
		||||
      state.custom_fan_mode = std::distance(custom_fan_modes.begin(), it);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_presets() && preset.has_value()) {
 | 
			
		||||
    state.uses_custom_preset = false;
 | 
			
		||||
    state.preset = this->preset.value();
 | 
			
		||||
  }
 | 
			
		||||
  if (!traits.get_supported_custom_presets().empty() && custom_preset.has_value()) {
 | 
			
		||||
    state.uses_custom_preset = true;
 | 
			
		||||
    auto custom_presets = traits.get_supported_custom_presets();
 | 
			
		||||
    auto it = std::find(custom_presets.begin(), custom_presets.end(), this->custom_preset.value());
 | 
			
		||||
    // only set custom preset if value exists, otherwise leave it as is
 | 
			
		||||
    if (it != custom_presets.cend()) {
 | 
			
		||||
      state.custom_preset = std::distance(custom_presets.begin(), it);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_swing_modes()) {
 | 
			
		||||
    state.swing_mode = this->swing_mode;
 | 
			
		||||
@@ -266,8 +381,17 @@ void Climate::publish_state() {
 | 
			
		||||
  if (traits.get_supports_action()) {
 | 
			
		||||
    ESP_LOGD(TAG, "  Action: %s", climate_action_to_string(this->action));
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_fan_modes()) {
 | 
			
		||||
    ESP_LOGD(TAG, "  Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode));
 | 
			
		||||
  if (traits.get_supports_fan_modes() && this->fan_mode.has_value()) {
 | 
			
		||||
    ESP_LOGD(TAG, "  Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode.value()));
 | 
			
		||||
  }
 | 
			
		||||
  if (!traits.get_supported_custom_fan_modes().empty() && this->custom_fan_mode.has_value()) {
 | 
			
		||||
    ESP_LOGD(TAG, "  Custom Fan Mode: %s", this->custom_fan_mode.value().c_str());
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_presets() && this->preset.has_value()) {
 | 
			
		||||
    ESP_LOGD(TAG, "  Preset: %s", climate_preset_to_string(this->preset.value()));
 | 
			
		||||
  }
 | 
			
		||||
  if (!traits.get_supported_custom_presets().empty() && this->custom_preset.has_value()) {
 | 
			
		||||
    ESP_LOGD(TAG, "  Custom Preset: %s", this->custom_preset.value().c_str());
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_swing_modes()) {
 | 
			
		||||
    ESP_LOGD(TAG, "  Swing Mode: %s", climate_swing_mode_to_string(this->swing_mode));
 | 
			
		||||
@@ -332,9 +456,12 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) {
 | 
			
		||||
  if (traits.get_supports_away()) {
 | 
			
		||||
    call.set_away(this->away);
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_fan_modes()) {
 | 
			
		||||
  if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) {
 | 
			
		||||
    call.set_fan_mode(this->fan_mode);
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
 | 
			
		||||
    call.set_preset(this->preset);
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_swing_modes()) {
 | 
			
		||||
    call.set_swing_mode(this->swing_mode);
 | 
			
		||||
  }
 | 
			
		||||
@@ -352,9 +479,21 @@ void ClimateDeviceRestoreState::apply(Climate *climate) {
 | 
			
		||||
  if (traits.get_supports_away()) {
 | 
			
		||||
    climate->away = this->away;
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_fan_modes()) {
 | 
			
		||||
  if (traits.get_supports_fan_modes() && !this->uses_custom_fan_mode) {
 | 
			
		||||
    climate->fan_mode = this->fan_mode;
 | 
			
		||||
  }
 | 
			
		||||
  if (!traits.get_supported_custom_fan_modes().empty() && this->uses_custom_fan_mode) {
 | 
			
		||||
    climate->custom_fan_mode = traits.get_supported_custom_fan_modes()[this->custom_fan_mode];
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_presets() && !this->uses_custom_preset) {
 | 
			
		||||
    climate->preset = this->preset;
 | 
			
		||||
  }
 | 
			
		||||
  if (!traits.get_supported_custom_presets().empty() && this->uses_custom_preset) {
 | 
			
		||||
    climate->custom_preset = traits.get_supported_custom_presets()[this->custom_preset];
 | 
			
		||||
  }
 | 
			
		||||
  if (!traits.get_supported_custom_presets().empty() && uses_custom_preset) {
 | 
			
		||||
    climate->custom_preset = traits.get_supported_custom_presets()[this->preset];
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_swing_modes()) {
 | 
			
		||||
    climate->swing_mode = this->swing_mode;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/preferences.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "climate_mode.h"
 | 
			
		||||
#include "climate_traits.h"
 | 
			
		||||
 | 
			
		||||
@@ -70,12 +71,22 @@ class ClimateCall {
 | 
			
		||||
  ClimateCall &set_fan_mode(optional<ClimateFanMode> fan_mode);
 | 
			
		||||
  /// Set the fan mode of the climate device based on a string.
 | 
			
		||||
  ClimateCall &set_fan_mode(const std::string &fan_mode);
 | 
			
		||||
  /// Set the fan mode of the climate device based on a string.
 | 
			
		||||
  ClimateCall &set_fan_mode(optional<std::string> fan_mode);
 | 
			
		||||
  /// Set the swing mode of the climate device.
 | 
			
		||||
  ClimateCall &set_swing_mode(ClimateSwingMode swing_mode);
 | 
			
		||||
  /// Set the swing mode of the climate device.
 | 
			
		||||
  ClimateCall &set_swing_mode(optional<ClimateSwingMode> swing_mode);
 | 
			
		||||
  /// Set the swing mode of the climate device based on a string.
 | 
			
		||||
  ClimateCall &set_swing_mode(const std::string &swing_mode);
 | 
			
		||||
  /// Set the preset of the climate device.
 | 
			
		||||
  ClimateCall &set_preset(ClimatePreset preset);
 | 
			
		||||
  /// Set the preset of the climate device.
 | 
			
		||||
  ClimateCall &set_preset(optional<ClimatePreset> preset);
 | 
			
		||||
  /// Set the preset of the climate device based on a string.
 | 
			
		||||
  ClimateCall &set_preset(const std::string &preset);
 | 
			
		||||
  /// Set the preset of the climate device based on a string.
 | 
			
		||||
  ClimateCall &set_preset(optional<std::string> preset);
 | 
			
		||||
 | 
			
		||||
  void perform();
 | 
			
		||||
 | 
			
		||||
@@ -86,6 +97,9 @@ class ClimateCall {
 | 
			
		||||
  const optional<bool> &get_away() const;
 | 
			
		||||
  const optional<ClimateFanMode> &get_fan_mode() const;
 | 
			
		||||
  const optional<ClimateSwingMode> &get_swing_mode() const;
 | 
			
		||||
  const optional<std::string> &get_custom_fan_mode() const;
 | 
			
		||||
  const optional<ClimatePreset> &get_preset() const;
 | 
			
		||||
  const optional<std::string> &get_custom_preset() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void validate_();
 | 
			
		||||
@@ -98,13 +112,25 @@ class ClimateCall {
 | 
			
		||||
  optional<bool> away_;
 | 
			
		||||
  optional<ClimateFanMode> fan_mode_;
 | 
			
		||||
  optional<ClimateSwingMode> swing_mode_;
 | 
			
		||||
  optional<std::string> custom_fan_mode_;
 | 
			
		||||
  optional<ClimatePreset> preset_;
 | 
			
		||||
  optional<std::string> custom_preset_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Struct used to save the state of the climate device in restore memory.
 | 
			
		||||
struct ClimateDeviceRestoreState {
 | 
			
		||||
  ClimateMode mode;
 | 
			
		||||
  bool away;
 | 
			
		||||
  ClimateFanMode fan_mode;
 | 
			
		||||
  bool uses_custom_fan_mode{false};
 | 
			
		||||
  union {
 | 
			
		||||
    ClimateFanMode fan_mode;
 | 
			
		||||
    uint8_t custom_fan_mode;
 | 
			
		||||
  };
 | 
			
		||||
  bool uses_custom_preset{false};
 | 
			
		||||
  union {
 | 
			
		||||
    ClimatePreset preset;
 | 
			
		||||
    uint8_t custom_preset;
 | 
			
		||||
  };
 | 
			
		||||
  ClimateSwingMode swing_mode;
 | 
			
		||||
  union {
 | 
			
		||||
    float target_temperature;
 | 
			
		||||
@@ -168,11 +194,20 @@ class Climate : public Nameable {
 | 
			
		||||
  bool away{false};
 | 
			
		||||
 | 
			
		||||
  /// The active fan mode of the climate device.
 | 
			
		||||
  ClimateFanMode fan_mode;
 | 
			
		||||
  optional<ClimateFanMode> fan_mode;
 | 
			
		||||
 | 
			
		||||
  /// The active swing mode of the climate device.
 | 
			
		||||
  ClimateSwingMode swing_mode;
 | 
			
		||||
 | 
			
		||||
  /// The active custom fan mode of the climate device.
 | 
			
		||||
  optional<std::string> custom_fan_mode;
 | 
			
		||||
 | 
			
		||||
  /// The active preset of the climate device.
 | 
			
		||||
  optional<ClimatePreset> preset;
 | 
			
		||||
 | 
			
		||||
  /// The active custom preset mode of the climate device.
 | 
			
		||||
  optional<std::string> custom_preset;
 | 
			
		||||
 | 
			
		||||
  /** Add a callback for the climate device state, each time the state of the climate device is updated
 | 
			
		||||
   * (using publish_state), this callback will be called.
 | 
			
		||||
   *
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,8 @@ const char *climate_mode_to_string(ClimateMode mode) {
 | 
			
		||||
      return "FAN_ONLY";
 | 
			
		||||
    case CLIMATE_MODE_DRY:
 | 
			
		||||
      return "DRY";
 | 
			
		||||
    case CLIMATE_MODE_HEAT_COOL:
 | 
			
		||||
      return "HEAT_COOL";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
@@ -80,5 +82,26 @@ const char *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *climate_preset_to_string(ClimatePreset preset) {
 | 
			
		||||
  switch (preset) {
 | 
			
		||||
    case climate::CLIMATE_PRESET_ECO:
 | 
			
		||||
      return "ECO";
 | 
			
		||||
    case climate::CLIMATE_PRESET_AWAY:
 | 
			
		||||
      return "AWAY";
 | 
			
		||||
    case climate::CLIMATE_PRESET_BOOST:
 | 
			
		||||
      return "BOOST";
 | 
			
		||||
    case climate::CLIMATE_PRESET_COMFORT:
 | 
			
		||||
      return "COMFORT";
 | 
			
		||||
    case climate::CLIMATE_PRESET_HOME:
 | 
			
		||||
      return "HOME";
 | 
			
		||||
    case climate::CLIMATE_PRESET_SLEEP:
 | 
			
		||||
      return "SLEEP";
 | 
			
		||||
    case climate::CLIMATE_PRESET_ACTIVITY:
 | 
			
		||||
      return "ACTIVITY";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace climate
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ enum ClimateMode : uint8_t {
 | 
			
		||||
  /// The climate device is off (not in auto, heat or cool mode)
 | 
			
		||||
  CLIMATE_MODE_OFF = 0,
 | 
			
		||||
  /// The climate device is set to automatically change the heating/cooling cycle
 | 
			
		||||
  CLIMATE_MODE_AUTO = 1,
 | 
			
		||||
  CLIMATE_MODE_HEAT_COOL = 1,
 | 
			
		||||
  /// The climate device is manually set to cool mode (not in auto mode!)
 | 
			
		||||
  CLIMATE_MODE_COOL = 2,
 | 
			
		||||
  /// The climate device is manually set to heat mode (not in auto mode!)
 | 
			
		||||
@@ -19,6 +19,8 @@ enum ClimateMode : uint8_t {
 | 
			
		||||
  CLIMATE_MODE_FAN_ONLY = 4,
 | 
			
		||||
  /// The climate device is manually set to dry mode
 | 
			
		||||
  CLIMATE_MODE_DRY = 5,
 | 
			
		||||
  /// The climate device is manually set to heat-cool mode
 | 
			
		||||
  CLIMATE_MODE_AUTO = 6
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Enum for the current action of the climate device. Values match those of ClimateMode.
 | 
			
		||||
@@ -61,7 +63,7 @@ enum ClimateFanMode : uint8_t {
 | 
			
		||||
 | 
			
		||||
/// Enum for all modes a climate swing can be in
 | 
			
		||||
enum ClimateSwingMode : uint8_t {
 | 
			
		||||
  /// The sing mode is set to Off
 | 
			
		||||
  /// The swing mode is set to Off
 | 
			
		||||
  CLIMATE_SWING_OFF = 0,
 | 
			
		||||
  /// The fan mode is set to Both
 | 
			
		||||
  CLIMATE_SWING_BOTH = 1,
 | 
			
		||||
@@ -71,6 +73,24 @@ enum ClimateSwingMode : uint8_t {
 | 
			
		||||
  CLIMATE_SWING_HORIZONTAL = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Enum for all modes a climate swing can be in
 | 
			
		||||
enum ClimatePreset : uint8_t {
 | 
			
		||||
  /// Preset is set to ECO
 | 
			
		||||
  CLIMATE_PRESET_ECO = 0,
 | 
			
		||||
  /// Preset is set to AWAY
 | 
			
		||||
  CLIMATE_PRESET_AWAY = 1,
 | 
			
		||||
  /// Preset is set to BOOST
 | 
			
		||||
  CLIMATE_PRESET_BOOST = 2,
 | 
			
		||||
  /// Preset is set to COMFORT
 | 
			
		||||
  CLIMATE_PRESET_COMFORT = 3,
 | 
			
		||||
  /// Preset is set to HOME
 | 
			
		||||
  CLIMATE_PRESET_HOME = 4,
 | 
			
		||||
  /// Preset is set to SLEEP
 | 
			
		||||
  CLIMATE_PRESET_SLEEP = 5,
 | 
			
		||||
  /// Preset is set to ACTIVITY
 | 
			
		||||
  CLIMATE_PRESET_ACTIVITY = 6,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Convert the given ClimateMode to a human-readable string.
 | 
			
		||||
const char *climate_mode_to_string(ClimateMode mode);
 | 
			
		||||
 | 
			
		||||
@@ -83,5 +103,8 @@ const char *climate_fan_mode_to_string(ClimateFanMode mode);
 | 
			
		||||
/// Convert the given ClimateSwingMode to a human-readable string.
 | 
			
		||||
const char *climate_swing_mode_to_string(ClimateSwingMode mode);
 | 
			
		||||
 | 
			
		||||
/// Convert the given ClimateSwingMode to a human-readable string.
 | 
			
		||||
const char *climate_preset_to_string(ClimatePreset preset);
 | 
			
		||||
 | 
			
		||||
}  // namespace climate
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -119,6 +119,71 @@ bool ClimateTraits::get_supports_fan_modes() const {
 | 
			
		||||
         this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ ||
 | 
			
		||||
         this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_;
 | 
			
		||||
}
 | 
			
		||||
void ClimateTraits::set_supported_custom_fan_modes(std::vector<std::string> &supported_custom_fan_modes) {
 | 
			
		||||
  this->supported_custom_fan_modes_ = supported_custom_fan_modes;
 | 
			
		||||
}
 | 
			
		||||
const std::vector<std::string> ClimateTraits::get_supported_custom_fan_modes() const {
 | 
			
		||||
  return this->supported_custom_fan_modes_;
 | 
			
		||||
}
 | 
			
		||||
bool ClimateTraits::supports_custom_fan_mode(std::string &custom_fan_mode) const {
 | 
			
		||||
  return std::count(this->supported_custom_fan_modes_.begin(), this->supported_custom_fan_modes_.end(),
 | 
			
		||||
                    custom_fan_mode);
 | 
			
		||||
}
 | 
			
		||||
bool ClimateTraits::supports_preset(ClimatePreset preset) const {
 | 
			
		||||
  switch (preset) {
 | 
			
		||||
    case climate::CLIMATE_PRESET_ECO:
 | 
			
		||||
      return this->supports_preset_eco_;
 | 
			
		||||
    case climate::CLIMATE_PRESET_AWAY:
 | 
			
		||||
      return this->supports_preset_away_;
 | 
			
		||||
    case climate::CLIMATE_PRESET_BOOST:
 | 
			
		||||
      return this->supports_preset_boost_;
 | 
			
		||||
    case climate::CLIMATE_PRESET_COMFORT:
 | 
			
		||||
      return this->supports_preset_comfort_;
 | 
			
		||||
    case climate::CLIMATE_PRESET_HOME:
 | 
			
		||||
      return this->supports_preset_home_;
 | 
			
		||||
    case climate::CLIMATE_PRESET_SLEEP:
 | 
			
		||||
      return this->supports_preset_sleep_;
 | 
			
		||||
    case climate::CLIMATE_PRESET_ACTIVITY:
 | 
			
		||||
      return this->supports_preset_activity_;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void ClimateTraits::set_supports_preset_eco(bool supports_preset_eco) {
 | 
			
		||||
  this->supports_preset_eco_ = supports_preset_eco;
 | 
			
		||||
}
 | 
			
		||||
void ClimateTraits::set_supports_preset_away(bool supports_preset_away) {
 | 
			
		||||
  this->supports_preset_away_ = supports_preset_away;
 | 
			
		||||
}
 | 
			
		||||
void ClimateTraits::set_supports_preset_boost(bool supports_preset_boost) {
 | 
			
		||||
  this->supports_preset_boost_ = supports_preset_boost;
 | 
			
		||||
}
 | 
			
		||||
void ClimateTraits::set_supports_preset_comfort(bool supports_preset_comfort) {
 | 
			
		||||
  this->supports_preset_comfort_ = supports_preset_comfort;
 | 
			
		||||
}
 | 
			
		||||
void ClimateTraits::set_supports_preset_home(bool supports_preset_home) {
 | 
			
		||||
  this->supports_preset_home_ = supports_preset_home;
 | 
			
		||||
}
 | 
			
		||||
void ClimateTraits::set_supports_preset_sleep(bool supports_preset_sleep) {
 | 
			
		||||
  this->supports_preset_sleep_ = supports_preset_sleep;
 | 
			
		||||
}
 | 
			
		||||
void ClimateTraits::set_supports_preset_activity(bool supports_preset_activity) {
 | 
			
		||||
  this->supports_preset_activity_ = supports_preset_activity;
 | 
			
		||||
}
 | 
			
		||||
bool ClimateTraits::get_supports_presets() const {
 | 
			
		||||
  return this->supports_preset_eco_ || this->supports_preset_away_ || this->supports_preset_boost_ ||
 | 
			
		||||
         this->supports_preset_comfort_ || this->supports_preset_home_ || this->supports_preset_sleep_ ||
 | 
			
		||||
         this->supports_preset_activity_;
 | 
			
		||||
}
 | 
			
		||||
void ClimateTraits::set_supported_custom_presets(std::vector<std::string> &supported_custom_presets) {
 | 
			
		||||
  this->supported_custom_presets_ = supported_custom_presets;
 | 
			
		||||
}
 | 
			
		||||
const std::vector<std::string> ClimateTraits::get_supported_custom_presets() const {
 | 
			
		||||
  return this->supported_custom_presets_;
 | 
			
		||||
}
 | 
			
		||||
bool ClimateTraits::supports_custom_preset(std::string &custom_preset) const {
 | 
			
		||||
  return std::count(this->supported_custom_presets_.begin(), this->supported_custom_presets_.end(), custom_preset);
 | 
			
		||||
}
 | 
			
		||||
void ClimateTraits::set_supports_swing_mode_off(bool supports_swing_mode_off) {
 | 
			
		||||
  this->supports_swing_mode_off_ = supports_swing_mode_off;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "climate_mode.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -65,6 +66,21 @@ class ClimateTraits {
 | 
			
		||||
  void set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse);
 | 
			
		||||
  bool supports_fan_mode(ClimateFanMode fan_mode) const;
 | 
			
		||||
  bool get_supports_fan_modes() const;
 | 
			
		||||
  void set_supported_custom_fan_modes(std::vector<std::string> &supported_custom_fan_modes);
 | 
			
		||||
  const std::vector<std::string> get_supported_custom_fan_modes() const;
 | 
			
		||||
  bool supports_custom_fan_mode(std::string &custom_fan_mode) const;
 | 
			
		||||
  bool supports_preset(ClimatePreset preset) const;
 | 
			
		||||
  void set_supports_preset_eco(bool supports_preset_eco);
 | 
			
		||||
  void set_supports_preset_away(bool supports_preset_away);
 | 
			
		||||
  void set_supports_preset_boost(bool supports_preset_boost);
 | 
			
		||||
  void set_supports_preset_comfort(bool supports_preset_comfort);
 | 
			
		||||
  void set_supports_preset_home(bool supports_preset_home);
 | 
			
		||||
  void set_supports_preset_sleep(bool supports_preset_sleep);
 | 
			
		||||
  void set_supports_preset_activity(bool supports_preset_activity);
 | 
			
		||||
  bool get_supports_presets() const;
 | 
			
		||||
  void set_supported_custom_presets(std::vector<std::string> &supported_custom_presets);
 | 
			
		||||
  const std::vector<std::string> get_supported_custom_presets() const;
 | 
			
		||||
  bool supports_custom_preset(std::string &custom_preset) const;
 | 
			
		||||
  void set_supports_swing_mode_off(bool supports_swing_mode_off);
 | 
			
		||||
  void set_supports_swing_mode_both(bool supports_swing_mode_both);
 | 
			
		||||
  void set_supports_swing_mode_vertical(bool supports_swing_mode_vertical);
 | 
			
		||||
@@ -103,6 +119,15 @@ class ClimateTraits {
 | 
			
		||||
  bool supports_swing_mode_both_{false};
 | 
			
		||||
  bool supports_swing_mode_vertical_{false};
 | 
			
		||||
  bool supports_swing_mode_horizontal_{false};
 | 
			
		||||
  bool supports_preset_eco_{false};
 | 
			
		||||
  bool supports_preset_away_{false};
 | 
			
		||||
  bool supports_preset_boost_{false};
 | 
			
		||||
  bool supports_preset_comfort_{false};
 | 
			
		||||
  bool supports_preset_home_{false};
 | 
			
		||||
  bool supports_preset_sleep_{false};
 | 
			
		||||
  bool supports_preset_activity_{false};
 | 
			
		||||
  std::vector<std::string> supported_custom_fan_modes_;
 | 
			
		||||
  std::vector<std::string> supported_custom_presets_;
 | 
			
		||||
 | 
			
		||||
  float visual_min_temperature_{10};
 | 
			
		||||
  float visual_max_temperature_{30};
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ from esphome.components import (
 | 
			
		||||
)
 | 
			
		||||
from esphome.components.remote_base import CONF_RECEIVER_ID, CONF_TRANSMITTER_ID
 | 
			
		||||
from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR
 | 
			
		||||
from esphome.core import coroutine
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["sensor", "remote_base"]
 | 
			
		||||
CODEOWNERS = ["@glmnet"]
 | 
			
		||||
@@ -39,19 +38,18 @@ CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def register_climate_ir(var, config):
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield climate.register_climate(var, config)
 | 
			
		||||
async def register_climate_ir(var, config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await climate.register_climate(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
 | 
			
		||||
    cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
 | 
			
		||||
    if CONF_SENSOR in config:
 | 
			
		||||
        sens = yield cg.get_variable(config[CONF_SENSOR])
 | 
			
		||||
        sens = await cg.get_variable(config[CONF_SENSOR])
 | 
			
		||||
        cg.add(var.set_sensor(sens))
 | 
			
		||||
    if CONF_RECEIVER_ID in config:
 | 
			
		||||
        receiver = yield cg.get_variable(config[CONF_RECEIVER_ID])
 | 
			
		||||
        receiver = await cg.get_variable(config[CONF_RECEIVER_ID])
 | 
			
		||||
        cg.add(receiver.register_listener(var))
 | 
			
		||||
 | 
			
		||||
    transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID])
 | 
			
		||||
    transmitter = await cg.get_variable(config[CONF_TRANSMITTER_ID])
 | 
			
		||||
    cg.add(var.set_transmitter(transmitter))
 | 
			
		||||
 
 | 
			
		||||
@@ -36,9 +36,9 @@ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield climate_ir.register_climate_ir(var, config)
 | 
			
		||||
    await climate_ir.register_climate_ir(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_header_high(config[CONF_HEADER_HIGH]))
 | 
			
		||||
    cg.add(var.set_header_low(config[CONF_HEADER_LOW]))
 | 
			
		||||
 
 | 
			
		||||
@@ -72,7 +72,7 @@ void LgIrClimate::transmit_state() {
 | 
			
		||||
      remote_state |= FAN_AUTO;
 | 
			
		||||
    } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY ||
 | 
			
		||||
               this->mode == climate::CLIMATE_MODE_HEAT) {
 | 
			
		||||
      switch (this->fan_mode) {
 | 
			
		||||
      switch (this->fan_mode.value()) {
 | 
			
		||||
        case climate::CLIMATE_FAN_HIGH:
 | 
			
		||||
          remote_state |= FAN_MAX;
 | 
			
		||||
          break;
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    r = 0
 | 
			
		||||
    if CONF_RED in config:
 | 
			
		||||
        r = int(config[CONF_RED] * 255)
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,6 @@ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield climate_ir.register_climate_ir(var, config)
 | 
			
		||||
    await climate_ir.register_climate_ir(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -93,7 +93,7 @@ void CoolixClimate::transmit_state() {
 | 
			
		||||
        this->fan_mode = climate::CLIMATE_FAN_AUTO;
 | 
			
		||||
        remote_state |= COOLIX_FAN_MODE_AUTO_DRY;
 | 
			
		||||
      } else {
 | 
			
		||||
        switch (this->fan_mode) {
 | 
			
		||||
        switch (this->fan_mode.value()) {
 | 
			
		||||
          case climate::CLIMATE_FAN_HIGH:
 | 
			
		||||
            remote_state |= COOLIX_FAN_MAX;
 | 
			
		||||
            break;
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_MQTT_ID,
 | 
			
		||||
    CONF_NAME,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine, coroutine_with_priority
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
 | 
			
		||||
IS_PLATFORM_COMPONENT = True
 | 
			
		||||
 | 
			
		||||
@@ -73,8 +73,7 @@ COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def setup_cover_core_(var, config):
 | 
			
		||||
async def setup_cover_core_(var, config):
 | 
			
		||||
    cg.add(var.set_name(config[CONF_NAME]))
 | 
			
		||||
    if CONF_INTERNAL in config:
 | 
			
		||||
        cg.add(var.set_internal(config[CONF_INTERNAL]))
 | 
			
		||||
@@ -83,15 +82,14 @@ def setup_cover_core_(var, config):
 | 
			
		||||
 | 
			
		||||
    if CONF_MQTT_ID in config:
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
 | 
			
		||||
        yield mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def register_cover(var, config):
 | 
			
		||||
async def register_cover(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
        var = cg.Pvariable(config[CONF_ID], var)
 | 
			
		||||
    cg.add(cg.App.register_cover(var))
 | 
			
		||||
    yield setup_cover_core_(var, config)
 | 
			
		||||
    await setup_cover_core_(var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
COVER_ACTION_SCHEMA = maybe_simple_id(
 | 
			
		||||
@@ -102,21 +100,21 @@ COVER_ACTION_SCHEMA = maybe_simple_id(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action("cover.open", OpenAction, COVER_ACTION_SCHEMA)
 | 
			
		||||
def cover_open_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
    yield cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
async def cover_open_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action("cover.close", CloseAction, COVER_ACTION_SCHEMA)
 | 
			
		||||
def cover_close_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
    yield cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
async def cover_close_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action("cover.stop", StopAction, COVER_ACTION_SCHEMA)
 | 
			
		||||
def cover_stop_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
    yield cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
async def cover_stop_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
@@ -131,25 +129,25 @@ COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action("cover.control", ControlAction, COVER_CONTROL_ACTION_SCHEMA)
 | 
			
		||||
def cover_control_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
async def cover_control_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
    if CONF_STOP in config:
 | 
			
		||||
        template_ = yield cg.templatable(config[CONF_STOP], args, bool)
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_STOP], args, bool)
 | 
			
		||||
        cg.add(var.set_stop(template_))
 | 
			
		||||
    if CONF_STATE in config:
 | 
			
		||||
        template_ = yield cg.templatable(config[CONF_STATE], args, float)
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_STATE], args, float)
 | 
			
		||||
        cg.add(var.set_position(template_))
 | 
			
		||||
    if CONF_POSITION in config:
 | 
			
		||||
        template_ = yield cg.templatable(config[CONF_POSITION], args, float)
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_POSITION], args, float)
 | 
			
		||||
        cg.add(var.set_position(template_))
 | 
			
		||||
    if CONF_TILT in config:
 | 
			
		||||
        template_ = yield cg.templatable(config[CONF_TILT], args, float)
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_TILT], args, float)
 | 
			
		||||
        cg.add(var.set_tilt(template_))
 | 
			
		||||
    yield var
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(100.0)
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    cg.add_define("USE_COVER")
 | 
			
		||||
    cg.add_global(cover_ns.using)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/cs5460a/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/cs5460a/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										342
									
								
								esphome/components/cs5460a/cs5460a.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										342
									
								
								esphome/components/cs5460a/cs5460a.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,342 @@
 | 
			
		||||
#include "cs5460a.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace cs5460a {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "cs5460a";
 | 
			
		||||
 | 
			
		||||
void CS5460AComponent::write_register_(enum CS5460ARegister addr, uint32_t value) {
 | 
			
		||||
  this->write_byte(CMD_WRITE | (addr << 1));
 | 
			
		||||
  this->write_byte(value >> 16);
 | 
			
		||||
  this->write_byte(value >> 8);
 | 
			
		||||
  this->write_byte(value >> 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t CS5460AComponent::read_register_(uint8_t addr) {
 | 
			
		||||
  uint32_t value;
 | 
			
		||||
 | 
			
		||||
  this->write_byte(CMD_READ | (addr << 1));
 | 
			
		||||
  value = (uint32_t) this->transfer_byte(CMD_SYNC0) << 16;
 | 
			
		||||
  value |= (uint32_t) this->transfer_byte(CMD_SYNC0) << 8;
 | 
			
		||||
  value |= this->transfer_byte(CMD_SYNC0) << 0;
 | 
			
		||||
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CS5460AComponent::softreset_() {
 | 
			
		||||
  uint32_t pc = ((uint8_t) phase_offset_ & 0x3f) | (phase_offset_ < 0 ? 0x40 : 0);
 | 
			
		||||
  uint32_t config = (1 << 0) |                    /* K = 0b0001 */
 | 
			
		||||
                    (current_hpf_ ? 1 << 5 : 0) | /* IHPF */
 | 
			
		||||
                    (voltage_hpf_ ? 1 << 6 : 0) | /* VHPF */
 | 
			
		||||
                    (pga_gain_ << 16) |           /* Gi */
 | 
			
		||||
                    (pc << 17);                   /* PC */
 | 
			
		||||
  int cnt = 0;
 | 
			
		||||
 | 
			
		||||
  /* Serial resynchronization */
 | 
			
		||||
  this->write_byte(CMD_SYNC1);
 | 
			
		||||
  this->write_byte(CMD_SYNC1);
 | 
			
		||||
  this->write_byte(CMD_SYNC1);
 | 
			
		||||
  this->write_byte(CMD_SYNC0);
 | 
			
		||||
 | 
			
		||||
  /* Reset */
 | 
			
		||||
  this->write_register_(REG_CONFIG, 1 << 7);
 | 
			
		||||
  delay(10);
 | 
			
		||||
  while (cnt++ < 50 && (this->read_register_(REG_CONFIG) & 0x81) != 0x000001)
 | 
			
		||||
    ;
 | 
			
		||||
  if (cnt > 50)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  this->write_register_(REG_CONFIG, config);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CS5460AComponent::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up CS5460A...");
 | 
			
		||||
 | 
			
		||||
  float current_full_scale = (pga_gain_ == CS5460A_PGA_GAIN_10X) ? 0.25 : 0.10;
 | 
			
		||||
  float voltage_full_scale = 0.25;
 | 
			
		||||
  current_multiplier_ = current_full_scale / (fabsf(current_gain_) * 0x1000000);
 | 
			
		||||
  voltage_multiplier_ = voltage_full_scale / (voltage_gain_ * 0x1000000);
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * Calculate power from the Energy register because the Power register
 | 
			
		||||
   * stores instantaneous power which varies a lot in each AC cycle,
 | 
			
		||||
   * while the Energy value is accumulated over the "computation cycle"
 | 
			
		||||
   * which should be an integer number of AC cycles.
 | 
			
		||||
   */
 | 
			
		||||
  power_multiplier_ =
 | 
			
		||||
      (current_full_scale * voltage_full_scale * 4096) / (current_gain_ * voltage_gain_ * samples_ * 0x800000);
 | 
			
		||||
 | 
			
		||||
  pulse_freq_ =
 | 
			
		||||
      (current_full_scale * voltage_full_scale) / (fabsf(current_gain_) * voltage_gain_ * pulse_energy_wh_ * 3600);
 | 
			
		||||
 | 
			
		||||
  hw_init_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CS5460AComponent::hw_init_() {
 | 
			
		||||
  this->spi_setup();
 | 
			
		||||
  this->enable();
 | 
			
		||||
 | 
			
		||||
  if (!this->softreset_()) {
 | 
			
		||||
    this->disable();
 | 
			
		||||
    ESP_LOGE(TAG, "CS5460A reset failed!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint32_t status = this->read_register_(REG_STATUS);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Version: %x", (status >> 6) & 7);
 | 
			
		||||
 | 
			
		||||
  this->write_register_(REG_CYCLE_COUNT, samples_);
 | 
			
		||||
  this->write_register_(REG_PULSE_RATE, lroundf(pulse_freq_ * 32.0f));
 | 
			
		||||
 | 
			
		||||
  /* Use one of the power saving features (assuming external oscillator), reset other CONTROL bits,
 | 
			
		||||
   * sometimes softreset_() is not enough */
 | 
			
		||||
  this->write_register_(REG_CONTROL, 0x000004);
 | 
			
		||||
 | 
			
		||||
  this->restart_();
 | 
			
		||||
  this->disable();
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Init ok");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Doesn't reset the register values etc., just restarts the "computation cycle" */
 | 
			
		||||
void CS5460AComponent::restart_() {
 | 
			
		||||
  int cnt;
 | 
			
		||||
 | 
			
		||||
  this->enable();
 | 
			
		||||
  /* Stop running conversion, wake up if needed */
 | 
			
		||||
  this->write_byte(CMD_POWER_UP);
 | 
			
		||||
  /* Start continuous conversion */
 | 
			
		||||
  this->write_byte(CMD_START_CONT);
 | 
			
		||||
  this->disable();
 | 
			
		||||
 | 
			
		||||
  this->started_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CS5460AComponent::started_() {
 | 
			
		||||
  /*
 | 
			
		||||
   * Try to guess when the next batch of results is going to be ready and
 | 
			
		||||
   * schedule next STATUS check some time before that moment.  This assumes
 | 
			
		||||
   * two things:
 | 
			
		||||
   *   * a new "computation cycle" started just now.  If it started some
 | 
			
		||||
   *     time ago we may be a late next time, but hopefully less late in each
 | 
			
		||||
   *     iteration -- that's why we schedule the next check in some 0.8 of
 | 
			
		||||
   *     the time we actually expect the next reading ready.
 | 
			
		||||
   *   * MCLK rate is 4.096MHz and K == 1.  If there's a CS5460A module in
 | 
			
		||||
   *     use with a different clock this will need to be parametrised.
 | 
			
		||||
   */
 | 
			
		||||
  expect_data_ts_ = millis() + samples_ * 1024 / 4096;
 | 
			
		||||
 | 
			
		||||
  schedule_next_check_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CS5460AComponent::schedule_next_check_() {
 | 
			
		||||
  int32_t time_left = expect_data_ts_ - millis();
 | 
			
		||||
 | 
			
		||||
  /* First try at 0.8 of the actual expected time (if it's in the future) */
 | 
			
		||||
  if (time_left > 0)
 | 
			
		||||
    time_left -= time_left / 5;
 | 
			
		||||
 | 
			
		||||
  if (time_left > -500) {
 | 
			
		||||
    /* But not sooner than in 30ms from now */
 | 
			
		||||
    if (time_left < 30)
 | 
			
		||||
      time_left = 30;
 | 
			
		||||
  } else {
 | 
			
		||||
    /*
 | 
			
		||||
     * If the measurement is more than 0.5s overdue start worrying.  The
 | 
			
		||||
     * device may be stuck because of an overcurrent error or similar,
 | 
			
		||||
     * from now on just retry every 1s.  After 15s try a reset, if it
 | 
			
		||||
     * fails we give up and mark the component "failed".
 | 
			
		||||
     */
 | 
			
		||||
    if (time_left > -15000) {
 | 
			
		||||
      time_left = 1000;
 | 
			
		||||
      this->status_momentary_warning("warning", 1000);
 | 
			
		||||
    } else {
 | 
			
		||||
      ESP_LOGCONFIG(TAG, "Device officially stuck, resetting");
 | 
			
		||||
      this->cancel_timeout("status-check");
 | 
			
		||||
      this->hw_init_();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->set_timeout("status-check", time_left, [this]() {
 | 
			
		||||
    if (!this->check_status_())
 | 
			
		||||
      this->schedule_next_check_();
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CS5460AComponent::check_status_() {
 | 
			
		||||
  this->enable();
 | 
			
		||||
  uint32_t status = this->read_register_(REG_STATUS);
 | 
			
		||||
 | 
			
		||||
  if (!(status & 0xcbf83c)) {
 | 
			
		||||
    this->disable();
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint32_t clear = 1 << 20;
 | 
			
		||||
 | 
			
		||||
  /* TODO: Report if IC=0 but only once as it can't be cleared */
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 2)) {
 | 
			
		||||
    clear |= 1 << 2;
 | 
			
		||||
    ESP_LOGE(TAG, "Low supply detected");
 | 
			
		||||
    this->status_momentary_warning("warning", 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 3)) {
 | 
			
		||||
    clear |= 1 << 3;
 | 
			
		||||
    ESP_LOGE(TAG, "Modulator oscillation on current channel");
 | 
			
		||||
    this->status_momentary_warning("warning", 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 4)) {
 | 
			
		||||
    clear |= 1 << 4;
 | 
			
		||||
    ESP_LOGE(TAG, "Modulator oscillation on voltage channel");
 | 
			
		||||
    this->status_momentary_warning("warning", 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 5)) {
 | 
			
		||||
    clear |= 1 << 5;
 | 
			
		||||
    ESP_LOGE(TAG, "Watch-dog timeout");
 | 
			
		||||
    this->status_momentary_warning("warning", 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 11)) {
 | 
			
		||||
    clear |= 1 << 11;
 | 
			
		||||
    ESP_LOGE(TAG, "EOUT Energy Accumulation Register out of range");
 | 
			
		||||
    this->status_momentary_warning("warning", 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 12)) {
 | 
			
		||||
    clear |= 1 << 12;
 | 
			
		||||
    ESP_LOGE(TAG, "Energy out of range");
 | 
			
		||||
    this->status_momentary_warning("warning", 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 13)) {
 | 
			
		||||
    clear |= 1 << 13;
 | 
			
		||||
    ESP_LOGE(TAG, "RMS voltage out of range");
 | 
			
		||||
    this->status_momentary_warning("warning", 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 14)) {
 | 
			
		||||
    clear |= 1 << 14;
 | 
			
		||||
    ESP_LOGE(TAG, "RMS current out of range");
 | 
			
		||||
    this->status_momentary_warning("warning", 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 15)) {
 | 
			
		||||
    clear |= 1 << 15;
 | 
			
		||||
    ESP_LOGE(TAG, "Power calculation out of range");
 | 
			
		||||
    this->status_momentary_warning("warning", 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 16)) {
 | 
			
		||||
    clear |= 1 << 16;
 | 
			
		||||
    ESP_LOGE(TAG, "Voltage out of range");
 | 
			
		||||
    this->status_momentary_warning("warning", 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 17)) {
 | 
			
		||||
    clear |= 1 << 17;
 | 
			
		||||
    ESP_LOGE(TAG, "Current out of range");
 | 
			
		||||
    this->status_momentary_warning("warning", 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 19)) {
 | 
			
		||||
    clear |= 1 << 19;
 | 
			
		||||
    ESP_LOGE(TAG, "Divide overflowed");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 22)) {
 | 
			
		||||
    bool dir = status & (1 << 21);
 | 
			
		||||
    if (current_gain_ < 0)
 | 
			
		||||
      dir = !dir;
 | 
			
		||||
    ESP_LOGI(TAG, "Energy counter %s pulse", dir ? "negative" : "positive");
 | 
			
		||||
    clear |= 1 << 22;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint32_t raw_current = 0; /* Calm the validators */
 | 
			
		||||
  uint32_t raw_voltage = 0;
 | 
			
		||||
  uint32_t raw_energy = 0;
 | 
			
		||||
 | 
			
		||||
  if (status & (1 << 23)) {
 | 
			
		||||
    clear |= 1 << 23;
 | 
			
		||||
 | 
			
		||||
    if (current_sensor_ != nullptr)
 | 
			
		||||
      raw_current = this->read_register_(REG_IRMS);
 | 
			
		||||
 | 
			
		||||
    if (voltage_sensor_ != nullptr)
 | 
			
		||||
      raw_voltage = this->read_register_(REG_VRMS);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (status & ((1 << 23) | (1 << 5))) {
 | 
			
		||||
    /* Read to clear the WDT bit */
 | 
			
		||||
    raw_energy = this->read_register_(REG_E);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->write_register_(REG_STATUS, clear);
 | 
			
		||||
  this->disable();
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * Schedule the next STATUS check assuming that DRDY was asserted very
 | 
			
		||||
   * recently, then publish the new values.  Do this last for reentrancy in
 | 
			
		||||
   * case the publish triggers a restart() or for whatever reason needs to
 | 
			
		||||
   * cancel the timeout set in schedule_next_check_(), or needs to use SPI.
 | 
			
		||||
   * If the current or power values haven't changed one bit it may be that
 | 
			
		||||
   * the chip somehow forgot to update the registers -- seen happening very
 | 
			
		||||
   * rarely.  In that case don't publish them because the user may have
 | 
			
		||||
   * the input connected to a multiplexer and may have switched channels
 | 
			
		||||
   * since the previous reading and we'd be publishing the stale value for
 | 
			
		||||
   * the new channel.  If the value *was* updated it's very unlikely that
 | 
			
		||||
   * it wouldn't have changed, especially power/energy which are affected
 | 
			
		||||
   * by the noise on both the current and value channels (in case of energy,
 | 
			
		||||
   * accumulated over many conversion cycles.)
 | 
			
		||||
   */
 | 
			
		||||
  if (status & (1 << 23)) {
 | 
			
		||||
    this->started_();
 | 
			
		||||
 | 
			
		||||
    if (current_sensor_ != nullptr && raw_current != prev_raw_current_) {
 | 
			
		||||
      current_sensor_->publish_state(raw_current * current_multiplier_);
 | 
			
		||||
      prev_raw_current_ = raw_current;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (voltage_sensor_ != nullptr)
 | 
			
		||||
      voltage_sensor_->publish_state(raw_voltage * voltage_multiplier_);
 | 
			
		||||
 | 
			
		||||
    if (power_sensor_ != nullptr && raw_energy != prev_raw_energy_) {
 | 
			
		||||
      int32_t raw = (int32_t)(raw_energy << 8) >> 8; /* Sign-extend */
 | 
			
		||||
      power_sensor_->publish_state(raw * power_multiplier_);
 | 
			
		||||
      prev_raw_energy_ = raw_energy;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CS5460AComponent::dump_config() {
 | 
			
		||||
  uint32_t state = this->get_component_state();
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "CS5460A:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Init status: %s",
 | 
			
		||||
                state == COMPONENT_STATE_LOOP ? "OK" : (state == COMPONENT_STATE_FAILED ? "failed" : "other"));
 | 
			
		||||
  LOG_PIN("  CS Pin: ", cs_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Samples / cycle: %u", samples_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Phase offset: %i", phase_offset_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  PGA Gain: %s", pga_gain_ == CS5460A_PGA_GAIN_50X ? "50x" : "10x");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Current gain: %.5f", current_gain_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Voltage gain: %.5f", voltage_gain_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Current HPF: %s", current_hpf_ ? "enabled" : "disabled");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Voltage HPF: %s", voltage_hpf_ ? "enabled" : "disabled");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Pulse energy: %.2f Wh", pulse_energy_wh_);
 | 
			
		||||
  LOG_SENSOR("  ", "Voltage", voltage_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Current", current_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Power", power_sensor_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace cs5460a
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										123
									
								
								esphome/components/cs5460a/cs5460a.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								esphome/components/cs5460a/cs5460a.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/spi/spi.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace cs5460a {
 | 
			
		||||
 | 
			
		||||
enum CS5460ACommand {
 | 
			
		||||
  CMD_SYNC0 = 0xfe,
 | 
			
		||||
  CMD_SYNC1 = 0xff,
 | 
			
		||||
  CMD_START_SINGLE = 0xe0,
 | 
			
		||||
  CMD_START_CONT = 0xe8,
 | 
			
		||||
  CMD_POWER_UP = 0xa0,
 | 
			
		||||
  CMD_POWER_STANDBY = 0x88,
 | 
			
		||||
  CMD_POWER_SLEEP = 0x90,
 | 
			
		||||
  CMD_CALIBRATION = 0xc0,
 | 
			
		||||
  CMD_READ = 0x00,
 | 
			
		||||
  CMD_WRITE = 0x40,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum CS5460ARegister {
 | 
			
		||||
  REG_CONFIG = 0x00,
 | 
			
		||||
  REG_IDCOFF = 0x01,
 | 
			
		||||
  REG_IGN = 0x02,
 | 
			
		||||
  REG_VDCOFF = 0x03,
 | 
			
		||||
  REG_VGN = 0x04,
 | 
			
		||||
  REG_CYCLE_COUNT = 0x05,
 | 
			
		||||
  REG_PULSE_RATE = 0x06,
 | 
			
		||||
  REG_I = 0x07,
 | 
			
		||||
  REG_V = 0x08,
 | 
			
		||||
  REG_P = 0x09,
 | 
			
		||||
  REG_E = 0x0a,
 | 
			
		||||
  REG_IRMS = 0x0b,
 | 
			
		||||
  REG_VRMS = 0x0c,
 | 
			
		||||
  REG_TBC = 0x0d,
 | 
			
		||||
  REG_POFF = 0x0e,
 | 
			
		||||
  REG_STATUS = 0x0f,
 | 
			
		||||
  REG_IACOFF = 0x10,
 | 
			
		||||
  REG_VACOFF = 0x11,
 | 
			
		||||
  REG_MASK = 0x1a,
 | 
			
		||||
  REG_CONTROL = 0x1c,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** Enum listing the current channel aplifiergain settings for the CS5460A.
 | 
			
		||||
 */
 | 
			
		||||
enum CS5460APGAGain {
 | 
			
		||||
  CS5460A_PGA_GAIN_10X = 0b0,
 | 
			
		||||
  CS5460A_PGA_GAIN_50X = 0b1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class CS5460AComponent : public Component,
 | 
			
		||||
                         public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
 | 
			
		||||
                                               spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_samples(uint32_t samples) { samples_ = samples; }
 | 
			
		||||
  void set_phase_offset(int8_t phase_offset) { phase_offset_ = phase_offset; }
 | 
			
		||||
  void set_pga_gain(CS5460APGAGain pga_gain) { pga_gain_ = pga_gain; }
 | 
			
		||||
  void set_gains(float current_gain, float voltage_gain) {
 | 
			
		||||
    current_gain_ = current_gain;
 | 
			
		||||
    voltage_gain_ = voltage_gain;
 | 
			
		||||
  }
 | 
			
		||||
  void set_hpf_enable(bool current_hpf, bool voltage_hpf) {
 | 
			
		||||
    current_hpf_ = current_hpf;
 | 
			
		||||
    voltage_hpf_ = voltage_hpf;
 | 
			
		||||
  }
 | 
			
		||||
  void set_pulse_energy_wh(float pulse_energy_wh) { pulse_energy_wh_ = pulse_energy_wh; }
 | 
			
		||||
  void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
 | 
			
		||||
  void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
 | 
			
		||||
  void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
 | 
			
		||||
 | 
			
		||||
  void restart() { restart_(); }
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void loop() override {}
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint32_t samples_;
 | 
			
		||||
  int8_t phase_offset_;
 | 
			
		||||
  CS5460APGAGain pga_gain_;
 | 
			
		||||
  float current_gain_;
 | 
			
		||||
  float voltage_gain_;
 | 
			
		||||
  bool current_hpf_;
 | 
			
		||||
  bool voltage_hpf_;
 | 
			
		||||
  float pulse_energy_wh_;
 | 
			
		||||
  sensor::Sensor *current_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *voltage_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *power_sensor_{nullptr};
 | 
			
		||||
 | 
			
		||||
  void write_register_(enum CS5460ARegister addr, uint32_t value);
 | 
			
		||||
  uint32_t read_register_(uint8_t addr);
 | 
			
		||||
  bool softreset_();
 | 
			
		||||
  void hw_init_();
 | 
			
		||||
  void restart_();
 | 
			
		||||
  void started_();
 | 
			
		||||
  void schedule_next_check_();
 | 
			
		||||
  bool check_status_();
 | 
			
		||||
 | 
			
		||||
  float current_multiplier_;
 | 
			
		||||
  float voltage_multiplier_;
 | 
			
		||||
  float power_multiplier_;
 | 
			
		||||
  float pulse_freq_;
 | 
			
		||||
  uint32_t expect_data_ts_;
 | 
			
		||||
  uint32_t prev_raw_current_{0};
 | 
			
		||||
  uint32_t prev_raw_energy_{0};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class CS5460ARestartAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  CS5460ARestartAction(CS5460AComponent *cs5460a) : cs5460a_(cs5460a) {}
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override { cs5460a_->restart(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  CS5460AComponent *cs5460a_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace cs5460a
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user