mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-03 08:31:47 +00:00 
			
		
		
		
	Compare commits
	
		
			143 Commits
		
	
	
		
			jesserockz
			...
			2023.2.0b2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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 | ||
| 
						 | 
					034b47c23a | ||
| 
						 | 
					3e017efa30 | ||
| 
						 | 
					aca56fcdcc | ||
| 
						 | 
					e778a445d9 | ||
| 
						 | 
					ded86493c2 | ||
| 
						 | 
					4d72eb42a5 | ||
| 
						 | 
					267f0587c6 | ||
| 
						 | 
					4a374a466a | ||
| 
						 | 
					b27a328d1e | ||
| 
						 | 
					d94e9d92ca | ||
| 
						 | 
					36c2e770bf | ||
| 
						 | 
					79040c116d | ||
| 
						 | 
					4aac76c549 | ||
| 
						 | 
					0ea97df1af | ||
| 
						 | 
					92e66a2764 | ||
| 
						 | 
					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 | ||
| 
						 | 
					0d50caa179 | ||
| 
						 | 
					8b06135b41 | ||
| 
						 | 
					573ea55187 | ||
| 
						 | 
					b48b5d6cc7 | ||
| 
						 | 
					cb520c00a5 | ||
| 
						 | 
					2f24138345 | ||
| 
						 | 
					96512b80cc | ||
| 
						 | 
					fcb9b51978 | ||
| 
						 | 
					f408f1a368 | ||
| 
						 | 
					7d8d563c62 | ||
| 
						 | 
					0a1f705fda | ||
| 
						 | 
					1952c1880b | ||
| 
						 | 
					b03967dac1 | ||
| 
						 | 
					bcae2596a6 | ||
| 
						 | 
					fc0347c86c | ||
| 
						 | 
					d9563d4de1 | ||
| 
						 | 
					cc7e2bf8db | ||
| 
						 | 
					5d98e2923b | ||
| 
						 | 
					07197d12f6 | ||
| 
						 | 
					7b0a298497 | ||
| 
						 | 
					21679cf2ba | ||
| 
						 | 
					4be7cd12a1 | ||
| 
						 | 
					dee4d0ccb7 | 
							
								
								
									
										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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								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,8 +216,12 @@ 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
 | 
			
		||||
@@ -234,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
 | 
			
		||||
@@ -258,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,7 +6,7 @@
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
@@ -26,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/* \
 | 
			
		||||
@@ -139,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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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]))
 | 
			
		||||
@@ -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,14 +1,17 @@
 | 
			
		||||
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
 | 
			
		||||
from esphome import git
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dashboard_import_ns = cg.esphome_ns.namespace("dashboard_import")
 | 
			
		||||
 | 
			
		||||
@@ -21,19 +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"
 | 
			
		||||
CONF_IMPORT_FULL_CONFIG = "import_full_config"
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Required(CONF_PACKAGE_IMPORT_URL): validate_import_url,
 | 
			
		||||
        cv.Optional(CONF_IMPORT_FULL_CONFIG, default=False): cv.boolean,
 | 
			
		||||
    }
 | 
			
		||||
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 = """
 | 
			
		||||
@@ -49,11 +65,17 @@ async def to_code(config):
 | 
			
		||||
    url = config[CONF_PACKAGE_IMPORT_URL]
 | 
			
		||||
    if config[CONF_IMPORT_FULL_CONFIG]:
 | 
			
		||||
        url += "?full_config"
 | 
			
		||||
    cg.add(dashboard_import_ns.set_package_import_url(config[CONF_PACKAGE_IMPORT_URL]))
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
@@ -61,14 +83,21 @@ 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:
 | 
			
		||||
@@ -85,14 +114,21 @@ def import_config(
 | 
			
		||||
            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": {"name": name},
 | 
			
		||||
                "substitutions": substitutions,
 | 
			
		||||
                "packages": {project_name: import_url},
 | 
			
		||||
                "esphome": {
 | 
			
		||||
                    "name": "${name}",
 | 
			
		||||
                    "name_add_mac_suffix": False,
 | 
			
		||||
                },
 | 
			
		||||
                "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:
 | 
			
		||||
 
 | 
			
		||||
@@ -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({
 | 
			
		||||
 
 | 
			
		||||
@@ -452,7 +452,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 +483,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 +527,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_)
 | 
			
		||||
 
 | 
			
		||||
@@ -448,18 +448,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 +475,8 @@ class Image {
 | 
			
		||||
  int get_height() const;
 | 
			
		||||
  ImageType get_type() const;
 | 
			
		||||
 | 
			
		||||
  virtual int get_current_frame() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  int width_;
 | 
			
		||||
  int height_;
 | 
			
		||||
@@ -489,7 +493,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))
 | 
			
		||||
@@ -356,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",
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,13 +6,19 @@ from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant
 | 
			
		||||
 | 
			
		||||
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]
 | 
			
		||||
 | 
			
		||||
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(
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ void BLEClientBase::loop() {
 | 
			
		||||
float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
 | 
			
		||||
 | 
			
		||||
bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) {
 | 
			
		||||
  if (device.address_uint64() != this->address_)
 | 
			
		||||
  if (this->address_ == 0 || device.address_uint64() != this->address_)
 | 
			
		||||
    return false;
 | 
			
		||||
  if (this->state_ != espbt::ClientState::IDLE && this->state_ != espbt::ClientState::SEARCHING)
 | 
			
		||||
    return false;
 | 
			
		||||
@@ -132,16 +132,17 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
 | 
			
		||||
        this->set_state(espbt::ClientState::IDLE);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
 | 
			
		||||
        this->set_state(espbt::ClientState::CONNECTED);
 | 
			
		||||
        this->state_ = espbt::ClientState::ESTABLISHED;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if_, param->open.conn_id);
 | 
			
		||||
      if (ret) {
 | 
			
		||||
        ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_send_mtu_req failed, status=%x", this->connection_index_,
 | 
			
		||||
                 this->address_str_.c_str(), ret);
 | 
			
		||||
      }
 | 
			
		||||
      if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
 | 
			
		||||
        ESP_LOGI(TAG, "[%d] [%s] Connected", this->connection_index_, this->address_str_.c_str());
 | 
			
		||||
        this->set_state(espbt::ClientState::CONNECTED);
 | 
			
		||||
        this->state_ = espbt::ClientState::ESTABLISHED;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -189,6 +190,7 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
 | 
			
		||||
        ESP_LOGV(TAG, "[%d] [%s]  start_handle: 0x%x  end_handle: 0x%x", this->connection_index_,
 | 
			
		||||
                 this->address_str_.c_str(), svc->start_handle, svc->end_handle);
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGI(TAG, "[%d] [%s] Connected", this->connection_index_, this->address_str_.c_str());
 | 
			
		||||
      this->set_state(espbt::ClientState::CONNECTED);
 | 
			
		||||
      this->state_ = espbt::ClientState::ESTABLISHED;
 | 
			
		||||
      break;
 | 
			
		||||
 
 | 
			
		||||
@@ -7,21 +7,25 @@ from esphome.components.esp32 import add_idf_sdkconfig_option
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["esp32_ble"]
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
CONFLICTS_WITH = ["esp32_ble_tracker", "esp32_ble_beacon"]
 | 
			
		||||
CONFLICTS_WITH = ["esp32_ble_beacon"]
 | 
			
		||||
DEPENDENCIES = ["esp32"]
 | 
			
		||||
 | 
			
		||||
CONF_MANUFACTURER = "manufacturer"
 | 
			
		||||
CONF_BLE_ID = "ble_id"
 | 
			
		||||
 | 
			
		||||
esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server")
 | 
			
		||||
BLEServer = esp32_ble_server_ns.class_("BLEServer", cg.Component)
 | 
			
		||||
BLEServer = esp32_ble_server_ns.class_(
 | 
			
		||||
    "BLEServer",
 | 
			
		||||
    cg.Component,
 | 
			
		||||
    esp32_ble.GATTsEventHandler,
 | 
			
		||||
    cg.Parented.template(esp32_ble.ESP32BLE),
 | 
			
		||||
)
 | 
			
		||||
BLEServiceComponent = esp32_ble_server_ns.class_("BLEServiceComponent")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(BLEServer),
 | 
			
		||||
        cv.GenerateID(CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE),
 | 
			
		||||
        cv.GenerateID(esp32_ble.CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE),
 | 
			
		||||
        cv.Optional(CONF_MANUFACTURER, default="ESPHome"): cv.string,
 | 
			
		||||
        cv.Optional(CONF_MODEL): cv.string,
 | 
			
		||||
    }
 | 
			
		||||
@@ -29,16 +33,18 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_BLE_ID])
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
 | 
			
		||||
    cg.add(parent.register_gatts_event_handler(var))
 | 
			
		||||
    cg.add(var.set_parent(parent))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_manufacturer(config[CONF_MANUFACTURER]))
 | 
			
		||||
    if CONF_MODEL in config:
 | 
			
		||||
        cg.add(var.set_model(config[CONF_MODEL]))
 | 
			
		||||
    cg.add_define("USE_ESP32_BLE_SERVER")
 | 
			
		||||
 | 
			
		||||
    cg.add(parent.set_server(var))
 | 
			
		||||
 | 
			
		||||
    if CORE.using_esp_idf:
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,8 @@ static const uint16_t VERSION_UUID = 0x2A26;
 | 
			
		||||
static const uint16_t MANUFACTURER_UUID = 0x2A29;
 | 
			
		||||
 | 
			
		||||
void BLEServer::setup() {
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
  if (this->parent_->is_failed()) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    ESP_LOGE(TAG, "BLE Server was marked failed by ESP32BLE");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#include "ble_service.h"
 | 
			
		||||
#include "ble_characteristic.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/esp32_ble/ble.h"
 | 
			
		||||
#include "esphome/components/esp32_ble/ble_advertising.h"
 | 
			
		||||
#include "esphome/components/esp32_ble/ble_uuid.h"
 | 
			
		||||
#include "esphome/components/esp32_ble/queue.h"
 | 
			
		||||
@@ -32,7 +33,7 @@ class BLEServiceComponent {
 | 
			
		||||
  virtual void stop();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BLEServer : public Component {
 | 
			
		||||
class BLEServer : public Component, public GATTsEventHandler, public Parented<ESP32BLE> {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
@@ -55,7 +56,8 @@ class BLEServer : public Component {
 | 
			
		||||
  uint32_t get_connected_client_count() { return this->connected_clients_; }
 | 
			
		||||
  const std::map<uint16_t, void *> &get_clients() { return this->clients_; }
 | 
			
		||||
 | 
			
		||||
  void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
 | 
			
		||||
  void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
 | 
			
		||||
                           esp_ble_gatts_cb_param_t *param) override;
 | 
			
		||||
 | 
			
		||||
  void register_service_component(BLEServiceComponent *component) { this->service_components_.push_back(component); }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,10 +15,11 @@ from esphome.const import (
 | 
			
		||||
    CONF_ON_BLE_SERVICE_DATA_ADVERTISE,
 | 
			
		||||
    CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE,
 | 
			
		||||
)
 | 
			
		||||
from esphome.components import esp32_ble
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
from esphome.components.esp32 import add_idf_sdkconfig_option
 | 
			
		||||
from esphome.components import esp32_ble
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["esp32_ble"]
 | 
			
		||||
DEPENDENCIES = ["esp32"]
 | 
			
		||||
 | 
			
		||||
CONF_ESP32_BLE_ID = "esp32_ble_id"
 | 
			
		||||
@@ -27,7 +28,13 @@ CONF_WINDOW = "window"
 | 
			
		||||
CONF_CONTINUOUS = "continuous"
 | 
			
		||||
CONF_ON_SCAN_END = "on_scan_end"
 | 
			
		||||
esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker")
 | 
			
		||||
ESP32BLETracker = esp32_ble_tracker_ns.class_("ESP32BLETracker", cg.Component)
 | 
			
		||||
ESP32BLETracker = esp32_ble_tracker_ns.class_(
 | 
			
		||||
    "ESP32BLETracker",
 | 
			
		||||
    cg.Component,
 | 
			
		||||
    esp32_ble.GAPEventHandler,
 | 
			
		||||
    esp32_ble.GATTcEventHandler,
 | 
			
		||||
    cg.Parented.template(esp32_ble.ESP32BLE),
 | 
			
		||||
)
 | 
			
		||||
ESPBTClient = esp32_ble_tracker_ns.class_("ESPBTClient")
 | 
			
		||||
ESPBTDeviceListener = esp32_ble_tracker_ns.class_("ESPBTDeviceListener")
 | 
			
		||||
ESPBTDevice = esp32_ble_tracker_ns.class_("ESPBTDevice")
 | 
			
		||||
@@ -138,6 +145,7 @@ def as_reversed_hex_array(value):
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(ESP32BLETracker),
 | 
			
		||||
        cv.GenerateID(esp32_ble.CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE),
 | 
			
		||||
        cv.Optional(CONF_SCAN_PARAMETERS, default={}): cv.All(
 | 
			
		||||
            cv.Schema(
 | 
			
		||||
                {
 | 
			
		||||
@@ -200,6 +208,12 @@ ESP_BLE_DEVICE_SCHEMA = cv.Schema(
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
 | 
			
		||||
    cg.add(parent.register_gap_event_handler(var))
 | 
			
		||||
    cg.add(parent.register_gattc_event_handler(var))
 | 
			
		||||
    cg.add(var.set_parent(parent))
 | 
			
		||||
 | 
			
		||||
    params = config[CONF_SCAN_PARAMETERS]
 | 
			
		||||
    cg.add(var.set_scan_duration(params[CONF_DURATION]))
 | 
			
		||||
    cg.add(var.set_scan_interval(int(params[CONF_INTERVAL].total_milliseconds / 0.625)))
 | 
			
		||||
@@ -248,6 +262,7 @@ async def to_code(config):
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_BTU_TASK_STACK_SIZE", 8192)
 | 
			
		||||
 | 
			
		||||
    cg.add_define("USE_OTA_STATE_CALLBACK")  # To be notified when an OTA update starts
 | 
			
		||||
    cg.add_define("USE_ESP32_BLE_CLIENT")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ESP32_BLE_START_SCAN_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
 
 | 
			
		||||
@@ -7,14 +7,14 @@
 | 
			
		||||
#include "esphome/core/helpers.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_gap_ble_api.h>
 | 
			
		||||
#include <esp_bt_defs.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_OTA
 | 
			
		||||
#include "esphome/components/ota/ota_component.h"
 | 
			
		||||
@@ -45,17 +45,19 @@ uint64_t ble_addr_to_uint64(const esp_bd_addr_t address) {
 | 
			
		||||
  return u;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float ESP32BLETracker::get_setup_priority() const { return setup_priority::BLUETOOTH; }
 | 
			
		||||
float ESP32BLETracker::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
 | 
			
		||||
 | 
			
		||||
void ESP32BLETracker::setup() {
 | 
			
		||||
  if (this->parent_->is_failed()) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    ESP_LOGE(TAG, "BLE Tracker was marked failed by ESP32BLE");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  global_esp32_ble_tracker = this;
 | 
			
		||||
  this->scan_result_lock_ = xSemaphoreCreateMutex();
 | 
			
		||||
  this->scan_end_lock_ = xSemaphoreCreateMutex();
 | 
			
		||||
  this->scanner_idle_ = true;
 | 
			
		||||
  if (!ESP32BLETracker::ble_setup()) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_OTA
 | 
			
		||||
  ota::global_ota_component->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t error) {
 | 
			
		||||
@@ -75,18 +77,6 @@ void ESP32BLETracker::setup() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32BLETracker::loop() {
 | 
			
		||||
  BLEEvent *ble_event = this->ble_events_.pop();
 | 
			
		||||
  while (ble_event != nullptr) {
 | 
			
		||||
    if (ble_event->type_) {
 | 
			
		||||
      this->real_gattc_event_handler_(ble_event->event_.gattc.gattc_event, ble_event->event_.gattc.gattc_if,
 | 
			
		||||
                                      &ble_event->event_.gattc.gattc_param);
 | 
			
		||||
    } else {
 | 
			
		||||
      this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param);
 | 
			
		||||
    }
 | 
			
		||||
    delete ble_event;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
    ble_event = this->ble_events_.pop();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int connecting = 0;
 | 
			
		||||
  int discovered = 0;
 | 
			
		||||
  int searching = 0;
 | 
			
		||||
@@ -238,85 +228,6 @@ void ESP32BLETracker::stop_scan() {
 | 
			
		||||
  this->cancel_timeout("scan");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ESP32BLETracker::ble_setup() {
 | 
			
		||||
  // Initialize non-volatile storage for the bluetooth controller
 | 
			
		||||
  esp_err_t err = nvs_flash_init();
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "nvs_flash_init failed: %d", err);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
  if (!btStart()) {
 | 
			
		||||
    ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status());
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
#else
 | 
			
		||||
  if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
 | 
			
		||||
    // start bt controller
 | 
			
		||||
    if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
 | 
			
		||||
      esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
 | 
			
		||||
      err = esp_bt_controller_init(&cfg);
 | 
			
		||||
      if (err != ESP_OK) {
 | 
			
		||||
        ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err));
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE)
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
    if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
 | 
			
		||||
      err = esp_bt_controller_enable(ESP_BT_MODE_BLE);
 | 
			
		||||
      if (err != ESP_OK) {
 | 
			
		||||
        ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err));
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
 | 
			
		||||
      ESP_LOGE(TAG, "esp bt controller enable failed");
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
 | 
			
		||||
 | 
			
		||||
  err = esp_bluedroid_init();
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", err);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  err = esp_bluedroid_enable();
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", err);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  err = esp_ble_gap_register_callback(ESP32BLETracker::gap_event_handler);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  err = esp_ble_gattc_register_callback(ESP32BLETracker::gattc_event_handler);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Empty name
 | 
			
		||||
  esp_ble_gap_set_device_name("");
 | 
			
		||||
 | 
			
		||||
  esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
 | 
			
		||||
  err = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "esp_ble_gap_set_security_param failed: %d", err);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // BLE takes some time to be fully set up, 200ms should be more than enough
 | 
			
		||||
  delay(200);  // NOLINT
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32BLETracker::start_scan_(bool first) {
 | 
			
		||||
  // The lock must be held when calling this function.
 | 
			
		||||
  if (xSemaphoreTake(this->scan_end_lock_, 0L)) {
 | 
			
		||||
@@ -369,11 +280,6 @@ void ESP32BLETracker::register_client(ESPBTClient *client) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
 | 
			
		||||
  BLEEvent *gap_event = new BLEEvent(event, param);  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
  global_esp32_ble_tracker->ble_events_.push(gap_event);
 | 
			
		||||
}  // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
 | 
			
		||||
 | 
			
		||||
void ESP32BLETracker::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
 | 
			
		||||
  switch (event) {
 | 
			
		||||
    case ESP_GAP_BLE_SCAN_RESULT_EVT:
 | 
			
		||||
      this->gap_scan_result_(param->scan_rst);
 | 
			
		||||
@@ -428,204 +334,11 @@ void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_re
 | 
			
		||||
 | 
			
		||||
void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                                          esp_ble_gattc_cb_param_t *param) {
 | 
			
		||||
  BLEEvent *gattc_event = new BLEEvent(event, gattc_if, param);  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
  global_esp32_ble_tracker->ble_events_.push(gattc_event);
 | 
			
		||||
}  // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
 | 
			
		||||
 | 
			
		||||
void ESP32BLETracker::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                                                esp_ble_gattc_cb_param_t *param) {
 | 
			
		||||
  for (auto *client : this->clients_) {
 | 
			
		||||
    client->gattc_event_handler(event, gattc_if, param);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ESPBTUUID::ESPBTUUID() : uuid_() {}
 | 
			
		||||
ESPBTUUID ESPBTUUID::from_uint16(uint16_t uuid) {
 | 
			
		||||
  ESPBTUUID ret;
 | 
			
		||||
  ret.uuid_.len = ESP_UUID_LEN_16;
 | 
			
		||||
  ret.uuid_.uuid.uuid16 = uuid;
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
ESPBTUUID ESPBTUUID::from_uint32(uint32_t uuid) {
 | 
			
		||||
  ESPBTUUID ret;
 | 
			
		||||
  ret.uuid_.len = ESP_UUID_LEN_32;
 | 
			
		||||
  ret.uuid_.uuid.uuid32 = uuid;
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
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];
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
 | 
			
		||||
  ESPBTUUID ret;
 | 
			
		||||
  if (data.length() == 4) {
 | 
			
		||||
    ret.uuid_.len = ESP_UUID_LEN_16;
 | 
			
		||||
    ret.uuid_.uuid.uuid16 = 0;
 | 
			
		||||
    for (int i = 0; i < data.length();) {
 | 
			
		||||
      uint8_t msb = data.c_str()[i];
 | 
			
		||||
      uint8_t lsb = data.c_str()[i + 1];
 | 
			
		||||
 | 
			
		||||
      if (msb > '9')
 | 
			
		||||
        msb -= 7;
 | 
			
		||||
      if (lsb > '9')
 | 
			
		||||
        lsb -= 7;
 | 
			
		||||
      ret.uuid_.uuid.uuid16 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (2 - i) * 4;
 | 
			
		||||
      i += 2;
 | 
			
		||||
    }
 | 
			
		||||
  } else if (data.length() == 8) {
 | 
			
		||||
    ret.uuid_.len = ESP_UUID_LEN_32;
 | 
			
		||||
    ret.uuid_.uuid.uuid32 = 0;
 | 
			
		||||
    for (int i = 0; i < data.length();) {
 | 
			
		||||
      uint8_t msb = data.c_str()[i];
 | 
			
		||||
      uint8_t lsb = data.c_str()[i + 1];
 | 
			
		||||
 | 
			
		||||
      if (msb > '9')
 | 
			
		||||
        msb -= 7;
 | 
			
		||||
      if (lsb > '9')
 | 
			
		||||
        lsb -= 7;
 | 
			
		||||
      ret.uuid_.uuid.uuid32 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (6 - i) * 4;
 | 
			
		||||
      i += 2;
 | 
			
		||||
    }
 | 
			
		||||
  } else if (data.length() == 16) {  // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be
 | 
			
		||||
                                     // investigated (lack of time)
 | 
			
		||||
    ret.uuid_.len = ESP_UUID_LEN_128;
 | 
			
		||||
    memcpy(ret.uuid_.uuid.uuid128, (uint8_t *) data.data(), 16);
 | 
			
		||||
  } else if (data.length() == 36) {
 | 
			
		||||
    // If the length of the string is 36 bytes then we will assume it is a long hex string in
 | 
			
		||||
    // UUID format.
 | 
			
		||||
    ret.uuid_.len = ESP_UUID_LEN_128;
 | 
			
		||||
    int n = 0;
 | 
			
		||||
    for (int i = 0; i < data.length();) {
 | 
			
		||||
      if (data.c_str()[i] == '-')
 | 
			
		||||
        i++;
 | 
			
		||||
      uint8_t msb = data.c_str()[i];
 | 
			
		||||
      uint8_t lsb = data.c_str()[i + 1];
 | 
			
		||||
 | 
			
		||||
      if (msb > '9')
 | 
			
		||||
        msb -= 7;
 | 
			
		||||
      if (lsb > '9')
 | 
			
		||||
        lsb -= 7;
 | 
			
		||||
      ret.uuid_.uuid.uuid128[15 - n++] = ((msb & 0x0F) << 4) | (lsb & 0x0F);
 | 
			
		||||
      i += 2;
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGE(TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes - %s", data.c_str());
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
ESPBTUUID ESPBTUUID::from_uuid(esp_bt_uuid_t uuid) {
 | 
			
		||||
  ESPBTUUID ret;
 | 
			
		||||
  ret.uuid_.len = uuid.len;
 | 
			
		||||
  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 {
 | 
			
		||||
  if (this->uuid_.len == ESP_UUID_LEN_128) {
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
  uint8_t data[] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 | 
			
		||||
  uint32_t uuid32;
 | 
			
		||||
  if (this->uuid_.len == ESP_UUID_LEN_32) {
 | 
			
		||||
    uuid32 = this->uuid_.uuid.uuid32;
 | 
			
		||||
  } else {
 | 
			
		||||
    uuid32 = this->uuid_.uuid.uuid16;
 | 
			
		||||
  }
 | 
			
		||||
  for (uint8_t i = 0; i < this->uuid_.len; i++) {
 | 
			
		||||
    data[12 + i] = ((uuid32 >> i * 8) & 0xFF);
 | 
			
		||||
  }
 | 
			
		||||
  return ESPBTUUID::from_raw(data);
 | 
			
		||||
}
 | 
			
		||||
bool ESPBTUUID::contains(uint8_t data1, uint8_t data2) const {
 | 
			
		||||
  if (this->uuid_.len == ESP_UUID_LEN_16) {
 | 
			
		||||
    return (this->uuid_.uuid.uuid16 >> 8) == data2 && (this->uuid_.uuid.uuid16 & 0xFF) == data1;
 | 
			
		||||
  } else if (this->uuid_.len == ESP_UUID_LEN_32) {
 | 
			
		||||
    for (uint8_t i = 0; i < 3; i++) {
 | 
			
		||||
      bool a = ((this->uuid_.uuid.uuid32 >> i * 8) & 0xFF) == data1;
 | 
			
		||||
      bool b = ((this->uuid_.uuid.uuid32 >> (i + 1) * 8) & 0xFF) == data2;
 | 
			
		||||
      if (a && b)
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    for (uint8_t i = 0; i < 15; i++) {
 | 
			
		||||
      if (this->uuid_.uuid.uuid128[i] == data1 && this->uuid_.uuid.uuid128[i + 1] == data2)
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const {
 | 
			
		||||
  if (this->uuid_.len == uuid.uuid_.len) {
 | 
			
		||||
    switch (this->uuid_.len) {
 | 
			
		||||
      case ESP_UUID_LEN_16:
 | 
			
		||||
        if (uuid.uuid_.uuid.uuid16 == this->uuid_.uuid.uuid16) {
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case ESP_UUID_LEN_32:
 | 
			
		||||
        if (uuid.uuid_.uuid.uuid32 == this->uuid_.uuid.uuid32) {
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case ESP_UUID_LEN_128:
 | 
			
		||||
        for (int i = 0; i < ESP_UUID_LEN_128; i++) {
 | 
			
		||||
          if (uuid.uuid_.uuid.uuid128[i] != this->uuid_.uuid.uuid128[i]) {
 | 
			
		||||
            return false;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    return this->as_128bit() == uuid.as_128bit();
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
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:
 | 
			
		||||
      return str_snprintf("0x%02X%02X", 6, this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16 & 0xff);
 | 
			
		||||
    case ESP_UUID_LEN_32:
 | 
			
		||||
      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:
 | 
			
		||||
      std::string buf;
 | 
			
		||||
      for (int8_t i = 15; i >= 0; i--) {
 | 
			
		||||
        buf += str_snprintf("%02X", 2, this->uuid_.uuid.uuid128[i]);
 | 
			
		||||
        if (i == 6 || i == 8 || i == 10 || i == 12)
 | 
			
		||||
          buf += "-";
 | 
			
		||||
      }
 | 
			
		||||
      return buf;
 | 
			
		||||
  }
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t ESPBTUUID::get_128bit_high() const {
 | 
			
		||||
  esp_bt_uuid_t uuid = this->as_128bit().get_uuid();
 | 
			
		||||
  return ((uint64_t) uuid.uuid.uuid128[15] << 56) | ((uint64_t) uuid.uuid.uuid128[14] << 48) |
 | 
			
		||||
         ((uint64_t) uuid.uuid.uuid128[13] << 40) | ((uint64_t) uuid.uuid.uuid128[12] << 32) |
 | 
			
		||||
         ((uint64_t) uuid.uuid.uuid128[11] << 24) | ((uint64_t) uuid.uuid.uuid128[10] << 16) |
 | 
			
		||||
         ((uint64_t) uuid.uuid.uuid128[9] << 8) | ((uint64_t) uuid.uuid.uuid128[8]);
 | 
			
		||||
}
 | 
			
		||||
uint64_t ESPBTUUID::get_128bit_low() const {
 | 
			
		||||
  esp_bt_uuid_t uuid = this->as_128bit().get_uuid();
 | 
			
		||||
  return ((uint64_t) uuid.uuid.uuid128[7] << 56) | ((uint64_t) uuid.uuid.uuid128[6] << 48) |
 | 
			
		||||
         ((uint64_t) uuid.uuid.uuid128[5] << 40) | ((uint64_t) uuid.uuid.uuid128[4] << 32) |
 | 
			
		||||
         ((uint64_t) uuid.uuid.uuid128[3] << 24) | ((uint64_t) uuid.uuid.uuid128[2] << 16) |
 | 
			
		||||
         ((uint64_t) uuid.uuid.uuid128[1] << 8) | ((uint64_t) uuid.uuid.uuid128[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); }
 | 
			
		||||
optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) {
 | 
			
		||||
  if (!data.uuid.contains(0x4C, 0x00))
 | 
			
		||||
@@ -706,11 +419,7 @@ void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_p
 | 
			
		||||
  while (offset + 2 < len) {
 | 
			
		||||
    const uint8_t field_length = payload[offset++];  // First byte is length of adv record
 | 
			
		||||
    if (field_length == 0) {
 | 
			
		||||
      if (offset < param.adv_data_len && param.scan_rsp_len > 0) {  // Zero padded advertisement data
 | 
			
		||||
        offset = param.adv_data_len;
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
      continue;  // Possible zero padded advertisement data
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // first byte of adv record is adv record type
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "queue.h"
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <string>
 | 
			
		||||
@@ -15,40 +14,16 @@
 | 
			
		||||
#include <esp_gattc_api.h>
 | 
			
		||||
#include <esp_bt_defs.h>
 | 
			
		||||
 | 
			
		||||
#include <freertos/FreeRTOS.h>
 | 
			
		||||
#include <freertos/semphr.h>
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/esp32_ble/ble.h"
 | 
			
		||||
#include "esphome/components/esp32_ble/ble_uuid.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32_ble_tracker {
 | 
			
		||||
 | 
			
		||||
class ESPBTUUID {
 | 
			
		||||
 public:
 | 
			
		||||
  ESPBTUUID();
 | 
			
		||||
 | 
			
		||||
  static ESPBTUUID from_uint16(uint16_t uuid);
 | 
			
		||||
 | 
			
		||||
  static ESPBTUUID from_uint32(uint32_t uuid);
 | 
			
		||||
 | 
			
		||||
  static ESPBTUUID from_raw(const uint8_t *data);
 | 
			
		||||
 | 
			
		||||
  static ESPBTUUID from_raw(const std::string &data);
 | 
			
		||||
 | 
			
		||||
  static ESPBTUUID from_uuid(esp_bt_uuid_t uuid);
 | 
			
		||||
 | 
			
		||||
  ESPBTUUID as_128bit() const;
 | 
			
		||||
 | 
			
		||||
  bool contains(uint8_t data1, uint8_t data2) const;
 | 
			
		||||
 | 
			
		||||
  bool operator==(const ESPBTUUID &uuid) const;
 | 
			
		||||
  bool operator!=(const ESPBTUUID &uuid) const { return !(*this == uuid); }
 | 
			
		||||
 | 
			
		||||
  esp_bt_uuid_t get_uuid() const;
 | 
			
		||||
 | 
			
		||||
  std::string to_string() const;
 | 
			
		||||
 | 
			
		||||
  uint64_t get_128bit_high() const;
 | 
			
		||||
  uint64_t get_128bit_low() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  esp_bt_uuid_t uuid_;
 | 
			
		||||
};
 | 
			
		||||
using namespace esp32_ble;
 | 
			
		||||
 | 
			
		||||
using adv_data_t = std::vector<uint8_t>;
 | 
			
		||||
 | 
			
		||||
@@ -191,7 +166,7 @@ class ESPBTClient : public ESPBTDeviceListener {
 | 
			
		||||
  ClientState state_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ESP32BLETracker : public Component {
 | 
			
		||||
class ESP32BLETracker : public Component, public GAPEventHandler, public GATTcEventHandler, public Parented<ESP32BLE> {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_scan_duration(uint32_t scan_duration) { scan_duration_ = scan_duration; }
 | 
			
		||||
  void set_scan_interval(uint32_t scan_interval) { scan_interval_ = scan_interval; }
 | 
			
		||||
@@ -218,16 +193,15 @@ class ESP32BLETracker : public Component {
 | 
			
		||||
  void start_scan();
 | 
			
		||||
  void stop_scan();
 | 
			
		||||
 | 
			
		||||
  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                           esp_ble_gattc_cb_param_t *param) override;
 | 
			
		||||
  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  /// The FreeRTOS task managing the bluetooth interface.
 | 
			
		||||
  static bool ble_setup();
 | 
			
		||||
  /// Start a single scan by setting up the parameters and doing some esp-idf calls.
 | 
			
		||||
  void start_scan_(bool first);
 | 
			
		||||
  /// Called when a scan ends
 | 
			
		||||
  void end_of_scan_();
 | 
			
		||||
  /// Callback that will handle all GAP events and redistribute them to other callbacks.
 | 
			
		||||
  static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
 | 
			
		||||
  void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
 | 
			
		||||
  /// Called when a `ESP_GAP_BLE_SCAN_RESULT_EVT` event is received.
 | 
			
		||||
  void gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m);
 | 
			
		||||
  /// Called when a `ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT` event is received.
 | 
			
		||||
@@ -238,9 +212,6 @@ class ESP32BLETracker : public Component {
 | 
			
		||||
  void gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m);
 | 
			
		||||
 | 
			
		||||
  int app_id_;
 | 
			
		||||
  /// Callback that will handle all GATTC events and redistribute them to other callbacks.
 | 
			
		||||
  static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
 | 
			
		||||
  void real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
 | 
			
		||||
 | 
			
		||||
  /// Vector of addresses that have already been printed in print_bt_device_info
 | 
			
		||||
  std::vector<uint64_t> already_discovered_;
 | 
			
		||||
@@ -263,8 +234,6 @@ class ESP32BLETracker : public Component {
 | 
			
		||||
  esp_ble_gap_cb_param_t::ble_scan_result_evt_param scan_result_buffer_[16];
 | 
			
		||||
  esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
 | 
			
		||||
  esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS};
 | 
			
		||||
 | 
			
		||||
  Queue<BLEEvent> ble_events_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// NOLINTNEXTLINE
 | 
			
		||||
 
 | 
			
		||||
@@ -156,7 +156,7 @@ CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
 | 
			
		||||
        cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum(
 | 
			
		||||
            FRAME_SIZES, upper=True
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=10, max=63),
 | 
			
		||||
        cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63),
 | 
			
		||||
        cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
 | 
			
		||||
        cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
 | 
			
		||||
        cv.Optional(CONF_SATURATION, default=0): camera_range_param,
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user