mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-03 00:21:56 +00:00 
			
		
		
		
	Compare commits
	
		
			281 Commits
		
	
	
		
			2022.8.0b3
			...
			2022.11.0b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					58fda40389 | ||
| 
						 | 
					6a73699a38 | ||
| 
						 | 
					3bd6456fbe | ||
| 
						 | 
					608be4e050 | ||
| 
						 | 
					10f590324b | ||
| 
						 | 
					39f0f748bf | ||
| 
						 | 
					9efe59a984 | ||
| 
						 | 
					fcb02af782 | ||
| 
						 | 
					3aeef1afd4 | ||
| 
						 | 
					a45ee8f4ac | ||
| 
						 | 
					31b62d7dca | ||
| 
						 | 
					22f81475db | ||
| 
						 | 
					cc7cf73d59 | ||
| 
						 | 
					9682e60a25 | ||
| 
						 | 
					c6afae0da5 | ||
| 
						 | 
					4fa0e860ad | ||
| 
						 | 
					8c122aa372 | ||
| 
						 | 
					5a0bf9fee9 | ||
| 
						 | 
					dc794918ed | ||
| 
						 | 
					02b15dbc4a | ||
| 
						 | 
					ed316b1ce3 | ||
| 
						 | 
					d7858f16c1 | ||
| 
						 | 
					291deb12ad | ||
| 
						 | 
					3e110681c9 | ||
| 
						 | 
					65fbfa2097 | ||
| 
						 | 
					16ebf9da4c | ||
| 
						 | 
					2c76381fcd | ||
| 
						 | 
					90683223dd | ||
| 
						 | 
					de79171815 | ||
| 
						 | 
					1554c5700e | ||
| 
						 | 
					5cf257b251 | ||
| 
						 | 
					04883e14f6 | ||
| 
						 | 
					0a649c184f | ||
| 
						 | 
					0e66c899ce | ||
| 
						 | 
					e7d236f939 | ||
| 
						 | 
					fae4d03473 | ||
| 
						 | 
					97bd3e7320 | ||
| 
						 | 
					dfca2f88d3 | ||
| 
						 | 
					8cad93de37 | ||
| 
						 | 
					e5b8dd7f2d | ||
| 
						 | 
					a1c8b8092b | ||
| 
						 | 
					109ca2406d | ||
| 
						 | 
					3a689112fd | ||
| 
						 | 
					40e0cd0f03 | ||
| 
						 | 
					bf4d3df906 | ||
| 
						 | 
					0e30c49e3f | ||
| 
						 | 
					e61a01f7bb | ||
| 
						 | 
					f8640cf2cd | ||
| 
						 | 
					4bcfeb6e33 | ||
| 
						 | 
					a5d4ca0f6d | ||
| 
						 | 
					85faecb2fd | ||
| 
						 | 
					991fc54994 | ||
| 
						 | 
					2de891dc32 | ||
| 
						 | 
					9865cb7f55 | ||
| 
						 | 
					f97252b93a | ||
| 
						 | 
					f2c4f018de | ||
| 
						 | 
					237c7dd169 | ||
| 
						 | 
					b781b8d77d | ||
| 
						 | 
					60b7d1c8a1 | ||
| 
						 | 
					8161222b33 | ||
| 
						 | 
					1000c4466f | ||
| 
						 | 
					60717b074e | ||
| 
						 | 
					c3fba97b4c | ||
| 
						 | 
					d93f35701f | ||
| 
						 | 
					702b60ce66 | ||
| 
						 | 
					f8ce597918 | ||
| 
						 | 
					22e0a944c8 | ||
| 
						 | 
					3a134ef009 | ||
| 
						 | 
					96e8cb66b6 | ||
| 
						 | 
					615288c151 | ||
| 
						 | 
					6153bcc6ad | ||
| 
						 | 
					e87edcc77a | ||
| 
						 | 
					a21c3e8e2d | ||
| 
						 | 
					d7576f67e8 | ||
| 
						 | 
					138de643a2 | ||
| 
						 | 
					f30e54d177 | ||
| 
						 | 
					41b5cb06d3 | ||
| 
						 | 
					4ac72d7d08 | ||
| 
						 | 
					06ac4980ba | ||
| 
						 | 
					ccbfa20bb9 | ||
| 
						 | 
					58cd754e07 | ||
| 
						 | 
					d1263e583b | ||
| 
						 | 
					67c911c37f | ||
| 
						 | 
					288e3c3e3e | ||
| 
						 | 
					a84378c6c2 | ||
| 
						 | 
					b6073408f4 | ||
| 
						 | 
					b2d91ac5de | ||
| 
						 | 
					8bf34e09f4 | ||
| 
						 | 
					be914f2c15 | ||
| 
						 | 
					8bb670521d | ||
| 
						 | 
					225b3c1494 | ||
| 
						 | 
					4bf94e0757 | ||
| 
						 | 
					3b21d1d81e | ||
| 
						 | 
					5ec1588110 | ||
| 
						 | 
					71387be72e | ||
| 
						 | 
					98171c9f49 | ||
| 
						 | 
					a6c999dea0 | ||
| 
						 | 
					bf15b1d302 | ||
| 
						 | 
					f422fabab4 | ||
| 
						 | 
					01b130ec59 | ||
| 
						 | 
					48a1797e72 | ||
| 
						 | 
					b34d24735a | ||
| 
						 | 
					fe38b36c26 | ||
| 
						 | 
					03fca8d91e | ||
| 
						 | 
					9f9980e338 | ||
| 
						 | 
					19900b004b | ||
| 
						 | 
					a8ff0a8913 | ||
| 
						 | 
					45861456f1 | ||
| 
						 | 
					44b335e7e3 | ||
| 
						 | 
					de23bbace2 | ||
| 
						 | 
					edff9ae322 | ||
| 
						 | 
					3c2766448d | ||
| 
						 | 
					786c8b6cfe | ||
| 
						 | 
					e8de6a3a67 | ||
| 
						 | 
					3c320c4c83 | ||
| 
						 | 
					3b83f967e4 | ||
| 
						 | 
					5e96b8ef7c | ||
| 
						 | 
					5df0e82c37 | ||
| 
						 | 
					fd57b21aff | ||
| 
						 | 
					6087183a0c | ||
| 
						 | 
					01b7c4200e | ||
| 
						 | 
					7171286c3c | ||
| 
						 | 
					2d58239b74 | ||
| 
						 | 
					6b52f62531 | ||
| 
						 | 
					1001d9c04e | ||
| 
						 | 
					d220d41182 | ||
| 
						 | 
					c3a8972550 | ||
| 
						 | 
					263b603188 | ||
| 
						 | 
					584b722e7e | ||
| 
						 | 
					05edfd0e82 | ||
| 
						 | 
					16249c02a5 | ||
| 
						 | 
					e8ff36d1f3 | ||
| 
						 | 
					ed443c6153 | ||
| 
						 | 
					f4a84765cd | ||
| 
						 | 
					106de3530d | ||
| 
						 | 
					119c3f6f46 | ||
| 
						 | 
					d2c1c7507c | ||
| 
						 | 
					efdb3d1f40 | ||
| 
						 | 
					8095db6715 | ||
| 
						 | 
					ce2e161b08 | ||
| 
						 | 
					9dbc32b85f | ||
| 
						 | 
					66226abb48 | ||
| 
						 | 
					34bef2f2ca | ||
| 
						 | 
					6ef93452f5 | ||
| 
						 | 
					fdd4ca6837 | ||
| 
						 | 
					9655362f23 | ||
| 
						 | 
					130c9fad22 | ||
| 
						 | 
					3de0b601bf | ||
| 
						 | 
					91560ae4e9 | ||
| 
						 | 
					fd6135aebb | ||
| 
						 | 
					68ea59f3ae | ||
| 
						 | 
					e5fe5d1249 | ||
| 
						 | 
					9a69769a7e | ||
| 
						 | 
					63b42f3608 | ||
| 
						 | 
					d56107e97f | ||
| 
						 | 
					33f296e05b | ||
| 
						 | 
					3572c62315 | ||
| 
						 | 
					97e067a277 | ||
| 
						 | 
					1444cddda9 | ||
| 
						 | 
					5f56cf3128 | ||
| 
						 | 
					5c4e83ebdc | ||
| 
						 | 
					91f1c25fcc | ||
| 
						 | 
					47a7a239ae | ||
| 
						 | 
					fb9984e21f | ||
| 
						 | 
					b2db524366 | ||
| 
						 | 
					ab8674a5c7 | ||
| 
						 | 
					d1c85fc3fa | ||
| 
						 | 
					55ad45e3ee | ||
| 
						 | 
					f6e5a8cb2a | ||
| 
						 | 
					7a91ca9809 | ||
| 
						 | 
					71dd04b09e | ||
| 
						 | 
					7deabbb512 | ||
| 
						 | 
					625a575e49 | ||
| 
						 | 
					df4d0da221 | ||
| 
						 | 
					6b23b7cad7 | ||
| 
						 | 
					cea7deab91 | ||
| 
						 | 
					c61abf6aca | ||
| 
						 | 
					917bbc669c | ||
| 
						 | 
					0ac4c055de | ||
| 
						 | 
					78b55d86e9 | ||
| 
						 | 
					aaf50fc2e6 | ||
| 
						 | 
					6a8f4e92df | ||
| 
						 | 
					6d267fda01 | ||
| 
						 | 
					88943103a2 | ||
| 
						 | 
					e557dc7208 | ||
| 
						 | 
					3558806b0e | ||
| 
						 | 
					f1e8cc2cf0 | ||
| 
						 | 
					6236db1a27 | ||
| 
						 | 
					f4b0917239 | ||
| 
						 | 
					a5e3cd1a42 | ||
| 
						 | 
					b3cca5dcb6 | ||
| 
						 | 
					49465223a4 | ||
| 
						 | 
					15f0e54cbf | ||
| 
						 | 
					ed8f343aad | ||
| 
						 | 
					cbd8d70431 | ||
| 
						 | 
					9ff187c3f8 | ||
| 
						 | 
					be473b97c4 | ||
| 
						 | 
					9a5f865eea | ||
| 
						 | 
					790280ace9 | ||
| 
						 | 
					8ba207fc7f | ||
| 
						 | 
					d66b2a1778 | ||
| 
						 | 
					e3f2562047 | ||
| 
						 | 
					f77118a90c | ||
| 
						 | 
					041eb8f6cc | ||
| 
						 | 
					733a84df75 | ||
| 
						 | 
					acd0b50b40 | ||
| 
						 | 
					635851807a | ||
| 
						 | 
					89fd367297 | ||
| 
						 | 
					60e46d485e | ||
| 
						 | 
					5bf0c92318 | ||
| 
						 | 
					39d493c278 | ||
| 
						 | 
					d2ce62aa13 | ||
| 
						 | 
					c8eb30ef27 | ||
| 
						 | 
					c317422ed7 | ||
| 
						 | 
					614eb81ad7 | ||
| 
						 | 
					219c5953f1 | ||
| 
						 | 
					5d712c73ea | ||
| 
						 | 
					7097b7677e | ||
| 
						 | 
					7a4cf13e0c | ||
| 
						 | 
					4788a6182e | ||
| 
						 | 
					e3dad7c632 | ||
| 
						 | 
					1b4156646e | ||
| 
						 | 
					31ad75d01b | ||
| 
						 | 
					aa2eb29274 | ||
| 
						 | 
					22eb4f9cb9 | ||
| 
						 | 
					3acc8e7479 | ||
| 
						 | 
					2650441013 | ||
| 
						 | 
					71697df2b6 | ||
| 
						 | 
					acd55b9601 | ||
| 
						 | 
					0907de8662 | ||
| 
						 | 
					15eb9605a8 | ||
| 
						 | 
					6d5cb866db | ||
| 
						 | 
					768490089e | ||
| 
						 | 
					4d66fab360 | ||
| 
						 | 
					bd6bc283b6 | ||
| 
						 | 
					3120a0ba83 | ||
| 
						 | 
					b2199d5464 | ||
| 
						 | 
					84bac8356a | ||
| 
						 | 
					2819166539 | ||
| 
						 | 
					8fa18ca7c7 | ||
| 
						 | 
					63290a265c | ||
| 
						 | 
					b854e17995 | ||
| 
						 | 
					5dec9d88f6 | ||
| 
						 | 
					3d0a85ee78 | ||
| 
						 | 
					ac3cdf487f | ||
| 
						 | 
					a47e92f2bc | ||
| 
						 | 
					fc15ddfa91 | ||
| 
						 | 
					d546ef941f | ||
| 
						 | 
					b4bbe3d7b5 | ||
| 
						 | 
					5561d4eaeb | ||
| 
						 | 
					0f6dab394a | ||
| 
						 | 
					adc8c1aa38 | ||
| 
						 | 
					3a82f500d4 | ||
| 
						 | 
					7d1d4831a8 | ||
| 
						 | 
					43539f2dbf | ||
| 
						 | 
					df6830110d | ||
| 
						 | 
					1a524a5a50 | ||
| 
						 | 
					1a2288cccf | ||
| 
						 | 
					8df27d4c3f | ||
| 
						 | 
					0688deca6b | ||
| 
						 | 
					01a4443b6c | ||
| 
						 | 
					e008b054cb | ||
| 
						 | 
					1cf213dad8 | ||
| 
						 | 
					aeb81e547b | ||
| 
						 | 
					479f7703a2 | ||
| 
						 | 
					c7dc396b6d | ||
| 
						 | 
					80e3030811 | ||
| 
						 | 
					ab86ddcf02 | ||
| 
						 | 
					bf8eddb13b | ||
| 
						 | 
					e5eaf7a3fe | ||
| 
						 | 
					50f32a3aa5 | ||
| 
						 | 
					eb878710c1 | ||
| 
						 | 
					311980e0e4 | ||
| 
						 | 
					df73170e5a | ||
| 
						 | 
					ccc13cc9e1 | ||
| 
						 | 
					020b2c05c8 | ||
| 
						 | 
					4c37c17df1 | ||
| 
						 | 
					8f67acadd8 | ||
| 
						 | 
					ca13c4c1a6 | ||
| 
						 | 
					d0c646c721 | ||
| 
						 | 
					8a055675af | 
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "ESPHome Dev",
 | 
			
		||||
  "image": "esphome/esphome-lint:dev",
 | 
			
		||||
  "image": "ghcr.io/esphome/esphome-lint:dev",
 | 
			
		||||
  "postCreateCommand": [
 | 
			
		||||
    "script/devcontainer-post-create"
 | 
			
		||||
  ],
 | 
			
		||||
 
 | 
			
		||||
@@ -25,10 +25,9 @@ indent_size = 2
 | 
			
		||||
[*.{yaml,yml}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 2
 | 
			
		||||
quote_type = single
 | 
			
		||||
quote_type = double
 | 
			
		||||
 | 
			
		||||
# JSON
 | 
			
		||||
[*.json]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 2
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,4 @@
 | 
			
		||||
---
 | 
			
		||||
# These are supported funding model platforms
 | 
			
		||||
 | 
			
		||||
custom: https://www.nabucasa.com
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,4 @@
 | 
			
		||||
---
 | 
			
		||||
blank_issues_enabled: false
 | 
			
		||||
contact_links:
 | 
			
		||||
  - name: Issue Tracker
 | 
			
		||||
@@ -5,7 +6,10 @@ contact_links:
 | 
			
		||||
    about: Please create bug reports in the dedicated issue tracker.
 | 
			
		||||
  - name: Feature Request Tracker
 | 
			
		||||
    url: https://github.com/esphome/feature-requests
 | 
			
		||||
    about: Please create feature requests in the dedicated feature request tracker.
 | 
			
		||||
    about: |
 | 
			
		||||
      Please create feature requests in the dedicated feature request tracker.
 | 
			
		||||
  - name: Frequently Asked Question
 | 
			
		||||
    url: https://esphome.io/guides/faq.html
 | 
			
		||||
    about: Please view the FAQ for common questions and what to include in a bug report.
 | 
			
		||||
    about: |
 | 
			
		||||
      Please view the FAQ for common questions and what
 | 
			
		||||
      to include in a bug report.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
# What does this implement/fix?
 | 
			
		||||
 | 
			
		||||
Quick description and explanation of changes
 | 
			
		||||
<!-- Quick description and explanation of changes -->
 | 
			
		||||
 | 
			
		||||
## Types of changes
 | 
			
		||||
 | 
			
		||||
@@ -18,6 +18,7 @@ Quick description and explanation of changes
 | 
			
		||||
- [ ] ESP32
 | 
			
		||||
- [ ] ESP32 IDF
 | 
			
		||||
- [ ] ESP8266
 | 
			
		||||
- [ ] RP2040
 | 
			
		||||
 | 
			
		||||
## Example entry for `config.yaml`:
 | 
			
		||||
<!--
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,13 +1,14 @@
 | 
			
		||||
---
 | 
			
		||||
version: 2
 | 
			
		||||
updates:
 | 
			
		||||
  - package-ecosystem: "pip"
 | 
			
		||||
  - package-ecosystem: pip
 | 
			
		||||
    directory: "/"
 | 
			
		||||
    schedule:
 | 
			
		||||
      interval: "daily"
 | 
			
		||||
      interval: daily
 | 
			
		||||
    ignore:
 | 
			
		||||
      # Hypotehsis is only used for testing and is updated quite often
 | 
			
		||||
      - dependency-name: hypothesis
 | 
			
		||||
  - package-ecosystem: "github-actions"
 | 
			
		||||
  - package-ecosystem: github-actions
 | 
			
		||||
    directory: "/"
 | 
			
		||||
    schedule:
 | 
			
		||||
      interval: daily
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										57
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										57
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,21 +1,23 @@
 | 
			
		||||
---
 | 
			
		||||
name: CI for docker images
 | 
			
		||||
 | 
			
		||||
# Only run when docker paths change
 | 
			
		||||
# yamllint disable-line rule:truthy
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [dev, beta, release]
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'docker/**'
 | 
			
		||||
      - '.github/workflows/**'
 | 
			
		||||
      - 'requirements*.txt'
 | 
			
		||||
      - 'platformio.ini'
 | 
			
		||||
      - "docker/**"
 | 
			
		||||
      - ".github/workflows/**"
 | 
			
		||||
      - "requirements*.txt"
 | 
			
		||||
      - "platformio.ini"
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'docker/**'
 | 
			
		||||
      - '.github/workflows/**'
 | 
			
		||||
      - 'requirements*.txt'
 | 
			
		||||
      - 'platformio.ini'
 | 
			
		||||
      - "docker/**"
 | 
			
		||||
      - ".github/workflows/**"
 | 
			
		||||
      - "requirements*.txt"
 | 
			
		||||
      - "platformio.ini"
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
@@ -26,28 +28,29 @@ jobs:
 | 
			
		||||
    name: Build docker containers
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        arch: [amd64, armv7, aarch64]
 | 
			
		||||
        build_type: ["ha-addon", "docker", "lint"]
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Set up Python
 | 
			
		||||
      uses: actions/setup-python@v4
 | 
			
		||||
      with:
 | 
			
		||||
        python-version: '3.9'
 | 
			
		||||
    - name: Set up Docker Buildx
 | 
			
		||||
      uses: docker/setup-buildx-action@v2
 | 
			
		||||
    - name: Set up QEMU
 | 
			
		||||
      uses: docker/setup-qemu-action@v2
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v4
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v2
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v2
 | 
			
		||||
 | 
			
		||||
    - name: Set TAG
 | 
			
		||||
      run: |
 | 
			
		||||
        echo "TAG=check" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set TAG
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "TAG=check" >> $GITHUB_ENV
 | 
			
		||||
 | 
			
		||||
    - name: Run build
 | 
			
		||||
      run: |
 | 
			
		||||
        docker/build.py \
 | 
			
		||||
          --tag "${TAG}" \
 | 
			
		||||
          --arch "${{ matrix.arch }}" \
 | 
			
		||||
          --build-type "${{ matrix.build_type }}" \
 | 
			
		||||
          build
 | 
			
		||||
      - name: Run build
 | 
			
		||||
        run: |
 | 
			
		||||
          docker/build.py \
 | 
			
		||||
            --tag "${TAG}" \
 | 
			
		||||
            --arch "${{ matrix.arch }}" \
 | 
			
		||||
            --build-type "${{ matrix.build_type }}" \
 | 
			
		||||
            build
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,7 @@
 | 
			
		||||
---
 | 
			
		||||
name: CI
 | 
			
		||||
 | 
			
		||||
# yamllint disable-line rule:truthy
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [dev, beta, release]
 | 
			
		||||
@@ -10,6 +12,7 @@ permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
 | 
			
		||||
concurrency:
 | 
			
		||||
  # yamllint disable-line rule:line-length
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
@@ -45,6 +48,10 @@ jobs:
 | 
			
		||||
            file: tests/test5.yaml
 | 
			
		||||
            name: Test tests/test5.yaml
 | 
			
		||||
            pio_cache_key: test5
 | 
			
		||||
          - id: test
 | 
			
		||||
            file: tests/test6.yaml
 | 
			
		||||
            name: Test tests/test6.yaml
 | 
			
		||||
            pio_cache_key: test6
 | 
			
		||||
          - id: pytest
 | 
			
		||||
            name: Run pytest
 | 
			
		||||
          - id: clang-format
 | 
			
		||||
@@ -73,6 +80,8 @@ jobs:
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 IDF
 | 
			
		||||
            options: --environment esp32-idf-tidy --grep USE_ESP_IDF
 | 
			
		||||
            pio_cache_key: tidyesp32-idf
 | 
			
		||||
          - id: yamllint
 | 
			
		||||
            name: Run yamllint
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
@@ -80,17 +89,19 @@ jobs:
 | 
			
		||||
        uses: actions/setup-python@v4
 | 
			
		||||
        id: python
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: '3.8'
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
 | 
			
		||||
      - name: Cache virtualenv
 | 
			
		||||
        uses: actions/cache@v3
 | 
			
		||||
        with:
 | 
			
		||||
          path: .venv
 | 
			
		||||
          # yamllint disable-line rule:line-length
 | 
			
		||||
          key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
 | 
			
		||||
          restore-keys: |
 | 
			
		||||
            venv-${{ steps.python.outputs.python-version }}-
 | 
			
		||||
 | 
			
		||||
      - name: Set up virtualenv
 | 
			
		||||
        # yamllint disable rule:line-length
 | 
			
		||||
        run: |
 | 
			
		||||
          python -m venv .venv
 | 
			
		||||
          source .venv/bin/activate
 | 
			
		||||
@@ -99,12 +110,14 @@ jobs:
 | 
			
		||||
          pip install -e .
 | 
			
		||||
          echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH
 | 
			
		||||
          echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> $GITHUB_ENV
 | 
			
		||||
        # yamllint enable rule:line-length
 | 
			
		||||
 | 
			
		||||
      # Use per check platformio cache because checks use different parts
 | 
			
		||||
      - name: Cache platformio
 | 
			
		||||
        uses: actions/cache@v3
 | 
			
		||||
        with:
 | 
			
		||||
          path: ~/.platformio
 | 
			
		||||
          # yamllint disable-line rule:line-length
 | 
			
		||||
          key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
 | 
			
		||||
        if: matrix.id == 'test' || matrix.id == 'clang-tidy'
 | 
			
		||||
 | 
			
		||||
@@ -145,8 +158,9 @@ jobs:
 | 
			
		||||
          pytest -vv --tb=native tests
 | 
			
		||||
        if: matrix.id == 'pytest'
 | 
			
		||||
 | 
			
		||||
      # Also run git-diff-index so that the step is marked as failed on formatting errors,
 | 
			
		||||
      # since clang-format doesn't do anything but change files if -i is passed.
 | 
			
		||||
      # Also run git-diff-index so that the step is marked as failed on
 | 
			
		||||
      # formatting errors, since clang-format doesn't do anything but
 | 
			
		||||
      # change files if -i is passed.
 | 
			
		||||
      - name: Run clang-format
 | 
			
		||||
        run: |
 | 
			
		||||
          script/clang-format -i
 | 
			
		||||
@@ -161,6 +175,11 @@ jobs:
 | 
			
		||||
          # Also cache libdeps, store them in a ~/.platformio subfolder
 | 
			
		||||
          PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
 | 
			
		||||
 | 
			
		||||
      - name: Run yamllint
 | 
			
		||||
        if: matrix.id == 'yamllint'
 | 
			
		||||
        uses: frenck/action-yamllint@v1.3.0
 | 
			
		||||
 | 
			
		||||
      - name: Suggested changes
 | 
			
		||||
        run: script/ci-suggest-changes
 | 
			
		||||
        # yamllint disable-line rule:line-length
 | 
			
		||||
        if: always() && (matrix.id == 'clang-tidy' || matrix.id == 'clang-format' || matrix.id == 'lint-python')
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,8 +1,10 @@
 | 
			
		||||
---
 | 
			
		||||
name: Lock
 | 
			
		||||
 | 
			
		||||
# yamllint disable-line rule:truthy
 | 
			
		||||
on:
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: '30 0 * * *'
 | 
			
		||||
    - cron: "30 0 * * *"
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										106
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										106
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,7 @@
 | 
			
		||||
---
 | 
			
		||||
name: Publish Release
 | 
			
		||||
 | 
			
		||||
# yamllint disable-line rule:truthy
 | 
			
		||||
on:
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
  release:
 | 
			
		||||
@@ -20,6 +22,7 @@ jobs:
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
      - name: Get tag
 | 
			
		||||
        id: tag
 | 
			
		||||
        # yamllint disable rule:line-length
 | 
			
		||||
        run: |
 | 
			
		||||
          if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then
 | 
			
		||||
            TAG="${GITHUB_REF#refs/tags/}"
 | 
			
		||||
@@ -28,7 +31,8 @@ jobs:
 | 
			
		||||
            today="$(date --utc '+%Y%m%d')"
 | 
			
		||||
            TAG="${TAG}${today}"
 | 
			
		||||
          fi
 | 
			
		||||
          echo "::set-output name=tag::${TAG}"
 | 
			
		||||
          echo "tag=${TAG}" >> $GITHUB_OUTPUT
 | 
			
		||||
        # yamllint enable rule:line-length
 | 
			
		||||
 | 
			
		||||
  deploy-pypi:
 | 
			
		||||
    name: Build and publish to PyPi
 | 
			
		||||
@@ -39,7 +43,7 @@ jobs:
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v4
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: '3.x'
 | 
			
		||||
          python-version: "3.x"
 | 
			
		||||
      - name: Set up python environment
 | 
			
		||||
        run: |
 | 
			
		||||
          script/setup
 | 
			
		||||
@@ -65,37 +69,37 @@ jobs:
 | 
			
		||||
        arch: [amd64, armv7, aarch64]
 | 
			
		||||
        build_type: ["ha-addon", "docker", "lint"]
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Set up Python
 | 
			
		||||
      uses: actions/setup-python@v4
 | 
			
		||||
      with:
 | 
			
		||||
        python-version: '3.9'
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v4
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
 | 
			
		||||
    - name: Set up Docker Buildx
 | 
			
		||||
      uses: docker/setup-buildx-action@v2
 | 
			
		||||
    - name: Set up QEMU
 | 
			
		||||
      uses: docker/setup-qemu-action@v2
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v2
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v2
 | 
			
		||||
 | 
			
		||||
    - name: Log in to docker hub
 | 
			
		||||
      uses: docker/login-action@v2
 | 
			
		||||
      with:
 | 
			
		||||
        username: ${{ secrets.DOCKER_USER }}
 | 
			
		||||
        password: ${{ secrets.DOCKER_PASSWORD }}
 | 
			
		||||
    - name: Log in to the GitHub container registry
 | 
			
		||||
      uses: docker/login-action@v2
 | 
			
		||||
      with:
 | 
			
		||||
      - name: Log in to docker hub
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKER_USER }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_PASSWORD }}
 | 
			
		||||
      - name: Log in to the GitHub container registry
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ghcr.io
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
    - name: Build and push
 | 
			
		||||
      run: |
 | 
			
		||||
        docker/build.py \
 | 
			
		||||
          --tag "${{ needs.init.outputs.tag }}" \
 | 
			
		||||
          --arch "${{ matrix.arch }}" \
 | 
			
		||||
          --build-type "${{ matrix.build_type }}" \
 | 
			
		||||
          build \
 | 
			
		||||
          --push
 | 
			
		||||
      - name: Build and push
 | 
			
		||||
        run: |
 | 
			
		||||
          docker/build.py \
 | 
			
		||||
            --tag "${{ needs.init.outputs.tag }}" \
 | 
			
		||||
            --arch "${{ matrix.arch }}" \
 | 
			
		||||
            --build-type "${{ matrix.build_type }}" \
 | 
			
		||||
            build \
 | 
			
		||||
            --push
 | 
			
		||||
 | 
			
		||||
  deploy-docker-manifest:
 | 
			
		||||
    if: github.repository == 'esphome/esphome'
 | 
			
		||||
@@ -108,34 +112,34 @@ jobs:
 | 
			
		||||
      matrix:
 | 
			
		||||
        build_type: ["ha-addon", "docker", "lint"]
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Set up Python
 | 
			
		||||
      uses: actions/setup-python@v4
 | 
			
		||||
      with:
 | 
			
		||||
        python-version: '3.9'
 | 
			
		||||
    - name: Enable experimental manifest support
 | 
			
		||||
      run: |
 | 
			
		||||
        mkdir -p ~/.docker
 | 
			
		||||
        echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v4
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
      - name: Enable experimental manifest support
 | 
			
		||||
        run: |
 | 
			
		||||
          mkdir -p ~/.docker
 | 
			
		||||
          echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
 | 
			
		||||
 | 
			
		||||
    - name: Log in to docker hub
 | 
			
		||||
      uses: docker/login-action@v2
 | 
			
		||||
      with:
 | 
			
		||||
        username: ${{ secrets.DOCKER_USER }}
 | 
			
		||||
        password: ${{ secrets.DOCKER_PASSWORD }}
 | 
			
		||||
    - name: Log in to the GitHub container registry
 | 
			
		||||
      uses: docker/login-action@v2
 | 
			
		||||
      with:
 | 
			
		||||
      - name: Log in to docker hub
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKER_USER }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_PASSWORD }}
 | 
			
		||||
      - name: Log in to the GitHub container registry
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ghcr.io
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
    - name: Run manifest
 | 
			
		||||
      run: |
 | 
			
		||||
        docker/build.py \
 | 
			
		||||
          --tag "${{ needs.init.outputs.tag }}" \
 | 
			
		||||
          --build-type "${{ matrix.build_type }}" \
 | 
			
		||||
          manifest
 | 
			
		||||
      - name: Run manifest
 | 
			
		||||
        run: |
 | 
			
		||||
          docker/build.py \
 | 
			
		||||
            --tag "${{ needs.init.outputs.tag }}" \
 | 
			
		||||
            --build-type "${{ matrix.build_type }}" \
 | 
			
		||||
            manifest
 | 
			
		||||
 | 
			
		||||
  deploy-ha-addon-repo:
 | 
			
		||||
    if: github.repository == 'esphome/esphome' && github.event_name == 'release'
 | 
			
		||||
@@ -144,6 +148,7 @@ jobs:
 | 
			
		||||
    steps:
 | 
			
		||||
      - env:
 | 
			
		||||
          TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
 | 
			
		||||
        # yamllint disable rule:line-length
 | 
			
		||||
        run: |
 | 
			
		||||
          TAG="${GITHUB_REF#refs/tags/}"
 | 
			
		||||
          curl \
 | 
			
		||||
@@ -152,3 +157,4 @@ jobs:
 | 
			
		||||
            -H "Accept: application/vnd.github.v3+json" \
 | 
			
		||||
            https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
 | 
			
		||||
            -d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"
 | 
			
		||||
        # yamllint enable rule:line-length
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,8 +1,10 @@
 | 
			
		||||
---
 | 
			
		||||
name: Stale
 | 
			
		||||
 | 
			
		||||
# yamllint disable-line rule:truthy
 | 
			
		||||
on:
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: '30 0 * * *'
 | 
			
		||||
    - cron: "30 0 * * *"
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
@@ -16,7 +18,7 @@ jobs:
 | 
			
		||||
  stale:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/stale@v5
 | 
			
		||||
      - uses: actions/stale@v6
 | 
			
		||||
        with:
 | 
			
		||||
          days-before-pr-stale: 90
 | 
			
		||||
          days-before-pr-close: 7
 | 
			
		||||
@@ -31,11 +33,12 @@ jobs:
 | 
			
		||||
            and will be closed if no further activity occurs within 7 days.
 | 
			
		||||
            Thank you for your contributions.
 | 
			
		||||
 | 
			
		||||
  # Use stale to automatically close issues with a reference to the issue tracker
 | 
			
		||||
  # Use stale to automatically close issues with a
 | 
			
		||||
  # reference to the issue tracker
 | 
			
		||||
  close-issues:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/stale@v5
 | 
			
		||||
      - uses: actions/stale@v6
 | 
			
		||||
        with:
 | 
			
		||||
          days-before-pr-stale: -1
 | 
			
		||||
          days-before-pr-close: -1
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
ports:
 | 
			
		||||
- port: 6052
 | 
			
		||||
  onOpen: open-preview
 | 
			
		||||
tasks:
 | 
			
		||||
- before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup
 | 
			
		||||
  command: python -m esphome dashboard config
 | 
			
		||||
@@ -1,16 +1,17 @@
 | 
			
		||||
---
 | 
			
		||||
# 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: 22.6.0
 | 
			
		||||
    rev: 22.10.0
 | 
			
		||||
    hooks:
 | 
			
		||||
    - id: black
 | 
			
		||||
      args:
 | 
			
		||||
        - --safe
 | 
			
		||||
        - --quiet
 | 
			
		||||
      files: ^((esphome|script|tests)/.+)?[^/]+\.py$
 | 
			
		||||
  - repo: https://gitlab.com/pycqa/flake8
 | 
			
		||||
    rev: 4.0.1
 | 
			
		||||
      - id: black
 | 
			
		||||
        args:
 | 
			
		||||
          - --safe
 | 
			
		||||
          - --quiet
 | 
			
		||||
        files: ^((esphome|script|tests)/.+)?[^/]+\.py$
 | 
			
		||||
  - repo: https://github.com/PyCQA/flake8
 | 
			
		||||
    rev: 5.0.4
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: flake8
 | 
			
		||||
        additional_dependencies:
 | 
			
		||||
@@ -26,7 +27,7 @@ repos:
 | 
			
		||||
          - --branch=release
 | 
			
		||||
          - --branch=beta
 | 
			
		||||
  - repo: https://github.com/asottile/pyupgrade
 | 
			
		||||
    rev: v2.37.3
 | 
			
		||||
    rev: v3.2.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: pyupgrade
 | 
			
		||||
        args: [--py38-plus]
 | 
			
		||||
        args: [--py39-plus]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								CODEOWNERS
									
									
									
									
									
								
							@@ -13,6 +13,7 @@ esphome/core/* @esphome/core
 | 
			
		||||
# Integrations
 | 
			
		||||
esphome/components/ac_dimmer/* @glmnet
 | 
			
		||||
esphome/components/adc/* @esphome/core
 | 
			
		||||
esphome/components/adc128s102/* @DeerMaximum
 | 
			
		||||
esphome/components/addressable_light/* @justfalter
 | 
			
		||||
esphome/components/airthings_ble/* @jeromelaban
 | 
			
		||||
esphome/components/airthings_wave_mini/* @ncareau
 | 
			
		||||
@@ -29,11 +30,15 @@ esphome/components/b_parasite/* @rbaron
 | 
			
		||||
esphome/components/ballu/* @bazuchan
 | 
			
		||||
esphome/components/bang_bang/* @OttoWinter
 | 
			
		||||
esphome/components/bedjet/* @jhansche
 | 
			
		||||
esphome/components/bedjet/climate/* @jhansche
 | 
			
		||||
esphome/components/bedjet/fan/* @jhansche
 | 
			
		||||
esphome/components/bh1750/* @OttoWinter
 | 
			
		||||
esphome/components/binary_sensor/* @esphome/core
 | 
			
		||||
esphome/components/bl0939/* @ziceva
 | 
			
		||||
esphome/components/bl0940/* @tobias-
 | 
			
		||||
esphome/components/bl0942/* @dbuezas
 | 
			
		||||
esphome/components/ble_client/* @buxtronix
 | 
			
		||||
esphome/components/bluetooth_proxy/* @jesserockz
 | 
			
		||||
esphome/components/bme680_bsec/* @trvrnrth
 | 
			
		||||
esphome/components/bmp3xx/* @martgras
 | 
			
		||||
esphome/components/button/* @esphome/core
 | 
			
		||||
@@ -53,25 +58,32 @@ esphome/components/cse7761/* @berfenger
 | 
			
		||||
esphome/components/ct_clamp/* @jesserockz
 | 
			
		||||
esphome/components/current_based/* @djwmarcx
 | 
			
		||||
esphome/components/dac7678/* @NickB1
 | 
			
		||||
esphome/components/daikin_brc/* @hagak
 | 
			
		||||
esphome/components/daly_bms/* @s1lvi0
 | 
			
		||||
esphome/components/dashboard_import/* @esphome/core
 | 
			
		||||
esphome/components/debug/* @OttoWinter
 | 
			
		||||
esphome/components/delonghi/* @grob6000
 | 
			
		||||
esphome/components/dfplayer/* @glmnet
 | 
			
		||||
esphome/components/dht/* @OttoWinter
 | 
			
		||||
esphome/components/display_menu_base/* @numo68
 | 
			
		||||
esphome/components/dps310/* @kbx81
 | 
			
		||||
esphome/components/ds1307/* @badbadc0ffee
 | 
			
		||||
esphome/components/dsmr/* @glmnet @zuidwijk
 | 
			
		||||
esphome/components/ektf2232/* @jesserockz
 | 
			
		||||
esphome/components/ens210/* @itn3rd77
 | 
			
		||||
esphome/components/esp32/* @esphome/core
 | 
			
		||||
esphome/components/esp32_ble/* @jesserockz
 | 
			
		||||
esphome/components/esp32_ble_client/* @jesserockz
 | 
			
		||||
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/esp8266/* @esphome/core
 | 
			
		||||
esphome/components/ethernet_info/* @gtjadsonsantos
 | 
			
		||||
esphome/components/exposure_notifications/* @OttoWinter
 | 
			
		||||
esphome/components/ezo/* @ssieb
 | 
			
		||||
esphome/components/ezo_pmp/* @carlos-sarmiento
 | 
			
		||||
esphome/components/factory_reset/* @anatoly-savchenkov
 | 
			
		||||
esphome/components/fastled_base/* @OttoWinter
 | 
			
		||||
esphome/components/feedback/* @ianchi
 | 
			
		||||
esphome/components/fingerprint_grow/* @OnFreund @loongyh
 | 
			
		||||
@@ -99,6 +111,7 @@ esphome/components/integration/* @OttoWinter
 | 
			
		||||
esphome/components/interval/* @esphome/core
 | 
			
		||||
esphome/components/json/* @OttoWinter
 | 
			
		||||
esphome/components/kalman_combinator/* @Cat-Ion
 | 
			
		||||
esphome/components/lcd_menu/* @numo68
 | 
			
		||||
esphome/components/ledc/* @OttoWinter
 | 
			
		||||
esphome/components/light/* @esphome/core
 | 
			
		||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
 | 
			
		||||
@@ -120,6 +133,7 @@ esphome/components/mcp2515/* @danielschramm @mvturnho
 | 
			
		||||
esphome/components/mcp3204/* @rsumner
 | 
			
		||||
esphome/components/mcp4728/* @berfenger
 | 
			
		||||
esphome/components/mcp47a1/* @jesserockz
 | 
			
		||||
esphome/components/mcp9600/* @MrEditor97
 | 
			
		||||
esphome/components/mcp9808/* @k7hpn
 | 
			
		||||
esphome/components/md5/* @esphome/core
 | 
			
		||||
esphome/components/mdns/* @esphome/core
 | 
			
		||||
@@ -138,6 +152,7 @@ esphome/components/modbus_controller/switch/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/text_sensor/* @martgras
 | 
			
		||||
esphome/components/mopeka_ble/* @spbrogan
 | 
			
		||||
esphome/components/mopeka_pro_check/* @spbrogan
 | 
			
		||||
esphome/components/mpl3115a2/* @kbickar
 | 
			
		||||
esphome/components/mpu6886/* @fabaff
 | 
			
		||||
esphome/components/network/* @esphome/core
 | 
			
		||||
esphome/components/nextion/* @senexcrenshaw
 | 
			
		||||
@@ -171,6 +186,8 @@ esphome/components/rc522_spi/* @glmnet
 | 
			
		||||
esphome/components/restart/* @esphome/core
 | 
			
		||||
esphome/components/rf_bridge/* @jesserockz
 | 
			
		||||
esphome/components/rgbct/* @jesserockz
 | 
			
		||||
esphome/components/rp2040/* @jesserockz
 | 
			
		||||
esphome/components/rp2040_pwm/* @jesserockz
 | 
			
		||||
esphome/components/rtttl/* @glmnet
 | 
			
		||||
esphome/components/safe_mode/* @jsuanet @paulmonigatti
 | 
			
		||||
esphome/components/scd4x/* @martgras @sjtrny
 | 
			
		||||
@@ -220,7 +237,9 @@ esphome/components/teleinfo/* @0hax
 | 
			
		||||
esphome/components/thermostat/* @kbx81
 | 
			
		||||
esphome/components/time/* @OttoWinter
 | 
			
		||||
esphome/components/tlc5947/* @rnauber
 | 
			
		||||
esphome/components/tm1621/* @Philippe12
 | 
			
		||||
esphome/components/tm1637/* @glmnet
 | 
			
		||||
esphome/components/tm1638/* @skykingjwc
 | 
			
		||||
esphome/components/tmp102/* @timsavage
 | 
			
		||||
esphome/components/tmp117/* @Azimath
 | 
			
		||||
esphome/components/tof10120/* @wstrzalka
 | 
			
		||||
@@ -235,14 +254,17 @@ esphome/components/tuya/sensor/* @jesserockz
 | 
			
		||||
esphome/components/tuya/switch/* @jesserockz
 | 
			
		||||
esphome/components/tuya/text_sensor/* @dentra
 | 
			
		||||
esphome/components/uart/* @esphome/core
 | 
			
		||||
esphome/components/ufire_ec/* @pvizeli
 | 
			
		||||
esphome/components/ufire_ise/* @pvizeli
 | 
			
		||||
esphome/components/ultrasonic/* @OttoWinter
 | 
			
		||||
esphome/components/version/* @esphome/core
 | 
			
		||||
esphome/components/wake_on_lan/* @willwill2will54
 | 
			
		||||
esphome/components/web_server_base/* @OttoWinter
 | 
			
		||||
esphome/components/whirlpool/* @glmnet
 | 
			
		||||
esphome/components/whynter/* @aeonsablaze
 | 
			
		||||
esphome/components/wl_134/* @hobbypunk90
 | 
			
		||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
 | 
			
		||||
esphome/components/xiaomi_mhoc303/* @drug123
 | 
			
		||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
 | 
			
		||||
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
 | 
			
		||||
esphome/components/xpt2046/* @numo68
 | 
			
		||||
esphome/components/xpt2046/* @nielsnl68 @numo68
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,7 @@ RUN \
 | 
			
		||||
    # Ubuntu python3-pip is missing wheel
 | 
			
		||||
    pip3 install --no-cache-dir \
 | 
			
		||||
        wheel==0.37.1 \
 | 
			
		||||
        platformio==6.0.2 \
 | 
			
		||||
        platformio==6.1.5 \
 | 
			
		||||
    # Change some platformio settings
 | 
			
		||||
    && platformio settings set enable_telemetry No \
 | 
			
		||||
    && platformio settings set check_platformio_interval 1000000 \
 | 
			
		||||
 
 | 
			
		||||
@@ -88,10 +88,12 @@ def main():
 | 
			
		||||
                sys.exit(1)
 | 
			
		||||
 | 
			
		||||
    # detect channel from tag
 | 
			
		||||
    match = re.match(r'^\d+\.\d+(?:\.\d+)?(b\d+)?$', args.tag)
 | 
			
		||||
    match = re.match(r"^(\d+\.\d+)(?:\.\d+)?(b\d+)?$", args.tag)
 | 
			
		||||
    major_minor_version = None
 | 
			
		||||
    if match is None:
 | 
			
		||||
        channel = CHANNEL_DEV
 | 
			
		||||
    elif match.group(1) is None:
 | 
			
		||||
    elif match.group(2) is None:
 | 
			
		||||
        major_minor_version = match.group(1)
 | 
			
		||||
        channel = CHANNEL_RELEASE
 | 
			
		||||
    else:
 | 
			
		||||
        channel = CHANNEL_BETA
 | 
			
		||||
@@ -106,6 +108,11 @@ def main():
 | 
			
		||||
        tags_to_push.append("beta")
 | 
			
		||||
        tags_to_push.append("latest")
 | 
			
		||||
 | 
			
		||||
        # Compatibility with HA tags
 | 
			
		||||
        if major_minor_version:
 | 
			
		||||
            tags_to_push.append("stable")
 | 
			
		||||
            tags_to_push.append(major_minor_version)
 | 
			
		||||
 | 
			
		||||
    if args.command == "build":
 | 
			
		||||
        # 1. pull cache image
 | 
			
		||||
        params = DockerParams.for_type_arch(args.build_type, args.arch)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import logging
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import sys
 | 
			
		||||
import time
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
 | 
			
		||||
from esphome import const, writer, yaml_util
 | 
			
		||||
@@ -22,6 +23,9 @@ from esphome.const import (
 | 
			
		||||
    CONF_ESPHOME,
 | 
			
		||||
    CONF_PLATFORMIO_OPTIONS,
 | 
			
		||||
    CONF_SUBSTITUTIONS,
 | 
			
		||||
    PLATFORM_ESP32,
 | 
			
		||||
    PLATFORM_ESP8266,
 | 
			
		||||
    PLATFORM_RP2040,
 | 
			
		||||
    SECRETS_FILES,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, EsphomeError, coroutine
 | 
			
		||||
@@ -101,11 +105,11 @@ def run_miniterm(config, port):
 | 
			
		||||
 | 
			
		||||
    if CONF_LOGGER not in config:
 | 
			
		||||
        _LOGGER.info("Logger is not enabled. Not starting UART logs.")
 | 
			
		||||
        return
 | 
			
		||||
        return 1
 | 
			
		||||
    baud_rate = config["logger"][CONF_BAUD_RATE]
 | 
			
		||||
    if baud_rate == 0:
 | 
			
		||||
        _LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
 | 
			
		||||
        return
 | 
			
		||||
        return 1
 | 
			
		||||
    _LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
 | 
			
		||||
 | 
			
		||||
    backtrace_state = False
 | 
			
		||||
@@ -119,25 +123,34 @@ def run_miniterm(config, port):
 | 
			
		||||
        ser.dtr = False
 | 
			
		||||
        ser.rts = False
 | 
			
		||||
 | 
			
		||||
    with ser:
 | 
			
		||||
        while True:
 | 
			
		||||
            try:
 | 
			
		||||
                raw = ser.readline()
 | 
			
		||||
            except serial.SerialException:
 | 
			
		||||
                _LOGGER.error("Serial port closed!")
 | 
			
		||||
                return
 | 
			
		||||
            line = (
 | 
			
		||||
                raw.replace(b"\r", b"")
 | 
			
		||||
                .replace(b"\n", b"")
 | 
			
		||||
                .decode("utf8", "backslashreplace")
 | 
			
		||||
            )
 | 
			
		||||
            time = datetime.now().time().strftime("[%H:%M:%S]")
 | 
			
		||||
            message = time + line
 | 
			
		||||
            safe_print(message)
 | 
			
		||||
    tries = 0
 | 
			
		||||
    while tries < 5:
 | 
			
		||||
        try:
 | 
			
		||||
            with ser:
 | 
			
		||||
                while True:
 | 
			
		||||
                    try:
 | 
			
		||||
                        raw = ser.readline()
 | 
			
		||||
                    except serial.SerialException:
 | 
			
		||||
                        _LOGGER.error("Serial port closed!")
 | 
			
		||||
                        return 0
 | 
			
		||||
                    line = (
 | 
			
		||||
                        raw.replace(b"\r", b"")
 | 
			
		||||
                        .replace(b"\n", b"")
 | 
			
		||||
                        .decode("utf8", "backslashreplace")
 | 
			
		||||
                    )
 | 
			
		||||
                    time_str = datetime.now().time().strftime("[%H:%M:%S]")
 | 
			
		||||
                    message = time_str + line
 | 
			
		||||
                    safe_print(message)
 | 
			
		||||
 | 
			
		||||
            backtrace_state = platformio_api.process_stacktrace(
 | 
			
		||||
                config, line, backtrace_state=backtrace_state
 | 
			
		||||
            )
 | 
			
		||||
                    backtrace_state = platformio_api.process_stacktrace(
 | 
			
		||||
                        config, line, backtrace_state=backtrace_state
 | 
			
		||||
                    )
 | 
			
		||||
        except serial.SerialException:
 | 
			
		||||
            tries += 1
 | 
			
		||||
            time.sleep(1)
 | 
			
		||||
    if tries >= 5:
 | 
			
		||||
        _LOGGER.error("Could not connect to serial port %s", port)
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def wrap_to_code(name, comp):
 | 
			
		||||
@@ -258,9 +271,21 @@ def upload_using_esptool(config, port):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def upload_program(config, args, host):
 | 
			
		||||
    # if upload is to a serial port use platformio, otherwise assume ota
 | 
			
		||||
    if get_port_type(host) == "SERIAL":
 | 
			
		||||
        return upload_using_esptool(config, host)
 | 
			
		||||
        if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
 | 
			
		||||
            return upload_using_esptool(config, host)
 | 
			
		||||
 | 
			
		||||
        if CORE.target_platform in (PLATFORM_RP2040):
 | 
			
		||||
            from esphome import platformio_api
 | 
			
		||||
 | 
			
		||||
            upload_args = ["-t", "upload"]
 | 
			
		||||
            if args.device is not None:
 | 
			
		||||
                upload_args += ["--upload-port", args.device]
 | 
			
		||||
            return platformio_api.run_platformio_cli_run(
 | 
			
		||||
                config, CORE.verbose, *upload_args
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        return 1  # Unknown target platform
 | 
			
		||||
 | 
			
		||||
    from esphome import espota2
 | 
			
		||||
 | 
			
		||||
@@ -280,8 +305,7 @@ def show_logs(config, args, port):
 | 
			
		||||
    if "logger" not in config:
 | 
			
		||||
        raise EsphomeError("Logger is not configured!")
 | 
			
		||||
    if get_port_type(port) == "SERIAL":
 | 
			
		||||
        run_miniterm(config, port)
 | 
			
		||||
        return 0
 | 
			
		||||
        return run_miniterm(config, port)
 | 
			
		||||
    if get_port_type(port) == "NETWORK" and "api" in config:
 | 
			
		||||
        from esphome.components.api.client import run_logs
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ from esphome.cpp_generator import (  # noqa
 | 
			
		||||
    static_const_array,
 | 
			
		||||
    statement,
 | 
			
		||||
    variable,
 | 
			
		||||
    with_local_variable,
 | 
			
		||||
    new_variable,
 | 
			
		||||
    Pvariable,
 | 
			
		||||
    new_Pvariable,
 | 
			
		||||
 
 | 
			
		||||
@@ -121,11 +121,8 @@ void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
 | 
			
		||||
      // calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
 | 
			
		||||
      // also take into account min_power
 | 
			
		||||
      auto min_us = this->cycle_time_us * this->min_power / 1000;
 | 
			
		||||
      // calculate required value to provide a true RMS voltage output
 | 
			
		||||
      this->enable_time_us =
 | 
			
		||||
          std::max((uint32_t) 1, (uint32_t)((65535 - (acos(1 - (2 * this->value / 65535.0)) / 3.14159 * 65535)) *
 | 
			
		||||
                                            (this->cycle_time_us - min_us)) /
 | 
			
		||||
                                     65535);
 | 
			
		||||
      this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
 | 
			
		||||
 | 
			
		||||
      if (this->method == DIM_METHOD_LEADING_PULSE) {
 | 
			
		||||
        // Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone
 | 
			
		||||
        // this is for brightness near 99%
 | 
			
		||||
@@ -206,6 +203,7 @@ void AcDimmer::setup() {
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
void AcDimmer::write_state(float state) {
 | 
			
		||||
  state = std::acos(1 - (2 * state)) / 3.14159;  // RMS power compensation
 | 
			
		||||
  auto new_value = static_cast<uint16_t>(roundf(state * 65535));
 | 
			
		||||
  if (new_value != 0 && this->store_.value == 0)
 | 
			
		||||
    this->store_.init_cycle = this->init_with_half_cycle_;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								esphome/components/adc128s102/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								esphome/components/adc128s102/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import spi
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["spi"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
CODEOWNERS = ["@DeerMaximum"]
 | 
			
		||||
 | 
			
		||||
adc128s102_ns = cg.esphome_ns.namespace("adc128s102")
 | 
			
		||||
ADC128S102 = adc128s102_ns.class_("ADC128S102", cg.Component, spi.SPIDevice)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(ADC128S102),
 | 
			
		||||
    }
 | 
			
		||||
).extend(spi.spi_device_schema(cs_pin_required=True))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await spi.register_spi_device(var, config)
 | 
			
		||||
							
								
								
									
										35
									
								
								esphome/components/adc128s102/adc128s102.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								esphome/components/adc128s102/adc128s102.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
#include "adc128s102.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace adc128s102 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "adc128s102";
 | 
			
		||||
 | 
			
		||||
float ADC128S102::get_setup_priority() const { return setup_priority::HARDWARE; }
 | 
			
		||||
 | 
			
		||||
void ADC128S102::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up adc128s102");
 | 
			
		||||
  this->spi_setup();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ADC128S102::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "ADC128S102:");
 | 
			
		||||
  LOG_PIN("  CS Pin:", this->cs_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t ADC128S102::read_data(uint8_t channel) {
 | 
			
		||||
  uint8_t control = channel << 3;
 | 
			
		||||
 | 
			
		||||
  this->enable();
 | 
			
		||||
  uint8_t adc_primary_byte = this->transfer_byte(control);
 | 
			
		||||
  uint8_t adc_secondary_byte = this->transfer_byte(0x00);
 | 
			
		||||
  this->disable();
 | 
			
		||||
 | 
			
		||||
  uint16_t digital_value = adc_primary_byte << 8 | adc_secondary_byte;
 | 
			
		||||
 | 
			
		||||
  return digital_value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace adc128s102
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										23
									
								
								esphome/components/adc128s102/adc128s102.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								esphome/components/adc128s102/adc128s102.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/components/spi/spi.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace adc128s102 {
 | 
			
		||||
 | 
			
		||||
class ADC128S102 : public Component,
 | 
			
		||||
                   public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
 | 
			
		||||
                                         spi::DATA_RATE_10MHZ> {
 | 
			
		||||
 public:
 | 
			
		||||
  ADC128S102() = default;
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  uint16_t read_data(uint8_t channel);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace adc128s102
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										35
									
								
								esphome/components/adc128s102/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								esphome/components/adc128s102/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, voltage_sampler
 | 
			
		||||
from esphome.const import CONF_ID, CONF_CHANNEL
 | 
			
		||||
 | 
			
		||||
from .. import adc128s102_ns, ADC128S102
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["voltage_sampler"]
 | 
			
		||||
DEPENDENCIES = ["adc128s102"]
 | 
			
		||||
 | 
			
		||||
ADC128S102Sensor = adc128s102_ns.class_(
 | 
			
		||||
    "ADC128S102Sensor",
 | 
			
		||||
    sensor.Sensor,
 | 
			
		||||
    cg.PollingComponent,
 | 
			
		||||
    voltage_sampler.VoltageSampler,
 | 
			
		||||
)
 | 
			
		||||
CONF_ADC128S102_ID = "adc128s102_id"
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(ADC128S102Sensor),
 | 
			
		||||
        cv.GenerateID(CONF_ADC128S102_ID): cv.use_id(ADC128S102),
 | 
			
		||||
        cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.polling_component_schema("60s"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(
 | 
			
		||||
        config[CONF_ID],
 | 
			
		||||
        config[CONF_CHANNEL],
 | 
			
		||||
    )
 | 
			
		||||
    await cg.register_parented(var, config[CONF_ADC128S102_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await sensor.register_sensor(var, config)
 | 
			
		||||
							
								
								
									
										24
									
								
								esphome/components/adc128s102/sensor/adc128s102_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphome/components/adc128s102/sensor/adc128s102_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
#include "adc128s102_sensor.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace adc128s102 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "adc128s102.sensor";
 | 
			
		||||
 | 
			
		||||
ADC128S102Sensor::ADC128S102Sensor(uint8_t channel) : channel_(channel) {}
 | 
			
		||||
 | 
			
		||||
float ADC128S102Sensor::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
void ADC128S102Sensor::dump_config() {
 | 
			
		||||
  LOG_SENSOR("", "ADC128S102 Sensor", this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Pin: %u", this->channel_);
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float ADC128S102Sensor::sample() { return this->parent_->read_data(this->channel_); }
 | 
			
		||||
void ADC128S102Sensor::update() { this->publish_state(this->sample()); }
 | 
			
		||||
 | 
			
		||||
}  // namespace adc128s102
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										29
									
								
								esphome/components/adc128s102/sensor/adc128s102_sensor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								esphome/components/adc128s102/sensor/adc128s102_sensor.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
 | 
			
		||||
#include "../adc128s102.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace adc128s102 {
 | 
			
		||||
 | 
			
		||||
class ADC128S102Sensor : public PollingComponent,
 | 
			
		||||
                         public Parented<ADC128S102>,
 | 
			
		||||
                         public sensor::Sensor,
 | 
			
		||||
                         public voltage_sampler::VoltageSampler {
 | 
			
		||||
 public:
 | 
			
		||||
  ADC128S102Sensor(uint8_t channel);
 | 
			
		||||
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  float sample() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint8_t channel_;
 | 
			
		||||
};
 | 
			
		||||
}  // namespace adc128s102
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -82,7 +82,7 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
 | 
			
		||||
    return i2c::ERROR_OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  InternalGPIOPin *irq_pin_ = nullptr;
 | 
			
		||||
  InternalGPIOPin *irq_pin_{nullptr};
 | 
			
		||||
  bool is_setup_{false};
 | 
			
		||||
  sensor::Sensor *voltage_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *current_a_sensor_{nullptr};
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,8 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
  void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  sensor::Sensor *temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *humidity_sensor_;
 | 
			
		||||
  sensor::Sensor *temperature_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *humidity_sensor_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace aht10
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case ESP_GATTC_READ_CHAR_EVT: {
 | 
			
		||||
      if (param->read.conn_id != this->parent()->conn_id)
 | 
			
		||||
      if (param->read.conn_id != this->parent()->get_conn_id())
 | 
			
		||||
        break;
 | 
			
		||||
      if (param->read.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
 | 
			
		||||
@@ -88,8 +88,8 @@ void AirthingsWaveMini::update() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AirthingsWaveMini::request_read_values_() {
 | 
			
		||||
  auto status =
 | 
			
		||||
      esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_,
 | 
			
		||||
                                        ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  if (status) {
 | 
			
		||||
    ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case ESP_GATTC_READ_CHAR_EVT: {
 | 
			
		||||
      if (param->read.conn_id != this->parent()->conn_id)
 | 
			
		||||
      if (param->read.conn_id != this->parent()->get_conn_id())
 | 
			
		||||
        break;
 | 
			
		||||
      if (param->read.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
 | 
			
		||||
@@ -109,8 +109,8 @@ void AirthingsWavePlus::update() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AirthingsWavePlus::request_read_values_() {
 | 
			
		||||
  auto status =
 | 
			
		||||
      esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_,
 | 
			
		||||
                                        ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  if (status) {
 | 
			
		||||
    ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,33 +4,15 @@
 | 
			
		||||
//  - Arduino - AM2320: https://github.com/EngDial/AM2320/blob/master/src/AM2320.cpp
 | 
			
		||||
 | 
			
		||||
#include "am2320.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace am2320 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "am2320";
 | 
			
		||||
 | 
			
		||||
// ---=== Calc CRC16 ===---
 | 
			
		||||
uint16_t crc_16(uint8_t *ptr, uint8_t length) {
 | 
			
		||||
  uint16_t crc = 0xFFFF;
 | 
			
		||||
  uint8_t i;
 | 
			
		||||
  //------------------------------
 | 
			
		||||
  while (length--) {
 | 
			
		||||
    crc ^= *ptr++;
 | 
			
		||||
    for (i = 0; i < 8; i++) {
 | 
			
		||||
      if ((crc & 0x01) != 0) {
 | 
			
		||||
        crc >>= 1;
 | 
			
		||||
        crc ^= 0xA001;
 | 
			
		||||
      } else {
 | 
			
		||||
        crc >>= 1;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return crc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AM2320Component::update() {
 | 
			
		||||
  uint8_t data[8];
 | 
			
		||||
  data[0] = 0;
 | 
			
		||||
@@ -98,7 +80,7 @@ bool AM2320Component::read_data_(uint8_t *data) {
 | 
			
		||||
  checksum = data[7] << 8;
 | 
			
		||||
  checksum += data[6];
 | 
			
		||||
 | 
			
		||||
  if (crc_16(data, 6) != checksum) {
 | 
			
		||||
  if (crc16(data, 6) != checksum) {
 | 
			
		||||
    ESP_LOGW(TAG, "AM2320 Checksum invalid!");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ class AM2320Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
  bool read_data_(uint8_t *data);
 | 
			
		||||
  bool read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0);
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *humidity_sensor_;
 | 
			
		||||
  sensor::Sensor *temperature_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *humidity_sensor_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace am2320
 | 
			
		||||
 
 | 
			
		||||
@@ -76,9 +76,9 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
 | 
			
		||||
      if (this->current_sensor_ > 0) {
 | 
			
		||||
        if (this->illuminance_ != nullptr) {
 | 
			
		||||
          auto *packet = this->encoder_->get_light_level_request();
 | 
			
		||||
          auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
 | 
			
		||||
                                                 packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
 | 
			
		||||
                                                 ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
          auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
 | 
			
		||||
                                                 this->char_handle_, packet->length, packet->data,
 | 
			
		||||
                                                 ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
          if (status) {
 | 
			
		||||
            ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
 | 
			
		||||
                     status);
 | 
			
		||||
@@ -102,8 +102,8 @@ void Am43::update() {
 | 
			
		||||
    if (this->battery_ != nullptr) {
 | 
			
		||||
      auto *packet = this->encoder_->get_battery_level_request();
 | 
			
		||||
      auto status =
 | 
			
		||||
          esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
 | 
			
		||||
                                   packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
          esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
 | 
			
		||||
                                   packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
      if (status)
 | 
			
		||||
        ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -27,8 +27,8 @@ void Am43Component::loop() {
 | 
			
		||||
  if (this->node_state == espbt::ClientState::ESTABLISHED && !this->logged_in_) {
 | 
			
		||||
    auto *packet = this->encoder_->get_send_pin_request(this->pin_);
 | 
			
		||||
    auto status =
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
 | 
			
		||||
                                 packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
 | 
			
		||||
                                 packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
    ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str());
 | 
			
		||||
    if (status) {
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status);
 | 
			
		||||
@@ -54,8 +54,8 @@ void Am43Component::control(const CoverCall &call) {
 | 
			
		||||
  if (call.get_stop()) {
 | 
			
		||||
    auto *packet = this->encoder_->get_stop_request();
 | 
			
		||||
    auto status =
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
 | 
			
		||||
                                 packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
 | 
			
		||||
                                 packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
    if (status)
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] Error writing stop command to device, error = %d", this->get_name().c_str(), status);
 | 
			
		||||
  }
 | 
			
		||||
@@ -66,8 +66,8 @@ void Am43Component::control(const CoverCall &call) {
 | 
			
		||||
      pos = 1 - pos;
 | 
			
		||||
    auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100));
 | 
			
		||||
    auto status =
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
 | 
			
		||||
                                 packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
 | 
			
		||||
                                 packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
    if (status)
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] Error writing set_position command to device, error = %d", this->get_name().c_str(), status);
 | 
			
		||||
  }
 | 
			
		||||
@@ -92,7 +92,8 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
 | 
			
		||||
      }
 | 
			
		||||
      this->char_handle_ = chr->handle;
 | 
			
		||||
 | 
			
		||||
      auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle);
 | 
			
		||||
      auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
 | 
			
		||||
                                                      chr->handle);
 | 
			
		||||
      if (status) {
 | 
			
		||||
        ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
 | 
			
		||||
      }
 | 
			
		||||
@@ -122,9 +123,9 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
 | 
			
		||||
        if (this->decoder_->pin_ok_) {
 | 
			
		||||
          ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str());
 | 
			
		||||
          auto *packet = this->encoder_->get_position_request();
 | 
			
		||||
          auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
 | 
			
		||||
                                                 packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
 | 
			
		||||
                                                 ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
          auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
 | 
			
		||||
                                                 this->char_handle_, packet->length, packet->data,
 | 
			
		||||
                                                 ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
          if (status)
 | 
			
		||||
            ESP_LOGW(TAG, "[%s] Error writing set_position to device, error = %d", this->get_name().c_str(), status);
 | 
			
		||||
        } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ _LOGGER = logging.getLogger(__name__)
 | 
			
		||||
DEPENDENCIES = ["display"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
Animation_ = display.display_ns.class_("Animation")
 | 
			
		||||
Animation_ = display.display_ns.class_("Animation", espImage.Image_)
 | 
			
		||||
 | 
			
		||||
ANIMATION_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -34,15 +34,17 @@ void Anova::control(const ClimateCall &call) {
 | 
			
		||||
        ESP_LOGW(TAG, "Unsupported mode: %d", mode);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
 | 
			
		||||
                                           pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
    auto status =
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
 | 
			
		||||
                                 pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
    if (status)
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
 | 
			
		||||
  }
 | 
			
		||||
  if (call.get_target_temperature().has_value()) {
 | 
			
		||||
    auto *pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature());
 | 
			
		||||
    auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
 | 
			
		||||
                                           pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
    auto status =
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
 | 
			
		||||
                                 pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
    if (status)
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
 | 
			
		||||
  }
 | 
			
		||||
@@ -65,7 +67,8 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
 | 
			
		||||
      }
 | 
			
		||||
      this->char_handle_ = chr->handle;
 | 
			
		||||
 | 
			
		||||
      auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle);
 | 
			
		||||
      auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
 | 
			
		||||
                                                      chr->handle);
 | 
			
		||||
      if (status) {
 | 
			
		||||
        ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
 | 
			
		||||
      }
 | 
			
		||||
@@ -112,8 +115,8 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
 | 
			
		||||
        }
 | 
			
		||||
        if (pkt != nullptr) {
 | 
			
		||||
          auto status =
 | 
			
		||||
              esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length,
 | 
			
		||||
                                       pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
              esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
 | 
			
		||||
                                       pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
          if (status) {
 | 
			
		||||
            ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
 | 
			
		||||
                     status);
 | 
			
		||||
@@ -137,8 +140,9 @@ void Anova::update() {
 | 
			
		||||
    auto *pkt = this->codec_->get_read_device_status_request();
 | 
			
		||||
    if (this->current_request_ == 0)
 | 
			
		||||
      this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
 | 
			
		||||
    auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
 | 
			
		||||
                                           pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
    auto status =
 | 
			
		||||
        esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
 | 
			
		||||
                                 pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
    if (status)
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
 | 
			
		||||
    this->current_request_++;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,27 @@ AUTO_LOAD = ["sensor", "binary_sensor"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
CONF_APDS9960_ID = "apds9960_id"
 | 
			
		||||
CONF_LED_DRIVE = "led_drive"
 | 
			
		||||
CONF_PROXIMITY_GAIN = "proximity_gain"
 | 
			
		||||
CONF_AMBIENT_LIGHT_GAIN = "ambient_light_gain"
 | 
			
		||||
CONF_GESTURE_LED_DRIVE = "gesture_led_drive"
 | 
			
		||||
CONF_GESTURE_GAIN = "gesture_gain"
 | 
			
		||||
CONF_GESTURE_WAIT_TIME = "gesture_wait_time"
 | 
			
		||||
 | 
			
		||||
DRIVE_LEVELS = {"100ma": 0, "50ma": 1, "25ma": 2, "12.5ma": 3}
 | 
			
		||||
PROXIMITY_LEVELS = {"1x": 0, "2x": 1, "4x": 2, "8x": 3}
 | 
			
		||||
AMBIENT_LEVELS = {"1x": 0, "4x": 1, "16x": 2, "64x": 3}
 | 
			
		||||
GESTURE_LEVELS = {"1x": 0, "2x": 1, "4x": 2, "8x": 3}
 | 
			
		||||
GESTURE_WAIT_TIMES = {
 | 
			
		||||
    "0ms": 0,
 | 
			
		||||
    "2.8ms": 1,
 | 
			
		||||
    "5.6ms": 2,
 | 
			
		||||
    "8.4ms": 3,
 | 
			
		||||
    "14ms": 4,
 | 
			
		||||
    "22.4ms": 5,
 | 
			
		||||
    "30.8ms": 6,
 | 
			
		||||
    "39.2ms": 7,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apds9960_nds = cg.esphome_ns.namespace("apds9960")
 | 
			
		||||
APDS9960 = apds9960_nds.class_("APDS9960", cg.PollingComponent, i2c.I2CDevice)
 | 
			
		||||
@@ -16,6 +37,20 @@ CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(APDS9960),
 | 
			
		||||
            cv.Optional(CONF_LED_DRIVE, "100mA"): cv.enum(DRIVE_LEVELS, lower=True),
 | 
			
		||||
            cv.Optional(CONF_PROXIMITY_GAIN, "4x"): cv.enum(
 | 
			
		||||
                PROXIMITY_LEVELS, lower=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_AMBIENT_LIGHT_GAIN, "4x"): cv.enum(
 | 
			
		||||
                AMBIENT_LEVELS, lower=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_GESTURE_LED_DRIVE, "100mA"): cv.enum(
 | 
			
		||||
                DRIVE_LEVELS, lower=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_GESTURE_GAIN, "4x"): cv.enum(GESTURE_LEVELS, lower=True),
 | 
			
		||||
            cv.Optional(CONF_GESTURE_WAIT_TIME, "2.8ms"): cv.enum(
 | 
			
		||||
                GESTURE_WAIT_TIMES, lower=True
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
@@ -27,3 +62,9 @@ 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_led_drive(config[CONF_LED_DRIVE]))
 | 
			
		||||
    cg.add(var.set_proximity_gain(config[CONF_PROXIMITY_GAIN]))
 | 
			
		||||
    cg.add(var.set_ambient_gain(config[CONF_AMBIENT_LIGHT_GAIN]))
 | 
			
		||||
    cg.add(var.set_gesture_led_drive(config[CONF_GESTURE_LED_DRIVE]))
 | 
			
		||||
    cg.add(var.set_gesture_gain(config[CONF_GESTURE_GAIN]))
 | 
			
		||||
    cg.add(var.set_gesture_wait_time(config[CONF_GESTURE_WAIT_TIME]))
 | 
			
		||||
 
 | 
			
		||||
@@ -46,16 +46,16 @@ void APDS9960::setup() {
 | 
			
		||||
  uint8_t val = 0;
 | 
			
		||||
  APDS9960_ERROR_CHECK(this->read_byte(0x8F, &val));
 | 
			
		||||
  val &= 0b00111111;
 | 
			
		||||
  uint8_t led_drive = 0;  // led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA
 | 
			
		||||
  val |= (led_drive & 0b11) << 6;
 | 
			
		||||
  // led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA
 | 
			
		||||
  val |= (this->led_drive_ & 0b11) << 6;
 | 
			
		||||
 | 
			
		||||
  val &= 0b11110011;
 | 
			
		||||
  uint8_t proximity_gain = 2;  // proximity gain, 0 -> 1x, 1 -> 2X, 2 -> 4X, 4 -> 8X
 | 
			
		||||
  val |= (proximity_gain & 0b11) << 2;
 | 
			
		||||
  // proximity gain, 0 -> 1x, 1 -> 2X, 2 -> 4X, 3 -> 8X
 | 
			
		||||
  val |= (this->proximity_gain_ & 0b11) << 2;
 | 
			
		||||
 | 
			
		||||
  val &= 0b11111100;
 | 
			
		||||
  uint8_t ambient_gain = 1;  // ambient light gain, 0 -> 1x, 1 -> 4x, 2 -> 16x, 3 -> 64x
 | 
			
		||||
  val |= (ambient_gain & 0b11) << 0;
 | 
			
		||||
  // ambient light gain, 0 -> 1x, 1 -> 4x, 2 -> 16x, 3 -> 64x
 | 
			
		||||
  val |= (this->ambient_gain_ & 0b11) << 0;
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x8F, val);
 | 
			
		||||
 | 
			
		||||
  // Pers (0x8C) -> 0x11 (2 consecutive proximity or ALS for interrupt)
 | 
			
		||||
@@ -75,19 +75,18 @@ void APDS9960::setup() {
 | 
			
		||||
  // GConf 2 (0xA3, gesture config 2) ->
 | 
			
		||||
  APDS9960_ERROR_CHECK(this->read_byte(0xA3, &val));
 | 
			
		||||
  val &= 0b10011111;
 | 
			
		||||
  uint8_t gesture_gain = 2;  // gesture gain, 0 -> 1x, 1 -> 2x, 2 -> 4x, 3 -> 8x
 | 
			
		||||
  val |= (gesture_gain & 0b11) << 5;
 | 
			
		||||
  // gesture gain, 0 -> 1x, 1 -> 2x, 2 -> 4x, 3 -> 8x
 | 
			
		||||
  val |= (this->gesture_gain_ & 0b11) << 5;
 | 
			
		||||
 | 
			
		||||
  val &= 0b11100111;
 | 
			
		||||
  uint8_t gesture_led_drive = 0;  // gesture led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA
 | 
			
		||||
  val |= (gesture_led_drive & 0b11) << 3;
 | 
			
		||||
  // gesture led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA
 | 
			
		||||
  val |= (this->gesture_led_drive_ & 0b11) << 3;
 | 
			
		||||
 | 
			
		||||
  val &= 0b11111000;
 | 
			
		||||
  // gesture wait time
 | 
			
		||||
  // 0 -> 0ms, 1 -> 2.8ms, 2 -> 5.6ms, 3 -> 8.4ms
 | 
			
		||||
  // 4 -> 14.0ms, 5 -> 22.4 ms, 6 -> 30.8ms, 7 -> 39.2 ms
 | 
			
		||||
  uint8_t gesture_wait_time = 1;  // gesture wait time
 | 
			
		||||
  val |= (gesture_wait_time & 0b111) << 0;
 | 
			
		||||
  val |= (this->gesture_wait_time_ & 0b111) << 0;
 | 
			
		||||
  APDS9960_WRITE_BYTE(0xA3, val);
 | 
			
		||||
 | 
			
		||||
  // GOffsetU (0xA4) -> 0x00 (no offset)
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,13 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void set_led_drive(uint8_t level) { this->led_drive_ = level; }
 | 
			
		||||
  void set_proximity_gain(uint8_t gain) { this->proximity_gain_ = gain; }
 | 
			
		||||
  void set_ambient_gain(uint8_t gain) { this->ambient_gain_ = gain; }
 | 
			
		||||
  void set_gesture_led_drive(uint8_t level) { this->gesture_led_drive_ = level; }
 | 
			
		||||
  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; }
 | 
			
		||||
@@ -36,6 +43,13 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
  void report_gesture_(int gesture);
 | 
			
		||||
  void process_dataset_(int up, int down, int left, int right);
 | 
			
		||||
 | 
			
		||||
  uint8_t led_drive_;
 | 
			
		||||
  uint8_t proximity_gain_;
 | 
			
		||||
  uint8_t ambient_gain_;
 | 
			
		||||
  uint8_t gesture_led_drive_;
 | 
			
		||||
  uint8_t gesture_gain_;
 | 
			
		||||
  uint8_t gesture_wait_time_;
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *red_channel_{nullptr};
 | 
			
		||||
  sensor::Sensor *green_channel_{nullptr};
 | 
			
		||||
  sensor::Sensor *blue_channel_{nullptr};
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,16 @@ service APIConnection {
 | 
			
		||||
  rpc button_command (ButtonCommandRequest) returns (void) {}
 | 
			
		||||
  rpc lock_command (LockCommandRequest) returns (void) {}
 | 
			
		||||
  rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
 | 
			
		||||
 | 
			
		||||
  rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
 | 
			
		||||
  rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
 | 
			
		||||
  rpc bluetooth_gatt_get_services(BluetoothGATTGetServicesRequest) returns (void) {}
 | 
			
		||||
  rpc bluetooth_gatt_read(BluetoothGATTReadRequest) returns (void) {}
 | 
			
		||||
  rpc bluetooth_gatt_write(BluetoothGATTWriteRequest) returns (void) {}
 | 
			
		||||
  rpc bluetooth_gatt_read_descriptor(BluetoothGATTReadDescriptorRequest) returns (void) {}
 | 
			
		||||
  rpc bluetooth_gatt_write_descriptor(BluetoothGATTWriteDescriptorRequest) returns (void) {}
 | 
			
		||||
  rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {}
 | 
			
		||||
  rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -77,6 +87,8 @@ message HelloRequest {
 | 
			
		||||
  // Not strictly necessary to send but nice for debugging
 | 
			
		||||
  // purposes.
 | 
			
		||||
  string client_info = 1;
 | 
			
		||||
  uint32 api_version_major = 2;
 | 
			
		||||
  uint32 api_version_minor = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Confirmation of successful connection request.
 | 
			
		||||
@@ -190,6 +202,10 @@ message DeviceInfoResponse {
 | 
			
		||||
  string project_version = 9;
 | 
			
		||||
 | 
			
		||||
  uint32 webserver_port = 10;
 | 
			
		||||
 | 
			
		||||
  uint32 bluetooth_proxy_version = 11;
 | 
			
		||||
 | 
			
		||||
  string manufacturer = 12;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ListEntitiesRequest {
 | 
			
		||||
@@ -1099,3 +1115,216 @@ message MediaPlayerCommandRequest {
 | 
			
		||||
  bool has_media_url = 6;
 | 
			
		||||
  string media_url = 7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== BLUETOOTH ====================
 | 
			
		||||
message SubscribeBluetoothLEAdvertisementsRequest {
 | 
			
		||||
  option (id) = 66;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothServiceData {
 | 
			
		||||
  string uuid = 1;
 | 
			
		||||
  repeated uint32 legacy_data = 2 [deprecated = true];
 | 
			
		||||
  bytes data = 3; // Changed in proto version 1.7
 | 
			
		||||
}
 | 
			
		||||
message BluetoothLEAdvertisementResponse {
 | 
			
		||||
  option (id) = 67;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  string name = 2;
 | 
			
		||||
  sint32 rssi = 3;
 | 
			
		||||
 | 
			
		||||
  repeated string service_uuids = 4;
 | 
			
		||||
  repeated BluetoothServiceData service_data = 5;
 | 
			
		||||
  repeated BluetoothServiceData manufacturer_data = 6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum BluetoothDeviceRequestType {
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0;
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1;
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2;
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothDeviceRequest {
 | 
			
		||||
  option (id) = 68;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  BluetoothDeviceRequestType request_type = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothDeviceConnectionResponse {
 | 
			
		||||
  option (id) = 69;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  bool connected = 2;
 | 
			
		||||
  uint32 mtu = 3;
 | 
			
		||||
  int32 error = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTGetServicesRequest {
 | 
			
		||||
  option (id) = 70;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTDescriptor {
 | 
			
		||||
  repeated uint64 uuid = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTCharacteristic {
 | 
			
		||||
  repeated uint64 uuid = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
  uint32 properties = 3;
 | 
			
		||||
  repeated BluetoothGATTDescriptor descriptors = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTService {
 | 
			
		||||
  repeated uint64 uuid = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
  repeated BluetoothGATTCharacteristic characteristics = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTGetServicesResponse {
 | 
			
		||||
  option (id) = 71;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  repeated BluetoothGATTService services = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTGetServicesDoneResponse {
 | 
			
		||||
  option (id) = 72;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTReadRequest {
 | 
			
		||||
  option (id) = 73;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTReadResponse {
 | 
			
		||||
  option (id) = 74;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
 | 
			
		||||
  bytes data = 3;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTWriteRequest {
 | 
			
		||||
  option (id) = 75;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
  bool response = 3;
 | 
			
		||||
 | 
			
		||||
  bytes data = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTReadDescriptorRequest {
 | 
			
		||||
  option (id) = 76;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTWriteDescriptorRequest {
 | 
			
		||||
  option (id) = 77;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
 | 
			
		||||
  bytes data = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTNotifyRequest {
 | 
			
		||||
  option (id) = 78;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
  bool enable = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTNotifyDataResponse {
 | 
			
		||||
  option (id) = 79;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
 | 
			
		||||
  bytes data = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message SubscribeBluetoothConnectionsFreeRequest {
 | 
			
		||||
  option (id) = 80;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothConnectionsFreeResponse {
 | 
			
		||||
  option (id) = 81;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint32 free = 1;
 | 
			
		||||
  uint32 limit = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTErrorResponse {
 | 
			
		||||
  option (id) = 82;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
  int32 error = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTWriteResponse {
 | 
			
		||||
  option (id) = 83;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothGATTNotifyResponse {
 | 
			
		||||
  option (id) = 84;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  uint32 handle = 2;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#include "esphome/core/entity_base.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
#include "esphome/core/version.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
#include "esphome/core/entity_base.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/version.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_DEEP_SLEEP
 | 
			
		||||
#include "esphome/components/deep_sleep/deep_sleep_component.h"
 | 
			
		||||
@@ -12,6 +12,9 @@
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
#include "esphome/components/homeassistant/time/homeassistant_time.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
@@ -823,6 +826,56 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) {
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg) {
 | 
			
		||||
  if (!this->bluetooth_le_advertisement_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
  if (this->client_api_version_major_ < 1 || this->client_api_version_minor_ < 7) {
 | 
			
		||||
    BluetoothLEAdvertisementResponse resp = msg;
 | 
			
		||||
    for (auto &service : resp.service_data) {
 | 
			
		||||
      service.legacy_data.assign(service.data.begin(), service.data.end());
 | 
			
		||||
      service.data.clear();
 | 
			
		||||
    }
 | 
			
		||||
    for (auto &manufacturer_data : resp.manufacturer_data) {
 | 
			
		||||
      manufacturer_data.legacy_data.assign(manufacturer_data.data.begin(), manufacturer_data.data.end());
 | 
			
		||||
      manufacturer_data.data.clear();
 | 
			
		||||
    }
 | 
			
		||||
    return this->send_bluetooth_le_advertisement_response(resp);
 | 
			
		||||
  }
 | 
			
		||||
  return this->send_bluetooth_le_advertisement_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) {
 | 
			
		||||
  bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) {
 | 
			
		||||
  bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_read(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) {
 | 
			
		||||
  bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_write(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) {
 | 
			
		||||
  bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_read_descriptor(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) {
 | 
			
		||||
  bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_write_descriptor(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) {
 | 
			
		||||
  bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_send_services(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIConnection::bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) {
 | 
			
		||||
  bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_notify(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_free(
 | 
			
		||||
    const SubscribeBluetoothConnectionsFreeRequest &msg) {
 | 
			
		||||
  BluetoothConnectionsFreeResponse resp;
 | 
			
		||||
  resp.free = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_free();
 | 
			
		||||
  resp.limit = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_limit();
 | 
			
		||||
  return resp;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
 | 
			
		||||
  if (this->log_subscription_ < level)
 | 
			
		||||
    return false;
 | 
			
		||||
@@ -840,11 +893,14 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
 | 
			
		||||
HelloResponse APIConnection::hello(const HelloRequest &msg) {
 | 
			
		||||
  this->client_info_ = msg.client_info + " (" + this->helper_->getpeername() + ")";
 | 
			
		||||
  this->helper_->set_log_info(client_info_);
 | 
			
		||||
  ESP_LOGV(TAG, "Hello from client: '%s'", this->client_info_.c_str());
 | 
			
		||||
  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(),
 | 
			
		||||
           this->client_api_version_major_, this->client_api_version_minor_);
 | 
			
		||||
 | 
			
		||||
  HelloResponse resp;
 | 
			
		||||
  resp.api_version_major = 1;
 | 
			
		||||
  resp.api_version_minor = 6;
 | 
			
		||||
  resp.api_version_minor = 7;
 | 
			
		||||
  resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
 | 
			
		||||
  resp.name = App.get_name();
 | 
			
		||||
 | 
			
		||||
@@ -876,6 +932,11 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
 | 
			
		||||
  resp.mac_address = get_mac_address_pretty();
 | 
			
		||||
  resp.esphome_version = ESPHOME_VERSION;
 | 
			
		||||
  resp.compilation_time = App.get_compilation_time();
 | 
			
		||||
#if defined(USE_ESP8266) || defined(USE_ESP32)
 | 
			
		||||
  resp.manufacturer = "Espressif";
 | 
			
		||||
#elif defined(USE_RP2040)
 | 
			
		||||
  resp.manufacturer = "Raspberry Pi";
 | 
			
		||||
#endif
 | 
			
		||||
  resp.model = ESPHOME_BOARD;
 | 
			
		||||
#ifdef USE_DEEP_SLEEP
 | 
			
		||||
  resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
 | 
			
		||||
@@ -886,6 +947,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_WEBSERVER
 | 
			
		||||
  resp.webserver_port = USE_WEBSERVER_PORT;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 2 : 1;
 | 
			
		||||
#endif
 | 
			
		||||
  return resp;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "api_frame_helper.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "api_pb2_service.h"
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#include "api_frame_helper.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
@@ -94,6 +94,20 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
      return;
 | 
			
		||||
    this->send_homeassistant_service_response(call);
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg);
 | 
			
		||||
 | 
			
		||||
  void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
 | 
			
		||||
  void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) override;
 | 
			
		||||
  void bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) override;
 | 
			
		||||
  void bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) override;
 | 
			
		||||
  void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) override;
 | 
			
		||||
  void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) override;
 | 
			
		||||
  void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
 | 
			
		||||
  BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
 | 
			
		||||
      const SubscribeBluetoothConnectionsFreeRequest &msg) override;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
  void send_time_request() {
 | 
			
		||||
    GetTimeRequest req;
 | 
			
		||||
@@ -134,6 +148,9 @@ 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();
 | 
			
		||||
@@ -167,6 +184,8 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  std::unique_ptr<APIFrameHelper> helper_;
 | 
			
		||||
 | 
			
		||||
  std::string client_info_;
 | 
			
		||||
  uint32_t client_api_version_major_{0};
 | 
			
		||||
  uint32_t client_api_version_minor_{0};
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  esp32_camera::CameraImageReader image_reader_;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -176,6 +195,7 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  uint32_t last_traffic_;
 | 
			
		||||
  bool sent_ping_{false};
 | 
			
		||||
  bool service_call_subscription_{false};
 | 
			
		||||
  bool bluetooth_le_advertisement_subscription_{false};
 | 
			
		||||
  bool next_close_ = false;
 | 
			
		||||
  APIServer *parent_;
 | 
			
		||||
  InitialStateIterator initial_state_iterator_;
 | 
			
		||||
 
 | 
			
		||||
@@ -116,9 +116,9 @@ class APINoiseFrameHelper : public APIFrameHelper {
 | 
			
		||||
  std::vector<uint8_t> prologue_;
 | 
			
		||||
 | 
			
		||||
  std::shared_ptr<APINoiseContext> ctx_;
 | 
			
		||||
  NoiseHandshakeState *handshake_ = nullptr;
 | 
			
		||||
  NoiseCipherState *send_cipher_ = nullptr;
 | 
			
		||||
  NoiseCipherState *recv_cipher_ = nullptr;
 | 
			
		||||
  NoiseHandshakeState *handshake_{nullptr};
 | 
			
		||||
  NoiseCipherState *send_cipher_{nullptr};
 | 
			
		||||
  NoiseCipherState *recv_cipher_{nullptr};
 | 
			
		||||
  NoiseProtocolId nid_;
 | 
			
		||||
 | 
			
		||||
  enum class State {
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -155,12 +155,20 @@ enum MediaPlayerCommand : uint32_t {
 | 
			
		||||
  MEDIA_PLAYER_COMMAND_MUTE = 3,
 | 
			
		||||
  MEDIA_PLAYER_COMMAND_UNMUTE = 4,
 | 
			
		||||
};
 | 
			
		||||
enum BluetoothDeviceRequestType : uint32_t {
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0,
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1,
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2,
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace enums
 | 
			
		||||
 | 
			
		||||
class HelloRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string client_info{};
 | 
			
		||||
  uint32_t api_version_major{0};
 | 
			
		||||
  uint32_t api_version_minor{0};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
@@ -168,6 +176,7 @@ class HelloRequest : public ProtoMessage {
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class HelloResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
@@ -263,6 +272,8 @@ class DeviceInfoResponse : public ProtoMessage {
 | 
			
		||||
  std::string project_name{};
 | 
			
		||||
  std::string project_version{};
 | 
			
		||||
  uint32_t webserver_port{0};
 | 
			
		||||
  uint32_t bluetooth_proxy_version{0};
 | 
			
		||||
  std::string manufacturer{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
@@ -1214,6 +1225,300 @@ class MediaPlayerCommandRequest : public ProtoMessage {
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class SubscribeBluetoothLEAdvertisementsRequest : 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 BluetoothServiceData : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string uuid{};
 | 
			
		||||
  std::vector<uint32_t> legacy_data{};
 | 
			
		||||
  std::string 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;
 | 
			
		||||
};
 | 
			
		||||
class BluetoothLEAdvertisementResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  int32_t rssi{0};
 | 
			
		||||
  std::vector<std::string> service_uuids{};
 | 
			
		||||
  std::vector<BluetoothServiceData> service_data{};
 | 
			
		||||
  std::vector<BluetoothServiceData> manufacturer_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;
 | 
			
		||||
};
 | 
			
		||||
class BluetoothDeviceRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  enums::BluetoothDeviceRequestType request_type{};
 | 
			
		||||
  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 BluetoothDeviceConnectionResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  bool connected{false};
 | 
			
		||||
  uint32_t mtu{0};
 | 
			
		||||
  int32_t error{0};
 | 
			
		||||
  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 BluetoothGATTGetServicesRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  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 BluetoothGATTDescriptor : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::vector<uint64_t> uuid{};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  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 BluetoothGATTCharacteristic : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::vector<uint64_t> uuid{};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  uint32_t properties{0};
 | 
			
		||||
  std::vector<BluetoothGATTDescriptor> descriptors{};
 | 
			
		||||
  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;
 | 
			
		||||
};
 | 
			
		||||
class BluetoothGATTService : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::vector<uint64_t> uuid{};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  std::vector<BluetoothGATTCharacteristic> characteristics{};
 | 
			
		||||
  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;
 | 
			
		||||
};
 | 
			
		||||
class BluetoothGATTGetServicesResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  std::vector<BluetoothGATTService> services{};
 | 
			
		||||
  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;
 | 
			
		||||
};
 | 
			
		||||
class BluetoothGATTGetServicesDoneResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  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 BluetoothGATTReadRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  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 BluetoothGATTReadResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  std::string 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;
 | 
			
		||||
};
 | 
			
		||||
class BluetoothGATTWriteRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  bool response{false};
 | 
			
		||||
  std::string 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;
 | 
			
		||||
};
 | 
			
		||||
class BluetoothGATTReadDescriptorRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  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 BluetoothGATTWriteDescriptorRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  std::string 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;
 | 
			
		||||
};
 | 
			
		||||
class BluetoothGATTNotifyRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  bool enable{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 BluetoothGATTNotifyDataResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  std::string 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;
 | 
			
		||||
};
 | 
			
		||||
class SubscribeBluetoothConnectionsFreeRequest : 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 BluetoothConnectionsFreeResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t free{0};
 | 
			
		||||
  uint32_t limit{0};
 | 
			
		||||
  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 BluetoothGATTErrorResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  int32_t error{0};
 | 
			
		||||
  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 BluetoothGATTWriteResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  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 BluetoothGATTNotifyResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  uint32_t handle{0};
 | 
			
		||||
  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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -328,6 +328,103 @@ bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayer
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
#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());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothLEAdvertisementResponse>(msg, 67);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_device_connection_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothDeviceConnectionResponse>(msg, 69);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothGATTGetServicesResponse>(msg, 71);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_gatt_get_services_done_response(
 | 
			
		||||
    const BluetoothGATTGetServicesDoneResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_done_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothGATTGetServicesDoneResponse>(msg, 72);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_gatt_read_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothGATTReadResponse>(msg, 74);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_data_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothGATTNotifyDataResponse>(msg, 79);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_connections_free_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothConnectionsFreeResponse>(msg, 81);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_gatt_error_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothGATTErrorResponse>(msg, 82);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_gatt_write_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothGATTWriteResponse>(msg, 83);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothGATTNotifyResponse>(msg, 84);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
 | 
			
		||||
  switch (msg_type) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
@@ -592,6 +689,103 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
			
		||||
      ESP_LOGVV(TAG, "on_media_player_command_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_media_player_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 66: {
 | 
			
		||||
      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);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 68: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      BluetoothDeviceRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_bluetooth_device_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_bluetooth_device_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 70: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      BluetoothGATTGetServicesRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_bluetooth_gatt_get_services_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_bluetooth_gatt_get_services_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 73: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      BluetoothGATTReadRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_bluetooth_gatt_read_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_bluetooth_gatt_read_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 75: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      BluetoothGATTWriteRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_bluetooth_gatt_write_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_bluetooth_gatt_write_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 76: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      BluetoothGATTReadDescriptorRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_bluetooth_gatt_read_descriptor_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_bluetooth_gatt_read_descriptor_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 77: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      BluetoothGATTWriteDescriptorRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_bluetooth_gatt_write_descriptor_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_bluetooth_gatt_write_descriptor_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 78: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      BluetoothGATTNotifyRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_bluetooth_gatt_notify_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_bluetooth_gatt_notify_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 80: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      SubscribeBluetoothConnectionsFreeRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      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;
 | 
			
		||||
    }
 | 
			
		||||
@@ -855,6 +1049,126 @@ void APIServerConnection::on_media_player_command_request(const MediaPlayerComma
 | 
			
		||||
  this->media_player_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
    const SubscribeBluetoothLEAdvertisementsRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->subscribe_bluetooth_le_advertisements(msg);
 | 
			
		||||
}
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->bluetooth_device_request(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->bluetooth_gatt_get_services(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->bluetooth_gatt_read(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->bluetooth_gatt_write(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->bluetooth_gatt_read_descriptor(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->bluetooth_gatt_write_descriptor(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->bluetooth_gatt_notify(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
 | 
			
		||||
    const SubscribeBluetoothConnectionsFreeRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
 | 
			
		||||
  if (!this->send_bluetooth_connections_free_response(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -153,6 +153,62 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
  virtual void on_subscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
      const SubscribeBluetoothLEAdvertisementsRequest &value){};
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_gatt_get_services_done_response(const BluetoothGATTGetServicesDoneResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
 | 
			
		||||
@@ -204,6 +260,32 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
  virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void bluetooth_device_request(const BluetoothDeviceRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
 | 
			
		||||
      const SubscribeBluetoothConnectionsFreeRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  void on_hello_request(const HelloRequest &msg) override;
 | 
			
		||||
@@ -250,6 +332,31 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
  void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -291,6 +291,79 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
 | 
			
		||||
    client->send_homeassistant_service_call(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServer::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call) {
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_bluetooth_le_advertisement(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServer::send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error) {
 | 
			
		||||
  BluetoothDeviceConnectionResponse call;
 | 
			
		||||
  call.address = address;
 | 
			
		||||
  call.connected = connected;
 | 
			
		||||
  call.mtu = mtu;
 | 
			
		||||
  call.error = error;
 | 
			
		||||
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_bluetooth_device_connection_response(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIServer::send_bluetooth_connections_free(uint8_t free, uint8_t limit) {
 | 
			
		||||
  BluetoothConnectionsFreeResponse call;
 | 
			
		||||
  call.free = free;
 | 
			
		||||
  call.limit = limit;
 | 
			
		||||
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_bluetooth_connections_free_response(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIServer::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call) {
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_bluetooth_gatt_read_response(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServer::send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call) {
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_bluetooth_gatt_write_response(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServer::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &call) {
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_bluetooth_gatt_notify_data_response(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServer::send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &call) {
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_bluetooth_gatt_notify_response(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServer::send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call) {
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_bluetooth_gatt_get_services_response(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServer::send_bluetooth_gatt_services_done(uint64_t address) {
 | 
			
		||||
  BluetoothGATTGetServicesDoneResponse call;
 | 
			
		||||
  call.address = address;
 | 
			
		||||
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_bluetooth_gatt_get_services_done_response(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServer::send_bluetooth_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) {
 | 
			
		||||
  BluetoothGATTErrorResponse call;
 | 
			
		||||
  call.address = address;
 | 
			
		||||
  call.handle = handle;
 | 
			
		||||
  call.error = error;
 | 
			
		||||
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_bluetooth_gatt_error_response(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
APIServer::APIServer() { global_api_server = this; }
 | 
			
		||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
 | 
			
		||||
                                               std::function<void(std::string)> f) {
 | 
			
		||||
 
 | 
			
		||||
@@ -73,6 +73,18 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
  void on_media_player_update(media_player::MediaPlayer *obj) override;
 | 
			
		||||
#endif
 | 
			
		||||
  void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call);
 | 
			
		||||
  void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu = 0, esp_err_t error = ESP_OK);
 | 
			
		||||
  void send_bluetooth_connections_free(uint8_t free, uint8_t limit);
 | 
			
		||||
  void send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call);
 | 
			
		||||
  void send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call);
 | 
			
		||||
  void send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &call);
 | 
			
		||||
  void send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &call);
 | 
			
		||||
  void send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call);
 | 
			
		||||
  void send_bluetooth_gatt_services_done(uint64_t address);
 | 
			
		||||
  void send_bluetooth_gatt_error(uint64_t address, uint16_t handle, esp_err_t error);
 | 
			
		||||
#endif
 | 
			
		||||
  void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
  void request_time();
 | 
			
		||||
 
 | 
			
		||||
@@ -70,7 +70,7 @@ class ProtoVarInt {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  void encode(std::vector<uint8_t> &out) {
 | 
			
		||||
    uint32_t val = this->value_;
 | 
			
		||||
    uint64_t val = this->value_;
 | 
			
		||||
    if (val <= 0x7F) {
 | 
			
		||||
      out.push_back(val);
 | 
			
		||||
      return;
 | 
			
		||||
 
 | 
			
		||||
@@ -92,9 +92,9 @@ 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_;
 | 
			
		||||
  sensor::Sensor *energy_sensor_;
 | 
			
		||||
  binary_sensor::BinarySensor *thunder_alert_binary_sensor_;
 | 
			
		||||
  sensor::Sensor *distance_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *energy_sensor_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *thunder_alert_binary_sensor_{nullptr};
 | 
			
		||||
  GPIOPin *irq_pin_;
 | 
			
		||||
 | 
			
		||||
  bool indoor_;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ CODEOWNERS = ["@OttoWinter"]
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.Schema({}),
 | 
			
		||||
    cv.only_with_arduino,
 | 
			
		||||
    cv.only_on(["esp32", "esp8266"]),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -89,8 +89,10 @@ enum BedjetCommand : uint8_t {
 | 
			
		||||
        "85%", "90%", "95%", "100%" \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
static const char *const BEDJET_FAN_STEP_NAMES[20] = BEDJET_FAN_STEP_NAMES_;
 | 
			
		||||
static const std::string BEDJET_FAN_STEP_NAME_STRINGS[20] = BEDJET_FAN_STEP_NAMES_;
 | 
			
		||||
static const uint8_t BEDJET_FAN_SPEED_COUNT = 20;
 | 
			
		||||
 | 
			
		||||
static const char *const BEDJET_FAN_STEP_NAMES[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_;
 | 
			
		||||
static const std::string BEDJET_FAN_STEP_NAME_STRINGS[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_;
 | 
			
		||||
static const std::set<std::string> BEDJET_FAN_STEP_NAMES_SET BEDJET_FAN_STEP_NAMES_;
 | 
			
		||||
 | 
			
		||||
}  // namespace bedjet
 | 
			
		||||
 
 | 
			
		||||
@@ -128,9 +128,9 @@ uint8_t BedJetHub::write_bedjet_packet_(BedjetPacket *pkt) {
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_cmd_,
 | 
			
		||||
                                         pkt->data_length + 1, (uint8_t *) &pkt->command, ESP_GATT_WRITE_TYPE_NO_RSP,
 | 
			
		||||
                                         ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
 | 
			
		||||
                                         this->char_handle_cmd_, pkt->data_length + 1, (uint8_t *) &pkt->command,
 | 
			
		||||
                                         ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -138,13 +138,13 @@ uint8_t BedJetHub::write_bedjet_packet_(BedjetPacket *pkt) {
 | 
			
		||||
uint8_t BedJetHub::set_notify_(const bool enable) {
 | 
			
		||||
  uint8_t status;
 | 
			
		||||
  if (enable) {
 | 
			
		||||
    status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda,
 | 
			
		||||
    status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
 | 
			
		||||
                                               this->char_handle_status_);
 | 
			
		||||
    if (status) {
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    status = esp_ble_gattc_unregister_for_notify(this->parent_->gattc_if, this->parent_->remote_bda,
 | 
			
		||||
    status = esp_ble_gattc_unregister_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
 | 
			
		||||
                                                 this->char_handle_status_);
 | 
			
		||||
    if (status) {
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] esp_ble_gattc_unregister_for_notify failed, status=%d", this->get_name().c_str(), status);
 | 
			
		||||
@@ -204,8 +204,8 @@ bool BedJetHub::discover_characteristics_() {
 | 
			
		||||
      result = false;
 | 
			
		||||
    } else {
 | 
			
		||||
      this->char_handle_name_ = chr->handle;
 | 
			
		||||
      auto status = esp_ble_gattc_read_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_name_,
 | 
			
		||||
                                            ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
      auto status = esp_ble_gattc_read_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
 | 
			
		||||
                                            this->char_handle_name_, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
      if (status) {
 | 
			
		||||
        ESP_LOGI(TAG, "[%s] Unable to read name characteristic: %d", this->get_name().c_str(), status);
 | 
			
		||||
      }
 | 
			
		||||
@@ -230,22 +230,6 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
 | 
			
		||||
      this->dispatch_state_(false);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_OPEN_EVT: {
 | 
			
		||||
      // FIXME: bug in BLEClient
 | 
			
		||||
      this->parent_->conn_id = param->open.conn_id;
 | 
			
		||||
      this->open_conn_id_ = param->open.conn_id;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case ESP_GATTC_CONNECT_EVT: {
 | 
			
		||||
      if (this->parent_->conn_id != param->connect.conn_id && this->open_conn_id_ != 0xff) {
 | 
			
		||||
        // FIXME: bug in BLEClient
 | 
			
		||||
        ESP_LOGW(TAG, "[%s] CONNECT_EVT unexpected conn_id; open=%d, parent=%d, param=%d", this->get_name().c_str(),
 | 
			
		||||
                 this->open_conn_id_, this->parent_->conn_id, param->connect.conn_id);
 | 
			
		||||
        this->parent_->conn_id = this->open_conn_id_;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_SEARCH_CMPL_EVT: {
 | 
			
		||||
      auto result = this->discover_characteristics_();
 | 
			
		||||
 | 
			
		||||
@@ -301,7 +285,7 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_READ_CHAR_EVT: {
 | 
			
		||||
      if (param->read.conn_id != this->parent_->conn_id)
 | 
			
		||||
      if (param->read.conn_id != this->parent_->get_conn_id())
 | 
			
		||||
        break;
 | 
			
		||||
      if (param->read.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
 | 
			
		||||
@@ -358,9 +342,9 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
 | 
			
		||||
      if (this->processing_)
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      if (param->notify.conn_id != this->parent_->conn_id) {
 | 
			
		||||
      if (param->notify.conn_id != this->parent_->get_conn_id()) {
 | 
			
		||||
        ESP_LOGW(TAG, "[%s] Received notify event for unexpected parent conn: expect %x, got %x",
 | 
			
		||||
                 this->get_name().c_str(), this->parent_->conn_id, param->notify.conn_id);
 | 
			
		||||
                 this->get_name().c_str(), this->parent_->get_conn_id(), param->notify.conn_id);
 | 
			
		||||
        // FIXME: bug in BLEClient holding wrong conn_id.
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@@ -394,7 +378,7 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
 | 
			
		||||
        if (needs_extra) {
 | 
			
		||||
          // This means the packet was partial, so read the status characteristic to get the second part.
 | 
			
		||||
          // Ideally this will complete quickly. We won't process additional notification events until it does.
 | 
			
		||||
          auto status = esp_ble_gattc_read_char(this->parent_->gattc_if, this->parent_->conn_id,
 | 
			
		||||
          auto status = esp_ble_gattc_read_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
 | 
			
		||||
                                                this->char_handle_status_, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
          if (status) {
 | 
			
		||||
            ESP_LOGI(TAG, "[%s] Unable to read extended status packet", this->get_name().c_str());
 | 
			
		||||
@@ -438,15 +422,15 @@ uint8_t BedJetHub::write_notify_config_descriptor_(bool enable) {
 | 
			
		||||
 | 
			
		||||
  // NOTE: BLEClient uses `uint8_t*` of length 1, but BLE spec requires 16 bits.
 | 
			
		||||
  uint16_t notify_en = enable ? 1 : 0;
 | 
			
		||||
  auto status =
 | 
			
		||||
      esp_ble_gattc_write_char_descr(this->parent_->gattc_if, this->parent_->conn_id, handle, sizeof(notify_en),
 | 
			
		||||
                                     (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  auto status = esp_ble_gattc_write_char_descr(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), handle,
 | 
			
		||||
                                               sizeof(notify_en), (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP,
 | 
			
		||||
                                               ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  if (status) {
 | 
			
		||||
    ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status);
 | 
			
		||||
    return status;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGD(TAG, "[%s] wrote notify=%s to status config 0x%04x, for conn %d", this->get_name().c_str(),
 | 
			
		||||
           enable ? "true" : "false", handle, this->parent_->conn_id);
 | 
			
		||||
           enable ? "true" : "false", handle, this->parent_->get_conn_id());
 | 
			
		||||
  return ESP_GATT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -500,7 +484,7 @@ void BedJetHub::update() { this->dispatch_status_(); }
 | 
			
		||||
void BedJetHub::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BedJet Hub '%s'", this->get_name().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  ble_client.app_id: %d", this->parent()->app_id);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  ble_client.conn_id: %d", this->parent()->conn_id);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  ble_client.conn_id: %d", this->parent()->get_conn_id());
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this)
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Child components (%d):", this->children_.size());
 | 
			
		||||
  for (auto *child : this->children_) {
 | 
			
		||||
 
 | 
			
		||||
@@ -167,8 +167,6 @@ class BedJetHub : public esphome::ble_client::BLEClientNode, public PollingCompo
 | 
			
		||||
  uint16_t char_handle_status_;
 | 
			
		||||
  uint16_t config_descr_status_;
 | 
			
		||||
 | 
			
		||||
  uint8_t open_conn_id_ = -1;
 | 
			
		||||
 | 
			
		||||
  uint8_t write_notify_config_descriptor_(bool enable);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,19 +9,17 @@ from esphome.const import (
 | 
			
		||||
    CONF_RECEIVE_TIMEOUT,
 | 
			
		||||
    CONF_TIME_ID,
 | 
			
		||||
)
 | 
			
		||||
from . import (
 | 
			
		||||
from .. import (
 | 
			
		||||
    BEDJET_CLIENT_SCHEMA,
 | 
			
		||||
    bedjet_ns,
 | 
			
		||||
    register_bedjet_child,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
CODEOWNERS = ["@jhansche"]
 | 
			
		||||
DEPENDENCIES = ["ble_client"]
 | 
			
		||||
DEPENDENCIES = ["bedjet"]
 | 
			
		||||
 | 
			
		||||
bedjet_ns = cg.esphome_ns.namespace("bedjet")
 | 
			
		||||
BedJetClimate = bedjet_ns.class_(
 | 
			
		||||
    "BedJetClimate", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent
 | 
			
		||||
)
 | 
			
		||||
BedJetClimate = bedjet_ns.class_("BedJetClimate", climate.Climate, cg.PollingComponent)
 | 
			
		||||
BedjetHeatMode = bedjet_ns.enum("BedjetHeatMode")
 | 
			
		||||
BEDJET_HEAT_MODES = {
 | 
			
		||||
    "heat": BedjetHeatMode.HEAT_MODE_HEAT,
 | 
			
		||||
@@ -15,13 +15,13 @@ float bedjet_temp_to_c(const uint8_t temp) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const std::string *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) {
 | 
			
		||||
  if (fan_step <= 19)
 | 
			
		||||
  if (fan_step < BEDJET_FAN_SPEED_COUNT)
 | 
			
		||||
    return &BEDJET_FAN_STEP_NAME_STRINGS[fan_step];
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t bedjet_fan_speed_to_step(const std::string &fan_step_percent) {
 | 
			
		||||
  for (int i = 0; i < sizeof(BEDJET_FAN_STEP_NAME_STRINGS); i++) {
 | 
			
		||||
  for (int i = 0; i < BEDJET_FAN_SPEED_COUNT; i++) {
 | 
			
		||||
    if (fan_step_percent == BEDJET_FAN_STEP_NAME_STRINGS[i]) {
 | 
			
		||||
      return i;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/climate/climate.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "bedjet_child.h"
 | 
			
		||||
#include "bedjet_codec.h"
 | 
			
		||||
#include "bedjet_hub.h"
 | 
			
		||||
#include "esphome/components/bedjet/bedjet_child.h"
 | 
			
		||||
#include "esphome/components/bedjet/bedjet_codec.h"
 | 
			
		||||
#include "esphome/components/bedjet/bedjet_hub.h"
 | 
			
		||||
#include "esphome/components/climate/climate.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								esphome/components/bedjet/fan/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								esphome/components/bedjet/fan/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import fan
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
)
 | 
			
		||||
from .. import (
 | 
			
		||||
    BEDJET_CLIENT_SCHEMA,
 | 
			
		||||
    bedjet_ns,
 | 
			
		||||
    register_bedjet_child,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
CODEOWNERS = ["@jhansche"]
 | 
			
		||||
DEPENDENCIES = ["bedjet"]
 | 
			
		||||
 | 
			
		||||
BedJetFan = bedjet_ns.class_("BedJetFan", fan.Fan, cg.PollingComponent)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    fan.FAN_SCHEMA.extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BedJetFan),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(BEDJET_CLIENT_SCHEMA)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await fan.register_fan(var, config)
 | 
			
		||||
    await register_bedjet_child(var, config)
 | 
			
		||||
							
								
								
									
										108
									
								
								esphome/components/bedjet/fan/bedjet_fan.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								esphome/components/bedjet/fan/bedjet_fan.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
#include "bedjet_fan.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bedjet {
 | 
			
		||||
 | 
			
		||||
using namespace esphome::fan;
 | 
			
		||||
 | 
			
		||||
void BedJetFan::dump_config() { LOG_FAN("", "BedJet Fan", this); }
 | 
			
		||||
std::string BedJetFan::describe() { return "BedJet Fan"; }
 | 
			
		||||
 | 
			
		||||
void BedJetFan::control(const fan::FanCall &call) {
 | 
			
		||||
  ESP_LOGD(TAG, "Received BedJetFan::control");
 | 
			
		||||
  if (!this->parent_->is_connected()) {
 | 
			
		||||
    ESP_LOGW(TAG, "Not connected, cannot handle control call yet.");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  bool did_change = false;
 | 
			
		||||
 | 
			
		||||
  if (call.get_state().has_value() && this->state != *call.get_state()) {
 | 
			
		||||
    // Turning off is easy:
 | 
			
		||||
    if (this->state && this->parent_->button_off()) {
 | 
			
		||||
      this->state = false;
 | 
			
		||||
      this->publish_state();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Turning on, we have to choose a specific mode; for now, use "COOL" mode
 | 
			
		||||
    // In the future we could configure the mode to use for fan.turn_on.
 | 
			
		||||
    if (this->parent_->button_cool()) {
 | 
			
		||||
      this->state = true;
 | 
			
		||||
      did_change = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ignore speed changes if not on or turning on
 | 
			
		||||
  if (this->state && call.get_speed().has_value()) {
 | 
			
		||||
    this->speed = *call.get_speed();
 | 
			
		||||
    this->parent_->set_fan_index(this->speed);
 | 
			
		||||
    did_change = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (did_change) {
 | 
			
		||||
    this->publish_state();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BedJetFan::on_status(const BedjetStatusPacket *data) {
 | 
			
		||||
  ESP_LOGVV(TAG, "[%s] Handling on_status with data=%p", this->get_name().c_str(), (void *) data);
 | 
			
		||||
  bool did_change = false;
 | 
			
		||||
  bool new_state = data->mode != MODE_STANDBY && data->mode != MODE_WAIT;
 | 
			
		||||
 | 
			
		||||
  if (new_state != this->state) {
 | 
			
		||||
    this->state = new_state;
 | 
			
		||||
    did_change = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (data->fan_step != this->speed) {
 | 
			
		||||
    this->speed = data->fan_step;
 | 
			
		||||
    did_change = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (did_change) {
 | 
			
		||||
    this->publish_state();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Attempts to update the fan device from the last received BedjetStatusPacket.
 | 
			
		||||
 *
 | 
			
		||||
 * This will be called from #on_status() when the parent dispatches new status packets,
 | 
			
		||||
 * and from #update() when the polling interval is triggered.
 | 
			
		||||
 *
 | 
			
		||||
 * @return `true` if the status has been applied; `false` if there is nothing to apply.
 | 
			
		||||
 */
 | 
			
		||||
bool BedJetFan::update_status_() {
 | 
			
		||||
  if (!this->parent_->is_connected())
 | 
			
		||||
    return false;
 | 
			
		||||
  if (!this->parent_->has_status())
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto *status = this->parent_->get_status_packet();
 | 
			
		||||
 | 
			
		||||
  if (status == nullptr)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  this->on_status(status);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BedJetFan::update() {
 | 
			
		||||
  ESP_LOGD(TAG, "[%s] update()", this->get_name().c_str());
 | 
			
		||||
  // TODO: if the hub component is already polling, do we also need to include polling?
 | 
			
		||||
  //  We're already going to get on_status() at the hub's polling interval.
 | 
			
		||||
  auto result = this->update_status_();
 | 
			
		||||
  ESP_LOGD(TAG, "[%s] update_status result=%s", this->get_name().c_str(), result ? "true" : "false");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Resets states to defaults. */
 | 
			
		||||
void BedJetFan::reset_state_() {
 | 
			
		||||
  this->state = false;
 | 
			
		||||
  this->publish_state();
 | 
			
		||||
}
 | 
			
		||||
}  // namespace bedjet
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										40
									
								
								esphome/components/bedjet/fan/bedjet_fan.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								esphome/components/bedjet/fan/bedjet_fan.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/components/bedjet/bedjet_child.h"
 | 
			
		||||
#include "esphome/components/bedjet/bedjet_codec.h"
 | 
			
		||||
#include "esphome/components/bedjet/bedjet_hub.h"
 | 
			
		||||
#include "esphome/components/fan/fan.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bedjet {
 | 
			
		||||
 | 
			
		||||
class BedJetFan : public fan::Fan, public BedJetClient, public PollingComponent {
 | 
			
		||||
 public:
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
 | 
			
		||||
 | 
			
		||||
  /* BedJetClient status update */
 | 
			
		||||
  void on_status(const BedjetStatusPacket *data) override;
 | 
			
		||||
  void on_bedjet_state(bool is_ready) override{};
 | 
			
		||||
  std::string describe() override;
 | 
			
		||||
 | 
			
		||||
  fan::FanTraits get_traits() override { return fan::FanTraits(false, true, false, BEDJET_FAN_SPEED_COUNT); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void control(const fan::FanCall &call) override;
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  void reset_state_();
 | 
			
		||||
  bool update_status_();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace bedjet
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -13,6 +13,9 @@ void BinarySensorMap::loop() {
 | 
			
		||||
    case BINARY_SENSOR_MAP_TYPE_GROUP:
 | 
			
		||||
      this->process_group_();
 | 
			
		||||
      break;
 | 
			
		||||
    case BINARY_SENSOR_MAP_TYPE_SUM:
 | 
			
		||||
      this->process_sum_();
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -46,6 +49,34 @@ void BinarySensorMap::process_group_() {
 | 
			
		||||
  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
 | 
			
		||||
  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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // 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);
 | 
			
		||||
  }
 | 
			
		||||
  this->last_mask_ = mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BinarySensorMap::add_channel(binary_sensor::BinarySensor *sensor, float value) {
 | 
			
		||||
  BinarySensorMapChannel sensor_channel{
 | 
			
		||||
      .binary_sensor = sensor,
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ namespace binary_sensor_map {
 | 
			
		||||
 | 
			
		||||
enum BinarySensorMapType {
 | 
			
		||||
  BINARY_SENSOR_MAP_TYPE_GROUP,
 | 
			
		||||
  BINARY_SENSOR_MAP_TYPE_SUM,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct BinarySensorMapChannel {
 | 
			
		||||
@@ -50,8 +51,10 @@ class BinarySensorMap : public sensor::Sensor, public Component {
 | 
			
		||||
  /**
 | 
			
		||||
   * methods to process the types of binary_sensor_maps
 | 
			
		||||
   * GROUP: process_group_() just map to a value
 | 
			
		||||
   * ADD: process_add_() adds all the values
 | 
			
		||||
   * */
 | 
			
		||||
  void process_group_();
 | 
			
		||||
  void process_sum_();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace binary_sensor_map
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ from esphome.const import (
 | 
			
		||||
    ICON_CHECK_CIRCLE_OUTLINE,
 | 
			
		||||
    CONF_BINARY_SENSOR,
 | 
			
		||||
    CONF_GROUP,
 | 
			
		||||
    CONF_SUM,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["binary_sensor"]
 | 
			
		||||
@@ -21,6 +22,7 @@ SensorMapType = binary_sensor_map_ns.enum("SensorMapType")
 | 
			
		||||
 | 
			
		||||
SENSOR_MAP_TYPES = {
 | 
			
		||||
    CONF_GROUP: SensorMapType.BINARY_SENSOR_MAP_TYPE_GROUP,
 | 
			
		||||
    CONF_SUM: SensorMapType.BINARY_SENSOR_MAP_TYPE_SUM,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
entry = {
 | 
			
		||||
@@ -41,6 +43,17 @@ CONFIG_SCHEMA = cv.typed_schema(
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        CONF_SUM: sensor.sensor_schema(
 | 
			
		||||
            BinarySensorMap,
 | 
			
		||||
            icon=ICON_CHECK_CIRCLE_OUTLINE,
 | 
			
		||||
            accuracy_decimals=0,
 | 
			
		||||
        ).extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Required(CONF_CHANNELS): cv.All(
 | 
			
		||||
                    cv.ensure_list(entry), cv.Length(min=1)
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    },
 | 
			
		||||
    lower=True,
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -75,16 +75,16 @@ class BL0939 : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  sensor::Sensor *voltage_sensor_;
 | 
			
		||||
  sensor::Sensor *current_sensor_1_;
 | 
			
		||||
  sensor::Sensor *current_sensor_2_;
 | 
			
		||||
  sensor::Sensor *voltage_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *current_sensor_1_{nullptr};
 | 
			
		||||
  sensor::Sensor *current_sensor_2_{nullptr};
 | 
			
		||||
  // NB This may be negative as the circuits is seemingly able to measure
 | 
			
		||||
  // power in both directions
 | 
			
		||||
  sensor::Sensor *power_sensor_1_;
 | 
			
		||||
  sensor::Sensor *power_sensor_2_;
 | 
			
		||||
  sensor::Sensor *energy_sensor_1_;
 | 
			
		||||
  sensor::Sensor *energy_sensor_2_;
 | 
			
		||||
  sensor::Sensor *energy_sensor_sum_;
 | 
			
		||||
  sensor::Sensor *power_sensor_1_{nullptr};
 | 
			
		||||
  sensor::Sensor *power_sensor_2_{nullptr};
 | 
			
		||||
  sensor::Sensor *energy_sensor_1_{nullptr};
 | 
			
		||||
  sensor::Sensor *energy_sensor_2_{nullptr};
 | 
			
		||||
  sensor::Sensor *energy_sensor_sum_{nullptr};
 | 
			
		||||
 | 
			
		||||
  // Divide by this to turn into Watt
 | 
			
		||||
  float power_reference_ = BL0939_PREF;
 | 
			
		||||
 
 | 
			
		||||
@@ -75,14 +75,14 @@ class BL0940 : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  sensor::Sensor *voltage_sensor_;
 | 
			
		||||
  sensor::Sensor *current_sensor_;
 | 
			
		||||
  sensor::Sensor *voltage_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *current_sensor_{nullptr};
 | 
			
		||||
  // NB This may be negative as the circuits is seemingly able to measure
 | 
			
		||||
  // power in both directions
 | 
			
		||||
  sensor::Sensor *power_sensor_;
 | 
			
		||||
  sensor::Sensor *energy_sensor_;
 | 
			
		||||
  sensor::Sensor *internal_temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *external_temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *power_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *energy_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *internal_temperature_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *external_temperature_sensor_{nullptr};
 | 
			
		||||
 | 
			
		||||
  // Max difference between two measurements of the temperature. Used to avoid noise.
 | 
			
		||||
  float max_temperature_diff_{0};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								esphome/components/bl0942/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/bl0942/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
CODEOWNERS = ["@dbuezas"]
 | 
			
		||||
							
								
								
									
										121
									
								
								esphome/components/bl0942/bl0942.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								esphome/components/bl0942/bl0942.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
			
		||||
#include "bl0942.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bl0942 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "bl0942";
 | 
			
		||||
 | 
			
		||||
static const uint8_t BL0942_READ_COMMAND = 0x58;
 | 
			
		||||
static const uint8_t BL0942_FULL_PACKET = 0xAA;
 | 
			
		||||
static const uint8_t BL0942_PACKET_HEADER = 0x55;
 | 
			
		||||
 | 
			
		||||
static const uint8_t BL0942_WRITE_COMMAND = 0xA8;
 | 
			
		||||
static const uint8_t BL0942_REG_I_FAST_RMS_CTRL = 0x10;
 | 
			
		||||
static const uint8_t BL0942_REG_MODE = 0x18;
 | 
			
		||||
static const uint8_t BL0942_REG_SOFT_RESET = 0x19;
 | 
			
		||||
static const uint8_t BL0942_REG_USR_WRPROT = 0x1A;
 | 
			
		||||
static const uint8_t BL0942_REG_TPS_CTRL = 0x1B;
 | 
			
		||||
 | 
			
		||||
// TODO: Confirm insialisation works as intended
 | 
			
		||||
const uint8_t BL0942_INIT[5][6] = {
 | 
			
		||||
    // Reset to default
 | 
			
		||||
    {BL0942_WRITE_COMMAND, BL0942_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38},
 | 
			
		||||
    // Enable User Operation Write
 | 
			
		||||
    {BL0942_WRITE_COMMAND, BL0942_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0},
 | 
			
		||||
    // 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS
 | 
			
		||||
    {BL0942_WRITE_COMMAND, BL0942_REG_MODE, 0x00, 0x10, 0x00, 0x37},
 | 
			
		||||
    // 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS
 | 
			
		||||
    {BL0942_WRITE_COMMAND, BL0942_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE},
 | 
			
		||||
    // 0x181C = Half cycle, Fast RMS threshold 6172
 | 
			
		||||
    {BL0942_WRITE_COMMAND, BL0942_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}};
 | 
			
		||||
 | 
			
		||||
void BL0942::loop() {
 | 
			
		||||
  DataPacket buffer;
 | 
			
		||||
  if (!this->available()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (read_array((uint8_t *) &buffer, sizeof(buffer))) {
 | 
			
		||||
    if (validate_checksum(&buffer)) {
 | 
			
		||||
      received_package_(&buffer);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGW(TAG, "Junk on wire. Throwing away partial message");
 | 
			
		||||
    while (read() >= 0)
 | 
			
		||||
      ;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BL0942::validate_checksum(DataPacket *data) {
 | 
			
		||||
  uint8_t checksum = BL0942_READ_COMMAND;
 | 
			
		||||
  // Whole package but checksum
 | 
			
		||||
  uint8_t *raw = (uint8_t *) data;
 | 
			
		||||
  for (uint32_t i = 0; i < sizeof(*data) - 1; i++) {
 | 
			
		||||
    checksum += raw[i];
 | 
			
		||||
  }
 | 
			
		||||
  checksum ^= 0xFF;
 | 
			
		||||
  if (checksum != data->checksum) {
 | 
			
		||||
    ESP_LOGW(TAG, "BL0942 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
 | 
			
		||||
  }
 | 
			
		||||
  return checksum == data->checksum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0942::update() {
 | 
			
		||||
  this->flush();
 | 
			
		||||
  this->write_byte(BL0942_READ_COMMAND);
 | 
			
		||||
  this->write_byte(BL0942_FULL_PACKET);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0942::setup() {
 | 
			
		||||
  for (auto *i : BL0942_INIT) {
 | 
			
		||||
    this->write_array(i, 6);
 | 
			
		||||
    delay(1);
 | 
			
		||||
  }
 | 
			
		||||
  this->flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0942::received_package_(DataPacket *data) {
 | 
			
		||||
  // Bad header
 | 
			
		||||
  if (data->frame_header != BL0942_PACKET_HEADER) {
 | 
			
		||||
    ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  float v_rms = (uint24_t) data->v_rms / voltage_reference_;
 | 
			
		||||
  float i_rms = (uint24_t) data->i_rms / current_reference_;
 | 
			
		||||
  float watt = (int24_t) data->watt / power_reference_;
 | 
			
		||||
  uint32_t cf_cnt = (uint24_t) data->cf_cnt;
 | 
			
		||||
  float total_energy_consumption = cf_cnt / energy_reference_;
 | 
			
		||||
  float frequency = 1000000.0f / data->frequency;
 | 
			
		||||
 | 
			
		||||
  if (voltage_sensor_ != nullptr) {
 | 
			
		||||
    voltage_sensor_->publish_state(v_rms);
 | 
			
		||||
  }
 | 
			
		||||
  if (current_sensor_ != nullptr) {
 | 
			
		||||
    current_sensor_->publish_state(i_rms);
 | 
			
		||||
  }
 | 
			
		||||
  if (power_sensor_ != nullptr) {
 | 
			
		||||
    power_sensor_->publish_state(watt);
 | 
			
		||||
  }
 | 
			
		||||
  if (energy_sensor_ != nullptr) {
 | 
			
		||||
    energy_sensor_->publish_state(total_energy_consumption);
 | 
			
		||||
  }
 | 
			
		||||
  if (frequency_sensor_ != nullptr) {
 | 
			
		||||
    frequency_sensor_->publish_state(frequency);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %d, ∫P %fkWh, frequency %f°Hz, status 0x%08X", v_rms, i_rms, watt,
 | 
			
		||||
           cf_cnt, total_energy_consumption, frequency, data->status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0942::dump_config() {  // NOLINT(readability-function-cognitive-complexity)
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BL0942:");
 | 
			
		||||
  LOG_SENSOR("", "Voltage", this->voltage_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Current", this->current_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Power", this->power_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Energy", this->energy_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "frequency", this->frequency_sensor_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace bl0942
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										68
									
								
								esphome/components/bl0942/bl0942.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								esphome/components/bl0942/bl0942.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/datatypes.h"
 | 
			
		||||
#include "esphome/components/uart/uart.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bl0942 {
 | 
			
		||||
 | 
			
		||||
static const float BL0942_PREF = 596;              // taken from tasmota
 | 
			
		||||
static const float BL0942_UREF = 15873.35944299;   // should be 73989/1.218
 | 
			
		||||
static const float BL0942_IREF = 251213.46469622;  // 305978/1.218
 | 
			
		||||
static const float BL0942_EREF = 3304.61127328;    // Measured
 | 
			
		||||
 | 
			
		||||
struct DataPacket {
 | 
			
		||||
  uint8_t frame_header;
 | 
			
		||||
  uint24_le_t i_rms;
 | 
			
		||||
  uint24_le_t v_rms;
 | 
			
		||||
  uint24_le_t i_fast_rms;
 | 
			
		||||
  int24_le_t watt;
 | 
			
		||||
  uint24_le_t cf_cnt;
 | 
			
		||||
  uint16_le_t frequency;
 | 
			
		||||
  uint8_t reserved1;
 | 
			
		||||
  uint8_t status;
 | 
			
		||||
  uint8_t reserved2;
 | 
			
		||||
  uint8_t reserved3;
 | 
			
		||||
  uint8_t checksum;
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
class BL0942 : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
 | 
			
		||||
  void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
 | 
			
		||||
  void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
 | 
			
		||||
  void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
 | 
			
		||||
  void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; }
 | 
			
		||||
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  sensor::Sensor *voltage_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *current_sensor_{nullptr};
 | 
			
		||||
  // NB This may be negative as the circuits is seemingly able to measure
 | 
			
		||||
  // power in both directions
 | 
			
		||||
  sensor::Sensor *power_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *energy_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *frequency_sensor_{nullptr};
 | 
			
		||||
 | 
			
		||||
  // Divide by this to turn into Watt
 | 
			
		||||
  float power_reference_ = BL0942_PREF;
 | 
			
		||||
  // Divide by this to turn into Volt
 | 
			
		||||
  float voltage_reference_ = BL0942_UREF;
 | 
			
		||||
  // Divide by this to turn into Ampere
 | 
			
		||||
  float current_reference_ = BL0942_IREF;
 | 
			
		||||
  // Divide by this to turn into kWh
 | 
			
		||||
  float energy_reference_ = BL0942_EREF;
 | 
			
		||||
 | 
			
		||||
  static bool validate_checksum(DataPacket *data);
 | 
			
		||||
 | 
			
		||||
  void received_package_(DataPacket *data);
 | 
			
		||||
};
 | 
			
		||||
}  // namespace bl0942
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										93
									
								
								esphome/components/bl0942/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								esphome/components/bl0942/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, uart
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CURRENT,
 | 
			
		||||
    CONF_ENERGY,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_POWER,
 | 
			
		||||
    CONF_VOLTAGE,
 | 
			
		||||
    CONF_FREQUENCY,
 | 
			
		||||
    DEVICE_CLASS_CURRENT,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_POWER,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_FREQUENCY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_AMPERE,
 | 
			
		||||
    UNIT_KILOWATT_HOURS,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    UNIT_WATT,
 | 
			
		||||
    UNIT_HERTZ,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["uart"]
 | 
			
		||||
 | 
			
		||||
bl0942_ns = cg.esphome_ns.namespace("bl0942")
 | 
			
		||||
BL0942 = bl0942_ns.class_("BL0942", cg.PollingComponent, uart.UARTDevice)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BL0942),
 | 
			
		||||
            cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_VOLT,
 | 
			
		||||
                accuracy_decimals=1,
 | 
			
		||||
                device_class=DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_CURRENT): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_AMPERE,
 | 
			
		||||
                accuracy_decimals=2,
 | 
			
		||||
                device_class=DEVICE_CLASS_CURRENT,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_POWER): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_WATT,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_POWER,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ENERGY): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_KILOWATT_HOURS,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_ENERGY,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_HERTZ,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_FREQUENCY,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(uart.UART_DEVICE_SCHEMA)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await uart.register_uart_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_VOLTAGE in config:
 | 
			
		||||
        conf = config[CONF_VOLTAGE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_voltage_sensor(sens))
 | 
			
		||||
    if CONF_CURRENT in config:
 | 
			
		||||
        conf = config[CONF_CURRENT]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_current_sensor(sens))
 | 
			
		||||
    if CONF_POWER in config:
 | 
			
		||||
        conf = config[CONF_POWER]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_power_sensor(sens))
 | 
			
		||||
    if CONF_ENERGY in config:
 | 
			
		||||
        conf = config[CONF_ENERGY]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_energy_sensor(sens))
 | 
			
		||||
    if CONF_FREQUENCY in config:
 | 
			
		||||
        conf = config[CONF_FREQUENCY]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_frequency_sensor(sens))
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import esp32_ble_tracker
 | 
			
		||||
from esphome.components import esp32_ble_tracker, esp32_ble_client
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CHARACTERISTIC_UUID,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
@@ -14,13 +14,12 @@ from esphome.const import (
 | 
			
		||||
)
 | 
			
		||||
from esphome import automation
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["esp32_ble_client"]
 | 
			
		||||
CODEOWNERS = ["@buxtronix"]
 | 
			
		||||
DEPENDENCIES = ["esp32_ble_tracker"]
 | 
			
		||||
 | 
			
		||||
ble_client_ns = cg.esphome_ns.namespace("ble_client")
 | 
			
		||||
BLEClient = ble_client_ns.class_(
 | 
			
		||||
    "BLEClient", cg.Component, esp32_ble_tracker.ESPBTClient
 | 
			
		||||
)
 | 
			
		||||
BLEClient = ble_client_ns.class_("BLEClient", esp32_ble_client.BLEClientBase)
 | 
			
		||||
BLEClientNode = ble_client_ns.class_("BLEClientNode")
 | 
			
		||||
BLEClientNodeConstRef = BLEClientNode.operator("ref").operator("const")
 | 
			
		||||
# Triggers
 | 
			
		||||
@@ -101,12 +100,40 @@ async def ble_write_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    else:
 | 
			
		||||
        cg.add(var.set_value_simple(value))
 | 
			
		||||
 | 
			
		||||
    serv_uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
 | 
			
		||||
    cg.add(var.set_service_uuid128(serv_uuid128))
 | 
			
		||||
    char_uuid128 = esp32_ble_tracker.as_reversed_hex_array(
 | 
			
		||||
        config[CONF_CHARACTERISTIC_UUID]
 | 
			
		||||
    )
 | 
			
		||||
    cg.add(var.set_char_uuid128(char_uuid128))
 | 
			
		||||
    if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
 | 
			
		||||
        )
 | 
			
		||||
    elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
 | 
			
		||||
        )
 | 
			
		||||
    elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
 | 
			
		||||
        uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
 | 
			
		||||
        cg.add(var.set_service_uuid128(uuid128))
 | 
			
		||||
 | 
			
		||||
    if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_char_uuid16(
 | 
			
		||||
                esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
 | 
			
		||||
        esp32_ble_tracker.bt_uuid32_format
 | 
			
		||||
    ):
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_char_uuid32(
 | 
			
		||||
                esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
 | 
			
		||||
        esp32_ble_tracker.bt_uuid128_format
 | 
			
		||||
    ):
 | 
			
		||||
        uuid128 = esp32_ble_tracker.as_reversed_hex_array(
 | 
			
		||||
            config[CONF_CHARACTERISTIC_UUID]
 | 
			
		||||
        )
 | 
			
		||||
        cg.add(var.set_char_uuid128(uuid128))
 | 
			
		||||
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#include "automation.h"
 | 
			
		||||
 | 
			
		||||
#include <esp_bt_defs.h>
 | 
			
		||||
#include <esp_gap_ble_api.h>
 | 
			
		||||
#include <esp_gattc_api.h>
 | 
			
		||||
#include <esp_bt_defs.h>
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
@@ -31,8 +31,8 @@ void BLEWriterClientNode::write(const std::vector<uint8_t> &value) {
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGVV(TAG, "Will write %d bytes: %s", value.size(), format_hex_pretty(value).c_str());
 | 
			
		||||
  esp_err_t err =
 | 
			
		||||
      esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, this->ble_char_handle_, value.size(),
 | 
			
		||||
                               const_cast<uint8_t *>(value.data()), write_type, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
      esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->ble_char_handle_,
 | 
			
		||||
                               value.size(), const_cast<uint8_t *>(value.data()), write_type, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Error writing to characteristic: %s!", esp_err_to_name(err));
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,8 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode {
 | 
			
		||||
  void loop() override {}
 | 
			
		||||
  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                           esp_ble_gattc_cb_param_t *param) override {
 | 
			
		||||
    if (event == ESP_GATTC_DISCONNECT_EVT && memcmp(param->disconnect.remote_bda, this->parent_->remote_bda, 6) == 0)
 | 
			
		||||
    if (event == ESP_GATTC_DISCONNECT_EVT &&
 | 
			
		||||
        memcmp(param->disconnect.remote_bda, this->parent_->get_remote_bda(), 6) == 0)
 | 
			
		||||
      this->trigger();
 | 
			
		||||
    if (event == ESP_GATTC_SEARCH_CMPL_EVT)
 | 
			
		||||
      this->node_state = espbt::ClientState::ESTABLISHED;
 | 
			
		||||
@@ -45,10 +46,14 @@ class BLEWriterClientNode : public BLEClientNode {
 | 
			
		||||
  // Attempts to write the contents of value to char_uuid_.
 | 
			
		||||
  void write(const std::vector<uint8_t> &value);
 | 
			
		||||
 | 
			
		||||
  void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
 | 
			
		||||
 | 
			
		||||
  void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
 | 
			
		||||
  void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
 | 
			
		||||
  void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
 | 
			
		||||
 | 
			
		||||
  void set_char_uuid16(uint16_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
 | 
			
		||||
  void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
 | 
			
		||||
  void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
 | 
			
		||||
 | 
			
		||||
  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                           esp_ble_gattc_cb_param_t *param) override;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,9 @@
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "ble_client.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_client/ble_client_base.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
 | 
			
		||||
#include "ble_client.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
@@ -11,22 +12,13 @@ namespace ble_client {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "ble_client";
 | 
			
		||||
 | 
			
		||||
float BLEClient::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
 | 
			
		||||
 | 
			
		||||
void BLEClient::setup() {
 | 
			
		||||
  auto ret = esp_ble_gattc_app_register(this->app_id);
 | 
			
		||||
  if (ret) {
 | 
			
		||||
    ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
  }
 | 
			
		||||
  this->set_states_(espbt::ClientState::IDLE);
 | 
			
		||||
  BLEClientBase::setup();
 | 
			
		||||
  this->enabled = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEClient::loop() {
 | 
			
		||||
  if (this->state() == espbt::ClientState::DISCOVERED) {
 | 
			
		||||
    this->connect();
 | 
			
		||||
  }
 | 
			
		||||
  BLEClientBase::loop();
 | 
			
		||||
  for (auto *node : this->nodes_)
 | 
			
		||||
    node->loop();
 | 
			
		||||
}
 | 
			
		||||
@@ -39,34 +31,7 @@ void BLEClient::dump_config() {
 | 
			
		||||
bool BLEClient::parse_device(const espbt::ESPBTDevice &device) {
 | 
			
		||||
  if (!this->enabled)
 | 
			
		||||
    return false;
 | 
			
		||||
  if (device.address_uint64() != this->address)
 | 
			
		||||
    return false;
 | 
			
		||||
  if (this->state() != espbt::ClientState::IDLE)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str());
 | 
			
		||||
  this->set_states_(espbt::ClientState::DISCOVERED);
 | 
			
		||||
 | 
			
		||||
  auto addr = device.address_uint64();
 | 
			
		||||
  this->remote_bda[0] = (addr >> 40) & 0xFF;
 | 
			
		||||
  this->remote_bda[1] = (addr >> 32) & 0xFF;
 | 
			
		||||
  this->remote_bda[2] = (addr >> 24) & 0xFF;
 | 
			
		||||
  this->remote_bda[3] = (addr >> 16) & 0xFF;
 | 
			
		||||
  this->remote_bda[4] = (addr >> 8) & 0xFF;
 | 
			
		||||
  this->remote_bda[5] = (addr >> 0) & 0xFF;
 | 
			
		||||
  this->remote_addr_type = device.get_address_type();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string BLEClient::address_str() const {
 | 
			
		||||
  char buf[20];
 | 
			
		||||
  sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", (uint8_t)(this->address >> 40) & 0xff,
 | 
			
		||||
          (uint8_t)(this->address >> 32) & 0xff, (uint8_t)(this->address >> 24) & 0xff,
 | 
			
		||||
          (uint8_t)(this->address >> 16) & 0xff, (uint8_t)(this->address >> 8) & 0xff,
 | 
			
		||||
          (uint8_t)(this->address >> 0) & 0xff);
 | 
			
		||||
  std::string ret;
 | 
			
		||||
  ret = buf;
 | 
			
		||||
  return ret;
 | 
			
		||||
  return BLEClientBase::parse_device(device);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEClient::set_enabled(bool enabled) {
 | 
			
		||||
@@ -74,7 +39,7 @@ void BLEClient::set_enabled(bool enabled) {
 | 
			
		||||
    return;
 | 
			
		||||
  if (!enabled && this->state() != espbt::ClientState::IDLE) {
 | 
			
		||||
    ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str());
 | 
			
		||||
    auto ret = esp_ble_gattc_close(this->gattc_if, this->conn_id);
 | 
			
		||||
    auto ret = esp_ble_gattc_close(this->gattc_if_, this->conn_id_);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
      ESP_LOGW(TAG, "esp_ble_gattc_close error, address=%s status=%d", this->address_str().c_str(), ret);
 | 
			
		||||
    }
 | 
			
		||||
@@ -82,125 +47,13 @@ void BLEClient::set_enabled(bool enabled) {
 | 
			
		||||
  this->enabled = enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEClient::connect() {
 | 
			
		||||
  ESP_LOGI(TAG, "Attempting BLE connection to %s", this->address_str().c_str());
 | 
			
		||||
  auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, this->remote_addr_type, true);
 | 
			
		||||
  if (ret) {
 | 
			
		||||
    ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret);
 | 
			
		||||
    this->set_states_(espbt::ClientState::IDLE);
 | 
			
		||||
  } else {
 | 
			
		||||
    this->set_states_(espbt::ClientState::CONNECTING);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if,
 | 
			
		||||
bool BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if,
 | 
			
		||||
                                    esp_ble_gattc_cb_param_t *param) {
 | 
			
		||||
  if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id)
 | 
			
		||||
    return;
 | 
			
		||||
  if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  bool all_established = this->all_nodes_established_();
 | 
			
		||||
 | 
			
		||||
  switch (event) {
 | 
			
		||||
    case ESP_GATTC_REG_EVT: {
 | 
			
		||||
      if (param->reg.status == ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGV(TAG, "gattc registered app id %d", this->app_id);
 | 
			
		||||
        this->gattc_if = esp_gattc_if;
 | 
			
		||||
      } else {
 | 
			
		||||
        ESP_LOGE(TAG, "gattc app registration failed id=%d code=%d", param->reg.app_id, param->reg.status);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_OPEN_EVT: {
 | 
			
		||||
      ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str());
 | 
			
		||||
      this->conn_id = param->open.conn_id;
 | 
			
		||||
      if (param->open.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status);
 | 
			
		||||
        this->set_states_(espbt::ClientState::IDLE);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_CONNECT_EVT: {
 | 
			
		||||
      ESP_LOGV(TAG, "[%s] ESP_GATTC_CONNECT_EVT", this->address_str().c_str());
 | 
			
		||||
      if (this->conn_id != param->connect.conn_id) {
 | 
			
		||||
        ESP_LOGD(TAG, "[%s] Unexpected conn_id in CONNECT_EVT: param conn=%d, open conn=%d",
 | 
			
		||||
                 this->address_str().c_str(), param->connect.conn_id, this->conn_id);
 | 
			
		||||
      }
 | 
			
		||||
      auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if, param->connect.conn_id);
 | 
			
		||||
      if (ret) {
 | 
			
		||||
        ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%x", ret);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_CFG_MTU_EVT: {
 | 
			
		||||
      if (param->cfg_mtu.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "cfg_mtu to %s failed, mtu %d, status %d", this->address_str().c_str(), param->cfg_mtu.mtu,
 | 
			
		||||
                 param->cfg_mtu.status);
 | 
			
		||||
        this->set_states_(espbt::ClientState::IDLE);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu);
 | 
			
		||||
      esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_DISCONNECT_EVT: {
 | 
			
		||||
      if (memcmp(param->disconnect.remote_bda, this->remote_bda, 6) != 0) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->address_str().c_str(), param->disconnect.reason);
 | 
			
		||||
      for (auto &svc : this->services_)
 | 
			
		||||
        delete svc;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
      this->services_.clear();
 | 
			
		||||
      this->set_states_(espbt::ClientState::IDLE);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_SEARCH_RES_EVT: {
 | 
			
		||||
      BLEService *ble_service = new BLEService();  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
      ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid);
 | 
			
		||||
      ble_service->start_handle = param->search_res.start_handle;
 | 
			
		||||
      ble_service->end_handle = param->search_res.end_handle;
 | 
			
		||||
      ble_service->client = this;
 | 
			
		||||
      this->services_.push_back(ble_service);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_SEARCH_CMPL_EVT: {
 | 
			
		||||
      ESP_LOGV(TAG, "[%s] ESP_GATTC_SEARCH_CMPL_EVT", this->address_str().c_str());
 | 
			
		||||
      for (auto &svc : this->services_) {
 | 
			
		||||
        ESP_LOGI(TAG, "Service UUID: %s", svc->uuid.to_string().c_str());
 | 
			
		||||
        ESP_LOGI(TAG, "  start_handle: 0x%x  end_handle: 0x%x", svc->start_handle, svc->end_handle);
 | 
			
		||||
        svc->parse_characteristics();
 | 
			
		||||
      }
 | 
			
		||||
      this->set_states_(espbt::ClientState::CONNECTED);
 | 
			
		||||
      this->set_state(espbt::ClientState::ESTABLISHED);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
 | 
			
		||||
      auto *descr = this->get_config_descriptor(param->reg_for_notify.handle);
 | 
			
		||||
      if (descr == nullptr) {
 | 
			
		||||
        ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", param->reg_for_notify.handle);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      if (descr->uuid.get_uuid().len != ESP_UUID_LEN_16 ||
 | 
			
		||||
          descr->uuid.get_uuid().uuid.uuid16 != ESP_GATT_UUID_CHAR_CLIENT_CONFIG) {
 | 
			
		||||
        ESP_LOGW(TAG, "Handle 0x%x (uuid %s) is not a client config char uuid", param->reg_for_notify.handle,
 | 
			
		||||
                 descr->uuid.to_string().c_str());
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      uint16_t notify_en = 1;
 | 
			
		||||
      auto status =
 | 
			
		||||
          esp_ble_gattc_write_char_descr(this->gattc_if, this->conn_id, descr->handle, sizeof(notify_en),
 | 
			
		||||
                                         (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
      if (status) {
 | 
			
		||||
        ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  if (!BLEClientBase::gattc_event_handler(event, esp_gattc_if, param))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  for (auto *node : this->nodes_)
 | 
			
		||||
    node->gattc_event_handler(event, esp_gattc_if, param);
 | 
			
		||||
 | 
			
		||||
@@ -210,239 +63,30 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
 | 
			
		||||
      delete svc;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
    this->services_.clear();
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEClient::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
 | 
			
		||||
  switch (event) {
 | 
			
		||||
    // This event is sent by the server when it requests security
 | 
			
		||||
    case ESP_GAP_BLE_SEC_REQ_EVT:
 | 
			
		||||
      ESP_LOGV(TAG, "ESP_GAP_BLE_SEC_REQ_EVT %x", event);
 | 
			
		||||
      esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
 | 
			
		||||
      break;
 | 
			
		||||
    // This event is sent once authentication has completed
 | 
			
		||||
    case ESP_GAP_BLE_AUTH_CMPL_EVT:
 | 
			
		||||
      esp_bd_addr_t bd_addr;
 | 
			
		||||
      memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
 | 
			
		||||
      ESP_LOGI(TAG, "auth complete. remote BD_ADDR: %s", format_hex(bd_addr, 6).c_str());
 | 
			
		||||
      if (!param->ble_security.auth_cmpl.success) {
 | 
			
		||||
        ESP_LOGE(TAG, "auth fail reason = 0x%x", param->ble_security.auth_cmpl.fail_reason);
 | 
			
		||||
      } else {
 | 
			
		||||
        ESP_LOGV(TAG, "auth success. address type = %d auth mode = %d", param->ble_security.auth_cmpl.addr_type,
 | 
			
		||||
                 param->ble_security.auth_cmpl.auth_mode);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    // There are other events we'll want to implement at some point to support things like pass key
 | 
			
		||||
    // https://github.com/espressif/esp-idf/blob/cba69dd088344ed9d26739f04736ae7a37541b3a/examples/bluetooth/bluedroid/ble/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  BLEClientBase::gap_event_handler(event, param);
 | 
			
		||||
 | 
			
		||||
  for (auto *node : this->nodes_)
 | 
			
		||||
    node->gap_event_handler(event, param);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEClient::set_state(espbt::ClientState state) {
 | 
			
		||||
  BLEClientBase::set_state(state);
 | 
			
		||||
  for (auto &node : nodes_)
 | 
			
		||||
    node->node_state = state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BLEClient::all_nodes_established_() {
 | 
			
		||||
  if (this->state() != espbt::ClientState::ESTABLISHED)
 | 
			
		||||
    return false;
 | 
			
		||||
  for (auto &node : nodes_) {
 | 
			
		||||
    if (node->node_state != espbt::ClientState::ESTABLISHED)
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse GATT values into a float for a sensor.
 | 
			
		||||
// Ref: https://www.bluetooth.com/specifications/assigned-numbers/format-types/
 | 
			
		||||
float BLEClient::parse_char_value(uint8_t *value, uint16_t length) {
 | 
			
		||||
  // A length of one means a single octet value.
 | 
			
		||||
  if (length == 0)
 | 
			
		||||
    return 0;
 | 
			
		||||
  if (length == 1)
 | 
			
		||||
    return (float) ((uint8_t) value[0]);
 | 
			
		||||
 | 
			
		||||
  switch (value[0]) {
 | 
			
		||||
    case 0x1:  // boolean.
 | 
			
		||||
    case 0x2:  // 2bit.
 | 
			
		||||
    case 0x3:  // nibble.
 | 
			
		||||
    case 0x4:  // uint8.
 | 
			
		||||
      return (float) ((uint8_t) value[1]);
 | 
			
		||||
    case 0x5:  // uint12.
 | 
			
		||||
    case 0x6:  // uint16.
 | 
			
		||||
      if (length > 2) {
 | 
			
		||||
        return (float) ((uint16_t)(value[1] << 8) + (uint16_t) value[2]);
 | 
			
		||||
      }
 | 
			
		||||
    case 0x7:  // uint24.
 | 
			
		||||
      if (length > 3) {
 | 
			
		||||
        return (float) ((uint32_t)(value[1] << 16) + (uint32_t)(value[2] << 8) + (uint32_t)(value[3]));
 | 
			
		||||
      }
 | 
			
		||||
    case 0x8:  // uint32.
 | 
			
		||||
      if (length > 4) {
 | 
			
		||||
        return (float) ((uint32_t)(value[1] << 24) + (uint32_t)(value[2] << 16) + (uint32_t)(value[3] << 8) +
 | 
			
		||||
                        (uint32_t)(value[4]));
 | 
			
		||||
      }
 | 
			
		||||
    case 0xC:  // int8.
 | 
			
		||||
      return (float) ((int8_t) value[1]);
 | 
			
		||||
    case 0xD:  // int12.
 | 
			
		||||
    case 0xE:  // int16.
 | 
			
		||||
      if (length > 2) {
 | 
			
		||||
        return (float) ((int16_t)(value[1] << 8) + (int16_t) value[2]);
 | 
			
		||||
      }
 | 
			
		||||
    case 0xF:  // int24.
 | 
			
		||||
      if (length > 3) {
 | 
			
		||||
        return (float) ((int32_t)(value[1] << 16) + (int32_t)(value[2] << 8) + (int32_t)(value[3]));
 | 
			
		||||
      }
 | 
			
		||||
    case 0x10:  // int32.
 | 
			
		||||
      if (length > 4) {
 | 
			
		||||
        return (float) ((int32_t)(value[1] << 24) + (int32_t)(value[2] << 16) + (int32_t)(value[3] << 8) +
 | 
			
		||||
                        (int32_t)(value[4]));
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGW(TAG, "Cannot parse characteristic value of type 0x%x length %d", value[0], length);
 | 
			
		||||
  return NAN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEService *BLEClient::get_service(espbt::ESPBTUUID uuid) {
 | 
			
		||||
  for (auto *svc : this->services_) {
 | 
			
		||||
    if (svc->uuid == uuid)
 | 
			
		||||
      return svc;
 | 
			
		||||
  }
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEService *BLEClient::get_service(uint16_t uuid) { return this->get_service(espbt::ESPBTUUID::from_uint16(uuid)); }
 | 
			
		||||
 | 
			
		||||
BLECharacteristic *BLEClient::get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr) {
 | 
			
		||||
  auto *svc = this->get_service(service);
 | 
			
		||||
  if (svc == nullptr)
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  return svc->get_characteristic(chr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLECharacteristic *BLEClient::get_characteristic(uint16_t service, uint16_t chr) {
 | 
			
		||||
  return this->get_characteristic(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEDescriptor *BLEClient::get_config_descriptor(uint16_t handle) {
 | 
			
		||||
  for (auto &svc : this->services_) {
 | 
			
		||||
    for (auto &chr : svc->characteristics) {
 | 
			
		||||
      if (chr->handle == handle) {
 | 
			
		||||
        for (auto &desc : chr->descriptors) {
 | 
			
		||||
          if (desc->uuid == espbt::ESPBTUUID::from_uint16(0x2902))
 | 
			
		||||
            return desc;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLECharacteristic *BLEService::get_characteristic(espbt::ESPBTUUID uuid) {
 | 
			
		||||
  for (auto &chr : this->characteristics) {
 | 
			
		||||
    if (chr->uuid == uuid)
 | 
			
		||||
      return chr;
 | 
			
		||||
  }
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLECharacteristic *BLEService::get_characteristic(uint16_t uuid) {
 | 
			
		||||
  return this->get_characteristic(espbt::ESPBTUUID::from_uint16(uuid));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEDescriptor *BLEClient::get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr) {
 | 
			
		||||
  auto *svc = this->get_service(service);
 | 
			
		||||
  if (svc == nullptr)
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  auto *ch = svc->get_characteristic(chr);
 | 
			
		||||
  if (ch == nullptr)
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  return ch->get_descriptor(descr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEDescriptor *BLEClient::get_descriptor(uint16_t service, uint16_t chr, uint16_t descr) {
 | 
			
		||||
  return this->get_descriptor(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr),
 | 
			
		||||
                              espbt::ESPBTUUID::from_uint16(descr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEService::~BLEService() {
 | 
			
		||||
  for (auto &chr : this->characteristics)
 | 
			
		||||
    delete chr;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEService::parse_characteristics() {
 | 
			
		||||
  uint16_t offset = 0;
 | 
			
		||||
  esp_gattc_char_elem_t result;
 | 
			
		||||
 | 
			
		||||
  while (true) {
 | 
			
		||||
    uint16_t count = 1;
 | 
			
		||||
    esp_gatt_status_t status = esp_ble_gattc_get_all_char(
 | 
			
		||||
        this->client->gattc_if, this->client->conn_id, this->start_handle, this->end_handle, &result, &count, offset);
 | 
			
		||||
    if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    if (status != ESP_GATT_OK) {
 | 
			
		||||
      ESP_LOGW(TAG, "esp_ble_gattc_get_all_char error, status=%d", status);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    if (count == 0) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BLECharacteristic *characteristic = new BLECharacteristic();  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
    characteristic->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
 | 
			
		||||
    characteristic->properties = result.properties;
 | 
			
		||||
    characteristic->handle = result.char_handle;
 | 
			
		||||
    characteristic->service = this;
 | 
			
		||||
    this->characteristics.push_back(characteristic);
 | 
			
		||||
    ESP_LOGI(TAG, " characteristic %s, handle 0x%x, properties 0x%x", characteristic->uuid.to_string().c_str(),
 | 
			
		||||
             characteristic->handle, characteristic->properties);
 | 
			
		||||
    characteristic->parse_descriptors();
 | 
			
		||||
    offset++;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLECharacteristic::~BLECharacteristic() {
 | 
			
		||||
  for (auto &desc : this->descriptors)
 | 
			
		||||
    delete desc;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLECharacteristic::parse_descriptors() {
 | 
			
		||||
  uint16_t offset = 0;
 | 
			
		||||
  esp_gattc_descr_elem_t result;
 | 
			
		||||
 | 
			
		||||
  while (true) {
 | 
			
		||||
    uint16_t count = 1;
 | 
			
		||||
    esp_gatt_status_t status = esp_ble_gattc_get_all_descr(
 | 
			
		||||
        this->service->client->gattc_if, this->service->client->conn_id, this->handle, &result, &count, offset);
 | 
			
		||||
    if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    if (status != ESP_GATT_OK) {
 | 
			
		||||
      ESP_LOGW(TAG, "esp_ble_gattc_get_all_descr error, status=%d", status);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    if (count == 0) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BLEDescriptor *desc = new BLEDescriptor();  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
    desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
 | 
			
		||||
    desc->handle = result.handle;
 | 
			
		||||
    desc->characteristic = this;
 | 
			
		||||
    this->descriptors.push_back(desc);
 | 
			
		||||
    ESP_LOGV(TAG, "   descriptor %s, handle 0x%x", desc->uuid.to_string().c_str(), desc->handle);
 | 
			
		||||
    offset++;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) {
 | 
			
		||||
  for (auto &desc : this->descriptors) {
 | 
			
		||||
    if (desc->uuid == uuid)
 | 
			
		||||
      return desc;
 | 
			
		||||
  }
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) {
 | 
			
		||||
  return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type) {
 | 
			
		||||
  auto *client = this->service->client;
 | 
			
		||||
  auto status = esp_ble_gattc_write_char(client->gattc_if, client->conn_id, this->handle, new_val_size, new_val,
 | 
			
		||||
                                         write_type, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  if (status) {
 | 
			
		||||
    ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) {
 | 
			
		||||
  write_value(new_val, new_val_size, ESP_GATT_WRITE_TYPE_NO_RSP);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ble_client
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/esp32_ble_client/ble_client_base.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
@@ -18,15 +19,16 @@ namespace ble_client {
 | 
			
		||||
 | 
			
		||||
namespace espbt = esphome::esp32_ble_tracker;
 | 
			
		||||
 | 
			
		||||
using namespace esp32_ble_client;
 | 
			
		||||
 | 
			
		||||
class BLEClient;
 | 
			
		||||
class BLEService;
 | 
			
		||||
class BLECharacteristic;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
  virtual void loop(){};
 | 
			
		||||
  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; }
 | 
			
		||||
  espbt::ESPBTClient *client;
 | 
			
		||||
  // This should be transitioned to Established once the node no longer needs
 | 
			
		||||
@@ -42,57 +44,17 @@ class BLEClientNode {
 | 
			
		||||
  uint64_t address_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BLEDescriptor {
 | 
			
		||||
 public:
 | 
			
		||||
  espbt::ESPBTUUID uuid;
 | 
			
		||||
  uint16_t handle;
 | 
			
		||||
 | 
			
		||||
  BLECharacteristic *characteristic;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BLECharacteristic {
 | 
			
		||||
 public:
 | 
			
		||||
  ~BLECharacteristic();
 | 
			
		||||
  espbt::ESPBTUUID uuid;
 | 
			
		||||
  uint16_t handle;
 | 
			
		||||
  esp_gatt_char_prop_t properties;
 | 
			
		||||
  std::vector<BLEDescriptor *> descriptors;
 | 
			
		||||
  void parse_descriptors();
 | 
			
		||||
  BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid);
 | 
			
		||||
  BLEDescriptor *get_descriptor(uint16_t uuid);
 | 
			
		||||
  void write_value(uint8_t *new_val, int16_t new_val_size);
 | 
			
		||||
  void write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type);
 | 
			
		||||
  BLEService *service;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BLEService {
 | 
			
		||||
 public:
 | 
			
		||||
  ~BLEService();
 | 
			
		||||
  espbt::ESPBTUUID uuid;
 | 
			
		||||
  uint16_t start_handle;
 | 
			
		||||
  uint16_t end_handle;
 | 
			
		||||
  std::vector<BLECharacteristic *> characteristics;
 | 
			
		||||
  BLEClient *client;
 | 
			
		||||
  void parse_characteristics();
 | 
			
		||||
  BLECharacteristic *get_characteristic(espbt::ESPBTUUID uuid);
 | 
			
		||||
  BLECharacteristic *get_characteristic(uint16_t uuid);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BLEClient : public espbt::ESPBTClient, public Component {
 | 
			
		||||
class BLEClient : public BLEClientBase {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 | 
			
		||||
  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
  bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                           esp_ble_gattc_cb_param_t *param) override;
 | 
			
		||||
 | 
			
		||||
  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
 | 
			
		||||
  bool parse_device(const espbt::ESPBTDevice &device) override;
 | 
			
		||||
  void on_scan_end() override {}
 | 
			
		||||
  void connect() override;
 | 
			
		||||
 | 
			
		||||
  void set_address(uint64_t address) { this->address = address; }
 | 
			
		||||
 | 
			
		||||
  void set_enabled(bool enabled);
 | 
			
		||||
 | 
			
		||||
@@ -102,43 +64,14 @@ class BLEClient : public espbt::ESPBTClient, public Component {
 | 
			
		||||
    this->nodes_.push_back(node);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  BLEService *get_service(espbt::ESPBTUUID uuid);
 | 
			
		||||
  BLEService *get_service(uint16_t uuid);
 | 
			
		||||
  BLECharacteristic *get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr);
 | 
			
		||||
  BLECharacteristic *get_characteristic(uint16_t service, uint16_t chr);
 | 
			
		||||
  BLEDescriptor *get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr);
 | 
			
		||||
  BLEDescriptor *get_descriptor(uint16_t service, uint16_t chr, uint16_t descr);
 | 
			
		||||
  // Get the configuration descriptor for the given characteristic handle.
 | 
			
		||||
  BLEDescriptor *get_config_descriptor(uint16_t handle);
 | 
			
		||||
 | 
			
		||||
  float parse_char_value(uint8_t *value, uint16_t length);
 | 
			
		||||
 | 
			
		||||
  int gattc_if;
 | 
			
		||||
  esp_bd_addr_t remote_bda;
 | 
			
		||||
  esp_ble_addr_type_t remote_addr_type;
 | 
			
		||||
  uint16_t conn_id;
 | 
			
		||||
  uint64_t address;
 | 
			
		||||
  bool enabled;
 | 
			
		||||
  std::string address_str() const;
 | 
			
		||||
 | 
			
		||||
  void set_state(espbt::ClientState state) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void set_states_(espbt::ClientState st) {
 | 
			
		||||
    this->set_state(st);
 | 
			
		||||
    for (auto &node : nodes_)
 | 
			
		||||
      node->node_state = st;
 | 
			
		||||
  }
 | 
			
		||||
  bool all_nodes_established_() {
 | 
			
		||||
    if (this->state() != espbt::ClientState::ESTABLISHED)
 | 
			
		||||
      return false;
 | 
			
		||||
    for (auto &node : nodes_) {
 | 
			
		||||
      if (node->node_state != espbt::ClientState::ESTABLISHED)
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  bool all_nodes_established_();
 | 
			
		||||
 | 
			
		||||
  std::vector<BLEClientNode *> nodes_;
 | 
			
		||||
  std::vector<BLEService *> services_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ble_client
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,11 @@ from esphome.const import (
 | 
			
		||||
    CONF_CHARACTERISTIC_UUID,
 | 
			
		||||
    CONF_LAMBDA,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_TYPE,
 | 
			
		||||
    CONF_SERVICE_UUID,
 | 
			
		||||
    DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_DECIBEL_MILLIWATT,
 | 
			
		||||
)
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from .. import ble_client_ns
 | 
			
		||||
@@ -16,6 +20,8 @@ CONF_DESCRIPTOR_UUID = "descriptor_uuid"
 | 
			
		||||
 | 
			
		||||
CONF_NOTIFY = "notify"
 | 
			
		||||
CONF_ON_NOTIFY = "on_notify"
 | 
			
		||||
TYPE_CHARACTERISTIC = "characteristic"
 | 
			
		||||
TYPE_RSSI = "rssi"
 | 
			
		||||
 | 
			
		||||
adv_data_t = cg.std_vector.template(cg.uint8)
 | 
			
		||||
adv_data_t_const_ref = adv_data_t.operator("ref").operator("const")
 | 
			
		||||
@@ -27,33 +33,67 @@ BLESensorNotifyTrigger = ble_client_ns.class_(
 | 
			
		||||
    "BLESensorNotifyTrigger", automation.Trigger.template(cg.float_)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        BLESensor,
 | 
			
		||||
        accuracy_decimals=0,
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Optional(CONF_LAMBDA): cv.returning_lambda,
 | 
			
		||||
            cv.Optional(CONF_NOTIFY, default=False): cv.boolean,
 | 
			
		||||
            cv.Optional(CONF_ON_NOTIFY): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
			
		||||
                        BLESensorNotifyTrigger
 | 
			
		||||
                    ),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(ble_client.BLE_CLIENT_SCHEMA)
 | 
			
		||||
BLEClientRssiSensor = ble_client_ns.class_(
 | 
			
		||||
    "BLEClientRSSISensor", sensor.Sensor, cg.PollingComponent, ble_client.BLEClientNode
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
def checkType(value):
 | 
			
		||||
    if CONF_TYPE not in value and CONF_SERVICE_UUID in value:
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            "Looks like you're trying to create a ble characteristic sensor. Please add `type: characteristic` to your sensor config."
 | 
			
		||||
        )
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    checkType,
 | 
			
		||||
    cv.typed_schema(
 | 
			
		||||
        {
 | 
			
		||||
            TYPE_CHARACTERISTIC: sensor.sensor_schema(
 | 
			
		||||
                BLESensor,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
            )
 | 
			
		||||
            .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
            .extend(ble_client.BLE_CLIENT_SCHEMA)
 | 
			
		||||
            .extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
                    cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
                    cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
                    cv.Optional(CONF_LAMBDA): cv.returning_lambda,
 | 
			
		||||
                    cv.Optional(CONF_NOTIFY, default=False): cv.boolean,
 | 
			
		||||
                    cv.Optional(CONF_ON_NOTIFY): automation.validate_automation(
 | 
			
		||||
                        {
 | 
			
		||||
                            cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
			
		||||
                                BLESensorNotifyTrigger
 | 
			
		||||
                            ),
 | 
			
		||||
                        }
 | 
			
		||||
                    ),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            TYPE_RSSI: sensor.sensor_schema(
 | 
			
		||||
                BLEClientRssiSensor,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                unit_of_measurement=UNIT_DECIBEL_MILLIWATT,
 | 
			
		||||
                device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            )
 | 
			
		||||
            .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
            .extend(ble_client.BLE_CLIENT_SCHEMA),
 | 
			
		||||
        },
 | 
			
		||||
        lower=True,
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def rssi_sensor_to_code(config):
 | 
			
		||||
    var = await sensor.new_sensor(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await ble_client.register_ble_node(var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def characteristic_sensor_to_code(config):
 | 
			
		||||
    var = await sensor.new_sensor(config)
 | 
			
		||||
    if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
        cg.add(
 | 
			
		||||
@@ -125,3 +165,10 @@ async def to_code(config):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await ble_client.register_ble_node(trigger, config)
 | 
			
		||||
        await automation.build_automation(trigger, [(float, "x")], conf)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    if config[CONF_TYPE] == TYPE_RSSI:
 | 
			
		||||
        await rssi_sensor_to_code(config)
 | 
			
		||||
    elif config[CONF_TYPE] == TYPE_CHARACTERISTIC:
 | 
			
		||||
        await characteristic_sensor_to_code(config)
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,8 @@ class BLESensorNotifyTrigger : public Trigger<float>, public BLESensor {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case ESP_GATTC_NOTIFY_EVT: {
 | 
			
		||||
        if (param->notify.conn_id != this->sensor_->parent()->conn_id || param->notify.handle != this->sensor_->handle)
 | 
			
		||||
        if (param->notify.conn_id != this->sensor_->parent()->get_conn_id() ||
 | 
			
		||||
            param->notify.handle != this->sensor_->handle)
 | 
			
		||||
          break;
 | 
			
		||||
        this->trigger(this->sensor_->parent()->parse_char_value(param->notify.value, param->notify.value_len));
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										78
									
								
								esphome/components/ble_client/sensor/ble_rssi_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								esphome/components/ble_client/sensor/ble_rssi_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
#include "ble_rssi_sensor.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ble_client {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "ble_rssi_sensor";
 | 
			
		||||
 | 
			
		||||
void BLEClientRSSISensor::loop() {}
 | 
			
		||||
 | 
			
		||||
void BLEClientRSSISensor::dump_config() {
 | 
			
		||||
  LOG_SENSOR("", "BLE Client RSSI Sensor", this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  MAC address        : %s", this->parent()->address_str().c_str());
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEClientRSSISensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                                              esp_ble_gattc_cb_param_t *param) {
 | 
			
		||||
  switch (event) {
 | 
			
		||||
    case ESP_GATTC_OPEN_EVT: {
 | 
			
		||||
      if (param->open.status == ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str());
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_DISCONNECT_EVT: {
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str());
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
      this->publish_state(NAN);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_SEARCH_CMPL_EVT:
 | 
			
		||||
      this->node_state = espbt::ClientState::ESTABLISHED;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEClientRSSISensor::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
 | 
			
		||||
  switch (event) {
 | 
			
		||||
    // server response on RSSI request:
 | 
			
		||||
    case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
 | 
			
		||||
      if (param->read_rssi_cmpl.status == ESP_BT_STATUS_SUCCESS) {
 | 
			
		||||
        int8_t rssi = param->read_rssi_cmpl.rssi;
 | 
			
		||||
        ESP_LOGI(TAG, "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT RSSI: %d", rssi);
 | 
			
		||||
        this->publish_state(rssi);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BLEClientRSSISensor::update() {
 | 
			
		||||
  if (this->node_state != espbt::ClientState::ESTABLISHED) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "requesting rssi from %s", this->parent()->address_str().c_str());
 | 
			
		||||
  auto status = esp_ble_gap_read_rssi(this->parent()->get_remote_bda());
 | 
			
		||||
  if (status != ESP_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "esp_ble_gap_read_rssi error, address=%s, status=%d", this->parent()->address_str().c_str(), status);
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    this->publish_state(NAN);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ble_client
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										31
									
								
								esphome/components/ble_client/sensor/ble_rssi_sensor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								esphome/components/ble_client/sensor/ble_rssi_sensor.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/ble_client/ble_client.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#include <esp_gattc_api.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ble_client {
 | 
			
		||||
 | 
			
		||||
namespace espbt = esphome::esp32_ble_tracker;
 | 
			
		||||
 | 
			
		||||
class BLEClientRSSISensor : public sensor::Sensor, public PollingComponent, public BLEClientNode {
 | 
			
		||||
 public:
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
  void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
 | 
			
		||||
 | 
			
		||||
  void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                           esp_ble_gattc_cb_param_t *param) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ble_client
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
@@ -63,8 +63,8 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
 | 
			
		||||
        this->handle = descr->handle;
 | 
			
		||||
      }
 | 
			
		||||
      if (this->notify_) {
 | 
			
		||||
        auto status =
 | 
			
		||||
            esp_ble_gattc_register_for_notify(this->parent()->gattc_if, this->parent()->remote_bda, chr->handle);
 | 
			
		||||
        auto status = esp_ble_gattc_register_for_notify(this->parent()->get_gattc_if(),
 | 
			
		||||
                                                        this->parent()->get_remote_bda(), chr->handle);
 | 
			
		||||
        if (status) {
 | 
			
		||||
          ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
 | 
			
		||||
        }
 | 
			
		||||
@@ -74,7 +74,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_READ_CHAR_EVT: {
 | 
			
		||||
      if (param->read.conn_id != this->parent()->conn_id)
 | 
			
		||||
      if (param->read.conn_id != this->parent()->get_conn_id())
 | 
			
		||||
        break;
 | 
			
		||||
      if (param->read.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
 | 
			
		||||
@@ -87,7 +87,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_NOTIFY_EVT: {
 | 
			
		||||
      if (param->notify.conn_id != this->parent()->conn_id || param->notify.handle != this->handle)
 | 
			
		||||
      if (param->notify.conn_id != this->parent()->get_conn_id() || param->notify.handle != this->handle)
 | 
			
		||||
        break;
 | 
			
		||||
      ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
 | 
			
		||||
               param->notify.handle, param->notify.value[0]);
 | 
			
		||||
@@ -122,8 +122,8 @@ void BLESensor::update() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto status =
 | 
			
		||||
      esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle,
 | 
			
		||||
                                        ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  if (status) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    this->publish_state(NAN);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import switch, ble_client
 | 
			
		||||
from esphome.const import CONF_ICON, CONF_ID, CONF_INVERTED, ICON_BLUETOOTH
 | 
			
		||||
from esphome.const import ICON_BLUETOOTH
 | 
			
		||||
from .. import ble_client_ns
 | 
			
		||||
 | 
			
		||||
BLEClientSwitch = ble_client_ns.class_(
 | 
			
		||||
@@ -9,22 +9,13 @@ BLEClientSwitch = ble_client_ns.class_(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    switch.SWITCH_SCHEMA.extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BLEClientSwitch),
 | 
			
		||||
            cv.Optional(CONF_INVERTED): cv.invalid(
 | 
			
		||||
                "BLE client switches do not support inverted mode!"
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ICON, default=ICON_BLUETOOTH): switch.icon,
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    switch.switch_schema(BLEClientSwitch, icon=ICON_BLUETOOTH, block_inverted=True)
 | 
			
		||||
    .extend(ble_client.BLE_CLIENT_SCHEMA)
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    var = await switch.new_switch(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await switch.register_switch(var, config)
 | 
			
		||||
    await ble_client.register_ble_node(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,8 @@ class BLETextSensorNotifyTrigger : public Trigger<std::string>, public BLETextSe
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case ESP_GATTC_NOTIFY_EVT: {
 | 
			
		||||
        if (param->notify.conn_id != this->sensor_->parent()->conn_id || param->notify.handle != this->sensor_->handle)
 | 
			
		||||
        if (param->notify.conn_id != this->sensor_->parent()->get_conn_id() ||
 | 
			
		||||
            param->notify.handle != this->sensor_->handle)
 | 
			
		||||
          break;
 | 
			
		||||
        this->trigger(this->sensor_->parse_data(param->notify.value, param->notify.value_len));
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -66,8 +66,8 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
 | 
			
		||||
        this->handle = descr->handle;
 | 
			
		||||
      }
 | 
			
		||||
      if (this->notify_) {
 | 
			
		||||
        auto status =
 | 
			
		||||
            esp_ble_gattc_register_for_notify(this->parent()->gattc_if, this->parent()->remote_bda, chr->handle);
 | 
			
		||||
        auto status = esp_ble_gattc_register_for_notify(this->parent()->get_gattc_if(),
 | 
			
		||||
                                                        this->parent()->get_remote_bda(), chr->handle);
 | 
			
		||||
        if (status) {
 | 
			
		||||
          ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
 | 
			
		||||
        }
 | 
			
		||||
@@ -77,7 +77,7 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_READ_CHAR_EVT: {
 | 
			
		||||
      if (param->read.conn_id != this->parent()->conn_id)
 | 
			
		||||
      if (param->read.conn_id != this->parent()->get_conn_id())
 | 
			
		||||
        break;
 | 
			
		||||
      if (param->read.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
 | 
			
		||||
@@ -90,7 +90,7 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_NOTIFY_EVT: {
 | 
			
		||||
      if (param->notify.conn_id != this->parent()->conn_id || param->notify.handle != this->handle)
 | 
			
		||||
      if (param->notify.conn_id != this->parent()->get_conn_id() || param->notify.handle != this->handle)
 | 
			
		||||
        break;
 | 
			
		||||
      ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
 | 
			
		||||
               param->notify.handle, param->notify.value[0]);
 | 
			
		||||
@@ -121,8 +121,8 @@ void BLETextSensor::update() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto status =
 | 
			
		||||
      esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle,
 | 
			
		||||
                                        ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  if (status) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    this->publish_state(EMPTY);
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
 | 
			
		||||
      case MATCH_BY_SERVICE_UUID:
 | 
			
		||||
        for (auto uuid : device.get_service_uuids()) {
 | 
			
		||||
          if (this->uuid_ == uuid) {
 | 
			
		||||
            this->publish_state(device.get_rssi());
 | 
			
		||||
            this->publish_state(true);
 | 
			
		||||
            this->found_ = true;
 | 
			
		||||
            return true;
 | 
			
		||||
          }
 | 
			
		||||
@@ -83,7 +83,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this->publish_state(device.get_rssi());
 | 
			
		||||
        this->publish_state(true);
 | 
			
		||||
        this->found_ = true;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,41 +12,78 @@ namespace ble_rssi {
 | 
			
		||||
class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_address(uint64_t address) {
 | 
			
		||||
    this->by_address_ = true;
 | 
			
		||||
    this->match_by_ = MATCH_BY_MAC_ADDRESS;
 | 
			
		||||
    this->address_ = address;
 | 
			
		||||
  }
 | 
			
		||||
  void set_service_uuid16(uint16_t uuid) {
 | 
			
		||||
    this->by_address_ = false;
 | 
			
		||||
    this->match_by_ = MATCH_BY_SERVICE_UUID;
 | 
			
		||||
    this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid);
 | 
			
		||||
  }
 | 
			
		||||
  void set_service_uuid32(uint32_t uuid) {
 | 
			
		||||
    this->by_address_ = false;
 | 
			
		||||
    this->match_by_ = MATCH_BY_SERVICE_UUID;
 | 
			
		||||
    this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint32(uuid);
 | 
			
		||||
  }
 | 
			
		||||
  void set_service_uuid128(uint8_t *uuid) {
 | 
			
		||||
    this->by_address_ = false;
 | 
			
		||||
    this->match_by_ = MATCH_BY_SERVICE_UUID;
 | 
			
		||||
    this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid);
 | 
			
		||||
  }
 | 
			
		||||
  void set_ibeacon_uuid(uint8_t *uuid) {
 | 
			
		||||
    this->match_by_ = MATCH_BY_IBEACON_UUID;
 | 
			
		||||
    this->ibeacon_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid);
 | 
			
		||||
  }
 | 
			
		||||
  void set_ibeacon_major(uint16_t major) {
 | 
			
		||||
    this->check_ibeacon_major_ = true;
 | 
			
		||||
    this->ibeacon_major_ = major;
 | 
			
		||||
  }
 | 
			
		||||
  void set_ibeacon_minor(uint16_t minor) {
 | 
			
		||||
    this->check_ibeacon_minor_ = true;
 | 
			
		||||
    this->ibeacon_minor_ = minor;
 | 
			
		||||
  }
 | 
			
		||||
  void on_scan_end() override {
 | 
			
		||||
    if (!this->found_)
 | 
			
		||||
      this->publish_state(NAN);
 | 
			
		||||
    this->found_ = false;
 | 
			
		||||
  }
 | 
			
		||||
  bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
 | 
			
		||||
    if (this->by_address_) {
 | 
			
		||||
      if (device.address_uint64() == this->address_) {
 | 
			
		||||
        this->publish_state(device.get_rssi());
 | 
			
		||||
        this->found_ = true;
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      for (auto uuid : device.get_service_uuids()) {
 | 
			
		||||
        if (this->uuid_ == uuid) {
 | 
			
		||||
    switch (this->match_by_) {
 | 
			
		||||
      case MATCH_BY_MAC_ADDRESS:
 | 
			
		||||
        if (device.address_uint64() == this->address_) {
 | 
			
		||||
          this->publish_state(device.get_rssi());
 | 
			
		||||
          this->found_ = true;
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
        break;
 | 
			
		||||
      case MATCH_BY_SERVICE_UUID:
 | 
			
		||||
        for (auto uuid : device.get_service_uuids()) {
 | 
			
		||||
          if (this->uuid_ == uuid) {
 | 
			
		||||
            this->publish_state(device.get_rssi());
 | 
			
		||||
            this->found_ = true;
 | 
			
		||||
            return true;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case MATCH_BY_IBEACON_UUID:
 | 
			
		||||
        if (!device.get_ibeacon().has_value()) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto ibeacon = device.get_ibeacon().value();
 | 
			
		||||
 | 
			
		||||
        if (this->ibeacon_uuid_ != ibeacon.get_uuid()) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this->check_ibeacon_major_ && this->ibeacon_major_ != ibeacon.get_major()) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this->check_ibeacon_minor_ && this->ibeacon_minor_ != ibeacon.get_minor()) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this->publish_state(device.get_rssi());
 | 
			
		||||
        this->found_ = true;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -54,10 +91,20 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
 | 
			
		||||
  MatchType match_by_;
 | 
			
		||||
 | 
			
		||||
  bool found_{false};
 | 
			
		||||
  bool by_address_{false};
 | 
			
		||||
 | 
			
		||||
  uint64_t address_;
 | 
			
		||||
 | 
			
		||||
  esp32_ble_tracker::ESPBTUUID uuid_;
 | 
			
		||||
 | 
			
		||||
  esp32_ble_tracker::ESPBTUUID ibeacon_uuid_;
 | 
			
		||||
  uint16_t ibeacon_major_;
 | 
			
		||||
  bool check_ibeacon_major_;
 | 
			
		||||
  uint16_t ibeacon_minor_;
 | 
			
		||||
  bool check_ibeacon_minor_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ble_rssi
 | 
			
		||||
 
 | 
			
		||||
@@ -2,11 +2,14 @@ import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, esp32_ble_tracker
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_IBEACON_MAJOR,
 | 
			
		||||
    CONF_IBEACON_MINOR,
 | 
			
		||||
    CONF_IBEACON_UUID,
 | 
			
		||||
    CONF_SERVICE_UUID,
 | 
			
		||||
    CONF_MAC_ADDRESS,
 | 
			
		||||
    DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_DECIBEL,
 | 
			
		||||
    UNIT_DECIBEL_MILLIWATT,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["esp32_ble_tracker"]
 | 
			
		||||
@@ -16,10 +19,19 @@ BLERSSISensor = ble_rssi_ns.class_(
 | 
			
		||||
    "BLERSSISensor", sensor.Sensor, cg.Component, esp32_ble_tracker.ESPBTDeviceListener
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _validate(config):
 | 
			
		||||
    if CONF_IBEACON_MAJOR in config and CONF_IBEACON_UUID not in config:
 | 
			
		||||
        raise cv.Invalid("iBeacon major identifier requires iBeacon UUID")
 | 
			
		||||
    if CONF_IBEACON_MINOR in config and CONF_IBEACON_UUID not in config:
 | 
			
		||||
        raise cv.Invalid("iBeacon minor identifier requires iBeacon UUID")
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        BLERSSISensor,
 | 
			
		||||
        unit_of_measurement=UNIT_DECIBEL,
 | 
			
		||||
        unit_of_measurement=UNIT_DECIBEL_MILLIWATT,
 | 
			
		||||
        accuracy_decimals=0,
 | 
			
		||||
        device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
			
		||||
        state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
@@ -28,11 +40,15 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
 | 
			
		||||
            cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t,
 | 
			
		||||
            cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t,
 | 
			
		||||
            cv.Optional(CONF_IBEACON_UUID): cv.uuid,
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
    cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID),
 | 
			
		||||
    cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_IBEACON_UUID),
 | 
			
		||||
    _validate,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -60,3 +76,13 @@ async def to_code(config):
 | 
			
		||||
        elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
 | 
			
		||||
            uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
 | 
			
		||||
            cg.add(var.set_service_uuid128(uuid128))
 | 
			
		||||
 | 
			
		||||
    if CONF_IBEACON_UUID in config:
 | 
			
		||||
        ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(config[CONF_IBEACON_UUID]))
 | 
			
		||||
        cg.add(var.set_ibeacon_uuid(ibeacon_uuid))
 | 
			
		||||
 | 
			
		||||
        if CONF_IBEACON_MAJOR in config:
 | 
			
		||||
            cg.add(var.set_ibeacon_major(config[CONF_IBEACON_MAJOR]))
 | 
			
		||||
 | 
			
		||||
        if CONF_IBEACON_MINOR in config:
 | 
			
		||||
            cg.add(var.set_ibeacon_minor(config[CONF_IBEACON_MINOR]))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										75
									
								
								esphome/components/bluetooth_proxy/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								esphome/components/bluetooth_proxy/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
from esphome.components import esp32_ble_tracker, esp32_ble_client
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.const import CONF_ACTIVE, CONF_ID
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"]
 | 
			
		||||
DEPENDENCIES = ["api", "esp32"]
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
 | 
			
		||||
CONF_CONNECTIONS = "connections"
 | 
			
		||||
MAX_CONNECTIONS = 3
 | 
			
		||||
 | 
			
		||||
bluetooth_proxy_ns = cg.esphome_ns.namespace("bluetooth_proxy")
 | 
			
		||||
 | 
			
		||||
BluetoothProxy = bluetooth_proxy_ns.class_(
 | 
			
		||||
    "BluetoothProxy", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
 | 
			
		||||
)
 | 
			
		||||
BluetoothConnection = bluetooth_proxy_ns.class_(
 | 
			
		||||
    "BluetoothConnection", esp32_ble_client.BLEClientBase
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONNECTION_SCHEMA = esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(BluetoothConnection),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_connections(config):
 | 
			
		||||
    if CONF_CONNECTIONS in config:
 | 
			
		||||
        if not config[CONF_ACTIVE]:
 | 
			
		||||
            raise cv.Invalid(
 | 
			
		||||
                "Connections can only be used if the proxy is set to active"
 | 
			
		||||
            )
 | 
			
		||||
    else:
 | 
			
		||||
        if config[CONF_ACTIVE]:
 | 
			
		||||
            conf = config.copy()
 | 
			
		||||
            conf[CONF_CONNECTIONS] = [
 | 
			
		||||
                CONNECTION_SCHEMA({}) for _ in range(MAX_CONNECTIONS)
 | 
			
		||||
            ]
 | 
			
		||||
            return conf
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BluetoothProxy),
 | 
			
		||||
            cv.Optional(CONF_ACTIVE, default=False): cv.boolean,
 | 
			
		||||
            cv.Optional(CONF_CONNECTIONS): cv.All(
 | 
			
		||||
                cv.ensure_list(CONNECTION_SCHEMA),
 | 
			
		||||
                cv.Length(min=1, max=MAX_CONNECTIONS),
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
    validate_connections,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_active(config[CONF_ACTIVE]))
 | 
			
		||||
    await esp32_ble_tracker.register_ble_device(var, config)
 | 
			
		||||
 | 
			
		||||
    for connection_conf in config.get(CONF_CONNECTIONS, []):
 | 
			
		||||
        connection_var = cg.new_Pvariable(connection_conf[CONF_ID])
 | 
			
		||||
        await cg.register_component(connection_var, connection_conf)
 | 
			
		||||
        cg.add(var.register_connection(connection_var))
 | 
			
		||||
        await esp32_ble_tracker.register_client(connection_var, connection_conf)
 | 
			
		||||
 | 
			
		||||
    cg.add_define("USE_BLUETOOTH_PROXY")
 | 
			
		||||
							
								
								
									
										281
									
								
								esphome/components/bluetooth_proxy/bluetooth_connection.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								esphome/components/bluetooth_proxy/bluetooth_connection.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,281 @@
 | 
			
		||||
#include "bluetooth_connection.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/api/api_server.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include "bluetooth_proxy.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bluetooth_proxy {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "bluetooth_proxy.connection";
 | 
			
		||||
 | 
			
		||||
bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                                              esp_ble_gattc_cb_param_t *param) {
 | 
			
		||||
  if (!BLEClientBase::gattc_event_handler(event, gattc_if, param))
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  switch (event) {
 | 
			
		||||
    case ESP_GATTC_DISCONNECT_EVT: {
 | 
			
		||||
      api::global_api_server->send_bluetooth_device_connection(this->address_, false, 0, param->disconnect.reason);
 | 
			
		||||
      this->set_address(0);
 | 
			
		||||
      api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(),
 | 
			
		||||
                                                              this->proxy_->get_bluetooth_connections_limit());
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_OPEN_EVT: {
 | 
			
		||||
      if (param->open.conn_id != this->conn_id_)
 | 
			
		||||
        break;
 | 
			
		||||
      if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) {
 | 
			
		||||
        api::global_api_server->send_bluetooth_device_connection(this->address_, false, 0, param->open.status);
 | 
			
		||||
        this->set_address(0);
 | 
			
		||||
        api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(),
 | 
			
		||||
                                                                this->proxy_->get_bluetooth_connections_limit());
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_SEARCH_CMPL_EVT: {
 | 
			
		||||
      if (param->search_cmpl.conn_id != this->conn_id_)
 | 
			
		||||
        break;
 | 
			
		||||
      api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_);
 | 
			
		||||
      api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(),
 | 
			
		||||
                                                              this->proxy_->get_bluetooth_connections_limit());
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_READ_DESCR_EVT:
 | 
			
		||||
    case ESP_GATTC_READ_CHAR_EVT: {
 | 
			
		||||
      if (param->read.conn_id != this->conn_id_)
 | 
			
		||||
        break;
 | 
			
		||||
      if (param->read.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "[%d] [%s] Error reading char/descriptor at handle 0x%2X, status=%d", this->connection_index_,
 | 
			
		||||
                 this->address_str_.c_str(), param->read.handle, param->read.status);
 | 
			
		||||
        api::global_api_server->send_bluetooth_gatt_error(this->address_, param->read.handle, param->read.status);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      api::BluetoothGATTReadResponse resp;
 | 
			
		||||
      resp.address = this->address_;
 | 
			
		||||
      resp.handle = param->read.handle;
 | 
			
		||||
      resp.data.reserve(param->read.value_len);
 | 
			
		||||
      for (uint16_t i = 0; i < param->read.value_len; i++) {
 | 
			
		||||
        resp.data.push_back(param->read.value[i]);
 | 
			
		||||
      }
 | 
			
		||||
      api::global_api_server->send_bluetooth_gatt_read_response(resp);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_WRITE_CHAR_EVT:
 | 
			
		||||
    case ESP_GATTC_WRITE_DESCR_EVT: {
 | 
			
		||||
      if (param->write.conn_id != this->conn_id_)
 | 
			
		||||
        break;
 | 
			
		||||
      if (param->write.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "[%d] [%s] Error writing char/descriptor at handle 0x%2X, status=%d", this->connection_index_,
 | 
			
		||||
                 this->address_str_.c_str(), param->write.handle, param->write.status);
 | 
			
		||||
        api::global_api_server->send_bluetooth_gatt_error(this->address_, param->write.handle, param->write.status);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      api::BluetoothGATTWriteResponse resp;
 | 
			
		||||
      resp.address = this->address_;
 | 
			
		||||
      resp.handle = param->write.handle;
 | 
			
		||||
      api::global_api_server->send_bluetooth_gatt_write_response(resp);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
 | 
			
		||||
      if (this->get_characteristic(param->unreg_for_notify.handle) == nullptr)  // No conn_id in this event
 | 
			
		||||
        break;
 | 
			
		||||
      if (param->unreg_for_notify.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "[%d] [%s] Error unregistering notifications for handle 0x%2X, status=%d",
 | 
			
		||||
                 this->connection_index_, this->address_str_.c_str(), param->unreg_for_notify.handle,
 | 
			
		||||
                 param->unreg_for_notify.status);
 | 
			
		||||
        api::global_api_server->send_bluetooth_gatt_error(this->address_, param->unreg_for_notify.handle,
 | 
			
		||||
                                                          param->unreg_for_notify.status);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      api::BluetoothGATTNotifyResponse resp;
 | 
			
		||||
      resp.address = this->address_;
 | 
			
		||||
      resp.handle = param->unreg_for_notify.handle;
 | 
			
		||||
      api::global_api_server->send_bluetooth_gatt_notify_response(resp);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
 | 
			
		||||
      if (this->get_characteristic(param->reg_for_notify.handle) == nullptr)  // No conn_id in this event
 | 
			
		||||
        break;
 | 
			
		||||
      if (param->reg_for_notify.status != ESP_GATT_OK) {
 | 
			
		||||
        ESP_LOGW(TAG, "[%d] [%s] Error registering notifications for handle 0x%2X, status=%d", this->connection_index_,
 | 
			
		||||
                 this->address_str_.c_str(), param->reg_for_notify.handle, param->reg_for_notify.status);
 | 
			
		||||
        api::global_api_server->send_bluetooth_gatt_error(this->address_, param->reg_for_notify.handle,
 | 
			
		||||
                                                          param->reg_for_notify.status);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      api::BluetoothGATTNotifyResponse resp;
 | 
			
		||||
      resp.address = this->address_;
 | 
			
		||||
      resp.handle = param->reg_for_notify.handle;
 | 
			
		||||
      api::global_api_server->send_bluetooth_gatt_notify_response(resp);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_NOTIFY_EVT: {
 | 
			
		||||
      if (param->notify.conn_id != this->conn_id_)
 | 
			
		||||
        break;
 | 
			
		||||
      ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_NOTIFY_EVT: handle=0x%2X", this->connection_index_, this->address_str_.c_str(),
 | 
			
		||||
               param->notify.handle);
 | 
			
		||||
      api::BluetoothGATTNotifyDataResponse resp;
 | 
			
		||||
      resp.address = this->address_;
 | 
			
		||||
      resp.handle = param->notify.handle;
 | 
			
		||||
      resp.data.reserve(param->notify.value_len);
 | 
			
		||||
      for (uint16_t i = 0; i < param->notify.value_len; i++) {
 | 
			
		||||
        resp.data.push_back(param->notify.value[i]);
 | 
			
		||||
      }
 | 
			
		||||
      api::global_api_server->send_bluetooth_gatt_notify_data_response(resp);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
esp_err_t BluetoothConnection::read_characteristic(uint16_t handle) {
 | 
			
		||||
  if (!this->connected()) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] Cannot read GATT characteristic, not connected.", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str());
 | 
			
		||||
    return ESP_GATT_NOT_CONNECTED;
 | 
			
		||||
  }
 | 
			
		||||
  auto *characteristic = this->get_characteristic(handle);
 | 
			
		||||
  if (characteristic == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] Cannot read GATT characteristic, not found.", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str());
 | 
			
		||||
    return ESP_GATT_INVALID_HANDLE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "[%d] [%s] Reading GATT characteristic %s", this->connection_index_, this->address_str_.c_str(),
 | 
			
		||||
           characteristic->uuid.to_string().c_str());
 | 
			
		||||
 | 
			
		||||
  esp_err_t err =
 | 
			
		||||
      esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, characteristic->handle, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  if (err != ERR_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_read_char error, err=%d", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str(), err);
 | 
			
		||||
    return err;
 | 
			
		||||
  }
 | 
			
		||||
  return ESP_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
esp_err_t BluetoothConnection::write_characteristic(uint16_t handle, const std::string &data, bool response) {
 | 
			
		||||
  if (!this->connected()) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] Cannot write GATT characteristic, not connected.", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str());
 | 
			
		||||
    return ESP_GATT_NOT_CONNECTED;
 | 
			
		||||
  }
 | 
			
		||||
  auto *characteristic = this->get_characteristic(handle);
 | 
			
		||||
  if (characteristic == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] Cannot write GATT characteristic, not found.", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str());
 | 
			
		||||
    return ESP_GATT_INVALID_HANDLE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "[%d] [%s] Writing GATT characteristic %s", this->connection_index_, this->address_str_.c_str(),
 | 
			
		||||
           characteristic->uuid.to_string().c_str());
 | 
			
		||||
 | 
			
		||||
  auto err = characteristic->write_value((uint8_t *) data.data(), data.size(),
 | 
			
		||||
                                         response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP);
 | 
			
		||||
  if (err != ERR_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_write_char error, err=%d", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str(), err);
 | 
			
		||||
    return err;
 | 
			
		||||
  }
 | 
			
		||||
  return ESP_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
esp_err_t BluetoothConnection::read_descriptor(uint16_t handle) {
 | 
			
		||||
  if (!this->connected()) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] Cannot read GATT descriptor, not connected.", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str());
 | 
			
		||||
    return ESP_GATT_NOT_CONNECTED;
 | 
			
		||||
  }
 | 
			
		||||
  auto *descriptor = this->get_descriptor(handle);
 | 
			
		||||
  if (descriptor == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] Cannot read GATT descriptor, not found.", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str());
 | 
			
		||||
    return ESP_GATT_INVALID_HANDLE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "[%d] [%s] Reading GATT descriptor %s", this->connection_index_, this->address_str_.c_str(),
 | 
			
		||||
           descriptor->uuid.to_string().c_str());
 | 
			
		||||
 | 
			
		||||
  esp_err_t err =
 | 
			
		||||
      esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  if (err != ERR_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_read_char_descr error, err=%d", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str(), err);
 | 
			
		||||
    return err;
 | 
			
		||||
  }
 | 
			
		||||
  return ESP_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
esp_err_t BluetoothConnection::write_descriptor(uint16_t handle, const std::string &data) {
 | 
			
		||||
  if (!this->connected()) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] Cannot write GATT descriptor, not connected.", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str());
 | 
			
		||||
    return ESP_GATT_NOT_CONNECTED;
 | 
			
		||||
  }
 | 
			
		||||
  auto *descriptor = this->get_descriptor(handle);
 | 
			
		||||
  if (descriptor == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] Cannot write GATT descriptor, not found.", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str());
 | 
			
		||||
    return ESP_GATT_INVALID_HANDLE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "[%d] [%s] Writing GATT descriptor %s", this->connection_index_, this->address_str_.c_str(),
 | 
			
		||||
           descriptor->uuid.to_string().c_str());
 | 
			
		||||
 | 
			
		||||
  auto err =
 | 
			
		||||
      esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, data.size(),
 | 
			
		||||
                                     (uint8_t *) data.data(), ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
 | 
			
		||||
  if (err != ERR_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_write_char_descr error, err=%d", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str(), err);
 | 
			
		||||
    return err;
 | 
			
		||||
  }
 | 
			
		||||
  return ESP_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
esp_err_t BluetoothConnection::notify_characteristic(uint16_t handle, bool enable) {
 | 
			
		||||
  if (!this->connected()) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] Cannot notify GATT characteristic, not connected.", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str());
 | 
			
		||||
    return ESP_GATT_NOT_CONNECTED;
 | 
			
		||||
  }
 | 
			
		||||
  auto *characteristic = this->get_characteristic(handle);
 | 
			
		||||
  if (characteristic == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] Cannot notify GATT characteristic, not found.", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str());
 | 
			
		||||
    return ESP_GATT_INVALID_HANDLE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (enable) {
 | 
			
		||||
    ESP_LOGV(TAG, "[%d] [%s] Registering for GATT characteristic notifications %s", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str(), characteristic->uuid.to_string().c_str());
 | 
			
		||||
    esp_err_t err = esp_ble_gattc_register_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle);
 | 
			
		||||
    if (err != ESP_OK) {
 | 
			
		||||
      ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_register_for_notify failed, err=%d", this->connection_index_,
 | 
			
		||||
               this->address_str_.c_str(), err);
 | 
			
		||||
      return err;
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGV(TAG, "[%d] [%s] Unregistering for GATT characteristic notifications %s", this->connection_index_,
 | 
			
		||||
             this->address_str_.c_str(), characteristic->uuid.to_string().c_str());
 | 
			
		||||
    esp_err_t err = esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle);
 | 
			
		||||
    if (err != ESP_OK) {
 | 
			
		||||
      ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_unregister_for_notify failed, err=%d", this->connection_index_,
 | 
			
		||||
               this->address_str_.c_str(), err);
 | 
			
		||||
      return err;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return ESP_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace bluetooth_proxy
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
							
								
								
									
										34
									
								
								esphome/components/bluetooth_proxy/bluetooth_connection.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								esphome/components/bluetooth_proxy/bluetooth_connection.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/esp32_ble_client/ble_client_base.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bluetooth_proxy {
 | 
			
		||||
 | 
			
		||||
class BluetoothProxy;
 | 
			
		||||
 | 
			
		||||
class BluetoothConnection : public esp32_ble_client::BLEClientBase {
 | 
			
		||||
 public:
 | 
			
		||||
  bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
 | 
			
		||||
                           esp_ble_gattc_cb_param_t *param) override;
 | 
			
		||||
 | 
			
		||||
  esp_err_t read_characteristic(uint16_t handle);
 | 
			
		||||
  esp_err_t write_characteristic(uint16_t handle, const std::string &data, bool response);
 | 
			
		||||
  esp_err_t read_descriptor(uint16_t handle);
 | 
			
		||||
  esp_err_t write_descriptor(uint16_t handle, const std::string &data);
 | 
			
		||||
 | 
			
		||||
  esp_err_t notify_characteristic(uint16_t handle, bool enable);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  friend class BluetoothProxy;
 | 
			
		||||
 | 
			
		||||
  int16_t send_service_{-1};
 | 
			
		||||
  BluetoothProxy *proxy_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace bluetooth_proxy
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
							
								
								
									
										245
									
								
								esphome/components/bluetooth_proxy/bluetooth_proxy.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								esphome/components/bluetooth_proxy/bluetooth_proxy.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,245 @@
 | 
			
		||||
#include "bluetooth_proxy.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/api/api_server.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bluetooth_proxy {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "bluetooth_proxy";
 | 
			
		||||
 | 
			
		||||
BluetoothProxy::BluetoothProxy() { global_bluetooth_proxy = this; }
 | 
			
		||||
 | 
			
		||||
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
 | 
			
		||||
  if (!api::global_api_server->is_connected())
 | 
			
		||||
    return false;
 | 
			
		||||
  ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(),
 | 
			
		||||
           device.get_rssi());
 | 
			
		||||
  this->send_api_packet_(device);
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
 | 
			
		||||
  api::BluetoothLEAdvertisementResponse resp;
 | 
			
		||||
  resp.address = device.address_uint64();
 | 
			
		||||
  if (!device.get_name().empty())
 | 
			
		||||
    resp.name = device.get_name();
 | 
			
		||||
  resp.rssi = device.get_rssi();
 | 
			
		||||
  for (auto uuid : device.get_service_uuids()) {
 | 
			
		||||
    resp.service_uuids.push_back(uuid.to_string());
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &data : device.get_service_datas()) {
 | 
			
		||||
    api::BluetoothServiceData service_data;
 | 
			
		||||
    service_data.uuid = data.uuid.to_string();
 | 
			
		||||
    service_data.data.assign(data.data.begin(), data.data.end());
 | 
			
		||||
    resp.service_data.push_back(std::move(service_data));
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &data : device.get_manufacturer_datas()) {
 | 
			
		||||
    api::BluetoothServiceData manufacturer_data;
 | 
			
		||||
    manufacturer_data.uuid = data.uuid.to_string();
 | 
			
		||||
    manufacturer_data.data.assign(data.data.begin(), data.data.end());
 | 
			
		||||
    resp.manufacturer_data.push_back(std::move(manufacturer_data));
 | 
			
		||||
  }
 | 
			
		||||
  api::global_api_server->send_bluetooth_le_advertisement(resp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Active: %s", YESNO(this->active_));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::loop() {
 | 
			
		||||
  if (!api::global_api_server->is_connected()) {
 | 
			
		||||
    for (auto *connection : this->connections_) {
 | 
			
		||||
      if (connection->get_address() != 0) {
 | 
			
		||||
        connection->disconnect();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  for (auto *connection : this->connections_) {
 | 
			
		||||
    if (connection->send_service_ == connection->services_.size()) {
 | 
			
		||||
      connection->send_service_ = -1;
 | 
			
		||||
      api::global_api_server->send_bluetooth_gatt_services_done(connection->get_address());
 | 
			
		||||
    } else if (connection->send_service_ >= 0) {
 | 
			
		||||
      auto &service = connection->services_[connection->send_service_];
 | 
			
		||||
      api::BluetoothGATTGetServicesResponse resp;
 | 
			
		||||
      resp.address = connection->get_address();
 | 
			
		||||
      api::BluetoothGATTService service_resp;
 | 
			
		||||
      service_resp.uuid = {service->uuid.get_128bit_high(), service->uuid.get_128bit_low()};
 | 
			
		||||
      service_resp.handle = service->start_handle;
 | 
			
		||||
      for (auto &characteristic : service->characteristics) {
 | 
			
		||||
        api::BluetoothGATTCharacteristic characteristic_resp;
 | 
			
		||||
        characteristic_resp.uuid = {characteristic->uuid.get_128bit_high(), characteristic->uuid.get_128bit_low()};
 | 
			
		||||
        characteristic_resp.handle = characteristic->handle;
 | 
			
		||||
        characteristic_resp.properties = characteristic->properties;
 | 
			
		||||
        for (auto &descriptor : characteristic->descriptors) {
 | 
			
		||||
          api::BluetoothGATTDescriptor descriptor_resp;
 | 
			
		||||
          descriptor_resp.uuid = {descriptor->uuid.get_128bit_high(), descriptor->uuid.get_128bit_low()};
 | 
			
		||||
          descriptor_resp.handle = descriptor->handle;
 | 
			
		||||
          characteristic_resp.descriptors.push_back(std::move(descriptor_resp));
 | 
			
		||||
        }
 | 
			
		||||
        service_resp.characteristics.push_back(std::move(characteristic_resp));
 | 
			
		||||
      }
 | 
			
		||||
      resp.services.push_back(std::move(service_resp));
 | 
			
		||||
      api::global_api_server->send_bluetooth_gatt_services(resp);
 | 
			
		||||
      connection->send_service_++;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool reserve) {
 | 
			
		||||
  for (auto *connection : this->connections_) {
 | 
			
		||||
    if (connection->get_address() == address)
 | 
			
		||||
      return connection;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!reserve)
 | 
			
		||||
    return nullptr;
 | 
			
		||||
 | 
			
		||||
  for (auto *connection : this->connections_) {
 | 
			
		||||
    if (connection->get_address() == 0) {
 | 
			
		||||
      connection->set_address(address);
 | 
			
		||||
      return connection;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest &msg) {
 | 
			
		||||
  switch (msg.request_type) {
 | 
			
		||||
    case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT: {
 | 
			
		||||
      auto *connection = this->get_connection_(msg.address, true);
 | 
			
		||||
      if (connection == nullptr) {
 | 
			
		||||
        ESP_LOGW(TAG, "No free connections available");
 | 
			
		||||
        api::global_api_server->send_bluetooth_device_connection(msg.address, false);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGV(TAG, "[%d] [%s] Searching to connect", connection->get_connection_index(),
 | 
			
		||||
               connection->address_str().c_str());
 | 
			
		||||
      connection->set_state(espbt::ClientState::SEARCHING);
 | 
			
		||||
      api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(),
 | 
			
		||||
                                                              this->get_bluetooth_connections_limit());
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT: {
 | 
			
		||||
      auto *connection = this->get_connection_(msg.address, false);
 | 
			
		||||
      if (connection == nullptr) {
 | 
			
		||||
        api::global_api_server->send_bluetooth_device_connection(msg.address, false);
 | 
			
		||||
        api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(),
 | 
			
		||||
                                                                this->get_bluetooth_connections_limit());
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (connection->state() != espbt::ClientState::IDLE) {
 | 
			
		||||
        connection->disconnect();
 | 
			
		||||
      } else {
 | 
			
		||||
        connection->set_address(0);
 | 
			
		||||
        api::global_api_server->send_bluetooth_device_connection(msg.address, false);
 | 
			
		||||
        api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(),
 | 
			
		||||
                                                                this->get_bluetooth_connections_limit());
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR:
 | 
			
		||||
    case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg) {
 | 
			
		||||
  auto *connection = this->get_connection_(msg.address, false);
 | 
			
		||||
  if (connection == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "Cannot read GATT characteristic, not connected");
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto err = connection->read_characteristic(msg.handle);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg) {
 | 
			
		||||
  auto *connection = this->get_connection_(msg.address, false);
 | 
			
		||||
  if (connection == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "Cannot write GATT characteristic, not connected");
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto err = connection->write_characteristic(msg.handle, msg.data, msg.response);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg) {
 | 
			
		||||
  auto *connection = this->get_connection_(msg.address, false);
 | 
			
		||||
  if (connection == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "Cannot read GATT descriptor, not connected");
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto err = connection->read_descriptor(msg.handle);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg) {
 | 
			
		||||
  auto *connection = this->get_connection_(msg.address, false);
 | 
			
		||||
  if (connection == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "Cannot write GATT descriptor, not connected");
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto err = connection->write_descriptor(msg.handle, msg.data);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg) {
 | 
			
		||||
  auto *connection = this->get_connection_(msg.address, false);
 | 
			
		||||
  if (connection == nullptr || !connection->connected()) {
 | 
			
		||||
    ESP_LOGW(TAG, "Cannot get GATT services, not connected");
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_error(msg.address, 0, ESP_GATT_NOT_CONNECTED);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (connection->services_.empty()) {
 | 
			
		||||
    ESP_LOGW(TAG, "[%d] [%s] No GATT services found", connection->connection_index_, connection->address_str().c_str());
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_services_done(msg.address);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (connection->send_service_ == -1)  // Don't start sending services again if we're already sending them
 | 
			
		||||
    connection->send_service_ = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg) {
 | 
			
		||||
  auto *connection = this->get_connection_(msg.address, false);
 | 
			
		||||
  if (connection == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "Cannot notify GATT characteristic, not connected");
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto err = connection->notify_characteristic(msg.handle, msg.enable);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BluetoothProxy *global_bluetooth_proxy = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 | 
			
		||||
}  // namespace bluetooth_proxy
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
							
								
								
									
										73
									
								
								esphome/components/bluetooth_proxy/bluetooth_proxy.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								esphome/components/bluetooth_proxy/bluetooth_proxy.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/api/api_pb2.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_client/ble_client_base.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
 | 
			
		||||
#include "bluetooth_connection.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bluetooth_proxy {
 | 
			
		||||
 | 
			
		||||
static const esp_err_t ESP_GATT_NOT_CONNECTED = -1;
 | 
			
		||||
 | 
			
		||||
using namespace esp32_ble_client;
 | 
			
		||||
 | 
			
		||||
class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  BluetoothProxy();
 | 
			
		||||
  bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void register_connection(BluetoothConnection *connection) {
 | 
			
		||||
    this->connections_.push_back(connection);
 | 
			
		||||
    connection->proxy_ = this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void bluetooth_device_request(const api::BluetoothDeviceRequest &msg);
 | 
			
		||||
  void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg);
 | 
			
		||||
  void bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg);
 | 
			
		||||
  void bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg);
 | 
			
		||||
  void bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg);
 | 
			
		||||
  void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg);
 | 
			
		||||
  void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg);
 | 
			
		||||
 | 
			
		||||
  int get_bluetooth_connections_free() {
 | 
			
		||||
    int free = 0;
 | 
			
		||||
    for (auto *connection : this->connections_) {
 | 
			
		||||
      if (connection->address_ == 0) {
 | 
			
		||||
        free++;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return free;
 | 
			
		||||
  }
 | 
			
		||||
  int get_bluetooth_connections_limit() { return this->connections_.size(); }
 | 
			
		||||
 | 
			
		||||
  void set_active(bool active) { this->active_ = active; }
 | 
			
		||||
  bool has_active() { return this->active_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device);
 | 
			
		||||
 | 
			
		||||
  BluetoothConnection *get_connection_(uint64_t address, bool reserve);
 | 
			
		||||
 | 
			
		||||
  int16_t send_service_{-1};
 | 
			
		||||
  bool active_;
 | 
			
		||||
 | 
			
		||||
  std::vector<BluetoothConnection *> connections_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern BluetoothProxy *global_bluetooth_proxy;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 | 
			
		||||
}  // namespace bluetooth_proxy
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
@@ -163,7 +163,7 @@ void BME280Component::setup() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  config_register &= ~0b11111100;
 | 
			
		||||
  config_register |= 0b000 << 5;  // 0.5 ms standby time
 | 
			
		||||
  config_register |= 0b101 << 5;  // 1000 ms standby time
 | 
			
		||||
  config_register |= (this->iir_filter_ & 0b111) << 2;
 | 
			
		||||
  if (!this->write_byte(BME280_REGISTER_CONFIG, config_register)) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
 
 | 
			
		||||
@@ -96,9 +96,9 @@ class BME280Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
  BME280Oversampling pressure_oversampling_{BME280_OVERSAMPLING_16X};
 | 
			
		||||
  BME280Oversampling humidity_oversampling_{BME280_OVERSAMPLING_16X};
 | 
			
		||||
  BME280IIRFilter iir_filter_{BME280_IIR_FILTER_OFF};
 | 
			
		||||
  sensor::Sensor *temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *pressure_sensor_;
 | 
			
		||||
  sensor::Sensor *humidity_sensor_;
 | 
			
		||||
  sensor::Sensor *temperature_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *pressure_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *humidity_sensor_{nullptr};
 | 
			
		||||
  enum ErrorCode {
 | 
			
		||||
    NONE = 0,
 | 
			
		||||
    COMMUNICATION_FAILED,
 | 
			
		||||
 
 | 
			
		||||
@@ -129,10 +129,10 @@ class BME680Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
  uint16_t heater_temperature_{320};
 | 
			
		||||
  uint16_t heater_duration_{150};
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *pressure_sensor_;
 | 
			
		||||
  sensor::Sensor *humidity_sensor_;
 | 
			
		||||
  sensor::Sensor *gas_resistance_sensor_;
 | 
			
		||||
  sensor::Sensor *temperature_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *pressure_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *humidity_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *gas_resistance_sensor_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace bme680
 | 
			
		||||
 
 | 
			
		||||
@@ -100,15 +100,15 @@ class BME680BSECComponent : public Component, public i2c::I2CDevice {
 | 
			
		||||
  SampleRate pressure_sample_rate_{SAMPLE_RATE_DEFAULT};
 | 
			
		||||
  SampleRate humidity_sample_rate_{SAMPLE_RATE_DEFAULT};
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *pressure_sensor_;
 | 
			
		||||
  sensor::Sensor *humidity_sensor_;
 | 
			
		||||
  sensor::Sensor *gas_resistance_sensor_;
 | 
			
		||||
  sensor::Sensor *iaq_sensor_;
 | 
			
		||||
  text_sensor::TextSensor *iaq_accuracy_text_sensor_;
 | 
			
		||||
  sensor::Sensor *iaq_accuracy_sensor_;
 | 
			
		||||
  sensor::Sensor *co2_equivalent_sensor_;
 | 
			
		||||
  sensor::Sensor *breath_voc_equivalent_sensor_;
 | 
			
		||||
  sensor::Sensor *temperature_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *pressure_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *humidity_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *gas_resistance_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *iaq_sensor_{nullptr};
 | 
			
		||||
  text_sensor::TextSensor *iaq_accuracy_text_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *iaq_accuracy_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *co2_equivalent_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *breath_voc_equivalent_sensor_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
}  // namespace bme680_bsec
 | 
			
		||||
 
 | 
			
		||||
@@ -81,8 +81,8 @@ class BMP280Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
  BMP280Oversampling temperature_oversampling_{BMP280_OVERSAMPLING_16X};
 | 
			
		||||
  BMP280Oversampling pressure_oversampling_{BMP280_OVERSAMPLING_16X};
 | 
			
		||||
  BMP280IIRFilter iir_filter_{BMP280_IIR_FILTER_OFF};
 | 
			
		||||
  sensor::Sensor *temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *pressure_sensor_;
 | 
			
		||||
  sensor::Sensor *temperature_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *pressure_sensor_{nullptr};
 | 
			
		||||
  enum ErrorCode {
 | 
			
		||||
    NONE = 0,
 | 
			
		||||
    COMMUNICATION_FAILED,
 | 
			
		||||
 
 | 
			
		||||
@@ -125,8 +125,8 @@ class BMP3XXComponent : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
  Oversampling pressure_oversampling_{OVERSAMPLING_X16};
 | 
			
		||||
  IIRFilter iir_filter_{IIR_FILTER_OFF};
 | 
			
		||||
  OperationMode operation_mode_{FORCED_MODE};
 | 
			
		||||
  sensor::Sensor *temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *pressure_sensor_;
 | 
			
		||||
  sensor::Sensor *temperature_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *pressure_sensor_{nullptr};
 | 
			
		||||
  enum ErrorCode {
 | 
			
		||||
    NONE = 0,
 | 
			
		||||
    ERROR_COMMUNICATION_FAILED,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,9 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.final_validate as fv
 | 
			
		||||
from esphome.components import web_server_base
 | 
			
		||||
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
from esphome.const import CONF_ID, CONF_NETWORKS, CONF_PASSWORD, CONF_SSID, CONF_WIFI
 | 
			
		||||
from esphome.core import coroutine_with_priority, CORE
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["web_server_base"]
 | 
			
		||||
@@ -12,6 +13,7 @@ CODEOWNERS = ["@OttoWinter"]
 | 
			
		||||
captive_portal_ns = cg.esphome_ns.namespace("captive_portal")
 | 
			
		||||
CaptivePortal = captive_portal_ns.class_("CaptivePortal", cg.Component)
 | 
			
		||||
 | 
			
		||||
CONF_KEEP_USER_CREDENTIALS = "keep_user_credentials"
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
@@ -19,12 +21,30 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
 | 
			
		||||
                web_server_base.WebServerBase
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_KEEP_USER_CREDENTIALS, default=False): cv.boolean,
 | 
			
		||||
        }
 | 
			
		||||
    ).extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
    cv.only_with_arduino,
 | 
			
		||||
    cv.only_on(["esp32", "esp8266"]),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_wifi(config):
 | 
			
		||||
    wifi_conf = fv.full_config.get()[CONF_WIFI]
 | 
			
		||||
    if config.get(CONF_KEEP_USER_CREDENTIALS, False) and (
 | 
			
		||||
        CONF_SSID in wifi_conf
 | 
			
		||||
        or CONF_PASSWORD in wifi_conf
 | 
			
		||||
        or CONF_NETWORKS in wifi_conf
 | 
			
		||||
    ):
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            f"WiFi credentials cannot be used together with {CONF_KEEP_USER_CREDENTIALS}"
 | 
			
		||||
        )
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = validate_wifi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(64.0)
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
 | 
			
		||||
@@ -38,3 +58,6 @@ async def to_code(config):
 | 
			
		||||
        cg.add_library("WiFi", None)
 | 
			
		||||
    if CORE.is_esp8266:
 | 
			
		||||
        cg.add_library("DNSServer", None)
 | 
			
		||||
 | 
			
		||||
    if config.get(CONF_KEEP_USER_CREDENTIALS, False):
 | 
			
		||||
        cg.add_define("USE_CAPTIVE_PORTAL_KEEP_USER_CREDENTIALS")
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_DEVICE_CLASS,
 | 
			
		||||
    CONF_ENTITY_CATEGORY,
 | 
			
		||||
    CONF_ICON,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_SOURCE_ID,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core.entity_helpers import inherit_property_from
 | 
			
		||||
@@ -15,12 +14,15 @@ from .. import copy_ns
 | 
			
		||||
CopySwitch = copy_ns.class_("CopySwitch", switch.Switch, cg.Component)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(CopySwitch),
 | 
			
		||||
        cv.Required(CONF_SOURCE_ID): cv.use_id(switch.Switch),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    switch.switch_schema(CopySwitch)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_SOURCE_ID): cv.use_id(switch.Switch),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = cv.All(
 | 
			
		||||
    inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
 | 
			
		||||
@@ -30,8 +32,7 @@ FINAL_VALIDATE_SCHEMA = cv.All(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await switch.register_switch(var, config)
 | 
			
		||||
    var = await switch.new_switch(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    source = await cg.get_variable(config[CONF_SOURCE_ID])
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,9 @@ void CSE7766Component::loop() {
 | 
			
		||||
    this->raw_data_index_ = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->available() == 0)
 | 
			
		||||
  if (this->available() == 0) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->last_transmission_ = now;
 | 
			
		||||
  while (this->available() != 0) {
 | 
			
		||||
@@ -22,6 +23,7 @@ void CSE7766Component::loop() {
 | 
			
		||||
    if (!this->check_byte_()) {
 | 
			
		||||
      this->raw_data_index_ = 0;
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this->raw_data_index_ == 23) {
 | 
			
		||||
@@ -51,8 +53,9 @@ bool CSE7766Component::check_byte_() {
 | 
			
		||||
 | 
			
		||||
  if (index == 23) {
 | 
			
		||||
    uint8_t checksum = 0;
 | 
			
		||||
    for (uint8_t i = 2; i < 23; i++)
 | 
			
		||||
    for (uint8_t i = 2; i < 23; i++) {
 | 
			
		||||
      checksum += this->raw_data_[i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (checksum != this->raw_data_[23]) {
 | 
			
		||||
      ESP_LOGW(TAG, "Invalid checksum from CSE7766: 0x%02X != 0x%02X", checksum, this->raw_data_[23]);
 | 
			
		||||
@@ -66,20 +69,34 @@ bool CSE7766Component::check_byte_() {
 | 
			
		||||
void CSE7766Component::parse_data_() {
 | 
			
		||||
  ESP_LOGVV(TAG, "CSE7766 Data: ");
 | 
			
		||||
  for (uint8_t i = 0; i < 23; i++) {
 | 
			
		||||
    ESP_LOGVV(TAG, "  i=%u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i, BYTE_TO_BINARY(this->raw_data_[i]),
 | 
			
		||||
    ESP_LOGVV(TAG, "  %u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i + 1, BYTE_TO_BINARY(this->raw_data_[i]),
 | 
			
		||||
              this->raw_data_[i]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t header1 = this->raw_data_[0];
 | 
			
		||||
  if (header1 == 0xAA) {
 | 
			
		||||
    ESP_LOGW(TAG, "CSE7766 not calibrated!");
 | 
			
		||||
    ESP_LOGE(TAG, "CSE7766 not calibrated!");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((header1 & 0xF0) == 0xF0 && ((header1 >> 0) & 1) == 1) {
 | 
			
		||||
    ESP_LOGW(TAG, "CSE7766 reports abnormal hardware: (0x%02X)", header1);
 | 
			
		||||
    ESP_LOGW(TAG, "  Coefficient storage area is abnormal.");
 | 
			
		||||
    return;
 | 
			
		||||
  bool power_cycle_exceeds_range = false;
 | 
			
		||||
 | 
			
		||||
  if ((header1 & 0xF0) == 0xF0) {
 | 
			
		||||
    if (header1 & 0xD) {
 | 
			
		||||
      ESP_LOGE(TAG, "CSE7766 reports abnormal external circuit or chip damage: (0x%02X)", header1);
 | 
			
		||||
      if (header1 & (1 << 3)) {
 | 
			
		||||
        ESP_LOGE(TAG, "  Voltage cycle exceeds range.");
 | 
			
		||||
      }
 | 
			
		||||
      if (header1 & (1 << 2)) {
 | 
			
		||||
        ESP_LOGE(TAG, "  Current cycle exceeds range.");
 | 
			
		||||
      }
 | 
			
		||||
      if (header1 & (1 << 0)) {
 | 
			
		||||
        ESP_LOGE(TAG, "  Coefficient storage area is abnormal.");
 | 
			
		||||
      }
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    power_cycle_exceeds_range = header1 & (1 << 1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint32_t voltage_calib = this->get_24_bit_uint_(2);
 | 
			
		||||
@@ -92,46 +109,29 @@ void CSE7766Component::parse_data_() {
 | 
			
		||||
  uint8_t adj = this->raw_data_[20];
 | 
			
		||||
  uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
 | 
			
		||||
 | 
			
		||||
  bool power_ok = true;
 | 
			
		||||
  bool voltage_ok = true;
 | 
			
		||||
  bool current_ok = true;
 | 
			
		||||
 | 
			
		||||
  if (header1 > 0xF0) {
 | 
			
		||||
    // ESP_LOGV(TAG, "CSE7766 reports abnormal hardware: (0x%02X)", byte);
 | 
			
		||||
    if ((header1 >> 3) & 1) {
 | 
			
		||||
      ESP_LOGV(TAG, "  Voltage cycle exceeds range.");
 | 
			
		||||
      voltage_ok = false;
 | 
			
		||||
    }
 | 
			
		||||
    if ((header1 >> 2) & 1) {
 | 
			
		||||
      ESP_LOGV(TAG, "  Current cycle exceeds range.");
 | 
			
		||||
      current_ok = false;
 | 
			
		||||
    }
 | 
			
		||||
    if ((header1 >> 1) & 1) {
 | 
			
		||||
      ESP_LOGV(TAG, "  Power cycle exceeds range.");
 | 
			
		||||
      power_ok = false;
 | 
			
		||||
    }
 | 
			
		||||
    if ((header1 >> 0) & 1) {
 | 
			
		||||
      ESP_LOGV(TAG, "  Coefficient storage area is abnormal.");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((adj & 0x40) == 0x40 && voltage_ok && current_ok) {
 | 
			
		||||
  bool have_voltage = adj & 0x40;
 | 
			
		||||
  if (have_voltage) {
 | 
			
		||||
    // voltage cycle of serial port outputted is a complete cycle;
 | 
			
		||||
    this->voltage_acc_ += voltage_calib / float(voltage_cycle);
 | 
			
		||||
    this->voltage_counts_ += 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  float power = 0;
 | 
			
		||||
  if ((adj & 0x10) == 0x10 && voltage_ok && current_ok && power_ok) {
 | 
			
		||||
  bool have_power = adj & 0x10;
 | 
			
		||||
  float power = 0.0f;
 | 
			
		||||
 | 
			
		||||
  if (have_power) {
 | 
			
		||||
    // power cycle of serial port outputted is a complete cycle;
 | 
			
		||||
    power = power_calib / float(power_cycle);
 | 
			
		||||
    // According to the user manual, power cycle exceeding range means the measured power is 0
 | 
			
		||||
    if (!power_cycle_exceeds_range) {
 | 
			
		||||
      power = power_calib / float(power_cycle);
 | 
			
		||||
    }
 | 
			
		||||
    this->power_acc_ += power;
 | 
			
		||||
    this->power_counts_ += 1;
 | 
			
		||||
 | 
			
		||||
    uint32_t difference;
 | 
			
		||||
    if (this->cf_pulses_last_ == 0)
 | 
			
		||||
    if (this->cf_pulses_last_ == 0) {
 | 
			
		||||
      this->cf_pulses_last_ = cf_pulses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cf_pulses < this->cf_pulses_last_) {
 | 
			
		||||
      difference = cf_pulses + (0x10000 - this->cf_pulses_last_);
 | 
			
		||||
@@ -139,41 +139,52 @@ void CSE7766Component::parse_data_() {
 | 
			
		||||
      difference = cf_pulses - this->cf_pulses_last_;
 | 
			
		||||
    }
 | 
			
		||||
    this->cf_pulses_last_ = cf_pulses;
 | 
			
		||||
    this->energy_total_ += difference * float(power_calib) / 1000000.0 / 3600.0;
 | 
			
		||||
    this->energy_total_ += difference * float(power_calib) / 1000000.0f / 3600.0f;
 | 
			
		||||
    this->energy_total_counts_ += 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((adj & 0x20) == 0x20 && current_ok && voltage_ok && power != 0.0) {
 | 
			
		||||
  if (adj & 0x20) {
 | 
			
		||||
    // indicates current cycle of serial port outputted is a complete cycle;
 | 
			
		||||
    this->current_acc_ += current_calib / float(current_cycle);
 | 
			
		||||
    float current = 0.0f;
 | 
			
		||||
    if (have_voltage && !have_power) {
 | 
			
		||||
      // Testing has shown that when we have voltage and current but not power, that means the power is 0.
 | 
			
		||||
      // We report a power of 0, which in turn means we should report a current of 0.
 | 
			
		||||
      this->power_counts_ += 1;
 | 
			
		||||
    } else if (power != 0.0f) {
 | 
			
		||||
      current = current_calib / float(current_cycle);
 | 
			
		||||
    }
 | 
			
		||||
    this->current_acc_ += current;
 | 
			
		||||
    this->current_counts_ += 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void CSE7766Component::update() {
 | 
			
		||||
  float voltage = this->voltage_counts_ > 0 ? this->voltage_acc_ / this->voltage_counts_ : 0.0f;
 | 
			
		||||
  float current = this->current_counts_ > 0 ? this->current_acc_ / this->current_counts_ : 0.0f;
 | 
			
		||||
  float power = this->power_counts_ > 0 ? this->power_acc_ / this->power_counts_ : 0.0f;
 | 
			
		||||
  const auto publish_state = [](const char *name, sensor::Sensor *sensor, float &acc, uint32_t &counts) {
 | 
			
		||||
    if (counts != 0) {
 | 
			
		||||
      const auto avg = acc / counts;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Got voltage_acc=%.2f current_acc=%.2f power_acc=%.2f", this->voltage_acc_, this->current_acc_,
 | 
			
		||||
           this->power_acc_);
 | 
			
		||||
  ESP_LOGV(TAG, "Got voltage_counts=%d current_counts=%d power_counts=%d", this->voltage_counts_, this->current_counts_,
 | 
			
		||||
           this->power_counts_);
 | 
			
		||||
  ESP_LOGD(TAG, "Got voltage=%.1fV current=%.1fA power=%.1fW", voltage, current, power);
 | 
			
		||||
      ESP_LOGV(TAG, "Got %s_acc=%.2f %s_counts=%d %s=%.1f", name, acc, name, counts, name, avg);
 | 
			
		||||
 | 
			
		||||
  if (this->voltage_sensor_ != nullptr)
 | 
			
		||||
    this->voltage_sensor_->publish_state(voltage);
 | 
			
		||||
  if (this->current_sensor_ != nullptr)
 | 
			
		||||
    this->current_sensor_->publish_state(current);
 | 
			
		||||
  if (this->power_sensor_ != nullptr)
 | 
			
		||||
    this->power_sensor_->publish_state(power);
 | 
			
		||||
  if (this->energy_sensor_ != nullptr)
 | 
			
		||||
    this->energy_sensor_->publish_state(this->energy_total_);
 | 
			
		||||
      if (sensor != nullptr) {
 | 
			
		||||
        sensor->publish_state(avg);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
  this->voltage_acc_ = 0.0f;
 | 
			
		||||
  this->current_acc_ = 0.0f;
 | 
			
		||||
  this->power_acc_ = 0.0f;
 | 
			
		||||
  this->voltage_counts_ = 0;
 | 
			
		||||
  this->power_counts_ = 0;
 | 
			
		||||
  this->current_counts_ = 0;
 | 
			
		||||
      acc = 0.0f;
 | 
			
		||||
      counts = 0;
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  publish_state("voltage", this->voltage_sensor_, this->voltage_acc_, this->voltage_counts_);
 | 
			
		||||
  publish_state("current", this->current_sensor_, this->current_acc_, this->current_counts_);
 | 
			
		||||
  publish_state("power", this->power_sensor_, this->power_acc_, this->power_counts_);
 | 
			
		||||
 | 
			
		||||
  if (this->energy_total_counts_ != 0) {
 | 
			
		||||
    ESP_LOGV(TAG, "Got energy_total=%.2f energy_total_counts=%d", this->energy_total_, this->energy_total_counts_);
 | 
			
		||||
 | 
			
		||||
    if (this->energy_sensor_ != nullptr) {
 | 
			
		||||
      this->energy_sensor_->publish_state(this->energy_total_);
 | 
			
		||||
    }
 | 
			
		||||
    this->energy_total_counts_ = 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) {
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user