mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-03 08:31:47 +00:00 
			
		
		
		
	Compare commits
	
		
			252 Commits
		
	
	
		
			jesserockz
			...
			2023.2.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					1447536906 | ||
| 
						 | 
					27ec517084 | ||
| 
						 | 
					ce1f034bac | ||
| 
						 | 
					f1f96f16e9 | ||
| 
						 | 
					7665e9b076 | ||
| 
						 | 
					227d94f38d | ||
| 
						 | 
					b724ae9e0e | ||
| 
						 | 
					df6cc14201 | ||
| 
						 | 
					d981d7859d | ||
| 
						 | 
					0f1ec515c1 | ||
| 
						 | 
					78e18256f7 | ||
| 
						 | 
					4899dfe642 | ||
| 
						 | 
					d6b6e94059 | ||
| 
						 | 
					310355a00b | ||
| 
						 | 
					8cf26d6f3c | ||
| 
						 | 
					b15a10f905 | ||
| 
						 | 
					5dcf1debd7 | ||
| 
						 | 
					9b57e1ac1d | ||
| 
						 | 
					68683e3a50 | ||
| 
						 | 
					d83324c4dc | ||
| 
						 | 
					ecde4c1d2d | ||
| 
						 | 
					bd8e470726 | ||
| 
						 | 
					d2913fe627 | ||
| 
						 | 
					43acc7dc2c | ||
| 
						 | 
					e2a16d758b | ||
| 
						 | 
					17ea0efb08 | ||
| 
						 | 
					2fbd33267e | ||
| 
						 | 
					cf3977f088 | ||
| 
						 | 
					d20d4947ac | ||
| 
						 | 
					7810ad40d7 | ||
| 
						 | 
					7e1e799b3a | ||
| 
						 | 
					dfafc41ce6 | ||
| 
						 | 
					e460792c43 | ||
| 
						 | 
					a9dc491a54 | ||
| 
						 | 
					ac6693f177 | ||
| 
						 | 
					c6742117d3 | ||
| 
						 | 
					b5c47b9669 | ||
| 
						 | 
					40df3aa55e | ||
| 
						 | 
					393ca64d70 | ||
| 
						 | 
					d3627f0972 | ||
| 
						 | 
					124ab31f22 | ||
| 
						 | 
					1b66fa5004 | ||
| 
						 | 
					9494c27ad8 | ||
| 
						 | 
					3facfa5c21 | ||
| 
						 | 
					93ddce2e79 | ||
| 
						 | 
					0bf6e21e1a | ||
| 
						 | 
					6b7b076875 | ||
| 
						 | 
					8d6ffb9169 | ||
| 
						 | 
					e95d6041d8 | ||
| 
						 | 
					0554b06b7e | ||
| 
						 | 
					e3d9c44bdc | ||
| 
						 | 
					e847766514 | ||
| 
						 | 
					d4a8df04b8 | ||
| 
						 | 
					4af4649e23 | ||
| 
						 | 
					8bcddef39d | ||
| 
						 | 
					4ac96ccea2 | ||
| 
						 | 
					3c5de77ae9 | ||
| 
						 | 
					a2925b1d37 | ||
| 
						 | 
					73748e9e20 | ||
| 
						 | 
					034b47c23a | ||
| 
						 | 
					3e017efa30 | ||
| 
						 | 
					aca56fcdcc | ||
| 
						 | 
					75c9823899 | ||
| 
						 | 
					c8c0bd3351 | ||
| 
						 | 
					e1cdeb7c8f | ||
| 
						 | 
					7f97f42552 | ||
| 
						 | 
					aa7f3569ec | ||
| 
						 | 
					2d0a08442e | ||
| 
						 | 
					d2380756b2 | ||
| 
						 | 
					e778a445d9 | ||
| 
						 | 
					ded86493c2 | ||
| 
						 | 
					4d72eb42a5 | ||
| 
						 | 
					267f0587c6 | ||
| 
						 | 
					4a374a466a | ||
| 
						 | 
					b27a328d1e | ||
| 
						 | 
					d94e9d92ca | ||
| 
						 | 
					36c2e770bf | ||
| 
						 | 
					79040c116d | ||
| 
						 | 
					4aac76c549 | ||
| 
						 | 
					0ea97df1af | ||
| 
						 | 
					92e66a2764 | ||
| 
						 | 
					925e3cb6c9 | ||
| 
						 | 
					6757acba56 | ||
| 
						 | 
					5cc91cdd95 | ||
| 
						 | 
					2b41886819 | ||
| 
						 | 
					72c6efd6a0 | ||
| 
						 | 
					a1f1804112 | ||
| 
						 | 
					a8b1ceb4e9 | ||
| 
						 | 
					4fb0f7f8c6 | ||
| 
						 | 
					958cadeca8 | ||
| 
						 | 
					00f2655f1a | ||
| 
						 | 
					074f5029eb | ||
| 
						 | 
					615d591367 | ||
| 
						 | 
					e236c53f05 | ||
| 
						 | 
					10c7055b41 | ||
| 
						 | 
					a127e60e1b | ||
| 
						 | 
					66a3361e9d | ||
| 
						 | 
					13cfe11a19 | ||
| 
						 | 
					6d65671f92 | ||
| 
						 | 
					f2eafa8fbe | ||
| 
						 | 
					e4ca3b18cc | ||
| 
						 | 
					84698ae888 | ||
| 
						 | 
					fd6d6cfb6c | ||
| 
						 | 
					8cad9dfc83 | ||
| 
						 | 
					5e2f33fde5 | ||
| 
						 | 
					029ac75a04 | ||
| 
						 | 
					3aa5953cd9 | ||
| 
						 | 
					582d90ad72 | ||
| 
						 | 
					bbb0105c2f | ||
| 
						 | 
					37d17feecf | ||
| 
						 | 
					4bf5faf808 | ||
| 
						 | 
					ddedc1cd76 | ||
| 
						 | 
					1bb90f304c | ||
| 
						 | 
					efc6a8df35 | ||
| 
						 | 
					e35f90d6e4 | ||
| 
						 | 
					11518364a1 | ||
| 
						 | 
					05420291ce | ||
| 
						 | 
					442faf92c6 | ||
| 
						 | 
					62c68f4d60 | ||
| 
						 | 
					c301ae3645 | ||
| 
						 | 
					3d2d681a7b | ||
| 
						 | 
					a45646af1b | ||
| 
						 | 
					27185265f6 | ||
| 
						 | 
					a9b7d98194 | ||
| 
						 | 
					ed4a7210d3 | ||
| 
						 | 
					351ea04517 | ||
| 
						 | 
					86a8e1f4a6 | ||
| 
						 | 
					1cf3424ebe | ||
| 
						 | 
					a19f0c0db0 | ||
| 
						 | 
					530df91044 | ||
| 
						 | 
					c16c0b11cb | ||
| 
						 | 
					74556b28a8 | ||
| 
						 | 
					48340d41d6 | ||
| 
						 | 
					6306348379 | ||
| 
						 | 
					b1f1329cee | ||
| 
						 | 
					75dff1e102 | ||
| 
						 | 
					fe55f3a43d | ||
| 
						 | 
					657fd9d0d5 | ||
| 
						 | 
					1511a6ebcd | ||
| 
						 | 
					ecac26aeba | ||
| 
						 | 
					19bf9b1e36 | ||
| 
						 | 
					119a6920f2 | ||
| 
						 | 
					8237e13c44 | ||
| 
						 | 
					53b60ac817 | ||
| 
						 | 
					a18ab748fd | ||
| 
						 | 
					917488bbc3 | ||
| 
						 | 
					7e376ae952 | ||
| 
						 | 
					57a1c207c2 | ||
| 
						 | 
					50e8e92f0b | ||
| 
						 | 
					ff4fd497c4 | ||
| 
						 | 
					33b1a853b9 | ||
| 
						 | 
					f2df542cb1 | ||
| 
						 | 
					ecbbf2d3f4 | ||
| 
						 | 
					b76c7a0131 | ||
| 
						 | 
					0b0984f9a0 | ||
| 
						 | 
					9767856784 | ||
| 
						 | 
					c1f09684e6 | ||
| 
						 | 
					22b384363b | ||
| 
						 | 
					5b23331751 | ||
| 
						 | 
					7a2bb32843 | ||
| 
						 | 
					c0a4e07e5a | ||
| 
						 | 
					322158cccb | ||
| 
						 | 
					8db3b59e0f | ||
| 
						 | 
					1691976587 | ||
| 
						 | 
					60e6b4d21e | ||
| 
						 | 
					5750591df2 | ||
| 
						 | 
					0d50caa179 | ||
| 
						 | 
					8b06135b41 | ||
| 
						 | 
					a75da54455 | ||
| 
						 | 
					de7f6c3f5f | ||
| 
						 | 
					4245480656 | ||
| 
						 | 
					1824c8131e | ||
| 
						 | 
					573ea55187 | ||
| 
						 | 
					b48b5d6cc7 | ||
| 
						 | 
					4e9606d2e0 | ||
| 
						 | 
					78500fa933 | ||
| 
						 | 
					9c69b98a49 | ||
| 
						 | 
					e6d8ef98d3 | ||
| 
						 | 
					3f1af1690b | ||
| 
						 | 
					39af967433 | ||
| 
						 | 
					83b5e01a28 | ||
| 
						 | 
					1eacbd50fa | ||
| 
						 | 
					84374b6b1e | ||
| 
						 | 
					391316c9b5 | ||
| 
						 | 
					705c62ebd7 | ||
| 
						 | 
					cb520c00a5 | ||
| 
						 | 
					2f24138345 | ||
| 
						 | 
					96512b80cc | ||
| 
						 | 
					fcb9b51978 | ||
| 
						 | 
					9bf7c97775 | ||
| 
						 | 
					24bf3674f3 | ||
| 
						 | 
					f408f1a368 | ||
| 
						 | 
					7d8d563c62 | ||
| 
						 | 
					0a1f705fda | ||
| 
						 | 
					1952c1880b | ||
| 
						 | 
					c47dc09d34 | ||
| 
						 | 
					db3096c6e1 | ||
| 
						 | 
					b03967dac1 | ||
| 
						 | 
					bcae2596a6 | ||
| 
						 | 
					fc0347c86c | ||
| 
						 | 
					eef578f4b8 | ||
| 
						 | 
					d9563d4de1 | ||
| 
						 | 
					cc7e2bf8db | ||
| 
						 | 
					5d98e2923b | ||
| 
						 | 
					d4e232f267 | ||
| 
						 | 
					cc45945fcf | ||
| 
						 | 
					07197d12f6 | ||
| 
						 | 
					7b0a298497 | ||
| 
						 | 
					21679cf2ba | ||
| 
						 | 
					0c24d951ff | ||
| 
						 | 
					92e44b8238 | ||
| 
						 | 
					4be7cd12a1 | ||
| 
						 | 
					34387adbcd | ||
| 
						 | 
					dee4d0ccb7 | ||
| 
						 | 
					c6885c1bf4 | ||
| 
						 | 
					8b8efb57af | ||
| 
						 | 
					499e120aa4 | ||
| 
						 | 
					6f198a4736 | ||
| 
						 | 
					f500f448b1 | ||
| 
						 | 
					6ad9baa870 | ||
| 
						 | 
					f843925301 | ||
| 
						 | 
					4a3b628946 | ||
| 
						 | 
					4ffdc38cf5 | ||
| 
						 | 
					f83f1bff19 | ||
| 
						 | 
					9370ff3dfa | ||
| 
						 | 
					2053b02c61 | ||
| 
						 | 
					f34e797a0d | ||
| 
						 | 
					30a2fc1273 | ||
| 
						 | 
					48da5ef1c4 | ||
| 
						 | 
					7209dd8bae | ||
| 
						 | 
					ab736c89bb | ||
| 
						 | 
					6911639617 | ||
| 
						 | 
					b9720d0715 | ||
| 
						 | 
					47b3267ed4 | ||
| 
						 | 
					e16ba2adb5 | ||
| 
						 | 
					0a19b1e32c | ||
| 
						 | 
					bae9a950c0 | ||
| 
						 | 
					72b2943332 | ||
| 
						 | 
					4ec0ef7548 | ||
| 
						 | 
					25bc6761f6 | ||
| 
						 | 
					81b6562c25 | ||
| 
						 | 
					ae74189fc2 | ||
| 
						 | 
					9e516efe10 | ||
| 
						 | 
					366e29439e | ||
| 
						 | 
					1c9c700d7f | ||
| 
						 | 
					b2e6b9d31f | ||
| 
						 | 
					7623f63846 | ||
| 
						 | 
					2bfaf9dce3 | ||
| 
						 | 
					5c2c1560bb | ||
| 
						 | 
					f7096ab78e | ||
| 
						 | 
					98f8feb625 | ||
| 
						 | 
					9944ca414e | 
							
								
								
									
										4
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -52,6 +52,10 @@ jobs:
 | 
			
		||||
            file: tests/test6.yaml
 | 
			
		||||
            name: Test tests/test6.yaml
 | 
			
		||||
            pio_cache_key: test6
 | 
			
		||||
          - id: test
 | 
			
		||||
            file: tests/test7.yaml
 | 
			
		||||
            name: Test tests/test7.yaml
 | 
			
		||||
            pio_cache_key: test7
 | 
			
		||||
          - id: pytest
 | 
			
		||||
            name: Run pytest
 | 
			
		||||
          - id: clang-format
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,7 +18,7 @@ jobs:
 | 
			
		||||
  lock:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: dessant/lock-threads@v3
 | 
			
		||||
      - uses: dessant/lock-threads@v4
 | 
			
		||||
        with:
 | 
			
		||||
          pr-inactive-days: "1"
 | 
			
		||||
          pr-lock-reason: ""
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										117
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										117
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -30,6 +30,10 @@ jobs:
 | 
			
		||||
            TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
 | 
			
		||||
            today="$(date --utc '+%Y%m%d')"
 | 
			
		||||
            TAG="${TAG}${today}"
 | 
			
		||||
            BRANCH=${GITHUB_REF#refs/heads/}
 | 
			
		||||
            if [[ "$BRANCH" != "dev" ]]; then
 | 
			
		||||
              TAG="${TAG}-${BRANCH}"
 | 
			
		||||
            fi
 | 
			
		||||
          fi
 | 
			
		||||
          echo "tag=${TAG}" >> $GITHUB_OUTPUT
 | 
			
		||||
        # yamllint enable rule:line-length
 | 
			
		||||
@@ -57,17 +61,30 @@ jobs:
 | 
			
		||||
        run: twine upload dist/*
 | 
			
		||||
 | 
			
		||||
  deploy-docker:
 | 
			
		||||
    name: Build and publish docker containers
 | 
			
		||||
    name: Build and publish ESPHome ${{ matrix.image.title}}
 | 
			
		||||
    if: github.repository == 'esphome/esphome'
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    continue-on-error: ${{ matrix.image.title == 'lint' }}
 | 
			
		||||
    needs: [init]
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        arch: [amd64, armv7, aarch64]
 | 
			
		||||
        build_type: ["ha-addon", "docker", "lint"]
 | 
			
		||||
        image:
 | 
			
		||||
          - title: "ha-addon"
 | 
			
		||||
            suffix: "hassio"
 | 
			
		||||
            target: "hassio"
 | 
			
		||||
            baseimg: "hassio"
 | 
			
		||||
          - title: "docker"
 | 
			
		||||
            suffix: ""
 | 
			
		||||
            target: "docker"
 | 
			
		||||
            baseimg: "docker"
 | 
			
		||||
          - title: "lint"
 | 
			
		||||
            suffix: "lint"
 | 
			
		||||
            target: "lint"
 | 
			
		||||
            baseimg: "docker"
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
@@ -92,69 +109,47 @@ jobs:
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Generate short tags
 | 
			
		||||
        id: tags
 | 
			
		||||
        run: |
 | 
			
		||||
          docker/generate_tags.py \
 | 
			
		||||
            --tag "${{ needs.init.outputs.tag }}" \
 | 
			
		||||
            --suffix "${{ matrix.image.suffix }}"
 | 
			
		||||
 | 
			
		||||
      - name: Build and push
 | 
			
		||||
        run: |
 | 
			
		||||
          docker/build.py \
 | 
			
		||||
            --tag "${{ needs.init.outputs.tag }}" \
 | 
			
		||||
            --arch "${{ matrix.arch }}" \
 | 
			
		||||
            --build-type "${{ matrix.build_type }}" \
 | 
			
		||||
            build \
 | 
			
		||||
            --push
 | 
			
		||||
 | 
			
		||||
  deploy-docker-manifest:
 | 
			
		||||
    if: github.repository == 'esphome/esphome'
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: [init, deploy-docker]
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        build_type: ["ha-addon", "docker", "lint"]
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v4
 | 
			
		||||
        uses: docker/build-push-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
      - name: Enable experimental manifest support
 | 
			
		||||
        run: |
 | 
			
		||||
          mkdir -p ~/.docker
 | 
			
		||||
          echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
 | 
			
		||||
 | 
			
		||||
      - name: Log in to docker hub
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKER_USER }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_PASSWORD }}
 | 
			
		||||
      - name: Log in to the GitHub container registry
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ghcr.io
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Run manifest
 | 
			
		||||
        run: |
 | 
			
		||||
          docker/build.py \
 | 
			
		||||
            --tag "${{ needs.init.outputs.tag }}" \
 | 
			
		||||
            --build-type "${{ matrix.build_type }}" \
 | 
			
		||||
            manifest
 | 
			
		||||
          context: .
 | 
			
		||||
          file: ./docker/Dockerfile
 | 
			
		||||
          platforms: linux/amd64,linux/arm/v7,linux/arm64
 | 
			
		||||
          target: ${{ matrix.image.target }}
 | 
			
		||||
          push: true
 | 
			
		||||
          # yamllint disable rule:line-length
 | 
			
		||||
          cache-from: type=registry,ref=ghcr.io/${{ steps.tags.outputs.image }}:cache-${{ steps.tags.outputs.channel }}
 | 
			
		||||
          cache-to: type=registry,ref=ghcr.io/${{ steps.tags.outputs.image }}:cache-${{ steps.tags.outputs.channel }},mode=max
 | 
			
		||||
          # yamllint enable rule:line-length
 | 
			
		||||
          tags: ${{ steps.tags.outputs.tags }}
 | 
			
		||||
          build-args: |
 | 
			
		||||
            BASEIMGTYPE=${{ matrix.image.baseimg }}
 | 
			
		||||
            BUILD_VERSION=${{ needs.init.outputs.tag }}
 | 
			
		||||
 | 
			
		||||
  deploy-ha-addon-repo:
 | 
			
		||||
    if: github.repository == 'esphome/esphome' && github.event_name == 'release'
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: [deploy-docker]
 | 
			
		||||
    steps:
 | 
			
		||||
      - env:
 | 
			
		||||
          TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
 | 
			
		||||
        # yamllint disable rule:line-length
 | 
			
		||||
        run: |
 | 
			
		||||
          TAG="${GITHUB_REF#refs/tags/}"
 | 
			
		||||
          curl \
 | 
			
		||||
            -u ":$TOKEN" \
 | 
			
		||||
            -X POST \
 | 
			
		||||
            -H "Accept: application/vnd.github.v3+json" \
 | 
			
		||||
            https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
 | 
			
		||||
            -d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"
 | 
			
		||||
        # yamllint enable rule:line-length
 | 
			
		||||
      - name: Trigger Workflow
 | 
			
		||||
        uses: actions/github-script@v6
 | 
			
		||||
        with:
 | 
			
		||||
          github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
 | 
			
		||||
          script: |
 | 
			
		||||
            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) }}
 | 
			
		||||
              }
 | 
			
		||||
            })
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,7 +18,7 @@ jobs:
 | 
			
		||||
  stale:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/stale@v6
 | 
			
		||||
      - uses: actions/stale@v7
 | 
			
		||||
        with:
 | 
			
		||||
          days-before-pr-stale: 90
 | 
			
		||||
          days-before-pr-close: 7
 | 
			
		||||
@@ -38,7 +38,7 @@ jobs:
 | 
			
		||||
  close-issues:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/stale@v6
 | 
			
		||||
      - uses: actions/stale@v7
 | 
			
		||||
        with:
 | 
			
		||||
          days-before-pr-stale: -1
 | 
			
		||||
          days-before-pr-close: -1
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -128,3 +128,5 @@ tests/.esphome/
 | 
			
		||||
 | 
			
		||||
sdkconfig.*
 | 
			
		||||
!sdkconfig.defaults
 | 
			
		||||
 | 
			
		||||
.tests/
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
# See https://pre-commit.com/hooks.html for more hooks
 | 
			
		||||
repos:
 | 
			
		||||
  - repo: https://github.com/ambv/black
 | 
			
		||||
    rev: 22.10.0
 | 
			
		||||
    rev: 22.12.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: black
 | 
			
		||||
        args:
 | 
			
		||||
@@ -27,7 +27,7 @@ repos:
 | 
			
		||||
          - --branch=release
 | 
			
		||||
          - --branch=beta
 | 
			
		||||
  - repo: https://github.com/asottile/pyupgrade
 | 
			
		||||
    rev: v3.2.2
 | 
			
		||||
    rev: v3.3.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: pyupgrade
 | 
			
		||||
        args: [--py39-plus]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								CODEOWNERS
									
									
									
									
									
								
							@@ -41,6 +41,8 @@ esphome/components/ble_client/* @buxtronix
 | 
			
		||||
esphome/components/bluetooth_proxy/* @jesserockz
 | 
			
		||||
esphome/components/bme680_bsec/* @trvrnrth
 | 
			
		||||
esphome/components/bmp3xx/* @martgras
 | 
			
		||||
esphome/components/bp1658cj/* @Cossid
 | 
			
		||||
esphome/components/bp5758d/* @Cossid
 | 
			
		||||
esphome/components/button/* @esphome/core
 | 
			
		||||
esphome/components/canbus/* @danielschramm @mvturnho
 | 
			
		||||
esphome/components/cap1188/* @MrEditor97
 | 
			
		||||
@@ -69,6 +71,7 @@ esphome/components/display_menu_base/* @numo68
 | 
			
		||||
esphome/components/dps310/* @kbx81
 | 
			
		||||
esphome/components/ds1307/* @badbadc0ffee
 | 
			
		||||
esphome/components/dsmr/* @glmnet @zuidwijk
 | 
			
		||||
esphome/components/ee895/* @Stock-M
 | 
			
		||||
esphome/components/ektf2232/* @jesserockz
 | 
			
		||||
esphome/components/ens210/* @itn3rd77
 | 
			
		||||
esphome/components/esp32/* @esphome/core
 | 
			
		||||
@@ -100,9 +103,11 @@ esphome/components/hitachi_ac424/* @sourabhjaiswal
 | 
			
		||||
esphome/components/homeassistant/* @OttoWinter
 | 
			
		||||
esphome/components/honeywellabp/* @RubyBailey
 | 
			
		||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
 | 
			
		||||
esphome/components/hte501/* @Stock-M
 | 
			
		||||
esphome/components/hydreon_rgxx/* @functionpointer
 | 
			
		||||
esphome/components/i2c/* @esphome/core
 | 
			
		||||
esphome/components/i2s_audio/* @jesserockz
 | 
			
		||||
esphome/components/improv_base/* @esphome/core
 | 
			
		||||
esphome/components/improv_serial/* @esphome/core
 | 
			
		||||
esphome/components/ina260/* @MrEditor97
 | 
			
		||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
 | 
			
		||||
@@ -111,13 +116,17 @@ esphome/components/integration/* @OttoWinter
 | 
			
		||||
esphome/components/interval/* @esphome/core
 | 
			
		||||
esphome/components/json/* @OttoWinter
 | 
			
		||||
esphome/components/kalman_combinator/* @Cat-Ion
 | 
			
		||||
esphome/components/key_collector/* @ssieb
 | 
			
		||||
esphome/components/key_provider/* @ssieb
 | 
			
		||||
esphome/components/lcd_menu/* @numo68
 | 
			
		||||
esphome/components/ld2410/* @sebcaps
 | 
			
		||||
esphome/components/ledc/* @OttoWinter
 | 
			
		||||
esphome/components/light/* @esphome/core
 | 
			
		||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
 | 
			
		||||
esphome/components/lock/* @esphome/core
 | 
			
		||||
esphome/components/logger/* @esphome/core
 | 
			
		||||
esphome/components/ltr390/* @sjtrny
 | 
			
		||||
esphome/components/matrix_keypad/* @ssieb
 | 
			
		||||
esphome/components/max31865/* @DAVe3283
 | 
			
		||||
esphome/components/max44009/* @berfenger
 | 
			
		||||
esphome/components/max7219digit/* @rspaargaren
 | 
			
		||||
@@ -138,6 +147,7 @@ esphome/components/mcp9808/* @k7hpn
 | 
			
		||||
esphome/components/md5/* @esphome/core
 | 
			
		||||
esphome/components/mdns/* @esphome/core
 | 
			
		||||
esphome/components/media_player/* @jesserockz
 | 
			
		||||
esphome/components/mics_4514/* @jesserockz
 | 
			
		||||
esphome/components/midea/* @dudanov
 | 
			
		||||
esphome/components/midea_ir/* @dudanov
 | 
			
		||||
esphome/components/mitsubishi/* @RubyBailey
 | 
			
		||||
@@ -164,6 +174,8 @@ esphome/components/nfc/* @jesserockz
 | 
			
		||||
esphome/components/number/* @esphome/core
 | 
			
		||||
esphome/components/ota/* @esphome/core
 | 
			
		||||
esphome/components/output/* @esphome/core
 | 
			
		||||
esphome/components/pca9554/* @hwstar
 | 
			
		||||
esphome/components/pcf85063/* @brogon
 | 
			
		||||
esphome/components/pid/* @OttoWinter
 | 
			
		||||
esphome/components/pipsolar/* @andreashergert1984
 | 
			
		||||
esphome/components/pm1006/* @habbie
 | 
			
		||||
@@ -204,10 +216,15 @@ esphome/components/sgp4x/* @SenexCrenshaw @martgras
 | 
			
		||||
esphome/components/shelly_dimmer/* @edge90 @rnauber
 | 
			
		||||
esphome/components/sht4x/* @sjtrny
 | 
			
		||||
esphome/components/shutdown/* @esphome/core @jsuanet
 | 
			
		||||
esphome/components/sigma_delta_output/* @Cat-Ion
 | 
			
		||||
esphome/components/sim800l/* @glmnet
 | 
			
		||||
esphome/components/sm10bit_base/* @Cossid
 | 
			
		||||
esphome/components/sm2135/* @BoukeHaarsma23
 | 
			
		||||
esphome/components/sm2235/* @Cossid
 | 
			
		||||
esphome/components/sm2335/* @Cossid
 | 
			
		||||
esphome/components/sml/* @alengwenus
 | 
			
		||||
esphome/components/smt100/* @piechade
 | 
			
		||||
esphome/components/sn74hc165/* @jesserockz
 | 
			
		||||
esphome/components/socket/* @esphome/core
 | 
			
		||||
esphome/components/sonoff_d1/* @anatoly-savchenkov
 | 
			
		||||
esphome/components/spi/* @esphome/core
 | 
			
		||||
@@ -233,6 +250,7 @@ esphome/components/switch/* @esphome/core
 | 
			
		||||
esphome/components/t6615/* @tylermenezes
 | 
			
		||||
esphome/components/tca9548a/* @andreashergert1984
 | 
			
		||||
esphome/components/tcl112/* @glmnet
 | 
			
		||||
esphome/components/tee501/* @Stock-M
 | 
			
		||||
esphome/components/teleinfo/* @0hax
 | 
			
		||||
esphome/components/thermostat/* @kbx81
 | 
			
		||||
esphome/components/time/* @OttoWinter
 | 
			
		||||
@@ -257,12 +275,15 @@ esphome/components/uart/* @esphome/core
 | 
			
		||||
esphome/components/ufire_ec/* @pvizeli
 | 
			
		||||
esphome/components/ufire_ise/* @pvizeli
 | 
			
		||||
esphome/components/ultrasonic/* @OttoWinter
 | 
			
		||||
esphome/components/vbus/* @ssieb
 | 
			
		||||
esphome/components/version/* @esphome/core
 | 
			
		||||
esphome/components/wake_on_lan/* @willwill2will54
 | 
			
		||||
esphome/components/web_server_base/* @OttoWinter
 | 
			
		||||
esphome/components/whirlpool/* @glmnet
 | 
			
		||||
esphome/components/whynter/* @aeonsablaze
 | 
			
		||||
esphome/components/wiegand/* @ssieb
 | 
			
		||||
esphome/components/wl_134/* @hobbypunk90
 | 
			
		||||
esphome/components/x9c/* @EtienneMD
 | 
			
		||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
 | 
			
		||||
esphome/components/xiaomi_mhoc303/* @drug123
 | 
			
		||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,15 @@
 | 
			
		||||
ARG BASEIMGTYPE=docker
 | 
			
		||||
 | 
			
		||||
# https://github.com/hassio-addons/addon-debian-base/releases
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base:6.1.3 AS base-hassio
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base:6.2.0 AS base-hassio
 | 
			
		||||
# https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye
 | 
			
		||||
FROM debian:bullseye-20221024-slim AS base-docker
 | 
			
		||||
 | 
			
		||||
FROM base-${BASEIMGTYPE} AS base
 | 
			
		||||
 | 
			
		||||
ARG TARGETARCH
 | 
			
		||||
ARG TARGETVARIANT
 | 
			
		||||
 | 
			
		||||
RUN \
 | 
			
		||||
    apt-get update \
 | 
			
		||||
    # Use pinned versions so that we get updates with build caching
 | 
			
		||||
@@ -23,7 +26,7 @@ RUN \
 | 
			
		||||
        python3-cryptography=3.3.2-1 \
 | 
			
		||||
        iputils-ping=3:20210202-1 \
 | 
			
		||||
        git=1:2.30.2-1 \
 | 
			
		||||
        curl=7.74.0-1.3+deb11u3 \
 | 
			
		||||
        curl=7.74.0-1.3+deb11u5 \
 | 
			
		||||
        openssh-client=1:8.4p1-5+deb11u1 \
 | 
			
		||||
    && rm -rf \
 | 
			
		||||
        /tmp/* \
 | 
			
		||||
@@ -36,6 +39,14 @@ ENV \
 | 
			
		||||
  # Store globally installed pio libs in /piolibs
 | 
			
		||||
  PLATFORMIO_GLOBALLIB_DIR=/piolibs
 | 
			
		||||
 | 
			
		||||
# Support legacy binaries on Debian multiarch system. There is no "correct" way
 | 
			
		||||
# to do this, other than using properly built toolchains...
 | 
			
		||||
# See: https://unix.stackexchange.com/questions/553743/correct-way-to-add-lib-ld-linux-so-3-in-debian
 | 
			
		||||
RUN \
 | 
			
		||||
    if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
 | 
			
		||||
        ln -s /lib/arm-linux-gnueabihf/ld-linux.so.3 /lib/ld-linux.so.3; \
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
RUN \
 | 
			
		||||
    # Ubuntu python3-pip is missing wheel
 | 
			
		||||
    pip3 install --no-cache-dir \
 | 
			
		||||
@@ -128,7 +139,7 @@ RUN \
 | 
			
		||||
        clang-tidy-11=1:11.0.1-2 \
 | 
			
		||||
        patch=2.7.6-7 \
 | 
			
		||||
        software-properties-common=0.96.20.2-2.1 \
 | 
			
		||||
        nano=5.4-2+deb11u1 \
 | 
			
		||||
        nano=5.4-2+deb11u2 \
 | 
			
		||||
        build-essential=12.9 \
 | 
			
		||||
        python3-dev=3.9.2-3 \
 | 
			
		||||
    && rm -rf \
 | 
			
		||||
 
 | 
			
		||||
@@ -8,32 +8,49 @@ import re
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CHANNEL_DEV = 'dev'
 | 
			
		||||
CHANNEL_BETA = 'beta'
 | 
			
		||||
CHANNEL_RELEASE = 'release'
 | 
			
		||||
CHANNEL_DEV = "dev"
 | 
			
		||||
CHANNEL_BETA = "beta"
 | 
			
		||||
CHANNEL_RELEASE = "release"
 | 
			
		||||
CHANNELS = [CHANNEL_DEV, CHANNEL_BETA, CHANNEL_RELEASE]
 | 
			
		||||
 | 
			
		||||
ARCH_AMD64 = 'amd64'
 | 
			
		||||
ARCH_ARMV7 = 'armv7'
 | 
			
		||||
ARCH_AARCH64 = 'aarch64'
 | 
			
		||||
ARCH_AMD64 = "amd64"
 | 
			
		||||
ARCH_ARMV7 = "armv7"
 | 
			
		||||
ARCH_AARCH64 = "aarch64"
 | 
			
		||||
ARCHS = [ARCH_AMD64, ARCH_ARMV7, ARCH_AARCH64]
 | 
			
		||||
 | 
			
		||||
TYPE_DOCKER = 'docker'
 | 
			
		||||
TYPE_HA_ADDON = 'ha-addon'
 | 
			
		||||
TYPE_LINT = 'lint'
 | 
			
		||||
TYPE_DOCKER = "docker"
 | 
			
		||||
TYPE_HA_ADDON = "ha-addon"
 | 
			
		||||
TYPE_LINT = "lint"
 | 
			
		||||
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
parser = argparse.ArgumentParser()
 | 
			
		||||
parser.add_argument("--tag", type=str, required=True, help="The main docker tag to push to. If a version number also adds latest and/or beta tag")
 | 
			
		||||
parser.add_argument("--arch", choices=ARCHS, required=False, help="The architecture to build for")
 | 
			
		||||
parser.add_argument("--build-type", choices=TYPES, required=True, help="The type of build to run")
 | 
			
		||||
parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them")
 | 
			
		||||
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
 | 
			
		||||
parser.add_argument(
 | 
			
		||||
    "--tag",
 | 
			
		||||
    type=str,
 | 
			
		||||
    required=True,
 | 
			
		||||
    help="The main docker tag to push to. If a version number also adds latest and/or beta tag",
 | 
			
		||||
)
 | 
			
		||||
parser.add_argument(
 | 
			
		||||
    "--arch", choices=ARCHS, required=False, help="The architecture to build for"
 | 
			
		||||
)
 | 
			
		||||
parser.add_argument(
 | 
			
		||||
    "--build-type", choices=TYPES, required=True, help="The type of build to run"
 | 
			
		||||
)
 | 
			
		||||
parser.add_argument(
 | 
			
		||||
    "--dry-run", action="store_true", help="Don't run any commands, just print them"
 | 
			
		||||
)
 | 
			
		||||
subparsers = parser.add_subparsers(
 | 
			
		||||
    help="Action to perform", dest="command", required=True
 | 
			
		||||
)
 | 
			
		||||
build_parser = subparsers.add_parser("build", help="Build the image")
 | 
			
		||||
build_parser.add_argument("--push", help="Also push the images", action="store_true")
 | 
			
		||||
build_parser.add_argument("--load", help="Load the docker image locally", action="store_true")
 | 
			
		||||
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
 | 
			
		||||
build_parser.add_argument(
 | 
			
		||||
    "--load", help="Load the docker image locally", action="store_true"
 | 
			
		||||
)
 | 
			
		||||
manifest_parser = subparsers.add_parser(
 | 
			
		||||
    "manifest", help="Create a manifest from already pushed images"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass(frozen=True)
 | 
			
		||||
@@ -49,7 +66,7 @@ class DockerParams:
 | 
			
		||||
        prefix = {
 | 
			
		||||
            TYPE_DOCKER: "esphome/esphome",
 | 
			
		||||
            TYPE_HA_ADDON: "esphome/esphome-hassio",
 | 
			
		||||
            TYPE_LINT: "esphome/esphome-lint"
 | 
			
		||||
            TYPE_LINT: "esphome/esphome-lint",
 | 
			
		||||
        }[build_type]
 | 
			
		||||
        build_to = f"{prefix}-{arch}"
 | 
			
		||||
        baseimgtype = {
 | 
			
		||||
@@ -128,13 +145,21 @@ def main():
 | 
			
		||||
 | 
			
		||||
        # 3. build
 | 
			
		||||
        cmd = [
 | 
			
		||||
            "docker", "buildx", "build",
 | 
			
		||||
            "--build-arg", f"BASEIMGTYPE={params.baseimgtype}",
 | 
			
		||||
            "--build-arg", f"BUILD_VERSION={args.tag}",
 | 
			
		||||
            "--cache-from", f"type=registry,ref={cache_img}",
 | 
			
		||||
            "--file", "docker/Dockerfile",
 | 
			
		||||
            "--platform", params.platform,
 | 
			
		||||
            "--target", params.target,
 | 
			
		||||
            "docker",
 | 
			
		||||
            "buildx",
 | 
			
		||||
            "build",
 | 
			
		||||
            "--build-arg",
 | 
			
		||||
            f"BASEIMGTYPE={params.baseimgtype}",
 | 
			
		||||
            "--build-arg",
 | 
			
		||||
            f"BUILD_VERSION={args.tag}",
 | 
			
		||||
            "--cache-from",
 | 
			
		||||
            f"type=registry,ref={cache_img}",
 | 
			
		||||
            "--file",
 | 
			
		||||
            "docker/Dockerfile",
 | 
			
		||||
            "--platform",
 | 
			
		||||
            params.platform,
 | 
			
		||||
            "--target",
 | 
			
		||||
            params.target,
 | 
			
		||||
        ]
 | 
			
		||||
        for img in imgs:
 | 
			
		||||
            cmd += ["--tag", img]
 | 
			
		||||
@@ -160,9 +185,7 @@ def main():
 | 
			
		||||
            run_command(*cmd)
 | 
			
		||||
        # 2. Push manifests
 | 
			
		||||
        for target in targets:
 | 
			
		||||
            run_command(
 | 
			
		||||
                "docker", "manifest", "push", target
 | 
			
		||||
            )
 | 
			
		||||
            run_command("docker", "manifest", "push", target)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								docker/generate_tags.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										68
									
								
								docker/generate_tags.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
import re
 | 
			
		||||
import os
 | 
			
		||||
import argparse
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
CHANNEL_DEV = "dev"
 | 
			
		||||
CHANNEL_BETA = "beta"
 | 
			
		||||
CHANNEL_RELEASE = "release"
 | 
			
		||||
 | 
			
		||||
parser = argparse.ArgumentParser()
 | 
			
		||||
parser.add_argument(
 | 
			
		||||
    "--tag",
 | 
			
		||||
    type=str,
 | 
			
		||||
    required=True,
 | 
			
		||||
    help="The main docker tag to push to. If a version number also adds latest and/or beta tag",
 | 
			
		||||
)
 | 
			
		||||
parser.add_argument(
 | 
			
		||||
    "--suffix",
 | 
			
		||||
    type=str,
 | 
			
		||||
    required=True,
 | 
			
		||||
    help="The suffix of the tag.",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
    # detect channel from tag
 | 
			
		||||
    match = re.match(r"^(\d+\.\d+)(?:\.\d+)?(b\d+)?$", args.tag)
 | 
			
		||||
    major_minor_version = None
 | 
			
		||||
    if match is None:
 | 
			
		||||
        channel = CHANNEL_DEV
 | 
			
		||||
    elif match.group(2) is None:
 | 
			
		||||
        major_minor_version = match.group(1)
 | 
			
		||||
        channel = CHANNEL_RELEASE
 | 
			
		||||
    else:
 | 
			
		||||
        channel = CHANNEL_BETA
 | 
			
		||||
 | 
			
		||||
    tags_to_push = [args.tag]
 | 
			
		||||
    if channel == CHANNEL_DEV:
 | 
			
		||||
        tags_to_push.append("dev")
 | 
			
		||||
    elif channel == CHANNEL_BETA:
 | 
			
		||||
        tags_to_push.append("beta")
 | 
			
		||||
    elif channel == CHANNEL_RELEASE:
 | 
			
		||||
        # Additionally push to beta
 | 
			
		||||
        tags_to_push.append("beta")
 | 
			
		||||
        tags_to_push.append("latest")
 | 
			
		||||
 | 
			
		||||
        if major_minor_version:
 | 
			
		||||
            tags_to_push.append("stable")
 | 
			
		||||
            tags_to_push.append(major_minor_version)
 | 
			
		||||
 | 
			
		||||
    suffix = f"-{args.suffix}" if args.suffix else ""
 | 
			
		||||
 | 
			
		||||
    with open(os.environ["GITHUB_OUTPUT"], "w") as f:
 | 
			
		||||
        print(f"channel={channel}", file=f)
 | 
			
		||||
        print(f"image=esphome/esphome{suffix}", file=f)
 | 
			
		||||
        full_tags = []
 | 
			
		||||
 | 
			
		||||
        for tag in tags_to_push:
 | 
			
		||||
            full_tags += [f"ghcr.io/esphome/esphome{suffix}:{tag}"]
 | 
			
		||||
            full_tags += [f"esphome/esphome{suffix}:{tag}"]
 | 
			
		||||
        print(f"tags={','.join(full_tags)}", file=f)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    main()
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
#!/usr/bin/with-contenv bashio
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# This files check if all user configuration requirements are met
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
# Check SSL requirements, if enabled
 | 
			
		||||
if bashio::config.true 'ssl'; then
 | 
			
		||||
    if ! bashio::config.has_value 'certfile'; then
 | 
			
		||||
        bashio::log.fatal 'SSL is enabled, but no certfile was specified.'
 | 
			
		||||
        bashio::exit.nok
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! bashio::config.has_value 'keyfile'; then
 | 
			
		||||
        bashio::log.fatal 'SSL is enabled, but no keyfile was specified'
 | 
			
		||||
        bashio::exit.nok
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    certfile="/ssl/$(bashio::config 'certfile')"
 | 
			
		||||
    keyfile="/ssl/$(bashio::config 'keyfile')"
 | 
			
		||||
 | 
			
		||||
    if ! bashio::fs.file_exists "${certfile}"; then
 | 
			
		||||
        if ! bashio::fs.file_exists "${keyfile}"; then
 | 
			
		||||
            # Both files are missing, let's print a friendlier error message
 | 
			
		||||
            bashio::log.fatal 'You enabled encrypted connections using the "ssl": true option.'
 | 
			
		||||
            bashio::log.fatal "However, the SSL files '${certfile}' and '${keyfile}'"
 | 
			
		||||
            bashio::log.fatal "were not found. If you're using Hass.io on your local network and don't want"
 | 
			
		||||
            bashio::log.fatal 'to encrypt connections to the ESPHome dashboard, you can manually disable'
 | 
			
		||||
            bashio::log.fatal 'SSL by setting "ssl" to false."'
 | 
			
		||||
            bashio::exit.nok
 | 
			
		||||
        fi
 | 
			
		||||
        bashio::log.fatal "The configured certfile '${certfile}' was not found."
 | 
			
		||||
        bashio::exit.nok
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! bashio::fs.file_exists "/ssl/$(bashio::config 'keyfile')"; then
 | 
			
		||||
        bashio::log.fatal "The configured keyfile '${keyfile}' was not found."
 | 
			
		||||
        bashio::exit.nok
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
#!/usr/bin/with-contenv bashio
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Configures NGINX for use with ESPHome
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
declare certfile
 | 
			
		||||
declare keyfile
 | 
			
		||||
declare direct_port
 | 
			
		||||
declare ingress_interface
 | 
			
		||||
declare ingress_port
 | 
			
		||||
 | 
			
		||||
mkdir -p /var/log/nginx
 | 
			
		||||
 | 
			
		||||
direct_port=$(bashio::addon.port 6052)
 | 
			
		||||
if bashio::var.has_value "${direct_port}"; then
 | 
			
		||||
    if bashio::config.true 'ssl'; then
 | 
			
		||||
        certfile=$(bashio::config 'certfile')
 | 
			
		||||
        keyfile=$(bashio::config 'keyfile')
 | 
			
		||||
 | 
			
		||||
        mv /etc/nginx/servers/direct-ssl.disabled /etc/nginx/servers/direct.conf
 | 
			
		||||
        sed -i "s/%%certfile%%/${certfile}/g" /etc/nginx/servers/direct.conf
 | 
			
		||||
        sed -i "s/%%keyfile%%/${keyfile}/g" /etc/nginx/servers/direct.conf
 | 
			
		||||
    else
 | 
			
		||||
        mv /etc/nginx/servers/direct.disabled /etc/nginx/servers/direct.conf
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    sed -i "s/%%port%%/${direct_port}/g" /etc/nginx/servers/direct.conf
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
ingress_port=$(bashio::addon.ingress_port)
 | 
			
		||||
ingress_interface=$(bashio::addon.ip_address)
 | 
			
		||||
sed -i "s/%%port%%/${ingress_port}/g" /etc/nginx/servers/ingress.conf
 | 
			
		||||
sed -i "s/%%interface%%/${ingress_interface}/g" /etc/nginx/servers/ingress.conf
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
#!/usr/bin/with-contenv bashio
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# This files creates all directories used by esphome
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
pio_cache_base=/data/cache/platformio
 | 
			
		||||
 | 
			
		||||
mkdir -p "${pio_cache_base}"
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
proxy_http_version 1.1;
 | 
			
		||||
proxy_ignore_client_abort off;
 | 
			
		||||
proxy_read_timeout 86400s;
 | 
			
		||||
proxy_redirect off;
 | 
			
		||||
proxy_send_timeout 86400s;
 | 
			
		||||
proxy_max_temp_file_size 0;
 | 
			
		||||
proxy_http_version          1.1;
 | 
			
		||||
proxy_ignore_client_abort   off;
 | 
			
		||||
proxy_read_timeout          86400s;
 | 
			
		||||
proxy_redirect              off;
 | 
			
		||||
proxy_send_timeout          86400s;
 | 
			
		||||
proxy_max_temp_file_size    0;
 | 
			
		||||
 | 
			
		||||
proxy_set_header Accept-Encoding "";
 | 
			
		||||
proxy_set_header Connection $connection_upgrade;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
root /dev/null;
 | 
			
		||||
server_name $hostname;
 | 
			
		||||
root            /dev/null;
 | 
			
		||||
server_name     $hostname;
 | 
			
		||||
 | 
			
		||||
client_max_body_size 512m;
 | 
			
		||||
 | 
			
		||||
add_header X-Content-Type-Options nosniff;
 | 
			
		||||
add_header X-XSS-Protection "1; mode=block";
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
ssl_protocols TLSv1.2;
 | 
			
		||||
ssl_prefer_server_ciphers on;
 | 
			
		||||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA;
 | 
			
		||||
ssl_ecdh_curve secp384r1;
 | 
			
		||||
ssl_protocols TLSv1.2 TLSv1.3;
 | 
			
		||||
ssl_prefer_server_ciphers off;
 | 
			
		||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
 | 
			
		||||
ssl_session_timeout  10m;
 | 
			
		||||
ssl_session_cache shared:SSL:10m;
 | 
			
		||||
ssl_session_tickets off;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								docker/ha-addon-rootfs/etc/nginx/includes/upstream.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docker/ha-addon-rootfs/etc/nginx/includes/upstream.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
upstream esphome {
 | 
			
		||||
    server unix:/var/run/esphome.sock;
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,6 @@ daemon off;
 | 
			
		||||
user root;
 | 
			
		||||
pid /var/run/nginx.pid;
 | 
			
		||||
worker_processes 1;
 | 
			
		||||
# Hass.io addon log
 | 
			
		||||
error_log /proc/1/fd/1 error;
 | 
			
		||||
events {
 | 
			
		||||
    worker_connections 1024;
 | 
			
		||||
@@ -10,24 +9,22 @@ events {
 | 
			
		||||
 | 
			
		||||
http {
 | 
			
		||||
    include /etc/nginx/includes/mime.types;
 | 
			
		||||
    access_log stdout;
 | 
			
		||||
    default_type application/octet-stream;
 | 
			
		||||
    gzip on;
 | 
			
		||||
    keepalive_timeout 65;
 | 
			
		||||
    sendfile on;
 | 
			
		||||
    server_tokens off;
 | 
			
		||||
 | 
			
		||||
    access_log              off;
 | 
			
		||||
    default_type            application/octet-stream;
 | 
			
		||||
    gzip                    on;
 | 
			
		||||
    keepalive_timeout       65;
 | 
			
		||||
    sendfile                on;
 | 
			
		||||
    server_tokens           off;
 | 
			
		||||
 | 
			
		||||
    tcp_nodelay             on;
 | 
			
		||||
    tcp_nopush              on;
 | 
			
		||||
 | 
			
		||||
    map $http_upgrade $connection_upgrade {
 | 
			
		||||
        default upgrade;
 | 
			
		||||
        ''      close;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # Use Hass.io supervisor as resolver
 | 
			
		||||
    resolver 172.30.32.2;
 | 
			
		||||
 | 
			
		||||
    upstream esphome {
 | 
			
		||||
        server unix:/var/run/esphome.sock;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    include /etc/nginx/includes/upstream.conf;
 | 
			
		||||
    include /etc/nginx/servers/*.conf;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								docker/ha-addon-rootfs/etc/nginx/servers/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docker/ha-addon-rootfs/etc/nginx/servers/.gitkeep
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
Without requirements or design, programming is the art of adding bugs to an empty text file. (Louis Srygley)
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
server {
 | 
			
		||||
    listen %%port%% default_server;
 | 
			
		||||
 | 
			
		||||
    include /etc/nginx/includes/server_params.conf;
 | 
			
		||||
    include /etc/nginx/includes/proxy_params.conf;
 | 
			
		||||
    # Clear Hass.io Ingress header
 | 
			
		||||
    proxy_set_header X-HA-Ingress "";
 | 
			
		||||
 | 
			
		||||
    location / {
 | 
			
		||||
        proxy_pass http://esphome;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,20 +1,26 @@
 | 
			
		||||
server {
 | 
			
		||||
    listen %%port%% default_server ssl http2;
 | 
			
		||||
    {{ if not .ssl }}
 | 
			
		||||
    listen 6052 default_server;
 | 
			
		||||
    {{ else }}
 | 
			
		||||
    listen 6052 default_server ssl http2;
 | 
			
		||||
    {{ end }}
 | 
			
		||||
 | 
			
		||||
    include /etc/nginx/includes/server_params.conf;
 | 
			
		||||
    include /etc/nginx/includes/proxy_params.conf;
 | 
			
		||||
 | 
			
		||||
    {{ if .ssl }}
 | 
			
		||||
    include /etc/nginx/includes/ssl_params.conf;
 | 
			
		||||
 | 
			
		||||
    ssl on;
 | 
			
		||||
    ssl_certificate /ssl/%%certfile%%;
 | 
			
		||||
    ssl_certificate_key /ssl/%%keyfile%%;
 | 
			
		||||
 | 
			
		||||
    # Clear Hass.io Ingress header
 | 
			
		||||
    proxy_set_header X-HA-Ingress "";
 | 
			
		||||
    ssl_certificate /ssl/{{ .certfile }};
 | 
			
		||||
    ssl_certificate_key /ssl/{{ .keyfile }};
 | 
			
		||||
 | 
			
		||||
    # Redirect http requests to https on the same port.
 | 
			
		||||
    # https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/
 | 
			
		||||
    error_page 497 https://$http_host$request_uri;
 | 
			
		||||
    {{ end }}
 | 
			
		||||
 | 
			
		||||
    # Clear Home Assistant Ingress header
 | 
			
		||||
    proxy_set_header X-HA-Ingress "";
 | 
			
		||||
 | 
			
		||||
    location / {
 | 
			
		||||
        proxy_pass http://esphome;
 | 
			
		||||
@@ -1,14 +1,16 @@
 | 
			
		||||
server {
 | 
			
		||||
    listen %%interface%%:%%port%% default_server;
 | 
			
		||||
    listen 127.0.0.1:{{ .port }} default_server;
 | 
			
		||||
    listen {{ .interface }}:{{ .port }} default_server;
 | 
			
		||||
 | 
			
		||||
    include /etc/nginx/includes/server_params.conf;
 | 
			
		||||
    include /etc/nginx/includes/proxy_params.conf;
 | 
			
		||||
 | 
			
		||||
    # Set Home Assistant Ingress header
 | 
			
		||||
    proxy_set_header X-HA-Ingress "YES";
 | 
			
		||||
 | 
			
		||||
    location / {
 | 
			
		||||
        # Only allow from Hass.io supervisor
 | 
			
		||||
        allow   172.30.32.2;
 | 
			
		||||
        allow   127.0.0.1;
 | 
			
		||||
        deny    all;
 | 
			
		||||
 | 
			
		||||
        proxy_pass http://esphome;
 | 
			
		||||
							
								
								
									
										32
									
								
								docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/discovery/run
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/discovery/run
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
#!/command/with-contenv bashio
 | 
			
		||||
# shellcheck shell=bash
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Home Assistant Add-on: ESPHome
 | 
			
		||||
# Sends discovery information to Home Assistant.
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
declare config
 | 
			
		||||
declare port
 | 
			
		||||
 | 
			
		||||
# We only disable it when disabled explicitly
 | 
			
		||||
if bashio::config.false 'home_assistant_dashboard_integration';
 | 
			
		||||
then
 | 
			
		||||
    bashio::log.info "Home Assistant discovery is disabled for this add-on."
 | 
			
		||||
    bashio::exit.ok
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
port=$(bashio::addon.ingress_port)
 | 
			
		||||
 | 
			
		||||
# Wait for NGINX to become available
 | 
			
		||||
bashio::net.wait_for "${port}" "127.0.0.1" 300
 | 
			
		||||
 | 
			
		||||
config=$(\
 | 
			
		||||
    bashio::var.json \
 | 
			
		||||
        host "127.0.0.1" \
 | 
			
		||||
        port "^${port}" \
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
if bashio::discovery "esphome" "${config}" > /dev/null; then
 | 
			
		||||
    bashio::log.info "Successfully send discovery information to Home Assistant."
 | 
			
		||||
else
 | 
			
		||||
    bashio::log.error "Discovery message to Home Assistant failed!"
 | 
			
		||||
fi
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
oneshot
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
/etc/s6-overlay/s6-rc.d/discovery/run
 | 
			
		||||
							
								
								
									
										26
									
								
								docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/finish
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										26
									
								
								docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/esphome/finish
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#!/command/with-contenv bashio
 | 
			
		||||
# shellcheck shell=bash
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Home Assistant Community Add-on: ESPHome
 | 
			
		||||
# Take down the S6 supervision tree when ESPHome dashboard fails
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
declare exit_code
 | 
			
		||||
readonly exit_code_container=$(</run/s6-linux-init-container-results/exitcode)
 | 
			
		||||
readonly exit_code_service="${1}"
 | 
			
		||||
readonly exit_code_signal="${2}"
 | 
			
		||||
 | 
			
		||||
bashio::log.info \
 | 
			
		||||
  "Service ESPHome dashboard exited with code ${exit_code_service}" \
 | 
			
		||||
  "(by signal ${exit_code_signal})"
 | 
			
		||||
 | 
			
		||||
if [[ "${exit_code_service}" -eq 256 ]]; then
 | 
			
		||||
  if [[ "${exit_code_container}" -eq 0 ]]; then
 | 
			
		||||
    echo $((128 + $exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
 | 
			
		||||
  fi
 | 
			
		||||
  [[ "${exit_code_signal}" -eq 15 ]] && exec /run/s6/basedir/bin/halt
 | 
			
		||||
elif [[ "${exit_code_service}" -ne 0 ]]; then
 | 
			
		||||
  if [[ "${exit_code_container}" -eq 0 ]]; then
 | 
			
		||||
    echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
 | 
			
		||||
  fi
 | 
			
		||||
  exec /run/s6/basedir/bin/halt
 | 
			
		||||
fi
 | 
			
		||||
@@ -1,10 +1,19 @@
 | 
			
		||||
#!/usr/bin/with-contenv bashio
 | 
			
		||||
#!/command/with-contenv bashio
 | 
			
		||||
# shellcheck shell=bash
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Runs the ESPHome dashboard
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
readonly pio_cache_base=/data/cache/platformio
 | 
			
		||||
 | 
			
		||||
export ESPHOME_IS_HA_ADDON=true
 | 
			
		||||
export PLATFORMIO_GLOBALLIB_DIR=/piolibs
 | 
			
		||||
 | 
			
		||||
# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json`
 | 
			
		||||
# setting `core_dir` would therefore prevent pio from accessing
 | 
			
		||||
export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
 | 
			
		||||
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
 | 
			
		||||
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
 | 
			
		||||
 | 
			
		||||
if bashio::config.true 'leave_front_door_open'; then
 | 
			
		||||
    export DISABLE_HA_AUTHENTICATION=true
 | 
			
		||||
@@ -30,14 +39,7 @@ else
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
pio_cache_base=/data/cache/platformio
 | 
			
		||||
# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json`
 | 
			
		||||
# setting `core_dir` would therefore prevent pio from accessing
 | 
			
		||||
export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
 | 
			
		||||
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
 | 
			
		||||
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
 | 
			
		||||
 | 
			
		||||
export PLATFORMIO_GLOBALLIB_DIR=/piolibs
 | 
			
		||||
mkdir -p "${pio_cache_base}"
 | 
			
		||||
 | 
			
		||||
bashio::log.info "Starting ESPHome dashboard..."
 | 
			
		||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
longrun
 | 
			
		||||
							
								
								
									
										27
									
								
								docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/init-nginx/run
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										27
									
								
								docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/init-nginx/run
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
#!/command/with-contenv bashio
 | 
			
		||||
# shellcheck shell=bash
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Configures NGINX for use with ESPHome
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
mkdir -p /var/log/nginx
 | 
			
		||||
 | 
			
		||||
# Generate Ingress configuration
 | 
			
		||||
bashio::var.json \
 | 
			
		||||
    interface "$(bashio::addon.ip_address)" \
 | 
			
		||||
    port "^$(bashio::addon.ingress_port)" \
 | 
			
		||||
    | tempio \
 | 
			
		||||
        -template /etc/nginx/templates/ingress.gtpl \
 | 
			
		||||
        -out /etc/nginx/servers/ingress.conf
 | 
			
		||||
 | 
			
		||||
# Generate direct access configuration, if enabled.
 | 
			
		||||
if bashio::var.has_value "$(bashio::addon.port 6052)"; then
 | 
			
		||||
    bashio::config.require.ssl
 | 
			
		||||
    bashio::var.json \
 | 
			
		||||
        certfile "$(bashio::config 'certfile')" \
 | 
			
		||||
        keyfile "$(bashio::config 'keyfile')" \
 | 
			
		||||
        ssl "^$(bashio::config 'ssl')" \
 | 
			
		||||
        | tempio \
 | 
			
		||||
            -template /etc/nginx/templates/direct.gtpl \
 | 
			
		||||
            -out /etc/nginx/servers/direct.conf
 | 
			
		||||
fi
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
oneshot
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
/etc/s6-overlay/s6-rc.d/init-nginx/run
 | 
			
		||||
							
								
								
									
										25
									
								
								docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/nginx/finish
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										25
									
								
								docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/nginx/finish
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
#!/command/with-contenv bashio
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Take down the S6 supervision tree when NGINX fails
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
declare exit_code
 | 
			
		||||
readonly exit_code_container=$(</run/s6-linux-init-container-results/exitcode)
 | 
			
		||||
readonly exit_code_service="${1}"
 | 
			
		||||
readonly exit_code_signal="${2}"
 | 
			
		||||
 | 
			
		||||
bashio::log.info \
 | 
			
		||||
  "Service NGINX exited with code ${exit_code_service}" \
 | 
			
		||||
  "(by signal ${exit_code_signal})"
 | 
			
		||||
 | 
			
		||||
if [[ "${exit_code_service}" -eq 256 ]]; then
 | 
			
		||||
  if [[ "${exit_code_container}" -eq 0 ]]; then
 | 
			
		||||
    echo $((128 + $exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
 | 
			
		||||
  fi
 | 
			
		||||
  [[ "${exit_code_signal}" -eq 15 ]] && exec /run/s6/basedir/bin/halt
 | 
			
		||||
elif [[ "${exit_code_service}" -ne 0 ]]; then
 | 
			
		||||
  if [[ "${exit_code_container}" -eq 0 ]]; then
 | 
			
		||||
    echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
 | 
			
		||||
  fi
 | 
			
		||||
  exec /run/s6/basedir/bin/halt
 | 
			
		||||
fi
 | 
			
		||||
@@ -1,10 +1,11 @@
 | 
			
		||||
#!/usr/bin/with-contenv bashio
 | 
			
		||||
#!/command/with-contenv bashio
 | 
			
		||||
# shellcheck shell=bash
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Runs the NGINX proxy
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
bashio::log.info "Waiting for dashboard to come up..."
 | 
			
		||||
bashio::log.info "Waiting for ESPHome dashboard to come up..."
 | 
			
		||||
 | 
			
		||||
while [[ ! -S /var/run/esphome.sock ]]; do
 | 
			
		||||
  sleep 0.5
 | 
			
		||||
							
								
								
									
										1
									
								
								docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/nginx/type
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docker/ha-addon-rootfs/etc/s6-overlay/s6-rc.d/nginx/type
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
longrun
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
#!/usr/bin/execlineb -S0
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Take down the S6 supervision tree when ESPHome fails
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
declare APP_EXIT_CODE=${1}
 | 
			
		||||
 | 
			
		||||
if [[ "${APP_EXIT_CODE}" -ne 0 ]] && [[ "${APP_EXIT_CODE}" -ne 256 ]]; then
 | 
			
		||||
  bashio::log.warning "Halt add-on with exit code ${APP_EXIT_CODE}"
 | 
			
		||||
  echo "${APP_EXIT_CODE}" > /run/s6-linux-init-container-results/exitcode
 | 
			
		||||
  exec /run/s6/basedir/bin/halt
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
bashio::log.info "Service restart after closing"
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
#!/usr/bin/execlineb -S0
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Take down the S6 supervision tree when NGINX fails
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
declare APP_EXIT_CODE=${1}
 | 
			
		||||
 | 
			
		||||
if [[ "${APP_EXIT_CODE}" -ne 0 ]] && [[ "${APP_EXIT_CODE}" -ne 256 ]]; then
 | 
			
		||||
  bashio::log.warning "Halt add-on with exit code ${APP_EXIT_CODE}"
 | 
			
		||||
  echo "${APP_EXIT_CODE}" > /run/s6-linux-init-container-results/exitcode
 | 
			
		||||
  exec /run/s6/basedir/bin/halt
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
bashio::log.info "Service restart after closing"
 | 
			
		||||
@@ -339,7 +339,7 @@ def command_config(args, config):
 | 
			
		||||
    _LOGGER.info("Configuration is valid!")
 | 
			
		||||
    if not CORE.verbose:
 | 
			
		||||
        config = strip_default_ids(config)
 | 
			
		||||
    safe_print(yaml_util.dump(config))
 | 
			
		||||
    safe_print(yaml_util.dump(config, args.show_secrets))
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -665,6 +665,9 @@ def parse_args(argv):
 | 
			
		||||
    parser_config.add_argument(
 | 
			
		||||
        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
			
		||||
    )
 | 
			
		||||
    parser_config.add_argument(
 | 
			
		||||
        "--show-secrets", help="Show secrets in output.", action="store_true"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    parser_compile = subparsers.add_parser(
 | 
			
		||||
        "compile", help="Read the configuration and compile a program."
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,7 @@ void A4988::loop() {
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->dir_pin_->digital_write(dir == 1);
 | 
			
		||||
  delayMicroseconds(50);
 | 
			
		||||
  this->step_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(5);
 | 
			
		||||
  this->step_pin_->digital_write(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ static const char *const TAG = "ads1115";
 | 
			
		||||
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
 | 
			
		||||
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
 | 
			
		||||
 | 
			
		||||
static const uint8_t ADS1115_DATA_RATE_860_SPS = 0b111;
 | 
			
		||||
static const uint8_t ADS1115_DATA_RATE_860_SPS = 0b111;  // 3300_SPS for ADS1015
 | 
			
		||||
 | 
			
		||||
void ADS1115Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
 | 
			
		||||
@@ -18,6 +18,9 @@ void ADS1115Component::setup() {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Configuring ADS1115...");
 | 
			
		||||
 | 
			
		||||
  uint16_t config = 0;
 | 
			
		||||
  // Clear single-shot bit
 | 
			
		||||
  //        0b0xxxxxxxxxxxxxxx
 | 
			
		||||
@@ -77,6 +80,7 @@ void ADS1115Component::dump_config() {
 | 
			
		||||
    LOG_SENSOR("  ", "Sensor", sensor);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Multiplexer: %u", sensor->get_multiplexer());
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Gain: %u", sensor->get_gain());
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Resolution: %u", sensor->get_resolution());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
 | 
			
		||||
@@ -127,27 +131,45 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return NAN;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (sensor->get_resolution() == ADS1015_12_BITS) {
 | 
			
		||||
    bool negative = (raw_conversion >> 15) == 1;
 | 
			
		||||
 | 
			
		||||
    // shift raw_conversion as it's only 12-bits, left justified
 | 
			
		||||
    raw_conversion = raw_conversion >> (16 - ADS1015_12_BITS);
 | 
			
		||||
 | 
			
		||||
    // check if number was negative in order to keep the sign
 | 
			
		||||
    if (negative) {
 | 
			
		||||
      // the number was negative
 | 
			
		||||
      // 1) set the negative bit back
 | 
			
		||||
      raw_conversion |= 0x8000;
 | 
			
		||||
      // 2) reset the former (shifted) negative bit
 | 
			
		||||
      raw_conversion &= 0xF7FF;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto signed_conversion = static_cast<int16_t>(raw_conversion);
 | 
			
		||||
 | 
			
		||||
  float millivolts;
 | 
			
		||||
  float divider = (sensor->get_resolution() == ADS1115_16_BITS) ? 32768.0f : 2048.0f;
 | 
			
		||||
  switch (sensor->get_gain()) {
 | 
			
		||||
    case ADS1115_GAIN_6P144:
 | 
			
		||||
      millivolts = signed_conversion * 0.187500f;
 | 
			
		||||
      millivolts = (signed_conversion * 6144) / divider;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADS1115_GAIN_4P096:
 | 
			
		||||
      millivolts = signed_conversion * 0.125000f;
 | 
			
		||||
      millivolts = (signed_conversion * 4096) / divider;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADS1115_GAIN_2P048:
 | 
			
		||||
      millivolts = signed_conversion * 0.062500f;
 | 
			
		||||
      millivolts = (signed_conversion * 2048) / divider;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADS1115_GAIN_1P024:
 | 
			
		||||
      millivolts = signed_conversion * 0.031250f;
 | 
			
		||||
      millivolts = (signed_conversion * 1024) / divider;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADS1115_GAIN_0P512:
 | 
			
		||||
      millivolts = signed_conversion * 0.015625f;
 | 
			
		||||
      millivolts = (signed_conversion * 512) / divider;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADS1115_GAIN_0P256:
 | 
			
		||||
      millivolts = signed_conversion * 0.007813f;
 | 
			
		||||
      millivolts = (signed_conversion * 256) / divider;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      millivolts = NAN;
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,11 @@ enum ADS1115Gain {
 | 
			
		||||
  ADS1115_GAIN_0P256 = 0b101,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ADS1115Resolution {
 | 
			
		||||
  ADS1115_16_BITS = 16,
 | 
			
		||||
  ADS1015_12_BITS = 12,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ADS1115Sensor;
 | 
			
		||||
 | 
			
		||||
class ADS1115Component : public Component, public i2c::I2CDevice {
 | 
			
		||||
@@ -58,15 +63,17 @@ class ADS1115Sensor : public sensor::Sensor, public PollingComponent, public vol
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void set_multiplexer(ADS1115Multiplexer multiplexer) { multiplexer_ = multiplexer; }
 | 
			
		||||
  void set_gain(ADS1115Gain gain) { gain_ = gain; }
 | 
			
		||||
 | 
			
		||||
  void set_resolution(ADS1115Resolution resolution) { resolution_ = resolution; }
 | 
			
		||||
  float sample() override;
 | 
			
		||||
  uint8_t get_multiplexer() const { return multiplexer_; }
 | 
			
		||||
  uint8_t get_gain() const { return gain_; }
 | 
			
		||||
  uint8_t get_resolution() const { return resolution_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  ADS1115Component *parent_;
 | 
			
		||||
  ADS1115Multiplexer multiplexer_;
 | 
			
		||||
  ADS1115Gain gain_;
 | 
			
		||||
  ADS1115Resolution resolution_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ads1115
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ from esphome.components import sensor, voltage_sampler
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_GAIN,
 | 
			
		||||
    CONF_MULTIPLEXER,
 | 
			
		||||
    CONF_RESOLUTION,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
@@ -35,6 +36,12 @@ GAIN = {
 | 
			
		||||
    "0.256": ADS1115Gain.ADS1115_GAIN_0P256,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ADS1115Resolution = ads1115_ns.enum("ADS1115Resolution")
 | 
			
		||||
RESOLUTION = {
 | 
			
		||||
    "16_BITS": ADS1115Resolution.ADS1115_16_BITS,
 | 
			
		||||
    "12_BITS": ADS1115Resolution.ADS1015_12_BITS,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_gain(value):
 | 
			
		||||
    if isinstance(value, float):
 | 
			
		||||
@@ -63,6 +70,9 @@ CONFIG_SCHEMA = (
 | 
			
		||||
            cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
 | 
			
		||||
            cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
 | 
			
		||||
            cv.Required(CONF_GAIN): validate_gain,
 | 
			
		||||
            cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum(
 | 
			
		||||
                RESOLUTION, upper=True, space="_"
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
@@ -77,5 +87,6 @@ async def to_code(config):
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
 | 
			
		||||
    cg.add(var.set_gain(config[CONF_GAIN]))
 | 
			
		||||
    cg.add(var.set_resolution(config[CONF_RESOLUTION]))
 | 
			
		||||
 | 
			
		||||
    cg.add(paren.register_sensor(var))
 | 
			
		||||
 
 | 
			
		||||
@@ -117,7 +117,7 @@ async def to_code(config):
 | 
			
		||||
                data[pos] = rgb & 255
 | 
			
		||||
                pos += 1
 | 
			
		||||
 | 
			
		||||
    elif config[CONF_TYPE] == "BINARY":
 | 
			
		||||
    elif config[CONF_TYPE] in ["BINARY", "TRANSPARENT_BINARY"]:
 | 
			
		||||
        width8 = ((width + 7) // 8) * 8
 | 
			
		||||
        data = [0 for _ in range((height * width8 // 8) * frames)]
 | 
			
		||||
        for frameIndex in range(frames):
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ void APDS9960::setup() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (id != 0xAB && id != 0x9C) {  // APDS9960 all should have one of these IDs
 | 
			
		||||
  if (id != 0xAB && id != 0x9C && id != 0xA8) {  // APDS9960 all should have one of these IDs
 | 
			
		||||
    this->error_code_ = WRONG_ID;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
 
 | 
			
		||||
@@ -206,6 +206,8 @@ message DeviceInfoResponse {
 | 
			
		||||
  uint32 bluetooth_proxy_version = 11;
 | 
			
		||||
 | 
			
		||||
  string manufacturer = 12;
 | 
			
		||||
 | 
			
		||||
  string friendly_name = 13;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ListEntitiesRequest {
 | 
			
		||||
@@ -785,6 +787,7 @@ enum ClimateFanMode {
 | 
			
		||||
  CLIMATE_FAN_MIDDLE = 6;
 | 
			
		||||
  CLIMATE_FAN_FOCUS = 7;
 | 
			
		||||
  CLIMATE_FAN_DIFFUSE = 8;
 | 
			
		||||
  CLIMATE_FAN_QUIET = 9;
 | 
			
		||||
}
 | 
			
		||||
enum ClimateSwingMode {
 | 
			
		||||
  CLIMATE_SWING_OFF = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -930,6 +930,7 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
 | 
			
		||||
  DeviceInfoResponse resp{};
 | 
			
		||||
  resp.uses_password = this->parent_->uses_password();
 | 
			
		||||
  resp.name = App.get_name();
 | 
			
		||||
  resp.friendly_name = App.get_friendly_name();
 | 
			
		||||
  resp.mac_address = get_mac_address_pretty();
 | 
			
		||||
  resp.esphome_version = ESPHOME_VERSION;
 | 
			
		||||
  resp.compilation_time = App.get_compilation_time();
 | 
			
		||||
 
 | 
			
		||||
@@ -616,6 +616,9 @@ APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
 | 
			
		||||
  struct iovec iov[2];
 | 
			
		||||
  iov[0].iov_base = header;
 | 
			
		||||
  iov[0].iov_len = 3;
 | 
			
		||||
  if (len == 0) {
 | 
			
		||||
    return write_raw_(iov, 1);
 | 
			
		||||
  }
 | 
			
		||||
  iov[1].iov_base = const_cast<uint8_t *>(data);
 | 
			
		||||
  iov[1].iov_len = len;
 | 
			
		||||
 | 
			
		||||
@@ -913,6 +916,9 @@ APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *pay
 | 
			
		||||
  struct iovec iov[2];
 | 
			
		||||
  iov[0].iov_base = &header[0];
 | 
			
		||||
  iov[0].iov_len = header.size();
 | 
			
		||||
  if (payload_len == 0) {
 | 
			
		||||
    return write_raw_(iov, 1);
 | 
			
		||||
  }
 | 
			
		||||
  iov[1].iov_base = const_cast<uint8_t *>(payload);
 | 
			
		||||
  iov[1].iov_len = payload_len;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -235,6 +235,8 @@ template<> const char *proto_enum_to_string<enums::ClimateFanMode>(enums::Climat
 | 
			
		||||
      return "CLIMATE_FAN_FOCUS";
 | 
			
		||||
    case enums::CLIMATE_FAN_DIFFUSE:
 | 
			
		||||
      return "CLIMATE_FAN_DIFFUSE";
 | 
			
		||||
    case enums::CLIMATE_FAN_QUIET:
 | 
			
		||||
      return "CLIMATE_FAN_QUIET";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
@@ -628,6 +630,10 @@ bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited v
 | 
			
		||||
      this->manufacturer = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 13: {
 | 
			
		||||
      this->friendly_name = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -645,6 +651,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_uint32(10, this->webserver_port);
 | 
			
		||||
  buffer.encode_uint32(11, this->bluetooth_proxy_version);
 | 
			
		||||
  buffer.encode_string(12, this->manufacturer);
 | 
			
		||||
  buffer.encode_string(13, this->friendly_name);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
			
		||||
@@ -699,6 +706,10 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  manufacturer: ");
 | 
			
		||||
  out.append("'").append(this->manufacturer).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  friendly_name: ");
 | 
			
		||||
  out.append("'").append(this->friendly_name).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -99,6 +99,7 @@ enum ClimateFanMode : uint32_t {
 | 
			
		||||
  CLIMATE_FAN_MIDDLE = 6,
 | 
			
		||||
  CLIMATE_FAN_FOCUS = 7,
 | 
			
		||||
  CLIMATE_FAN_DIFFUSE = 8,
 | 
			
		||||
  CLIMATE_FAN_QUIET = 9,
 | 
			
		||||
};
 | 
			
		||||
enum ClimateSwingMode : uint32_t {
 | 
			
		||||
  CLIMATE_SWING_OFF = 0,
 | 
			
		||||
@@ -276,6 +277,7 @@ class DeviceInfoResponse : public ProtoMessage {
 | 
			
		||||
  uint32_t webserver_port{0};
 | 
			
		||||
  uint32_t bluetooth_proxy_version{0};
 | 
			
		||||
  std::string manufacturer{};
 | 
			
		||||
  std::string friendly_name{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 
 | 
			
		||||
@@ -37,9 +37,13 @@ void BedJetFan::control(const fan::FanCall &call) {
 | 
			
		||||
 | 
			
		||||
  // ignore speed changes if not on or turning on
 | 
			
		||||
  if (this->state && call.get_speed().has_value()) {
 | 
			
		||||
    this->speed = *call.get_speed();
 | 
			
		||||
    this->parent_->set_fan_index(this->speed);
 | 
			
		||||
    did_change = true;
 | 
			
		||||
    auto speed = *call.get_speed();
 | 
			
		||||
    if (speed >= 1) {
 | 
			
		||||
      this->speed = speed;
 | 
			
		||||
      // Fan.speed is 1-20, but Bedjet expects 0-19, so subtract 1
 | 
			
		||||
      this->parent_->set_fan_index(this->speed - 1);
 | 
			
		||||
      did_change = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (did_change) {
 | 
			
		||||
@@ -57,8 +61,9 @@ void BedJetFan::on_status(const BedjetStatusPacket *data) {
 | 
			
		||||
    did_change = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (data->fan_step != this->speed) {
 | 
			
		||||
    this->speed = data->fan_step;
 | 
			
		||||
  // BedjetStatusPacket.fan_step is in range 0-19, but Fan.speed wants 1-20.
 | 
			
		||||
  if (data->fan_step + 1 != this->speed) {
 | 
			
		||||
    this->speed = data->fan_step + 1;
 | 
			
		||||
    did_change = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,11 +2,13 @@ from esphome.components import esp32_ble_tracker, esp32_ble_client
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.const import CONF_ACTIVE, CONF_ID
 | 
			
		||||
from esphome.components.esp32 import add_idf_sdkconfig_option
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"]
 | 
			
		||||
DEPENDENCIES = ["api", "esp32"]
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
 | 
			
		||||
CONF_CACHE_SERVICES = "cache_services"
 | 
			
		||||
CONF_CONNECTIONS = "connections"
 | 
			
		||||
MAX_CONNECTIONS = 3
 | 
			
		||||
 | 
			
		||||
@@ -47,6 +49,9 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BluetoothProxy),
 | 
			
		||||
            cv.Optional(CONF_ACTIVE, default=False): cv.boolean,
 | 
			
		||||
            cv.SplitDefault(CONF_CACHE_SERVICES, esp32_idf=True): cv.All(
 | 
			
		||||
                cv.only_with_esp_idf, cv.boolean
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_CONNECTIONS): cv.All(
 | 
			
		||||
                cv.ensure_list(CONNECTION_SCHEMA),
 | 
			
		||||
                cv.Length(min=1, max=MAX_CONNECTIONS),
 | 
			
		||||
@@ -72,4 +77,7 @@ async def to_code(config):
 | 
			
		||||
        cg.add(var.register_connection(connection_var))
 | 
			
		||||
        await esp32_ble_tracker.register_client(connection_var, connection_conf)
 | 
			
		||||
 | 
			
		||||
    if config.get(CONF_CACHE_SERVICES):
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_BT_GATTC_CACHE_NVS_FLASH", True)
 | 
			
		||||
 | 
			
		||||
    cg.add_define("USE_BLUETOOTH_PROXY")
 | 
			
		||||
 
 | 
			
		||||
@@ -88,7 +88,10 @@ void BME280Component::setup() {
 | 
			
		||||
 | 
			
		||||
  // Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
 | 
			
		||||
  // and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
 | 
			
		||||
  this->component_state_ &= ~COMPONENT_STATE_FAILED;
 | 
			
		||||
  if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
 | 
			
		||||
    this->component_state_ &= ~COMPONENT_STATE_MASK;
 | 
			
		||||
    this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) {
 | 
			
		||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "bmp280.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -11,8 +12,11 @@ static const uint8_t BMP280_REGISTER_CONTROL = 0xF4;
 | 
			
		||||
static const uint8_t BMP280_REGISTER_CONFIG = 0xF5;
 | 
			
		||||
static const uint8_t BMP280_REGISTER_PRESSUREDATA = 0xF7;
 | 
			
		||||
static const uint8_t BMP280_REGISTER_TEMPDATA = 0xFA;
 | 
			
		||||
static const uint8_t BMP280_REGISTER_RESET = 0xE0;
 | 
			
		||||
 | 
			
		||||
static const uint8_t BMP280_MODE_FORCED = 0b01;
 | 
			
		||||
static const uint8_t BMP280_SOFT_RESET = 0xB6;
 | 
			
		||||
static const uint8_t BMP280_STATUS_IM_UPDATE = 0b01;
 | 
			
		||||
 | 
			
		||||
inline uint16_t combine_bytes(uint8_t msb, uint8_t lsb) { return ((msb & 0xFF) << 8) | (lsb & 0xFF); }
 | 
			
		||||
 | 
			
		||||
@@ -66,6 +70,28 @@ void BMP280Component::setup() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Send a soft reset.
 | 
			
		||||
  if (!this->write_byte(BMP280_REGISTER_RESET, BMP280_SOFT_RESET)) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  // Wait until the NVM data has finished loading.
 | 
			
		||||
  uint8_t status;
 | 
			
		||||
  uint8_t retry = 5;
 | 
			
		||||
  do {
 | 
			
		||||
    delay(2);
 | 
			
		||||
    if (!this->read_byte(BMP280_REGISTER_STATUS, &status)) {
 | 
			
		||||
      ESP_LOGW(TAG, "Error reading status register.");
 | 
			
		||||
      this->mark_failed();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  } while ((status & BMP280_STATUS_IM_UPDATE) && (--retry));
 | 
			
		||||
  if (status & BMP280_STATUS_IM_UPDATE) {
 | 
			
		||||
    ESP_LOGW(TAG, "Timeout loading NVM.");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Read calibration
 | 
			
		||||
  this->calibration_.t1 = this->read_u16_le_(0x88);
 | 
			
		||||
  this->calibration_.t2 = this->read_s16_le_(0x8A);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								esphome/components/bp1658cj/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphome/components/bp1658cj/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CLOCK_PIN,
 | 
			
		||||
    CONF_DATA_PIN,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@Cossid"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
CONF_MAX_POWER_COLOR_CHANNELS = "max_power_color_channels"
 | 
			
		||||
CONF_MAX_POWER_WHITE_CHANNELS = "max_power_white_channels"
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["output"]
 | 
			
		||||
bp1658cj_ns = cg.esphome_ns.namespace("bp1658cj")
 | 
			
		||||
BP1658CJ = bp1658cj_ns.class_("BP1658CJ", cg.Component)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(BP1658CJ),
 | 
			
		||||
        cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
        cv.Optional(CONF_MAX_POWER_COLOR_CHANNELS, default=2): cv.int_range(
 | 
			
		||||
            min=0, max=15
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_MAX_POWER_WHITE_CHANNELS, default=4): cv.int_range(
 | 
			
		||||
            min=0, max=15
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    data = await cg.gpio_pin_expression(config[CONF_DATA_PIN])
 | 
			
		||||
    cg.add(var.set_data_pin(data))
 | 
			
		||||
    clock = await cg.gpio_pin_expression(config[CONF_CLOCK_PIN])
 | 
			
		||||
    cg.add(var.set_clock_pin(clock))
 | 
			
		||||
    cg.add(var.set_max_power_color_channels(config[CONF_MAX_POWER_COLOR_CHANNELS]))
 | 
			
		||||
    cg.add(var.set_max_power_white_channels(config[CONF_MAX_POWER_WHITE_CHANNELS]))
 | 
			
		||||
							
								
								
									
										110
									
								
								esphome/components/bp1658cj/bp1658cj.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								esphome/components/bp1658cj/bp1658cj.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
#include "bp1658cj.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bp1658cj {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "bp1658cj";
 | 
			
		||||
 | 
			
		||||
static const uint8_t BP1658CJ_MODEL_ID = 0x80;
 | 
			
		||||
static const uint8_t BP1658CJ_ADDR_STANDBY = 0x0;
 | 
			
		||||
static const uint8_t BP1658CJ_ADDR_START_3CH = 0x10;
 | 
			
		||||
static const uint8_t BP1658CJ_ADDR_START_2CH = 0x20;
 | 
			
		||||
static const uint8_t BP1658CJ_ADDR_START_5CH = 0x30;
 | 
			
		||||
 | 
			
		||||
void BP1658CJ::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up BP1658CJ Output Component...");
 | 
			
		||||
  this->data_pin_->setup();
 | 
			
		||||
  this->data_pin_->digital_write(false);
 | 
			
		||||
  this->clock_pin_->setup();
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  this->pwm_amounts_.resize(5, 0);
 | 
			
		||||
}
 | 
			
		||||
void BP1658CJ::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BP1658CJ:");
 | 
			
		||||
  LOG_PIN("  Data Pin: ", this->data_pin_);
 | 
			
		||||
  LOG_PIN("  Clock Pin: ", this->clock_pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Color Channels Max Power: %u", this->max_power_color_channels_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  White Channels Max Power: %u", this->max_power_white_channels_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP1658CJ::loop() {
 | 
			
		||||
  if (!this->update_)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  uint8_t data[12];
 | 
			
		||||
  if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
 | 
			
		||||
      this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) {
 | 
			
		||||
    // Off / Sleep
 | 
			
		||||
    data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_STANDBY;
 | 
			
		||||
    for (int i = 1; i < 12; i++)
 | 
			
		||||
      data[i] = 0;
 | 
			
		||||
    this->write_buffer_(data, 12);
 | 
			
		||||
  } else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
 | 
			
		||||
             (this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) {
 | 
			
		||||
    // Only data on white channels
 | 
			
		||||
    data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_START_2CH;
 | 
			
		||||
    data[1] = 0 << 4 | this->max_power_white_channels_;
 | 
			
		||||
    for (int i = 2, j = 0; i < 12; i += 2, j++) {
 | 
			
		||||
      data[i] = this->pwm_amounts_[j] & 0x1F;
 | 
			
		||||
      data[i + 1] = (this->pwm_amounts_[j] >> 5) & 0x1F;
 | 
			
		||||
    }
 | 
			
		||||
    this->write_buffer_(data, 12);
 | 
			
		||||
  } else if ((this->pwm_amounts_[0] > 0 || this->pwm_amounts_[1] > 0 || this->pwm_amounts_[2] > 0) &&
 | 
			
		||||
             this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) {
 | 
			
		||||
    // Only data on RGB channels
 | 
			
		||||
    data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_START_3CH;
 | 
			
		||||
    data[1] = this->max_power_color_channels_ << 4 | 0;
 | 
			
		||||
    for (int i = 2, j = 0; i < 12; i += 2, j++) {
 | 
			
		||||
      data[i] = this->pwm_amounts_[j] & 0x1F;
 | 
			
		||||
      data[i + 1] = (this->pwm_amounts_[j] >> 5) & 0x1F;
 | 
			
		||||
    }
 | 
			
		||||
    this->write_buffer_(data, 12);
 | 
			
		||||
  } else {
 | 
			
		||||
    // All channels
 | 
			
		||||
    data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_START_5CH;
 | 
			
		||||
    data[1] = this->max_power_color_channels_ << 4 | this->max_power_white_channels_;
 | 
			
		||||
    for (int i = 2, j = 0; i < 12; i += 2, j++) {
 | 
			
		||||
      data[i] = this->pwm_amounts_[j] & 0x1F;
 | 
			
		||||
      data[i + 1] = (this->pwm_amounts_[j] >> 5) & 0x1F;
 | 
			
		||||
    }
 | 
			
		||||
    this->write_buffer_(data, 12);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->update_ = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP1658CJ::set_channel_value_(uint8_t channel, uint16_t value) {
 | 
			
		||||
  if (this->pwm_amounts_[channel] != value) {
 | 
			
		||||
    this->update_ = true;
 | 
			
		||||
    this->update_channel_ = channel;
 | 
			
		||||
  }
 | 
			
		||||
  this->pwm_amounts_[channel] = value;
 | 
			
		||||
}
 | 
			
		||||
void BP1658CJ::write_bit_(bool value) {
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  this->data_pin_->digital_write(value);
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP1658CJ::write_byte_(uint8_t data) {
 | 
			
		||||
  for (uint8_t mask = 0x80; mask; mask >>= 1) {
 | 
			
		||||
    this->write_bit_(data & mask);
 | 
			
		||||
  }
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  this->data_pin_->digital_write(true);
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP1658CJ::write_buffer_(uint8_t *buffer, uint8_t size) {
 | 
			
		||||
  this->data_pin_->digital_write(false);
 | 
			
		||||
  for (uint32_t i = 0; i < size; i++) {
 | 
			
		||||
    this->write_byte_(buffer[i]);
 | 
			
		||||
  }
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
  this->data_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace bp1658cj
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										64
									
								
								esphome/components/bp1658cj/bp1658cj.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								esphome/components/bp1658cj/bp1658cj.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/components/output/float_output.h"
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bp1658cj {
 | 
			
		||||
 | 
			
		||||
class BP1658CJ : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  class Channel;
 | 
			
		||||
 | 
			
		||||
  void set_data_pin(GPIOPin *data_pin) { data_pin_ = data_pin; }
 | 
			
		||||
  void set_clock_pin(GPIOPin *clock_pin) { clock_pin_ = clock_pin; }
 | 
			
		||||
  void set_max_power_color_channels(uint8_t max_power_color_channels) {
 | 
			
		||||
    max_power_color_channels_ = max_power_color_channels;
 | 
			
		||||
  }
 | 
			
		||||
  void set_max_power_white_channels(uint8_t max_power_white_channels) {
 | 
			
		||||
    max_power_white_channels_ = max_power_white_channels;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::HARDWARE; }
 | 
			
		||||
 | 
			
		||||
  /// Send new values if they were updated.
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  class Channel : public output::FloatOutput {
 | 
			
		||||
   public:
 | 
			
		||||
    void set_parent(BP1658CJ *parent) { parent_ = parent; }
 | 
			
		||||
    void set_channel(uint8_t channel) { channel_ = channel; }
 | 
			
		||||
 | 
			
		||||
   protected:
 | 
			
		||||
    void write_state(float state) override {
 | 
			
		||||
      auto amount = static_cast<uint16_t>(state * 0x3FF);
 | 
			
		||||
      this->parent_->set_channel_value_(this->channel_, amount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BP1658CJ *parent_;
 | 
			
		||||
    uint8_t channel_;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void set_channel_value_(uint8_t channel, uint16_t value);
 | 
			
		||||
  void write_bit_(bool value);
 | 
			
		||||
  void write_byte_(uint8_t data);
 | 
			
		||||
  void write_buffer_(uint8_t *buffer, uint8_t size);
 | 
			
		||||
 | 
			
		||||
  GPIOPin *data_pin_;
 | 
			
		||||
  GPIOPin *clock_pin_;
 | 
			
		||||
  uint8_t max_power_color_channels_{4};
 | 
			
		||||
  uint8_t max_power_white_channels_{6};
 | 
			
		||||
  uint8_t update_channel_;
 | 
			
		||||
  std::vector<uint16_t> pwm_amounts_;
 | 
			
		||||
  bool update_{true};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace bp1658cj
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										27
									
								
								esphome/components/bp1658cj/output.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/bp1658cj/output.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import output
 | 
			
		||||
from esphome.const import CONF_CHANNEL, CONF_ID
 | 
			
		||||
from . import BP1658CJ
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["bp1658cj"]
 | 
			
		||||
 | 
			
		||||
Channel = BP1658CJ.class_("Channel", output.FloatOutput)
 | 
			
		||||
 | 
			
		||||
CONF_BP1658CJ_ID = "bp1658cj_id"
 | 
			
		||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_BP1658CJ_ID): cv.use_id(BP1658CJ),
 | 
			
		||||
        cv.Required(CONF_ID): cv.declare_id(Channel),
 | 
			
		||||
        cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=65535),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await output.register_output(var, config)
 | 
			
		||||
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_BP1658CJ_ID])
 | 
			
		||||
    cg.add(var.set_parent(parent))
 | 
			
		||||
    cg.add(var.set_channel(config[CONF_CHANNEL]))
 | 
			
		||||
							
								
								
									
										33
									
								
								esphome/components/bp5758d/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								esphome/components/bp5758d/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CLOCK_PIN,
 | 
			
		||||
    CONF_DATA_PIN,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@Cossid"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["output"]
 | 
			
		||||
bp5758d_ns = cg.esphome_ns.namespace("bp5758d")
 | 
			
		||||
BP5758D = bp5758d_ns.class_("BP5758D", cg.Component)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(BP5758D),
 | 
			
		||||
        cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    data = await cg.gpio_pin_expression(config[CONF_DATA_PIN])
 | 
			
		||||
    cg.add(var.set_data_pin(data))
 | 
			
		||||
    clock = await cg.gpio_pin_expression(config[CONF_CLOCK_PIN])
 | 
			
		||||
    cg.add(var.set_clock_pin(clock))
 | 
			
		||||
							
								
								
									
										147
									
								
								esphome/components/bp5758d/bp5758d.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								esphome/components/bp5758d/bp5758d.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,147 @@
 | 
			
		||||
#include "bp5758d.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bp5758d {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "bp5758d";
 | 
			
		||||
 | 
			
		||||
static const uint8_t BP5758D_MODEL_ID = 0b10000000;
 | 
			
		||||
static const uint8_t BP5758D_ADDR_STANDBY = 0b00000000;
 | 
			
		||||
// Note, channel start address seems ambiguous and mis-translated.
 | 
			
		||||
// Documentation states all are "invalid sleep"
 | 
			
		||||
// All 3 values appear to activate all 5 channels.  Using the mapping
 | 
			
		||||
// from BP1658CJ ordering since it won't break anything.
 | 
			
		||||
static const uint8_t BP5758D_ADDR_START_3CH = 0b00010000;
 | 
			
		||||
static const uint8_t BP5758D_ADDR_START_2CH = 0b00100000;
 | 
			
		||||
static const uint8_t BP5758D_ADDR_START_5CH = 0b00110000;
 | 
			
		||||
static const uint8_t BP5758D_ALL_DATA_CHANNEL_ENABLEMENT = 0b00011111;
 | 
			
		||||
 | 
			
		||||
void BP5758D::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up BP5758D Output Component...");
 | 
			
		||||
  this->data_pin_->setup();
 | 
			
		||||
  this->data_pin_->digital_write(false);
 | 
			
		||||
  this->clock_pin_->setup();
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  this->channel_current_.resize(5, 0);
 | 
			
		||||
  this->pwm_amounts_.resize(5, 0);
 | 
			
		||||
}
 | 
			
		||||
void BP5758D::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BP5758D:");
 | 
			
		||||
  LOG_PIN("  Data Pin: ", this->data_pin_);
 | 
			
		||||
  LOG_PIN("  Clock Pin: ", this->clock_pin_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP5758D::loop() {
 | 
			
		||||
  if (!this->update_)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  uint8_t data[17];
 | 
			
		||||
  if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
 | 
			
		||||
      this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) {
 | 
			
		||||
    // Off / Sleep
 | 
			
		||||
    data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_STANDBY;
 | 
			
		||||
    for (int i = 1; i < 16; i++)
 | 
			
		||||
      data[i] = 0;
 | 
			
		||||
    this->write_buffer_(data, 17);
 | 
			
		||||
  } else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
 | 
			
		||||
             (this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) {
 | 
			
		||||
    // Only data on white channels
 | 
			
		||||
    data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_2CH;
 | 
			
		||||
    data[1] = BP5758D_ALL_DATA_CHANNEL_ENABLEMENT;
 | 
			
		||||
    data[2] = 0;
 | 
			
		||||
    data[3] = 0;
 | 
			
		||||
    data[4] = 0;
 | 
			
		||||
    data[5] = this->pwm_amounts_[3] > 0 ? correct_current_level_bits_(this->channel_current_[3]) : 0;
 | 
			
		||||
    data[6] = this->pwm_amounts_[4] > 0 ? correct_current_level_bits_(this->channel_current_[4]) : 0;
 | 
			
		||||
    for (int i = 7, j = 0; i <= 15; i += 2, j++) {
 | 
			
		||||
      data[i] = this->pwm_amounts_[j] & 0x1F;
 | 
			
		||||
      data[i + 1] = (this->pwm_amounts_[j] >> 5) & 0x1F;
 | 
			
		||||
    }
 | 
			
		||||
    this->write_buffer_(data, 17);
 | 
			
		||||
  } else if ((this->pwm_amounts_[0] > 0 || this->pwm_amounts_[1] > 0 || this->pwm_amounts_[2] > 0) &&
 | 
			
		||||
             this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) {
 | 
			
		||||
    // Only data on RGB channels
 | 
			
		||||
    data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_3CH;
 | 
			
		||||
    data[1] = BP5758D_ALL_DATA_CHANNEL_ENABLEMENT;
 | 
			
		||||
    data[2] = this->pwm_amounts_[0] > 0 ? correct_current_level_bits_(this->channel_current_[0]) : 0;
 | 
			
		||||
    data[3] = this->pwm_amounts_[1] > 0 ? correct_current_level_bits_(this->channel_current_[1]) : 0;
 | 
			
		||||
    data[4] = this->pwm_amounts_[2] > 0 ? correct_current_level_bits_(this->channel_current_[2]) : 0;
 | 
			
		||||
    data[5] = 0;
 | 
			
		||||
    data[6] = 0;
 | 
			
		||||
    for (int i = 7, j = 0; i <= 15; i += 2, j++) {
 | 
			
		||||
      data[i] = this->pwm_amounts_[j] & 0x1F;
 | 
			
		||||
      data[i + 1] = (this->pwm_amounts_[j] >> 5) & 0x1F;
 | 
			
		||||
    }
 | 
			
		||||
    this->write_buffer_(data, 17);
 | 
			
		||||
  } else {
 | 
			
		||||
    // All channels
 | 
			
		||||
    data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_5CH;
 | 
			
		||||
    data[1] = BP5758D_ALL_DATA_CHANNEL_ENABLEMENT;
 | 
			
		||||
    data[2] = this->pwm_amounts_[0] > 0 ? correct_current_level_bits_(this->channel_current_[0]) : 0;
 | 
			
		||||
    data[3] = this->pwm_amounts_[1] > 0 ? correct_current_level_bits_(this->channel_current_[1]) : 0;
 | 
			
		||||
    data[4] = this->pwm_amounts_[2] > 0 ? correct_current_level_bits_(this->channel_current_[2]) : 0;
 | 
			
		||||
    data[5] = this->pwm_amounts_[3] > 0 ? correct_current_level_bits_(this->channel_current_[3]) : 0;
 | 
			
		||||
    data[6] = this->pwm_amounts_[4] > 0 ? correct_current_level_bits_(this->channel_current_[4]) : 0;
 | 
			
		||||
    for (int i = 7, j = 0; i <= 15; i += 2, j++) {
 | 
			
		||||
      data[i] = this->pwm_amounts_[j] & 0x1F;
 | 
			
		||||
      data[i + 1] = (this->pwm_amounts_[j] >> 5) & 0x1F;
 | 
			
		||||
    }
 | 
			
		||||
    this->write_buffer_(data, 17);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->update_ = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t BP5758D::correct_current_level_bits_(uint8_t current) {
 | 
			
		||||
  // Anything below 64 uses normal bitmapping.
 | 
			
		||||
  if (current < 64) {
 | 
			
		||||
    return current;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Anything above 63 needs to be offset by +34 because the driver remaps bit 7 (normally 64) to 30.
 | 
			
		||||
  // (no idea why(!) but it is documented)
 | 
			
		||||
  // Example:
 | 
			
		||||
  // integer 64 would normally put out 0b01000000 but here 0b01000000 = 30 whereas everything lower
 | 
			
		||||
  // is normal, so we add 34 to the integer where
 | 
			
		||||
  // integer 98 = 0b01100010 which is 30 (7th bit adjusted) + 34 (1st-6th bits).
 | 
			
		||||
  return current + 34;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP5758D::set_channel_value_(uint8_t channel, uint16_t value) {
 | 
			
		||||
  if (this->pwm_amounts_[channel] != value) {
 | 
			
		||||
    this->update_ = true;
 | 
			
		||||
    this->update_channel_ = channel;
 | 
			
		||||
  }
 | 
			
		||||
  this->pwm_amounts_[channel] = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP5758D::set_channel_current_(uint8_t channel, uint8_t current) { this->channel_current_[channel] = current; }
 | 
			
		||||
 | 
			
		||||
void BP5758D::write_bit_(bool value) {
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  this->data_pin_->digital_write(value);
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP5758D::write_byte_(uint8_t data) {
 | 
			
		||||
  for (uint8_t mask = 0x80; mask; mask >>= 1) {
 | 
			
		||||
    this->write_bit_(data & mask);
 | 
			
		||||
  }
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  this->data_pin_->digital_write(true);
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP5758D::write_buffer_(uint8_t *buffer, uint8_t size) {
 | 
			
		||||
  this->data_pin_->digital_write(false);
 | 
			
		||||
  for (uint32_t i = 0; i < size; i++) {
 | 
			
		||||
    this->write_byte_(buffer[i]);
 | 
			
		||||
  }
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
  this->data_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace bp5758d
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										64
									
								
								esphome/components/bp5758d/bp5758d.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								esphome/components/bp5758d/bp5758d.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/components/output/float_output.h"
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bp5758d {
 | 
			
		||||
 | 
			
		||||
class BP5758D : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  class Channel;
 | 
			
		||||
 | 
			
		||||
  void set_data_pin(GPIOPin *data_pin) { data_pin_ = data_pin; }
 | 
			
		||||
  void set_clock_pin(GPIOPin *clock_pin) { clock_pin_ = clock_pin; }
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::HARDWARE; }
 | 
			
		||||
 | 
			
		||||
  /// Send new values if they were updated.
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  class Channel : public output::FloatOutput {
 | 
			
		||||
   public:
 | 
			
		||||
    void set_parent(BP5758D *parent) { parent_ = parent; }
 | 
			
		||||
    void set_channel(uint8_t channel) { channel_ = channel; }
 | 
			
		||||
    void set_current(uint8_t current) { current_ = current; }
 | 
			
		||||
 | 
			
		||||
   protected:
 | 
			
		||||
    void write_state(float state) override {
 | 
			
		||||
      auto amount = static_cast<uint16_t>(state * 0x3FF);
 | 
			
		||||
      // We're enforcing channels start at 1 to mach OUT1-OUT5, we must adjust
 | 
			
		||||
      // to our 0-based array internally here by subtracting 1.
 | 
			
		||||
      this->parent_->set_channel_value_(this->channel_ - 1, amount);
 | 
			
		||||
      this->parent_->set_channel_current_(this->channel_ - 1, this->current_);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BP5758D *parent_;
 | 
			
		||||
    uint8_t channel_;
 | 
			
		||||
    uint8_t current_;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint8_t correct_current_level_bits_(uint8_t current);
 | 
			
		||||
  void set_channel_value_(uint8_t channel, uint16_t value);
 | 
			
		||||
  void set_channel_current_(uint8_t channel, uint8_t current);
 | 
			
		||||
  void write_bit_(bool value);
 | 
			
		||||
  void write_byte_(uint8_t data);
 | 
			
		||||
  void write_buffer_(uint8_t *buffer, uint8_t size);
 | 
			
		||||
 | 
			
		||||
  GPIOPin *data_pin_;
 | 
			
		||||
  GPIOPin *clock_pin_;
 | 
			
		||||
  uint8_t update_channel_;
 | 
			
		||||
  std::vector<uint8_t> channel_current_;
 | 
			
		||||
  std::vector<uint16_t> pwm_amounts_;
 | 
			
		||||
  bool update_{true};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace bp5758d
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										29
									
								
								esphome/components/bp5758d/output.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								esphome/components/bp5758d/output.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import output
 | 
			
		||||
from esphome.const import CONF_CHANNEL, CONF_ID, CONF_CURRENT
 | 
			
		||||
from . import BP5758D
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["bp5758d"]
 | 
			
		||||
 | 
			
		||||
Channel = BP5758D.class_("Channel", output.FloatOutput)
 | 
			
		||||
 | 
			
		||||
CONF_BP5758D_ID = "bp5758d_id"
 | 
			
		||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_BP5758D_ID): cv.use_id(BP5758D),
 | 
			
		||||
        cv.Required(CONF_ID): cv.declare_id(Channel),
 | 
			
		||||
        cv.Required(CONF_CHANNEL): cv.int_range(min=1, max=5),
 | 
			
		||||
        cv.Optional(CONF_CURRENT, default=10): cv.int_range(min=0, max=90),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await output.register_output(var, config)
 | 
			
		||||
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_BP5758D_ID])
 | 
			
		||||
    cg.add(var.set_parent(parent))
 | 
			
		||||
    cg.add(var.set_channel(config[CONF_CHANNEL]))
 | 
			
		||||
    cg.add(var.set_current(config[CONF_CURRENT]))
 | 
			
		||||
@@ -27,10 +27,10 @@ DEFAULT_DELAY = "2ms"
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(CD74HC4067Component),
 | 
			
		||||
        cv.Required(CONF_PIN_S0): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_PIN_S1): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_PIN_S2): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_PIN_S3): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_PIN_S0): pins.gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_PIN_S1): pins.gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_PIN_S2): pins.gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_PIN_S3): pins.gpio_output_pin_schema,
 | 
			
		||||
        cv.Optional(
 | 
			
		||||
            CONF_DELAY, default=DEFAULT_DELAY
 | 
			
		||||
        ): cv.positive_time_period_milliseconds,
 | 
			
		||||
 
 | 
			
		||||
@@ -19,22 +19,22 @@ class CD74HC4067Component : public Component {
 | 
			
		||||
  void activate_pin(uint8_t pin);
 | 
			
		||||
 | 
			
		||||
  /// set the pin connected to multiplexer control pin 0
 | 
			
		||||
  void set_pin_s0(InternalGPIOPin *pin) { this->pin_s0_ = pin; }
 | 
			
		||||
  void set_pin_s0(GPIOPin *pin) { this->pin_s0_ = pin; }
 | 
			
		||||
  /// set the pin connected to multiplexer control pin 1
 | 
			
		||||
  void set_pin_s1(InternalGPIOPin *pin) { this->pin_s1_ = pin; }
 | 
			
		||||
  void set_pin_s1(GPIOPin *pin) { this->pin_s1_ = pin; }
 | 
			
		||||
  /// set the pin connected to multiplexer control pin 2
 | 
			
		||||
  void set_pin_s2(InternalGPIOPin *pin) { this->pin_s2_ = pin; }
 | 
			
		||||
  void set_pin_s2(GPIOPin *pin) { this->pin_s2_ = pin; }
 | 
			
		||||
  /// set the pin connected to multiplexer control pin 3
 | 
			
		||||
  void set_pin_s3(InternalGPIOPin *pin) { this->pin_s3_ = pin; }
 | 
			
		||||
  void set_pin_s3(GPIOPin *pin) { this->pin_s3_ = pin; }
 | 
			
		||||
 | 
			
		||||
  /// set the delay needed after an input switch
 | 
			
		||||
  void set_switch_delay(uint32_t switch_delay) { this->switch_delay_ = switch_delay; }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  InternalGPIOPin *pin_s0_;
 | 
			
		||||
  InternalGPIOPin *pin_s1_;
 | 
			
		||||
  InternalGPIOPin *pin_s2_;
 | 
			
		||||
  InternalGPIOPin *pin_s3_;
 | 
			
		||||
  GPIOPin *pin_s0_;
 | 
			
		||||
  GPIOPin *pin_s1_;
 | 
			
		||||
  GPIOPin *pin_s2_;
 | 
			
		||||
  GPIOPin *pin_s3_;
 | 
			
		||||
  /// the currently active pin
 | 
			
		||||
  uint8_t active_pin_;
 | 
			
		||||
  uint32_t switch_delay_;
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,8 @@ from esphome.const import (
 | 
			
		||||
    CONF_MODE_STATE_TOPIC,
 | 
			
		||||
    CONF_ON_STATE,
 | 
			
		||||
    CONF_PRESET,
 | 
			
		||||
    CONF_PRESET_COMMAND_TOPIC,
 | 
			
		||||
    CONF_PRESET_STATE_TOPIC,
 | 
			
		||||
    CONF_SWING_MODE,
 | 
			
		||||
    CONF_SWING_MODE_COMMAND_TOPIC,
 | 
			
		||||
    CONF_SWING_MODE_STATE_TOPIC,
 | 
			
		||||
@@ -73,6 +75,7 @@ CLIMATE_FAN_MODES = {
 | 
			
		||||
    "MIDDLE": ClimateFanMode.CLIMATE_FAN_MIDDLE,
 | 
			
		||||
    "FOCUS": ClimateFanMode.CLIMATE_FAN_FOCUS,
 | 
			
		||||
    "DIFFUSE": ClimateFanMode.CLIMATE_FAN_DIFFUSE,
 | 
			
		||||
    "QUIET": ClimateFanMode.CLIMATE_FAN_QUIET,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True)
 | 
			
		||||
@@ -142,6 +145,12 @@ CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).
 | 
			
		||||
        cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_PRESET_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_PRESET_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
@@ -216,7 +225,12 @@ async def setup_climate_core_(var, 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(
 | 
			
		||||
 
 | 
			
		||||
@@ -174,6 +174,8 @@ ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
 | 
			
		||||
    this->set_fan_mode(CLIMATE_FAN_FOCUS);
 | 
			
		||||
  } else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) {
 | 
			
		||||
    this->set_fan_mode(CLIMATE_FAN_DIFFUSE);
 | 
			
		||||
  } else if (str_equals_case_insensitive(fan_mode, "QUIET")) {
 | 
			
		||||
    this->set_fan_mode(CLIMATE_FAN_QUIET);
 | 
			
		||||
  } else {
 | 
			
		||||
    if (this->parent_->get_traits().supports_custom_fan_mode(fan_mode)) {
 | 
			
		||||
      this->custom_fan_mode_ = fan_mode;
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,8 @@ const LogString *climate_fan_mode_to_string(ClimateFanMode fan_mode) {
 | 
			
		||||
      return LOG_STR("FOCUS");
 | 
			
		||||
    case climate::CLIMATE_FAN_DIFFUSE:
 | 
			
		||||
      return LOG_STR("DIFFUSE");
 | 
			
		||||
    case climate::CLIMATE_FAN_QUIET:
 | 
			
		||||
      return LOG_STR("QUIET");
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("UNKNOWN");
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,8 @@ enum ClimateFanMode : uint8_t {
 | 
			
		||||
  CLIMATE_FAN_FOCUS = 7,
 | 
			
		||||
  /// The fan mode is set to Diffuse
 | 
			
		||||
  CLIMATE_FAN_DIFFUSE = 8,
 | 
			
		||||
  /// The fan mode is set to Quiet
 | 
			
		||||
  CLIMATE_FAN_QUIET = 9,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Enum for all modes a climate swing can be in
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ namespace climate {
 | 
			
		||||
 *  - supports action - if the climate device supports reporting the active
 | 
			
		||||
 *    current action of the device with the action property.
 | 
			
		||||
 *  - supports fan modes - optionally, if it has a fan which can be configured in different ways:
 | 
			
		||||
 *    - on, off, auto, high, medium, low, middle, focus, diffuse
 | 
			
		||||
 *    - on, off, auto, high, medium, low, middle, focus, diffuse, quiet
 | 
			
		||||
 *  - supports swing modes - optionally, if it has a swing which can be configured in different ways:
 | 
			
		||||
 *    - off, both, vertical, horizontal
 | 
			
		||||
 *
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,18 @@
 | 
			
		||||
import base64
 | 
			
		||||
import secrets
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
import requests
 | 
			
		||||
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import git
 | 
			
		||||
from esphome.components.packages import validate_source_shorthand
 | 
			
		||||
from esphome.const import CONF_WIFI
 | 
			
		||||
from esphome.const import CONF_REF, CONF_WIFI
 | 
			
		||||
from esphome.wizard import wizard_file
 | 
			
		||||
from esphome.yaml_util import dump
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dashboard_import_ns = cg.esphome_ns.namespace("dashboard_import")
 | 
			
		||||
 | 
			
		||||
# payload is in `esphomelib` mdns record, which only exists if api
 | 
			
		||||
@@ -19,16 +24,32 @@ CODEOWNERS = ["@esphome/core"]
 | 
			
		||||
def validate_import_url(value):
 | 
			
		||||
    value = cv.string_strict(value)
 | 
			
		||||
    value = cv.Length(max=255)(value)
 | 
			
		||||
    # ignore result, only check if it's a valid shorthand
 | 
			
		||||
    validate_source_shorthand(value)
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_full_url(config):
 | 
			
		||||
    if not config[CONF_IMPORT_FULL_CONFIG]:
 | 
			
		||||
        return config
 | 
			
		||||
    source = validate_source_shorthand(config[CONF_PACKAGE_IMPORT_URL])
 | 
			
		||||
    if CONF_REF not in source:
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            "Must specify a ref (branch or tag) to import from when importing full config"
 | 
			
		||||
        )
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONF_PACKAGE_IMPORT_URL = "package_import_url"
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Required(CONF_PACKAGE_IMPORT_URL): validate_import_url,
 | 
			
		||||
    }
 | 
			
		||||
CONF_IMPORT_FULL_CONFIG = "import_full_config"
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_PACKAGE_IMPORT_URL): validate_import_url,
 | 
			
		||||
            cv.Optional(CONF_IMPORT_FULL_CONFIG, default=False): cv.boolean,
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
    validate_full_url,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
WIFI_CONFIG = """
 | 
			
		||||
@@ -41,11 +62,20 @@ wifi:
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    cg.add_define("USE_DASHBOARD_IMPORT")
 | 
			
		||||
    cg.add(dashboard_import_ns.set_package_import_url(config[CONF_PACKAGE_IMPORT_URL]))
 | 
			
		||||
    url = config[CONF_PACKAGE_IMPORT_URL]
 | 
			
		||||
    if config[CONF_IMPORT_FULL_CONFIG]:
 | 
			
		||||
        url += "?full_config"
 | 
			
		||||
    cg.add(dashboard_import_ns.set_package_import_url(url))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def import_config(
 | 
			
		||||
    path: str, name: str, project_name: str, import_url: str, network: str = CONF_WIFI
 | 
			
		||||
    path: str,
 | 
			
		||||
    name: str,
 | 
			
		||||
    friendly_name: Optional[str],
 | 
			
		||||
    project_name: str,
 | 
			
		||||
    import_url: str,
 | 
			
		||||
    network: str = CONF_WIFI,
 | 
			
		||||
    encryption: bool = False,
 | 
			
		||||
) -> None:
 | 
			
		||||
    p = Path(path)
 | 
			
		||||
 | 
			
		||||
@@ -53,28 +83,55 @@ def import_config(
 | 
			
		||||
        raise FileExistsError
 | 
			
		||||
 | 
			
		||||
    if project_name == "esphome.web":
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "name": name,
 | 
			
		||||
            "friendly_name": friendly_name,
 | 
			
		||||
            "platform": "ESP32" if "esp32" in import_url else "ESP8266",
 | 
			
		||||
            "board": "esp32dev" if "esp32" in import_url else "esp01_1m",
 | 
			
		||||
            "ssid": "!secret wifi_ssid",
 | 
			
		||||
            "psk": "!secret wifi_password",
 | 
			
		||||
        }
 | 
			
		||||
        if encryption:
 | 
			
		||||
            noise_psk = secrets.token_bytes(32)
 | 
			
		||||
            key = base64.b64encode(noise_psk).decode()
 | 
			
		||||
            kwargs["api_encryption_key"] = key
 | 
			
		||||
 | 
			
		||||
        p.write_text(
 | 
			
		||||
            wizard_file(
 | 
			
		||||
                name=name,
 | 
			
		||||
                platform="ESP32" if "esp32" in import_url else "ESP8266",
 | 
			
		||||
                board="esp32dev" if "esp32" in import_url else "esp01_1m",
 | 
			
		||||
                ssid="!secret wifi_ssid",
 | 
			
		||||
                psk="!secret wifi_password",
 | 
			
		||||
            ),
 | 
			
		||||
            wizard_file(**kwargs),
 | 
			
		||||
            encoding="utf8",
 | 
			
		||||
        )
 | 
			
		||||
    else:
 | 
			
		||||
        config = {
 | 
			
		||||
            "substitutions": {"name": name},
 | 
			
		||||
            "packages": {project_name: import_url},
 | 
			
		||||
            "esphome": {
 | 
			
		||||
                "name": "${name}",
 | 
			
		||||
                "name_add_mac_suffix": False,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
        output = dump(config)
 | 
			
		||||
        git_file = git.GitFile.from_shorthand(import_url)
 | 
			
		||||
 | 
			
		||||
        if network == CONF_WIFI:
 | 
			
		||||
            output += WIFI_CONFIG
 | 
			
		||||
        if git_file.query and "full_config" in git_file.query:
 | 
			
		||||
            url = git_file.raw_url
 | 
			
		||||
            try:
 | 
			
		||||
                req = requests.get(url, timeout=30)
 | 
			
		||||
                req.raise_for_status()
 | 
			
		||||
            except requests.exceptions.RequestException as e:
 | 
			
		||||
                raise ValueError(f"Error while fetching {url}: {e}") from e
 | 
			
		||||
 | 
			
		||||
        p.write_text(output, encoding="utf8")
 | 
			
		||||
            p.write_text(req.text, encoding="utf8")
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            substitutions = {"name": name}
 | 
			
		||||
            esphome_core = {"name": "${name}", "name_add_mac_suffix": False}
 | 
			
		||||
            if friendly_name:
 | 
			
		||||
                substitutions["friendly_name"] = friendly_name
 | 
			
		||||
                esphome_core["friendly_name"] = "${friendly_name}"
 | 
			
		||||
            config = {
 | 
			
		||||
                "substitutions": substitutions,
 | 
			
		||||
                "packages": {project_name: import_url},
 | 
			
		||||
                "esphome": esphome_core,
 | 
			
		||||
            }
 | 
			
		||||
            if encryption:
 | 
			
		||||
                noise_psk = secrets.token_bytes(32)
 | 
			
		||||
                key = base64.b64encode(noise_psk).decode()
 | 
			
		||||
                config["api"] = {"encryption": {"key": key}}
 | 
			
		||||
 | 
			
		||||
            output = dump(config)
 | 
			
		||||
 | 
			
		||||
            if network == CONF_WIFI:
 | 
			
		||||
                output += WIFI_CONFIG
 | 
			
		||||
 | 
			
		||||
            p.write_text(output, encoding="utf8")
 | 
			
		||||
 
 | 
			
		||||
@@ -147,7 +147,7 @@ void DeepSleepComponent::begin_sleep(bool manual) {
 | 
			
		||||
    if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
 | 
			
		||||
      level = !level;
 | 
			
		||||
    }
 | 
			
		||||
    esp_deep_sleep_enable_gpio_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()),
 | 
			
		||||
    esp_deep_sleep_enable_gpio_wakeup(1 << this->wakeup_pin_->get_pin(),
 | 
			
		||||
                                      static_cast<esp_deepsleep_gpio_wake_up_mode_t>(level));
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -111,6 +111,7 @@ class DemoClimate : public climate::Climate, public Component {
 | 
			
		||||
            climate::CLIMATE_FAN_MIDDLE,
 | 
			
		||||
            climate::CLIMATE_FAN_FOCUS,
 | 
			
		||||
            climate::CLIMATE_FAN_DIFFUSE,
 | 
			
		||||
            climate::CLIMATE_FAN_QUIET,
 | 
			
		||||
        });
 | 
			
		||||
        traits.set_supported_custom_fan_modes({"Auto Low", "Auto High"});
 | 
			
		||||
        traits.set_supported_swing_modes({
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,84 @@ static const char *const TAG = "display";
 | 
			
		||||
const Color COLOR_OFF(0, 0, 0, 0);
 | 
			
		||||
const Color COLOR_ON(255, 255, 255, 255);
 | 
			
		||||
 | 
			
		||||
void Rect::expand(int16_t horizontal, int16_t vertical) {
 | 
			
		||||
  if (this->is_set() && (this->w >= (-2 * horizontal)) && (this->h >= (-2 * vertical))) {
 | 
			
		||||
    this->x = this->x - horizontal;
 | 
			
		||||
    this->y = this->y - vertical;
 | 
			
		||||
    this->w = this->w + (2 * horizontal);
 | 
			
		||||
    this->h = this->h + (2 * vertical);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Rect::extend(Rect rect) {
 | 
			
		||||
  if (!this->is_set()) {
 | 
			
		||||
    this->x = rect.x;
 | 
			
		||||
    this->y = rect.y;
 | 
			
		||||
    this->w = rect.w;
 | 
			
		||||
    this->h = rect.h;
 | 
			
		||||
  } else {
 | 
			
		||||
    if (this->x > rect.x) {
 | 
			
		||||
      this->x = rect.x;
 | 
			
		||||
    }
 | 
			
		||||
    if (this->y > rect.y) {
 | 
			
		||||
      this->y = rect.y;
 | 
			
		||||
    }
 | 
			
		||||
    if (this->x2() < rect.x2()) {
 | 
			
		||||
      this->w = rect.x2() - this->x;
 | 
			
		||||
    }
 | 
			
		||||
    if (this->y2() < rect.y2()) {
 | 
			
		||||
      this->h = rect.y2() - this->y;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void Rect::shrink(Rect rect) {
 | 
			
		||||
  if (!this->inside(rect)) {
 | 
			
		||||
    (*this) = Rect();
 | 
			
		||||
  } else {
 | 
			
		||||
    if (this->x < rect.x) {
 | 
			
		||||
      this->x = rect.x;
 | 
			
		||||
    }
 | 
			
		||||
    if (this->y < rect.y) {
 | 
			
		||||
      this->y = rect.y;
 | 
			
		||||
    }
 | 
			
		||||
    if (this->x2() > rect.x2()) {
 | 
			
		||||
      this->w = rect.x2() - this->x;
 | 
			
		||||
    }
 | 
			
		||||
    if (this->y2() > rect.y2()) {
 | 
			
		||||
      this->h = rect.y2() - this->y;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Rect::inside(int16_t x, int16_t y, bool absolute) {  // NOLINT
 | 
			
		||||
  if (!this->is_set()) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  if (absolute) {
 | 
			
		||||
    return ((x >= 0) && (x <= this->w) && (y >= 0) && (y <= this->h));
 | 
			
		||||
  } else {
 | 
			
		||||
    return ((x >= this->x) && (x <= this->x2()) && (y >= this->y) && (y <= this->y2()));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Rect::inside(Rect rect, bool absolute) {
 | 
			
		||||
  if (!this->is_set() || !rect.is_set()) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  if (absolute) {
 | 
			
		||||
    return ((rect.x <= this->w) && (rect.w >= 0) && (rect.y <= this->h) && (rect.h >= 0));
 | 
			
		||||
  } else {
 | 
			
		||||
    return ((rect.x <= this->x2()) && (rect.x2() >= this->x) && (rect.y <= this->y2()) && (rect.y2() >= this->y));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Rect::info(const std::string &prefix) {
 | 
			
		||||
  if (this->is_set()) {
 | 
			
		||||
    ESP_LOGI(TAG, "%s [%3d,%3d,%3d,%3d]", prefix.c_str(), this->x, this->y, this->w, this->h);
 | 
			
		||||
  } else
 | 
			
		||||
    ESP_LOGI(TAG, "%s ** IS NOT SET **", prefix.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayBuffer::init_internal_(uint32_t buffer_length) {
 | 
			
		||||
  ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
 | 
			
		||||
  this->buffer_ = allocator.allocate(buffer_length);
 | 
			
		||||
@@ -24,6 +102,7 @@ void DisplayBuffer::init_internal_(uint32_t buffer_length) {
 | 
			
		||||
  }
 | 
			
		||||
  this->clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayBuffer::fill(Color color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); }
 | 
			
		||||
void DisplayBuffer::clear() { this->fill(COLOR_OFF); }
 | 
			
		||||
int DisplayBuffer::get_width() {
 | 
			
		||||
@@ -50,6 +129,9 @@ int DisplayBuffer::get_height() {
 | 
			
		||||
}
 | 
			
		||||
void DisplayBuffer::set_rotation(DisplayRotation rotation) { this->rotation_ = rotation; }
 | 
			
		||||
void HOT DisplayBuffer::draw_pixel_at(int x, int y, Color color) {
 | 
			
		||||
  if (!this->get_clipping().inside(x, y))
 | 
			
		||||
    return;  // NOLINT
 | 
			
		||||
 | 
			
		||||
  switch (this->rotation_) {
 | 
			
		||||
    case DISPLAY_ROTATION_0_DEGREES:
 | 
			
		||||
      break;
 | 
			
		||||
@@ -368,6 +450,10 @@ void DisplayBuffer::do_update_() {
 | 
			
		||||
  } else if (this->writer_.has_value()) {
 | 
			
		||||
    (*this->writer_)(*this);
 | 
			
		||||
  }
 | 
			
		||||
  // remove all not ended clipping regions
 | 
			
		||||
  while (is_clipping()) {
 | 
			
		||||
    end_clipping();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) {
 | 
			
		||||
  if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
 | 
			
		||||
@@ -392,6 +478,41 @@ void DisplayBuffer::strftime(int x, int y, Font *font, const char *format, time:
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void DisplayBuffer::start_clipping(Rect rect) {
 | 
			
		||||
  if (!this->clipping_rectangle_.empty()) {
 | 
			
		||||
    Rect r = this->clipping_rectangle_.back();
 | 
			
		||||
    rect.shrink(r);
 | 
			
		||||
  }
 | 
			
		||||
  this->clipping_rectangle_.push_back(rect);
 | 
			
		||||
}
 | 
			
		||||
void DisplayBuffer::end_clipping() {
 | 
			
		||||
  if (this->clipping_rectangle_.empty()) {
 | 
			
		||||
    ESP_LOGE(TAG, "clear: Clipping is not set.");
 | 
			
		||||
  } else {
 | 
			
		||||
    this->clipping_rectangle_.pop_back();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void DisplayBuffer::extend_clipping(Rect add_rect) {
 | 
			
		||||
  if (this->clipping_rectangle_.empty()) {
 | 
			
		||||
    ESP_LOGE(TAG, "add: Clipping is not set.");
 | 
			
		||||
  } else {
 | 
			
		||||
    this->clipping_rectangle_.back().extend(add_rect);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void DisplayBuffer::shrink_clipping(Rect add_rect) {
 | 
			
		||||
  if (this->clipping_rectangle_.empty()) {
 | 
			
		||||
    ESP_LOGE(TAG, "add: Clipping is not set.");
 | 
			
		||||
  } else {
 | 
			
		||||
    this->clipping_rectangle_.back().shrink(add_rect);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
Rect DisplayBuffer::get_clipping() {
 | 
			
		||||
  if (this->clipping_rectangle_.empty()) {
 | 
			
		||||
    return Rect();
 | 
			
		||||
  } else {
 | 
			
		||||
    return this->clipping_rectangle_.back();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool Glyph::get_pixel(int x, int y) const {
 | 
			
		||||
  const int x_data = x - this->glyph_data_->offset_x;
 | 
			
		||||
  const int y_data = y - this->glyph_data_->offset_y;
 | 
			
		||||
@@ -452,7 +573,7 @@ int Font::match_next_glyph(const char *str, int *match_length) {
 | 
			
		||||
}
 | 
			
		||||
void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) {
 | 
			
		||||
  *baseline = this->baseline_;
 | 
			
		||||
  *height = this->bottom_;
 | 
			
		||||
  *height = this->height_;
 | 
			
		||||
  int i = 0;
 | 
			
		||||
  int min_x = 0;
 | 
			
		||||
  bool has_char = false;
 | 
			
		||||
@@ -483,7 +604,7 @@ void Font::measure(const char *str, int *width, int *x_offset, int *baseline, in
 | 
			
		||||
  *width = x - min_x;
 | 
			
		||||
}
 | 
			
		||||
const std::vector<Glyph> &Font::get_glyphs() const { return this->glyphs_; }
 | 
			
		||||
Font::Font(const GlyphData *data, int data_nr, int baseline, int bottom) : baseline_(baseline), bottom_(bottom) {
 | 
			
		||||
Font::Font(const GlyphData *data, int data_nr, int baseline, int height) : baseline_(baseline), height_(height) {
 | 
			
		||||
  for (int i = 0; i < data_nr; ++i)
 | 
			
		||||
    glyphs_.emplace_back(data + i);
 | 
			
		||||
}
 | 
			
		||||
@@ -527,6 +648,7 @@ int Image::get_height() const { return this->height_; }
 | 
			
		||||
ImageType Image::get_type() const { return this->type_; }
 | 
			
		||||
Image::Image(const uint8_t *data_start, int width, int height, ImageType type)
 | 
			
		||||
    : width_(width), height_(height), type_(type), data_start_(data_start) {}
 | 
			
		||||
int Image::get_current_frame() const { return 0; }
 | 
			
		||||
 | 
			
		||||
bool Animation::get_pixel(int x, int y) const {
 | 
			
		||||
  if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "display_color_utils.h"
 | 
			
		||||
 | 
			
		||||
#include <cstdarg>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -100,6 +99,32 @@ enum DisplayRotation {
 | 
			
		||||
  DISPLAY_ROTATION_270_DEGREES = 270,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const int16_t VALUE_NO_SET = 32766;
 | 
			
		||||
 | 
			
		||||
class Rect {
 | 
			
		||||
 public:
 | 
			
		||||
  int16_t x;  ///< X coordinate of corner
 | 
			
		||||
  int16_t y;  ///< Y coordinate of corner
 | 
			
		||||
  int16_t w;  ///< Width of region
 | 
			
		||||
  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 int16_t x2() { return this->x + this->w; };  ///< X coordinate of corner
 | 
			
		||||
  inline int16_t y2() { return this->y + this->h; };  ///< Y coordinate of corner
 | 
			
		||||
 | 
			
		||||
  inline bool is_set() ALWAYS_INLINE { return (this->h != VALUE_NO_SET) && (this->w != VALUE_NO_SET); }
 | 
			
		||||
 | 
			
		||||
  void expand(int16_t horizontal, int16_t vertical);
 | 
			
		||||
 | 
			
		||||
  void extend(Rect rect);
 | 
			
		||||
  void shrink(Rect rect);
 | 
			
		||||
 | 
			
		||||
  bool inside(Rect rect, bool absolute = false);
 | 
			
		||||
  bool inside(int16_t x, int16_t y, bool absolute = false);
 | 
			
		||||
  void info(const std::string &prefix = "rect info:");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Font;
 | 
			
		||||
class Image;
 | 
			
		||||
class DisplayBuffer;
 | 
			
		||||
@@ -126,6 +151,7 @@ class DisplayBuffer {
 | 
			
		||||
  int get_width();
 | 
			
		||||
  /// Get the height of the image in pixels with rotation applied.
 | 
			
		||||
  int get_height();
 | 
			
		||||
 | 
			
		||||
  /// Set a single pixel at the specified coordinates to the given color.
 | 
			
		||||
  void draw_pixel_at(int x, int y, Color color = COLOR_ON);
 | 
			
		||||
 | 
			
		||||
@@ -374,6 +400,49 @@ class DisplayBuffer {
 | 
			
		||||
   */
 | 
			
		||||
  virtual DisplayType get_display_type() = 0;
 | 
			
		||||
 | 
			
		||||
  /** Set the clipping rectangle for further drawing
 | 
			
		||||
   *
 | 
			
		||||
   * @param[in]  rect:       Pointer to Rect for clipping (or NULL for entire screen)
 | 
			
		||||
   *
 | 
			
		||||
   * return true if success, false if error
 | 
			
		||||
   */
 | 
			
		||||
  void start_clipping(Rect rect);
 | 
			
		||||
  void start_clipping(int16_t left, int16_t top, int16_t right, int16_t bottom) {
 | 
			
		||||
    start_clipping(Rect(left, top, right - left, bottom - top));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /** Add a rectangular region to the invalidation region
 | 
			
		||||
   * - This is usually called when an element has been modified
 | 
			
		||||
   *
 | 
			
		||||
   * @param[in]  rect: Rectangle to add to the invalidation region
 | 
			
		||||
   */
 | 
			
		||||
  void extend_clipping(Rect rect);
 | 
			
		||||
  void extend_clipping(int16_t left, int16_t top, int16_t right, int16_t bottom) {
 | 
			
		||||
    this->extend_clipping(Rect(left, top, right - left, bottom - top));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /** substract a rectangular region to the invalidation region
 | 
			
		||||
   *  - This is usually called when an element has been modified
 | 
			
		||||
   *
 | 
			
		||||
   * @param[in]  rect: Rectangle to add to the invalidation region
 | 
			
		||||
   */
 | 
			
		||||
  void shrink_clipping(Rect rect);
 | 
			
		||||
  void shrink_clipping(uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {
 | 
			
		||||
    this->shrink_clipping(Rect(left, top, right - left, bottom - top));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /** Reset the invalidation region
 | 
			
		||||
   */
 | 
			
		||||
  void end_clipping();
 | 
			
		||||
 | 
			
		||||
  /** Get the current the clipping rectangle
 | 
			
		||||
   *
 | 
			
		||||
   * return rect for active clipping region
 | 
			
		||||
   */
 | 
			
		||||
  Rect get_clipping();
 | 
			
		||||
 | 
			
		||||
  bool is_clipping() const { return !this->clipping_rectangle_.empty(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg);
 | 
			
		||||
 | 
			
		||||
@@ -390,6 +459,7 @@ class DisplayBuffer {
 | 
			
		||||
  DisplayPage *previous_page_{nullptr};
 | 
			
		||||
  std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;
 | 
			
		||||
  bool auto_clear_enabled_{true};
 | 
			
		||||
  std::vector<Rect> clipping_rectangle_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DisplayPage {
 | 
			
		||||
@@ -448,18 +518,20 @@ class Font {
 | 
			
		||||
   * @param baseline The y-offset from the top of the text to the baseline.
 | 
			
		||||
   * @param bottom The y-offset from the top of the text to the bottom (i.e. height).
 | 
			
		||||
   */
 | 
			
		||||
  Font(const GlyphData *data, int data_nr, int baseline, int bottom);
 | 
			
		||||
  Font(const GlyphData *data, int data_nr, int baseline, int height);
 | 
			
		||||
 | 
			
		||||
  int match_next_glyph(const char *str, int *match_length);
 | 
			
		||||
 | 
			
		||||
  void measure(const char *str, int *width, int *x_offset, int *baseline, int *height);
 | 
			
		||||
  inline int get_baseline() { return this->baseline_; }
 | 
			
		||||
  inline int get_height() { return this->height_; }
 | 
			
		||||
 | 
			
		||||
  const std::vector<Glyph> &get_glyphs() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::vector<Glyph> glyphs_;
 | 
			
		||||
  int baseline_;
 | 
			
		||||
  int bottom_;
 | 
			
		||||
  int height_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Image {
 | 
			
		||||
@@ -473,6 +545,8 @@ class Image {
 | 
			
		||||
  int get_height() const;
 | 
			
		||||
  ImageType get_type() const;
 | 
			
		||||
 | 
			
		||||
  virtual int get_current_frame() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  int width_;
 | 
			
		||||
  int height_;
 | 
			
		||||
@@ -489,7 +563,7 @@ class Animation : public Image {
 | 
			
		||||
  Color get_grayscale_pixel(int x, int y) const override;
 | 
			
		||||
 | 
			
		||||
  int get_animation_frame_count() const;
 | 
			
		||||
  int get_current_frame() const;
 | 
			
		||||
  int get_current_frame() const override;
 | 
			
		||||
  void next_frame();
 | 
			
		||||
  void prev_frame();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_ON_VALUE,
 | 
			
		||||
    CONF_COMMAND,
 | 
			
		||||
    CONF_CUSTOM,
 | 
			
		||||
    CONF_NUMBER,
 | 
			
		||||
    CONF_FORMAT,
 | 
			
		||||
    CONF_MODE,
 | 
			
		||||
@@ -32,7 +33,6 @@ CONF_BACK = "back"
 | 
			
		||||
CONF_TEXT = "text"
 | 
			
		||||
CONF_SELECT = "select"
 | 
			
		||||
CONF_SWITCH = "switch"
 | 
			
		||||
CONF_CUSTOM = "custom"
 | 
			
		||||
CONF_ITEMS = "items"
 | 
			
		||||
CONF_ON_TEXT = "on_text"
 | 
			
		||||
CONF_OFF_TEXT = "off_text"
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,8 @@ from esphome.const import (
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@glmnet", "@zuidwijk"]
 | 
			
		||||
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["uart"]
 | 
			
		||||
AUTO_LOAD = ["sensor", "text_sensor"]
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/ee895/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/ee895/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										115
									
								
								esphome/components/ee895/ee895.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								esphome/components/ee895/ee895.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
			
		||||
#include "ee895.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ee895 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "ee895";
 | 
			
		||||
 | 
			
		||||
static const uint16_t CRC16_ONEWIRE_START = 0xFFFF;
 | 
			
		||||
static const uint8_t FUNCTION_CODE_READ = 0x03;
 | 
			
		||||
static const uint16_t SERIAL_NUMBER = 0x0000;
 | 
			
		||||
static const uint16_t TEMPERATURE_ADDRESS = 0x03EA;
 | 
			
		||||
static const uint16_t CO2_ADDRESS = 0x0424;
 | 
			
		||||
static const uint16_t PRESSURE_ADDRESS = 0x04B0;
 | 
			
		||||
 | 
			
		||||
void EE895Component::setup() {
 | 
			
		||||
  uint16_t crc16_check = 0;
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up EE895...");
 | 
			
		||||
  write_command_(SERIAL_NUMBER, 8);
 | 
			
		||||
  uint8_t serial_number[20];
 | 
			
		||||
  this->read(serial_number, 20);
 | 
			
		||||
 | 
			
		||||
  crc16_check = (serial_number[19] << 8) + serial_number[18];
 | 
			
		||||
  if (crc16_check != calc_crc16_(serial_number, 19)) {
 | 
			
		||||
    this->error_code_ = CRC_CHECK_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGV(TAG, "    Serial Number: 0x%s", format_hex(serial_number + 2, 16).c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EE895Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "EE895:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  switch (this->error_code_) {
 | 
			
		||||
    case COMMUNICATION_FAILED:
 | 
			
		||||
      ESP_LOGE(TAG, "Communication with EE895 failed!");
 | 
			
		||||
      break;
 | 
			
		||||
    case CRC_CHECK_FAILED:
 | 
			
		||||
      ESP_LOGE(TAG, "The crc check failed");
 | 
			
		||||
      break;
 | 
			
		||||
    case NONE:
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
  LOG_SENSOR("  ", "Temperature", this->temperature_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "CO2", this->co2_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Pressure", this->pressure_sensor_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float EE895Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
void EE895Component::update() {
 | 
			
		||||
  write_command_(TEMPERATURE_ADDRESS, 2);
 | 
			
		||||
  this->set_timeout(50, [this]() {
 | 
			
		||||
    float temperature = read_float_();
 | 
			
		||||
 | 
			
		||||
    write_command_(CO2_ADDRESS, 2);
 | 
			
		||||
    float co2 = read_float_();
 | 
			
		||||
 | 
			
		||||
    write_command_(PRESSURE_ADDRESS, 2);
 | 
			
		||||
    float pressure = read_float_();
 | 
			
		||||
    ESP_LOGD(TAG, "Got temperature=%.1f°C co2=%.0fppm pressure=%.1f%mbar", temperature, co2, pressure);
 | 
			
		||||
    if (this->temperature_sensor_ != nullptr)
 | 
			
		||||
      this->temperature_sensor_->publish_state(temperature);
 | 
			
		||||
    if (this->co2_sensor_ != nullptr)
 | 
			
		||||
      this->co2_sensor_->publish_state(co2);
 | 
			
		||||
    if (this->pressure_sensor_ != nullptr)
 | 
			
		||||
      this->pressure_sensor_->publish_state(pressure);
 | 
			
		||||
    this->status_clear_warning();
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EE895Component::write_command_(uint16_t addr, uint16_t reg_cnt) {
 | 
			
		||||
  uint8_t address[7];
 | 
			
		||||
  uint16_t crc16 = 0;
 | 
			
		||||
  address[0] = FUNCTION_CODE_READ;
 | 
			
		||||
  address[1] = (addr >> 8) & 0xFF;
 | 
			
		||||
  address[2] = addr & 0xFF;
 | 
			
		||||
  address[3] = (reg_cnt >> 8) & 0xFF;
 | 
			
		||||
  address[4] = reg_cnt & 0xFF;
 | 
			
		||||
  crc16 = calc_crc16_(address, 6);
 | 
			
		||||
  address[5] = crc16 & 0xFF;
 | 
			
		||||
  address[6] = (crc16 >> 8) & 0xFF;
 | 
			
		||||
  this->write(address, 7, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float EE895Component::read_float_() {
 | 
			
		||||
  uint16_t crc16_check = 0;
 | 
			
		||||
  uint8_t i2c_response[8];
 | 
			
		||||
  this->read(i2c_response, 8);
 | 
			
		||||
  crc16_check = (i2c_response[7] << 8) + i2c_response[6];
 | 
			
		||||
  if (crc16_check != calc_crc16_(i2c_response, 7)) {
 | 
			
		||||
    this->error_code_ = CRC_CHECK_FAILED;
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  uint32_t x = encode_uint32(i2c_response[4], i2c_response[5], i2c_response[2], i2c_response[3]);
 | 
			
		||||
  float value;
 | 
			
		||||
  memcpy(&value, &x, sizeof(value));  // convert uin32_t IEEE-754 format to float
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t EE895Component::calc_crc16_(const uint8_t buf[], uint8_t len) {
 | 
			
		||||
  uint8_t crc_check_buf[22];
 | 
			
		||||
  for (int i = 0; i < len; i++) {
 | 
			
		||||
    crc_check_buf[i + 1] = buf[i];
 | 
			
		||||
  }
 | 
			
		||||
  crc_check_buf[0] = this->address_;
 | 
			
		||||
  return crc16(crc_check_buf, len);
 | 
			
		||||
}
 | 
			
		||||
}  // namespace ee895
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										34
									
								
								esphome/components/ee895/ee895.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								esphome/components/ee895/ee895.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ee895 {
 | 
			
		||||
 | 
			
		||||
/// This class implements support for the ee895 of temperature i2c sensors.
 | 
			
		||||
class EE895Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_co2_sensor(sensor::Sensor *co2) { co2_sensor_ = co2; }
 | 
			
		||||
  void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
 | 
			
		||||
  void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
 | 
			
		||||
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void update() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void write_command_(uint16_t addr, uint16_t reg_cnt);
 | 
			
		||||
  float read_float_();
 | 
			
		||||
  uint16_t calc_crc16_(const uint8_t buf[], uint8_t len);
 | 
			
		||||
  sensor::Sensor *co2_sensor_;
 | 
			
		||||
  sensor::Sensor *temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *pressure_sensor_;
 | 
			
		||||
 | 
			
		||||
  enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, CRC_CHECK_FAILED } error_code_{NONE};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ee895
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										69
									
								
								esphome/components/ee895/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								esphome/components/ee895/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_PRESSURE,
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    CONF_CO2,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_PRESSURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_HECTOPASCAL,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    ICON_MOLECULE_CO2,
 | 
			
		||||
    UNIT_PARTS_PER_MILLION,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@Stock-M"]
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
ee895_ns = cg.esphome_ns.namespace("ee895")
 | 
			
		||||
EE895Component = ee895_ns.class_("EE895Component", cg.PollingComponent, i2c.I2CDevice)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(EE895Component),
 | 
			
		||||
            cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_CELSIUS,
 | 
			
		||||
                accuracy_decimals=1,
 | 
			
		||||
                device_class=DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Required(CONF_CO2): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_PARTS_PER_MILLION,
 | 
			
		||||
                icon=ICON_MOLECULE_CO2,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Required(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.polling_component_schema("60s"))
 | 
			
		||||
    .extend(i2c.i2c_device_schema(0x5F))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_CO2 in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_CO2])
 | 
			
		||||
        cg.add(var.set_co2_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_PRESSURE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_PRESSURE])
 | 
			
		||||
        cg.add(var.set_pressure_sensor(sens))
 | 
			
		||||
@@ -33,7 +33,7 @@ from .const import (  # noqa
 | 
			
		||||
    VARIANT_FRIENDLY,
 | 
			
		||||
    VARIANTS,
 | 
			
		||||
)
 | 
			
		||||
from .boards import BOARD_TO_VARIANT
 | 
			
		||||
from .boards import BOARDS
 | 
			
		||||
 | 
			
		||||
# force import gpio to register pin schema
 | 
			
		||||
from .gpio import esp32_pin_to_code  # noqa
 | 
			
		||||
@@ -230,14 +230,14 @@ def _parse_platform_version(value):
 | 
			
		||||
def _detect_variant(value):
 | 
			
		||||
    if CONF_VARIANT not in value:
 | 
			
		||||
        board = value[CONF_BOARD]
 | 
			
		||||
        if board not in BOARD_TO_VARIANT:
 | 
			
		||||
        if board not in BOARDS:
 | 
			
		||||
            raise cv.Invalid(
 | 
			
		||||
                "This board is unknown, please set the variant manually",
 | 
			
		||||
                path=[CONF_BOARD],
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        value = value.copy()
 | 
			
		||||
        value[CONF_VARIANT] = BOARD_TO_VARIANT[board]
 | 
			
		||||
        value[CONF_VARIANT] = BOARDS[board][KEY_VARIANT]
 | 
			
		||||
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
@@ -327,6 +327,11 @@ async def to_code(config):
 | 
			
		||||
            "platform_packages",
 | 
			
		||||
            [f"platformio/framework-espidf @ {conf[CONF_SOURCE]}"],
 | 
			
		||||
        )
 | 
			
		||||
        # platformio/toolchain-esp32ulp does not support linux_aarch64 yet and has not been updated for over 2 years
 | 
			
		||||
        # This is espressif's own published version which is more up to date.
 | 
			
		||||
        cg.add_platformio_option(
 | 
			
		||||
            "platform_packages", ["espressif/toolchain-esp32ulp @ 2.35.0-20220830"]
 | 
			
		||||
        )
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False)
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True)
 | 
			
		||||
        add_idf_sdkconfig_option(
 | 
			
		||||
@@ -351,9 +356,14 @@ async def to_code(config):
 | 
			
		||||
 | 
			
		||||
        if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_MAC_CRC]:
 | 
			
		||||
            cg.add_define("USE_ESP32_IGNORE_EFUSE_MAC_CRC")
 | 
			
		||||
            add_idf_sdkconfig_option(
 | 
			
		||||
                "CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE", False
 | 
			
		||||
            )
 | 
			
		||||
            if (framework_ver.major, framework_ver.minor) >= (4, 4):
 | 
			
		||||
                add_idf_sdkconfig_option(
 | 
			
		||||
                    "CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE", False
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                add_idf_sdkconfig_option(
 | 
			
		||||
                    "CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE", False
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        cg.add_define(
 | 
			
		||||
            "USE_ESP_IDF_VERSION_CODE",
 | 
			
		||||
@@ -393,11 +403,11 @@ spiffs,   data, spiffs,  0x391000, 0x00F000
 | 
			
		||||
 | 
			
		||||
IDF_PARTITIONS_CSV = """\
 | 
			
		||||
# Name,   Type, SubType, Offset,   Size, Flags
 | 
			
		||||
nvs,      data, nvs,     ,        0x4000,
 | 
			
		||||
otadata,  data, ota,     ,        0x2000,
 | 
			
		||||
phy_init, data, phy,     ,        0x1000,
 | 
			
		||||
app0,     app,  ota_0,   ,      0x1C0000,
 | 
			
		||||
app1,     app,  ota_1,   ,      0x1C0000,
 | 
			
		||||
nvs,      data, nvs,     ,       0x6d000,
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1066,197 +1066,738 @@ ESP32_BOARD_PINS = {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
BOARD_TO_VARIANT generated with:
 | 
			
		||||
BOARDS generated with:
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/platformio/platform-espressif32
 | 
			
		||||
for x in platform-espressif32/boards/*.json; do
 | 
			
		||||
  mcu=$(jq -r .build.mcu <"$x");
 | 
			
		||||
  name=$(jq -r .name <"$x");
 | 
			
		||||
  fname=$(basename "$x")
 | 
			
		||||
  board="${fname%.*}"
 | 
			
		||||
  variant=$(echo "$mcu" | tr '[:lower:]' '[:upper:]')
 | 
			
		||||
  echo "    \"$board\": VARIANT_${variant},"
 | 
			
		||||
  echo "    \"$board\": {\"name\": \"$name\", \"variant\": VARIANT_${variant},},"
 | 
			
		||||
done | sort
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
BOARD_TO_VARIANT = {
 | 
			
		||||
    "adafruit_feather_esp32s2_tft": VARIANT_ESP32S2,
 | 
			
		||||
    "adafruit_feather_esp32s3_nopsram": VARIANT_ESP32S3,
 | 
			
		||||
    "adafruit_feather_esp32s3_tft": VARIANT_ESP32S3,
 | 
			
		||||
    "adafruit_feather_esp32s3": VARIANT_ESP32S3,
 | 
			
		||||
    "adafruit_feather_esp32_v2": VARIANT_ESP32,
 | 
			
		||||
    "adafruit_funhouse_esp32s2": VARIANT_ESP32S2,
 | 
			
		||||
    "adafruit_itsybitsy_esp32": VARIANT_ESP32,
 | 
			
		||||
    "adafruit_magtag29_esp32s2": VARIANT_ESP32S2,
 | 
			
		||||
    "adafruit_metro_esp32s2": VARIANT_ESP32S2,
 | 
			
		||||
    "adafruit_qtpy_esp32c3": VARIANT_ESP32C3,
 | 
			
		||||
    "adafruit_qtpy_esp32s2": VARIANT_ESP32S2,
 | 
			
		||||
    "adafruit_qtpy_esp32s3_nopsram": VARIANT_ESP32S3,
 | 
			
		||||
    "adafruit_qtpy_esp32": VARIANT_ESP32,
 | 
			
		||||
    "airm2m_core_esp32c3": VARIANT_ESP32C3,
 | 
			
		||||
    "alksesp32": VARIANT_ESP32,
 | 
			
		||||
    "atmegazero_esp32s2": VARIANT_ESP32S2,
 | 
			
		||||
    "az-delivery-devkit-v4": VARIANT_ESP32,
 | 
			
		||||
    "bee_motion_mini": VARIANT_ESP32C3,
 | 
			
		||||
    "bee_motion_s3": VARIANT_ESP32S3,
 | 
			
		||||
    "bee_motion": VARIANT_ESP32S2,
 | 
			
		||||
    "bee_s3": VARIANT_ESP32S3,
 | 
			
		||||
    "bpi-bit": VARIANT_ESP32,
 | 
			
		||||
    "briki_abc_esp32": VARIANT_ESP32,
 | 
			
		||||
    "briki_mbc-wb_esp32": VARIANT_ESP32,
 | 
			
		||||
    "cnrs_aw2eth": VARIANT_ESP32,
 | 
			
		||||
    "connaxio_espoir": VARIANT_ESP32,
 | 
			
		||||
    "d-duino-32": VARIANT_ESP32,
 | 
			
		||||
    "deneyapkart1A": VARIANT_ESP32,
 | 
			
		||||
    "deneyapkartg": VARIANT_ESP32C3,
 | 
			
		||||
    "deneyapkart": VARIANT_ESP32,
 | 
			
		||||
    "deneyapmini": VARIANT_ESP32S2,
 | 
			
		||||
    "denky32": VARIANT_ESP32,
 | 
			
		||||
    "denky_d4": VARIANT_ESP32,
 | 
			
		||||
    "dfrobot_beetle_esp32c3": VARIANT_ESP32C3,
 | 
			
		||||
    "dfrobot_firebeetle2_esp32s3": VARIANT_ESP32S3,
 | 
			
		||||
    "dpu_esp32": VARIANT_ESP32,
 | 
			
		||||
    "esp320": VARIANT_ESP32,
 | 
			
		||||
    "esp32-c3-devkitm-1": VARIANT_ESP32C3,
 | 
			
		||||
    "esp32cam": VARIANT_ESP32,
 | 
			
		||||
    "esp32-devkitlipo": VARIANT_ESP32,
 | 
			
		||||
    "esp32dev": VARIANT_ESP32,
 | 
			
		||||
    "esp32doit-devkit-v1": VARIANT_ESP32,
 | 
			
		||||
    "esp32doit-espduino": VARIANT_ESP32,
 | 
			
		||||
    "esp32-evb": VARIANT_ESP32,
 | 
			
		||||
    "esp32-gateway": VARIANT_ESP32,
 | 
			
		||||
    "esp32-poe-iso": VARIANT_ESP32,
 | 
			
		||||
    "esp32-poe": VARIANT_ESP32,
 | 
			
		||||
    "esp32-pro": VARIANT_ESP32,
 | 
			
		||||
    "esp32-s2-franzininho": VARIANT_ESP32S2,
 | 
			
		||||
    "esp32-s2-kaluga-1": VARIANT_ESP32S2,
 | 
			
		||||
    "esp32-s2-saola-1": VARIANT_ESP32S2,
 | 
			
		||||
    "esp32s3box": VARIANT_ESP32S3,
 | 
			
		||||
    "esp32s3camlcd": VARIANT_ESP32S3,
 | 
			
		||||
    "esp32-s3-devkitc-1": VARIANT_ESP32S3,
 | 
			
		||||
    "esp32thing_plus": VARIANT_ESP32,
 | 
			
		||||
    "esp32thing": VARIANT_ESP32,
 | 
			
		||||
    "esp32vn-iot-uno": VARIANT_ESP32,
 | 
			
		||||
    "espea32": VARIANT_ESP32,
 | 
			
		||||
    "espectro32": VARIANT_ESP32,
 | 
			
		||||
    "espino32": VARIANT_ESP32,
 | 
			
		||||
    "esp-wrover-kit": VARIANT_ESP32,
 | 
			
		||||
    "etboard": VARIANT_ESP32,
 | 
			
		||||
    "featheresp32-s2": VARIANT_ESP32S2,
 | 
			
		||||
    "featheresp32": VARIANT_ESP32,
 | 
			
		||||
    "firebeetle32": VARIANT_ESP32,
 | 
			
		||||
    "fm-devkit": VARIANT_ESP32,
 | 
			
		||||
    "franzininho_wifi_esp32s2": VARIANT_ESP32S2,
 | 
			
		||||
    "franzininho_wifi_msc_esp32s2": VARIANT_ESP32S2,
 | 
			
		||||
    "frogboard": VARIANT_ESP32,
 | 
			
		||||
    "healthypi4": VARIANT_ESP32,
 | 
			
		||||
    "heltec_wifi_kit_32_v2": VARIANT_ESP32,
 | 
			
		||||
    "heltec_wifi_kit_32": VARIANT_ESP32,
 | 
			
		||||
    "heltec_wifi_lora_32_V2": VARIANT_ESP32,
 | 
			
		||||
    "heltec_wifi_lora_32": VARIANT_ESP32,
 | 
			
		||||
    "heltec_wireless_stick_lite": VARIANT_ESP32,
 | 
			
		||||
    "heltec_wireless_stick": VARIANT_ESP32,
 | 
			
		||||
    "honeylemon": VARIANT_ESP32,
 | 
			
		||||
    "hornbill32dev": VARIANT_ESP32,
 | 
			
		||||
    "hornbill32minima": VARIANT_ESP32,
 | 
			
		||||
    "imbrios-logsens-v1p1": VARIANT_ESP32,
 | 
			
		||||
    "inex_openkb": VARIANT_ESP32,
 | 
			
		||||
    "intorobot": VARIANT_ESP32,
 | 
			
		||||
    "iotaap_magnolia": VARIANT_ESP32,
 | 
			
		||||
    "iotbusio": VARIANT_ESP32,
 | 
			
		||||
    "iotbusproteus": VARIANT_ESP32,
 | 
			
		||||
    "kb32-ft": VARIANT_ESP32,
 | 
			
		||||
    "kits-edu": VARIANT_ESP32,
 | 
			
		||||
    "labplus_mpython": VARIANT_ESP32,
 | 
			
		||||
    "lionbit": VARIANT_ESP32,
 | 
			
		||||
    "lolin32_lite": VARIANT_ESP32,
 | 
			
		||||
    "lolin32": VARIANT_ESP32,
 | 
			
		||||
    "lolin_c3_mini": VARIANT_ESP32C3,
 | 
			
		||||
    "lolin_d32_pro": VARIANT_ESP32,
 | 
			
		||||
    "lolin_d32": VARIANT_ESP32,
 | 
			
		||||
    "lolin_s2_mini": VARIANT_ESP32S2,
 | 
			
		||||
    "lolin_s2_pico": VARIANT_ESP32S2,
 | 
			
		||||
    "lolin_s3": VARIANT_ESP32S3,
 | 
			
		||||
    "lopy4": VARIANT_ESP32,
 | 
			
		||||
    "lopy": VARIANT_ESP32,
 | 
			
		||||
    "m5stack-atom": VARIANT_ESP32,
 | 
			
		||||
    "m5stack-core2": VARIANT_ESP32,
 | 
			
		||||
    "m5stack-core-esp32": VARIANT_ESP32,
 | 
			
		||||
    "m5stack-coreink": VARIANT_ESP32,
 | 
			
		||||
    "m5stack-fire": VARIANT_ESP32,
 | 
			
		||||
    "m5stack-grey": VARIANT_ESP32,
 | 
			
		||||
    "m5stack-station": VARIANT_ESP32,
 | 
			
		||||
    "m5stack-timer-cam": VARIANT_ESP32,
 | 
			
		||||
    "m5stick-c": VARIANT_ESP32,
 | 
			
		||||
    "magicbit": VARIANT_ESP32,
 | 
			
		||||
    "mgbot-iotik32a": VARIANT_ESP32,
 | 
			
		||||
    "mgbot-iotik32b": VARIANT_ESP32,
 | 
			
		||||
    "mhetesp32devkit": VARIANT_ESP32,
 | 
			
		||||
    "mhetesp32minikit": VARIANT_ESP32,
 | 
			
		||||
    "microduino-core-esp32": VARIANT_ESP32,
 | 
			
		||||
    "micros2": VARIANT_ESP32S2,
 | 
			
		||||
    "minimain_esp32s2": VARIANT_ESP32S2,
 | 
			
		||||
    "nano32": VARIANT_ESP32,
 | 
			
		||||
    "nina_w10": VARIANT_ESP32,
 | 
			
		||||
    "node32s": VARIANT_ESP32,
 | 
			
		||||
    "nodemcu-32s2": VARIANT_ESP32S2,
 | 
			
		||||
    "nodemcu-32s": VARIANT_ESP32,
 | 
			
		||||
    "nscreen-32": VARIANT_ESP32,
 | 
			
		||||
    "odroid_esp32": VARIANT_ESP32,
 | 
			
		||||
    "onehorse32dev": VARIANT_ESP32,
 | 
			
		||||
    "oroca_edubot": VARIANT_ESP32,
 | 
			
		||||
    "pico32": VARIANT_ESP32,
 | 
			
		||||
    "piranha_esp32": VARIANT_ESP32,
 | 
			
		||||
    "pocket_32": VARIANT_ESP32,
 | 
			
		||||
    "pycom_gpy": VARIANT_ESP32,
 | 
			
		||||
    "qchip": VARIANT_ESP32,
 | 
			
		||||
    "quantum": VARIANT_ESP32,
 | 
			
		||||
    "seeed_xiao_esp32c3": VARIANT_ESP32C3,
 | 
			
		||||
    "sensesiot_weizen": VARIANT_ESP32,
 | 
			
		||||
    "sg-o_airMon": VARIANT_ESP32,
 | 
			
		||||
    "s_odi_ultra": VARIANT_ESP32,
 | 
			
		||||
    "sparkfun_esp32_iot_redboard": VARIANT_ESP32,
 | 
			
		||||
    "sparkfun_esp32micromod": VARIANT_ESP32,
 | 
			
		||||
    "sparkfun_esp32s2_thing_plus_c": VARIANT_ESP32,
 | 
			
		||||
    "sparkfun_esp32s2_thing_plus": VARIANT_ESP32S2,
 | 
			
		||||
    "sparkfun_lora_gateway_1-channel": VARIANT_ESP32,
 | 
			
		||||
    "tamc_termod_s3": VARIANT_ESP32S3,
 | 
			
		||||
    "tinypico": VARIANT_ESP32,
 | 
			
		||||
    "trueverit-iot-driver-mk2": VARIANT_ESP32,
 | 
			
		||||
    "trueverit-iot-driver-mk3": VARIANT_ESP32,
 | 
			
		||||
    "trueverit-iot-driver": VARIANT_ESP32,
 | 
			
		||||
    "ttgo-lora32-v1": VARIANT_ESP32,
 | 
			
		||||
    "ttgo-lora32-v21": VARIANT_ESP32,
 | 
			
		||||
    "ttgo-lora32-v2": VARIANT_ESP32,
 | 
			
		||||
    "ttgo-t1": VARIANT_ESP32,
 | 
			
		||||
    "ttgo-t7-v13-mini32": VARIANT_ESP32,
 | 
			
		||||
    "ttgo-t7-v14-mini32": VARIANT_ESP32,
 | 
			
		||||
    "ttgo-t-beam": VARIANT_ESP32,
 | 
			
		||||
    "ttgo-t-oi-plus": VARIANT_ESP32C3,
 | 
			
		||||
    "ttgo-t-watch": VARIANT_ESP32,
 | 
			
		||||
    "turta_iot_node": VARIANT_ESP32,
 | 
			
		||||
    "um_feathers2_neo": VARIANT_ESP32S2,
 | 
			
		||||
    "um_feathers2": VARIANT_ESP32S2,
 | 
			
		||||
    "um_feathers3": VARIANT_ESP32S3,
 | 
			
		||||
    "um_pros3": VARIANT_ESP32S3,
 | 
			
		||||
    "um_rmp": VARIANT_ESP32S2,
 | 
			
		||||
    "um_tinys2": VARIANT_ESP32S2,
 | 
			
		||||
    "um_tinys3": VARIANT_ESP32S3,
 | 
			
		||||
    "unphone7": VARIANT_ESP32,
 | 
			
		||||
    "unphone8": VARIANT_ESP32S3,
 | 
			
		||||
    "unphone9": VARIANT_ESP32S3,
 | 
			
		||||
    "upesy_wroom": VARIANT_ESP32,
 | 
			
		||||
    "upesy_wrover": VARIANT_ESP32,
 | 
			
		||||
    "vintlabs-devkit-v1": VARIANT_ESP32,
 | 
			
		||||
    "watchy": VARIANT_ESP32,
 | 
			
		||||
    "wemosbat": VARIANT_ESP32,
 | 
			
		||||
    "wemos_d1_mini32": VARIANT_ESP32,
 | 
			
		||||
    "wemos_d1_uno32": VARIANT_ESP32,
 | 
			
		||||
    "wesp32": VARIANT_ESP32,
 | 
			
		||||
    "widora-air": VARIANT_ESP32,
 | 
			
		||||
    "wifiduino32c3": VARIANT_ESP32C3,
 | 
			
		||||
    "wifiduino32s3": VARIANT_ESP32S3,
 | 
			
		||||
    "wifiduino32": VARIANT_ESP32,
 | 
			
		||||
    "wipy3": VARIANT_ESP32,
 | 
			
		||||
    "wt32-eth01": VARIANT_ESP32,
 | 
			
		||||
    "xinabox_cw02": VARIANT_ESP32,
 | 
			
		||||
BOARDS = {
 | 
			
		||||
    "adafruit_feather_esp32s2_tft": {
 | 
			
		||||
        "name": "Adafruit Feather ESP32-S2 TFT",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_feather_esp32s3": {
 | 
			
		||||
        "name": "Adafruit Feather ESP32-S3 2MB PSRAM",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_feather_esp32s3_nopsram": {
 | 
			
		||||
        "name": "Adafruit Feather ESP32-S3 No PSRAM",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_feather_esp32s3_tft": {
 | 
			
		||||
        "name": "Adafruit Feather ESP32-S3 TFT",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_feather_esp32_v2": {
 | 
			
		||||
        "name": "Adafruit Feather ESP32 V2",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_funhouse_esp32s2": {
 | 
			
		||||
        "name": "Adafruit FunHouse",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_itsybitsy_esp32": {
 | 
			
		||||
        "name": "Adafruit ItsyBitsy ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_magtag29_esp32s2": {
 | 
			
		||||
        "name": "Adafruit MagTag 2.9",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_metro_esp32s2": {
 | 
			
		||||
        "name": "Adafruit Metro ESP32-S2",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_qtpy_esp32c3": {
 | 
			
		||||
        "name": "Adafruit QT Py ESP32-C3",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_qtpy_esp32": {
 | 
			
		||||
        "name": "Adafruit QT Py ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_qtpy_esp32s2": {
 | 
			
		||||
        "name": "Adafruit QT Py ESP32-S2",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "adafruit_qtpy_esp32s3_nopsram": {
 | 
			
		||||
        "name": "Adafruit QT Py ESP32-S3 No PSRAM",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "airm2m_core_esp32c3": {
 | 
			
		||||
        "name": "AirM2M CORE ESP32C3",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "alksesp32": {
 | 
			
		||||
        "name": "ALKS ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "atmegazero_esp32s2": {
 | 
			
		||||
        "name": "EspinalLab ATMegaZero ESP32-S2",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "az-delivery-devkit-v4": {
 | 
			
		||||
        "name": "AZ-Delivery ESP-32 Dev Kit C V4",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "bee_motion_mini": {
 | 
			
		||||
        "name": "Smart Bee Motion Mini",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "bee_motion": {
 | 
			
		||||
        "name": "Smart Bee Motion",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "bee_motion_s3": {
 | 
			
		||||
        "name": "Smart Bee Motion S3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "bee_s3": {
 | 
			
		||||
        "name": "Smart Bee S3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "bpi-bit": {
 | 
			
		||||
        "name": "BPI-Bit",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "briki_abc_esp32": {
 | 
			
		||||
        "name": "Briki ABC (MBC-WB) - ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "briki_mbc-wb_esp32": {
 | 
			
		||||
        "name": "Briki MBC-WB - ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "cnrs_aw2eth": {
 | 
			
		||||
        "name": "CNRS AW2ETH",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "connaxio_espoir": {
 | 
			
		||||
        "name": "Connaxio's Espoir",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "d-duino-32": {
 | 
			
		||||
        "name": "D-duino-32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "deneyapkart1A": {
 | 
			
		||||
        "name": "Deneyap Kart 1A",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "deneyapkartg": {
 | 
			
		||||
        "name": "Deneyap Kart G",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "deneyapkart": {
 | 
			
		||||
        "name": "Deneyap Kart",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "deneyapmini": {
 | 
			
		||||
        "name": "Deneyap Mini",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "denky32": {
 | 
			
		||||
        "name": "Denky32 (WROOM32)",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "denky_d4": {
 | 
			
		||||
        "name": "Denky D4 (PICO-V3-02)",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "dfrobot_beetle_esp32c3": {
 | 
			
		||||
        "name": "DFRobot Beetle ESP32-C3",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "dfrobot_firebeetle2_esp32s3": {
 | 
			
		||||
        "name": "DFRobot Firebeetle 2 ESP32-S3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "dpu_esp32": {
 | 
			
		||||
        "name": "TAMC DPU ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp320": {
 | 
			
		||||
        "name": "Electronic SweetPeas ESP320",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-c3-devkitm-1": {
 | 
			
		||||
        "name": "Espressif ESP32-C3-DevKitM-1",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32cam": {
 | 
			
		||||
        "name": "AI Thinker ESP32-CAM",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-devkitlipo": {
 | 
			
		||||
        "name": "OLIMEX ESP32-DevKit-LiPo",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32dev": {
 | 
			
		||||
        "name": "Espressif ESP32 Dev Module",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32doit-devkit-v1": {
 | 
			
		||||
        "name": "DOIT ESP32 DEVKIT V1",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32doit-espduino": {
 | 
			
		||||
        "name": "DOIT ESPduino32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-evb": {
 | 
			
		||||
        "name": "OLIMEX ESP32-EVB",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-gateway": {
 | 
			
		||||
        "name": "OLIMEX ESP32-GATEWAY",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-poe-iso": {
 | 
			
		||||
        "name": "OLIMEX ESP32-PoE-ISO",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-poe": {
 | 
			
		||||
        "name": "OLIMEX ESP32-PoE",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-pro": {
 | 
			
		||||
        "name": "OLIMEX ESP32-PRO",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-s2-franzininho": {
 | 
			
		||||
        "name": "Franzininho WiFi Board",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-s2-kaluga-1": {
 | 
			
		||||
        "name": "Espressif ESP32-S2-Kaluga-1 Kit",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-s2-saola-1": {
 | 
			
		||||
        "name": "Espressif ESP32-S2-Saola-1",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32s3box": {
 | 
			
		||||
        "name": "Espressif ESP32-S3-Box",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32s3camlcd": {
 | 
			
		||||
        "name": "ESP32S3 CAM LCD",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32-s3-devkitc-1": {
 | 
			
		||||
        "name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32thing": {
 | 
			
		||||
        "name": "SparkFun ESP32 Thing",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32thing_plus": {
 | 
			
		||||
        "name": "SparkFun ESP32 Thing Plus",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp32vn-iot-uno": {
 | 
			
		||||
        "name": "ESP32vn IoT Uno",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "espea32": {
 | 
			
		||||
        "name": "April Brother ESPea32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "espectro32": {
 | 
			
		||||
        "name": "ESPectro32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "espino32": {
 | 
			
		||||
        "name": "ESPino32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "esp-wrover-kit": {
 | 
			
		||||
        "name": "Espressif ESP-WROVER-KIT",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "etboard": {
 | 
			
		||||
        "name": "ETBoard",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "featheresp32": {
 | 
			
		||||
        "name": "Adafruit ESP32 Feather",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "featheresp32-s2": {
 | 
			
		||||
        "name": "Adafruit ESP32-S2 Feather Development Board",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "firebeetle32": {
 | 
			
		||||
        "name": "FireBeetle-ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "fm-devkit": {
 | 
			
		||||
        "name": "ESP32 FM DevKit",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "franzininho_wifi_esp32s2": {
 | 
			
		||||
        "name": "Franzininho WiFi",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "franzininho_wifi_msc_esp32s2": {
 | 
			
		||||
        "name": "Franzininho WiFi MSC",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "frogboard": {
 | 
			
		||||
        "name": "Frog Board ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "healthypi4": {
 | 
			
		||||
        "name": "ProtoCentral HealthyPi 4",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "heltec_wifi_kit_32": {
 | 
			
		||||
        "name": "Heltec WiFi Kit 32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "heltec_wifi_kit_32_v2": {
 | 
			
		||||
        "name": "Heltec WiFi Kit 32 (V2)",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "heltec_wifi_lora_32": {
 | 
			
		||||
        "name": "Heltec WiFi LoRa 32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "heltec_wifi_lora_32_V2": {
 | 
			
		||||
        "name": "Heltec WiFi LoRa 32 (V2)",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "heltec_wireless_stick_lite": {
 | 
			
		||||
        "name": "Heltec Wireless Stick Lite",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "heltec_wireless_stick": {
 | 
			
		||||
        "name": "Heltec Wireless Stick",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "honeylemon": {
 | 
			
		||||
        "name": "HONEYLemon",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "hornbill32dev": {
 | 
			
		||||
        "name": "Hornbill ESP32 Dev",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "hornbill32minima": {
 | 
			
		||||
        "name": "Hornbill ESP32 Minima",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "imbrios-logsens-v1p1": {
 | 
			
		||||
        "name": "Imbrios LogSens V1P1",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "inex_openkb": {
 | 
			
		||||
        "name": "INEX OpenKB",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "intorobot": {
 | 
			
		||||
        "name": "IntoRobot Fig",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "iotaap_magnolia": {
 | 
			
		||||
        "name": "IoTaaP Magnolia",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "iotbusio": {
 | 
			
		||||
        "name": "oddWires IoT-Bus Io",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "iotbusproteus": {
 | 
			
		||||
        "name": "oddWires IoT-Bus Proteus",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "kb32-ft": {
 | 
			
		||||
        "name": "MakerAsia KB32-FT",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "kits-edu": {
 | 
			
		||||
        "name": "KITS ESP32 EDU",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "labplus_mpython": {
 | 
			
		||||
        "name": "Labplus mPython",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "lionbit": {
 | 
			
		||||
        "name": "Lion:Bit Dev Board",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "lolin32_lite": {
 | 
			
		||||
        "name": "WEMOS LOLIN32 Lite",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "lolin32": {
 | 
			
		||||
        "name": "WEMOS LOLIN32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "lolin_c3_mini": {
 | 
			
		||||
        "name": "WEMOS LOLIN C3 Mini",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "lolin_d32": {
 | 
			
		||||
        "name": "WEMOS LOLIN D32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "lolin_d32_pro": {
 | 
			
		||||
        "name": "WEMOS LOLIN D32 PRO",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "lolin_s2_mini": {
 | 
			
		||||
        "name": "WEMOS LOLIN S2 Mini",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "lolin_s2_pico": {
 | 
			
		||||
        "name": "WEMOS LOLIN S2 PICO",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "lolin_s3": {
 | 
			
		||||
        "name": "WEMOS LOLIN S3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "lopy4": {
 | 
			
		||||
        "name": "Pycom LoPy4",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "lopy": {
 | 
			
		||||
        "name": "Pycom LoPy",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "m5stack-atom": {
 | 
			
		||||
        "name": "M5Stack-ATOM",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "m5stack-core2": {
 | 
			
		||||
        "name": "M5Stack Core2",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "m5stack-core-esp32": {
 | 
			
		||||
        "name": "M5Stack Core ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "m5stack-coreink": {
 | 
			
		||||
        "name": "M5Stack-Core Ink",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "m5stack-fire": {
 | 
			
		||||
        "name": "M5Stack FIRE",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "m5stack-grey": {
 | 
			
		||||
        "name": "M5Stack GREY ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "m5stack-station": {
 | 
			
		||||
        "name": "M5Stack Station",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "m5stack-timer-cam": {
 | 
			
		||||
        "name": "M5Stack Timer CAM",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "m5stick-c": {
 | 
			
		||||
        "name": "M5Stick-C",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "magicbit": {
 | 
			
		||||
        "name": "MagicBit",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "mgbot-iotik32a": {
 | 
			
		||||
        "name": "MGBOT IOTIK 32A",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "mgbot-iotik32b": {
 | 
			
		||||
        "name": "MGBOT IOTIK 32B",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "mhetesp32devkit": {
 | 
			
		||||
        "name": "MH ET LIVE ESP32DevKIT",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "mhetesp32minikit": {
 | 
			
		||||
        "name": "MH ET LIVE ESP32MiniKit",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "microduino-core-esp32": {
 | 
			
		||||
        "name": "Microduino Core ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "micros2": {
 | 
			
		||||
        "name": "microS2",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "minimain_esp32s2": {
 | 
			
		||||
        "name": "Deparment of Alchemy MiniMain ESP32-S2",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "nano32": {
 | 
			
		||||
        "name": "MakerAsia Nano32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "nina_w10": {
 | 
			
		||||
        "name": "u-blox NINA-W10 series",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "node32s": {
 | 
			
		||||
        "name": "Node32s",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "nodemcu-32s2": {
 | 
			
		||||
        "name": "Ai-Thinker NodeMCU-32S2 (ESP-12K)",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "nodemcu-32s": {
 | 
			
		||||
        "name": "NodeMCU-32S",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "nscreen-32": {
 | 
			
		||||
        "name": "YeaCreate NSCREEN-32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "odroid_esp32": {
 | 
			
		||||
        "name": "ODROID-GO",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "onehorse32dev": {
 | 
			
		||||
        "name": "Onehorse ESP32 Dev Module",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "oroca_edubot": {
 | 
			
		||||
        "name": "OROCA EduBot",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "pico32": {
 | 
			
		||||
        "name": "ESP32 Pico Kit",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "piranha_esp32": {
 | 
			
		||||
        "name": "Fishino Piranha ESP-32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "pocket_32": {
 | 
			
		||||
        "name": "Dongsen Tech Pocket 32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "pycom_gpy": {
 | 
			
		||||
        "name": "Pycom GPy",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "qchip": {
 | 
			
		||||
        "name": "Qchip",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "quantum": {
 | 
			
		||||
        "name": "Noduino Quantum",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "seeed_xiao_esp32c3": {
 | 
			
		||||
        "name": "Seeed Studio XIAO ESP32C3",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "sensesiot_weizen": {
 | 
			
		||||
        "name": "LOGISENSES Senses Weizen",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "sg-o_airMon": {
 | 
			
		||||
        "name": "SG-O AirMon",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "s_odi_ultra": {
 | 
			
		||||
        "name": "S.ODI Ultra v1",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "sparkfun_esp32_iot_redboard": {
 | 
			
		||||
        "name": "SparkFun ESP32 IoT RedBoard",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "sparkfun_esp32micromod": {
 | 
			
		||||
        "name": "SparkFun ESP32 MicroMod",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "sparkfun_esp32s2_thing_plus_c": {
 | 
			
		||||
        "name": "SparkFun ESP32 Thing Plus C",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "sparkfun_esp32s2_thing_plus": {
 | 
			
		||||
        "name": "SparkFun ESP32-S2 Thing Plus",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "sparkfun_lora_gateway_1-channel": {
 | 
			
		||||
        "name": "SparkFun LoRa Gateway 1-Channel",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "tamc_termod_s3": {
 | 
			
		||||
        "name": "TAMC Termod S3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "tinypico": {
 | 
			
		||||
        "name": "Unexpected Maker TinyPICO",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "trueverit-iot-driver-mk2": {
 | 
			
		||||
        "name": "Trueverit ESP32 Universal IoT Driver MK II",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "trueverit-iot-driver-mk3": {
 | 
			
		||||
        "name": "Trueverit ESP32 Universal IoT Driver MK III",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "trueverit-iot-driver": {
 | 
			
		||||
        "name": "Trueverit ESP32 Universal IoT Driver",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "ttgo-lora32-v1": {
 | 
			
		||||
        "name": "TTGO LoRa32-OLED V1",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "ttgo-lora32-v21": {
 | 
			
		||||
        "name": "TTGO LoRa32-OLED v2.1.6",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "ttgo-lora32-v2": {
 | 
			
		||||
        "name": "TTGO LoRa32-OLED V2",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "ttgo-t1": {
 | 
			
		||||
        "name": "TTGO T1",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "ttgo-t7-v13-mini32": {
 | 
			
		||||
        "name": "TTGO T7 V1.3 Mini32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "ttgo-t7-v14-mini32": {
 | 
			
		||||
        "name": "TTGO T7 V1.4 Mini32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "ttgo-t-beam": {
 | 
			
		||||
        "name": "TTGO T-Beam",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "ttgo-t-oi-plus": {
 | 
			
		||||
        "name": "TTGO T-OI PLUS RISC-V ESP32-C3",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "ttgo-t-watch": {
 | 
			
		||||
        "name": "TTGO T-Watch",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "turta_iot_node": {
 | 
			
		||||
        "name": "Turta IoT Node",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "um_feathers2": {
 | 
			
		||||
        "name": "Unexpected Maker FeatherS2",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "um_feathers2_neo": {
 | 
			
		||||
        "name": "Unexpected Maker FeatherS2 Neo",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "um_feathers3": {
 | 
			
		||||
        "name": "Unexpected Maker FeatherS3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "um_pros3": {
 | 
			
		||||
        "name": "Unexpected Maker PROS3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "um_rmp": {
 | 
			
		||||
        "name": "Unexpected Maker RMP",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "um_tinys2": {
 | 
			
		||||
        "name": "Unexpected Maker TinyS2",
 | 
			
		||||
        "variant": VARIANT_ESP32S2,
 | 
			
		||||
    },
 | 
			
		||||
    "um_tinys3": {
 | 
			
		||||
        "name": "Unexpected Maker TinyS3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "unphone7": {
 | 
			
		||||
        "name": "unPhone 7",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "unphone8": {
 | 
			
		||||
        "name": "unPhone 8",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "unphone9": {
 | 
			
		||||
        "name": "unPhone 9",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "upesy_wroom": {
 | 
			
		||||
        "name": "uPesy ESP32 Wroom DevKit",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "upesy_wrover": {
 | 
			
		||||
        "name": "uPesy ESP32 Wrover DevKit",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "vintlabs-devkit-v1": {
 | 
			
		||||
        "name": "VintLabs ESP32 Devkit",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "watchy": {
 | 
			
		||||
        "name": "SQFMI Watchy v2.0",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "wemosbat": {
 | 
			
		||||
        "name": "WeMos WiFi and Bluetooth Battery",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "wemos_d1_mini32": {
 | 
			
		||||
        "name": "WEMOS D1 MINI ESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "wemos_d1_uno32": {
 | 
			
		||||
        "name": "WEMOS D1 R32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "wesp32": {
 | 
			
		||||
        "name": "Silicognition wESP32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "widora-air": {
 | 
			
		||||
        "name": "Widora AIR",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "wifiduino32c3": {
 | 
			
		||||
        "name": "Blinker WiFiduinoV2 (ESP32-C3)",
 | 
			
		||||
        "variant": VARIANT_ESP32C3,
 | 
			
		||||
    },
 | 
			
		||||
    "wifiduino32": {
 | 
			
		||||
        "name": "Blinker WiFiduino32",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "wifiduino32s3": {
 | 
			
		||||
        "name": "Blinker WiFiduino32S3",
 | 
			
		||||
        "variant": VARIANT_ESP32S3,
 | 
			
		||||
    },
 | 
			
		||||
    "wipy3": {
 | 
			
		||||
        "name": "Pycom WiPy3",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "wt32-eth01": {
 | 
			
		||||
        "name": "Wireless-Tag WT32-ETH01 Ethernet Module",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
    "xinabox_cw02": {
 | 
			
		||||
        "name": "XinaBox CW02",
 | 
			
		||||
        "variant": VARIANT_ESP32,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -95,7 +95,7 @@ void ESP32InternalGPIOPin::pin_mode(gpio::Flags flags) {
 | 
			
		||||
  // can't call gpio_config here because that logs in esp-idf which may cause issues
 | 
			
		||||
  gpio_set_direction(pin_, flags_to_mode(flags));
 | 
			
		||||
  gpio_pull_mode_t pull_mode = GPIO_FLOATING;
 | 
			
		||||
  if (flags & (gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN)) {
 | 
			
		||||
  if ((flags & gpio::FLAG_PULLUP) && (flags & gpio::FLAG_PULLDOWN)) {
 | 
			
		||||
    pull_mode = GPIO_PULLUP_PULLDOWN;
 | 
			
		||||
  } else if (flags & gpio::FLAG_PULLUP) {
 | 
			
		||||
    pull_mode = GPIO_PULLUP_ONLY;
 | 
			
		||||
@@ -128,7 +128,7 @@ void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) {
 | 
			
		||||
  auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
 | 
			
		||||
  gpio_set_direction(arg->pin, flags_to_mode(flags));
 | 
			
		||||
  gpio_pull_mode_t pull_mode = GPIO_FLOATING;
 | 
			
		||||
  if (flags & (gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN)) {
 | 
			
		||||
  if ((flags & gpio::FLAG_PULLUP) && (flags & gpio::FLAG_PULLDOWN)) {
 | 
			
		||||
    pull_mode = GPIO_PULLUP_PULLDOWN;
 | 
			
		||||
  } else if (flags & gpio::FLAG_PULLUP) {
 | 
			
		||||
    pull_mode = GPIO_PULLUP_ONLY;
 | 
			
		||||
 
 | 
			
		||||
@@ -123,11 +123,8 @@ def validate_gpio_pin(value):
 | 
			
		||||
 | 
			
		||||
def validate_supports(value):
 | 
			
		||||
    mode = value[CONF_MODE]
 | 
			
		||||
    is_input = mode[CONF_INPUT]
 | 
			
		||||
    is_output = mode[CONF_OUTPUT]
 | 
			
		||||
    is_open_drain = mode[CONF_OPEN_DRAIN]
 | 
			
		||||
    is_pullup = mode[CONF_PULLUP]
 | 
			
		||||
    is_pulldown = mode[CONF_PULLDOWN]
 | 
			
		||||
    variant = CORE.data[KEY_ESP32][KEY_VARIANT]
 | 
			
		||||
    if variant not in _esp32_validations:
 | 
			
		||||
        raise cv.Invalid(f"Unsupported ESP32 variant {variant}")
 | 
			
		||||
@@ -138,26 +135,6 @@ def validate_supports(value):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    value = _esp32_validations[variant].usage_validation(value)
 | 
			
		||||
    if CORE.using_arduino:
 | 
			
		||||
        # (input, output, open_drain, pullup, pulldown)
 | 
			
		||||
        supported_modes = {
 | 
			
		||||
            # INPUT
 | 
			
		||||
            (True, False, False, False, False),
 | 
			
		||||
            # OUTPUT
 | 
			
		||||
            (False, True, False, False, False),
 | 
			
		||||
            # INPUT_PULLUP
 | 
			
		||||
            (True, False, False, True, False),
 | 
			
		||||
            # INPUT_PULLDOWN
 | 
			
		||||
            (True, False, False, False, True),
 | 
			
		||||
            # OUTPUT_OPEN_DRAIN
 | 
			
		||||
            (False, True, True, False, False),
 | 
			
		||||
        }
 | 
			
		||||
        key = (is_input, is_output, is_open_drain, is_pullup, is_pulldown)
 | 
			
		||||
        if key not in supported_modes:
 | 
			
		||||
            raise cv.Invalid(
 | 
			
		||||
                "This pin mode is not supported on ESP32 for arduino frameworks",
 | 
			
		||||
                [CONF_MODE],
 | 
			
		||||
            )
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,25 @@ import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
from esphome.components.esp32 import add_idf_sdkconfig_option
 | 
			
		||||
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["esp32"]
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
CONFLICTS_WITH = ["esp32_ble_tracker", "esp32_ble_beacon"]
 | 
			
		||||
CONFLICTS_WITH = ["esp32_ble_beacon"]
 | 
			
		||||
 | 
			
		||||
CONF_BLE_ID = "ble_id"
 | 
			
		||||
 | 
			
		||||
NO_BLUTOOTH_VARIANTS = [const.VARIANT_ESP32S2]
 | 
			
		||||
 | 
			
		||||
NO_BLUTOOTH_VARIANTS = [const.VARIANT_ESP32S2]
 | 
			
		||||
 | 
			
		||||
esp32_ble_ns = cg.esphome_ns.namespace("esp32_ble")
 | 
			
		||||
ESP32BLE = esp32_ble_ns.class_("ESP32BLE", cg.Component)
 | 
			
		||||
 | 
			
		||||
GAPEventHandler = esp32_ble_ns.class_("GAPEventHandler")
 | 
			
		||||
GATTcEventHandler = esp32_ble_ns.class_("GATTcEventHandler")
 | 
			
		||||
GATTsEventHandler = esp32_ble_ns.class_("GATTsEventHandler")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
@@ -19,6 +29,15 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_variant(_):
 | 
			
		||||
    variant = get_esp32_variant()
 | 
			
		||||
    if variant in NO_BLUTOOTH_VARIANTS:
 | 
			
		||||
        raise cv.Invalid(f"{variant} does not support Bluetooth")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = validate_variant
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,13 @@
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#include <nvs_flash.h>
 | 
			
		||||
#include <freertos/FreeRTOSConfig.h>
 | 
			
		||||
#include <esp_bt_main.h>
 | 
			
		||||
#include <esp_bt.h>
 | 
			
		||||
#include <freertos/FreeRTOS.h>
 | 
			
		||||
#include <freertos/task.h>
 | 
			
		||||
#include <esp_bt_main.h>
 | 
			
		||||
#include <esp_gap_ble_api.h>
 | 
			
		||||
#include <freertos/FreeRTOS.h>
 | 
			
		||||
#include <freertos/FreeRTOSConfig.h>
 | 
			
		||||
#include <freertos/task.h>
 | 
			
		||||
#include <nvs_flash.h>
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
#include <esp32-hal-bt.h>
 | 
			
		||||
@@ -31,24 +31,17 @@ void ESP32BLE::setup() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_BLE_SERVER
 | 
			
		||||
  this->advertising_ = new BLEAdvertising();  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
 | 
			
		||||
  this->advertising_->set_scan_response(true);
 | 
			
		||||
  this->advertising_->set_min_preferred_interval(0x06);
 | 
			
		||||
  this->advertising_->start();
 | 
			
		||||
#endif  // USE_ESP32_BLE_SERVER
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "BLE setup complete");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32BLE::mark_failed() {
 | 
			
		||||
  Component::mark_failed();
 | 
			
		||||
#ifdef USE_ESP32_BLE_SERVER
 | 
			
		||||
  if (this->server_ != nullptr) {
 | 
			
		||||
    this->server_->mark_failed();
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ESP32BLE::ble_setup_() {
 | 
			
		||||
  esp_err_t err = nvs_flash_init();
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
@@ -100,13 +93,16 @@ bool ESP32BLE::ble_setup_() {
 | 
			
		||||
    ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", err);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  err = esp_ble_gap_register_callback(ESP32BLE::gap_event_handler);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err);
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (!this->gap_event_handlers_.empty()) {
 | 
			
		||||
    err = esp_ble_gap_register_callback(ESP32BLE::gap_event_handler);
 | 
			
		||||
    if (err != ESP_OK) {
 | 
			
		||||
      ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err);
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->has_server()) {
 | 
			
		||||
  if (!this->gatts_event_handlers_.empty()) {
 | 
			
		||||
    err = esp_ble_gatts_register_callback(ESP32BLE::gatts_event_handler);
 | 
			
		||||
    if (err != ESP_OK) {
 | 
			
		||||
      ESP_LOGE(TAG, "esp_ble_gatts_register_callback failed: %d", err);
 | 
			
		||||
@@ -114,7 +110,7 @@ bool ESP32BLE::ble_setup_() {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->has_client()) {
 | 
			
		||||
  if (!this->gattc_event_handlers_.empty()) {
 | 
			
		||||
    err = esp_ble_gattc_register_callback(ESP32BLE::gattc_event_handler);
 | 
			
		||||
    if (err != ESP_OK) {
 | 
			
		||||
      ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err);
 | 
			
		||||
@@ -158,6 +154,10 @@ void ESP32BLE::loop() {
 | 
			
		||||
        this->real_gatts_event_handler_(ble_event->event_.gatts.gatts_event, ble_event->event_.gatts.gatts_if,
 | 
			
		||||
                                        &ble_event->event_.gatts.gatts_param);
 | 
			
		||||
        break;
 | 
			
		||||
      case BLEEvent::GATTC:
 | 
			
		||||
        this->real_gattc_event_handler_(ble_event->event_.gattc.gattc_event, ble_event->event_.gattc.gattc_if,
 | 
			
		||||
                                        &ble_event->event_.gattc.gattc_param);
 | 
			
		||||
        break;
 | 
			
		||||
      case BLEEvent::GAP:
 | 
			
		||||
        this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param);
 | 
			
		||||
        break;
 | 
			
		||||
@@ -176,9 +176,8 @@ void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa
 | 
			
		||||
 | 
			
		||||
void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
 | 
			
		||||
  ESP_LOGV(TAG, "(BLE) gap_event_handler - %d", event);
 | 
			
		||||
  switch (event) {
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  for (auto *gap_handler : this->gap_event_handlers_) {
 | 
			
		||||
    gap_handler->gap_event_handler(event, param);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -191,14 +190,23 @@ void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gat
 | 
			
		||||
void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
 | 
			
		||||
                                         esp_ble_gatts_cb_param_t *param) {
 | 
			
		||||
  ESP_LOGV(TAG, "(BLE) gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
 | 
			
		||||
#ifdef USE_ESP32_BLE_SERVER
 | 
			
		||||
  this->server_->gatts_event_handler(event, gatts_if, param);
 | 
			
		||||
#endif
 | 
			
		||||
  for (auto *gatts_handler : this->gatts_event_handlers_) {
 | 
			
		||||
    gatts_handler->gatts_event_handler(event, gatts_if, param);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                                   esp_ble_gattc_cb_param_t *param) {
 | 
			
		||||
  BLEEvent *new_event = new BLEEvent(event, gattc_if, param);  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
  global_ble->ble_events_.push(new_event);
 | 
			
		||||
}  // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
 | 
			
		||||
 | 
			
		||||
void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                                         esp_ble_gattc_cb_param_t *param) {
 | 
			
		||||
  // this->client_->gattc_event_handler(event, gattc_if, param);
 | 
			
		||||
  ESP_LOGV(TAG, "(BLE) gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
 | 
			
		||||
  for (auto *gattc_handler : this->gattc_event_handlers_) {
 | 
			
		||||
    gattc_handler->gattc_event_handler(event, gattc_if, param);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,17 +5,16 @@
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "queue.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_BLE_SERVER
 | 
			
		||||
#include "esphome/components/esp32_ble_server/ble_server.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "queue.h"
 | 
			
		||||
#include "ble_event.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include <esp_gap_ble_api.h>
 | 
			
		||||
#include <esp_gatts_api.h>
 | 
			
		||||
#include <esp_gattc_api.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32_ble {
 | 
			
		||||
 | 
			
		||||
@@ -26,28 +25,36 @@ typedef struct {
 | 
			
		||||
  uint16_t mtu;
 | 
			
		||||
} conn_status_t;
 | 
			
		||||
 | 
			
		||||
class GAPEventHandler {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GATTcEventHandler {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                                   esp_ble_gattc_cb_param_t *param) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GATTsEventHandler {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
 | 
			
		||||
                                   esp_ble_gatts_cb_param_t *param) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ESP32BLE : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void mark_failed() override;
 | 
			
		||||
 | 
			
		||||
  bool has_server() {
 | 
			
		||||
#ifdef USE_ESP32_BLE_SERVER
 | 
			
		||||
    return this->server_ != nullptr;
 | 
			
		||||
#else
 | 
			
		||||
    return false;
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
  bool has_client() { return false; }
 | 
			
		||||
 | 
			
		||||
  BLEAdvertising *get_advertising() { return this->advertising_; }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_BLE_SERVER
 | 
			
		||||
  void set_server(esp32_ble_server::BLEServer *server) { this->server_ = server; }
 | 
			
		||||
#endif
 | 
			
		||||
  void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); }
 | 
			
		||||
  void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); }
 | 
			
		||||
  void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
 | 
			
		||||
  static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
 | 
			
		||||
@@ -59,9 +66,10 @@ class ESP32BLE : public Component {
 | 
			
		||||
 | 
			
		||||
  bool ble_setup_();
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_BLE_SERVER
 | 
			
		||||
  esp32_ble_server::BLEServer *server_{nullptr};
 | 
			
		||||
#endif
 | 
			
		||||
  std::vector<GAPEventHandler *> gap_event_handlers_;
 | 
			
		||||
  std::vector<GATTcEventHandler *> gattc_event_handlers_;
 | 
			
		||||
  std::vector<GATTsEventHandler *> gatts_event_handlers_;
 | 
			
		||||
 | 
			
		||||
  Queue<BLEEvent> ble_events_;
 | 
			
		||||
  BLEAdvertising *advertising_;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,69 +1,23 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <esp_gap_ble_api.h>
 | 
			
		||||
#include <esp_gattc_api.h>
 | 
			
		||||
#include <freertos/FreeRTOS.h>
 | 
			
		||||
#include <freertos/semphr.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * BLE events come in from a separate Task (thread) in the ESP32 stack. Rather
 | 
			
		||||
 * than trying to deal with various locking strategies, all incoming GAP and GATT
 | 
			
		||||
 * events will simply be placed on a semaphore guarded queue. The next time the
 | 
			
		||||
 * component runs loop(), these events are popped off the queue and handed at
 | 
			
		||||
 * this safer time.
 | 
			
		||||
 */
 | 
			
		||||
#include <esp_gatts_api.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32_ble_tracker {
 | 
			
		||||
 | 
			
		||||
template<class T> class Queue {
 | 
			
		||||
 public:
 | 
			
		||||
  Queue() { m_ = xSemaphoreCreateMutex(); }
 | 
			
		||||
 | 
			
		||||
  void push(T *element) {
 | 
			
		||||
    if (element == nullptr)
 | 
			
		||||
      return;
 | 
			
		||||
    if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) {
 | 
			
		||||
      q_.push(element);
 | 
			
		||||
      xSemaphoreGive(m_);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  T *pop() {
 | 
			
		||||
    T *element = nullptr;
 | 
			
		||||
 | 
			
		||||
    if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) {
 | 
			
		||||
      if (!q_.empty()) {
 | 
			
		||||
        element = q_.front();
 | 
			
		||||
        q_.pop();
 | 
			
		||||
      }
 | 
			
		||||
      xSemaphoreGive(m_);
 | 
			
		||||
    }
 | 
			
		||||
    return element;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::queue<T *> q_;
 | 
			
		||||
  SemaphoreHandle_t m_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Received GAP and GATTC events are only queued, and get processed in the main loop().
 | 
			
		||||
namespace esp32_ble {
 | 
			
		||||
// Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
 | 
			
		||||
// This class stores each event in a single type.
 | 
			
		||||
class BLEEvent {
 | 
			
		||||
 public:
 | 
			
		||||
  BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
 | 
			
		||||
    this->event_.gap.gap_event = e;
 | 
			
		||||
    memcpy(&this->event_.gap.gap_param, p, sizeof(esp_ble_gap_cb_param_t));
 | 
			
		||||
    this->type_ = 0;
 | 
			
		||||
    this->type_ = GAP;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
 | 
			
		||||
@@ -84,26 +38,57 @@ class BLEEvent {
 | 
			
		||||
      default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    this->type_ = 1;
 | 
			
		||||
    this->type_ = GATTC;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
 | 
			
		||||
    this->event_.gatts.gatts_event = e;
 | 
			
		||||
    this->event_.gatts.gatts_if = i;
 | 
			
		||||
    memcpy(&this->event_.gatts.gatts_param, p, sizeof(esp_ble_gatts_cb_param_t));
 | 
			
		||||
    // Need to also make a copy of relevant event data.
 | 
			
		||||
    switch (e) {
 | 
			
		||||
      case ESP_GATTS_WRITE_EVT:
 | 
			
		||||
        this->data.assign(p->write.value, p->write.value + p->write.len);
 | 
			
		||||
        this->event_.gatts.gatts_param.write.value = this->data.data();
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    this->type_ = GATTS;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  union {
 | 
			
		||||
    struct gap_event {  // NOLINT(readability-identifier-naming)
 | 
			
		||||
    // NOLINTNEXTLINE(readability-identifier-naming)
 | 
			
		||||
    struct gap_event {
 | 
			
		||||
      esp_gap_ble_cb_event_t gap_event;
 | 
			
		||||
      esp_ble_gap_cb_param_t gap_param;
 | 
			
		||||
    } gap;
 | 
			
		||||
 | 
			
		||||
    struct gattc_event {  // NOLINT(readability-identifier-naming)
 | 
			
		||||
    // NOLINTNEXTLINE(readability-identifier-naming)
 | 
			
		||||
    struct gattc_event {
 | 
			
		||||
      esp_gattc_cb_event_t gattc_event;
 | 
			
		||||
      esp_gatt_if_t gattc_if;
 | 
			
		||||
      esp_ble_gattc_cb_param_t gattc_param;
 | 
			
		||||
    } gattc;
 | 
			
		||||
 | 
			
		||||
    // NOLINTNEXTLINE(readability-identifier-naming)
 | 
			
		||||
    struct gatts_event {
 | 
			
		||||
      esp_gatts_cb_event_t gatts_event;
 | 
			
		||||
      esp_gatt_if_t gatts_if;
 | 
			
		||||
      esp_ble_gatts_cb_param_t gatts_param;
 | 
			
		||||
    } gatts;
 | 
			
		||||
  } event_;
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> data{};
 | 
			
		||||
  uint8_t type_;  // 0=gap 1=gattc
 | 
			
		||||
  // NOLINTNEXTLINE(readability-identifier-naming)
 | 
			
		||||
  enum ble_event_t : uint8_t {
 | 
			
		||||
    GAP,
 | 
			
		||||
    GATTC,
 | 
			
		||||
    GATTS,
 | 
			
		||||
  } type_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace esp32_ble_tracker
 | 
			
		||||
}  // namespace esp32_ble
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -27,8 +27,7 @@ ESPBTUUID ESPBTUUID::from_uint32(uint32_t uuid) {
 | 
			
		||||
ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) {
 | 
			
		||||
  ESPBTUUID ret;
 | 
			
		||||
  ret.uuid_.len = ESP_UUID_LEN_128;
 | 
			
		||||
  for (size_t i = 0; i < ESP_UUID_LEN_128; i++)
 | 
			
		||||
    ret.uuid_.uuid.uuid128[i] = data[i];
 | 
			
		||||
  memcpy(ret.uuid_.uuid.uuid128, data, ESP_UUID_LEN_128);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
 | 
			
		||||
@@ -91,10 +90,13 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
 | 
			
		||||
ESPBTUUID ESPBTUUID::from_uuid(esp_bt_uuid_t uuid) {
 | 
			
		||||
  ESPBTUUID ret;
 | 
			
		||||
  ret.uuid_.len = uuid.len;
 | 
			
		||||
  ret.uuid_.uuid.uuid16 = uuid.uuid.uuid16;
 | 
			
		||||
  ret.uuid_.uuid.uuid32 = uuid.uuid.uuid32;
 | 
			
		||||
  for (size_t i = 0; i < ESP_UUID_LEN_128; i++)
 | 
			
		||||
    ret.uuid_.uuid.uuid128[i] = uuid.uuid.uuid128[i];
 | 
			
		||||
  if (uuid.len == ESP_UUID_LEN_16) {
 | 
			
		||||
    ret.uuid_.uuid.uuid16 = uuid.uuid.uuid16;
 | 
			
		||||
  } else if (uuid.len == ESP_UUID_LEN_32) {
 | 
			
		||||
    ret.uuid_.uuid.uuid32 = uuid.uuid.uuid32;
 | 
			
		||||
  } else if (uuid.len == ESP_UUID_LEN_128) {
 | 
			
		||||
    memcpy(ret.uuid_.uuid.uuid128, uuid.uuid.uuid128, ESP_UUID_LEN_128);
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
ESPBTUUID ESPBTUUID::as_128bit() const {
 | 
			
		||||
@@ -158,30 +160,26 @@ bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const {
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
esp_bt_uuid_t ESPBTUUID::get_uuid() { return this->uuid_; }
 | 
			
		||||
std::string ESPBTUUID::to_string() {
 | 
			
		||||
  char sbuf[64];
 | 
			
		||||
esp_bt_uuid_t ESPBTUUID::get_uuid() const { return this->uuid_; }
 | 
			
		||||
std::string ESPBTUUID::to_string() const {
 | 
			
		||||
  switch (this->uuid_.len) {
 | 
			
		||||
    case ESP_UUID_LEN_16:
 | 
			
		||||
      sprintf(sbuf, "0x%02X%02X", this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16 & 0xff);
 | 
			
		||||
      break;
 | 
			
		||||
      return str_snprintf("0x%02X%02X", 6, this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16 & 0xff);
 | 
			
		||||
    case ESP_UUID_LEN_32:
 | 
			
		||||
      sprintf(sbuf, "0x%02X%02X%02X%02X", this->uuid_.uuid.uuid32 >> 24, (this->uuid_.uuid.uuid32 >> 16 & 0xff),
 | 
			
		||||
              (this->uuid_.uuid.uuid32 >> 8 & 0xff), this->uuid_.uuid.uuid32 & 0xff);
 | 
			
		||||
      break;
 | 
			
		||||
      return str_snprintf("0x%02X%02X%02X%02X", 10, this->uuid_.uuid.uuid32 >> 24,
 | 
			
		||||
                          (this->uuid_.uuid.uuid32 >> 16 & 0xff), (this->uuid_.uuid.uuid32 >> 8 & 0xff),
 | 
			
		||||
                          this->uuid_.uuid.uuid32 & 0xff);
 | 
			
		||||
    default:
 | 
			
		||||
    case ESP_UUID_LEN_128:
 | 
			
		||||
      char *bpos = sbuf;
 | 
			
		||||
      std::string buf;
 | 
			
		||||
      for (int8_t i = 15; i >= 0; i--) {
 | 
			
		||||
        sprintf(bpos, "%02X", this->uuid_.uuid.uuid128[i]);
 | 
			
		||||
        bpos += 2;
 | 
			
		||||
        buf += str_snprintf("%02X", 2, this->uuid_.uuid.uuid128[i]);
 | 
			
		||||
        if (i == 6 || i == 8 || i == 10 || i == 12)
 | 
			
		||||
          sprintf(bpos++, "-");
 | 
			
		||||
          buf += "-";
 | 
			
		||||
      }
 | 
			
		||||
      sbuf[47] = '\0';
 | 
			
		||||
      break;
 | 
			
		||||
      return buf;
 | 
			
		||||
  }
 | 
			
		||||
  return sbuf;
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace esp32_ble
 | 
			
		||||
 
 | 
			
		||||
@@ -32,9 +32,9 @@ class ESPBTUUID {
 | 
			
		||||
  bool operator==(const ESPBTUUID &uuid) const;
 | 
			
		||||
  bool operator!=(const ESPBTUUID &uuid) const { return !(*this == uuid); }
 | 
			
		||||
 | 
			
		||||
  esp_bt_uuid_t get_uuid();
 | 
			
		||||
  esp_bt_uuid_t get_uuid() const;
 | 
			
		||||
 | 
			
		||||
  std::string to_string();
 | 
			
		||||
  std::string to_string() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  esp_bt_uuid_t uuid_;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,16 +2,9 @@
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <queue>
 | 
			
		||||
 | 
			
		||||
#include <esp_gap_ble_api.h>
 | 
			
		||||
#include <esp_gatts_api.h>
 | 
			
		||||
#include <esp_gattc_api.h>
 | 
			
		||||
#include <freertos/FreeRTOS.h>
 | 
			
		||||
#include <freertos/semphr.h>
 | 
			
		||||
 | 
			
		||||
@@ -57,84 +50,6 @@ template<class T> class Queue {
 | 
			
		||||
  SemaphoreHandle_t m_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
 | 
			
		||||
// This class stores each event in a single type.
 | 
			
		||||
class BLEEvent {
 | 
			
		||||
 public:
 | 
			
		||||
  BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
 | 
			
		||||
    this->event_.gap.gap_event = e;
 | 
			
		||||
    memcpy(&this->event_.gap.gap_param, p, sizeof(esp_ble_gap_cb_param_t));
 | 
			
		||||
    this->type_ = GAP;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
 | 
			
		||||
    this->event_.gattc.gattc_event = e;
 | 
			
		||||
    this->event_.gattc.gattc_if = i;
 | 
			
		||||
    memcpy(&this->event_.gattc.gattc_param, p, sizeof(esp_ble_gattc_cb_param_t));
 | 
			
		||||
    // Need to also make a copy of notify event data.
 | 
			
		||||
    switch (e) {
 | 
			
		||||
      case ESP_GATTC_NOTIFY_EVT:
 | 
			
		||||
        memcpy(this->event_.gattc.data, p->notify.value, p->notify.value_len);
 | 
			
		||||
        this->event_.gattc.gattc_param.notify.value = this->event_.gattc.data;
 | 
			
		||||
        break;
 | 
			
		||||
      case ESP_GATTC_READ_CHAR_EVT:
 | 
			
		||||
      case ESP_GATTC_READ_DESCR_EVT:
 | 
			
		||||
        memcpy(this->event_.gattc.data, p->read.value, p->read.value_len);
 | 
			
		||||
        this->event_.gattc.gattc_param.read.value = this->event_.gattc.data;
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    this->type_ = GATTC;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
 | 
			
		||||
    this->event_.gatts.gatts_event = e;
 | 
			
		||||
    this->event_.gatts.gatts_if = i;
 | 
			
		||||
    memcpy(&this->event_.gatts.gatts_param, p, sizeof(esp_ble_gatts_cb_param_t));
 | 
			
		||||
    // Need to also make a copy of write data.
 | 
			
		||||
    switch (e) {
 | 
			
		||||
      case ESP_GATTS_WRITE_EVT:
 | 
			
		||||
        memcpy(this->event_.gatts.data, p->write.value, p->write.len);
 | 
			
		||||
        this->event_.gatts.gatts_param.write.value = this->event_.gatts.data;
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    this->type_ = GATTS;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  union {
 | 
			
		||||
    // NOLINTNEXTLINE(readability-identifier-naming)
 | 
			
		||||
    struct gap_event {
 | 
			
		||||
      esp_gap_ble_cb_event_t gap_event;
 | 
			
		||||
      esp_ble_gap_cb_param_t gap_param;
 | 
			
		||||
    } gap;
 | 
			
		||||
 | 
			
		||||
    // NOLINTNEXTLINE(readability-identifier-naming)
 | 
			
		||||
    struct gattc_event {
 | 
			
		||||
      esp_gattc_cb_event_t gattc_event;
 | 
			
		||||
      esp_gatt_if_t gattc_if;
 | 
			
		||||
      esp_ble_gattc_cb_param_t gattc_param;
 | 
			
		||||
      uint8_t data[64];
 | 
			
		||||
    } gattc;
 | 
			
		||||
 | 
			
		||||
    // NOLINTNEXTLINE(readability-identifier-naming)
 | 
			
		||||
    struct gatts_event {
 | 
			
		||||
      esp_gatts_cb_event_t gatts_event;
 | 
			
		||||
      esp_gatt_if_t gatts_if;
 | 
			
		||||
      esp_ble_gatts_cb_param_t gatts_param;
 | 
			
		||||
      uint8_t data[64];
 | 
			
		||||
    } gatts;
 | 
			
		||||
  } event_;
 | 
			
		||||
  // NOLINTNEXTLINE(readability-identifier-naming)
 | 
			
		||||
  enum ble_event_t : uint8_t {
 | 
			
		||||
    GAP,
 | 
			
		||||
    GATTC,
 | 
			
		||||
    GATTS,
 | 
			
		||||
  } type_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace esp32_ble
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, CONF_TX_POWER
 | 
			
		||||
from esphome.core import CORE, TimePeriod
 | 
			
		||||
from esphome.components.esp32 import add_idf_sdkconfig_option
 | 
			
		||||
from esphome.components import esp32_ble
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["esp32"]
 | 
			
		||||
CONFLICTS_WITH = ["esp32_ble_tracker"]
 | 
			
		||||
@@ -54,6 +55,8 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    validate_config,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = esp32_ble.validate_variant
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    uuid = config[CONF_UUID].hex
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user