mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Merge pull request #1204 from esphome/bump-1.15.0b1
This commit is contained in:
		
							
								
								
									
										2
									
								
								.coveragerc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.coveragerc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| [run] | ||||
| omit = esphome/components/* | ||||
							
								
								
									
										32
									
								
								.devcontainer/devcontainer.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.devcontainer/devcontainer.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| { | ||||
|   "name": "ESPHome Dev", | ||||
|   "context": "..", | ||||
|   "dockerFile": "../docker/Dockerfile.dev", | ||||
|   "postCreateCommand": "mkdir -p config && pip3 install -e .", | ||||
|   "runArgs": ["--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1"], | ||||
|   "appPort": 6052, | ||||
|   "extensions": [ | ||||
|     "ms-python.python", | ||||
|     "visualstudioexptteam.vscodeintellicode", | ||||
|     "redhat.vscode-yaml" | ||||
|   ], | ||||
|   "settings": { | ||||
|     "python.pythonPath": "/usr/local/bin/python", | ||||
|     "python.linting.pylintEnabled": true, | ||||
|     "python.linting.enabled": true, | ||||
|     "python.formatting.provider": "black", | ||||
|     "editor.formatOnPaste": false, | ||||
|     "editor.formatOnSave": true, | ||||
|     "editor.formatOnType": true, | ||||
|     "files.trimTrailingWhitespace": true, | ||||
|     "terminal.integrated.shell.linux": "/bin/bash", | ||||
|     "yaml.customTags": [ | ||||
|       "!secret scalar", | ||||
|       "!lambda scalar", | ||||
|       "!include_dir_named scalar", | ||||
|       "!include_dir_list scalar", | ||||
|       "!include_dir_merge_list scalar", | ||||
|       "!include_dir_merge_named scalar" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
							
								
								
									
										12
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| blank_issues_enabled: false | ||||
| contact_links: | ||||
|   - name: Issue Tracker | ||||
|     url: https://github.com/esphome/issues | ||||
|     about: Please create bug reports in the dedicated issue tracker. | ||||
|   - name: Feature Request Tracker | ||||
|     url: https://github.com/esphome/feature-requests | ||||
|     about: Please create feature requests in the dedicated feature request tracker. | ||||
|   - name: Frequently Asked Question | ||||
|     url: https://esphome.io/guides/faq.html | ||||
|     about: Please view the FAQ for common questions and what to include in a bug report. | ||||
|      | ||||
							
								
								
									
										9
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| version: 2 | ||||
| updates: | ||||
|   - package-ecosystem: "pip" | ||||
|     directory: "/" | ||||
|     schedule: | ||||
|       interval: "daily" | ||||
|     ignore: | ||||
|       # Hypotehsis is only used for testing and is updated quite often | ||||
|       - dependency-name: hypothesis | ||||
							
								
								
									
										36
									
								
								.github/lock.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								.github/lock.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| # Configuration for Lock Threads - https://github.com/dessant/lock-threads | ||||
|  | ||||
| # Number of days of inactivity before a closed issue or pull request is locked | ||||
| daysUntilLock: 7 | ||||
|  | ||||
| # Skip issues and pull requests created before a given timestamp. Timestamp must | ||||
| # follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable | ||||
| skipCreatedBefore: false | ||||
|  | ||||
| # Issues and pull requests with these labels will be ignored. Set to `[]` to disable | ||||
| exemptLabels: | ||||
|   - keep-open | ||||
|  | ||||
| # Label to add before locking, such as `outdated`. Set to `false` to disable | ||||
| lockLabel: false | ||||
|  | ||||
| # Comment to post before locking. Set to `false` to disable | ||||
| lockComment: false | ||||
|  | ||||
| # Assign `resolved` as the reason for locking. Set to `false` to disable | ||||
| setLockReason: false | ||||
|  | ||||
| # Limit to only `issues` or `pulls` | ||||
| # only: issues | ||||
|  | ||||
| # Optionally, specify configuration settings just for `issues` or `pulls` | ||||
| # issues: | ||||
| #   exemptLabels: | ||||
| #     - help-wanted | ||||
| #   lockLabel: outdated | ||||
|  | ||||
| # pulls: | ||||
| #   daysUntilLock: 30 | ||||
|  | ||||
| # Repository to extend settings from | ||||
| # _extends: repo | ||||
							
								
								
									
										59
									
								
								.github/stale.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								.github/stale.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| # Configuration for probot-stale - https://github.com/probot/stale | ||||
|  | ||||
| # Number of days of inactivity before an Issue or Pull Request becomes stale | ||||
| daysUntilStale: 60 | ||||
|  | ||||
| # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. | ||||
| # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. | ||||
| daysUntilClose: 7 | ||||
|  | ||||
| # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) | ||||
| onlyLabels: [] | ||||
|  | ||||
| # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable | ||||
| exemptLabels: | ||||
|   - not-stale | ||||
|  | ||||
| # Set to true to ignore issues in a project (defaults to false) | ||||
| exemptProjects: false | ||||
|  | ||||
| # Set to true to ignore issues in a milestone (defaults to false) | ||||
| exemptMilestones: true | ||||
|  | ||||
| # Set to true to ignore issues with an assignee (defaults to false) | ||||
| exemptAssignees: false | ||||
|  | ||||
| # Label to use when marking as stale | ||||
| staleLabel: stale | ||||
|  | ||||
| # Comment to post when marking as stale. Set to `false` to disable | ||||
| markComment: > | ||||
|   This issue has been automatically marked as stale because it has not had | ||||
|   recent activity. It will be closed if no further activity occurs. Thank you | ||||
|   for your contributions. | ||||
|  | ||||
| # Comment to post when removing the stale label. | ||||
| # unmarkComment: > | ||||
| #   Your comment here. | ||||
|  | ||||
| # Comment to post when closing a stale Issue or Pull Request. | ||||
| # closeComment: > | ||||
| #   Your comment here. | ||||
|  | ||||
| # Limit the number of actions per hour, from 1-30. Default is 30 | ||||
| limitPerRun: 10 | ||||
|  | ||||
| # Limit to only `issues` or `pulls` | ||||
| only: pulls | ||||
|  | ||||
| # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': | ||||
| # pulls: | ||||
| #   daysUntilStale: 30 | ||||
| #   markComment: > | ||||
| #     This pull request has been automatically marked as stale because it has not had | ||||
| #     recent activity. It will be closed if no further activity occurs. Thank you | ||||
| #     for your contributions. | ||||
|  | ||||
| # issues: | ||||
| #   exemptLabels: | ||||
| #     - confirmed | ||||
							
								
								
									
										54
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| name: CI for docker images | ||||
|  | ||||
| # Only run when docker paths change | ||||
| on: | ||||
|   push: | ||||
|     branches: [dev, beta, master] | ||||
|     paths: | ||||
|       - 'docker/**' | ||||
|       - '.github/workflows/**' | ||||
|  | ||||
|   pull_request: | ||||
|     paths: | ||||
|       - 'docker/**' | ||||
|       - '.github/workflows/**' | ||||
|  | ||||
| jobs: | ||||
|   check-docker: | ||||
|     name: Build docker containers | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       matrix: | ||||
|         arch: [amd64, armv7, aarch64] | ||||
|         build_type: ["hassio", "docker"] | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up env variables | ||||
|         run: | | ||||
|           base_version="2.3.4" | ||||
|  | ||||
|           if [[ "${{ matrix.build_type }}" == "hassio" ]]; then | ||||
|             build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" | ||||
|             build_to="esphome/esphome-hassio-${{ matrix.arch }}" | ||||
|             dockerfile="docker/Dockerfile.hassio" | ||||
|           else | ||||
|             build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}" | ||||
|             build_to="esphome/esphome-${{ matrix.arch }}" | ||||
|             dockerfile="docker/Dockerfile" | ||||
|           fi | ||||
|  | ||||
|           echo "::set-env name=BUILD_FROM::${build_from}" | ||||
|           echo "::set-env name=BUILD_TO::${build_to}" | ||||
|           echo "::set-env name=DOCKERFILE::${dockerfile}" | ||||
|       - name: Pull for cache | ||||
|         run: | | ||||
|           docker pull "${BUILD_TO}:dev" || true | ||||
|       - name: Register QEMU binfmt | ||||
|         run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes | ||||
|       - run: | | ||||
|           docker build \ | ||||
|             --build-arg "BUILD_FROM=${BUILD_FROM}" \ | ||||
|             --build-arg "BUILD_VERSION=ci" \ | ||||
|             --cache-from "${BUILD_TO}:dev" \ | ||||
|             --file "${DOCKERFILE}" \ | ||||
|             . | ||||
							
								
								
									
										215
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | ||||
| # THESE JOBS ARE COPIED IN release.yml and release-dev.yml | ||||
| # PLEASE ALSO UPDATE THOSE FILES WHEN CHANGING LINES HERE | ||||
| name: CI | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     # On dev branch release-dev already performs CI checks | ||||
|     # On other branches the `pull_request` trigger will be used | ||||
|     branches: [beta, master] | ||||
|  | ||||
|   pull_request: | ||||
|  | ||||
| jobs: | ||||
|   # A fast overview job that checks only changed files | ||||
|   overview: | ||||
|     runs-on: ubuntu-latest | ||||
|     container: esphome/esphome-lint:latest | ||||
|     steps: | ||||
|       # Also fetch history and dev branch so that we can check which files changed | ||||
|       - uses: actions/checkout@v2 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: Fetch dev branch | ||||
|         run: git fetch origin dev | ||||
|  | ||||
|       # Cache the .pio directory with (primarily) library dependencies | ||||
|       - name: Cache .pio lib_deps | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: .pio | ||||
|           key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} | ||||
|           restore-keys: | | ||||
|             lint-cpp-pio- | ||||
|       - name: Set up python environment | ||||
|         run: script/setup | ||||
|       # Set up the pio project so that the cpp checks know how files are compiled | ||||
|       # (build flags, libraries etc) | ||||
|       - name: Set up platformio environment | ||||
|         run: pio init --ide atom | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/ci-custom.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/gcc.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/lint-python.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/python.json" | ||||
|       - name: Run a quick lint over all changed files | ||||
|         run: script/quicklint | ||||
|       - name: Suggest changes | ||||
|         run: script/ci-suggest-changes | ||||
|  | ||||
|   lint-clang-format: | ||||
|     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 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       # Cache platformio intermediary files (like libraries etc) | ||||
|       # Note: platformio platform versions should be cached via the esphome-lint image | ||||
|       - name: Cache Platformio | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: .pio | ||||
|           key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} | ||||
|           restore-keys: | | ||||
|             lint-cpp-pio- | ||||
|       # Set up the pio project so that the cpp checks know how files are compiled | ||||
|       # (build flags, libraries etc) | ||||
|       - name: Set up platformio environment | ||||
|         run: pio init --ide atom | ||||
|  | ||||
|       - name: Run clang-format | ||||
|         run: script/clang-format -i | ||||
|       - name: Suggest changes | ||||
|         run: script/ci-suggest-changes | ||||
|  | ||||
|   lint-clang-tidy: | ||||
|     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 | ||||
|     # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files | ||||
|     strategy: | ||||
|       matrix: | ||||
|         split: [1, 2, 3, 4] | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       # Cache platformio intermediary files (like libraries etc) | ||||
|       # Note: platformio platform versions should be cached via the esphome-lint image | ||||
|       - name: Cache Platformio | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: .pio | ||||
|           key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} | ||||
|           restore-keys: | | ||||
|             lint-cpp-pio- | ||||
|       # Set up the pio project so that the cpp checks know how files are compiled | ||||
|       # (build flags, libraries etc) | ||||
|       - name: Set up platformio environment | ||||
|         run: pio init --ide atom | ||||
|  | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/gcc.json" | ||||
|       - name: Run clang-tidy | ||||
|         run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }} | ||||
|       - name: Suggest changes | ||||
|         run: script/ci-suggest-changes | ||||
|  | ||||
|   lint-python: | ||||
|     # Don't use the esphome-lint docker image because it may contain outdated requirements. | ||||
|     # This way, all dependencies are cached via the cache action. | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v2 | ||||
|         with: | ||||
|           python-version: '3.7' | ||||
|       - name: Cache pip modules | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.cache/pip | ||||
|           key: esphome-pip-3.7-${{ hashFiles('setup.py') }} | ||||
|           restore-keys: | | ||||
|             esphome-pip-3.7- | ||||
|       - name: Set up python environment | ||||
|         run: script/setup | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/ci-custom.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/lint-python.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/python.json" | ||||
|       - name: Lint Custom | ||||
|         run: script/ci-custom.py | ||||
|       - name: Lint Python | ||||
|         run: script/lint-python | ||||
|       - name: Lint CODEOWNERS | ||||
|         run: script/build_codeowners.py --check | ||||
|  | ||||
|   test: | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       matrix: | ||||
|           test: | ||||
|           - test1 | ||||
|           - test2 | ||||
|           - test3 | ||||
|           - test4 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v2 | ||||
|         with: | ||||
|           python-version: '3.7' | ||||
|       - name: Cache pip modules | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.cache/pip | ||||
|           key: esphome-pip-3.7-${{ hashFiles('setup.py') }} | ||||
|           restore-keys: | | ||||
|             esphome-pip-3.7- | ||||
|       # Use per test platformio cache because tests have different platform versions | ||||
|       - name: Cache ~/.platformio | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.platformio | ||||
|           key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core_config.py') }} | ||||
|           restore-keys: | | ||||
|             test-home-platformio-${{ matrix.test }}- | ||||
|       - name: Set up environment | ||||
|         run: script/setup | ||||
|  | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/gcc.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/python.json" | ||||
|       - run: esphome tests/${{ matrix.test }}.yaml compile | ||||
|  | ||||
|   pytest: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v2 | ||||
|         with: | ||||
|           python-version: '3.7' | ||||
|       - name: Cache pip modules | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.cache/pip | ||||
|           key: esphome-pip-3.7-${{ hashFiles('setup.py') }} | ||||
|           restore-keys: | | ||||
|             esphome-pip-3.7- | ||||
|       - name: Set up environment | ||||
|         run: script/setup | ||||
|       - name: Install Github Actions annotator | ||||
|         run: pip install pytest-github-actions-annotate-failures | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/python.json" | ||||
|       - name: Run pytest | ||||
|         run: | | ||||
|           pytest \ | ||||
|             -qq \ | ||||
|             --durations=10 \ | ||||
|             -o console_output_style=count \ | ||||
|             tests | ||||
							
								
								
									
										16
									
								
								.github/workflows/matchers/ci-custom.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/matchers/ci-custom.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| { | ||||
|     "problemMatcher": [ | ||||
|         { | ||||
|             "owner": "ci-custom", | ||||
|             "pattern": [ | ||||
|                 { | ||||
|                     "regexp": "^ERROR (.*):(\\d+):(\\d+) - (.*)$", | ||||
|                     "file": 1, | ||||
|                     "line": 2, | ||||
|                     "column": 3, | ||||
|                     "message": 4 | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										17
									
								
								.github/workflows/matchers/clang-tidy.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.github/workflows/matchers/clang-tidy.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| { | ||||
|     "problemMatcher": [ | ||||
|         { | ||||
|             "owner": "clang-tidy", | ||||
|             "pattern": [ | ||||
|                 { | ||||
|                     "regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$", | ||||
|                     "file": 1, | ||||
|                     "line": 2, | ||||
|                     "column": 3, | ||||
|                     "severity": 4, | ||||
|                     "message": 5 | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										18
									
								
								.github/workflows/matchers/gcc.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.github/workflows/matchers/gcc.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| { | ||||
|   "problemMatcher": [ | ||||
|     { | ||||
|       "owner": "gcc", | ||||
|       "severity": "error", | ||||
|       "pattern": [ | ||||
|         { | ||||
|           "regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", | ||||
|           "file": 1, | ||||
|           "line": 2, | ||||
|           "column": 3, | ||||
|           "severity": 4, | ||||
|           "message": 5 | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										28
									
								
								.github/workflows/matchers/lint-python.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								.github/workflows/matchers/lint-python.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| { | ||||
|   "problemMatcher": [ | ||||
|     { | ||||
|       "owner": "flake8", | ||||
|       "severity": "error", | ||||
|       "pattern": [ | ||||
|           { | ||||
|           "regexp": "^(.*):(\\d+) - ([EFCDNW]\\d{3}.*)$", | ||||
|           "file": 1, | ||||
|           "line": 2, | ||||
|           "message": 3 | ||||
|           } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "owner": "pylint", | ||||
|       "severity": "error", | ||||
|       "pattern": [ | ||||
|         { | ||||
|           "regexp": "^(.*):(\\d+) - (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$", | ||||
|           "file": 1, | ||||
|           "line": 2, | ||||
|           "message": 3 | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										18
									
								
								.github/workflows/matchers/python.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.github/workflows/matchers/python.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| { | ||||
|     "problemMatcher": [ | ||||
|         { | ||||
|             "owner": "python", | ||||
|             "pattern": [ | ||||
|                 { | ||||
|                     "regexp": "^\\s*File\\s\\\"(.*)\\\",\\sline\\s(\\d+),\\sin\\s(.*)$", | ||||
|                     "file": 1, | ||||
|                     "line": 2 | ||||
|                 }, | ||||
|                 { | ||||
|                     "regexp": "^\\s*raise\\s(.*)\\(\\'(.*)\\'\\)$", | ||||
|                     "message": 2 | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										262
									
								
								.github/workflows/release-dev.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								.github/workflows/release-dev.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,262 @@ | ||||
| name: Publish dev releases to docker hub | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|     - dev | ||||
|  | ||||
| jobs: | ||||
|   # THE LINT/TEST JOBS ARE COPIED FROM ci.yaml | ||||
|  | ||||
|   lint-clang-format: | ||||
|     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 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       # Cache platformio intermediary files (like libraries etc) | ||||
|       # Note: platformio platform versions should be cached via the esphome-lint image | ||||
|       - name: Cache Platformio | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: .pio | ||||
|           key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} | ||||
|           restore-keys: | | ||||
|             lint-cpp-pio- | ||||
|       # Set up the pio project so that the cpp checks know how files are compiled | ||||
|       # (build flags, libraries etc) | ||||
|       - name: Set up platformio environment | ||||
|         run: pio init --ide atom | ||||
|  | ||||
|       - name: Run clang-format | ||||
|         run: script/clang-format -i | ||||
|       - name: Suggest changes | ||||
|         run: script/ci-suggest-changes | ||||
|  | ||||
|   lint-clang-tidy: | ||||
|     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 | ||||
|     # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files | ||||
|     strategy: | ||||
|       matrix: | ||||
|         split: [1, 2, 3, 4] | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       # Cache platformio intermediary files (like libraries etc) | ||||
|       # Note: platformio platform versions should be cached via the esphome-lint image | ||||
|       - name: Cache Platformio | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: .pio | ||||
|           key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} | ||||
|           restore-keys: | | ||||
|             lint-cpp-pio- | ||||
|       # Set up the pio project so that the cpp checks know how files are compiled | ||||
|       # (build flags, libraries etc) | ||||
|       - name: Set up platformio environment | ||||
|         run: pio init --ide atom | ||||
|  | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/gcc.json" | ||||
|       - name: Run clang-tidy | ||||
|         run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }} | ||||
|       - name: Suggest changes | ||||
|         run: script/ci-suggest-changes | ||||
|  | ||||
|   lint-python: | ||||
|     # Don't use the esphome-lint docker image because it may contain outdated requirements. | ||||
|     # This way, all dependencies are cached via the cache action. | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v2 | ||||
|         with: | ||||
|           python-version: '3.7' | ||||
|       - name: Cache pip modules | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.cache/pip | ||||
|           key: esphome-pip-3.7-${{ hashFiles('setup.py') }} | ||||
|           restore-keys: | | ||||
|             esphome-pip-3.7- | ||||
|       - name: Set up python environment | ||||
|         run: script/setup | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/ci-custom.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/lint-python.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/python.json" | ||||
|       - name: Lint Custom | ||||
|         run: script/ci-custom.py | ||||
|       - name: Lint Python | ||||
|         run: script/lint-python | ||||
|       - name: Lint CODEOWNERS | ||||
|         run: script/build_codeowners.py --check | ||||
|  | ||||
|   test: | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       matrix: | ||||
|           test: | ||||
|           - test1 | ||||
|           - test2 | ||||
|           - test3 | ||||
|           - test4 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v2 | ||||
|         with: | ||||
|           python-version: '3.7' | ||||
|       - name: Cache pip modules | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.cache/pip | ||||
|           key: esphome-pip-3.7-${{ hashFiles('setup.py') }} | ||||
|           restore-keys: | | ||||
|             esphome-pip-3.7- | ||||
|       # Use per test platformio cache because tests have different platform versions | ||||
|       - name: Cache ~/.platformio | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.platformio | ||||
|           key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core_config.py') }} | ||||
|           restore-keys: | | ||||
|             test-home-platformio-${{ matrix.test }}- | ||||
|       - name: Set up environment | ||||
|         run: script/setup | ||||
|  | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/gcc.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/python.json" | ||||
|       - run: esphome tests/${{ matrix.test }}.yaml compile | ||||
|  | ||||
|   pytest: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v2 | ||||
|         with: | ||||
|           python-version: '3.7' | ||||
|       - name: Cache pip modules | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.cache/pip | ||||
|           key: esphome-pip-3.7-${{ hashFiles('setup.py') }} | ||||
|           restore-keys: | | ||||
|             esphome-pip-3.7- | ||||
|       - name: Set up environment | ||||
|         run: script/setup | ||||
|       - name: Install Github Actions annotator | ||||
|         run: pip install pytest-github-actions-annotate-failures | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/python.json" | ||||
|       - name: Run pytest | ||||
|         run: | | ||||
|           pytest \ | ||||
|             -qq \ | ||||
|             --durations=10 \ | ||||
|             -o console_output_style=count \ | ||||
|             tests | ||||
|  | ||||
|   deploy-docker: | ||||
|     name: Build and publish docker containers | ||||
|     if: github.repository == 'esphome/esphome' | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest] | ||||
|     strategy: | ||||
|       matrix: | ||||
|         arch: [amd64, armv7, aarch64] | ||||
|         # Hassio dev image doesn't use esphome/esphome-hassio-$arch and uses base directly | ||||
|         build_type: ["docker"] | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set TAG | ||||
|         run: | | ||||
|           TAG="${GITHUB_SHA:0:7}" | ||||
|           echo "::set-env name=TAG::${TAG}" | ||||
|       - name: Set up env variables | ||||
|         run: | | ||||
|           base_version="2.3.4" | ||||
|  | ||||
|           if [[ "${{ matrix.build_type }}" == "hassio" ]]; then | ||||
|             build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" | ||||
|             build_to="esphome/esphome-hassio-${{ matrix.arch }}" | ||||
|             dockerfile="docker/Dockerfile.hassio" | ||||
|           else | ||||
|             build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}" | ||||
|             build_to="esphome/esphome-${{ matrix.arch }}" | ||||
|             dockerfile="docker/Dockerfile" | ||||
|           fi | ||||
|  | ||||
|           echo "::set-env name=BUILD_FROM::${build_from}" | ||||
|           echo "::set-env name=BUILD_TO::${build_to}" | ||||
|           echo "::set-env name=DOCKERFILE::${dockerfile}" | ||||
|       - name: Pull for cache | ||||
|         run: | | ||||
|           docker pull "${BUILD_TO}:dev" || true | ||||
|       - name: Register QEMU binfmt | ||||
|         run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes | ||||
|       - run: | | ||||
|           docker build \ | ||||
|             --build-arg "BUILD_FROM=${BUILD_FROM}" \ | ||||
|             --build-arg "BUILD_VERSION=${TAG}" \ | ||||
|             --tag "${BUILD_TO}:${TAG}" \ | ||||
|             --tag "${BUILD_TO}:dev" \ | ||||
|             --cache-from "${BUILD_TO}:dev" \ | ||||
|             --file "${DOCKERFILE}" \ | ||||
|             . | ||||
|       - name: Log in to docker hub | ||||
|         env: | ||||
|           DOCKER_USER: ${{ secrets.DOCKER_USER }} | ||||
|           DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | ||||
|         run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" | ||||
|       - run: | | ||||
|           docker push "${BUILD_TO}:${TAG}" | ||||
|           docker push "${BUILD_TO}:dev" | ||||
|  | ||||
|  | ||||
|   deploy-docker-manifest: | ||||
|     if: github.repository == 'esphome/esphome' | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: [deploy-docker] | ||||
|     steps: | ||||
|     - name: Enable experimental manifest support | ||||
|       run: | | ||||
|         mkdir -p ~/.docker | ||||
|         echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json | ||||
|     - name: Set TAG | ||||
|       run: | | ||||
|         TAG="${GITHUB_SHA:0:7}" | ||||
|         echo "::set-env name=TAG::${TAG}" | ||||
|     - name: Log in to docker hub | ||||
|       env: | ||||
|         DOCKER_USER: ${{ secrets.DOCKER_USER }} | ||||
|         DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | ||||
|       run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" | ||||
|     - name: "Create the manifest" | ||||
|       run: | | ||||
|         docker manifest create esphome/esphome:${TAG} \ | ||||
|           esphome/esphome-aarch64:${TAG} \ | ||||
|           esphome/esphome-amd64:${TAG} \ | ||||
|           esphome/esphome-armv7:${TAG} | ||||
|         docker manifest push esphome/esphome:${TAG} | ||||
|  | ||||
|         docker manifest create esphome/esphome:dev \ | ||||
|           esphome/esphome-aarch64:${TAG} \ | ||||
|           esphome/esphome-amd64:${TAG} \ | ||||
|           esphome/esphome-armv7:${TAG} | ||||
|         docker manifest push esphome/esphome:dev | ||||
							
								
								
									
										302
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,302 @@ | ||||
| name: Publish Release | ||||
|  | ||||
| on: | ||||
|   release: | ||||
|     types: [published] | ||||
|  | ||||
| jobs: | ||||
|   # THE LINT/TEST JOBS ARE COPIED FROM ci.yaml | ||||
|  | ||||
|   lint-clang-format: | ||||
|     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 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       # Cache platformio intermediary files (like libraries etc) | ||||
|       # Note: platformio platform versions should be cached via the esphome-lint image | ||||
|       - name: Cache Platformio | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: .pio | ||||
|           key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} | ||||
|           restore-keys: | | ||||
|             lint-cpp-pio- | ||||
|       # Set up the pio project so that the cpp checks know how files are compiled | ||||
|       # (build flags, libraries etc) | ||||
|       - name: Set up platformio environment | ||||
|         run: pio init --ide atom | ||||
|  | ||||
|       - name: Run clang-format | ||||
|         run: script/clang-format -i | ||||
|       - name: Suggest changes | ||||
|         run: script/ci-suggest-changes | ||||
|  | ||||
|   lint-clang-tidy: | ||||
|     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 | ||||
|     # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files | ||||
|     strategy: | ||||
|       matrix: | ||||
|         split: [1, 2, 3, 4] | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       # Cache platformio intermediary files (like libraries etc) | ||||
|       # Note: platformio platform versions should be cached via the esphome-lint image | ||||
|       - name: Cache Platformio | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: .pio | ||||
|           key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} | ||||
|           restore-keys: | | ||||
|             lint-cpp-pio- | ||||
|       # Set up the pio project so that the cpp checks know how files are compiled | ||||
|       # (build flags, libraries etc) | ||||
|       - name: Set up platformio environment | ||||
|         run: pio init --ide atom | ||||
|  | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/gcc.json" | ||||
|       - name: Run clang-tidy | ||||
|         run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }} | ||||
|       - name: Suggest changes | ||||
|         run: script/ci-suggest-changes | ||||
|  | ||||
|   lint-python: | ||||
|     # Don't use the esphome-lint docker image because it may contain outdated requirements. | ||||
|     # This way, all dependencies are cached via the cache action. | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v2 | ||||
|         with: | ||||
|           python-version: '3.7' | ||||
|       - name: Cache pip modules | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.cache/pip | ||||
|           key: esphome-pip-3.7-${{ hashFiles('setup.py') }} | ||||
|           restore-keys: | | ||||
|             esphome-pip-3.7- | ||||
|       - name: Set up python environment | ||||
|         run: script/setup | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/ci-custom.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/lint-python.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/python.json" | ||||
|       - name: Lint Custom | ||||
|         run: script/ci-custom.py | ||||
|       - name: Lint Python | ||||
|         run: script/lint-python | ||||
|       - name: Lint CODEOWNERS | ||||
|         run: script/build_codeowners.py --check | ||||
|  | ||||
|   test: | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       matrix: | ||||
|           test: | ||||
|           - test1 | ||||
|           - test2 | ||||
|           - test3 | ||||
|           - test4 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v2 | ||||
|         with: | ||||
|           python-version: '3.7' | ||||
|       - name: Cache pip modules | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.cache/pip | ||||
|           key: esphome-pip-3.7-${{ hashFiles('setup.py') }} | ||||
|           restore-keys: | | ||||
|             esphome-pip-3.7- | ||||
|       # Use per test platformio cache because tests have different platform versions | ||||
|       - name: Cache ~/.platformio | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.platformio | ||||
|           key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core_config.py') }} | ||||
|           restore-keys: | | ||||
|             test-home-platformio-${{ matrix.test }}- | ||||
|       - name: Set up environment | ||||
|         run: script/setup | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/gcc.json" | ||||
|           echo "::add-matcher::.github/workflows/matchers/python.json" | ||||
|       - run: esphome tests/${{ matrix.test }}.yaml compile | ||||
|  | ||||
|   pytest: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v2 | ||||
|         with: | ||||
|           python-version: '3.7' | ||||
|       - name: Cache pip modules | ||||
|         uses: actions/cache@v1 | ||||
|         with: | ||||
|           path: ~/.cache/pip | ||||
|           key: esphome-pip-3.7-${{ hashFiles('setup.py') }} | ||||
|           restore-keys: | | ||||
|             esphome-pip-3.7- | ||||
|       - name: Set up environment | ||||
|         run: script/setup | ||||
|       - name: Install Github Actions annotator | ||||
|         run: pip install pytest-github-actions-annotate-failures | ||||
|  | ||||
|       - name: Register problem matchers | ||||
|         run: | | ||||
|           echo "::add-matcher::.github/workflows/matchers/python.json" | ||||
|       - name: Run pytest | ||||
|         run: | | ||||
|           pytest \ | ||||
|             -qq \ | ||||
|             --durations=10 \ | ||||
|             -o console_output_style=count \ | ||||
|             tests | ||||
|  | ||||
|   deploy-pypi: | ||||
|     name: Build and publish to PyPi | ||||
|     if: github.repository == 'esphome/esphome' | ||||
|     needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest] | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v1 | ||||
|         with: | ||||
|           python-version: '3.x' | ||||
|       - name: Set up python environment | ||||
|         run: | | ||||
|           script/setup | ||||
|           pip install setuptools wheel twine | ||||
|       - name: Build | ||||
|         run: python setup.py sdist bdist_wheel | ||||
|       - name: Upload | ||||
|         env: | ||||
|           TWINE_USERNAME: __token__ | ||||
|           TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} | ||||
|         run: twine upload dist/* | ||||
|  | ||||
|   deploy-docker: | ||||
|     name: Build and publish docker containers | ||||
|     if: github.repository == 'esphome/esphome' | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest] | ||||
|     strategy: | ||||
|       matrix: | ||||
|         arch: [amd64, armv7, aarch64] | ||||
|         build_type: ["hassio", "docker"] | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set TAG | ||||
|         run: | | ||||
|           TAG="${GITHUB_REF#refs/tags/v}" | ||||
|           echo "::set-env name=TAG::${TAG}" | ||||
|       - name: Set up env variables | ||||
|         run: | | ||||
|           base_version="2.3.4" | ||||
|  | ||||
|           if [[ "${{ matrix.build_type }}" == "hassio" ]]; then | ||||
|             build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" | ||||
|             build_to="esphome/esphome-hassio-${{ matrix.arch }}" | ||||
|             dockerfile="docker/Dockerfile.hassio" | ||||
|           else | ||||
|             build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}" | ||||
|             build_to="esphome/esphome-${{ matrix.arch }}" | ||||
|             dockerfile="docker/Dockerfile" | ||||
|           fi | ||||
|  | ||||
|           # Set env variables so these values don't need to be calculated again | ||||
|           echo "::set-env name=BUILD_FROM::${build_from}" | ||||
|           echo "::set-env name=BUILD_TO::${build_to}" | ||||
|           echo "::set-env name=DOCKERFILE::${dockerfile}" | ||||
|       - name: Pull for cache | ||||
|         run: | | ||||
|           docker pull "${BUILD_TO}:latest" || true | ||||
|       - name: Register QEMU binfmt | ||||
|         run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes | ||||
|       - run: | | ||||
|           docker build \ | ||||
|             --build-arg "BUILD_FROM=${BUILD_FROM}" \ | ||||
|             --build-arg "BUILD_VERSION=${TAG}" \ | ||||
|             --tag "${BUILD_TO}:${TAG}" \ | ||||
|             --cache-from "${BUILD_TO}:latest" \ | ||||
|             --file "${DOCKERFILE}" \ | ||||
|             . | ||||
|       - name: Log in to docker hub | ||||
|         env: | ||||
|           DOCKER_USER: ${{ secrets.DOCKER_USER }} | ||||
|           DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | ||||
|         run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" | ||||
|       - run: docker push "${BUILD_TO}:${TAG}" | ||||
|  | ||||
|       # Always publish to beta tag (also full releases) | ||||
|       - name: Publish docker beta tag | ||||
|         run: | | ||||
|           docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:beta" | ||||
|           docker push "${BUILD_TO}:beta" | ||||
|  | ||||
|       - if: ${{ !github.event.release.prerelease) }} | ||||
|         name: Publish docker latest tag | ||||
|         run: | | ||||
|           docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:latest" | ||||
|           docker push "${BUILD_TO}:latest" | ||||
|  | ||||
|   deploy-docker-manifest: | ||||
|     if: github.repository == 'esphome/esphome' | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: [deploy-docker] | ||||
|     steps: | ||||
|     - name: Enable experimental manifest support | ||||
|       run: | | ||||
|         mkdir -p ~/.docker | ||||
|         echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json | ||||
|     - name: Set TAG | ||||
|       run: | | ||||
|         TAG="${GITHUB_REF#refs/tags/v}" | ||||
|         echo "::set-env name=TAG::${TAG}" | ||||
|     - name: Log in to docker hub | ||||
|       env: | ||||
|         DOCKER_USER: ${{ secrets.DOCKER_USER }} | ||||
|         DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | ||||
|       run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" | ||||
|     - name: "Create the manifest" | ||||
|       run: | | ||||
|         docker manifest create esphome/esphome:${TAG} \ | ||||
|           esphome/esphome-aarch64:${TAG} \ | ||||
|           esphome/esphome-amd64:${TAG} \ | ||||
|           esphome/esphome-armv7:${TAG} | ||||
|         docker manifest push esphome/esphome:${TAG} | ||||
|  | ||||
|     - name: Publish docker beta tag | ||||
|       run: | | ||||
|         docker manifest create esphome/esphome:beta \ | ||||
|           esphome/esphome-aarch64:${TAG} \ | ||||
|           esphome/esphome-amd64:${TAG} \ | ||||
|           esphome/esphome-armv7:${TAG} | ||||
|         docker manifest push esphome/esphome:beta | ||||
|  | ||||
|     - name: Publish docker latest tag | ||||
|       if: ${{ !github.event.release.prerelease) }} | ||||
|       run: | | ||||
|         docker manifest create esphome/esphome:latest \ | ||||
|           esphome/esphome-aarch64:${TAG} \ | ||||
|           esphome/esphome-amd64:${TAG} \ | ||||
|           esphome/esphome-armv7:${TAG} | ||||
|         docker manifest push esphome/esphome:latest | ||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -10,6 +10,9 @@ __pycache__/ | ||||
| *.sublime-project | ||||
| *.sublime-workspace | ||||
|  | ||||
| # Intellij Idea | ||||
| .idea | ||||
|  | ||||
| # Hide some OS X stuff | ||||
| .DS_Store | ||||
| .AppleDouble | ||||
| @@ -48,8 +51,10 @@ htmlcov/ | ||||
| .coverage | ||||
| .coverage.* | ||||
| .cache | ||||
| .esphome | ||||
| nosetests.xml | ||||
| coverage.xml | ||||
| cov.xml | ||||
| *.cover | ||||
| .hypothesis/ | ||||
| .pytest_cache/ | ||||
|   | ||||
							
								
								
									
										342
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							
							
						
						
									
										342
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							| @@ -1,342 +0,0 @@ | ||||
| --- | ||||
| # Based on https://gitlab.com/hassio-addons/addon-node-red/blob/master/.gitlab-ci.yml | ||||
| variables: | ||||
|   DOCKER_DRIVER: overlay2 | ||||
|   DOCKER_HOST: tcp://docker:2375/ | ||||
|   BASE_VERSION: '2.0.1' | ||||
|   TZ: UTC | ||||
|  | ||||
| stages: | ||||
|   - lint | ||||
|   - test | ||||
|   - deploy | ||||
|  | ||||
| .lint: &lint | ||||
|   image: esphome/esphome-lint:latest | ||||
|   stage: lint | ||||
|   before_script: | ||||
|     - script/setup | ||||
|   tags: | ||||
|     - docker | ||||
|  | ||||
| .test: &test | ||||
|   image: esphome/esphome-lint:latest | ||||
|   stage: test | ||||
|   before_script: | ||||
|     - script/setup | ||||
|   tags: | ||||
|     - docker | ||||
|  | ||||
| .docker-base: &docker-base | ||||
|   image: esphome/esphome-base-builder | ||||
|   before_script: | ||||
|     - docker info | ||||
|     - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" | ||||
|   script: | ||||
|     - docker run --rm --privileged multiarch/qemu-user-static:4.1.0-1 --reset -p yes | ||||
|     - TAG="${CI_COMMIT_TAG#v}" | ||||
|     - TAG="${TAG:-${CI_COMMIT_SHA:0:7}}" | ||||
|     - echo "Tag ${TAG}" | ||||
|  | ||||
|     - | | ||||
|       if [[ "${IS_HASSIO}" == "YES" ]]; then | ||||
|         BUILD_FROM=esphome/esphome-hassio-base-${BUILD_ARCH}:${BASE_VERSION} | ||||
|         BUILD_TO=esphome/esphome-hassio-${BUILD_ARCH} | ||||
|         DOCKERFILE=docker/Dockerfile.hassio | ||||
|       else | ||||
|         BUILD_FROM=esphome/esphome-base-${BUILD_ARCH}:${BASE_VERSION} | ||||
|         if [[ "${BUILD_ARCH}" == "amd64" ]]; then | ||||
|           BUILD_TO=esphome/esphome | ||||
|         else | ||||
|           BUILD_TO=esphome/esphome-${BUILD_ARCH} | ||||
|         fi | ||||
|         DOCKERFILE=docker/Dockerfile | ||||
|       fi | ||||
|  | ||||
|     - | | ||||
|       docker build \ | ||||
|         --build-arg "BUILD_FROM=${BUILD_FROM}" \ | ||||
|         --build-arg "BUILD_VERSION=${TAG}" \ | ||||
|         --tag "${BUILD_TO}:${TAG}" \ | ||||
|         --file "${DOCKERFILE}" \ | ||||
|         . | ||||
|     - | | ||||
|       if [[ "${RELEASE}" = "YES" ]]; then | ||||
|         echo "Pushing to ${BUILD_TO}:${TAG}" | ||||
|         docker push "${BUILD_TO}:${TAG}" | ||||
|       fi | ||||
|     - | | ||||
|       if [[ "${LATEST}" = "YES" ]]; then | ||||
|         echo "Pushing to :latest" | ||||
|         docker tag ${BUILD_TO}:${TAG} ${BUILD_TO}:latest | ||||
|         docker push ${BUILD_TO}:latest | ||||
|       fi | ||||
|     - | | ||||
|       if [[ "${BETA}" = "YES" ]]; then | ||||
|         echo "Pushing to :beta" | ||||
|         docker tag \ | ||||
|           ${BUILD_TO}:${TAG} \ | ||||
|           ${BUILD_TO}:beta | ||||
|         docker push ${BUILD_TO}:beta | ||||
|       fi | ||||
|     - | | ||||
|       if [[ "${DEV}" = "YES" ]]; then | ||||
|         echo "Pushing to :dev" | ||||
|         docker tag \ | ||||
|           ${BUILD_TO}:${TAG} \ | ||||
|           ${BUILD_TO}:dev | ||||
|         docker push ${BUILD_TO}:dev | ||||
|       fi | ||||
|   services: | ||||
|     - docker:dind | ||||
|   tags: | ||||
|     - docker | ||||
|   stage: deploy | ||||
|  | ||||
| lint-custom: | ||||
|   <<: *lint | ||||
|   script: | ||||
|     - script/ci-custom.py | ||||
|  | ||||
| lint-python: | ||||
|   <<: *lint | ||||
|   script: | ||||
|     - script/lint-python | ||||
|  | ||||
| lint-tidy: | ||||
|   <<: *lint | ||||
|   script: | ||||
|     - pio init --ide atom | ||||
|     - script/clang-tidy --all-headers --fix | ||||
|     - script/ci-suggest-changes | ||||
|  | ||||
| lint-format: | ||||
|   <<: *lint | ||||
|   script: | ||||
|     - script/clang-format -i | ||||
|     - script/ci-suggest-changes | ||||
|  | ||||
| test1: | ||||
|   <<: *test | ||||
|   script: | ||||
|     - esphome tests/test1.yaml compile | ||||
|  | ||||
| test2: | ||||
|   <<: *test | ||||
|   script: | ||||
|     - esphome tests/test2.yaml compile | ||||
|  | ||||
| test3: | ||||
|   <<: *test | ||||
|   script: | ||||
|     - esphome tests/test3.yaml compile | ||||
|  | ||||
| .deploy-pypi: &deploy-pypi | ||||
|   <<: *lint | ||||
|   stage: deploy | ||||
|   script: | ||||
|     - pip install twine wheel | ||||
|     - python setup.py sdist bdist_wheel | ||||
|     - twine upload dist/* | ||||
|  | ||||
| deploy-release:pypi: | ||||
|   <<: *deploy-pypi | ||||
|   only: | ||||
|     - /^v\d+\.\d+\.\d+$/ | ||||
|   except: | ||||
|     - /^(?!master).+@/ | ||||
|  | ||||
| deploy-beta:pypi: | ||||
|   <<: *deploy-pypi | ||||
|   only: | ||||
|     - /^v\d+\.\d+\.\d+b\d+$/ | ||||
|   except: | ||||
|     - /^(?!rc).+@/ | ||||
|  | ||||
| .latest: &latest | ||||
|   <<: *docker-base | ||||
|   only: | ||||
|     - /^v([0-9\.]+)$/ | ||||
|   except: | ||||
|     - branches | ||||
|  | ||||
| .beta: &beta | ||||
|   <<: *docker-base | ||||
|   only: | ||||
|     - /^v([0-9\.]+b\d+)$/ | ||||
|   except: | ||||
|     - branches | ||||
|  | ||||
| .dev: &dev | ||||
|   <<: *docker-base | ||||
|   only: | ||||
|     - dev | ||||
|  | ||||
| aarch64-beta-docker: | ||||
|   <<: *beta | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: aarch64 | ||||
|     IS_HASSIO: "NO" | ||||
|     RELEASE: "YES" | ||||
| aarch64-beta-hassio: | ||||
|   <<: *beta | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: aarch64 | ||||
|     IS_HASSIO: "YES" | ||||
|     RELEASE: "YES" | ||||
| aarch64-dev-docker: | ||||
|   <<: *dev | ||||
|   variables: | ||||
|     BUILD_ARCH: aarch64 | ||||
|     DEV: "YES" | ||||
|     IS_HASSIO: "NO" | ||||
| aarch64-dev-hassio: | ||||
|   <<: *dev | ||||
|   variables: | ||||
|     BUILD_ARCH: aarch64 | ||||
|     DEV: "YES" | ||||
|     IS_HASSIO: "YES" | ||||
| aarch64-latest-docker: | ||||
|   <<: *latest | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: aarch64 | ||||
|     IS_HASSIO: "NO" | ||||
|     LATEST: "YES" | ||||
|     RELEASE: "YES" | ||||
| aarch64-latest-hassio: | ||||
|   <<: *latest | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: aarch64 | ||||
|     IS_HASSIO: "YES" | ||||
|     LATEST: "YES" | ||||
|     RELEASE: "YES" | ||||
| amd64-beta-docker: | ||||
|   <<: *beta | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: amd64 | ||||
|     IS_HASSIO: "NO" | ||||
|     RELEASE: "YES" | ||||
| amd64-beta-hassio: | ||||
|   <<: *beta | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: amd64 | ||||
|     IS_HASSIO: "YES" | ||||
|     RELEASE: "YES" | ||||
| amd64-dev-docker: | ||||
|   <<: *dev | ||||
|   variables: | ||||
|     BUILD_ARCH: amd64 | ||||
|     DEV: "YES" | ||||
|     IS_HASSIO: "NO" | ||||
| amd64-dev-hassio: | ||||
|   <<: *dev | ||||
|   variables: | ||||
|     BUILD_ARCH: amd64 | ||||
|     DEV: "YES" | ||||
|     IS_HASSIO: "YES" | ||||
| amd64-latest-docker: | ||||
|   <<: *latest | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: amd64 | ||||
|     IS_HASSIO: "NO" | ||||
|     LATEST: "YES" | ||||
|     RELEASE: "YES" | ||||
| amd64-latest-hassio: | ||||
|   <<: *latest | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: amd64 | ||||
|     IS_HASSIO: "YES" | ||||
|     LATEST: "YES" | ||||
|     RELEASE: "YES" | ||||
| armv7-beta-docker: | ||||
|   <<: *beta | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: armv7 | ||||
|     IS_HASSIO: "NO" | ||||
|     RELEASE: "YES" | ||||
| armv7-beta-hassio: | ||||
|   <<: *beta | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: armv7 | ||||
|     IS_HASSIO: "YES" | ||||
|     RELEASE: "YES" | ||||
| armv7-dev-docker: | ||||
|   <<: *dev | ||||
|   variables: | ||||
|     BUILD_ARCH: armv7 | ||||
|     DEV: "YES" | ||||
|     IS_HASSIO: "NO" | ||||
| armv7-dev-hassio: | ||||
|   <<: *dev | ||||
|   variables: | ||||
|     BUILD_ARCH: armv7 | ||||
|     DEV: "YES" | ||||
|     IS_HASSIO: "YES" | ||||
| armv7-latest-docker: | ||||
|   <<: *latest | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: armv7 | ||||
|     IS_HASSIO: "NO" | ||||
|     LATEST: "YES" | ||||
|     RELEASE: "YES" | ||||
| armv7-latest-hassio: | ||||
|   <<: *latest | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: armv7 | ||||
|     IS_HASSIO: "YES" | ||||
|     LATEST: "YES" | ||||
|     RELEASE: "YES" | ||||
| i386-beta-docker: | ||||
|   <<: *beta | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: i386 | ||||
|     IS_HASSIO: "NO" | ||||
|     RELEASE: "YES" | ||||
| i386-beta-hassio: | ||||
|   <<: *beta | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: i386 | ||||
|     IS_HASSIO: "YES" | ||||
|     RELEASE: "YES" | ||||
| i386-dev-docker: | ||||
|   <<: *dev | ||||
|   variables: | ||||
|     BUILD_ARCH: i386 | ||||
|     DEV: "YES" | ||||
|     IS_HASSIO: "NO" | ||||
| i386-dev-hassio: | ||||
|   <<: *dev | ||||
|   variables: | ||||
|     BUILD_ARCH: i386 | ||||
|     DEV: "YES" | ||||
|     IS_HASSIO: "YES" | ||||
| i386-latest-docker: | ||||
|   <<: *latest | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: i386 | ||||
|     IS_HASSIO: "NO" | ||||
|     LATEST: "YES" | ||||
|     RELEASE: "YES" | ||||
| i386-latest-hassio: | ||||
|   <<: *latest | ||||
|   variables: | ||||
|     BETA: "YES" | ||||
|     BUILD_ARCH: i386 | ||||
|     IS_HASSIO: "YES" | ||||
|     LATEST: "YES" | ||||
|     RELEASE: "YES" | ||||
| @@ -2,5 +2,5 @@ ports: | ||||
| - port: 6052 | ||||
|   onOpen: open-preview | ||||
| tasks: | ||||
| - before: script/setup | ||||
| - before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup | ||||
|   command: python -m esphome config dashboard | ||||
|   | ||||
							
								
								
									
										11
									
								
								.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| # See https://pre-commit.com for more information | ||||
| # See https://pre-commit.com/hooks.html for more hooks | ||||
| repos: | ||||
| -   repo: https://github.com/pre-commit/pre-commit-hooks | ||||
|     rev: v2.4.0 | ||||
|     hooks: | ||||
|     -   id: trailing-whitespace | ||||
|     -   id: end-of-file-fixer | ||||
|     -   id: check-yaml | ||||
|     -   id: check-added-large-files | ||||
|     -   id: flake8 | ||||
							
								
								
									
										49
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,49 +0,0 @@ | ||||
| sudo: false | ||||
| language: python | ||||
| python: '3.5' | ||||
| install: script/setup | ||||
| cache: | ||||
|   directories: | ||||
|     - "~/.platformio" | ||||
|  | ||||
| matrix: | ||||
|   fast_finish: true | ||||
|   include: | ||||
|     - python: "3.7" | ||||
|       env: TARGET=Lint3.7 | ||||
|       script: | ||||
|         - script/ci-custom.py | ||||
|         - flake8 esphome | ||||
|         - pylint esphome | ||||
|     - python: "3.5" | ||||
|       env: TARGET=Test3.5 | ||||
|       script: | ||||
|         - esphome tests/test1.yaml compile | ||||
|         - esphome tests/test2.yaml compile | ||||
|         - esphome tests/test3.yaml compile | ||||
|     - python: "2.7" | ||||
|       env: TARGET=Test2.7 | ||||
|       script: | ||||
|         - esphome tests/test1.yaml compile | ||||
|         - esphome tests/test2.yaml compile | ||||
|         - esphome tests/test3.yaml compile | ||||
|     - env: TARGET=Cpp-Lint | ||||
|       dist: trusty | ||||
|       sudo: required | ||||
|       addons: | ||||
|         apt: | ||||
|           sources: | ||||
|             - ubuntu-toolchain-r-test | ||||
|             - llvm-toolchain-trusty-7 | ||||
|           packages: | ||||
|             - clang-tidy-7 | ||||
|             - clang-format-7 | ||||
|       before_script: | ||||
|         - pio init --ide atom | ||||
|         - clang-tidy-7 -version | ||||
|         - clang-format-7 -version | ||||
|         - clang-apply-replacements-7 -version | ||||
|       script: | ||||
|         - script/clang-tidy --all-headers -j 2 --fix | ||||
|         - script/clang-format -i -j 2 | ||||
|         - script/ci-suggest-changes | ||||
							
								
								
									
										69
									
								
								CODEOWNERS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								CODEOWNERS
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| # This file is generated by script/build_codeowners.py | ||||
| # People marked here will be automatically requested for a review | ||||
| # when the code that they own is touched. | ||||
| # | ||||
| # Every time an issue is created with a label corresponding to an integration, | ||||
| # the integration's code owner is automatically notified. | ||||
|  | ||||
| # Core Code | ||||
| setup.py @esphome/core | ||||
| esphome/*.py @esphome/core | ||||
| esphome/core/* @esphome/core | ||||
|  | ||||
| # Integrations | ||||
| esphome/components/ac_dimmer/* @glmnet | ||||
| esphome/components/adc/* @esphome/core | ||||
| esphome/components/api/* @OttoWinter | ||||
| esphome/components/async_tcp/* @OttoWinter | ||||
| esphome/components/bang_bang/* @OttoWinter | ||||
| esphome/components/binary_sensor/* @esphome/core | ||||
| esphome/components/captive_portal/* @OttoWinter | ||||
| esphome/components/climate/* @esphome/core | ||||
| esphome/components/climate_ir/* @glmnet | ||||
| esphome/components/coolix/* @glmnet | ||||
| esphome/components/cover/* @esphome/core | ||||
| esphome/components/ct_clamp/* @jesserockz | ||||
| esphome/components/debug/* @OttoWinter | ||||
| esphome/components/dfplayer/* @glmnet | ||||
| esphome/components/dht/* @OttoWinter | ||||
| esphome/components/exposure_notifications/* @OttoWinter | ||||
| esphome/components/fastled_base/* @OttoWinter | ||||
| esphome/components/globals/* @esphome/core | ||||
| esphome/components/gpio/* @esphome/core | ||||
| esphome/components/homeassistant/* @OttoWinter | ||||
| esphome/components/i2c/* @esphome/core | ||||
| esphome/components/integration/* @OttoWinter | ||||
| esphome/components/interval/* @esphome/core | ||||
| esphome/components/json/* @OttoWinter | ||||
| esphome/components/ledc/* @OttoWinter | ||||
| esphome/components/light/* @esphome/core | ||||
| esphome/components/logger/* @esphome/core | ||||
| esphome/components/network/* @esphome/core | ||||
| esphome/components/ota/* @esphome/core | ||||
| esphome/components/output/* @esphome/core | ||||
| esphome/components/pid/* @OttoWinter | ||||
| esphome/components/pn532/* @OttoWinter | ||||
| esphome/components/power_supply/* @esphome/core | ||||
| esphome/components/restart/* @esphome/core | ||||
| esphome/components/rf_bridge/* @jesserockz | ||||
| esphome/components/rtttl/* @glmnet | ||||
| esphome/components/script/* @esphome/core | ||||
| esphome/components/sensor/* @esphome/core | ||||
| esphome/components/shutdown/* @esphome/core | ||||
| esphome/components/sim800l/* @glmnet | ||||
| esphome/components/spi/* @esphome/core | ||||
| esphome/components/substitutions/* @esphome/core | ||||
| esphome/components/sun/* @OttoWinter | ||||
| esphome/components/switch/* @esphome/core | ||||
| esphome/components/tcl112/* @glmnet | ||||
| esphome/components/time/* @OttoWinter | ||||
| esphome/components/tm1637/* @glmnet | ||||
| esphome/components/tuya/binary_sensor/* @jesserockz | ||||
| esphome/components/tuya/climate/* @jesserockz | ||||
| esphome/components/tuya/sensor/* @jesserockz | ||||
| esphome/components/tuya/switch/* @jesserockz | ||||
| esphome/components/uart/* @esphome/core | ||||
| esphome/components/ultrasonic/* @OttoWinter | ||||
| esphome/components/version/* @esphome/core | ||||
| esphome/components/web_server_base/* @OttoWinter | ||||
| esphome/components/whirlpool/* @glmnet | ||||
| @@ -1,12 +1,21 @@ | ||||
| ARG BUILD_FROM=esphome/esphome-base-amd64:2.0.1 | ||||
| ARG BUILD_FROM=esphome/esphome-base-amd64:2.3.4 | ||||
| FROM ${BUILD_FROM} | ||||
|  | ||||
| # First install requirements to leverage caching when requirements don't change | ||||
| COPY requirements.txt / | ||||
| RUN pip3 install --no-cache-dir -r /requirements.txt | ||||
|  | ||||
| # Then copy esphome and install | ||||
| COPY . . | ||||
| RUN pip3 install --no-cache-dir -e . | ||||
|  | ||||
| ENV USERNAME="" | ||||
| ENV PASSWORD="" | ||||
| # Settings for dashboard | ||||
| ENV USERNAME="" PASSWORD="" | ||||
|  | ||||
| # The directory the user should mount their configuration files to | ||||
| WORKDIR /config | ||||
| # Set entrypoint to esphome so that the user doesn't have to type 'esphome' | ||||
| # in every docker command twice | ||||
| ENTRYPOINT ["esphome"] | ||||
| # When no arguments given, start the dashboard in the workdir | ||||
| CMD ["/config", "dashboard"] | ||||
|   | ||||
							
								
								
									
										13
									
								
								docker/Dockerfile.dev
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								docker/Dockerfile.dev
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| FROM esphome/esphome-base-amd64:2.3.4 | ||||
|  | ||||
| COPY . . | ||||
|  | ||||
| RUN apt-get update \ | ||||
|     && apt-get install -y --no-install-recommends \ | ||||
|         python3-wheel \ | ||||
|         net-tools \ | ||||
|     && apt-get clean \ | ||||
|     && rm -rf /var/lib/apt/lists/* | ||||
|  | ||||
| WORKDIR /workspaces | ||||
| ENV SHELL /bin/bash | ||||
| @@ -1,11 +1,15 @@ | ||||
| ARG BUILD_FROM | ||||
| FROM ${BUILD_FROM} | ||||
|  | ||||
| # First install requirements to leverage caching when requirements don't change | ||||
| COPY requirements.txt / | ||||
| RUN pip3 install --no-cache-dir -r /requirements.txt | ||||
|  | ||||
| # Copy root filesystem | ||||
| COPY docker/rootfs/ / | ||||
| COPY setup.py setup.cfg MANIFEST.in /opt/esphome/ | ||||
| COPY esphome /opt/esphome/esphome | ||||
|  | ||||
| # Then copy esphome and install | ||||
| COPY . /opt/esphome/ | ||||
| RUN pip3 install --no-cache-dir -e /opt/esphome | ||||
|  | ||||
| # Build arguments | ||||
|   | ||||
| @@ -1,20 +1,7 @@ | ||||
| FROM esphome/esphome-base-amd64:2.0.1 | ||||
| FROM esphome/esphome-lint-base:2.3.4 | ||||
|  | ||||
| RUN \ | ||||
|     apt-get update \ | ||||
|     && apt-get install -y --no-install-recommends \ | ||||
|         clang-format-7 \ | ||||
|         clang-tidy-7 \ | ||||
|         patch \ | ||||
|     && rm -rf \ | ||||
|         /tmp/* \ | ||||
|         /var/{cache,log}/* \ | ||||
|         /var/lib/apt/lists/* | ||||
|  | ||||
| COPY requirements_test.txt /requirements_test.txt | ||||
| RUN pip3 install --no-cache-dir wheel && pip3 install --no-cache-dir -r /requirements_test.txt | ||||
|  | ||||
| RUN ln -s /usr/bin/pip3 /usr/bin/pip && ln -f -s /usr/bin/python3 /usr/bin/python | ||||
| COPY requirements.txt requirements_test.txt / | ||||
| RUN pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt | ||||
|  | ||||
| VOLUME ["/esphome"] | ||||
| WORKDIR /esphome | ||||
|   | ||||
							
								
								
									
										10
									
								
								docker/rootfs/etc/cont-init.d/30-esphome.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										10
									
								
								docker/rootfs/etc/cont-init.d/30-esphome.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -8,7 +8,15 @@ declare esphome_version | ||||
|  | ||||
| if bashio::config.has_value 'esphome_version'; then | ||||
|     esphome_version=$(bashio::config 'esphome_version') | ||||
|     full_url="https://github.com/esphome/esphome/archive/${esphome_version}.zip" | ||||
|     if [[ $esphome_version == *":"* ]]; then | ||||
|       IFS=':' read -r -a array <<< "$esphome_version" | ||||
|       username=${array[0]} | ||||
|       ref=${array[1]} | ||||
|     else | ||||
|       username="esphome" | ||||
|       ref=$esphome_version | ||||
|     fi | ||||
|     full_url="https://github.com/${username}/esphome/archive/${ref}.zip" | ||||
|     bashio::log.info "Installing esphome version '${esphome_version}' (${full_url})..." | ||||
|     pip3 install -U --no-cache-dir "${full_url}" \ | ||||
|       || bashio::exit.nok "Failed installing esphome pinned version." | ||||
|   | ||||
							
								
								
									
										0
									
								
								docker/rootfs/etc/cont-init.d/40-migrate.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								docker/rootfs/etc/cont-init.d/40-migrate.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										0
									
								
								docker/rootfs/etc/nginx/nginx.conf
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								docker/rootfs/etc/nginx/nginx.conf
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @@ -1,5 +1,3 @@ | ||||
| from __future__ import print_function | ||||
|  | ||||
| import argparse | ||||
| import functools | ||||
| import logging | ||||
| @@ -14,40 +12,27 @@ from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \ | ||||
|     CONF_PASSWORD, CONF_PORT, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS | ||||
| from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority | ||||
| from esphome.helpers import color, indent | ||||
| from esphome.py_compat import IS_PY2, safe_input | ||||
| from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files | ||||
| from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files, \ | ||||
|     get_serial_ports | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| def get_serial_ports(): | ||||
|     # from https://github.com/pyserial/pyserial/blob/master/serial/tools/list_ports.py | ||||
|     from serial.tools.list_ports import comports | ||||
|     result = [] | ||||
|     for port, desc, info in comports(include_links=True): | ||||
|         if not port: | ||||
|             continue | ||||
|         if "VID:PID" in info: | ||||
|             result.append((port, desc)) | ||||
|     result.sort(key=lambda x: x[0]) | ||||
|     return result | ||||
|  | ||||
|  | ||||
| def choose_prompt(options): | ||||
|     if not options: | ||||
|         raise EsphomeError("Found no valid options for upload/logging, please make sure relevant " | ||||
|                            "sections (ota, mqtt, ...) are in your configuration and/or the device " | ||||
|                            "is plugged in.") | ||||
|                            "sections (ota, api, mqtt, ...) are in your configuration and/or the " | ||||
|                            "device is plugged in.") | ||||
|  | ||||
|     if len(options) == 1: | ||||
|         return options[0][1] | ||||
|  | ||||
|     safe_print(u"Found multiple options, please choose one:") | ||||
|     safe_print("Found multiple options, please choose one:") | ||||
|     for i, (desc, _) in enumerate(options): | ||||
|         safe_print(u"  [{}] {}".format(i + 1, desc)) | ||||
|         safe_print(f"  [{i+1}] {desc}") | ||||
|  | ||||
|     while True: | ||||
|         opt = safe_input('(number): ') | ||||
|         opt = input('(number): ') | ||||
|         if opt in options: | ||||
|             opt = options.index(opt) | ||||
|             break | ||||
| @@ -57,20 +42,20 @@ def choose_prompt(options): | ||||
|                 raise ValueError | ||||
|             break | ||||
|         except ValueError: | ||||
|             safe_print(color('red', u"Invalid option: '{}'".format(opt))) | ||||
|             safe_print(color('red', f"Invalid option: '{opt}'")) | ||||
|     return options[opt - 1][1] | ||||
|  | ||||
|  | ||||
| def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api): | ||||
|     options = [] | ||||
|     for res, desc in get_serial_ports(): | ||||
|         options.append((u"{} ({})".format(res, desc), res)) | ||||
|     for port in get_serial_ports(): | ||||
|         options.append((f"{port.path} ({port.description})", port.path)) | ||||
|     if (show_ota and 'ota' in CORE.config) or (show_api and 'api' in CORE.config): | ||||
|         options.append((u"Over The Air ({})".format(CORE.address), CORE.address)) | ||||
|         options.append((f"Over The Air ({CORE.address})", CORE.address)) | ||||
|         if default == 'OTA': | ||||
|             return CORE.address | ||||
|     if show_mqtt and 'mqtt' in CORE.config: | ||||
|         options.append((u"MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT')) | ||||
|         options.append(("MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT')) | ||||
|         if default == 'OTA': | ||||
|             return 'MQTT' | ||||
|     if default is not None: | ||||
| @@ -108,11 +93,7 @@ def run_miniterm(config, port): | ||||
|             except serial.SerialException: | ||||
|                 _LOGGER.error("Serial port closed!") | ||||
|                 return | ||||
|             if IS_PY2: | ||||
|                 line = raw.replace('\r', '').replace('\n', '') | ||||
|             else: | ||||
|                 line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8', | ||||
|                                                                           'backslashreplace') | ||||
|             line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8', 'backslashreplace') | ||||
|             time = datetime.now().time().strftime('[%H:%M:%S]') | ||||
|             message = time + line | ||||
|             safe_print(message) | ||||
| @@ -127,11 +108,9 @@ def wrap_to_code(name, comp): | ||||
|     @functools.wraps(comp.to_code) | ||||
|     @coroutine_with_priority(coro.priority) | ||||
|     def wrapped(conf): | ||||
|         cg.add(cg.LineComment(u"{}:".format(name))) | ||||
|         cg.add(cg.LineComment(f"{name}:")) | ||||
|         if comp.config_schema is not None: | ||||
|             conf_str = yaml_util.dump(conf) | ||||
|             if IS_PY2: | ||||
|                 conf_str = conf_str.decode('utf-8') | ||||
|             conf_str = conf_str.replace('//', '') | ||||
|             cg.add(cg.LineComment(indent(conf_str))) | ||||
|         yield coro(conf) | ||||
| @@ -140,6 +119,11 @@ def wrap_to_code(name, comp): | ||||
|  | ||||
|  | ||||
| def write_cpp(config): | ||||
|     generate_cpp_contents(config) | ||||
|     return write_cpp_file() | ||||
|  | ||||
|  | ||||
| def generate_cpp_contents(config): | ||||
|     _LOGGER.info("Generating C++ source...") | ||||
|  | ||||
|     for name, component, conf in iter_components(CORE.config): | ||||
| @@ -149,6 +133,8 @@ def write_cpp(config): | ||||
|  | ||||
|     CORE.flush_tasks() | ||||
|  | ||||
|  | ||||
| def write_cpp_file(): | ||||
|     writer.write_platformio_project() | ||||
|  | ||||
|     code_s = indent(CORE.cpp_main_section) | ||||
| @@ -165,16 +151,27 @@ def compile_program(args, config): | ||||
|  | ||||
| def upload_using_esptool(config, port): | ||||
|     path = CORE.firmware_bin | ||||
|     cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset', | ||||
|            '--baud', str(config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800)), | ||||
|            '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path] | ||||
|     first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800) | ||||
|  | ||||
|     if os.environ.get('ESPHOME_USE_SUBPROCESS') is None: | ||||
|         import esptool | ||||
|         # pylint: disable=protected-access | ||||
|         return run_external_command(esptool._main, *cmd) | ||||
|     def run_esptool(baud_rate): | ||||
|         cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset', | ||||
|                '--baud', str(baud_rate), | ||||
|                '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path] | ||||
|  | ||||
|     return run_external_process(*cmd) | ||||
|         if os.environ.get('ESPHOME_USE_SUBPROCESS') is None: | ||||
|             import esptool | ||||
|             # pylint: disable=protected-access | ||||
|             return run_external_command(esptool._main, *cmd) | ||||
|  | ||||
|         return run_external_process(*cmd) | ||||
|  | ||||
|     rc = run_esptool(first_baudrate) | ||||
|     if rc == 0 or first_baudrate == 115200: | ||||
|         return rc | ||||
|     # Try with 115200 baud rate, with some serial chips the faster baud rates do not work well | ||||
|     _LOGGER.info("Upload with baud rate %s failed. Trying again with baud rate 115200.", | ||||
|                  first_baudrate) | ||||
|     return run_esptool(115200) | ||||
|  | ||||
|  | ||||
| def upload_program(config, args, host): | ||||
| @@ -188,6 +185,10 @@ def upload_program(config, args, host): | ||||
|  | ||||
|     from esphome import espota2 | ||||
|  | ||||
|     if CONF_OTA not in config: | ||||
|         raise EsphomeError("Cannot upload Over the Air as the config does not include the ota: " | ||||
|                            "component") | ||||
|  | ||||
|     ota_conf = config[CONF_OTA] | ||||
|     remote_port = ota_conf[CONF_PORT] | ||||
|     password = ota_conf[CONF_PASSWORD] | ||||
| @@ -228,7 +229,7 @@ def setup_log(debug=False, quiet=False): | ||||
|         log_level = logging.INFO | ||||
|     logging.basicConfig(level=log_level) | ||||
|     fmt = "%(levelname)s %(message)s" | ||||
|     colorfmt = "%(log_color)s{}%(reset)s".format(fmt) | ||||
|     colorfmt = f"%(log_color)s{fmt}%(reset)s" | ||||
|     datefmt = '%H:%M:%S' | ||||
|  | ||||
|     logging.getLogger('urllib3').setLevel(logging.WARNING) | ||||
| @@ -277,12 +278,12 @@ def command_compile(args, config): | ||||
|     if exit_code != 0: | ||||
|         return exit_code | ||||
|     if args.only_generate: | ||||
|         _LOGGER.info(u"Successfully generated source code.") | ||||
|         _LOGGER.info("Successfully generated source code.") | ||||
|         return 0 | ||||
|     exit_code = compile_program(args, config) | ||||
|     if exit_code != 0: | ||||
|         return exit_code | ||||
|     _LOGGER.info(u"Successfully compiled program.") | ||||
|     _LOGGER.info("Successfully compiled program.") | ||||
|     return 0 | ||||
|  | ||||
|  | ||||
| @@ -292,7 +293,7 @@ def command_upload(args, config): | ||||
|     exit_code = upload_program(config, args, port) | ||||
|     if exit_code != 0: | ||||
|         return exit_code | ||||
|     _LOGGER.info(u"Successfully uploaded program.") | ||||
|     _LOGGER.info("Successfully uploaded program.") | ||||
|     return 0 | ||||
|  | ||||
|  | ||||
| @@ -309,13 +310,13 @@ def command_run(args, config): | ||||
|     exit_code = compile_program(args, config) | ||||
|     if exit_code != 0: | ||||
|         return exit_code | ||||
|     _LOGGER.info(u"Successfully compiled program.") | ||||
|     _LOGGER.info("Successfully compiled program.") | ||||
|     port = choose_upload_log_host(default=args.upload_port, check_default=None, | ||||
|                                   show_ota=True, show_mqtt=False, show_api=True) | ||||
|     exit_code = upload_program(config, args, port) | ||||
|     if exit_code != 0: | ||||
|         return exit_code | ||||
|     _LOGGER.info(u"Successfully uploaded program.") | ||||
|     _LOGGER.info("Successfully uploaded program.") | ||||
|     if args.no_logs: | ||||
|         return 0 | ||||
|     port = choose_upload_log_host(default=args.upload_port, check_default=port, | ||||
| @@ -334,7 +335,7 @@ def command_mqtt_fingerprint(args, config): | ||||
|  | ||||
|  | ||||
| def command_version(args): | ||||
|     safe_print(u"Version: {}".format(const.__version__)) | ||||
|     safe_print(f"Version: {const.__version__}") | ||||
|     return 0 | ||||
|  | ||||
|  | ||||
| @@ -362,16 +363,17 @@ def command_update_all(args): | ||||
|     twidth = 60 | ||||
|  | ||||
|     def print_bar(middle_text): | ||||
|         middle_text = " {} ".format(middle_text) | ||||
|         middle_text = f" {middle_text} " | ||||
|         width = len(click.unstyle(middle_text)) | ||||
|         half_line = "=" * ((twidth - width) // 2) | ||||
|         click.echo("%s%s%s" % (half_line, middle_text, half_line)) | ||||
|         click.echo(f"{half_line}{middle_text}{half_line}") | ||||
|  | ||||
|     for f in files: | ||||
|         print("Updating {}".format(color('cyan', f))) | ||||
|         print('-' * twidth) | ||||
|         print() | ||||
|         rc = run_external_process('esphome', '--dashboard', f, 'run', '--no-logs') | ||||
|         rc = run_external_process('esphome', '--dashboard', f, 'run', '--no-logs', '--upload-port', | ||||
|                                   'OTA') | ||||
|         if rc == 0: | ||||
|             print_bar("[{}] {}".format(color('bold_green', 'SUCCESS'), f)) | ||||
|             success[f] = True | ||||
| @@ -415,12 +417,14 @@ POST_CONFIG_ACTIONS = { | ||||
|  | ||||
|  | ||||
| def parse_args(argv): | ||||
|     parser = argparse.ArgumentParser(description='ESPHome v{}'.format(const.__version__)) | ||||
|     parser = argparse.ArgumentParser(description=f'ESPHome v{const.__version__}') | ||||
|     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') | ||||
|     parser.add_argument('--dashboard', help=argparse.SUPPRESS, action='store_true') | ||||
|     parser.add_argument('-s', '--substitution', nargs=2, action='append', | ||||
|                         help='Add a substitution', metavar=('key', 'value')) | ||||
|     parser.add_argument('configuration', help='Your YAML configuration file.', nargs='*') | ||||
|  | ||||
|     subparsers = parser.add_subparsers(help='Commands', dest='command') | ||||
| @@ -509,10 +513,10 @@ def run_esphome(argv): | ||||
|         _LOGGER.error("Missing configuration parameter, see esphome --help.") | ||||
|         return 1 | ||||
|  | ||||
|     if IS_PY2: | ||||
|         _LOGGER.warning("You're using ESPHome with python 2. Support for python 2 is deprecated " | ||||
|                         "and will be removed in 1.15.0. Please reinstall ESPHome with python 3.6 " | ||||
|                         "or higher.") | ||||
|     if sys.version_info < (3, 6, 0): | ||||
|         _LOGGER.error("You're running ESPHome with Python <3.6. ESPHome is no longer compatible " | ||||
|                       "with this Python version. Please reinstall ESPHome with Python 3.6+") | ||||
|         return 1 | ||||
|  | ||||
|     if args.command in PRE_CONFIG_ACTIONS: | ||||
|         try: | ||||
| @@ -525,13 +529,13 @@ def run_esphome(argv): | ||||
|         CORE.config_path = conf_path | ||||
|         CORE.dashboard = args.dashboard | ||||
|  | ||||
|         config = read_config() | ||||
|         config = read_config(dict(args.substitution) if args.substitution else {}) | ||||
|         if config is None: | ||||
|             return 1 | ||||
|         CORE.config = config | ||||
|  | ||||
|         if args.command not in POST_CONFIG_ACTIONS: | ||||
|             safe_print(u"Unknown command {}".format(args.command)) | ||||
|             safe_print(f"Unknown command {args.command}") | ||||
|  | ||||
|         try: | ||||
|             rc = POST_CONFIG_ACTIONS[args.command](args, config) | ||||
|   | ||||
| @@ -14,7 +14,6 @@ import esphome.api.api_pb2 as pb | ||||
| from esphome.const import CONF_PASSWORD, CONF_PORT | ||||
| from esphome.core import EsphomeError | ||||
| from esphome.helpers import resolve_ip_address, indent, color | ||||
| from esphome.py_compat import text_type, IS_PY2, byte_to_bytes, char_to_byte | ||||
| from esphome.util import safe_print | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
| @@ -67,16 +66,16 @@ MESSAGE_TYPE_TO_PROTO = { | ||||
|  | ||||
| def _varuint_to_bytes(value): | ||||
|     if value <= 0x7F: | ||||
|         return byte_to_bytes(value) | ||||
|         return bytes([value]) | ||||
|  | ||||
|     ret = bytes() | ||||
|     while value: | ||||
|         temp = value & 0x7F | ||||
|         value >>= 7 | ||||
|         if value: | ||||
|             ret += byte_to_bytes(temp | 0x80) | ||||
|             ret += bytes([temp | 0x80]) | ||||
|         else: | ||||
|             ret += byte_to_bytes(temp) | ||||
|             ret += bytes([temp]) | ||||
|  | ||||
|     return ret | ||||
|  | ||||
| @@ -84,8 +83,7 @@ def _varuint_to_bytes(value): | ||||
| def _bytes_to_varuint(value): | ||||
|     result = 0 | ||||
|     bitpos = 0 | ||||
|     for c in value: | ||||
|         val = char_to_byte(c) | ||||
|     for val in value: | ||||
|         result |= (val & 0x7F) << bitpos | ||||
|         bitpos += 7 | ||||
|         if (val & 0x80) == 0: | ||||
| @@ -191,8 +189,8 @@ class APIClient(threading.Thread): | ||||
|         self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) | ||||
|         try: | ||||
|             self._socket.connect((ip, self._port)) | ||||
|         except socket.error as err: | ||||
|             err = APIConnectionError("Error connecting to {}: {}".format(ip, err)) | ||||
|         except OSError as err: | ||||
|             err = APIConnectionError(f"Error connecting to {ip}: {err}") | ||||
|             self._fatal_error(err) | ||||
|             raise err | ||||
|         self._socket.settimeout(0.1) | ||||
| @@ -200,7 +198,7 @@ class APIClient(threading.Thread): | ||||
|         self._socket_open_event.set() | ||||
|  | ||||
|         hello = pb.HelloRequest() | ||||
|         hello.client_info = 'ESPHome v{}'.format(const.__version__) | ||||
|         hello.client_info = f'ESPHome v{const.__version__}' | ||||
|         try: | ||||
|             resp = self._send_message_await_response(hello, pb.HelloResponse) | ||||
|         except APIConnectionError as err: | ||||
| @@ -251,8 +249,8 @@ class APIClient(threading.Thread): | ||||
|         with self._socket_write_lock: | ||||
|             try: | ||||
|                 self._socket.sendall(data) | ||||
|             except socket.error as err: | ||||
|                 err = APIConnectionError("Error while writing data: {}".format(err)) | ||||
|             except OSError as err: | ||||
|                 err = APIConnectionError(f"Error while writing data: {err}") | ||||
|                 self._fatal_error(err) | ||||
|                 raise err | ||||
|  | ||||
| @@ -265,11 +263,8 @@ class APIClient(threading.Thread): | ||||
|             raise ValueError | ||||
|  | ||||
|         encoded = msg.SerializeToString() | ||||
|         _LOGGER.debug("Sending %s:\n%s", type(msg), indent(text_type(msg))) | ||||
|         if IS_PY2: | ||||
|             req = chr(0x00) | ||||
|         else: | ||||
|             req = bytes([0]) | ||||
|         _LOGGER.debug("Sending %s:\n%s", type(msg), indent(str(msg))) | ||||
|         req = bytes([0]) | ||||
|         req += _varuint_to_bytes(len(encoded)) | ||||
|         req += _varuint_to_bytes(message_type) | ||||
|         req += encoded | ||||
| @@ -355,14 +350,14 @@ class APIClient(threading.Thread): | ||||
|                 raise APIConnectionError("Socket was closed") | ||||
|             except socket.timeout: | ||||
|                 continue | ||||
|             except socket.error as err: | ||||
|                 raise APIConnectionError("Error while receiving data: {}".format(err)) | ||||
|             except OSError as err: | ||||
|                 raise APIConnectionError(f"Error while receiving data: {err}") | ||||
|             ret += val | ||||
|         return ret | ||||
|  | ||||
|     def _recv_varint(self): | ||||
|         raw = bytes() | ||||
|         while not raw or char_to_byte(raw[-1]) & 0x80: | ||||
|         while not raw or raw[-1] & 0x80: | ||||
|             raw += self._recv(1) | ||||
|         return _bytes_to_varuint(raw) | ||||
|  | ||||
| @@ -371,7 +366,7 @@ class APIClient(threading.Thread): | ||||
|             return | ||||
|  | ||||
|         # Preamble | ||||
|         if char_to_byte(self._recv(1)[0]) != 0x00: | ||||
|         if self._recv(1)[0] != 0x00: | ||||
|             raise APIConnectionError("Invalid preamble") | ||||
|  | ||||
|         length = self._recv_varint() | ||||
| @@ -436,7 +431,7 @@ def run_logs(config, address): | ||||
|             return | ||||
|  | ||||
|         if err: | ||||
|             _LOGGER.warning(u"Disconnected from API: %s", err) | ||||
|             _LOGGER.warning("Disconnected from API: %s", err) | ||||
|  | ||||
|         while retry_timer: | ||||
|             retry_timer.pop(0).cancel() | ||||
| @@ -454,18 +449,18 @@ def run_logs(config, address): | ||||
|  | ||||
|         wait_time = int(min(1.5**min(tries, 100), 30)) | ||||
|         if not has_connects: | ||||
|             _LOGGER.warning(u"Initial connection failed. The ESP might not be connected " | ||||
|                             u"to WiFi yet (%s). Re-Trying in %s seconds", | ||||
|             _LOGGER.warning("Initial connection failed. The ESP might not be connected " | ||||
|                             "to WiFi yet (%s). Re-Trying in %s seconds", | ||||
|                             error, wait_time) | ||||
|         else: | ||||
|             _LOGGER.warning(u"Couldn't connect to API (%s). Trying to reconnect in %s seconds", | ||||
|             _LOGGER.warning("Couldn't connect to API (%s). Trying to reconnect in %s seconds", | ||||
|                             error, wait_time) | ||||
|         timer = threading.Timer(wait_time, functools.partial(try_connect, None, tries + 1)) | ||||
|         timer.start() | ||||
|         retry_timer.append(timer) | ||||
|  | ||||
|     def on_log(msg): | ||||
|         time_ = datetime.now().time().strftime(u'[%H:%M:%S]') | ||||
|         time_ = datetime.now().time().strftime('[%H:%M:%S]') | ||||
|         text = msg.message | ||||
|         if msg.send_failed: | ||||
|             text = color('white', '(Message skipped because it was too big to fit in ' | ||||
|   | ||||
| @@ -7,13 +7,17 @@ from esphome.util import Registry | ||||
|  | ||||
|  | ||||
| def maybe_simple_id(*validators): | ||||
|     return maybe_conf(CONF_ID, *validators) | ||||
|  | ||||
|  | ||||
| def maybe_conf(conf, *validators): | ||||
|     validator = cv.All(*validators) | ||||
|  | ||||
|     def validate(value): | ||||
|         if isinstance(value, dict): | ||||
|             return validator(value) | ||||
|         with cv.remove_prepend_path([CONF_ID]): | ||||
|             return validator({CONF_ID: value}) | ||||
|         with cv.remove_prepend_path([conf]): | ||||
|             return validator({conf: value}) | ||||
|  | ||||
|     return validate | ||||
|  | ||||
| @@ -79,9 +83,9 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): | ||||
|                 try: | ||||
|                     return cv.Schema([schema])(value) | ||||
|                 except cv.Invalid as err2: | ||||
|                     if u'extra keys not allowed' in str(err2) and len(err2.path) == 2: | ||||
|                     if 'extra keys not allowed' in str(err2) and len(err2.path) == 2: | ||||
|                         raise err | ||||
|                     if u'Unable to find action' in str(err): | ||||
|                     if 'Unable to find action' in str(err): | ||||
|                         raise err2 | ||||
|                     raise cv.MultipleInvalid([err, err2]) | ||||
|         elif isinstance(value, dict): | ||||
|   | ||||
| @@ -19,7 +19,7 @@ from esphome.cpp_helpers import (  # noqa | ||||
|     gpio_pin_expression, register_component, build_registry_entry, | ||||
|     build_registry_list, extract_registry_entry_config, register_parented) | ||||
| from esphome.cpp_types import (  # noqa | ||||
|     global_ns, void, nullptr, float_, double, bool_, std_ns, std_string, | ||||
|     global_ns, void, nullptr, float_, double, bool_, int_, std_ns, std_string, | ||||
|     std_vector, uint8, uint16, uint32, int32, const_char_ptr, NAN, | ||||
|     esphome_ns, App, Nameable, Component, ComponentPtr, | ||||
|     PollingComponent, Application, optional, arduino_json_ns, JsonObject, | ||||
|   | ||||
							
								
								
									
										0
									
								
								esphome/components/ac_dimmer/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/ac_dimmer/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										217
									
								
								esphome/components/ac_dimmer/ac_dimmer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								esphome/components/ac_dimmer/ac_dimmer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | ||||
| #include "ac_dimmer.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| #ifdef ARDUINO_ARCH_ESP8266 | ||||
| #include <core_esp8266_waveform.h> | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ac_dimmer { | ||||
|  | ||||
| static const char *TAG = "ac_dimmer"; | ||||
|  | ||||
| // Global array to store dimmer objects | ||||
| static AcDimmerDataStore *all_dimmers[32]; | ||||
|  | ||||
| /// Time in microseconds the gate should be held high | ||||
| /// 10µs should be long enough for most triacs | ||||
| /// For reference: BT136 datasheet says 2µs nominal (page 7) | ||||
| static uint32_t GATE_ENABLE_TIME = 10; | ||||
|  | ||||
| /// Function called from timer interrupt | ||||
| /// Input is current time in microseconds (micros()) | ||||
| /// Returns when next "event" is expected in µs, or 0 if no such event known. | ||||
| uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) { | ||||
|   // If no ZC signal received yet. | ||||
|   if (this->crossed_zero_at == 0) | ||||
|     return 0; | ||||
|  | ||||
|   uint32_t time_since_zc = now - this->crossed_zero_at; | ||||
|   if (this->value == 65535 || this->value == 0) { | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) { | ||||
|     this->enable_time_us = 0; | ||||
|     this->gate_pin->digital_write(true); | ||||
|     // Prevent too short pulses | ||||
|     this->disable_time_us = max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME); | ||||
|   } | ||||
|   if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) { | ||||
|     this->disable_time_us = 0; | ||||
|     this->gate_pin->digital_write(false); | ||||
|   } | ||||
|  | ||||
|   if (time_since_zc < this->enable_time_us) | ||||
|     // Next event is enable, return time until that event | ||||
|     return this->enable_time_us - time_since_zc; | ||||
|   else if (time_since_zc < disable_time_us) { | ||||
|     // Next event is disable, return time until that event | ||||
|     return this->disable_time_us - time_since_zc; | ||||
|   } | ||||
|  | ||||
|   if (time_since_zc >= this->cycle_time_us) { | ||||
|     // Already past last cycle time, schedule next call shortly | ||||
|     return 100; | ||||
|   } | ||||
|  | ||||
|   return this->cycle_time_us - time_since_zc; | ||||
| } | ||||
|  | ||||
| /// Run timer interrupt code and return in how many µs the next event is expected | ||||
| uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() { | ||||
|   // run at least with 1kHz | ||||
|   uint32_t min_dt_us = 1000; | ||||
|   uint32_t now = micros(); | ||||
|   for (auto *dimmer : all_dimmers) { | ||||
|     if (dimmer == nullptr) | ||||
|       // no more dimmers | ||||
|       break; | ||||
|     uint32_t res = dimmer->timer_intr(now); | ||||
|     if (res != 0 && res < min_dt_us) | ||||
|       min_dt_us = res; | ||||
|   } | ||||
|   // return time until next timer1 interrupt in µs | ||||
|   return min_dt_us; | ||||
| } | ||||
|  | ||||
| /// GPIO interrupt routine, called when ZC pin triggers | ||||
| void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() { | ||||
|   uint32_t prev_crossed = this->crossed_zero_at; | ||||
|  | ||||
|   // 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms | ||||
|   // in any case the cycle last at least 5ms | ||||
|   this->crossed_zero_at = micros(); | ||||
|   uint32_t cycle_time = this->crossed_zero_at - prev_crossed; | ||||
|   if (cycle_time > 5000) { | ||||
|     this->cycle_time_us = cycle_time; | ||||
|   } else { | ||||
|     // Otherwise this is noise and this is 2nd (or 3rd...) fall in the same pulse | ||||
|     // Consider this is the right fall edge and accumulate the cycle time instead | ||||
|     this->cycle_time_us += cycle_time; | ||||
|   } | ||||
|  | ||||
|   if (this->value == 65535) { | ||||
|     // fully on, enable output immediately | ||||
|     this->gate_pin->digital_write(true); | ||||
|   } else if (this->init_cycle) { | ||||
|     // send a full cycle | ||||
|     this->init_cycle = false; | ||||
|     this->enable_time_us = 0; | ||||
|     this->disable_time_us = cycle_time_us; | ||||
|   } else if (this->value == 0) { | ||||
|     // fully off, disable output immediately | ||||
|     this->gate_pin->digital_write(false); | ||||
|   } else { | ||||
|     if (this->method == DIM_METHOD_TRAILING) { | ||||
|       this->enable_time_us = 1;  // cannot be 0 | ||||
|       this->disable_time_us = max((uint32_t) 10, this->value * this->cycle_time_us / 65535); | ||||
|     } else { | ||||
|       // calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic | ||||
|       // also take into account min_power | ||||
|       auto min_us = this->cycle_time_us * this->min_power / 1000; | ||||
|       this->enable_time_us = max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535); | ||||
|       if (this->method == DIM_METHOD_LEADING_PULSE) { | ||||
|         // Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone | ||||
|         // this is for brightness near 99% | ||||
|         this->disable_time_us = max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10); | ||||
|       } else { | ||||
|         this->gate_pin->digital_write(false); | ||||
|         this->disable_time_us = this->cycle_time_us; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) { | ||||
|   // Attaching pin interrupts on the same pin will override the previous interupt | ||||
|   // However, the user expects that multiple dimmers sharing the same ZC pin will work. | ||||
|   // We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers | ||||
|   // if any of them are using the same ZC pin, and also trigger the interrupt for *them*. | ||||
|   for (auto *dimmer : all_dimmers) { | ||||
|     if (dimmer == nullptr) | ||||
|       break; | ||||
|     if (dimmer->zero_cross_pin_number == store->zero_cross_pin_number) { | ||||
|       dimmer->gpio_intr(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| #ifdef ARDUINO_ARCH_ESP32 | ||||
| // ESP32 implementation, uses basically the same code but needs to wrap | ||||
| // timer_interrupt() function to auto-reschedule | ||||
| static hw_timer_t *dimmer_timer = nullptr; | ||||
| void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); } | ||||
| #endif | ||||
|  | ||||
| void AcDimmer::setup() { | ||||
|   // extend all_dimmers array with our dimmer | ||||
|  | ||||
|   // Need to be sure the zero cross pin is setup only once, ESP8266 fails and ESP32 seems to fail silently | ||||
|   auto setup_zero_cross_pin = true; | ||||
|  | ||||
|   for (auto &all_dimmer : all_dimmers) { | ||||
|     if (all_dimmer == nullptr) { | ||||
|       all_dimmer = &this->store_; | ||||
|       break; | ||||
|     } | ||||
|     if (all_dimmer->zero_cross_pin_number == this->zero_cross_pin_->get_pin()) { | ||||
|       setup_zero_cross_pin = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   this->gate_pin_->setup(); | ||||
|   this->store_.gate_pin = this->gate_pin_->to_isr(); | ||||
|   this->store_.zero_cross_pin_number = this->zero_cross_pin_->get_pin(); | ||||
|   this->store_.min_power = static_cast<uint16_t>(this->min_power_ * 1000); | ||||
|   this->min_power_ = 0; | ||||
|   this->store_.method = this->method_; | ||||
|  | ||||
|   if (setup_zero_cross_pin) { | ||||
|     this->zero_cross_pin_->setup(); | ||||
|     this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr(); | ||||
|     this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, FALLING); | ||||
|   } | ||||
|  | ||||
| #ifdef ARDUINO_ARCH_ESP8266 | ||||
|   // Uses ESP8266 waveform (soft PWM) class | ||||
|   // PWM and AcDimmer can even run at the same time this way | ||||
|   setTimer1Callback(&timer_interrupt); | ||||
| #endif | ||||
| #ifdef ARDUINO_ARCH_ESP32 | ||||
|   // 80 Divider -> 1 count=1µs | ||||
|   dimmer_timer = timerBegin(0, 80, true); | ||||
|   timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true); | ||||
|   // For ESP32, we can't use dynamic interval calculation because the timerX functions | ||||
|   // are not callable from ISR (placed in flash storage). | ||||
|   // Here we just use an interrupt firing every 50 µs. | ||||
|   timerAlarmWrite(dimmer_timer, 50, true); | ||||
|   timerAlarmEnable(dimmer_timer); | ||||
| #endif | ||||
| } | ||||
| void AcDimmer::write_state(float state) { | ||||
|   auto new_value = static_cast<uint16_t>(roundf(state * 65535)); | ||||
|   if (new_value != 0 && this->store_.value == 0) | ||||
|     this->store_.init_cycle = this->init_with_half_cycle_; | ||||
|   this->store_.value = new_value; | ||||
| } | ||||
| void AcDimmer::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "AcDimmer:"); | ||||
|   LOG_PIN("  Output Pin: ", this->gate_pin_); | ||||
|   LOG_PIN("  Zero-Cross Pin: ", this->zero_cross_pin_); | ||||
|   ESP_LOGCONFIG(TAG, "   Min Power: %.1f%%", this->store_.min_power / 10.0f); | ||||
|   ESP_LOGCONFIG(TAG, "   Init with half cycle: %s", YESNO(this->init_with_half_cycle_)); | ||||
|   if (method_ == DIM_METHOD_LEADING_PULSE) | ||||
|     ESP_LOGCONFIG(TAG, "   Method: leading pulse"); | ||||
|   else if (method_ == DIM_METHOD_LEADING) | ||||
|     ESP_LOGCONFIG(TAG, "   Method: leading"); | ||||
|   else | ||||
|     ESP_LOGCONFIG(TAG, "   Method: trailing"); | ||||
|  | ||||
|   LOG_FLOAT_OUTPUT(this); | ||||
|   ESP_LOGV(TAG, "  Estimated Frequency: %.3fHz", 1e6f / this->store_.cycle_time_us / 2); | ||||
| } | ||||
|  | ||||
| }  // namespace ac_dimmer | ||||
| }  // namespace esphome | ||||
							
								
								
									
										66
									
								
								esphome/components/ac_dimmer/ac_dimmer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								esphome/components/ac_dimmer/ac_dimmer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/esphal.h" | ||||
| #include "esphome/components/output/float_output.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ac_dimmer { | ||||
|  | ||||
| enum DimMethod { DIM_METHOD_LEADING_PULSE = 0, DIM_METHOD_LEADING, DIM_METHOD_TRAILING }; | ||||
|  | ||||
| struct AcDimmerDataStore { | ||||
|   /// Zero-cross pin | ||||
|   ISRInternalGPIOPin *zero_cross_pin; | ||||
|   /// Zero-cross pin number - used to share ZC pin across multiple dimmers | ||||
|   uint8_t zero_cross_pin_number; | ||||
|   /// Output pin to write to | ||||
|   ISRInternalGPIOPin *gate_pin; | ||||
|   /// Value of the dimmer - 0 to 65535. | ||||
|   uint16_t value; | ||||
|   /// Minimum power for activation | ||||
|   uint16_t min_power; | ||||
|   /// Time between the last two ZC pulses | ||||
|   uint32_t cycle_time_us; | ||||
|   /// Time (in micros()) of last ZC signal | ||||
|   uint32_t crossed_zero_at; | ||||
|   /// Time since last ZC pulse to enable gate pin. 0 means not set. | ||||
|   uint32_t enable_time_us; | ||||
|   /// Time since last ZC pulse to disable gate pin. 0 means no disable. | ||||
|   uint32_t disable_time_us; | ||||
|   /// Set to send the first half ac cycle complete | ||||
|   bool init_cycle; | ||||
|   /// Dimmer method | ||||
|   DimMethod method; | ||||
|  | ||||
|   uint32_t timer_intr(uint32_t now); | ||||
|  | ||||
|   void gpio_intr(); | ||||
|   static void s_gpio_intr(AcDimmerDataStore *store); | ||||
| #ifdef ARDUINO_ARCH_ESP32 | ||||
|   static void s_timer_intr(); | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| class AcDimmer : public output::FloatOutput, public Component { | ||||
|  public: | ||||
|   void setup() override; | ||||
|  | ||||
|   void dump_config() override; | ||||
|   void set_gate_pin(GPIOPin *gate_pin) { gate_pin_ = gate_pin; } | ||||
|   void set_zero_cross_pin(GPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; } | ||||
|   void set_init_with_half_cycle(bool init_with_half_cycle) { init_with_half_cycle_ = init_with_half_cycle; } | ||||
|   void set_method(DimMethod method) { method_ = method; } | ||||
|  | ||||
|  protected: | ||||
|   void write_state(float state) override; | ||||
|  | ||||
|   GPIOPin *gate_pin_; | ||||
|   GPIOPin *zero_cross_pin_; | ||||
|   AcDimmerDataStore store_; | ||||
|   bool init_with_half_cycle_; | ||||
|   DimMethod method_; | ||||
| }; | ||||
|  | ||||
| }  // namespace ac_dimmer | ||||
| }  // namespace esphome | ||||
							
								
								
									
										45
									
								
								esphome/components/ac_dimmer/output.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								esphome/components/ac_dimmer/output.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins | ||||
| from esphome.components import output | ||||
| from esphome.const import CONF_ID, CONF_MIN_POWER, CONF_METHOD | ||||
|  | ||||
| CODEOWNERS = ['@glmnet'] | ||||
|  | ||||
| ac_dimmer_ns = cg.esphome_ns.namespace('ac_dimmer') | ||||
| AcDimmer = ac_dimmer_ns.class_('AcDimmer', output.FloatOutput, cg.Component) | ||||
|  | ||||
| DimMethod = ac_dimmer_ns.enum('DimMethod') | ||||
| DIM_METHODS = { | ||||
|     'LEADING_PULSE': DimMethod.DIM_METHOD_LEADING_PULSE, | ||||
|     'LEADING': DimMethod.DIM_METHOD_LEADING, | ||||
|     'TRAILING': DimMethod.DIM_METHOD_TRAILING, | ||||
| } | ||||
|  | ||||
| CONF_GATE_PIN = 'gate_pin' | ||||
| CONF_ZERO_CROSS_PIN = 'zero_cross_pin' | ||||
| CONF_INIT_WITH_HALF_CYCLE = 'init_with_half_cycle' | ||||
| CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ | ||||
|     cv.Required(CONF_ID): cv.declare_id(AcDimmer), | ||||
|     cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema, | ||||
|     cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema, | ||||
|     cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean, | ||||
|     cv.Optional(CONF_METHOD, default='leading pulse'): cv.enum(DIM_METHODS, upper=True, space='_'), | ||||
| }).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     yield 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) | ||||
|  | ||||
|     pin = yield 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]) | ||||
|     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])) | ||||
							
								
								
									
										24
									
								
								esphome/components/adalight/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphome/components/adalight/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import uart | ||||
| from esphome.components.light.types import AddressableLightEffect | ||||
| from esphome.components.light.effects import register_addressable_effect | ||||
| from esphome.const import CONF_NAME, CONF_UART_ID | ||||
|  | ||||
| DEPENDENCIES = ['uart'] | ||||
|  | ||||
| adalight_ns = cg.esphome_ns.namespace('adalight') | ||||
| AdalightLightEffect = adalight_ns.class_( | ||||
|     'AdalightLightEffect', uart.UARTDevice, AddressableLightEffect) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema({}) | ||||
|  | ||||
|  | ||||
| @register_addressable_effect('adalight', AdalightLightEffect, "Adalight", { | ||||
|     cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent) | ||||
| }) | ||||
| 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 | ||||
							
								
								
									
										140
									
								
								esphome/components/adalight/adalight_light_effect.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								esphome/components/adalight/adalight_light_effect.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| #include "adalight_light_effect.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace adalight { | ||||
|  | ||||
| static const char *TAG = "adalight_light_effect"; | ||||
|  | ||||
| static const uint32_t ADALIGHT_ACK_INTERVAL = 1000; | ||||
| static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000; | ||||
|  | ||||
| AdalightLightEffect::AdalightLightEffect(const std::string &name) : AddressableLightEffect(name) {} | ||||
|  | ||||
| void AdalightLightEffect::start() { | ||||
|   AddressableLightEffect::start(); | ||||
|  | ||||
|   last_ack_ = 0; | ||||
|   last_byte_ = 0; | ||||
|   last_reset_ = 0; | ||||
| } | ||||
|  | ||||
| void AdalightLightEffect::stop() { | ||||
|   frame_.resize(0); | ||||
|  | ||||
|   AddressableLightEffect::stop(); | ||||
| } | ||||
|  | ||||
| int AdalightLightEffect::get_frame_size_(int led_count) const { | ||||
|   // 3 bytes: Ada | ||||
|   // 2 bytes: LED count | ||||
|   // 1 byte: checksum | ||||
|   // 3 bytes per LED | ||||
|   return 3 + 2 + 1 + led_count * 3; | ||||
| } | ||||
|  | ||||
| void AdalightLightEffect::reset_frame_(light::AddressableLight &it) { | ||||
|   int buffer_capacity = get_frame_size_(it.size()); | ||||
|  | ||||
|   frame_.clear(); | ||||
|   frame_.reserve(buffer_capacity); | ||||
| } | ||||
|  | ||||
| void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) { | ||||
|   for (int led = it.size(); led-- > 0;) { | ||||
|     it[led].set(light::ESPColor::BLACK); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void AdalightLightEffect::apply(light::AddressableLight &it, const light::ESPColor ¤t_color) { | ||||
|   const uint32_t now = millis(); | ||||
|  | ||||
|   if (now - this->last_ack_ >= ADALIGHT_ACK_INTERVAL) { | ||||
|     ESP_LOGV(TAG, "Sending ACK"); | ||||
|     this->write_str("Ada\n"); | ||||
|     this->last_ack_ = now; | ||||
|   } | ||||
|  | ||||
|   if (!this->last_reset_) { | ||||
|     ESP_LOGW(TAG, "Frame: Reset."); | ||||
|     reset_frame_(it); | ||||
|     blank_all_leds_(it); | ||||
|     this->last_reset_ = now; | ||||
|   } | ||||
|  | ||||
|   if (!this->frame_.empty() && now - this->last_byte_ >= ADALIGHT_RECEIVE_TIMEOUT) { | ||||
|     ESP_LOGW(TAG, "Frame: Receive timeout (size=%zu).", this->frame_.size()); | ||||
|     reset_frame_(it); | ||||
|     blank_all_leds_(it); | ||||
|   } | ||||
|  | ||||
|   if (this->available() > 0) { | ||||
|     ESP_LOGV(TAG, "Frame: Available (size=%d).", this->available()); | ||||
|   } | ||||
|  | ||||
|   while (this->available() != 0) { | ||||
|     uint8_t data; | ||||
|     if (!this->read_byte(&data)) | ||||
|       break; | ||||
|     this->frame_.push_back(data); | ||||
|     this->last_byte_ = now; | ||||
|  | ||||
|     switch (this->parse_frame_(it)) { | ||||
|       case INVALID: | ||||
|         ESP_LOGD(TAG, "Frame: Invalid (size=%zu, first=%d).", this->frame_.size(), this->frame_[0]); | ||||
|         reset_frame_(it); | ||||
|         break; | ||||
|  | ||||
|       case PARTIAL: | ||||
|         break; | ||||
|  | ||||
|       case CONSUMED: | ||||
|         ESP_LOGV(TAG, "Frame: Consumed (size=%zu).", this->frame_.size()); | ||||
|         reset_frame_(it); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableLight &it) { | ||||
|   if (frame_.empty()) | ||||
|     return INVALID; | ||||
|  | ||||
|   // Check header: `Ada` | ||||
|   if (frame_[0] != 'A') | ||||
|     return INVALID; | ||||
|   if (frame_.size() > 1 && frame_[1] != 'd') | ||||
|     return INVALID; | ||||
|   if (frame_.size() > 2 && frame_[2] != 'a') | ||||
|     return INVALID; | ||||
|  | ||||
|   // 3 bytes: Count Hi, Count Lo, Checksum | ||||
|   if (frame_.size() < 6) | ||||
|     return PARTIAL; | ||||
|  | ||||
|   // Check checksum | ||||
|   uint16_t checksum = frame_[3] ^ frame_[4] ^ 0x55; | ||||
|   if (checksum != frame_[5]) | ||||
|     return INVALID; | ||||
|  | ||||
|   // Check if we received the full frame | ||||
|   uint16_t led_count = (frame_[3] << 8) + frame_[4] + 1; | ||||
|   auto buffer_size = get_frame_size_(led_count); | ||||
|   if (frame_.size() < buffer_size) | ||||
|     return PARTIAL; | ||||
|  | ||||
|   // Apply lights | ||||
|   auto accepted_led_count = std::min<int>(led_count, it.size()); | ||||
|   uint8_t *led_data = &frame_[6]; | ||||
|  | ||||
|   for (int led = 0; led < accepted_led_count; led++, led_data += 3) { | ||||
|     auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]); | ||||
|  | ||||
|     it[led].set(light::ESPColor(led_data[0], led_data[1], led_data[2], white)); | ||||
|   } | ||||
|  | ||||
|   return CONSUMED; | ||||
| } | ||||
|  | ||||
| }  // namespace adalight | ||||
| }  // namespace esphome | ||||
							
								
								
									
										41
									
								
								esphome/components/adalight/adalight_light_effect.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								esphome/components/adalight/adalight_light_effect.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/light/addressable_light_effect.h" | ||||
| #include "esphome/components/uart/uart.h" | ||||
|  | ||||
| #include <vector> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace adalight { | ||||
|  | ||||
| class AdalightLightEffect : public light::AddressableLightEffect, public uart::UARTDevice { | ||||
|  public: | ||||
|   AdalightLightEffect(const std::string &name); | ||||
|  | ||||
|  public: | ||||
|   void start() override; | ||||
|   void stop() override; | ||||
|   void apply(light::AddressableLight &it, const light::ESPColor ¤t_color) override; | ||||
|  | ||||
|  protected: | ||||
|   enum Frame { | ||||
|     INVALID, | ||||
|     PARTIAL, | ||||
|     CONSUMED, | ||||
|   }; | ||||
|  | ||||
|   int get_frame_size_(int led_count) const; | ||||
|   void reset_frame_(light::AddressableLight &it); | ||||
|   void blank_all_leds_(light::AddressableLight &it); | ||||
|   Frame parse_frame_(light::AddressableLight &it); | ||||
|  | ||||
|  protected: | ||||
|   uint32_t last_ack_{0}; | ||||
|   uint32_t last_byte_{0}; | ||||
|   uint32_t last_reset_{0}; | ||||
|   std::vector<uint8_t> frame_; | ||||
| }; | ||||
|  | ||||
| }  // namespace adalight | ||||
| }  // namespace esphome | ||||
| @@ -0,0 +1 @@ | ||||
| CODEOWNERS = ['@esphome/core'] | ||||
|   | ||||
| @@ -58,7 +58,7 @@ void ADCSensor::update() { | ||||
| } | ||||
| float ADCSensor::sample() { | ||||
| #ifdef ARDUINO_ARCH_ESP32 | ||||
|   float value_v = analogRead(this->pin_) / 4095.0f; | ||||
|   float value_v = analogRead(this->pin_) / 4095.0f;  // NOLINT | ||||
|   switch (this->attenuation_) { | ||||
|     case ADC_0db: | ||||
|       value_v *= 1.1; | ||||
| @@ -80,7 +80,7 @@ float ADCSensor::sample() { | ||||
| #ifdef USE_ADC_SENSOR_VCC | ||||
|   return ESP.getVcc() / 1024.0f; | ||||
| #else | ||||
|   return analogRead(this->pin_) / 1024.0f; | ||||
|   return analogRead(this->pin_) / 1024.0f;  // NOLINT | ||||
| #endif | ||||
| #endif | ||||
| } | ||||
|   | ||||
| @@ -18,7 +18,7 @@ void ADE7953::dump_config() { | ||||
| } | ||||
|  | ||||
| #define ADE_PUBLISH_(name, factor) \ | ||||
|   if (name) { \ | ||||
|   if (name && this->name##_sensor_) { \ | ||||
|     float value = *name / factor; \ | ||||
|     this->name##_sensor_->publish_state(value); \ | ||||
|   } | ||||
|   | ||||
| @@ -36,4 +36,4 @@ def to_code(config): | ||||
|             continue | ||||
|         conf = config[key] | ||||
|         sens = yield sensor.new_sensor(conf) | ||||
|         cg.add(getattr(var, 'set_{}_sensor'.format(key))(sens)) | ||||
|         cg.add(getattr(var, f'set_{key}_sensor')(sens)) | ||||
|   | ||||
| @@ -2,7 +2,6 @@ import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import sensor, voltage_sampler | ||||
| from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, ICON_FLASH, UNIT_VOLT, CONF_ID | ||||
| from esphome.py_compat import string_types | ||||
| from . import ads1115_ns, ADS1115Component | ||||
|  | ||||
| DEPENDENCIES = ['ads1115'] | ||||
| @@ -32,9 +31,9 @@ GAIN = { | ||||
|  | ||||
| def validate_gain(value): | ||||
|     if isinstance(value, float): | ||||
|         value = u'{:0.03f}'.format(value) | ||||
|     elif not isinstance(value, string_types): | ||||
|         raise cv.Invalid('invalid gain "{}"'.format(value)) | ||||
|         value = f'{value:0.03f}' | ||||
|     elif not isinstance(value, str): | ||||
|         raise cv.Invalid(f'invalid gain "{value}"') | ||||
|  | ||||
|     return cv.enum(GAIN)(value) | ||||
|  | ||||
|   | ||||
							
								
								
									
										0
									
								
								esphome/components/aht10/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/aht10/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										127
									
								
								esphome/components/aht10/aht10.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								esphome/components/aht10/aht10.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| // Implementation based on: | ||||
| //  - AHT10: https://github.com/Thinary/AHT10 | ||||
| //  - Official Datasheet (cn): | ||||
| //  http://www.aosong.com/userfiles/files/media/aht10%E8%A7%84%E6%A0%BC%E4%B9%A6v1_1%EF%BC%8820191015%EF%BC%89.pdf | ||||
| //  - Unofficial Translated Datasheet (en): | ||||
| //  https://wiki.liutyi.info/download/attachments/30507639/Aosong_AHT10_en_draft_0c.pdf | ||||
| // | ||||
| // When configured for humidity, the log 'Components should block for at most 20-30ms in loop().' will be generated in | ||||
| // verbose mode. This is due to technical specs of the sensor and can not be avoided. | ||||
| // | ||||
| // According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost | ||||
| // immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best | ||||
| // results making successive requests; the current implementation make 3 attemps with a delay of 30ms each time. | ||||
|  | ||||
| #include "aht10.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace aht10 { | ||||
|  | ||||
| static const char *TAG = "aht10"; | ||||
| static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1}; | ||||
| static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00}; | ||||
| static const uint8_t AHT10_DEFAULT_DELAY = 5;    // ms, for calibration and temperature measurement | ||||
| static const uint8_t AHT10_HUMIDITY_DELAY = 30;  // ms | ||||
| static const uint8_t AHT10_ATTEMPS = 3;          // safety margin, normally 3 attemps are enough: 3*30=90ms | ||||
|  | ||||
| void AHT10Component::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up AHT10..."); | ||||
|  | ||||
|   if (!this->write_bytes(0, AHT10_CALIBRATE_CMD, sizeof(AHT10_CALIBRATE_CMD))) { | ||||
|     ESP_LOGE(TAG, "Communication with AHT10 failed!"); | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|   uint8_t data; | ||||
|   if (!this->read_byte(0, &data, AHT10_DEFAULT_DELAY)) { | ||||
|     ESP_LOGD(TAG, "Communication with AHT10 failed!"); | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|   if ((data & 0x68) != 0x08) {  // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED | ||||
|     ESP_LOGE(TAG, "AHT10 calibration failed!"); | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   ESP_LOGV(TAG, "AHT10 calibrated"); | ||||
| } | ||||
|  | ||||
| void AHT10Component::update() { | ||||
|   if (!this->write_bytes(0, AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD))) { | ||||
|     ESP_LOGE(TAG, "Communication with AHT10 failed!"); | ||||
|     this->status_set_warning(); | ||||
|     return; | ||||
|   } | ||||
|   uint8_t data[6]; | ||||
|   uint8_t delay = AHT10_DEFAULT_DELAY; | ||||
|   if (this->humidity_sensor_ != nullptr) | ||||
|     delay = AHT10_HUMIDITY_DELAY; | ||||
|   for (int i = 0; i < AHT10_ATTEMPS; ++i) { | ||||
|     ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis()); | ||||
|     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 | ||||
|       ESP_LOGD(TAG, "AHT10 is busy, waiting..."); | ||||
|     } else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) { | ||||
|       // Unrealistic humidity (0x0) | ||||
|       if (this->humidity_sensor_ == nullptr) { | ||||
|         ESP_LOGVV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required"); | ||||
|         break; | ||||
|       } else { | ||||
|         ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying..."); | ||||
|         if (!this->write_bytes(0, AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD))) { | ||||
|           ESP_LOGE(TAG, "Communication with AHT10 failed!"); | ||||
|           this->status_set_warning(); | ||||
|           return; | ||||
|         } | ||||
|       } | ||||
|     } else { | ||||
|       // data is valid, we can break the loop | ||||
|       ESP_LOGVV(TAG, "Answer at %6ld", millis()); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   if ((data[0] & 0x80) == 0x80) { | ||||
|     ESP_LOGE(TAG, "Measurements reading timed-out!"); | ||||
|     this->status_set_warning(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]; | ||||
|   uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4; | ||||
|  | ||||
|   float temperature = ((200.0 * (float) raw_temperature) / 1048576.0) - 50.0; | ||||
|   float humidity; | ||||
|   if (raw_humidity == 0) {  // unrealistic value | ||||
|     humidity = NAN; | ||||
|   } else { | ||||
|     humidity = (float) raw_humidity * 100.0 / 1048576.0; | ||||
|   } | ||||
|  | ||||
|   if (this->temperature_sensor_ != nullptr) { | ||||
|     this->temperature_sensor_->publish_state(temperature); | ||||
|   } | ||||
|   if (this->humidity_sensor_ != nullptr) { | ||||
|     if (isnan(humidity)) | ||||
|       ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum"); | ||||
|     this->humidity_sensor_->publish_state(humidity); | ||||
|   } | ||||
|   this->status_clear_warning(); | ||||
| } | ||||
|  | ||||
| float AHT10Component::get_setup_priority() const { return setup_priority::DATA; } | ||||
|  | ||||
| void AHT10Component::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "AHT10:"); | ||||
|   LOG_I2C_DEVICE(this); | ||||
|   if (this->is_failed()) { | ||||
|     ESP_LOGE(TAG, "Communication with AHT10 failed!"); | ||||
|   } | ||||
|   LOG_SENSOR("  ", "Temperature", this->temperature_sensor_); | ||||
|   LOG_SENSOR("  ", "Humidity", this->humidity_sensor_); | ||||
| } | ||||
|  | ||||
| }  // namespace aht10 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										26
									
								
								esphome/components/aht10/aht10.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								esphome/components/aht10/aht10.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #include "esphome/components/i2c/i2c.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace aht10 { | ||||
|  | ||||
| class AHT10Component : public PollingComponent, public i2c::I2CDevice { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   void update() override; | ||||
|   void dump_config() override; | ||||
|   float get_setup_priority() const override; | ||||
|  | ||||
|   void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } | ||||
|   void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } | ||||
|  | ||||
|  protected: | ||||
|   sensor::Sensor *temperature_sensor_; | ||||
|   sensor::Sensor *humidity_sensor_; | ||||
| }; | ||||
|  | ||||
| }  // namespace aht10 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										30
									
								
								esphome/components/aht10/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								esphome/components/aht10/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import i2c, sensor | ||||
| from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ | ||||
|     UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT | ||||
|  | ||||
| DEPENDENCIES = ['i2c'] | ||||
|  | ||||
| aht10_ns = cg.esphome_ns.namespace('aht10') | ||||
| AHT10Component = aht10_ns.class_('AHT10Component', cg.PollingComponent, i2c.I2CDevice) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema({ | ||||
|     cv.GenerateID(): cv.declare_id(AHT10Component), | ||||
|     cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 2), | ||||
|     cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 2), | ||||
| }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38)) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     yield cg.register_component(var, config) | ||||
|     yield i2c.register_i2c_device(var, config) | ||||
|  | ||||
|     if CONF_TEMPERATURE in config: | ||||
|         sens = yield 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]) | ||||
|         cg.add(var.set_humidity_sensor(sens)) | ||||
| @@ -8,6 +8,7 @@ from esphome.core import coroutine_with_priority | ||||
|  | ||||
| DEPENDENCIES = ['network'] | ||||
| AUTO_LOAD = ['async_tcp'] | ||||
| CODEOWNERS = ['@OttoWinter'] | ||||
|  | ||||
| api_ns = cg.esphome_ns.namespace('api') | ||||
| APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller) | ||||
| @@ -70,14 +71,14 @@ def to_code(config): | ||||
|     cg.add_global(api_ns.using) | ||||
|  | ||||
|  | ||||
| KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string)}) | ||||
| KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)}) | ||||
|  | ||||
| HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({ | ||||
|     cv.GenerateID(): cv.use_id(APIServer), | ||||
|     cv.Required(CONF_SERVICE): cv.templatable(cv.string), | ||||
|     cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, | ||||
|     cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, | ||||
|     cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA, | ||||
|     cv.Optional(CONF_VARIABLES, default={}): cv.Schema({cv.string: cv.returning_lambda}), | ||||
| }) | ||||
|  | ||||
|  | ||||
| @@ -102,7 +103,7 @@ def homeassistant_service_to_code(config, action_id, template_arg, args): | ||||
|  | ||||
| def validate_homeassistant_event(value): | ||||
|     value = cv.string(value) | ||||
|     if not value.startswith(u'esphome.'): | ||||
|     if not value.startswith('esphome.'): | ||||
|         raise cv.Invalid("ESPHome can only generate Home Assistant events that begin with " | ||||
|                          "esphome. For example 'esphome.xyz'") | ||||
|     return value | ||||
|   | ||||
| @@ -216,6 +216,9 @@ message BinarySensorStateResponse { | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   bool state = 2; | ||||
|   // If the binary sensor does not have a valid state yet. | ||||
|   // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller | ||||
|   bool missing_state = 3; | ||||
| } | ||||
|  | ||||
| // ==================== COVER ==================== | ||||
| @@ -298,12 +301,17 @@ message ListEntitiesFanResponse { | ||||
|  | ||||
|   bool supports_oscillation = 5; | ||||
|   bool supports_speed = 6; | ||||
|   bool supports_direction = 7; | ||||
| } | ||||
| enum FanSpeed { | ||||
|   FAN_SPEED_LOW = 0; | ||||
|   FAN_SPEED_MEDIUM = 1; | ||||
|   FAN_SPEED_HIGH = 2; | ||||
| } | ||||
| enum FanDirection { | ||||
|   FAN_DIRECTION_FORWARD = 0; | ||||
|   FAN_DIRECTION_REVERSE = 1; | ||||
| } | ||||
| message FanStateResponse { | ||||
|   option (id) = 23; | ||||
|   option (source) = SOURCE_SERVER; | ||||
| @@ -314,6 +322,7 @@ message FanStateResponse { | ||||
|   bool state = 2; | ||||
|   bool oscillating = 3; | ||||
|   FanSpeed speed = 4; | ||||
|   FanDirection direction = 5; | ||||
| } | ||||
| message FanCommandRequest { | ||||
|   option (id) = 31; | ||||
| @@ -328,6 +337,8 @@ message FanCommandRequest { | ||||
|   FanSpeed speed = 5; | ||||
|   bool has_oscillating = 6; | ||||
|   bool oscillating = 7; | ||||
|   bool has_direction = 8; | ||||
|   FanDirection direction = 9; | ||||
| } | ||||
|  | ||||
| // ==================== LIGHT ==================== | ||||
| @@ -416,6 +427,9 @@ message SensorStateResponse { | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   float state = 2; | ||||
|   // If the sensor does not have a valid state yet. | ||||
|   // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller | ||||
|   bool missing_state = 3; | ||||
| } | ||||
|  | ||||
| // ==================== SWITCH ==================== | ||||
| @@ -472,6 +486,9 @@ message TextSensorStateResponse { | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   string state = 2; | ||||
|   // If the text sensor does not have a valid state yet. | ||||
|   // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller | ||||
|   bool missing_state = 3; | ||||
| } | ||||
|  | ||||
| // ==================== SUBSCRIBE LOGS ==================== | ||||
| @@ -644,12 +661,34 @@ enum ClimateMode { | ||||
|   CLIMATE_MODE_AUTO = 1; | ||||
|   CLIMATE_MODE_COOL = 2; | ||||
|   CLIMATE_MODE_HEAT = 3; | ||||
|   CLIMATE_MODE_FAN_ONLY = 4; | ||||
|   CLIMATE_MODE_DRY = 5; | ||||
| } | ||||
| enum ClimateFanMode { | ||||
|   CLIMATE_FAN_ON = 0; | ||||
|   CLIMATE_FAN_OFF = 1; | ||||
|   CLIMATE_FAN_AUTO = 2; | ||||
|   CLIMATE_FAN_LOW = 3; | ||||
|   CLIMATE_FAN_MEDIUM = 4; | ||||
|   CLIMATE_FAN_HIGH = 5; | ||||
|   CLIMATE_FAN_MIDDLE = 6; | ||||
|   CLIMATE_FAN_FOCUS = 7; | ||||
|   CLIMATE_FAN_DIFFUSE = 8; | ||||
| } | ||||
| enum ClimateSwingMode { | ||||
|   CLIMATE_SWING_OFF = 0; | ||||
|   CLIMATE_SWING_BOTH = 1; | ||||
|   CLIMATE_SWING_VERTICAL = 2; | ||||
|   CLIMATE_SWINT_HORIZONTAL = 3; | ||||
| } | ||||
| enum ClimateAction { | ||||
|   CLIMATE_ACTION_OFF = 0; | ||||
|   // values same as mode for readability | ||||
|   CLIMATE_ACTION_COOLING = 2; | ||||
|   CLIMATE_ACTION_HEATING = 3; | ||||
|   CLIMATE_ACTION_IDLE = 4; | ||||
|   CLIMATE_ACTION_DRYING = 5; | ||||
|   CLIMATE_ACTION_FAN = 6; | ||||
| } | ||||
| message ListEntitiesClimateResponse { | ||||
|   option (id) = 46; | ||||
| @@ -669,6 +708,8 @@ message ListEntitiesClimateResponse { | ||||
|   float visual_temperature_step = 10; | ||||
|   bool supports_away = 11; | ||||
|   bool supports_action = 12; | ||||
|   repeated ClimateFanMode supported_fan_modes = 13; | ||||
|   repeated ClimateSwingMode supported_swing_modes = 14; | ||||
| } | ||||
| message ClimateStateResponse { | ||||
|   option (id) = 47; | ||||
| @@ -684,6 +725,8 @@ message ClimateStateResponse { | ||||
|   float target_temperature_high = 6; | ||||
|   bool away = 7; | ||||
|   ClimateAction action = 8; | ||||
|   ClimateFanMode fan_mode = 9; | ||||
|   ClimateSwingMode swing_mode = 10; | ||||
| } | ||||
| message ClimateCommandRequest { | ||||
|   option (id) = 48; | ||||
| @@ -702,4 +745,8 @@ message ClimateCommandRequest { | ||||
|   float target_temperature_high = 9; | ||||
|   bool has_away = 10; | ||||
|   bool away = 11; | ||||
|   bool has_fan_mode = 12; | ||||
|   ClimateFanMode fan_mode = 13; | ||||
|   bool has_swing_mode = 14; | ||||
|   ClimateSwingMode swing_mode = 15; | ||||
| } | ||||
|   | ||||
| @@ -137,7 +137,6 @@ void APIConnection::loop() { | ||||
|       // bool done = 3; | ||||
|       bool done = this->image_reader_.available() == to_send; | ||||
|       buffer.encode_bool(3, done); | ||||
|       this->set_nodelay(false); | ||||
|       bool success = this->send_buffer(buffer, 44); | ||||
|  | ||||
|       if (success) { | ||||
| @@ -163,6 +162,7 @@ bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary | ||||
|   BinarySensorStateResponse resp; | ||||
|   resp.key = binary_sensor->get_object_id_hash(); | ||||
|   resp.state = state; | ||||
|   resp.missing_state = !binary_sensor->has_state(); | ||||
|   return this->send_binary_sensor_state_response(resp); | ||||
| } | ||||
| bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) { | ||||
| @@ -185,11 +185,12 @@ bool APIConnection::send_cover_state(cover::Cover *cover) { | ||||
|   auto traits = cover->get_traits(); | ||||
|   CoverStateResponse resp{}; | ||||
|   resp.key = cover->get_object_id_hash(); | ||||
|   resp.legacy_state = (cover->position == cover::COVER_OPEN) ? LEGACY_COVER_STATE_OPEN : LEGACY_COVER_STATE_CLOSED; | ||||
|   resp.legacy_state = | ||||
|       (cover->position == cover::COVER_OPEN) ? enums::LEGACY_COVER_STATE_OPEN : enums::LEGACY_COVER_STATE_CLOSED; | ||||
|   resp.position = cover->position; | ||||
|   if (traits.get_supports_tilt()) | ||||
|     resp.tilt = cover->tilt; | ||||
|   resp.current_operation = static_cast<EnumCoverOperation>(cover->current_operation); | ||||
|   resp.current_operation = static_cast<enums::CoverOperation>(cover->current_operation); | ||||
|   return this->send_cover_state_response(resp); | ||||
| } | ||||
| bool APIConnection::send_cover_info(cover::Cover *cover) { | ||||
| @@ -213,13 +214,13 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) { | ||||
|   auto call = cover->make_call(); | ||||
|   if (msg.has_legacy_command) { | ||||
|     switch (msg.legacy_command) { | ||||
|       case LEGACY_COVER_COMMAND_OPEN: | ||||
|       case enums::LEGACY_COVER_COMMAND_OPEN: | ||||
|         call.set_command_open(); | ||||
|         break; | ||||
|       case LEGACY_COVER_COMMAND_CLOSE: | ||||
|       case enums::LEGACY_COVER_COMMAND_CLOSE: | ||||
|         call.set_command_close(); | ||||
|         break; | ||||
|       case LEGACY_COVER_COMMAND_STOP: | ||||
|       case enums::LEGACY_COVER_COMMAND_STOP: | ||||
|         call.set_command_stop(); | ||||
|         break; | ||||
|     } | ||||
| @@ -246,7 +247,9 @@ bool APIConnection::send_fan_state(fan::FanState *fan) { | ||||
|   if (traits.supports_oscillation()) | ||||
|     resp.oscillating = fan->oscillating; | ||||
|   if (traits.supports_speed()) | ||||
|     resp.speed = static_cast<EnumFanSpeed>(fan->speed); | ||||
|     resp.speed = static_cast<enums::FanSpeed>(fan->speed); | ||||
|   if (traits.supports_direction()) | ||||
|     resp.direction = static_cast<enums::FanDirection>(fan->direction); | ||||
|   return this->send_fan_state_response(resp); | ||||
| } | ||||
| bool APIConnection::send_fan_info(fan::FanState *fan) { | ||||
| @@ -258,6 +261,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) { | ||||
|   msg.unique_id = get_default_unique_id("fan", fan); | ||||
|   msg.supports_oscillation = traits.supports_oscillation(); | ||||
|   msg.supports_speed = traits.supports_speed(); | ||||
|   msg.supports_direction = traits.supports_direction(); | ||||
|   return this->send_list_entities_fan_response(msg); | ||||
| } | ||||
| void APIConnection::fan_command(const FanCommandRequest &msg) { | ||||
| @@ -272,6 +276,8 @@ void APIConnection::fan_command(const FanCommandRequest &msg) { | ||||
|     call.set_oscillating(msg.oscillating); | ||||
|   if (msg.has_speed) | ||||
|     call.set_speed(static_cast<fan::FanSpeed>(msg.speed)); | ||||
|   if (msg.has_direction) | ||||
|     call.set_direction(static_cast<fan::FanDirection>(msg.direction)); | ||||
|   call.perform(); | ||||
| } | ||||
| #endif | ||||
| @@ -361,6 +367,7 @@ bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) { | ||||
|   SensorStateResponse resp{}; | ||||
|   resp.key = sensor->get_object_id_hash(); | ||||
|   resp.state = state; | ||||
|   resp.missing_state = !sensor->has_state(); | ||||
|   return this->send_sensor_state_response(resp); | ||||
| } | ||||
| bool APIConnection::send_sensor_info(sensor::Sensor *sensor) { | ||||
| @@ -374,6 +381,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) { | ||||
|   msg.icon = sensor->get_icon(); | ||||
|   msg.unit_of_measurement = sensor->get_unit_of_measurement(); | ||||
|   msg.accuracy_decimals = sensor->get_accuracy_decimals(); | ||||
|   msg.force_update = sensor->get_force_update(); | ||||
|   return this->send_list_entities_sensor_response(msg); | ||||
| } | ||||
| #endif | ||||
| @@ -418,6 +426,7 @@ bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, | ||||
|   TextSensorStateResponse resp{}; | ||||
|   resp.key = text_sensor->get_object_id_hash(); | ||||
|   resp.state = std::move(state); | ||||
|   resp.missing_state = !text_sensor->has_state(); | ||||
|   return this->send_text_sensor_state_response(resp); | ||||
| } | ||||
| bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) { | ||||
| @@ -441,8 +450,8 @@ bool APIConnection::send_climate_state(climate::Climate *climate) { | ||||
|   auto traits = climate->get_traits(); | ||||
|   ClimateStateResponse resp{}; | ||||
|   resp.key = climate->get_object_id_hash(); | ||||
|   resp.mode = static_cast<EnumClimateMode>(climate->mode); | ||||
|   resp.action = static_cast<EnumClimateAction>(climate->action); | ||||
|   resp.mode = static_cast<enums::ClimateMode>(climate->mode); | ||||
|   resp.action = static_cast<enums::ClimateAction>(climate->action); | ||||
|   if (traits.get_supports_current_temperature()) | ||||
|     resp.current_temperature = climate->current_temperature; | ||||
|   if (traits.get_supports_two_point_target_temperature()) { | ||||
| @@ -453,6 +462,10 @@ 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_swing_modes()) | ||||
|     resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode); | ||||
|   return this->send_climate_state_response(resp); | ||||
| } | ||||
| bool APIConnection::send_climate_info(climate::Climate *climate) { | ||||
| @@ -465,15 +478,26 @@ bool APIConnection::send_climate_info(climate::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_HEAT, climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY}) { | ||||
|     if (traits.supports_mode(mode)) | ||||
|       msg.supported_modes.push_back(static_cast<EnumClimateMode>(mode)); | ||||
|       msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode)); | ||||
|   } | ||||
|   msg.visual_min_temperature = traits.get_visual_min_temperature(); | ||||
|   msg.visual_max_temperature = traits.get_visual_max_temperature(); | ||||
|   msg.visual_temperature_step = traits.get_visual_temperature_step(); | ||||
|   msg.supports_away = traits.get_supports_away(); | ||||
|   msg.supports_action = traits.get_supports_action(); | ||||
|   for (auto fan_mode : {climate::CLIMATE_FAN_ON, climate::CLIMATE_FAN_OFF, climate::CLIMATE_FAN_AUTO, | ||||
|                         climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH, | ||||
|                         climate::CLIMATE_FAN_MIDDLE, climate::CLIMATE_FAN_FOCUS, climate::CLIMATE_FAN_DIFFUSE}) { | ||||
|     if (traits.supports_fan_mode(fan_mode)) | ||||
|       msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode)); | ||||
|   } | ||||
|   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)) | ||||
|       msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode)); | ||||
|   } | ||||
|   return this->send_list_entities_climate_response(msg); | ||||
| } | ||||
| void APIConnection::climate_command(const ClimateCommandRequest &msg) { | ||||
| @@ -492,6 +516,10 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) { | ||||
|     call.set_target_temperature_high(msg.target_temperature_high); | ||||
|   if (msg.has_away) | ||||
|     call.set_away(msg.away); | ||||
|   if (msg.has_fan_mode) | ||||
|     call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode)); | ||||
|   if (msg.has_swing_mode) | ||||
|     call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode)); | ||||
|   call.perform(); | ||||
| } | ||||
| #endif | ||||
| @@ -534,8 +562,6 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin | ||||
|   if (this->log_subscription_ < level) | ||||
|     return false; | ||||
|  | ||||
|   this->set_nodelay(false); | ||||
|  | ||||
|   // Send raw so that we don't copy too much | ||||
|   auto buffer = this->create_buffer(); | ||||
|   // LogLevel level = 1; | ||||
|   | ||||
| @@ -138,12 +138,6 @@ class APIConnection : public APIServerConnection { | ||||
|   void on_timeout_(uint32_t time); | ||||
|   void on_data_(uint8_t *buf, size_t len); | ||||
|   void parse_recv_buffer_(); | ||||
|   void set_nodelay(bool nodelay) override { | ||||
|     if (nodelay == this->current_nodelay_) | ||||
|       return; | ||||
|     this->client_->setNoDelay(nodelay); | ||||
|     this->current_nodelay_ = nodelay; | ||||
|   } | ||||
|  | ||||
|   enum class ConnectionState { | ||||
|     WAITING_FOR_HELLO, | ||||
|   | ||||
| @@ -1,119 +1,179 @@ | ||||
| // This file was automatically generated with a tool. | ||||
| // See scripts/api_protobuf/api_protobuf.py | ||||
| #include "api_pb2.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace api { | ||||
|  | ||||
| template<> const char *proto_enum_to_string<EnumLegacyCoverState>(EnumLegacyCoverState value) { | ||||
| template<> const char *proto_enum_to_string<enums::LegacyCoverState>(enums::LegacyCoverState value) { | ||||
|   switch (value) { | ||||
|     case LEGACY_COVER_STATE_OPEN: | ||||
|     case enums::LEGACY_COVER_STATE_OPEN: | ||||
|       return "LEGACY_COVER_STATE_OPEN"; | ||||
|     case LEGACY_COVER_STATE_CLOSED: | ||||
|     case enums::LEGACY_COVER_STATE_CLOSED: | ||||
|       return "LEGACY_COVER_STATE_CLOSED"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| template<> const char *proto_enum_to_string<EnumCoverOperation>(EnumCoverOperation value) { | ||||
| template<> const char *proto_enum_to_string<enums::CoverOperation>(enums::CoverOperation value) { | ||||
|   switch (value) { | ||||
|     case COVER_OPERATION_IDLE: | ||||
|     case enums::COVER_OPERATION_IDLE: | ||||
|       return "COVER_OPERATION_IDLE"; | ||||
|     case COVER_OPERATION_IS_OPENING: | ||||
|     case enums::COVER_OPERATION_IS_OPENING: | ||||
|       return "COVER_OPERATION_IS_OPENING"; | ||||
|     case COVER_OPERATION_IS_CLOSING: | ||||
|     case enums::COVER_OPERATION_IS_CLOSING: | ||||
|       return "COVER_OPERATION_IS_CLOSING"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| template<> const char *proto_enum_to_string<EnumLegacyCoverCommand>(EnumLegacyCoverCommand value) { | ||||
| template<> const char *proto_enum_to_string<enums::LegacyCoverCommand>(enums::LegacyCoverCommand value) { | ||||
|   switch (value) { | ||||
|     case LEGACY_COVER_COMMAND_OPEN: | ||||
|     case enums::LEGACY_COVER_COMMAND_OPEN: | ||||
|       return "LEGACY_COVER_COMMAND_OPEN"; | ||||
|     case LEGACY_COVER_COMMAND_CLOSE: | ||||
|     case enums::LEGACY_COVER_COMMAND_CLOSE: | ||||
|       return "LEGACY_COVER_COMMAND_CLOSE"; | ||||
|     case LEGACY_COVER_COMMAND_STOP: | ||||
|     case enums::LEGACY_COVER_COMMAND_STOP: | ||||
|       return "LEGACY_COVER_COMMAND_STOP"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| template<> const char *proto_enum_to_string<EnumFanSpeed>(EnumFanSpeed value) { | ||||
| template<> const char *proto_enum_to_string<enums::FanSpeed>(enums::FanSpeed value) { | ||||
|   switch (value) { | ||||
|     case FAN_SPEED_LOW: | ||||
|     case enums::FAN_SPEED_LOW: | ||||
|       return "FAN_SPEED_LOW"; | ||||
|     case FAN_SPEED_MEDIUM: | ||||
|     case enums::FAN_SPEED_MEDIUM: | ||||
|       return "FAN_SPEED_MEDIUM"; | ||||
|     case FAN_SPEED_HIGH: | ||||
|     case enums::FAN_SPEED_HIGH: | ||||
|       return "FAN_SPEED_HIGH"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| template<> const char *proto_enum_to_string<EnumLogLevel>(EnumLogLevel value) { | ||||
| template<> const char *proto_enum_to_string<enums::FanDirection>(enums::FanDirection value) { | ||||
|   switch (value) { | ||||
|     case LOG_LEVEL_NONE: | ||||
|     case enums::FAN_DIRECTION_FORWARD: | ||||
|       return "FAN_DIRECTION_FORWARD"; | ||||
|     case enums::FAN_DIRECTION_REVERSE: | ||||
|       return "FAN_DIRECTION_REVERSE"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) { | ||||
|   switch (value) { | ||||
|     case enums::LOG_LEVEL_NONE: | ||||
|       return "LOG_LEVEL_NONE"; | ||||
|     case LOG_LEVEL_ERROR: | ||||
|     case enums::LOG_LEVEL_ERROR: | ||||
|       return "LOG_LEVEL_ERROR"; | ||||
|     case LOG_LEVEL_WARN: | ||||
|     case enums::LOG_LEVEL_WARN: | ||||
|       return "LOG_LEVEL_WARN"; | ||||
|     case LOG_LEVEL_INFO: | ||||
|     case enums::LOG_LEVEL_INFO: | ||||
|       return "LOG_LEVEL_INFO"; | ||||
|     case LOG_LEVEL_DEBUG: | ||||
|     case enums::LOG_LEVEL_DEBUG: | ||||
|       return "LOG_LEVEL_DEBUG"; | ||||
|     case LOG_LEVEL_VERBOSE: | ||||
|     case enums::LOG_LEVEL_VERBOSE: | ||||
|       return "LOG_LEVEL_VERBOSE"; | ||||
|     case LOG_LEVEL_VERY_VERBOSE: | ||||
|     case enums::LOG_LEVEL_VERY_VERBOSE: | ||||
|       return "LOG_LEVEL_VERY_VERBOSE"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| template<> const char *proto_enum_to_string<EnumServiceArgType>(EnumServiceArgType value) { | ||||
| template<> const char *proto_enum_to_string<enums::ServiceArgType>(enums::ServiceArgType value) { | ||||
|   switch (value) { | ||||
|     case SERVICE_ARG_TYPE_BOOL: | ||||
|     case enums::SERVICE_ARG_TYPE_BOOL: | ||||
|       return "SERVICE_ARG_TYPE_BOOL"; | ||||
|     case SERVICE_ARG_TYPE_INT: | ||||
|     case enums::SERVICE_ARG_TYPE_INT: | ||||
|       return "SERVICE_ARG_TYPE_INT"; | ||||
|     case SERVICE_ARG_TYPE_FLOAT: | ||||
|     case enums::SERVICE_ARG_TYPE_FLOAT: | ||||
|       return "SERVICE_ARG_TYPE_FLOAT"; | ||||
|     case SERVICE_ARG_TYPE_STRING: | ||||
|     case enums::SERVICE_ARG_TYPE_STRING: | ||||
|       return "SERVICE_ARG_TYPE_STRING"; | ||||
|     case SERVICE_ARG_TYPE_BOOL_ARRAY: | ||||
|     case enums::SERVICE_ARG_TYPE_BOOL_ARRAY: | ||||
|       return "SERVICE_ARG_TYPE_BOOL_ARRAY"; | ||||
|     case SERVICE_ARG_TYPE_INT_ARRAY: | ||||
|     case enums::SERVICE_ARG_TYPE_INT_ARRAY: | ||||
|       return "SERVICE_ARG_TYPE_INT_ARRAY"; | ||||
|     case SERVICE_ARG_TYPE_FLOAT_ARRAY: | ||||
|     case enums::SERVICE_ARG_TYPE_FLOAT_ARRAY: | ||||
|       return "SERVICE_ARG_TYPE_FLOAT_ARRAY"; | ||||
|     case SERVICE_ARG_TYPE_STRING_ARRAY: | ||||
|     case enums::SERVICE_ARG_TYPE_STRING_ARRAY: | ||||
|       return "SERVICE_ARG_TYPE_STRING_ARRAY"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| template<> const char *proto_enum_to_string<EnumClimateMode>(EnumClimateMode value) { | ||||
| template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMode value) { | ||||
|   switch (value) { | ||||
|     case CLIMATE_MODE_OFF: | ||||
|     case enums::CLIMATE_MODE_OFF: | ||||
|       return "CLIMATE_MODE_OFF"; | ||||
|     case CLIMATE_MODE_AUTO: | ||||
|     case enums::CLIMATE_MODE_AUTO: | ||||
|       return "CLIMATE_MODE_AUTO"; | ||||
|     case CLIMATE_MODE_COOL: | ||||
|     case enums::CLIMATE_MODE_COOL: | ||||
|       return "CLIMATE_MODE_COOL"; | ||||
|     case CLIMATE_MODE_HEAT: | ||||
|     case enums::CLIMATE_MODE_HEAT: | ||||
|       return "CLIMATE_MODE_HEAT"; | ||||
|     case enums::CLIMATE_MODE_FAN_ONLY: | ||||
|       return "CLIMATE_MODE_FAN_ONLY"; | ||||
|     case enums::CLIMATE_MODE_DRY: | ||||
|       return "CLIMATE_MODE_DRY"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| template<> const char *proto_enum_to_string<EnumClimateAction>(EnumClimateAction value) { | ||||
| template<> const char *proto_enum_to_string<enums::ClimateFanMode>(enums::ClimateFanMode value) { | ||||
|   switch (value) { | ||||
|     case CLIMATE_ACTION_OFF: | ||||
|     case enums::CLIMATE_FAN_ON: | ||||
|       return "CLIMATE_FAN_ON"; | ||||
|     case enums::CLIMATE_FAN_OFF: | ||||
|       return "CLIMATE_FAN_OFF"; | ||||
|     case enums::CLIMATE_FAN_AUTO: | ||||
|       return "CLIMATE_FAN_AUTO"; | ||||
|     case enums::CLIMATE_FAN_LOW: | ||||
|       return "CLIMATE_FAN_LOW"; | ||||
|     case enums::CLIMATE_FAN_MEDIUM: | ||||
|       return "CLIMATE_FAN_MEDIUM"; | ||||
|     case enums::CLIMATE_FAN_HIGH: | ||||
|       return "CLIMATE_FAN_HIGH"; | ||||
|     case enums::CLIMATE_FAN_MIDDLE: | ||||
|       return "CLIMATE_FAN_MIDDLE"; | ||||
|     case enums::CLIMATE_FAN_FOCUS: | ||||
|       return "CLIMATE_FAN_FOCUS"; | ||||
|     case enums::CLIMATE_FAN_DIFFUSE: | ||||
|       return "CLIMATE_FAN_DIFFUSE"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| template<> const char *proto_enum_to_string<enums::ClimateSwingMode>(enums::ClimateSwingMode value) { | ||||
|   switch (value) { | ||||
|     case enums::CLIMATE_SWING_OFF: | ||||
|       return "CLIMATE_SWING_OFF"; | ||||
|     case enums::CLIMATE_SWING_BOTH: | ||||
|       return "CLIMATE_SWING_BOTH"; | ||||
|     case enums::CLIMATE_SWING_VERTICAL: | ||||
|       return "CLIMATE_SWING_VERTICAL"; | ||||
|     case enums::CLIMATE_SWINT_HORIZONTAL: | ||||
|       return "CLIMATE_SWINT_HORIZONTAL"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| template<> const char *proto_enum_to_string<enums::ClimateAction>(enums::ClimateAction value) { | ||||
|   switch (value) { | ||||
|     case enums::CLIMATE_ACTION_OFF: | ||||
|       return "CLIMATE_ACTION_OFF"; | ||||
|     case CLIMATE_ACTION_COOLING: | ||||
|     case enums::CLIMATE_ACTION_COOLING: | ||||
|       return "CLIMATE_ACTION_COOLING"; | ||||
|     case CLIMATE_ACTION_HEATING: | ||||
|     case enums::CLIMATE_ACTION_HEATING: | ||||
|       return "CLIMATE_ACTION_HEATING"; | ||||
|     case enums::CLIMATE_ACTION_IDLE: | ||||
|       return "CLIMATE_ACTION_IDLE"; | ||||
|     case enums::CLIMATE_ACTION_DRYING: | ||||
|       return "CLIMATE_ACTION_DRYING"; | ||||
|     case enums::CLIMATE_ACTION_FAN: | ||||
|       return "CLIMATE_ACTION_FAN"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| @@ -404,6 +464,10 @@ bool BinarySensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt val | ||||
|       this->state = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 3: { | ||||
|       this->missing_state = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -421,6 +485,7 @@ bool BinarySensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value | ||||
| void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_bool(2, this->state); | ||||
|   buffer.encode_bool(3, this->missing_state); | ||||
| } | ||||
| void BinarySensorStateResponse::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
| @@ -433,6 +498,10 @@ void BinarySensorStateResponse::dump_to(std::string &out) const { | ||||
|   out.append("  state: "); | ||||
|   out.append(YESNO(this->state)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  missing_state: "); | ||||
|   out.append(YESNO(this->missing_state)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
| @@ -535,11 +604,11 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const { | ||||
| bool CoverStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->legacy_state = value.as_enum<EnumLegacyCoverState>(); | ||||
|       this->legacy_state = value.as_enum<enums::LegacyCoverState>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 5: { | ||||
|       this->current_operation = value.as_enum<EnumCoverOperation>(); | ||||
|       this->current_operation = value.as_enum<enums::CoverOperation>(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
| @@ -566,10 +635,10 @@ bool CoverStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
| } | ||||
| void CoverStateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_enum<EnumLegacyCoverState>(2, this->legacy_state); | ||||
|   buffer.encode_enum<enums::LegacyCoverState>(2, this->legacy_state); | ||||
|   buffer.encode_float(3, this->position); | ||||
|   buffer.encode_float(4, this->tilt); | ||||
|   buffer.encode_enum<EnumCoverOperation>(5, this->current_operation); | ||||
|   buffer.encode_enum<enums::CoverOperation>(5, this->current_operation); | ||||
| } | ||||
| void CoverStateResponse::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
| @@ -580,7 +649,7 @@ void CoverStateResponse::dump_to(std::string &out) const { | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  legacy_state: "); | ||||
|   out.append(proto_enum_to_string<EnumLegacyCoverState>(this->legacy_state)); | ||||
|   out.append(proto_enum_to_string<enums::LegacyCoverState>(this->legacy_state)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  position: "); | ||||
| @@ -594,7 +663,7 @@ void CoverStateResponse::dump_to(std::string &out) const { | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  current_operation: "); | ||||
|   out.append(proto_enum_to_string<EnumCoverOperation>(this->current_operation)); | ||||
|   out.append(proto_enum_to_string<enums::CoverOperation>(this->current_operation)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| @@ -605,7 +674,7 @@ bool CoverCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|       return true; | ||||
|     } | ||||
|     case 3: { | ||||
|       this->legacy_command = value.as_enum<EnumLegacyCoverCommand>(); | ||||
|       this->legacy_command = value.as_enum<enums::LegacyCoverCommand>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 4: { | ||||
| @@ -645,7 +714,7 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
| void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_bool(2, this->has_legacy_command); | ||||
|   buffer.encode_enum<EnumLegacyCoverCommand>(3, this->legacy_command); | ||||
|   buffer.encode_enum<enums::LegacyCoverCommand>(3, this->legacy_command); | ||||
|   buffer.encode_bool(4, this->has_position); | ||||
|   buffer.encode_float(5, this->position); | ||||
|   buffer.encode_bool(6, this->has_tilt); | ||||
| @@ -665,7 +734,7 @@ void CoverCommandRequest::dump_to(std::string &out) const { | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  legacy_command: "); | ||||
|   out.append(proto_enum_to_string<EnumLegacyCoverCommand>(this->legacy_command)); | ||||
|   out.append(proto_enum_to_string<enums::LegacyCoverCommand>(this->legacy_command)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  has_position: "); | ||||
| @@ -701,6 +770,10 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value | ||||
|       this->supports_speed = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 7: { | ||||
|       this->supports_direction = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -740,6 +813,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_string(4, this->unique_id); | ||||
|   buffer.encode_bool(5, this->supports_oscillation); | ||||
|   buffer.encode_bool(6, this->supports_speed); | ||||
|   buffer.encode_bool(7, this->supports_direction); | ||||
| } | ||||
| void ListEntitiesFanResponse::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
| @@ -768,6 +842,10 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const { | ||||
|   out.append("  supports_speed: "); | ||||
|   out.append(YESNO(this->supports_speed)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  supports_direction: "); | ||||
|   out.append(YESNO(this->supports_direction)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
| @@ -781,7 +859,11 @@ bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|       return true; | ||||
|     } | ||||
|     case 4: { | ||||
|       this->speed = value.as_enum<EnumFanSpeed>(); | ||||
|       this->speed = value.as_enum<enums::FanSpeed>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 5: { | ||||
|       this->direction = value.as_enum<enums::FanDirection>(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
| @@ -802,7 +884,8 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_bool(2, this->state); | ||||
|   buffer.encode_bool(3, this->oscillating); | ||||
|   buffer.encode_enum<EnumFanSpeed>(4, this->speed); | ||||
|   buffer.encode_enum<enums::FanSpeed>(4, this->speed); | ||||
|   buffer.encode_enum<enums::FanDirection>(5, this->direction); | ||||
| } | ||||
| void FanStateResponse::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
| @@ -821,7 +904,11 @@ void FanStateResponse::dump_to(std::string &out) const { | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  speed: "); | ||||
|   out.append(proto_enum_to_string<EnumFanSpeed>(this->speed)); | ||||
|   out.append(proto_enum_to_string<enums::FanSpeed>(this->speed)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  direction: "); | ||||
|   out.append(proto_enum_to_string<enums::FanDirection>(this->direction)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| @@ -840,7 +927,7 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|       return true; | ||||
|     } | ||||
|     case 5: { | ||||
|       this->speed = value.as_enum<EnumFanSpeed>(); | ||||
|       this->speed = value.as_enum<enums::FanSpeed>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 6: { | ||||
| @@ -851,6 +938,14 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|       this->oscillating = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 8: { | ||||
|       this->has_direction = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->direction = value.as_enum<enums::FanDirection>(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -870,9 +965,11 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(2, this->has_state); | ||||
|   buffer.encode_bool(3, this->state); | ||||
|   buffer.encode_bool(4, this->has_speed); | ||||
|   buffer.encode_enum<EnumFanSpeed>(5, this->speed); | ||||
|   buffer.encode_enum<enums::FanSpeed>(5, this->speed); | ||||
|   buffer.encode_bool(6, this->has_oscillating); | ||||
|   buffer.encode_bool(7, this->oscillating); | ||||
|   buffer.encode_bool(8, this->has_direction); | ||||
|   buffer.encode_enum<enums::FanDirection>(9, this->direction); | ||||
| } | ||||
| void FanCommandRequest::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
| @@ -895,7 +992,7 @@ void FanCommandRequest::dump_to(std::string &out) const { | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  speed: "); | ||||
|   out.append(proto_enum_to_string<EnumFanSpeed>(this->speed)); | ||||
|   out.append(proto_enum_to_string<enums::FanSpeed>(this->speed)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  has_oscillating: "); | ||||
| @@ -905,6 +1002,14 @@ void FanCommandRequest::dump_to(std::string &out) const { | ||||
|   out.append("  oscillating: "); | ||||
|   out.append(YESNO(this->oscillating)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  has_direction: "); | ||||
|   out.append(YESNO(this->has_direction)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  direction: "); | ||||
|   out.append(proto_enum_to_string<enums::FanDirection>(this->direction)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
| @@ -1451,6 +1556,16 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const { | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 3: { | ||||
|       this->missing_state = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
| @@ -1468,6 +1583,7 @@ bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
| void SensorStateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_float(2, this->state); | ||||
|   buffer.encode_bool(3, this->missing_state); | ||||
| } | ||||
| void SensorStateResponse::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
| @@ -1481,6 +1597,10 @@ void SensorStateResponse::dump_to(std::string &out) const { | ||||
|   sprintf(buffer, "%g", this->state); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  missing_state: "); | ||||
|   out.append(YESNO(this->missing_state)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
| @@ -1700,6 +1820,16 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const { | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| bool TextSensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 3: { | ||||
|       this->missing_state = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool TextSensorStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
| @@ -1723,6 +1853,7 @@ bool TextSensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) | ||||
| void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_string(2, this->state); | ||||
|   buffer.encode_bool(3, this->missing_state); | ||||
| } | ||||
| void TextSensorStateResponse::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
| @@ -1735,12 +1866,16 @@ void TextSensorStateResponse::dump_to(std::string &out) const { | ||||
|   out.append("  state: "); | ||||
|   out.append("'").append(this->state).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  missing_state: "); | ||||
|   out.append(YESNO(this->missing_state)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->level = value.as_enum<EnumLogLevel>(); | ||||
|       this->level = value.as_enum<enums::LogLevel>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 2: { | ||||
| @@ -1752,14 +1887,14 @@ bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   } | ||||
| } | ||||
| void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_enum<EnumLogLevel>(1, this->level); | ||||
|   buffer.encode_enum<enums::LogLevel>(1, this->level); | ||||
|   buffer.encode_bool(2, this->dump_config); | ||||
| } | ||||
| void SubscribeLogsRequest::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
|   out.append("SubscribeLogsRequest {\n"); | ||||
|   out.append("  level: "); | ||||
|   out.append(proto_enum_to_string<EnumLogLevel>(this->level)); | ||||
|   out.append(proto_enum_to_string<enums::LogLevel>(this->level)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  dump_config: "); | ||||
| @@ -1770,7 +1905,7 @@ void SubscribeLogsRequest::dump_to(std::string &out) const { | ||||
| bool SubscribeLogsResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->level = value.as_enum<EnumLogLevel>(); | ||||
|       this->level = value.as_enum<enums::LogLevel>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 4: { | ||||
| @@ -1796,7 +1931,7 @@ bool SubscribeLogsResponse::decode_length(uint32_t field_id, ProtoLengthDelimite | ||||
|   } | ||||
| } | ||||
| void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_enum<EnumLogLevel>(1, this->level); | ||||
|   buffer.encode_enum<enums::LogLevel>(1, this->level); | ||||
|   buffer.encode_string(2, this->tag); | ||||
|   buffer.encode_string(3, this->message); | ||||
|   buffer.encode_bool(4, this->send_failed); | ||||
| @@ -1805,7 +1940,7 @@ void SubscribeLogsResponse::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
|   out.append("SubscribeLogsResponse {\n"); | ||||
|   out.append("  level: "); | ||||
|   out.append(proto_enum_to_string<EnumLogLevel>(this->level)); | ||||
|   out.append(proto_enum_to_string<enums::LogLevel>(this->level)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  tag: "); | ||||
| @@ -2010,7 +2145,7 @@ void GetTimeResponse::dump_to(std::string &out) const { | ||||
| bool ListEntitiesServicesArgument::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->type = value.as_enum<EnumServiceArgType>(); | ||||
|       this->type = value.as_enum<enums::ServiceArgType>(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
| @@ -2029,7 +2164,7 @@ bool ListEntitiesServicesArgument::decode_length(uint32_t field_id, ProtoLengthD | ||||
| } | ||||
| void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_string(1, this->name); | ||||
|   buffer.encode_enum<EnumServiceArgType>(2, this->type); | ||||
|   buffer.encode_enum<enums::ServiceArgType>(2, this->type); | ||||
| } | ||||
| void ListEntitiesServicesArgument::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
| @@ -2039,7 +2174,7 @@ void ListEntitiesServicesArgument::dump_to(std::string &out) const { | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  type: "); | ||||
|   out.append(proto_enum_to_string<EnumServiceArgType>(this->type)); | ||||
|   out.append(proto_enum_to_string<enums::ServiceArgType>(this->type)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| @@ -2408,7 +2543,7 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v | ||||
|       return true; | ||||
|     } | ||||
|     case 7: { | ||||
|       this->supported_modes.push_back(value.as_enum<EnumClimateMode>()); | ||||
|       this->supported_modes.push_back(value.as_enum<enums::ClimateMode>()); | ||||
|       return true; | ||||
|     } | ||||
|     case 11: { | ||||
| @@ -2419,6 +2554,14 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v | ||||
|       this->supports_action = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 13: { | ||||
|       this->supported_fan_modes.push_back(value.as_enum<enums::ClimateFanMode>()); | ||||
|       return true; | ||||
|     } | ||||
|     case 14: { | ||||
|       this->supported_swing_modes.push_back(value.as_enum<enums::ClimateSwingMode>()); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -2471,13 +2614,19 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(5, this->supports_current_temperature); | ||||
|   buffer.encode_bool(6, this->supports_two_point_target_temperature); | ||||
|   for (auto &it : this->supported_modes) { | ||||
|     buffer.encode_enum<EnumClimateMode>(7, it, true); | ||||
|     buffer.encode_enum<enums::ClimateMode>(7, it, true); | ||||
|   } | ||||
|   buffer.encode_float(8, this->visual_min_temperature); | ||||
|   buffer.encode_float(9, this->visual_max_temperature); | ||||
|   buffer.encode_float(10, this->visual_temperature_step); | ||||
|   buffer.encode_bool(11, this->supports_away); | ||||
|   buffer.encode_bool(12, this->supports_action); | ||||
|   for (auto &it : this->supported_fan_modes) { | ||||
|     buffer.encode_enum<enums::ClimateFanMode>(13, it, true); | ||||
|   } | ||||
|   for (auto &it : this->supported_swing_modes) { | ||||
|     buffer.encode_enum<enums::ClimateSwingMode>(14, it, true); | ||||
|   } | ||||
| } | ||||
| void ListEntitiesClimateResponse::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
| @@ -2509,7 +2658,7 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const { | ||||
|  | ||||
|   for (const auto &it : this->supported_modes) { | ||||
|     out.append("  supported_modes: "); | ||||
|     out.append(proto_enum_to_string<EnumClimateMode>(it)); | ||||
|     out.append(proto_enum_to_string<enums::ClimateMode>(it)); | ||||
|     out.append("\n"); | ||||
|   } | ||||
|  | ||||
| @@ -2535,12 +2684,24 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const { | ||||
|   out.append("  supports_action: "); | ||||
|   out.append(YESNO(this->supports_action)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   for (const auto &it : this->supported_fan_modes) { | ||||
|     out.append("  supported_fan_modes: "); | ||||
|     out.append(proto_enum_to_string<enums::ClimateFanMode>(it)); | ||||
|     out.append("\n"); | ||||
|   } | ||||
|  | ||||
|   for (const auto &it : this->supported_swing_modes) { | ||||
|     out.append("  supported_swing_modes: "); | ||||
|     out.append(proto_enum_to_string<enums::ClimateSwingMode>(it)); | ||||
|     out.append("\n"); | ||||
|   } | ||||
|   out.append("}"); | ||||
| } | ||||
| bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->mode = value.as_enum<EnumClimateMode>(); | ||||
|       this->mode = value.as_enum<enums::ClimateMode>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 7: { | ||||
| @@ -2548,7 +2709,15 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|       return true; | ||||
|     } | ||||
|     case 8: { | ||||
|       this->action = value.as_enum<EnumClimateAction>(); | ||||
|       this->action = value.as_enum<enums::ClimateAction>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->fan_mode = value.as_enum<enums::ClimateFanMode>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 10: { | ||||
|       this->swing_mode = value.as_enum<enums::ClimateSwingMode>(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
| @@ -2583,13 +2752,15 @@ bool ClimateStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
| } | ||||
| void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_enum<EnumClimateMode>(2, this->mode); | ||||
|   buffer.encode_enum<enums::ClimateMode>(2, this->mode); | ||||
|   buffer.encode_float(3, this->current_temperature); | ||||
|   buffer.encode_float(4, this->target_temperature); | ||||
|   buffer.encode_float(5, this->target_temperature_low); | ||||
|   buffer.encode_float(6, this->target_temperature_high); | ||||
|   buffer.encode_bool(7, this->away); | ||||
|   buffer.encode_enum<EnumClimateAction>(8, this->action); | ||||
|   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); | ||||
| } | ||||
| void ClimateStateResponse::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
| @@ -2600,7 +2771,7 @@ void ClimateStateResponse::dump_to(std::string &out) const { | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  mode: "); | ||||
|   out.append(proto_enum_to_string<EnumClimateMode>(this->mode)); | ||||
|   out.append(proto_enum_to_string<enums::ClimateMode>(this->mode)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  current_temperature: "); | ||||
| @@ -2628,7 +2799,15 @@ void ClimateStateResponse::dump_to(std::string &out) const { | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  action: "); | ||||
|   out.append(proto_enum_to_string<EnumClimateAction>(this->action)); | ||||
|   out.append(proto_enum_to_string<enums::ClimateAction>(this->action)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  fan_mode: "); | ||||
|   out.append(proto_enum_to_string<enums::ClimateFanMode>(this->fan_mode)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  swing_mode: "); | ||||
|   out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| @@ -2639,7 +2818,7 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) | ||||
|       return true; | ||||
|     } | ||||
|     case 3: { | ||||
|       this->mode = value.as_enum<EnumClimateMode>(); | ||||
|       this->mode = value.as_enum<enums::ClimateMode>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 4: { | ||||
| @@ -2662,6 +2841,22 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) | ||||
|       this->away = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 12: { | ||||
|       this->has_fan_mode = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 13: { | ||||
|       this->fan_mode = value.as_enum<enums::ClimateFanMode>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 14: { | ||||
|       this->has_swing_mode = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 15: { | ||||
|       this->swing_mode = value.as_enum<enums::ClimateSwingMode>(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -2691,7 +2886,7 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
| void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_bool(2, this->has_mode); | ||||
|   buffer.encode_enum<EnumClimateMode>(3, this->mode); | ||||
|   buffer.encode_enum<enums::ClimateMode>(3, this->mode); | ||||
|   buffer.encode_bool(4, this->has_target_temperature); | ||||
|   buffer.encode_float(5, this->target_temperature); | ||||
|   buffer.encode_bool(6, this->has_target_temperature_low); | ||||
| @@ -2700,6 +2895,10 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_float(9, this->target_temperature_high); | ||||
|   buffer.encode_bool(10, this->has_away); | ||||
|   buffer.encode_bool(11, this->away); | ||||
|   buffer.encode_bool(12, this->has_fan_mode); | ||||
|   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); | ||||
| } | ||||
| void ClimateCommandRequest::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
| @@ -2714,7 +2913,7 @@ void ClimateCommandRequest::dump_to(std::string &out) const { | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  mode: "); | ||||
|   out.append(proto_enum_to_string<EnumClimateMode>(this->mode)); | ||||
|   out.append(proto_enum_to_string<enums::ClimateMode>(this->mode)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  has_target_temperature: "); | ||||
| @@ -2751,6 +2950,22 @@ void ClimateCommandRequest::dump_to(std::string &out) const { | ||||
|   out.append("  away: "); | ||||
|   out.append(YESNO(this->away)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  has_fan_mode: "); | ||||
|   out.append(YESNO(this->has_fan_mode)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  fan_mode: "); | ||||
|   out.append(proto_enum_to_string<enums::ClimateFanMode>(this->fan_mode)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  has_swing_mode: "); | ||||
|   out.append(YESNO(this->has_swing_mode)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  swing_mode: "); | ||||
|   out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| // This file was automatically generated with a tool. | ||||
| // See scripts/api_protobuf/api_protobuf.py | ||||
| #pragma once | ||||
|  | ||||
| #include "proto.h" | ||||
| @@ -5,26 +7,32 @@ | ||||
| namespace esphome { | ||||
| namespace api { | ||||
|  | ||||
| enum EnumLegacyCoverState : uint32_t { | ||||
| namespace enums { | ||||
|  | ||||
| enum LegacyCoverState : uint32_t { | ||||
|   LEGACY_COVER_STATE_OPEN = 0, | ||||
|   LEGACY_COVER_STATE_CLOSED = 1, | ||||
| }; | ||||
| enum EnumCoverOperation : uint32_t { | ||||
| enum CoverOperation : uint32_t { | ||||
|   COVER_OPERATION_IDLE = 0, | ||||
|   COVER_OPERATION_IS_OPENING = 1, | ||||
|   COVER_OPERATION_IS_CLOSING = 2, | ||||
| }; | ||||
| enum EnumLegacyCoverCommand : uint32_t { | ||||
| enum LegacyCoverCommand : uint32_t { | ||||
|   LEGACY_COVER_COMMAND_OPEN = 0, | ||||
|   LEGACY_COVER_COMMAND_CLOSE = 1, | ||||
|   LEGACY_COVER_COMMAND_STOP = 2, | ||||
| }; | ||||
| enum EnumFanSpeed : uint32_t { | ||||
| enum FanSpeed : uint32_t { | ||||
|   FAN_SPEED_LOW = 0, | ||||
|   FAN_SPEED_MEDIUM = 1, | ||||
|   FAN_SPEED_HIGH = 2, | ||||
| }; | ||||
| enum EnumLogLevel : uint32_t { | ||||
| enum FanDirection : uint32_t { | ||||
|   FAN_DIRECTION_FORWARD = 0, | ||||
|   FAN_DIRECTION_REVERSE = 1, | ||||
| }; | ||||
| enum LogLevel : uint32_t { | ||||
|   LOG_LEVEL_NONE = 0, | ||||
|   LOG_LEVEL_ERROR = 1, | ||||
|   LOG_LEVEL_WARN = 2, | ||||
| @@ -33,7 +41,7 @@ enum EnumLogLevel : uint32_t { | ||||
|   LOG_LEVEL_VERBOSE = 5, | ||||
|   LOG_LEVEL_VERY_VERBOSE = 6, | ||||
| }; | ||||
| enum EnumServiceArgType : uint32_t { | ||||
| enum ServiceArgType : uint32_t { | ||||
|   SERVICE_ARG_TYPE_BOOL = 0, | ||||
|   SERVICE_ARG_TYPE_INT = 1, | ||||
|   SERVICE_ARG_TYPE_FLOAT = 2, | ||||
| @@ -43,17 +51,42 @@ enum EnumServiceArgType : uint32_t { | ||||
|   SERVICE_ARG_TYPE_FLOAT_ARRAY = 6, | ||||
|   SERVICE_ARG_TYPE_STRING_ARRAY = 7, | ||||
| }; | ||||
| enum EnumClimateMode : uint32_t { | ||||
| enum ClimateMode : uint32_t { | ||||
|   CLIMATE_MODE_OFF = 0, | ||||
|   CLIMATE_MODE_AUTO = 1, | ||||
|   CLIMATE_MODE_COOL = 2, | ||||
|   CLIMATE_MODE_HEAT = 3, | ||||
|   CLIMATE_MODE_FAN_ONLY = 4, | ||||
|   CLIMATE_MODE_DRY = 5, | ||||
| }; | ||||
| enum EnumClimateAction : uint32_t { | ||||
| enum ClimateFanMode : uint32_t { | ||||
|   CLIMATE_FAN_ON = 0, | ||||
|   CLIMATE_FAN_OFF = 1, | ||||
|   CLIMATE_FAN_AUTO = 2, | ||||
|   CLIMATE_FAN_LOW = 3, | ||||
|   CLIMATE_FAN_MEDIUM = 4, | ||||
|   CLIMATE_FAN_HIGH = 5, | ||||
|   CLIMATE_FAN_MIDDLE = 6, | ||||
|   CLIMATE_FAN_FOCUS = 7, | ||||
|   CLIMATE_FAN_DIFFUSE = 8, | ||||
| }; | ||||
| enum ClimateSwingMode : uint32_t { | ||||
|   CLIMATE_SWING_OFF = 0, | ||||
|   CLIMATE_SWING_BOTH = 1, | ||||
|   CLIMATE_SWING_VERTICAL = 2, | ||||
|   CLIMATE_SWINT_HORIZONTAL = 3, | ||||
| }; | ||||
| enum ClimateAction : uint32_t { | ||||
|   CLIMATE_ACTION_OFF = 0, | ||||
|   CLIMATE_ACTION_COOLING = 2, | ||||
|   CLIMATE_ACTION_HEATING = 3, | ||||
|   CLIMATE_ACTION_IDLE = 4, | ||||
|   CLIMATE_ACTION_DRYING = 5, | ||||
|   CLIMATE_ACTION_FAN = 6, | ||||
| }; | ||||
|  | ||||
| }  // namespace enums | ||||
|  | ||||
| class HelloRequest : public ProtoMessage { | ||||
|  public: | ||||
|   std::string client_info{};  // NOLINT | ||||
| @@ -183,8 +216,9 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage { | ||||
| }; | ||||
| class BinarySensorStateResponse : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0};    // NOLINT | ||||
|   bool state{false};  // NOLINT | ||||
|   uint32_t key{0};            // NOLINT | ||||
|   bool state{false};          // NOLINT | ||||
|   bool missing_state{false};  // NOLINT | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void dump_to(std::string &out) const override; | ||||
|  | ||||
| @@ -212,11 +246,11 @@ class ListEntitiesCoverResponse : public ProtoMessage { | ||||
| }; | ||||
| class CoverStateResponse : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0};                         // NOLINT | ||||
|   EnumLegacyCoverState legacy_state{};     // NOLINT | ||||
|   float position{0.0f};                    // NOLINT | ||||
|   float tilt{0.0f};                        // NOLINT | ||||
|   EnumCoverOperation current_operation{};  // NOLINT | ||||
|   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 | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void dump_to(std::string &out) const override; | ||||
|  | ||||
| @@ -226,14 +260,14 @@ class CoverStateResponse : public ProtoMessage { | ||||
| }; | ||||
| class CoverCommandRequest : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0};                          // NOLINT | ||||
|   bool has_legacy_command{false};           // NOLINT | ||||
|   EnumLegacyCoverCommand 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};                             // 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 | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void dump_to(std::string &out) const override; | ||||
|  | ||||
| @@ -249,6 +283,7 @@ class ListEntitiesFanResponse : public ProtoMessage { | ||||
|   std::string unique_id{};           // NOLINT | ||||
|   bool supports_oscillation{false};  // NOLINT | ||||
|   bool supports_speed{false};        // NOLINT | ||||
|   bool supports_direction{false};    // NOLINT | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void dump_to(std::string &out) const override; | ||||
|  | ||||
| @@ -259,10 +294,11 @@ class ListEntitiesFanResponse : public ProtoMessage { | ||||
| }; | ||||
| class FanStateResponse : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0};          // NOLINT | ||||
|   bool state{false};        // NOLINT | ||||
|   bool oscillating{false};  // NOLINT | ||||
|   EnumFanSpeed speed{};     // NOLINT | ||||
|   uint32_t key{0};                  // NOLINT | ||||
|   bool state{false};                // NOLINT | ||||
|   bool oscillating{false};          // NOLINT | ||||
|   enums::FanSpeed speed{};          // NOLINT | ||||
|   enums::FanDirection direction{};  // NOLINT | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void dump_to(std::string &out) const override; | ||||
|  | ||||
| @@ -272,13 +308,15 @@ 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 | ||||
|   EnumFanSpeed speed{};         // NOLINT | ||||
|   bool has_oscillating{false};  // NOLINT | ||||
|   bool oscillating{false};      // NOLINT | ||||
|   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 | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void dump_to(std::string &out) const override; | ||||
|  | ||||
| @@ -375,13 +413,15 @@ class ListEntitiesSensorResponse : public ProtoMessage { | ||||
| }; | ||||
| class SensorStateResponse : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0};    // NOLINT | ||||
|   float state{0.0f};  // NOLINT | ||||
|   uint32_t key{0};            // NOLINT | ||||
|   float state{0.0f};          // NOLINT | ||||
|   bool missing_state{false};  // NOLINT | ||||
|   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_varint(uint32_t field_id, ProtoVarInt value) override; | ||||
| }; | ||||
| class ListEntitiesSwitchResponse : public ProtoMessage { | ||||
|  public: | ||||
| @@ -437,18 +477,20 @@ class ListEntitiesTextSensorResponse : public ProtoMessage { | ||||
| }; | ||||
| class TextSensorStateResponse : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0};      // NOLINT | ||||
|   std::string state{};  // NOLINT | ||||
|   uint32_t key{0};            // NOLINT | ||||
|   std::string state{};        // NOLINT | ||||
|   bool missing_state{false};  // NOLINT | ||||
|   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 SubscribeLogsRequest : public ProtoMessage { | ||||
|  public: | ||||
|   EnumLogLevel level{};     // NOLINT | ||||
|   enums::LogLevel level{};  // NOLINT | ||||
|   bool dump_config{false};  // NOLINT | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void dump_to(std::string &out) const override; | ||||
| @@ -458,7 +500,7 @@ class SubscribeLogsRequest : public ProtoMessage { | ||||
| }; | ||||
| class SubscribeLogsResponse : public ProtoMessage { | ||||
|  public: | ||||
|   EnumLogLevel level{};     // NOLINT | ||||
|   enums::LogLevel level{};  // NOLINT | ||||
|   std::string tag{};        // NOLINT | ||||
|   std::string message{};    // NOLINT | ||||
|   bool send_failed{false};  // NOLINT | ||||
| @@ -544,8 +586,8 @@ class GetTimeResponse : public ProtoMessage { | ||||
| }; | ||||
| class ListEntitiesServicesArgument : public ProtoMessage { | ||||
|  public: | ||||
|   std::string name{};         // NOLINT | ||||
|   EnumServiceArgType type{};  // NOLINT | ||||
|   std::string name{};            // NOLINT | ||||
|   enums::ServiceArgType type{};  // NOLINT | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void dump_to(std::string &out) const override; | ||||
|  | ||||
| @@ -633,18 +675,20 @@ 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<EnumClimateMode> 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::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 | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void dump_to(std::string &out) const override; | ||||
|  | ||||
| @@ -655,14 +699,16 @@ class ListEntitiesClimateResponse : public ProtoMessage { | ||||
| }; | ||||
| class ClimateStateResponse : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0};                      // NOLINT | ||||
|   EnumClimateMode 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 | ||||
|   EnumClimateAction action{};           // NOLINT | ||||
|   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 | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void dump_to(std::string &out) const override; | ||||
|  | ||||
| @@ -674,7 +720,7 @@ class ClimateCommandRequest : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0};                          // NOLINT | ||||
|   bool has_mode{false};                     // NOLINT | ||||
|   EnumClimateMode mode{};                   // NOLINT | ||||
|   enums::ClimateMode mode{};                // NOLINT | ||||
|   bool has_target_temperature{false};       // NOLINT | ||||
|   float target_temperature{0.0f};           // NOLINT | ||||
|   bool has_target_temperature_low{false};   // NOLINT | ||||
| @@ -683,6 +729,10 @@ class ClimateCommandRequest : public ProtoMessage { | ||||
|   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 | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void dump_to(std::string &out) const override; | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| // This file was automatically generated with a tool. | ||||
| // See scripts/api_protobuf/api_protobuf.py | ||||
| #include "api_pb2_service.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| @@ -8,69 +10,57 @@ static const char *TAG = "api.service"; | ||||
|  | ||||
| bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<HelloResponse>(msg, 2); | ||||
| } | ||||
| bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<ConnectResponse>(msg, 4); | ||||
| } | ||||
| bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) { | ||||
|   ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<DisconnectRequest>(msg, 5); | ||||
| } | ||||
| bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<DisconnectResponse>(msg, 6); | ||||
| } | ||||
| bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) { | ||||
|   ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<PingRequest>(msg, 7); | ||||
| } | ||||
| bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<PingResponse>(msg, 8); | ||||
| } | ||||
| bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<DeviceInfoResponse>(msg, 10); | ||||
| } | ||||
| bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<ListEntitiesDoneResponse>(msg, 19); | ||||
| } | ||||
| #ifdef USE_BINARY_SENSOR | ||||
| bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_BINARY_SENSOR | ||||
| bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<BinarySensorStateResponse>(msg, 21); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_COVER | ||||
| bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<ListEntitiesCoverResponse>(msg, 13); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_COVER | ||||
| bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<CoverStateResponse>(msg, 22); | ||||
| } | ||||
| #endif | ||||
| @@ -79,14 +69,12 @@ bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse | ||||
| #ifdef USE_FAN | ||||
| bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<ListEntitiesFanResponse>(msg, 14); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_FAN | ||||
| bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<FanStateResponse>(msg, 23); | ||||
| } | ||||
| #endif | ||||
| @@ -95,14 +83,12 @@ bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &ms | ||||
| #ifdef USE_LIGHT | ||||
| bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<ListEntitiesLightResponse>(msg, 15); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_LIGHT | ||||
| bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<LightStateResponse>(msg, 24); | ||||
| } | ||||
| #endif | ||||
| @@ -111,28 +97,24 @@ bool APIServerConnectionBase::send_light_state_response(const LightStateResponse | ||||
| #ifdef USE_SENSOR | ||||
| bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<ListEntitiesSensorResponse>(msg, 16); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_SENSOR | ||||
| bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<SensorStateResponse>(msg, 25); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_SWITCH | ||||
| bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<ListEntitiesSwitchResponse>(msg, 17); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_SWITCH | ||||
| bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<SwitchStateResponse>(msg, 26); | ||||
| } | ||||
| #endif | ||||
| @@ -141,58 +123,48 @@ bool APIServerConnectionBase::send_switch_state_response(const SwitchStateRespon | ||||
| #ifdef USE_TEXT_SENSOR | ||||
| bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_TEXT_SENSOR | ||||
| bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<TextSensorStateResponse>(msg, 27); | ||||
| } | ||||
| #endif | ||||
| bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) { | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<SubscribeLogsResponse>(msg, 29); | ||||
| } | ||||
| bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<HomeassistantServiceResponse>(msg, 35); | ||||
| } | ||||
| bool APIServerConnectionBase::send_subscribe_home_assistant_state_response( | ||||
|     const SubscribeHomeAssistantStateResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39); | ||||
| } | ||||
| bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) { | ||||
|   ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<GetTimeRequest>(msg, 36); | ||||
| } | ||||
| bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<GetTimeResponse>(msg, 37); | ||||
| } | ||||
| bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<ListEntitiesServicesResponse>(msg, 41); | ||||
| } | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<ListEntitiesCameraResponse>(msg, 43); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_ESP32_CAMERA | ||||
| bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<CameraImageResponse>(msg, 44); | ||||
| } | ||||
| #endif | ||||
| @@ -201,14 +173,12 @@ bool APIServerConnectionBase::send_camera_image_response(const CameraImageRespon | ||||
| #ifdef USE_CLIMATE | ||||
| bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(false); | ||||
|   return this->send_message_<ListEntitiesClimateResponse>(msg, 46); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_CLIMATE | ||||
| bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) { | ||||
|   ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str()); | ||||
|   this->set_nodelay(true); | ||||
|   return this->send_message_<ClimateStateResponse>(msg, 47); | ||||
| } | ||||
| #endif | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| // This file was automatically generated with a tool. | ||||
| // See scripts/api_protobuf/api_protobuf.py | ||||
| #pragma once | ||||
|  | ||||
| #include "api_pb2.h" | ||||
|   | ||||
| @@ -29,6 +29,7 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts | ||||
|   template<typename T> void add_variable(std::string key, T value) { | ||||
|     this->variables_.push_back(TemplatableKeyValuePair<Ts...>(key, value)); | ||||
|   } | ||||
|  | ||||
|   void play(Ts... x) override { | ||||
|     HomeassistantServiceResponse resp; | ||||
|     resp.service = this->service_.value(x...); | ||||
|   | ||||
| @@ -266,7 +266,6 @@ class ProtoService { | ||||
|   virtual ProtoWriteBuffer create_buffer() = 0; | ||||
|   virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0; | ||||
|   virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0; | ||||
|   virtual void set_nodelay(bool nodelay) = 0; | ||||
|  | ||||
|   template<class C> bool send_message_(const C &msg, uint32_t message_type) { | ||||
|     auto buffer = this->create_buffer(); | ||||
|   | ||||
| @@ -7,9 +7,6 @@ namespace api { | ||||
|  | ||||
| #ifdef USE_BINARY_SENSOR | ||||
| bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { | ||||
|   if (!binary_sensor->has_state()) | ||||
|     return true; | ||||
|  | ||||
|   return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state); | ||||
| } | ||||
| #endif | ||||
| @@ -24,9 +21,6 @@ bool InitialStateIterator::on_light(light::LightState *light) { return this->cli | ||||
| #endif | ||||
| #ifdef USE_SENSOR | ||||
| bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { | ||||
|   if (!sensor->has_state()) | ||||
|     return true; | ||||
|  | ||||
|   return this->client_->send_sensor_state(sensor, sensor->state); | ||||
| } | ||||
| #endif | ||||
| @@ -37,9 +31,6 @@ bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { | ||||
| #endif | ||||
| #ifdef USE_TEXT_SENSOR | ||||
| bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) { | ||||
|   if (!text_sensor->has_state()) | ||||
|     return true; | ||||
|  | ||||
|   return this->client_->send_text_sensor_state(text_sensor, text_sensor->state); | ||||
| } | ||||
| #endif | ||||
|   | ||||
| @@ -25,14 +25,18 @@ template<> std::vector<std::string> get_execute_arg_value<std::vector<std::strin | ||||
|   return arg.string_array; | ||||
| } | ||||
|  | ||||
| template<> EnumServiceArgType to_service_arg_type<bool>() { return SERVICE_ARG_TYPE_BOOL; } | ||||
| template<> EnumServiceArgType to_service_arg_type<int>() { return SERVICE_ARG_TYPE_INT; } | ||||
| template<> EnumServiceArgType to_service_arg_type<float>() { return SERVICE_ARG_TYPE_FLOAT; } | ||||
| template<> EnumServiceArgType to_service_arg_type<std::string>() { return SERVICE_ARG_TYPE_STRING; } | ||||
| template<> EnumServiceArgType to_service_arg_type<std::vector<bool>>() { return SERVICE_ARG_TYPE_BOOL_ARRAY; } | ||||
| template<> EnumServiceArgType to_service_arg_type<std::vector<int>>() { return SERVICE_ARG_TYPE_INT_ARRAY; } | ||||
| template<> EnumServiceArgType to_service_arg_type<std::vector<float>>() { return SERVICE_ARG_TYPE_FLOAT_ARRAY; } | ||||
| template<> EnumServiceArgType to_service_arg_type<std::vector<std::string>>() { return SERVICE_ARG_TYPE_STRING_ARRAY; } | ||||
| template<> enums::ServiceArgType to_service_arg_type<bool>() { return enums::SERVICE_ARG_TYPE_BOOL; } | ||||
| template<> enums::ServiceArgType to_service_arg_type<int>() { return enums::SERVICE_ARG_TYPE_INT; } | ||||
| template<> enums::ServiceArgType to_service_arg_type<float>() { return enums::SERVICE_ARG_TYPE_FLOAT; } | ||||
| template<> enums::ServiceArgType to_service_arg_type<std::string>() { return enums::SERVICE_ARG_TYPE_STRING; } | ||||
| template<> enums::ServiceArgType to_service_arg_type<std::vector<bool>>() { return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; } | ||||
| template<> enums::ServiceArgType to_service_arg_type<std::vector<int>>() { return enums::SERVICE_ARG_TYPE_INT_ARRAY; } | ||||
| template<> enums::ServiceArgType to_service_arg_type<std::vector<float>>() { | ||||
|   return enums::SERVICE_ARG_TYPE_FLOAT_ARRAY; | ||||
| } | ||||
| template<> enums::ServiceArgType to_service_arg_type<std::vector<std::string>>() { | ||||
|   return enums::SERVICE_ARG_TYPE_STRING_ARRAY; | ||||
| } | ||||
|  | ||||
| }  // namespace api | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -16,7 +16,7 @@ class UserServiceDescriptor { | ||||
|  | ||||
| template<typename T> T get_execute_arg_value(const ExecuteServiceArgument &arg); | ||||
|  | ||||
| template<typename T> EnumServiceArgType to_service_arg_type(); | ||||
| template<typename T> enums::ServiceArgType to_service_arg_type(); | ||||
|  | ||||
| template<typename... Ts> class UserServiceBase : public UserServiceDescriptor { | ||||
|  public: | ||||
| @@ -29,7 +29,7 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor { | ||||
|     ListEntitiesServicesResponse msg; | ||||
|     msg.name = this->name_; | ||||
|     msg.key = this->key_; | ||||
|     std::array<EnumServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...}; | ||||
|     std::array<enums::ServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...}; | ||||
|     for (int i = 0; i < sizeof...(Ts); i++) { | ||||
|       ListEntitiesServicesArgument arg; | ||||
|       arg.type = arg_types[i]; | ||||
|   | ||||
| @@ -10,8 +10,8 @@ as3935_spi_ns = cg.esphome_ns.namespace('as3935_spi') | ||||
| SPIAS3935 = as3935_spi_ns.class_('SPIAS3935Component', as3935.AS3935, spi.SPIDevice) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All(as3935.AS3935_SCHEMA.extend({ | ||||
|     cv.GenerateID(): cv.declare_id(SPIAS3935) | ||||
| }).extend(cv.COMPONENT_SCHEMA).extend(spi.SPI_DEVICE_SCHEMA)) | ||||
|     cv.GenerateID(): cv.declare_id(SPIAS3935), | ||||
| }).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema(cs_pin_required=True))) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
|   | ||||
| @@ -1,23 +1,15 @@ | ||||
| # Dummy integration to allow relying on AsyncTCP | ||||
| import esphome.codegen as cg | ||||
| from esphome.const import ARDUINO_VERSION_ESP32_1_0_0, ARDUINO_VERSION_ESP32_1_0_1, \ | ||||
|     ARDUINO_VERSION_ESP32_1_0_2 | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
|  | ||||
| CODEOWNERS = ['@OttoWinter'] | ||||
|  | ||||
|  | ||||
| @coroutine_with_priority(200.0) | ||||
| def to_code(config): | ||||
|     if CORE.is_esp32: | ||||
|         # https://github.com/me-no-dev/AsyncTCP/blob/master/library.json | ||||
|         versions_requiring_older_asynctcp = [ | ||||
|             ARDUINO_VERSION_ESP32_1_0_0, | ||||
|             ARDUINO_VERSION_ESP32_1_0_1, | ||||
|             ARDUINO_VERSION_ESP32_1_0_2, | ||||
|         ] | ||||
|         if CORE.arduino_version in versions_requiring_older_asynctcp: | ||||
|             cg.add_library('AsyncTCP', '1.0.3') | ||||
|         else: | ||||
|             cg.add_library('AsyncTCP', '1.1.1') | ||||
|         # https://github.com/OttoWinter/AsyncTCP/blob/master/library.json | ||||
|         cg.add_library('AsyncTCP-esphome', '1.1.1') | ||||
|     elif CORE.is_esp8266: | ||||
|         # https://github.com/OttoWinter/ESPAsyncTCP | ||||
|         cg.add_library('ESPAsyncTCP-esphome', '1.2.2') | ||||
|   | ||||
| @@ -40,19 +40,45 @@ void ATM90E32Component::update() { | ||||
|   if (this->phase_[2].power_sensor_ != nullptr) { | ||||
|     this->phase_[2].power_sensor_->publish_state(this->get_active_power_c_()); | ||||
|   } | ||||
|   if (this->phase_[0].reactive_power_sensor_ != nullptr) { | ||||
|     this->phase_[0].reactive_power_sensor_->publish_state(this->get_reactive_power_a_()); | ||||
|   } | ||||
|   if (this->phase_[1].reactive_power_sensor_ != nullptr) { | ||||
|     this->phase_[1].reactive_power_sensor_->publish_state(this->get_reactive_power_b_()); | ||||
|   } | ||||
|   if (this->phase_[2].reactive_power_sensor_ != nullptr) { | ||||
|     this->phase_[2].reactive_power_sensor_->publish_state(this->get_reactive_power_c_()); | ||||
|   } | ||||
|   if (this->phase_[0].power_factor_sensor_ != nullptr) { | ||||
|     this->phase_[0].power_factor_sensor_->publish_state(this->get_power_factor_a_()); | ||||
|   } | ||||
|   if (this->phase_[1].power_factor_sensor_ != nullptr) { | ||||
|     this->phase_[1].power_factor_sensor_->publish_state(this->get_power_factor_b_()); | ||||
|   } | ||||
|   if (this->phase_[2].power_factor_sensor_ != nullptr) { | ||||
|     this->phase_[2].power_factor_sensor_->publish_state(this->get_power_factor_c_()); | ||||
|   } | ||||
|   if (this->freq_sensor_ != nullptr) { | ||||
|     this->freq_sensor_->publish_state(this->get_frequency_()); | ||||
|   } | ||||
|   if (this->chip_temperature_sensor_ != nullptr) { | ||||
|     this->chip_temperature_sensor_->publish_state(this->get_chip_temperature_()); | ||||
|   } | ||||
|   this->status_clear_warning(); | ||||
| } | ||||
|  | ||||
| void ATM90E32Component::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up ATM90E32Component..."); | ||||
|   ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component..."); | ||||
|   this->spi_setup(); | ||||
|  | ||||
|   uint16_t mmode0 = 0x185; | ||||
|   uint16_t mmode0 = 0x87;  // 3P4W 50Hz | ||||
|   if (line_freq_ == 60) { | ||||
|     mmode0 |= 1 << 12; | ||||
|     mmode0 |= 1 << 12;  // sets 12th bit to 1, 60Hz | ||||
|   } | ||||
|  | ||||
|   if (current_phases_ == 2) { | ||||
|     mmode0 |= 1 << 8;  // sets 8th bit to 1, 3P3W | ||||
|     mmode0 |= 0 << 1;  // sets 1st bit to 0, phase b is not counted into the all-phase sum energy/power (P/Q/S) | ||||
|   } | ||||
|  | ||||
|   this->write16_(ATM90E32_REGISTER_SOFTRESET, 0x789A);    // Perform soft reset | ||||
| @@ -63,13 +89,15 @@ void ATM90E32Component::setup() { | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0x0A55);                    // ZX2, ZX1, ZX0 pin config | ||||
|   this->write16_(ATM90E32_REGISTER_MMODE0, mmode0);                      // Mode Config (frequency set in main program) | ||||
|   this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_);                   // PGA Gain Configuration for Current Channels | ||||
|   this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x0AFC);                    // Active Startup Power Threshold = 50% | ||||
|   this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x0AEC);                    // Reactive Startup Power Threshold = 50% | ||||
|   this->write16_(ATM90E32_REGISTER_PPHASETH, 0x00BC);                    // Active Phase Threshold = 10% | ||||
|   this->write16_(ATM90E32_REGISTER_PLCONSTH, 0x0861);   // PL Constant MSB (default) = 140625000 | ||||
|   this->write16_(ATM90E32_REGISTER_PLCONSTL, 0xC468);   // PL Constant LSB (default) | ||||
|   this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0xD654);   // ZX2, ZX1, ZX0 pin config | ||||
|   this->write16_(ATM90E32_REGISTER_MMODE0, mmode0);     // Mode Config (frequency set in main program) | ||||
|   this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_);  // PGA Gain Configuration for Current Channels | ||||
|   this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x1D4C);   // All Active Startup Power Threshold - 0.02A/0.00032 = 7500 | ||||
|   this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x1D4C);   // All Reactive Startup Power Threshold - 50% | ||||
|   this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE);   // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750 | ||||
|   this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE);   // Each phase Reactive Phase Threshold - 10% | ||||
|   this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[0].volt_gain_);  // A Voltage rms gain | ||||
|   this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[0].ct_gain_);    // A line current gain | ||||
|   this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[1].volt_gain_);  // B Voltage rms gain | ||||
| @@ -89,13 +117,20 @@ void ATM90E32Component::dump_config() { | ||||
|   LOG_SENSOR("  ", "Voltage A", this->phase_[0].voltage_sensor_); | ||||
|   LOG_SENSOR("  ", "Current A", this->phase_[0].current_sensor_); | ||||
|   LOG_SENSOR("  ", "Power A", this->phase_[0].power_sensor_); | ||||
|   LOG_SENSOR("  ", "Reactive Power A", this->phase_[0].reactive_power_sensor_); | ||||
|   LOG_SENSOR("  ", "PF A", this->phase_[0].power_factor_sensor_); | ||||
|   LOG_SENSOR("  ", "Voltage B", this->phase_[1].voltage_sensor_); | ||||
|   LOG_SENSOR("  ", "Current B", this->phase_[1].current_sensor_); | ||||
|   LOG_SENSOR("  ", "Power B", this->phase_[1].power_sensor_); | ||||
|   LOG_SENSOR("  ", "Reactive Power B", this->phase_[1].reactive_power_sensor_); | ||||
|   LOG_SENSOR("  ", "PF B", this->phase_[1].power_factor_sensor_); | ||||
|   LOG_SENSOR("  ", "Voltage C", this->phase_[2].voltage_sensor_); | ||||
|   LOG_SENSOR("  ", "Current C", this->phase_[2].current_sensor_); | ||||
|   LOG_SENSOR("  ", "Power C", this->phase_[2].power_sensor_); | ||||
|   LOG_SENSOR("  ", "Frequency", this->freq_sensor_) | ||||
|   LOG_SENSOR("  ", "Reactive Power C", this->phase_[2].reactive_power_sensor_); | ||||
|   LOG_SENSOR("  ", "PF C", this->phase_[2].power_factor_sensor_); | ||||
|   LOG_SENSOR("  ", "Frequency", this->freq_sensor_); | ||||
|   LOG_SENSOR("  ", "Chip Temp", this->chip_temperature_sensor_); | ||||
| } | ||||
| float ATM90E32Component::get_setup_priority() const { return setup_priority::DATA; } | ||||
|  | ||||
| @@ -180,9 +215,37 @@ float ATM90E32Component::get_active_power_c_() { | ||||
|   int val = this->read32_(ATM90E32_REGISTER_PMEANC, ATM90E32_REGISTER_PMEANCLSB); | ||||
|   return val * 0.00032f; | ||||
| } | ||||
| float ATM90E32Component::get_reactive_power_a_() { | ||||
|   int val = this->read32_(ATM90E32_REGISTER_QMEANA, ATM90E32_REGISTER_QMEANALSB); | ||||
|   return val * 0.00032f; | ||||
| } | ||||
| float ATM90E32Component::get_reactive_power_b_() { | ||||
|   int val = this->read32_(ATM90E32_REGISTER_QMEANB, ATM90E32_REGISTER_QMEANBLSB); | ||||
|   return val * 0.00032f; | ||||
| } | ||||
| float ATM90E32Component::get_reactive_power_c_() { | ||||
|   int val = this->read32_(ATM90E32_REGISTER_QMEANC, ATM90E32_REGISTER_QMEANCLSB); | ||||
|   return val * 0.00032f; | ||||
| } | ||||
| float ATM90E32Component::get_power_factor_a_() { | ||||
|   int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANA); | ||||
|   return (float) pf / 1000; | ||||
| } | ||||
| float ATM90E32Component::get_power_factor_b_() { | ||||
|   int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANB); | ||||
|   return (float) pf / 1000; | ||||
| } | ||||
| float ATM90E32Component::get_power_factor_c_() { | ||||
|   int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANC); | ||||
|   return (float) pf / 1000; | ||||
| } | ||||
| float ATM90E32Component::get_frequency_() { | ||||
|   uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ); | ||||
|   return (float) freq / 100; | ||||
| } | ||||
| float ATM90E32Component::get_chip_temperature_() { | ||||
|   uint16_t ctemp = this->read16_(ATM90E32_REGISTER_TEMP); | ||||
|   return (float) ctemp; | ||||
| } | ||||
| }  // namespace atm90e32 | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -19,11 +19,17 @@ class ATM90E32Component : public PollingComponent, | ||||
|   void set_voltage_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].voltage_sensor_ = obj; } | ||||
|   void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; } | ||||
|   void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; } | ||||
|   void set_reactive_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].reactive_power_sensor_ = obj; } | ||||
|   void set_power_factor_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_factor_sensor_ = obj; } | ||||
|   void set_volt_gain(int phase, uint16_t gain) { this->phase_[phase].volt_gain_ = gain; } | ||||
|   void set_ct_gain(int phase, uint16_t gain) { this->phase_[phase].ct_gain_ = gain; } | ||||
|  | ||||
|   void set_freq_sensor(sensor::Sensor *freq_sensor) { freq_sensor_ = freq_sensor; } | ||||
|   void set_chip_temperature_sensor(sensor::Sensor *chip_temperature_sensor) { | ||||
|     chip_temperature_sensor_ = chip_temperature_sensor; | ||||
|   } | ||||
|   void set_line_freq(int freq) { line_freq_ = freq; } | ||||
|   void set_current_phases(int phases) { current_phases_ = phases; } | ||||
|   void set_pga_gain(uint16_t gain) { pga_gain_ = gain; } | ||||
|  | ||||
|  protected: | ||||
| @@ -40,18 +46,29 @@ class ATM90E32Component : public PollingComponent, | ||||
|   float get_active_power_a_(); | ||||
|   float get_active_power_b_(); | ||||
|   float get_active_power_c_(); | ||||
|   float get_reactive_power_a_(); | ||||
|   float get_reactive_power_b_(); | ||||
|   float get_reactive_power_c_(); | ||||
|   float get_power_factor_a_(); | ||||
|   float get_power_factor_b_(); | ||||
|   float get_power_factor_c_(); | ||||
|   float get_frequency_(); | ||||
|   float get_chip_temperature_(); | ||||
|  | ||||
|   struct ATM90E32Phase { | ||||
|     uint16_t volt_gain_{41820}; | ||||
|     uint16_t ct_gain_{25498}; | ||||
|     uint16_t volt_gain_{7305}; | ||||
|     uint16_t ct_gain_{27961}; | ||||
|     sensor::Sensor *voltage_sensor_{nullptr}; | ||||
|     sensor::Sensor *current_sensor_{nullptr}; | ||||
|     sensor::Sensor *power_sensor_{nullptr}; | ||||
|     sensor::Sensor *reactive_power_sensor_{nullptr}; | ||||
|     sensor::Sensor *power_factor_sensor_{nullptr}; | ||||
|   } phase_[3]; | ||||
|   sensor::Sensor *freq_sensor_{nullptr}; | ||||
|   sensor::Sensor *chip_temperature_sensor_{nullptr}; | ||||
|   uint16_t pga_gain_{0x15}; | ||||
|   int line_freq_{60}; | ||||
|   int current_phases_{3}; | ||||
| }; | ||||
|  | ||||
| }  // namespace atm90e32 | ||||
|   | ||||
| @@ -234,12 +234,12 @@ static const uint16_t ATM90E32_REGISTER_IRMSBLSB = 0xEE;    // Lower Word (B RMS | ||||
| static const uint16_t ATM90E32_REGISTER_IRMSCLSB = 0xEF;    // Lower Word (C RMS Current) | ||||
|  | ||||
| /* THD, FREQUENCY, ANGLE & TEMPTEMP REGISTERS*/ | ||||
| static const uint16_t ATM90E32_REGISTER_THDNUA = 0xF1;   // A Voltage THD+N | ||||
| static const uint16_t ATM90E32_REGISTER_THDNUB = 0xF2;   // B Voltage THD+N | ||||
| static const uint16_t ATM90E32_REGISTER_THDNUC = 0xF3;   // C Voltage THD+N | ||||
| static const uint16_t ATM90E32_REGISTER_THDNIA = 0xF5;   // A Current THD+N | ||||
| static const uint16_t ATM90E32_REGISTER_THDNIB = 0xF6;   // B Current THD+N | ||||
| static const uint16_t ATM90E32_REGISTER_THDNIC = 0xF7;   // C Current THD+N | ||||
| static const uint16_t ATM90E32_REGISTER_UPEAKA = 0xF1;   // A Voltage Peak | ||||
| static const uint16_t ATM90E32_REGISTER_UPEAKB = 0xF2;   // B Voltage Peak | ||||
| static const uint16_t ATM90E32_REGISTER_UPEAKC = 0xF3;   // C Voltage Peak | ||||
| static const uint16_t ATM90E32_REGISTER_IPEAKA = 0xF5;   // A Current Peak | ||||
| static const uint16_t ATM90E32_REGISTER_IPEAKB = 0xF6;   // B Current Peak | ||||
| static const uint16_t ATM90E32_REGISTER_IPEAKC = 0xF7;   // C Current Peak | ||||
| static const uint16_t ATM90E32_REGISTER_FREQ = 0xF8;     // Frequency | ||||
| static const uint16_t ATM90E32_REGISTER_PANGLEA = 0xF9;  // A Mean Phase Angle | ||||
| static const uint16_t ATM90E32_REGISTER_PANGLEB = 0xFA;  // B Mean Phase Angle | ||||
|   | ||||
| @@ -2,21 +2,29 @@ import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import sensor, spi | ||||
| from esphome.const import \ | ||||
|     CONF_ID, CONF_VOLTAGE, CONF_CURRENT, CONF_POWER, CONF_FREQUENCY, \ | ||||
|     ICON_FLASH, UNIT_HZ, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT | ||||
|     CONF_ID, CONF_VOLTAGE, CONF_CURRENT, CONF_POWER, CONF_POWER_FACTOR, CONF_FREQUENCY, \ | ||||
|     ICON_FLASH, ICON_LIGHTBULB, ICON_CURRENT_AC, ICON_THERMOMETER, \ | ||||
|     UNIT_HERTZ, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_EMPTY, UNIT_CELSIUS, UNIT_VOLT_AMPS_REACTIVE | ||||
|  | ||||
| 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' | ||||
| CONF_CURRENT_PHASES = 'current_phases' | ||||
| CONF_GAIN_VOLTAGE = 'gain_voltage' | ||||
| CONF_GAIN_CT = 'gain_ct' | ||||
| LINE_FREQS = { | ||||
|     '50HZ': 50, | ||||
|     '60HZ': 60, | ||||
| } | ||||
| CURRENT_PHASES = { | ||||
|     '2': 2, | ||||
|     '3': 3, | ||||
| } | ||||
| PGA_GAINS = { | ||||
|     '1X': 0x0, | ||||
|     '2X': 0x15, | ||||
| @@ -28,10 +36,13 @@ ATM90E32Component = atm90e32_ns.class_('ATM90E32Component', cg.PollingComponent, | ||||
|  | ||||
| ATM90E32_PHASE_SCHEMA = cv.Schema({ | ||||
|     cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), | ||||
|     cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), | ||||
|     cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_CURRENT_AC, 2), | ||||
|     cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2), | ||||
|     cv.Optional(CONF_GAIN_VOLTAGE, default=41820): cv.uint16_t, | ||||
|     cv.Optional(CONF_GAIN_CT, default=25498): cv.uint16_t, | ||||
|     cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(UNIT_VOLT_AMPS_REACTIVE, | ||||
|                                                            ICON_LIGHTBULB, 2), | ||||
|     cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 2), | ||||
|     cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t, | ||||
|     cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t, | ||||
| }) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema({ | ||||
| @@ -39,10 +50,12 @@ CONFIG_SCHEMA = cv.Schema({ | ||||
|     cv.Optional(CONF_PHASE_A): ATM90E32_PHASE_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_HZ, ICON_FLASH, 1), | ||||
|     cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HERTZ, ICON_CURRENT_AC, 1), | ||||
|     cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), | ||||
|     cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True), | ||||
|     cv.Optional(CONF_CURRENT_PHASES, default='3'): cv.enum(CURRENT_PHASES, upper=True), | ||||
|     cv.Optional(CONF_GAIN_PGA, default='2X'): cv.enum(PGA_GAINS, upper=True), | ||||
| }).extend(cv.polling_component_schema('60s')).extend(spi.SPI_DEVICE_SCHEMA) | ||||
| }).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema()) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
| @@ -65,8 +78,18 @@ def to_code(config): | ||||
|         if CONF_POWER in conf: | ||||
|             sens = yield 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]) | ||||
|             cg.add(var.set_reactive_power_sensor(i, sens)) | ||||
|         if CONF_POWER_FACTOR in conf: | ||||
|             sens = yield sensor.new_sensor(conf[CONF_POWER_FACTOR]) | ||||
|             cg.add(var.set_power_factor_sensor(i, sens)) | ||||
|     if CONF_FREQUENCY in config: | ||||
|         sens = yield 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]) | ||||
|         cg.add(var.set_chip_temperature_sensor(sens)) | ||||
|     cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY])) | ||||
|     cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES])) | ||||
|     cg.add(var.set_pga_gain(config[CONF_GAIN_PGA])) | ||||
|   | ||||
| @@ -0,0 +1 @@ | ||||
| CODEOWNERS = ['@OttoWinter'] | ||||
|   | ||||
| @@ -51,12 +51,15 @@ climate::ClimateTraits BangBangClimate::traits() { | ||||
| } | ||||
| void BangBangClimate::compute_state_() { | ||||
|   if (this->mode != climate::CLIMATE_MODE_AUTO) { | ||||
|     // in non-auto mode | ||||
|     // in non-auto mode, switch directly to appropriate action | ||||
|     //  - HEAT mode -> HEATING action | ||||
|     //  - COOL mode -> COOLING action | ||||
|     //  - OFF mode -> OFF action (not IDLE!) | ||||
|     this->switch_to_action_(static_cast<climate::ClimateAction>(this->mode)); | ||||
|     return; | ||||
|   } | ||||
|   if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) { | ||||
|     // if any control values are nan, go to OFF (idle) mode | ||||
|     // if any control parameters are nan, go to OFF action (not IDLE!) | ||||
|     this->switch_to_action_(climate::CLIMATE_ACTION_OFF); | ||||
|     return; | ||||
|   } | ||||
| @@ -69,18 +72,18 @@ void BangBangClimate::compute_state_() { | ||||
|     if (this->supports_heat_) | ||||
|       target_action = climate::CLIMATE_ACTION_HEATING; | ||||
|     else | ||||
|       target_action = climate::CLIMATE_ACTION_OFF; | ||||
|       target_action = climate::CLIMATE_ACTION_IDLE; | ||||
|   } else if (too_hot) { | ||||
|     // too hot -> enable cooling if possible, else idle | ||||
|     if (this->supports_cool_) | ||||
|       target_action = climate::CLIMATE_ACTION_COOLING; | ||||
|     else | ||||
|       target_action = climate::CLIMATE_ACTION_OFF; | ||||
|       target_action = climate::CLIMATE_ACTION_IDLE; | ||||
|   } else { | ||||
|     // neither too hot nor too cold -> in range | ||||
|     if (this->supports_cool_ && this->supports_heat_) { | ||||
|       // if supports both ends, go to idle mode | ||||
|       target_action = climate::CLIMATE_ACTION_OFF; | ||||
|       // if supports both ends, go to idle action | ||||
|       target_action = climate::CLIMATE_ACTION_IDLE; | ||||
|     } else { | ||||
|       // else use current mode and don't change (hysteresis) | ||||
|       target_action = this->action; | ||||
| @@ -94,13 +97,24 @@ void BangBangClimate::switch_to_action_(climate::ClimateAction action) { | ||||
|     // already in target mode | ||||
|     return; | ||||
|  | ||||
|   if ((action == climate::CLIMATE_ACTION_OFF && this->action == climate::CLIMATE_ACTION_IDLE) || | ||||
|       (action == climate::CLIMATE_ACTION_IDLE && this->action == climate::CLIMATE_ACTION_OFF)) { | ||||
|     // switching from OFF to IDLE or vice-versa | ||||
|     // these only have visual difference. OFF means user manually disabled, | ||||
|     // IDLE means it's in auto mode but value is in target range. | ||||
|     this->action = action; | ||||
|     this->publish_state(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (this->prev_trigger_ != nullptr) { | ||||
|     this->prev_trigger_->stop(); | ||||
|     this->prev_trigger_->stop_action(); | ||||
|     this->prev_trigger_ = nullptr; | ||||
|   } | ||||
|   Trigger<> *trig; | ||||
|   switch (action) { | ||||
|     case climate::CLIMATE_ACTION_OFF: | ||||
|     case climate::CLIMATE_ACTION_IDLE: | ||||
|       trig = this->idle_trigger_; | ||||
|       break; | ||||
|     case climate::CLIMATE_ACTION_COOLING: | ||||
| @@ -112,13 +126,11 @@ void BangBangClimate::switch_to_action_(climate::ClimateAction action) { | ||||
|     default: | ||||
|       trig = nullptr; | ||||
|   } | ||||
|   if (trig != nullptr) { | ||||
|     // trig should never be null, but still check so that we don't crash | ||||
|     trig->trigger(); | ||||
|     this->action = action; | ||||
|     this->prev_trigger_ = trig; | ||||
|     this->publish_state(); | ||||
|   } | ||||
|   assert(trig != nullptr); | ||||
|   trig->trigger(); | ||||
|   this->action = action; | ||||
|   this->prev_trigger_ = trig; | ||||
|   this->publish_state(); | ||||
| } | ||||
| void BangBangClimate::change_away_(bool away) { | ||||
|   if (!away) { | ||||
|   | ||||
| @@ -7,6 +7,8 @@ namespace bh1750 { | ||||
| static const char *TAG = "bh1750.sensor"; | ||||
|  | ||||
| static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001; | ||||
| static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000;  // last 3 bits | ||||
| static const uint8_t BH1750_COMMAND_MT_REG_LO = 0b01100000;  // last 5 bits | ||||
|  | ||||
| void BH1750Sensor::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up BH1750 '%s'...", this->name_.c_str()); | ||||
| @@ -14,7 +16,13 @@ void BH1750Sensor::setup() { | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   uint8_t mtreg_hi = (this->measurement_time_ >> 5) & 0b111; | ||||
|   uint8_t mtreg_lo = (this->measurement_time_ >> 0) & 0b11111; | ||||
|   this->write_bytes(BH1750_COMMAND_MT_REG_HI | mtreg_hi, nullptr, 0); | ||||
|   this->write_bytes(BH1750_COMMAND_MT_REG_LO | mtreg_lo, nullptr, 0); | ||||
| } | ||||
|  | ||||
| void BH1750Sensor::dump_config() { | ||||
|   LOG_SENSOR("", "BH1750", this); | ||||
|   LOG_I2C_DEVICE(this); | ||||
| @@ -59,6 +67,7 @@ void BH1750Sensor::update() { | ||||
|  | ||||
|   this->set_timeout("illuminance", wait, [this]() { this->read_data_(); }); | ||||
| } | ||||
|  | ||||
| float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; } | ||||
| void BH1750Sensor::read_data_() { | ||||
|   uint16_t raw_value; | ||||
| @@ -68,10 +77,12 @@ void BH1750Sensor::read_data_() { | ||||
|   } | ||||
|  | ||||
|   float lx = float(raw_value) / 1.2f; | ||||
|   lx *= 69.0f / this->measurement_time_; | ||||
|   ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), lx); | ||||
|   this->publish_state(lx); | ||||
|   this->status_clear_warning(); | ||||
| } | ||||
|  | ||||
| void BH1750Sensor::set_resolution(BH1750Resolution resolution) { this->resolution_ = resolution; } | ||||
|  | ||||
| }  // namespace bh1750 | ||||
|   | ||||
| @@ -28,6 +28,7 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c: | ||||
|    * @param resolution The new resolution of the sensor. | ||||
|    */ | ||||
|   void set_resolution(BH1750Resolution resolution); | ||||
|   void set_measurement_time(uint8_t measurement_time) { measurement_time_ = measurement_time; } | ||||
|  | ||||
|   // ========== INTERNAL METHODS ========== | ||||
|   // (In most use cases you won't need these) | ||||
| @@ -40,6 +41,7 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c: | ||||
|   void read_data_(); | ||||
|  | ||||
|   BH1750Resolution resolution_{BH1750_RESOLUTION_0P5_LX}; | ||||
|   uint8_t measurement_time_; | ||||
| }; | ||||
|  | ||||
| }  // namespace bh1750 | ||||
|   | ||||
| @@ -15,9 +15,11 @@ BH1750_RESOLUTIONS = { | ||||
|  | ||||
| BH1750Sensor = bh1750_ns.class_('BH1750Sensor', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice) | ||||
|  | ||||
| CONF_MEASUREMENT_TIME = 'measurement_time' | ||||
| CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1).extend({ | ||||
|     cv.GenerateID(): cv.declare_id(BH1750Sensor), | ||||
|     cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(BH1750_RESOLUTIONS, float=True), | ||||
|     cv.Optional(CONF_MEASUREMENT_TIME, default=69): cv.int_range(min=31, max=254), | ||||
| }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x23)) | ||||
|  | ||||
|  | ||||
| @@ -28,3 +30,4 @@ def to_code(config): | ||||
|     yield i2c.register_i2c_device(var, config) | ||||
|  | ||||
|     cg.add(var.set_resolution(config[CONF_RESOLUTION])) | ||||
|     cg.add(var.set_measurement_time(config[CONF_MEASUREMENT_TIME])) | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import fan, output | ||||
| from esphome.const import CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_OUTPUT_ID | ||||
| from esphome.const import CONF_DIRECTION_OUTPUT, CONF_OSCILLATION_OUTPUT, \ | ||||
|     CONF_OUTPUT, CONF_OUTPUT_ID | ||||
| from .. import binary_ns | ||||
|  | ||||
| BinaryFan = binary_ns.class_('BinaryFan', cg.Component) | ||||
| @@ -9,6 +10,7 @@ BinaryFan = binary_ns.class_('BinaryFan', cg.Component) | ||||
| CONFIG_SCHEMA = fan.FAN_SCHEMA.extend({ | ||||
|     cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryFan), | ||||
|     cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), | ||||
|     cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput), | ||||
|     cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput), | ||||
| }).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
| @@ -25,3 +27,7 @@ def to_code(config): | ||||
|     if CONF_OSCILLATION_OUTPUT in config: | ||||
|         oscillation_output = yield 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]) | ||||
|         cg.add(var.set_direction(direction_output)) | ||||
|   | ||||
| @@ -11,9 +11,12 @@ void binary::BinaryFan::dump_config() { | ||||
|   if (this->fan_->get_traits().supports_oscillation()) { | ||||
|     ESP_LOGCONFIG(TAG, "  Oscillation: YES"); | ||||
|   } | ||||
|   if (this->fan_->get_traits().supports_direction()) { | ||||
|     ESP_LOGCONFIG(TAG, "  Direction: YES"); | ||||
|   } | ||||
| } | ||||
| void BinaryFan::setup() { | ||||
|   auto traits = fan::FanTraits(this->oscillating_ != nullptr, false); | ||||
|   auto traits = fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr); | ||||
|   this->fan_->set_traits(traits); | ||||
|   this->fan_->add_on_state_callback([this]() { this->next_update_ = true; }); | ||||
| } | ||||
| @@ -41,6 +44,16 @@ void BinaryFan::loop() { | ||||
|     } | ||||
|     ESP_LOGD(TAG, "Setting oscillation: %s", ONOFF(enable)); | ||||
|   } | ||||
|  | ||||
|   if (this->direction_ != nullptr) { | ||||
|     bool enable = this->fan_->direction == fan::FAN_DIRECTION_REVERSE; | ||||
|     if (enable) { | ||||
|       this->direction_->turn_on(); | ||||
|     } else { | ||||
|       this->direction_->turn_off(); | ||||
|     } | ||||
|     ESP_LOGD(TAG, "Setting reverse direction: %s", ONOFF(enable)); | ||||
|   } | ||||
| } | ||||
| float BinaryFan::get_setup_priority() const { return setup_priority::DATA; } | ||||
|  | ||||
|   | ||||
| @@ -16,11 +16,13 @@ class BinaryFan : public Component { | ||||
|   void dump_config() override; | ||||
|   float get_setup_priority() const override; | ||||
|   void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; } | ||||
|   void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; } | ||||
|  | ||||
|  protected: | ||||
|   fan::FanState *fan_; | ||||
|   output::BinaryOutput *output_; | ||||
|   output::BinaryOutput *oscillating_{nullptr}; | ||||
|   output::BinaryOutput *direction_{nullptr}; | ||||
|   bool next_update_{true}; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -9,9 +9,9 @@ from esphome.const import CONF_DEVICE_CLASS, CONF_FILTERS, \ | ||||
|     CONF_ON_DOUBLE_CLICK, CONF_ON_MULTI_CLICK, CONF_ON_PRESS, CONF_ON_RELEASE, CONF_ON_STATE, \ | ||||
|     CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID, CONF_FOR, CONF_NAME, CONF_MQTT_ID | ||||
| from esphome.core import CORE, coroutine, coroutine_with_priority | ||||
| from esphome.py_compat import string_types | ||||
| from esphome.util import Registry | ||||
|  | ||||
| CODEOWNERS = ['@esphome/core'] | ||||
| DEVICE_CLASSES = [ | ||||
|     '', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas', | ||||
|     'heat', 'light', 'lock', 'moisture', 'motion', 'moving', 'occupancy', | ||||
| @@ -94,7 +94,7 @@ MULTI_CLICK_TIMING_SCHEMA = cv.Schema({ | ||||
|  | ||||
|  | ||||
| def parse_multi_click_timing_str(value): | ||||
|     if not isinstance(value, string_types): | ||||
|     if not isinstance(value, str): | ||||
|         return value | ||||
|  | ||||
|     parts = value.lower().split(' ') | ||||
| @@ -104,10 +104,10 @@ def parse_multi_click_timing_str(value): | ||||
|     try: | ||||
|         state = cv.boolean(parts[0]) | ||||
|     except cv.Invalid: | ||||
|         raise cv.Invalid(u"First word must either be ON or OFF, not {}".format(parts[0])) | ||||
|         raise cv.Invalid("First word must either be ON or OFF, not {}".format(parts[0])) | ||||
|  | ||||
|     if parts[1] != 'for': | ||||
|         raise cv.Invalid(u"Second word must be 'for', got {}".format(parts[1])) | ||||
|         raise cv.Invalid("Second word must be 'for', got {}".format(parts[1])) | ||||
|  | ||||
|     if parts[2] == 'at': | ||||
|         if parts[3] == 'least': | ||||
| @@ -115,12 +115,12 @@ def parse_multi_click_timing_str(value): | ||||
|         elif parts[3] == 'most': | ||||
|             key = CONF_MAX_LENGTH | ||||
|         else: | ||||
|             raise cv.Invalid(u"Third word after at must either be 'least' or 'most', got {}" | ||||
|                              u"".format(parts[3])) | ||||
|             raise cv.Invalid("Third word after at must either be 'least' or 'most', got {}" | ||||
|                              "".format(parts[3])) | ||||
|         try: | ||||
|             length = cv.positive_time_period_milliseconds(parts[4]) | ||||
|         except cv.Invalid as err: | ||||
|             raise cv.Invalid(u"Multi Click Grammar Parsing length failed: {}".format(err)) | ||||
|             raise cv.Invalid(f"Multi Click Grammar Parsing length failed: {err}") | ||||
|         return { | ||||
|             CONF_STATE: state, | ||||
|             key: str(length) | ||||
| @@ -132,12 +132,12 @@ def parse_multi_click_timing_str(value): | ||||
|     try: | ||||
|         min_length = cv.positive_time_period_milliseconds(parts[2]) | ||||
|     except cv.Invalid as err: | ||||
|         raise cv.Invalid(u"Multi Click Grammar Parsing minimum length failed: {}".format(err)) | ||||
|         raise cv.Invalid(f"Multi Click Grammar Parsing minimum length failed: {err}") | ||||
|  | ||||
|     try: | ||||
|         max_length = cv.positive_time_period_milliseconds(parts[4]) | ||||
|     except cv.Invalid as err: | ||||
|         raise cv.Invalid(u"Multi Click Grammar Parsing minimum length failed: {}".format(err)) | ||||
|         raise cv.Invalid(f"Multi Click Grammar Parsing minimum length failed: {err}") | ||||
|  | ||||
|     return { | ||||
|         CONF_STATE: state, | ||||
| @@ -225,7 +225,7 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ | ||||
| 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(CONF_INTERNAL)) | ||||
|         cg.add(var.set_internal(config[CONF_INTERNAL])) | ||||
|     if CONF_DEVICE_CLASS in config: | ||||
|         cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) | ||||
|     if CONF_INVERTED in config: | ||||
|   | ||||
| @@ -137,6 +137,7 @@ template<typename... Ts> class BinarySensorPublishAction : public Action<Ts...> | ||||
|  public: | ||||
|   explicit BinarySensorPublishAction(BinarySensor *sensor) : sensor_(sensor) {} | ||||
|   TEMPLATABLE_VALUE(bool, state) | ||||
|  | ||||
|   void play(Ts... x) override { | ||||
|     auto val = this->state_.value(x...); | ||||
|     this->sensor_->publish_state(val); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import binary_sensor, esp32_ble_tracker | ||||
| from esphome.const import CONF_MAC_ADDRESS, CONF_ID | ||||
| from esphome.const import CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_ID | ||||
|  | ||||
| DEPENDENCIES = ['esp32_ble_tracker'] | ||||
|  | ||||
| @@ -9,10 +9,12 @@ ble_presence_ns = cg.esphome_ns.namespace('ble_presence') | ||||
| BLEPresenceDevice = ble_presence_ns.class_('BLEPresenceDevice', binary_sensor.BinarySensor, | ||||
|                                            cg.Component, esp32_ble_tracker.ESPBTDeviceListener) | ||||
|  | ||||
| CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ | ||||
| CONFIG_SCHEMA = cv.All(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ | ||||
|     cv.GenerateID(): cv.declare_id(BLEPresenceDevice), | ||||
|     cv.Required(CONF_MAC_ADDRESS): cv.mac_address, | ||||
| }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) | ||||
|     cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, | ||||
|     cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, | ||||
| }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend( | ||||
|     cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID)) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
| @@ -21,4 +23,14 @@ def to_code(config): | ||||
|     yield esp32_ble_tracker.register_ble_device(var, config) | ||||
|     yield binary_sensor.register_binary_sensor(var, config) | ||||
|  | ||||
|     cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) | ||||
|     if CONF_MAC_ADDRESS in config: | ||||
|         cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) | ||||
|  | ||||
|     if CONF_SERVICE_UUID in config: | ||||
|         if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): | ||||
|             cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) | ||||
|         elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format): | ||||
|             cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) | ||||
|         elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): | ||||
|             uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID]) | ||||
|             cg.add(var.set_service_uuid128(uuid128)) | ||||
|   | ||||
| @@ -13,17 +13,42 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff, | ||||
|                           public esp32_ble_tracker::ESPBTDeviceListener, | ||||
|                           public Component { | ||||
|  public: | ||||
|   void set_address(uint64_t address) { address_ = address; } | ||||
|   void set_address(uint64_t address) { | ||||
|     this->by_address_ = true; | ||||
|     this->address_ = address; | ||||
|   } | ||||
|   void set_service_uuid16(uint16_t uuid) { | ||||
|     this->by_address_ = false; | ||||
|     this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid); | ||||
|   } | ||||
|   void set_service_uuid32(uint32_t uuid) { | ||||
|     this->by_address_ = false; | ||||
|     this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint32(uuid); | ||||
|   } | ||||
|   void set_service_uuid128(uint8_t *uuid) { | ||||
|     this->by_address_ = false; | ||||
|     this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid); | ||||
|   } | ||||
|   void on_scan_end() override { | ||||
|     if (!this->found_) | ||||
|       this->publish_state(false); | ||||
|     this->found_ = false; | ||||
|   } | ||||
|   bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { | ||||
|     if (device.address_uint64() == this->address_) { | ||||
|       this->publish_state(true); | ||||
|       this->found_ = true; | ||||
|       return true; | ||||
|     if (this->by_address_) { | ||||
|       if (device.address_uint64() == this->address_) { | ||||
|         this->publish_state(true); | ||||
|         this->found_ = true; | ||||
|         return true; | ||||
|       } | ||||
|     } else { | ||||
|       for (auto uuid : device.get_service_uuids()) { | ||||
|         if (this->uuid_ == uuid) { | ||||
|           this->publish_state(device.get_rssi()); | ||||
|           this->found_ = true; | ||||
|           return true; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| @@ -32,7 +57,9 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff, | ||||
|  | ||||
|  protected: | ||||
|   bool found_{false}; | ||||
|   bool by_address_{false}; | ||||
|   uint64_t address_; | ||||
|   esp32_ble_tracker::ESPBTUUID uuid_; | ||||
| }; | ||||
|  | ||||
| }  // namespace ble_presence | ||||
|   | ||||
| @@ -11,17 +11,42 @@ namespace ble_rssi { | ||||
|  | ||||
| class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component { | ||||
|  public: | ||||
|   void set_address(uint64_t address) { address_ = address; } | ||||
|   void set_address(uint64_t address) { | ||||
|     this->by_address_ = true; | ||||
|     this->address_ = address; | ||||
|   } | ||||
|   void set_service_uuid16(uint16_t uuid) { | ||||
|     this->by_address_ = false; | ||||
|     this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid); | ||||
|   } | ||||
|   void set_service_uuid32(uint32_t uuid) { | ||||
|     this->by_address_ = false; | ||||
|     this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint32(uuid); | ||||
|   } | ||||
|   void set_service_uuid128(uint8_t *uuid) { | ||||
|     this->by_address_ = false; | ||||
|     this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid); | ||||
|   } | ||||
|   void on_scan_end() override { | ||||
|     if (!this->found_) | ||||
|       this->publish_state(NAN); | ||||
|     this->found_ = false; | ||||
|   } | ||||
|   bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { | ||||
|     if (device.address_uint64() == this->address_) { | ||||
|       this->publish_state(device.get_rssi()); | ||||
|       this->found_ = true; | ||||
|       return true; | ||||
|     if (this->by_address_) { | ||||
|       if (device.address_uint64() == this->address_) { | ||||
|         this->publish_state(device.get_rssi()); | ||||
|         this->found_ = true; | ||||
|         return true; | ||||
|       } | ||||
|     } else { | ||||
|       for (auto uuid : device.get_service_uuids()) { | ||||
|         if (this->uuid_ == uuid) { | ||||
|           this->publish_state(device.get_rssi()); | ||||
|           this->found_ = true; | ||||
|           return true; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| @@ -30,7 +55,9 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi | ||||
|  | ||||
|  protected: | ||||
|   bool found_{false}; | ||||
|   bool by_address_{false}; | ||||
|   uint64_t address_; | ||||
|   esp32_ble_tracker::ESPBTUUID uuid_; | ||||
| }; | ||||
|  | ||||
| }  // namespace ble_rssi | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import sensor, esp32_ble_tracker | ||||
| from esphome.const import CONF_MAC_ADDRESS, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL | ||||
| from esphome.const import CONF_SERVICE_UUID, CONF_MAC_ADDRESS, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL | ||||
|  | ||||
| DEPENDENCIES = ['esp32_ble_tracker'] | ||||
|  | ||||
| @@ -9,10 +9,12 @@ ble_rssi_ns = cg.esphome_ns.namespace('ble_rssi') | ||||
| BLERSSISensor = ble_rssi_ns.class_('BLERSSISensor', sensor.Sensor, cg.Component, | ||||
|                                    esp32_ble_tracker.ESPBTDeviceListener) | ||||
|  | ||||
| CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({ | ||||
| CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({ | ||||
|     cv.GenerateID(): cv.declare_id(BLERSSISensor), | ||||
|     cv.Required(CONF_MAC_ADDRESS): cv.mac_address, | ||||
| }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) | ||||
|     cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, | ||||
|     cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, | ||||
| }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend( | ||||
|     cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID)) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
| @@ -21,4 +23,14 @@ def to_code(config): | ||||
|     yield esp32_ble_tracker.register_ble_device(var, config) | ||||
|     yield sensor.register_sensor(var, config) | ||||
|  | ||||
|     cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) | ||||
|     if CONF_MAC_ADDRESS in config: | ||||
|         cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) | ||||
|  | ||||
|     if CONF_SERVICE_UUID in config: | ||||
|         if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): | ||||
|             cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) | ||||
|         elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format): | ||||
|             cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) | ||||
|         elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): | ||||
|             uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID]) | ||||
|             cg.add(var.set_service_uuid128(uuid128)) | ||||
|   | ||||
							
								
								
									
										0
									
								
								esphome/components/ble_scanner/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/ble_scanner/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										16
									
								
								esphome/components/ble_scanner/ble_scanner.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								esphome/components/ble_scanner/ble_scanner.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| #include "ble_scanner.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| #ifdef ARDUINO_ARCH_ESP32 | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ble_scanner { | ||||
|  | ||||
| static const char *TAG = "ble_scanner"; | ||||
|  | ||||
| void BLEScanner::dump_config() { LOG_TEXT_SENSOR("", "BLE Scanner", this); } | ||||
|  | ||||
| }  // namespace ble_scanner | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										38
									
								
								esphome/components/ble_scanner/ble_scanner.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								esphome/components/ble_scanner/ble_scanner.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <ctime> | ||||
| #include <string> | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | ||||
| #include "esphome/components/text_sensor/text_sensor.h" | ||||
|  | ||||
| #ifdef ARDUINO_ARCH_ESP32 | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ble_scanner { | ||||
|  | ||||
| class BLEScanner : public text_sensor::TextSensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component { | ||||
|  public: | ||||
|   bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { | ||||
|     this->publish_state("{\"timestamp\":" + to_string(::time(NULL)) + | ||||
|                         "," | ||||
|                         "\"address\":\"" + | ||||
|                         device.address_str() + | ||||
|                         "\"," | ||||
|                         "\"rssi\":" + | ||||
|                         to_string(device.get_rssi()) + | ||||
|                         "," | ||||
|                         "\"name\":\"" + | ||||
|                         device.get_name() + "\"}"); | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|   void dump_config() override; | ||||
|   float get_setup_priority() const override { return setup_priority::DATA; } | ||||
| }; | ||||
|  | ||||
| }  // namespace ble_scanner | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										22
									
								
								esphome/components/ble_scanner/text_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								esphome/components/ble_scanner/text_sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import text_sensor, esp32_ble_tracker | ||||
| from esphome.const import CONF_ID | ||||
|  | ||||
| DEPENDENCIES = ['esp32_ble_tracker'] | ||||
|  | ||||
| ble_scanner_ns = cg.esphome_ns.namespace('ble_scanner') | ||||
| BLEScanner = ble_scanner_ns.class_('BLEScanner', text_sensor.TextSensor, cg.Component, | ||||
|                                    esp32_ble_tracker.ESPBTDeviceListener) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All(text_sensor.TEXT_SENSOR_SCHEMA.extend({ | ||||
|     cv.GenerateID(): cv.declare_id(BLEScanner), | ||||
| }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend( | ||||
|     cv.COMPONENT_SCHEMA)) | ||||
|  | ||||
|  | ||||
| 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) | ||||
| @@ -146,7 +146,7 @@ void BME280Component::dump_config() { | ||||
|       ESP_LOGE(TAG, "Communication with BME280 failed!"); | ||||
|       break; | ||||
|     case WRONG_CHIP_ID: | ||||
|       ESP_LOGE(TAG, "BMP280 has wrong chip ID! Is it a BMP280?"); | ||||
|       ESP_LOGE(TAG, "BME280 has wrong chip ID! Is it a BME280?"); | ||||
|       break; | ||||
|     case NONE: | ||||
|     default: | ||||
| @@ -172,7 +172,7 @@ void BME280Component::update() { | ||||
|   uint8_t meas_register = 0; | ||||
|   meas_register |= (this->temperature_oversampling_ & 0b111) << 5; | ||||
|   meas_register |= (this->pressure_oversampling_ & 0b111) << 2; | ||||
|   meas_register |= 0b01;  // Forced mode | ||||
|   meas_register |= BME280_MODE_FORCED; | ||||
|   if (!this->write_byte(BME280_REGISTER_CONTROL, meas_register)) { | ||||
|     this->status_set_warning(); | ||||
|     return; | ||||
|   | ||||
| @@ -7,6 +7,7 @@ from esphome.core import coroutine_with_priority | ||||
|  | ||||
| AUTO_LOAD = ['web_server_base'] | ||||
| DEPENDENCIES = ['wifi'] | ||||
| CODEOWNERS = ['@OttoWinter'] | ||||
|  | ||||
| captive_portal_ns = cg.esphome_ns.namespace('captive_portal') | ||||
| CaptivePortal = captive_portal_ns.class_('CaptivePortal', cg.Component) | ||||
|   | ||||
| @@ -5,11 +5,12 @@ from esphome.components import mqtt | ||||
| from esphome.const import CONF_AWAY, CONF_ID, CONF_INTERNAL, CONF_MAX_TEMPERATURE, \ | ||||
|     CONF_MIN_TEMPERATURE, CONF_MODE, CONF_TARGET_TEMPERATURE, \ | ||||
|     CONF_TARGET_TEMPERATURE_HIGH, CONF_TARGET_TEMPERATURE_LOW, CONF_TEMPERATURE_STEP, CONF_VISUAL, \ | ||||
|     CONF_MQTT_ID, CONF_NAME | ||||
|     CONF_MQTT_ID, CONF_NAME, CONF_FAN_MODE, CONF_SWING_MODE | ||||
| from esphome.core import CORE, coroutine, coroutine_with_priority | ||||
|  | ||||
| IS_PLATFORM_COMPONENT = True | ||||
|  | ||||
| CODEOWNERS = ['@esphome/core'] | ||||
| climate_ns = cg.esphome_ns.namespace('climate') | ||||
|  | ||||
| Climate = climate_ns.class_('Climate', cg.Nameable) | ||||
| @@ -22,9 +23,35 @@ CLIMATE_MODES = { | ||||
|     'AUTO': ClimateMode.CLIMATE_MODE_AUTO, | ||||
|     'COOL': ClimateMode.CLIMATE_MODE_COOL, | ||||
|     'HEAT': ClimateMode.CLIMATE_MODE_HEAT, | ||||
|     'DRY': ClimateMode.CLIMATE_MODE_DRY, | ||||
|     'FAN_ONLY': ClimateMode.CLIMATE_MODE_FAN_ONLY, | ||||
| } | ||||
| validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True) | ||||
|  | ||||
| ClimateFanMode = climate_ns.enum('ClimateFanMode') | ||||
| CLIMATE_FAN_MODES = { | ||||
|     'ON': ClimateFanMode.CLIMATE_FAN_ON, | ||||
|     'OFF': ClimateFanMode.CLIMATE_FAN_OFF, | ||||
|     'AUTO': ClimateFanMode.CLIMATE_FAN_AUTO, | ||||
|     'LOW': ClimateFanMode.CLIMATE_FAN_LOW, | ||||
|     'MEDIUM': ClimateFanMode.CLIMATE_FAN_MEDIUM, | ||||
|     'HIGH': ClimateFanMode.CLIMATE_FAN_HIGH, | ||||
|     'MIDDLE': ClimateFanMode.CLIMATE_FAN_MIDDLE, | ||||
|     'FOCUS': ClimateFanMode.CLIMATE_FAN_FOCUS, | ||||
|     'DIFFUSE': ClimateFanMode.CLIMATE_FAN_DIFFUSE, | ||||
| } | ||||
|  | ||||
| validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True) | ||||
| validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True) | ||||
|  | ||||
| ClimateSwingMode = climate_ns.enum('ClimateSwingMode') | ||||
| CLIMATE_SWING_MODES = { | ||||
|     'OFF': ClimateSwingMode.CLIMATE_SWING_OFF, | ||||
|     'BOTH': ClimateSwingMode.CLIMATE_SWING_BOTH, | ||||
|     'VERTICAL': ClimateSwingMode.CLIMATE_SWING_VERTICAL, | ||||
|     'HORIZONTAL': ClimateSwingMode.CLIMATE_SWING_HORIZONTAL, | ||||
| } | ||||
|  | ||||
| validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True) | ||||
|  | ||||
| # Actions | ||||
| ControlAction = climate_ns.class_('ControlAction', automation.Action) | ||||
| @@ -74,6 +101,8 @@ 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.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode), | ||||
| }) | ||||
|  | ||||
|  | ||||
| @@ -96,6 +125,12 @@ def climate_control_to_code(config, action_id, template_arg, args): | ||||
|     if CONF_AWAY in config: | ||||
|         template_ = yield 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) | ||||
|         cg.add(var.set_fan_mode(template_)) | ||||
|     if CONF_SWING_MODE in config: | ||||
|         template_ = yield cg.templatable(config[CONF_SWING_MODE], args, ClimateSwingMode) | ||||
|         cg.add(var.set_swing_mode(template_)) | ||||
|     yield var | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -15,6 +15,8 @@ template<typename... Ts> class ControlAction : public Action<Ts...> { | ||||
|   TEMPLATABLE_VALUE(float, target_temperature_low) | ||||
|   TEMPLATABLE_VALUE(float, target_temperature_high) | ||||
|   TEMPLATABLE_VALUE(bool, away) | ||||
|   TEMPLATABLE_VALUE(ClimateFanMode, fan_mode) | ||||
|   TEMPLATABLE_VALUE(ClimateSwingMode, swing_mode) | ||||
|  | ||||
|   void play(Ts... x) override { | ||||
|     auto call = this->climate_->make_call(); | ||||
| @@ -23,6 +25,8 @@ template<typename... Ts> class ControlAction : public Action<Ts...> { | ||||
|     call.set_target_temperature_low(this->target_temperature_low_.optional_value(x...)); | ||||
|     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_swing_mode(this->swing_mode_.optional_value(x...)); | ||||
|     call.perform(); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,14 @@ void ClimateCall::perform() { | ||||
|     const char *mode_s = climate_mode_to_string(*this->mode_); | ||||
|     ESP_LOGD(TAG, "  Mode: %s", mode_s); | ||||
|   } | ||||
|   if (this->fan_mode_.has_value()) { | ||||
|     const char *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_); | ||||
|     ESP_LOGD(TAG, "  Fan: %s", fan_mode_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); | ||||
|   } | ||||
|   if (this->target_temperature_.has_value()) { | ||||
|     ESP_LOGD(TAG, "  Target Temperature: %.2f", *this->target_temperature_); | ||||
|   } | ||||
| @@ -36,6 +44,20 @@ void ClimateCall::validate_() { | ||||
|       this->mode_.reset(); | ||||
|     } | ||||
|   } | ||||
|   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->swing_mode_.has_value()) { | ||||
|     auto swing_mode = *this->swing_mode_; | ||||
|     if (!traits.supports_swing_mode(swing_mode)) { | ||||
|       ESP_LOGW(TAG, "  Swing Mode %s is not supported by this device!", climate_swing_mode_to_string(swing_mode)); | ||||
|       this->swing_mode_.reset(); | ||||
|     } | ||||
|   } | ||||
|   if (this->target_temperature_.has_value()) { | ||||
|     auto target = *this->target_temperature_; | ||||
|     if (traits.get_supports_two_point_target_temperature()) { | ||||
| @@ -91,11 +113,63 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) { | ||||
|     this->set_mode(CLIMATE_MODE_COOL); | ||||
|   } else if (str_equals_case_insensitive(mode, "HEAT")) { | ||||
|     this->set_mode(CLIMATE_MODE_HEAT); | ||||
|   } else if (str_equals_case_insensitive(mode, "FAN_ONLY")) { | ||||
|     this->set_mode(CLIMATE_MODE_FAN_ONLY); | ||||
|   } else if (str_equals_case_insensitive(mode, "DRY")) { | ||||
|     this->set_mode(CLIMATE_MODE_DRY); | ||||
|   } else { | ||||
|     ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode.c_str()); | ||||
|   } | ||||
|   return *this; | ||||
| } | ||||
| ClimateCall &ClimateCall::set_fan_mode(ClimateFanMode fan_mode) { | ||||
|   this->fan_mode_ = fan_mode; | ||||
|   return *this; | ||||
| } | ||||
| ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) { | ||||
|   if (str_equals_case_insensitive(fan_mode, "ON")) { | ||||
|     this->set_fan_mode(CLIMATE_FAN_ON); | ||||
|   } else if (str_equals_case_insensitive(fan_mode, "OFF")) { | ||||
|     this->set_fan_mode(CLIMATE_FAN_OFF); | ||||
|   } else if (str_equals_case_insensitive(fan_mode, "AUTO")) { | ||||
|     this->set_fan_mode(CLIMATE_FAN_AUTO); | ||||
|   } else if (str_equals_case_insensitive(fan_mode, "LOW")) { | ||||
|     this->set_fan_mode(CLIMATE_FAN_LOW); | ||||
|   } else if (str_equals_case_insensitive(fan_mode, "MEDIUM")) { | ||||
|     this->set_fan_mode(CLIMATE_FAN_MEDIUM); | ||||
|   } else if (str_equals_case_insensitive(fan_mode, "HIGH")) { | ||||
|     this->set_fan_mode(CLIMATE_FAN_HIGH); | ||||
|   } else if (str_equals_case_insensitive(fan_mode, "MIDDLE")) { | ||||
|     this->set_fan_mode(CLIMATE_FAN_MIDDLE); | ||||
|   } else if (str_equals_case_insensitive(fan_mode, "FOCUS")) { | ||||
|     this->set_fan_mode(CLIMATE_FAN_FOCUS); | ||||
|   } 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()); | ||||
|   } | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| ClimateCall &ClimateCall::set_swing_mode(ClimateSwingMode swing_mode) { | ||||
|   this->swing_mode_ = swing_mode; | ||||
|   return *this; | ||||
| } | ||||
| ClimateCall &ClimateCall::set_swing_mode(const std::string &swing_mode) { | ||||
|   if (str_equals_case_insensitive(swing_mode, "OFF")) { | ||||
|     this->set_swing_mode(CLIMATE_SWING_OFF); | ||||
|   } else if (str_equals_case_insensitive(swing_mode, "BOTH")) { | ||||
|     this->set_swing_mode(CLIMATE_SWING_BOTH); | ||||
|   } else if (str_equals_case_insensitive(swing_mode, "VERTICAL")) { | ||||
|     this->set_swing_mode(CLIMATE_SWING_VERTICAL); | ||||
|   } else if (str_equals_case_insensitive(swing_mode, "HORIZONTAL")) { | ||||
|     this->set_swing_mode(CLIMATE_SWING_HORIZONTAL); | ||||
|   } else { | ||||
|     ESP_LOGW(TAG, "'%s' - Unrecognized swing mode %s", this->parent_->get_name().c_str(), swing_mode.c_str()); | ||||
|   } | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| ClimateCall &ClimateCall::set_target_temperature(float target_temperature) { | ||||
|   this->target_temperature_ = target_temperature; | ||||
|   return *this; | ||||
| @@ -113,6 +187,8 @@ const optional<float> &ClimateCall::get_target_temperature() const { return this | ||||
| const optional<float> &ClimateCall::get_target_temperature_low() const { return this->target_temperature_low_; } | ||||
| 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<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; } | ||||
| ClimateCall &ClimateCall::set_away(bool away) { | ||||
|   this->away_ = away; | ||||
|   return *this; | ||||
| @@ -137,6 +213,14 @@ ClimateCall &ClimateCall::set_mode(optional<ClimateMode> mode) { | ||||
|   this->mode_ = mode; | ||||
|   return *this; | ||||
| } | ||||
| ClimateCall &ClimateCall::set_fan_mode(optional<ClimateFanMode> fan_mode) { | ||||
|   this->fan_mode_ = fan_mode; | ||||
|   return *this; | ||||
| } | ||||
| ClimateCall &ClimateCall::set_swing_mode(optional<ClimateSwingMode> swing_mode) { | ||||
|   this->swing_mode_ = swing_mode; | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| void Climate::add_on_state_callback(std::function<void()> &&callback) { | ||||
|   this->state_callback_.add(std::move(callback)); | ||||
| @@ -165,6 +249,12 @@ 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_swing_modes()) { | ||||
|     state.swing_mode = this->swing_mode; | ||||
|   } | ||||
|  | ||||
|   this->rtc_.save(&state); | ||||
| } | ||||
| @@ -176,6 +266,12 @@ 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_swing_modes()) { | ||||
|     ESP_LOGD(TAG, "  Swing Mode: %s", climate_swing_mode_to_string(this->swing_mode)); | ||||
|   } | ||||
|   if (traits.get_supports_current_temperature()) { | ||||
|     ESP_LOGD(TAG, "  Current Temperature: %.2f°C", this->current_temperature); | ||||
|   } | ||||
| @@ -236,6 +332,12 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) { | ||||
|   if (traits.get_supports_away()) { | ||||
|     call.set_away(this->away); | ||||
|   } | ||||
|   if (traits.get_supports_fan_modes()) { | ||||
|     call.set_fan_mode(this->fan_mode); | ||||
|   } | ||||
|   if (traits.get_supports_swing_modes()) { | ||||
|     call.set_swing_mode(this->swing_mode); | ||||
|   } | ||||
|   return call; | ||||
| } | ||||
| void ClimateDeviceRestoreState::apply(Climate *climate) { | ||||
| @@ -250,6 +352,12 @@ void ClimateDeviceRestoreState::apply(Climate *climate) { | ||||
|   if (traits.get_supports_away()) { | ||||
|     climate->away = this->away; | ||||
|   } | ||||
|   if (traits.get_supports_fan_modes()) { | ||||
|     climate->fan_mode = this->fan_mode; | ||||
|   } | ||||
|   if (traits.get_supports_swing_modes()) { | ||||
|     climate->swing_mode = this->swing_mode; | ||||
|   } | ||||
|   climate->publish_state(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -64,6 +64,18 @@ class ClimateCall { | ||||
|   ClimateCall &set_target_temperature_high(optional<float> target_temperature_high); | ||||
|   ClimateCall &set_away(bool away); | ||||
|   ClimateCall &set_away(optional<bool> away); | ||||
|   /// Set the fan mode of the climate device. | ||||
|   ClimateCall &set_fan_mode(ClimateFanMode fan_mode); | ||||
|   /// Set the fan mode of the climate device. | ||||
|   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 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); | ||||
|  | ||||
|   void perform(); | ||||
|  | ||||
| @@ -72,6 +84,8 @@ class ClimateCall { | ||||
|   const optional<float> &get_target_temperature_low() const; | ||||
|   const optional<float> &get_target_temperature_high() const; | ||||
|   const optional<bool> &get_away() const; | ||||
|   const optional<ClimateFanMode> &get_fan_mode() const; | ||||
|   const optional<ClimateSwingMode> &get_swing_mode() const; | ||||
|  | ||||
|  protected: | ||||
|   void validate_(); | ||||
| @@ -82,12 +96,16 @@ class ClimateCall { | ||||
|   optional<float> target_temperature_low_; | ||||
|   optional<float> target_temperature_high_; | ||||
|   optional<bool> away_; | ||||
|   optional<ClimateFanMode> fan_mode_; | ||||
|   optional<ClimateSwingMode> swing_mode_; | ||||
| }; | ||||
|  | ||||
| /// Struct used to save the state of the climate device in restore memory. | ||||
| struct ClimateDeviceRestoreState { | ||||
|   ClimateMode mode; | ||||
|   bool away; | ||||
|   ClimateFanMode fan_mode; | ||||
|   ClimateSwingMode swing_mode; | ||||
|   union { | ||||
|     float target_temperature; | ||||
|     struct { | ||||
| @@ -149,6 +167,12 @@ class Climate : public Nameable { | ||||
|    */ | ||||
|   bool away{false}; | ||||
|  | ||||
|   /// The active fan mode of the climate device. | ||||
|   ClimateFanMode fan_mode; | ||||
|  | ||||
|   /// The active swing mode of the climate device. | ||||
|   ClimateSwingMode swing_mode; | ||||
|  | ||||
|   /** 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. | ||||
|    * | ||||
|   | ||||
| @@ -13,6 +13,10 @@ const char *climate_mode_to_string(ClimateMode mode) { | ||||
|       return "COOL"; | ||||
|     case CLIMATE_MODE_HEAT: | ||||
|       return "HEAT"; | ||||
|     case CLIMATE_MODE_FAN_ONLY: | ||||
|       return "FAN_ONLY"; | ||||
|     case CLIMATE_MODE_DRY: | ||||
|       return "DRY"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| @@ -25,6 +29,52 @@ const char *climate_action_to_string(ClimateAction action) { | ||||
|       return "COOLING"; | ||||
|     case CLIMATE_ACTION_HEATING: | ||||
|       return "HEATING"; | ||||
|     case CLIMATE_ACTION_IDLE: | ||||
|       return "IDLE"; | ||||
|     case CLIMATE_ACTION_DRYING: | ||||
|       return "DRYING"; | ||||
|     case CLIMATE_ACTION_FAN: | ||||
|       return "FAN"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| const char *climate_fan_mode_to_string(ClimateFanMode fan_mode) { | ||||
|   switch (fan_mode) { | ||||
|     case climate::CLIMATE_FAN_ON: | ||||
|       return "ON"; | ||||
|     case climate::CLIMATE_FAN_OFF: | ||||
|       return "OFF"; | ||||
|     case climate::CLIMATE_FAN_AUTO: | ||||
|       return "AUTO"; | ||||
|     case climate::CLIMATE_FAN_LOW: | ||||
|       return "LOW"; | ||||
|     case climate::CLIMATE_FAN_MEDIUM: | ||||
|       return "MEDIUM"; | ||||
|     case climate::CLIMATE_FAN_HIGH: | ||||
|       return "HIGH"; | ||||
|     case climate::CLIMATE_FAN_MIDDLE: | ||||
|       return "MIDDLE"; | ||||
|     case climate::CLIMATE_FAN_FOCUS: | ||||
|       return "FOCUS"; | ||||
|     case climate::CLIMATE_FAN_DIFFUSE: | ||||
|       return "DIFFUSE"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| const char *climate_swing_mode_to_string(ClimateSwingMode swing_mode) { | ||||
|   switch (swing_mode) { | ||||
|     case climate::CLIMATE_SWING_OFF: | ||||
|       return "OFF"; | ||||
|     case climate::CLIMATE_SWING_BOTH: | ||||
|       return "BOTH"; | ||||
|     case climate::CLIMATE_SWING_VERTICAL: | ||||
|       return "VERTICAL"; | ||||
|     case climate::CLIMATE_SWING_HORIZONTAL: | ||||
|       return "HORIZONTAL"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
|   | ||||
| @@ -15,6 +15,10 @@ enum ClimateMode : uint8_t { | ||||
|   CLIMATE_MODE_COOL = 2, | ||||
|   /// The climate device is manually set to heat mode (not in auto mode!) | ||||
|   CLIMATE_MODE_HEAT = 3, | ||||
|   /// The climate device is manually set to fan only mode | ||||
|   CLIMATE_MODE_FAN_ONLY = 4, | ||||
|   /// The climate device is manually set to dry mode | ||||
|   CLIMATE_MODE_DRY = 5, | ||||
| }; | ||||
|  | ||||
| /// Enum for the current action of the climate device. Values match those of ClimateMode. | ||||
| @@ -25,11 +29,59 @@ enum ClimateAction : uint8_t { | ||||
|   CLIMATE_ACTION_COOLING = 2, | ||||
|   /// The climate device is actively heating (usually in heat or auto mode) | ||||
|   CLIMATE_ACTION_HEATING = 3, | ||||
|   /// The climate device is idle (monitoring climate but no action needed) | ||||
|   CLIMATE_ACTION_IDLE = 4, | ||||
|   /// The climate device is drying (either mode DRY or AUTO) | ||||
|   CLIMATE_ACTION_DRYING = 5, | ||||
|   /// The climate device is in fan only mode (either mode FAN_ONLY or AUTO) | ||||
|   CLIMATE_ACTION_FAN = 6, | ||||
| }; | ||||
|  | ||||
| /// Enum for all modes a climate fan can be in | ||||
| enum ClimateFanMode : uint8_t { | ||||
|   /// The fan mode is set to On | ||||
|   CLIMATE_FAN_ON = 0, | ||||
|   /// The fan mode is set to Off | ||||
|   CLIMATE_FAN_OFF = 1, | ||||
|   /// The fan mode is set to Auto | ||||
|   CLIMATE_FAN_AUTO = 2, | ||||
|   /// The fan mode is set to Low | ||||
|   CLIMATE_FAN_LOW = 3, | ||||
|   /// The fan mode is set to Medium | ||||
|   CLIMATE_FAN_MEDIUM = 4, | ||||
|   /// The fan mode is set to High | ||||
|   CLIMATE_FAN_HIGH = 5, | ||||
|   /// The fan mode is set to Middle | ||||
|   CLIMATE_FAN_MIDDLE = 6, | ||||
|   /// The fan mode is set to Focus | ||||
|   CLIMATE_FAN_FOCUS = 7, | ||||
|   /// The fan mode is set to Diffuse | ||||
|   CLIMATE_FAN_DIFFUSE = 8, | ||||
| }; | ||||
|  | ||||
| /// Enum for all modes a climate swing can be in | ||||
| enum ClimateSwingMode : uint8_t { | ||||
|   /// The sing mode is set to Off | ||||
|   CLIMATE_SWING_OFF = 0, | ||||
|   /// The fan mode is set to Both | ||||
|   CLIMATE_SWING_BOTH = 1, | ||||
|   /// The fan mode is set to Vertical | ||||
|   CLIMATE_SWING_VERTICAL = 2, | ||||
|   /// The fan mode is set to Horizontal | ||||
|   CLIMATE_SWING_HORIZONTAL = 3, | ||||
| }; | ||||
|  | ||||
| /// Convert the given ClimateMode to a human-readable string. | ||||
| const char *climate_mode_to_string(ClimateMode mode); | ||||
|  | ||||
| /// Convert the given ClimateAction to a human-readable string. | ||||
| const char *climate_action_to_string(ClimateAction action); | ||||
|  | ||||
| /// Convert the given ClimateFanMode to a human-readable string. | ||||
| 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); | ||||
|  | ||||
| }  // namespace climate | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -14,6 +14,10 @@ bool ClimateTraits::supports_mode(ClimateMode mode) const { | ||||
|       return this->supports_cool_mode_; | ||||
|     case CLIMATE_MODE_HEAT: | ||||
|       return this->supports_heat_mode_; | ||||
|     case CLIMATE_MODE_FAN_ONLY: | ||||
|       return this->supports_fan_only_mode_; | ||||
|     case CLIMATE_MODE_DRY: | ||||
|       return this->supports_dry_mode_; | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -29,6 +33,10 @@ void ClimateTraits::set_supports_two_point_target_temperature(bool supports_two_ | ||||
| void ClimateTraits::set_supports_auto_mode(bool supports_auto_mode) { supports_auto_mode_ = supports_auto_mode; } | ||||
| void ClimateTraits::set_supports_cool_mode(bool supports_cool_mode) { supports_cool_mode_ = supports_cool_mode; } | ||||
| void ClimateTraits::set_supports_heat_mode(bool supports_heat_mode) { supports_heat_mode_ = supports_heat_mode; } | ||||
| void ClimateTraits::set_supports_fan_only_mode(bool supports_fan_only_mode) { | ||||
|   supports_fan_only_mode_ = supports_fan_only_mode; | ||||
| } | ||||
| void ClimateTraits::set_supports_dry_mode(bool supports_dry_mode) { supports_dry_mode_ = supports_dry_mode; } | ||||
| void ClimateTraits::set_supports_away(bool supports_away) { supports_away_ = supports_away; } | ||||
| void ClimateTraits::set_supports_action(bool supports_action) { supports_action_ = supports_action; } | ||||
| float ClimateTraits::get_visual_min_temperature() const { return visual_min_temperature_; } | ||||
| @@ -55,5 +63,91 @@ void ClimateTraits::set_visual_temperature_step(float temperature_step) { visual | ||||
| bool ClimateTraits::get_supports_away() const { return supports_away_; } | ||||
| bool ClimateTraits::get_supports_action() const { return supports_action_; } | ||||
|  | ||||
| void ClimateTraits::set_supports_fan_mode_on(bool supports_fan_mode_on) { | ||||
|   this->supports_fan_mode_on_ = supports_fan_mode_on; | ||||
| } | ||||
| void ClimateTraits::set_supports_fan_mode_off(bool supports_fan_mode_off) { | ||||
|   this->supports_fan_mode_off_ = supports_fan_mode_off; | ||||
| } | ||||
| void ClimateTraits::set_supports_fan_mode_auto(bool supports_fan_mode_auto) { | ||||
|   this->supports_fan_mode_auto_ = supports_fan_mode_auto; | ||||
| } | ||||
| void ClimateTraits::set_supports_fan_mode_low(bool supports_fan_mode_low) { | ||||
|   this->supports_fan_mode_low_ = supports_fan_mode_low; | ||||
| } | ||||
| void ClimateTraits::set_supports_fan_mode_medium(bool supports_fan_mode_medium) { | ||||
|   this->supports_fan_mode_medium_ = supports_fan_mode_medium; | ||||
| } | ||||
| void ClimateTraits::set_supports_fan_mode_high(bool supports_fan_mode_high) { | ||||
|   this->supports_fan_mode_high_ = supports_fan_mode_high; | ||||
| } | ||||
| void ClimateTraits::set_supports_fan_mode_middle(bool supports_fan_mode_middle) { | ||||
|   this->supports_fan_mode_middle_ = supports_fan_mode_middle; | ||||
| } | ||||
| void ClimateTraits::set_supports_fan_mode_focus(bool supports_fan_mode_focus) { | ||||
|   this->supports_fan_mode_focus_ = supports_fan_mode_focus; | ||||
| } | ||||
| void ClimateTraits::set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse) { | ||||
|   this->supports_fan_mode_diffuse_ = supports_fan_mode_diffuse; | ||||
| } | ||||
| bool ClimateTraits::supports_fan_mode(ClimateFanMode fan_mode) const { | ||||
|   switch (fan_mode) { | ||||
|     case climate::CLIMATE_FAN_ON: | ||||
|       return this->supports_fan_mode_on_; | ||||
|     case climate::CLIMATE_FAN_OFF: | ||||
|       return this->supports_fan_mode_off_; | ||||
|     case climate::CLIMATE_FAN_AUTO: | ||||
|       return this->supports_fan_mode_auto_; | ||||
|     case climate::CLIMATE_FAN_LOW: | ||||
|       return this->supports_fan_mode_low_; | ||||
|     case climate::CLIMATE_FAN_MEDIUM: | ||||
|       return this->supports_fan_mode_medium_; | ||||
|     case climate::CLIMATE_FAN_HIGH: | ||||
|       return this->supports_fan_mode_high_; | ||||
|     case climate::CLIMATE_FAN_MIDDLE: | ||||
|       return this->supports_fan_mode_middle_; | ||||
|     case climate::CLIMATE_FAN_FOCUS: | ||||
|       return this->supports_fan_mode_focus_; | ||||
|     case climate::CLIMATE_FAN_DIFFUSE: | ||||
|       return this->supports_fan_mode_diffuse_; | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ClimateTraits::get_supports_fan_modes() const { | ||||
|   return this->supports_fan_mode_on_ || this->supports_fan_mode_off_ || this->supports_fan_mode_auto_ || | ||||
|          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_supports_swing_mode_off(bool supports_swing_mode_off) { | ||||
|   this->supports_swing_mode_off_ = supports_swing_mode_off; | ||||
| } | ||||
| void ClimateTraits::set_supports_swing_mode_both(bool supports_swing_mode_both) { | ||||
|   this->supports_swing_mode_both_ = supports_swing_mode_both; | ||||
| } | ||||
| void ClimateTraits::set_supports_swing_mode_vertical(bool supports_swing_mode_vertical) { | ||||
|   this->supports_swing_mode_vertical_ = supports_swing_mode_vertical; | ||||
| } | ||||
| void ClimateTraits::set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal) { | ||||
|   this->supports_swing_mode_horizontal_ = supports_swing_mode_horizontal; | ||||
| } | ||||
| bool ClimateTraits::supports_swing_mode(ClimateSwingMode swing_mode) const { | ||||
|   switch (swing_mode) { | ||||
|     case climate::CLIMATE_SWING_OFF: | ||||
|       return this->supports_swing_mode_off_; | ||||
|     case climate::CLIMATE_SWING_BOTH: | ||||
|       return this->supports_swing_mode_both_; | ||||
|     case climate::CLIMATE_SWING_VERTICAL: | ||||
|       return this->supports_swing_mode_vertical_; | ||||
|     case climate::CLIMATE_SWING_HORIZONTAL: | ||||
|       return this->supports_swing_mode_horizontal_; | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ClimateTraits::get_supports_swing_modes() const { | ||||
|   return this->supports_swing_mode_off_ || this->supports_swing_mode_both_ || supports_swing_mode_vertical_ || | ||||
|          supports_swing_mode_horizontal_; | ||||
| } | ||||
| }  // namespace climate | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -21,10 +21,16 @@ namespace climate { | ||||
|  *    - auto mode (automatic control) | ||||
|  *    - cool mode (lowers current temperature) | ||||
|  *    - heat mode (increases current temperature) | ||||
|  *    - dry mode (removes humidity from air) | ||||
|  *    - fan mode (only turns on fan) | ||||
|  *  - supports away - away mode means that the climate device supports two different | ||||
|  *      target temperature settings: one target temp setting for "away" mode and one for non-away mode. | ||||
|  *  - supports action - if the climate device supports reporting the active | ||||
|  *    current action of the device with the action property. | ||||
|  *  - supports fan modes - optionally, if it has a fan which can be configured in different ways: | ||||
|  *    - on, off, auto, high, medium, low, middle, focus, diffuse | ||||
|  *  - supports swing modes - optionally, if it has a swing which can be configured in different ways: | ||||
|  *    - off, both, vertical, horizontal | ||||
|  * | ||||
|  * This class also contains static data for the climate device display: | ||||
|  *  - visual min/max temperature - tells the frontend what range of temperatures the climate device | ||||
| @@ -41,11 +47,30 @@ class ClimateTraits { | ||||
|   void set_supports_auto_mode(bool supports_auto_mode); | ||||
|   void set_supports_cool_mode(bool supports_cool_mode); | ||||
|   void set_supports_heat_mode(bool supports_heat_mode); | ||||
|   void set_supports_fan_only_mode(bool supports_fan_only_mode); | ||||
|   void set_supports_dry_mode(bool supports_dry_mode); | ||||
|   void set_supports_away(bool supports_away); | ||||
|   bool get_supports_away() const; | ||||
|   void set_supports_action(bool supports_action); | ||||
|   bool get_supports_action() const; | ||||
|   bool supports_mode(ClimateMode mode) const; | ||||
|   void set_supports_fan_mode_on(bool supports_fan_mode_on); | ||||
|   void set_supports_fan_mode_off(bool supports_fan_mode_off); | ||||
|   void set_supports_fan_mode_auto(bool supports_fan_mode_auto); | ||||
|   void set_supports_fan_mode_low(bool supports_fan_mode_low); | ||||
|   void set_supports_fan_mode_medium(bool supports_fan_mode_medium); | ||||
|   void set_supports_fan_mode_high(bool supports_fan_mode_high); | ||||
|   void set_supports_fan_mode_middle(bool supports_fan_mode_middle); | ||||
|   void set_supports_fan_mode_focus(bool supports_fan_mode_focus); | ||||
|   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_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); | ||||
|   void set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal); | ||||
|   bool supports_swing_mode(ClimateSwingMode swing_mode) const; | ||||
|   bool get_supports_swing_modes() const; | ||||
|  | ||||
|   float get_visual_min_temperature() const; | ||||
|   void set_visual_min_temperature(float visual_min_temperature); | ||||
| @@ -61,8 +86,23 @@ class ClimateTraits { | ||||
|   bool supports_auto_mode_{false}; | ||||
|   bool supports_cool_mode_{false}; | ||||
|   bool supports_heat_mode_{false}; | ||||
|   bool supports_fan_only_mode_{false}; | ||||
|   bool supports_dry_mode_{false}; | ||||
|   bool supports_away_{false}; | ||||
|   bool supports_action_{false}; | ||||
|   bool supports_fan_mode_on_{false}; | ||||
|   bool supports_fan_mode_off_{false}; | ||||
|   bool supports_fan_mode_auto_{false}; | ||||
|   bool supports_fan_mode_low_{false}; | ||||
|   bool supports_fan_mode_medium_{false}; | ||||
|   bool supports_fan_mode_high_{false}; | ||||
|   bool supports_fan_mode_middle_{false}; | ||||
|   bool supports_fan_mode_focus_{false}; | ||||
|   bool supports_fan_mode_diffuse_{false}; | ||||
|   bool supports_swing_mode_off_{false}; | ||||
|   bool supports_swing_mode_both_{false}; | ||||
|   bool supports_swing_mode_vertical_{false}; | ||||
|   bool supports_swing_mode_horizontal_{false}; | ||||
|  | ||||
|   float visual_min_temperature_{10}; | ||||
|   float visual_max_temperature_{30}; | ||||
|   | ||||
| @@ -6,6 +6,7 @@ from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR | ||||
| from esphome.core import coroutine | ||||
|  | ||||
| AUTO_LOAD = ['sensor', 'remote_base'] | ||||
| CODEOWNERS = ['@glmnet'] | ||||
|  | ||||
| climate_ir_ns = cg.esphome_ns.namespace('climate_ir') | ||||
| ClimateIR = climate_ir_ns.class_('ClimateIR', climate.Climate, cg.Component, | ||||
|   | ||||
| @@ -12,11 +12,60 @@ climate::ClimateTraits ClimateIR::traits() { | ||||
|   traits.set_supports_auto_mode(true); | ||||
|   traits.set_supports_cool_mode(this->supports_cool_); | ||||
|   traits.set_supports_heat_mode(this->supports_heat_); | ||||
|   traits.set_supports_dry_mode(this->supports_dry_); | ||||
|   traits.set_supports_fan_only_mode(this->supports_fan_only_); | ||||
|   traits.set_supports_two_point_target_temperature(false); | ||||
|   traits.set_supports_away(false); | ||||
|   traits.set_visual_min_temperature(this->minimum_temperature_); | ||||
|   traits.set_visual_max_temperature(this->maximum_temperature_); | ||||
|   traits.set_visual_temperature_step(this->temperature_step_); | ||||
|   for (auto fan_mode : this->fan_modes_) { | ||||
|     switch (fan_mode) { | ||||
|       case climate::CLIMATE_FAN_AUTO: | ||||
|         traits.set_supports_fan_mode_auto(true); | ||||
|         break; | ||||
|       case climate::CLIMATE_FAN_DIFFUSE: | ||||
|         traits.set_supports_fan_mode_diffuse(true); | ||||
|         break; | ||||
|       case climate::CLIMATE_FAN_FOCUS: | ||||
|         traits.set_supports_fan_mode_focus(true); | ||||
|         break; | ||||
|       case climate::CLIMATE_FAN_HIGH: | ||||
|         traits.set_supports_fan_mode_high(true); | ||||
|         break; | ||||
|       case climate::CLIMATE_FAN_LOW: | ||||
|         traits.set_supports_fan_mode_low(true); | ||||
|         break; | ||||
|       case climate::CLIMATE_FAN_MEDIUM: | ||||
|         traits.set_supports_fan_mode_medium(true); | ||||
|         break; | ||||
|       case climate::CLIMATE_FAN_MIDDLE: | ||||
|         traits.set_supports_fan_mode_middle(true); | ||||
|         break; | ||||
|       case climate::CLIMATE_FAN_OFF: | ||||
|         traits.set_supports_fan_mode_off(true); | ||||
|         break; | ||||
|       case climate::CLIMATE_FAN_ON: | ||||
|         traits.set_supports_fan_mode_on(true); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|   for (auto swing_mode : this->swing_modes_) { | ||||
|     switch (swing_mode) { | ||||
|       case climate::CLIMATE_SWING_OFF: | ||||
|         traits.set_supports_swing_mode_off(true); | ||||
|         break; | ||||
|       case climate::CLIMATE_SWING_BOTH: | ||||
|         traits.set_supports_swing_mode_both(true); | ||||
|         break; | ||||
|       case climate::CLIMATE_SWING_VERTICAL: | ||||
|         traits.set_supports_swing_mode_vertical(true); | ||||
|         break; | ||||
|       case climate::CLIMATE_SWING_HORIZONTAL: | ||||
|         traits.set_supports_swing_mode_horizontal(true); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|   return traits; | ||||
| } | ||||
|  | ||||
| @@ -40,6 +89,8 @@ void ClimateIR::setup() { | ||||
|     // initialize target temperature to some value so that it's not NAN | ||||
|     this->target_temperature = | ||||
|         roundf(clamp(this->current_temperature, this->minimum_temperature_, this->maximum_temperature_)); | ||||
|     this->fan_mode = climate::CLIMATE_FAN_AUTO; | ||||
|     this->swing_mode = climate::CLIMATE_SWING_OFF; | ||||
|   } | ||||
|   // Never send nan to HA | ||||
|   if (isnan(this->target_temperature)) | ||||
| @@ -51,7 +102,10 @@ void ClimateIR::control(const climate::ClimateCall &call) { | ||||
|     this->mode = *call.get_mode(); | ||||
|   if (call.get_target_temperature().has_value()) | ||||
|     this->target_temperature = *call.get_target_temperature(); | ||||
|  | ||||
|   if (call.get_fan_mode().has_value()) | ||||
|     this->fan_mode = *call.get_fan_mode(); | ||||
|   if (call.get_swing_mode().has_value()) | ||||
|     this->swing_mode = *call.get_swing_mode(); | ||||
|   this->transmit_state(); | ||||
|   this->publish_state(); | ||||
| } | ||||
|   | ||||
| @@ -18,10 +18,17 @@ namespace climate_ir { | ||||
| */ | ||||
| class ClimateIR : public climate::Climate, public Component, public remote_base::RemoteReceiverListener { | ||||
|  public: | ||||
|   ClimateIR(float minimum_temperature, float maximum_temperature, float temperature_step = 1.0f) { | ||||
|   ClimateIR(float minimum_temperature, float maximum_temperature, float temperature_step = 1.0f, | ||||
|             bool supports_dry = false, bool supports_fan_only = false, | ||||
|             std::vector<climate::ClimateFanMode> fan_modes = {}, | ||||
|             std::vector<climate::ClimateSwingMode> swing_modes = {}) { | ||||
|     this->minimum_temperature_ = minimum_temperature; | ||||
|     this->maximum_temperature_ = maximum_temperature; | ||||
|     this->temperature_step_ = temperature_step; | ||||
|     this->supports_dry_ = supports_dry; | ||||
|     this->supports_fan_only_ = supports_fan_only; | ||||
|     this->fan_modes_ = fan_modes; | ||||
|     this->swing_modes_ = swing_modes; | ||||
|   } | ||||
|  | ||||
|   void setup() override; | ||||
| @@ -44,8 +51,15 @@ class ClimateIR : public climate::Climate, public Component, public remote_base: | ||||
|   /// Transmit via IR the state of this climate controller. | ||||
|   virtual void transmit_state() = 0; | ||||
|  | ||||
|   // Dummy implement on_receive so implementation is optional for inheritors | ||||
|   bool on_receive(remote_base::RemoteReceiveData data) override { return false; }; | ||||
|  | ||||
|   bool supports_cool_{true}; | ||||
|   bool supports_heat_{true}; | ||||
|   bool supports_dry_{false}; | ||||
|   bool supports_fan_only_{false}; | ||||
|   std::vector<climate::ClimateFanMode> fan_modes_ = {}; | ||||
|   std::vector<climate::ClimateSwingMode> swing_modes_ = {}; | ||||
|  | ||||
|   remote_transmitter::RemoteTransmitterComponent *transmitter_; | ||||
|   sensor::Sensor *sensor_{nullptr}; | ||||
|   | ||||
							
								
								
									
										0
									
								
								esphome/components/climate_ir_lg/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/climate_ir_lg/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										18
									
								
								esphome/components/climate_ir_lg/climate.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/climate_ir_lg/climate.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import climate_ir | ||||
| from esphome.const import CONF_ID | ||||
|  | ||||
| AUTO_LOAD = ['climate_ir'] | ||||
|  | ||||
| climate_ir_lg_ns = cg.esphome_ns.namespace('climate_ir_lg') | ||||
| LgIrClimate = climate_ir_lg_ns.class_('LgIrClimate', climate_ir.ClimateIR) | ||||
|  | ||||
| CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ | ||||
|     cv.GenerateID(): cv.declare_id(LgIrClimate), | ||||
| }) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     yield climate_ir.register_climate_ir(var, config) | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user