mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 00:51:49 +00:00 
			
		
		
		
	Compare commits
	
		
			358 Commits
		
	
	
		
			jesserockz
			...
			2023.5.5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b8c27bc17d | ||
| 
						 | 
					1057ac4db7 | ||
| 
						 | 
					69f5674d9e | ||
| 
						 | 
					b723728177 | ||
| 
						 | 
					316171491f | ||
| 
						 | 
					91ff502872 | ||
| 
						 | 
					6e414180e0 | ||
| 
						 | 
					7d2ae4e252 | ||
| 
						 | 
					fb4cb07c6f | ||
| 
						 | 
					19f91a7deb | ||
| 
						 | 
					3807350c61 | ||
| 
						 | 
					9d2467cf62 | ||
| 
						 | 
					d2480d3194 | ||
| 
						 | 
					148eb03d13 | ||
| 
						 | 
					70aa5d0f6c | ||
| 
						 | 
					8fcec8e2cb | ||
| 
						 | 
					2d3b48f86f | ||
| 
						 | 
					99c10bc6de | ||
| 
						 | 
					f30f20db86 | ||
| 
						 | 
					9bf946e196 | ||
| 
						 | 
					bfaad1f28d | ||
| 
						 | 
					f90e9ba871 | ||
| 
						 | 
					726bdd7be2 | ||
| 
						 | 
					52db40eb41 | ||
| 
						 | 
					3c371a0c59 | ||
| 
						 | 
					c941bc4109 | ||
| 
						 | 
					cc76e5353c | ||
| 
						 | 
					11bb46e393 | ||
| 
						 | 
					71c4714a6e | ||
| 
						 | 
					a4e63c5f86 | ||
| 
						 | 
					c71e7d0132 | ||
| 
						 | 
					daa966975e | ||
| 
						 | 
					2e5757a3f0 | ||
| 
						 | 
					f66024b37c | ||
| 
						 | 
					c16ca7be13 | ||
| 
						 | 
					e13e754bc4 | ||
| 
						 | 
					6ccea59f71 | ||
| 
						 | 
					71b28be3c8 | ||
| 
						 | 
					e25d92e1f5 | ||
| 
						 | 
					65cda10884 | ||
| 
						 | 
					625126df68 | ||
| 
						 | 
					e0ee8ca17c | ||
| 
						 | 
					af95e781f5 | ||
| 
						 | 
					9b230a7d93 | ||
| 
						 | 
					e2f3e7c3a6 | ||
| 
						 | 
					2fd2e5ceb2 | ||
| 
						 | 
					d88358be8e | ||
| 
						 | 
					d80885c7a8 | ||
| 
						 | 
					c7c9c49f4e | ||
| 
						 | 
					2c160a8a25 | ||
| 
						 | 
					cc576cf1a9 | ||
| 
						 | 
					97a71482a9 | ||
| 
						 | 
					8822b6c808 | ||
| 
						 | 
					5099595aee | ||
| 
						 | 
					39a650ee54 | ||
| 
						 | 
					b19c7d462b | ||
| 
						 | 
					a8b821c213 | ||
| 
						 | 
					535003014b | ||
| 
						 | 
					3c05ae4e1a | ||
| 
						 | 
					c835b67bac | ||
| 
						 | 
					8c32941428 | ||
| 
						 | 
					b5dac00dcb | ||
| 
						 | 
					ffdc721c79 | ||
| 
						 | 
					0828a9fc11 | ||
| 
						 | 
					4b664b6f09 | ||
| 
						 | 
					679633245d | ||
| 
						 | 
					ce8a77c765 | ||
| 
						 | 
					2f78c4acfa | ||
| 
						 | 
					72f6841aac | ||
| 
						 | 
					10bd9b14fc | ||
| 
						 | 
					3498aade85 | ||
| 
						 | 
					c4539e10fb | ||
| 
						 | 
					2b3052e9d7 | ||
| 
						 | 
					e725e15f7a | ||
| 
						 | 
					4d1113e265 | ||
| 
						 | 
					52352ac27a | ||
| 
						 | 
					f60b2b754d | ||
| 
						 | 
					4a3f9712b2 | ||
| 
						 | 
					fb094fca0f | ||
| 
						 | 
					de10b356cf | ||
| 
						 | 
					bd6d6caa8a | ||
| 
						 | 
					1c4af08ed3 | ||
| 
						 | 
					c97d361b6c | ||
| 
						 | 
					379b1d84dd | ||
| 
						 | 
					c13e20643b | ||
| 
						 | 
					76b6fcf554 | ||
| 
						 | 
					57e909e790 | ||
| 
						 | 
					d6f7876e68 | ||
| 
						 | 
					56e0923c22 | ||
| 
						 | 
					f4b98f5e32 | ||
| 
						 | 
					2d56b70a36 | ||
| 
						 | 
					980cfaf295 | ||
| 
						 | 
					c2a43c733a | ||
| 
						 | 
					568e65a6ab | ||
| 
						 | 
					59d6b3afa0 | ||
| 
						 | 
					b89c04b928 | ||
| 
						 | 
					12090657bb | ||
| 
						 | 
					4e21cf0bdd | ||
| 
						 | 
					ba4ef72d56 | ||
| 
						 | 
					f3e6a4314f | ||
| 
						 | 
					e14ce3d950 | ||
| 
						 | 
					70aa38f5bd | ||
| 
						 | 
					55ec082628 | ||
| 
						 | 
					6f27126c8d | ||
| 
						 | 
					ee21a91313 | ||
| 
						 | 
					c5efaa1c00 | ||
| 
						 | 
					6476357596 | ||
| 
						 | 
					77f71acbc8 | ||
| 
						 | 
					4a08a5413d | ||
| 
						 | 
					e3d89cc6b6 | ||
| 
						 | 
					64afb07e91 | ||
| 
						 | 
					f639f7c280 | ||
| 
						 | 
					986dd2ddd2 | ||
| 
						 | 
					7abdb5d046 | ||
| 
						 | 
					0f1e186189 | ||
| 
						 | 
					96d208e0d8 | ||
| 
						 | 
					38ed38864e | ||
| 
						 | 
					a12ba7bd38 | ||
| 
						 | 
					4a177e3931 | ||
| 
						 | 
					bef5b38d49 | ||
| 
						 | 
					0a95f116fc | ||
| 
						 | 
					327cd662b4 | ||
| 
						 | 
					bb05ba3d00 | ||
| 
						 | 
					c0ad5d1d16 | ||
| 
						 | 
					4c39631428 | ||
| 
						 | 
					afc2b3b74f | ||
| 
						 | 
					19fc1417ae | ||
| 
						 | 
					e2fefa51f5 | ||
| 
						 | 
					f668d5617f | ||
| 
						 | 
					47c4ff15d6 | ||
| 
						 | 
					ccf1bdc0b4 | ||
| 
						 | 
					e993fcf80c | ||
| 
						 | 
					1bdc30a09e | ||
| 
						 | 
					0f7e34e7ec | ||
| 
						 | 
					f56e89597f | ||
| 
						 | 
					9460fb28c4 | ||
| 
						 | 
					7207b9734f | ||
| 
						 | 
					2be703b329 | ||
| 
						 | 
					4cea74ef3b | ||
| 
						 | 
					3be3267d06 | ||
| 
						 | 
					98db604dba | ||
| 
						 | 
					ebf6f8c6de | ||
| 
						 | 
					2ebacad398 | ||
| 
						 | 
					53c59cf675 | ||
| 
						 | 
					9da261cb39 | ||
| 
						 | 
					3a587ea0d4 | ||
| 
						 | 
					8a60919e1f | ||
| 
						 | 
					382dcddf12 | ||
| 
						 | 
					6b67acbeb5 | ||
| 
						 | 
					7b0fca6824 | ||
| 
						 | 
					47555d314a | ||
| 
						 | 
					0643b71908 | ||
| 
						 | 
					afc848bf22 | ||
| 
						 | 
					9fbbcd6d8a | ||
| 
						 | 
					cc1eb648f9 | ||
| 
						 | 
					04a139fe3d | ||
| 
						 | 
					1a86167a47 | ||
| 
						 | 
					614ed7fd0c | ||
| 
						 | 
					4eb69d6af5 | ||
| 
						 | 
					0547f2a931 | ||
| 
						 | 
					b5fbe0b145 | ||
| 
						 | 
					443c3c2a56 | ||
| 
						 | 
					d75daa9644 | ||
| 
						 | 
					7963abb27a | ||
| 
						 | 
					0b9e8fda34 | ||
| 
						 | 
					a3cacc0c8b | ||
| 
						 | 
					5a4840f641 | ||
| 
						 | 
					3d7d689040 | ||
| 
						 | 
					b60c08dd28 | ||
| 
						 | 
					80bc567c31 | ||
| 
						 | 
					888ac2e180 | ||
| 
						 | 
					421ebcc8b2 | ||
| 
						 | 
					b56fa8c50a | ||
| 
						 | 
					42401775e1 | ||
| 
						 | 
					9c9bc58c16 | ||
| 
						 | 
					fbc129cccc | ||
| 
						 | 
					99638190cb | ||
| 
						 | 
					d78e9e6aa8 | ||
| 
						 | 
					878155a03d | ||
| 
						 | 
					9922eb83e2 | ||
| 
						 | 
					79f861f012 | ||
| 
						 | 
					4faa9d109e | ||
| 
						 | 
					28534ecc61 | ||
| 
						 | 
					616e0a21d8 | ||
| 
						 | 
					a546ffd490 | ||
| 
						 | 
					b5d0aede38 | ||
| 
						 | 
					a014d853a4 | ||
| 
						 | 
					c4ddf7697d | ||
| 
						 | 
					a2931b6774 | ||
| 
						 | 
					3ac7bf3761 | ||
| 
						 | 
					922344811f | ||
| 
						 | 
					cb2fcaa9b1 | ||
| 
						 | 
					1f50bd0649 | ||
| 
						 | 
					e4b2de5c68 | ||
| 
						 | 
					f862b479e7 | ||
| 
						 | 
					358c59bd8d | ||
| 
						 | 
					74fe135c9c | ||
| 
						 | 
					8d3896172d | ||
| 
						 | 
					9d9725144d | ||
| 
						 | 
					dd8dc1ef1d | ||
| 
						 | 
					bc427de16a | ||
| 
						 | 
					db5988bbe1 | ||
| 
						 | 
					a3875af4b4 | ||
| 
						 | 
					e6737479f7 | ||
| 
						 | 
					7f75832bf1 | ||
| 
						 | 
					33339e3bd8 | ||
| 
						 | 
					c298c1166f | ||
| 
						 | 
					c3d9eef01f | ||
| 
						 | 
					5ffdc66864 | ||
| 
						 | 
					9922c1503a | ||
| 
						 | 
					fce99d4b17 | ||
| 
						 | 
					11567085d8 | ||
| 
						 | 
					83f8e84247 | ||
| 
						 | 
					11eb5cb0fa | ||
| 
						 | 
					9a7af97b2d | ||
| 
						 | 
					5e11469f50 | ||
| 
						 | 
					0c7a3d1fff | ||
| 
						 | 
					8a705bf4b0 | ||
| 
						 | 
					6a89180deb | ||
| 
						 | 
					65d2b806cc | ||
| 
						 | 
					c149a3033c | ||
| 
						 | 
					4b7c233f1a | ||
| 
						 | 
					6e8e9c2aa9 | ||
| 
						 | 
					b6f628ee40 | ||
| 
						 | 
					bf79a700b7 | ||
| 
						 | 
					cdeb6e750f | ||
| 
						 | 
					dd226360bb | ||
| 
						 | 
					1a9aedf152 | ||
| 
						 | 
					d82c6df57e | ||
| 
						 | 
					7c91b4474a | ||
| 
						 | 
					a4f21db272 | ||
| 
						 | 
					58a8e1859e | ||
| 
						 | 
					2bed5b18c1 | ||
| 
						 | 
					fb5eb57345 | ||
| 
						 | 
					63db42a1d4 | ||
| 
						 | 
					f58ffe41f8 | ||
| 
						 | 
					445d2e372c | ||
| 
						 | 
					c037e95861 | ||
| 
						 | 
					2e1b35959f | ||
| 
						 | 
					7f46d9e0f9 | ||
| 
						 | 
					069b5f81a0 | ||
| 
						 | 
					3a36d0b13f | ||
| 
						 | 
					f0760e99b7 | ||
| 
						 | 
					18fecf8c09 | ||
| 
						 | 
					414cf1b333 | ||
| 
						 | 
					d10f891f51 | ||
| 
						 | 
					b5927322e6 | ||
| 
						 | 
					1cf4107e1c | ||
| 
						 | 
					c12408326c | ||
| 
						 | 
					4434e59e5a | ||
| 
						 | 
					45180d98f6 | ||
| 
						 | 
					44494ad18e | ||
| 
						 | 
					1447536906 | ||
| 
						 | 
					27ec517084 | ||
| 
						 | 
					ce1f034bac | ||
| 
						 | 
					f1f96f16e9 | ||
| 
						 | 
					7665e9b076 | ||
| 
						 | 
					227d94f38d | ||
| 
						 | 
					b724ae9e0e | ||
| 
						 | 
					df6cc14201 | ||
| 
						 | 
					d981d7859d | ||
| 
						 | 
					0f1ec515c1 | ||
| 
						 | 
					78e18256f7 | ||
| 
						 | 
					4899dfe642 | ||
| 
						 | 
					d6b6e94059 | ||
| 
						 | 
					310355a00b | ||
| 
						 | 
					8cf26d6f3c | ||
| 
						 | 
					b15a10f905 | ||
| 
						 | 
					5dcf1debd7 | ||
| 
						 | 
					9b57e1ac1d | ||
| 
						 | 
					68683e3a50 | ||
| 
						 | 
					d83324c4dc | ||
| 
						 | 
					ecde4c1d2d | ||
| 
						 | 
					bd8e470726 | ||
| 
						 | 
					d2913fe627 | ||
| 
						 | 
					43acc7dc2c | ||
| 
						 | 
					e2a16d758b | ||
| 
						 | 
					4af4649e23 | ||
| 
						 | 
					8bcddef39d | ||
| 
						 | 
					4ac96ccea2 | ||
| 
						 | 
					3c5de77ae9 | ||
| 
						 | 
					a2925b1d37 | ||
| 
						 | 
					73748e9e20 | ||
| 
						 | 
					75c9823899 | ||
| 
						 | 
					c8c0bd3351 | ||
| 
						 | 
					e1cdeb7c8f | ||
| 
						 | 
					7f97f42552 | ||
| 
						 | 
					aa7f3569ec | ||
| 
						 | 
					2d0a08442e | ||
| 
						 | 
					d2380756b2 | ||
| 
						 | 
					925e3cb6c9 | ||
| 
						 | 
					6757acba56 | ||
| 
						 | 
					5cc91cdd95 | ||
| 
						 | 
					2b41886819 | ||
| 
						 | 
					72c6efd6a0 | ||
| 
						 | 
					a1f1804112 | ||
| 
						 | 
					a8b1ceb4e9 | ||
| 
						 | 
					4fb0f7f8c6 | ||
| 
						 | 
					958cadeca8 | ||
| 
						 | 
					00f2655f1a | ||
| 
						 | 
					074f5029eb | ||
| 
						 | 
					1691976587 | ||
| 
						 | 
					60e6b4d21e | ||
| 
						 | 
					5750591df2 | ||
| 
						 | 
					a75da54455 | ||
| 
						 | 
					de7f6c3f5f | ||
| 
						 | 
					4245480656 | ||
| 
						 | 
					1824c8131e | ||
| 
						 | 
					4e9606d2e0 | ||
| 
						 | 
					78500fa933 | ||
| 
						 | 
					9c69b98a49 | ||
| 
						 | 
					e6d8ef98d3 | ||
| 
						 | 
					3f1af1690b | ||
| 
						 | 
					84374b6b1e | ||
| 
						 | 
					391316c9b5 | ||
| 
						 | 
					705c62ebd7 | ||
| 
						 | 
					cb520c00a5 | ||
| 
						 | 
					2f24138345 | ||
| 
						 | 
					96512b80cc | ||
| 
						 | 
					fcb9b51978 | ||
| 
						 | 
					f408f1a368 | ||
| 
						 | 
					7d8d563c62 | ||
| 
						 | 
					0a1f705fda | ||
| 
						 | 
					1952c1880b | ||
| 
						 | 
					b03967dac1 | ||
| 
						 | 
					bcae2596a6 | ||
| 
						 | 
					fc0347c86c | ||
| 
						 | 
					d9563d4de1 | ||
| 
						 | 
					cc7e2bf8db | ||
| 
						 | 
					5d98e2923b | ||
| 
						 | 
					07197d12f6 | ||
| 
						 | 
					7b0a298497 | ||
| 
						 | 
					21679cf2ba | ||
| 
						 | 
					4be7cd12a1 | ||
| 
						 | 
					dee4d0ccb7 | ||
| 
						 | 
					7209dd8bae | ||
| 
						 | 
					ab736c89bb | ||
| 
						 | 
					6911639617 | ||
| 
						 | 
					b9720d0715 | ||
| 
						 | 
					47b3267ed4 | ||
| 
						 | 
					e16ba2adb5 | ||
| 
						 | 
					0a19b1e32c | ||
| 
						 | 
					bae9a950c0 | ||
| 
						 | 
					72b2943332 | ||
| 
						 | 
					4ec0ef7548 | ||
| 
						 | 
					25bc6761f6 | ||
| 
						 | 
					81b6562c25 | ||
| 
						 | 
					ae74189fc2 | ||
| 
						 | 
					9e516efe10 | ||
| 
						 | 
					366e29439e | ||
| 
						 | 
					1c9c700d7f | ||
| 
						 | 
					b2e6b9d31f | ||
| 
						 | 
					7623f63846 | ||
| 
						 | 
					2bfaf9dce3 | ||
| 
						 | 
					5c2c1560bb | ||
| 
						 | 
					f7096ab78e | ||
| 
						 | 
					98f8feb625 | ||
| 
						 | 
					9944ca414e | 
@@ -4,12 +4,17 @@
 | 
			
		||||
  "postCreateCommand": [
 | 
			
		||||
    "script/devcontainer-post-create"
 | 
			
		||||
  ],
 | 
			
		||||
  "containerEnv": {
 | 
			
		||||
    "DEVCONTAINER": "1"
 | 
			
		||||
  },
 | 
			
		||||
  "runArgs": [
 | 
			
		||||
    "--privileged",
 | 
			
		||||
    "-e",
 | 
			
		||||
    "ESPHOME_DASHBOARD_USE_PING=1"
 | 
			
		||||
  ],
 | 
			
		||||
  "appPort": 6052,
 | 
			
		||||
  "customizations": {
 | 
			
		||||
    "vscode": {
 | 
			
		||||
      "extensions": [
 | 
			
		||||
        // python
 | 
			
		||||
        "ms-python.python",
 | 
			
		||||
@@ -51,6 +56,8 @@
 | 
			
		||||
        "files.associations": {
 | 
			
		||||
          "**/.vscode/*.json": "jsonc"
 | 
			
		||||
        },
 | 
			
		||||
    "C_Cpp.clang_format_path": "/usr/bin/clang-format-13",
 | 
			
		||||
        "C_Cpp.clang_format_path": "/usr/bin/clang-format-13"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							@@ -11,6 +11,7 @@ on:
 | 
			
		||||
      - ".github/workflows/**"
 | 
			
		||||
      - "requirements*.txt"
 | 
			
		||||
      - "platformio.ini"
 | 
			
		||||
      - "script/platformio_install_deps.py"
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    paths:
 | 
			
		||||
@@ -18,6 +19,7 @@ on:
 | 
			
		||||
      - ".github/workflows/**"
 | 
			
		||||
      - "requirements*.txt"
 | 
			
		||||
      - "platformio.ini"
 | 
			
		||||
      - "script/platformio_install_deps.py"
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -23,6 +23,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      max-parallel: 5
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
          - id: ci-custom
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -117,7 +117,7 @@ jobs:
 | 
			
		||||
            --suffix "${{ matrix.image.suffix }}"
 | 
			
		||||
 | 
			
		||||
      - name: Build and push
 | 
			
		||||
        uses: docker/build-push-action@v3
 | 
			
		||||
        uses: docker/build-push-action@v4
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: ./docker/Dockerfile
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
---
 | 
			
		||||
name: Synchronise Device Classes from Home Assistant
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: '45 6 * * *'
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: write
 | 
			
		||||
  pull-requests: write
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  sync:
 | 
			
		||||
    name: Sync Device Classes
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
 | 
			
		||||
      - name: Checkout Home Assistant
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
        with:
 | 
			
		||||
          repository: home-assistant/core
 | 
			
		||||
          path: lib/home-assistant
 | 
			
		||||
 | 
			
		||||
      - name: Setup Python
 | 
			
		||||
        uses: actions/setup-python@v4
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: 3.11
 | 
			
		||||
 | 
			
		||||
      - name: Install Home Assistant
 | 
			
		||||
        run: |
 | 
			
		||||
          python -m pip install --upgrade pip
 | 
			
		||||
          pip install -e lib/home-assistant
 | 
			
		||||
 | 
			
		||||
      - name: Sync
 | 
			
		||||
        run: |
 | 
			
		||||
          python ./script/sync-device_class.py
 | 
			
		||||
 | 
			
		||||
      - name: Get PR template
 | 
			
		||||
        id: pr-template-body
 | 
			
		||||
        run: |
 | 
			
		||||
          body=$(cat .github/PULL_REQUEST_TEMPLATE.md)
 | 
			
		||||
          delimiter="$(openssl rand -hex 8)"
 | 
			
		||||
          echo "body<<$delimiter" >> $GITHUB_OUTPUT
 | 
			
		||||
          echo "$body" >> $GITHUB_OUTPUT
 | 
			
		||||
          echo "$delimiter" >> $GITHUB_OUTPUT
 | 
			
		||||
 | 
			
		||||
      - name: Commit changes
 | 
			
		||||
        uses: peter-evans/create-pull-request@v5
 | 
			
		||||
        with:
 | 
			
		||||
          commit-message: "Synchronise Device Classes from Home Assistant"
 | 
			
		||||
          committer: esphomebot <esphome@nabucasa.com>
 | 
			
		||||
          author: esphomebot <esphome@nabucasa.com>
 | 
			
		||||
          branch: sync/device-classes/
 | 
			
		||||
          branch-suffix: timestamp
 | 
			
		||||
          delete-branch: true
 | 
			
		||||
          title: "Synchronise Device Classes from Home Assistant"
 | 
			
		||||
          body: ${{ steps.pr-template-body.outputs.body }}
 | 
			
		||||
@@ -2,8 +2,8 @@
 | 
			
		||||
# See https://pre-commit.com for more information
 | 
			
		||||
# See https://pre-commit.com/hooks.html for more hooks
 | 
			
		||||
repos:
 | 
			
		||||
  - repo: https://github.com/ambv/black
 | 
			
		||||
    rev: 23.1.0
 | 
			
		||||
  - repo: https://github.com/psf/black
 | 
			
		||||
    rev: 23.3.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: black
 | 
			
		||||
        args:
 | 
			
		||||
@@ -27,7 +27,7 @@ repos:
 | 
			
		||||
          - --branch=release
 | 
			
		||||
          - --branch=beta
 | 
			
		||||
  - repo: https://github.com/asottile/pyupgrade
 | 
			
		||||
    rev: v3.3.1
 | 
			
		||||
    rev: v3.3.2
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: pyupgrade
 | 
			
		||||
        args: [--py39-plus]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							@@ -2,15 +2,24 @@
 | 
			
		||||
  "version": "2.0.0",
 | 
			
		||||
  "tasks": [
 | 
			
		||||
    {
 | 
			
		||||
      "label": "run",
 | 
			
		||||
      "label": "Run Dashboard",
 | 
			
		||||
      "type": "shell",
 | 
			
		||||
      "command": "python3 -m esphome dashboard config/",
 | 
			
		||||
      "command": "${command:python.interpreterPath}",
 | 
			
		||||
      "args": [
 | 
			
		||||
        "-m",
 | 
			
		||||
        "esphome",
 | 
			
		||||
        "dashboard",
 | 
			
		||||
        "config/"
 | 
			
		||||
      ],
 | 
			
		||||
      "problemMatcher": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "label": "clang-tidy",
 | 
			
		||||
      "type": "shell",
 | 
			
		||||
      "command": "./script/clang-tidy",
 | 
			
		||||
      "command": "${command:python.interpreterPath}",
 | 
			
		||||
      "args": [
 | 
			
		||||
        "./script/clang-tidy"
 | 
			
		||||
      ],
 | 
			
		||||
      "problemMatcher": [
 | 
			
		||||
        {
 | 
			
		||||
          "owner": "clang-tidy",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								CODEOWNERS
									
									
									
									
									
								
							@@ -21,6 +21,7 @@ esphome/components/airthings_wave_mini/* @ncareau
 | 
			
		||||
esphome/components/airthings_wave_plus/* @jeromelaban
 | 
			
		||||
esphome/components/am43/* @buxtronix
 | 
			
		||||
esphome/components/am43/cover/* @buxtronix
 | 
			
		||||
esphome/components/am43/sensor/* @buxtronix
 | 
			
		||||
esphome/components/analog_threshold/* @ianchi
 | 
			
		||||
esphome/components/animation/* @syndlex
 | 
			
		||||
esphome/components/anova/* @buxtronix
 | 
			
		||||
@@ -83,6 +84,7 @@ esphome/components/esp32_ble_server/* @jesserockz
 | 
			
		||||
esphome/components/esp32_camera_web_server/* @ayufan
 | 
			
		||||
esphome/components/esp32_can/* @Sympatron
 | 
			
		||||
esphome/components/esp32_improv/* @jesserockz
 | 
			
		||||
esphome/components/esp32_rmt_led_strip/* @jesserockz
 | 
			
		||||
esphome/components/esp8266/* @esphome/core
 | 
			
		||||
esphome/components/ethernet_info/* @gtjadsonsantos
 | 
			
		||||
esphome/components/exposure_notifications/* @OttoWinter
 | 
			
		||||
@@ -94,6 +96,7 @@ esphome/components/feedback/* @ianchi
 | 
			
		||||
esphome/components/fingerprint_grow/* @OnFreund @loongyh
 | 
			
		||||
esphome/components/fs3000/* @kahrendt
 | 
			
		||||
esphome/components/globals/* @esphome/core
 | 
			
		||||
esphome/components/gp8403/* @jesserockz
 | 
			
		||||
esphome/components/gpio/* @esphome/core
 | 
			
		||||
esphome/components/gps/* @coogle
 | 
			
		||||
esphome/components/graph/* @synco
 | 
			
		||||
@@ -106,11 +109,16 @@ esphome/components/heatpumpir/* @rob-deutsch
 | 
			
		||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
 | 
			
		||||
esphome/components/homeassistant/* @OttoWinter
 | 
			
		||||
esphome/components/honeywellabp/* @RubyBailey
 | 
			
		||||
esphome/components/host/* @esphome/core
 | 
			
		||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
 | 
			
		||||
esphome/components/hte501/* @Stock-M
 | 
			
		||||
esphome/components/hydreon_rgxx/* @functionpointer
 | 
			
		||||
esphome/components/hyt271/* @Philippe12
 | 
			
		||||
esphome/components/i2c/* @esphome/core
 | 
			
		||||
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/improv_base/* @esphome/core
 | 
			
		||||
esphome/components/improv_serial/* @esphome/core
 | 
			
		||||
@@ -136,6 +144,7 @@ esphome/components/ltr390/* @sjtrny
 | 
			
		||||
esphome/components/matrix_keypad/* @ssieb
 | 
			
		||||
esphome/components/max31865/* @DAVe3283
 | 
			
		||||
esphome/components/max44009/* @berfenger
 | 
			
		||||
esphome/components/max6956/* @looping40
 | 
			
		||||
esphome/components/max7219digit/* @rspaargaren
 | 
			
		||||
esphome/components/max9611/* @mckaymatthew
 | 
			
		||||
esphome/components/mcp23008/* @jesserockz
 | 
			
		||||
@@ -154,11 +163,14 @@ esphome/components/mcp9808/* @k7hpn
 | 
			
		||||
esphome/components/md5/* @esphome/core
 | 
			
		||||
esphome/components/mdns/* @esphome/core
 | 
			
		||||
esphome/components/media_player/* @jesserockz
 | 
			
		||||
esphome/components/microphone/* @jesserockz
 | 
			
		||||
esphome/components/mics_4514/* @jesserockz
 | 
			
		||||
esphome/components/midea/* @dudanov
 | 
			
		||||
esphome/components/midea_ir/* @dudanov
 | 
			
		||||
esphome/components/mitsubishi/* @RubyBailey
 | 
			
		||||
esphome/components/mlx90393/* @functionpointer
 | 
			
		||||
esphome/components/mlx90614/* @jesserockz
 | 
			
		||||
esphome/components/mmc5603/* @benhoff
 | 
			
		||||
esphome/components/modbus_controller/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/binary_sensor/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/number/* @martgras
 | 
			
		||||
@@ -182,6 +194,7 @@ esphome/components/nfc/* @jesserockz
 | 
			
		||||
esphome/components/number/* @esphome/core
 | 
			
		||||
esphome/components/ota/* @esphome/core
 | 
			
		||||
esphome/components/output/* @esphome/core
 | 
			
		||||
esphome/components/pca6416a/* @Mat931
 | 
			
		||||
esphome/components/pca9554/* @hwstar
 | 
			
		||||
esphome/components/pcf85063/* @brogon
 | 
			
		||||
esphome/components/pid/* @OttoWinter
 | 
			
		||||
@@ -228,7 +241,7 @@ esphome/components/shutdown/* @esphome/core @jsuanet
 | 
			
		||||
esphome/components/sigma_delta_output/* @Cat-Ion
 | 
			
		||||
esphome/components/sim800l/* @glmnet
 | 
			
		||||
esphome/components/sm10bit_base/* @Cossid
 | 
			
		||||
esphome/components/sm2135/* @BoukeHaarsma23
 | 
			
		||||
esphome/components/sm2135/* @BoukeHaarsma23 @dd32 @matika77
 | 
			
		||||
esphome/components/sm2235/* @Cossid
 | 
			
		||||
esphome/components/sm2335/* @Cossid
 | 
			
		||||
esphome/components/sml/* @alengwenus
 | 
			
		||||
@@ -236,6 +249,7 @@ esphome/components/smt100/* @piechade
 | 
			
		||||
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/sprinkler/* @kbx81
 | 
			
		||||
esphome/components/sps30/* @martgras
 | 
			
		||||
@@ -286,6 +300,7 @@ esphome/components/ufire_ise/* @pvizeli
 | 
			
		||||
esphome/components/ultrasonic/* @OttoWinter
 | 
			
		||||
esphome/components/vbus/* @ssieb
 | 
			
		||||
esphome/components/version/* @esphome/core
 | 
			
		||||
esphome/components/voice_assistant/* @jesserockz
 | 
			
		||||
esphome/components/wake_on_lan/* @willwill2will54
 | 
			
		||||
esphome/components/web_server_base/* @OttoWinter
 | 
			
		||||
esphome/components/whirlpool/* @glmnet
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,9 @@ RUN \
 | 
			
		||||
        python3-setuptools=52.0.0-4 \
 | 
			
		||||
        python3-pil=8.1.2+dfsg-0.3+deb11u1 \
 | 
			
		||||
        python3-cryptography=3.3.2-1 \
 | 
			
		||||
        python3-venv=3.9.2-3 \
 | 
			
		||||
        iputils-ping=3:20210202-1 \
 | 
			
		||||
        git=1:2.30.2-1 \
 | 
			
		||||
        git=1:2.30.2-1+deb11u2 \
 | 
			
		||||
        curl=7.74.0-1.3+deb11u7 \
 | 
			
		||||
        openssh-client=1:8.4p1-5+deb11u1 \
 | 
			
		||||
    && rm -rf \
 | 
			
		||||
@@ -59,10 +60,10 @@ RUN \
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# First install requirements to leverage caching when requirements don't change
 | 
			
		||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
 | 
			
		||||
COPY requirements.txt requirements_optional.txt script/platformio_install_deps.py platformio.ini /
 | 
			
		||||
RUN \
 | 
			
		||||
    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
 | 
			
		||||
    && /platformio_install_deps.py /platformio.ini
 | 
			
		||||
    && /platformio_install_deps.py /platformio.ini --libraries
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ======================= docker-type image =======================
 | 
			
		||||
 
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# This script is used in the docker containers to preinstall
 | 
			
		||||
# all platformio libraries in the global storage
 | 
			
		||||
 | 
			
		||||
import configparser
 | 
			
		||||
import subprocess
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
config = configparser.ConfigParser(inline_comment_prefixes=(';', ))
 | 
			
		||||
config.read(sys.argv[1])
 | 
			
		||||
 | 
			
		||||
libs = []
 | 
			
		||||
# Extract from every lib_deps key in all sections
 | 
			
		||||
for section in config.sections():
 | 
			
		||||
    conf = config[section]
 | 
			
		||||
    if "lib_deps" not in conf:
 | 
			
		||||
        continue
 | 
			
		||||
    for lib_dep in conf["lib_deps"].splitlines():
 | 
			
		||||
        if not lib_dep:
 | 
			
		||||
            # Empty line or comment
 | 
			
		||||
            continue
 | 
			
		||||
        if lib_dep.startswith("${"):
 | 
			
		||||
            # Extending from another section
 | 
			
		||||
            continue
 | 
			
		||||
        if "@" not in lib_dep:
 | 
			
		||||
            # No version pinned, this is an internal lib
 | 
			
		||||
            continue
 | 
			
		||||
        libs.append(lib_dep)
 | 
			
		||||
 | 
			
		||||
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])
 | 
			
		||||
@@ -152,6 +152,8 @@ def run_miniterm(config, port):
 | 
			
		||||
        _LOGGER.error("Could not connect to serial port %s", port)
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def wrap_to_code(name, comp):
 | 
			
		||||
    coro = coroutine(comp.to_code)
 | 
			
		||||
@@ -930,6 +932,8 @@ def run_esphome(argv):
 | 
			
		||||
            _LOGGER.error(e, exc_info=args.verbose)
 | 
			
		||||
            return 1
 | 
			
		||||
 | 
			
		||||
    _LOGGER.info("ESPHome %s", const.__version__)
 | 
			
		||||
 | 
			
		||||
    for conf_path in args.configuration:
 | 
			
		||||
        if any(os.path.basename(conf_path) == x for x in SECRETS_FILES):
 | 
			
		||||
            _LOGGER.warning("Skipping secrets file %s", conf_path)
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,118 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.const import CONF_INPUT
 | 
			
		||||
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
from esphome.components.esp32 import get_esp32_variant
 | 
			
		||||
from esphome.components.esp32.const import (
 | 
			
		||||
    VARIANT_ESP32,
 | 
			
		||||
    VARIANT_ESP32C3,
 | 
			
		||||
    VARIANT_ESP32H2,
 | 
			
		||||
    VARIANT_ESP32S2,
 | 
			
		||||
    VARIANT_ESP32S3,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@esphome/core"]
 | 
			
		||||
 | 
			
		||||
ATTENUATION_MODES = {
 | 
			
		||||
    "0db": cg.global_ns.ADC_ATTEN_DB_0,
 | 
			
		||||
    "2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
 | 
			
		||||
    "6db": cg.global_ns.ADC_ATTEN_DB_6,
 | 
			
		||||
    "11db": cg.global_ns.ADC_ATTEN_DB_11,
 | 
			
		||||
    "auto": "auto",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
 | 
			
		||||
 | 
			
		||||
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
 | 
			
		||||
# pin to adc1 channel mapping
 | 
			
		||||
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
 | 
			
		||||
    VARIANT_ESP32: {
 | 
			
		||||
        36: adc1_channel_t.ADC1_CHANNEL_0,
 | 
			
		||||
        37: adc1_channel_t.ADC1_CHANNEL_1,
 | 
			
		||||
        38: adc1_channel_t.ADC1_CHANNEL_2,
 | 
			
		||||
        39: adc1_channel_t.ADC1_CHANNEL_3,
 | 
			
		||||
        32: adc1_channel_t.ADC1_CHANNEL_4,
 | 
			
		||||
        33: adc1_channel_t.ADC1_CHANNEL_5,
 | 
			
		||||
        34: adc1_channel_t.ADC1_CHANNEL_6,
 | 
			
		||||
        35: adc1_channel_t.ADC1_CHANNEL_7,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32S2: {
 | 
			
		||||
        1: adc1_channel_t.ADC1_CHANNEL_0,
 | 
			
		||||
        2: adc1_channel_t.ADC1_CHANNEL_1,
 | 
			
		||||
        3: adc1_channel_t.ADC1_CHANNEL_2,
 | 
			
		||||
        4: adc1_channel_t.ADC1_CHANNEL_3,
 | 
			
		||||
        5: adc1_channel_t.ADC1_CHANNEL_4,
 | 
			
		||||
        6: adc1_channel_t.ADC1_CHANNEL_5,
 | 
			
		||||
        7: adc1_channel_t.ADC1_CHANNEL_6,
 | 
			
		||||
        8: adc1_channel_t.ADC1_CHANNEL_7,
 | 
			
		||||
        9: adc1_channel_t.ADC1_CHANNEL_8,
 | 
			
		||||
        10: adc1_channel_t.ADC1_CHANNEL_9,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32S3: {
 | 
			
		||||
        1: adc1_channel_t.ADC1_CHANNEL_0,
 | 
			
		||||
        2: adc1_channel_t.ADC1_CHANNEL_1,
 | 
			
		||||
        3: adc1_channel_t.ADC1_CHANNEL_2,
 | 
			
		||||
        4: adc1_channel_t.ADC1_CHANNEL_3,
 | 
			
		||||
        5: adc1_channel_t.ADC1_CHANNEL_4,
 | 
			
		||||
        6: adc1_channel_t.ADC1_CHANNEL_5,
 | 
			
		||||
        7: adc1_channel_t.ADC1_CHANNEL_6,
 | 
			
		||||
        8: adc1_channel_t.ADC1_CHANNEL_7,
 | 
			
		||||
        9: adc1_channel_t.ADC1_CHANNEL_8,
 | 
			
		||||
        10: adc1_channel_t.ADC1_CHANNEL_9,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32C3: {
 | 
			
		||||
        0: adc1_channel_t.ADC1_CHANNEL_0,
 | 
			
		||||
        1: adc1_channel_t.ADC1_CHANNEL_1,
 | 
			
		||||
        2: adc1_channel_t.ADC1_CHANNEL_2,
 | 
			
		||||
        3: adc1_channel_t.ADC1_CHANNEL_3,
 | 
			
		||||
        4: adc1_channel_t.ADC1_CHANNEL_4,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32H2: {
 | 
			
		||||
        0: adc1_channel_t.ADC1_CHANNEL_0,
 | 
			
		||||
        1: adc1_channel_t.ADC1_CHANNEL_1,
 | 
			
		||||
        2: adc1_channel_t.ADC1_CHANNEL_2,
 | 
			
		||||
        3: adc1_channel_t.ADC1_CHANNEL_3,
 | 
			
		||||
        4: adc1_channel_t.ADC1_CHANNEL_4,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_adc_pin(value):
 | 
			
		||||
    if str(value).upper() == "VCC":
 | 
			
		||||
        return cv.only_on_esp8266("VCC")
 | 
			
		||||
 | 
			
		||||
    if str(value).upper() == "TEMPERATURE":
 | 
			
		||||
        return cv.only_on_rp2040("TEMPERATURE")
 | 
			
		||||
 | 
			
		||||
    if CORE.is_esp32:
 | 
			
		||||
        value = pins.internal_gpio_input_pin_number(value)
 | 
			
		||||
        variant = get_esp32_variant()
 | 
			
		||||
        if variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL:
 | 
			
		||||
            raise cv.Invalid(f"This ESP32 variant ({variant}) is not supported")
 | 
			
		||||
 | 
			
		||||
        if value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]:
 | 
			
		||||
            raise cv.Invalid(f"{variant} doesn't support ADC on this pin")
 | 
			
		||||
        return pins.internal_gpio_input_pin_schema(value)
 | 
			
		||||
 | 
			
		||||
    if CORE.is_esp8266:
 | 
			
		||||
        from esphome.components.esp8266.gpio import CONF_ANALOG
 | 
			
		||||
 | 
			
		||||
        value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})(
 | 
			
		||||
            value
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if value != 17:  # A0
 | 
			
		||||
            raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.")
 | 
			
		||||
        return pins.gpio_pin_schema(
 | 
			
		||||
            {CONF_ANALOG: True, CONF_INPUT: True}, internal=True
 | 
			
		||||
        )(value)
 | 
			
		||||
 | 
			
		||||
    if CORE.is_rp2040:
 | 
			
		||||
        value = pins.internal_gpio_input_pin_number(value)
 | 
			
		||||
        if value not in (26, 27, 28, 29):
 | 
			
		||||
            raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC.")
 | 
			
		||||
        return pins.internal_gpio_input_pin_schema(value)
 | 
			
		||||
 | 
			
		||||
    raise NotImplementedError
 | 
			
		||||
 
 | 
			
		||||
@@ -1,133 +1,27 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.components import sensor, voltage_sampler
 | 
			
		||||
from esphome.components.esp32 import get_esp32_variant
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ATTENUATION,
 | 
			
		||||
    CONF_RAW,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_INPUT,
 | 
			
		||||
    CONF_NUMBER,
 | 
			
		||||
    CONF_PIN,
 | 
			
		||||
    CONF_RAW,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
from esphome.components.esp32 import get_esp32_variant
 | 
			
		||||
from esphome.components.esp32.const import (
 | 
			
		||||
    VARIANT_ESP32,
 | 
			
		||||
    VARIANT_ESP32C3,
 | 
			
		||||
    VARIANT_ESP32H2,
 | 
			
		||||
    VARIANT_ESP32S2,
 | 
			
		||||
    VARIANT_ESP32S3,
 | 
			
		||||
 | 
			
		||||
from . import (
 | 
			
		||||
    ATTENUATION_MODES,
 | 
			
		||||
    ESP32_VARIANT_ADC1_PIN_TO_CHANNEL,
 | 
			
		||||
    validate_adc_pin,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["voltage_sampler"]
 | 
			
		||||
 | 
			
		||||
ATTENUATION_MODES = {
 | 
			
		||||
    "0db": cg.global_ns.ADC_ATTEN_DB_0,
 | 
			
		||||
    "2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
 | 
			
		||||
    "6db": cg.global_ns.ADC_ATTEN_DB_6,
 | 
			
		||||
    "11db": cg.global_ns.ADC_ATTEN_DB_11,
 | 
			
		||||
    "auto": "auto",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
 | 
			
		||||
 | 
			
		||||
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
 | 
			
		||||
# pin to adc1 channel mapping
 | 
			
		||||
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
 | 
			
		||||
    VARIANT_ESP32: {
 | 
			
		||||
        36: adc1_channel_t.ADC1_CHANNEL_0,
 | 
			
		||||
        37: adc1_channel_t.ADC1_CHANNEL_1,
 | 
			
		||||
        38: adc1_channel_t.ADC1_CHANNEL_2,
 | 
			
		||||
        39: adc1_channel_t.ADC1_CHANNEL_3,
 | 
			
		||||
        32: adc1_channel_t.ADC1_CHANNEL_4,
 | 
			
		||||
        33: adc1_channel_t.ADC1_CHANNEL_5,
 | 
			
		||||
        34: adc1_channel_t.ADC1_CHANNEL_6,
 | 
			
		||||
        35: adc1_channel_t.ADC1_CHANNEL_7,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32S2: {
 | 
			
		||||
        1: adc1_channel_t.ADC1_CHANNEL_0,
 | 
			
		||||
        2: adc1_channel_t.ADC1_CHANNEL_1,
 | 
			
		||||
        3: adc1_channel_t.ADC1_CHANNEL_2,
 | 
			
		||||
        4: adc1_channel_t.ADC1_CHANNEL_3,
 | 
			
		||||
        5: adc1_channel_t.ADC1_CHANNEL_4,
 | 
			
		||||
        6: adc1_channel_t.ADC1_CHANNEL_5,
 | 
			
		||||
        7: adc1_channel_t.ADC1_CHANNEL_6,
 | 
			
		||||
        8: adc1_channel_t.ADC1_CHANNEL_7,
 | 
			
		||||
        9: adc1_channel_t.ADC1_CHANNEL_8,
 | 
			
		||||
        10: adc1_channel_t.ADC1_CHANNEL_9,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32S3: {
 | 
			
		||||
        1: adc1_channel_t.ADC1_CHANNEL_0,
 | 
			
		||||
        2: adc1_channel_t.ADC1_CHANNEL_1,
 | 
			
		||||
        3: adc1_channel_t.ADC1_CHANNEL_2,
 | 
			
		||||
        4: adc1_channel_t.ADC1_CHANNEL_3,
 | 
			
		||||
        5: adc1_channel_t.ADC1_CHANNEL_4,
 | 
			
		||||
        6: adc1_channel_t.ADC1_CHANNEL_5,
 | 
			
		||||
        7: adc1_channel_t.ADC1_CHANNEL_6,
 | 
			
		||||
        8: adc1_channel_t.ADC1_CHANNEL_7,
 | 
			
		||||
        9: adc1_channel_t.ADC1_CHANNEL_8,
 | 
			
		||||
        10: adc1_channel_t.ADC1_CHANNEL_9,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32C3: {
 | 
			
		||||
        0: adc1_channel_t.ADC1_CHANNEL_0,
 | 
			
		||||
        1: adc1_channel_t.ADC1_CHANNEL_1,
 | 
			
		||||
        2: adc1_channel_t.ADC1_CHANNEL_2,
 | 
			
		||||
        3: adc1_channel_t.ADC1_CHANNEL_3,
 | 
			
		||||
        4: adc1_channel_t.ADC1_CHANNEL_4,
 | 
			
		||||
    },
 | 
			
		||||
    VARIANT_ESP32H2: {
 | 
			
		||||
        0: adc1_channel_t.ADC1_CHANNEL_0,
 | 
			
		||||
        1: adc1_channel_t.ADC1_CHANNEL_1,
 | 
			
		||||
        2: adc1_channel_t.ADC1_CHANNEL_2,
 | 
			
		||||
        3: adc1_channel_t.ADC1_CHANNEL_3,
 | 
			
		||||
        4: adc1_channel_t.ADC1_CHANNEL_4,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_adc_pin(value):
 | 
			
		||||
    if str(value).upper() == "VCC":
 | 
			
		||||
        return cv.only_on_esp8266("VCC")
 | 
			
		||||
 | 
			
		||||
    if str(value).upper() == "TEMPERATURE":
 | 
			
		||||
        return cv.only_on_rp2040("TEMPERATURE")
 | 
			
		||||
 | 
			
		||||
    if CORE.is_esp32:
 | 
			
		||||
        value = pins.internal_gpio_input_pin_number(value)
 | 
			
		||||
        variant = get_esp32_variant()
 | 
			
		||||
        if variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL:
 | 
			
		||||
            raise cv.Invalid(f"This ESP32 variant ({variant}) is not supported")
 | 
			
		||||
 | 
			
		||||
        if value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]:
 | 
			
		||||
            raise cv.Invalid(f"{variant} doesn't support ADC on this pin")
 | 
			
		||||
        return pins.internal_gpio_input_pin_schema(value)
 | 
			
		||||
 | 
			
		||||
    if CORE.is_esp8266:
 | 
			
		||||
        from esphome.components.esp8266.gpio import CONF_ANALOG
 | 
			
		||||
 | 
			
		||||
        value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})(
 | 
			
		||||
            value
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if value != 17:  # A0
 | 
			
		||||
            raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.")
 | 
			
		||||
        return pins.gpio_pin_schema(
 | 
			
		||||
            {CONF_ANALOG: True, CONF_INPUT: True}, internal=True
 | 
			
		||||
        )(value)
 | 
			
		||||
 | 
			
		||||
    if CORE.is_rp2040:
 | 
			
		||||
        value = pins.internal_gpio_input_pin_number(value)
 | 
			
		||||
        if value not in (26, 27, 28, 29):
 | 
			
		||||
            raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC.")
 | 
			
		||||
        return pins.internal_gpio_input_pin_schema(value)
 | 
			
		||||
 | 
			
		||||
    raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_config(config):
 | 
			
		||||
    if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
CODEOWNERS = ["@buxtronix"]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ from esphome.const import CONF_ID, CONF_PIN
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@buxtronix"]
 | 
			
		||||
DEPENDENCIES = ["ble_client"]
 | 
			
		||||
AUTO_LOAD = ["am43", "sensor"]
 | 
			
		||||
AUTO_LOAD = ["am43"]
 | 
			
		||||
 | 
			
		||||
CONF_INVERT_POSITION = "invert_position"
 | 
			
		||||
 | 
			
		||||
@@ -27,10 +27,10 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    cg.add(var.set_pin(config[CONF_PIN]))
 | 
			
		||||
    cg.add(var.set_invert_position(config[CONF_INVERT_POSITION]))
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield cover.register_cover(var, config)
 | 
			
		||||
    yield ble_client.register_ble_node(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await cover.register_cover(var, config)
 | 
			
		||||
    await ble_client.register_ble_node(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,7 @@ void Am43Component::loop() {
 | 
			
		||||
 | 
			
		||||
CoverTraits Am43Component::get_traits() {
 | 
			
		||||
  auto traits = CoverTraits();
 | 
			
		||||
  traits.set_supports_stop(true);
 | 
			
		||||
  traits.set_supports_position(true);
 | 
			
		||||
  traits.set_supports_tilt(false);
 | 
			
		||||
  traits.set_is_assumed_state(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ from esphome.const import (
 | 
			
		||||
    UNIT_PERCENT,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["am43"]
 | 
			
		||||
CODEOWNERS = ["@buxtronix"]
 | 
			
		||||
 | 
			
		||||
am43_ns = cg.esphome_ns.namespace("am43")
 | 
			
		||||
@@ -38,15 +39,15 @@ CONFIG_SCHEMA = (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield ble_client.register_ble_node(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await ble_client.register_ble_node(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_BATTERY_LEVEL in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
 | 
			
		||||
        cg.add(var.set_battery(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_ILLUMINANCE in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_ILLUMINANCE])
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_ILLUMINANCE])
 | 
			
		||||
        cg.add(var.set_illuminance(sens))
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#include "am43.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "am43_sensor.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
@@ -4,7 +4,6 @@ from esphome.components import i2c
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
AUTO_LOAD = ["sensor", "binary_sensor"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
CONF_APDS9960_ID = "apds9960_id"
 | 
			
		||||
 
 | 
			
		||||
@@ -116,8 +116,12 @@ void APDS9960::setup() {
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x80, val);
 | 
			
		||||
}
 | 
			
		||||
bool APDS9960::is_color_enabled_() const {
 | 
			
		||||
  return this->red_channel_ != nullptr || this->green_channel_ != nullptr || this->blue_channel_ != nullptr ||
 | 
			
		||||
         this->clear_channel_ != nullptr;
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  return this->red_sensor_ != nullptr || this->green_sensor_ != nullptr || this->blue_sensor_ != nullptr ||
 | 
			
		||||
         this->clear_sensor_ != nullptr;
 | 
			
		||||
#else
 | 
			
		||||
  return false;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APDS9960::dump_config() {
 | 
			
		||||
@@ -125,6 +129,15 @@ void APDS9960::dump_config() {
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  LOG_SENSOR("  ", "Red channel", this->red_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Green channel", this->green_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Blue channel", this->blue_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Clear channel", this->clear_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Proximity", this->proximity_sensor_);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    switch (this->error_code_) {
 | 
			
		||||
      case COMMUNICATION_FAILED:
 | 
			
		||||
@@ -181,17 +194,22 @@ void APDS9960::read_color_data_(uint8_t status) {
 | 
			
		||||
  float blue_perc = (uint_blue / float(UINT16_MAX)) * 100.0f;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Got clear=%.1f%% red=%.1f%% green=%.1f%% blue=%.1f%%", clear_perc, red_perc, green_perc, blue_perc);
 | 
			
		||||
  if (this->clear_channel_ != nullptr)
 | 
			
		||||
    this->clear_channel_->publish_state(clear_perc);
 | 
			
		||||
  if (this->red_channel_ != nullptr)
 | 
			
		||||
    this->red_channel_->publish_state(red_perc);
 | 
			
		||||
  if (this->green_channel_ != nullptr)
 | 
			
		||||
    this->green_channel_->publish_state(green_perc);
 | 
			
		||||
  if (this->blue_channel_ != nullptr)
 | 
			
		||||
    this->blue_channel_->publish_state(blue_perc);
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  if (this->clear_sensor_ != nullptr)
 | 
			
		||||
    this->clear_sensor_->publish_state(clear_perc);
 | 
			
		||||
  if (this->red_sensor_ != nullptr)
 | 
			
		||||
    this->red_sensor_->publish_state(red_perc);
 | 
			
		||||
  if (this->green_sensor_ != nullptr)
 | 
			
		||||
    this->green_sensor_->publish_state(green_perc);
 | 
			
		||||
  if (this->blue_sensor_ != nullptr)
 | 
			
		||||
    this->blue_sensor_->publish_state(blue_perc);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
void APDS9960::read_proximity_data_(uint8_t status) {
 | 
			
		||||
  if (this->proximity_ == nullptr)
 | 
			
		||||
#ifndef USE_SENSOR
 | 
			
		||||
  return;
 | 
			
		||||
#else
 | 
			
		||||
  if (this->proximity_sensor_ == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if ((status & 0b10) == 0x00) {
 | 
			
		||||
@@ -204,7 +222,8 @@ void APDS9960::read_proximity_data_(uint8_t status) {
 | 
			
		||||
 | 
			
		||||
  float prox_perc = (prox / float(UINT8_MAX)) * 100.0f;
 | 
			
		||||
  ESP_LOGD(TAG, "Got proximity=%.1f%%", prox_perc);
 | 
			
		||||
  this->proximity_->publish_state(prox_perc);
 | 
			
		||||
  this->proximity_sensor_->publish_state(prox_perc);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
void APDS9960::read_gesture_data_() {
 | 
			
		||||
  if (!this->is_gesture_enabled_())
 | 
			
		||||
@@ -256,28 +275,29 @@ void APDS9960::read_gesture_data_() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APDS9960::report_gesture_(int gesture) {
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  binary_sensor::BinarySensor *bin;
 | 
			
		||||
  switch (gesture) {
 | 
			
		||||
    case 1:
 | 
			
		||||
      bin = this->up_direction_;
 | 
			
		||||
      bin = this->up_direction_binary_sensor_;
 | 
			
		||||
      this->gesture_up_started_ = false;
 | 
			
		||||
      this->gesture_down_started_ = false;
 | 
			
		||||
      ESP_LOGD(TAG, "Got gesture UP");
 | 
			
		||||
      break;
 | 
			
		||||
    case 2:
 | 
			
		||||
      bin = this->down_direction_;
 | 
			
		||||
      bin = this->down_direction_binary_sensor_;
 | 
			
		||||
      this->gesture_up_started_ = false;
 | 
			
		||||
      this->gesture_down_started_ = false;
 | 
			
		||||
      ESP_LOGD(TAG, "Got gesture DOWN");
 | 
			
		||||
      break;
 | 
			
		||||
    case 3:
 | 
			
		||||
      bin = this->left_direction_;
 | 
			
		||||
      bin = this->left_direction_binary_sensor_;
 | 
			
		||||
      this->gesture_left_started_ = false;
 | 
			
		||||
      this->gesture_right_started_ = false;
 | 
			
		||||
      ESP_LOGD(TAG, "Got gesture LEFT");
 | 
			
		||||
      break;
 | 
			
		||||
    case 4:
 | 
			
		||||
      bin = this->right_direction_;
 | 
			
		||||
      bin = this->right_direction_binary_sensor_;
 | 
			
		||||
      this->gesture_left_started_ = false;
 | 
			
		||||
      this->gesture_right_started_ = false;
 | 
			
		||||
      ESP_LOGD(TAG, "Got gesture RIGHT");
 | 
			
		||||
@@ -290,6 +310,7 @@ void APDS9960::report_gesture_(int gesture) {
 | 
			
		||||
    bin->publish_state(true);
 | 
			
		||||
    bin->publish_state(false);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
void APDS9960::process_dataset_(int up, int down, int left, int right) {
 | 
			
		||||
  /* Algorithm: (see Figure 11 in datasheet)
 | 
			
		||||
@@ -365,10 +386,22 @@ void APDS9960::process_dataset_(int up, int down, int left, int right) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
float APDS9960::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
bool APDS9960::is_proximity_enabled_() const { return this->proximity_ != nullptr || this->is_gesture_enabled_(); }
 | 
			
		||||
bool APDS9960::is_proximity_enabled_() const {
 | 
			
		||||
  return
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
      this->proximity_sensor_ != nullptr
 | 
			
		||||
#else
 | 
			
		||||
      false
 | 
			
		||||
#endif
 | 
			
		||||
      || this->is_gesture_enabled_();
 | 
			
		||||
}
 | 
			
		||||
bool APDS9960::is_gesture_enabled_() const {
 | 
			
		||||
  return this->up_direction_ != nullptr || this->left_direction_ != nullptr || this->down_direction_ != nullptr ||
 | 
			
		||||
         this->right_direction_ != nullptr;
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  return this->up_direction_binary_sensor_ != nullptr || this->left_direction_binary_sensor_ != nullptr ||
 | 
			
		||||
         this->down_direction_binary_sensor_ != nullptr || this->right_direction_binary_sensor_ != nullptr;
 | 
			
		||||
#else
 | 
			
		||||
  return false;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace apds9960
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,34 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace apds9960 {
 | 
			
		||||
 | 
			
		||||
class APDS9960 : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  SUB_SENSOR(red)
 | 
			
		||||
  SUB_SENSOR(green)
 | 
			
		||||
  SUB_SENSOR(blue)
 | 
			
		||||
  SUB_SENSOR(clear)
 | 
			
		||||
  SUB_SENSOR(proximity)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  SUB_BINARY_SENSOR(up_direction)
 | 
			
		||||
  SUB_BINARY_SENSOR(right_direction)
 | 
			
		||||
  SUB_BINARY_SENSOR(down_direction)
 | 
			
		||||
  SUB_BINARY_SENSOR(left_direction)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
@@ -23,16 +43,6 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
  void set_gesture_gain(uint8_t gain) { this->gesture_gain_ = gain; }
 | 
			
		||||
  void set_gesture_wait_time(uint8_t wait_time) { this->gesture_wait_time_ = wait_time; }
 | 
			
		||||
 | 
			
		||||
  void set_red_channel(sensor::Sensor *red_channel) { red_channel_ = red_channel; }
 | 
			
		||||
  void set_green_channel(sensor::Sensor *green_channel) { green_channel_ = green_channel; }
 | 
			
		||||
  void set_blue_channel(sensor::Sensor *blue_channel) { blue_channel_ = blue_channel; }
 | 
			
		||||
  void set_clear_channel(sensor::Sensor *clear_channel) { clear_channel_ = clear_channel; }
 | 
			
		||||
  void set_up_direction(binary_sensor::BinarySensor *up_direction) { up_direction_ = up_direction; }
 | 
			
		||||
  void set_right_direction(binary_sensor::BinarySensor *right_direction) { right_direction_ = right_direction; }
 | 
			
		||||
  void set_down_direction(binary_sensor::BinarySensor *down_direction) { down_direction_ = down_direction; }
 | 
			
		||||
  void set_left_direction(binary_sensor::BinarySensor *left_direction) { left_direction_ = left_direction; }
 | 
			
		||||
  void set_proximity(sensor::Sensor *proximity) { proximity_ = proximity; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool is_color_enabled_() const;
 | 
			
		||||
  bool is_proximity_enabled_() const;
 | 
			
		||||
@@ -50,15 +60,6 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
  uint8_t gesture_gain_;
 | 
			
		||||
  uint8_t gesture_wait_time_;
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *red_channel_{nullptr};
 | 
			
		||||
  sensor::Sensor *green_channel_{nullptr};
 | 
			
		||||
  sensor::Sensor *blue_channel_{nullptr};
 | 
			
		||||
  sensor::Sensor *clear_channel_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *up_direction_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *right_direction_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *down_direction_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *left_direction_{nullptr};
 | 
			
		||||
  sensor::Sensor *proximity_{nullptr};
 | 
			
		||||
  enum ErrorCode {
 | 
			
		||||
    NONE = 0,
 | 
			
		||||
    COMMUNICATION_FAILED,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,19 +6,14 @@ from . import APDS9960, CONF_APDS9960_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["apds9960"]
 | 
			
		||||
 | 
			
		||||
DIRECTIONS = {
 | 
			
		||||
    "UP": "set_up_direction",
 | 
			
		||||
    "DOWN": "set_down_direction",
 | 
			
		||||
    "LEFT": "set_left_direction",
 | 
			
		||||
    "RIGHT": "set_right_direction",
 | 
			
		||||
}
 | 
			
		||||
DIRECTIONS = ["up", "down", "left", "right"]
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(
 | 
			
		||||
    device_class=DEVICE_CLASS_MOVING
 | 
			
		||||
).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
 | 
			
		||||
        cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
 | 
			
		||||
        cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, lower=True),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -26,5 +21,5 @@ CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    hub = await cg.get_variable(config[CONF_APDS9960_ID])
 | 
			
		||||
    var = await binary_sensor.new_binary_sensor(config)
 | 
			
		||||
    func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
 | 
			
		||||
    func = getattr(hub, f"set_{config[CONF_DIRECTION]}_direction_binary_sensor")
 | 
			
		||||
    cg.add(func(var))
 | 
			
		||||
 
 | 
			
		||||
@@ -11,13 +11,7 @@ from . import APDS9960, CONF_APDS9960_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["apds9960"]
 | 
			
		||||
 | 
			
		||||
TYPES = {
 | 
			
		||||
    "CLEAR": "set_clear_channel",
 | 
			
		||||
    "RED": "set_red_channel",
 | 
			
		||||
    "GREEN": "set_green_channel",
 | 
			
		||||
    "BLUE": "set_blue_channel",
 | 
			
		||||
    "PROXIMITY": "set_proximity",
 | 
			
		||||
}
 | 
			
		||||
TYPES = ["clear", "red", "green", "blue", "proximity"]
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = sensor.sensor_schema(
 | 
			
		||||
    unit_of_measurement=UNIT_PERCENT,
 | 
			
		||||
@@ -26,7 +20,7 @@ CONFIG_SCHEMA = sensor.sensor_schema(
 | 
			
		||||
    state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
 | 
			
		||||
        cv.Required(CONF_TYPE): cv.one_of(*TYPES, lower=True),
 | 
			
		||||
        cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
@@ -35,5 +29,5 @@ CONFIG_SCHEMA = sensor.sensor_schema(
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    hub = await cg.get_variable(config[CONF_APDS9960_ID])
 | 
			
		||||
    var = await sensor.new_sensor(config)
 | 
			
		||||
    func = getattr(hub, TYPES[config[CONF_TYPE]])
 | 
			
		||||
    func = getattr(hub, f"set_{config[CONF_TYPE]}_sensor")
 | 
			
		||||
    cg.add(func(var))
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,9 @@ service APIConnection {
 | 
			
		||||
  rpc bluetooth_gatt_write_descriptor(BluetoothGATTWriteDescriptorRequest) returns (void) {}
 | 
			
		||||
  rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {}
 | 
			
		||||
  rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {}
 | 
			
		||||
  rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
 | 
			
		||||
 | 
			
		||||
  rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -208,6 +211,8 @@ message DeviceInfoResponse {
 | 
			
		||||
  string manufacturer = 12;
 | 
			
		||||
 | 
			
		||||
  string friendly_name = 13;
 | 
			
		||||
 | 
			
		||||
  uint32 voice_assistant_version = 14;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ListEntitiesRequest {
 | 
			
		||||
@@ -283,6 +288,7 @@ message ListEntitiesCoverResponse {
 | 
			
		||||
  bool disabled_by_default = 9;
 | 
			
		||||
  string icon = 10;
 | 
			
		||||
  EntityCategory entity_category = 11;
 | 
			
		||||
  bool supports_stop = 12;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum LegacyCoverState {
 | 
			
		||||
@@ -856,8 +862,7 @@ message ClimateStateResponse {
 | 
			
		||||
  float target_temperature = 4;
 | 
			
		||||
  float target_temperature_low = 5;
 | 
			
		||||
  float target_temperature_high = 6;
 | 
			
		||||
  // For older peers, equal to preset == CLIMATE_PRESET_AWAY
 | 
			
		||||
  bool legacy_away = 7;
 | 
			
		||||
  bool unused_legacy_away = 7;
 | 
			
		||||
  ClimateAction action = 8;
 | 
			
		||||
  ClimateFanMode fan_mode = 9;
 | 
			
		||||
  ClimateSwingMode swing_mode = 10;
 | 
			
		||||
@@ -880,9 +885,8 @@ message ClimateCommandRequest {
 | 
			
		||||
  float target_temperature_low = 7;
 | 
			
		||||
  bool has_target_temperature_high = 8;
 | 
			
		||||
  float target_temperature_high = 9;
 | 
			
		||||
  // legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
 | 
			
		||||
  bool has_legacy_away = 10;
 | 
			
		||||
  bool legacy_away = 11;
 | 
			
		||||
  bool unused_has_legacy_away = 10;
 | 
			
		||||
  bool unused_legacy_away = 11;
 | 
			
		||||
  bool has_fan_mode = 12;
 | 
			
		||||
  ClimateFanMode fan_mode = 13;
 | 
			
		||||
  bool has_swing_mode = 14;
 | 
			
		||||
@@ -1125,6 +1129,7 @@ message MediaPlayerCommandRequest {
 | 
			
		||||
message SubscribeBluetoothLEAdvertisementsRequest {
 | 
			
		||||
  option (id) = 66;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothServiceData {
 | 
			
		||||
@@ -1361,6 +1366,12 @@ message BluetoothDeviceUnpairingResponse {
 | 
			
		||||
  int32 error = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message UnsubscribeBluetoothLEAdvertisementsRequest {
 | 
			
		||||
  option (id) = 87;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothDeviceClearCacheResponse {
 | 
			
		||||
  option (id) = 88;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
@@ -1370,3 +1381,55 @@ message BluetoothDeviceClearCacheResponse {
 | 
			
		||||
  bool success = 2;
 | 
			
		||||
  int32 error = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== PUSH TO TALK ====================
 | 
			
		||||
message SubscribeVoiceAssistantRequest {
 | 
			
		||||
  option (id) = 89;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
 | 
			
		||||
  bool subscribe = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantRequest {
 | 
			
		||||
  option (id) = 90;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
 | 
			
		||||
  bool start = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantResponse {
 | 
			
		||||
  option (id) = 91;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
 | 
			
		||||
  uint32 port = 1;
 | 
			
		||||
  bool error = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum VoiceAssistantEvent {
 | 
			
		||||
  VOICE_ASSISTANT_ERROR = 0;
 | 
			
		||||
  VOICE_ASSISTANT_RUN_START = 1;
 | 
			
		||||
  VOICE_ASSISTANT_RUN_END = 2;
 | 
			
		||||
  VOICE_ASSISTANT_STT_START = 3;
 | 
			
		||||
  VOICE_ASSISTANT_STT_END = 4;
 | 
			
		||||
  VOICE_ASSISTANT_INTENT_START = 5;
 | 
			
		||||
  VOICE_ASSISTANT_INTENT_END = 6;
 | 
			
		||||
  VOICE_ASSISTANT_TTS_START = 7;
 | 
			
		||||
  VOICE_ASSISTANT_TTS_END = 8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantEventData {
 | 
			
		||||
  string name = 1;
 | 
			
		||||
  string value = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantEventResponse {
 | 
			
		||||
  option (id) = 92;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
 | 
			
		||||
  VoiceAssistantEvent event_type = 1;
 | 
			
		||||
  repeated VoiceAssistantEventData data = 2;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
#include "esphome/core/entity_base.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
@@ -15,6 +16,9 @@
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#include "esphome/components/voice_assistant/voice_assistant.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
@@ -219,6 +223,7 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
 | 
			
		||||
  msg.assumed_state = traits.get_is_assumed_state();
 | 
			
		||||
  msg.supports_position = traits.get_supports_position();
 | 
			
		||||
  msg.supports_tilt = traits.get_supports_tilt();
 | 
			
		||||
  msg.supports_stop = traits.get_supports_stop();
 | 
			
		||||
  msg.device_class = cover->get_device_class();
 | 
			
		||||
  msg.disabled_by_default = cover->is_disabled_by_default();
 | 
			
		||||
  msg.icon = cover->get_icon();
 | 
			
		||||
@@ -526,7 +531,6 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
 | 
			
		||||
    resp.custom_fan_mode = climate->custom_fan_mode.value();
 | 
			
		||||
  if (traits.get_supports_presets() && climate->preset.has_value()) {
 | 
			
		||||
    resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
 | 
			
		||||
    resp.legacy_away = resp.preset == enums::CLIMATE_PRESET_AWAY;
 | 
			
		||||
  }
 | 
			
		||||
  if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
 | 
			
		||||
    resp.custom_preset = climate->custom_preset.value();
 | 
			
		||||
@@ -587,8 +591,6 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
 | 
			
		||||
    call.set_target_temperature_low(msg.target_temperature_low);
 | 
			
		||||
  if (msg.has_target_temperature_high)
 | 
			
		||||
    call.set_target_temperature_high(msg.target_temperature_high);
 | 
			
		||||
  if (msg.has_legacy_away)
 | 
			
		||||
    call.set_preset(msg.legacy_away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME);
 | 
			
		||||
  if (msg.has_fan_mode)
 | 
			
		||||
    call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
 | 
			
		||||
  if (msg.has_custom_fan_mode)
 | 
			
		||||
@@ -892,6 +894,30 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIConnection::request_voice_assistant(bool start) {
 | 
			
		||||
  if (!this->voice_assistant_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
  VoiceAssistantRequest msg;
 | 
			
		||||
  msg.start = start;
 | 
			
		||||
  return this->send_voice_assistant_request(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {
 | 
			
		||||
  if (voice_assistant::global_voice_assistant != nullptr) {
 | 
			
		||||
    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);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
 | 
			
		||||
  if (voice_assistant::global_voice_assistant != nullptr) {
 | 
			
		||||
    voice_assistant::global_voice_assistant->on_event(msg);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
 | 
			
		||||
  if (this->log_subscription_ < level)
 | 
			
		||||
    return false;
 | 
			
		||||
@@ -911,12 +937,12 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
 | 
			
		||||
  this->helper_->set_log_info(client_info_);
 | 
			
		||||
  this->client_api_version_major_ = msg.api_version_major;
 | 
			
		||||
  this->client_api_version_minor_ = msg.api_version_minor;
 | 
			
		||||
  ESP_LOGV(TAG, "Hello from client: '%s' | API Version %d.%d", this->client_info_.c_str(),
 | 
			
		||||
  ESP_LOGV(TAG, "Hello from client: '%s' | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
 | 
			
		||||
           this->client_api_version_major_, this->client_api_version_minor_);
 | 
			
		||||
 | 
			
		||||
  HelloResponse resp;
 | 
			
		||||
  resp.api_version_major = 1;
 | 
			
		||||
  resp.api_version_minor = 7;
 | 
			
		||||
  resp.api_version_minor = 8;
 | 
			
		||||
  resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
 | 
			
		||||
  resp.name = App.get_name();
 | 
			
		||||
 | 
			
		||||
@@ -953,6 +979,8 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
 | 
			
		||||
  resp.manufacturer = "Espressif";
 | 
			
		||||
#elif defined(USE_RP2040)
 | 
			
		||||
  resp.manufacturer = "Raspberry Pi";
 | 
			
		||||
#elif defined(USE_HOST)
 | 
			
		||||
  resp.manufacturer = "Host";
 | 
			
		||||
#endif
 | 
			
		||||
  resp.model = ESPHOME_BOARD;
 | 
			
		||||
#ifdef USE_DEEP_SLEEP
 | 
			
		||||
@@ -969,6 +997,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
 | 
			
		||||
  resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active()
 | 
			
		||||
                                     ? bluetooth_proxy::ACTIVE_CONNECTIONS_VERSION
 | 
			
		||||
                                     : bluetooth_proxy::PASSIVE_ONLY_VERSION;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  resp.voice_assistant_version = voice_assistant::global_voice_assistant->get_version();
 | 
			
		||||
#endif
 | 
			
		||||
  return resp;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -97,6 +98,12 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
    this->send_homeassistant_service_response(call);
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override {
 | 
			
		||||
    this->bluetooth_le_advertisement_subscription_ = true;
 | 
			
		||||
  }
 | 
			
		||||
  void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override {
 | 
			
		||||
    this->bluetooth_le_advertisement_subscription_ = false;
 | 
			
		||||
  }
 | 
			
		||||
  bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg);
 | 
			
		||||
 | 
			
		||||
  void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
 | 
			
		||||
@@ -117,6 +124,15 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override {
 | 
			
		||||
    this->voice_assistant_subscription_ = msg.subscribe;
 | 
			
		||||
  }
 | 
			
		||||
  bool request_voice_assistant(bool start);
 | 
			
		||||
  void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
 | 
			
		||||
  void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  void on_disconnect_response(const DisconnectResponse &value) override;
 | 
			
		||||
  void on_ping_response(const PingResponse &value) override {
 | 
			
		||||
    // we initiated ping
 | 
			
		||||
@@ -150,9 +166,7 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  void execute_service(const ExecuteServiceRequest &msg) override;
 | 
			
		||||
  void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override {
 | 
			
		||||
    this->bluetooth_le_advertisement_subscription_ = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; }
 | 
			
		||||
  bool is_connection_setup() override {
 | 
			
		||||
    return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated();
 | 
			
		||||
@@ -197,7 +211,12 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  uint32_t last_traffic_;
 | 
			
		||||
  bool sent_ping_{false};
 | 
			
		||||
  bool service_call_subscription_{false};
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool bluetooth_le_advertisement_subscription_{false};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool voice_assistant_subscription_{false};
 | 
			
		||||
#endif
 | 
			
		||||
  bool next_close_ = false;
 | 
			
		||||
  APIServer *parent_;
 | 
			
		||||
  InitialStateIterator initial_state_iterator_;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,8 +10,8 @@
 | 
			
		||||
#include "noise/protocol.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/socket/socket.h"
 | 
			
		||||
#include "api_noise_context.h"
 | 
			
		||||
#include "esphome/components/socket/socket.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
@@ -67,6 +67,7 @@ class APIFrameHelper {
 | 
			
		||||
  virtual bool can_write_without_blocking() = 0;
 | 
			
		||||
  virtual APIError write_packet(uint16_t type, const uint8_t *data, size_t len) = 0;
 | 
			
		||||
  virtual std::string getpeername() = 0;
 | 
			
		||||
  virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen) = 0;
 | 
			
		||||
  virtual APIError close() = 0;
 | 
			
		||||
  virtual APIError shutdown(int how) = 0;
 | 
			
		||||
  // Give this helper a name for logging
 | 
			
		||||
@@ -84,7 +85,10 @@ class APINoiseFrameHelper : public APIFrameHelper {
 | 
			
		||||
  APIError read_packet(ReadPacketBuffer *buffer) override;
 | 
			
		||||
  bool can_write_without_blocking() override;
 | 
			
		||||
  APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
 | 
			
		||||
  std::string getpeername() override { return socket_->getpeername(); }
 | 
			
		||||
  std::string getpeername() override { return this->socket_->getpeername(); }
 | 
			
		||||
  int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
 | 
			
		||||
    return this->socket_->getpeername(addr, addrlen);
 | 
			
		||||
  }
 | 
			
		||||
  APIError close() override;
 | 
			
		||||
  APIError shutdown(int how) override;
 | 
			
		||||
  // Give this helper a name for logging
 | 
			
		||||
@@ -144,7 +148,10 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
 | 
			
		||||
  APIError read_packet(ReadPacketBuffer *buffer) override;
 | 
			
		||||
  bool can_write_without_blocking() override;
 | 
			
		||||
  APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
 | 
			
		||||
  std::string getpeername() override { return socket_->getpeername(); }
 | 
			
		||||
  std::string getpeername() override { return this->socket_->getpeername(); }
 | 
			
		||||
  int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
 | 
			
		||||
    return this->socket_->getpeername(addr, addrlen);
 | 
			
		||||
  }
 | 
			
		||||
  APIError close() override;
 | 
			
		||||
  APIError shutdown(int how) override;
 | 
			
		||||
  // Give this helper a name for logging
 | 
			
		||||
 
 | 
			
		||||
@@ -407,6 +407,32 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#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:
 | 
			
		||||
      return "VOICE_ASSISTANT_ERROR";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_RUN_START:
 | 
			
		||||
      return "VOICE_ASSISTANT_RUN_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_RUN_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_RUN_END";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_STT_START:
 | 
			
		||||
      return "VOICE_ASSISTANT_STT_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_STT_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_STT_END";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_INTENT_START:
 | 
			
		||||
      return "VOICE_ASSISTANT_INTENT_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_INTENT_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_INTENT_END";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_TTS_START:
 | 
			
		||||
      return "VOICE_ASSISTANT_TTS_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_TTS_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_TTS_END";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
@@ -594,6 +620,10 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
      this->bluetooth_proxy_version = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 14: {
 | 
			
		||||
      this->voice_assistant_version = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -654,6 +684,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_uint32(11, this->bluetooth_proxy_version);
 | 
			
		||||
  buffer.encode_string(12, this->manufacturer);
 | 
			
		||||
  buffer.encode_string(13, this->friendly_name);
 | 
			
		||||
  buffer.encode_uint32(14, this->voice_assistant_version);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
			
		||||
@@ -712,6 +743,11 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  friendly_name: ");
 | 
			
		||||
  out.append("'").append(this->friendly_name).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  voice_assistant_version: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->voice_assistant_version);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -905,6 +941,10 @@ bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt val
 | 
			
		||||
      this->entity_category = value.as_enum<enums::EntityCategory>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 12: {
 | 
			
		||||
      this->supports_stop = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -957,6 +997,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_bool(9, this->disabled_by_default);
 | 
			
		||||
  buffer.encode_string(10, this->icon);
 | 
			
		||||
  buffer.encode_enum<enums::EntityCategory>(11, this->entity_category);
 | 
			
		||||
  buffer.encode_bool(12, this->supports_stop);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
 | 
			
		||||
@@ -1006,6 +1047,10 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  entity_category: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  supports_stop: ");
 | 
			
		||||
  out.append(YESNO(this->supports_stop));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -3613,7 +3658,7 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 7: {
 | 
			
		||||
      this->legacy_away = value.as_bool();
 | 
			
		||||
      this->unused_legacy_away = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 8: {
 | 
			
		||||
@@ -3683,7 +3728,7 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_float(4, this->target_temperature);
 | 
			
		||||
  buffer.encode_float(5, this->target_temperature_low);
 | 
			
		||||
  buffer.encode_float(6, this->target_temperature_high);
 | 
			
		||||
  buffer.encode_bool(7, this->legacy_away);
 | 
			
		||||
  buffer.encode_bool(7, this->unused_legacy_away);
 | 
			
		||||
  buffer.encode_enum<enums::ClimateAction>(8, this->action);
 | 
			
		||||
  buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode);
 | 
			
		||||
  buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode);
 | 
			
		||||
@@ -3724,8 +3769,8 @@ void ClimateStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  legacy_away: ");
 | 
			
		||||
  out.append(YESNO(this->legacy_away));
 | 
			
		||||
  out.append("  unused_legacy_away: ");
 | 
			
		||||
  out.append(YESNO(this->unused_legacy_away));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  action: ");
 | 
			
		||||
@@ -3777,11 +3822,11 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 10: {
 | 
			
		||||
      this->has_legacy_away = value.as_bool();
 | 
			
		||||
      this->unused_has_legacy_away = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 11: {
 | 
			
		||||
      this->legacy_away = value.as_bool();
 | 
			
		||||
      this->unused_legacy_away = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 12: {
 | 
			
		||||
@@ -3866,8 +3911,8 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_float(7, this->target_temperature_low);
 | 
			
		||||
  buffer.encode_bool(8, this->has_target_temperature_high);
 | 
			
		||||
  buffer.encode_float(9, this->target_temperature_high);
 | 
			
		||||
  buffer.encode_bool(10, this->has_legacy_away);
 | 
			
		||||
  buffer.encode_bool(11, this->legacy_away);
 | 
			
		||||
  buffer.encode_bool(10, this->unused_has_legacy_away);
 | 
			
		||||
  buffer.encode_bool(11, this->unused_legacy_away);
 | 
			
		||||
  buffer.encode_bool(12, this->has_fan_mode);
 | 
			
		||||
  buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode);
 | 
			
		||||
  buffer.encode_bool(14, this->has_swing_mode);
 | 
			
		||||
@@ -3923,12 +3968,12 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  has_legacy_away: ");
 | 
			
		||||
  out.append(YESNO(this->has_legacy_away));
 | 
			
		||||
  out.append("  unused_has_legacy_away: ");
 | 
			
		||||
  out.append(YESNO(this->unused_has_legacy_away));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  legacy_away: ");
 | 
			
		||||
  out.append(YESNO(this->legacy_away));
 | 
			
		||||
  out.append("  unused_legacy_away: ");
 | 
			
		||||
  out.append(YESNO(this->unused_legacy_away));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  has_fan_mode: ");
 | 
			
		||||
@@ -6062,6 +6107,12 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
void UnsubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool BluetoothDeviceClearCacheResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
@@ -6105,6 +6156,155 @@ void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->subscribe = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->subscribe); }
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("SubscribeVoiceAssistantRequest {\n");
 | 
			
		||||
  out.append("  subscribe: ");
 | 
			
		||||
  out.append(YESNO(this->subscribe));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->start = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->start); }
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantRequest::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantRequest {\n");
 | 
			
		||||
  out.append("  start: ");
 | 
			
		||||
  out.append(YESNO(this->start));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->port = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->error = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_uint32(1, this->port);
 | 
			
		||||
  buffer.encode_bool(2, this->error);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantResponse {\n");
 | 
			
		||||
  out.append("  port: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->port);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  error: ");
 | 
			
		||||
  out.append(YESNO(this->error));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantEventData::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->name = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->value = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantEventData::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_string(1, this->name);
 | 
			
		||||
  buffer.encode_string(2, this->value);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantEventData::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantEventData {\n");
 | 
			
		||||
  out.append("  name: ");
 | 
			
		||||
  out.append("'").append(this->name).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  value: ");
 | 
			
		||||
  out.append("'").append(this->value).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->event_type = value.as_enum<enums::VoiceAssistantEvent>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool VoiceAssistantEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->data.push_back(value.as_message<VoiceAssistantEventData>());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantEventResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_enum<enums::VoiceAssistantEvent>(1, this->event_type);
 | 
			
		||||
  for (auto &it : this->data) {
 | 
			
		||||
    buffer.encode_message<VoiceAssistantEventData>(2, it, true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantEventResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantEventResponse {\n");
 | 
			
		||||
  out.append("  event_type: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::VoiceAssistantEvent>(this->event_type));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  for (const auto &it : this->data) {
 | 
			
		||||
    out.append("  data: ");
 | 
			
		||||
    it.dump_to(out);
 | 
			
		||||
    out.append("\n");
 | 
			
		||||
  }
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -165,6 +165,17 @@ enum BluetoothDeviceRequestType : uint32_t {
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
 | 
			
		||||
};
 | 
			
		||||
enum VoiceAssistantEvent : uint32_t {
 | 
			
		||||
  VOICE_ASSISTANT_ERROR = 0,
 | 
			
		||||
  VOICE_ASSISTANT_RUN_START = 1,
 | 
			
		||||
  VOICE_ASSISTANT_RUN_END = 2,
 | 
			
		||||
  VOICE_ASSISTANT_STT_START = 3,
 | 
			
		||||
  VOICE_ASSISTANT_STT_END = 4,
 | 
			
		||||
  VOICE_ASSISTANT_INTENT_START = 5,
 | 
			
		||||
  VOICE_ASSISTANT_INTENT_END = 6,
 | 
			
		||||
  VOICE_ASSISTANT_TTS_START = 7,
 | 
			
		||||
  VOICE_ASSISTANT_TTS_END = 8,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace enums
 | 
			
		||||
 | 
			
		||||
@@ -279,6 +290,7 @@ class DeviceInfoResponse : public ProtoMessage {
 | 
			
		||||
  uint32_t bluetooth_proxy_version{0};
 | 
			
		||||
  std::string manufacturer{};
 | 
			
		||||
  std::string friendly_name{};
 | 
			
		||||
  uint32_t voice_assistant_version{0};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
@@ -363,6 +375,7 @@ class ListEntitiesCoverResponse : public ProtoMessage {
 | 
			
		||||
  bool disabled_by_default{false};
 | 
			
		||||
  std::string icon{};
 | 
			
		||||
  enums::EntityCategory entity_category{};
 | 
			
		||||
  bool supports_stop{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
@@ -946,7 +959,7 @@ class ClimateStateResponse : public ProtoMessage {
 | 
			
		||||
  float target_temperature{0.0f};
 | 
			
		||||
  float target_temperature_low{0.0f};
 | 
			
		||||
  float target_temperature_high{0.0f};
 | 
			
		||||
  bool legacy_away{false};
 | 
			
		||||
  bool unused_legacy_away{false};
 | 
			
		||||
  enums::ClimateAction action{};
 | 
			
		||||
  enums::ClimateFanMode fan_mode{};
 | 
			
		||||
  enums::ClimateSwingMode swing_mode{};
 | 
			
		||||
@@ -974,8 +987,8 @@ class ClimateCommandRequest : public ProtoMessage {
 | 
			
		||||
  float target_temperature_low{0.0f};
 | 
			
		||||
  bool has_target_temperature_high{false};
 | 
			
		||||
  float target_temperature_high{0.0f};
 | 
			
		||||
  bool has_legacy_away{false};
 | 
			
		||||
  bool legacy_away{false};
 | 
			
		||||
  bool unused_has_legacy_away{false};
 | 
			
		||||
  bool unused_legacy_away{false};
 | 
			
		||||
  bool has_fan_mode{false};
 | 
			
		||||
  enums::ClimateFanMode fan_mode{};
 | 
			
		||||
  bool has_swing_mode{false};
 | 
			
		||||
@@ -1555,6 +1568,15 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage {
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class BluetoothDeviceClearCacheResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
@@ -1568,6 +1590,65 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage {
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class SubscribeVoiceAssistantRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool subscribe{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool start{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t port{0};
 | 
			
		||||
  bool error{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantEventData : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string value{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantEventResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  enums::VoiceAssistantEvent event_type{};
 | 
			
		||||
  std::vector<VoiceAssistantEventData> data{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -329,6 +329,8 @@ bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayer
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str());
 | 
			
		||||
@@ -442,6 +444,8 @@ bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const Blu
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str());
 | 
			
		||||
@@ -449,6 +453,20 @@ bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const B
 | 
			
		||||
  return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantRequest &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_voice_assistant_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<VoiceAssistantRequest>(msg, 90);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
 | 
			
		||||
  switch (msg_type) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
@@ -717,12 +735,14 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 66: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      SubscribeBluetoothLEAdvertisementsRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_subscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_subscribe_bluetooth_le_advertisements_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 68: {
 | 
			
		||||
@@ -810,6 +830,50 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
			
		||||
      ESP_LOGVV(TAG, "on_subscribe_bluetooth_connections_free_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_subscribe_bluetooth_connections_free_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 87: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      UnsubscribeBluetoothLEAdvertisementsRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_unsubscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_unsubscribe_bluetooth_le_advertisements_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 89: {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
      SubscribeVoiceAssistantRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_subscribe_voice_assistant_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_subscribe_voice_assistant_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 91: {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
      VoiceAssistantResponse msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_voice_assistant_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_voice_assistant_response(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 92: {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
      VoiceAssistantEventResponse msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_voice_assistant_event_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_voice_assistant_event_response(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1073,6 +1137,7 @@ void APIServerConnection::on_media_player_command_request(const MediaPlayerComma
 | 
			
		||||
  this->media_player_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
    const SubscribeBluetoothLEAdvertisementsRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
@@ -1085,6 +1150,7 @@ void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
  }
 | 
			
		||||
  this->subscribe_bluetooth_le_advertisements(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
@@ -1193,6 +1259,33 @@ void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
    const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->unsubscribe_bluetooth_le_advertisements(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->subscribe_voice_assistant(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -154,8 +154,10 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_subscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
      const SubscribeBluetoothLEAdvertisementsRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -216,8 +218,24 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_unsubscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
      const UnsubscribeBluetoothLEAdvertisementsRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool send_voice_assistant_request(const VoiceAssistantRequest &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
 | 
			
		||||
@@ -270,7 +288,9 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void bluetooth_device_request(const BluetoothDeviceRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -295,6 +315,12 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
 | 
			
		||||
      const SubscribeBluetoothConnectionsFreeRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  void on_hello_request(const HelloRequest &msg) override;
 | 
			
		||||
@@ -342,7 +368,9 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -367,6 +395,13 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_unsubscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
      const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
 
 | 
			
		||||
@@ -427,5 +427,21 @@ void APIServer::on_shutdown() {
 | 
			
		||||
  delay(10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIServer::start_voice_assistant() {
 | 
			
		||||
  for (auto &c : this->clients_) {
 | 
			
		||||
    if (c->request_voice_assistant(true))
 | 
			
		||||
      return true;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
void APIServer::stop_voice_assistant() {
 | 
			
		||||
  for (auto &c : this->clients_) {
 | 
			
		||||
    if (c->request_voice_assistant(false))
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -95,6 +95,11 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
  void request_time();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool start_voice_assistant();
 | 
			
		||||
  void stop_voice_assistant();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  bool is_connected() const;
 | 
			
		||||
 | 
			
		||||
  struct HomeAssistantStateSubscription {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "proto.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -13,7 +14,7 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
 | 
			
		||||
    uint32_t consumed;
 | 
			
		||||
    auto res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
 | 
			
		||||
    if (!res.has_value()) {
 | 
			
		||||
      ESP_LOGV(TAG, "Invalid field start at %u", i);
 | 
			
		||||
      ESP_LOGV(TAG, "Invalid field start at %" PRIu32, i);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -25,12 +26,12 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
 | 
			
		||||
      case 0: {  // VarInt
 | 
			
		||||
        res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
 | 
			
		||||
        if (!res.has_value()) {
 | 
			
		||||
          ESP_LOGV(TAG, "Invalid VarInt at %u", i);
 | 
			
		||||
          ESP_LOGV(TAG, "Invalid VarInt at %" PRIu32, i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this->decode_varint(field_id, *res)) {
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode VarInt field %u with value %u!", field_id, res->as_uint32());
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu32 "!", field_id, res->as_uint32());
 | 
			
		||||
        }
 | 
			
		||||
        i += consumed;
 | 
			
		||||
        break;
 | 
			
		||||
@@ -38,38 +39,38 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
 | 
			
		||||
      case 2: {  // Length-delimited
 | 
			
		||||
        res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
 | 
			
		||||
        if (!res.has_value()) {
 | 
			
		||||
          ESP_LOGV(TAG, "Invalid Length Delimited at %u", i);
 | 
			
		||||
          ESP_LOGV(TAG, "Invalid Length Delimited at %" PRIu32, i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        uint32_t field_length = res->as_uint32();
 | 
			
		||||
        i += consumed;
 | 
			
		||||
        if (field_length > length - i) {
 | 
			
		||||
          ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %u", i);
 | 
			
		||||
          ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %" PRIu32, i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this->decode_length(field_id, ProtoLengthDelimited(&buffer[i], field_length))) {
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode Length Delimited field %u!", field_id);
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode Length Delimited field %" PRIu32 "!", field_id);
 | 
			
		||||
        }
 | 
			
		||||
        i += field_length;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 5: {  // 32-bit
 | 
			
		||||
        if (length - i < 4) {
 | 
			
		||||
          ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %u", i);
 | 
			
		||||
          ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %" PRIu32, i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]);
 | 
			
		||||
        if (!this->decode_32bit(field_id, Proto32Bit(val))) {
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val);
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode 32-bit field %" PRIu32 " with value %" PRIu32 "!", field_id, val);
 | 
			
		||||
        }
 | 
			
		||||
        i += 4;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      default:
 | 
			
		||||
        ESP_LOGV(TAG, "Invalid field type at %u", i);
 | 
			
		||||
        ESP_LOGV(TAG, "Invalid field type at %" PRIu32, i);
 | 
			
		||||
        error = true;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_CAPACITANCE,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["sensor", "binary_sensor"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
CONF_AS3935_ID = "as3935_id"
 | 
			
		||||
 
 | 
			
		||||
@@ -26,9 +26,13 @@ void AS3935Component::setup() {
 | 
			
		||||
void AS3935Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "AS3935:");
 | 
			
		||||
  LOG_PIN("  Interrupt Pin: ", this->irq_pin_);
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  LOG_BINARY_SENSOR("  ", "Thunder alert", this->thunder_alert_binary_sensor_);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  LOG_SENSOR("  ", "Distance", this->distance_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Lightning energy", this->energy_sensor_);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
@@ -44,16 +48,22 @@ void AS3935Component::loop() {
 | 
			
		||||
    ESP_LOGI(TAG, "Disturber was detected - try increasing the spike rejection value!");
 | 
			
		||||
  } else if (int_value == LIGHTNING_INT) {
 | 
			
		||||
    ESP_LOGI(TAG, "Lightning has been detected!");
 | 
			
		||||
    if (this->thunder_alert_binary_sensor_ != nullptr)
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
    if (this->thunder_alert_binary_sensor_ != nullptr) {
 | 
			
		||||
      this->thunder_alert_binary_sensor_->publish_state(true);
 | 
			
		||||
      this->set_timeout(10, [this]() { this->thunder_alert_binary_sensor_->publish_state(false); });
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
    uint8_t distance = this->get_distance_to_storm_();
 | 
			
		||||
    if (this->distance_sensor_ != nullptr)
 | 
			
		||||
      this->distance_sensor_->publish_state(distance);
 | 
			
		||||
 | 
			
		||||
    uint32_t energy = this->get_lightning_energy_();
 | 
			
		||||
    if (this->energy_sensor_ != nullptr)
 | 
			
		||||
      this->energy_sensor_->publish_state(energy);
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
  this->thunder_alert_binary_sensor_->publish_state(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AS3935Component::write_indoor(bool indoor) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,14 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace as3935 {
 | 
			
		||||
@@ -52,6 +57,15 @@ enum AS3935Values {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AS3935Component : public Component {
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  SUB_SENSOR(distance)
 | 
			
		||||
  SUB_SENSOR(energy)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  SUB_BINARY_SENSOR(thunder_alert)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
@@ -59,11 +73,7 @@ class AS3935Component : public Component {
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void set_irq_pin(GPIOPin *irq_pin) { irq_pin_ = irq_pin; }
 | 
			
		||||
  void set_distance_sensor(sensor::Sensor *distance_sensor) { distance_sensor_ = distance_sensor; }
 | 
			
		||||
  void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
 | 
			
		||||
  void set_thunder_alert_binary_sensor(binary_sensor::BinarySensor *thunder_alert_binary_sensor) {
 | 
			
		||||
    thunder_alert_binary_sensor_ = thunder_alert_binary_sensor;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_indoor(bool indoor) { indoor_ = indoor; }
 | 
			
		||||
  void write_indoor(bool indoor);
 | 
			
		||||
  void set_noise_level(uint8_t noise_level) { noise_level_ = noise_level; }
 | 
			
		||||
@@ -92,9 +102,6 @@ class AS3935Component : public Component {
 | 
			
		||||
 | 
			
		||||
  virtual void write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position) = 0;
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *distance_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *energy_sensor_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *thunder_alert_binary_sensor_{nullptr};
 | 
			
		||||
  GPIOPin *irq_pin_;
 | 
			
		||||
 | 
			
		||||
  bool indoor_;
 | 
			
		||||
 
 | 
			
		||||
@@ -18,5 +18,5 @@ async def to_code(config):
 | 
			
		||||
        # https://github.com/esphome/AsyncTCP/blob/master/library.json
 | 
			
		||||
        cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
 | 
			
		||||
    elif CORE.is_esp8266:
 | 
			
		||||
        # https://github.com/OttoWinter/ESPAsyncTCP
 | 
			
		||||
        cg.add_library("ottowinter/ESPAsyncTCP-esphome", "1.2.3")
 | 
			
		||||
        # https://github.com/esphome/ESPAsyncTCP
 | 
			
		||||
        cg.add_library("esphome/ESPAsyncTCP-esphome", "1.2.3")
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,7 @@ class BedJetHub : public esphome::ble_client::BLEClientNode, public PollingCompo
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void setup() override { this->codec_ = make_unique<BedjetCodec>(); }
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::BLUETOOTH; }
 | 
			
		||||
 | 
			
		||||
  /** @return The BedJet's configured name, or the MAC address if not discovered yet. */
 | 
			
		||||
  std::string get_name() {
 | 
			
		||||
 
 | 
			
		||||
@@ -43,12 +43,7 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BinarySensor::BinarySensor() : state(false) {}
 | 
			
		||||
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
 | 
			
		||||
std::string BinarySensor::get_device_class() {
 | 
			
		||||
  if (this->device_class_.has_value())
 | 
			
		||||
    return *this->device_class_;
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BinarySensor::add_filter(Filter *filter) {
 | 
			
		||||
  filter->parent_ = this;
 | 
			
		||||
  if (this->filter_list_ == nullptr) {
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ namespace binary_sensor {
 | 
			
		||||
 * The sub classes should notify the front-end of new states via the publish_state() method which
 | 
			
		||||
 * handles inverted inputs for you.
 | 
			
		||||
 */
 | 
			
		||||
class BinarySensor : public EntityBase {
 | 
			
		||||
class BinarySensor : public EntityBase, public EntityBase_DeviceClass {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit BinarySensor();
 | 
			
		||||
 | 
			
		||||
@@ -60,12 +60,6 @@ class BinarySensor : public EntityBase {
 | 
			
		||||
  /// The current reported state of the binary sensor.
 | 
			
		||||
  bool state;
 | 
			
		||||
 | 
			
		||||
  /// Manually set the Home Assistant device class (see binary_sensor::device_class)
 | 
			
		||||
  void set_device_class(const std::string &device_class);
 | 
			
		||||
 | 
			
		||||
  /// Get the device class for this binary sensor, using the manual override if specified.
 | 
			
		||||
  std::string get_device_class();
 | 
			
		||||
 | 
			
		||||
  void add_filter(Filter *filter);
 | 
			
		||||
  void add_filters(const std::vector<Filter *> &filters);
 | 
			
		||||
 | 
			
		||||
@@ -82,7 +76,6 @@ class BinarySensor : public EntityBase {
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  CallbackManager<void(bool)> state_callback_{};
 | 
			
		||||
  optional<std::string> device_class_{};  ///< Stores the override of the device class
 | 
			
		||||
  Filter *filter_list_{nullptr};
 | 
			
		||||
  bool has_state_{false};
 | 
			
		||||
  bool publish_initial_state_{false};
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,9 @@ void BinarySensorMap::loop() {
 | 
			
		||||
    case BINARY_SENSOR_MAP_TYPE_SUM:
 | 
			
		||||
      this->process_sum_();
 | 
			
		||||
      break;
 | 
			
		||||
    case BINARY_SENSOR_MAP_TYPE_BAYESIAN:
 | 
			
		||||
      this->process_bayesian_();
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -23,69 +26,117 @@ void BinarySensorMap::process_group_() {
 | 
			
		||||
  float total_current_value = 0.0;
 | 
			
		||||
  uint8_t num_active_sensors = 0;
 | 
			
		||||
  uint64_t mask = 0x00;
 | 
			
		||||
  // check all binary_sensors for its state. when active add its value to total_current_value.
 | 
			
		||||
  // create a bitmask for the binary_sensor status on all channels
 | 
			
		||||
 | 
			
		||||
  // - check all binary_sensors for its state
 | 
			
		||||
  //  - if active, add its value to total_current_value.
 | 
			
		||||
  // - creates a bitmask for the binary_sensor states on all channels
 | 
			
		||||
  for (size_t i = 0; i < this->channels_.size(); i++) {
 | 
			
		||||
    auto bs = this->channels_[i];
 | 
			
		||||
    if (bs.binary_sensor->state) {
 | 
			
		||||
      num_active_sensors++;
 | 
			
		||||
      total_current_value += bs.sensor_value;
 | 
			
		||||
      mask |= 1 << i;
 | 
			
		||||
      total_current_value += bs.parameters.sensor_value;
 | 
			
		||||
      mask |= 1ULL << i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // check if the sensor map was touched
 | 
			
		||||
 | 
			
		||||
  // potentially update state only if a binary_sensor is active
 | 
			
		||||
  if (mask != 0ULL) {
 | 
			
		||||
    // did the bit_mask change or is it a new sensor touch
 | 
			
		||||
    // publish the average if the bitmask has changed
 | 
			
		||||
    if (this->last_mask_ != mask) {
 | 
			
		||||
      float publish_value = total_current_value / num_active_sensors;
 | 
			
		||||
      ESP_LOGD(TAG, "'%s' - Publishing %.2f", this->name_.c_str(), publish_value);
 | 
			
		||||
      this->publish_state(publish_value);
 | 
			
		||||
    }
 | 
			
		||||
  } else if (this->last_mask_ != 0ULL) {
 | 
			
		||||
    // is this a new sensor release
 | 
			
		||||
    ESP_LOGD(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str());
 | 
			
		||||
    // no buttons are pressed and the states have changed since last run, so publish NAN
 | 
			
		||||
    ESP_LOGV(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str());
 | 
			
		||||
    this->publish_state(NAN);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->last_mask_ = mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BinarySensorMap::process_sum_() {
 | 
			
		||||
  float total_current_value = 0.0;
 | 
			
		||||
  uint64_t mask = 0x00;
 | 
			
		||||
  // check all binary_sensors for its state. when active add its value to total_current_value.
 | 
			
		||||
  // create a bitmask for the binary_sensor status on all channels
 | 
			
		||||
 | 
			
		||||
  // - check all binary_sensor states
 | 
			
		||||
  // - if active, add its value to total_current_value
 | 
			
		||||
  // - creates a bitmask for the binary_sensor states on all channels
 | 
			
		||||
  for (size_t i = 0; i < this->channels_.size(); i++) {
 | 
			
		||||
    auto bs = this->channels_[i];
 | 
			
		||||
    if (bs.binary_sensor->state) {
 | 
			
		||||
      total_current_value += bs.sensor_value;
 | 
			
		||||
      mask |= 1 << i;
 | 
			
		||||
      total_current_value += bs.parameters.sensor_value;
 | 
			
		||||
      mask |= 1ULL << i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // check if the sensor map was touched
 | 
			
		||||
  if (mask != 0ULL) {
 | 
			
		||||
    // did the bit_mask change or is it a new sensor touch
 | 
			
		||||
    if (this->last_mask_ != mask) {
 | 
			
		||||
      float publish_value = total_current_value;
 | 
			
		||||
      ESP_LOGD(TAG, "'%s' - Publishing %.2f", this->name_.c_str(), publish_value);
 | 
			
		||||
      this->publish_state(publish_value);
 | 
			
		||||
    }
 | 
			
		||||
  } else if (this->last_mask_ != 0ULL) {
 | 
			
		||||
    // is this a new sensor release
 | 
			
		||||
    ESP_LOGD(TAG, "'%s' - No binary sensor active, publishing 0", this->name_.c_str());
 | 
			
		||||
    this->publish_state(0.0);
 | 
			
		||||
 | 
			
		||||
  // update state only if any binary_sensor states have changed or if no state has ever been sent on boot
 | 
			
		||||
  if ((this->last_mask_ != mask) || (!this->has_state())) {
 | 
			
		||||
    this->publish_state(total_current_value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->last_mask_ = mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BinarySensorMap::process_bayesian_() {
 | 
			
		||||
  float posterior_probability = this->bayesian_prior_;
 | 
			
		||||
  uint64_t mask = 0x00;
 | 
			
		||||
 | 
			
		||||
  // - compute the posterior probability by taking the product of the predicate probablities for each observation
 | 
			
		||||
  // - create a bitmask for the binary_sensor states on all channels/observations
 | 
			
		||||
  for (size_t i = 0; i < this->channels_.size(); i++) {
 | 
			
		||||
    auto bs = this->channels_[i];
 | 
			
		||||
 | 
			
		||||
    posterior_probability *=
 | 
			
		||||
        this->bayesian_predicate_(bs.binary_sensor->state, posterior_probability,
 | 
			
		||||
                                  bs.parameters.probabilities.given_true, bs.parameters.probabilities.given_false);
 | 
			
		||||
 | 
			
		||||
    mask |= ((uint64_t) (bs.binary_sensor->state)) << i;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // update state only if any binary_sensor states have changed or if no state has ever been sent on boot
 | 
			
		||||
  if ((this->last_mask_ != mask) || (!this->has_state())) {
 | 
			
		||||
    this->publish_state(posterior_probability);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->last_mask_ = mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float BinarySensorMap::bayesian_predicate_(bool sensor_state, float prior, float prob_given_true,
 | 
			
		||||
                                           float prob_given_false) {
 | 
			
		||||
  float prob_state_source_true = prob_given_true;
 | 
			
		||||
  float prob_state_source_false = prob_given_false;
 | 
			
		||||
 | 
			
		||||
  // if sensor is off, then we use the probabilities for the observation's complement
 | 
			
		||||
  if (!sensor_state) {
 | 
			
		||||
    prob_state_source_true = 1 - prob_given_true;
 | 
			
		||||
    prob_state_source_false = 1 - prob_given_false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return prob_state_source_true / (prior * prob_state_source_true + (1.0 - prior) * prob_state_source_false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BinarySensorMap::add_channel(binary_sensor::BinarySensor *sensor, float value) {
 | 
			
		||||
  BinarySensorMapChannel sensor_channel{
 | 
			
		||||
      .binary_sensor = sensor,
 | 
			
		||||
      .parameters{
 | 
			
		||||
          .sensor_value = value,
 | 
			
		||||
      },
 | 
			
		||||
  };
 | 
			
		||||
  this->channels_.push_back(sensor_channel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BinarySensorMap::set_sensor_type(BinarySensorMapType sensor_type) { this->sensor_type_ = sensor_type; }
 | 
			
		||||
 | 
			
		||||
void BinarySensorMap::add_channel(binary_sensor::BinarySensor *sensor, float prob_given_true, float prob_given_false) {
 | 
			
		||||
  BinarySensorMapChannel sensor_channel{
 | 
			
		||||
      .binary_sensor = sensor,
 | 
			
		||||
      .parameters{
 | 
			
		||||
          .probabilities{
 | 
			
		||||
              .given_true = prob_given_true,
 | 
			
		||||
              .given_false = prob_given_false,
 | 
			
		||||
          },
 | 
			
		||||
      },
 | 
			
		||||
  };
 | 
			
		||||
  this->channels_.push_back(sensor_channel);
 | 
			
		||||
}
 | 
			
		||||
}  // namespace binary_sensor_map
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -12,51 +12,88 @@ namespace binary_sensor_map {
 | 
			
		||||
enum BinarySensorMapType {
 | 
			
		||||
  BINARY_SENSOR_MAP_TYPE_GROUP,
 | 
			
		||||
  BINARY_SENSOR_MAP_TYPE_SUM,
 | 
			
		||||
  BINARY_SENSOR_MAP_TYPE_BAYESIAN,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct BinarySensorMapChannel {
 | 
			
		||||
  binary_sensor::BinarySensor *binary_sensor;
 | 
			
		||||
  union {
 | 
			
		||||
    float sensor_value;
 | 
			
		||||
    struct {
 | 
			
		||||
      float given_true;
 | 
			
		||||
      float given_false;
 | 
			
		||||
    } probabilities;
 | 
			
		||||
  } parameters;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** Class to group binary_sensors to one Sensor.
 | 
			
		||||
/** Class to map one or more binary_sensors to one Sensor.
 | 
			
		||||
 *
 | 
			
		||||
 * Each binary sensor represents a float value in the group.
 | 
			
		||||
 * Each binary sensor has configured parameters that each mapping type uses to compute the single numerical result
 | 
			
		||||
 */
 | 
			
		||||
class BinarySensorMap : public sensor::Sensor, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The loop checks all binary_sensor states
 | 
			
		||||
   * When the binary_sensor reports a true value for its state, then the float value it represents is added to the
 | 
			
		||||
   * total_current_value
 | 
			
		||||
   * The loop calls the configured type processing method
 | 
			
		||||
   *
 | 
			
		||||
   * Only when the total_current_value changed and at least one sensor reports an active state we publish the sensors
 | 
			
		||||
   * average value. When the value changed and no sensors ar active we publish NAN.
 | 
			
		||||
   * */
 | 
			
		||||
   * The processing method loops through all sensors and calculates the numerical result
 | 
			
		||||
   * The result is only published if a binary sensor state has changed or, for some types, on initial boot
 | 
			
		||||
   */
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
  /** Add binary_sensors to the group.
 | 
			
		||||
   * Each binary_sensor represents a float value when its state is true
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Add binary_sensors to the group when only one parameter is needed for the configured mapping type.
 | 
			
		||||
   *
 | 
			
		||||
   * @param *sensor The binary sensor.
 | 
			
		||||
   * @param value  The value this binary_sensor represents
 | 
			
		||||
   */
 | 
			
		||||
  void add_channel(binary_sensor::BinarySensor *sensor, float value);
 | 
			
		||||
  void set_sensor_type(BinarySensorMapType sensor_type);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Add binary_sensors to the group when two parameters are needed for the Bayesian mapping type.
 | 
			
		||||
   *
 | 
			
		||||
   * @param *sensor The binary sensor.
 | 
			
		||||
   * @param prob_given_true Probability this observation is on when the Bayesian event is true
 | 
			
		||||
   * @param prob_given_false Probability this observation is on when the Bayesian event is false
 | 
			
		||||
   */
 | 
			
		||||
  void add_channel(binary_sensor::BinarySensor *sensor, float prob_given_true, float prob_given_false);
 | 
			
		||||
 | 
			
		||||
  void set_sensor_type(BinarySensorMapType sensor_type) { this->sensor_type_ = sensor_type; }
 | 
			
		||||
 | 
			
		||||
  void set_bayesian_prior(float prior) { this->bayesian_prior_ = prior; };
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::vector<BinarySensorMapChannel> channels_{};
 | 
			
		||||
  BinarySensorMapType sensor_type_{BINARY_SENSOR_MAP_TYPE_GROUP};
 | 
			
		||||
  // this gives max 64 channels per binary_sensor_map
 | 
			
		||||
 | 
			
		||||
  // this allows a max of 64 channels/observations in order to keep track of binary_sensor states
 | 
			
		||||
  uint64_t last_mask_{0x00};
 | 
			
		||||
 | 
			
		||||
  // Bayesian event prior probability before taking into account any observations
 | 
			
		||||
  float bayesian_prior_{};
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * methods to process the types of binary_sensor_maps
 | 
			
		||||
   * GROUP: process_group_() just map to a value
 | 
			
		||||
   * Methods to process the binary_sensor_maps types
 | 
			
		||||
   *
 | 
			
		||||
   * GROUP: process_group_() averages all the values
 | 
			
		||||
   * ADD: process_add_() adds all the values
 | 
			
		||||
   * BAYESIAN: process_bayesian_() computes the predicate probability
 | 
			
		||||
   * */
 | 
			
		||||
  void process_group_();
 | 
			
		||||
  void process_sum_();
 | 
			
		||||
  void process_bayesian_();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Computes the Bayesian predicate for a specific observation
 | 
			
		||||
   * If the sensor state is false, then we use the parameters' probabilities for the observatiosn complement
 | 
			
		||||
   *
 | 
			
		||||
   * @param sensor_state  State of observation
 | 
			
		||||
   * @param prior Prior probability before accounting for this observation
 | 
			
		||||
   * @param prob_given_true Probability this observation is on when the Bayesian event is true
 | 
			
		||||
   * @param prob_given_false Probability this observation is on when the Bayesian event is false
 | 
			
		||||
   * */
 | 
			
		||||
  float bayesian_predicate_(bool sensor_state, float prior, float prob_given_true, float prob_given_false);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace binary_sensor_map
 | 
			
		||||
 
 | 
			
		||||
@@ -20,16 +20,29 @@ BinarySensorMap = binary_sensor_map_ns.class_(
 | 
			
		||||
)
 | 
			
		||||
SensorMapType = binary_sensor_map_ns.enum("SensorMapType")
 | 
			
		||||
 | 
			
		||||
CONF_BAYESIAN = "bayesian"
 | 
			
		||||
CONF_PRIOR = "prior"
 | 
			
		||||
CONF_PROB_GIVEN_TRUE = "prob_given_true"
 | 
			
		||||
CONF_PROB_GIVEN_FALSE = "prob_given_false"
 | 
			
		||||
CONF_OBSERVATIONS = "observations"
 | 
			
		||||
 | 
			
		||||
SENSOR_MAP_TYPES = {
 | 
			
		||||
    CONF_GROUP: SensorMapType.BINARY_SENSOR_MAP_TYPE_GROUP,
 | 
			
		||||
    CONF_SUM: SensorMapType.BINARY_SENSOR_MAP_TYPE_SUM,
 | 
			
		||||
    CONF_BAYESIAN: SensorMapType.BINARY_SENSOR_MAP_TYPE_BAYESIAN,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
entry = {
 | 
			
		||||
entry_one_parameter = {
 | 
			
		||||
    cv.Required(CONF_BINARY_SENSOR): cv.use_id(binary_sensor.BinarySensor),
 | 
			
		||||
    cv.Required(CONF_VALUE): cv.float_,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
entry_bayesian_parameters = {
 | 
			
		||||
    cv.Required(CONF_BINARY_SENSOR): cv.use_id(binary_sensor.BinarySensor),
 | 
			
		||||
    cv.Required(CONF_PROB_GIVEN_TRUE): cv.float_range(min=0, max=1),
 | 
			
		||||
    cv.Required(CONF_PROB_GIVEN_FALSE): cv.float_range(min=0, max=1),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.typed_schema(
 | 
			
		||||
    {
 | 
			
		||||
        CONF_GROUP: sensor.sensor_schema(
 | 
			
		||||
@@ -39,7 +52,7 @@ CONFIG_SCHEMA = cv.typed_schema(
 | 
			
		||||
        ).extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Required(CONF_CHANNELS): cv.All(
 | 
			
		||||
                    cv.ensure_list(entry), cv.Length(min=1)
 | 
			
		||||
                    cv.ensure_list(entry_one_parameter), cv.Length(min=1, max=64)
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
@@ -50,7 +63,18 @@ CONFIG_SCHEMA = cv.typed_schema(
 | 
			
		||||
        ).extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Required(CONF_CHANNELS): cv.All(
 | 
			
		||||
                    cv.ensure_list(entry), cv.Length(min=1)
 | 
			
		||||
                    cv.ensure_list(entry_one_parameter), cv.Length(min=1, max=64)
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        CONF_BAYESIAN: sensor.sensor_schema(
 | 
			
		||||
            BinarySensorMap,
 | 
			
		||||
            accuracy_decimals=2,
 | 
			
		||||
        ).extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Required(CONF_PRIOR): cv.float_range(min=0, max=1),
 | 
			
		||||
                cv.Required(CONF_OBSERVATIONS): cv.All(
 | 
			
		||||
                    cv.ensure_list(entry_bayesian_parameters), cv.Length(min=1, max=64)
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
@@ -66,6 +90,17 @@ async def to_code(config):
 | 
			
		||||
    constant = SENSOR_MAP_TYPES[config[CONF_TYPE]]
 | 
			
		||||
    cg.add(var.set_sensor_type(constant))
 | 
			
		||||
 | 
			
		||||
    if config[CONF_TYPE] == CONF_BAYESIAN:
 | 
			
		||||
        cg.add(var.set_bayesian_prior(config[CONF_PRIOR]))
 | 
			
		||||
 | 
			
		||||
        for obs in config[CONF_OBSERVATIONS]:
 | 
			
		||||
            input_var = await cg.get_variable(obs[CONF_BINARY_SENSOR])
 | 
			
		||||
            cg.add(
 | 
			
		||||
                var.add_channel(
 | 
			
		||||
                    input_var, obs[CONF_PROB_GIVEN_TRUE], obs[CONF_PROB_GIVEN_FALSE]
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
    else:
 | 
			
		||||
        for ch in config[CONF_CHANNELS]:
 | 
			
		||||
            input_var = await cg.get_variable(ch[CONF_BINARY_SENSOR])
 | 
			
		||||
            cg.add(var.add_channel(input_var, ch[CONF_VALUE]))
 | 
			
		||||
 
 | 
			
		||||
@@ -29,8 +29,35 @@ BLEClientConnectTrigger = ble_client_ns.class_(
 | 
			
		||||
BLEClientDisconnectTrigger = ble_client_ns.class_(
 | 
			
		||||
    "BLEClientDisconnectTrigger", automation.Trigger.template(BLEClientNodeConstRef)
 | 
			
		||||
)
 | 
			
		||||
BLEClientPasskeyRequestTrigger = ble_client_ns.class_(
 | 
			
		||||
    "BLEClientPasskeyRequestTrigger", automation.Trigger.template(BLEClientNodeConstRef)
 | 
			
		||||
)
 | 
			
		||||
BLEClientPasskeyNotificationTrigger = ble_client_ns.class_(
 | 
			
		||||
    "BLEClientPasskeyNotificationTrigger",
 | 
			
		||||
    automation.Trigger.template(BLEClientNodeConstRef, cg.uint32),
 | 
			
		||||
)
 | 
			
		||||
BLEClientNumericComparisonRequestTrigger = ble_client_ns.class_(
 | 
			
		||||
    "BLEClientNumericComparisonRequestTrigger",
 | 
			
		||||
    automation.Trigger.template(BLEClientNodeConstRef, cg.uint32),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Actions
 | 
			
		||||
BLEWriteAction = ble_client_ns.class_("BLEClientWriteAction", automation.Action)
 | 
			
		||||
BLEPasskeyReplyAction = ble_client_ns.class_(
 | 
			
		||||
    "BLEClientPasskeyReplyAction", automation.Action
 | 
			
		||||
)
 | 
			
		||||
BLENumericComparisonReplyAction = ble_client_ns.class_(
 | 
			
		||||
    "BLEClientNumericComparisonReplyAction", automation.Action
 | 
			
		||||
)
 | 
			
		||||
BLERemoveBondAction = ble_client_ns.class_(
 | 
			
		||||
    "BLEClientRemoveBondAction", automation.Action
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONF_PASSKEY = "passkey"
 | 
			
		||||
CONF_ACCEPT = "accept"
 | 
			
		||||
CONF_ON_PASSKEY_REQUEST = "on_passkey_request"
 | 
			
		||||
CONF_ON_PASSKEY_NOTIFICATION = "on_passkey_notification"
 | 
			
		||||
CONF_ON_NUMERIC_COMPARISON_REQUEST = "on_numeric_comparison_request"
 | 
			
		||||
 | 
			
		||||
# Espressif platformio framework is built with MAX_BLE_CONN to 3, so
 | 
			
		||||
# enforce this in yaml checks.
 | 
			
		||||
@@ -56,6 +83,29 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                    ),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_PASSKEY_REQUEST): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
			
		||||
                        BLEClientPasskeyRequestTrigger
 | 
			
		||||
                    ),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_PASSKEY_NOTIFICATION): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
			
		||||
                        BLEClientPasskeyNotificationTrigger
 | 
			
		||||
                    ),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(
 | 
			
		||||
                CONF_ON_NUMERIC_COMPARISON_REQUEST
 | 
			
		||||
            ): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
			
		||||
                        BLEClientNumericComparisonRequestTrigger
 | 
			
		||||
                    ),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
@@ -85,13 +135,34 @@ BLE_WRITE_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
BLE_NUMERIC_COMPARISON_REPLY_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_ID): cv.use_id(BLEClient),
 | 
			
		||||
        cv.Required(CONF_ACCEPT): cv.templatable(cv.boolean),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
BLE_PASSKEY_REPLY_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_ID): cv.use_id(BLEClient),
 | 
			
		||||
        cv.Required(CONF_PASSKEY): cv.templatable(cv.int_range(min=0, max=999999)),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BLE_REMOVE_BOND_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_ID): cv.use_id(BLEClient),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "ble_client.ble_write", BLEWriteAction, BLE_WRITE_ACTION_SCHEMA
 | 
			
		||||
)
 | 
			
		||||
async def ble_write_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, parent)
 | 
			
		||||
 | 
			
		||||
    value = config[CONF_VALUE]
 | 
			
		||||
    if cg.is_template(value):
 | 
			
		||||
@@ -137,6 +208,54 @@ async def ble_write_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "ble_client.numeric_comparison_reply",
 | 
			
		||||
    BLENumericComparisonReplyAction,
 | 
			
		||||
    BLE_NUMERIC_COMPARISON_REPLY_ACTION_SCHEMA,
 | 
			
		||||
)
 | 
			
		||||
async def numeric_comparison_reply_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, parent)
 | 
			
		||||
 | 
			
		||||
    accept = config[CONF_ACCEPT]
 | 
			
		||||
    if cg.is_template(accept):
 | 
			
		||||
        templ = await cg.templatable(accept, args, cg.bool_)
 | 
			
		||||
        cg.add(var.set_value_template(templ))
 | 
			
		||||
    else:
 | 
			
		||||
        cg.add(var.set_value_simple(accept))
 | 
			
		||||
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "ble_client.passkey_reply", BLEPasskeyReplyAction, BLE_PASSKEY_REPLY_ACTION_SCHEMA
 | 
			
		||||
)
 | 
			
		||||
async def passkey_reply_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, parent)
 | 
			
		||||
 | 
			
		||||
    passkey = config[CONF_PASSKEY]
 | 
			
		||||
    if cg.is_template(passkey):
 | 
			
		||||
        templ = await cg.templatable(passkey, args, cg.uint32)
 | 
			
		||||
        cg.add(var.set_value_template(templ))
 | 
			
		||||
    else:
 | 
			
		||||
        cg.add(var.set_value_simple(passkey))
 | 
			
		||||
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "ble_client.remove_bond",
 | 
			
		||||
    BLERemoveBondAction,
 | 
			
		||||
    BLE_REMOVE_BOND_ACTION_SCHEMA,
 | 
			
		||||
)
 | 
			
		||||
async def remove_bond_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, parent)
 | 
			
		||||
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
@@ -148,3 +267,12 @@ async def to_code(config):
 | 
			
		||||
    for conf in config.get(CONF_ON_DISCONNECT, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_PASSKEY_REQUEST, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_PASSKEY_NOTIFICATION, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [(cg.uint32, "passkey")], conf)
 | 
			
		||||
    for conf in config.get(CONF_ON_NUMERIC_COMPARISON_REQUEST, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [(cg.uint32, "passkey")], conf)
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,44 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BLEClientPasskeyRequestTrigger : public Trigger<>, public BLEClientNode {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit BLEClientPasskeyRequestTrigger(BLEClient *parent) { parent->register_ble_node(this); }
 | 
			
		||||
  void loop() override {}
 | 
			
		||||
  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override {
 | 
			
		||||
    if (event == ESP_GAP_BLE_PASSKEY_REQ_EVT &&
 | 
			
		||||
        memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) {
 | 
			
		||||
      this->trigger();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BLEClientPasskeyNotificationTrigger : public Trigger<uint32_t>, public BLEClientNode {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit BLEClientPasskeyNotificationTrigger(BLEClient *parent) { parent->register_ble_node(this); }
 | 
			
		||||
  void loop() override {}
 | 
			
		||||
  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override {
 | 
			
		||||
    if (event == ESP_GAP_BLE_PASSKEY_NOTIF_EVT &&
 | 
			
		||||
        memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) {
 | 
			
		||||
      uint32_t passkey = param->ble_security.key_notif.passkey;
 | 
			
		||||
      this->trigger(passkey);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BLEClientNumericComparisonRequestTrigger : public Trigger<uint32_t>, public BLEClientNode {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit BLEClientNumericComparisonRequestTrigger(BLEClient *parent) { parent->register_ble_node(this); }
 | 
			
		||||
  void loop() override {}
 | 
			
		||||
  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override {
 | 
			
		||||
    if (event == ESP_GAP_BLE_NC_REQ_EVT &&
 | 
			
		||||
        memcmp(param->ble_security.auth_cmpl.bd_addr, this->parent_->get_remote_bda(), 6) == 0) {
 | 
			
		||||
      uint32_t passkey = param->ble_security.key_notif.passkey;
 | 
			
		||||
      this->trigger(passkey);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BLEWriterClientNode : public BLEClientNode {
 | 
			
		||||
 public:
 | 
			
		||||
  BLEWriterClientNode(BLEClient *ble_client) {
 | 
			
		||||
@@ -94,6 +132,86 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
 | 
			
		||||
  std::function<std::vector<uint8_t>(Ts...)> value_template_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class BLEClientPasskeyReplyAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  BLEClientPasskeyReplyAction(BLEClient *ble_client) { parent_ = ble_client; }
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override {
 | 
			
		||||
    uint32_t passkey;
 | 
			
		||||
    if (has_simple_value_) {
 | 
			
		||||
      passkey = this->value_simple_;
 | 
			
		||||
    } else {
 | 
			
		||||
      passkey = this->value_template_(x...);
 | 
			
		||||
    }
 | 
			
		||||
    if (passkey > 999999)
 | 
			
		||||
      return;
 | 
			
		||||
    esp_bd_addr_t remote_bda;
 | 
			
		||||
    memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
 | 
			
		||||
    esp_ble_passkey_reply(remote_bda, true, passkey);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_value_template(std::function<uint32_t(Ts...)> func) {
 | 
			
		||||
    this->value_template_ = std::move(func);
 | 
			
		||||
    has_simple_value_ = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_value_simple(const uint32_t &value) {
 | 
			
		||||
    this->value_simple_ = value;
 | 
			
		||||
    has_simple_value_ = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  BLEClient *parent_{nullptr};
 | 
			
		||||
  bool has_simple_value_ = true;
 | 
			
		||||
  uint32_t value_simple_{0};
 | 
			
		||||
  std::function<uint32_t(Ts...)> value_template_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  BLEClientNumericComparisonReplyAction(BLEClient *ble_client) { parent_ = ble_client; }
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override {
 | 
			
		||||
    esp_bd_addr_t remote_bda;
 | 
			
		||||
    memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
 | 
			
		||||
    if (has_simple_value_) {
 | 
			
		||||
      esp_ble_confirm_reply(remote_bda, this->value_simple_);
 | 
			
		||||
    } else {
 | 
			
		||||
      esp_ble_confirm_reply(remote_bda, this->value_template_(x...));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_value_template(std::function<bool(Ts...)> func) {
 | 
			
		||||
    this->value_template_ = std::move(func);
 | 
			
		||||
    has_simple_value_ = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_value_simple(const bool &value) {
 | 
			
		||||
    this->value_simple_ = value;
 | 
			
		||||
    has_simple_value_ = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  BLEClient *parent_{nullptr};
 | 
			
		||||
  bool has_simple_value_ = true;
 | 
			
		||||
  bool value_simple_{false};
 | 
			
		||||
  std::function<bool(Ts...)> value_template_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class BLEClientRemoveBondAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  BLEClientRemoveBondAction(BLEClient *ble_client) { parent_ = ble_client; }
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override {
 | 
			
		||||
    esp_bd_addr_t remote_bda;
 | 
			
		||||
    memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
 | 
			
		||||
    esp_ble_remove_bond_device(remote_bda);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  BLEClient *parent_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ble_client
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ class BLEClient;
 | 
			
		||||
class BLEClientNode {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                                   esp_ble_gattc_cb_param_t *param) = 0;
 | 
			
		||||
                                   esp_ble_gattc_cb_param_t *param){};
 | 
			
		||||
  virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {}
 | 
			
		||||
  virtual void loop() {}
 | 
			
		||||
  void set_address(uint64_t address) { address_ = address; }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,6 @@ namespace button {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "button";
 | 
			
		||||
 | 
			
		||||
Button::Button(const std::string &name) : EntityBase(name) {}
 | 
			
		||||
Button::Button() : Button("") {}
 | 
			
		||||
 | 
			
		||||
void Button::press() {
 | 
			
		||||
  ESP_LOGD(TAG, "'%s' Pressed.", this->get_name().c_str());
 | 
			
		||||
  this->press_action();
 | 
			
		||||
@@ -16,8 +13,5 @@ void Button::press() {
 | 
			
		||||
}
 | 
			
		||||
void Button::add_on_press_callback(std::function<void()> &&callback) { this->press_callback_.add(std::move(callback)); }
 | 
			
		||||
 | 
			
		||||
void Button::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
 | 
			
		||||
std::string Button::get_device_class() { return this->device_class_; }
 | 
			
		||||
 | 
			
		||||
}  // namespace button
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -26,11 +26,8 @@ namespace button {
 | 
			
		||||
 *
 | 
			
		||||
 * A button is just a momentary switch that does not have a state, only a trigger.
 | 
			
		||||
 */
 | 
			
		||||
class Button : public EntityBase {
 | 
			
		||||
class Button : public EntityBase, public EntityBase_DeviceClass {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit Button();
 | 
			
		||||
  explicit Button(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /** Press this button. This is called by the front-end.
 | 
			
		||||
   *
 | 
			
		||||
   * For implementing buttons, please override press_action.
 | 
			
		||||
@@ -43,19 +40,12 @@ class Button : public EntityBase {
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_press_callback(std::function<void()> &&callback);
 | 
			
		||||
 | 
			
		||||
  /// Set the Home Assistant device class (see button::device_class).
 | 
			
		||||
  void set_device_class(const std::string &device_class);
 | 
			
		||||
 | 
			
		||||
  /// Get the device class for this button.
 | 
			
		||||
  std::string get_device_class();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  /** You should implement this virtual method if you want to create your own button.
 | 
			
		||||
   */
 | 
			
		||||
  virtual void press_action() = 0;
 | 
			
		||||
 | 
			
		||||
  CallbackManager<void()> press_callback_{};
 | 
			
		||||
  std::string device_class_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace button
 | 
			
		||||
 
 | 
			
		||||
@@ -343,7 +343,7 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
 | 
			
		||||
        cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature),
 | 
			
		||||
        cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
 | 
			
		||||
        cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature),
 | 
			
		||||
        cv.Optional(CONF_AWAY): cv.templatable(cv.boolean),
 | 
			
		||||
        cv.Optional(CONF_AWAY): cv.invalid("Use preset instead"),
 | 
			
		||||
        cv.Exclusive(CONF_FAN_MODE, "fan_mode"): cv.templatable(
 | 
			
		||||
            validate_climate_fan_mode
 | 
			
		||||
        ),
 | 
			
		||||
@@ -379,9 +379,6 @@ async def climate_control_to_code(config, action_id, template_arg, args):
 | 
			
		||||
            config[CONF_TARGET_TEMPERATURE_HIGH], args, float
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_target_temperature_high(template_))
 | 
			
		||||
    if CONF_AWAY in config:
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_AWAY], args, bool)
 | 
			
		||||
        cg.add(var.set_away(template_))
 | 
			
		||||
    if CONF_FAN_MODE in config:
 | 
			
		||||
        template_ = await cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode)
 | 
			
		||||
        cg.add(var.set_fan_mode(template_))
 | 
			
		||||
 
 | 
			
		||||
@@ -264,25 +264,11 @@ const optional<ClimateMode> &ClimateCall::get_mode() const { return this->mode_;
 | 
			
		||||
const optional<float> &ClimateCall::get_target_temperature() const { return this->target_temperature_; }
 | 
			
		||||
const optional<float> &ClimateCall::get_target_temperature_low() const { return this->target_temperature_low_; }
 | 
			
		||||
const optional<float> &ClimateCall::get_target_temperature_high() const { return this->target_temperature_high_; }
 | 
			
		||||
optional<bool> ClimateCall::get_away() const {
 | 
			
		||||
  if (!this->preset_.has_value())
 | 
			
		||||
    return {};
 | 
			
		||||
  return *this->preset_ == ClimatePreset::CLIMATE_PRESET_AWAY;
 | 
			
		||||
}
 | 
			
		||||
const optional<ClimateFanMode> &ClimateCall::get_fan_mode() const { return this->fan_mode_; }
 | 
			
		||||
const optional<std::string> &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; }
 | 
			
		||||
const optional<ClimatePreset> &ClimateCall::get_preset() const { return this->preset_; }
 | 
			
		||||
const optional<std::string> &ClimateCall::get_custom_preset() const { return this->custom_preset_; }
 | 
			
		||||
const optional<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; }
 | 
			
		||||
ClimateCall &ClimateCall::set_away(bool away) {
 | 
			
		||||
  this->preset_ = away ? CLIMATE_PRESET_AWAY : CLIMATE_PRESET_HOME;
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
ClimateCall &ClimateCall::set_away(optional<bool> away) {
 | 
			
		||||
  if (away.has_value())
 | 
			
		||||
    this->preset_ = *away ? CLIMATE_PRESET_AWAY : CLIMATE_PRESET_HOME;
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
ClimateCall &ClimateCall::set_target_temperature_high(optional<float> target_temperature_high) {
 | 
			
		||||
  this->target_temperature_high_ = target_temperature_high;
 | 
			
		||||
  return *this;
 | 
			
		||||
@@ -453,12 +439,7 @@ void Climate::set_visual_temperature_step_override(float target, float current)
 | 
			
		||||
  this->visual_target_temperature_step_override_ = target;
 | 
			
		||||
  this->visual_current_temperature_step_override_ = current;
 | 
			
		||||
}
 | 
			
		||||
#pragma GCC diagnostic push
 | 
			
		||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 | 
			
		||||
Climate::Climate(const std::string &name) : EntityBase(name) {}
 | 
			
		||||
#pragma GCC diagnostic pop
 | 
			
		||||
 | 
			
		||||
Climate::Climate() : Climate("") {}
 | 
			
		||||
ClimateCall Climate::make_call() { return ClimateCall(this); }
 | 
			
		||||
 | 
			
		||||
ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) {
 | 
			
		||||
 
 | 
			
		||||
@@ -64,10 +64,6 @@ class ClimateCall {
 | 
			
		||||
   * For climate devices with two point target temperature control
 | 
			
		||||
   */
 | 
			
		||||
  ClimateCall &set_target_temperature_high(optional<float> target_temperature_high);
 | 
			
		||||
  ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead", "v1.20")
 | 
			
		||||
  ClimateCall &set_away(bool away);
 | 
			
		||||
  ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead", "v1.20")
 | 
			
		||||
  ClimateCall &set_away(optional<bool> away);
 | 
			
		||||
  /// Set the fan mode of the climate device.
 | 
			
		||||
  ClimateCall &set_fan_mode(ClimateFanMode fan_mode);
 | 
			
		||||
  /// Set the fan mode of the climate device.
 | 
			
		||||
@@ -97,8 +93,6 @@ class ClimateCall {
 | 
			
		||||
  const optional<float> &get_target_temperature() const;
 | 
			
		||||
  const optional<float> &get_target_temperature_low() const;
 | 
			
		||||
  const optional<float> &get_target_temperature_high() const;
 | 
			
		||||
  ESPDEPRECATED("get_away() is deprecated, please use .get_preset() instead", "v1.20")
 | 
			
		||||
  optional<bool> get_away() const;
 | 
			
		||||
  const optional<ClimateFanMode> &get_fan_mode() const;
 | 
			
		||||
  const optional<ClimateSwingMode> &get_swing_mode() const;
 | 
			
		||||
  const optional<std::string> &get_custom_fan_mode() const;
 | 
			
		||||
@@ -166,11 +160,6 @@ struct ClimateDeviceRestoreState {
 | 
			
		||||
 */
 | 
			
		||||
class Climate : public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  /// Construct a climate device with empty name (will be set later).
 | 
			
		||||
  Climate();
 | 
			
		||||
  /// Construct a climate device with a name.
 | 
			
		||||
  Climate(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /// The active mode of the climate device.
 | 
			
		||||
  ClimateMode mode{CLIMATE_MODE_OFF};
 | 
			
		||||
  /// The active state of the climate device.
 | 
			
		||||
@@ -189,14 +178,6 @@ class Climate : public EntityBase {
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /** Whether the climate device is in away mode.
 | 
			
		||||
   *
 | 
			
		||||
   * Away allows climate devices to have two different target temperature configs:
 | 
			
		||||
   * one for normal mode and one for away mode.
 | 
			
		||||
   */
 | 
			
		||||
  ESPDEPRECATED("away is deprecated, use preset instead", "v1.20")
 | 
			
		||||
  bool away{false};
 | 
			
		||||
 | 
			
		||||
  /// The active fan mode of the climate device.
 | 
			
		||||
  optional<ClimateFanMode> fan_mode;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -117,15 +117,6 @@ class ClimateTraits {
 | 
			
		||||
  bool supports_custom_preset(const std::string &custom_preset) const {
 | 
			
		||||
    return supported_custom_presets_.count(custom_preset);
 | 
			
		||||
  }
 | 
			
		||||
  ESPDEPRECATED("This method is deprecated, use set_supported_presets() instead", "v1.20")
 | 
			
		||||
  void set_supports_away(bool supports) {
 | 
			
		||||
    if (supports) {
 | 
			
		||||
      supported_presets_.insert(CLIMATE_PRESET_AWAY);
 | 
			
		||||
      supported_presets_.insert(CLIMATE_PRESET_HOME);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ESPDEPRECATED("This method is deprecated, use supports_preset() instead", "v1.20")
 | 
			
		||||
  bool get_supports_away() const { return supports_preset(CLIMATE_PRESET_AWAY); }
 | 
			
		||||
 | 
			
		||||
  void set_supported_swing_modes(std::set<ClimateSwingMode> modes) { supported_swing_modes_ = std::move(modes); }
 | 
			
		||||
  void add_supported_swing_mode(ClimateSwingMode mode) { supported_swing_modes_.insert(mode); }
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ cover::CoverTraits CopyCover::get_traits() {
 | 
			
		||||
  // copy traits manually so it doesn't break when new options are added
 | 
			
		||||
  // but the control() method hasn't implemented them yet.
 | 
			
		||||
  traits.set_is_assumed_state(base.get_is_assumed_state());
 | 
			
		||||
  traits.set_supports_stop(base.get_supports_stop());
 | 
			
		||||
  traits.set_supports_position(base.get_supports_position());
 | 
			
		||||
  traits.set_supports_tilt(base.get_supports_tilt());
 | 
			
		||||
  traits.set_supports_toggle(base.get_supports_toggle());
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ const char *cover_operation_to_str(CoverOperation op) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Cover::Cover(const std::string &name) : EntityBase(name), position{COVER_OPEN} {}
 | 
			
		||||
Cover::Cover() : position{COVER_OPEN} {}
 | 
			
		||||
 | 
			
		||||
CoverCall::CoverCall(Cover *parent) : parent_(parent) {}
 | 
			
		||||
CoverCall &CoverCall::set_command(const char *command) {
 | 
			
		||||
@@ -145,7 +145,7 @@ CoverCall &CoverCall::set_stop(bool stop) {
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
bool CoverCall::get_stop() const { return this->stop_; }
 | 
			
		||||
void Cover::set_device_class(const std::string &device_class) { this->device_class_override_ = device_class; }
 | 
			
		||||
 | 
			
		||||
CoverCall Cover::make_call() { return {this}; }
 | 
			
		||||
void Cover::open() {
 | 
			
		||||
  auto call = this->make_call();
 | 
			
		||||
@@ -204,12 +204,7 @@ optional<CoverRestoreState> Cover::restore_state_() {
 | 
			
		||||
    return {};
 | 
			
		||||
  return recovered;
 | 
			
		||||
}
 | 
			
		||||
Cover::Cover() : Cover("") {}
 | 
			
		||||
std::string Cover::get_device_class() {
 | 
			
		||||
  if (this->device_class_override_.has_value())
 | 
			
		||||
    return *this->device_class_override_;
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Cover::is_fully_open() const { return this->position == COVER_OPEN; }
 | 
			
		||||
bool Cover::is_fully_closed() const { return this->position == COVER_CLOSED; }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -108,10 +108,9 @@ const char *cover_operation_to_str(CoverOperation op);
 | 
			
		||||
 * to control all values of the cover. Also implement get_traits() to return what operations
 | 
			
		||||
 * the cover supports.
 | 
			
		||||
 */
 | 
			
		||||
class Cover : public EntityBase {
 | 
			
		||||
class Cover : public EntityBase, public EntityBase_DeviceClass {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit Cover();
 | 
			
		||||
  explicit Cover(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /// The current operation of the cover (idle, opening, closing).
 | 
			
		||||
  CoverOperation current_operation{COVER_OPERATION_IDLE};
 | 
			
		||||
@@ -141,8 +140,9 @@ class Cover : public EntityBase {
 | 
			
		||||
  /** Stop the cover.
 | 
			
		||||
   *
 | 
			
		||||
   * This is a legacy method and may be removed later, please use `.make_call()` instead.
 | 
			
		||||
   * As per solution from issue #2885 the call should include perform()
 | 
			
		||||
   */
 | 
			
		||||
  ESPDEPRECATED("stop() is deprecated, use make_call().set_command_stop() instead.", "2021.9")
 | 
			
		||||
  ESPDEPRECATED("stop() is deprecated, use make_call().set_command_stop().perform() instead.", "2021.9")
 | 
			
		||||
  void stop();
 | 
			
		||||
 | 
			
		||||
  void add_on_state_callback(std::function<void()> &&f);
 | 
			
		||||
@@ -157,8 +157,6 @@ class Cover : public EntityBase {
 | 
			
		||||
  void publish_state(bool save = true);
 | 
			
		||||
 | 
			
		||||
  virtual CoverTraits get_traits() = 0;
 | 
			
		||||
  void set_device_class(const std::string &device_class);
 | 
			
		||||
  std::string get_device_class();
 | 
			
		||||
 | 
			
		||||
  /// Helper method to check if the cover is fully open. Equivalent to comparing .position against 1.0
 | 
			
		||||
  bool is_fully_open() const;
 | 
			
		||||
@@ -173,7 +171,6 @@ class Cover : public EntityBase {
 | 
			
		||||
  optional<CoverRestoreState> restore_state_();
 | 
			
		||||
 | 
			
		||||
  CallbackManager<void()> state_callback_{};
 | 
			
		||||
  optional<std::string> device_class_override_{};
 | 
			
		||||
 | 
			
		||||
  ESPPreferenceObject rtc_;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -15,12 +15,15 @@ class CoverTraits {
 | 
			
		||||
  void set_supports_tilt(bool supports_tilt) { this->supports_tilt_ = supports_tilt; }
 | 
			
		||||
  bool get_supports_toggle() const { return this->supports_toggle_; }
 | 
			
		||||
  void set_supports_toggle(bool supports_toggle) { this->supports_toggle_ = supports_toggle; }
 | 
			
		||||
  bool get_supports_stop() const { return this->supports_stop_; }
 | 
			
		||||
  void set_supports_stop(bool supports_stop) { this->supports_stop_ = supports_stop; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool is_assumed_state_{false};
 | 
			
		||||
  bool supports_position_{false};
 | 
			
		||||
  bool supports_tilt_{false};
 | 
			
		||||
  bool supports_toggle_{false};
 | 
			
		||||
  bool supports_stop_{false};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace cover
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ using namespace esphome::cover;
 | 
			
		||||
 | 
			
		||||
CoverTraits CurrentBasedCover::get_traits() {
 | 
			
		||||
  auto traits = CoverTraits();
 | 
			
		||||
  traits.set_supports_stop(true);
 | 
			
		||||
  traits.set_supports_position(true);
 | 
			
		||||
  traits.set_supports_toggle(true);
 | 
			
		||||
  traits.set_is_assumed_state(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,10 @@ import requests
 | 
			
		||||
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.final_validate as fv
 | 
			
		||||
from esphome import git
 | 
			
		||||
from esphome.components.packages import validate_source_shorthand
 | 
			
		||||
from esphome.const import CONF_REF, CONF_WIFI
 | 
			
		||||
from esphome.const import CONF_REF, CONF_WIFI, CONF_ESPHOME, CONF_PROJECT
 | 
			
		||||
from esphome.wizard import wizard_file
 | 
			
		||||
from esphome.yaml_util import dump
 | 
			
		||||
 | 
			
		||||
@@ -52,6 +53,17 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    validate_full_url,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _final_validate(config):
 | 
			
		||||
    full_config = fv.full_config.get()[CONF_ESPHOME]
 | 
			
		||||
    if CONF_PROJECT not in full_config:
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            "Dashboard import requires the `esphome` -> `project` information to be provided."
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = _final_validate
 | 
			
		||||
 | 
			
		||||
WIFI_CONFIG = """
 | 
			
		||||
 | 
			
		||||
wifi:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,11 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.final_validate as fv
 | 
			
		||||
from esphome.components import logger
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_BLOCK,
 | 
			
		||||
    CONF_DEVICE,
 | 
			
		||||
    CONF_FRAGMENTATION,
 | 
			
		||||
    CONF_FREE,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_LEVEL,
 | 
			
		||||
    CONF_LOGGER,
 | 
			
		||||
    CONF_LOOP_TIME,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -21,7 +17,8 @@ debug_ns = cg.esphome_ns.namespace("debug")
 | 
			
		||||
DebugComponent = debug_ns.class_("DebugComponent", cg.PollingComponent)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(DebugComponent),
 | 
			
		||||
            cv.Optional(CONF_DEVICE): cv.invalid(
 | 
			
		||||
@@ -40,19 +37,9 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
                "The 'loop_time' option has been moved to the 'debug' sensor component"
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
).extend(cv.polling_component_schema("60s"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _final_validate(_):
 | 
			
		||||
    logger_conf = fv.full_config.get()[CONF_LOGGER]
 | 
			
		||||
    severity = logger.LOG_LEVEL_SEVERITY.index(logger_conf[CONF_LEVEL])
 | 
			
		||||
    if severity < logger.LOG_LEVEL_SEVERITY.index("DEBUG"):
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            "The debug component requires the logger to be at least at DEBUG level"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = _final_validate
 | 
			
		||||
    ).extend(cv.polling_component_schema("60s")),
 | 
			
		||||
    cv.only_on(["esp32", "esp8266"]),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,10 @@ static uint32_t get_free_heap() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebugComponent::dump_config() {
 | 
			
		||||
#ifndef ESPHOME_LOG_HAS_DEBUG
 | 
			
		||||
  return;  // Can't log below if debug logging is disabled
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  std::string device_info;
 | 
			
		||||
  std::string reset_reason;
 | 
			
		||||
  device_info.reserve(256);
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,7 @@ class DemoCover : public cover::Cover, public Component {
 | 
			
		||||
        traits.set_supports_tilt(true);
 | 
			
		||||
        break;
 | 
			
		||||
      case DemoCoverType::TYPE_4:
 | 
			
		||||
        traits.set_supports_stop(true);
 | 
			
		||||
        traits.set_is_assumed_state(true);
 | 
			
		||||
        traits.set_supports_tilt(true);
 | 
			
		||||
        break;
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,7 @@ DEVICE = {
 | 
			
		||||
 | 
			
		||||
NextAction = dfplayer_ns.class_("NextAction", automation.Action)
 | 
			
		||||
PreviousAction = dfplayer_ns.class_("PreviousAction", automation.Action)
 | 
			
		||||
PlayMp3Action = dfplayer_ns.class_("PlayMp3Action", automation.Action)
 | 
			
		||||
PlayFileAction = dfplayer_ns.class_("PlayFileAction", automation.Action)
 | 
			
		||||
PlayFolderAction = dfplayer_ns.class_("PlayFolderAction", automation.Action)
 | 
			
		||||
SetVolumeAction = dfplayer_ns.class_("SetVolumeAction", automation.Action)
 | 
			
		||||
@@ -113,6 +114,25 @@ async def dfplayer_previous_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "dfplayer.play_mp3",
 | 
			
		||||
    PlayMp3Action,
 | 
			
		||||
    cv.maybe_simple_value(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.use_id(DFPlayer),
 | 
			
		||||
            cv.Required(CONF_FILE): cv.templatable(cv.int_),
 | 
			
		||||
        },
 | 
			
		||||
        key=CONF_FILE,
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
async def dfplayer_play_mp3_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_ = await cg.templatable(config[CONF_FILE], args, float)
 | 
			
		||||
    cg.add(var.set_file(template_))
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "dfplayer.play",
 | 
			
		||||
    PlayFileAction,
 | 
			
		||||
 
 | 
			
		||||
@@ -7,10 +7,10 @@ namespace dfplayer {
 | 
			
		||||
static const char *const TAG = "dfplayer";
 | 
			
		||||
 | 
			
		||||
void DFPlayer::play_folder(uint16_t folder, uint16_t file) {
 | 
			
		||||
  if (folder < 100 && file < 256) {
 | 
			
		||||
  if (folder <= 10 && file <= 1000) {
 | 
			
		||||
    this->ack_set_is_playing_ = true;
 | 
			
		||||
    this->send_cmd_(0x0F, (uint8_t) folder, (uint8_t) file);
 | 
			
		||||
  } else if (folder <= 10 && file <= 1000) {
 | 
			
		||||
  } else if (folder < 100 && file < 256) {
 | 
			
		||||
    this->ack_set_is_playing_ = true;
 | 
			
		||||
    this->send_cmd_(0x14, (((uint16_t) folder) << 12) | file);
 | 
			
		||||
  } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,10 @@ class DFPlayer : public uart::UARTDevice, public Component {
 | 
			
		||||
    this->ack_set_is_playing_ = true;
 | 
			
		||||
    this->send_cmd_(0x02);
 | 
			
		||||
  }
 | 
			
		||||
  void play_mp3(uint16_t file) {
 | 
			
		||||
    this->ack_set_is_playing_ = true;
 | 
			
		||||
    this->send_cmd_(0x12, file);
 | 
			
		||||
  }
 | 
			
		||||
  void play_file(uint16_t file) {
 | 
			
		||||
    this->ack_set_is_playing_ = true;
 | 
			
		||||
    this->send_cmd_(0x03, file);
 | 
			
		||||
@@ -113,6 +117,16 @@ class DFPlayer : public uart::UARTDevice, public Component {
 | 
			
		||||
DFPLAYER_SIMPLE_ACTION(NextAction, next)
 | 
			
		||||
DFPLAYER_SIMPLE_ACTION(PreviousAction, previous)
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class PlayMp3Action : public Action<Ts...>, public Parented<DFPlayer> {
 | 
			
		||||
 public:
 | 
			
		||||
  TEMPLATABLE_VALUE(uint16_t, file)
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override {
 | 
			
		||||
    auto file = this->file_.value(x...);
 | 
			
		||||
    this->parent_->play_mp3(file);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class PlayFileAction : public Action<Ts...>, public Parented<DFPlayer> {
 | 
			
		||||
 public:
 | 
			
		||||
  TEMPLATABLE_VALUE(uint16_t, file)
 | 
			
		||||
 
 | 
			
		||||
@@ -282,13 +282,17 @@ void DisplayBuffer::print(int x, int y, Font *font, Color color, TextAlign align
 | 
			
		||||
    int scan_x1, scan_y1, scan_width, scan_height;
 | 
			
		||||
    glyph.scan_area(&scan_x1, &scan_y1, &scan_width, &scan_height);
 | 
			
		||||
 | 
			
		||||
    for (int glyph_x = scan_x1; glyph_x < scan_x1 + scan_width; glyph_x++) {
 | 
			
		||||
      for (int glyph_y = scan_y1; glyph_y < scan_y1 + scan_height; glyph_y++) {
 | 
			
		||||
    {
 | 
			
		||||
      const int glyph_x_max = scan_x1 + scan_width;
 | 
			
		||||
      const int glyph_y_max = scan_y1 + scan_height;
 | 
			
		||||
      for (int glyph_x = scan_x1; glyph_x < glyph_x_max; glyph_x++) {
 | 
			
		||||
        for (int glyph_y = scan_y1; glyph_y < glyph_y_max; glyph_y++) {
 | 
			
		||||
          if (glyph.get_pixel(glyph_x, glyph_y)) {
 | 
			
		||||
            this->draw_pixel_at(glyph_x + x_at, glyph_y + y_start, color);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    x_at += glyph.glyph_data_->width + glyph.glyph_data_->offset_x;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ using namespace esphome::cover;
 | 
			
		||||
 | 
			
		||||
CoverTraits EndstopCover::get_traits() {
 | 
			
		||||
  auto traits = CoverTraits();
 | 
			
		||||
  traits.set_supports_stop(true);
 | 
			
		||||
  traits.set_supports_position(true);
 | 
			
		||||
  traits.set_supports_toggle(true);
 | 
			
		||||
  traits.set_is_assumed_state(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -163,7 +163,7 @@ RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 0, 5)
 | 
			
		||||
# The platformio/espressif32 version to use for arduino frameworks
 | 
			
		||||
#  - https://github.com/platformio/platform-espressif32/releases
 | 
			
		||||
#  - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
 | 
			
		||||
ARDUINO_PLATFORM_VERSION = cv.Version(5, 2, 0)
 | 
			
		||||
ARDUINO_PLATFORM_VERSION = cv.Version(5, 3, 0)
 | 
			
		||||
 | 
			
		||||
# The default/recommended esp-idf framework version
 | 
			
		||||
#  - https://github.com/espressif/esp-idf/releases
 | 
			
		||||
@@ -252,7 +252,7 @@ def _parse_platform_version(value):
 | 
			
		||||
    try:
 | 
			
		||||
        # if platform version is a valid version constraint, prefix the default package
 | 
			
		||||
        cv.platformio_version_constraint(value)
 | 
			
		||||
        return f"platformio/espressif32 @ {value}"
 | 
			
		||||
        return f"platformio/espressif32@{value}"
 | 
			
		||||
    except cv.Invalid:
 | 
			
		||||
        return value
 | 
			
		||||
 | 
			
		||||
@@ -367,12 +367,12 @@ async def to_code(config):
 | 
			
		||||
        cg.add_build_flag("-Wno-nonnull-compare")
 | 
			
		||||
        cg.add_platformio_option(
 | 
			
		||||
            "platform_packages",
 | 
			
		||||
            [f"platformio/framework-espidf @ {conf[CONF_SOURCE]}"],
 | 
			
		||||
            [f"platformio/framework-espidf@{conf[CONF_SOURCE]}"],
 | 
			
		||||
        )
 | 
			
		||||
        # platformio/toolchain-esp32ulp does not support linux_aarch64 yet and has not been updated for over 2 years
 | 
			
		||||
        # This is espressif's own published version which is more up to date.
 | 
			
		||||
        cg.add_platformio_option(
 | 
			
		||||
            "platform_packages", ["espressif/toolchain-esp32ulp @ 2.35.0-20220830"]
 | 
			
		||||
            "platform_packages", ["espressif/toolchain-esp32ulp@2.35.0-20220830"]
 | 
			
		||||
        )
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False)
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True)
 | 
			
		||||
@@ -433,7 +433,7 @@ async def to_code(config):
 | 
			
		||||
        cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO")
 | 
			
		||||
        cg.add_platformio_option(
 | 
			
		||||
            "platform_packages",
 | 
			
		||||
            [f"platformio/framework-arduinoespressif32 @ {conf[CONF_SOURCE]}"],
 | 
			
		||||
            [f"platformio/framework-arduinoespressif32@{conf[CONF_SOURCE]}"],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        cg.add_platformio_option("board_build.partitions", "partitions.csv")
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
#include <freertos/task.h>
 | 
			
		||||
#include <esp_idf_version.h>
 | 
			
		||||
#include <esp_task_wdt.h>
 | 
			
		||||
#include <esp_timer.h>
 | 
			
		||||
#include <soc/rtc.h>
 | 
			
		||||
 | 
			
		||||
#if ESP_IDF_VERSION_MAJOR >= 4
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
#include "gpio.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32 {
 | 
			
		||||
@@ -74,7 +75,7 @@ void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpi
 | 
			
		||||
 | 
			
		||||
std::string ESP32InternalGPIOPin::dump_summary() const {
 | 
			
		||||
  char buffer[32];
 | 
			
		||||
  snprintf(buffer, sizeof(buffer), "GPIO%u", static_cast<uint32_t>(pin_));
 | 
			
		||||
  snprintf(buffer, sizeof(buffer), "GPIO%" PRIu32, static_cast<uint32_t>(pin_));
 | 
			
		||||
  return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <nvs_flash.h>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
@@ -101,7 +102,7 @@ class ESP32Preferences : public ESPPreferences {
 | 
			
		||||
    pref->nvs_handle = nvs_handle;
 | 
			
		||||
 | 
			
		||||
    uint32_t keyval = type;
 | 
			
		||||
    pref->key = str_sprintf("%u", keyval);
 | 
			
		||||
    pref->key = str_sprintf("%" PRIu32, keyval);
 | 
			
		||||
 | 
			
		||||
    return ESPPreferenceObject(pref);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,9 @@ CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
CONFLICTS_WITH = ["esp32_ble_beacon"]
 | 
			
		||||
 | 
			
		||||
CONF_BLE_ID = "ble_id"
 | 
			
		||||
CONF_IO_CAPABILITY = "io_capability"
 | 
			
		||||
 | 
			
		||||
NO_BLUTOOTH_VARIANTS = [const.VARIANT_ESP32S2]
 | 
			
		||||
NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2]
 | 
			
		||||
 | 
			
		||||
esp32_ble_ns = cg.esphome_ns.namespace("esp32_ble")
 | 
			
		||||
ESP32BLE = esp32_ble_ns.class_("ESP32BLE", cg.Component)
 | 
			
		||||
@@ -19,17 +20,28 @@ GAPEventHandler = esp32_ble_ns.class_("GAPEventHandler")
 | 
			
		||||
GATTcEventHandler = esp32_ble_ns.class_("GATTcEventHandler")
 | 
			
		||||
GATTsEventHandler = esp32_ble_ns.class_("GATTsEventHandler")
 | 
			
		||||
 | 
			
		||||
IoCapability = esp32_ble_ns.enum("IoCapability")
 | 
			
		||||
IO_CAPABILITY = {
 | 
			
		||||
    "none": IoCapability.IO_CAP_NONE,
 | 
			
		||||
    "keyboard_only": IoCapability.IO_CAP_IN,
 | 
			
		||||
    "keyboard_display": IoCapability.IO_CAP_KBDISP,
 | 
			
		||||
    "display_only": IoCapability.IO_CAP_OUT,
 | 
			
		||||
    "display_yes_no": IoCapability.IO_CAP_IO,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(ESP32BLE),
 | 
			
		||||
        cv.Optional(CONF_IO_CAPABILITY, default="none"): cv.enum(
 | 
			
		||||
            IO_CAPABILITY, lower=True
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_variant(_):
 | 
			
		||||
    variant = get_esp32_variant()
 | 
			
		||||
    if variant in NO_BLUTOOTH_VARIANTS:
 | 
			
		||||
    if variant in NO_BLUETOOTH_VARIANTS:
 | 
			
		||||
        raise cv.Invalid(f"{variant} does not support Bluetooth")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +51,7 @@ FINAL_VALIDATE_SCHEMA = validate_variant
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY]))
 | 
			
		||||
 | 
			
		||||
    if CORE.using_esp_idf:
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
 | 
			
		||||
#include <esp_bt.h>
 | 
			
		||||
#include <esp_bt_main.h>
 | 
			
		||||
#include <esp_bt_device.h>
 | 
			
		||||
#include <esp_gap_ble_api.h>
 | 
			
		||||
#include <freertos/FreeRTOS.h>
 | 
			
		||||
#include <freertos/FreeRTOSConfig.h>
 | 
			
		||||
@@ -133,8 +134,7 @@ bool ESP32BLE::ble_setup_() {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
 | 
			
		||||
  err = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
 | 
			
		||||
  err = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &(this->io_cap_), sizeof(uint8_t));
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "esp_ble_gap_set_security_param failed: %d", err);
 | 
			
		||||
    return false;
 | 
			
		||||
@@ -211,7 +211,38 @@ void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if
 | 
			
		||||
 | 
			
		||||
float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; }
 | 
			
		||||
 | 
			
		||||
void ESP32BLE::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 BLE:"); }
 | 
			
		||||
void ESP32BLE::dump_config() {
 | 
			
		||||
  const uint8_t *mac_address = esp_bt_dev_get_address();
 | 
			
		||||
  if (mac_address) {
 | 
			
		||||
    const char *io_capability_s;
 | 
			
		||||
    switch (this->io_cap_) {
 | 
			
		||||
      case ESP_IO_CAP_OUT:
 | 
			
		||||
        io_capability_s = "display_only";
 | 
			
		||||
        break;
 | 
			
		||||
      case ESP_IO_CAP_IO:
 | 
			
		||||
        io_capability_s = "display_yes_no";
 | 
			
		||||
        break;
 | 
			
		||||
      case ESP_IO_CAP_IN:
 | 
			
		||||
        io_capability_s = "keyboard_only";
 | 
			
		||||
        break;
 | 
			
		||||
      case ESP_IO_CAP_NONE:
 | 
			
		||||
        io_capability_s = "none";
 | 
			
		||||
        break;
 | 
			
		||||
      case ESP_IO_CAP_KBDISP:
 | 
			
		||||
        io_capability_s = "keyboard_display";
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        io_capability_s = "invalid";
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "ESP32 BLE:");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  MAC address: %02X:%02X:%02X:%02X:%02X:%02X", mac_address[0], mac_address[1], mac_address[2],
 | 
			
		||||
                  mac_address[3], mac_address[4], mac_address[5]);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  IO Capability: %s", io_capability_s);
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ESP32BLE *global_ble = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,14 @@ typedef struct {
 | 
			
		||||
  uint16_t mtu;
 | 
			
		||||
} conn_status_t;
 | 
			
		||||
 | 
			
		||||
enum IoCapability {
 | 
			
		||||
  IO_CAP_OUT = ESP_IO_CAP_OUT,
 | 
			
		||||
  IO_CAP_IO = ESP_IO_CAP_IO,
 | 
			
		||||
  IO_CAP_IN = ESP_IO_CAP_IN,
 | 
			
		||||
  IO_CAP_NONE = ESP_IO_CAP_NONE,
 | 
			
		||||
  IO_CAP_KBDISP = ESP_IO_CAP_KBDISP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GAPEventHandler {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
 | 
			
		||||
@@ -44,6 +52,8 @@ class GATTsEventHandler {
 | 
			
		||||
 | 
			
		||||
class ESP32BLE : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; }
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
@@ -72,6 +82,7 @@ class ESP32BLE : public Component {
 | 
			
		||||
 | 
			
		||||
  Queue<BLEEvent> ble_events_;
 | 
			
		||||
  BLEAdvertising *advertising_;
 | 
			
		||||
  esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 
 | 
			
		||||
@@ -202,7 +202,7 @@ void ESP32Camera::loop() {
 | 
			
		||||
float ESP32Camera::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
/* ---------------- constructors ---------------- */
 | 
			
		||||
ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) {
 | 
			
		||||
ESP32Camera::ESP32Camera() {
 | 
			
		||||
  this->config_.pin_pwdn = -1;
 | 
			
		||||
  this->config_.pin_reset = -1;
 | 
			
		||||
  this->config_.pin_xclk = -1;
 | 
			
		||||
@@ -215,7 +215,6 @@ ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) {
 | 
			
		||||
 | 
			
		||||
  global_esp32_camera = this;
 | 
			
		||||
}
 | 
			
		||||
ESP32Camera::ESP32Camera() : ESP32Camera("") {}
 | 
			
		||||
 | 
			
		||||
/* ---------------- setters ---------------- */
 | 
			
		||||
/* set pin assignment */
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,6 @@ class CameraImageReader {
 | 
			
		||||
/* ---------------- ESP32Camera class ---------------- */
 | 
			
		||||
class ESP32Camera : public Component, public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  ESP32Camera(const std::string &name);
 | 
			
		||||
  ESP32Camera();
 | 
			
		||||
 | 
			
		||||
  /* setters */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/esp32_rmt_led_strip/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/esp32_rmt_led_strip/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										207
									
								
								esphome/components/esp32_rmt_led_strip/led_strip.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								esphome/components/esp32_rmt_led_strip/led_strip.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,207 @@
 | 
			
		||||
#include "led_strip.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#include <esp_attr.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32_rmt_led_strip {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "esp32_rmt_led_strip";
 | 
			
		||||
 | 
			
		||||
static const uint8_t RMT_CLK_DIV = 2;
 | 
			
		||||
 | 
			
		||||
void ESP32RMTLEDStripLightOutput::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ESP32 LED Strip...");
 | 
			
		||||
 | 
			
		||||
  size_t buffer_size = this->get_buffer_size_();
 | 
			
		||||
 | 
			
		||||
  ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
 | 
			
		||||
  this->buf_ = allocator.allocate(buffer_size);
 | 
			
		||||
  if (this->buf_ == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "Cannot allocate LED buffer!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->effect_data_ = allocator.allocate(this->num_leds_);
 | 
			
		||||
  if (this->effect_data_ == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "Cannot allocate effect data!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ExternalRAMAllocator<rmt_item32_t> rmt_allocator(ExternalRAMAllocator<rmt_item32_t>::ALLOW_FAILURE);
 | 
			
		||||
  this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8);  // 8 bits per byte, 1 rmt_item32_t per bit
 | 
			
		||||
 | 
			
		||||
  rmt_config_t config;
 | 
			
		||||
  memset(&config, 0, sizeof(config));
 | 
			
		||||
  config.channel = this->channel_;
 | 
			
		||||
  config.rmt_mode = RMT_MODE_TX;
 | 
			
		||||
  config.gpio_num = gpio_num_t(this->pin_);
 | 
			
		||||
  config.mem_block_num = 1;
 | 
			
		||||
  config.clk_div = RMT_CLK_DIV;
 | 
			
		||||
  config.tx_config.loop_en = false;
 | 
			
		||||
  config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
 | 
			
		||||
  config.tx_config.carrier_en = false;
 | 
			
		||||
  config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
 | 
			
		||||
  config.tx_config.idle_output_en = true;
 | 
			
		||||
 | 
			
		||||
  if (rmt_config(&config) != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Cannot initialize RMT!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (rmt_driver_install(config.channel, 0, 0) != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Cannot install RMT driver!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high,
 | 
			
		||||
                                                 uint32_t bit1_low) {
 | 
			
		||||
  float ratio = (float) APB_CLK_FREQ / RMT_CLK_DIV / 1e09f;
 | 
			
		||||
 | 
			
		||||
  // 0-bit
 | 
			
		||||
  this->bit0_.duration0 = (uint32_t) (ratio * bit0_high);
 | 
			
		||||
  this->bit0_.level0 = 1;
 | 
			
		||||
  this->bit0_.duration1 = (uint32_t) (ratio * bit0_low);
 | 
			
		||||
  this->bit0_.level1 = 0;
 | 
			
		||||
  // 1-bit
 | 
			
		||||
  this->bit1_.duration0 = (uint32_t) (ratio * bit1_high);
 | 
			
		||||
  this->bit1_.level0 = 1;
 | 
			
		||||
  this->bit1_.duration1 = (uint32_t) (ratio * bit1_low);
 | 
			
		||||
  this->bit1_.level1 = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
 | 
			
		||||
  // protect from refreshing too often
 | 
			
		||||
  uint32_t now = micros();
 | 
			
		||||
  if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) {
 | 
			
		||||
    // try again next loop iteration, so that this change won't get lost
 | 
			
		||||
    this->schedule_show();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->last_refresh_ = now;
 | 
			
		||||
  this->mark_shown_();
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, "Writing RGB values to bus...");
 | 
			
		||||
 | 
			
		||||
  if (rmt_wait_tx_done(this->channel_, pdMS_TO_TICKS(1000)) != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "RMT TX timeout");
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  delayMicroseconds(50);
 | 
			
		||||
 | 
			
		||||
  size_t buffer_size = this->get_buffer_size_();
 | 
			
		||||
 | 
			
		||||
  size_t size = 0;
 | 
			
		||||
  size_t len = 0;
 | 
			
		||||
  uint8_t *psrc = this->buf_;
 | 
			
		||||
  rmt_item32_t *pdest = this->rmt_buf_;
 | 
			
		||||
  while (size < buffer_size) {
 | 
			
		||||
    uint8_t b = *psrc;
 | 
			
		||||
    for (int i = 0; i < 8; i++) {
 | 
			
		||||
      pdest->val = b & (1 << (7 - i)) ? this->bit1_.val : this->bit0_.val;
 | 
			
		||||
      pdest++;
 | 
			
		||||
      len++;
 | 
			
		||||
    }
 | 
			
		||||
    size++;
 | 
			
		||||
    psrc++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (rmt_write_items(this->channel_, this->rmt_buf_, len, false) != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "RMT TX error");
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
light::ESPColorView ESP32RMTLEDStripLightOutput::get_view_internal(int32_t index) const {
 | 
			
		||||
  int32_t r = 0, g = 0, b = 0;
 | 
			
		||||
  switch (this->rgb_order_) {
 | 
			
		||||
    case ORDER_RGB:
 | 
			
		||||
      r = 0;
 | 
			
		||||
      g = 1;
 | 
			
		||||
      b = 2;
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_RBG:
 | 
			
		||||
      r = 0;
 | 
			
		||||
      g = 2;
 | 
			
		||||
      b = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_GRB:
 | 
			
		||||
      r = 1;
 | 
			
		||||
      g = 0;
 | 
			
		||||
      b = 2;
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_GBR:
 | 
			
		||||
      r = 2;
 | 
			
		||||
      g = 0;
 | 
			
		||||
      b = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_BGR:
 | 
			
		||||
      r = 2;
 | 
			
		||||
      g = 1;
 | 
			
		||||
      b = 0;
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_BRG:
 | 
			
		||||
      r = 1;
 | 
			
		||||
      g = 2;
 | 
			
		||||
      b = 0;
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  uint8_t multiplier = this->is_rgbw_ ? 4 : 3;
 | 
			
		||||
  return {this->buf_ + (index * multiplier) + r,
 | 
			
		||||
          this->buf_ + (index * multiplier) + g,
 | 
			
		||||
          this->buf_ + (index * multiplier) + b,
 | 
			
		||||
          this->is_rgbw_ ? this->buf_ + (index * multiplier) + 3 : nullptr,
 | 
			
		||||
          &this->effect_data_[index],
 | 
			
		||||
          &this->correction_};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32RMTLEDStripLightOutput::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "ESP32 RMT LED Strip:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Pin: %u", this->pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Channel: %u", this->channel_);
 | 
			
		||||
  const char *rgb_order;
 | 
			
		||||
  switch (this->rgb_order_) {
 | 
			
		||||
    case ORDER_RGB:
 | 
			
		||||
      rgb_order = "RGB";
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_RBG:
 | 
			
		||||
      rgb_order = "RBG";
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_GRB:
 | 
			
		||||
      rgb_order = "GRB";
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_GBR:
 | 
			
		||||
      rgb_order = "GBR";
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_BGR:
 | 
			
		||||
      rgb_order = "BGR";
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_BRG:
 | 
			
		||||
      rgb_order = "BRG";
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      rgb_order = "UNKNOWN";
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  RGB Order: %s", rgb_order);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Max refresh rate: %u", *this->max_refresh_rate_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Number of LEDs: %u", this->num_leds_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float ESP32RMTLEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }
 | 
			
		||||
 | 
			
		||||
}  // namespace esp32_rmt_led_strip
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
							
								
								
									
										87
									
								
								esphome/components/esp32_rmt_led_strip/led_strip.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								esphome/components/esp32_rmt_led_strip/led_strip.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/light/addressable_light.h"
 | 
			
		||||
#include "esphome/components/light/light_output.h"
 | 
			
		||||
#include "esphome/core/color.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
#include <driver/gpio.h>
 | 
			
		||||
#include <driver/rmt.h>
 | 
			
		||||
#include <esp_err.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32_rmt_led_strip {
 | 
			
		||||
 | 
			
		||||
enum RGBOrder : uint8_t {
 | 
			
		||||
  ORDER_RGB,
 | 
			
		||||
  ORDER_RBG,
 | 
			
		||||
  ORDER_GRB,
 | 
			
		||||
  ORDER_GBR,
 | 
			
		||||
  ORDER_BGR,
 | 
			
		||||
  ORDER_BRG,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void write_state(light::LightState *state) override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 | 
			
		||||
  int32_t size() const override { return this->num_leds_; }
 | 
			
		||||
  light::LightTraits get_traits() override {
 | 
			
		||||
    auto traits = light::LightTraits();
 | 
			
		||||
    if (this->is_rgbw_) {
 | 
			
		||||
      traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE});
 | 
			
		||||
    } else {
 | 
			
		||||
      traits.set_supported_color_modes({light::ColorMode::RGB});
 | 
			
		||||
    }
 | 
			
		||||
    return traits;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_pin(uint8_t pin) { this->pin_ = pin; }
 | 
			
		||||
  void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; }
 | 
			
		||||
  void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; }
 | 
			
		||||
 | 
			
		||||
  /// Set a maximum refresh rate in µs as some lights do not like being updated too often.
 | 
			
		||||
  void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
 | 
			
		||||
 | 
			
		||||
  void set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, uint32_t bit1_low);
 | 
			
		||||
 | 
			
		||||
  void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
 | 
			
		||||
  void set_rmt_channel(rmt_channel_t channel) { this->channel_ = channel; }
 | 
			
		||||
 | 
			
		||||
  void clear_effect_data() override {
 | 
			
		||||
    for (int i = 0; i < this->size(); i++)
 | 
			
		||||
      this->effect_data_[i] = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  light::ESPColorView get_view_internal(int32_t index) const override;
 | 
			
		||||
 | 
			
		||||
  size_t get_buffer_size_() const { return this->num_leds_ * (3 + this->is_rgbw_); }
 | 
			
		||||
 | 
			
		||||
  uint8_t *buf_{nullptr};
 | 
			
		||||
  uint8_t *effect_data_{nullptr};
 | 
			
		||||
  rmt_item32_t *rmt_buf_{nullptr};
 | 
			
		||||
 | 
			
		||||
  uint8_t pin_;
 | 
			
		||||
  uint16_t num_leds_;
 | 
			
		||||
  bool is_rgbw_;
 | 
			
		||||
 | 
			
		||||
  rmt_item32_t bit0_, bit1_;
 | 
			
		||||
  RGBOrder rgb_order_;
 | 
			
		||||
  rmt_channel_t channel_;
 | 
			
		||||
 | 
			
		||||
  uint32_t last_refresh_{0};
 | 
			
		||||
  optional<uint32_t> max_refresh_rate_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace esp32_rmt_led_strip
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
							
								
								
									
										151
									
								
								esphome/components/esp32_rmt_led_strip/light.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								esphome/components/esp32_rmt_led_strip/light.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.components import esp32, light
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CHIPSET,
 | 
			
		||||
    CONF_MAX_REFRESH_RATE,
 | 
			
		||||
    CONF_NUM_LEDS,
 | 
			
		||||
    CONF_OUTPUT_ID,
 | 
			
		||||
    CONF_PIN,
 | 
			
		||||
    CONF_RGB_ORDER,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
DEPENDENCIES = ["esp32"]
 | 
			
		||||
 | 
			
		||||
esp32_rmt_led_strip_ns = cg.esphome_ns.namespace("esp32_rmt_led_strip")
 | 
			
		||||
ESP32RMTLEDStripLightOutput = esp32_rmt_led_strip_ns.class_(
 | 
			
		||||
    "ESP32RMTLEDStripLightOutput", light.AddressableLight
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
rmt_channel_t = cg.global_ns.enum("rmt_channel_t")
 | 
			
		||||
 | 
			
		||||
RGBOrder = esp32_rmt_led_strip_ns.enum("RGBOrder")
 | 
			
		||||
 | 
			
		||||
RGB_ORDERS = {
 | 
			
		||||
    "RGB": RGBOrder.ORDER_RGB,
 | 
			
		||||
    "RBG": RGBOrder.ORDER_RBG,
 | 
			
		||||
    "GRB": RGBOrder.ORDER_GRB,
 | 
			
		||||
    "GBR": RGBOrder.ORDER_GBR,
 | 
			
		||||
    "BGR": RGBOrder.ORDER_BGR,
 | 
			
		||||
    "BRG": RGBOrder.ORDER_BRG,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class LEDStripTimings:
 | 
			
		||||
    bit0_high: int
 | 
			
		||||
    bit0_low: int
 | 
			
		||||
    bit1_high: int
 | 
			
		||||
    bit1_low: int
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CHIPSETS = {
 | 
			
		||||
    "WS2812": LEDStripTimings(400, 1000, 1000, 400),
 | 
			
		||||
    "SK6812": LEDStripTimings(300, 900, 600, 600),
 | 
			
		||||
    "APA106": LEDStripTimings(350, 1360, 1360, 350),
 | 
			
		||||
    "SM16703": LEDStripTimings(300, 900, 1360, 350),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONF_IS_RGBW = "is_rgbw"
 | 
			
		||||
CONF_BIT0_HIGH = "bit0_high"
 | 
			
		||||
CONF_BIT0_LOW = "bit0_low"
 | 
			
		||||
CONF_BIT1_HIGH = "bit1_high"
 | 
			
		||||
CONF_BIT1_LOW = "bit1_low"
 | 
			
		||||
CONF_RMT_CHANNEL = "rmt_channel"
 | 
			
		||||
 | 
			
		||||
RMT_CHANNELS = {
 | 
			
		||||
    esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7],
 | 
			
		||||
    esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
 | 
			
		||||
    esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3],
 | 
			
		||||
    esp32.const.VARIANT_ESP32C3: [0, 1],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _validate_rmt_channel(value):
 | 
			
		||||
    variant = esp32.get_esp32_variant()
 | 
			
		||||
    if variant not in RMT_CHANNELS:
 | 
			
		||||
        raise cv.Invalid(f"ESP32 variant {variant} does not support RMT.")
 | 
			
		||||
    if value not in RMT_CHANNELS[variant]:
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            f"RMT channel {value} is not supported for ESP32 variant {variant}."
 | 
			
		||||
        )
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    light.ADDRESSABLE_LIGHT_SCHEMA.extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(ESP32RMTLEDStripLightOutput),
 | 
			
		||||
            cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
 | 
			
		||||
            cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
 | 
			
		||||
            cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
 | 
			
		||||
            cv.Required(CONF_RMT_CHANNEL): _validate_rmt_channel,
 | 
			
		||||
            cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
 | 
			
		||||
            cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
 | 
			
		||||
            cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
 | 
			
		||||
            cv.Inclusive(
 | 
			
		||||
                CONF_BIT0_HIGH,
 | 
			
		||||
                "custom",
 | 
			
		||||
            ): cv.positive_time_period_microseconds,
 | 
			
		||||
            cv.Inclusive(
 | 
			
		||||
                CONF_BIT0_LOW,
 | 
			
		||||
                "custom",
 | 
			
		||||
            ): cv.positive_time_period_microseconds,
 | 
			
		||||
            cv.Inclusive(
 | 
			
		||||
                CONF_BIT1_HIGH,
 | 
			
		||||
                "custom",
 | 
			
		||||
            ): cv.positive_time_period_microseconds,
 | 
			
		||||
            cv.Inclusive(
 | 
			
		||||
                CONF_BIT1_LOW,
 | 
			
		||||
                "custom",
 | 
			
		||||
            ): cv.positive_time_period_microseconds,
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
    cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
 | 
			
		||||
    await light.register_light(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_num_leds(config[CONF_NUM_LEDS]))
 | 
			
		||||
    cg.add(var.set_pin(config[CONF_PIN]))
 | 
			
		||||
 | 
			
		||||
    if CONF_MAX_REFRESH_RATE in config:
 | 
			
		||||
        cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
 | 
			
		||||
 | 
			
		||||
    if CONF_CHIPSET in config:
 | 
			
		||||
        chipset = CHIPSETS[config[CONF_CHIPSET]]
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_led_params(
 | 
			
		||||
                chipset.bit0_high,
 | 
			
		||||
                chipset.bit0_low,
 | 
			
		||||
                chipset.bit1_high,
 | 
			
		||||
                chipset.bit1_low,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    else:
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_led_params(
 | 
			
		||||
                config[CONF_BIT0_HIGH],
 | 
			
		||||
                config[CONF_BIT0_LOW],
 | 
			
		||||
                config[CONF_BIT1_HIGH],
 | 
			
		||||
                config[CONF_BIT1_LOW],
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_rgb_order(config[CONF_RGB_ORDER]))
 | 
			
		||||
    cg.add(var.set_is_rgbw(config[CONF_IS_RGBW]))
 | 
			
		||||
 | 
			
		||||
    cg.add(
 | 
			
		||||
        var.set_rmt_channel(
 | 
			
		||||
            getattr(rmt_channel_t, f"RMT_CHANNEL_{config[CONF_RMT_CHANNEL]}")
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
@@ -125,7 +125,7 @@ def _parse_platform_version(value):
 | 
			
		||||
    try:
 | 
			
		||||
        # if platform version is a valid version constraint, prefix the default package
 | 
			
		||||
        cv.platformio_version_constraint(value)
 | 
			
		||||
        return f"platformio/espressif8266 @ {value}"
 | 
			
		||||
        return f"platformio/espressif8266@{value}"
 | 
			
		||||
    except cv.Invalid:
 | 
			
		||||
        return value
 | 
			
		||||
 | 
			
		||||
@@ -181,7 +181,7 @@ async def to_code(config):
 | 
			
		||||
    cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION])
 | 
			
		||||
    cg.add_platformio_option(
 | 
			
		||||
        "platform_packages",
 | 
			
		||||
        [f"platformio/framework-arduinoespressif8266 @ {conf[CONF_SOURCE]}"],
 | 
			
		||||
        [f"platformio/framework-arduinoespressif8266@{conf[CONF_SOURCE]}"],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Default for platformio is LWIP2_LOW_MEMORY with:
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ ETHERNET_TYPES = {
 | 
			
		||||
    "DP83848": EthernetType.ETHERNET_TYPE_DP83848,
 | 
			
		||||
    "IP101": EthernetType.ETHERNET_TYPE_IP101,
 | 
			
		||||
    "JL1101": EthernetType.ETHERNET_TYPE_JL1101,
 | 
			
		||||
    "KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t")
 | 
			
		||||
 
 | 
			
		||||
@@ -26,8 +26,10 @@ EthernetComponent::EthernetComponent() { global_eth_component = this; }
 | 
			
		||||
 | 
			
		||||
void EthernetComponent::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Ethernet...");
 | 
			
		||||
  // Delay here to allow power to stabilise before Ethernet is initialised.
 | 
			
		||||
  if (esp_reset_reason() != ESP_RST_DEEPSLEEP) {
 | 
			
		||||
    // Delay here to allow power to stabilise before Ethernet is initialized.
 | 
			
		||||
    delay(300);  // NOLINT
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  esp_err_t err;
 | 
			
		||||
  err = esp_netif_init();
 | 
			
		||||
@@ -52,26 +54,29 @@ void EthernetComponent::setup() {
 | 
			
		||||
 | 
			
		||||
  esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
 | 
			
		||||
 | 
			
		||||
  esp_eth_phy_t *phy;
 | 
			
		||||
  switch (this->type_) {
 | 
			
		||||
    case ETHERNET_TYPE_LAN8720: {
 | 
			
		||||
      phy = esp_eth_phy_new_lan87xx(&phy_config);
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_lan87xx(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ETHERNET_TYPE_RTL8201: {
 | 
			
		||||
      phy = esp_eth_phy_new_rtl8201(&phy_config);
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_rtl8201(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ETHERNET_TYPE_DP83848: {
 | 
			
		||||
      phy = esp_eth_phy_new_dp83848(&phy_config);
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_dp83848(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ETHERNET_TYPE_IP101: {
 | 
			
		||||
      phy = esp_eth_phy_new_ip101(&phy_config);
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_ip101(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ETHERNET_TYPE_JL1101: {
 | 
			
		||||
      phy = esp_eth_phy_new_jl1101(&phy_config);
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_jl1101(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ETHERNET_TYPE_KSZ8081: {
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_ksz8081(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    default: {
 | 
			
		||||
@@ -80,7 +85,7 @@ void EthernetComponent::setup() {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
 | 
			
		||||
  esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, this->phy_);
 | 
			
		||||
  this->eth_handle_ = nullptr;
 | 
			
		||||
  err = esp_eth_driver_install(ð_config, &this->eth_handle_);
 | 
			
		||||
  ESPHL_ERROR_CHECK(err, "ETH driver install error");
 | 
			
		||||
@@ -140,7 +145,7 @@ void EthernetComponent::loop() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EthernetComponent::dump_config() {
 | 
			
		||||
  std::string eth_type;
 | 
			
		||||
  const char *eth_type;
 | 
			
		||||
  switch (this->type_) {
 | 
			
		||||
    case ETHERNET_TYPE_LAN8720:
 | 
			
		||||
      eth_type = "LAN8720";
 | 
			
		||||
@@ -158,6 +163,14 @@ void EthernetComponent::dump_config() {
 | 
			
		||||
      eth_type = "IP101";
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case ETHERNET_TYPE_JL1101:
 | 
			
		||||
      eth_type = "JL1101";
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case ETHERNET_TYPE_KSZ8081:
 | 
			
		||||
      eth_type = "KSZ8081";
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      eth_type = "Unknown";
 | 
			
		||||
      break;
 | 
			
		||||
@@ -170,7 +183,8 @@ void EthernetComponent::dump_config() {
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  MDC Pin: %u", this->mdc_pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  MDIO Pin: %u", this->mdio_pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Type: %s", eth_type.c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Type: %s", eth_type);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  PHY addr: %u", this->phy_addr_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; }
 | 
			
		||||
@@ -263,7 +277,7 @@ void EthernetComponent::start_connect_() {
 | 
			
		||||
#endif
 | 
			
		||||
      dns_setserver(0, &d);
 | 
			
		||||
    }
 | 
			
		||||
    if (uint32_t(this->manual_ip_->dns1) != 0) {
 | 
			
		||||
    if (uint32_t(this->manual_ip_->dns2) != 0) {
 | 
			
		||||
      ip_addr_t d;
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
      d.type = IPADDR_TYPE_V4;
 | 
			
		||||
@@ -343,6 +357,21 @@ std::string EthernetComponent::get_use_address() const {
 | 
			
		||||
 | 
			
		||||
void EthernetComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; }
 | 
			
		||||
 | 
			
		||||
bool EthernetComponent::powerdown() {
 | 
			
		||||
  ESP_LOGI(TAG, "Powering down ethernet PHY");
 | 
			
		||||
  if (this->phy_ == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "Ethernet PHY not assigned");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  this->connected_ = false;
 | 
			
		||||
  this->started_ = false;
 | 
			
		||||
  if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Error powering down ethernet PHY");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ethernet
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,11 +14,13 @@ namespace esphome {
 | 
			
		||||
namespace ethernet {
 | 
			
		||||
 | 
			
		||||
enum EthernetType {
 | 
			
		||||
  ETHERNET_TYPE_LAN8720 = 0,
 | 
			
		||||
  ETHERNET_TYPE_UNKNOWN = 0,
 | 
			
		||||
  ETHERNET_TYPE_LAN8720,
 | 
			
		||||
  ETHERNET_TYPE_RTL8201,
 | 
			
		||||
  ETHERNET_TYPE_DP83848,
 | 
			
		||||
  ETHERNET_TYPE_IP101,
 | 
			
		||||
  ETHERNET_TYPE_JL1101,
 | 
			
		||||
  ETHERNET_TYPE_KSZ8081,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ManualIP {
 | 
			
		||||
@@ -43,6 +45,7 @@ class EthernetComponent : public Component {
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  bool can_proceed() override;
 | 
			
		||||
  void on_shutdown() override { powerdown(); }
 | 
			
		||||
  bool is_connected();
 | 
			
		||||
 | 
			
		||||
  void set_phy_addr(uint8_t phy_addr);
 | 
			
		||||
@@ -56,6 +59,7 @@ class EthernetComponent : public Component {
 | 
			
		||||
  network::IPAddress get_ip_address();
 | 
			
		||||
  std::string get_use_address() const;
 | 
			
		||||
  void set_use_address(const std::string &use_address);
 | 
			
		||||
  bool powerdown();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
 | 
			
		||||
@@ -69,7 +73,7 @@ class EthernetComponent : public Component {
 | 
			
		||||
  int power_pin_{-1};
 | 
			
		||||
  uint8_t mdc_pin_{23};
 | 
			
		||||
  uint8_t mdio_pin_{18};
 | 
			
		||||
  EthernetType type_{ETHERNET_TYPE_LAN8720};
 | 
			
		||||
  EthernetType type_{ETHERNET_TYPE_UNKNOWN};
 | 
			
		||||
  emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN};
 | 
			
		||||
  emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO};
 | 
			
		||||
  optional<ManualIP> manual_ip_{};
 | 
			
		||||
@@ -80,6 +84,7 @@ class EthernetComponent : public Component {
 | 
			
		||||
  uint32_t connect_begin_;
 | 
			
		||||
  esp_netif_t *eth_netif_{nullptr};
 | 
			
		||||
  esp_eth_handle_t eth_handle_;
 | 
			
		||||
  esp_eth_phy_t *phy_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 
 | 
			
		||||
@@ -106,20 +106,18 @@ void EZOSensor::loop() {
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Received buffer \"%s\" for command type %s", buf, EZO_COMMAND_TYPE_STRINGS[to_run->command_type]);
 | 
			
		||||
  ESP_LOGV(TAG, "Received buffer \"%s\" for command type %s", &buf[1], EZO_COMMAND_TYPE_STRINGS[to_run->command_type]);
 | 
			
		||||
 | 
			
		||||
  if ((buf[0] == 1) || (to_run->command_type == EzoCommandType::EZO_CALIBRATION)) {  // EZO_CALIBRATION returns 0-3
 | 
			
		||||
    // some sensors return multiple comma-separated values, terminate string after first one
 | 
			
		||||
    for (size_t i = 1; i < sizeof(buf) - 1; i++) {
 | 
			
		||||
      if (buf[i] == ',') {
 | 
			
		||||
        buf[i] = '\0';
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  if (buf[0] == 1) {
 | 
			
		||||
    std::string payload = reinterpret_cast<char *>(&buf[1]);
 | 
			
		||||
    if (!payload.empty()) {
 | 
			
		||||
      switch (to_run->command_type) {
 | 
			
		||||
        case EzoCommandType::EZO_READ: {
 | 
			
		||||
          // some sensors return multiple comma-separated values, terminate string after first one
 | 
			
		||||
          int start_location = 0;
 | 
			
		||||
          if ((start_location = payload.find(',')) != std::string::npos) {
 | 
			
		||||
            payload.erase(start_location);
 | 
			
		||||
          }
 | 
			
		||||
          auto val = parse_number<float>(payload);
 | 
			
		||||
          if (!val.has_value()) {
 | 
			
		||||
            ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
 | 
			
		||||
@@ -154,7 +152,10 @@ void EZOSensor::loop() {
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        case EzoCommandType::EZO_T: {
 | 
			
		||||
          this->t_callback_.call(payload);
 | 
			
		||||
          int start_location = 0;
 | 
			
		||||
          if ((start_location = payload.find(',')) != std::string::npos) {
 | 
			
		||||
            this->t_callback_.call(payload.substr(start_location + 1));
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        case EzoCommandType::EZO_CUSTOM: {
 | 
			
		||||
 
 | 
			
		||||
@@ -12,14 +12,14 @@ static const char *const TAG = "ezo.sensor";
 | 
			
		||||
 | 
			
		||||
enum EzoCommandType : uint8_t {
 | 
			
		||||
  EZO_READ = 0,
 | 
			
		||||
  EZO_LED = 1,
 | 
			
		||||
  EZO_DEVICE_INFORMATION = 2,
 | 
			
		||||
  EZO_SLOPE = 3,
 | 
			
		||||
  EZO_LED,
 | 
			
		||||
  EZO_DEVICE_INFORMATION,
 | 
			
		||||
  EZO_SLOPE,
 | 
			
		||||
  EZO_CALIBRATION,
 | 
			
		||||
  EZO_SLEEP = 4,
 | 
			
		||||
  EZO_I2C = 5,
 | 
			
		||||
  EZO_T = 6,
 | 
			
		||||
  EZO_CUSTOM = 7
 | 
			
		||||
  EZO_SLEEP,
 | 
			
		||||
  EZO_I2C,
 | 
			
		||||
  EZO_T,
 | 
			
		||||
  EZO_CUSTOM
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum EzoCalibrationType : uint8_t { EZO_CAL_LOW = 0, EZO_CAL_MID = 1, EZO_CAL_HIGH = 2 };
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ FanIsOffCondition = fan_ns.class_("FanIsOffCondition", automation.Condition.temp
 | 
			
		||||
FAN_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(Fan),
 | 
			
		||||
        cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum(
 | 
			
		||||
        cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
 | 
			
		||||
            RESTORE_MODES, upper=True, space="_"
 | 
			
		||||
        ),
 | 
			
		||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent),
 | 
			
		||||
 
 | 
			
		||||
@@ -80,9 +80,6 @@ void FanRestoreState::apply(Fan &fan) {
 | 
			
		||||
  fan.publish_state();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Fan::Fan() : EntityBase("") {}
 | 
			
		||||
Fan::Fan(const std::string &name) : EntityBase(name) {}
 | 
			
		||||
 | 
			
		||||
FanCall Fan::turn_on() { return this->make_call().set_state(true); }
 | 
			
		||||
FanCall Fan::turn_off() { return this->make_call().set_state(false); }
 | 
			
		||||
FanCall Fan::toggle() { return this->make_call().set_state(!this->state); }
 | 
			
		||||
 
 | 
			
		||||
@@ -99,10 +99,6 @@ struct FanRestoreState {
 | 
			
		||||
 | 
			
		||||
class Fan : public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  Fan();
 | 
			
		||||
  /// Construct the fan with name.
 | 
			
		||||
  explicit Fan(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /// The current on/off state of the fan.
 | 
			
		||||
  bool state{false};
 | 
			
		||||
  /// The current oscillation state of the fan.
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@ enum ESPDEPRECATED("LegacyFanDirection members are deprecated, use FanDirection
 | 
			
		||||
class ESPDEPRECATED("FanState is deprecated, use Fan instead.", "2022.2") FanState : public Fan, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  FanState() = default;
 | 
			
		||||
  explicit FanState(const std::string &name) : Fan(name) {}
 | 
			
		||||
 | 
			
		||||
  /// Get the traits of this fan.
 | 
			
		||||
  FanTraits get_traits() override { return this->traits_; }
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,7 @@ void FeedbackCover::setup() {
 | 
			
		||||
 | 
			
		||||
CoverTraits FeedbackCover::get_traits() {
 | 
			
		||||
  auto traits = CoverTraits();
 | 
			
		||||
  traits.set_supports_stop(true);
 | 
			
		||||
  traits.set_supports_position(true);
 | 
			
		||||
  traits.set_supports_toggle(true);
 | 
			
		||||
  traits.set_is_assumed_state(this->assumed_state_);
 | 
			
		||||
 
 | 
			
		||||
@@ -77,10 +77,12 @@ void FingerprintGrowComponent::finish_enrollment(uint8_t result) {
 | 
			
		||||
    this->enrollment_done_callback_.call(this->enrollment_slot_);
 | 
			
		||||
    this->get_fingerprint_count_();
 | 
			
		||||
  } else {
 | 
			
		||||
    if (this->enrollment_slot_ != ENROLLMENT_SLOT_UNUSED) {
 | 
			
		||||
      this->enrollment_failed_callback_.call(this->enrollment_slot_);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  this->enrollment_image_ = 0;
 | 
			
		||||
  this->enrollment_slot_ = 0;
 | 
			
		||||
  this->enrollment_slot_ = ENROLLMENT_SLOT_UNUSED;
 | 
			
		||||
  if (this->enrolling_binary_sensor_ != nullptr) {
 | 
			
		||||
    this->enrolling_binary_sensor_->publish_state(false);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@ namespace fingerprint_grow {
 | 
			
		||||
 | 
			
		||||
static const uint16_t START_CODE = 0xEF01;
 | 
			
		||||
 | 
			
		||||
static const uint16_t ENROLLMENT_SLOT_UNUSED = 0xFFFF;
 | 
			
		||||
 | 
			
		||||
enum GrowPacketType {
 | 
			
		||||
  COMMAND = 0x01,
 | 
			
		||||
  DATA = 0x02,
 | 
			
		||||
@@ -158,7 +160,7 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
 | 
			
		||||
  uint32_t new_password_ = -1;
 | 
			
		||||
  GPIOPin *sensing_pin_{nullptr};
 | 
			
		||||
  uint8_t enrollment_image_ = 0;
 | 
			
		||||
  uint16_t enrollment_slot_ = 0;
 | 
			
		||||
  uint16_t enrollment_slot_ = ENROLLMENT_SLOT_UNUSED;
 | 
			
		||||
  uint8_t enrollment_buffers_ = 5;
 | 
			
		||||
  bool waiting_removal_ = false;
 | 
			
		||||
  uint32_t last_aura_led_control_ = 0;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										40
									
								
								esphome/components/gp8403/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								esphome/components/gp8403/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
 | 
			
		||||
from esphome.components import i2c
 | 
			
		||||
from esphome.const import CONF_ID, CONF_VOLTAGE
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
gp8403_ns = cg.esphome_ns.namespace("gp8403")
 | 
			
		||||
GP8403 = gp8403_ns.class_("GP8403", cg.Component, i2c.I2CDevice)
 | 
			
		||||
 | 
			
		||||
GP8403Voltage = gp8403_ns.enum("GP8403Voltage")
 | 
			
		||||
 | 
			
		||||
CONF_GP8403_ID = "gp8403_id"
 | 
			
		||||
 | 
			
		||||
VOLTAGES = {
 | 
			
		||||
    "5V": GP8403Voltage.GP8403_VOLTAGE_5V,
 | 
			
		||||
    "10V": GP8403Voltage.GP8403_VOLTAGE_10V,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(GP8403),
 | 
			
		||||
            cv.Required(CONF_VOLTAGE): cv.enum(VOLTAGES, upper=True),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
    .extend(i2c.i2c_device_schema(0x58))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_voltage(config[CONF_VOLTAGE]))
 | 
			
		||||
							
								
								
									
										21
									
								
								esphome/components/gp8403/gp8403.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								esphome/components/gp8403/gp8403.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
#include "gp8403.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace gp8403 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "gp8403";
 | 
			
		||||
 | 
			
		||||
static const uint8_t RANGE_REGISTER = 0x01;
 | 
			
		||||
 | 
			
		||||
void GP8403::setup() { this->write_register(RANGE_REGISTER, (uint8_t *) (&this->voltage_), 1); }
 | 
			
		||||
 | 
			
		||||
void GP8403::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "GP8403:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Voltage: %dV", this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10);
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace gp8403
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										27
									
								
								esphome/components/gp8403/gp8403.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/gp8403/gp8403.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace gp8403 {
 | 
			
		||||
 | 
			
		||||
enum GP8403Voltage {
 | 
			
		||||
  GP8403_VOLTAGE_5V = 0x00,
 | 
			
		||||
  GP8403_VOLTAGE_10V = 0x11,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GP8403 : public Component, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
  void set_voltage(gp8403::GP8403Voltage voltage) { this->voltage_ = voltage; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  GP8403Voltage voltage_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace gp8403
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										31
									
								
								esphome/components/gp8403/output/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								esphome/components/gp8403/output/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
 | 
			
		||||
from esphome.components import i2c, output
 | 
			
		||||
from esphome.const import CONF_ID, CONF_CHANNEL
 | 
			
		||||
 | 
			
		||||
from .. import gp8403_ns, GP8403, CONF_GP8403_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["gp8403"]
 | 
			
		||||
 | 
			
		||||
GP8403Output = gp8403_ns.class_(
 | 
			
		||||
    "GP8403Output", cg.Component, i2c.I2CDevice, output.FloatOutput
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(GP8403Output),
 | 
			
		||||
        cv.GenerateID(CONF_GP8403_ID): cv.use_id(GP8403),
 | 
			
		||||
        cv.Required(CONF_CHANNEL): cv.one_of(0, 1),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await output.register_output(var, config)
 | 
			
		||||
 | 
			
		||||
    await cg.register_parented(var, config[CONF_GP8403_ID])
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_channel(config[CONF_CHANNEL]))
 | 
			
		||||
							
								
								
									
										26
									
								
								esphome/components/gp8403/output/gp8403_output.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								esphome/components/gp8403/output/gp8403_output.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#include "gp8403_output.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace gp8403 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "gp8403.output";
 | 
			
		||||
 | 
			
		||||
static const uint8_t OUTPUT_REGISTER = 0x02;
 | 
			
		||||
 | 
			
		||||
void GP8403Output::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "GP8403 Output:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Channel: %u", this->channel_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GP8403Output::write_state(float state) {
 | 
			
		||||
  uint16_t value = ((uint16_t) (state * 4095)) << 4;
 | 
			
		||||
  i2c::ErrorCode err = this->parent_->write_register(OUTPUT_REGISTER + (2 * this->channel_), (uint8_t *) &value, 2);
 | 
			
		||||
  if (err != i2c::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Error writing to GP8403, code %d", err);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace gp8403
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										25
									
								
								esphome/components/gp8403/output/gp8403_output.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								esphome/components/gp8403/output/gp8403_output.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/output/float_output.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/gp8403/gp8403.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace gp8403 {
 | 
			
		||||
 | 
			
		||||
class GP8403Output : public Component, public output::FloatOutput, public Parented<GP8403> {
 | 
			
		||||
 public:
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA - 1; }
 | 
			
		||||
 | 
			
		||||
  void set_channel(uint8_t channel) { this->channel_ = channel; }
 | 
			
		||||
 | 
			
		||||
  void write_state(float state) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint8_t channel_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace gp8403
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user