mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-03 16:41:50 +00:00 
			
		
		
		
	Compare commits
	
		
			127 Commits
		
	
	
		
			2023.9.0b3
			...
			2023.10.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b973238323 | ||
| 
						 | 
					582b8383d2 | ||
| 
						 | 
					e1c9418aee | ||
| 
						 | 
					2aa787f5f0 | ||
| 
						 | 
					2189a40a39 | ||
| 
						 | 
					51688d4078 | ||
| 
						 | 
					cc4c0e3e0b | ||
| 
						 | 
					1a44c6487e | ||
| 
						 | 
					5e7ce610a0 | ||
| 
						 | 
					1f02096edb | ||
| 
						 | 
					fd7d3c4332 | ||
| 
						 | 
					61cf566560 | ||
| 
						 | 
					97d624114d | ||
| 
						 | 
					52e8a2e9e4 | ||
| 
						 | 
					261c271d60 | ||
| 
						 | 
					cb6e314336 | ||
| 
						 | 
					90315b3c40 | ||
| 
						 | 
					5d7c3d1622 | ||
| 
						 | 
					8c1ad1e9a6 | ||
| 
						 | 
					969f6dbe13 | ||
| 
						 | 
					6cce6d4c36 | ||
| 
						 | 
					d27e5e9c97 | ||
| 
						 | 
					af3b22f8b7 | ||
| 
						 | 
					cbc1b29f3e | ||
| 
						 | 
					f4ce8b8b6c | ||
| 
						 | 
					54363f1246 | ||
| 
						 | 
					11727391ad | ||
| 
						 | 
					d500531c04 | ||
| 
						 | 
					689dcd1e24 | ||
| 
						 | 
					2fc4e88271 | ||
| 
						 | 
					853d81c6dd | ||
| 
						 | 
					e35de626a4 | ||
| 
						 | 
					41f29c46d0 | ||
| 
						 | 
					9d95f5c1da | ||
| 
						 | 
					5d5cc96017 | ||
| 
						 | 
					c9a8911029 | ||
| 
						 | 
					c77a9ad363 | ||
| 
						 | 
					511af5845e | ||
| 
						 | 
					6b96089f02 | ||
| 
						 | 
					46be886ca6 | ||
| 
						 | 
					be7e167c63 | ||
| 
						 | 
					c65d78f568 | ||
| 
						 | 
					412a866de8 | ||
| 
						 | 
					e09c217fde | ||
| 
						 | 
					af62c2d9cf | ||
| 
						 | 
					aba3cd557a | ||
| 
						 | 
					7e7c83b3ca | ||
| 
						 | 
					ee4ccf2762 | ||
| 
						 | 
					fa0dcac2c7 | ||
| 
						 | 
					44e5b0c745 | ||
| 
						 | 
					b6d5cb4142 | ||
| 
						 | 
					4e8cba49f1 | ||
| 
						 | 
					506c2ba6c7 | ||
| 
						 | 
					050fa0d4c1 | ||
| 
						 | 
					88bb051f37 | ||
| 
						 | 
					0d800958aa | ||
| 
						 | 
					471533d041 | ||
| 
						 | 
					7dfc4c74da | ||
| 
						 | 
					f709350b04 | ||
| 
						 | 
					85c5928baa | ||
| 
						 | 
					f5dfbaff4b | ||
| 
						 | 
					689c2f11a3 | ||
| 
						 | 
					f73fd97525 | ||
| 
						 | 
					f38849828d | ||
| 
						 | 
					e95ba57a61 | ||
| 
						 | 
					4913256597 | ||
| 
						 | 
					401a386219 | ||
| 
						 | 
					205f41509b | ||
| 
						 | 
					e87c8d550b | ||
| 
						 | 
					a33b8abce8 | ||
| 
						 | 
					40523e6823 | ||
| 
						 | 
					5e1472185c | ||
| 
						 | 
					af005a6554 | ||
| 
						 | 
					efd31be21c | ||
| 
						 | 
					e9bda2810f | ||
| 
						 | 
					ec4777b8d0 | ||
| 
						 | 
					589b9e10b2 | ||
| 
						 | 
					2513ede3ec | ||
| 
						 | 
					0b5a57ead4 | ||
| 
						 | 
					b3dc2d43a5 | ||
| 
						 | 
					d3913be7e5 | ||
| 
						 | 
					2c94c3d96f | ||
| 
						 | 
					4d81153150 | ||
| 
						 | 
					507dc5f496 | ||
| 
						 | 
					4335543575 | ||
| 
						 | 
					9b75121337 | ||
| 
						 | 
					d262548d2e | ||
| 
						 | 
					b5b654e054 | ||
| 
						 | 
					dae8ab563c | ||
| 
						 | 
					12365976c4 | ||
| 
						 | 
					4ac4492241 | ||
| 
						 | 
					57b7dd0fa2 | ||
| 
						 | 
					9d4f471855 | ||
| 
						 | 
					5751e9ec59 | ||
| 
						 | 
					cc1b7a7a56 | ||
| 
						 | 
					29249cdc1b | ||
| 
						 | 
					e5bae8187f | ||
| 
						 | 
					69adebfefa | ||
| 
						 | 
					7dabbb65d0 | ||
| 
						 | 
					b30bab8c1b | ||
| 
						 | 
					5360e14a9c | ||
| 
						 | 
					86db559f6e | ||
| 
						 | 
					2f7a378c7b | ||
| 
						 | 
					607d0b4264 | ||
| 
						 | 
					17e1d4c245 | ||
| 
						 | 
					a031cc3b84 | ||
| 
						 | 
					c34d5111fc | ||
| 
						 | 
					727056a28c | ||
| 
						 | 
					0ca8dcd08e | ||
| 
						 | 
					3c7c4e1dba | ||
| 
						 | 
					518ecb4cc4 | ||
| 
						 | 
					1100f67b66 | ||
| 
						 | 
					056a28906b | ||
| 
						 | 
					2c2821cd96 | ||
| 
						 | 
					157a3e53dd | ||
| 
						 | 
					61edf8c196 | ||
| 
						 | 
					397f57ce74 | ||
| 
						 | 
					bf253c21fa | ||
| 
						 | 
					e3eef1cc6d | ||
| 
						 | 
					11f6e555f9 | ||
| 
						 | 
					164631fcec | ||
| 
						 | 
					a61e3fadf6 | ||
| 
						 | 
					b5f2d69ca5 | ||
| 
						 | 
					55b5c0fc32 | ||
| 
						 | 
					280b090dfc | ||
| 
						 | 
					9d97807587 | ||
| 
						 | 
					11433c8c17 | 
							
								
								
									
										4
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							@@ -40,9 +40,9 @@ jobs:
 | 
			
		||||
        arch: [amd64, armv7, aarch64]
 | 
			
		||||
        build_type: ["ha-addon", "docker", "lint"]
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.0.0
 | 
			
		||||
      - uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v4.7.0
 | 
			
		||||
        uses: actions/setup-python@v4.7.1
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										40
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -34,13 +34,13 @@ jobs:
 | 
			
		||||
      cache-key: ${{ steps.cache-key.outputs.key }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Generate cache-key
 | 
			
		||||
        id: cache-key
 | 
			
		||||
        run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Set up Python ${{ env.DEFAULT_PYTHON }}
 | 
			
		||||
        id: python
 | 
			
		||||
        uses: actions/setup-python@v4.7.0
 | 
			
		||||
        uses: actions/setup-python@v4.7.1
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: ${{ env.DEFAULT_PYTHON }}
 | 
			
		||||
      - name: Restore Python virtual environment
 | 
			
		||||
@@ -66,7 +66,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -87,7 +87,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -108,7 +108,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -129,7 +129,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -150,7 +150,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -171,7 +171,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -191,7 +191,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -210,6 +210,17 @@ jobs:
 | 
			
		||||
        run: script/ci-suggest-changes
 | 
			
		||||
        if: always()
 | 
			
		||||
 | 
			
		||||
  compile-tests-list:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    outputs:
 | 
			
		||||
      matrix: ${{ steps.set-matrix.outputs.matrix }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Find all YAML test files
 | 
			
		||||
        id: set-matrix
 | 
			
		||||
        run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
 | 
			
		||||
 | 
			
		||||
  compile-tests:
 | 
			
		||||
    name: Run YAML test ${{ matrix.file }}
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
@@ -222,23 +233,24 @@ jobs:
 | 
			
		||||
      - pylint
 | 
			
		||||
      - pytest
 | 
			
		||||
      - pyupgrade
 | 
			
		||||
      - compile-tests-list
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      max-parallel: 2
 | 
			
		||||
      matrix:
 | 
			
		||||
        file: [1, 2, 3, 3.1, 4, 5, 6, 7, 8, 10, 11.5]
 | 
			
		||||
        file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: ${{ env.DEFAULT_PYTHON }}
 | 
			
		||||
          cache-key: ${{ needs.common.outputs.cache-key }}
 | 
			
		||||
      - name: Run esphome compile tests/test${{ matrix.file }}.yaml
 | 
			
		||||
      - name: Run esphome compile ${{ matrix.file }}
 | 
			
		||||
        run: |
 | 
			
		||||
          . venv/bin/activate
 | 
			
		||||
          esphome compile tests/test${{ matrix.file }}.yaml
 | 
			
		||||
          esphome compile ${{ matrix.file }}
 | 
			
		||||
 | 
			
		||||
  clang-tidy:
 | 
			
		||||
    name: ${{ matrix.name }}
 | 
			
		||||
@@ -284,7 +296,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -19,7 +19,7 @@ jobs:
 | 
			
		||||
    outputs:
 | 
			
		||||
      tag: ${{ steps.tag.outputs.tag }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.0.0
 | 
			
		||||
      - uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Get tag
 | 
			
		||||
        id: tag
 | 
			
		||||
        # yamllint disable rule:line-length
 | 
			
		||||
@@ -43,9 +43,9 @@ jobs:
 | 
			
		||||
    if: github.repository == 'esphome/esphome' && github.event_name == 'release'
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.0.0
 | 
			
		||||
      - uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v4.7.0
 | 
			
		||||
        uses: actions/setup-python@v4.7.1
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.x"
 | 
			
		||||
      - name: Set up python environment
 | 
			
		||||
@@ -88,9 +88,9 @@ jobs:
 | 
			
		||||
            target: "lint"
 | 
			
		||||
            baseimg: "docker"
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.0.0
 | 
			
		||||
      - uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v4.7.0
 | 
			
		||||
        uses: actions/setup-python@v4.7.1
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							@@ -13,16 +13,16 @@ jobs:
 | 
			
		||||
    if: github.repository == 'esphome/esphome'
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
 | 
			
		||||
      - name: Checkout Home Assistant
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
        with:
 | 
			
		||||
          repository: home-assistant/core
 | 
			
		||||
          path: lib/home-assistant
 | 
			
		||||
 | 
			
		||||
      - name: Setup Python
 | 
			
		||||
        uses: actions/setup-python@v4.7.0
 | 
			
		||||
        uses: actions/setup-python@v4.7.1
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: 3.11
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/yaml-lint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/yaml-lint.yml
									
									
									
									
										vendored
									
									
								
							@@ -17,6 +17,6 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.0.0
 | 
			
		||||
        uses: actions/checkout@v4.1.0
 | 
			
		||||
      - name: Run yamllint
 | 
			
		||||
        uses: frenck/action-yamllint@v1.4.1
 | 
			
		||||
 
 | 
			
		||||
@@ -131,7 +131,7 @@ esphome/components/i2s_audio/* @jesserockz
 | 
			
		||||
esphome/components/i2s_audio/media_player/* @jesserockz
 | 
			
		||||
esphome/components/i2s_audio/microphone/* @jesserockz
 | 
			
		||||
esphome/components/i2s_audio/speaker/* @jesserockz
 | 
			
		||||
esphome/components/ili9xxx/* @nielsnl68
 | 
			
		||||
esphome/components/ili9xxx/* @clydebarrow @nielsnl68
 | 
			
		||||
esphome/components/improv_base/* @esphome/core
 | 
			
		||||
esphome/components/improv_serial/* @esphome/core
 | 
			
		||||
esphome/components/ina260/* @mreditor97
 | 
			
		||||
@@ -186,6 +186,7 @@ esphome/components/mitsubishi/* @RubyBailey
 | 
			
		||||
esphome/components/mlx90393/* @functionpointer
 | 
			
		||||
esphome/components/mlx90614/* @jesserockz
 | 
			
		||||
esphome/components/mmc5603/* @benhoff
 | 
			
		||||
esphome/components/mmc5983/* @agoode
 | 
			
		||||
esphome/components/modbus_controller/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/binary_sensor/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/number/* @martgras
 | 
			
		||||
@@ -270,7 +271,7 @@ esphome/components/sn74hc165/* @jesserockz
 | 
			
		||||
esphome/components/socket/* @esphome/core
 | 
			
		||||
esphome/components/sonoff_d1/* @anatoly-savchenkov
 | 
			
		||||
esphome/components/speaker/* @jesserockz
 | 
			
		||||
esphome/components/spi/* @esphome/core
 | 
			
		||||
esphome/components/spi/* @clydebarrow @esphome/core
 | 
			
		||||
esphome/components/spi_device/* @clydebarrow
 | 
			
		||||
esphome/components/spi_led_strip/* @clydebarrow
 | 
			
		||||
esphome/components/sprinkler/* @kbx81
 | 
			
		||||
 
 | 
			
		||||
@@ -26,8 +26,8 @@ RUN \
 | 
			
		||||
        python3-venv=3.9.2-3 \
 | 
			
		||||
        iputils-ping=3:20210202-1 \
 | 
			
		||||
        git=1:2.30.2-1+deb11u2 \
 | 
			
		||||
        curl=7.74.0-1.3+deb11u7 \
 | 
			
		||||
        openssh-client=1:8.4p1-5+deb11u1 \
 | 
			
		||||
        curl=7.74.0-1.3+deb11u10 \
 | 
			
		||||
        openssh-client=1:8.4p1-5+deb11u2 \
 | 
			
		||||
        python3-cffi=1.14.5-1 \
 | 
			
		||||
        libcairo2=1.16.0-5 \
 | 
			
		||||
        patch=2.7.6-7; \
 | 
			
		||||
 
 | 
			
		||||
@@ -41,8 +41,15 @@ fi
 | 
			
		||||
 | 
			
		||||
mkdir -p "${pio_cache_base}"
 | 
			
		||||
 | 
			
		||||
mkdir -p /config/esphome
 | 
			
		||||
 | 
			
		||||
if bashio::fs.directory_exists '/config/esphome/.esphome'; then
 | 
			
		||||
    bashio::log.info "Removing old .esphome directory..."
 | 
			
		||||
    bashio::log.info "Migrating old .esphome directory..."
 | 
			
		||||
    if bashio::fs.file_exists '/config/esphome/.esphome/esphome.json'; then
 | 
			
		||||
        mv /config/esphome/.esphome/esphome.json /data/esphome.json
 | 
			
		||||
    fi
 | 
			
		||||
    mkdir -p "/data/storage"
 | 
			
		||||
    mv /config/esphome/.esphome/*.json /data/storage/ || true
 | 
			
		||||
    rm -rf /config/esphome/.esphome
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_TYPE_ID,
 | 
			
		||||
    CONF_TIME,
 | 
			
		||||
    CONF_UPDATE_INTERVAL,
 | 
			
		||||
)
 | 
			
		||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
 | 
			
		||||
from esphome.util import Registry
 | 
			
		||||
@@ -69,6 +70,8 @@ WhileAction = cg.esphome_ns.class_("WhileAction", Action)
 | 
			
		||||
RepeatAction = cg.esphome_ns.class_("RepeatAction", Action)
 | 
			
		||||
WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
 | 
			
		||||
UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
 | 
			
		||||
SuspendComponentAction = cg.esphome_ns.class_("SuspendComponentAction", Action)
 | 
			
		||||
ResumeComponentAction = cg.esphome_ns.class_("ResumeComponentAction", Action)
 | 
			
		||||
Automation = cg.esphome_ns.class_("Automation")
 | 
			
		||||
 | 
			
		||||
LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
 | 
			
		||||
@@ -138,6 +141,7 @@ AUTOMATION_SCHEMA = cv.Schema(
 | 
			
		||||
AndCondition = cg.esphome_ns.class_("AndCondition", Condition)
 | 
			
		||||
OrCondition = cg.esphome_ns.class_("OrCondition", Condition)
 | 
			
		||||
NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
 | 
			
		||||
XorCondition = cg.esphome_ns.class_("XorCondition", Condition)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition("and", AndCondition, validate_condition_list)
 | 
			
		||||
@@ -158,6 +162,12 @@ async def not_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    return cg.new_Pvariable(condition_id, template_arg, condition)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition("xor", XorCondition, validate_condition_list)
 | 
			
		||||
async def xor_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    conditions = await build_condition_list(config, template_arg, args)
 | 
			
		||||
    return cg.new_Pvariable(condition_id, template_arg, conditions)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition("lambda", LambdaCondition, cv.returning_lambda)
 | 
			
		||||
async def lambda_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    lambda_ = await cg.process_lambda(config, args, return_type=bool)
 | 
			
		||||
@@ -303,6 +313,41 @@ async def component_update_action_to_code(config, action_id, template_arg, args)
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, comp)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action(
 | 
			
		||||
    "component.suspend",
 | 
			
		||||
    SuspendComponentAction,
 | 
			
		||||
    maybe_simple_id(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
async def component_suspend_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    comp = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, comp)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action(
 | 
			
		||||
    "component.resume",
 | 
			
		||||
    ResumeComponentAction,
 | 
			
		||||
    maybe_simple_id(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
 | 
			
		||||
            cv.Optional(CONF_UPDATE_INTERVAL): cv.templatable(
 | 
			
		||||
                cv.positive_time_period_milliseconds
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
async def component_resume_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    comp = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, comp)
 | 
			
		||||
    if CONF_UPDATE_INTERVAL in config:
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, int)
 | 
			
		||||
        cg.add(var.set_update_interval(template_))
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def build_action(full_config, template_arg, args):
 | 
			
		||||
    registry_entry, config = cg.extract_registry_entry_config(
 | 
			
		||||
        ACTION_REGISTRY, full_config
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,14 @@ from esphome.components import sensor, i2c
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_IRQ_PIN,
 | 
			
		||||
    CONF_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_CURRENT,
 | 
			
		||||
    DEVICE_CLASS_POWER,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    UNIT_AMPERE,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    UNIT_WATT,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -19,7 +20,6 @@ DEPENDENCIES = ["i2c"]
 | 
			
		||||
ade7953_ns = cg.esphome_ns.namespace("ade7953")
 | 
			
		||||
ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice)
 | 
			
		||||
 | 
			
		||||
CONF_IRQ_PIN = "irq_pin"
 | 
			
		||||
CONF_CURRENT_A = "current_a"
 | 
			
		||||
CONF_CURRENT_B = "current_b"
 | 
			
		||||
CONF_ACTIVE_POWER_A = "active_power_a"
 | 
			
		||||
 
 | 
			
		||||
@@ -151,7 +151,7 @@ async def to_code(config):
 | 
			
		||||
        pos = 0
 | 
			
		||||
        for frameIndex in range(frames):
 | 
			
		||||
            image.seek(frameIndex)
 | 
			
		||||
            frame = image.convert("LA", dither=Image.NONE)
 | 
			
		||||
            frame = image.convert("LA", dither=Image.Dither.NONE)
 | 
			
		||||
            if CONF_RESIZE in config:
 | 
			
		||||
                frame = frame.resize([width, height])
 | 
			
		||||
            pixels = list(frame.getdata())
 | 
			
		||||
@@ -259,7 +259,7 @@ async def to_code(config):
 | 
			
		||||
            if transparent:
 | 
			
		||||
                alpha = image.split()[-1]
 | 
			
		||||
                has_alpha = alpha.getextrema()[0] < 0xFF
 | 
			
		||||
            frame = image.convert("1", dither=Image.NONE)
 | 
			
		||||
            frame = image.convert("1", dither=Image.Dither.NONE)
 | 
			
		||||
            if CONF_RESIZE in config:
 | 
			
		||||
                frame = frame.resize([width, height])
 | 
			
		||||
                if transparent:
 | 
			
		||||
 
 | 
			
		||||
@@ -1413,6 +1413,18 @@ message SubscribeVoiceAssistantRequest {
 | 
			
		||||
  bool subscribe = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum VoiceAssistantRequestFlag {
 | 
			
		||||
  VOICE_ASSISTANT_REQUEST_NONE = 0;
 | 
			
		||||
  VOICE_ASSISTANT_REQUEST_USE_VAD = 1;
 | 
			
		||||
  VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantAudioSettings {
 | 
			
		||||
  uint32 noise_suppression_level = 1;
 | 
			
		||||
  uint32 auto_gain = 2;
 | 
			
		||||
  float volume_multiplier = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantRequest {
 | 
			
		||||
  option (id) = 90;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
@@ -1420,7 +1432,8 @@ message VoiceAssistantRequest {
 | 
			
		||||
 | 
			
		||||
  bool start = 1;
 | 
			
		||||
  string conversation_id = 2;
 | 
			
		||||
  bool use_vad = 3;
 | 
			
		||||
  uint32 flags = 3;
 | 
			
		||||
  VoiceAssistantAudioSettings audio_settings = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantResponse {
 | 
			
		||||
@@ -1442,6 +1455,12 @@ enum VoiceAssistantEvent {
 | 
			
		||||
  VOICE_ASSISTANT_INTENT_END = 6;
 | 
			
		||||
  VOICE_ASSISTANT_TTS_START = 7;
 | 
			
		||||
  VOICE_ASSISTANT_TTS_END = 8;
 | 
			
		||||
  VOICE_ASSISTANT_WAKE_WORD_START = 9;
 | 
			
		||||
  VOICE_ASSISTANT_WAKE_WORD_END = 10;
 | 
			
		||||
  VOICE_ASSISTANT_STT_VAD_START = 11;
 | 
			
		||||
  VOICE_ASSISTANT_STT_VAD_END = 12;
 | 
			
		||||
  VOICE_ASSISTANT_TTS_STREAM_START = 98;
 | 
			
		||||
  VOICE_ASSISTANT_TTS_STREAM_END = 99;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantEventData {
 | 
			
		||||
 
 | 
			
		||||
@@ -907,21 +907,22 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIConnection::request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad) {
 | 
			
		||||
bool APIConnection::request_voice_assistant(const VoiceAssistantRequest &msg) {
 | 
			
		||||
  if (!this->voice_assistant_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
  VoiceAssistantRequest msg;
 | 
			
		||||
  msg.start = start;
 | 
			
		||||
  msg.conversation_id = conversation_id;
 | 
			
		||||
  msg.use_vad = use_vad;
 | 
			
		||||
 | 
			
		||||
  return this->send_voice_assistant_request(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {
 | 
			
		||||
  if (voice_assistant::global_voice_assistant != nullptr) {
 | 
			
		||||
    if (msg.error) {
 | 
			
		||||
      voice_assistant::global_voice_assistant->failed_to_start();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    struct sockaddr_storage storage;
 | 
			
		||||
    socklen_t len = sizeof(storage);
 | 
			
		||||
    this->helper_->getpeername((struct sockaddr *) &storage, &len);
 | 
			
		||||
    voice_assistant::global_voice_assistant->start(&storage, msg.port);
 | 
			
		||||
    voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
 | 
			
		||||
 
 | 
			
		||||
@@ -124,7 +124,7 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override {
 | 
			
		||||
    this->voice_assistant_subscription_ = msg.subscribe;
 | 
			
		||||
  }
 | 
			
		||||
  bool request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad);
 | 
			
		||||
  bool request_voice_assistant(const VoiceAssistantRequest &msg);
 | 
			
		||||
  void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
 | 
			
		||||
  void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -410,6 +410,20 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::VoiceAssistantRequestFlag>(enums::VoiceAssistantRequestFlag value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::VOICE_ASSISTANT_REQUEST_NONE:
 | 
			
		||||
      return "VOICE_ASSISTANT_REQUEST_NONE";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_REQUEST_USE_VAD:
 | 
			
		||||
      return "VOICE_ASSISTANT_REQUEST_USE_VAD";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD:
 | 
			
		||||
      return "VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::VoiceAssistantEvent value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::VOICE_ASSISTANT_ERROR:
 | 
			
		||||
@@ -430,6 +444,18 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::V
 | 
			
		||||
      return "VOICE_ASSISTANT_TTS_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_TTS_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_TTS_END";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_WAKE_WORD_START:
 | 
			
		||||
      return "VOICE_ASSISTANT_WAKE_WORD_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_WAKE_WORD_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_WAKE_WORD_END";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_STT_VAD_START:
 | 
			
		||||
      return "VOICE_ASSISTANT_STT_VAD_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_STT_VAD_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_STT_VAD_END";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_TTS_STREAM_START:
 | 
			
		||||
      return "VOICE_ASSISTANT_TTS_STREAM_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_TTS_STREAM_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_TTS_STREAM_END";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
@@ -6344,6 +6370,56 @@ void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantAudioSettings::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->noise_suppression_level = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->auto_gain = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool VoiceAssistantAudioSettings::decode_32bit(uint32_t field_id, Proto32Bit value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->volume_multiplier = value.as_float();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantAudioSettings::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_uint32(1, this->noise_suppression_level);
 | 
			
		||||
  buffer.encode_uint32(2, this->auto_gain);
 | 
			
		||||
  buffer.encode_float(3, this->volume_multiplier);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantAudioSettings::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantAudioSettings {\n");
 | 
			
		||||
  out.append("  noise_suppression_level: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->noise_suppression_level);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  auto_gain: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->auto_gain);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  volume_multiplier: ");
 | 
			
		||||
  sprintf(buffer, "%g", this->volume_multiplier);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
@@ -6351,7 +6427,7 @@ bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->use_vad = value.as_bool();
 | 
			
		||||
      this->flags = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
@@ -6364,6 +6440,10 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite
 | 
			
		||||
      this->conversation_id = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 4: {
 | 
			
		||||
      this->audio_settings = value.as_message<VoiceAssistantAudioSettings>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -6371,7 +6451,8 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite
 | 
			
		||||
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_bool(1, this->start);
 | 
			
		||||
  buffer.encode_string(2, this->conversation_id);
 | 
			
		||||
  buffer.encode_bool(3, this->use_vad);
 | 
			
		||||
  buffer.encode_uint32(3, this->flags);
 | 
			
		||||
  buffer.encode_message<VoiceAssistantAudioSettings>(4, this->audio_settings);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantRequest::dump_to(std::string &out) const {
 | 
			
		||||
@@ -6385,8 +6466,13 @@ void VoiceAssistantRequest::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("'").append(this->conversation_id).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  use_vad: ");
 | 
			
		||||
  out.append(YESNO(this->use_vad));
 | 
			
		||||
  out.append("  flags: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->flags);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  audio_settings: ");
 | 
			
		||||
  this->audio_settings.dump_to(out);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -165,6 +165,11 @@ enum BluetoothDeviceRequestType : uint32_t {
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
 | 
			
		||||
};
 | 
			
		||||
enum VoiceAssistantRequestFlag : uint32_t {
 | 
			
		||||
  VOICE_ASSISTANT_REQUEST_NONE = 0,
 | 
			
		||||
  VOICE_ASSISTANT_REQUEST_USE_VAD = 1,
 | 
			
		||||
  VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD = 2,
 | 
			
		||||
};
 | 
			
		||||
enum VoiceAssistantEvent : uint32_t {
 | 
			
		||||
  VOICE_ASSISTANT_ERROR = 0,
 | 
			
		||||
  VOICE_ASSISTANT_RUN_START = 1,
 | 
			
		||||
@@ -175,6 +180,12 @@ enum VoiceAssistantEvent : uint32_t {
 | 
			
		||||
  VOICE_ASSISTANT_INTENT_END = 6,
 | 
			
		||||
  VOICE_ASSISTANT_TTS_START = 7,
 | 
			
		||||
  VOICE_ASSISTANT_TTS_END = 8,
 | 
			
		||||
  VOICE_ASSISTANT_WAKE_WORD_START = 9,
 | 
			
		||||
  VOICE_ASSISTANT_WAKE_WORD_END = 10,
 | 
			
		||||
  VOICE_ASSISTANT_STT_VAD_START = 11,
 | 
			
		||||
  VOICE_ASSISTANT_STT_VAD_END = 12,
 | 
			
		||||
  VOICE_ASSISTANT_TTS_STREAM_START = 98,
 | 
			
		||||
  VOICE_ASSISTANT_TTS_STREAM_END = 99,
 | 
			
		||||
};
 | 
			
		||||
enum AlarmControlPanelState : uint32_t {
 | 
			
		||||
  ALARM_STATE_DISARMED = 0,
 | 
			
		||||
@@ -1651,11 +1662,26 @@ class SubscribeVoiceAssistantRequest : public ProtoMessage {
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantAudioSettings : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t noise_suppression_level{0};
 | 
			
		||||
  uint32_t auto_gain{0};
 | 
			
		||||
  float volume_multiplier{0.0f};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool start{false};
 | 
			
		||||
  std::string conversation_id{};
 | 
			
		||||
  bool use_vad{false};
 | 
			
		||||
  uint32_t flags{0};
 | 
			
		||||
  VoiceAssistantAudioSettings audio_settings{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/util.h"
 | 
			
		||||
#include "esphome/core/version.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LOGGER
 | 
			
		||||
#include "esphome/components/logger/logger.h"
 | 
			
		||||
@@ -323,16 +323,24 @@ void APIServer::on_shutdown() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIServer::start_voice_assistant(const std::string &conversation_id, bool use_vad) {
 | 
			
		||||
bool APIServer::start_voice_assistant(const std::string &conversation_id, uint32_t flags,
 | 
			
		||||
                                      const api::VoiceAssistantAudioSettings &audio_settings) {
 | 
			
		||||
  VoiceAssistantRequest msg;
 | 
			
		||||
  msg.start = true;
 | 
			
		||||
  msg.conversation_id = conversation_id;
 | 
			
		||||
  msg.flags = flags;
 | 
			
		||||
  msg.audio_settings = audio_settings;
 | 
			
		||||
  for (auto &c : this->clients_) {
 | 
			
		||||
    if (c->request_voice_assistant(true, conversation_id, use_vad))
 | 
			
		||||
    if (c->request_voice_assistant(msg))
 | 
			
		||||
      return true;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
void APIServer::stop_voice_assistant() {
 | 
			
		||||
  VoiceAssistantRequest msg;
 | 
			
		||||
  msg.start = false;
 | 
			
		||||
  for (auto &c : this->clients_) {
 | 
			
		||||
    if (c->request_voice_assistant(false, "", false))
 | 
			
		||||
    if (c->request_voice_assistant(msg))
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "api_noise_context.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "api_pb2_service.h"
 | 
			
		||||
#include "esphome/components/socket/socket.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/controller.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/components/socket/socket.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "api_pb2_service.h"
 | 
			
		||||
#include "list_entities.h"
 | 
			
		||||
#include "subscribe_state.h"
 | 
			
		||||
#include "user_services.h"
 | 
			
		||||
#include "api_noise_context.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -81,7 +81,8 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool start_voice_assistant(const std::string &conversation_id, bool use_vad);
 | 
			
		||||
  bool start_voice_assistant(const std::string &conversation_id, uint32_t flags,
 | 
			
		||||
                             const api::VoiceAssistantAudioSettings &audio_settings);
 | 
			
		||||
  void stop_voice_assistant();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,17 @@ import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CAPACITANCE,
 | 
			
		||||
    CONF_DIV_RATIO,
 | 
			
		||||
    CONF_INDOOR,
 | 
			
		||||
    CONF_WATCHDOG_THRESHOLD,
 | 
			
		||||
    CONF_NOISE_LEVEL,
 | 
			
		||||
    CONF_SPIKE_REJECTION,
 | 
			
		||||
    CONF_IRQ_PIN,
 | 
			
		||||
    CONF_LIGHTNING_THRESHOLD,
 | 
			
		||||
    CONF_MASK_DISTURBER,
 | 
			
		||||
    CONF_DIV_RATIO,
 | 
			
		||||
    CONF_CAPACITANCE,
 | 
			
		||||
    CONF_CALIBRATION,
 | 
			
		||||
    CONF_TUNE_ANTENNA,
 | 
			
		||||
    CONF_NOISE_LEVEL,
 | 
			
		||||
    CONF_SPIKE_REJECTION,
 | 
			
		||||
    CONF_WATCHDOG_THRESHOLD,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
@@ -19,7 +22,6 @@ CONF_AS3935_ID = "as3935_id"
 | 
			
		||||
as3935_ns = cg.esphome_ns.namespace("as3935")
 | 
			
		||||
AS3935 = as3935_ns.class_("AS3935Component", cg.Component)
 | 
			
		||||
 | 
			
		||||
CONF_IRQ_PIN = "irq_pin"
 | 
			
		||||
AS3935_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(AS3935),
 | 
			
		||||
@@ -34,6 +36,8 @@ AS3935_SCHEMA = cv.Schema(
 | 
			
		||||
        cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean,
 | 
			
		||||
        cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 32, 64, 128, int=True),
 | 
			
		||||
        cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15),
 | 
			
		||||
        cv.Optional(CONF_TUNE_ANTENNA, default=False): cv.boolean,
 | 
			
		||||
        cv.Optional(CONF_CALIBRATION, default=True): cv.boolean,
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -51,3 +55,5 @@ async def setup_as3935(var, config):
 | 
			
		||||
    cg.add(var.set_mask_disturber(config[CONF_MASK_DISTURBER]))
 | 
			
		||||
    cg.add(var.set_div_ratio(config[CONF_DIV_RATIO]))
 | 
			
		||||
    cg.add(var.set_capacitance(config[CONF_CAPACITANCE]))
 | 
			
		||||
    cg.add(var.set_tune_antenna(config[CONF_TUNE_ANTENNA]))
 | 
			
		||||
    cg.add(var.set_calibration(config[CONF_CALIBRATION]))
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,14 @@ void AS3935Component::setup() {
 | 
			
		||||
  this->write_mask_disturber(this->mask_disturber_);
 | 
			
		||||
  this->write_div_ratio(this->div_ratio_);
 | 
			
		||||
  this->write_capacitance(this->capacitance_);
 | 
			
		||||
 | 
			
		||||
  // Handle setting up tuning or auto-calibration
 | 
			
		||||
  if (this->tune_antenna_) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Antenna tuning: ENABLED - lightning detection will not function in this mode");
 | 
			
		||||
    this->tune_antenna();
 | 
			
		||||
  } else if (this->calibration_) {
 | 
			
		||||
    this->calibrate_oscillator();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AS3935Component::dump_config() {
 | 
			
		||||
@@ -227,6 +235,87 @@ uint32_t AS3935Component::get_lightning_energy_() {
 | 
			
		||||
  return pure_light;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// REG0x03, bit [7:6], manufacturer default: 0 (16 division ratio).
 | 
			
		||||
// This function returns the current division ratio of the resonance frequency.
 | 
			
		||||
// The antenna resonance frequency should be within 3.5 percent of 500kHz, and
 | 
			
		||||
// so when modifying the resonance frequency with the internal capacitors
 | 
			
		||||
// (tuneCap()) it's important to keep in mind that the displayed frequency on
 | 
			
		||||
// the IRQ pin is divided by this number.
 | 
			
		||||
uint8_t AS3935Component::read_div_ratio() {
 | 
			
		||||
  ESP_LOGV(TAG, "Calling read_div_ratio");
 | 
			
		||||
  uint8_t reg_val = this->read_register_(INT_MASK_ANT, DIV_MASK);
 | 
			
		||||
  reg_val >>= 6;  // Front of the line.
 | 
			
		||||
 | 
			
		||||
  if (reg_val == 0) {
 | 
			
		||||
    return 16;
 | 
			
		||||
  } else if (reg_val == 1) {
 | 
			
		||||
    return 32;
 | 
			
		||||
  } else if (reg_val == 2) {
 | 
			
		||||
    return 64;
 | 
			
		||||
  } else if (reg_val == 3) {
 | 
			
		||||
    return 128;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGW(TAG, "Unknown response received for div_ratio");
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t AS3935Component::read_capacitance() {
 | 
			
		||||
  ESP_LOGV(TAG, "Calling read_capacitance");
 | 
			
		||||
  uint8_t reg_val = this->read_register_(FREQ_DISP_IRQ, CAP_MASK) * 8;
 | 
			
		||||
  return (reg_val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// REG0x08, bits [5,6,7], manufacturer default: 0.
 | 
			
		||||
// This will send the frequency of the oscillators to the IRQ pin.
 | 
			
		||||
//  _osc 1, bit[5] = TRCO - System RCO at 32.768kHz
 | 
			
		||||
//  _osc 2, bit[6] = SRCO - Timer RCO Oscillators 1.1MHz
 | 
			
		||||
//  _osc 3, bit[7] = LCO - Frequency of the Antenna
 | 
			
		||||
void AS3935Component::display_oscillator(bool state, uint8_t osc) {
 | 
			
		||||
  if ((osc < 1) || (osc > 3))
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->write_register(FREQ_DISP_IRQ, OSC_MASK, state, 4 + osc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// REG0x3D, bits[7:0]
 | 
			
		||||
// This function calibrates both internal oscillators The oscillators are tuned
 | 
			
		||||
// based on the resonance frequency of the antenna and so it should be trimmed
 | 
			
		||||
// before the calibration is done.
 | 
			
		||||
bool AS3935Component::calibrate_oscillator() {
 | 
			
		||||
  ESP_LOGI(TAG, "Starting oscillators calibration...");
 | 
			
		||||
  this->write_register(CALIB_RCO, WIPE_ALL, DIRECT_COMMAND, 0);  // Send command to calibrate the oscillators
 | 
			
		||||
 | 
			
		||||
  this->display_oscillator(true, 2);
 | 
			
		||||
  delay(2);  // Give time for the internal oscillators to start up.
 | 
			
		||||
  this->display_oscillator(false, 2);
 | 
			
		||||
 | 
			
		||||
  // Check it they were calibrated successfully.
 | 
			
		||||
  uint8_t reg_val_srco = this->read_register_(CALIB_SRCO, CALIB_MASK_NOK);
 | 
			
		||||
  uint8_t reg_val_trco = this->read_register_(CALIB_TRCO, CALIB_MASK_NOK);
 | 
			
		||||
 | 
			
		||||
  // reg_val_srco &= CALIB_MASK;
 | 
			
		||||
  // reg_val_srco >>= 6;
 | 
			
		||||
  // reg_val_trco &= CALIB_MASK;
 | 
			
		||||
  // reg_val_trco >>= 6;
 | 
			
		||||
  if (!reg_val_srco && !reg_val_trco) {  // Zero upon success
 | 
			
		||||
    ESP_LOGI(TAG, "Calibration was succesful");
 | 
			
		||||
    return true;
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGW(TAG, "Calibration was NOT succesful");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AS3935Component::tune_antenna() {
 | 
			
		||||
  ESP_LOGI(TAG, "Starting antenna tuning...");
 | 
			
		||||
  uint8_t div_ratio = this->read_div_ratio();
 | 
			
		||||
  uint8_t tune_val = this->read_capacitance();
 | 
			
		||||
  ESP_LOGI(TAG, "Division Ratio is set to: %d", div_ratio);
 | 
			
		||||
  ESP_LOGI(TAG, "Internal Capacitor is set to: %d", tune_val);
 | 
			
		||||
  ESP_LOGI(TAG, "Displaying oscillator on INT pin. Measure its frequency - multiply value by Division Ratio");
 | 
			
		||||
  this->display_oscillator(true, ANTFREQ);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t AS3935Component::read_register_(uint8_t reg, uint8_t mask) {
 | 
			
		||||
  uint8_t value = this->read_register(reg);
 | 
			
		||||
  value &= (~mask);
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,9 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace as3935 {
 | 
			
		||||
 | 
			
		||||
static const uint8_t DIRECT_COMMAND = 0x96;
 | 
			
		||||
static const uint8_t ANTFREQ = 3;
 | 
			
		||||
 | 
			
		||||
enum AS3935RegisterNames {
 | 
			
		||||
  AFE_GAIN = 0x00,
 | 
			
		||||
  THRESHOLD,
 | 
			
		||||
@@ -30,6 +33,7 @@ enum AS3935RegisterNames {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum AS3935RegisterMasks {
 | 
			
		||||
  WIPE_ALL = 0x0,
 | 
			
		||||
  GAIN_MASK = 0x3E,
 | 
			
		||||
  SPIKE_MASK = 0xF,
 | 
			
		||||
  IO_MASK = 0xC1,
 | 
			
		||||
@@ -44,6 +48,7 @@ enum AS3935RegisterMasks {
 | 
			
		||||
  NOISE_FLOOR_MASK = 0x70,
 | 
			
		||||
  OSC_MASK = 0xE0,
 | 
			
		||||
  CALIB_MASK = 0x7F,
 | 
			
		||||
  CALIB_MASK_NOK = 0xBF,
 | 
			
		||||
  DIV_MASK = 0x3F
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -90,6 +95,13 @@ class AS3935Component : public Component {
 | 
			
		||||
  void write_div_ratio(uint8_t div_ratio);
 | 
			
		||||
  void set_capacitance(uint8_t capacitance) { capacitance_ = capacitance; }
 | 
			
		||||
  void write_capacitance(uint8_t capacitance);
 | 
			
		||||
  uint8_t read_div_ratio();
 | 
			
		||||
  uint8_t read_capacitance();
 | 
			
		||||
  bool calibrate_oscillator();
 | 
			
		||||
  void display_oscillator(bool state, uint8_t osc);
 | 
			
		||||
  void tune_antenna();
 | 
			
		||||
  void set_tune_antenna(bool tune_antenna) { tune_antenna_ = tune_antenna; }
 | 
			
		||||
  void set_calibration(bool calibration) { calibration_ = calibration; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint8_t read_interrupt_register_();
 | 
			
		||||
@@ -112,6 +124,8 @@ class AS3935Component : public Component {
 | 
			
		||||
  bool mask_disturber_;
 | 
			
		||||
  uint8_t div_ratio_;
 | 
			
		||||
  uint8_t capacitance_;
 | 
			
		||||
  bool tune_antenna_;
 | 
			
		||||
  bool calibration_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace as3935
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,19 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    PLATFORM_ESP32,
 | 
			
		||||
    PLATFORM_ESP8266,
 | 
			
		||||
    PLATFORM_BK72XX,
 | 
			
		||||
    PLATFORM_RTL87XX,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@OttoWinter"]
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.Schema({}),
 | 
			
		||||
    cv.only_with_arduino,
 | 
			
		||||
    cv.only_on(["esp32", "esp8266", "bk72xx", "rtl87xx"]),
 | 
			
		||||
    cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,8 @@ 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;
 | 
			
		||||
 | 
			
		||||
static const uint8_t BP1658CJ_DELAY = 2;
 | 
			
		||||
 | 
			
		||||
void BP1658CJ::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up BP1658CJ Output Component...");
 | 
			
		||||
  this->data_pin_->setup();
 | 
			
		||||
@@ -35,10 +37,14 @@ void BP1658CJ::loop() {
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
    // First turn all channels off
 | 
			
		||||
    data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_START_5CH;
 | 
			
		||||
    this->write_buffer_(data, 12);
 | 
			
		||||
    // Then sleep
 | 
			
		||||
    data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_STANDBY;
 | 
			
		||||
    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)) {
 | 
			
		||||
@@ -81,27 +87,41 @@ void BP1658CJ::set_channel_value_(uint8_t channel, uint16_t value) {
 | 
			
		||||
  }
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  delayMicroseconds(BP1658CJ_DELAY);
 | 
			
		||||
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP1658CJ::write_byte_(uint8_t data) {
 | 
			
		||||
  for (uint8_t mask = 0x80; mask; mask >>= 1) {
 | 
			
		||||
    this->write_bit_(data & mask);
 | 
			
		||||
    delayMicroseconds(BP1658CJ_DELAY);
 | 
			
		||||
  }
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  this->data_pin_->digital_write(true);
 | 
			
		||||
 | 
			
		||||
  // ack bit
 | 
			
		||||
  this->data_pin_->pin_mode(gpio::FLAG_INPUT);
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
 | 
			
		||||
  delayMicroseconds(BP1658CJ_DELAY);
 | 
			
		||||
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  this->data_pin_->pin_mode(gpio::FLAG_OUTPUT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP1658CJ::write_buffer_(uint8_t *buffer, uint8_t size) {
 | 
			
		||||
  this->data_pin_->digital_write(false);
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
 | 
			
		||||
  for (uint32_t i = 0; i < size; i++) {
 | 
			
		||||
    this->write_byte_(buffer[i]);
 | 
			
		||||
    delayMicroseconds(BP1658CJ_DELAY);
 | 
			
		||||
  }
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
  this->data_pin_->digital_write(true);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,12 +17,16 @@ 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;
 | 
			
		||||
 | 
			
		||||
static const uint8_t BP5758D_DELAY = 2;
 | 
			
		||||
 | 
			
		||||
void BP5758D::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up BP5758D Output Component...");
 | 
			
		||||
  this->data_pin_->setup();
 | 
			
		||||
  this->data_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(BP5758D_DELAY);
 | 
			
		||||
  this->clock_pin_->setup();
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(BP5758D_DELAY);
 | 
			
		||||
  this->channel_current_.resize(5, 0);
 | 
			
		||||
  this->pwm_amounts_.resize(5, 0);
 | 
			
		||||
}
 | 
			
		||||
@@ -39,10 +43,14 @@ void BP5758D::loop() {
 | 
			
		||||
  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++)
 | 
			
		||||
    for (int i = 1; i < 17; i++)
 | 
			
		||||
      data[i] = 0;
 | 
			
		||||
 | 
			
		||||
    // First turn all channels off
 | 
			
		||||
    data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_5CH;
 | 
			
		||||
    this->write_buffer_(data, 17);
 | 
			
		||||
    // Then sleep
 | 
			
		||||
    data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_STANDBY;
 | 
			
		||||
    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)) {
 | 
			
		||||
@@ -119,28 +127,42 @@ void BP5758D::set_channel_value_(uint8_t channel, uint16_t 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);
 | 
			
		||||
  delayMicroseconds(BP5758D_DELAY);
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(BP5758D_DELAY);
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(BP5758D_DELAY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
  // ack bit
 | 
			
		||||
  this->data_pin_->pin_mode(gpio::FLAG_INPUT);
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(BP5758D_DELAY);
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(BP5758D_DELAY);
 | 
			
		||||
  this->data_pin_->pin_mode(gpio::FLAG_OUTPUT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BP5758D::write_buffer_(uint8_t *buffer, uint8_t size) {
 | 
			
		||||
  this->data_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(BP5758D_DELAY);
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(BP5758D_DELAY);
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
  delayMicroseconds(BP5758D_DELAY);
 | 
			
		||||
  this->data_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(BP5758D_DELAY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace bp5758d
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,13 @@ import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import web_server_base
 | 
			
		||||
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    PLATFORM_ESP32,
 | 
			
		||||
    PLATFORM_ESP8266,
 | 
			
		||||
    PLATFORM_BK72XX,
 | 
			
		||||
    PLATFORM_RTL87XX,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import coroutine_with_priority, CORE
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["web_server_base"]
 | 
			
		||||
@@ -21,7 +27,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    ).extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
    cv.only_on(["esp32", "esp8266", "bk72xx", "rtl87xx"]),
 | 
			
		||||
    cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ void CaptivePortal::start() {
 | 
			
		||||
  this->dns_server_ = make_unique<DNSServer>();
 | 
			
		||||
  this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
 | 
			
		||||
  network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
 | 
			
		||||
  this->dns_server_->start(53, "*", (uint32_t) ip);
 | 
			
		||||
  this->dns_server_->start(53, "*", IPAddress(ip));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,8 @@ from esphome.const import (
 | 
			
		||||
    CONF_SLEEP_DURATION,
 | 
			
		||||
    CONF_TIME_ID,
 | 
			
		||||
    CONF_WAKEUP_PIN,
 | 
			
		||||
    PLATFORM_ESP32,
 | 
			
		||||
    PLATFORM_ESP8266,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from esphome.components.esp32 import get_esp32_variant
 | 
			
		||||
@@ -24,6 +26,7 @@ from esphome.components.esp32.const import (
 | 
			
		||||
    VARIANT_ESP32S3,
 | 
			
		||||
    VARIANT_ESP32C2,
 | 
			
		||||
    VARIANT_ESP32C6,
 | 
			
		||||
    VARIANT_ESP32H2,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
WAKEUP_PINS = {
 | 
			
		||||
@@ -98,6 +101,7 @@ WAKEUP_PINS = {
 | 
			
		||||
    ],
 | 
			
		||||
    VARIANT_ESP32C2: [0, 1, 2, 3, 4, 5],
 | 
			
		||||
    VARIANT_ESP32C6: [0, 1, 2, 3, 4, 5, 6, 7],
 | 
			
		||||
    VARIANT_ESP32H2: [7, 8, 9, 10, 11, 12, 13, 14],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -163,34 +167,39 @@ WAKEUP_CAUSES_SCHEMA = cv.Schema(
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(DeepSleepComponent),
 | 
			
		||||
        cv.Optional(CONF_RUN_DURATION): cv.Any(
 | 
			
		||||
            cv.All(cv.only_on_esp32, WAKEUP_CAUSES_SCHEMA),
 | 
			
		||||
            cv.positive_time_period_milliseconds,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
 | 
			
		||||
        cv.Optional(CONF_WAKEUP_PIN): cv.All(
 | 
			
		||||
            cv.only_on_esp32, pins.internal_gpio_input_pin_schema, validate_pin_number
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All(
 | 
			
		||||
            cv.only_on_esp32, cv.enum(WAKEUP_PIN_MODES), upper=True
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ESP32_EXT1_WAKEUP): cv.All(
 | 
			
		||||
            cv.only_on_esp32,
 | 
			
		||||
            cv.Schema(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Required(CONF_PINS): cv.ensure_list(
 | 
			
		||||
                        pins.internal_gpio_input_pin_schema, validate_pin_number
 | 
			
		||||
                    ),
 | 
			
		||||
                    cv.Required(CONF_MODE): cv.enum(EXT1_WAKEUP_MODES, upper=True),
 | 
			
		||||
                }
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(DeepSleepComponent),
 | 
			
		||||
            cv.Optional(CONF_RUN_DURATION): cv.Any(
 | 
			
		||||
                cv.All(cv.only_on_esp32, WAKEUP_CAUSES_SCHEMA),
 | 
			
		||||
                cv.positive_time_period_milliseconds,
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TOUCH_WAKEUP): cv.All(cv.only_on_esp32, cv.boolean),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
            cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
 | 
			
		||||
            cv.Optional(CONF_WAKEUP_PIN): cv.All(
 | 
			
		||||
                cv.only_on_esp32,
 | 
			
		||||
                pins.internal_gpio_input_pin_schema,
 | 
			
		||||
                validate_pin_number,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All(
 | 
			
		||||
                cv.only_on_esp32, cv.enum(WAKEUP_PIN_MODES), upper=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ESP32_EXT1_WAKEUP): cv.All(
 | 
			
		||||
                cv.only_on_esp32,
 | 
			
		||||
                cv.Schema(
 | 
			
		||||
                    {
 | 
			
		||||
                        cv.Required(CONF_PINS): cv.ensure_list(
 | 
			
		||||
                            pins.internal_gpio_input_pin_schema, validate_pin_number
 | 
			
		||||
                        ),
 | 
			
		||||
                        cv.Required(CONF_MODE): cv.enum(EXT1_WAKEUP_MODES, upper=True),
 | 
			
		||||
                    }
 | 
			
		||||
                ),
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TOUCH_WAKEUP): cv.All(cv.only_on_esp32, cv.boolean),
 | 
			
		||||
        }
 | 
			
		||||
    ).extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
    cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266]),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,6 @@ void E131Component::setup() {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  server.ss_family = AF_INET;
 | 
			
		||||
 | 
			
		||||
  err = this->socket_->bind((struct sockaddr *) &server, sizeof(server));
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -67,8 +67,8 @@ bool E131Component::join_igmp_groups_() {
 | 
			
		||||
    if (!universe.second)
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    ip4_addr_t multicast_addr = {static_cast<uint32_t>(
 | 
			
		||||
        network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)))};
 | 
			
		||||
    ip4_addr_t multicast_addr =
 | 
			
		||||
        network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff));
 | 
			
		||||
 | 
			
		||||
    auto err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr);
 | 
			
		||||
 | 
			
		||||
@@ -101,8 +101,7 @@ void E131Component::leave_(int universe) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (listen_method_ == E131_MULTICAST) {
 | 
			
		||||
    ip4_addr_t multicast_addr = {
 | 
			
		||||
        static_cast<uint32_t>(network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)))};
 | 
			
		||||
    ip4_addr_t multicast_addr = network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff));
 | 
			
		||||
 | 
			
		||||
    igmp_leavegroup(IP4_ADDR_ANY4, &multicast_addr);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ from esphome.const import (
 | 
			
		||||
    KEY_NAME,
 | 
			
		||||
    KEY_TARGET_FRAMEWORK,
 | 
			
		||||
    KEY_TARGET_PLATFORM,
 | 
			
		||||
    PLATFORM_ESP32,
 | 
			
		||||
    TYPE_GIT,
 | 
			
		||||
    TYPE_LOCAL,
 | 
			
		||||
    __version__,
 | 
			
		||||
@@ -62,7 +63,7 @@ AUTO_LOAD = ["preferences"]
 | 
			
		||||
 | 
			
		||||
def set_core_data(config):
 | 
			
		||||
    CORE.data[KEY_ESP32] = {}
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp32"
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_ESP32
 | 
			
		||||
    conf = config[CONF_FRAMEWORK]
 | 
			
		||||
    if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF:
 | 
			
		||||
        CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "esp-idf"
 | 
			
		||||
 
 | 
			
		||||
@@ -235,6 +235,7 @@ ESP32_BOARD_PINS = {
 | 
			
		||||
        "SDA": 5,
 | 
			
		||||
        "SS": 15,
 | 
			
		||||
    },
 | 
			
		||||
    "denky_d4": {"RX": 8, "LED": 14},
 | 
			
		||||
    "esp-wrover-kit": {},
 | 
			
		||||
    "esp32-devkitlipo": {},
 | 
			
		||||
    "esp32-evb": {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,53 @@
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
 | 
			
		||||
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
 | 
			
		||||
_ESP32H2_SPI_FLASH_PINS = {6, 7, 15, 16, 17, 18, 19, 20, 21}
 | 
			
		||||
 | 
			
		||||
_ESP32H2_USB_JTAG_PINS = {26, 27}
 | 
			
		||||
 | 
			
		||||
_ESP32H2_STRAPPING_PINS = {2, 3, 8, 9, 25}
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def esp32_h2_validate_gpio_pin(value):
 | 
			
		||||
    # ESP32-H2 not yet supported
 | 
			
		||||
    raise cv.Invalid("ESP32-H2 isn't supported yet")
 | 
			
		||||
    if value < 0 or value > 27:
 | 
			
		||||
        raise cv.Invalid(f"Invalid pin number: {value} (must be 0-27)")
 | 
			
		||||
    if value in _ESP32H2_STRAPPING_PINS:
 | 
			
		||||
        _LOGGER.warning(
 | 
			
		||||
            "GPIO%d is a Strapping PIN and should be avoided.\n"
 | 
			
		||||
            "Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n"
 | 
			
		||||
            "See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins",
 | 
			
		||||
            value,
 | 
			
		||||
        )
 | 
			
		||||
    if value in _ESP32H2_SPI_FLASH_PINS:
 | 
			
		||||
        _LOGGER.warning(
 | 
			
		||||
            "GPIO%d is reserved for SPI Flash communication on some ESP32-H2 chip variants.\n"
 | 
			
		||||
            "Utilizing SPI-reserved pins could cause unexpected failures.\n"
 | 
			
		||||
            "See https://docs.espressif.com/projects/esp-idf/en/latest/esp32h2/api-reference/peripherals/gpio.html",
 | 
			
		||||
            value,
 | 
			
		||||
        )
 | 
			
		||||
    if value in _ESP32H2_USB_JTAG_PINS:
 | 
			
		||||
        _LOGGER.warning(
 | 
			
		||||
            "GPIO%d is reserved for the USB-Serial-JTAG interface.\n"
 | 
			
		||||
            "To use this pin as GPIO, USB-Serial-JTAG will be disabled.",
 | 
			
		||||
            value,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def esp32_h2_validate_supports(value):
 | 
			
		||||
    # ESP32-H2 not yet supported
 | 
			
		||||
    raise cv.Invalid("ESP32-H2 isn't supported yet")
 | 
			
		||||
    num = value[CONF_NUMBER]
 | 
			
		||||
    mode = value[CONF_MODE]
 | 
			
		||||
    is_input = mode[CONF_INPUT]
 | 
			
		||||
 | 
			
		||||
    if num < 0 or num > 27:
 | 
			
		||||
        raise cv.Invalid(f"Invalid pin number: {value} (must be 0-27)")
 | 
			
		||||
    if is_input:
 | 
			
		||||
        # All ESP32 pins support input mode
 | 
			
		||||
        pass
 | 
			
		||||
    return value
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,9 @@
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include "ble_uuid.h"
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include "ble_uuid.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -16,8 +16,8 @@ BLEAdvertising::BLEAdvertising() {
 | 
			
		||||
  this->advertising_data_.set_scan_rsp = false;
 | 
			
		||||
  this->advertising_data_.include_name = true;
 | 
			
		||||
  this->advertising_data_.include_txpower = true;
 | 
			
		||||
  this->advertising_data_.min_interval = 0x20;
 | 
			
		||||
  this->advertising_data_.max_interval = 0x40;
 | 
			
		||||
  this->advertising_data_.min_interval = 0;
 | 
			
		||||
  this->advertising_data_.max_interval = 0;
 | 
			
		||||
  this->advertising_data_.appearance = 0x00;
 | 
			
		||||
  this->advertising_data_.manufacturer_len = 0;
 | 
			
		||||
  this->advertising_data_.p_manufacturer_data = nullptr;
 | 
			
		||||
@@ -42,6 +42,17 @@ void BLEAdvertising::remove_service_uuid(ESPBTUUID uuid) {
 | 
			
		||||
                                 this->advertising_uuids_.end());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEAdvertising::set_service_data(const std::vector<uint8_t> &data) {
 | 
			
		||||
  delete[] this->advertising_data_.p_service_data;
 | 
			
		||||
  this->advertising_data_.p_service_data = nullptr;
 | 
			
		||||
  this->advertising_data_.service_data_len = data.size();
 | 
			
		||||
  if (!data.empty()) {
 | 
			
		||||
    // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
 | 
			
		||||
    this->advertising_data_.p_service_data = new uint8_t[data.size()];
 | 
			
		||||
    memcpy(this->advertising_data_.p_service_data, data.data(), data.size());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) {
 | 
			
		||||
  delete[] this->advertising_data_.p_manufacturer_data;
 | 
			
		||||
  this->advertising_data_.p_manufacturer_data = nullptr;
 | 
			
		||||
@@ -85,8 +96,6 @@ void BLEAdvertising::start() {
 | 
			
		||||
    this->scan_response_data_.set_scan_rsp = true;
 | 
			
		||||
    this->scan_response_data_.include_name = true;
 | 
			
		||||
    this->scan_response_data_.include_txpower = true;
 | 
			
		||||
    this->scan_response_data_.min_interval = 0;
 | 
			
		||||
    this->scan_response_data_.max_interval = 0;
 | 
			
		||||
    this->scan_response_data_.manufacturer_len = 0;
 | 
			
		||||
    this->scan_response_data_.appearance = 0;
 | 
			
		||||
    this->scan_response_data_.flag = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ class BLEAdvertising {
 | 
			
		||||
  void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; }
 | 
			
		||||
  void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; }
 | 
			
		||||
  void set_manufacturer_data(const std::vector<uint8_t> &data);
 | 
			
		||||
  void set_service_data(const std::vector<uint8_t> &data);
 | 
			
		||||
 | 
			
		||||
  void start();
 | 
			
		||||
  void stop();
 | 
			
		||||
 
 | 
			
		||||
@@ -90,6 +90,8 @@ void BLEService::stop() {
 | 
			
		||||
    ESP_LOGE(TAG, "esp_ble_gatts_stop_service failed: %d", err);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  esp32_ble::global_ble->get_advertising()->remove_service_uuid(this->uuid_);
 | 
			
		||||
  esp32_ble::global_ble->get_advertising()->start();
 | 
			
		||||
  this->running_state_ = STOPPING;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,11 @@
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include <esp_bt_defs.h>
 | 
			
		||||
#include <esp_gap_ble_api.h>
 | 
			
		||||
#include <esp_gatt_defs.h>
 | 
			
		||||
#include <esp_gattc_api.h>
 | 
			
		||||
#include <esp_gatts_api.h>
 | 
			
		||||
#include <esp_bt_defs.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32_ble_server {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ from esphome.components import binary_sensor, output, esp32_ble_server
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["binary_sensor", "output", "esp32_ble_server"]
 | 
			
		||||
AUTO_LOAD = ["esp32_ble_server"]
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
CONFLICTS_WITH = ["esp32_ble_beacon"]
 | 
			
		||||
DEPENDENCIES = ["wifi", "esp32"]
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,17 @@ ESP32ImprovComponent::ESP32ImprovComponent() { global_improv_component = this; }
 | 
			
		||||
void ESP32ImprovComponent::setup() {
 | 
			
		||||
  this->service_ = global_ble_server->create_service(improv::SERVICE_UUID, true);
 | 
			
		||||
  this->setup_characteristics();
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  if (this->authorizer_ != nullptr) {
 | 
			
		||||
    this->authorizer_->add_on_state_callback([this](bool state) {
 | 
			
		||||
      if (state) {
 | 
			
		||||
        this->authorized_start_ = millis();
 | 
			
		||||
        this->identify_start_ = 0;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32ImprovComponent::setup_characteristics() {
 | 
			
		||||
@@ -50,8 +61,10 @@ void ESP32ImprovComponent::setup_characteristics() {
 | 
			
		||||
  BLEDescriptor *capabilities_descriptor = new BLE2902();
 | 
			
		||||
  this->capabilities_->add_descriptor(capabilities_descriptor);
 | 
			
		||||
  uint8_t capabilities = 0x00;
 | 
			
		||||
#ifdef USE_OUTPUT
 | 
			
		||||
  if (this->status_indicator_ != nullptr)
 | 
			
		||||
    capabilities |= improv::CAPABILITY_IDENTIFY;
 | 
			
		||||
#endif
 | 
			
		||||
  this->capabilities_->set_value(capabilities);
 | 
			
		||||
  this->setup_complete_ = true;
 | 
			
		||||
}
 | 
			
		||||
@@ -63,8 +76,7 @@ void ESP32ImprovComponent::loop() {
 | 
			
		||||
 | 
			
		||||
  switch (this->state_) {
 | 
			
		||||
    case improv::STATE_STOPPED:
 | 
			
		||||
      if (this->status_indicator_ != nullptr)
 | 
			
		||||
        this->status_indicator_->turn_off();
 | 
			
		||||
      this->set_status_indicator_state_(false);
 | 
			
		||||
 | 
			
		||||
      if (this->service_->is_created() && this->should_start_ && this->setup_complete_) {
 | 
			
		||||
        if (this->service_->is_running()) {
 | 
			
		||||
@@ -80,18 +92,22 @@ void ESP32ImprovComponent::loop() {
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case improv::STATE_AWAITING_AUTHORIZATION: {
 | 
			
		||||
      if (this->authorizer_ == nullptr || this->authorizer_->state) {
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
      if (this->authorizer_ == nullptr ||
 | 
			
		||||
          (this->authorized_start_ != 0 && ((now - this->authorized_start_) < this->authorized_duration_))) {
 | 
			
		||||
        this->set_state_(improv::STATE_AUTHORIZED);
 | 
			
		||||
        this->authorized_start_ = now;
 | 
			
		||||
      } else {
 | 
			
		||||
        if (this->status_indicator_ != nullptr) {
 | 
			
		||||
          if (!this->check_identify_())
 | 
			
		||||
            this->status_indicator_->turn_on();
 | 
			
		||||
        }
 | 
			
		||||
      } else
 | 
			
		||||
#else
 | 
			
		||||
      this->set_state_(improv::STATE_AUTHORIZED);
 | 
			
		||||
#endif
 | 
			
		||||
      {
 | 
			
		||||
        if (!this->check_identify_())
 | 
			
		||||
          this->set_status_indicator_state_(true);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case improv::STATE_AUTHORIZED: {
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
      if (this->authorizer_ != nullptr) {
 | 
			
		||||
        if (now - this->authorized_start_ > this->authorized_duration_) {
 | 
			
		||||
          ESP_LOGD(TAG, "Authorization timeout");
 | 
			
		||||
@@ -99,25 +115,14 @@ void ESP32ImprovComponent::loop() {
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (this->status_indicator_ != nullptr) {
 | 
			
		||||
        if (!this->check_identify_()) {
 | 
			
		||||
          if ((now % 1000) < 500) {
 | 
			
		||||
            this->status_indicator_->turn_on();
 | 
			
		||||
          } else {
 | 
			
		||||
            this->status_indicator_->turn_off();
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
      if (!this->check_identify_()) {
 | 
			
		||||
        this->set_status_indicator_state_((now % 1000) < 500);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case improv::STATE_PROVISIONING: {
 | 
			
		||||
      if (this->status_indicator_ != nullptr) {
 | 
			
		||||
        if ((now % 200) < 100) {
 | 
			
		||||
          this->status_indicator_->turn_on();
 | 
			
		||||
        } else {
 | 
			
		||||
          this->status_indicator_->turn_off();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this->set_status_indicator_state_((now % 200) < 100);
 | 
			
		||||
      if (wifi::global_wifi_component->is_connected()) {
 | 
			
		||||
        wifi::global_wifi_component->save_wifi_sta(this->connecting_sta_.get_ssid(),
 | 
			
		||||
                                                   this->connecting_sta_.get_password());
 | 
			
		||||
@@ -142,13 +147,27 @@ void ESP32ImprovComponent::loop() {
 | 
			
		||||
    }
 | 
			
		||||
    case improv::STATE_PROVISIONED: {
 | 
			
		||||
      this->incoming_data_.clear();
 | 
			
		||||
      if (this->status_indicator_ != nullptr)
 | 
			
		||||
        this->status_indicator_->turn_off();
 | 
			
		||||
      this->set_status_indicator_state_(false);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32ImprovComponent::set_status_indicator_state_(bool state) {
 | 
			
		||||
#ifdef USE_OUTPUT
 | 
			
		||||
  if (this->status_indicator_ == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
  if (this->status_indicator_state_ == state)
 | 
			
		||||
    return;
 | 
			
		||||
  this->status_indicator_state_ = state;
 | 
			
		||||
  if (state) {
 | 
			
		||||
    this->status_indicator_->turn_on();
 | 
			
		||||
  } else {
 | 
			
		||||
    this->status_indicator_->turn_off();
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ESP32ImprovComponent::check_identify_() {
 | 
			
		||||
  uint32_t now = millis();
 | 
			
		||||
 | 
			
		||||
@@ -156,11 +175,7 @@ bool ESP32ImprovComponent::check_identify_() {
 | 
			
		||||
 | 
			
		||||
  if (identify) {
 | 
			
		||||
    uint32_t time = now % 1000;
 | 
			
		||||
    if (time < 600 && time % 200 < 100) {
 | 
			
		||||
      this->status_indicator_->turn_on();
 | 
			
		||||
    } else {
 | 
			
		||||
      this->status_indicator_->turn_off();
 | 
			
		||||
    }
 | 
			
		||||
    this->set_status_indicator_state_(time < 600 && time % 200 < 100);
 | 
			
		||||
  }
 | 
			
		||||
  return identify;
 | 
			
		||||
}
 | 
			
		||||
@@ -174,6 +189,25 @@ void ESP32ImprovComponent::set_state_(improv::State state) {
 | 
			
		||||
    if (state != improv::STATE_STOPPED)
 | 
			
		||||
      this->status_->notify();
 | 
			
		||||
  }
 | 
			
		||||
  std::vector<uint8_t> service_data(8, 0);
 | 
			
		||||
  service_data[0] = 0x77;  // PR
 | 
			
		||||
  service_data[1] = 0x46;  // IM
 | 
			
		||||
  service_data[2] = static_cast<uint8_t>(state);
 | 
			
		||||
 | 
			
		||||
  uint8_t capabilities = 0x00;
 | 
			
		||||
#ifdef USE_OUTPUT
 | 
			
		||||
  if (this->status_indicator_ != nullptr)
 | 
			
		||||
    capabilities |= improv::CAPABILITY_IDENTIFY;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  service_data[3] = capabilities;
 | 
			
		||||
  service_data[4] = 0x00;  // Reserved
 | 
			
		||||
  service_data[5] = 0x00;  // Reserved
 | 
			
		||||
  service_data[6] = 0x00;  // Reserved
 | 
			
		||||
  service_data[7] = 0x00;  // Reserved
 | 
			
		||||
 | 
			
		||||
  esp32_ble::global_ble->get_advertising()->set_service_data(service_data);
 | 
			
		||||
  esp32_ble::global_ble->get_advertising()->start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32ImprovComponent::set_error_(improv::Error error) {
 | 
			
		||||
@@ -213,8 +247,12 @@ float ESP32ImprovComponent::get_setup_priority() const { return setup_priority::
 | 
			
		||||
 | 
			
		||||
void ESP32ImprovComponent::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "ESP32 Improv:");
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  LOG_BINARY_SENSOR("  ", "Authorizer", this->authorizer_);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_OUTPUT
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Status Indicator: '%s'", YESNO(this->status_indicator_ != nullptr));
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32ImprovComponent::process_incoming_data_() {
 | 
			
		||||
@@ -273,8 +311,10 @@ void ESP32ImprovComponent::process_incoming_data_() {
 | 
			
		||||
void ESP32ImprovComponent::on_wifi_connect_timeout_() {
 | 
			
		||||
  this->set_error_(improv::ERROR_UNABLE_TO_CONNECT);
 | 
			
		||||
  this->set_state_(improv::STATE_AUTHORIZED);
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  if (this->authorizer_ != nullptr)
 | 
			
		||||
    this->authorized_start_ = millis();
 | 
			
		||||
#endif
 | 
			
		||||
  ESP_LOGW(TAG, "Timed out trying to connect to given WiFi network");
 | 
			
		||||
  wifi::global_wifi_component->clear_sta();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,22 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_server/ble_characteristic.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_server/ble_server.h"
 | 
			
		||||
#include "esphome/components/output/binary_output.h"
 | 
			
		||||
#include "esphome/components/wifi/wifi_component.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/preferences.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/esp32_ble_server/ble_characteristic.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_server/ble_server.h"
 | 
			
		||||
#include "esphome/components/wifi/wifi_component.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_OUTPUT
 | 
			
		||||
#include "esphome/components/output/binary_output.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
@@ -34,8 +42,12 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
 | 
			
		||||
  void stop() override;
 | 
			
		||||
  bool is_active() const { return this->state_ != improv::STATE_STOPPED; }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_OUTPUT
 | 
			
		||||
  void set_status_indicator(output::BinaryOutput *status_indicator) { this->status_indicator_ = status_indicator; }
 | 
			
		||||
#endif
 | 
			
		||||
  void set_identify_duration(uint32_t identify_duration) { this->identify_duration_ = identify_duration; }
 | 
			
		||||
  void set_authorized_duration(uint32_t authorized_duration) { this->authorized_duration_ = authorized_duration; }
 | 
			
		||||
 | 
			
		||||
@@ -58,12 +70,19 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
 | 
			
		||||
  BLECharacteristic *rpc_response_;
 | 
			
		||||
  BLECharacteristic *capabilities_;
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  binary_sensor::BinarySensor *authorizer_{nullptr};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_OUTPUT
 | 
			
		||||
  output::BinaryOutput *status_indicator_{nullptr};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  improv::State state_{improv::STATE_STOPPED};
 | 
			
		||||
  improv::Error error_state_{improv::ERROR_NONE};
 | 
			
		||||
 | 
			
		||||
  bool status_indicator_state_{false};
 | 
			
		||||
  void set_status_indicator_state_(bool state);
 | 
			
		||||
 | 
			
		||||
  void set_state_(improv::State state);
 | 
			
		||||
  void set_error_(improv::Error error);
 | 
			
		||||
  void send_response_(std::vector<uint8_t> &response);
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,7 @@ RMT_CHANNELS = {
 | 
			
		||||
    esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3],
 | 
			
		||||
    esp32.const.VARIANT_ESP32C3: [0, 1],
 | 
			
		||||
    esp32.const.VARIANT_ESP32C6: [0, 1],
 | 
			
		||||
    esp32.const.VARIANT_ESP32H2: [0, 1],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ from esphome.const import (
 | 
			
		||||
    KEY_FRAMEWORK_VERSION,
 | 
			
		||||
    KEY_TARGET_FRAMEWORK,
 | 
			
		||||
    KEY_TARGET_PLATFORM,
 | 
			
		||||
    PLATFORM_ESP8266,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
@@ -38,7 +39,7 @@ AUTO_LOAD = ["preferences"]
 | 
			
		||||
 | 
			
		||||
def set_core_data(config):
 | 
			
		||||
    CORE.data[KEY_ESP8266] = {}
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp8266"
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_ESP8266
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
 | 
			
		||||
        config[CONF_FRAMEWORK][CONF_VERSION]
 | 
			
		||||
 
 | 
			
		||||
@@ -236,7 +236,7 @@ bool EthernetComponent::can_proceed() { return this->is_connected(); }
 | 
			
		||||
network::IPAddress EthernetComponent::get_ip_address() {
 | 
			
		||||
  esp_netif_ip_info_t ip;
 | 
			
		||||
  esp_netif_get_ip_info(this->eth_netif_, &ip);
 | 
			
		||||
  return {ip.ip.addr};
 | 
			
		||||
  return network::IPAddress(&ip.ip);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) {
 | 
			
		||||
@@ -293,9 +293,9 @@ void EthernetComponent::start_connect_() {
 | 
			
		||||
 | 
			
		||||
  esp_netif_ip_info_t info;
 | 
			
		||||
  if (this->manual_ip_.has_value()) {
 | 
			
		||||
    info.ip.addr = static_cast<uint32_t>(this->manual_ip_->static_ip);
 | 
			
		||||
    info.gw.addr = static_cast<uint32_t>(this->manual_ip_->gateway);
 | 
			
		||||
    info.netmask.addr = static_cast<uint32_t>(this->manual_ip_->subnet);
 | 
			
		||||
    info.ip = this->manual_ip_->static_ip;
 | 
			
		||||
    info.gw = this->manual_ip_->gateway;
 | 
			
		||||
    info.netmask = this->manual_ip_->subnet;
 | 
			
		||||
  } else {
 | 
			
		||||
    info.ip.addr = 0;
 | 
			
		||||
    info.gw.addr = 0;
 | 
			
		||||
@@ -318,24 +318,14 @@ void EthernetComponent::start_connect_() {
 | 
			
		||||
  ESPHL_ERROR_CHECK(err, "DHCPC set IP info error");
 | 
			
		||||
 | 
			
		||||
  if (this->manual_ip_.has_value()) {
 | 
			
		||||
    if (uint32_t(this->manual_ip_->dns1) != 0) {
 | 
			
		||||
    if (this->manual_ip_->dns1.is_set()) {
 | 
			
		||||
      ip_addr_t d;
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
      d.type = IPADDR_TYPE_V4;
 | 
			
		||||
      d.u_addr.ip4.addr = static_cast<uint32_t>(this->manual_ip_->dns1);
 | 
			
		||||
#else
 | 
			
		||||
      d.addr = static_cast<uint32_t>(this->manual_ip_->dns1);
 | 
			
		||||
#endif
 | 
			
		||||
      d = this->manual_ip_->dns1;
 | 
			
		||||
      dns_setserver(0, &d);
 | 
			
		||||
    }
 | 
			
		||||
    if (uint32_t(this->manual_ip_->dns2) != 0) {
 | 
			
		||||
    if (this->manual_ip_->dns2.is_set()) {
 | 
			
		||||
      ip_addr_t d;
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
      d.type = IPADDR_TYPE_V4;
 | 
			
		||||
      d.u_addr.ip4.addr = static_cast<uint32_t>(this->manual_ip_->dns2);
 | 
			
		||||
#else
 | 
			
		||||
      d.addr = static_cast<uint32_t>(this->manual_ip_->dns2);
 | 
			
		||||
#endif
 | 
			
		||||
      d = this->manual_ip_->dns2;
 | 
			
		||||
      dns_setserver(1, &d);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
@@ -360,21 +350,16 @@ bool EthernetComponent::is_connected() { return this->state_ == EthernetComponen
 | 
			
		||||
void EthernetComponent::dump_connect_params_() {
 | 
			
		||||
  esp_netif_ip_info_t ip;
 | 
			
		||||
  esp_netif_get_ip_info(this->eth_netif_, &ip);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  IP Address: %s", network::IPAddress(ip.ip.addr).str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  IP Address: %s", network::IPAddress(&ip.ip).str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Hostname: '%s'", App.get_name().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Subnet: %s", network::IPAddress(ip.netmask.addr).str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Gateway: %s", network::IPAddress(ip.gw.addr).str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Subnet: %s", network::IPAddress(&ip.netmask).str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Gateway: %s", network::IPAddress(&ip.gw).str().c_str());
 | 
			
		||||
 | 
			
		||||
  const ip_addr_t *dns_ip1 = dns_getserver(0);
 | 
			
		||||
  const ip_addr_t *dns_ip2 = dns_getserver(1);
 | 
			
		||||
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  DNS1: %s", network::IPAddress(dns_ip1->u_addr.ip4.addr).str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  DNS2: %s", network::IPAddress(dns_ip2->u_addr.ip4.addr).str().c_str());
 | 
			
		||||
#else
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  DNS1: %s", network::IPAddress(dns_ip1->addr).str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  DNS2: %s", network::IPAddress(dns_ip2->addr).str().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  DNS1: %s", network::IPAddress(dns_ip1).str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  DNS2: %s", network::IPAddress(dns_ip2).str().c_str());
 | 
			
		||||
 | 
			
		||||
#if ENABLE_IPV6
 | 
			
		||||
  if (this->ipv6_count_ > 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -67,18 +67,13 @@ def validate_pillow_installed(value):
 | 
			
		||||
    except ImportError as err:
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            "Please install the pillow python package to use this feature. "
 | 
			
		||||
            '(pip install pillow">4.0.0,<10.0.0")'
 | 
			
		||||
            '(pip install "pillow==10.0.1")'
 | 
			
		||||
        ) from err
 | 
			
		||||
 | 
			
		||||
    if version.parse(PIL.__version__) < version.parse("4.0.0"):
 | 
			
		||||
    if version.parse(PIL.__version__) != version.parse("10.0.1"):
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            "Please update your pillow installation to at least 4.0.x. "
 | 
			
		||||
            '(pip install pillow">4.0.0,<10.0.0")'
 | 
			
		||||
        )
 | 
			
		||||
    if version.parse(PIL.__version__) >= version.parse("10.0.0"):
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            "Please downgrade your pillow installation to below 10.0.0. "
 | 
			
		||||
            '(pip install pillow">4.0.0,<10.0.0")'
 | 
			
		||||
            "Please update your pillow installation to 10.0.1. "
 | 
			
		||||
            '(pip install "pillow==10.0.1")'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return value
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,15 @@ from esphome.const import (
 | 
			
		||||
    CONF_CHANNEL,
 | 
			
		||||
    CONF_SPEED,
 | 
			
		||||
    CONF_DIRECTION,
 | 
			
		||||
    CONF_ADDRESS,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@max246"]
 | 
			
		||||
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
grove_tb6612fng_ns = cg.esphome_ns.namespace("grove_tb6612fng")
 | 
			
		||||
GROVE_TB6612FNG = grove_tb6612fng_ns.class_(
 | 
			
		||||
    "GroveMotorDriveTB6612FNG", cg.Component, i2c.I2CDevice
 | 
			
		||||
@@ -33,6 +36,9 @@ GROVETB6612FNGMotorStandbyAction = grove_tb6612fng_ns.class_(
 | 
			
		||||
GROVETB6612FNGMotorNoStandbyAction = grove_tb6612fng_ns.class_(
 | 
			
		||||
    "GROVETB6612FNGMotorNoStandbyAction", automation.Action
 | 
			
		||||
)
 | 
			
		||||
GROVETB6612FNGMotorChangeAddressAction = grove_tb6612fng_ns.class_(
 | 
			
		||||
    "GROVETB6612FNGMotorChangeAddressAction", automation.Action
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DIRECTION_TYPE = {
 | 
			
		||||
    "FORWARD": 1,
 | 
			
		||||
@@ -150,3 +156,22 @@ async def grove_tb6612fng_no_standby_to_code(config, action_id, template_arg, ar
 | 
			
		||||
    await cg.register_parented(var, config[CONF_ID])
 | 
			
		||||
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "grove_tb6612fng.change_address",
 | 
			
		||||
    GROVETB6612FNGMotorChangeAddressAction,
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
 | 
			
		||||
            cv.Required(CONF_ADDRESS): cv.i2c_address,
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
async def grove_tb6612fng_change_address_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg)
 | 
			
		||||
    await cg.register_parented(var, config[CONF_ID])
 | 
			
		||||
 | 
			
		||||
    template_channel = await cg.templatable(config[CONF_ADDRESS], args, int)
 | 
			
		||||
    cg.add(var.set_address(template_channel))
 | 
			
		||||
    return var
 | 
			
		||||
 
 | 
			
		||||
@@ -84,8 +84,7 @@ class GroveMotorDriveTB6612FNG : public Component, public i2c::I2CDevice {
 | 
			
		||||
  *************************************************************/
 | 
			
		||||
  void set_i2c_addr(uint8_t addr);
 | 
			
		||||
 | 
			
		||||
  /*************************************************************
 | 
			
		||||
      Description
 | 
			
		||||
  /***********************************change_address
 | 
			
		||||
       Drive a motor.
 | 
			
		||||
      Parameter
 | 
			
		||||
       chl: MOTOR_CHA or MOTOR_CHB
 | 
			
		||||
@@ -204,5 +203,13 @@ class GROVETB6612FNGMotorNoStandbyAction : public Action<Ts...>, public Parented
 | 
			
		||||
  void play(Ts... x) override { this->parent_->not_standby(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts>
 | 
			
		||||
class GROVETB6612FNGMotorChangeAddressAction : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
 | 
			
		||||
 public:
 | 
			
		||||
  TEMPLATABLE_VALUE(uint8_t, address)
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override { this->parent_->set_i2c_addr(this->address_.value(x...)); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace grove_tb6612fng
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,9 @@ import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ADDRESS,
 | 
			
		||||
    CONF_FIELD_STRENGTH_X,
 | 
			
		||||
    CONF_FIELD_STRENGTH_Y,
 | 
			
		||||
    CONF_FIELD_STRENGTH_Z,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_OVERSAMPLING,
 | 
			
		||||
    CONF_RANGE,
 | 
			
		||||
@@ -18,9 +21,6 @@ DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
hmc5883l_ns = cg.esphome_ns.namespace("hmc5883l")
 | 
			
		||||
 | 
			
		||||
CONF_FIELD_STRENGTH_X = "field_strength_x"
 | 
			
		||||
CONF_FIELD_STRENGTH_Y = "field_strength_y"
 | 
			
		||||
CONF_FIELD_STRENGTH_Z = "field_strength_z"
 | 
			
		||||
CONF_HEADING = "heading"
 | 
			
		||||
 | 
			
		||||
HMC5883LComponent = hmc5883l_ns.class_(
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ from esphome.const import (
 | 
			
		||||
    KEY_FRAMEWORK_VERSION,
 | 
			
		||||
    KEY_TARGET_FRAMEWORK,
 | 
			
		||||
    KEY_TARGET_PLATFORM,
 | 
			
		||||
    PLATFORM_HOST,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
@@ -20,7 +21,7 @@ AUTO_LOAD = ["network"]
 | 
			
		||||
 | 
			
		||||
def set_core_data(config):
 | 
			
		||||
    CORE.data[KEY_HOST] = {}
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "host"
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_HOST
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "host"
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version(1, 0, 0)
 | 
			
		||||
    return config
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,7 @@ void HTU21DComponent::update() {
 | 
			
		||||
  if (this->humidity_ != nullptr)
 | 
			
		||||
    this->humidity_->publish_state(humidity);
 | 
			
		||||
  if (this->heater_ != nullptr)
 | 
			
		||||
    this->heater_->publish_state(humidity);
 | 
			
		||||
    this->heater_->publish_state(heater_level);
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,9 @@ from esphome.const import (
 | 
			
		||||
    CONF_SDA,
 | 
			
		||||
    CONF_ADDRESS,
 | 
			
		||||
    CONF_I2C_ID,
 | 
			
		||||
    PLATFORM_ESP32,
 | 
			
		||||
    PLATFORM_ESP8266,
 | 
			
		||||
    PLATFORM_RP2040,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import coroutine_with_priority, CORE
 | 
			
		||||
 | 
			
		||||
@@ -60,7 +63,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Optional(CONF_SCAN, default=True): cv.boolean,
 | 
			
		||||
        }
 | 
			
		||||
    ).extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
    cv.only_on(["esp32", "esp8266", "rp2040"]),
 | 
			
		||||
    cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,8 @@ void I2SAudioMicrophone::setup() {
 | 
			
		||||
void I2SAudioMicrophone::start() {
 | 
			
		||||
  if (this->is_failed())
 | 
			
		||||
    return;
 | 
			
		||||
  if (this->state_ == microphone::STATE_RUNNING)
 | 
			
		||||
    return;  // Already running
 | 
			
		||||
  this->state_ = microphone::STATE_STARTING;
 | 
			
		||||
}
 | 
			
		||||
void I2SAudioMicrophone::start_() {
 | 
			
		||||
@@ -66,8 +68,9 @@ void I2SAudioMicrophone::start_() {
 | 
			
		||||
 | 
			
		||||
    i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_);
 | 
			
		||||
    i2s_adc_enable(this->parent_->get_port());
 | 
			
		||||
  } else {
 | 
			
		||||
  } else
 | 
			
		||||
#endif
 | 
			
		||||
  {
 | 
			
		||||
    if (this->pdm_)
 | 
			
		||||
      config.mode = (i2s_mode_t) (config.mode | I2S_MODE_PDM);
 | 
			
		||||
 | 
			
		||||
@@ -77,9 +80,7 @@ void I2SAudioMicrophone::start_() {
 | 
			
		||||
    pin_config.data_in_num = this->din_pin_;
 | 
			
		||||
 | 
			
		||||
    i2s_set_pin(this->parent_->get_port(), &pin_config);
 | 
			
		||||
#if SOC_I2S_SUPPORTS_ADC
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  this->state_ = microphone::STATE_RUNNING;
 | 
			
		||||
  this->high_freq_.start();
 | 
			
		||||
}
 | 
			
		||||
@@ -110,6 +111,10 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (bytes_read == 0) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
  if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_16BIT) {
 | 
			
		||||
    return bytes_read;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace i2s_audio {
 | 
			
		||||
 | 
			
		||||
static const size_t BUFFER_COUNT = 10;
 | 
			
		||||
static const size_t BUFFER_COUNT = 20;
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "i2s_audio.speaker";
 | 
			
		||||
 | 
			
		||||
@@ -19,7 +19,7 @@ void I2SAudioSpeaker::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker...");
 | 
			
		||||
 | 
			
		||||
  this->buffer_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(DataEvent));
 | 
			
		||||
  this->event_queue_ = xQueueCreate(20, sizeof(TaskEvent));
 | 
			
		||||
  this->event_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(TaskEvent));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I2SAudioSpeaker::start() { this->state_ = speaker::STATE_STARTING; }
 | 
			
		||||
@@ -47,7 +47,7 @@ void I2SAudioSpeaker::player_task(void *params) {
 | 
			
		||||
      .communication_format = I2S_COMM_FORMAT_STAND_I2S,
 | 
			
		||||
      .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
 | 
			
		||||
      .dma_buf_count = 8,
 | 
			
		||||
      .dma_buf_len = 1024,
 | 
			
		||||
      .dma_buf_len = 128,
 | 
			
		||||
      .use_apll = false,
 | 
			
		||||
      .tx_desc_auto_clear = true,
 | 
			
		||||
      .fixed_mclk = I2S_PIN_NO_CHANGE,
 | 
			
		||||
@@ -60,7 +60,17 @@ void I2SAudioSpeaker::player_task(void *params) {
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  i2s_driver_install(this_speaker->parent_->get_port(), &config, 0, nullptr);
 | 
			
		||||
  esp_err_t err = i2s_driver_install(this_speaker->parent_->get_port(), &config, 0, nullptr);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    event.type = TaskEventType::WARNING;
 | 
			
		||||
    event.err = err;
 | 
			
		||||
    xQueueSend(this_speaker->event_queue_, &event, 0);
 | 
			
		||||
    event.type = TaskEventType::STOPPED;
 | 
			
		||||
    xQueueSend(this_speaker->event_queue_, &event, 0);
 | 
			
		||||
    while (true) {
 | 
			
		||||
      delay(10);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#if SOC_I2S_SUPPORTS_DAC
 | 
			
		||||
  if (this_speaker->internal_dac_mode_ == I2S_DAC_CHANNEL_DISABLE) {
 | 
			
		||||
@@ -88,9 +98,7 @@ void I2SAudioSpeaker::player_task(void *params) {
 | 
			
		||||
    }
 | 
			
		||||
    if (data_event.stop) {
 | 
			
		||||
      // Stop signal from main thread
 | 
			
		||||
      while (xQueueReceive(this_speaker->buffer_queue_, &data_event, 0) == pdTRUE) {
 | 
			
		||||
        // Flush queue
 | 
			
		||||
      }
 | 
			
		||||
      xQueueReset(this_speaker->buffer_queue_);  // Flush queue
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    size_t bytes_written;
 | 
			
		||||
@@ -103,7 +111,7 @@ void I2SAudioSpeaker::player_task(void *params) {
 | 
			
		||||
      uint32_t sample = (buffer[current] << 16) | (buffer[current] & 0xFFFF);
 | 
			
		||||
 | 
			
		||||
      esp_err_t err = i2s_write(this_speaker->parent_->get_port(), &sample, sizeof(sample), &bytes_written,
 | 
			
		||||
                                (100 / portTICK_PERIOD_MS));
 | 
			
		||||
                                (10 / portTICK_PERIOD_MS));
 | 
			
		||||
      if (err != ESP_OK) {
 | 
			
		||||
        event = {.type = TaskEventType::WARNING, .err = err};
 | 
			
		||||
        xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
 | 
			
		||||
@@ -122,7 +130,6 @@ void I2SAudioSpeaker::player_task(void *params) {
 | 
			
		||||
  event.type = TaskEventType::STOPPING;
 | 
			
		||||
  xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
 | 
			
		||||
 | 
			
		||||
  i2s_stop(this_speaker->parent_->get_port());
 | 
			
		||||
  i2s_driver_uninstall(this_speaker->parent_->get_port());
 | 
			
		||||
 | 
			
		||||
  event.type = TaskEventType::STOPPED;
 | 
			
		||||
@@ -151,17 +158,24 @@ void I2SAudioSpeaker::watch_() {
 | 
			
		||||
  if (xQueueReceive(this->event_queue_, &event, 0) == pdTRUE) {
 | 
			
		||||
    switch (event.type) {
 | 
			
		||||
      case TaskEventType::STARTING:
 | 
			
		||||
        ESP_LOGD(TAG, "Starting I2S Audio Speaker");
 | 
			
		||||
        break;
 | 
			
		||||
      case TaskEventType::STARTED:
 | 
			
		||||
        ESP_LOGD(TAG, "Started I2S Audio Speaker");
 | 
			
		||||
        break;
 | 
			
		||||
      case TaskEventType::STOPPING:
 | 
			
		||||
        ESP_LOGD(TAG, "Stopping I2S Audio Speaker");
 | 
			
		||||
        break;
 | 
			
		||||
      case TaskEventType::PLAYING:
 | 
			
		||||
        this->status_clear_warning();
 | 
			
		||||
        break;
 | 
			
		||||
      case TaskEventType::STOPPED:
 | 
			
		||||
        this->parent_->unlock();
 | 
			
		||||
        this->state_ = speaker::STATE_STOPPED;
 | 
			
		||||
        vTaskDelete(this->player_task_handle_);
 | 
			
		||||
        this->player_task_handle_ = nullptr;
 | 
			
		||||
        this->parent_->unlock();
 | 
			
		||||
        xQueueReset(this->buffer_queue_);
 | 
			
		||||
        ESP_LOGD(TAG, "Stopped I2S Audio Speaker");
 | 
			
		||||
        break;
 | 
			
		||||
      case TaskEventType::WARNING:
 | 
			
		||||
        ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err));
 | 
			
		||||
@@ -177,9 +191,9 @@ void I2SAudioSpeaker::loop() {
 | 
			
		||||
      this->start_();
 | 
			
		||||
      break;
 | 
			
		||||
    case speaker::STATE_RUNNING:
 | 
			
		||||
    case speaker::STATE_STOPPING:
 | 
			
		||||
      this->watch_();
 | 
			
		||||
      break;
 | 
			
		||||
    case speaker::STATE_STOPPING:
 | 
			
		||||
    case speaker::STATE_STOPPED:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import core, pins
 | 
			
		||||
from esphome.components import display, spi
 | 
			
		||||
from esphome.components import display, spi, font
 | 
			
		||||
from esphome.core import CORE, HexInt
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_COLOR_PALETTE,
 | 
			
		||||
@@ -13,7 +13,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_PAGES,
 | 
			
		||||
    CONF_RESET_PIN,
 | 
			
		||||
    CONF_DIMENSIONS,
 | 
			
		||||
    CONF_DATA_RATE,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["spi"]
 | 
			
		||||
@@ -25,7 +24,7 @@ def AUTO_LOAD():
 | 
			
		||||
    return []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@nielsnl68"]
 | 
			
		||||
CODEOWNERS = ["@nielsnl68", "@clydebarrow"]
 | 
			
		||||
 | 
			
		||||
ili9XXX_ns = cg.esphome_ns.namespace("ili9xxx")
 | 
			
		||||
ili9XXXSPI = ili9XXX_ns.class_(
 | 
			
		||||
@@ -42,6 +41,7 @@ MODELS = {
 | 
			
		||||
    "ILI9341": ili9XXX_ns.class_("ILI9XXXILI9341", ili9XXXSPI),
 | 
			
		||||
    "ILI9342": ili9XXX_ns.class_("ILI9XXXILI9342", ili9XXXSPI),
 | 
			
		||||
    "ILI9481": ili9XXX_ns.class_("ILI9XXXILI9481", ili9XXXSPI),
 | 
			
		||||
    "ILI9481-18": ili9XXX_ns.class_("ILI9XXXILI948118", ili9XXXSPI),
 | 
			
		||||
    "ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI),
 | 
			
		||||
    "ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI),
 | 
			
		||||
    "ILI9488_A": ili9XXX_ns.class_("ILI9XXXILI9488A", ili9XXXSPI),
 | 
			
		||||
@@ -54,6 +54,7 @@ COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE")
 | 
			
		||||
 | 
			
		||||
CONF_LED_PIN = "led_pin"
 | 
			
		||||
CONF_COLOR_PALETTE_IMAGES = "color_palette_images"
 | 
			
		||||
CONF_INVERT_DISPLAY = "invert_display"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _validate(config):
 | 
			
		||||
@@ -84,6 +85,7 @@ def _validate(config):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    font.validate_pillow_installed,
 | 
			
		||||
    display.FULL_DISPLAY_SCHEMA.extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(ili9XXXSPI),
 | 
			
		||||
@@ -99,11 +101,11 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Optional(CONF_COLOR_PALETTE_IMAGES, default=[]): cv.ensure_list(
 | 
			
		||||
                cv.file_
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_DATA_RATE, default="40MHz"): spi.SPI_DATA_RATE_SCHEMA,
 | 
			
		||||
            cv.Optional(CONF_INVERT_DISPLAY): cv.boolean,
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("1s"))
 | 
			
		||||
    .extend(spi.spi_device_schema(False)),
 | 
			
		||||
    .extend(spi.spi_device_schema(False, "40MHz")),
 | 
			
		||||
    cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
 | 
			
		||||
    _validate,
 | 
			
		||||
)
 | 
			
		||||
@@ -140,8 +142,6 @@ async def to_code(config):
 | 
			
		||||
        rhs = []
 | 
			
		||||
        for x in range(256):
 | 
			
		||||
            rhs.extend([HexInt(x), HexInt(x), HexInt(x)])
 | 
			
		||||
        prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
 | 
			
		||||
        cg.add(var.set_palette(prog_arr))
 | 
			
		||||
    elif config[CONF_COLOR_PALETTE] == "IMAGE_ADAPTIVE":
 | 
			
		||||
        cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_8_INDEXED))
 | 
			
		||||
        from PIL import Image
 | 
			
		||||
@@ -165,7 +165,7 @@ async def to_code(config):
 | 
			
		||||
            x = x + i.width
 | 
			
		||||
 | 
			
		||||
        # reduce the colors on combined image to 256.
 | 
			
		||||
        converted = ref_image.convert("P", palette=Image.ADAPTIVE, colors=256)
 | 
			
		||||
        converted = ref_image.convert("P", palette=Image.Palette.ADAPTIVE, colors=256)
 | 
			
		||||
        # if you want to verify how the images look use
 | 
			
		||||
        # ref_image.save("ref_in.png")
 | 
			
		||||
        # converted.save("ref_out.png")
 | 
			
		||||
@@ -179,5 +179,5 @@ async def to_code(config):
 | 
			
		||||
        prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
 | 
			
		||||
        cg.add(var.set_palette(prog_arr))
 | 
			
		||||
 | 
			
		||||
    spi_data_rate = str(spi.SPI_DATA_RATE_OPTIONS[config[CONF_DATA_RATE]])
 | 
			
		||||
    cg.add_define("ILI9XXXDisplay_DATA_RATE", cg.RawExpression(spi_data_rate))
 | 
			
		||||
    if CONF_INVERT_DISPLAY in config:
 | 
			
		||||
        cg.add(var.invert_display(config[CONF_INVERT_DISPLAY]))
 | 
			
		||||
 
 | 
			
		||||
@@ -12,11 +12,13 @@ static const char *const TAG = "ili9xxx";
 | 
			
		||||
void ILI9XXXDisplay::setup() {
 | 
			
		||||
  this->setup_pins_();
 | 
			
		||||
  this->initialize();
 | 
			
		||||
  this->command(this->pre_invertdisplay_ ? ILI9XXX_INVON : ILI9XXX_INVOFF);
 | 
			
		||||
 | 
			
		||||
  this->x_low_ = this->width_;
 | 
			
		||||
  this->y_low_ = this->height_;
 | 
			
		||||
  this->x_high_ = 0;
 | 
			
		||||
  this->y_high_ = 0;
 | 
			
		||||
 | 
			
		||||
  if (this->buffer_color_mode_ == BITS_16) {
 | 
			
		||||
    this->init_internal_(this->get_buffer_length_() * 2);
 | 
			
		||||
    if (this->buffer_ != nullptr) {
 | 
			
		||||
@@ -59,6 +61,7 @@ void ILI9XXXDisplay::dump_config() {
 | 
			
		||||
  if (this->is_18bitdisplay_) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  18-Bit Mode: YES");
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000));
 | 
			
		||||
 | 
			
		||||
  LOG_PIN("  Reset Pin: ", this->reset_pin_);
 | 
			
		||||
  LOG_PIN("  DC Pin: ", this->dc_pin_);
 | 
			
		||||
@@ -332,7 +335,12 @@ void ILI9XXXDisplay::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t w, uint
 | 
			
		||||
  this->command(ILI9XXX_RAMWR);  // Write to RAM
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ILI9XXXDisplay::invert_display_(bool invert) { this->command(invert ? ILI9XXX_INVON : ILI9XXX_INVOFF); }
 | 
			
		||||
void ILI9XXXDisplay::invert_display(bool invert) {
 | 
			
		||||
  this->pre_invertdisplay_ = invert;
 | 
			
		||||
  if (is_ready()) {
 | 
			
		||||
    this->command(invert ? ILI9XXX_INVON : ILI9XXX_INVOFF);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ILI9XXXDisplay::get_width_internal() { return this->width_; }
 | 
			
		||||
int ILI9XXXDisplay::get_height_internal() { return this->height_; }
 | 
			
		||||
@@ -344,7 +352,7 @@ void ILI9XXXM5Stack::initialize() {
 | 
			
		||||
    this->width_ = 320;
 | 
			
		||||
  if (this->height_ == 0)
 | 
			
		||||
    this->height_ = 240;
 | 
			
		||||
  this->invert_display_(true);
 | 
			
		||||
  this->pre_invertdisplay_ = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//   M5CORE display // Based on the configuration settings of M5stact's M5GFX code.
 | 
			
		||||
@@ -354,7 +362,7 @@ void ILI9XXXM5CORE::initialize() {
 | 
			
		||||
    this->width_ = 320;
 | 
			
		||||
  if (this->height_ == 0)
 | 
			
		||||
    this->height_ = 240;
 | 
			
		||||
  this->invert_display_(true);
 | 
			
		||||
  this->pre_invertdisplay_ = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//   24_TFT display
 | 
			
		||||
@@ -387,6 +395,17 @@ void ILI9XXXILI9481::initialize() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ILI9XXXILI948118::initialize() {
 | 
			
		||||
  this->init_lcd_(INITCMD_ILI9481_18);
 | 
			
		||||
  if (this->width_ == 0) {
 | 
			
		||||
    this->width_ = 320;
 | 
			
		||||
  }
 | 
			
		||||
  if (this->height_ == 0) {
 | 
			
		||||
    this->height_ = 480;
 | 
			
		||||
  }
 | 
			
		||||
  this->is_18bitdisplay_ = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//   35_TFT display
 | 
			
		||||
void ILI9XXXILI9486::initialize() {
 | 
			
		||||
  this->init_lcd_(INITCMD_ILI9486);
 | 
			
		||||
@@ -450,7 +469,7 @@ void ILI9XXXS3BoxLite::initialize() {
 | 
			
		||||
  if (this->height_ == 0) {
 | 
			
		||||
    this->height_ = 240;
 | 
			
		||||
  }
 | 
			
		||||
  this->invert_display_(true);
 | 
			
		||||
  this->pre_invertdisplay_ = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ili9xxx
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,7 @@ class ILI9XXXDisplay : public PollingComponent,
 | 
			
		||||
    this->height_ = height;
 | 
			
		||||
    this->width_ = width;
 | 
			
		||||
  }
 | 
			
		||||
  void invert_display(bool invert);
 | 
			
		||||
  void command(uint8_t value);
 | 
			
		||||
  void data(uint8_t value);
 | 
			
		||||
  void send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes);
 | 
			
		||||
@@ -55,7 +56,7 @@ class ILI9XXXDisplay : public PollingComponent,
 | 
			
		||||
  void display_();
 | 
			
		||||
  void init_lcd_(const uint8_t *init_cmd);
 | 
			
		||||
  void set_addr_window_(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
 | 
			
		||||
  void invert_display_(bool invert);
 | 
			
		||||
 | 
			
		||||
  void reset_();
 | 
			
		||||
 | 
			
		||||
  int16_t width_{0};   ///< Display width as modified by current rotation
 | 
			
		||||
@@ -88,6 +89,7 @@ class ILI9XXXDisplay : public PollingComponent,
 | 
			
		||||
  bool prossing_update_ = false;
 | 
			
		||||
  bool need_update_ = false;
 | 
			
		||||
  bool is_18bitdisplay_ = false;
 | 
			
		||||
  bool pre_invertdisplay_ = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   M5Stack display --------------
 | 
			
		||||
@@ -120,6 +122,12 @@ class ILI9XXXILI9481 : public ILI9XXXDisplay {
 | 
			
		||||
  void initialize() override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   ILI9481 in 18 bit mode --------------
 | 
			
		||||
class ILI9XXXILI948118 : public ILI9XXXDisplay {
 | 
			
		||||
 protected:
 | 
			
		||||
  void initialize() override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------   ILI9XXX_35_TFT rotated display --------------
 | 
			
		||||
class ILI9XXXILI9486 : public ILI9XXXDisplay {
 | 
			
		||||
 protected:
 | 
			
		||||
 
 | 
			
		||||
@@ -94,12 +94,36 @@ static const uint8_t PROGMEM INITCMD_ILI9481[] = {
 | 
			
		||||
  ILI9XXX_IFCTR  , 1, 0x83,
 | 
			
		||||
  ILI9XXX_GMCTR  ,12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, 0x23, 0x77, 0x00, 0x0F, 0x00,
 | 
			
		||||
  ILI9XXX_IFMODE , 1, 0x00,  // CommandAccessProtect
 | 
			
		||||
  ILI9XXX_PTLAR , 4, 0, 0, 1, 0xDF,
 | 
			
		||||
  0xE4        , 1, 0xA0,
 | 
			
		||||
  ILI9XXX_MADCTL  , 1, MADCTL_MV | MADCTL_BGR,       // Memory Access Control
 | 
			
		||||
  ILI9XXX_CSCON , 1, 0x01,
 | 
			
		||||
  ILI9XXX_PIXFMT, 1, 0x55,  // 16 bit mode
 | 
			
		||||
  ILI9XXX_INVON, 0,
 | 
			
		||||
  ILI9XXX_DISPON, 0x80,     // Set display on
 | 
			
		||||
  0x00 // end
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const uint8_t PROGMEM INITCMD_ILI9481_18[] = {
 | 
			
		||||
    ILI9XXX_SLPOUT ,  0x80,    // Exit sleep mode
 | 
			
		||||
    ILI9XXX_PWSET  , 3, 0x07, 0x41, 0x1D,
 | 
			
		||||
    ILI9XXX_VMCTR  , 3, 0x00, 0x1C, 0x1F,
 | 
			
		||||
    ILI9XXX_PWSETN , 2, 0x01, 0x11,
 | 
			
		||||
    ILI9XXX_PWCTR1 , 5, 0x10, 0x3B, 0x00, 0x02, 0x11,
 | 
			
		||||
    ILI9XXX_VMCTR1 , 1, 0x03,
 | 
			
		||||
    ILI9XXX_IFCTR  , 1, 0x83,
 | 
			
		||||
    ILI9XXX_GMCTR  ,12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, 0x23, 0x77, 0x00, 0x0F, 0x00,
 | 
			
		||||
    ILI9XXX_IFMODE , 1, 0x00,  // CommandAccessProtect
 | 
			
		||||
    ILI9XXX_PTLAR , 4, 0, 0, 1, 0xDF,
 | 
			
		||||
    0xE4        , 1, 0xA0,
 | 
			
		||||
    ILI9XXX_MADCTL  , 1, MADCTL_MX| MADCTL_BGR,       // Memory Access Control
 | 
			
		||||
    ILI9XXX_CSCON , 1, 0x01,
 | 
			
		||||
    ILI9XXX_PIXFMT, 1, 0x66,  // 18 bit mode
 | 
			
		||||
    ILI9XXX_INVON, 0,
 | 
			
		||||
    ILI9XXX_DISPON, 0x80,     // Set display on
 | 
			
		||||
    0x00 // end
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const uint8_t PROGMEM INITCMD_ILI9486[] = {
 | 
			
		||||
  ILI9XXX_SLPOUT, 0x80,
 | 
			
		||||
  ILI9XXX_PIXFMT, 1, 0x55,
 | 
			
		||||
 
 | 
			
		||||
@@ -255,7 +255,11 @@ async def to_code(config):
 | 
			
		||||
 | 
			
		||||
    transparent = config[CONF_USE_TRANSPARENCY]
 | 
			
		||||
 | 
			
		||||
    dither = Image.NONE if config[CONF_DITHER] == "NONE" else Image.FLOYDSTEINBERG
 | 
			
		||||
    dither = (
 | 
			
		||||
        Image.Dither.NONE
 | 
			
		||||
        if config[CONF_DITHER] == "NONE"
 | 
			
		||||
        else Image.Dither.FLOYDSTEINBERG
 | 
			
		||||
    )
 | 
			
		||||
    if config[CONF_TYPE] == "GRAYSCALE":
 | 
			
		||||
        image = image.convert("LA", dither=dither)
 | 
			
		||||
        pixels = list(image.getdata())
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,8 @@ from esphome.const import (
 | 
			
		||||
    ENTITY_CATEGORY_DIAGNOSTIC,
 | 
			
		||||
    KEY_CORE,
 | 
			
		||||
    KEY_FRAMEWORK_VERSION,
 | 
			
		||||
    PLATFORM_ESP32,
 | 
			
		||||
    PLATFORM_RP2040,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
 | 
			
		||||
@@ -49,7 +51,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
        state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
			
		||||
    ).extend(cv.polling_component_schema("60s")),
 | 
			
		||||
    cv.only_on(["esp32", "rp2040"]),
 | 
			
		||||
    cv.only_on([PLATFORM_ESP32, PLATFORM_RP2040]),
 | 
			
		||||
    validate_config,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -170,7 +170,7 @@ def _notify_old_style(config):
 | 
			
		||||
ARDUINO_VERSIONS = {
 | 
			
		||||
    "dev": (cv.Version(0, 0, 0), "https://github.com/libretiny-eu/libretiny.git"),
 | 
			
		||||
    "latest": (cv.Version(0, 0, 0), None),
 | 
			
		||||
    "recommended": (cv.Version(1, 4, 0), None),
 | 
			
		||||
    "recommended": (cv.Version(1, 4, 1), None),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -251,7 +251,7 @@ async def component_to_code(config):
 | 
			
		||||
    # setup board config
 | 
			
		||||
    cg.add_platformio_option("board", config[CONF_BOARD])
 | 
			
		||||
    cg.add_build_flag("-DUSE_LIBRETINY")
 | 
			
		||||
    cg.add_build_flag(f"-DUSE_{config[CONF_COMPONENT_ID]}")
 | 
			
		||||
    cg.add_build_flag(f"-DUSE_{config[CONF_COMPONENT_ID].upper()}")
 | 
			
		||||
    cg.add_build_flag(f"-DUSE_LIBRETINY_VARIANT_{config[CONF_FAMILY]}")
 | 
			
		||||
    cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
 | 
			
		||||
    cg.add_define("ESPHOME_VARIANT", FAMILY_FRIENDLY[config[CONF_FAMILY]])
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@ from esphome.components.esp32.const import (
 | 
			
		||||
    VARIANT_ESP32S3,
 | 
			
		||||
    VARIANT_ESP32C2,
 | 
			
		||||
    VARIANT_ESP32C6,
 | 
			
		||||
    VARIANT_ESP32H2,
 | 
			
		||||
)
 | 
			
		||||
from esphome.components.libretiny import get_libretiny_component, get_libretiny_family
 | 
			
		||||
from esphome.components.libretiny.const import (
 | 
			
		||||
@@ -86,6 +87,7 @@ UART_SELECTION_ESP32 = {
 | 
			
		||||
    VARIANT_ESP32C3: [UART0, UART1, USB_SERIAL_JTAG],
 | 
			
		||||
    VARIANT_ESP32C2: [UART0, UART1],
 | 
			
		||||
    VARIANT_ESP32C6: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
 | 
			
		||||
    VARIANT_ESP32H2: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
UART_SELECTION_ESP8266 = [UART0, UART0_SWAP, UART1]
 | 
			
		||||
 
 | 
			
		||||
@@ -121,7 +121,7 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) {
 | 
			
		||||
    if (
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2)
 | 
			
		||||
        uart_ == UART_SELECTION_USB_CDC
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32H2)
 | 
			
		||||
        uart_ == UART_SELECTION_USB_SERIAL_JTAG
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
        uart_ == UART_SELECTION_USB_CDC || uart_ == UART_SELECTION_USB_SERIAL_JTAG
 | 
			
		||||
@@ -218,21 +218,24 @@ void Logger::pre_setup() {
 | 
			
		||||
        uart_num_ = UART_NUM_1;
 | 
			
		||||
        break;
 | 
			
		||||
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
 | 
			
		||||
    !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) && !defined(USE_ESP32_VARIANT_ESP32H2)
 | 
			
		||||
      case UART_SELECTION_UART2:
 | 
			
		||||
        uart_num_ = UART_NUM_2;
 | 
			
		||||
        break;
 | 
			
		||||
#endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3
 | 
			
		||||
#endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 &&
 | 
			
		||||
        // !USE_ESP32_VARIANT_ESP32H2
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
      case UART_SELECTION_USB_CDC:
 | 
			
		||||
        uart_num_ = -1;
 | 
			
		||||
        break;
 | 
			
		||||
#endif  // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
 | 
			
		||||
    defined(USE_ESP32_VARIANT_ESP32H2)
 | 
			
		||||
      case UART_SELECTION_USB_SERIAL_JTAG:
 | 
			
		||||
        uart_num_ = -1;
 | 
			
		||||
        break;
 | 
			
		||||
#endif  // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3
 | 
			
		||||
#endif  // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 ||
 | 
			
		||||
        // USE_ESP32_VARIANT_ESP32H2
 | 
			
		||||
    }
 | 
			
		||||
    if (uart_num_ >= 0) {
 | 
			
		||||
      uart_config_t uart_config{};
 | 
			
		||||
@@ -331,9 +334,10 @@ const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DE
 | 
			
		||||
const char *const UART_SELECTIONS[] = {
 | 
			
		||||
    "UART0",           "UART1",
 | 
			
		||||
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
 | 
			
		||||
    !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) && !defined(USE_ESP32_VARIANT_ESP32H2)
 | 
			
		||||
    "UART2",
 | 
			
		||||
#endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3
 | 
			
		||||
#endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARINT_ESP32C6 && !USE_ESP32_VARIANT_ESP32S2 &&
 | 
			
		||||
        // !USE_ESP32_VARIANT_ESP32S3 && !USE_ESP32_VARIANT_ESP32H2
 | 
			
		||||
#if defined(USE_ESP_IDF)
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    "USB_CDC",
 | 
			
		||||
 
 | 
			
		||||
@@ -41,16 +41,19 @@ enum UARTSelection {
 | 
			
		||||
  UART_SELECTION_UART1,
 | 
			
		||||
#if defined(USE_ESP32)
 | 
			
		||||
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
 | 
			
		||||
    !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) && !defined(USE_ESP32_VARIANT_ESP32H2)
 | 
			
		||||
  UART_SELECTION_UART2,
 | 
			
		||||
#endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3
 | 
			
		||||
#endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32C6 && !USE_ESP32_VARIANT_ESP32S2 &&
 | 
			
		||||
        // !USE_ESP32_VARIANT_ESP32S3 && !USE_ESP32_VARIANT_ESP32H2
 | 
			
		||||
#ifdef USE_ESP_IDF
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
  UART_SELECTION_USB_CDC,
 | 
			
		||||
#endif  // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
 | 
			
		||||
    defined(USE_ESP32_VARIANT_ESP32H2)
 | 
			
		||||
  UART_SELECTION_USB_SERIAL_JTAG,
 | 
			
		||||
#endif  // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3
 | 
			
		||||
#endif  // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 ||
 | 
			
		||||
        // USE_ESP32_VARIANT_ESP32H2
 | 
			
		||||
#endif  // USE_ESP_IDF
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ namespace max6675 {
 | 
			
		||||
class MAX6675Sensor : public sensor::Sensor,
 | 
			
		||||
                      public PollingComponent,
 | 
			
		||||
                      public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
 | 
			
		||||
                                            spi::DATA_RATE_1KHZ> {
 | 
			
		||||
                                            spi::DATA_RATE_1MHZ> {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 
 | 
			
		||||
@@ -164,6 +164,10 @@ void MAX7219Component::send_to_all_(uint8_t a_register, uint8_t data) {
 | 
			
		||||
  this->disable();
 | 
			
		||||
}
 | 
			
		||||
void MAX7219Component::update() {
 | 
			
		||||
  if (this->intensity_changed_) {
 | 
			
		||||
    this->send_to_all_(MAX7219_REGISTER_INTENSITY, this->intensity_);
 | 
			
		||||
    this->intensity_changed_ = false;
 | 
			
		||||
  }
 | 
			
		||||
  for (uint8_t i = 0; i < this->num_chips_ * 8; i++)
 | 
			
		||||
    this->buffer_[i] = 0;
 | 
			
		||||
  if (this->writer_.has_value())
 | 
			
		||||
@@ -218,8 +222,11 @@ uint8_t MAX7219Component::printf(const char *format, ...) {
 | 
			
		||||
}
 | 
			
		||||
void MAX7219Component::set_writer(max7219_writer_t &&writer) { this->writer_ = writer; }
 | 
			
		||||
void MAX7219Component::set_intensity(uint8_t intensity) {
 | 
			
		||||
  this->intensity_ = intensity;
 | 
			
		||||
  this->send_to_all_(MAX7219_REGISTER_INTENSITY, this->intensity_);
 | 
			
		||||
  intensity &= 0xF;
 | 
			
		||||
  if (intensity != this->intensity_) {
 | 
			
		||||
    this->intensity_changed_ = true;
 | 
			
		||||
    this->intensity_ = intensity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void MAX7219Component::set_num_chips(uint8_t num_chips) { this->num_chips_ = num_chips; }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,8 @@ class MAX7219Component : public PollingComponent,
 | 
			
		||||
  void send_byte_(uint8_t a_register, uint8_t data);
 | 
			
		||||
  void send_to_all_(uint8_t a_register, uint8_t data);
 | 
			
		||||
 | 
			
		||||
  uint8_t intensity_{15};  /// Intensity of the display from 0 to 15 (most)
 | 
			
		||||
  uint8_t intensity_{15};     // Intensity of the display from 0 to 15 (most)
 | 
			
		||||
  bool intensity_changed_{};  // True if we need to re-send the intensity
 | 
			
		||||
  uint8_t num_chips_{1};
 | 
			
		||||
  uint8_t *buffer_;
 | 
			
		||||
  bool reverse_{false};
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,7 @@ namespace mdns {
 | 
			
		||||
void MDNSComponent::setup() {
 | 
			
		||||
  this->compile_records_();
 | 
			
		||||
 | 
			
		||||
  network::IPAddress addr = network::get_ip_address();
 | 
			
		||||
  MDNS.begin(this->hostname_.c_str(), (uint32_t) addr);
 | 
			
		||||
  MDNS.begin(this->hostname_.c_str());
 | 
			
		||||
 | 
			
		||||
  for (const auto &service : this->services_) {
 | 
			
		||||
    // Strip the leading underscore from the proto and service_type. While it is
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,9 @@ import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ADDRESS,
 | 
			
		||||
    CONF_FIELD_STRENGTH_X,
 | 
			
		||||
    CONF_FIELD_STRENGTH_Y,
 | 
			
		||||
    CONF_FIELD_STRENGTH_Z,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    ICON_MAGNET,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
@@ -16,9 +19,6 @@ DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
mmc5603_ns = cg.esphome_ns.namespace("mmc5603")
 | 
			
		||||
 | 
			
		||||
CONF_FIELD_STRENGTH_X = "field_strength_x"
 | 
			
		||||
CONF_FIELD_STRENGTH_Y = "field_strength_y"
 | 
			
		||||
CONF_FIELD_STRENGTH_Z = "field_strength_z"
 | 
			
		||||
CONF_HEADING = "heading"
 | 
			
		||||
 | 
			
		||||
MMC5603Component = mmc5603_ns.class_(
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								esphome/components/mmc5983/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/mmc5983/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
CODEOWNERS = ["@agoode"]
 | 
			
		||||
							
								
								
									
										141
									
								
								esphome/components/mmc5983/mmc5983.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								esphome/components/mmc5983/mmc5983.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
// See https://github.com/sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library/tree/main
 | 
			
		||||
// for datasheets and an Arduino implementation.
 | 
			
		||||
 | 
			
		||||
#include "mmc5983.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace mmc5983 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "mmc5983";
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
constexpr uint8_t IC0_ADDR = 0x09;
 | 
			
		||||
constexpr uint8_t IC1_ADDR = 0x0a;
 | 
			
		||||
constexpr uint8_t IC2_ADDR = 0x0b;
 | 
			
		||||
constexpr uint8_t IC3_ADDR = 0x0c;
 | 
			
		||||
constexpr uint8_t PRODUCT_ID_ADDR = 0x2f;
 | 
			
		||||
 | 
			
		||||
float convert_data_to_millitesla(uint8_t data_17_10, uint8_t data_9_2, uint8_t data_1_0) {
 | 
			
		||||
  int32_t counts = (data_17_10 << 10) | (data_9_2 << 2) | data_1_0;
 | 
			
		||||
  counts -= 131072;  // "Null Field Output" from datasheet.
 | 
			
		||||
 | 
			
		||||
  // Sensitivity is 16384 counts/gauss, which is 163840 counts/mT.
 | 
			
		||||
  return counts / 163840.0f;
 | 
			
		||||
}
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
void MMC5983Component::update() {
 | 
			
		||||
  // Schedule a SET/RESET. This will recalibrate the sensor.
 | 
			
		||||
  // We are supposed to be able to set this once, and have it automatically continue every reading, but
 | 
			
		||||
  // this does not appear to work in continuous mode, even with En_prd_set turned on in Internal Control 2.
 | 
			
		||||
  // Bit 5 = Auto_SR_en (automatic SET/RESET enable).
 | 
			
		||||
  const uint8_t ic0_value = 0b10000;
 | 
			
		||||
  i2c::ErrorCode err = this->write_register(IC0_ADDR, &ic0_value, 1);
 | 
			
		||||
  if (err != i2c::ErrorCode::ERROR_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "Writing Internal Control 0 failed with i2c error %d", err);
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Read out the data, 7 bytes starting from 0x00.
 | 
			
		||||
  uint8_t data[7];
 | 
			
		||||
  err = this->read_register(0x00, data, sizeof(data));
 | 
			
		||||
  if (err != i2c::ErrorCode::ERROR_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "Reading data failed with i2c error %d", err);
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Unpack the data and publish to sensors.
 | 
			
		||||
  // Data is in this format:
 | 
			
		||||
  //   data[0]: Xout[17:10]
 | 
			
		||||
  //   data[1]: Xout[9:2]
 | 
			
		||||
  //   data[2]: Yout[17:10]
 | 
			
		||||
  //   data[3]: Yout[9:2]
 | 
			
		||||
  //   data[4]: Zout[17:10]
 | 
			
		||||
  //   data[5]: Zout[9:2]
 | 
			
		||||
  //   data[6]: { Xout[1], Xout[0], Yout[1], Yout[0], Zout[1], Zout[0], 0, 0 }
 | 
			
		||||
  if (this->x_sensor_) {
 | 
			
		||||
    this->x_sensor_->publish_state(convert_data_to_millitesla(data[0], data[1], (data[6] & 0b11000000) >> 6));
 | 
			
		||||
  }
 | 
			
		||||
  if (this->y_sensor_) {
 | 
			
		||||
    this->y_sensor_->publish_state(convert_data_to_millitesla(data[2], data[3], (data[6] & 0b00110000) >> 4));
 | 
			
		||||
  }
 | 
			
		||||
  if (this->z_sensor_) {
 | 
			
		||||
    this->z_sensor_->publish_state(convert_data_to_millitesla(data[4], data[5], (data[6] & 0b00001100) >> 2));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MMC5983Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up MMC5983...");
 | 
			
		||||
 | 
			
		||||
  // Verify product id.
 | 
			
		||||
  const uint8_t mmc5983_product_id = 0x30;
 | 
			
		||||
  uint8_t id;
 | 
			
		||||
  i2c::ErrorCode err = this->read_register(PRODUCT_ID_ADDR, &id, 1);
 | 
			
		||||
  if (err != i2c::ErrorCode::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Reading product id failed with i2c error %d", err);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (id != mmc5983_product_id) {
 | 
			
		||||
    ESP_LOGE(TAG, "Product id 0x%02x does not match expected value 0x%02x", id, mmc5983_product_id);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Initialize Internal Control registers to 0.
 | 
			
		||||
  // Internal Control 0.
 | 
			
		||||
  const uint8_t zero = 0;
 | 
			
		||||
  err = this->write_register(IC0_ADDR, &zero, 1);
 | 
			
		||||
  if (err != i2c::ErrorCode::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Initializing Internal Control 0 failed with i2c error %d", err);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  // Internal Control 1.
 | 
			
		||||
  err = this->write_register(IC1_ADDR, &zero, 1);
 | 
			
		||||
  if (err != i2c::ErrorCode::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Initializing Internal Control 1 failed with i2c error %d", err);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  // Internal Control 2.
 | 
			
		||||
  err = this->write_register(IC2_ADDR, &zero, 1);
 | 
			
		||||
  if (err != i2c::ErrorCode::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Initializing Internal Control 2 failed with i2c error %d", err);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  // Internal Control 3.
 | 
			
		||||
  err = this->write_register(IC3_ADDR, &zero, 1);
 | 
			
		||||
  if (err != i2c::ErrorCode::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Initializing Internal Control 3 failed with i2c error %d", err);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Enable continuous mode at 100 Hz, using Internal Control 2.
 | 
			
		||||
  // Bit 3 = Cmm_en (continuous mode enable).
 | 
			
		||||
  // Bit [2:0] = Cm_freq. 0b101 = 100 Hz, the fastest reading speed at Bandwidth=100 Hz.
 | 
			
		||||
  const uint8_t ic2_value = 0b00001101;
 | 
			
		||||
  err = this->write_register(IC2_ADDR, &ic2_value, 1);
 | 
			
		||||
  if (err != i2c::ErrorCode::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Writing Internal Control 2 failed with i2c error %d", err);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MMC5983Component::dump_config() {
 | 
			
		||||
  ESP_LOGD(TAG, "MMC5983:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  LOG_SENSOR("  ", "X", this->x_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Y", this->y_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Z", this->z_sensor_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float MMC5983Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
}  // namespace mmc5983
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										28
									
								
								esphome/components/mmc5983/mmc5983.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/mmc5983/mmc5983.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace mmc5983 {
 | 
			
		||||
 | 
			
		||||
class MMC5983Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 | 
			
		||||
  void set_x_sensor(sensor::Sensor *x_sensor) { x_sensor_ = x_sensor; }
 | 
			
		||||
  void set_y_sensor(sensor::Sensor *y_sensor) { y_sensor_ = y_sensor; }
 | 
			
		||||
  void set_z_sensor(sensor::Sensor *z_sensor) { z_sensor_ = z_sensor; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  sensor::Sensor *x_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *y_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *z_sensor_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace mmc5983
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										55
									
								
								esphome/components/mmc5983/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								esphome/components/mmc5983/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_FIELD_STRENGTH_X,
 | 
			
		||||
    CONF_FIELD_STRENGTH_Y,
 | 
			
		||||
    CONF_FIELD_STRENGTH_Z,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    ICON_MAGNET,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_MICROTESLA,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
mmc5983_ns = cg.esphome_ns.namespace("mmc5983")
 | 
			
		||||
MMC5983Component = mmc5983_ns.class_(
 | 
			
		||||
    "MMC5983Component", cg.PollingComponent, i2c.I2CDevice
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
field_strength_schema = sensor.sensor_schema(
 | 
			
		||||
    unit_of_measurement=UNIT_MICROTESLA,
 | 
			
		||||
    icon=ICON_MAGNET,
 | 
			
		||||
    accuracy_decimals=4,
 | 
			
		||||
    state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(MMC5983Component),
 | 
			
		||||
            cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema,
 | 
			
		||||
            cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema,
 | 
			
		||||
            cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(i2c.i2c_device_schema(0x30))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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 x_config := config.get(CONF_FIELD_STRENGTH_X):
 | 
			
		||||
        sens = await sensor.new_sensor(x_config)
 | 
			
		||||
        cg.add(var.set_x_sensor(sens))
 | 
			
		||||
    if y_config := config.get(CONF_FIELD_STRENGTH_Y):
 | 
			
		||||
        sens = await sensor.new_sensor(y_config)
 | 
			
		||||
        cg.add(var.set_y_sensor(sens))
 | 
			
		||||
    if z_config := config.get(CONF_FIELD_STRENGTH_Z):
 | 
			
		||||
        sens = await sensor.new_sensor(z_config)
 | 
			
		||||
        cg.add(var.set_z_sensor(sens))
 | 
			
		||||
@@ -43,6 +43,9 @@ from esphome.const import (
 | 
			
		||||
    CONF_USE_ABBREVIATIONS,
 | 
			
		||||
    CONF_USERNAME,
 | 
			
		||||
    CONF_WILL_MESSAGE,
 | 
			
		||||
    PLATFORM_ESP32,
 | 
			
		||||
    PLATFORM_ESP8266,
 | 
			
		||||
    PLATFORM_BK72XX,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import coroutine_with_priority, CORE
 | 
			
		||||
from esphome.components.esp32 import add_idf_sdkconfig_option
 | 
			
		||||
@@ -250,7 +253,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
    validate_config,
 | 
			
		||||
    cv.only_on(["esp32", "esp8266"]),
 | 
			
		||||
    cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX]),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -271,10 +274,10 @@ def exp_mqtt_message(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    # Add required libraries for ESP8266
 | 
			
		||||
    if CORE.is_esp8266:
 | 
			
		||||
    # Add required libraries for ESP8266 and LibreTiny
 | 
			
		||||
    if CORE.is_esp8266 or CORE.is_libretiny:
 | 
			
		||||
        # https://github.com/heman/async-mqtt-client/blob/master/library.json
 | 
			
		||||
        cg.add_library("heman/AsyncMqttClient-esphome", "1.0.0")
 | 
			
		||||
        cg.add_library("heman/AsyncMqttClient-esphome", "2.0.0")
 | 
			
		||||
 | 
			
		||||
    cg.add_define("USE_MQTT")
 | 
			
		||||
    cg.add_global(mqtt_ns.using)
 | 
			
		||||
 
 | 
			
		||||
@@ -19,9 +19,7 @@ class MQTTBackendESP8266 final : public MQTTBackend {
 | 
			
		||||
  void set_will(const char *topic, uint8_t qos, bool retain, const char *payload) final {
 | 
			
		||||
    mqtt_client_.setWill(topic, qos, retain, payload);
 | 
			
		||||
  }
 | 
			
		||||
  void set_server(network::IPAddress ip, uint16_t port) final {
 | 
			
		||||
    mqtt_client_.setServer(IPAddress(static_cast<uint32_t>(ip)), port);
 | 
			
		||||
  }
 | 
			
		||||
  void set_server(network::IPAddress ip, uint16_t port) final { mqtt_client_.setServer(IPAddress(ip), port); }
 | 
			
		||||
  void set_server(const char *host, uint16_t port) final { mqtt_client_.setServer(host, port); }
 | 
			
		||||
#if ASYNC_TCP_SSL_ENABLED
 | 
			
		||||
  void set_secure(bool secure) { mqtt_client.setSecure(secure); }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										74
									
								
								esphome/components/mqtt/mqtt_backend_libretiny.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								esphome/components/mqtt/mqtt_backend_libretiny.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LIBRETINY
 | 
			
		||||
 | 
			
		||||
#include "mqtt_backend.h"
 | 
			
		||||
#include <AsyncMqttClient.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace mqtt {
 | 
			
		||||
 | 
			
		||||
class MQTTBackendLibreTiny final : public MQTTBackend {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_keep_alive(uint16_t keep_alive) final { mqtt_client_.setKeepAlive(keep_alive); }
 | 
			
		||||
  void set_client_id(const char *client_id) final { mqtt_client_.setClientId(client_id); }
 | 
			
		||||
  void set_clean_session(bool clean_session) final { mqtt_client_.setCleanSession(clean_session); }
 | 
			
		||||
  void set_credentials(const char *username, const char *password) final {
 | 
			
		||||
    mqtt_client_.setCredentials(username, password);
 | 
			
		||||
  }
 | 
			
		||||
  void set_will(const char *topic, uint8_t qos, bool retain, const char *payload) final {
 | 
			
		||||
    mqtt_client_.setWill(topic, qos, retain, payload);
 | 
			
		||||
  }
 | 
			
		||||
  void set_server(network::IPAddress ip, uint16_t port) final {
 | 
			
		||||
    mqtt_client_.setServer(IPAddress(static_cast<uint32_t>(ip)), port);
 | 
			
		||||
  }
 | 
			
		||||
  void set_server(const char *host, uint16_t port) final { mqtt_client_.setServer(host, port); }
 | 
			
		||||
#if ASYNC_TCP_SSL_ENABLED
 | 
			
		||||
  void set_secure(bool secure) { mqtt_client.setSecure(secure); }
 | 
			
		||||
  void add_server_fingerprint(const uint8_t *fingerprint) { mqtt_client.addServerFingerprint(fingerprint); }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  void set_on_connect(std::function<on_connect_callback_t> &&callback) final {
 | 
			
		||||
    this->mqtt_client_.onConnect(std::move(callback));
 | 
			
		||||
  }
 | 
			
		||||
  void set_on_disconnect(std::function<on_disconnect_callback_t> &&callback) final {
 | 
			
		||||
    auto async_callback = [callback](AsyncMqttClientDisconnectReason reason) {
 | 
			
		||||
      // int based enum so casting isn't a problem
 | 
			
		||||
      callback(static_cast<MQTTClientDisconnectReason>(reason));
 | 
			
		||||
    };
 | 
			
		||||
    this->mqtt_client_.onDisconnect(std::move(async_callback));
 | 
			
		||||
  }
 | 
			
		||||
  void set_on_subscribe(std::function<on_subscribe_callback_t> &&callback) final {
 | 
			
		||||
    this->mqtt_client_.onSubscribe(std::move(callback));
 | 
			
		||||
  }
 | 
			
		||||
  void set_on_unsubscribe(std::function<on_unsubscribe_callback_t> &&callback) final {
 | 
			
		||||
    this->mqtt_client_.onUnsubscribe(std::move(callback));
 | 
			
		||||
  }
 | 
			
		||||
  void set_on_message(std::function<on_message_callback_t> &&callback) final {
 | 
			
		||||
    auto async_callback = [callback](const char *topic, const char *payload,
 | 
			
		||||
                                     AsyncMqttClientMessageProperties async_properties, size_t len, size_t index,
 | 
			
		||||
                                     size_t total) { callback(topic, payload, len, index, total); };
 | 
			
		||||
    mqtt_client_.onMessage(std::move(async_callback));
 | 
			
		||||
  }
 | 
			
		||||
  void set_on_publish(std::function<on_publish_user_callback_t> &&callback) final {
 | 
			
		||||
    this->mqtt_client_.onPublish(std::move(callback));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool connected() const final { return mqtt_client_.connected(); }
 | 
			
		||||
  void connect() final { mqtt_client_.connect(); }
 | 
			
		||||
  void disconnect() final { mqtt_client_.disconnect(true); }
 | 
			
		||||
  bool subscribe(const char *topic, uint8_t qos) final { return mqtt_client_.subscribe(topic, qos) != 0; }
 | 
			
		||||
  bool unsubscribe(const char *topic) final { return mqtt_client_.unsubscribe(topic) != 0; }
 | 
			
		||||
  bool publish(const char *topic, const char *payload, size_t length, uint8_t qos, bool retain) final {
 | 
			
		||||
    return mqtt_client_.publish(topic, qos, retain, payload, length, false, 0) != 0;
 | 
			
		||||
  }
 | 
			
		||||
  using MQTTBackend::publish;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  AsyncMqttClient mqtt_client_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace mqtt
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // defined(USE_LIBRETINY)
 | 
			
		||||
@@ -106,6 +106,9 @@ void MQTTClientComponent::send_device_info_() {
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
        root["platform"] = "ESP32";
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIBRETINY
 | 
			
		||||
        root["platform"] = lt_cpu_get_model_name();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        root["board"] = ESPHOME_BOARD;
 | 
			
		||||
#if defined(USE_WIFI)
 | 
			
		||||
@@ -156,7 +159,7 @@ void MQTTClientComponent::start_dnslookup_() {
 | 
			
		||||
  this->dns_resolve_error_ = false;
 | 
			
		||||
  this->dns_resolved_ = false;
 | 
			
		||||
  ip_addr_t addr;
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
 | 
			
		||||
  err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr,
 | 
			
		||||
                                         MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -168,11 +171,7 @@ void MQTTClientComponent::start_dnslookup_() {
 | 
			
		||||
    case ERR_OK: {
 | 
			
		||||
      // Got IP immediately
 | 
			
		||||
      this->dns_resolved_ = true;
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
      this->ip_ = addr.u_addr.ip4.addr;
 | 
			
		||||
#else
 | 
			
		||||
      this->ip_ = addr.addr;
 | 
			
		||||
#endif
 | 
			
		||||
      this->ip_ = network::IPAddress(&addr);
 | 
			
		||||
      this->start_connect_();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -223,11 +222,7 @@ void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t *
 | 
			
		||||
  if (ipaddr == nullptr) {
 | 
			
		||||
    a_this->dns_resolve_error_ = true;
 | 
			
		||||
  } else {
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
    a_this->ip_ = ipaddr->u_addr.ip4.addr;
 | 
			
		||||
#else
 | 
			
		||||
    a_this->ip_ = ipaddr->addr;
 | 
			
		||||
#endif
 | 
			
		||||
    a_this->ip_ = network::IPAddress(ipaddr);
 | 
			
		||||
    a_this->dns_resolved_ = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@
 | 
			
		||||
#include "mqtt_backend_esp32.h"
 | 
			
		||||
#elif defined(USE_ESP8266)
 | 
			
		||||
#include "mqtt_backend_esp8266.h"
 | 
			
		||||
#elif defined(USE_LIBRETINY)
 | 
			
		||||
#include "mqtt_backend_libretiny.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "lwip/ip_addr.h"
 | 
			
		||||
 | 
			
		||||
@@ -300,6 +302,8 @@ class MQTTClientComponent : public Component {
 | 
			
		||||
  MQTTBackendESP32 mqtt_backend_;
 | 
			
		||||
#elif defined(USE_ESP8266)
 | 
			
		||||
  MQTTBackendESP8266 mqtt_backend_;
 | 
			
		||||
#elif defined(USE_LIBRETINY)
 | 
			
		||||
  MQTTBackendLibreTiny mqtt_backend_;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  MQTTClientState state_{MQTT_CLIENT_DISCONNECTED};
 | 
			
		||||
 
 | 
			
		||||
@@ -3,42 +3,104 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <lwip/ip_addr.h>
 | 
			
		||||
 | 
			
		||||
#if USE_ARDUINO
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <IPAddress.h>
 | 
			
		||||
#endif /* USE_ADRDUINO */
 | 
			
		||||
 | 
			
		||||
#if USE_ESP32_FRAMEWORK_ARDUINO
 | 
			
		||||
#define arduino_ns Arduino_h
 | 
			
		||||
#elif USE_LIBRETINY
 | 
			
		||||
#define arduino_ns arduino
 | 
			
		||||
#elif USE_ARDUINO
 | 
			
		||||
#define arduino_ns
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <esp_netif.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace network {
 | 
			
		||||
 | 
			
		||||
struct IPAddress {
 | 
			
		||||
 public:
 | 
			
		||||
  IPAddress() : addr_({0, 0, 0, 0}) {}
 | 
			
		||||
  IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) : addr_({first, second, third, fourth}) {}
 | 
			
		||||
  IPAddress(uint32_t raw) {
 | 
			
		||||
    addr_[0] = (uint8_t) (raw >> 0);
 | 
			
		||||
    addr_[1] = (uint8_t) (raw >> 8);
 | 
			
		||||
    addr_[2] = (uint8_t) (raw >> 16);
 | 
			
		||||
    addr_[3] = (uint8_t) (raw >> 24);
 | 
			
		||||
  IPAddress() { ip_addr_set_zero(&ip_addr_); }
 | 
			
		||||
  IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) {
 | 
			
		||||
    IP_ADDR4(&ip_addr_, first, second, third, fourth);
 | 
			
		||||
  }
 | 
			
		||||
  operator uint32_t() const {
 | 
			
		||||
    uint32_t res = 0;
 | 
			
		||||
    res |= ((uint32_t) addr_[0]) << 0;
 | 
			
		||||
    res |= ((uint32_t) addr_[1]) << 8;
 | 
			
		||||
    res |= ((uint32_t) addr_[2]) << 16;
 | 
			
		||||
    res |= ((uint32_t) addr_[3]) << 24;
 | 
			
		||||
    return res;
 | 
			
		||||
  IPAddress(const ip_addr_t *other_ip) { ip_addr_copy(ip_addr_, *other_ip); }
 | 
			
		||||
  IPAddress(const std::string &in_address) { ipaddr_aton(in_address.c_str(), &ip_addr_); }
 | 
			
		||||
  IPAddress(ip4_addr_t *other_ip) { memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip4_addr_t)); }
 | 
			
		||||
#if USE_ARDUINO
 | 
			
		||||
  IPAddress(const arduino_ns::IPAddress &other_ip) { ip_addr_set_ip4_u32(&ip_addr_, other_ip); }
 | 
			
		||||
#endif
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
  IPAddress(ip6_addr_t *other_ip) {
 | 
			
		||||
    memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip6_addr_t));
 | 
			
		||||
    ip_addr_.type = IPADDR_TYPE_V6;
 | 
			
		||||
  }
 | 
			
		||||
  std::string str() const {
 | 
			
		||||
    char buffer[24];
 | 
			
		||||
    snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d", addr_[0], addr_[1], addr_[2], addr_[3]);
 | 
			
		||||
    return buffer;
 | 
			
		||||
#endif /* LWIP_IPV6 */
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
  IPAddress(esp_ip6_addr_t *other_ip) {
 | 
			
		||||
    memcpy((void *) &ip_addr_.u_addr.ip6, (void *) other_ip, sizeof(esp_ip6_addr_t));
 | 
			
		||||
    ip_addr_.type = IPADDR_TYPE_V6;
 | 
			
		||||
  }
 | 
			
		||||
  bool operator==(const IPAddress &other) const {
 | 
			
		||||
    return addr_[0] == other.addr_[0] && addr_[1] == other.addr_[1] && addr_[2] == other.addr_[2] &&
 | 
			
		||||
           addr_[3] == other.addr_[3];
 | 
			
		||||
#endif /* LWIP_IPV6 */
 | 
			
		||||
  IPAddress(esp_ip4_addr_t *other_ip) { memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(esp_ip4_addr_t)); }
 | 
			
		||||
  operator esp_ip_addr_t() const {
 | 
			
		||||
    esp_ip_addr_t tmp;
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
    memcpy((void *) &tmp, (void *) &ip_addr_, sizeof(ip_addr_));
 | 
			
		||||
#else
 | 
			
		||||
    memcpy((void *) &tmp.u_addr.ip4, (void *) &ip_addr_, sizeof(ip_addr_));
 | 
			
		||||
#endif /* LWIP_IPV6 */
 | 
			
		||||
    return tmp;
 | 
			
		||||
  }
 | 
			
		||||
  operator esp_ip4_addr_t() const {
 | 
			
		||||
    esp_ip4_addr_t tmp;
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
    memcpy((void *) &tmp, (void *) &ip_addr_.u_addr.ip4, sizeof(esp_ip4_addr_t));
 | 
			
		||||
#else
 | 
			
		||||
    memcpy((void *) &tmp, (void *) &ip_addr_, sizeof(ip_addr_));
 | 
			
		||||
#endif /* LWIP_IPV6 */
 | 
			
		||||
    return tmp;
 | 
			
		||||
  }
 | 
			
		||||
#endif /* USE_ESP32 */
 | 
			
		||||
 | 
			
		||||
  operator ip_addr_t() const { return ip_addr_; }
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
  operator ip4_addr_t() const { return *ip_2_ip4(&ip_addr_); }
 | 
			
		||||
#endif /* LWIP_IPV6 */
 | 
			
		||||
 | 
			
		||||
#if USE_ARDUINO
 | 
			
		||||
  operator arduino_ns::IPAddress() const { return ip_addr_get_ip4_u32(&ip_addr_); }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  bool is_set() { return !ip_addr_isany(&ip_addr_); }
 | 
			
		||||
  bool is_ip4() { return IP_IS_V4(&ip_addr_); }
 | 
			
		||||
  bool is_ip6() { return IP_IS_V6(&ip_addr_); }
 | 
			
		||||
  std::string str() const { return ipaddr_ntoa(&ip_addr_); }
 | 
			
		||||
  bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
 | 
			
		||||
  bool operator!=(const IPAddress &other) const { return !ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
 | 
			
		||||
  IPAddress &operator+=(uint8_t increase) {
 | 
			
		||||
    if (IP_IS_V4(&ip_addr_)) {
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
      (((u8_t *) (&ip_addr_.u_addr.ip4))[3]) += increase;
 | 
			
		||||
#else
 | 
			
		||||
      (((u8_t *) (&ip_addr_.addr))[3]) += increase;
 | 
			
		||||
#endif /* LWIP_IPV6 */
 | 
			
		||||
    }
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
  uint8_t operator[](int index) const { return addr_[index]; }
 | 
			
		||||
  uint8_t &operator[](int index) { return addr_[index]; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::array<uint8_t, 4> addr_;
 | 
			
		||||
  ip_addr_t ip_addr_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace network
 | 
			
		||||
 
 | 
			
		||||
@@ -61,6 +61,11 @@ bool Nextion::check_connect_() {
 | 
			
		||||
  std::string response;
 | 
			
		||||
 | 
			
		||||
  this->recv_ret_string_(response, 0, false);
 | 
			
		||||
  if (!response.empty() && response[0] == 0x1A) {
 | 
			
		||||
    // Swallow invalid variable name responses that may be caused by the above commands
 | 
			
		||||
    ESP_LOGD(TAG, "0x1A error ignored during setup");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (response.empty() || response.find("comok") == std::string::npos) {
 | 
			
		||||
#ifdef NEXTION_PROTOCOL_LOG
 | 
			
		||||
    ESP_LOGN(TAG, "Bad connect request %s", response.c_str());
 | 
			
		||||
 
 | 
			
		||||
@@ -76,9 +76,15 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  float published_state = state;
 | 
			
		||||
  if (this->wave_chan_id_ == UINT8_MAX) {
 | 
			
		||||
    if (publish) {
 | 
			
		||||
      this->publish_state(state);
 | 
			
		||||
      if (this->precision_ > 0) {
 | 
			
		||||
        double to_multiply = pow(10, -this->precision_);
 | 
			
		||||
        published_state = (float) (state * to_multiply);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this->publish_state(published_state);
 | 
			
		||||
    } else {
 | 
			
		||||
      this->raw_state = state;
 | 
			
		||||
      this->state = state;
 | 
			
		||||
@@ -87,7 +93,7 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) {
 | 
			
		||||
  }
 | 
			
		||||
  this->update_component_settings();
 | 
			
		||||
 | 
			
		||||
  ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %lf", this->variable_name_.c_str(), state);
 | 
			
		||||
  ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %lf", this->variable_name_.c_str(), published_state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NextionSensor::wave_update_() {
 | 
			
		||||
 
 | 
			
		||||
@@ -106,4 +106,5 @@ async def output_set_level_to_code(config, action_id, template_arg, args):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    cg.add_define("USE_OUTPUT")
 | 
			
		||||
    cg.add_global(output_ns.using)
 | 
			
		||||
 
 | 
			
		||||
@@ -17,12 +17,12 @@ struct PM25AQIData {
 | 
			
		||||
  uint16_t pm10_env,        ///< Environmental PM1.0
 | 
			
		||||
      pm25_env,             ///< Environmental PM2.5
 | 
			
		||||
      pm100_env;            ///< Environmental PM10.0
 | 
			
		||||
  uint16_t particles_03um,  ///< 0.3um Particle Count
 | 
			
		||||
      particles_05um,       ///< 0.5um Particle Count
 | 
			
		||||
      particles_10um,       ///< 1.0um Particle Count
 | 
			
		||||
      particles_25um,       ///< 2.5um Particle Count
 | 
			
		||||
      particles_50um,       ///< 5.0um Particle Count
 | 
			
		||||
      particles_100um;      ///< 10.0um Particle Count
 | 
			
		||||
  uint16_t particles_03um,  ///> 0.3um Particle Count
 | 
			
		||||
      particles_05um,       ///> 0.5um Particle Count
 | 
			
		||||
      particles_10um,       ///> 1.0um Particle Count
 | 
			
		||||
      particles_25um,       ///> 2.5um Particle Count
 | 
			
		||||
      particles_50um,       ///> 5.0um Particle Count
 | 
			
		||||
      particles_100um;      ///> 10.0um Particle Count
 | 
			
		||||
  uint16_t unused;          ///< Unused
 | 
			
		||||
  uint16_t checksum;        ///< Packet checksum
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
 | 
			
		||||
#include "prometheus_handler.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
 | 
			
		||||
@@ -89,7 +87,7 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor
 | 
			
		||||
    stream->print(obj->get_unit_of_measurement().c_str());
 | 
			
		||||
    stream->print(F("\"} "));
 | 
			
		||||
    stream->print(value_accuracy_to_string(obj->state, obj->get_accuracy_decimals()).c_str());
 | 
			
		||||
    stream->print('\n');
 | 
			
		||||
    stream->print(F("\n"));
 | 
			
		||||
  } else {
 | 
			
		||||
    // Invalid state
 | 
			
		||||
    stream->print(F("esphome_sensor_failed{id=\""));
 | 
			
		||||
@@ -124,7 +122,7 @@ void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_s
 | 
			
		||||
    stream->print(relabel_name_(obj).c_str());
 | 
			
		||||
    stream->print(F("\"} "));
 | 
			
		||||
    stream->print(obj->state);
 | 
			
		||||
    stream->print('\n');
 | 
			
		||||
    stream->print(F("\n"));
 | 
			
		||||
  } else {
 | 
			
		||||
    // Invalid state
 | 
			
		||||
    stream->print(F("esphome_binary_sensor_failed{id=\""));
 | 
			
		||||
@@ -158,7 +156,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
 | 
			
		||||
  stream->print(relabel_name_(obj).c_str());
 | 
			
		||||
  stream->print(F("\"} "));
 | 
			
		||||
  stream->print(obj->state);
 | 
			
		||||
  stream->print('\n');
 | 
			
		||||
  stream->print(F("\n"));
 | 
			
		||||
  // Speed if available
 | 
			
		||||
  if (obj->get_traits().supports_speed()) {
 | 
			
		||||
    stream->print(F("esphome_fan_speed{id=\""));
 | 
			
		||||
@@ -167,7 +165,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
 | 
			
		||||
    stream->print(relabel_name_(obj).c_str());
 | 
			
		||||
    stream->print(F("\"} "));
 | 
			
		||||
    stream->print(obj->speed);
 | 
			
		||||
    stream->print('\n');
 | 
			
		||||
    stream->print(F("\n"));
 | 
			
		||||
  }
 | 
			
		||||
  // Oscillation if available
 | 
			
		||||
  if (obj->get_traits().supports_oscillation()) {
 | 
			
		||||
@@ -177,7 +175,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
 | 
			
		||||
    stream->print(relabel_name_(obj).c_str());
 | 
			
		||||
    stream->print(F("\"} "));
 | 
			
		||||
    stream->print(obj->oscillating);
 | 
			
		||||
    stream->print('\n');
 | 
			
		||||
    stream->print(F("\n"));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -281,7 +279,7 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob
 | 
			
		||||
    stream->print(relabel_name_(obj).c_str());
 | 
			
		||||
    stream->print(F("\"} "));
 | 
			
		||||
    stream->print(obj->position);
 | 
			
		||||
    stream->print('\n');
 | 
			
		||||
    stream->print(F("\n"));
 | 
			
		||||
    if (obj->get_traits().get_supports_tilt()) {
 | 
			
		||||
      stream->print(F("esphome_cover_tilt{id=\""));
 | 
			
		||||
      stream->print(relabel_id_(obj).c_str());
 | 
			
		||||
@@ -289,7 +287,7 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob
 | 
			
		||||
      stream->print(relabel_name_(obj).c_str());
 | 
			
		||||
      stream->print(F("\"} "));
 | 
			
		||||
      stream->print(obj->tilt);
 | 
			
		||||
      stream->print('\n');
 | 
			
		||||
      stream->print(F("\n"));
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // Invalid state
 | 
			
		||||
@@ -322,7 +320,7 @@ void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch
 | 
			
		||||
  stream->print(relabel_name_(obj).c_str());
 | 
			
		||||
  stream->print(F("\"} "));
 | 
			
		||||
  stream->print(obj->state);
 | 
			
		||||
  stream->print('\n');
 | 
			
		||||
  stream->print(F("\n"));
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -346,11 +344,9 @@ void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj)
 | 
			
		||||
  stream->print(relabel_name_(obj).c_str());
 | 
			
		||||
  stream->print(F("\"} "));
 | 
			
		||||
  stream->print(obj->state);
 | 
			
		||||
  stream->print('\n');
 | 
			
		||||
  stream->print(F("\n"));
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace prometheus
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ARDUINO
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,12 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/entity_base.h"
 | 
			
		||||
#include "esphome/components/web_server_base/web_server_base.h"
 | 
			
		||||
#include "esphome/core/controller.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/controller.h"
 | 
			
		||||
#include "esphome/core/entity_base.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace prometheus {
 | 
			
		||||
@@ -119,5 +117,3 @@ class PrometheusHandler : public AsyncWebHandler, public Component {
 | 
			
		||||
 | 
			
		||||
}  // namespace prometheus
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ARDUINO
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,9 @@ import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ADDRESS,
 | 
			
		||||
    CONF_FIELD_STRENGTH_X,
 | 
			
		||||
    CONF_FIELD_STRENGTH_Y,
 | 
			
		||||
    CONF_FIELD_STRENGTH_Z,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_OVERSAMPLING,
 | 
			
		||||
    CONF_RANGE,
 | 
			
		||||
@@ -18,9 +21,6 @@ DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
qmc5883l_ns = cg.esphome_ns.namespace("qmc5883l")
 | 
			
		||||
 | 
			
		||||
CONF_FIELD_STRENGTH_X = "field_strength_x"
 | 
			
		||||
CONF_FIELD_STRENGTH_Y = "field_strength_y"
 | 
			
		||||
CONF_FIELD_STRENGTH_Z = "field_strength_z"
 | 
			
		||||
CONF_HEADING = "heading"
 | 
			
		||||
 | 
			
		||||
QMC5883LComponent = qmc5883l_ns.class_(
 | 
			
		||||
 
 | 
			
		||||
@@ -1558,3 +1558,37 @@ async def aeha_action(var, config, args):
 | 
			
		||||
        config[CONF_DATA], args, cg.std_vector.template(cg.uint8)
 | 
			
		||||
    )
 | 
			
		||||
    cg.add(var.set_data(template_))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Haier
 | 
			
		||||
HaierData, HaierBinarySensor, HaierTrigger, HaierAction, HaierDumper = declare_protocol(
 | 
			
		||||
    "Haier"
 | 
			
		||||
)
 | 
			
		||||
HaierAction = ns.class_("HaierAction", RemoteTransmitterActionBase)
 | 
			
		||||
HAIER_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Required(CONF_CODE): cv.All([cv.hex_uint8_t], cv.Length(min=13, max=13)),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_binary_sensor("haier", HaierBinarySensor, HAIER_SCHEMA)
 | 
			
		||||
def haier_binary_sensor(var, config):
 | 
			
		||||
    cg.add(var.set_code(config[CONF_CODE]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_trigger("haier", HaierTrigger, HaierData)
 | 
			
		||||
def haier_trigger(var, config):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_dumper("haier", HaierDumper)
 | 
			
		||||
def haier_dumper(var, config):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action("haier", HaierAction, HAIER_SCHEMA)
 | 
			
		||||
async def haier_action(var, config, args):
 | 
			
		||||
    vec_ = cg.std_vector.template(cg.uint8)
 | 
			
		||||
    template_ = await cg.templatable(config[CONF_CODE], args, vec_, vec_)
 | 
			
		||||
    cg.add(var.set_code(template_))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										84
									
								
								esphome/components/remote_base/haier_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								esphome/components/remote_base/haier_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
			
		||||
#include "haier_protocol.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace remote_base {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "remote.haier";
 | 
			
		||||
 | 
			
		||||
constexpr uint32_t HEADER_LOW_US = 3100;
 | 
			
		||||
constexpr uint32_t HEADER_HIGH_US = 4400;
 | 
			
		||||
constexpr uint32_t BIT_MARK_US = 540;
 | 
			
		||||
constexpr uint32_t BIT_ONE_SPACE_US = 1650;
 | 
			
		||||
constexpr uint32_t BIT_ZERO_SPACE_US = 580;
 | 
			
		||||
constexpr unsigned int HAIER_IR_PACKET_BIT_SIZE = 112;
 | 
			
		||||
 | 
			
		||||
void HaierProtocol::encode_byte_(RemoteTransmitData *dst, uint8_t item) {
 | 
			
		||||
  for (uint8_t mask = 1 << 7; mask != 0; mask >>= 1) {
 | 
			
		||||
    if (item & mask) {
 | 
			
		||||
      dst->space(BIT_ONE_SPACE_US);
 | 
			
		||||
    } else {
 | 
			
		||||
      dst->space(BIT_ZERO_SPACE_US);
 | 
			
		||||
    }
 | 
			
		||||
    dst->mark(BIT_MARK_US);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HaierProtocol::encode(RemoteTransmitData *dst, const HaierData &data) {
 | 
			
		||||
  dst->set_carrier_frequency(38000);
 | 
			
		||||
  dst->reserve(5 + ((data.data.size() + 1) * 2));
 | 
			
		||||
  dst->mark(HEADER_LOW_US);
 | 
			
		||||
  dst->space(HEADER_LOW_US);
 | 
			
		||||
  dst->mark(HEADER_LOW_US);
 | 
			
		||||
  dst->space(HEADER_HIGH_US);
 | 
			
		||||
  dst->mark(BIT_MARK_US);
 | 
			
		||||
  uint8_t checksum = 0;
 | 
			
		||||
  for (uint8_t item : data.data) {
 | 
			
		||||
    this->encode_byte_(dst, item);
 | 
			
		||||
    checksum += item;
 | 
			
		||||
  }
 | 
			
		||||
  this->encode_byte_(dst, checksum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
optional<HaierData> HaierProtocol::decode(RemoteReceiveData src) {
 | 
			
		||||
  if (!src.expect_item(HEADER_LOW_US, HEADER_LOW_US) || !src.expect_item(HEADER_LOW_US, HEADER_HIGH_US)) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  if (!src.expect_mark(BIT_MARK_US)) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  size_t size = src.size() - src.get_index() - 1;
 | 
			
		||||
  if (size < HAIER_IR_PACKET_BIT_SIZE * 2)
 | 
			
		||||
    return {};
 | 
			
		||||
  size = HAIER_IR_PACKET_BIT_SIZE * 2;
 | 
			
		||||
  uint8_t checksum = 0;
 | 
			
		||||
  HaierData out;
 | 
			
		||||
  while (size > 0) {
 | 
			
		||||
    uint8_t data = 0;
 | 
			
		||||
    for (uint8_t mask = 0x80; mask != 0; mask >>= 1) {
 | 
			
		||||
      if (src.expect_space(BIT_ONE_SPACE_US)) {
 | 
			
		||||
        data |= mask;
 | 
			
		||||
      } else if (!src.expect_space(BIT_ZERO_SPACE_US)) {
 | 
			
		||||
        return {};
 | 
			
		||||
      }
 | 
			
		||||
      if (!src.expect_mark(BIT_MARK_US)) {
 | 
			
		||||
        return {};
 | 
			
		||||
      }
 | 
			
		||||
      size -= 2;
 | 
			
		||||
    }
 | 
			
		||||
    if (size > 0) {
 | 
			
		||||
      checksum += data;
 | 
			
		||||
      out.data.push_back(data);
 | 
			
		||||
    } else if (checksum != data) {
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HaierProtocol::dump(const HaierData &data) {
 | 
			
		||||
  ESP_LOGI(TAG, "Received Haier: %s", format_hex_pretty(data.data).c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace remote_base
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										40
									
								
								esphome/components/remote_base/haier_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								esphome/components/remote_base/haier_protocol.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "remote_base.h"
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace remote_base {
 | 
			
		||||
 | 
			
		||||
struct HaierData {
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
 | 
			
		||||
  bool operator==(const HaierData &rhs) const { return data == rhs.data; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HaierProtocol : public RemoteProtocol<HaierData> {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(RemoteTransmitData *dst, const HaierData &data) override;
 | 
			
		||||
  optional<HaierData> decode(RemoteReceiveData src) override;
 | 
			
		||||
  void dump(const HaierData &data) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void encode_byte_(RemoteTransmitData *dst, uint8_t item);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DECLARE_REMOTE_PROTOCOL(Haier)
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class HaierAction : public RemoteTransmitterActionBase<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  TEMPLATABLE_VALUE(std::vector<uint8_t>, data)
 | 
			
		||||
 | 
			
		||||
  void set_code(const std::vector<uint8_t> &code) { data_ = code; }
 | 
			
		||||
  void encode(RemoteTransmitData *dst, Ts... x) override {
 | 
			
		||||
    HaierData data{};
 | 
			
		||||
    data.data = this->data_.value(x...);
 | 
			
		||||
    HaierProtocol().encode(dst, data);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace remote_base
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -14,6 +14,7 @@ from esphome.const import (
 | 
			
		||||
    KEY_FRAMEWORK_VERSION,
 | 
			
		||||
    KEY_TARGET_FRAMEWORK,
 | 
			
		||||
    KEY_TARGET_PLATFORM,
 | 
			
		||||
    PLATFORM_RP2040,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority, EsphomeError
 | 
			
		||||
from esphome.helpers import mkdir_p, write_file, copy_file_if_changed
 | 
			
		||||
@@ -30,7 +31,7 @@ AUTO_LOAD = []
 | 
			
		||||
 | 
			
		||||
def set_core_data(config):
 | 
			
		||||
    CORE.data[KEY_RP2040] = {}
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "rp2040"
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_RP2040
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
 | 
			
		||||
    CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
 | 
			
		||||
        config[CONF_FRAMEWORK][CONF_VERSION]
 | 
			
		||||
 
 | 
			
		||||
@@ -85,6 +85,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_WATER,
 | 
			
		||||
    DEVICE_CLASS_WEIGHT,
 | 
			
		||||
    DEVICE_CLASS_WIND_SPEED,
 | 
			
		||||
    ENTITY_CATEGORY_CONFIG,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
from esphome.cpp_generator import MockObjClass
 | 
			
		||||
@@ -188,6 +189,15 @@ def validate_datapoint(value):
 | 
			
		||||
    return validate_datapoint({CONF_FROM: cv.float_(a), CONF_TO: cv.float_(b)})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_SENSOR_ENTITY_CATEGORIES = {
 | 
			
		||||
    k: v for k, v in cv.ENTITY_CATEGORIES.items() if k != ENTITY_CATEGORY_CONFIG
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def sensor_entity_category(value):
 | 
			
		||||
    return cv.enum(_SENSOR_ENTITY_CATEGORIES, lower=True)(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Base
 | 
			
		||||
Sensor = sensor_ns.class_("Sensor", cg.EntityBase)
 | 
			
		||||
SensorPtr = Sensor.operator("ptr")
 | 
			
		||||
@@ -246,6 +256,7 @@ SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
 | 
			
		||||
        cv.Optional(CONF_ACCURACY_DECIMALS): validate_accuracy_decimals,
 | 
			
		||||
        cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
			
		||||
        cv.Optional(CONF_STATE_CLASS): validate_state_class,
 | 
			
		||||
        cv.Optional(CONF_ENTITY_CATEGORY): sensor_entity_category,
 | 
			
		||||
        cv.Optional("last_reset_type"): cv.invalid(
 | 
			
		||||
            "last_reset_type has been removed since 2021.9.0. state_class: total_increasing should be used for total values."
 | 
			
		||||
        ),
 | 
			
		||||
@@ -301,7 +312,7 @@ def sensor_schema(
 | 
			
		||||
        (CONF_ACCURACY_DECIMALS, accuracy_decimals, validate_accuracy_decimals),
 | 
			
		||||
        (CONF_DEVICE_CLASS, device_class, validate_device_class),
 | 
			
		||||
        (CONF_STATE_CLASS, state_class, validate_state_class),
 | 
			
		||||
        (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
 | 
			
		||||
        (CONF_ENTITY_CATEGORY, entity_category, sensor_entity_category),
 | 
			
		||||
    ]:
 | 
			
		||||
        if default is not _UNDEF:
 | 
			
		||||
            schema[cv.Optional(key, default=default)] = validator
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                device_class=DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_HEATER_ENABLED, default=True): cv.boolean,
 | 
			
		||||
            cv.Optional(CONF_HEATER_ENABLED, default=False): cv.boolean,
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,8 @@ static const uint8_t SM10BIT_ADDR_START_3CH = 0x8;
 | 
			
		||||
static const uint8_t SM10BIT_ADDR_START_2CH = 0x10;
 | 
			
		||||
static const uint8_t SM10BIT_ADDR_START_5CH = 0x18;
 | 
			
		||||
 | 
			
		||||
static const uint8_t SM10BIT_DELAY = 2;
 | 
			
		||||
 | 
			
		||||
// Power current values
 | 
			
		||||
// HEX | Binary | RGB level | White level | Config value
 | 
			
		||||
// 0x0 | 0000   | RGB 10mA  | CW 5mA      | 0
 | 
			
		||||
@@ -37,10 +39,13 @@ void Sm10BitBase::loop() {
 | 
			
		||||
  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] = this->model_id_ + SM10BIT_ADDR_STANDBY;
 | 
			
		||||
    for (int i = 1; i < 12; i++)
 | 
			
		||||
      data[i] = 0;
 | 
			
		||||
    // First turn all channels off
 | 
			
		||||
    data[0] = this->model_id_ + SM10BIT_ADDR_START_5CH;
 | 
			
		||||
    this->write_buffer_(data, 12);
 | 
			
		||||
    // Then sleep
 | 
			
		||||
    data[0] = this->model_id_ + SM10BIT_ADDR_STANDBY;
 | 
			
		||||
    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)) {
 | 
			
		||||
@@ -84,28 +89,42 @@ void Sm10BitBase::set_channel_value_(uint8_t channel, uint16_t value) {
 | 
			
		||||
  this->pwm_amounts_[channel] = value;
 | 
			
		||||
}
 | 
			
		||||
void Sm10BitBase::write_bit_(bool value) {
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  this->data_pin_->digital_write(value);
 | 
			
		||||
  delayMicroseconds(SM10BIT_DELAY);
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(SM10BIT_DELAY);
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(SM10BIT_DELAY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Sm10BitBase::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);
 | 
			
		||||
 | 
			
		||||
  // ack bit
 | 
			
		||||
  this->data_pin_->pin_mode(gpio::FLAG_INPUT);
 | 
			
		||||
  this->clock_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(SM10BIT_DELAY);
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(SM10BIT_DELAY);
 | 
			
		||||
  this->data_pin_->pin_mode(gpio::FLAG_OUTPUT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Sm10BitBase::write_buffer_(uint8_t *buffer, uint8_t size) {
 | 
			
		||||
  this->data_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(SM10BIT_DELAY);
 | 
			
		||||
  this->clock_pin_->digital_write(false);
 | 
			
		||||
  delayMicroseconds(SM10BIT_DELAY);
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
  delayMicroseconds(SM10BIT_DELAY);
 | 
			
		||||
  this->data_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(SM10BIT_DELAY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace sm10bit_base
 | 
			
		||||
 
 | 
			
		||||
@@ -88,11 +88,6 @@ uint64_t bytes_to_uint(const bytes &buffer) {
 | 
			
		||||
  for (auto const value : buffer) {
 | 
			
		||||
    val = (val << 8) + value;
 | 
			
		||||
  }
 | 
			
		||||
  // Some smart meters send 24 bit signed integers. Sign extend to 64 bit if the
 | 
			
		||||
  // 24 bit value is negative.
 | 
			
		||||
  if (buffer.size() == 3 && buffer[0] & 0x80) {
 | 
			
		||||
    val |= 0xFFFFFFFFFF000000;
 | 
			
		||||
  }
 | 
			
		||||
  return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -100,19 +95,15 @@ int64_t bytes_to_int(const bytes &buffer) {
 | 
			
		||||
  uint64_t tmp = bytes_to_uint(buffer);
 | 
			
		||||
  int64_t val;
 | 
			
		||||
 | 
			
		||||
  switch (buffer.size()) {
 | 
			
		||||
    case 1:  // int8
 | 
			
		||||
      val = (int8_t) tmp;
 | 
			
		||||
      break;
 | 
			
		||||
    case 2:  // int16
 | 
			
		||||
      val = (int16_t) tmp;
 | 
			
		||||
      break;
 | 
			
		||||
    case 4:  // int32
 | 
			
		||||
      val = (int32_t) tmp;
 | 
			
		||||
      break;
 | 
			
		||||
    default:  // int64
 | 
			
		||||
      val = (int64_t) tmp;
 | 
			
		||||
  // sign extension for abbreviations of leading ones (e.g. 3 byte transmissions, see 6.2.2 of SML protocol definition)
 | 
			
		||||
  // see https://stackoverflow.com/questions/42534749/signed-extension-from-24-bit-to-32-bit-in-c
 | 
			
		||||
  if (buffer.size() < 8) {
 | 
			
		||||
    const int bits = buffer.size() * 8;
 | 
			
		||||
    const uint64_t m = 1u << (bits - 1);
 | 
			
		||||
    tmp = (tmp ^ m) - m;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  val = (int64_t) tmp;
 | 
			
		||||
  return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,14 +25,19 @@ from esphome.const import (
 | 
			
		||||
    KEY_CORE,
 | 
			
		||||
    KEY_TARGET_PLATFORM,
 | 
			
		||||
    KEY_VARIANT,
 | 
			
		||||
    CONF_DATA_RATE,
 | 
			
		||||
    PLATFORM_ESP32,
 | 
			
		||||
    PLATFORM_ESP8266,
 | 
			
		||||
    PLATFORM_RP2040,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import coroutine_with_priority, CORE
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@esphome/core"]
 | 
			
		||||
CODEOWNERS = ["@esphome/core", "@clydebarrow"]
 | 
			
		||||
spi_ns = cg.esphome_ns.namespace("spi")
 | 
			
		||||
SPIComponent = spi_ns.class_("SPIComponent", cg.Component)
 | 
			
		||||
SPIDevice = spi_ns.class_("SPIDevice")
 | 
			
		||||
SPIDataRate = spi_ns.enum("SPIDataRate")
 | 
			
		||||
SPIMode = spi_ns.enum("SPIMode")
 | 
			
		||||
 | 
			
		||||
SPI_DATA_RATE_OPTIONS = {
 | 
			
		||||
    80e6: SPIDataRate.DATA_RATE_80MHZ,
 | 
			
		||||
@@ -50,10 +55,37 @@ SPI_DATA_RATE_OPTIONS = {
 | 
			
		||||
}
 | 
			
		||||
SPI_DATA_RATE_SCHEMA = cv.All(cv.frequency, cv.enum(SPI_DATA_RATE_OPTIONS))
 | 
			
		||||
 | 
			
		||||
SPI_MODE_OPTIONS = {
 | 
			
		||||
    "MODE0": SPIMode.MODE0,
 | 
			
		||||
    "MODE1": SPIMode.MODE1,
 | 
			
		||||
    "MODE2": SPIMode.MODE2,
 | 
			
		||||
    "MODE3": SPIMode.MODE3,
 | 
			
		||||
    0: SPIMode.MODE0,
 | 
			
		||||
    1: SPIMode.MODE1,
 | 
			
		||||
    2: SPIMode.MODE2,
 | 
			
		||||
    3: SPIMode.MODE3,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONF_SPI_MODE = "spi_mode"
 | 
			
		||||
CONF_FORCE_SW = "force_sw"
 | 
			
		||||
CONF_INTERFACE = "interface"
 | 
			
		||||
CONF_INTERFACE_INDEX = "interface_index"
 | 
			
		||||
 | 
			
		||||
# RP2040 SPI pin assignments are complicated. Refer to https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
 | 
			
		||||
 | 
			
		||||
RP_SPI_PINSETS = [
 | 
			
		||||
    {
 | 
			
		||||
        CONF_MISO_PIN: [0, 4, 16, 20, -1],
 | 
			
		||||
        CONF_CLK_PIN: [2, 6, 18, 22],
 | 
			
		||||
        CONF_MOSI_PIN: [3, 7, 19, 23, -1],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        CONF_MISO_PIN: [8, 12, 24, 28, -1],
 | 
			
		||||
        CONF_CLK_PIN: [10, 14, 26],
 | 
			
		||||
        CONF_MOSI_PIN: [11, 23, 27, -1],
 | 
			
		||||
    },
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_target_platform():
 | 
			
		||||
    return (
 | 
			
		||||
@@ -73,9 +105,9 @@ def get_target_variant():
 | 
			
		||||
# The returned value is a list of lists of names
 | 
			
		||||
def get_hw_interface_list():
 | 
			
		||||
    target_platform = get_target_platform()
 | 
			
		||||
    if target_platform == "esp8266":
 | 
			
		||||
    if target_platform == PLATFORM_ESP8266:
 | 
			
		||||
        return [["spi", "hspi"]]
 | 
			
		||||
    if target_platform == "esp32":
 | 
			
		||||
    if target_platform == PLATFORM_ESP32:
 | 
			
		||||
        if get_target_variant() in [
 | 
			
		||||
            VARIANT_ESP32C2,
 | 
			
		||||
            VARIANT_ESP32C3,
 | 
			
		||||
@@ -84,8 +116,8 @@ def get_hw_interface_list():
 | 
			
		||||
        ]:
 | 
			
		||||
            return [["spi", "spi2"]]
 | 
			
		||||
        return [["spi", "spi2"], ["spi3"]]
 | 
			
		||||
    if target_platform == "rp2040":
 | 
			
		||||
        return [["spi"]]
 | 
			
		||||
    if target_platform == PLATFORM_RP2040:
 | 
			
		||||
        return [["spi"], ["spi1"]]
 | 
			
		||||
    return []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -99,8 +131,10 @@ def get_spi_index(name):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Check that pins are suitable for HW spi
 | 
			
		||||
# \param spi the config data for the spi instance
 | 
			
		||||
# \param index the selected hw interface number, -1 if not yet known
 | 
			
		||||
# TODO verify that the pins are internal
 | 
			
		||||
def validate_hw_pins(spi):
 | 
			
		||||
def validate_hw_pins(spi, index=-1):
 | 
			
		||||
    clk_pin = spi[CONF_CLK_PIN]
 | 
			
		||||
    if clk_pin[CONF_INVERTED]:
 | 
			
		||||
        return False
 | 
			
		||||
@@ -119,19 +153,40 @@ def validate_hw_pins(spi):
 | 
			
		||||
        sdi_pin_no = sdi_pin[CONF_NUMBER]
 | 
			
		||||
 | 
			
		||||
    target_platform = get_target_platform()
 | 
			
		||||
    if target_platform == "esp8266":
 | 
			
		||||
    if target_platform == PLATFORM_ESP8266:
 | 
			
		||||
        if clk_pin_no == 6:
 | 
			
		||||
            return sdo_pin_no in (-1, 8) and sdi_pin_no in (-1, 7)
 | 
			
		||||
        if clk_pin_no == 14:
 | 
			
		||||
            return sdo_pin_no in (-1, 13) and sdi_pin_no in (-1, 12)
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    if target_platform == "esp32":
 | 
			
		||||
    if target_platform == PLATFORM_ESP32:
 | 
			
		||||
        return clk_pin_no >= 0
 | 
			
		||||
 | 
			
		||||
    if target_platform == PLATFORM_RP2040:
 | 
			
		||||
        pin_set = (
 | 
			
		||||
            list(filter(lambda s: clk_pin_no in s[CONF_CLK_PIN], RP_SPI_PINSETS))[0]
 | 
			
		||||
            if index == -1
 | 
			
		||||
            else RP_SPI_PINSETS[index]
 | 
			
		||||
        )
 | 
			
		||||
        if pin_set is None:
 | 
			
		||||
            return False
 | 
			
		||||
        if sdo_pin_no not in pin_set[CONF_MOSI_PIN]:
 | 
			
		||||
            return False
 | 
			
		||||
        if sdi_pin_no not in pin_set[CONF_MISO_PIN]:
 | 
			
		||||
            return False
 | 
			
		||||
        return True
 | 
			
		||||
    return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_hw_spi(config, available):
 | 
			
		||||
    """Get an available hardware spi interface suitable for this config"""
 | 
			
		||||
    matching = list(filter(lambda idx: validate_hw_pins(config, idx), available))
 | 
			
		||||
    if len(matching) != 0:
 | 
			
		||||
        return matching[0]
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_spi_config(config):
 | 
			
		||||
    available = list(range(len(get_hw_interface_list())))
 | 
			
		||||
    for spi in config:
 | 
			
		||||
@@ -147,9 +202,10 @@ def validate_spi_config(config):
 | 
			
		||||
            if not validate_hw_pins(spi):
 | 
			
		||||
                spi[CONF_INTERFACE] = "software"
 | 
			
		||||
        elif interface == "hardware":
 | 
			
		||||
            if len(available) == 0:
 | 
			
		||||
                raise cv.Invalid("No hardware interface available")
 | 
			
		||||
            index = spi[CONF_INTERFACE_INDEX] = available[0]
 | 
			
		||||
            index = get_hw_spi(spi, available)
 | 
			
		||||
            if index is None:
 | 
			
		||||
                raise cv.Invalid("No suitable hardware interface available")
 | 
			
		||||
            spi[CONF_INTERFACE_INDEX] = index
 | 
			
		||||
            available.remove(index)
 | 
			
		||||
        else:
 | 
			
		||||
            # Must be a specific name
 | 
			
		||||
@@ -164,11 +220,14 @@ def validate_spi_config(config):
 | 
			
		||||
    # Any specific names and any 'hardware' requests will have already been filled,
 | 
			
		||||
    # so just need to assign remaining hardware to 'any' requests.
 | 
			
		||||
    for spi in config:
 | 
			
		||||
        if spi[CONF_INTERFACE] == "any" and len(available) != 0:
 | 
			
		||||
            index = available[0]
 | 
			
		||||
            spi[CONF_INTERFACE_INDEX] = index
 | 
			
		||||
            available.remove(index)
 | 
			
		||||
        if CONF_INTERFACE_INDEX in spi and not validate_hw_pins(spi):
 | 
			
		||||
        if spi[CONF_INTERFACE] == "any":
 | 
			
		||||
            index = get_hw_spi(spi, available)
 | 
			
		||||
            if index is not None:
 | 
			
		||||
                spi[CONF_INTERFACE_INDEX] = index
 | 
			
		||||
                available.remove(index)
 | 
			
		||||
        if CONF_INTERFACE_INDEX in spi and not validate_hw_pins(
 | 
			
		||||
            spi, spi[CONF_INTERFACE_INDEX]
 | 
			
		||||
        ):
 | 
			
		||||
            raise cv.Invalid("Invalid pin selections for hardware SPI interface")
 | 
			
		||||
 | 
			
		||||
    return config
 | 
			
		||||
@@ -180,14 +239,14 @@ def get_spi_interface(index):
 | 
			
		||||
        return ["SPI2_HOST", "SPI3_HOST"][index]
 | 
			
		||||
    # Arduino code follows
 | 
			
		||||
    platform = get_target_platform()
 | 
			
		||||
    if platform == "rp2040":
 | 
			
		||||
        return "&spi1"
 | 
			
		||||
    if platform == PLATFORM_RP2040:
 | 
			
		||||
        return ["&SPI", "&SPI1"][index]
 | 
			
		||||
    if index == 0:
 | 
			
		||||
        return "&SPI"
 | 
			
		||||
    # Following code can't apply to C2, H2 or 8266 since they have only one SPI
 | 
			
		||||
    if get_target_variant() in (VARIANT_ESP32S3, VARIANT_ESP32S2):
 | 
			
		||||
        return "new SPIClass(FSPI)"
 | 
			
		||||
    return "return new SPIClass(HSPI)"
 | 
			
		||||
    return "new SPIClass(HSPI)"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SPI_SCHEMA = cv.All(
 | 
			
		||||
@@ -205,7 +264,7 @@ SPI_SCHEMA = cv.All(
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
    cv.has_at_least_one_key(CONF_MISO_PIN, CONF_MOSI_PIN),
 | 
			
		||||
    cv.only_on(["esp32", "esp8266", "rp2040"]),
 | 
			
		||||
    cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
@@ -216,6 +275,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(1.0)
 | 
			
		||||
async def to_code(configs):
 | 
			
		||||
    cg.add_define("USE_SPI")
 | 
			
		||||
    cg.add_global(spi_ns.using)
 | 
			
		||||
    for spi in configs:
 | 
			
		||||
        var = cg.new_Pvariable(spi[CONF_ID])
 | 
			
		||||
@@ -244,13 +304,20 @@ async def to_code(configs):
 | 
			
		||||
        cg.add_library("SPI", None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def spi_device_schema(cs_pin_required=True):
 | 
			
		||||
def spi_device_schema(
 | 
			
		||||
    cs_pin_required=True, default_data_rate=cv.UNDEFINED, default_mode=cv.UNDEFINED
 | 
			
		||||
):
 | 
			
		||||
    """Create a schema for an SPI device.
 | 
			
		||||
    :param cs_pin_required: If true, make the CS_PIN required in the config.
 | 
			
		||||
    :param default_data_rate: Optional data_rate to use as default
 | 
			
		||||
    :return: The SPI device schema, `extend` this in your config schema.
 | 
			
		||||
    """
 | 
			
		||||
    schema = {
 | 
			
		||||
        cv.GenerateID(CONF_SPI_ID): cv.use_id(SPIComponent),
 | 
			
		||||
        cv.Optional(CONF_DATA_RATE, default=default_data_rate): SPI_DATA_RATE_SCHEMA,
 | 
			
		||||
        cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum(
 | 
			
		||||
            SPI_MODE_OPTIONS, upper=True
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
    if cs_pin_required:
 | 
			
		||||
        schema[cv.Required(CONF_CS_PIN)] = pins.gpio_output_pin_schema
 | 
			
		||||
@@ -265,6 +332,10 @@ async def register_spi_device(var, config):
 | 
			
		||||
    if CONF_CS_PIN in config:
 | 
			
		||||
        pin = await cg.gpio_pin_expression(config[CONF_CS_PIN])
 | 
			
		||||
        cg.add(var.set_cs_pin(pin))
 | 
			
		||||
    if CONF_DATA_RATE in config:
 | 
			
		||||
        cg.add(var.set_data_rate(config[CONF_DATA_RATE]))
 | 
			
		||||
    if CONF_SPI_MODE in config:
 | 
			
		||||
        cg.add(var.set_mode(config[CONF_SPI_MODE]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def final_validate_device_schema(name: str, *, require_mosi: bool, require_miso: bool):
 | 
			
		||||
 
 | 
			
		||||
@@ -248,6 +248,7 @@ class SPIDelegateDummy : public SPIDelegate {
 | 
			
		||||
  SPIDelegateDummy() = default;
 | 
			
		||||
 | 
			
		||||
  uint8_t transfer(uint8_t data) override { return 0; }
 | 
			
		||||
  void end_transaction() override{};
 | 
			
		||||
 | 
			
		||||
  void begin_transaction() override;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import spi
 | 
			
		||||
from esphome.const import CONF_ID, CONF_DATA_RATE, CONF_MODE
 | 
			
		||||
from esphome.const import CONF_ID, CONF_MODE
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["spi"]
 | 
			
		||||
CODEOWNERS = ["@clydebarrow"]
 | 
			
		||||
@@ -33,17 +33,15 @@ CONF_BIT_ORDER = "bit_order"
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_ID): cv.declare_id(spi_device),
 | 
			
		||||
        cv.Optional(CONF_DATA_RATE, default="1MHz"): spi.SPI_DATA_RATE_SCHEMA,
 | 
			
		||||
        cv.Optional(CONF_BIT_ORDER, default="msb_first"): cv.enum(ORDERS, lower=True),
 | 
			
		||||
        cv.Optional(CONF_MODE, default="0"): cv.enum(MODES, upper=True),
 | 
			
		||||
    }
 | 
			
		||||
).extend(spi.spi_device_schema(False))
 | 
			
		||||
).extend(spi.spi_device_schema(False, "1MHz"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    cg.add(var.set_data_rate(config[CONF_DATA_RATE]))
 | 
			
		||||
    cg.add(var.set_mode(config[CONF_MODE]))
 | 
			
		||||
    cg.add(var.set_bit_order(config[CONF_BIT_ORDER]))
 | 
			
		||||
    await spi.register_spi_device(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import light
 | 
			
		||||
from esphome.components import spi
 | 
			
		||||
from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_DATA_RATE
 | 
			
		||||
from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS
 | 
			
		||||
 | 
			
		||||
spi_led_strip_ns = cg.esphome_ns.namespace("spi_led_strip")
 | 
			
		||||
SpiLedStrip = spi_led_strip_ns.class_(
 | 
			
		||||
@@ -13,14 +13,12 @@ CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpiLedStrip),
 | 
			
		||||
        cv.Optional(CONF_NUM_LEDS, default=1): cv.positive_not_null_int,
 | 
			
		||||
        cv.Optional(CONF_DATA_RATE, default="1MHz"): spi.SPI_DATA_RATE_SCHEMA,
 | 
			
		||||
    }
 | 
			
		||||
).extend(spi.spi_device_schema(False))
 | 
			
		||||
).extend(spi.spi_device_schema(False, "1MHz"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
 | 
			
		||||
    cg.add(var.set_data_rate(spi.SPI_DATA_RATE_OPTIONS[config[CONF_DATA_RATE]]))
 | 
			
		||||
    cg.add(var.set_num_leds(config[CONF_NUM_LEDS]))
 | 
			
		||||
    await light.register_light(var, config)
 | 
			
		||||
    await spi.register_spi_device(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -571,18 +571,12 @@ async def sprinkler_simple_action_to_code(config, action_id, template_arg, args)
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    for sprinkler_controller in config:
 | 
			
		||||
        var = cg.new_Pvariable(sprinkler_controller[CONF_ID])
 | 
			
		||||
 | 
			
		||||
        if CONF_NAME in sprinkler_controller:
 | 
			
		||||
            cg.add(var.set_name(sprinkler_controller[CONF_NAME]))
 | 
			
		||||
        if len(sprinkler_controller[CONF_VALVES]) > 1:
 | 
			
		||||
            name = sprinkler_controller[CONF_MAIN_SWITCH][CONF_NAME]
 | 
			
		||||
        else:
 | 
			
		||||
            if len(sprinkler_controller[CONF_VALVES]) > 1:
 | 
			
		||||
                name = sprinkler_controller[CONF_MAIN_SWITCH][CONF_NAME]
 | 
			
		||||
            else:
 | 
			
		||||
                name = sprinkler_controller[CONF_VALVES][0][CONF_VALVE_SWITCH][
 | 
			
		||||
                    CONF_NAME
 | 
			
		||||
                ]
 | 
			
		||||
            cg.add(var.set_name(name))
 | 
			
		||||
            name = sprinkler_controller[CONF_VALVES][0][CONF_VALVE_SWITCH][CONF_NAME]
 | 
			
		||||
        name = sprinkler_controller.get(CONF_NAME, name)
 | 
			
		||||
        var = cg.new_Pvariable(sprinkler_controller[CONF_ID], name)
 | 
			
		||||
 | 
			
		||||
        await cg.register_component(var, sprinkler_controller)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -386,12 +386,17 @@ SprinklerValveOperator *SprinklerValveRunRequest::valve_operator() { return this
 | 
			
		||||
 | 
			
		||||
SprinklerValveRunRequestOrigin SprinklerValveRunRequest::request_is_from() { return this->origin_; }
 | 
			
		||||
 | 
			
		||||
void Sprinkler::setup() {
 | 
			
		||||
Sprinkler::Sprinkler() {}
 | 
			
		||||
Sprinkler::Sprinkler(const std::string &name) {
 | 
			
		||||
  // The `name` is needed to set timers up, hence non-default constructor
 | 
			
		||||
  // replaces `set_name()` method previously existed
 | 
			
		||||
  this->name_ = name;
 | 
			
		||||
  this->timer_.push_back({this->name_ + "sm", false, 0, 0, std::bind(&Sprinkler::sm_timer_callback_, this)});
 | 
			
		||||
  this->timer_.push_back({this->name_ + "vs", false, 0, 0, std::bind(&Sprinkler::valve_selection_callback_, this)});
 | 
			
		||||
  this->all_valves_off_(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Sprinkler::setup() { this->all_valves_off_(true); }
 | 
			
		||||
 | 
			
		||||
void Sprinkler::loop() {
 | 
			
		||||
  for (auto &p : this->pump_) {
 | 
			
		||||
    p.loop();
 | 
			
		||||
 
 | 
			
		||||
@@ -204,12 +204,12 @@ class SprinklerValveRunRequest {
 | 
			
		||||
 | 
			
		||||
class Sprinkler : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  Sprinkler();
 | 
			
		||||
  Sprinkler(const std::string &name);
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  void set_name(const std::string &name) { this->name_ = name; }
 | 
			
		||||
 | 
			
		||||
  /// add a valve to the controller
 | 
			
		||||
  void add_valve(SprinklerControllerSwitch *valve_sw, SprinklerControllerSwitch *enable_sw = nullptr);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_PM25,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_MICROGRAMS_PER_CUBIC_METER,
 | 
			
		||||
    UNIT_COUNTS_PER_CUBIC_METER,
 | 
			
		||||
    UNIT_COUNTS_PER_CUBIC_CENTIMETER,
 | 
			
		||||
    UNIT_MICROMETER,
 | 
			
		||||
    ICON_CHEMICAL_WEAPON,
 | 
			
		||||
    ICON_COUNTER,
 | 
			
		||||
@@ -73,31 +73,31 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PMC_0_5): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_COUNTS_PER_CUBIC_METER,
 | 
			
		||||
                unit_of_measurement=UNIT_COUNTS_PER_CUBIC_CENTIMETER,
 | 
			
		||||
                icon=ICON_COUNTER,
 | 
			
		||||
                accuracy_decimals=2,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PMC_1_0): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_COUNTS_PER_CUBIC_METER,
 | 
			
		||||
                unit_of_measurement=UNIT_COUNTS_PER_CUBIC_CENTIMETER,
 | 
			
		||||
                icon=ICON_COUNTER,
 | 
			
		||||
                accuracy_decimals=2,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PMC_2_5): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_COUNTS_PER_CUBIC_METER,
 | 
			
		||||
                unit_of_measurement=UNIT_COUNTS_PER_CUBIC_CENTIMETER,
 | 
			
		||||
                icon=ICON_COUNTER,
 | 
			
		||||
                accuracy_decimals=2,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PMC_4_0): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_COUNTS_PER_CUBIC_METER,
 | 
			
		||||
                unit_of_measurement=UNIT_COUNTS_PER_CUBIC_CENTIMETER,
 | 
			
		||||
                icon=ICON_COUNTER,
 | 
			
		||||
                accuracy_decimals=2,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PMC_10_0): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_COUNTS_PER_CUBIC_METER,
 | 
			
		||||
                unit_of_measurement=UNIT_COUNTS_PER_CUBIC_CENTIMETER,
 | 
			
		||||
                icon=ICON_COUNTER,
 | 
			
		||||
                accuracy_decimals=2,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
 
 | 
			
		||||
@@ -112,6 +112,9 @@ void SSD1351::set_brightness(float brightness) {
 | 
			
		||||
  } else {
 | 
			
		||||
    this->brightness_ = brightness;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_ready()) {
 | 
			
		||||
    return;  // Component is not yet setup skip the command
 | 
			
		||||
  }
 | 
			
		||||
  // now write the new brightness level to the display
 | 
			
		||||
  this->command(SSD1351_CONTRASTMASTER);
 | 
			
		||||
  this->data(int(SSD1351_MAX_CONTRAST * (this->brightness_)));
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user