mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	
							
								
								
									
										16
									
								
								.github/actions/build-image/action.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/actions/build-image/action.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -57,14 +57,6 @@ runs: | ||||
|         digest="${{ steps.build-ghcr.outputs.digest }}" | ||||
|         touch "/tmp/digests/${{ inputs.target }}/ghcr/${digest#sha256:}" | ||||
|  | ||||
|     - name: Upload ghcr digest | ||||
|       uses: actions/upload-artifact@v3.1.3 | ||||
|       with: | ||||
|         name: digests-${{ inputs.target }}-ghcr | ||||
|         path: /tmp/digests/${{ inputs.target }}/ghcr/* | ||||
|         if-no-files-found: error | ||||
|         retention-days: 1 | ||||
|  | ||||
|     - name: Build and push to dockerhub by digest | ||||
|       id: build-dockerhub | ||||
|       uses: docker/build-push-action@v5.3.0 | ||||
| @@ -87,11 +79,3 @@ runs: | ||||
|         mkdir -p /tmp/digests/${{ inputs.target }}/dockerhub | ||||
|         digest="${{ steps.build-dockerhub.outputs.digest }}" | ||||
|         touch "/tmp/digests/${{ inputs.target }}/dockerhub/${digest#sha256:}" | ||||
|  | ||||
|     - name: Upload dockerhub digest | ||||
|       uses: actions/upload-artifact@v3.1.3 | ||||
|       with: | ||||
|         name: digests-${{ inputs.target }}-dockerhub | ||||
|         path: /tmp/digests/${{ inputs.target }}/dockerhub/* | ||||
|         if-no-files-found: error | ||||
|         retention-days: 1 | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/ci-api-proto.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-api-proto.yml
									
									
									
									
										vendored
									
									
								
							| @@ -21,7 +21,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v5.1.0 | ||||
|         with: | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							| @@ -40,7 +40,7 @@ jobs: | ||||
|         arch: [amd64, armv7, aarch64] | ||||
|         build_type: ["ha-addon", "docker", "lint"] | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4.1.1 | ||||
|       - uses: actions/checkout@v4.1.5 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v5.1.0 | ||||
|         with: | ||||
|   | ||||
							
								
								
									
										117
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										117
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -20,7 +20,6 @@ permissions: | ||||
| env: | ||||
|   DEFAULT_PYTHON: "3.9" | ||||
|   PYUPGRADE_TARGET: "--py39-plus" | ||||
|   CLANG_FORMAT_VERSION: "13.0.1" | ||||
|  | ||||
| concurrency: | ||||
|   # yamllint disable-line rule:line-length | ||||
| @@ -35,7 +34,7 @@ jobs: | ||||
|       cache-key: ${{ steps.cache-key.outputs.key }} | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Generate cache-key | ||||
|         id: cache-key | ||||
|         run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT | ||||
| @@ -67,7 +66,7 @@ jobs: | ||||
|       - common | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
| @@ -88,7 +87,7 @@ jobs: | ||||
|       - common | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
| @@ -109,7 +108,7 @@ jobs: | ||||
|       - common | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
| @@ -130,7 +129,7 @@ jobs: | ||||
|       - common | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
| @@ -151,7 +150,7 @@ jobs: | ||||
|       - common | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
| @@ -200,7 +199,7 @@ jobs: | ||||
|       - common | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
| @@ -230,7 +229,7 @@ jobs: | ||||
|       - common | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
| @@ -239,7 +238,7 @@ jobs: | ||||
|       - name: Install clang-format | ||||
|         run: | | ||||
|           . venv/bin/activate | ||||
|           pip install clang-format==${{ env.CLANG_FORMAT_VERSION }} | ||||
|           pip install clang-format -c requirements_dev.txt | ||||
|       - name: Run clang-format | ||||
|         run: | | ||||
|           . venv/bin/activate | ||||
| @@ -255,7 +254,7 @@ jobs: | ||||
|       matrix: ${{ steps.set-matrix.outputs.matrix }} | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Find all YAML test files | ||||
|         id: set-matrix | ||||
|         run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT | ||||
| @@ -272,7 +271,7 @@ jobs: | ||||
|         file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }} | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
| @@ -304,7 +303,7 @@ jobs: | ||||
|         file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }} | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
| @@ -359,7 +358,7 @@ jobs: | ||||
|  | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
| @@ -399,10 +398,11 @@ jobs: | ||||
|       - common | ||||
|     if: github.event_name == 'pull_request' | ||||
|     outputs: | ||||
|       matrix: ${{ steps.set-matrix.outputs.matrix }} | ||||
|       components: ${{ steps.list-components.outputs.components }} | ||||
|       count: ${{ steps.list-components.outputs.count }} | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|         with: | ||||
|           # Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works. | ||||
|           fetch-depth: 500 | ||||
| @@ -420,10 +420,18 @@ jobs: | ||||
|           python-version: ${{ env.DEFAULT_PYTHON }} | ||||
|           cache-key: ${{ needs.common.outputs.cache-key }} | ||||
|       - name: Find changed components | ||||
|         id: set-matrix | ||||
|         id: list-components | ||||
|         run: | | ||||
|           . venv/bin/activate | ||||
|           echo "matrix=$(script/list-components.py --changed --branch ${{ steps.target-branch.outputs.branch }} | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT | ||||
|           components=$(script/list-components.py --changed --branch ${{ steps.target-branch.outputs.branch }}) | ||||
|           output_components=$(echo "$components" | jq -R -s -c 'split("\n")[:-1] | map(select(length > 0))') | ||||
|           count=$(echo "$output_components" | jq length) | ||||
|  | ||||
|           echo "components=$output_components" >> $GITHUB_OUTPUT | ||||
|           echo "count=$count" >> $GITHUB_OUTPUT | ||||
|  | ||||
|           echo "$count Components:" | ||||
|           echo "$output_components" | jq | ||||
|  | ||||
|   test-build-components: | ||||
|     name: Component test ${{ matrix.file }} | ||||
| @@ -431,18 +439,18 @@ jobs: | ||||
|     needs: | ||||
|       - common | ||||
|       - list-components | ||||
|     if: ${{ github.event_name == 'pull_request' && needs.list-components.outputs.matrix != '[]' && needs.list-components.outputs.matrix != '' }} | ||||
|     if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) > 0 && fromJSON(needs.list-components.outputs.count) < 100 | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       max-parallel: 2 | ||||
|       matrix: | ||||
|         file: ${{ fromJson(needs.list-components.outputs.matrix) }} | ||||
|         file: ${{ fromJson(needs.list-components.outputs.components) }} | ||||
|     steps: | ||||
|       - name: Install libsodium | ||||
|         run: sudo apt-get install libsodium-dev | ||||
|  | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
| @@ -457,6 +465,64 @@ jobs: | ||||
|           . venv/bin/activate | ||||
|           ./script/test_build_components -e compile -c ${{ matrix.file }} | ||||
|  | ||||
|   test-build-components-splitter: | ||||
|     name: Split components for testing into 20 groups maximum | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: | ||||
|       - common | ||||
|       - list-components | ||||
|     if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) >= 100 | ||||
|     outputs: | ||||
|       matrix: ${{ steps.split.outputs.components }} | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Split components into 20 groups | ||||
|         id: split | ||||
|         run: | | ||||
|           components=$(echo '${{ needs.list-components.outputs.components }}' | jq -c '.[]' | shuf | jq -s -c '[_nwise(20) | join(" ")]') | ||||
|           echo "components=$components" >> $GITHUB_OUTPUT | ||||
|  | ||||
|   test-build-components-split: | ||||
|     name: Test split components | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: | ||||
|       - common | ||||
|       - list-components | ||||
|       - test-build-components-splitter | ||||
|     if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) >= 100 | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       max-parallel: 4 | ||||
|       matrix: | ||||
|         components: ${{ fromJson(needs.test-build-components-splitter.outputs.matrix) }} | ||||
|     steps: | ||||
|       - name: List components | ||||
|         run: echo ${{ matrix.components }} | ||||
|  | ||||
|       - name: Install libsodium | ||||
|         run: sudo apt-get install libsodium-dev | ||||
|  | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
|           python-version: ${{ env.DEFAULT_PYTHON }} | ||||
|           cache-key: ${{ needs.common.outputs.cache-key }} | ||||
|       - name: Validate config | ||||
|         run: | | ||||
|           . venv/bin/activate | ||||
|           for component in ${{ matrix.components }}; do | ||||
|             ./script/test_build_components -e config -c $component | ||||
|           done | ||||
|       - name: Compile config | ||||
|         run: | | ||||
|           . venv/bin/activate | ||||
|           for component in ${{ matrix.components }}; do | ||||
|             ./script/test_build_components -e compile -c $component | ||||
|           done | ||||
|  | ||||
|   ci-status: | ||||
|     name: CI Status | ||||
|     runs-on: ubuntu-latest | ||||
| @@ -471,7 +537,10 @@ jobs: | ||||
|       - pyupgrade | ||||
|       - compile-tests | ||||
|       - clang-tidy | ||||
|       - list-components | ||||
|       - test-build-components | ||||
|       - test-build-components-splitter | ||||
|       - test-build-components-split | ||||
|     if: always() | ||||
|     steps: | ||||
|       - name: Success | ||||
| @@ -479,4 +548,8 @@ jobs: | ||||
|         run: exit 0 | ||||
|       - name: Failure | ||||
|         if: ${{ contains(needs.*.result, 'failure') }} | ||||
|         run: exit 1 | ||||
|         env: | ||||
|           JSON_DOC: ${{ toJSON(needs) }} | ||||
|         run: | | ||||
|           echo $JSON_DOC | jq | ||||
|           exit 1 | ||||
|   | ||||
							
								
								
									
										68
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										68
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -17,14 +17,16 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     outputs: | ||||
|       tag: ${{ steps.tag.outputs.tag }} | ||||
|       branch_build: ${{ steps.tag.outputs.branch_build }} | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4.1.1 | ||||
|       - uses: actions/checkout@v4.1.5 | ||||
|       - name: Get tag | ||||
|         id: tag | ||||
|         # yamllint disable rule:line-length | ||||
|         run: | | ||||
|           if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then | ||||
|             TAG="${GITHUB_REF#refs/tags/}" | ||||
|           if [[ "${{ github.event_name }}" = "release" ]]; then | ||||
|             TAG="${{ github.event.release.tag_name}}" | ||||
|             BRANCH_BUILD="false" | ||||
|           else | ||||
|             TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p") | ||||
|             today="$(date --utc '+%Y%m%d')" | ||||
| @@ -32,17 +34,24 @@ jobs: | ||||
|             BRANCH=${GITHUB_REF#refs/heads/} | ||||
|             if [[ "$BRANCH" != "dev" ]]; then | ||||
|               TAG="${TAG}-${BRANCH}" | ||||
|               BRANCH_BUILD="true" | ||||
|             else | ||||
|               BRANCH_BUILD="false" | ||||
|             fi | ||||
|           fi | ||||
|           echo "tag=${TAG}" >> $GITHUB_OUTPUT | ||||
|           echo "branch_build=${BRANCH_BUILD}" >> $GITHUB_OUTPUT | ||||
|         # yamllint enable rule:line-length | ||||
|  | ||||
|   deploy-pypi: | ||||
|     name: Build and publish to PyPi | ||||
|     if: github.repository == 'esphome/esphome' && github.event_name == 'release' | ||||
|     runs-on: ubuntu-latest | ||||
|     permissions: | ||||
|       contents: read | ||||
|       id-token: write | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4.1.1 | ||||
|       - uses: actions/checkout@v4.1.5 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v5.1.0 | ||||
|         with: | ||||
| @@ -50,16 +59,11 @@ jobs: | ||||
|       - name: Set up python environment | ||||
|         env: | ||||
|           ESPHOME_NO_VENV: 1 | ||||
|         run: | | ||||
|           script/setup | ||||
|           pip install twine | ||||
|         run: script/setup | ||||
|       - name: Build | ||||
|         run: python setup.py sdist bdist_wheel | ||||
|       - name: Upload | ||||
|         env: | ||||
|           TWINE_USERNAME: __token__ | ||||
|           TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} | ||||
|         run: twine upload dist/* | ||||
|       - name: Publish | ||||
|         uses: pypa/gh-action-pypi-publish@v1.8.14 | ||||
|  | ||||
|   deploy-docker: | ||||
|     name: Build ESPHome ${{ matrix.platform }} | ||||
| @@ -77,7 +81,7 @@ jobs: | ||||
|           - linux/arm/v7 | ||||
|           - linux/arm64 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4.1.1 | ||||
|       - uses: actions/checkout@v4.1.5 | ||||
|       - name: Set up Python | ||||
|         uses: actions/setup-python@v5.1.0 | ||||
|         with: | ||||
| @@ -128,6 +132,19 @@ jobs: | ||||
|           suffix: lint | ||||
|           version: ${{ needs.init.outputs.tag }} | ||||
|  | ||||
|       - name: Sanitize platform name | ||||
|         id: sanitize | ||||
|         run: | | ||||
|           echo "${{ matrix.platform }}" | sed 's|/|-|g' > /tmp/platform | ||||
|           echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT | ||||
|  | ||||
|       - name: Upload digests | ||||
|         uses: actions/upload-artifact@v4.3.3 | ||||
|         with: | ||||
|           name: digests-${{ steps.sanitize.outputs.name }} | ||||
|           path: /tmp/digests | ||||
|           retention-days: 1 | ||||
|  | ||||
|   deploy-manifest: | ||||
|     name: Publish ESPHome ${{ matrix.image.title }} to ${{ matrix.registry }} | ||||
|     runs-on: ubuntu-latest | ||||
| @@ -155,12 +172,15 @@ jobs: | ||||
|           - ghcr | ||||
|           - dockerhub | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4.1.1 | ||||
|       - uses: actions/checkout@v4.1.5 | ||||
|  | ||||
|       - name: Download digests | ||||
|         uses: actions/download-artifact@v3.0.2 | ||||
|         uses: actions/download-artifact@v4.1.7 | ||||
|         with: | ||||
|           name: digests-${{ matrix.image.target }}-${{ matrix.registry }} | ||||
|           pattern: digests-* | ||||
|           path: /tmp/digests | ||||
|           merge-multiple: true | ||||
|  | ||||
|       - name: Set up Docker Buildx | ||||
|         uses: docker/setup-buildx-action@v3.3.0 | ||||
|  | ||||
| @@ -191,28 +211,34 @@ jobs: | ||||
|           done | ||||
|  | ||||
|       - name: Create manifest list and push | ||||
|         working-directory: /tmp/digests | ||||
|         working-directory: /tmp/digests/${{ matrix.image.target }}/${{ matrix.registry }} | ||||
|         run: | | ||||
|           docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \ | ||||
|             $(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *) | ||||
|  | ||||
|   deploy-ha-addon-repo: | ||||
|     if: github.repository == 'esphome/esphome' && github.event_name == 'release' | ||||
|     if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false' | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: [deploy-manifest] | ||||
|     needs: | ||||
|       - init | ||||
|       - deploy-manifest | ||||
|     steps: | ||||
|       - name: Trigger Workflow | ||||
|         uses: actions/github-script@v7.0.1 | ||||
|         with: | ||||
|           github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }} | ||||
|           script: | | ||||
|             let description = "ESPHome"; | ||||
|             if (context.eventName == "release") { | ||||
|               description = ${{ toJSON(github.event.release.body) }}; | ||||
|             } | ||||
|             github.rest.actions.createWorkflowDispatch({ | ||||
|               owner: "esphome", | ||||
|               repo: "home-assistant-addon", | ||||
|               workflow_id: "bump-version.yml", | ||||
|               ref: "main", | ||||
|               inputs: { | ||||
|                 version: "${{ github.event.release.tag_name }}", | ||||
|                 content: ${{ toJSON(github.event.release.body) }} | ||||
|                 version: "${{ needs.init.outputs.tag }}", | ||||
|                 content: description | ||||
|               } | ||||
|             }) | ||||
|   | ||||
							
								
								
									
										8
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							| @@ -13,10 +13,10 @@ jobs: | ||||
|     if: github.repository == 'esphome/esphome' | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|  | ||||
|       - name: Checkout Home Assistant | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|         with: | ||||
|           repository: home-assistant/core | ||||
|           path: lib/home-assistant | ||||
| @@ -24,7 +24,7 @@ jobs: | ||||
|       - name: Setup Python | ||||
|         uses: actions/setup-python@v5.1.0 | ||||
|         with: | ||||
|           python-version: 3.11 | ||||
|           python-version: 3.12 | ||||
|  | ||||
|       - name: Install Home Assistant | ||||
|         run: | | ||||
| @@ -36,7 +36,7 @@ jobs: | ||||
|           python ./script/sync-device_class.py | ||||
|  | ||||
|       - name: Commit changes | ||||
|         uses: peter-evans/create-pull-request@v6.0.2 | ||||
|         uses: peter-evans/create-pull-request@v6.0.4 | ||||
|         with: | ||||
|           commit-message: "Synchronise Device Classes from Home Assistant" | ||||
|           committer: esphomebot <esphome@nabucasa.com> | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/yaml-lint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/yaml-lint.yml
									
									
									
									
										vendored
									
									
								
							| @@ -18,7 +18,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|         uses: actions/checkout@v4.1.5 | ||||
|       - name: Run yamllint | ||||
|         uses: frenck/action-yamllint@v1.5.0 | ||||
|         with: | ||||
|   | ||||
| @@ -27,7 +27,16 @@ repos: | ||||
|           - --branch=release | ||||
|           - --branch=beta | ||||
|   - repo: https://github.com/asottile/pyupgrade | ||||
|     rev: v3.15.1 | ||||
|     rev: v3.15.2 | ||||
|     hooks: | ||||
|       - id: pyupgrade | ||||
|         args: [--py39-plus] | ||||
|   - repo: https://github.com/adrienverge/yamllint.git | ||||
|     rev: v1.35.1 | ||||
|     hooks: | ||||
|       - id: yamllint | ||||
|   - repo: https://github.com/pre-commit/mirrors-clang-format | ||||
|     rev: v13.0.1 | ||||
|     hooks: | ||||
|       - id: clang-format | ||||
|         types_or: [c, c++] | ||||
|   | ||||
							
								
								
									
										21
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								CODEOWNERS
									
									
									
									
									
								
							| @@ -63,7 +63,10 @@ esphome/components/bme280_base/* @esphome/core | ||||
| esphome/components/bme280_spi/* @apbodrov | ||||
| esphome/components/bme680_bsec/* @trvrnrth | ||||
| esphome/components/bmi160/* @flaviut | ||||
| esphome/components/bmp3xx/* @martgras | ||||
| esphome/components/bmp3xx/* @latonita | ||||
| esphome/components/bmp3xx_base/* @latonita @martgras | ||||
| esphome/components/bmp3xx_i2c/* @latonita | ||||
| esphome/components/bmp3xx_spi/* @latonita | ||||
| esphome/components/bmp581/* @kahrendt | ||||
| esphome/components/bp1658cj/* @Cossid | ||||
| esphome/components/bp5758d/* @Cossid | ||||
| @@ -119,6 +122,7 @@ esphome/components/esp32_rmt/* @jesserockz | ||||
| esphome/components/esp32_rmt_led_strip/* @jesserockz | ||||
| esphome/components/esp8266/* @esphome/core | ||||
| esphome/components/ethernet_info/* @gtjadsonsantos | ||||
| esphome/components/event/* @nohat | ||||
| esphome/components/exposure_notifications/* @OttoWinter | ||||
| esphome/components/ezo/* @ssieb | ||||
| esphome/components/ezo_pmp/* @carlos-sarmiento | ||||
| @@ -241,7 +245,7 @@ esphome/components/mpl3115a2/* @kbickar | ||||
| esphome/components/mpu6886/* @fabaff | ||||
| esphome/components/ms8607/* @e28eta | ||||
| esphome/components/network/* @esphome/core | ||||
| esphome/components/nextion/* @senexcrenshaw | ||||
| esphome/components/nextion/* @edwardtfn @senexcrenshaw | ||||
| esphome/components/nextion/binary_sensor/* @senexcrenshaw | ||||
| esphome/components/nextion/sensor/* @senexcrenshaw | ||||
| esphome/components/nextion/switch/* @senexcrenshaw | ||||
| @@ -359,6 +363,7 @@ esphome/components/tee501/* @Stock-M | ||||
| esphome/components/teleinfo/* @0hax | ||||
| esphome/components/template/alarm_control_panel/* @grahambrown11 @hwstar | ||||
| esphome/components/template/datetime/* @rfdarter | ||||
| esphome/components/template/event/* @nohat | ||||
| esphome/components/template/fan/* @ssieb | ||||
| esphome/components/text/* @mauritskorse | ||||
| esphome/components/thermostat/* @kbx81 | ||||
| @@ -390,6 +395,7 @@ esphome/components/ufire_ec/* @pvizeli | ||||
| esphome/components/ufire_ise/* @pvizeli | ||||
| esphome/components/ultrasonic/* @OttoWinter | ||||
| esphome/components/uponor_smatrix/* @kroimon | ||||
| esphome/components/valve/* @esphome/core | ||||
| esphome/components/vbus/* @ssieb | ||||
| esphome/components/veml3235/* @kbx81 | ||||
| esphome/components/veml7700/* @latonita | ||||
| @@ -399,10 +405,21 @@ esphome/components/wake_on_lan/* @willwill2will54 | ||||
| esphome/components/waveshare_epaper/* @clydebarrow | ||||
| esphome/components/web_server_base/* @OttoWinter | ||||
| esphome/components/web_server_idf/* @dentra | ||||
| esphome/components/weikai/* @DrCoolZic | ||||
| esphome/components/weikai_i2c/* @DrCoolZic | ||||
| esphome/components/weikai_spi/* @DrCoolZic | ||||
| esphome/components/whirlpool/* @glmnet | ||||
| esphome/components/whynter/* @aeonsablaze | ||||
| esphome/components/wiegand/* @ssieb | ||||
| esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard | ||||
| esphome/components/wk2132_i2c/* @DrCoolZic | ||||
| esphome/components/wk2132_spi/* @DrCoolZic | ||||
| esphome/components/wk2168_i2c/* @DrCoolZic | ||||
| esphome/components/wk2168_spi/* @DrCoolZic | ||||
| esphome/components/wk2204_i2c/* @DrCoolZic | ||||
| esphome/components/wk2204_spi/* @DrCoolZic | ||||
| esphome/components/wk2212_i2c/* @DrCoolZic | ||||
| esphome/components/wk2212_spi/* @DrCoolZic | ||||
| esphome/components/wl_134/* @hobbypunk90 | ||||
| esphome/components/x9c/* @EtienneMD | ||||
| esphome/components/xgzp68xx/* @gcormier | ||||
|   | ||||
							
								
								
									
										47
									
								
								docker/ha-addon-rootfs/etc/cont-init.d/30-esphome-fork.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										47
									
								
								docker/ha-addon-rootfs/etc/cont-init.d/30-esphome-fork.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| #!/usr/bin/with-contenv bashio | ||||
| # ============================================================================== | ||||
| # This file installs the user ESPHome fork if specified. | ||||
| # The fork must be up to date with the latest ESPHome dev branch | ||||
| # and have no conflicts. | ||||
| # This config option only exists in the ESPHome Dev add-on. | ||||
| # ============================================================================== | ||||
|  | ||||
| declare esphome_fork | ||||
|  | ||||
| if bashio::config.has_value 'esphome_fork'; then | ||||
|   esphome_fork=$(bashio::config 'esphome_fork') | ||||
|   # format: [username][/repository]:ref | ||||
|   if [[ "$esphome_fork" =~ ^(([^/]+)(/([^:]+))?:)?([^:/]+)$ ]]; then | ||||
|     username="${BASH_REMATCH[2]:-esphome}" | ||||
|     repository="${BASH_REMATCH[4]:-esphome}" | ||||
|     ref="${BASH_REMATCH[5]}" | ||||
|   else | ||||
|     bashio::exit.nok "Invalid esphome_fork format: $esphome_fork" | ||||
|   fi | ||||
|   full_url="https://github.com/${username}/${repository}/archive/${ref}.tar.gz" | ||||
|   bashio::log.info "Checking forked ESPHome" | ||||
|   dev_version=$(python3 -c "from esphome.const import __version__; print(__version__)") | ||||
|   bashio::log.info "Downloading ESPHome from fork '${esphome_fork}' (${full_url})..." | ||||
|   curl -L -o /tmp/esphome.tar.gz "${full_url}" -qq || | ||||
|     bashio::exit.nok "Failed downloading ESPHome fork." | ||||
|   bashio::log.info "Installing ESPHome from fork '${esphome_fork}' (${full_url})..." | ||||
|   rm -rf /esphome || bashio::exit.nok "Failed to remove ESPHome." | ||||
|   mkdir /esphome | ||||
|   tar -zxf /tmp/esphome.tar.gz -C /esphome --strip-components=1 || | ||||
|     bashio::exit.nok "Failed installing ESPHome from fork." | ||||
|   pip install -U -e /esphome || bashio::exit.nok "Failed installing ESPHome from fork." | ||||
|   rm -f /tmp/esphome.tar.gz | ||||
|   fork_version=$(python3 -c "from esphome.const import __version__; print(__version__)") | ||||
|  | ||||
|   if [[ "$fork_version" != "$dev_version" ]]; then | ||||
|     bashio::log.error "############################" | ||||
|     bashio::log.error "Uninstalled fork as version does not match" | ||||
|     bashio::log.error "Update (or ask the author to update) the branch" | ||||
|     bashio::log.error "This is important as the dev addon and the dev ESPHome" | ||||
|     bashio::log.error "branch can have changes that are not compatible with old forks" | ||||
|     bashio::log.error "and get reported as bugs which we cannot solve easily." | ||||
|     bashio::log.error "############################" | ||||
|     bashio::exit.nok | ||||
|   fi | ||||
|   bashio::log.info "Installed ESPHome from fork '${esphome_fork}' (${full_url})..." | ||||
| fi | ||||
| @@ -343,9 +343,10 @@ def upload_program(config, args, host): | ||||
|     password = ota_conf.get(CONF_PASSWORD, "") | ||||
|  | ||||
|     if ( | ||||
|         not is_ip_address(CORE.address) | ||||
|         not is_ip_address(CORE.address)  # pylint: disable=too-many-boolean-expressions | ||||
|         and (get_port_type(host) == "MQTT" or config[CONF_MDNS][CONF_DISABLED]) | ||||
|         and CONF_MQTT in config | ||||
|         and (not args.device or args.device == "MQTT") | ||||
|     ): | ||||
|         from esphome import mqtt | ||||
|  | ||||
| @@ -768,7 +769,9 @@ def parse_args(argv): | ||||
|     ) | ||||
|  | ||||
|     parser_upload = subparsers.add_parser( | ||||
|         "upload", help="Validate the configuration and upload the latest binary." | ||||
|         "upload", | ||||
|         help="Validate the configuration and upload the latest binary.", | ||||
|         parents=[mqtt_options], | ||||
|     ) | ||||
|     parser_upload.add_argument( | ||||
|         "configuration", help="Your YAML configuration file(s).", nargs="+" | ||||
| @@ -785,6 +788,7 @@ def parse_args(argv): | ||||
|     parser_logs = subparsers.add_parser( | ||||
|         "logs", | ||||
|         help="Validate the configuration and show all logs.", | ||||
|         aliases=["log"], | ||||
|         parents=[mqtt_options], | ||||
|     ) | ||||
|     parser_logs.add_argument( | ||||
|   | ||||
| @@ -18,10 +18,20 @@ from esphome.util import Registry | ||||
|  | ||||
|  | ||||
| def maybe_simple_id(*validators): | ||||
|     """Allow a raw ID to be specified in place of a config block. | ||||
|     If the value that's being validated is a dictionary, it's passed as-is to the specified validators. Otherwise, it's | ||||
|     wrapped in a dict that looks like ``{"id": <value>}``, and that dict is then handed off to the specified validators. | ||||
|     """ | ||||
|     return maybe_conf(CONF_ID, *validators) | ||||
|  | ||||
|  | ||||
| def maybe_conf(conf, *validators): | ||||
|     """Allow a raw value to be specified in place of a config block. | ||||
|     If the value that's being validated is a dictionary, it's passed as-is to the specified validators. Otherwise, it's | ||||
|     wrapped in a dict that looks like ``{<conf>: <value>}``, and that dict is then handed off to the specified | ||||
|     validators. | ||||
|     (This is a general case of ``maybe_simple_id`` that allows the wrapping key to be something other than ``id``.) | ||||
|     """ | ||||
|     validator = cv.All(*validators) | ||||
|  | ||||
|     @schema_extractor("maybe") | ||||
|   | ||||
| @@ -18,11 +18,23 @@ from esphome.components.esp32.const import ( | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
|  | ||||
| adc_ns = cg.esphome_ns.namespace("adc") | ||||
|  | ||||
|  | ||||
| """ | ||||
| From the below patch versions (and 5.2+) ADC_ATTEN_DB_11 is deprecated and replaced with ADC_ATTEN_DB_12. | ||||
| 4.4.7 | ||||
| 5.0.5 | ||||
| 5.1.3 | ||||
| 5.2+ | ||||
| """ | ||||
|  | ||||
| ATTENUATION_MODES = { | ||||
|     "0db": cg.global_ns.ADC_ATTEN_DB_0, | ||||
|     "2.5db": cg.global_ns.ADC_ATTEN_DB_2_5, | ||||
|     "6db": cg.global_ns.ADC_ATTEN_DB_6, | ||||
|     "11db": cg.global_ns.ADC_ATTEN_DB_11, | ||||
|     "11db": adc_ns.ADC_ATTEN_DB_12_COMPAT, | ||||
|     "12db": adc_ns.ADC_ATTEN_DB_12_COMPAT, | ||||
|     "auto": "auto", | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -62,7 +62,7 @@ extern "C" | ||||
|   } | ||||
|  | ||||
|   // load characteristics for each attenuation | ||||
|   for (int32_t i = 0; i <= ADC_ATTEN_DB_11; i++) { | ||||
|   for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) { | ||||
|     auto adc_unit = channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2; | ||||
|     auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS, | ||||
|                                               1100,  // default vref | ||||
| @@ -118,8 +118,8 @@ void ADCSensor::dump_config() { | ||||
|       case ADC_ATTEN_DB_6: | ||||
|         ESP_LOGCONFIG(TAG, " Attenuation: 6db"); | ||||
|         break; | ||||
|       case ADC_ATTEN_DB_11: | ||||
|         ESP_LOGCONFIG(TAG, " Attenuation: 11db"); | ||||
|       case ADC_ATTEN_DB_12_COMPAT: | ||||
|         ESP_LOGCONFIG(TAG, " Attenuation: 12db"); | ||||
|         break; | ||||
|       default:  // This is to satisfy the unused ADC_ATTEN_MAX | ||||
|         break; | ||||
| @@ -183,12 +183,12 @@ float ADCSensor::sample() { | ||||
|     return mv / 1000.0f; | ||||
|   } | ||||
|  | ||||
|   int raw11 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX; | ||||
|   int raw12 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX; | ||||
|  | ||||
|   if (channel1_ != ADC1_CHANNEL_MAX) { | ||||
|     adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_11); | ||||
|     raw11 = adc1_get_raw(channel1_); | ||||
|     if (raw11 < ADC_MAX) { | ||||
|     adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_12_COMPAT); | ||||
|     raw12 = adc1_get_raw(channel1_); | ||||
|     if (raw12 < ADC_MAX) { | ||||
|       adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_6); | ||||
|       raw6 = adc1_get_raw(channel1_); | ||||
|       if (raw6 < ADC_MAX) { | ||||
| @@ -201,9 +201,9 @@ float ADCSensor::sample() { | ||||
|       } | ||||
|     } | ||||
|   } else if (channel2_ != ADC2_CHANNEL_MAX) { | ||||
|     adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_11); | ||||
|     adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw11); | ||||
|     if (raw11 < ADC_MAX) { | ||||
|     adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_12_COMPAT); | ||||
|     adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw12); | ||||
|     if (raw12 < ADC_MAX) { | ||||
|       adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_6); | ||||
|       adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6); | ||||
|       if (raw6 < ADC_MAX) { | ||||
| @@ -217,25 +217,25 @@ float ADCSensor::sample() { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw11 == -1) { | ||||
|   if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw12 == -1) { | ||||
|     return NAN; | ||||
|   } | ||||
|  | ||||
|   uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_11]); | ||||
|   uint32_t mv12 = esp_adc_cal_raw_to_voltage(raw12, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_12_COMPAT]); | ||||
|   uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]); | ||||
|   uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]); | ||||
|   uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]); | ||||
|  | ||||
|   // Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC) | ||||
|   uint32_t c11 = std::min(raw11, ADC_HALF); | ||||
|   uint32_t c12 = std::min(raw12, ADC_HALF); | ||||
|   uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF); | ||||
|   uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF); | ||||
|   uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF); | ||||
|   // max theoretical csum value is 4096*4 = 16384 | ||||
|   uint32_t csum = c11 + c6 + c2 + c0; | ||||
|   uint32_t csum = c12 + c6 + c2 + c0; | ||||
|  | ||||
|   // each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32 | ||||
|   uint32_t mv_scaled = (mv11 * c11) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0); | ||||
|   uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0); | ||||
|   return mv_scaled / (float) (csum * 1000U); | ||||
| } | ||||
| #endif  // USE_ESP32 | ||||
|   | ||||
| @@ -1,19 +1,34 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/core/defines.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #include "esphome/components/voltage_sampler/voltage_sampler.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/defines.h" | ||||
| #include "esphome/core/hal.h" | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
| #include "driver/adc.h" | ||||
| #include <esp_adc_cal.h> | ||||
| #include "driver/adc.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace adc { | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
| // clang-format off | ||||
| #if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 7)) || \ | ||||
|     (ESP_IDF_VERSION_MAJOR == 5 && \ | ||||
|      ((ESP_IDF_VERSION_MINOR == 0 && ESP_IDF_VERSION_PATCH >= 5) || \ | ||||
|       (ESP_IDF_VERSION_MINOR == 1 && ESP_IDF_VERSION_PATCH >= 3) || \ | ||||
|       (ESP_IDF_VERSION_MINOR >= 2)) \ | ||||
|     ) | ||||
| // clang-format on | ||||
| static const adc_atten_t ADC_ATTEN_DB_12_COMPAT = ADC_ATTEN_DB_12; | ||||
| #else | ||||
| static const adc_atten_t ADC_ATTEN_DB_12_COMPAT = ADC_ATTEN_DB_11; | ||||
| #endif | ||||
| #endif  // USE_ESP32 | ||||
|  | ||||
| class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler { | ||||
|  public: | ||||
| #ifdef USE_ESP32 | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import logging | ||||
|  | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| import esphome.final_validate as fv | ||||
| @@ -19,16 +21,29 @@ from . import ( | ||||
|     ATTENUATION_MODES, | ||||
|     ESP32_VARIANT_ADC1_PIN_TO_CHANNEL, | ||||
|     ESP32_VARIANT_ADC2_PIN_TO_CHANNEL, | ||||
|     adc_ns, | ||||
|     validate_adc_pin, | ||||
| ) | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
| AUTO_LOAD = ["voltage_sampler"] | ||||
|  | ||||
|  | ||||
| _attenuation = cv.enum(ATTENUATION_MODES, lower=True) | ||||
|  | ||||
|  | ||||
| def validate_config(config): | ||||
|     if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto": | ||||
|         raise cv.Invalid("Automatic attenuation cannot be used when raw output is set") | ||||
|  | ||||
|     if config.get(CONF_ATTENUATION) == "11db": | ||||
|         _LOGGER.warning( | ||||
|             "`attenuation: 11db` is deprecated, use `attenuation: 12db` instead" | ||||
|         ) | ||||
|         # Alter value here so `config` command prints the recommended change | ||||
|         config[CONF_ATTENUATION] = _attenuation("12db") | ||||
|  | ||||
|     return config | ||||
|  | ||||
|  | ||||
| @@ -47,7 +62,6 @@ def final_validate_config(config): | ||||
|     return config | ||||
|  | ||||
|  | ||||
| adc_ns = cg.esphome_ns.namespace("adc") | ||||
| ADCSensor = adc_ns.class_( | ||||
|     "ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler | ||||
| ) | ||||
| @@ -65,7 +79,7 @@ CONFIG_SCHEMA = cv.All( | ||||
|             cv.Required(CONF_PIN): validate_adc_pin, | ||||
|             cv.Optional(CONF_RAW, default=False): cv.boolean, | ||||
|             cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All( | ||||
|                 cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True) | ||||
|                 cv.only_on_esp32, _attenuation | ||||
|             ), | ||||
|         } | ||||
|     ) | ||||
|   | ||||
| @@ -19,6 +19,7 @@ from esphome.const import ( | ||||
|     CONF_RESET_PIN, | ||||
|     CONF_REVERSE_ACTIVE_ENERGY, | ||||
|     CONF_VOLTAGE, | ||||
|     CONF_VOLTAGE_GAIN, | ||||
|     DEVICE_CLASS_APPARENT_POWER, | ||||
|     DEVICE_CLASS_CURRENT, | ||||
|     DEVICE_CLASS_ENERGY, | ||||
| @@ -47,7 +48,6 @@ CONF_CURRENT_GAIN = "current_gain" | ||||
| CONF_IRQ0_PIN = "irq0_pin" | ||||
| CONF_IRQ1_PIN = "irq1_pin" | ||||
| CONF_POWER_GAIN = "power_gain" | ||||
| CONF_VOLTAGE_GAIN = "voltage_gain" | ||||
|  | ||||
| CONF_NEUTRAL = "neutral" | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ from esphome.const import ( | ||||
|     CONF_IRQ_PIN, | ||||
|     CONF_VOLTAGE, | ||||
|     CONF_FREQUENCY, | ||||
|     CONF_VOLTAGE_GAIN, | ||||
|     DEVICE_CLASS_CURRENT, | ||||
|     DEVICE_CLASS_APPARENT_POWER, | ||||
|     DEVICE_CLASS_POWER, | ||||
| @@ -36,7 +37,6 @@ CONF_POWER_FACTOR_B = "power_factor_b" | ||||
| CONF_VOLTAGE_PGA_GAIN = "voltage_pga_gain" | ||||
| CONF_CURRENT_PGA_GAIN_A = "current_pga_gain_a" | ||||
| CONF_CURRENT_PGA_GAIN_B = "current_pga_gain_b" | ||||
| CONF_VOLTAGE_GAIN = "voltage_gain" | ||||
| CONF_CURRENT_GAIN_A = "current_gain_a" | ||||
| CONF_CURRENT_GAIN_B = "current_gain_b" | ||||
| CONF_ACTIVE_POWER_GAIN_A = "active_power_gain_a" | ||||
|   | ||||
| @@ -97,9 +97,11 @@ void Alpha3::handle_geni_response_(const uint8_t *response, uint16_t length) { | ||||
| void Alpha3::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { | ||||
|   switch (event) { | ||||
|     case ESP_GATTC_OPEN_EVT: { | ||||
|       this->response_offset_ = 0; | ||||
|       this->response_length_ = 0; | ||||
|       ESP_LOGI(TAG, "[%s] connection open", this->parent_->address_str().c_str()); | ||||
|       if (param->open.status == ESP_GATT_OK) { | ||||
|         this->response_offset_ = 0; | ||||
|         this->response_length_ = 0; | ||||
|         ESP_LOGI(TAG, "[%s] connection open", this->parent_->address_str().c_str()); | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     case ESP_GATTC_CONNECT_EVT: { | ||||
|   | ||||
| @@ -26,7 +26,9 @@ void Am43::setup() { | ||||
| void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { | ||||
|   switch (event) { | ||||
|     case ESP_GATTC_OPEN_EVT: { | ||||
|       this->logged_in_ = false; | ||||
|       if (param->open.status == ESP_GATT_OK) { | ||||
|         this->logged_in_ = false; | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     case ESP_GATTC_DISCONNECT_EVT: { | ||||
|   | ||||
| @@ -43,9 +43,11 @@ service APIConnection { | ||||
|   rpc select_command (SelectCommandRequest) returns (void) {} | ||||
|   rpc button_command (ButtonCommandRequest) returns (void) {} | ||||
|   rpc lock_command (LockCommandRequest) returns (void) {} | ||||
|   rpc valve_command (ValveCommandRequest) returns (void) {} | ||||
|   rpc media_player_command (MediaPlayerCommandRequest) returns (void) {} | ||||
|   rpc date_command (DateCommandRequest) returns (void) {} | ||||
|   rpc time_command (TimeCommandRequest) returns (void) {} | ||||
|   rpc datetime_command (DateTimeCommandRequest) returns (void) {} | ||||
|  | ||||
|   rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {} | ||||
|   rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {} | ||||
| @@ -1145,6 +1147,9 @@ message MediaPlayerCommandRequest { | ||||
|  | ||||
|   bool has_media_url = 6; | ||||
|   string media_url = 7; | ||||
|  | ||||
|   bool has_announcement = 8; | ||||
|   bool announcement = 9; | ||||
| } | ||||
|  | ||||
| // ==================== BLUETOOTH ==================== | ||||
| @@ -1700,3 +1705,116 @@ message TimeCommandRequest { | ||||
|   uint32 minute = 3; | ||||
|   uint32 second = 4; | ||||
| } | ||||
|  | ||||
| // ==================== EVENT ==================== | ||||
| message ListEntitiesEventResponse { | ||||
|   option (id) = 107; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_EVENT"; | ||||
|  | ||||
|   string object_id = 1; | ||||
|   fixed32 key = 2; | ||||
|   string name = 3; | ||||
|   string unique_id = 4; | ||||
|  | ||||
|   string icon = 5; | ||||
|   bool disabled_by_default = 6; | ||||
|   EntityCategory entity_category = 7; | ||||
|   string device_class = 8; | ||||
|  | ||||
|   repeated string event_types = 9; | ||||
| } | ||||
| message EventResponse { | ||||
|   option (id) = 108; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_EVENT"; | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   string event_type = 2; | ||||
| } | ||||
|  | ||||
| // ==================== VALVE ==================== | ||||
| message ListEntitiesValveResponse { | ||||
|   option (id) = 109; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_VALVE"; | ||||
|  | ||||
|   string object_id = 1; | ||||
|   fixed32 key = 2; | ||||
|   string name = 3; | ||||
|   string unique_id = 4; | ||||
|  | ||||
|   string icon = 5; | ||||
|   bool disabled_by_default = 6; | ||||
|   EntityCategory entity_category = 7; | ||||
|   string device_class = 8; | ||||
|  | ||||
|   bool assumed_state = 9; | ||||
|   bool supports_position = 10; | ||||
|   bool supports_stop = 11; | ||||
| } | ||||
|  | ||||
| enum ValveOperation { | ||||
|   VALVE_OPERATION_IDLE = 0; | ||||
|   VALVE_OPERATION_IS_OPENING = 1; | ||||
|   VALVE_OPERATION_IS_CLOSING = 2; | ||||
| } | ||||
| message ValveStateResponse { | ||||
|   option (id) = 110; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_VALVE"; | ||||
|   option (no_delay) = true; | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   float position = 2; | ||||
|   ValveOperation current_operation = 3; | ||||
| } | ||||
|  | ||||
| message ValveCommandRequest { | ||||
|   option (id) = 111; | ||||
|   option (source) = SOURCE_CLIENT; | ||||
|   option (ifdef) = "USE_VALVE"; | ||||
|   option (no_delay) = true; | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   bool has_position = 2; | ||||
|   float position = 3; | ||||
|   bool stop = 4; | ||||
| } | ||||
|  | ||||
| // ==================== DATETIME DATETIME ==================== | ||||
| message ListEntitiesDateTimeResponse { | ||||
|   option (id) = 112; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_DATETIME_DATETIME"; | ||||
|  | ||||
|   string object_id = 1; | ||||
|   fixed32 key = 2; | ||||
|   string name = 3; | ||||
|   string unique_id = 4; | ||||
|  | ||||
|   string icon = 5; | ||||
|   bool disabled_by_default = 6; | ||||
|   EntityCategory entity_category = 7; | ||||
| } | ||||
| message DateTimeStateResponse { | ||||
|   option (id) = 113; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_DATETIME_DATETIME"; | ||||
|   option (no_delay) = true; | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   // If the datetime does not have a valid state yet. | ||||
|   // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller | ||||
|   bool missing_state = 2; | ||||
|   fixed32 epoch_seconds = 3; | ||||
| } | ||||
| message DateTimeCommandRequest { | ||||
|   option (id) = 114; | ||||
|   option (source) = SOURCE_CLIENT; | ||||
|   option (ifdef) = "USE_DATETIME_DATETIME"; | ||||
|   option (no_delay) = true; | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   fixed32 epoch_seconds = 2; | ||||
| } | ||||
|   | ||||
| @@ -772,6 +772,44 @@ void APIConnection::time_command(const TimeCommandRequest &msg) { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
| bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) { | ||||
|   if (!this->state_subscription_) | ||||
|     return false; | ||||
|  | ||||
|   DateTimeStateResponse resp{}; | ||||
|   resp.key = datetime->get_object_id_hash(); | ||||
|   resp.missing_state = !datetime->has_state(); | ||||
|   if (datetime->has_state()) { | ||||
|     ESPTime state = datetime->state_as_esptime(); | ||||
|     resp.epoch_seconds = state.timestamp; | ||||
|   } | ||||
|   return this->send_date_time_state_response(resp); | ||||
| } | ||||
| bool APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) { | ||||
|   ListEntitiesDateTimeResponse msg; | ||||
|   msg.key = datetime->get_object_id_hash(); | ||||
|   msg.object_id = datetime->get_object_id(); | ||||
|   if (datetime->has_own_name()) | ||||
|     msg.name = datetime->get_name(); | ||||
|   msg.unique_id = get_default_unique_id("datetime", datetime); | ||||
|   msg.icon = datetime->get_icon(); | ||||
|   msg.disabled_by_default = datetime->is_disabled_by_default(); | ||||
|   msg.entity_category = static_cast<enums::EntityCategory>(datetime->get_entity_category()); | ||||
|  | ||||
|   return this->send_list_entities_date_time_response(msg); | ||||
| } | ||||
| void APIConnection::datetime_command(const DateTimeCommandRequest &msg) { | ||||
|   datetime::DateTimeEntity *datetime = App.get_datetime_by_key(msg.key); | ||||
|   if (datetime == nullptr) | ||||
|     return; | ||||
|  | ||||
|   auto call = datetime->make_call(); | ||||
|   call.set_datetime(msg.epoch_seconds); | ||||
|   call.perform(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_TEXT | ||||
| bool APIConnection::send_text_state(text::Text *text, std::string state) { | ||||
|   if (!this->state_subscription_) | ||||
| @@ -915,6 +953,48 @@ void APIConnection::lock_command(const LockCommandRequest &msg) { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_VALVE | ||||
| bool APIConnection::send_valve_state(valve::Valve *valve) { | ||||
|   if (!this->state_subscription_) | ||||
|     return false; | ||||
|  | ||||
|   ValveStateResponse resp{}; | ||||
|   resp.key = valve->get_object_id_hash(); | ||||
|   resp.position = valve->position; | ||||
|   resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation); | ||||
|   return this->send_valve_state_response(resp); | ||||
| } | ||||
| bool APIConnection::send_valve_info(valve::Valve *valve) { | ||||
|   auto traits = valve->get_traits(); | ||||
|   ListEntitiesValveResponse msg; | ||||
|   msg.key = valve->get_object_id_hash(); | ||||
|   msg.object_id = valve->get_object_id(); | ||||
|   if (valve->has_own_name()) | ||||
|     msg.name = valve->get_name(); | ||||
|   msg.unique_id = get_default_unique_id("valve", valve); | ||||
|   msg.icon = valve->get_icon(); | ||||
|   msg.disabled_by_default = valve->is_disabled_by_default(); | ||||
|   msg.entity_category = static_cast<enums::EntityCategory>(valve->get_entity_category()); | ||||
|   msg.device_class = valve->get_device_class(); | ||||
|   msg.assumed_state = traits.get_is_assumed_state(); | ||||
|   msg.supports_position = traits.get_supports_position(); | ||||
|   msg.supports_stop = traits.get_supports_stop(); | ||||
|   return this->send_list_entities_valve_response(msg); | ||||
| } | ||||
| void APIConnection::valve_command(const ValveCommandRequest &msg) { | ||||
|   valve::Valve *valve = App.get_valve_by_key(msg.key); | ||||
|   if (valve == nullptr) | ||||
|     return; | ||||
|  | ||||
|   auto call = valve->make_call(); | ||||
|   if (msg.has_position) | ||||
|     call.set_position(msg.position); | ||||
|   if (msg.stop) | ||||
|     call.set_command_stop(); | ||||
|   call.perform(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) { | ||||
|   if (!this->state_subscription_) | ||||
| @@ -922,7 +1002,11 @@ bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_pla | ||||
|  | ||||
|   MediaPlayerStateResponse resp{}; | ||||
|   resp.key = media_player->get_object_id_hash(); | ||||
|   resp.state = static_cast<enums::MediaPlayerState>(media_player->state); | ||||
|  | ||||
|   media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING | ||||
|                                                     ? media_player::MEDIA_PLAYER_STATE_PLAYING | ||||
|                                                     : media_player->state; | ||||
|   resp.state = static_cast<enums::MediaPlayerState>(report_state); | ||||
|   resp.volume = media_player->volume; | ||||
|   resp.muted = media_player->is_muted(); | ||||
|   return this->send_media_player_state_response(resp); | ||||
| @@ -958,6 +1042,9 @@ void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) { | ||||
|   if (msg.has_media_url) { | ||||
|     call.set_media_url(msg.media_url); | ||||
|   } | ||||
|   if (msg.has_announcement) { | ||||
|     call.set_announcement(msg.announcement); | ||||
|   } | ||||
|   call.perform(); | ||||
| } | ||||
| #endif | ||||
| @@ -1167,6 +1254,30 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
| bool APIConnection::send_event(event::Event *event, std::string event_type) { | ||||
|   EventResponse resp{}; | ||||
|   resp.key = event->get_object_id_hash(); | ||||
|   resp.event_type = std::move(event_type); | ||||
|   return this->send_event_response(resp); | ||||
| } | ||||
| bool APIConnection::send_event_info(event::Event *event) { | ||||
|   ListEntitiesEventResponse msg; | ||||
|   msg.key = event->get_object_id_hash(); | ||||
|   msg.object_id = event->get_object_id(); | ||||
|   if (event->has_own_name()) | ||||
|     msg.name = event->get_name(); | ||||
|   msg.unique_id = get_default_unique_id("event", event); | ||||
|   msg.icon = event->get_icon(); | ||||
|   msg.disabled_by_default = event->is_disabled_by_default(); | ||||
|   msg.entity_category = static_cast<enums::EntityCategory>(event->get_entity_category()); | ||||
|   msg.device_class = event->get_device_class(); | ||||
|   for (const auto &event_type : event->get_event_types()) | ||||
|     msg.event_types.push_back(event_type); | ||||
|   return this->send_list_entities_event_response(msg); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| bool APIConnection::send_log_message(int level, const char *tag, const char *line) { | ||||
|   if (this->log_subscription_ < level) | ||||
|     return false; | ||||
|   | ||||
| @@ -82,6 +82,11 @@ class APIConnection : public APIServerConnection { | ||||
|   bool send_time_info(datetime::TimeEntity *time); | ||||
|   void time_command(const TimeCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|   bool send_datetime_state(datetime::DateTimeEntity *datetime); | ||||
|   bool send_datetime_info(datetime::DateTimeEntity *datetime); | ||||
|   void datetime_command(const DateTimeCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_TEXT | ||||
|   bool send_text_state(text::Text *text, std::string state); | ||||
|   bool send_text_info(text::Text *text); | ||||
| @@ -101,6 +106,11 @@ class APIConnection : public APIServerConnection { | ||||
|   bool send_lock_info(lock::Lock *a_lock); | ||||
|   void lock_command(const LockCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   bool send_valve_state(valve::Valve *valve); | ||||
|   bool send_valve_info(valve::Valve *valve); | ||||
|   void valve_command(const ValveCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   bool send_media_player_state(media_player::MediaPlayer *media_player); | ||||
|   bool send_media_player_info(media_player::MediaPlayer *media_player); | ||||
| @@ -148,6 +158,11 @@ class APIConnection : public APIServerConnection { | ||||
|   void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override; | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
|   bool send_event(event::Event *event, std::string event_type); | ||||
|   bool send_event_info(event::Event *event); | ||||
| #endif | ||||
|  | ||||
|   void on_disconnect_response(const DisconnectResponse &value) override; | ||||
|   void on_ping_response(const PingResponse &value) override { | ||||
|     // we initiated ping | ||||
|   | ||||
| @@ -537,6 +537,20 @@ template<> const char *proto_enum_to_string<enums::TextMode>(enums::TextMode val | ||||
|   } | ||||
| } | ||||
| #endif | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| template<> const char *proto_enum_to_string<enums::ValveOperation>(enums::ValveOperation value) { | ||||
|   switch (value) { | ||||
|     case enums::VALVE_OPERATION_IDLE: | ||||
|       return "VALVE_OPERATION_IDLE"; | ||||
|     case enums::VALVE_OPERATION_IS_OPENING: | ||||
|       return "VALVE_OPERATION_IS_OPENING"; | ||||
|     case enums::VALVE_OPERATION_IS_CLOSING: | ||||
|       return "VALVE_OPERATION_IS_CLOSING"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| #endif | ||||
| bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
| @@ -5239,6 +5253,14 @@ bool MediaPlayerCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt val | ||||
|       this->has_media_url = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 8: { | ||||
|       this->has_announcement = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->announcement = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -5275,6 +5297,8 @@ void MediaPlayerCommandRequest::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_float(5, this->volume); | ||||
|   buffer.encode_bool(6, this->has_media_url); | ||||
|   buffer.encode_string(7, this->media_url); | ||||
|   buffer.encode_bool(8, this->has_announcement); | ||||
|   buffer.encode_bool(9, this->announcement); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void MediaPlayerCommandRequest::dump_to(std::string &out) const { | ||||
| @@ -5309,6 +5333,14 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const { | ||||
|   out.append("  media_url: "); | ||||
|   out.append("'").append(this->media_url).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  has_announcement: "); | ||||
|   out.append(YESNO(this->has_announcement)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  announcement: "); | ||||
|   out.append(YESNO(this->announcement)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -7695,6 +7727,563 @@ void TimeCommandRequest::dump_to(std::string &out) const { | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool ListEntitiesEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 6: { | ||||
|       this->disabled_by_default = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 7: { | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ListEntitiesEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->object_id = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 3: { | ||||
|       this->name = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 4: { | ||||
|       this->unique_id = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 5: { | ||||
|       this->icon = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 8: { | ||||
|       this->device_class = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->event_types.push_back(value.as_string()); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ListEntitiesEventResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_string(1, this->object_id); | ||||
|   buffer.encode_fixed32(2, this->key); | ||||
|   buffer.encode_string(3, this->name); | ||||
|   buffer.encode_string(4, this->unique_id); | ||||
|   buffer.encode_string(5, this->icon); | ||||
|   buffer.encode_bool(6, this->disabled_by_default); | ||||
|   buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); | ||||
|   buffer.encode_string(8, this->device_class); | ||||
|   for (auto &it : this->event_types) { | ||||
|     buffer.encode_string(9, it, true); | ||||
|   } | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void ListEntitiesEventResponse::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("ListEntitiesEventResponse {\n"); | ||||
|   out.append("  object_id: "); | ||||
|   out.append("'").append(this->object_id).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->key); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  name: "); | ||||
|   out.append("'").append(this->name).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  unique_id: "); | ||||
|   out.append("'").append(this->unique_id).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  icon: "); | ||||
|   out.append("'").append(this->icon).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  disabled_by_default: "); | ||||
|   out.append(YESNO(this->disabled_by_default)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  entity_category: "); | ||||
|   out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_class: "); | ||||
|   out.append("'").append(this->device_class).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   for (const auto &it : this->event_types) { | ||||
|     out.append("  event_types: "); | ||||
|     out.append("'").append(it).append("'"); | ||||
|     out.append("\n"); | ||||
|   } | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool EventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->event_type = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool EventResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void EventResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_string(2, this->event_type); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void EventResponse::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("EventResponse {\n"); | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->key); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  event_type: "); | ||||
|   out.append("'").append(this->event_type).append("'"); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool ListEntitiesValveResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 6: { | ||||
|       this->disabled_by_default = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 7: { | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->assumed_state = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 10: { | ||||
|       this->supports_position = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 11: { | ||||
|       this->supports_stop = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ListEntitiesValveResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->object_id = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 3: { | ||||
|       this->name = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 4: { | ||||
|       this->unique_id = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 5: { | ||||
|       this->icon = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 8: { | ||||
|       this->device_class = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ListEntitiesValveResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void ListEntitiesValveResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_string(1, this->object_id); | ||||
|   buffer.encode_fixed32(2, this->key); | ||||
|   buffer.encode_string(3, this->name); | ||||
|   buffer.encode_string(4, this->unique_id); | ||||
|   buffer.encode_string(5, this->icon); | ||||
|   buffer.encode_bool(6, this->disabled_by_default); | ||||
|   buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); | ||||
|   buffer.encode_string(8, this->device_class); | ||||
|   buffer.encode_bool(9, this->assumed_state); | ||||
|   buffer.encode_bool(10, this->supports_position); | ||||
|   buffer.encode_bool(11, this->supports_stop); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void ListEntitiesValveResponse::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("ListEntitiesValveResponse {\n"); | ||||
|   out.append("  object_id: "); | ||||
|   out.append("'").append(this->object_id).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->key); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  name: "); | ||||
|   out.append("'").append(this->name).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  unique_id: "); | ||||
|   out.append("'").append(this->unique_id).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  icon: "); | ||||
|   out.append("'").append(this->icon).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  disabled_by_default: "); | ||||
|   out.append(YESNO(this->disabled_by_default)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  entity_category: "); | ||||
|   out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_class: "); | ||||
|   out.append("'").append(this->device_class).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  assumed_state: "); | ||||
|   out.append(YESNO(this->assumed_state)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  supports_position: "); | ||||
|   out.append(YESNO(this->supports_position)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  supports_stop: "); | ||||
|   out.append(YESNO(this->supports_stop)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool ValveStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 3: { | ||||
|       this->current_operation = value.as_enum<enums::ValveOperation>(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ValveStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     case 2: { | ||||
|       this->position = value.as_float(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void ValveStateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_float(2, this->position); | ||||
|   buffer.encode_enum<enums::ValveOperation>(3, this->current_operation); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void ValveStateResponse::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("ValveStateResponse {\n"); | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->key); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  position: "); | ||||
|   sprintf(buffer, "%g", this->position); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  current_operation: "); | ||||
|   out.append(proto_enum_to_string<enums::ValveOperation>(this->current_operation)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool ValveCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->has_position = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 4: { | ||||
|       this->stop = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ValveCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     case 3: { | ||||
|       this->position = value.as_float(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void ValveCommandRequest::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_bool(2, this->has_position); | ||||
|   buffer.encode_float(3, this->position); | ||||
|   buffer.encode_bool(4, this->stop); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void ValveCommandRequest::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("ValveCommandRequest {\n"); | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->key); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  has_position: "); | ||||
|   out.append(YESNO(this->has_position)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  position: "); | ||||
|   sprintf(buffer, "%g", this->position); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  stop: "); | ||||
|   out.append(YESNO(this->stop)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool ListEntitiesDateTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 6: { | ||||
|       this->disabled_by_default = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 7: { | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ListEntitiesDateTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->object_id = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 3: { | ||||
|       this->name = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 4: { | ||||
|       this->unique_id = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 5: { | ||||
|       this->icon = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ListEntitiesDateTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_string(1, this->object_id); | ||||
|   buffer.encode_fixed32(2, this->key); | ||||
|   buffer.encode_string(3, this->name); | ||||
|   buffer.encode_string(4, this->unique_id); | ||||
|   buffer.encode_string(5, this->icon); | ||||
|   buffer.encode_bool(6, this->disabled_by_default); | ||||
|   buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void ListEntitiesDateTimeResponse::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("ListEntitiesDateTimeResponse {\n"); | ||||
|   out.append("  object_id: "); | ||||
|   out.append("'").append(this->object_id).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->key); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  name: "); | ||||
|   out.append("'").append(this->name).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  unique_id: "); | ||||
|   out.append("'").append(this->unique_id).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  icon: "); | ||||
|   out.append("'").append(this->icon).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  disabled_by_default: "); | ||||
|   out.append(YESNO(this->disabled_by_default)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  entity_category: "); | ||||
|   out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool DateTimeStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->missing_state = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool DateTimeStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     case 3: { | ||||
|       this->epoch_seconds = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void DateTimeStateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_bool(2, this->missing_state); | ||||
|   buffer.encode_fixed32(3, this->epoch_seconds); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void DateTimeStateResponse::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("DateTimeStateResponse {\n"); | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->key); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  missing_state: "); | ||||
|   out.append(YESNO(this->missing_state)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  epoch_seconds: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->epoch_seconds); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     case 2: { | ||||
|       this->epoch_seconds = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void DateTimeCommandRequest::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_fixed32(2, this->epoch_seconds); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void DateTimeCommandRequest::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("DateTimeCommandRequest {\n"); | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->key); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  epoch_seconds: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->epoch_seconds); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| }  // namespace api | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -216,6 +216,11 @@ enum TextMode : uint32_t { | ||||
|   TEXT_MODE_TEXT = 0, | ||||
|   TEXT_MODE_PASSWORD = 1, | ||||
| }; | ||||
| enum ValveOperation : uint32_t { | ||||
|   VALVE_OPERATION_IDLE = 0, | ||||
|   VALVE_OPERATION_IS_OPENING = 1, | ||||
|   VALVE_OPERATION_IS_CLOSING = 2, | ||||
| }; | ||||
|  | ||||
| }  // namespace enums | ||||
|  | ||||
| @@ -1293,6 +1298,8 @@ class MediaPlayerCommandRequest : public ProtoMessage { | ||||
|   float volume{0.0f}; | ||||
|   bool has_media_url{false}; | ||||
|   std::string media_url{}; | ||||
|   bool has_announcement{false}; | ||||
|   bool announcement{false}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   void dump_to(std::string &out) const override; | ||||
| @@ -1969,6 +1976,137 @@ class TimeCommandRequest : public ProtoMessage { | ||||
|   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||
|   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; | ||||
| }; | ||||
| class ListEntitiesEventResponse : public ProtoMessage { | ||||
|  public: | ||||
|   std::string object_id{}; | ||||
|   uint32_t key{0}; | ||||
|   std::string name{}; | ||||
|   std::string unique_id{}; | ||||
|   std::string icon{}; | ||||
|   bool disabled_by_default{false}; | ||||
|   enums::EntityCategory entity_category{}; | ||||
|   std::string device_class{}; | ||||
|   std::vector<std::string> event_types{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   void dump_to(std::string &out) const override; | ||||
| #endif | ||||
|  | ||||
|  protected: | ||||
|   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||
|   bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; | ||||
|   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; | ||||
| }; | ||||
| class EventResponse : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0}; | ||||
|   std::string event_type{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   void dump_to(std::string &out) const override; | ||||
| #endif | ||||
|  | ||||
|  protected: | ||||
|   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||
|   bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; | ||||
| }; | ||||
| class ListEntitiesValveResponse : public ProtoMessage { | ||||
|  public: | ||||
|   std::string object_id{}; | ||||
|   uint32_t key{0}; | ||||
|   std::string name{}; | ||||
|   std::string unique_id{}; | ||||
|   std::string icon{}; | ||||
|   bool disabled_by_default{false}; | ||||
|   enums::EntityCategory entity_category{}; | ||||
|   std::string device_class{}; | ||||
|   bool assumed_state{false}; | ||||
|   bool supports_position{false}; | ||||
|   bool supports_stop{false}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   void dump_to(std::string &out) const override; | ||||
| #endif | ||||
|  | ||||
|  protected: | ||||
|   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||
|   bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; | ||||
|   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; | ||||
| }; | ||||
| class ValveStateResponse : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0}; | ||||
|   float position{0.0f}; | ||||
|   enums::ValveOperation current_operation{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   void dump_to(std::string &out) const override; | ||||
| #endif | ||||
|  | ||||
|  protected: | ||||
|   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||
|   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; | ||||
| }; | ||||
| class ValveCommandRequest : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0}; | ||||
|   bool has_position{false}; | ||||
|   float position{0.0f}; | ||||
|   bool stop{false}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   void dump_to(std::string &out) const override; | ||||
| #endif | ||||
|  | ||||
|  protected: | ||||
|   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||
|   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; | ||||
| }; | ||||
| class ListEntitiesDateTimeResponse : public ProtoMessage { | ||||
|  public: | ||||
|   std::string object_id{}; | ||||
|   uint32_t key{0}; | ||||
|   std::string name{}; | ||||
|   std::string unique_id{}; | ||||
|   std::string icon{}; | ||||
|   bool disabled_by_default{false}; | ||||
|   enums::EntityCategory entity_category{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   void dump_to(std::string &out) const override; | ||||
| #endif | ||||
|  | ||||
|  protected: | ||||
|   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||
|   bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; | ||||
|   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; | ||||
| }; | ||||
| class DateTimeStateResponse : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0}; | ||||
|   bool missing_state{false}; | ||||
|   uint32_t epoch_seconds{0}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   void dump_to(std::string &out) const override; | ||||
| #endif | ||||
|  | ||||
|  protected: | ||||
|   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||
|   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; | ||||
| }; | ||||
| class DateTimeCommandRequest : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0}; | ||||
|   uint32_t epoch_seconds{0}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   void dump_to(std::string &out) const override; | ||||
| #endif | ||||
|  | ||||
|  protected: | ||||
|   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace api | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -557,6 +557,58 @@ bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse & | ||||
| #endif | ||||
| #ifdef USE_DATETIME_TIME | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
| bool APIServerConnectionBase::send_list_entities_event_response(const ListEntitiesEventResponse &msg) { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   ESP_LOGVV(TAG, "send_list_entities_event_response: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|   return this->send_message_<ListEntitiesEventResponse>(msg, 107); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
| bool APIServerConnectionBase::send_event_response(const EventResponse &msg) { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   ESP_LOGVV(TAG, "send_event_response: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|   return this->send_message_<EventResponse>(msg, 108); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| bool APIServerConnectionBase::send_list_entities_valve_response(const ListEntitiesValveResponse &msg) { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   ESP_LOGVV(TAG, "send_list_entities_valve_response: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|   return this->send_message_<ListEntitiesValveResponse>(msg, 109); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| bool APIServerConnectionBase::send_valve_state_response(const ValveStateResponse &msg) { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   ESP_LOGVV(TAG, "send_valve_state_response: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|   return this->send_message_<ValveStateResponse>(msg, 110); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
| bool APIServerConnectionBase::send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg) { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   ESP_LOGVV(TAG, "send_list_entities_date_time_response: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|   return this->send_message_<ListEntitiesDateTimeResponse>(msg, 112); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
| bool APIServerConnectionBase::send_date_time_state_response(const DateTimeStateResponse &msg) { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   ESP_LOGVV(TAG, "send_date_time_state_response: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|   return this->send_message_<DateTimeStateResponse>(msg, 113); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
| #endif | ||||
| bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { | ||||
|   switch (msg_type) { | ||||
|     case 1: { | ||||
| @@ -1019,6 +1071,28 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, | ||||
|       ESP_LOGVV(TAG, "on_voice_assistant_audio: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|       this->on_voice_assistant_audio(msg); | ||||
| #endif | ||||
|       break; | ||||
|     } | ||||
|     case 111: { | ||||
| #ifdef USE_VALVE | ||||
|       ValveCommandRequest msg; | ||||
|       msg.decode(msg_data, msg_size); | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|       ESP_LOGVV(TAG, "on_valve_command_request: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|       this->on_valve_command_request(msg); | ||||
| #endif | ||||
|       break; | ||||
|     } | ||||
|     case 114: { | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|       DateTimeCommandRequest msg; | ||||
|       msg.decode(msg_data, msg_size); | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|       ESP_LOGVV(TAG, "on_date_time_command_request: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|       this->on_date_time_command_request(msg); | ||||
| #endif | ||||
|       break; | ||||
|     } | ||||
| @@ -1282,6 +1356,19 @@ void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) | ||||
|   this->lock_command(msg); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) { | ||||
|   if (!this->is_connection_setup()) { | ||||
|     this->on_no_setup_connection(); | ||||
|     return; | ||||
|   } | ||||
|   if (!this->is_authenticated()) { | ||||
|     this->on_unauthenticated_access(); | ||||
|     return; | ||||
|   } | ||||
|   this->valve_command(msg); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) { | ||||
|   if (!this->is_connection_setup()) { | ||||
| @@ -1321,6 +1408,19 @@ void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) | ||||
|   this->time_command(msg); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
| void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) { | ||||
|   if (!this->is_connection_setup()) { | ||||
|     this->on_no_setup_connection(); | ||||
|     return; | ||||
|   } | ||||
|   if (!this->is_authenticated()) { | ||||
|     this->on_unauthenticated_access(); | ||||
|     return; | ||||
|   } | ||||
|   this->datetime_command(msg); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_BLUETOOTH_PROXY | ||||
| void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request( | ||||
|     const SubscribeBluetoothLEAdvertisementsRequest &msg) { | ||||
|   | ||||
| @@ -279,6 +279,30 @@ class APIServerConnectionBase : public ProtoService { | ||||
| #endif | ||||
| #ifdef USE_DATETIME_TIME | ||||
|   virtual void on_time_command_request(const TimeCommandRequest &value){}; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   bool send_list_entities_event_response(const ListEntitiesEventResponse &msg); | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   bool send_event_response(const EventResponse &msg); | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   bool send_list_entities_valve_response(const ListEntitiesValveResponse &msg); | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   bool send_valve_state_response(const ValveStateResponse &msg); | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   virtual void on_valve_command_request(const ValveCommandRequest &value){}; | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|   bool send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg); | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|   bool send_date_time_state_response(const DateTimeStateResponse &msg); | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|   virtual void on_date_time_command_request(const DateTimeCommandRequest &value){}; | ||||
| #endif | ||||
|  protected: | ||||
|   bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; | ||||
| @@ -331,6 +355,9 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
| #ifdef USE_LOCK | ||||
|   virtual void lock_command(const LockCommandRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   virtual void valve_command(const ValveCommandRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0; | ||||
| #endif | ||||
| @@ -340,6 +367,9 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
| #ifdef USE_DATETIME_TIME | ||||
|   virtual void time_command(const TimeCommandRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|   virtual void datetime_command(const DateTimeCommandRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_BLUETOOTH_PROXY | ||||
|   virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0; | ||||
| #endif | ||||
| @@ -423,6 +453,9 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
| #ifdef USE_LOCK | ||||
|   void on_lock_command_request(const LockCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   void on_valve_command_request(const ValveCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override; | ||||
| #endif | ||||
| @@ -432,6 +465,9 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
| #ifdef USE_DATETIME_TIME | ||||
|   void on_time_command_request(const TimeCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|   void on_date_time_command_request(const DateTimeCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_BLUETOOTH_PROXY | ||||
|   void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; | ||||
| #endif | ||||
|   | ||||
| @@ -273,6 +273,15 @@ void APIServer::on_time_update(datetime::TimeEntity *obj) { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
| void APIServer::on_datetime_update(datetime::DateTimeEntity *obj) { | ||||
|   if (obj->is_internal()) | ||||
|     return; | ||||
|   for (auto &c : this->clients_) | ||||
|     c->send_datetime_state(obj); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_TEXT | ||||
| void APIServer::on_text_update(text::Text *obj, const std::string &state) { | ||||
|   if (obj->is_internal()) | ||||
| @@ -300,6 +309,15 @@ void APIServer::on_lock_update(lock::Lock *obj) { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_VALVE | ||||
| void APIServer::on_valve_update(valve::Valve *obj) { | ||||
|   if (obj->is_internal()) | ||||
|     return; | ||||
|   for (auto &c : this->clients_) | ||||
|     c->send_valve_state(obj); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| void APIServer::on_media_player_update(media_player::MediaPlayer *obj) { | ||||
|   if (obj->is_internal()) | ||||
| @@ -309,6 +327,13 @@ void APIServer::on_media_player_update(media_player::MediaPlayer *obj) { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
| void APIServer::on_event(event::Event *obj, const std::string &event_type) { | ||||
|   for (auto &c : this->clients_) | ||||
|     c->send_event(obj, event_type); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; } | ||||
| void APIServer::set_port(uint16_t port) { this->port_ = port; } | ||||
| APIServer *global_api_server = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | ||||
|   | ||||
| @@ -72,6 +72,9 @@ class APIServer : public Component, public Controller { | ||||
| #ifdef USE_DATETIME_TIME | ||||
|   void on_time_update(datetime::TimeEntity *obj) override; | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|   void on_datetime_update(datetime::DateTimeEntity *obj) override; | ||||
| #endif | ||||
| #ifdef USE_TEXT | ||||
|   void on_text_update(text::Text *obj, const std::string &state) override; | ||||
| #endif | ||||
| @@ -81,6 +84,9 @@ class APIServer : public Component, public Controller { | ||||
| #ifdef USE_LOCK | ||||
|   void on_lock_update(lock::Lock *obj) override; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   void on_valve_update(valve::Valve *obj) override; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   void on_media_player_update(media_player::MediaPlayer *obj) override; | ||||
| #endif | ||||
| @@ -93,6 +99,9 @@ class APIServer : public Component, public Controller { | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   void on_event(event::Event *obj, const std::string &event_type) override; | ||||
| #endif | ||||
|  | ||||
|   bool is_connected() const; | ||||
|  | ||||
|   | ||||
| @@ -38,6 +38,9 @@ bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) | ||||
| #ifdef USE_LOCK | ||||
| bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_info(a_lock); } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| bool ListEntitiesIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_info(valve); } | ||||
| #endif | ||||
|  | ||||
| bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); } | ||||
| ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {} | ||||
| @@ -68,6 +71,12 @@ bool ListEntitiesIterator::on_date(datetime::DateEntity *date) { return this->cl | ||||
| bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_info(time); } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
| bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *datetime) { | ||||
|   return this->client_->send_datetime_info(datetime); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_TEXT | ||||
| bool ListEntitiesIterator::on_text(text::Text *text) { return this->client_->send_text_info(text); } | ||||
| #endif | ||||
| @@ -86,6 +95,9 @@ bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmCont | ||||
|   return this->client_->send_alarm_control_panel_info(a_alarm_control_panel); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
| bool ListEntitiesIterator::on_event(event::Event *event) { return this->client_->send_event_info(event); } | ||||
| #endif | ||||
|  | ||||
| }  // namespace api | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -52,6 +52,9 @@ class ListEntitiesIterator : public ComponentIterator { | ||||
| #ifdef USE_DATETIME_TIME | ||||
|   bool on_time(datetime::TimeEntity *time) override; | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|   bool on_datetime(datetime::DateTimeEntity *datetime) override; | ||||
| #endif | ||||
| #ifdef USE_TEXT | ||||
|   bool on_text(text::Text *text) override; | ||||
| #endif | ||||
| @@ -61,11 +64,17 @@ class ListEntitiesIterator : public ComponentIterator { | ||||
| #ifdef USE_LOCK | ||||
|   bool on_lock(lock::Lock *a_lock) override; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   bool on_valve(valve::Valve *valve) override; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   bool on_media_player(media_player::MediaPlayer *media_player) override; | ||||
| #endif | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   bool on_event(event::Event *event) override; | ||||
| #endif | ||||
|   bool on_end() override; | ||||
|  | ||||
|   | ||||
| @@ -48,6 +48,11 @@ bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->cl | ||||
| #ifdef USE_DATETIME_TIME | ||||
| bool InitialStateIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_state(time); } | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
| bool InitialStateIterator::on_datetime(datetime::DateTimeEntity *datetime) { | ||||
|   return this->client_->send_datetime_state(datetime); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_TEXT | ||||
| bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); } | ||||
| #endif | ||||
| @@ -59,6 +64,9 @@ bool InitialStateIterator::on_select(select::Select *select) { | ||||
| #ifdef USE_LOCK | ||||
| bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| bool InitialStateIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_state(valve); } | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| bool InitialStateIterator::on_media_player(media_player::MediaPlayer *media_player) { | ||||
|   return this->client_->send_media_player_state(media_player); | ||||
|   | ||||
| @@ -49,6 +49,9 @@ class InitialStateIterator : public ComponentIterator { | ||||
| #ifdef USE_DATETIME_TIME | ||||
|   bool on_time(datetime::TimeEntity *time) override; | ||||
| #endif | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|   bool on_datetime(datetime::DateTimeEntity *datetime) override; | ||||
| #endif | ||||
| #ifdef USE_TEXT | ||||
|   bool on_text(text::Text *text) override; | ||||
| #endif | ||||
| @@ -58,11 +61,17 @@ class InitialStateIterator : public ComponentIterator { | ||||
| #ifdef USE_LOCK | ||||
|   bool on_lock(lock::Lock *a_lock) override; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   bool on_valve(valve::Valve *valve) override; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   bool on_media_player(media_player::MediaPlayer *media_player) override; | ||||
| #endif | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   bool on_event(event::Event *event) override { return true; }; | ||||
| #endif | ||||
|  protected: | ||||
|   APIConnection *client_; | ||||
|   | ||||
| @@ -54,7 +54,6 @@ FAST_FILTER = { | ||||
|     "LSB10": 7, | ||||
| } | ||||
|  | ||||
| CONF_ANGLE = "angle" | ||||
| CONF_RAW_ANGLE = "raw_angle" | ||||
| CONF_RAW_POSITION = "raw_position" | ||||
| CONF_WATCHDOG = "watchdog" | ||||
|   | ||||
| @@ -11,6 +11,7 @@ from esphome.const import ( | ||||
|     CONF_MAGNITUDE, | ||||
|     CONF_STATUS, | ||||
|     CONF_POSITION, | ||||
|     CONF_ANGLE, | ||||
| ) | ||||
| from .. import as5600_ns, AS5600Component | ||||
|  | ||||
| @@ -19,7 +20,6 @@ DEPENDENCIES = ["as5600"] | ||||
|  | ||||
| AS5600Sensor = as5600_ns.class_("AS5600Sensor", sensor.Sensor, cg.PollingComponent) | ||||
|  | ||||
| CONF_ANGLE = "angle" | ||||
| CONF_RAW_ANGLE = "raw_angle" | ||||
| CONF_RAW_POSITION = "raw_position" | ||||
| CONF_WATCHDOG = "watchdog" | ||||
|   | ||||
| @@ -22,7 +22,6 @@ CONF_AT581X_ID = "at581x_id" | ||||
|  | ||||
|  | ||||
| CONF_SENSING_DISTANCE = "sensing_distance" | ||||
| CONF_SENSITIVITY = "sensitivity" | ||||
| CONF_POWERON_SELFCHECK_TIME = "poweron_selfcheck_time" | ||||
| CONF_PROTECT_TIME = "protect_time" | ||||
| CONF_TRIGGER_BASE = "trigger_base" | ||||
|   | ||||
| @@ -51,15 +51,15 @@ void binary_sensor::MultiClickTrigger::on_state_(bool state) { | ||||
|   MultiClickTriggerEvent evt = this->timing_[*this->at_index_]; | ||||
|  | ||||
|   if (evt.max_length != 4294967294UL) { | ||||
|     ESP_LOGV(TAG, "A i=%u min=%" PRIu32 " max=%" PRIu32, *this->at_index_, evt.min_length, evt.max_length);  // NOLINT | ||||
|     ESP_LOGV(TAG, "A i=%zu min=%" PRIu32 " max=%" PRIu32, *this->at_index_, evt.min_length, evt.max_length);  // NOLINT | ||||
|     this->schedule_is_valid_(evt.min_length); | ||||
|     this->schedule_is_not_valid_(evt.max_length); | ||||
|   } else if (*this->at_index_ + 1 != this->timing_.size()) { | ||||
|     ESP_LOGV(TAG, "B i=%u min=%" PRIu32, *this->at_index_, evt.min_length);  // NOLINT | ||||
|     ESP_LOGV(TAG, "B i=%zu min=%" PRIu32, *this->at_index_, evt.min_length);  // NOLINT | ||||
|     this->cancel_timeout("is_not_valid"); | ||||
|     this->schedule_is_valid_(evt.min_length); | ||||
|   } else { | ||||
|     ESP_LOGV(TAG, "C i=%u min=%" PRIu32, *this->at_index_, evt.min_length);  // NOLINT | ||||
|     ESP_LOGV(TAG, "C i=%zu min=%" PRIu32, *this->at_index_, evt.min_length);  // NOLINT | ||||
|     this->is_valid_ = false; | ||||
|     this->cancel_timeout("is_not_valid"); | ||||
|     this->set_timeout("trigger", evt.min_length, [this]() { this->trigger_(); }); | ||||
|   | ||||
| @@ -6,6 +6,7 @@ from esphome.const import ( | ||||
|     CONF_ENERGY, | ||||
|     CONF_EXTERNAL_TEMPERATURE, | ||||
|     CONF_ID, | ||||
|     CONF_INTERNAL_TEMPERATURE, | ||||
|     CONF_POWER, | ||||
|     CONF_VOLTAGE, | ||||
|     DEVICE_CLASS_CURRENT, | ||||
| @@ -24,7 +25,6 @@ from esphome.const import ( | ||||
|  | ||||
| DEPENDENCIES = ["uart"] | ||||
|  | ||||
| CONF_INTERNAL_TEMPERATURE = "internal_temperature" | ||||
|  | ||||
| bl0940_ns = cg.esphome_ns.namespace("bl0940") | ||||
| BL0940 = bl0940_ns.class_("BL0940", cg.PollingComponent, uart.UARTDevice) | ||||
|   | ||||
| @@ -25,9 +25,13 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga | ||||
|       this->proxy_->send_connections_free(); | ||||
|       break; | ||||
|     } | ||||
|     case ESP_GATTC_CLOSE_EVT: { | ||||
|       this->proxy_->send_device_connection(this->address_, false, 0, param->close.reason); | ||||
|       this->set_address(0); | ||||
|       this->proxy_->send_connections_free(); | ||||
|       break; | ||||
|     } | ||||
|     case ESP_GATTC_OPEN_EVT: { | ||||
|       if (param->open.conn_id != this->conn_id_) | ||||
|         break; | ||||
|       if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) { | ||||
|         this->proxy_->send_device_connection(this->address_, false, 0, param->open.status); | ||||
|         this->set_address(0); | ||||
| @@ -39,9 +43,8 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga | ||||
|       this->seen_mtu_or_services_ = false; | ||||
|       break; | ||||
|     } | ||||
|     case ESP_GATTC_CFG_MTU_EVT: { | ||||
|       if (param->cfg_mtu.conn_id != this->conn_id_) | ||||
|         break; | ||||
|     case ESP_GATTC_CFG_MTU_EVT: | ||||
|     case ESP_GATTC_SEARCH_CMPL_EVT: { | ||||
|       if (!this->seen_mtu_or_services_) { | ||||
|         // We don't know if we will get the MTU or the services first, so | ||||
|         // only send the device connection true if we have already received | ||||
| @@ -53,24 +56,8 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga | ||||
|       this->proxy_->send_connections_free(); | ||||
|       break; | ||||
|     } | ||||
|     case ESP_GATTC_SEARCH_CMPL_EVT: { | ||||
|       if (param->search_cmpl.conn_id != this->conn_id_) | ||||
|         break; | ||||
|       if (!this->seen_mtu_or_services_) { | ||||
|         // We don't know if we will get the MTU or the services first, so | ||||
|         // only send the device connection true if we have already received | ||||
|         // the mtu. | ||||
|         this->seen_mtu_or_services_ = true; | ||||
|         break; | ||||
|       } | ||||
|       this->proxy_->send_device_connection(this->address_, true, this->mtu_); | ||||
|       this->proxy_->send_connections_free(); | ||||
|       break; | ||||
|     } | ||||
|     case ESP_GATTC_READ_DESCR_EVT: | ||||
|     case ESP_GATTC_READ_CHAR_EVT: { | ||||
|       if (param->read.conn_id != this->conn_id_) | ||||
|         break; | ||||
|       if (param->read.status != ESP_GATT_OK) { | ||||
|         ESP_LOGW(TAG, "[%d] [%s] Error reading char/descriptor at handle 0x%2X, status=%d", this->connection_index_, | ||||
|                  this->address_str_.c_str(), param->read.handle, param->read.status); | ||||
| @@ -89,8 +76,6 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga | ||||
|     } | ||||
|     case ESP_GATTC_WRITE_CHAR_EVT: | ||||
|     case ESP_GATTC_WRITE_DESCR_EVT: { | ||||
|       if (param->write.conn_id != this->conn_id_) | ||||
|         break; | ||||
|       if (param->write.status != ESP_GATT_OK) { | ||||
|         ESP_LOGW(TAG, "[%d] [%s] Error writing char/descriptor at handle 0x%2X, status=%d", this->connection_index_, | ||||
|                  this->address_str_.c_str(), param->write.handle, param->write.status); | ||||
| @@ -131,8 +116,6 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga | ||||
|       break; | ||||
|     } | ||||
|     case ESP_GATTC_NOTIFY_EVT: { | ||||
|       if (param->notify.conn_id != this->conn_id_) | ||||
|         break; | ||||
|       ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_NOTIFY_EVT: handle=0x%2X", this->connection_index_, this->address_str_.c_str(), | ||||
|                param->notify.handle); | ||||
|       api::BluetoothGATTNotifyDataResponse resp; | ||||
|   | ||||
| @@ -1 +1,108 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import sensor | ||||
| from esphome.const import ( | ||||
|     CONF_HUMIDITY, | ||||
|     CONF_ID, | ||||
|     CONF_IIR_FILTER, | ||||
|     CONF_OVERSAMPLING, | ||||
|     CONF_PRESSURE, | ||||
|     CONF_TEMPERATURE, | ||||
|     DEVICE_CLASS_HUMIDITY, | ||||
|     DEVICE_CLASS_PRESSURE, | ||||
|     DEVICE_CLASS_TEMPERATURE, | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
|     UNIT_CELSIUS, | ||||
|     UNIT_HECTOPASCAL, | ||||
|     UNIT_PERCENT, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
|  | ||||
| bme280_ns = cg.esphome_ns.namespace("bme280_base") | ||||
| BME280Oversampling = bme280_ns.enum("BME280Oversampling") | ||||
| OVERSAMPLING_OPTIONS = { | ||||
|     "NONE": BME280Oversampling.BME280_OVERSAMPLING_NONE, | ||||
|     "1X": BME280Oversampling.BME280_OVERSAMPLING_1X, | ||||
|     "2X": BME280Oversampling.BME280_OVERSAMPLING_2X, | ||||
|     "4X": BME280Oversampling.BME280_OVERSAMPLING_4X, | ||||
|     "8X": BME280Oversampling.BME280_OVERSAMPLING_8X, | ||||
|     "16X": BME280Oversampling.BME280_OVERSAMPLING_16X, | ||||
| } | ||||
|  | ||||
| BME280IIRFilter = bme280_ns.enum("BME280IIRFilter") | ||||
| IIR_FILTER_OPTIONS = { | ||||
|     "OFF": BME280IIRFilter.BME280_IIR_FILTER_OFF, | ||||
|     "2X": BME280IIRFilter.BME280_IIR_FILTER_2X, | ||||
|     "4X": BME280IIRFilter.BME280_IIR_FILTER_4X, | ||||
|     "8X": BME280IIRFilter.BME280_IIR_FILTER_8X, | ||||
|     "16X": BME280IIRFilter.BME280_IIR_FILTER_16X, | ||||
| } | ||||
|  | ||||
| CONFIG_SCHEMA_BASE = cv.Schema( | ||||
|     { | ||||
|         cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( | ||||
|             unit_of_measurement=UNIT_CELSIUS, | ||||
|             accuracy_decimals=1, | ||||
|             device_class=DEVICE_CLASS_TEMPERATURE, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|         ).extend( | ||||
|             { | ||||
|                 cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( | ||||
|                     OVERSAMPLING_OPTIONS, upper=True | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_PRESSURE): sensor.sensor_schema( | ||||
|             unit_of_measurement=UNIT_HECTOPASCAL, | ||||
|             accuracy_decimals=1, | ||||
|             device_class=DEVICE_CLASS_PRESSURE, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|         ).extend( | ||||
|             { | ||||
|                 cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( | ||||
|                     OVERSAMPLING_OPTIONS, upper=True | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( | ||||
|             unit_of_measurement=UNIT_PERCENT, | ||||
|             accuracy_decimals=1, | ||||
|             device_class=DEVICE_CLASS_HUMIDITY, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|         ).extend( | ||||
|             { | ||||
|                 cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( | ||||
|                     OVERSAMPLING_OPTIONS, upper=True | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( | ||||
|             IIR_FILTER_OPTIONS, upper=True | ||||
|         ), | ||||
|     } | ||||
| ).extend(cv.polling_component_schema("60s")) | ||||
|  | ||||
|  | ||||
| async def to_code_base(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await cg.register_component(var, config) | ||||
|  | ||||
|     if temperature_config := config.get(CONF_TEMPERATURE): | ||||
|         sens = await sensor.new_sensor(temperature_config) | ||||
|         cg.add(var.set_temperature_sensor(sens)) | ||||
|         cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING])) | ||||
|  | ||||
|     if pressure_config := config.get(CONF_PRESSURE): | ||||
|         sens = await sensor.new_sensor(pressure_config) | ||||
|         cg.add(var.set_pressure_sensor(sens)) | ||||
|         cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING])) | ||||
|  | ||||
|     if humidity_config := config.get(CONF_HUMIDITY): | ||||
|         sens = await sensor.new_sensor(humidity_config) | ||||
|         cg.add(var.set_humidity_sensor(sens)) | ||||
|         cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING])) | ||||
|  | ||||
|     cg.add(var.set_iir_filter(config[CONF_IIR_FILTER])) | ||||
|  | ||||
|     return var | ||||
|   | ||||
| @@ -1,106 +0,0 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import sensor | ||||
| from esphome.const import ( | ||||
|     CONF_HUMIDITY, | ||||
|     CONF_ID, | ||||
|     CONF_IIR_FILTER, | ||||
|     CONF_OVERSAMPLING, | ||||
|     CONF_PRESSURE, | ||||
|     CONF_TEMPERATURE, | ||||
|     DEVICE_CLASS_HUMIDITY, | ||||
|     DEVICE_CLASS_PRESSURE, | ||||
|     DEVICE_CLASS_TEMPERATURE, | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
|     UNIT_CELSIUS, | ||||
|     UNIT_HECTOPASCAL, | ||||
|     UNIT_PERCENT, | ||||
| ) | ||||
|  | ||||
| bme280_ns = cg.esphome_ns.namespace("bme280_base") | ||||
| BME280Oversampling = bme280_ns.enum("BME280Oversampling") | ||||
| OVERSAMPLING_OPTIONS = { | ||||
|     "NONE": BME280Oversampling.BME280_OVERSAMPLING_NONE, | ||||
|     "1X": BME280Oversampling.BME280_OVERSAMPLING_1X, | ||||
|     "2X": BME280Oversampling.BME280_OVERSAMPLING_2X, | ||||
|     "4X": BME280Oversampling.BME280_OVERSAMPLING_4X, | ||||
|     "8X": BME280Oversampling.BME280_OVERSAMPLING_8X, | ||||
|     "16X": BME280Oversampling.BME280_OVERSAMPLING_16X, | ||||
| } | ||||
|  | ||||
| BME280IIRFilter = bme280_ns.enum("BME280IIRFilter") | ||||
| IIR_FILTER_OPTIONS = { | ||||
|     "OFF": BME280IIRFilter.BME280_IIR_FILTER_OFF, | ||||
|     "2X": BME280IIRFilter.BME280_IIR_FILTER_2X, | ||||
|     "4X": BME280IIRFilter.BME280_IIR_FILTER_4X, | ||||
|     "8X": BME280IIRFilter.BME280_IIR_FILTER_8X, | ||||
|     "16X": BME280IIRFilter.BME280_IIR_FILTER_16X, | ||||
| } | ||||
|  | ||||
| CONFIG_SCHEMA_BASE = cv.Schema( | ||||
|     { | ||||
|         cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( | ||||
|             unit_of_measurement=UNIT_CELSIUS, | ||||
|             accuracy_decimals=1, | ||||
|             device_class=DEVICE_CLASS_TEMPERATURE, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|         ).extend( | ||||
|             { | ||||
|                 cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( | ||||
|                     OVERSAMPLING_OPTIONS, upper=True | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_PRESSURE): sensor.sensor_schema( | ||||
|             unit_of_measurement=UNIT_HECTOPASCAL, | ||||
|             accuracy_decimals=1, | ||||
|             device_class=DEVICE_CLASS_PRESSURE, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|         ).extend( | ||||
|             { | ||||
|                 cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( | ||||
|                     OVERSAMPLING_OPTIONS, upper=True | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( | ||||
|             unit_of_measurement=UNIT_PERCENT, | ||||
|             accuracy_decimals=1, | ||||
|             device_class=DEVICE_CLASS_HUMIDITY, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|         ).extend( | ||||
|             { | ||||
|                 cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( | ||||
|                     OVERSAMPLING_OPTIONS, upper=True | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( | ||||
|             IIR_FILTER_OPTIONS, upper=True | ||||
|         ), | ||||
|     } | ||||
| ).extend(cv.polling_component_schema("60s")) | ||||
|  | ||||
|  | ||||
| async def to_code(config, func=None): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await cg.register_component(var, config) | ||||
|     if func is not None: | ||||
|         await func(var, config) | ||||
|  | ||||
|     if temperature_config := config.get(CONF_TEMPERATURE): | ||||
|         sens = await sensor.new_sensor(temperature_config) | ||||
|         cg.add(var.set_temperature_sensor(sens)) | ||||
|         cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING])) | ||||
|  | ||||
|     if pressure_config := config.get(CONF_PRESSURE): | ||||
|         sens = await sensor.new_sensor(pressure_config) | ||||
|         cg.add(var.set_pressure_sensor(sens)) | ||||
|         cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING])) | ||||
|  | ||||
|     if humidity_config := config.get(CONF_HUMIDITY): | ||||
|         sens = await sensor.new_sensor(humidity_config) | ||||
|         cg.add(var.set_humidity_sensor(sens)) | ||||
|         cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING])) | ||||
|  | ||||
|     cg.add(var.set_iir_filter(config[CONF_IIR_FILTER])) | ||||
| @@ -1,9 +1,10 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import i2c | ||||
| from ..bme280_base.sensor import to_code as to_code_base, cv, CONFIG_SCHEMA_BASE | ||||
| from ..bme280_base import to_code_base, CONFIG_SCHEMA_BASE | ||||
|  | ||||
| DEPENDENCIES = ["i2c"] | ||||
| AUTO_LOAD = ["bme280_base"] | ||||
| DEPENDENCIES = ["i2c"] | ||||
|  | ||||
| bme280_ns = cg.esphome_ns.namespace("bme280_i2c") | ||||
| BME280I2CComponent = bme280_ns.class_( | ||||
| @@ -16,4 +17,5 @@ CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend( | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     await to_code_base(config, func=i2c.register_i2c_device) | ||||
|     var = await to_code_base(config) | ||||
|     await i2c.register_i2c_device(var, config) | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| CODEOWNERS = ["@apbodrov"] | ||||
|   | ||||
| @@ -4,19 +4,19 @@ | ||||
| #include "bme280_spi.h" | ||||
| #include <esphome/components/bme280_base/bme280_base.h> | ||||
|  | ||||
| int set_bit(uint8_t num, int position) { | ||||
| namespace esphome { | ||||
| namespace bme280_spi { | ||||
|  | ||||
| uint8_t set_bit(uint8_t num, int position) { | ||||
|   int mask = 1 << position; | ||||
|   return num | mask; | ||||
| } | ||||
|  | ||||
| int clear_bit(uint8_t num, int position) { | ||||
| uint8_t clear_bit(uint8_t num, int position) { | ||||
|   int mask = 1 << position; | ||||
|   return num & ~mask; | ||||
| } | ||||
|  | ||||
| namespace esphome { | ||||
| namespace bme280_spi { | ||||
|  | ||||
| void BME280SPIComponent::setup() { | ||||
|   this->spi_setup(); | ||||
|   BME280Component::setup(); | ||||
| @@ -30,34 +30,33 @@ void BME280SPIComponent::setup() { | ||||
|  | ||||
| bool BME280SPIComponent::read_byte(uint8_t a_register, uint8_t *data) { | ||||
|   this->enable(); | ||||
|   // cause: *data = this->delegate_->transfer(tmp) doesnt work | ||||
|   this->delegate_->transfer(set_bit(a_register, 7)); | ||||
|   *data = this->delegate_->transfer(0); | ||||
|   this->transfer_byte(set_bit(a_register, 7)); | ||||
|   *data = this->transfer_byte(0); | ||||
|   this->disable(); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool BME280SPIComponent::write_byte(uint8_t a_register, uint8_t data) { | ||||
|   this->enable(); | ||||
|   this->delegate_->transfer(clear_bit(a_register, 7)); | ||||
|   this->delegate_->transfer(data); | ||||
|   this->transfer_byte(clear_bit(a_register, 7)); | ||||
|   this->transfer_byte(data); | ||||
|   this->disable(); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool BME280SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) { | ||||
|   this->enable(); | ||||
|   this->delegate_->transfer(set_bit(a_register, 7)); | ||||
|   this->delegate_->read_array(data, len); | ||||
|   this->transfer_byte(set_bit(a_register, 7)); | ||||
|   this->read_array(data, len); | ||||
|   this->disable(); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool BME280SPIComponent::read_byte_16(uint8_t a_register, uint16_t *data) { | ||||
|   this->enable(); | ||||
|   this->delegate_->transfer(set_bit(a_register, 7)); | ||||
|   ((uint8_t *) data)[1] = this->delegate_->transfer(0); | ||||
|   ((uint8_t *) data)[0] = this->delegate_->transfer(0); | ||||
|   this->transfer_byte(set_bit(a_register, 7)); | ||||
|   ((uint8_t *) data)[1] = this->transfer_byte(0); | ||||
|   ((uint8_t *) data)[0] = this->transfer_byte(0); | ||||
|   this->disable(); | ||||
|   return true; | ||||
| } | ||||
|   | ||||
| @@ -1,13 +1,11 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import spi | ||||
| from esphome.components.bme280_base.sensor import ( | ||||
|     to_code as to_code_base, | ||||
|     cv, | ||||
|     CONFIG_SCHEMA_BASE, | ||||
| ) | ||||
| from ..bme280_base import to_code_base, CONFIG_SCHEMA_BASE | ||||
|  | ||||
| DEPENDENCIES = ["spi"] | ||||
| AUTO_LOAD = ["bme280_base"] | ||||
| CODEOWNERS = ["@apbodrov"] | ||||
| DEPENDENCIES = ["spi"] | ||||
|  | ||||
|  | ||||
| bme280_spi_ns = cg.esphome_ns.namespace("bme280_spi") | ||||
| @@ -21,4 +19,5 @@ CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(spi.spi_device_schema()).extend( | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     await to_code_base(config, func=spi.register_spi_device) | ||||
|     var = await to_code_base(config) | ||||
|     await spi.register_spi_device(var, config) | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import i2c, esp32 | ||||
| from esphome.const import CONF_ID | ||||
| from esphome.const import CONF_ID, CONF_TEMPERATURE_OFFSET | ||||
|  | ||||
| CODEOWNERS = ["@trvrnrth"] | ||||
| DEPENDENCIES = ["i2c"] | ||||
| @@ -9,7 +9,6 @@ AUTO_LOAD = ["sensor", "text_sensor"] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| CONF_BME680_BSEC_ID = "bme680_bsec_id" | ||||
| CONF_TEMPERATURE_OFFSET = "temperature_offset" | ||||
| CONF_IAQ_MODE = "iaq_mode" | ||||
| CONF_SUPPLY_VOLTAGE = "supply_voltage" | ||||
| CONF_SAMPLE_RATE = "sample_rate" | ||||
|   | ||||
| @@ -1,102 +1,7 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import i2c, sensor | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_IIR_FILTER, | ||||
|     CONF_OVERSAMPLING, | ||||
|     CONF_PRESSURE, | ||||
|     CONF_TEMPERATURE, | ||||
|     DEVICE_CLASS_PRESSURE, | ||||
|     DEVICE_CLASS_TEMPERATURE, | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
|     UNIT_CELSIUS, | ||||
|     UNIT_HECTOPASCAL, | ||||
|  | ||||
| CODEOWNERS = ["@latonita"] | ||||
|  | ||||
| CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid( | ||||
|     "The bmp3xx sensor component has been renamed to bmp3xx_i2c." | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@martgras"] | ||||
| DEPENDENCIES = ["i2c"] | ||||
|  | ||||
| bmp3xx_ns = cg.esphome_ns.namespace("bmp3xx") | ||||
| Oversampling = bmp3xx_ns.enum("Oversampling") | ||||
| OVERSAMPLING_OPTIONS = { | ||||
|     "NONE": Oversampling.OVERSAMPLING_NONE, | ||||
|     "2X": Oversampling.OVERSAMPLING_X2, | ||||
|     "4X": Oversampling.OVERSAMPLING_X4, | ||||
|     "8X": Oversampling.OVERSAMPLING_X8, | ||||
|     "16X": Oversampling.OVERSAMPLING_X16, | ||||
|     "32X": Oversampling.OVERSAMPLING_X32, | ||||
| } | ||||
|  | ||||
| IIRFilter = bmp3xx_ns.enum("IIRFilter") | ||||
| IIR_FILTER_OPTIONS = { | ||||
|     "OFF": IIRFilter.IIR_FILTER_OFF, | ||||
|     "2X": IIRFilter.IIR_FILTER_2, | ||||
|     "4X": IIRFilter.IIR_FILTER_4, | ||||
|     "8X": IIRFilter.IIR_FILTER_8, | ||||
|     "16X": IIRFilter.IIR_FILTER_16, | ||||
|     "32X": IIRFilter.IIR_FILTER_32, | ||||
|     "64X": IIRFilter.IIR_FILTER_64, | ||||
|     "128X": IIRFilter.IIR_FILTER_128, | ||||
| } | ||||
|  | ||||
| BMP3XXComponent = bmp3xx_ns.class_( | ||||
|     "BMP3XXComponent", cg.PollingComponent, i2c.I2CDevice | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = ( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(BMP3XXComponent), | ||||
|             cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( | ||||
|                 unit_of_measurement=UNIT_CELSIUS, | ||||
|                 accuracy_decimals=1, | ||||
|                 device_class=DEVICE_CLASS_TEMPERATURE, | ||||
|                 state_class=STATE_CLASS_MEASUREMENT, | ||||
|             ).extend( | ||||
|                 { | ||||
|                     cv.Optional(CONF_OVERSAMPLING, default="2X"): cv.enum( | ||||
|                         OVERSAMPLING_OPTIONS, upper=True | ||||
|                     ), | ||||
|                 } | ||||
|             ), | ||||
|             cv.Optional(CONF_PRESSURE): sensor.sensor_schema( | ||||
|                 unit_of_measurement=UNIT_HECTOPASCAL, | ||||
|                 accuracy_decimals=1, | ||||
|                 device_class=DEVICE_CLASS_PRESSURE, | ||||
|                 state_class=STATE_CLASS_MEASUREMENT, | ||||
|             ).extend( | ||||
|                 { | ||||
|                     cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( | ||||
|                         OVERSAMPLING_OPTIONS, upper=True | ||||
|                     ), | ||||
|                 } | ||||
|             ), | ||||
|             cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( | ||||
|                 IIR_FILTER_OPTIONS, upper=True | ||||
|             ), | ||||
|         } | ||||
|     ) | ||||
|     .extend(cv.polling_component_schema("60s")) | ||||
|     .extend(i2c.i2c_device_schema(0x77)) | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await cg.register_component(var, config) | ||||
|     await i2c.register_i2c_device(var, config) | ||||
|     cg.add(var.set_iir_filter_config(config[CONF_IIR_FILTER])) | ||||
|     if temperature_config := config.get(CONF_TEMPERATURE): | ||||
|         sens = await sensor.new_sensor(temperature_config) | ||||
|         cg.add(var.set_temperature_sensor(sens)) | ||||
|         cg.add( | ||||
|             var.set_temperature_oversampling_config( | ||||
|                 temperature_config[CONF_OVERSAMPLING] | ||||
|             ) | ||||
|         ) | ||||
|  | ||||
|     if pressure_config := config.get(CONF_PRESSURE): | ||||
|         sens = await sensor.new_sensor(pressure_config) | ||||
|         cg.add(var.set_pressure_sensor(sens)) | ||||
|         cg.add(var.set_pressure_oversampling_config(pressure_config[CONF_OVERSAMPLING])) | ||||
|   | ||||
							
								
								
									
										95
									
								
								esphome/components/bmp3xx_base/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								esphome/components/bmp3xx_base/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import sensor | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_IIR_FILTER, | ||||
|     CONF_OVERSAMPLING, | ||||
|     CONF_PRESSURE, | ||||
|     CONF_TEMPERATURE, | ||||
|     DEVICE_CLASS_PRESSURE, | ||||
|     DEVICE_CLASS_TEMPERATURE, | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
|     UNIT_CELSIUS, | ||||
|     UNIT_HECTOPASCAL, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@martgras", "@latonita"] | ||||
|  | ||||
| bmp3xx_ns = cg.esphome_ns.namespace("bmp3xx_base") | ||||
| Oversampling = bmp3xx_ns.enum("Oversampling") | ||||
| OVERSAMPLING_OPTIONS = { | ||||
|     "NONE": Oversampling.OVERSAMPLING_NONE, | ||||
|     "2X": Oversampling.OVERSAMPLING_X2, | ||||
|     "4X": Oversampling.OVERSAMPLING_X4, | ||||
|     "8X": Oversampling.OVERSAMPLING_X8, | ||||
|     "16X": Oversampling.OVERSAMPLING_X16, | ||||
|     "32X": Oversampling.OVERSAMPLING_X32, | ||||
| } | ||||
|  | ||||
| IIRFilter = bmp3xx_ns.enum("IIRFilter") | ||||
| IIR_FILTER_OPTIONS = { | ||||
|     "OFF": IIRFilter.IIR_FILTER_OFF, | ||||
|     "2X": IIRFilter.IIR_FILTER_2, | ||||
|     "4X": IIRFilter.IIR_FILTER_4, | ||||
|     "8X": IIRFilter.IIR_FILTER_8, | ||||
|     "16X": IIRFilter.IIR_FILTER_16, | ||||
|     "32X": IIRFilter.IIR_FILTER_32, | ||||
|     "64X": IIRFilter.IIR_FILTER_64, | ||||
|     "128X": IIRFilter.IIR_FILTER_128, | ||||
| } | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA_BASE = cv.Schema( | ||||
|     { | ||||
|         cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( | ||||
|             unit_of_measurement=UNIT_CELSIUS, | ||||
|             accuracy_decimals=1, | ||||
|             device_class=DEVICE_CLASS_TEMPERATURE, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|         ).extend( | ||||
|             { | ||||
|                 cv.Optional(CONF_OVERSAMPLING, default="2X"): cv.enum( | ||||
|                     OVERSAMPLING_OPTIONS, upper=True | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_PRESSURE): sensor.sensor_schema( | ||||
|             unit_of_measurement=UNIT_HECTOPASCAL, | ||||
|             accuracy_decimals=1, | ||||
|             device_class=DEVICE_CLASS_PRESSURE, | ||||
|             state_class=STATE_CLASS_MEASUREMENT, | ||||
|         ).extend( | ||||
|             { | ||||
|                 cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( | ||||
|                     OVERSAMPLING_OPTIONS, upper=True | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( | ||||
|             IIR_FILTER_OPTIONS, upper=True | ||||
|         ), | ||||
|     } | ||||
| ).extend(cv.polling_component_schema("60s")) | ||||
|  | ||||
|  | ||||
| async def to_code_base(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await cg.register_component(var, config) | ||||
|  | ||||
|     cg.add(var.set_iir_filter_config(config[CONF_IIR_FILTER])) | ||||
|     if temperature_config := config.get(CONF_TEMPERATURE): | ||||
|         sens = await sensor.new_sensor(temperature_config) | ||||
|         cg.add(var.set_temperature_sensor(sens)) | ||||
|         cg.add( | ||||
|             var.set_temperature_oversampling_config( | ||||
|                 temperature_config[CONF_OVERSAMPLING] | ||||
|             ) | ||||
|         ) | ||||
|  | ||||
|     if pressure_config := config.get(CONF_PRESSURE): | ||||
|         sens = await sensor.new_sensor(pressure_config) | ||||
|         cg.add(var.set_pressure_sensor(sens)) | ||||
|         cg.add(var.set_pressure_oversampling_config(pressure_config[CONF_OVERSAMPLING])) | ||||
|  | ||||
|     return var | ||||
| @@ -5,13 +5,13 @@ | ||||
|   http://github.com/MartinL1/BMP388_DEV
 | ||||
| */ | ||||
| 
 | ||||
| #include "bmp3xx.h" | ||||
| #include "bmp3xx_base.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include <cinttypes> | ||||
| 
 | ||||
| namespace esphome { | ||||
| namespace bmp3xx { | ||||
| namespace bmp3xx_base { | ||||
| 
 | ||||
| static const char *const TAG = "bmp3xx.sensor"; | ||||
| 
 | ||||
| @@ -150,7 +150,6 @@ void BMP3XXComponent::setup() { | ||||
| void BMP3XXComponent::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "BMP3XX:"); | ||||
|   ESP_LOGCONFIG(TAG, "  Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg); | ||||
|   LOG_I2C_DEVICE(this); | ||||
|   switch (this->error_code_) { | ||||
|     case NONE: | ||||
|       break; | ||||
| @@ -386,5 +385,5 @@ float BMP3XXComponent::bmp388_compensate_pressure_(float uncomp_press, float t_l | ||||
|   return partial_out1 + partial_out2 + partial_data4; | ||||
| } | ||||
| 
 | ||||
| }  // namespace bmp3xx
 | ||||
| }  // namespace bmp3xx_base
 | ||||
| }  // namespace esphome
 | ||||
| @@ -9,10 +9,9 @@ | ||||
| 
 | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #include "esphome/components/i2c/i2c.h" | ||||
| 
 | ||||
| namespace esphome { | ||||
| namespace bmp3xx { | ||||
| namespace bmp3xx_base { | ||||
| 
 | ||||
| static const uint8_t BMP388_ID = 0x50;   // The BMP388 device ID
 | ||||
| static const uint8_t BMP390_ID = 0x60;   // The BMP390 device ID
 | ||||
| @@ -69,8 +68,8 @@ enum IIRFilter { | ||||
|   IIR_FILTER_128 = 0x07 | ||||
| }; | ||||
| 
 | ||||
| /// This class implements support for the BMP3XX Temperature+Pressure i2c sensor.
 | ||||
| class BMP3XXComponent : public PollingComponent, public i2c::I2CDevice { | ||||
| /// This class implements support for the BMP3XX Temperature+Pressure sensor.
 | ||||
| class BMP3XXComponent : public PollingComponent { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   void dump_config() override; | ||||
| @@ -231,7 +230,13 @@ class BMP3XXComponent : public PollingComponent, public i2c::I2CDevice { | ||||
|   float bmp388_compensate_temperature_(float uncomp_temp); | ||||
|   // Bosch pressure compensation function
 | ||||
|   float bmp388_compensate_pressure_(float uncomp_press, float t_lin); | ||||
| 
 | ||||
|   // interface specific functions
 | ||||
|   virtual bool read_byte(uint8_t a_register, uint8_t *data) = 0; | ||||
|   virtual bool write_byte(uint8_t a_register, uint8_t data) = 0; | ||||
|   virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0; | ||||
|   virtual bool write_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0; | ||||
| }; | ||||
| 
 | ||||
| }  // namespace bmp3xx
 | ||||
| }  // namespace bmp3xx_base
 | ||||
| }  // namespace esphome
 | ||||
							
								
								
									
										0
									
								
								esphome/components/bmp3xx_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/bmp3xx_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										29
									
								
								esphome/components/bmp3xx_i2c/bmp3xx_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								esphome/components/bmp3xx_i2c/bmp3xx_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| #include "esphome/components/i2c/i2c.h" | ||||
| #include "bmp3xx_i2c.h" | ||||
| #include <cinttypes> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace bmp3xx_i2c { | ||||
|  | ||||
| static const char *const TAG = "bmp3xx_i2c.sensor"; | ||||
|  | ||||
| bool BMP3XXI2CComponent::read_byte(uint8_t a_register, uint8_t *data) { | ||||
|   return I2CDevice::read_byte(a_register, data); | ||||
| }; | ||||
| bool BMP3XXI2CComponent::write_byte(uint8_t a_register, uint8_t data) { | ||||
|   return I2CDevice::write_byte(a_register, data); | ||||
| }; | ||||
| bool BMP3XXI2CComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) { | ||||
|   return I2CDevice::read_bytes(a_register, data, len); | ||||
| }; | ||||
| bool BMP3XXI2CComponent::write_bytes(uint8_t a_register, uint8_t *data, size_t len) { | ||||
|   return I2CDevice::write_bytes(a_register, data, len); | ||||
| }; | ||||
|  | ||||
| void BMP3XXI2CComponent::dump_config() { | ||||
|   LOG_I2C_DEVICE(this); | ||||
|   BMP3XXComponent::dump_config(); | ||||
| } | ||||
|  | ||||
| }  // namespace bmp3xx_i2c | ||||
| }  // namespace esphome | ||||
							
								
								
									
										17
									
								
								esphome/components/bmp3xx_i2c/bmp3xx_i2c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								esphome/components/bmp3xx_i2c/bmp3xx_i2c.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| #pragma once | ||||
| #include "esphome/components/i2c/i2c.h" | ||||
| #include "esphome/components/bmp3xx_base/bmp3xx_base.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace bmp3xx_i2c { | ||||
|  | ||||
| class BMP3XXI2CComponent : public bmp3xx_base::BMP3XXComponent, public i2c::I2CDevice { | ||||
|   bool read_byte(uint8_t a_register, uint8_t *data) override; | ||||
|   bool write_byte(uint8_t a_register, uint8_t data) override; | ||||
|   bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override; | ||||
|   bool write_bytes(uint8_t a_register, uint8_t *data, size_t len) override; | ||||
|   void dump_config() override; | ||||
| }; | ||||
|  | ||||
| }  // namespace bmp3xx_i2c | ||||
| }  // namespace esphome | ||||
							
								
								
									
										22
									
								
								esphome/components/bmp3xx_i2c/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								esphome/components/bmp3xx_i2c/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import i2c | ||||
| from ..bmp3xx_base import to_code_base, cv, CONFIG_SCHEMA_BASE | ||||
|  | ||||
| AUTO_LOAD = ["bmp3xx_base"] | ||||
| CODEOWNERS = ["@latonita"] | ||||
| DEPENDENCIES = ["i2c"] | ||||
|  | ||||
| bmp3xx_ns = cg.esphome_ns.namespace("bmp3xx_i2c") | ||||
|  | ||||
| BMP3XXI2CComponent = bmp3xx_ns.class_( | ||||
|     "BMP3XXI2CComponent", cg.PollingComponent, i2c.I2CDevice | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend( | ||||
|     i2c.i2c_device_schema(default_address=0x77) | ||||
| ).extend({cv.GenerateID(): cv.declare_id(BMP3XXI2CComponent)}) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = await to_code_base(config) | ||||
|     await i2c.register_i2c_device(var, config) | ||||
							
								
								
									
										0
									
								
								esphome/components/bmp3xx_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/bmp3xx_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										57
									
								
								esphome/components/bmp3xx_spi/bmp3xx_spi.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								esphome/components/bmp3xx_spi/bmp3xx_spi.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| #include "bmp3xx_spi.h" | ||||
| #include <cinttypes> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace bmp3xx_spi { | ||||
|  | ||||
| static const char *const TAG = "bmp3xx_spi.sensor"; | ||||
|  | ||||
| uint8_t set_bit(uint8_t num, int position) { | ||||
|   int mask = 1 << position; | ||||
|   return num | mask; | ||||
| } | ||||
|  | ||||
| uint8_t clear_bit(uint8_t num, int position) { | ||||
|   int mask = 1 << position; | ||||
|   return num & ~mask; | ||||
| } | ||||
|  | ||||
| void BMP3XXSPIComponent::setup() { | ||||
|   this->spi_setup(); | ||||
|   BMP3XXComponent::setup(); | ||||
| } | ||||
|  | ||||
| bool BMP3XXSPIComponent::read_byte(uint8_t a_register, uint8_t *data) { | ||||
|   this->enable(); | ||||
|   this->transfer_byte(set_bit(a_register, 7)); | ||||
|   *data = this->transfer_byte(0); | ||||
|   this->disable(); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool BMP3XXSPIComponent::write_byte(uint8_t a_register, uint8_t data) { | ||||
|   this->enable(); | ||||
|   this->transfer_byte(clear_bit(a_register, 7)); | ||||
|   this->transfer_byte(data); | ||||
|   this->disable(); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool BMP3XXSPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) { | ||||
|   this->enable(); | ||||
|   this->transfer_byte(set_bit(a_register, 7)); | ||||
|   this->read_array(data, len); | ||||
|   this->disable(); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool BMP3XXSPIComponent::write_bytes(uint8_t a_register, uint8_t *data, size_t len) { | ||||
|   this->enable(); | ||||
|   this->transfer_byte(clear_bit(a_register, 7)); | ||||
|   this->transfer_array(data, len); | ||||
|   this->disable(); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| }  // namespace bmp3xx_spi | ||||
| }  // namespace esphome | ||||
							
								
								
									
										19
									
								
								esphome/components/bmp3xx_spi/bmp3xx_spi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								esphome/components/bmp3xx_spi/bmp3xx_spi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| #pragma once | ||||
| #include "esphome/components/bmp3xx_base/bmp3xx_base.h" | ||||
| #include "esphome/components/spi/spi.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace bmp3xx_spi { | ||||
|  | ||||
| class BMP3XXSPIComponent : public bmp3xx_base::BMP3XXComponent, | ||||
|                            public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, | ||||
|                                                  spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> { | ||||
|   void setup() override; | ||||
|   bool read_byte(uint8_t a_register, uint8_t *data) override; | ||||
|   bool write_byte(uint8_t a_register, uint8_t data) override; | ||||
|   bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override; | ||||
|   bool write_bytes(uint8_t a_register, uint8_t *data, size_t len) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace bmp3xx_spi | ||||
| }  // namespace esphome | ||||
							
								
								
									
										22
									
								
								esphome/components/bmp3xx_spi/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								esphome/components/bmp3xx_spi/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import spi | ||||
| from ..bmp3xx_base import to_code_base, cv, CONFIG_SCHEMA_BASE | ||||
|  | ||||
| AUTO_LOAD = ["bmp3xx_base"] | ||||
| CODEOWNERS = ["@latonita"] | ||||
| DEPENDENCIES = ["spi"] | ||||
|  | ||||
| bmp3xx_ns = cg.esphome_ns.namespace("bmp3xx_spi") | ||||
|  | ||||
| BMP3XXSPIComponent = bmp3xx_ns.class_( | ||||
|     "BMP3XXSPIComponent", cg.PollingComponent, spi.SPIDevice | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(spi.spi_device_schema()).extend( | ||||
|     {cv.GenerateID(): cv.declare_id(BMP3XXSPIComponent)} | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = await to_code_base(config) | ||||
|     await spi.register_spi_device(var, config) | ||||
| @@ -1,106 +1,108 @@ | ||||
| #pragma once | ||||
| // Generated from https://github.com/esphome/esphome-webserver | ||||
| #include "esphome/core/hal.h" | ||||
| namespace esphome { | ||||
|  | ||||
| #include "esphome/core/hal.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace captive_portal { | ||||
|  | ||||
| const uint8_t INDEX_GZ[] PROGMEM = { | ||||
|     0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x58, 0x69, 0x6f, 0xdc, 0x36, 0x1a, 0xfe, 0xde, | ||||
|     0x5f, 0xc1, 0x2a, 0x49, 0x47, 0xd3, 0x58, 0xd4, 0x35, 0x9a, 0x53, 0x9a, 0xc2, 0xf1, 0xa6, 0x68, 0x81, 0xa4, 0x0d, | ||||
|     0x60, 0xb7, 0xfd, 0x10, 0x04, 0x30, 0x47, 0xa2, 0x46, 0x8c, 0x25, 0x4a, 0x2b, 0x72, 0xae, 0x0c, 0x66, 0x7f, 0xfb, | ||||
|     0xbe, 0x24, 0x35, 0xe3, 0xb1, 0x37, 0x5e, 0x6c, 0x8a, 0x2d, 0x8a, 0xd6, 0x71, 0x68, 0x1e, 0xef, 0xf9, 0x88, 0xef, | ||||
|     0x21, 0xc5, 0x5f, 0x67, 0x75, 0x2a, 0x77, 0x0d, 0x45, 0x85, 0xac, 0xca, 0x79, 0xac, 0x46, 0x54, 0x12, 0xbe, 0x4c, | ||||
|     0x28, 0x87, 0x15, 0x25, 0xd9, 0x3c, 0xae, 0xa8, 0x24, 0x28, 0x2d, 0x48, 0x2b, 0xa8, 0x4c, 0x7e, 0xb9, 0xf9, 0xde, | ||||
|     0x19, 0x23, 0x77, 0x1e, 0x97, 0x8c, 0xdf, 0xa1, 0x96, 0x96, 0x09, 0x4b, 0x6b, 0x8e, 0x8a, 0x96, 0xe6, 0x49, 0x46, | ||||
|     0x24, 0x99, 0xb2, 0x8a, 0x2c, 0xa9, 0x22, 0xd0, 0x6c, 0x9c, 0x54, 0x34, 0x59, 0x33, 0xba, 0x69, 0xea, 0x56, 0x22, | ||||
|     0xa0, 0x94, 0x94, 0xcb, 0xc4, 0xda, 0xb0, 0x4c, 0x16, 0x49, 0x46, 0xd7, 0x2c, 0xa5, 0x8e, 0x5e, 0x5c, 0x30, 0xce, | ||||
|     0x24, 0x23, 0xa5, 0x23, 0x52, 0x52, 0xd2, 0xc4, 0xbf, 0x58, 0x09, 0xda, 0xea, 0x05, 0x59, 0xc0, 0x9a, 0xd7, 0x16, | ||||
|     0x88, 0x14, 0x69, 0xcb, 0x1a, 0x89, 0x94, 0xbd, 0x49, 0x55, 0x67, 0xab, 0x92, 0xce, 0x5d, 0x97, 0x08, 0xb0, 0x4b, | ||||
|     0xb8, 0x8c, 0x67, 0x74, 0x8b, 0x87, 0x61, 0x98, 0x06, 0x64, 0x94, 0xe3, 0x8f, 0xe2, 0x2b, 0xf0, 0x6c, 0x55, 0x81, | ||||
|     0x3a, 0x5c, 0xd6, 0x29, 0x91, 0xac, 0xe6, 0x58, 0x50, 0xd2, 0xa6, 0x45, 0x92, 0x24, 0xd6, 0x77, 0x82, 0xac, 0xa9, | ||||
|     0xf5, 0xcd, 0x37, 0xf6, 0x89, 0x68, 0x49, 0xe5, 0xeb, 0x92, 0xaa, 0xa9, 0x78, 0xb5, 0xbb, 0x21, 0xcb, 0x9f, 0xc0, | ||||
|     0x72, 0xdb, 0x22, 0x82, 0x65, 0xd4, 0xea, 0xbf, 0xf7, 0x3e, 0x60, 0x21, 0x77, 0x25, 0xc5, 0x19, 0x13, 0x4d, 0x49, | ||||
|     0x76, 0x89, 0xb5, 0x00, 0xa9, 0x77, 0x56, 0x7f, 0x96, 0xaf, 0x78, 0xaa, 0x84, 0x23, 0x61, 0xd3, 0xfe, 0xbe, 0xa4, | ||||
|     0x60, 0x5e, 0xf2, 0x96, 0xc8, 0x02, 0x57, 0x64, 0x6b, 0x9b, 0x09, 0xe3, 0x76, 0xf0, 0xad, 0x4d, 0x5f, 0xfa, 0x9e, | ||||
|     0xd7, 0xbf, 0xd0, 0x83, 0xd7, 0x77, 0xe1, 0xef, 0xac, 0xa5, 0x72, 0xd5, 0x72, 0x44, 0xec, 0xdb, 0xb8, 0x01, 0x4a, | ||||
|     0x94, 0x25, 0x56, 0xe5, 0x07, 0xd8, 0xf3, 0xc6, 0xc8, 0x9f, 0xe0, 0x20, 0x72, 0x7c, 0x1f, 0x87, 0x8e, 0x1f, 0xa5, | ||||
|     0x23, 0x27, 0x42, 0xfe, 0x00, 0x86, 0x20, 0xc0, 0x11, 0xf2, 0x3e, 0x59, 0x28, 0x67, 0x65, 0x99, 0x58, 0xbc, 0xe6, | ||||
|     0xd4, 0x42, 0x42, 0xb6, 0xf5, 0x1d, 0x4d, 0xac, 0x74, 0xd5, 0xb6, 0x60, 0xff, 0x55, 0x5d, 0xd6, 0x2d, 0xc0, 0xf5, | ||||
|     0x15, 0x7a, 0xf0, 0xf3, 0xc5, 0x2a, 0x64, 0x4b, 0xb8, 0xc8, 0xeb, 0xb6, 0x4a, 0x2c, 0xfd, 0x50, 0xec, 0xe7, 0x7b, | ||||
|     0x79, 0x40, 0x6a, 0xe8, 0x9f, 0x1d, 0x3a, 0x75, 0xcb, 0x96, 0x8c, 0x27, 0x96, 0x1f, 0x20, 0x7f, 0x0c, 0x6a, 0x6f, | ||||
|     0xfb, 0x87, 0x13, 0x26, 0x44, 0x61, 0xd2, 0x79, 0x59, 0xdb, 0xef, 0x6f, 0x63, 0xb1, 0x5e, 0xa2, 0x6d, 0x55, 0x72, | ||||
|     0x91, 0x58, 0x85, 0x94, 0xcd, 0xd4, 0x75, 0x37, 0x9b, 0x0d, 0xde, 0x84, 0xb8, 0x6e, 0x97, 0x6e, 0xe0, 0x79, 0x9e, | ||||
|     0x0b, 0x14, 0x16, 0x32, 0xf7, 0xc3, 0x0a, 0x06, 0x16, 0x2a, 0x28, 0x5b, 0x16, 0x52, 0xcf, 0xe7, 0xcf, 0xf7, 0xf4, | ||||
|     0x10, 0x2b, 0x8a, 0xf9, 0xed, 0x87, 0x33, 0x2d, 0xec, 0x4c, 0x0b, 0xfd, 0xee, 0x0c, 0xcd, 0xde, 0x5b, 0x65, 0xd4, | ||||
|     0x88, 0x04, 0x28, 0x40, 0x9e, 0xfe, 0x17, 0x38, 0x6a, 0xde, 0xad, 0x9c, 0x47, 0x2b, 0x74, 0xb6, 0x82, 0xbf, 0x80, | ||||
|     0x5f, 0x50, 0x0d, 0x9d, 0xc9, 0x89, 0xdd, 0x57, 0xc7, 0x6b, 0xdf, 0xbb, 0xdf, 0x50, 0x3c, 0x3f, 0x0c, 0xcf, 0xd7, | ||||
|     0x4e, 0xf0, 0xab, 0x22, 0x50, 0xd8, 0x9f, 0x98, 0x9c, 0xa0, 0xf0, 0x7f, 0x1d, 0x92, 0x08, 0x45, 0xdd, 0x4e, 0xe4, | ||||
|     0xa8, 0xf9, 0x69, 0xa5, 0x34, 0xa1, 0x68, 0x0d, 0x54, 0x95, 0x33, 0x74, 0x22, 0x12, 0xa2, 0xb0, 0x33, 0x09, 0x66, | ||||
|     0xb0, 0x3d, 0x04, 0xe6, 0xb3, 0x3d, 0x27, 0xfc, 0xd4, 0x53, 0x30, 0x4f, 0x2d, 0xeb, 0x1e, 0x83, 0xfa, 0x1c, 0x03, | ||||
|     0xfc, 0xb1, 0x86, 0x3b, 0x67, 0x59, 0x80, 0x11, 0x95, 0x69, 0x61, 0x5b, 0x2e, 0x44, 0x5e, 0xce, 0x96, 0x10, 0x15, | ||||
|     0x35, 0xb7, 0xfa, 0x58, 0x16, 0x94, 0xdb, 0x47, 0x56, 0xc5, 0x48, 0xf5, 0x89, 0xfd, 0xf8, 0x44, 0xf6, 0xf7, 0xa7, | ||||
|     0xf8, 0x90, 0x4c, 0x42, 0x1c, 0x4a, 0xac, 0x22, 0xfa, 0xe2, 0xb4, 0xbb, 0xa8, 0xb3, 0xdd, 0x13, 0xa1, 0x53, 0xf8, | ||||
|     0x26, 0x6e, 0x18, 0xe7, 0xb4, 0xbd, 0xa1, 0x5b, 0x78, 0x86, 0x6f, 0x2f, 0xaf, 0xd0, 0x65, 0x96, 0xb5, 0x54, 0x88, | ||||
|     0x29, 0xb2, 0x5e, 0x4a, 0x88, 0x91, 0xf4, 0x7f, 0x97, 0xe5, 0x3f, 0x90, 0xf5, 0x1b, 0xfb, 0x9e, 0xa1, 0x9f, 0xa8, | ||||
|     0xdc, 0xd4, 0xed, 0x5d, 0x27, 0x4d, 0x99, 0x36, 0x53, 0x11, 0xd8, 0x82, 0x9d, 0xa4, 0x11, 0x58, 0x94, 0x90, 0x5f, | ||||
|     0x6c, 0xbf, 0x0f, 0x7a, 0x9a, 0x7b, 0xaf, 0xf8, 0x11, 0xa8, 0xdb, 0x38, 0x63, 0x6b, 0x94, 0x96, 0x90, 0x41, 0x20, | ||||
|     0x94, 0x8c, 0x28, 0x0b, 0x75, 0x61, 0x53, 0xf3, 0x14, 0xb8, 0xef, 0x12, 0xeb, 0x33, 0x19, 0xe2, 0xd5, 0xee, 0xc7, | ||||
|     0xcc, 0xee, 0x09, 0xc8, 0x0d, 0xbd, 0x3e, 0x5e, 0x93, 0x72, 0x45, 0x51, 0x82, 0x64, 0xc1, 0xc4, 0xbd, 0x81, 0xb3, | ||||
|     0x27, 0xd9, 0x1a, 0x71, 0x07, 0x5c, 0x39, 0x1c, 0x0b, 0xbb, 0x6f, 0x1d, 0xa3, 0x34, 0x26, 0x26, 0x87, 0x5a, 0xcf, | ||||
|     0xac, 0x47, 0x16, 0x39, 0x25, 0xcd, 0xa5, 0x75, 0x1f, 0xcd, 0xcf, 0xf7, 0xc2, 0xe6, 0xb8, 0x05, 0xed, 0xfd, 0xc3, | ||||
|     0x69, 0x33, 0x16, 0x0d, 0xe1, 0x8f, 0x19, 0x95, 0x81, 0x2a, 0x68, 0x20, 0xf1, 0xc1, 0x4c, 0x45, 0x0e, 0x10, 0x9d, | ||||
|     0x14, 0xba, 0xe4, 0x38, 0x7d, 0xbe, 0x67, 0x20, 0x51, 0xe5, 0xb3, 0x93, 0xc4, 0xd8, 0x05, 0x68, 0xe6, 0xb7, 0x87, | ||||
|     0xfe, 0xbd, 0x1f, 0xff, 0x5c, 0xd1, 0x76, 0x77, 0x4d, 0x4b, 0x9a, 0xca, 0xba, 0xb5, 0xad, 0x67, 0xa0, 0x05, 0xae, | ||||
|     0x92, 0x76, 0xf8, 0x87, 0x9b, 0xb7, 0x6f, 0x92, 0xda, 0x6e, 0xfb, 0x17, 0x4f, 0x51, 0xab, 0x6a, 0xf1, 0x1e, 0xaa, | ||||
|     0xc5, 0xbf, 0x92, 0x9e, 0xaa, 0x17, 0xbd, 0x0f, 0xc0, 0xaa, 0xfd, 0xbd, 0xbd, 0x2f, 0x1a, 0x2a, 0xb0, 0x5f, 0x42, | ||||
|     0x72, 0xb8, 0x50, 0x1e, 0x3a, 0xc3, 0xa8, 0x7f, 0x00, 0xfd, 0x60, 0x01, 0xd8, 0xad, 0xf3, 0x3e, 0xe4, 0x7f, 0x95, | ||||
|     0x82, 0xe7, 0xdf, 0xee, 0x17, 0xf5, 0xd6, 0x11, 0xec, 0x13, 0xe3, 0xcb, 0x29, 0xe3, 0x05, 0x6d, 0x99, 0x3c, 0x80, | ||||
|     0xb9, 0x50, 0x42, 0x9a, 0x95, 0xdc, 0x37, 0x24, 0xcb, 0xd4, 0x49, 0xd4, 0x6c, 0x67, 0x39, 0x14, 0x1c, 0x45, 0x49, | ||||
|     0xa7, 0x3e, 0xad, 0x0e, 0xe6, 0x5c, 0xe7, 0x96, 0xe9, 0x24, 0x7a, 0x71, 0x50, 0x17, 0x6e, 0x2f, 0xe1, 0x61, 0x39, | ||||
|     0xa4, 0x64, 0x4b, 0x3e, 0x4d, 0xc1, 0x70, 0xda, 0x1a, 0xa6, 0x9c, 0x54, 0xac, 0xdc, 0x4d, 0x05, 0x64, 0x39, 0x07, | ||||
|     0x2a, 0x11, 0xcb, 0x0f, 0x8b, 0x95, 0x94, 0x35, 0x07, 0xdd, 0x6d, 0x46, 0xdb, 0xa9, 0x37, 0x33, 0x13, 0xa7, 0x25, | ||||
|     0x19, 0x5b, 0x89, 0x29, 0x0e, 0x5b, 0x5a, 0xcd, 0x16, 0x24, 0xbd, 0x5b, 0xb6, 0xf5, 0x8a, 0x67, 0x4e, 0xaa, 0xb2, | ||||
|     0xf0, 0xf4, 0x99, 0x9f, 0x93, 0x90, 0xa6, 0xb3, 0x6e, 0x95, 0xe7, 0xf9, 0x0c, 0xa0, 0xa0, 0x8e, 0xc9, 0x6a, 0xd3, | ||||
|     0x00, 0x0f, 0x14, 0xdb, 0x99, 0x99, 0x38, 0x50, 0x1b, 0xc6, 0x46, 0x28, 0x11, 0x2f, 0x66, 0x47, 0x77, 0xbc, 0x19, | ||||
|     0xa4, 0x77, 0x01, 0x42, 0x1a, 0x88, 0x6d, 0x30, 0xf3, 0x50, 0x11, 0xc6, 0xcf, 0xad, 0x57, 0xd7, 0x64, 0xd6, 0x95, | ||||
|     0x27, 0x80, 0x45, 0xab, 0xd1, 0x45, 0x6a, 0x06, 0x05, 0xc8, 0x14, 0xd9, 0x69, 0x30, 0xf4, 0x9a, 0xed, 0x01, 0x77, | ||||
|     0x17, 0x64, 0x7f, 0xa4, 0xce, 0x4b, 0xba, 0x9d, 0x7d, 0x5c, 0x09, 0xc9, 0xf2, 0x9d, 0xd3, 0x15, 0xe9, 0x29, 0x5c, | ||||
|     0x16, 0x28, 0xce, 0x0b, 0x20, 0xa5, 0x94, 0xcf, 0xb4, 0x0e, 0x87, 0x49, 0x5a, 0x89, 0x0e, 0xa7, 0x93, 0x18, 0x7d, | ||||
|     0x41, 0x1f, 0xca, 0xfa, 0x6f, 0xd4, 0xea, 0x2e, 0xee, 0x2b, 0xd2, 0x42, 0xd1, 0x70, 0x16, 0x35, 0x60, 0x5a, 0x4d, | ||||
|     0x9d, 0x11, 0x3c, 0xab, 0x6e, 0x4b, 0x09, 0x03, 0xcf, 0xc1, 0x4c, 0x5d, 0x7b, 0x8f, 0x78, 0xfb, 0xcd, 0x16, 0x89, | ||||
|     0xba, 0x64, 0x59, 0x47, 0xa7, 0x49, 0x90, 0x77, 0x82, 0xc7, 0x87, 0xc7, 0x8d, 0xd4, 0xde, 0x11, 0xea, 0x41, 0x3e, | ||||
|     0x26, 0xbe, 0xf7, 0x99, 0x27, 0x92, 0xe5, 0x79, 0xb0, 0xc8, 0x4f, 0x48, 0xa9, 0x12, 0x7a, 0x60, 0xdd, 0xad, 0x08, | ||||
|     0x06, 0x20, 0xe0, 0xf8, 0x6c, 0x60, 0x7e, 0x60, 0x3a, 0x2c, 0xf6, 0x67, 0x52, 0xf4, 0x55, 0x9d, 0xae, 0xda, 0xd2, | ||||
|     0xb6, 0x3e, 0x73, 0x75, 0x5f, 0x84, 0x57, 0xf7, 0x25, 0xae, 0xf7, 0x74, 0x89, 0xeb, 0x21, 0xd5, 0x14, 0xbd, 0xaa, | ||||
|     0xb7, 0x49, 0x4f, 0x17, 0x9b, 0x01, 0xfc, 0xf6, 0x5e, 0x84, 0xaf, 0x81, 0xff, 0xff, 0x52, 0xbb, 0x7e, 0x77, 0xe1, | ||||
|     0xfa, 0x82, 0xaa, 0xf5, 0x85, 0x15, 0xcb, 0x78, 0xa7, 0x9c, 0x87, 0x19, 0x94, 0x26, 0x86, 0x05, 0x5b, 0xfa, 0x7f, | ||||
|     0x04, 0xb4, 0xff, 0x89, 0x63, 0x78, 0xe9, 0x8f, 0xf1, 0x04, 0xe9, 0xc1, 0x40, 0x84, 0xc3, 0x31, 0x1a, 0x5d, 0x0d, | ||||
|     0xf0, 0xc0, 0x47, 0xaa, 0x1d, 0x1a, 0xa2, 0x11, 0x1e, 0x03, 0xc1, 0x10, 0x87, 0x23, 0xd8, 0x40, 0x81, 0x8f, 0xa3, | ||||
|     0x37, 0x41, 0x88, 0x87, 0x11, 0x50, 0x05, 0x1e, 0x0e, 0x03, 0x64, 0x68, 0x87, 0x38, 0x00, 0x71, 0x8a, 0x24, 0xac, | ||||
|     0x00, 0xe8, 0x34, 0xc4, 0xde, 0x08, 0xc4, 0x0d, 0xb1, 0x37, 0xc1, 0xe3, 0x21, 0x1a, 0xe3, 0x11, 0x40, 0x87, 0x07, | ||||
|     0x51, 0xe9, 0x44, 0xd8, 0x87, 0xed, 0x70, 0x48, 0xc6, 0x78, 0x10, 0x22, 0x3d, 0x18, 0x38, 0x46, 0x20, 0xc2, 0xc1, | ||||
|     0x9e, 0xff, 0x26, 0xc4, 0xc1, 0x08, 0xf4, 0x0e, 0x06, 0x97, 0x20, 0x76, 0x32, 0x40, 0x66, 0x34, 0xf0, 0x82, 0x82, | ||||
|     0xe8, 0x29, 0xd0, 0x82, 0xbf, 0x2f, 0x68, 0x00, 0x89, 0x8f, 0x42, 0x3c, 0x81, 0xd8, 0xf5, 0x15, 0xbf, 0x19, 0x0d, | ||||
|     0x6e, 0xbe, 0x8f, 0xbc, 0xdf, 0x8d, 0x59, 0xf8, 0xf7, 0xc5, 0xcc, 0x57, 0x08, 0xc0, 0x14, 0x74, 0x83, 0x1c, 0xa4, | ||||
|     0x07, 0xa3, 0x1b, 0x98, 0xc7, 0x57, 0x13, 0x34, 0x06, 0xae, 0xe1, 0x18, 0x4d, 0x50, 0xa4, 0xd0, 0x05, 0xf6, 0x81, | ||||
|     0x61, 0x72, 0x80, 0xe9, 0x0b, 0x61, 0x1c, 0xfc, 0x85, 0x61, 0x7c, 0xca, 0xa7, 0xbf, 0xb0, 0x4b, 0x7f, 0x46, 0x0a, | ||||
|     0x82, 0x76, 0x4c, 0xb7, 0x61, 0xb1, 0x6b, 0x3e, 0x0f, 0xa8, 0x2e, 0x0a, 0xde, 0xf6, 0xa1, 0x1b, 0x99, 0xc7, 0x85, | ||||
|     0x8f, 0x58, 0x96, 0x40, 0x57, 0x3f, 0x3f, 0x6b, 0xf5, 0x81, 0xd0, 0x3f, 0x1e, 0xc1, 0xec, 0x41, 0xe3, 0x6e, 0xce, | ||||
|     0x74, 0xa5, 0x9f, 0xdf, 0x14, 0x14, 0xbd, 0xbe, 0x7e, 0x07, 0x2f, 0x7f, 0x65, 0x89, 0x78, 0xbd, 0x81, 0x77, 0xcc, | ||||
|     0x1d, 0x92, 0xb5, 0xfa, 0x6a, 0xc0, 0xa1, 0x8d, 0x54, 0x53, 0x78, 0x3d, 0x41, 0x5d, 0x1f, 0x81, 0x31, 0x8e, 0x17, | ||||
|     0xed, 0xfc, 0x5d, 0x49, 0x89, 0xa0, 0x68, 0xc9, 0xd6, 0x14, 0x31, 0x09, 0x2d, 0x42, 0x45, 0x91, 0x64, 0x6a, 0x38, | ||||
|     0x31, 0x6a, 0x3a, 0x68, 0x77, 0xb5, 0x12, 0xd3, 0x28, 0x83, 0x25, 0x20, 0x66, 0xde, 0x75, 0xc4, 0x71, 0x11, 0x1a, | ||||
|     0xab, 0xae, 0xa9, 0x94, 0xd0, 0x4c, 0x28, 0xab, 0xc2, 0x79, 0xac, 0xde, 0x6e, 0x11, 0xd1, 0xef, 0x0c, 0x89, 0xbb, | ||||
|     0x61, 0x39, 0x53, 0xdf, 0x0c, 0xe6, 0xb1, 0xee, 0x22, 0x95, 0x04, 0xd5, 0xc8, 0x98, 0x0f, 0x1c, 0x7a, 0x56, 0x52, | ||||
|     0xbe, 0x84, 0x97, 0x56, 0x78, 0x4c, 0xd0, 0x57, 0xa4, 0xb4, 0xa8, 0x4b, 0xe8, 0x5b, 0x92, 0xeb, 0xeb, 0x1f, 0xff, | ||||
|     0xa1, 0xbe, 0x86, 0x28, 0x13, 0x4e, 0x9c, 0xf0, 0x0a, 0x60, 0x18, 0xd5, 0xa4, 0xe3, 0x1b, 0x0e, 0xcc, 0x77, 0x8d, | ||||
|     0x06, 0x5a, 0x78, 0xf0, 0x2f, 0x7b, 0x20, 0xe5, 0xdd, 0x71, 0xb3, 0x93, 0xa4, 0xff, 0xeb, 0x7e, 0xd4, 0x30, 0x89, | ||||
|     0xd5, 0xa2, 0x62, 0x72, 0x7e, 0x0d, 0x06, 0xc6, 0xae, 0x39, 0x00, 0xe7, 0x94, 0x03, 0x86, 0xb6, 0xe8, 0x78, 0x00, | ||||
|     0xec, 0x9f, 0x6f, 0x2e, 0xd1, 0x2f, 0x0d, 0x5c, 0x6e, 0x6a, 0xb0, 0xd7, 0x5e, 0x56, 0x54, 0x16, 0x75, 0x96, 0xbc, | ||||
|     0xfb, 0xf9, 0xfa, 0xe6, 0xe4, 0xf1, 0x4a, 0x13, 0x21, 0xca, 0x53, 0xf3, 0xbd, 0x65, 0x55, 0x4a, 0xd6, 0x90, 0x56, | ||||
|     0x6a, 0xb1, 0x8e, 0x8a, 0x8e, 0xa3, 0x47, 0xfa, 0x3c, 0x67, 0x25, 0x35, 0x4e, 0x75, 0x8c, 0xee, 0x1c, 0x7d, 0xce, | ||||
|     0xc6, 0xa3, 0xee, 0x47, 0x56, 0xba, 0xe6, 0x02, 0xb9, 0xe6, 0x36, 0xb9, 0xfa, 0x6b, 0xd4, 0xbf, 0x01, 0x14, 0xee, | ||||
|     0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x58, 0x6d, 0x6f, 0xdb, 0x38, 0x12, 0xfe, 0xde, | ||||
|     0x5f, 0x31, 0xa7, 0x36, 0x6b, 0x6b, 0x1b, 0x51, 0x22, 0xe5, 0xb7, 0xd8, 0x92, 0x16, 0x69, 0xae, 0x8b, 0x5d, 0xa0, | ||||
|     0xdd, 0x2d, 0x90, 0x6c, 0xef, 0x43, 0x51, 0x20, 0xb4, 0x34, 0xb2, 0xd8, 0x48, 0xa4, 0x4e, 0xa4, 0x5f, 0x52, 0xc3, | ||||
|     0xf7, 0xdb, 0x0f, 0x94, 0x6c, 0xc7, 0xe9, 0x35, 0x87, 0xeb, 0xe2, 0x0e, 0x87, 0xdd, 0x18, 0x21, 0x86, 0xe4, 0xcc, | ||||
|     0x70, 0xe6, 0xf1, 0x0c, 0x67, 0xcc, 0xe8, 0x2f, 0x99, 0x4a, 0xcd, 0x7d, 0x8d, 0x50, 0x98, 0xaa, 0x4c, 0x22, 0x3b, | ||||
|     0x42, 0xc9, 0xe5, 0x22, 0x46, 0x99, 0x44, 0x05, 0xf2, 0x2c, 0x89, 0x2a, 0x34, 0x1c, 0xd2, 0x82, 0x37, 0x1a, 0x4d, | ||||
|     0xfc, 0xdb, 0xcd, 0x8f, 0xde, 0x04, 0xfc, 0x24, 0x2a, 0x85, 0xbc, 0x83, 0x06, 0xcb, 0x58, 0xa4, 0x4a, 0x42, 0xd1, | ||||
|     0x60, 0x1e, 0x67, 0xdc, 0xf0, 0xa9, 0xa8, 0xf8, 0x02, 0x2d, 0x43, 0x2b, 0x26, 0x79, 0x85, 0xf1, 0x4a, 0xe0, 0xba, | ||||
|     0x56, 0x8d, 0x81, 0x54, 0x49, 0x83, 0xd2, 0xc4, 0xce, 0x5a, 0x64, 0xa6, 0x88, 0x33, 0x5c, 0x89, 0x14, 0xbd, 0x76, | ||||
|     0x72, 0x2e, 0xa4, 0x30, 0x82, 0x97, 0x9e, 0x4e, 0x79, 0x89, 0x31, 0x3d, 0x5f, 0x6a, 0x6c, 0xda, 0x09, 0x9f, 0x97, | ||||
|     0x18, 0x4b, 0xe5, 0xf8, 0x49, 0xa4, 0xd3, 0x46, 0xd4, 0x06, 0xac, 0xbd, 0x71, 0xa5, 0xb2, 0x65, 0x89, 0x89, 0xef, | ||||
|     0x73, 0xad, 0xd1, 0x68, 0x5f, 0xc8, 0x0c, 0x37, 0x64, 0x14, 0x86, 0x29, 0xe3, 0xe3, 0x9c, 0x7c, 0xd2, 0xcf, 0x32, | ||||
|     0x95, 0x2e, 0x2b, 0x94, 0x86, 0x94, 0x2a, 0xe5, 0x46, 0x28, 0x49, 0x34, 0xf2, 0x26, 0x2d, 0xe2, 0x38, 0x76, 0x7e, | ||||
|     0xd0, 0x7c, 0x85, 0xce, 0x77, 0xdf, 0xf5, 0x8f, 0x4c, 0x0b, 0x34, 0xaf, 0x4b, 0xb4, 0xa4, 0x7e, 0x75, 0x7f, 0xc3, | ||||
|     0x17, 0xbf, 0xf0, 0x0a, 0xfb, 0x0e, 0xd7, 0x22, 0x43, 0xc7, 0xfd, 0x10, 0x7c, 0x24, 0xda, 0xdc, 0x97, 0x48, 0x32, | ||||
|     0xa1, 0xeb, 0x92, 0xdf, 0xc7, 0xce, 0xbc, 0x54, 0xe9, 0x9d, 0xe3, 0xce, 0xf2, 0xa5, 0x4c, 0xad, 0x72, 0xd0, 0x7d, | ||||
|     0x74, 0xb7, 0x25, 0x1a, 0x30, 0xf1, 0x5b, 0x6e, 0x0a, 0x52, 0xf1, 0x4d, 0xbf, 0x23, 0x84, 0xec, 0xb3, 0xef, 0xfb, | ||||
|     0xf8, 0x92, 0x06, 0x81, 0x7b, 0xde, 0x0e, 0x81, 0xeb, 0xd3, 0x20, 0x98, 0x35, 0x68, 0x96, 0x8d, 0x04, 0xde, 0xbf, | ||||
|     0x8d, 0x6a, 0x6e, 0x0a, 0xc8, 0x62, 0xa7, 0xa2, 0x8c, 0x04, 0xc1, 0x04, 0xe8, 0x05, 0x61, 0x43, 0x8f, 0x52, 0x12, | ||||
|     0x7a, 0x74, 0x98, 0x8e, 0xbd, 0x21, 0xd0, 0x81, 0x37, 0x04, 0xc6, 0xc8, 0x10, 0x82, 0xcf, 0x0e, 0xe4, 0xa2, 0x2c, | ||||
|     0x63, 0x47, 0x2a, 0x89, 0x0e, 0x68, 0xd3, 0xa8, 0x3b, 0x8c, 0x9d, 0x74, 0xd9, 0x34, 0x28, 0xcd, 0x95, 0x2a, 0x55, | ||||
|     0xe3, 0xf8, 0xc9, 0x33, 0x78, 0xf4, 0xf7, 0xcd, 0x47, 0x98, 0x86, 0x4b, 0x9d, 0xab, 0xa6, 0x8a, 0x9d, 0xf6, 0x4b, | ||||
|     0xe9, 0xbf, 0xd8, 0x9a, 0x1d, 0xd8, 0xc1, 0x3d, 0xd9, 0xf4, 0x54, 0x23, 0x16, 0x42, 0xc6, 0x0e, 0x65, 0x40, 0x27, | ||||
|     0x8e, 0x9f, 0xdc, 0xba, 0xbb, 0x23, 0x26, 0xdc, 0x62, 0xb2, 0xf7, 0x52, 0xf5, 0x3f, 0xdc, 0x46, 0x7a, 0xb5, 0x80, | ||||
|     0x4d, 0x55, 0x4a, 0x1d, 0x3b, 0x85, 0x31, 0xf5, 0xd4, 0xf7, 0xd7, 0xeb, 0x35, 0x59, 0x87, 0x44, 0x35, 0x0b, 0x9f, | ||||
|     0x05, 0x41, 0xe0, 0xeb, 0xd5, 0xc2, 0x81, 0x2e, 0x3e, 0x1c, 0x36, 0x70, 0xa0, 0x40, 0xb1, 0x28, 0x4c, 0x4b, 0x27, | ||||
|     0x2f, 0xb6, 0xb8, 0x8b, 0x2c, 0x47, 0x72, 0xfb, 0xf1, 0xe4, 0x14, 0x71, 0x72, 0x0a, 0xfe, 0x70, 0x82, 0x66, 0xef, | ||||
|     0xad, 0x35, 0x6a, 0xcc, 0x19, 0x30, 0x08, 0xda, 0x0f, 0xf3, 0x2c, 0xbd, 0x9f, 0x79, 0x5f, 0xcc, 0xe0, 0x64, 0x06, | ||||
|     0x0c, 0x9e, 0x01, 0xb0, 0x6a, 0xe4, 0x5d, 0x1c, 0xc5, 0xa9, 0xdd, 0x5e, 0xd1, 0xe0, 0x61, 0xc1, 0xca, 0xfc, 0x34, | ||||
|     0x3a, 0x9d, 0x7b, 0xec, 0xbd, 0x65, 0xb0, 0xd8, 0x1f, 0x85, 0x3c, 0x56, 0xd0, 0xf7, 0x23, 0x3e, 0x84, 0xe1, 0x7e, | ||||
|     0x65, 0xe8, 0x59, 0xfa, 0x38, 0xb3, 0x27, 0xc1, 0x70, 0xc5, 0x0a, 0x5a, 0x79, 0x23, 0x6f, 0xc8, 0x43, 0x08, 0xf7, | ||||
|     0x26, 0x85, 0x10, 0xae, 0x58, 0x31, 0x7a, 0x3f, 0x3a, 0x5d, 0xf3, 0xc2, 0xcf, 0x3d, 0x0b, 0xf3, 0xd4, 0x71, 0x1e, | ||||
|     0x30, 0x50, 0xa7, 0x18, 0x90, 0x4f, 0x4a, 0xc8, 0xbe, 0xe3, 0xb8, 0xbb, 0x1c, 0x4d, 0x5a, 0xf4, 0x1d, 0x3f, 0x55, | ||||
|     0x32, 0x17, 0x0b, 0xf2, 0x49, 0x2b, 0xe9, 0xb8, 0xc4, 0x14, 0x28, 0xfb, 0x07, 0x51, 0x2b, 0x88, 0xed, 0x4e, 0xff, | ||||
|     0xcb, 0x1d, 0xe3, 0x6e, 0x8f, 0xf9, 0x61, 0x84, 0x29, 0x31, 0x36, 0xc4, 0x66, 0xf4, 0xf9, 0x71, 0x75, 0xae, 0xb2, | ||||
|     0xfb, 0x27, 0x52, 0xa7, 0xa0, 0x5d, 0xde, 0x08, 0x29, 0xb1, 0xb9, 0xc1, 0x8d, 0x89, 0x9d, 0xb7, 0x97, 0x57, 0x70, | ||||
|     0x99, 0x65, 0x0d, 0x6a, 0x3d, 0x05, 0xe7, 0xa5, 0x21, 0x15, 0x4f, 0xff, 0x73, 0x5d, 0xf4, 0x91, 0xae, 0xbf, 0x89, | ||||
|     0x1f, 0x05, 0xfc, 0x82, 0x66, 0xad, 0x9a, 0xbb, 0xbd, 0x36, 0x6b, 0xda, 0xcc, 0x66, 0x60, 0x13, 0x1b, 0xc2, 0x6b, | ||||
|     0x4d, 0x74, 0x29, 0x52, 0xec, 0x53, 0x97, 0x54, 0xbc, 0x7e, 0xf0, 0x4a, 0x1e, 0x80, 0xba, 0x8d, 0x32, 0xb1, 0x82, | ||||
|     0xb4, 0xe4, 0x5a, 0xc7, 0x8e, 0xec, 0x54, 0x39, 0xb0, 0x4f, 0x1b, 0x25, 0xd3, 0x52, 0xa4, 0x77, 0xb1, 0xf3, 0x95, | ||||
|     0x1b, 0xe2, 0xd5, 0xfd, 0xcf, 0x59, 0xbf, 0xa7, 0xb5, 0xc8, 0x7a, 0x2e, 0x59, 0xf1, 0x72, 0x89, 0x10, 0x83, 0x29, | ||||
|     0x84, 0x7e, 0x30, 0x70, 0xf6, 0xa4, 0x58, 0xad, 0xef, 0x7a, 0x2e, 0xc9, 0x55, 0xba, 0xd4, 0x7d, 0xd7, 0x39, 0x64, | ||||
|     0x69, 0xc4, 0xbb, 0x3b, 0xd4, 0x79, 0xee, 0x7c, 0x61, 0x91, 0x57, 0x62, 0x6e, 0x9c, 0x87, 0x6c, 0x7e, 0xb1, 0xd5, | ||||
|     0x7d, 0x49, 0x1a, 0xad, 0x85, 0xbb, 0x3b, 0x2e, 0x46, 0xba, 0xe6, 0xf2, 0x4b, 0x41, 0x6b, 0xa0, 0x4d, 0x1a, 0x49, | ||||
|     0x2c, 0x65, 0x33, 0xa7, 0xe6, 0xf2, 0x78, 0xa0, 0xcf, 0x0f, 0xe4, 0x8b, 0xad, 0xe8, 0x4b, 0x7b, 0x4b, 0xde, 0x1d, | ||||
|     0x35, 0x46, 0x7e, 0x26, 0x56, 0xc9, 0xed, 0xce, 0x7d, 0xf0, 0xe3, 0xef, 0x4b, 0x6c, 0xee, 0xaf, 0xb1, 0xc4, 0xd4, | ||||
|     0xa8, 0xa6, 0xef, 0x3c, 0x97, 0x68, 0x1c, 0xb7, 0x73, 0xf8, 0xa7, 0x9b, 0xb7, 0x6f, 0x62, 0xd5, 0x6f, 0xdc, 0xf3, | ||||
|     0xa7, 0xb8, 0x6d, 0xb5, 0xf8, 0xd0, 0x60, 0xf9, 0x8f, 0xb8, 0x67, 0xeb, 0x45, 0xef, 0xa3, 0xe3, 0x92, 0xd6, 0xdf, | ||||
|     0xdb, 0x87, 0xa2, 0x61, 0x13, 0xfb, 0xe5, 0xa6, 0x2a, 0xcf, 0xad, 0x87, 0xde, 0x68, 0xe8, 0xee, 0x6e, 0x77, 0xee, | ||||
|     0xce, 0x9d, 0x45, 0x7e, 0x77, 0xef, 0x27, 0x51, 0x7b, 0x05, 0x27, 0xdf, 0x6f, 0xe7, 0x6a, 0xe3, 0x69, 0xf1, 0x59, | ||||
|     0xc8, 0xc5, 0x54, 0xc8, 0x02, 0x1b, 0x61, 0x76, 0x99, 0x58, 0x9d, 0x0b, 0x59, 0x2f, 0xcd, 0xb6, 0xe6, 0x59, 0x66, | ||||
|     0x77, 0x86, 0xf5, 0x66, 0x96, 0x2b, 0x69, 0x2c, 0x27, 0x4e, 0x29, 0x56, 0xbb, 0x6e, 0xbf, 0xbd, 0x5b, 0xa6, 0x17, | ||||
|     0xc3, 0xb3, 0x9d, 0x0d, 0xb8, 0xad, 0xc1, 0x8d, 0xf1, 0x78, 0x29, 0x16, 0x72, 0x9a, 0xa2, 0x34, 0xd8, 0x74, 0x42, | ||||
|     0x39, 0xaf, 0x44, 0x79, 0x3f, 0xd5, 0x5c, 0x6a, 0x4f, 0x63, 0x23, 0xf2, 0xdd, 0x7c, 0x69, 0x8c, 0x92, 0xdb, 0xb9, | ||||
|     0x6a, 0x32, 0x6c, 0xa6, 0xc1, 0xac, 0x23, 0xbc, 0x86, 0x67, 0x62, 0xa9, 0xa7, 0x24, 0x6c, 0xb0, 0x9a, 0xcd, 0x79, | ||||
|     0x7a, 0xb7, 0x68, 0xd4, 0x52, 0x66, 0x5e, 0x6a, 0x6f, 0xe1, 0xe9, 0x73, 0x9a, 0xf3, 0x10, 0xd3, 0xd9, 0x7e, 0x96, | ||||
|     0xe7, 0xf9, 0xac, 0x14, 0x12, 0xbd, 0xee, 0x56, 0x9b, 0x32, 0x32, 0xb0, 0x62, 0x27, 0x66, 0x12, 0x66, 0x17, 0x3a, | ||||
|     0x1b, 0x69, 0x10, 0x9c, 0xcd, 0x0e, 0xee, 0x04, 0xb3, 0x74, 0xd9, 0x68, 0xd5, 0x4c, 0x6b, 0x25, 0xac, 0x99, 0xbb, | ||||
|     0x8a, 0x0b, 0x79, 0x6a, 0xbd, 0x0d, 0x93, 0xd9, 0xbe, 0x3c, 0x4d, 0x85, 0x6c, 0x8f, 0x69, 0x8b, 0xd4, 0xac, 0x12, | ||||
|     0xb2, 0x2b, 0xb2, 0x53, 0x36, 0x0a, 0xea, 0xcd, 0x8e, 0xec, 0x03, 0x64, 0x7b, 0xe0, 0xce, 0x4b, 0xdc, 0xcc, 0x3e, | ||||
|     0x2d, 0xb5, 0x11, 0xf9, 0xbd, 0xb7, 0x2f, 0xd2, 0x53, 0x5d, 0xf3, 0x14, 0xbd, 0x39, 0x9a, 0x35, 0xa2, 0x9c, 0xb5, | ||||
|     0x67, 0x78, 0xc2, 0x60, 0xa5, 0xf7, 0x38, 0x1d, 0xd5, 0xb4, 0x01, 0xfa, 0x58, 0xd7, 0xbf, 0xe3, 0xb6, 0xb1, 0xb8, | ||||
|     0xad, 0x78, 0xb3, 0x10, 0xd2, 0x9b, 0x2b, 0x63, 0x54, 0x35, 0xf5, 0xc6, 0xf5, 0x66, 0xb6, 0x5f, 0xb2, 0xca, 0xa6, | ||||
|     0xd4, 0x9a, 0xd9, 0xd6, 0xde, 0x03, 0xde, 0xb4, 0xde, 0x80, 0x56, 0xa5, 0xc8, 0xf6, 0x7c, 0x2d, 0x0b, 0x04, 0x47, | ||||
|     0x78, 0xe8, 0xb0, 0xde, 0x80, 0x5d, 0x3b, 0x40, 0x3d, 0xc8, 0x27, 0x9c, 0x06, 0x5f, 0xf9, 0x46, 0xb2, 0x3c, 0x67, | ||||
|     0xf3, 0xfc, 0x88, 0x94, 0x2d, 0xa1, 0x3b, 0xb1, 0x8f, 0x0a, 0x36, 0xa8, 0x37, 0xb3, 0xc3, 0x77, 0x33, 0xa8, 0x37, | ||||
|     0x3b, 0xd1, 0xa6, 0xc5, 0xf6, 0x44, 0x4b, 0x1b, 0xaa, 0xd3, 0x65, 0x53, 0xf6, 0x9d, 0xaf, 0x84, 0xee, 0x59, 0x78, | ||||
|     0xf5, 0x50, 0xe2, 0x7a, 0x4f, 0x97, 0xb8, 0x1e, 0xd8, 0xa6, 0xe8, 0x95, 0xda, 0xc4, 0xbd, 0xb6, 0xd8, 0x0c, 0x80, | ||||
|     0x0d, 0x7a, 0x67, 0xe1, 0xeb, 0xb3, 0xf0, 0xea, 0xbf, 0x52, 0xbb, 0x7e, 0x77, 0xe1, 0xfa, 0x86, 0xaa, 0xf5, 0x8d, | ||||
|     0x15, 0xab, 0xf3, 0xce, 0x3a, 0x7f, 0x16, 0xbe, 0x76, 0xdc, 0x9d, 0x20, 0x5a, 0x2c, 0xe8, 0xff, 0x02, 0xda, 0x7f, | ||||
|     0xc5, 0x31, 0xbc, 0xa4, 0x13, 0x72, 0x01, 0xed, 0xd0, 0x41, 0x44, 0xc2, 0x09, 0x8c, 0xaf, 0x06, 0x64, 0x40, 0xc1, | ||||
|     0xb6, 0x43, 0x23, 0x18, 0x93, 0xc9, 0x05, 0xd0, 0x11, 0x09, 0xc7, 0x40, 0x19, 0x30, 0x4a, 0x86, 0x6f, 0x58, 0x48, | ||||
|     0x46, 0x43, 0x18, 0x5f, 0xb1, 0x80, 0x84, 0x0c, 0x3a, 0xde, 0x11, 0x61, 0x0c, 0x42, 0xcb, 0x12, 0x56, 0x01, 0xb0, | ||||
|     0x34, 0x24, 0xc1, 0x18, 0x02, 0x18, 0x91, 0xe0, 0x82, 0x4c, 0x46, 0x30, 0x21, 0x63, 0x0a, 0x8c, 0x0c, 0x86, 0xa5, | ||||
|     0x37, 0x24, 0x14, 0x46, 0x24, 0x1c, 0xf1, 0x09, 0x19, 0x84, 0xd0, 0x0e, 0x1d, 0x1c, 0x63, 0xc2, 0x98, 0x47, 0x02, | ||||
|     0xfa, 0x26, 0x24, 0x6c, 0x0c, 0x63, 0x32, 0x18, 0x5c, 0xd2, 0x11, 0xb9, 0x18, 0x40, 0x37, 0x76, 0xf0, 0x52, 0x06, | ||||
|     0xc3, 0xa7, 0x40, 0x63, 0x7f, 0x5e, 0xd0, 0x42, 0xc2, 0x28, 0x84, 0xe4, 0x62, 0xc2, 0x6d, 0x5f, 0xca, 0xa0, 0x1b, | ||||
|     0x3b, 0xdc, 0x28, 0x85, 0xe0, 0x77, 0x63, 0x16, 0xfe, 0x79, 0x31, 0xa3, 0x16, 0x01, 0x46, 0x06, 0xe1, 0x25, 0x0d, | ||||
|     0xc9, 0x08, 0xda, 0xa1, 0x3b, 0x9b, 0x32, 0x98, 0x5c, 0x5d, 0xc0, 0x04, 0x46, 0x64, 0x34, 0x81, 0x0b, 0x18, 0x5a, | ||||
|     0x74, 0x2f, 0xc8, 0x64, 0xd0, 0x09, 0x79, 0x8c, 0x7c, 0x2b, 0x8c, 0x83, 0x3f, 0x30, 0x8c, 0x4f, 0xf9, 0xf4, 0x07, | ||||
|     0x76, 0xe9, 0xff, 0x71, 0x05, 0x45, 0x7e, 0xd7, 0x86, 0x45, 0x7e, 0xf7, 0x3c, 0x60, 0xbb, 0xa8, 0x24, 0xb2, 0xdd, | ||||
|     0x48, 0x12, 0x15, 0x14, 0x44, 0x16, 0x57, 0x3c, 0x4d, 0x4e, 0x5a, 0xfd, 0xc8, 0x2f, 0xe8, 0x61, 0xab, 0xa0, 0xc9, | ||||
|     0xa3, 0xc6, 0xbd, 0xdb, 0x6b, 0x2b, 0x7d, 0x72, 0x53, 0x20, 0xbc, 0xbe, 0x7e, 0x07, 0x6b, 0x51, 0x96, 0x20, 0xd5, | ||||
|     0x1a, 0x4c, 0x73, 0x0f, 0x46, 0xd9, 0x57, 0x03, 0x89, 0xa9, 0xb1, 0xa4, 0x29, 0x10, 0xf6, 0x7d, 0x04, 0x21, 0x24, | ||||
|     0x9a, 0x37, 0xc9, 0xbb, 0x12, 0xb9, 0x46, 0x58, 0x88, 0x15, 0x82, 0x30, 0xa0, 0x55, 0x85, 0x60, 0x84, 0x1d, 0x8e, | ||||
|     0x82, 0x2d, 0x5f, 0xe4, 0x77, 0x87, 0x74, 0x8d, 0xb2, 0xc8, 0x62, 0x89, 0x26, 0xd9, 0x77, 0xc4, 0x51, 0x11, 0x76, | ||||
|     0x56, 0x5d, 0xa3, 0x31, 0x42, 0x2e, 0xac, 0x55, 0x61, 0x12, 0xd9, 0x5f, 0xb7, 0xc0, 0xdb, 0xdf, 0x0c, 0xb1, 0xbf, | ||||
|     0x16, 0xb9, 0xb0, 0x6f, 0x06, 0x49, 0xd4, 0x76, 0x91, 0x56, 0x83, 0x6d, 0x64, 0xba, 0x07, 0x8e, 0x96, 0x2a, 0x51, | ||||
|     0x2e, 0x4c, 0x11, 0x87, 0x0c, 0xea, 0x92, 0xa7, 0x58, 0xa8, 0x32, 0xc3, 0x26, 0xbe, 0xbe, 0xfe, 0xf9, 0xaf, 0xf6, | ||||
|     0x35, 0xc4, 0x9a, 0x70, 0x94, 0xac, 0xf5, 0x5d, 0x27, 0x68, 0x89, 0xbd, 0xdc, 0x68, 0xd0, 0xbd, 0x6b, 0xd4, 0x5c, | ||||
|     0xeb, 0xb5, 0x6a, 0xb2, 0x47, 0x5a, 0xde, 0x1d, 0x16, 0xf7, 0x9a, 0xda, 0xff, 0xb6, 0x1f, 0xed, 0x84, 0xf4, 0x72, | ||||
|     0x5e, 0x09, 0x93, 0x5c, 0xf3, 0x15, 0x46, 0x7e, 0xb7, 0x91, 0x44, 0xbe, 0x75, 0xa0, 0xe3, 0x2d, 0xf6, 0x32, 0x05, | ||||
|     0x4d, 0x7e, 0xbd, 0xb9, 0x84, 0xdf, 0xea, 0x8c, 0x1b, 0xec, 0xb0, 0x6f, 0xbd, 0xac, 0xd0, 0x14, 0x2a, 0x8b, 0xdf, | ||||
|     0xfd, 0x7a, 0x7d, 0x73, 0xf4, 0x78, 0xd9, 0x32, 0x01, 0xca, 0xb4, 0x7b, 0x6f, 0x59, 0x96, 0x46, 0xd4, 0xbc, 0x31, | ||||
|     0xad, 0x5a, 0xcf, 0x66, 0xc7, 0xc1, 0xa3, 0x76, 0x3f, 0x17, 0x25, 0x76, 0x4e, 0xed, 0x05, 0xfd, 0x04, 0xbe, 0x66, | ||||
|     0xe3, 0xe1, 0xec, 0x2f, 0xac, 0xf4, 0xbb, 0x00, 0xf2, 0xbb, 0x68, 0xf2, 0xdb, 0xd7, 0xa8, 0x7f, 0x02, 0x14, 0xee, | ||||
|     0xbc, 0x64, 0x9d, 0x12, 0x00, 0x00}; | ||||
|  | ||||
| }  // namespace captive_portal | ||||
|   | ||||
| @@ -244,122 +244,150 @@ async def setup_climate_core_(var, config): | ||||
|     await setup_entity(var, config) | ||||
|  | ||||
|     visual = config[CONF_VISUAL] | ||||
|     if CONF_MIN_TEMPERATURE in visual: | ||||
|         cg.add(var.set_visual_min_temperature_override(visual[CONF_MIN_TEMPERATURE])) | ||||
|     if CONF_MAX_TEMPERATURE in visual: | ||||
|         cg.add(var.set_visual_max_temperature_override(visual[CONF_MAX_TEMPERATURE])) | ||||
|     if CONF_TEMPERATURE_STEP in visual: | ||||
|     if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None: | ||||
|         cg.add(var.set_visual_min_temperature_override(min_temp)) | ||||
|     if (max_temp := visual.get(CONF_MAX_TEMPERATURE)) is not None: | ||||
|         cg.add(var.set_visual_max_temperature_override(max_temp)) | ||||
|     if (temp_step := visual.get(CONF_TEMPERATURE_STEP)) is not None: | ||||
|         cg.add( | ||||
|             var.set_visual_temperature_step_override( | ||||
|                 visual[CONF_TEMPERATURE_STEP][CONF_TARGET_TEMPERATURE], | ||||
|                 visual[CONF_TEMPERATURE_STEP][CONF_CURRENT_TEMPERATURE], | ||||
|                 temp_step[CONF_TARGET_TEMPERATURE], | ||||
|                 temp_step[CONF_CURRENT_TEMPERATURE], | ||||
|             ) | ||||
|         ) | ||||
|     if CONF_MIN_HUMIDITY in visual: | ||||
|         cg.add(var.set_visual_min_humidity_override(visual[CONF_MIN_HUMIDITY])) | ||||
|     if CONF_MAX_HUMIDITY in visual: | ||||
|         cg.add(var.set_visual_max_humidity_override(visual[CONF_MAX_HUMIDITY])) | ||||
|     if (min_humidity := visual.get(CONF_MIN_HUMIDITY)) is not None: | ||||
|         cg.add(var.set_visual_min_humidity_override(min_humidity)) | ||||
|     if (max_humidity := visual.get(CONF_MAX_HUMIDITY)) is not None: | ||||
|         cg.add(var.set_visual_max_humidity_override(max_humidity)) | ||||
|  | ||||
|     if CONF_MQTT_ID in config: | ||||
|         mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) | ||||
|     if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|         if CONF_ACTION_STATE_TOPIC in config: | ||||
|             cg.add(mqtt_.set_custom_action_state_topic(config[CONF_ACTION_STATE_TOPIC])) | ||||
|         if CONF_AWAY_COMMAND_TOPIC in config: | ||||
|             cg.add(mqtt_.set_custom_away_command_topic(config[CONF_AWAY_COMMAND_TOPIC])) | ||||
|         if CONF_AWAY_STATE_TOPIC in config: | ||||
|             cg.add(mqtt_.set_custom_away_state_topic(config[CONF_AWAY_STATE_TOPIC])) | ||||
|         if CONF_CURRENT_TEMPERATURE_STATE_TOPIC in config: | ||||
|         if (action_state_topic := config.get(CONF_ACTION_STATE_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_action_state_topic(action_state_topic)) | ||||
|         if (away_command_topic := config.get(CONF_AWAY_COMMAND_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_away_command_topic(away_command_topic)) | ||||
|         if (away_state_topic := config.get(CONF_AWAY_STATE_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_away_state_topic(away_state_topic)) | ||||
|         if ( | ||||
|             current_temperature_state_topic := config.get( | ||||
|                 CONF_CURRENT_TEMPERATURE_STATE_TOPIC | ||||
|             ) | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_current_temperature_state_topic( | ||||
|                     config[CONF_CURRENT_TEMPERATURE_STATE_TOPIC] | ||||
|                     current_temperature_state_topic | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_CURRENT_HUMIDITY_STATE_TOPIC in config: | ||||
|         if ( | ||||
|             current_humidity_state_topic := config.get( | ||||
|                 CONF_CURRENT_HUMIDITY_STATE_TOPIC | ||||
|             ) | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_current_humidity_state_topic( | ||||
|                     config[CONF_CURRENT_HUMIDITY_STATE_TOPIC] | ||||
|                     current_humidity_state_topic | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_FAN_MODE_COMMAND_TOPIC in config: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_fan_mode_command_topic( | ||||
|                     config[CONF_FAN_MODE_COMMAND_TOPIC] | ||||
|                 ) | ||||
|         if ( | ||||
|             fan_mode_command_topic := config.get(CONF_FAN_MODE_COMMAND_TOPIC) | ||||
|         ) is not None: | ||||
|             cg.add(mqtt_.set_custom_fan_mode_command_topic(fan_mode_command_topic)) | ||||
|         if (fan_mode_state_topic := config.get(CONF_FAN_MODE_STATE_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_fan_mode_state_topic(fan_mode_state_topic)) | ||||
|         if (mode_command_topic := config.get(CONF_MODE_COMMAND_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_mode_command_topic(mode_command_topic)) | ||||
|         if (mode_state_topic := config.get(CONF_MODE_STATE_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_mode_state_topic(mode_state_topic)) | ||||
|         if (preset_command_topic := config.get(CONF_PRESET_COMMAND_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_preset_command_topic(preset_command_topic)) | ||||
|         if (preset_state_topic := config.get(CONF_PRESET_STATE_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_preset_state_topic(preset_state_topic)) | ||||
|         if ( | ||||
|             swing_mode_command_topic := config.get(CONF_SWING_MODE_COMMAND_TOPIC) | ||||
|         ) is not None: | ||||
|             cg.add(mqtt_.set_custom_swing_mode_command_topic(swing_mode_command_topic)) | ||||
|         if ( | ||||
|             swing_mode_state_topic := config.get(CONF_SWING_MODE_STATE_TOPIC) | ||||
|         ) is not None: | ||||
|             cg.add(mqtt_.set_custom_swing_mode_state_topic(swing_mode_state_topic)) | ||||
|         if ( | ||||
|             target_temperature_command_topic := config.get( | ||||
|                 CONF_TARGET_TEMPERATURE_COMMAND_TOPIC | ||||
|             ) | ||||
|         if CONF_FAN_MODE_STATE_TOPIC in config: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_fan_mode_state_topic(config[CONF_FAN_MODE_STATE_TOPIC]) | ||||
|             ) | ||||
|         if CONF_MODE_COMMAND_TOPIC in config: | ||||
|             cg.add(mqtt_.set_custom_mode_command_topic(config[CONF_MODE_COMMAND_TOPIC])) | ||||
|         if CONF_MODE_STATE_TOPIC in config: | ||||
|             cg.add(mqtt_.set_custom_mode_state_topic(config[CONF_MODE_STATE_TOPIC])) | ||||
|         if CONF_PRESET_COMMAND_TOPIC in config: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_preset_command_topic(config[CONF_PRESET_COMMAND_TOPIC]) | ||||
|             ) | ||||
|         if CONF_PRESET_STATE_TOPIC in config: | ||||
|             cg.add(mqtt_.set_custom_preset_state_topic(config[CONF_PRESET_STATE_TOPIC])) | ||||
|         if CONF_SWING_MODE_COMMAND_TOPIC in config: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_swing_mode_command_topic( | ||||
|                     config[CONF_SWING_MODE_COMMAND_TOPIC] | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_SWING_MODE_STATE_TOPIC in config: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_swing_mode_state_topic( | ||||
|                     config[CONF_SWING_MODE_STATE_TOPIC] | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_TARGET_TEMPERATURE_COMMAND_TOPIC in config: | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_target_temperature_command_topic( | ||||
|                     config[CONF_TARGET_TEMPERATURE_COMMAND_TOPIC] | ||||
|                     target_temperature_command_topic | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_TARGET_TEMPERATURE_STATE_TOPIC in config: | ||||
|         if ( | ||||
|             target_temperature_state_topic := config.get( | ||||
|                 CONF_TARGET_TEMPERATURE_STATE_TOPIC | ||||
|             ) | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_target_temperature_state_topic( | ||||
|                     config[CONF_TARGET_TEMPERATURE_STATE_TOPIC] | ||||
|                     target_temperature_state_topic | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC in config: | ||||
|         if ( | ||||
|             target_temperature_high_command_topic := config.get( | ||||
|                 CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC | ||||
|             ) | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_target_temperature_high_command_topic( | ||||
|                     config[CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC] | ||||
|                     target_temperature_high_command_topic | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC in config: | ||||
|         if ( | ||||
|             target_temperature_high_state_topic := config.get( | ||||
|                 CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC | ||||
|             ) | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_target_temperature_high_state_topic( | ||||
|                     config[CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC] | ||||
|                     target_temperature_high_state_topic | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC in config: | ||||
|         if ( | ||||
|             target_temperature_low_command_topic := config.get( | ||||
|                 CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC | ||||
|             ) | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_target_temperature_low_command_topic( | ||||
|                     config[CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC] | ||||
|                     target_temperature_low_command_topic | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC in config: | ||||
|         if ( | ||||
|             target_temperature_low_state_topic := config.get( | ||||
|                 CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC | ||||
|             ) | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_target_temperature_state_topic( | ||||
|                     config[CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC] | ||||
|                     target_temperature_low_state_topic | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_TARGET_HUMIDITY_COMMAND_TOPIC in config: | ||||
|         if ( | ||||
|             target_humidity_command_topic := config.get( | ||||
|                 CONF_TARGET_HUMIDITY_COMMAND_TOPIC | ||||
|             ) | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_target_humidity_command_topic( | ||||
|                     config[CONF_TARGET_HUMIDITY_COMMAND_TOPIC] | ||||
|                     target_humidity_command_topic | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_TARGET_HUMIDITY_STATE_TOPIC in config: | ||||
|         if ( | ||||
|             target_humidity_state_topic := config.get(CONF_TARGET_HUMIDITY_STATE_TOPIC) | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_target_humidity_state_topic( | ||||
|                     config[CONF_TARGET_HUMIDITY_STATE_TOPIC] | ||||
|                     target_humidity_state_topic | ||||
|                 ) | ||||
|             ) | ||||
|  | ||||
| @@ -411,45 +439,35 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema( | ||||
| async def climate_control_to_code(config, action_id, template_arg, args): | ||||
|     paren = await cg.get_variable(config[CONF_ID]) | ||||
|     var = cg.new_Pvariable(action_id, template_arg, paren) | ||||
|     if CONF_MODE in config: | ||||
|         template_ = await cg.templatable(config[CONF_MODE], args, ClimateMode) | ||||
|     if (mode := config.get(CONF_MODE)) is not None: | ||||
|         template_ = await cg.templatable(mode, args, ClimateMode) | ||||
|         cg.add(var.set_mode(template_)) | ||||
|     if CONF_TARGET_TEMPERATURE in config: | ||||
|         template_ = await cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float) | ||||
|     if (target_temp := config.get(CONF_TARGET_TEMPERATURE)) is not None: | ||||
|         template_ = await cg.templatable(target_temp, args, float) | ||||
|         cg.add(var.set_target_temperature(template_)) | ||||
|     if CONF_TARGET_TEMPERATURE_LOW in config: | ||||
|         template_ = await cg.templatable( | ||||
|             config[CONF_TARGET_TEMPERATURE_LOW], args, float | ||||
|         ) | ||||
|     if (target_temp_low := config.get(CONF_TARGET_TEMPERATURE_LOW)) is not None: | ||||
|         template_ = await cg.templatable(target_temp_low, args, float) | ||||
|         cg.add(var.set_target_temperature_low(template_)) | ||||
|     if CONF_TARGET_TEMPERATURE_HIGH in config: | ||||
|         template_ = await cg.templatable( | ||||
|             config[CONF_TARGET_TEMPERATURE_HIGH], args, float | ||||
|         ) | ||||
|     if (target_temp_high := config.get(CONF_TARGET_TEMPERATURE_HIGH)) is not None: | ||||
|         template_ = await cg.templatable(target_temp_high, args, float) | ||||
|         cg.add(var.set_target_temperature_high(template_)) | ||||
|     if CONF_TARGET_HUMIDITY in config: | ||||
|         template_ = await cg.templatable(config[CONF_TARGET_HUMIDITY], args, float) | ||||
|     if (target_humidity := config.get(CONF_TARGET_HUMIDITY)) is not None: | ||||
|         template_ = await cg.templatable(target_humidity, args, float) | ||||
|         cg.add(var.set_target_humidity(template_)) | ||||
|     if CONF_FAN_MODE in config: | ||||
|         template_ = await cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode) | ||||
|     if (fan_mode := config.get(CONF_FAN_MODE)) is not None: | ||||
|         template_ = await cg.templatable(fan_mode, args, ClimateFanMode) | ||||
|         cg.add(var.set_fan_mode(template_)) | ||||
|     if CONF_CUSTOM_FAN_MODE in config: | ||||
|         template_ = await cg.templatable( | ||||
|             config[CONF_CUSTOM_FAN_MODE], args, cg.std_string | ||||
|         ) | ||||
|     if (custom_fan_mode := config.get(CONF_CUSTOM_FAN_MODE)) is not None: | ||||
|         template_ = await cg.templatable(custom_fan_mode, args, cg.std_string) | ||||
|         cg.add(var.set_custom_fan_mode(template_)) | ||||
|     if CONF_PRESET in config: | ||||
|         template_ = await cg.templatable(config[CONF_PRESET], args, ClimatePreset) | ||||
|     if (preset := config.get(CONF_PRESET)) is not None: | ||||
|         template_ = await cg.templatable(preset, args, ClimatePreset) | ||||
|         cg.add(var.set_preset(template_)) | ||||
|     if CONF_CUSTOM_PRESET in config: | ||||
|         template_ = await cg.templatable( | ||||
|             config[CONF_CUSTOM_PRESET], args, cg.std_string | ||||
|         ) | ||||
|     if (custom_preset := config.get(CONF_CUSTOM_PRESET)) is not None: | ||||
|         template_ = await cg.templatable(custom_preset, args, cg.std_string) | ||||
|         cg.add(var.set_custom_preset(template_)) | ||||
|     if CONF_SWING_MODE in config: | ||||
|         template_ = await cg.templatable( | ||||
|             config[CONF_SWING_MODE], args, ClimateSwingMode | ||||
|         ) | ||||
|     if (swing_mode := config.get(CONF_SWING_MODE)) is not None: | ||||
|         template_ = await cg.templatable(swing_mode, args, ClimateSwingMode) | ||||
|         cg.add(var.set_swing_mode(template_)) | ||||
|     return var | ||||
|  | ||||
|   | ||||
| @@ -14,15 +14,41 @@ CONF_HEX = "hex" | ||||
|  | ||||
|  | ||||
| def hex_color(value): | ||||
|     if isinstance(value, int): | ||||
|         value = str(value) | ||||
|     if not isinstance(value, str): | ||||
|         raise cv.Invalid("Invalid value for hex color") | ||||
|     if len(value) != 6: | ||||
|         raise cv.Invalid("Color must have six digits") | ||||
|         raise cv.Invalid("Hex color must have six digits") | ||||
|     try: | ||||
|         return (int(value[0:2], 16), int(value[2:4], 16), int(value[4:6], 16)) | ||||
|         return int(value[0:2], 16), int(value[2:4], 16), int(value[4:6], 16) | ||||
|     except ValueError as exc: | ||||
|         raise cv.Invalid("Color must be hexadecimal") from exc | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Any( | ||||
| components = { | ||||
|     CONF_RED, | ||||
|     CONF_RED_INT, | ||||
|     CONF_GREEN, | ||||
|     CONF_GREEN_INT, | ||||
|     CONF_BLUE, | ||||
|     CONF_BLUE_INT, | ||||
|     CONF_WHITE, | ||||
|     CONF_WHITE_INT, | ||||
| } | ||||
|  | ||||
|  | ||||
| def validate_color(config): | ||||
|     has_components = set(config) & components | ||||
|     has_hex = CONF_HEX in config | ||||
|     if has_hex and has_components: | ||||
|         raise cv.Invalid("Hex color value may not be combined with component values") | ||||
|     if not has_hex and not has_components: | ||||
|         raise cv.Invalid("Must provide at least one color option") | ||||
|     return config | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.Required(CONF_ID): cv.declare_id(ColorStruct), | ||||
| @@ -34,14 +60,10 @@ CONFIG_SCHEMA = cv.Any( | ||||
|             cv.Exclusive(CONF_BLUE_INT, "blue"): cv.uint8_t, | ||||
|             cv.Exclusive(CONF_WHITE, "white"): cv.percentage, | ||||
|             cv.Exclusive(CONF_WHITE_INT, "white"): cv.uint8_t, | ||||
|             cv.Optional(CONF_HEX): hex_color, | ||||
|         } | ||||
|     ).extend(cv.COMPONENT_SCHEMA), | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.Required(CONF_ID): cv.declare_id(ColorStruct), | ||||
|             cv.Required(CONF_HEX): hex_color, | ||||
|         } | ||||
|     ).extend(cv.COMPONENT_SCHEMA), | ||||
|     validate_color, | ||||
| ) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -122,8 +122,8 @@ COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ex | ||||
| async def setup_cover_core_(var, config): | ||||
|     await setup_entity(var, config) | ||||
|  | ||||
|     if CONF_DEVICE_CLASS in config: | ||||
|         cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) | ||||
|     if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: | ||||
|         cg.add(var.set_device_class(device_class)) | ||||
|  | ||||
|     for conf in config.get(CONF_ON_OPEN, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
| @@ -132,24 +132,20 @@ async def setup_cover_core_(var, config): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation(trigger, [], conf) | ||||
|  | ||||
|     if CONF_MQTT_ID in config: | ||||
|         mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) | ||||
|     if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|         if CONF_POSITION_STATE_TOPIC in config: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_position_state_topic(config[CONF_POSITION_STATE_TOPIC]) | ||||
|             ) | ||||
|         if CONF_POSITION_COMMAND_TOPIC in config: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_position_command_topic( | ||||
|                     config[CONF_POSITION_COMMAND_TOPIC] | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_TILT_STATE_TOPIC in config: | ||||
|             cg.add(mqtt_.set_custom_tilt_state_topic(config[CONF_TILT_STATE_TOPIC])) | ||||
|         if CONF_TILT_COMMAND_TOPIC in config: | ||||
|             cg.add(mqtt_.set_custom_tilt_command_topic(config[CONF_TILT_COMMAND_TOPIC])) | ||||
|         if (position_state_topic := config.get(CONF_POSITION_STATE_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_position_state_topic(position_state_topic)) | ||||
|         if ( | ||||
|             position_command_topic := config.get(CONF_POSITION_COMMAND_TOPIC) | ||||
|         ) is not None: | ||||
|             cg.add(mqtt_.set_custom_position_command_topic(position_command_topic)) | ||||
|         if (tilt_state_topic := config.get(CONF_TILT_STATE_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_tilt_state_topic(tilt_state_topic)) | ||||
|         if (tilt_command_topic := config.get(CONF_TILT_COMMAND_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_tilt_command_topic(tilt_command_topic)) | ||||
|  | ||||
|  | ||||
| async def register_cover(var, config): | ||||
| @@ -205,17 +201,17 @@ COVER_CONTROL_ACTION_SCHEMA = cv.Schema( | ||||
| async def cover_control_to_code(config, action_id, template_arg, args): | ||||
|     paren = await cg.get_variable(config[CONF_ID]) | ||||
|     var = cg.new_Pvariable(action_id, template_arg, paren) | ||||
|     if CONF_STOP in config: | ||||
|         template_ = await cg.templatable(config[CONF_STOP], args, bool) | ||||
|     if (stop := config.get(CONF_STOP)) is not None: | ||||
|         template_ = await cg.templatable(stop, args, bool) | ||||
|         cg.add(var.set_stop(template_)) | ||||
|     if CONF_STATE in config: | ||||
|         template_ = await cg.templatable(config[CONF_STATE], args, float) | ||||
|     if (state := config.get(CONF_STATE)) is not None: | ||||
|         template_ = await cg.templatable(state, args, float) | ||||
|         cg.add(var.set_position(template_)) | ||||
|     if CONF_POSITION in config: | ||||
|         template_ = await cg.templatable(config[CONF_POSITION], args, float) | ||||
|     if (position := config.get(CONF_POSITION)) is not None: | ||||
|         template_ = await cg.templatable(position, args, float) | ||||
|         cg.add(var.set_position(template_)) | ||||
|     if CONF_TILT in config: | ||||
|         template_ = await cg.templatable(config[CONF_TILT], args, float) | ||||
|     if (tilt := config.get(CONF_TILT)) is not None: | ||||
|         template_ = await cg.templatable(tilt, args, float) | ||||
|         cg.add(var.set_tilt(template_)) | ||||
|     return var | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_POWER, | ||||
|     CONF_VOLTAGE, | ||||
|     CONF_VOLTAGE_GAIN, | ||||
|     UNIT_VOLT, | ||||
|     UNIT_AMPERE, | ||||
|     UNIT_WATT, | ||||
| @@ -33,7 +34,6 @@ CONF_SAMPLES = "samples" | ||||
| CONF_PHASE_OFFSET = "phase_offset" | ||||
| CONF_PGA_GAIN = "pga_gain" | ||||
| CONF_CURRENT_GAIN = "current_gain" | ||||
| CONF_VOLTAGE_GAIN = "voltage_gain" | ||||
| CONF_CURRENT_HPF = "current_hpf" | ||||
| CONF_VOLTAGE_HPF = "voltage_hpf" | ||||
| CONF_PULSE_ENERGY = "pulse_energy" | ||||
|   | ||||
| @@ -5,17 +5,13 @@ namespace cst226 { | ||||
|  | ||||
| void CST226Touchscreen::setup() { | ||||
|   esph_log_config(TAG, "Setting up CST226 Touchscreen..."); | ||||
|   if (this->reset_pin_ != nullptr) { | ||||
|     this->reset_pin_->setup(); | ||||
|     this->reset_pin_->digital_write(true); | ||||
|     delay(5); | ||||
|     this->reset_pin_->digital_write(false); | ||||
|     delay(5); | ||||
|     this->reset_pin_->digital_write(true); | ||||
|     this->set_timeout(30, [this] { this->continue_setup_(); }); | ||||
|   } else { | ||||
|     this->continue_setup_(); | ||||
|   } | ||||
|   this->reset_pin_->setup(); | ||||
|   this->reset_pin_->digital_write(true); | ||||
|   delay(5); | ||||
|   this->reset_pin_->digital_write(false); | ||||
|   delay(5); | ||||
|   this->reset_pin_->digital_write(true); | ||||
|   this->set_timeout(30, [this] { this->continue_setup_(); }); | ||||
| } | ||||
|  | ||||
| void CST226Touchscreen::update_touches() { | ||||
|   | ||||
| @@ -35,7 +35,7 @@ class CST226Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice | ||||
|   void continue_setup_(); | ||||
|  | ||||
|   InternalGPIOPin *interrupt_pin_{}; | ||||
|   GPIOPin *reset_pin_{}; | ||||
|   GPIOPin *reset_pin_{NULL_PIN}; | ||||
|   uint8_t chip_id_{}; | ||||
|   bool setup_complete_{}; | ||||
| }; | ||||
|   | ||||
| @@ -1,14 +1,13 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import climate_ir | ||||
| from esphome.const import CONF_ID | ||||
| from esphome.const import CONF_ID, CONF_USE_FAHRENHEIT | ||||
|  | ||||
| AUTO_LOAD = ["climate_ir"] | ||||
|  | ||||
| daikin_brc_ns = cg.esphome_ns.namespace("daikin_brc") | ||||
| DaikinBrcClimate = daikin_brc_ns.class_("DaikinBrcClimate", climate_ir.ClimateIR) | ||||
|  | ||||
| CONF_USE_FAHRENHEIT = "use_fahrenheit" | ||||
|  | ||||
| CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( | ||||
|     { | ||||
|   | ||||
| @@ -4,6 +4,7 @@ from esphome.components import uart | ||||
| from esphome.const import CONF_ID, CONF_ADDRESS | ||||
|  | ||||
| CODEOWNERS = ["@s1lvi0"] | ||||
| MULTI_CONF = True | ||||
| DEPENDENCIES = ["uart"] | ||||
|  | ||||
| CONF_BMS_DALY_ID = "bms_daly_id" | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import esphome.codegen as cg | ||||
|  | ||||
| # import cpp_generator as cpp | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation | ||||
| from esphome.components import mqtt, time | ||||
| @@ -13,6 +12,7 @@ from esphome.const import ( | ||||
|     CONF_TYPE, | ||||
|     CONF_MQTT_ID, | ||||
|     CONF_DATE, | ||||
|     CONF_DATETIME, | ||||
|     CONF_TIME, | ||||
|     CONF_YEAR, | ||||
|     CONF_MONTH, | ||||
| @@ -27,6 +27,7 @@ from esphome.cpp_helpers import setup_entity | ||||
|  | ||||
|  | ||||
| CODEOWNERS = ["@rfdarter", "@jesserockz"] | ||||
| DEPENDENCIES = ["time"] | ||||
|  | ||||
| IS_PLATFORM_COMPONENT = True | ||||
|  | ||||
| @@ -34,10 +35,12 @@ datetime_ns = cg.esphome_ns.namespace("datetime") | ||||
| DateTimeBase = datetime_ns.class_("DateTimeBase", cg.EntityBase) | ||||
| DateEntity = datetime_ns.class_("DateEntity", DateTimeBase) | ||||
| TimeEntity = datetime_ns.class_("TimeEntity", DateTimeBase) | ||||
| DateTimeEntity = datetime_ns.class_("DateTimeEntity", DateTimeBase) | ||||
|  | ||||
| # Actions | ||||
| DateSetAction = datetime_ns.class_("DateSetAction", automation.Action) | ||||
| TimeSetAction = datetime_ns.class_("TimeSetAction", automation.Action) | ||||
| DateTimeSetAction = datetime_ns.class_("DateTimeSetAction", automation.Action) | ||||
|  | ||||
| DateTimeStateTrigger = datetime_ns.class_( | ||||
|     "DateTimeStateTrigger", automation.Trigger.template(cg.ESPTime) | ||||
| @@ -46,6 +49,12 @@ DateTimeStateTrigger = datetime_ns.class_( | ||||
| OnTimeTrigger = datetime_ns.class_( | ||||
|     "OnTimeTrigger", automation.Trigger, cg.Component, cg.Parented.template(TimeEntity) | ||||
| ) | ||||
| OnDateTimeTrigger = datetime_ns.class_( | ||||
|     "OnDateTimeTrigger", | ||||
|     automation.Trigger, | ||||
|     cg.Component, | ||||
|     cg.Parented.template(DateTimeEntity), | ||||
| ) | ||||
|  | ||||
| DATETIME_MODES = [ | ||||
|     "DATE", | ||||
| @@ -61,65 +70,73 @@ _DATETIME_SCHEMA = cv.Schema( | ||||
|                 cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger), | ||||
|             } | ||||
|         ), | ||||
|         cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock), | ||||
|     } | ||||
| ).extend(cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)) | ||||
|  | ||||
|  | ||||
| def date_schema(class_: MockObjClass) -> cv.Schema: | ||||
|     schema = { | ||||
|         cv.GenerateID(): cv.declare_id(class_), | ||||
|         cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTDateComponent), | ||||
|         cv.Optional(CONF_TYPE, default="DATE"): cv.one_of("DATE", upper=True), | ||||
|     } | ||||
|     schema = cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(class_), | ||||
|             cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTDateComponent), | ||||
|             cv.Optional(CONF_TYPE, default="DATE"): cv.one_of("DATE", upper=True), | ||||
|         } | ||||
|     ) | ||||
|     return _DATETIME_SCHEMA.extend(schema) | ||||
|  | ||||
|  | ||||
| def time_schema(class_: MockObjClass) -> cv.Schema: | ||||
|     schema = { | ||||
|         cv.GenerateID(): cv.declare_id(class_), | ||||
|         cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTimeComponent), | ||||
|         cv.Optional(CONF_TYPE, default="TIME"): cv.one_of("TIME", upper=True), | ||||
|         cv.Inclusive( | ||||
|             CONF_ON_TIME, | ||||
|             group_of_inclusion=CONF_ON_TIME, | ||||
|             msg="`on_time` and `time_id` must both be specified", | ||||
|         ): automation.validate_automation( | ||||
|             { | ||||
|                 cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OnTimeTrigger), | ||||
|             } | ||||
|         ), | ||||
|         cv.Inclusive(CONF_TIME_ID, group_of_inclusion=CONF_ON_TIME): cv.use_id( | ||||
|             time.RealTimeClock | ||||
|         ), | ||||
|     } | ||||
|     schema = cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(class_), | ||||
|             cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTimeComponent), | ||||
|             cv.Optional(CONF_TYPE, default="TIME"): cv.one_of("TIME", upper=True), | ||||
|             cv.Optional(CONF_ON_TIME): automation.validate_automation( | ||||
|                 { | ||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OnTimeTrigger), | ||||
|                 } | ||||
|             ), | ||||
|         } | ||||
|     ) | ||||
|     return _DATETIME_SCHEMA.extend(schema) | ||||
|  | ||||
|  | ||||
| def datetime_schema(class_: MockObjClass) -> cv.Schema: | ||||
|     schema = { | ||||
|         cv.GenerateID(): cv.declare_id(class_), | ||||
|         cv.Optional(CONF_TYPE, default="DATETIME"): cv.one_of("DATETIME", upper=True), | ||||
|     } | ||||
|     schema = cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(class_), | ||||
|             cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id( | ||||
|                 mqtt.MQTTDateTimeComponent | ||||
|             ), | ||||
|             cv.Optional(CONF_TYPE, default="DATETIME"): cv.one_of( | ||||
|                 "DATETIME", upper=True | ||||
|             ), | ||||
|             cv.Optional(CONF_ON_TIME): automation.validate_automation( | ||||
|                 { | ||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OnDateTimeTrigger), | ||||
|                 } | ||||
|             ), | ||||
|         } | ||||
|     ) | ||||
|     return _DATETIME_SCHEMA.extend(schema) | ||||
|  | ||||
|  | ||||
| async def setup_datetime_core_(var, config): | ||||
|     await setup_entity(var, config) | ||||
|  | ||||
|     if CONF_MQTT_ID in config: | ||||
|         mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) | ||||
|     if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|     for conf in config.get(CONF_ON_VALUE, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf) | ||||
|  | ||||
|     rtc_id = config.get(CONF_TIME_ID) | ||||
|     rtc = None | ||||
|     if rtc_id is not None: | ||||
|         rtc = await cg.get_variable(rtc_id) | ||||
|     rtc = await cg.get_variable(config[CONF_TIME_ID]) | ||||
|     cg.add(var.set_rtc(rtc)) | ||||
|  | ||||
|     for conf in config.get(CONF_ON_TIME, []): | ||||
|         assert rtc is not None | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], rtc) | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) | ||||
|         await automation.build_automation(trigger, [], conf) | ||||
|         await cg.register_component(trigger, conf) | ||||
|         await cg.register_parented(trigger, var) | ||||
| @@ -152,7 +169,7 @@ async def to_code(config): | ||||
|         { | ||||
|             cv.Required(CONF_ID): cv.use_id(DateEntity), | ||||
|             cv.Required(CONF_DATE): cv.Any( | ||||
|                 cv.returning_lambda, cv.date_time(allowed_time=False) | ||||
|                 cv.returning_lambda, cv.date_time(date=True, time=False) | ||||
|             ), | ||||
|         } | ||||
|     ), | ||||
| @@ -161,16 +178,16 @@ async def datetime_date_set_to_code(config, action_id, template_arg, args): | ||||
|     action_var = cg.new_Pvariable(action_id, template_arg) | ||||
|     await cg.register_parented(action_var, config[CONF_ID]) | ||||
|  | ||||
|     date = config[CONF_DATE] | ||||
|     if cg.is_template(date): | ||||
|         template_ = await cg.templatable(config[CONF_DATE], [], cg.ESPTime) | ||||
|     date_config = config[CONF_DATE] | ||||
|     if cg.is_template(date_config): | ||||
|         template_ = await cg.templatable(date_config, [], cg.ESPTime) | ||||
|         cg.add(action_var.set_date(template_)) | ||||
|     else: | ||||
|         date_struct = cg.StructInitializer( | ||||
|             cg.ESPTime, | ||||
|             ("day_of_month", date[CONF_DAY]), | ||||
|             ("month", date[CONF_MONTH]), | ||||
|             ("year", date[CONF_YEAR]), | ||||
|             ("day_of_month", date_config[CONF_DAY]), | ||||
|             ("month", date_config[CONF_MONTH]), | ||||
|             ("year", date_config[CONF_YEAR]), | ||||
|         ) | ||||
|         cg.add(action_var.set_date(date_struct)) | ||||
|     return action_var | ||||
| @@ -183,7 +200,7 @@ async def datetime_date_set_to_code(config, action_id, template_arg, args): | ||||
|         { | ||||
|             cv.Required(CONF_ID): cv.use_id(TimeEntity), | ||||
|             cv.Required(CONF_TIME): cv.Any( | ||||
|                 cv.returning_lambda, cv.date_time(allowed_date=False) | ||||
|                 cv.returning_lambda, cv.date_time(date=False, time=True) | ||||
|             ), | ||||
|         } | ||||
|     ), | ||||
| @@ -194,7 +211,7 @@ async def datetime_time_set_to_code(config, action_id, template_arg, args): | ||||
|  | ||||
|     time_config = config[CONF_TIME] | ||||
|     if cg.is_template(time_config): | ||||
|         template_ = await cg.templatable(config[CONF_TIME], [], cg.ESPTime) | ||||
|         template_ = await cg.templatable(time_config, [], cg.ESPTime) | ||||
|         cg.add(action_var.set_time(template_)) | ||||
|     else: | ||||
|         time_struct = cg.StructInitializer( | ||||
| @@ -205,3 +222,37 @@ async def datetime_time_set_to_code(config, action_id, template_arg, args): | ||||
|         ) | ||||
|         cg.add(action_var.set_time(time_struct)) | ||||
|     return action_var | ||||
|  | ||||
|  | ||||
| @automation.register_action( | ||||
|     "datetime.datetime.set", | ||||
|     DateTimeSetAction, | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.Required(CONF_ID): cv.use_id(DateTimeEntity), | ||||
|             cv.Required(CONF_DATETIME): cv.Any( | ||||
|                 cv.returning_lambda, cv.date_time(date=True, time=True) | ||||
|             ), | ||||
|         }, | ||||
|     ), | ||||
| ) | ||||
| async def datetime_datetime_set_to_code(config, action_id, template_arg, args): | ||||
|     action_var = cg.new_Pvariable(action_id, template_arg) | ||||
|     await cg.register_parented(action_var, config[CONF_ID]) | ||||
|  | ||||
|     datetime_config = config[CONF_DATETIME] | ||||
|     if cg.is_template(datetime_config): | ||||
|         template_ = await cg.templatable(datetime_config, [], cg.ESPTime) | ||||
|         cg.add(action_var.set_datetime(template_)) | ||||
|     else: | ||||
|         datetime_struct = cg.StructInitializer( | ||||
|             cg.ESPTime, | ||||
|             ("second", datetime_config[CONF_SECOND]), | ||||
|             ("minute", datetime_config[CONF_MINUTE]), | ||||
|             ("hour", datetime_config[CONF_HOUR]), | ||||
|             ("day_of_month", datetime_config[CONF_DAY]), | ||||
|             ("month", datetime_config[CONF_MONTH]), | ||||
|             ("year", datetime_config[CONF_YEAR]), | ||||
|         ) | ||||
|         cg.add(action_var.set_datetime(datetime_struct)) | ||||
|     return action_var | ||||
|   | ||||
| @@ -40,10 +40,13 @@ void DateCall::validate_() { | ||||
|   if (this->year_.has_value() && (this->year_ < 1970 || this->year_ > 3000)) { | ||||
|     ESP_LOGE(TAG, "Year must be between 1970 and 3000"); | ||||
|     this->year_.reset(); | ||||
|     this->month_.reset(); | ||||
|     this->day_.reset(); | ||||
|   } | ||||
|   if (this->month_.has_value() && (this->month_ < 1 || this->month_ > 12)) { | ||||
|     ESP_LOGE(TAG, "Month must be between 1 and 12"); | ||||
|     this->month_.reset(); | ||||
|     this->day_.reset(); | ||||
|   } | ||||
|   if (this->day_.has_value()) { | ||||
|     uint16_t year = 0; | ||||
|   | ||||
| @@ -5,6 +5,8 @@ | ||||
| #include "esphome/core/entity_base.h" | ||||
| #include "esphome/core/time.h" | ||||
|  | ||||
| #include "esphome/components/time/real_time_clock.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace datetime { | ||||
|  | ||||
| @@ -17,9 +19,14 @@ class DateTimeBase : public EntityBase { | ||||
|  | ||||
|   void add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); } | ||||
|  | ||||
|   void set_rtc(time::RealTimeClock *rtc) { this->rtc_ = rtc; } | ||||
|   time::RealTimeClock *get_rtc() const { return this->rtc_; } | ||||
|  | ||||
|  protected: | ||||
|   CallbackManager<void()> state_callback_; | ||||
|  | ||||
|   time::RealTimeClock *rtc_; | ||||
|  | ||||
|   bool has_state_{false}; | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										252
									
								
								esphome/components/datetime/datetime_entity.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								esphome/components/datetime/datetime_entity.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,252 @@ | ||||
| #include "datetime_entity.h" | ||||
|  | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|  | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace datetime { | ||||
|  | ||||
| static const char *const TAG = "datetime.datetime_entity"; | ||||
|  | ||||
| void DateTimeEntity::publish_state() { | ||||
|   if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) { | ||||
|     this->has_state_ = false; | ||||
|     return; | ||||
|   } | ||||
|   if (this->year_ < 1970 || this->year_ > 3000) { | ||||
|     this->has_state_ = false; | ||||
|     ESP_LOGE(TAG, "Year must be between 1970 and 3000"); | ||||
|     return; | ||||
|   } | ||||
|   if (this->month_ < 1 || this->month_ > 12) { | ||||
|     this->has_state_ = false; | ||||
|     ESP_LOGE(TAG, "Month must be between 1 and 12"); | ||||
|     return; | ||||
|   } | ||||
|   if (this->day_ > days_in_month(this->month_, this->year_)) { | ||||
|     this->has_state_ = false; | ||||
|     ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_); | ||||
|     return; | ||||
|   } | ||||
|   if (this->hour_ > 23) { | ||||
|     this->has_state_ = false; | ||||
|     ESP_LOGE(TAG, "Hour must be between 0 and 23"); | ||||
|     return; | ||||
|   } | ||||
|   if (this->minute_ > 59) { | ||||
|     this->has_state_ = false; | ||||
|     ESP_LOGE(TAG, "Minute must be between 0 and 59"); | ||||
|     return; | ||||
|   } | ||||
|   if (this->second_ > 59) { | ||||
|     this->has_state_ = false; | ||||
|     ESP_LOGE(TAG, "Second must be between 0 and 59"); | ||||
|     return; | ||||
|   } | ||||
|   this->has_state_ = true; | ||||
|   ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_, | ||||
|            this->month_, this->day_, this->hour_, this->minute_, this->second_); | ||||
|   this->state_callback_.call(); | ||||
| } | ||||
|  | ||||
| DateTimeCall DateTimeEntity::make_call() { return DateTimeCall(this); } | ||||
|  | ||||
| ESPTime DateTimeEntity::state_as_esptime() const { | ||||
|   ESPTime obj; | ||||
|   obj.year = this->year_; | ||||
|   obj.month = this->month_; | ||||
|   obj.day_of_month = this->day_; | ||||
|   obj.hour = this->hour_; | ||||
|   obj.minute = this->minute_; | ||||
|   obj.second = this->second_; | ||||
|   obj.day_of_week = 1;  // Required to be valid for recalc_timestamp_local but not used. | ||||
|   obj.day_of_year = 1;  // Required to be valid for recalc_timestamp_local but not used. | ||||
|   obj.recalc_timestamp_local(false); | ||||
|   return obj; | ||||
| } | ||||
|  | ||||
| void DateTimeCall::validate_() { | ||||
|   if (this->year_.has_value() && (this->year_ < 1970 || this->year_ > 3000)) { | ||||
|     ESP_LOGE(TAG, "Year must be between 1970 and 3000"); | ||||
|     this->year_.reset(); | ||||
|     this->month_.reset(); | ||||
|     this->day_.reset(); | ||||
|   } | ||||
|   if (this->month_.has_value() && (this->month_ < 1 || this->month_ > 12)) { | ||||
|     ESP_LOGE(TAG, "Month must be between 1 and 12"); | ||||
|     this->month_.reset(); | ||||
|     this->day_.reset(); | ||||
|   } | ||||
|   if (this->day_.has_value()) { | ||||
|     uint16_t year = 0; | ||||
|     uint8_t month = 0; | ||||
|     if (this->month_.has_value()) { | ||||
|       month = *this->month_; | ||||
|     } else { | ||||
|       if (this->parent_->month != 0) { | ||||
|         month = this->parent_->month; | ||||
|       } else { | ||||
|         ESP_LOGE(TAG, "Month must be set to validate day"); | ||||
|         this->day_.reset(); | ||||
|       } | ||||
|     } | ||||
|     if (this->year_.has_value()) { | ||||
|       year = *this->year_; | ||||
|     } else { | ||||
|       if (this->parent_->year != 0) { | ||||
|         year = this->parent_->year; | ||||
|       } else { | ||||
|         ESP_LOGE(TAG, "Year must be set to validate day"); | ||||
|         this->day_.reset(); | ||||
|       } | ||||
|     } | ||||
|     if (this->day_.has_value() && *this->day_ > days_in_month(month, year)) { | ||||
|       ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(month, year), month); | ||||
|       this->day_.reset(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (this->hour_.has_value() && this->hour_ > 23) { | ||||
|     ESP_LOGE(TAG, "Hour must be between 0 and 23"); | ||||
|     this->hour_.reset(); | ||||
|   } | ||||
|   if (this->minute_.has_value() && this->minute_ > 59) { | ||||
|     ESP_LOGE(TAG, "Minute must be between 0 and 59"); | ||||
|     this->minute_.reset(); | ||||
|   } | ||||
|   if (this->second_.has_value() && this->second_ > 59) { | ||||
|     ESP_LOGE(TAG, "Second must be between 0 and 59"); | ||||
|     this->second_.reset(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void DateTimeCall::perform() { | ||||
|   this->validate_(); | ||||
|   ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str()); | ||||
|  | ||||
|   if (this->year_.has_value()) { | ||||
|     ESP_LOGD(TAG, " Year: %d", *this->year_); | ||||
|   } | ||||
|   if (this->month_.has_value()) { | ||||
|     ESP_LOGD(TAG, " Month: %d", *this->month_); | ||||
|   } | ||||
|   if (this->day_.has_value()) { | ||||
|     ESP_LOGD(TAG, " Day: %d", *this->day_); | ||||
|   } | ||||
|   if (this->hour_.has_value()) { | ||||
|     ESP_LOGD(TAG, " Hour: %d", *this->hour_); | ||||
|   } | ||||
|   if (this->minute_.has_value()) { | ||||
|     ESP_LOGD(TAG, " Minute: %d", *this->minute_); | ||||
|   } | ||||
|   if (this->second_.has_value()) { | ||||
|     ESP_LOGD(TAG, " Second: %d", *this->second_); | ||||
|   } | ||||
|   this->parent_->control(*this); | ||||
| } | ||||
|  | ||||
| DateTimeCall &DateTimeCall::set_datetime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, | ||||
|                                          uint8_t second) { | ||||
|   this->year_ = year; | ||||
|   this->month_ = month; | ||||
|   this->day_ = day; | ||||
|   this->hour_ = hour; | ||||
|   this->minute_ = minute; | ||||
|   this->second_ = second; | ||||
|   return *this; | ||||
| }; | ||||
|  | ||||
| DateTimeCall &DateTimeCall::set_datetime(ESPTime datetime) { | ||||
|   return this->set_datetime(datetime.year, datetime.month, datetime.day_of_month, datetime.hour, datetime.minute, | ||||
|                             datetime.second); | ||||
| }; | ||||
|  | ||||
| DateTimeCall &DateTimeCall::set_datetime(const std::string &datetime) { | ||||
|   ESPTime val{}; | ||||
|   if (!ESPTime::strptime(datetime, val)) { | ||||
|     ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object"); | ||||
|     return *this; | ||||
|   } | ||||
|   return this->set_datetime(val); | ||||
| } | ||||
|  | ||||
| DateTimeCall &DateTimeCall::set_datetime(time_t epoch_seconds) { | ||||
|   ESPTime val = ESPTime::from_epoch_local(epoch_seconds); | ||||
|   return this->set_datetime(val); | ||||
| } | ||||
|  | ||||
| DateTimeCall DateTimeEntityRestoreState::to_call(DateTimeEntity *datetime) { | ||||
|   DateTimeCall call = datetime->make_call(); | ||||
|   call.set_datetime(this->year, this->month, this->day, this->hour, this->minute, this->second); | ||||
|   return call; | ||||
| } | ||||
|  | ||||
| void DateTimeEntityRestoreState::apply(DateTimeEntity *time) { | ||||
|   time->year_ = this->year; | ||||
|   time->month_ = this->month; | ||||
|   time->day_ = this->day; | ||||
|   time->hour_ = this->hour; | ||||
|   time->minute_ = this->minute; | ||||
|   time->second_ = this->second; | ||||
|   time->publish_state(); | ||||
| } | ||||
|  | ||||
| static const int MAX_TIMESTAMP_DRIFT = 900;  // how far can the clock drift before we consider | ||||
|                                              // there has been a drastic time synchronization | ||||
|  | ||||
| void OnDateTimeTrigger::loop() { | ||||
|   if (!this->parent_->has_state()) { | ||||
|     return; | ||||
|   } | ||||
|   ESPTime time = this->parent_->rtc_->now(); | ||||
|   if (!time.is_valid()) { | ||||
|     return; | ||||
|   } | ||||
|   if (this->last_check_.has_value()) { | ||||
|     if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) { | ||||
|       // We went back in time (a lot), probably caused by time synchronization | ||||
|       ESP_LOGW(TAG, "Time has jumped back!"); | ||||
|     } else if (*this->last_check_ >= time) { | ||||
|       // already handled this one | ||||
|       return; | ||||
|     } else if (time > *this->last_check_ && time.timestamp - this->last_check_->timestamp > MAX_TIMESTAMP_DRIFT) { | ||||
|       // We went ahead in time (a lot), probably caused by time synchronization | ||||
|       ESP_LOGW(TAG, "Time has jumped ahead!"); | ||||
|       this->last_check_ = time; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     while (true) { | ||||
|       this->last_check_->increment_second(); | ||||
|       if (*this->last_check_ >= time) | ||||
|         break; | ||||
|  | ||||
|       if (this->matches_(*this->last_check_)) { | ||||
|         this->trigger(); | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   this->last_check_ = time; | ||||
|   if (!time.fields_in_range()) { | ||||
|     ESP_LOGW(TAG, "Time is out of range!"); | ||||
|     ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u Day=%02u Month=%02u Year=%04u", time.second, time.minute, | ||||
|              time.hour, time.day_of_month, time.month, time.year); | ||||
|   } | ||||
|  | ||||
|   if (this->matches_(time)) | ||||
|     this->trigger(); | ||||
| } | ||||
|  | ||||
| bool OnDateTimeTrigger::matches_(const ESPTime &time) const { | ||||
|   return time.is_valid() && time.year == this->parent_->year && time.month == this->parent_->month && | ||||
|          time.day_of_month == this->parent_->day && time.hour == this->parent_->hour && | ||||
|          time.minute == this->parent_->minute && time.second == this->parent_->second; | ||||
| } | ||||
|  | ||||
| }  // namespace datetime | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif  // USE_DATETIME_TIME | ||||
							
								
								
									
										150
									
								
								esphome/components/datetime/datetime_entity.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								esphome/components/datetime/datetime_entity.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/defines.h" | ||||
|  | ||||
| #ifdef USE_DATETIME_DATETIME | ||||
|  | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/time.h" | ||||
|  | ||||
| #include "datetime_base.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace datetime { | ||||
|  | ||||
| #define LOG_DATETIME_DATETIME(prefix, type, obj) \ | ||||
|   if ((obj) != nullptr) { \ | ||||
|     ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ | ||||
|     if (!(obj)->get_icon().empty()) { \ | ||||
|       ESP_LOGCONFIG(TAG, "%s  Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ | ||||
|     } \ | ||||
|   } | ||||
|  | ||||
| class DateTimeCall; | ||||
| class DateTimeEntity; | ||||
|  | ||||
| struct DateTimeEntityRestoreState { | ||||
|   uint16_t year; | ||||
|   uint8_t month; | ||||
|   uint8_t day; | ||||
|   uint8_t hour; | ||||
|   uint8_t minute; | ||||
|   uint8_t second; | ||||
|  | ||||
|   DateTimeCall to_call(DateTimeEntity *datetime); | ||||
|   void apply(DateTimeEntity *datetime); | ||||
| } __attribute__((packed)); | ||||
|  | ||||
| class DateTimeEntity : public DateTimeBase { | ||||
|  protected: | ||||
|   uint16_t year_; | ||||
|   uint8_t month_; | ||||
|   uint8_t day_; | ||||
|   uint8_t hour_; | ||||
|   uint8_t minute_; | ||||
|   uint8_t second_; | ||||
|  | ||||
|  public: | ||||
|   void publish_state(); | ||||
|   DateTimeCall make_call(); | ||||
|  | ||||
|   ESPTime state_as_esptime() const override; | ||||
|  | ||||
|   const uint16_t &year = year_; | ||||
|   const uint8_t &month = month_; | ||||
|   const uint8_t &day = day_; | ||||
|   const uint8_t &hour = hour_; | ||||
|   const uint8_t &minute = minute_; | ||||
|   const uint8_t &second = second_; | ||||
|  | ||||
|  protected: | ||||
|   friend class DateTimeCall; | ||||
|   friend struct DateTimeEntityRestoreState; | ||||
|   friend class OnDateTimeTrigger; | ||||
|  | ||||
|   virtual void control(const DateTimeCall &call) = 0; | ||||
| }; | ||||
|  | ||||
| class DateTimeCall { | ||||
|  public: | ||||
|   explicit DateTimeCall(DateTimeEntity *parent) : parent_(parent) {} | ||||
|   void perform(); | ||||
|   DateTimeCall &set_datetime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); | ||||
|   DateTimeCall &set_datetime(ESPTime datetime); | ||||
|   DateTimeCall &set_datetime(const std::string &datetime); | ||||
|   DateTimeCall &set_datetime(time_t epoch_seconds); | ||||
|  | ||||
|   DateTimeCall &set_year(uint16_t year) { | ||||
|     this->year_ = year; | ||||
|     return *this; | ||||
|   } | ||||
|   DateTimeCall &set_month(uint8_t month) { | ||||
|     this->month_ = month; | ||||
|     return *this; | ||||
|   } | ||||
|   DateTimeCall &set_day(uint8_t day) { | ||||
|     this->day_ = day; | ||||
|     return *this; | ||||
|   } | ||||
|   DateTimeCall &set_hour(uint8_t hour) { | ||||
|     this->hour_ = hour; | ||||
|     return *this; | ||||
|   } | ||||
|   DateTimeCall &set_minute(uint8_t minute) { | ||||
|     this->minute_ = minute; | ||||
|     return *this; | ||||
|   } | ||||
|   DateTimeCall &set_second(uint8_t second) { | ||||
|     this->second_ = second; | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   optional<uint16_t> get_year() const { return this->year_; } | ||||
|   optional<uint8_t> get_month() const { return this->month_; } | ||||
|   optional<uint8_t> get_day() const { return this->day_; } | ||||
|   optional<uint8_t> get_hour() const { return this->hour_; } | ||||
|   optional<uint8_t> get_minute() const { return this->minute_; } | ||||
|   optional<uint8_t> get_second() const { return this->second_; } | ||||
|  | ||||
|  protected: | ||||
|   void validate_(); | ||||
|  | ||||
|   DateTimeEntity *parent_; | ||||
|  | ||||
|   optional<uint16_t> year_; | ||||
|   optional<uint8_t> month_; | ||||
|   optional<uint8_t> day_; | ||||
|   optional<uint8_t> hour_; | ||||
|   optional<uint8_t> minute_; | ||||
|   optional<uint8_t> second_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class DateTimeSetAction : public Action<Ts...>, public Parented<DateTimeEntity> { | ||||
|  public: | ||||
|   TEMPLATABLE_VALUE(ESPTime, datetime) | ||||
|  | ||||
|   void play(Ts... x) override { | ||||
|     auto call = this->parent_->make_call(); | ||||
|  | ||||
|     if (this->datetime_.has_value()) { | ||||
|       call.set_datetime(this->datetime_.value(x...)); | ||||
|     } | ||||
|     call.perform(); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| class OnDateTimeTrigger : public Trigger<>, public Component, public Parented<DateTimeEntity> { | ||||
|  public: | ||||
|   void loop() override; | ||||
|  | ||||
|  protected: | ||||
|   bool matches_(const ESPTime &time) const; | ||||
|  | ||||
|   optional<ESPTime> last_check_; | ||||
| }; | ||||
|  | ||||
| }  // namespace datetime | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif  // USE_DATETIME_DATETIME | ||||
| @@ -94,8 +94,6 @@ void TimeEntityRestoreState::apply(TimeEntity *time) { | ||||
|   time->publish_state(); | ||||
| } | ||||
|  | ||||
| #ifdef USE_TIME | ||||
|  | ||||
| static const int MAX_TIMESTAMP_DRIFT = 900;  // how far can the clock drift before we consider | ||||
|                                              // there has been a drastic time synchronization | ||||
|  | ||||
| @@ -103,7 +101,7 @@ void OnTimeTrigger::loop() { | ||||
|   if (!this->parent_->has_state()) { | ||||
|     return; | ||||
|   } | ||||
|   ESPTime time = this->rtc_->now(); | ||||
|   ESPTime time = this->parent_->rtc_->now(); | ||||
|   if (!time.is_valid()) { | ||||
|     return; | ||||
|   } | ||||
| @@ -148,8 +146,6 @@ bool OnTimeTrigger::matches_(const ESPTime &time) const { | ||||
|          time.second == this->parent_->second; | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| }  // namespace datetime | ||||
| }  // namespace esphome | ||||
|  | ||||
|   | ||||
| @@ -10,10 +10,6 @@ | ||||
|  | ||||
| #include "datetime_base.h" | ||||
|  | ||||
| #ifdef USE_TIME | ||||
| #include "esphome/components/time/real_time_clock.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace datetime { | ||||
|  | ||||
| @@ -27,6 +23,7 @@ namespace datetime { | ||||
|  | ||||
| class TimeCall; | ||||
| class TimeEntity; | ||||
| class OnTimeTrigger; | ||||
|  | ||||
| struct TimeEntityRestoreState { | ||||
|   uint8_t hour; | ||||
| @@ -62,6 +59,7 @@ class TimeEntity : public DateTimeBase { | ||||
|  protected: | ||||
|   friend class TimeCall; | ||||
|   friend struct TimeEntityRestoreState; | ||||
|   friend class OnTimeTrigger; | ||||
|  | ||||
|   virtual void control(const TimeCall &call) = 0; | ||||
| }; | ||||
| @@ -115,22 +113,16 @@ template<typename... Ts> class TimeSetAction : public Action<Ts...>, public Pare | ||||
|   } | ||||
| }; | ||||
|  | ||||
| #ifdef USE_TIME | ||||
|  | ||||
| class OnTimeTrigger : public Trigger<>, public Component, public Parented<TimeEntity> { | ||||
|  public: | ||||
|   explicit OnTimeTrigger(time::RealTimeClock *rtc) : rtc_(rtc) {} | ||||
|   void loop() override; | ||||
|  | ||||
|  protected: | ||||
|   bool matches_(const ESPTime &time) const; | ||||
|  | ||||
|   time::RealTimeClock *rtc_; | ||||
|   optional<ESPTime> last_check_; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|  | ||||
| }  // namespace datetime | ||||
| }  // namespace esphome | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation | ||||
| from esphome.automation import maybe_simple_id | ||||
| from esphome.const import CONF_ID | ||||
| from esphome.const import CONF_FACTORY_RESET, CONF_ID, CONF_SENSITIVITY | ||||
| from esphome.components import uart | ||||
|  | ||||
| CODEOWNERS = ["@niklasweber"] | ||||
| @@ -28,8 +28,6 @@ CONF_DELAY_AFTER_DETECT = "delay_after_detect" | ||||
| CONF_DELAY_AFTER_DISAPPEAR = "delay_after_disappear" | ||||
| CONF_DETECTION_SEGMENTS = "detection_segments" | ||||
| CONF_OUTPUT_LATENCY = "output_latency" | ||||
| CONF_FACTORY_RESET = "factory_reset" | ||||
| CONF_SENSITIVITY = "sensitivity" | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     cv.Schema( | ||||
|   | ||||
| @@ -38,6 +38,7 @@ DisplayOnPageChangeTrigger = display_ns.class_( | ||||
| ) | ||||
|  | ||||
| CONF_ON_PAGE_CHANGE = "on_page_change" | ||||
| CONF_SHOW_TEST_CARD = "show_test_card" | ||||
|  | ||||
| DISPLAY_ROTATIONS = { | ||||
|     0: display_ns.DISPLAY_ROTATION_0_DEGREES, | ||||
| @@ -82,6 +83,7 @@ FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend( | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_AUTO_CLEAR_ENABLED, default=True): cv.boolean, | ||||
|         cv.Optional(CONF_SHOW_TEST_CARD): cv.boolean, | ||||
|     } | ||||
| ) | ||||
|  | ||||
| @@ -113,6 +115,8 @@ async def setup_display_core_(var, config): | ||||
|         await automation.build_automation( | ||||
|             trigger, [(DisplayPagePtr, "from"), (DisplayPagePtr, "to")], conf | ||||
|         ) | ||||
|     if config.get(CONF_SHOW_TEST_CARD): | ||||
|         cg.add(var.show_test_card()) | ||||
|  | ||||
|  | ||||
| async def register_display(var, config): | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| #include "display.h" | ||||
|  | ||||
| #include "display_color_utils.h" | ||||
| #include <utility> | ||||
|  | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -507,7 +507,9 @@ void Display::do_update_() { | ||||
|   if (this->auto_clear_enabled_) { | ||||
|     this->clear(); | ||||
|   } | ||||
|   if (this->page_ != nullptr) { | ||||
|   if (this->show_test_card_) { | ||||
|     this->test_card(); | ||||
|   } else if (this->page_ != nullptr) { | ||||
|     this->page_->get_writer()(*this); | ||||
|   } else if (this->writer_.has_value()) { | ||||
|     (*this->writer_)(*this); | ||||
| @@ -608,6 +610,62 @@ bool Display::clamp_y_(int y, int h, int &min_y, int &max_y) { | ||||
|   return min_y < max_y; | ||||
| } | ||||
|  | ||||
| const uint8_t TESTCARD_FONT[3][8] PROGMEM = {{0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00},   // 'R' | ||||
|                                              {0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00},   // 'G' | ||||
|                                              {0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00}};  // 'B' | ||||
|  | ||||
| void Display::test_card() { | ||||
|   int w = get_width(), h = get_height(), image_w, image_h; | ||||
|   this->clear(); | ||||
|   this->show_test_card_ = false; | ||||
|   if (this->get_display_type() == DISPLAY_TYPE_COLOR) { | ||||
|     Color r(255, 0, 0), g(0, 255, 0), b(0, 0, 255); | ||||
|     image_w = std::min(w - 20, 310); | ||||
|     image_h = std::min(h - 20, 255); | ||||
|  | ||||
|     int shift_x = (w - image_w) / 2; | ||||
|     int shift_y = (h - image_h) / 2; | ||||
|     int line_w = (image_w - 6) / 6; | ||||
|     int image_c = image_w / 2; | ||||
|     for (auto i = 0; i <= image_h; i++) { | ||||
|       int c = esp_scale(i, image_h); | ||||
|       this->horizontal_line(shift_x + 0, shift_y + i, line_w, r.fade_to_white(c)); | ||||
|       this->horizontal_line(shift_x + line_w, shift_y + i, line_w, r.fade_to_black(c));  // | ||||
|  | ||||
|       this->horizontal_line(shift_x + image_c - line_w, shift_y + i, line_w, g.fade_to_white(c)); | ||||
|       this->horizontal_line(shift_x + image_c, shift_y + i, line_w, g.fade_to_black(c)); | ||||
|  | ||||
|       this->horizontal_line(shift_x + image_w - (line_w * 2), shift_y + i, line_w, b.fade_to_white(c)); | ||||
|       this->horizontal_line(shift_x + image_w - line_w, shift_y + i, line_w, b.fade_to_black(c)); | ||||
|     } | ||||
|     this->rectangle(shift_x, shift_y, image_w, image_h, Color(127, 127, 0)); | ||||
|  | ||||
|     uint16_t shift_r = shift_x + line_w - (8 * 3); | ||||
|     uint16_t shift_g = shift_x + image_c - (8 * 3); | ||||
|     uint16_t shift_b = shift_x + image_w - line_w - (8 * 3); | ||||
|     shift_y = h / 2 - (8 * 3); | ||||
|     for (auto i = 0; i < 8; i++) { | ||||
|       uint8_t ftr = progmem_read_byte(&TESTCARD_FONT[0][i]); | ||||
|       uint8_t ftg = progmem_read_byte(&TESTCARD_FONT[1][i]); | ||||
|       uint8_t ftb = progmem_read_byte(&TESTCARD_FONT[2][i]); | ||||
|       for (auto k = 0; k < 8; k++) { | ||||
|         if ((ftr & (1 << k)) != 0) { | ||||
|           this->filled_rectangle(shift_r + (i * 6), shift_y + (k * 6), 6, 6, COLOR_OFF); | ||||
|         } | ||||
|         if ((ftg & (1 << k)) != 0) { | ||||
|           this->filled_rectangle(shift_g + (i * 6), shift_y + (k * 6), 6, 6, COLOR_OFF); | ||||
|         } | ||||
|         if ((ftb & (1 << k)) != 0) { | ||||
|           this->filled_rectangle(shift_b + (i * 6), shift_y + (k * 6), 6, 6, COLOR_OFF); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   this->rectangle(0, 0, w, h, Color(127, 0, 127)); | ||||
|   this->filled_rectangle(0, 0, 10, 10, Color(255, 0, 255)); | ||||
|   this->stop_poller(); | ||||
| } | ||||
|  | ||||
| DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {} | ||||
| void DisplayPage::show() { this->parent_->show_page(this); } | ||||
| void DisplayPage::show_next() { this->next_->show(); } | ||||
|   | ||||
| @@ -631,6 +631,9 @@ class Display : public PollingComponent { | ||||
|    */ | ||||
|   bool clip(int x, int y); | ||||
|  | ||||
|   void test_card(); | ||||
|   void show_test_card() { this->show_test_card_ = true; } | ||||
|  | ||||
|  protected: | ||||
|   bool clamp_x_(int x, int w, int &min_x, int &max_x); | ||||
|   bool clamp_y_(int y, int h, int &min_y, int &max_y); | ||||
| @@ -659,6 +662,7 @@ class Display : public PollingComponent { | ||||
|   std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_; | ||||
|   bool auto_clear_enabled_{true}; | ||||
|   std::vector<Rect> clipping_rectangle_; | ||||
|   bool show_test_card_{false}; | ||||
| }; | ||||
|  | ||||
| class DisplayPage { | ||||
|   | ||||
| @@ -15,11 +15,11 @@ class Rect { | ||||
|   int16_t h;  ///< Height of region | ||||
|  | ||||
|   Rect() : x(VALUE_NO_SET), y(VALUE_NO_SET), w(VALUE_NO_SET), h(VALUE_NO_SET) {}  // NOLINT | ||||
|   inline Rect(int16_t x, int16_t y, int16_t w, int16_t h) ALWAYS_INLINE : x(x), y(y), w(w), h(h) {} | ||||
|   inline Rect(int16_t x, int16_t y, int16_t w, int16_t h) ESPHOME_ALWAYS_INLINE : x(x), y(y), w(w), h(h) {} | ||||
|   inline int16_t x2() const { return this->x + this->w; };  ///< X coordinate of corner | ||||
|   inline int16_t y2() const { return this->y + this->h; };  ///< Y coordinate of corner | ||||
|  | ||||
|   inline bool is_set() const ALWAYS_INLINE { return (this->h != VALUE_NO_SET) && (this->w != VALUE_NO_SET); } | ||||
|   inline bool is_set() const ESPHOME_ALWAYS_INLINE { return (this->h != VALUE_NO_SET) && (this->w != VALUE_NO_SET); } | ||||
|  | ||||
|   void expand(int16_t horizontal, int16_t vertical); | ||||
|  | ||||
|   | ||||
| @@ -23,7 +23,6 @@ CODEOWNERS = ["@numo68"] | ||||
|  | ||||
| display_menu_base_ns = cg.esphome_ns.namespace("display_menu_base") | ||||
|  | ||||
| CONF_DISPLAY_ID = "display_id" | ||||
|  | ||||
| CONF_ROTARY = "rotary" | ||||
| CONF_JOYSTICK = "joystick" | ||||
|   | ||||
| @@ -4,6 +4,7 @@ from esphome.components import sensor | ||||
| from esphome.const import ( | ||||
|     CONF_EXTERNAL_TEMPERATURE, | ||||
|     CONF_ID, | ||||
|     CONF_INTERNAL_TEMPERATURE, | ||||
|     CONF_SPEED, | ||||
|     DEVICE_CLASS_TEMPERATURE, | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
| @@ -16,7 +17,6 @@ from .. import EMC2101_COMPONENT_SCHEMA, CONF_EMC2101_ID, emc2101_ns | ||||
|  | ||||
| DEPENDENCIES = ["emc2101"] | ||||
|  | ||||
| CONF_INTERNAL_TEMPERATURE = "internal_temperature" | ||||
| CONF_DUTY_CYCLE = "duty_cycle" | ||||
|  | ||||
| EMC2101Sensor = emc2101_ns.class_("EMC2101Sensor", cg.PollingComponent) | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import i2c, sensor | ||||
| from esphome.const import ( | ||||
|     CONF_COMPENSATION, | ||||
|     CONF_ECO2, | ||||
|     CONF_HUMIDITY, | ||||
|     CONF_ID, | ||||
| @@ -27,7 +28,6 @@ ENS160Component = ens160_ns.class_( | ||||
| ) | ||||
|  | ||||
| CONF_AQI = "aqi" | ||||
| CONF_COMPENSATION = "compensation" | ||||
| UNIT_INDEX = "index" | ||||
|  | ||||
| CONFIG_SCHEMA = ( | ||||
|   | ||||
| @@ -32,6 +32,7 @@ from esphome.const import ( | ||||
|     TYPE_GIT, | ||||
|     TYPE_LOCAL, | ||||
|     __version__, | ||||
|     CONF_PLATFORM_VERSION, | ||||
| ) | ||||
| from esphome.core import CORE, HexInt, TimePeriod | ||||
| import esphome.config_validation as cv | ||||
| @@ -226,7 +227,7 @@ ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0) | ||||
| # The default/recommended esp-idf framework version | ||||
| #  - https://github.com/espressif/esp-idf/releases | ||||
| #  - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf | ||||
| RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 6) | ||||
| RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 7) | ||||
| # The platformio/espressif32 version to use for esp-idf frameworks | ||||
| #  - https://github.com/platformio/platform-espressif32/releases | ||||
| #  - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 | ||||
| @@ -365,8 +366,6 @@ def final_validate(config): | ||||
|     return config | ||||
|  | ||||
|  | ||||
| CONF_PLATFORM_VERSION = "platform_version" | ||||
|  | ||||
| ARDUINO_FRAMEWORK_SCHEMA = cv.All( | ||||
|     cv.Schema( | ||||
|         { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation | ||||
| from esphome.const import CONF_ID | ||||
| from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ID | ||||
| from esphome.core import CORE | ||||
| from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const | ||||
|  | ||||
| @@ -11,7 +11,6 @@ CONFLICTS_WITH = ["esp32_ble_beacon"] | ||||
|  | ||||
| CONF_BLE_ID = "ble_id" | ||||
| CONF_IO_CAPABILITY = "io_capability" | ||||
| CONF_ENABLE_ON_BOOT = "enable_on_boot" | ||||
|  | ||||
| NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] | ||||
|  | ||||
| @@ -65,6 +64,8 @@ async def to_code(config): | ||||
|         add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) | ||||
|         add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True) | ||||
|  | ||||
|     cg.add_define("USE_ESP32_BLE") | ||||
|  | ||||
|  | ||||
| @automation.register_condition("ble.enabled", BLEEnabledCondition, cv.Schema({})) | ||||
| async def ble_enabled_to_code(config, condition_id, template_arg, args): | ||||
|   | ||||
| @@ -142,7 +142,7 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ | ||||
|         ESP_LOGW(TAG, "[%d] [%s] Connection failed, status=%d", this->connection_index_, this->address_str_.c_str(), | ||||
|                  param->open.status); | ||||
|         this->set_state(espbt::ClientState::IDLE); | ||||
|         return false; | ||||
|         break; | ||||
|       } | ||||
|       auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if_, param->open.conn_id); | ||||
|       if (ret) { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/defines.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| #include <array> | ||||
| @@ -248,11 +249,11 @@ class ESP32BLETracker : public Component, | ||||
|   SemaphoreHandle_t scan_result_lock_; | ||||
|   SemaphoreHandle_t scan_end_lock_; | ||||
|   size_t scan_result_index_{0}; | ||||
| #if CONFIG_SPIRAM | ||||
| #ifdef USE_PSRAM | ||||
|   const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32; | ||||
| #else | ||||
|   const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 16; | ||||
| #endif  // CONFIG_SPIRAM | ||||
| #endif  // USE_PSRAM | ||||
|   esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_; | ||||
|   esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS}; | ||||
|   esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS}; | ||||
|   | ||||
| @@ -14,6 +14,7 @@ from esphome.const import ( | ||||
|     CONF_BRIGHTNESS, | ||||
|     CONF_CONTRAST, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_VSYNC_PIN, | ||||
| ) | ||||
| from esphome.core import CORE | ||||
| from esphome.components.esp32 import add_idf_sdkconfig_option | ||||
| @@ -112,7 +113,6 @@ ENUM_SPECIAL_EFFECT = { | ||||
| } | ||||
|  | ||||
| # pin assignment | ||||
| CONF_VSYNC_PIN = "vsync_pin" | ||||
| CONF_HREF_PIN = "href_pin" | ||||
| CONF_PIXEL_CLOCK_PIN = "pixel_clock_pin" | ||||
| CONF_EXTERNAL_CLOCK = "external_clock" | ||||
|   | ||||
| @@ -12,6 +12,7 @@ from esphome.const import ( | ||||
|     KEY_TARGET_FRAMEWORK, | ||||
|     KEY_TARGET_PLATFORM, | ||||
|     PLATFORM_ESP8266, | ||||
|     CONF_PLATFORM_VERSION, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| import esphome.config_validation as cv | ||||
| @@ -146,7 +147,6 @@ def _parse_platform_version(value): | ||||
|         return value | ||||
|  | ||||
|  | ||||
| CONF_PLATFORM_VERSION = "platform_version" | ||||
| ARDUINO_FRAMEWORK_SCHEMA = cv.All( | ||||
|     cv.Schema( | ||||
|         { | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| #include "ethernet_component.h" | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/util.h" | ||||
| #include "esphome/core/application.h" | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
|  | ||||
| #include <cinttypes> | ||||
| #include <lwip/dns.h> | ||||
| #include <cinttypes> | ||||
| #include "esp_event.h" | ||||
|  | ||||
| #ifdef USE_ETHERNET_SPI | ||||
| @@ -184,6 +184,10 @@ void EthernetComponent::setup() { | ||||
|     // KSZ8081RNA default is incorrect. It expects a 25MHz clock instead of the 50MHz we provide. | ||||
|     this->ksz8081_set_clock_reference_(mac); | ||||
|   } | ||||
|   if (this->type_ == ETHERNET_TYPE_RTL8201 && this->clk_mode_ == EMAC_CLK_EXT_IN) { | ||||
|     // Change in default behavior of RTL8201FI may require register setting to enable external clock | ||||
|     this->rtl8201_set_rmii_mode_(mac); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   // use ESP internal eth mac | ||||
| @@ -340,6 +344,11 @@ network::IPAddresses EthernetComponent::get_ip_addresses() { | ||||
|   return addresses; | ||||
| } | ||||
|  | ||||
| network::IPAddress EthernetComponent::get_dns_address(uint8_t num) { | ||||
|   const ip_addr_t *dns_ip = dns_getserver(num); | ||||
|   return dns_ip; | ||||
| } | ||||
|  | ||||
| void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) { | ||||
|   const char *event_name; | ||||
|  | ||||
| @@ -549,9 +558,10 @@ bool EthernetComponent::powerdown() { | ||||
| } | ||||
|  | ||||
| #ifndef USE_ETHERNET_SPI | ||||
| void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) { | ||||
| #define KSZ80XX_PC2R_REG_ADDR (0x1F) | ||||
|  | ||||
| constexpr uint8_t KSZ80XX_PC2R_REG_ADDR = 0x1F; | ||||
|  | ||||
| void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) { | ||||
|   esp_err_t err; | ||||
|  | ||||
|   uint32_t phy_control_2; | ||||
| @@ -576,9 +586,47 @@ void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) { | ||||
|     ESPHL_ERROR_CHECK(err, "Read PHY Control 2 failed"); | ||||
|     ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty((u_int8_t *) &phy_control_2, 2).c_str()); | ||||
|   } | ||||
|  | ||||
| #undef KSZ80XX_PC2R_REG_ADDR | ||||
| } | ||||
| constexpr uint8_t RTL8201_RMSR_REG_ADDR = 0x10; | ||||
| void EthernetComponent::rtl8201_set_rmii_mode_(esp_eth_mac_t *mac) { | ||||
|   esp_err_t err; | ||||
|   uint32_t phy_rmii_mode; | ||||
|   err = mac->write_phy_reg(mac, this->phy_addr_, 0x1f, 0x07); | ||||
|   ESPHL_ERROR_CHECK(err, "Setting Page 7 failed"); | ||||
|  | ||||
|   /* | ||||
|    * RTL8201 RMII Mode Setting Register (RMSR) | ||||
|    * Page 7 Register 16 | ||||
|    * | ||||
|    * bit 0      Reserved            0 | ||||
|    * bit 1      Rg_rmii_rxdsel      1 (default) | ||||
|    * bit 2      Rg_rmii_rxdv_sel:   0 (default) | ||||
|    * bit 3      RMII Mode:          1 (RMII Mode) | ||||
|    * bit 4~7    Rg_rmii_rx_offset:  1111 (default) | ||||
|    * bit 8~11   Rg_rmii_tx_offset:  1111 (default) | ||||
|    * bit 12     Rg_rmii_clkdir:     1 (Input) | ||||
|    * bit 13~15  Reserved            000 | ||||
|    * | ||||
|    * Binary: 0001 1111 1111 1010 | ||||
|    * Hex: 0x1FFA | ||||
|    * | ||||
|    */ | ||||
|  | ||||
|   err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode)); | ||||
|   ESPHL_ERROR_CHECK(err, "Read PHY RMSR Register failed"); | ||||
|   ESP_LOGV(TAG, "Hardware default RTL8201 RMII Mode Register is: 0x%04X", phy_rmii_mode); | ||||
|  | ||||
|   err = mac->write_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, 0x1FFA); | ||||
|   ESPHL_ERROR_CHECK(err, "Setting Register 16 RMII Mode Setting failed"); | ||||
|  | ||||
|   err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode)); | ||||
|   ESPHL_ERROR_CHECK(err, "Read PHY RMSR Register failed"); | ||||
|   ESP_LOGV(TAG, "Setting RTL8201 RMII Mode Register to: 0x%04X", phy_rmii_mode); | ||||
|  | ||||
|   err = mac->write_phy_reg(mac, this->phy_addr_, 0x1f, 0x0); | ||||
|   ESPHL_ERROR_CHECK(err, "Setting Page 0 failed"); | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| }  // namespace ethernet | ||||
|   | ||||
| @@ -70,6 +70,7 @@ class EthernetComponent : public Component { | ||||
|   void set_manual_ip(const ManualIP &manual_ip); | ||||
|  | ||||
|   network::IPAddresses get_ip_addresses(); | ||||
|   network::IPAddress get_dns_address(uint8_t num); | ||||
|   std::string get_use_address() const; | ||||
|   void set_use_address(const std::string &use_address); | ||||
|   bool powerdown(); | ||||
| @@ -85,6 +86,8 @@ class EthernetComponent : public Component { | ||||
|   void dump_connect_params_(); | ||||
|   /// @brief Set `RMII Reference Clock Select` bit for KSZ8081. | ||||
|   void ksz8081_set_clock_reference_(esp_eth_mac_t *mac); | ||||
|   /// @brief Set `RMII Mode Setting Register` for RTL8201. | ||||
|   void rtl8201_set_rmii_mode_(esp_eth_mac_t *mac); | ||||
|  | ||||
|   std::string use_address_; | ||||
| #ifdef USE_ETHERNET_SPI | ||||
|   | ||||
| @@ -9,6 +9,7 @@ namespace ethernet_info { | ||||
| static const char *const TAG = "ethernet_info"; | ||||
|  | ||||
| void IPAddressEthernetInfo::dump_config() { LOG_TEXT_SENSOR("", "EthernetInfo IPAddress", this); } | ||||
| void DNSAddressEthernetInfo::dump_config() { LOG_TEXT_SENSOR("", "EthernetInfo DNS Address", this); } | ||||
|  | ||||
| }  // namespace ethernet_info | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -38,6 +38,27 @@ class IPAddressEthernetInfo : public PollingComponent, public text_sensor::TextS | ||||
|   std::array<text_sensor::TextSensor *, 5> ip_sensors_; | ||||
| }; | ||||
|  | ||||
| class DNSAddressEthernetInfo : public PollingComponent, public text_sensor::TextSensor { | ||||
|  public: | ||||
|   void update() override { | ||||
|     auto dns_one = ethernet::global_eth_component->get_dns_address(0); | ||||
|     auto dns_two = ethernet::global_eth_component->get_dns_address(1); | ||||
|  | ||||
|     std::string dns_results = dns_one.str() + " " + dns_two.str(); | ||||
|  | ||||
|     if (dns_results != this->last_results_) { | ||||
|       this->last_results_ = dns_results; | ||||
|       this->publish_state(dns_results); | ||||
|     } | ||||
|   } | ||||
|   float get_setup_priority() const override { return setup_priority::ETHERNET; } | ||||
|   std::string unique_id() override { return get_mac_address() + "-ethernetinfo-dns"; } | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   std::string last_results_; | ||||
| }; | ||||
|  | ||||
| }  // namespace ethernet_info | ||||
| }  // namespace esphome | ||||
|  | ||||
|   | ||||
| @@ -3,6 +3,7 @@ import esphome.config_validation as cv | ||||
| from esphome.components import text_sensor | ||||
| from esphome.const import ( | ||||
|     CONF_IP_ADDRESS, | ||||
|     CONF_DNS_ADDRESS, | ||||
|     ENTITY_CATEGORY_DIAGNOSTIC, | ||||
| ) | ||||
|  | ||||
| @@ -10,14 +11,18 @@ DEPENDENCIES = ["ethernet"] | ||||
|  | ||||
| ethernet_info_ns = cg.esphome_ns.namespace("ethernet_info") | ||||
|  | ||||
| IPAddressEsthernetInfo = ethernet_info_ns.class_( | ||||
| IPAddressEthernetInfo = ethernet_info_ns.class_( | ||||
|     "IPAddressEthernetInfo", text_sensor.TextSensor, cg.PollingComponent | ||||
| ) | ||||
|  | ||||
| DNSAddressEthernetInfo = ethernet_info_ns.class_( | ||||
|     "DNSAddressEthernetInfo", text_sensor.TextSensor, cg.PollingComponent | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Optional(CONF_IP_ADDRESS): text_sensor.text_sensor_schema( | ||||
|             IPAddressEsthernetInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC | ||||
|             IPAddressEthernetInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC | ||||
|         ) | ||||
|         .extend(cv.polling_component_schema("1s")) | ||||
|         .extend( | ||||
| @@ -27,7 +32,10 @@ CONFIG_SCHEMA = cv.Schema( | ||||
|                 ) | ||||
|                 for x in range(5) | ||||
|             } | ||||
|         ) | ||||
|         ), | ||||
|         cv.Optional(CONF_DNS_ADDRESS): text_sensor.text_sensor_schema( | ||||
|             DNSAddressEthernetInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC | ||||
|         ).extend(cv.polling_component_schema("1s")), | ||||
|     } | ||||
| ) | ||||
|  | ||||
| @@ -40,3 +48,6 @@ async def to_code(config): | ||||
|             if sensor_conf := conf.get(f"address_{x}"): | ||||
|                 sens = await text_sensor.new_text_sensor(sensor_conf) | ||||
|                 cg.add(ip_info.add_ip_sensors(x, sens)) | ||||
|     if conf := config.get(CONF_DNS_ADDRESS): | ||||
|         dns_info = await text_sensor.new_text_sensor(config[CONF_DNS_ADDRESS]) | ||||
|         await cg.register_component(dns_info, config[CONF_DNS_ADDRESS]) | ||||
|   | ||||
							
								
								
									
										134
									
								
								esphome/components/event/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								esphome/components/event/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation | ||||
| from esphome.components import mqtt | ||||
| from esphome.const import ( | ||||
|     CONF_DEVICE_CLASS, | ||||
|     CONF_ENTITY_CATEGORY, | ||||
|     CONF_ICON, | ||||
|     CONF_ID, | ||||
|     CONF_ON_EVENT, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_MQTT_ID, | ||||
|     CONF_EVENT_TYPE, | ||||
|     DEVICE_CLASS_BUTTON, | ||||
|     DEVICE_CLASS_DOORBELL, | ||||
|     DEVICE_CLASS_EMPTY, | ||||
|     DEVICE_CLASS_MOTION, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| from esphome.cpp_helpers import setup_entity | ||||
| from esphome.cpp_generator import MockObjClass | ||||
|  | ||||
| CODEOWNERS = ["@nohat"] | ||||
| IS_PLATFORM_COMPONENT = True | ||||
|  | ||||
| DEVICE_CLASSES = [ | ||||
|     DEVICE_CLASS_BUTTON, | ||||
|     DEVICE_CLASS_DOORBELL, | ||||
|     DEVICE_CLASS_EMPTY, | ||||
|     DEVICE_CLASS_MOTION, | ||||
| ] | ||||
|  | ||||
| event_ns = cg.esphome_ns.namespace("event") | ||||
| Event = event_ns.class_("Event", cg.EntityBase) | ||||
| EventPtr = Event.operator("ptr") | ||||
|  | ||||
| TriggerEventAction = event_ns.class_("TriggerEventAction", automation.Action) | ||||
|  | ||||
| EventTrigger = event_ns.class_("EventTrigger", automation.Trigger.template()) | ||||
|  | ||||
| validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") | ||||
|  | ||||
| EVENT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( | ||||
|     { | ||||
|         cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent), | ||||
|         cv.GenerateID(): cv.declare_id(Event), | ||||
|         cv.Optional(CONF_DEVICE_CLASS): validate_device_class, | ||||
|         cv.Optional(CONF_ON_EVENT): automation.validate_automation( | ||||
|             { | ||||
|                 cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger), | ||||
|             } | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
|  | ||||
| _UNDEF = object() | ||||
|  | ||||
|  | ||||
| def event_schema( | ||||
|     class_: MockObjClass = _UNDEF, | ||||
|     *, | ||||
|     icon: str = _UNDEF, | ||||
|     entity_category: str = _UNDEF, | ||||
|     device_class: str = _UNDEF, | ||||
| ) -> cv.Schema: | ||||
|     schema = {} | ||||
|  | ||||
|     if class_ is not _UNDEF: | ||||
|         schema[cv.GenerateID()] = cv.declare_id(class_) | ||||
|  | ||||
|     for key, default, validator in [ | ||||
|         (CONF_ICON, icon, cv.icon), | ||||
|         (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category), | ||||
|         (CONF_DEVICE_CLASS, device_class, validate_device_class), | ||||
|     ]: | ||||
|         if default is not _UNDEF: | ||||
|             schema[cv.Optional(key, default=default)] = validator | ||||
|  | ||||
|     return EVENT_SCHEMA.extend(schema) | ||||
|  | ||||
|  | ||||
| async def setup_event_core_(var, config, *, event_types: list[str]): | ||||
|     await setup_entity(var, config) | ||||
|  | ||||
|     for conf in config.get(CONF_ON_EVENT, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation( | ||||
|             trigger, [(cg.std_string, "event_type")], conf | ||||
|         ) | ||||
|  | ||||
|     cg.add(var.set_event_types(event_types)) | ||||
|  | ||||
|     if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: | ||||
|         cg.add(var.set_device_class(device_class)) | ||||
|  | ||||
|     if mqtt_id := config.get(CONF_MQTT_ID): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|  | ||||
| async def register_event(var, config, *, event_types: list[str]): | ||||
|     if not CORE.has_id(config[CONF_ID]): | ||||
|         var = cg.Pvariable(config[CONF_ID], var) | ||||
|     cg.add(cg.App.register_event(var)) | ||||
|     await setup_event_core_(var, config, event_types=event_types) | ||||
|  | ||||
|  | ||||
| async def new_event(config, *, event_types: list[str]): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await register_event(var, config, event_types=event_types) | ||||
|     return var | ||||
|  | ||||
|  | ||||
| TRIGGER_EVENT_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_ID): cv.use_id(Event), | ||||
|         cv.Required(CONF_EVENT_TYPE): cv.templatable(cv.string_strict), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| @automation.register_action("event.trigger", TriggerEventAction, TRIGGER_EVENT_SCHEMA) | ||||
| async def event_fire_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     await cg.register_parented(var, config[CONF_ID]) | ||||
|     templ = await cg.templatable(config[CONF_EVENT_TYPE], args, cg.std_string) | ||||
|     cg.add(var.set_event_type(templ)) | ||||
|     return var | ||||
|  | ||||
|  | ||||
| @coroutine_with_priority(100.0) | ||||
| async def to_code(config): | ||||
|     cg.add_define("USE_EVENT") | ||||
|     cg.add_global(event_ns.using) | ||||
							
								
								
									
										25
									
								
								esphome/components/event/automation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								esphome/components/event/automation.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/event/event.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace event { | ||||
|  | ||||
| template<typename... Ts> class TriggerEventAction : public Action<Ts...>, public Parented<Event> { | ||||
|  public: | ||||
|   TEMPLATABLE_VALUE(std::string, event_type) | ||||
|  | ||||
|   void play(Ts... x) override { this->parent_->trigger(this->event_type_.value(x...)); } | ||||
| }; | ||||
|  | ||||
| class EventTrigger : public Trigger<std::string> { | ||||
|  public: | ||||
|   EventTrigger(Event *event) { | ||||
|     event->add_on_event_callback([this](const std::string &event_type) { this->trigger(event_type); }); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| }  // namespace event | ||||
| }  // namespace esphome | ||||
							
								
								
									
										24
									
								
								esphome/components/event/event.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphome/components/event/event.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| #include "event.h" | ||||
|  | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace event { | ||||
|  | ||||
| static const char *const TAG = "event"; | ||||
|  | ||||
| void Event::trigger(const std::string &event_type) { | ||||
|   if (types_.find(event_type) == types_.end()) { | ||||
|     ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str()); | ||||
|     return; | ||||
|   } | ||||
|   ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), event_type.c_str()); | ||||
|   this->event_callback_.call(event_type); | ||||
| } | ||||
|  | ||||
| void Event::add_on_event_callback(std::function<void(const std::string &event_type)> &&callback) { | ||||
|   this->event_callback_.add(std::move(callback)); | ||||
| } | ||||
|  | ||||
| }  // namespace event | ||||
| }  // namespace esphome | ||||
							
								
								
									
										37
									
								
								esphome/components/event/event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								esphome/components/event/event.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <set> | ||||
| #include <string> | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/entity_base.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace event { | ||||
|  | ||||
| #define LOG_EVENT(prefix, type, obj) \ | ||||
|   if ((obj) != nullptr) { \ | ||||
|     ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ | ||||
|     if (!(obj)->get_icon().empty()) { \ | ||||
|       ESP_LOGCONFIG(TAG, "%s  Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ | ||||
|     } \ | ||||
|     if (!(obj)->get_device_class().empty()) { \ | ||||
|       ESP_LOGCONFIG(TAG, "%s  Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ | ||||
|     } \ | ||||
|   } | ||||
|  | ||||
| class Event : public EntityBase, public EntityBase_DeviceClass { | ||||
|  public: | ||||
|   void trigger(const std::string &event_type); | ||||
|   void set_event_types(const std::set<std::string> &event_types) { this->types_ = event_types; } | ||||
|   std::set<std::string> get_event_types() const { return this->types_; } | ||||
|   void add_on_event_callback(std::function<void(const std::string &event_type)> &&callback); | ||||
|  | ||||
|  protected: | ||||
|   CallbackManager<void(const std::string &event_type)> event_callback_; | ||||
|   std::set<std::string> types_; | ||||
| }; | ||||
|  | ||||
| }  // namespace event | ||||
| }  // namespace esphome | ||||
| @@ -49,7 +49,16 @@ def _process_git_config(config: dict, refresh) -> str: | ||||
|         password=config.get(CONF_PASSWORD), | ||||
|     ) | ||||
|  | ||||
|     if (repo_dir / "esphome" / "components").is_dir(): | ||||
|     if path := config.get(CONF_PATH): | ||||
|         if (repo_dir / path).is_dir(): | ||||
|             components_dir = repo_dir / path | ||||
|         else: | ||||
|             raise cv.Invalid( | ||||
|                 "Could not find components folder for source. Please check the source contains a '" | ||||
|                 + path | ||||
|                 + "' folder" | ||||
|             ) | ||||
|     elif (repo_dir / "esphome" / "components").is_dir(): | ||||
|         components_dir = repo_dir / "esphome" / "components" | ||||
|     elif (repo_dir / "components").is_dir(): | ||||
|         components_dir = repo_dir / "components" | ||||
|   | ||||
| @@ -180,40 +180,34 @@ async def setup_fan_core_(var, config): | ||||
|  | ||||
|     cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) | ||||
|  | ||||
|     if CONF_MQTT_ID in config: | ||||
|         mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) | ||||
|     if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|         if CONF_OSCILLATION_STATE_TOPIC in config: | ||||
|         if ( | ||||
|             oscillation_state_topic := config.get(CONF_OSCILLATION_STATE_TOPIC) | ||||
|         ) is not None: | ||||
|             cg.add(mqtt_.set_custom_oscillation_state_topic(oscillation_state_topic)) | ||||
|         if ( | ||||
|             oscillation_command_topic := config.get(CONF_OSCILLATION_COMMAND_TOPIC) | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_oscillation_state_topic( | ||||
|                     config[CONF_OSCILLATION_STATE_TOPIC] | ||||
|                 ) | ||||
|                 mqtt_.set_custom_oscillation_command_topic(oscillation_command_topic) | ||||
|             ) | ||||
|         if CONF_OSCILLATION_COMMAND_TOPIC in config: | ||||
|         if ( | ||||
|             speed_level_state_topic := config.get(CONF_SPEED_LEVEL_STATE_TOPIC) | ||||
|         ) is not None: | ||||
|             cg.add(mqtt_.set_custom_speed_level_state_topic(speed_level_state_topic)) | ||||
|         if ( | ||||
|             speed_level_command_topic := config.get(CONF_SPEED_LEVEL_COMMAND_TOPIC) | ||||
|         ) is not None: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_oscillation_command_topic( | ||||
|                     config[CONF_OSCILLATION_COMMAND_TOPIC] | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_SPEED_LEVEL_STATE_TOPIC in config: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_speed_level_state_topic( | ||||
|                     config[CONF_SPEED_LEVEL_STATE_TOPIC] | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_SPEED_LEVEL_COMMAND_TOPIC in config: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_speed_level_command_topic( | ||||
|                     config[CONF_SPEED_LEVEL_COMMAND_TOPIC] | ||||
|                 ) | ||||
|             ) | ||||
|         if CONF_SPEED_STATE_TOPIC in config: | ||||
|             cg.add(mqtt_.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC])) | ||||
|         if CONF_SPEED_COMMAND_TOPIC in config: | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]) | ||||
|                 mqtt_.set_custom_speed_level_command_topic(speed_level_command_topic) | ||||
|             ) | ||||
|         if (speed_state_topic := config.get(CONF_SPEED_STATE_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_speed_state_topic(speed_state_topic)) | ||||
|         if (speed_command_topic := config.get(CONF_SPEED_COMMAND_TOPIC)) is not None: | ||||
|             cg.add(mqtt_.set_custom_speed_command_topic(speed_command_topic)) | ||||
|  | ||||
|     for conf in config.get(CONF_ON_STATE, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
| @@ -288,14 +282,14 @@ async def fan_turn_off_to_code(config, action_id, template_arg, args): | ||||
| async def fan_turn_on_to_code(config, action_id, template_arg, args): | ||||
|     paren = await cg.get_variable(config[CONF_ID]) | ||||
|     var = cg.new_Pvariable(action_id, template_arg, paren) | ||||
|     if CONF_OSCILLATING in config: | ||||
|         template_ = await cg.templatable(config[CONF_OSCILLATING], args, bool) | ||||
|     if (oscillating := config.get(CONF_OSCILLATING)) is not None: | ||||
|         template_ = await cg.templatable(oscillating, args, bool) | ||||
|         cg.add(var.set_oscillating(template_)) | ||||
|     if CONF_SPEED in config: | ||||
|         template_ = await cg.templatable(config[CONF_SPEED], args, int) | ||||
|     if (speed := config.get(CONF_SPEED)) is not None: | ||||
|         template_ = await cg.templatable(speed, args, int) | ||||
|         cg.add(var.set_speed(template_)) | ||||
|     if CONF_DIRECTION in config: | ||||
|         template_ = await cg.templatable(config[CONF_DIRECTION], args, FanDirection) | ||||
|     if (direction := config.get(CONF_DIRECTION)) is not None: | ||||
|         template_ = await cg.templatable(direction, args, FanDirection) | ||||
|         cg.add(var.set_direction(template_)) | ||||
|     return var | ||||
|  | ||||
|   | ||||
| @@ -400,8 +400,7 @@ class BitmapFontWrapper: | ||||
|         for glyph in glyphs: | ||||
|             mask = self.getmask(glyph, mode="1") | ||||
|             _, height = mask.size | ||||
|             if height > max_height: | ||||
|                 max_height = height | ||||
|             max_height = max(max_height, height) | ||||
|         return (max_height, 0) | ||||
|  | ||||
|  | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user