mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-03 16:41:50 +00:00 
			
		
		
		
	Compare commits
	
		
			296 Commits
		
	
	
		
			2025.5.0b1
			...
			jesserockz
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					140792d8a9 | ||
| 
						 | 
					14e6918eb1 | ||
| 
						 | 
					a794a8f3e4 | ||
| 
						 | 
					261b561bb2 | ||
| 
						 | 
					0228379a2e | ||
| 
						 | 
					da79215bc3 | ||
| 
						 | 
					44323dc285 | ||
| 
						 | 
					a59e1c7011 | ||
| 
						 | 
					abb4d991ad | ||
| 
						 | 
					f467c79a20 | ||
| 
						 | 
					dcf41db878 | ||
| 
						 | 
					c3c3a27af2 | ||
| 
						 | 
					052f558131 | ||
| 
						 | 
					e8aa7cff36 | ||
| 
						 | 
					3411e45a0a | ||
| 
						 | 
					a488c8cd5c | ||
| 
						 | 
					9652b1a556 | ||
| 
						 | 
					69f2c79ccb | ||
| 
						 | 
					9d9d210176 | ||
| 
						 | 
					487e1f871f | ||
| 
						 | 
					0e27ac281f | ||
| 
						 | 
					ad37f103fa | ||
| 
						 | 
					b579bbf03b | ||
| 
						 | 
					7f4d2534aa | ||
| 
						 | 
					9e26daeb94 | ||
| 
						 | 
					da6af184a6 | ||
| 
						 | 
					4d347f1cc6 | ||
| 
						 | 
					84e57b8136 | ||
| 
						 | 
					63882c4a74 | ||
| 
						 | 
					2ed5611a08 | ||
| 
						 | 
					1467b704b8 | ||
| 
						 | 
					94848e4811 | ||
| 
						 | 
					3174f7ae86 | ||
| 
						 | 
					962a339a8a | ||
| 
						 | 
					6a76e6ae4a | ||
| 
						 | 
					ce4371a80d | ||
| 
						 | 
					b7ca6e087a | ||
| 
						 | 
					368a0eea8a | ||
| 
						 | 
					99c368fe62 | ||
| 
						 | 
					ff406f8e11 | ||
| 
						 | 
					b98165e077 | ||
| 
						 | 
					e2a9cced94 | ||
| 
						 | 
					cdae06e571 | ||
| 
						 | 
					c0b05ada1a | ||
| 
						 | 
					80dddb4cae | ||
| 
						 | 
					245c89a6c1 | ||
| 
						 | 
					4d044d4ac9 | ||
| 
						 | 
					9cc2a04d54 | ||
| 
						 | 
					50cdec19dd | ||
| 
						 | 
					6d587278bd | ||
| 
						 | 
					dde63e7459 | ||
| 
						 | 
					8894f5030a | ||
| 
						 | 
					9e862b8b53 | ||
| 
						 | 
					24d4ada841 | ||
| 
						 | 
					b1a8887548 | ||
| 
						 | 
					d19997a056 | ||
| 
						 | 
					de7591882d | ||
| 
						 | 
					a00fc75c77 | ||
| 
						 | 
					80fd827f8b | ||
| 
						 | 
					1dd3c6de90 | ||
| 
						 | 
					c8c43f13fd | ||
| 
						 | 
					518bce50a5 | ||
| 
						 | 
					4f87bea788 | ||
| 
						 | 
					8054c9b4f5 | ||
| 
						 | 
					935e0a365f | ||
| 
						 | 
					b39a9924d8 | ||
| 
						 | 
					19ec922e28 | ||
| 
						 | 
					a225d6881f | ||
| 
						 | 
					6675e99862 | ||
| 
						 | 
					8cbe2b41f6 | ||
| 
						 | 
					6a225cb4c0 | ||
| 
						 | 
					e62d8bfabe | ||
| 
						 | 
					d4cea84b1b | ||
| 
						 | 
					b63f90a6c0 | ||
| 
						 | 
					4370b6695e | ||
| 
						 | 
					589f13f0f7 | ||
| 
						 | 
					367017b352 | ||
| 
						 | 
					ec26d31499 | ||
| 
						 | 
					1bbc6db1c3 | ||
| 
						 | 
					162472bdc2 | ||
| 
						 | 
					aecac15809 | ||
| 
						 | 
					6554af21b9 | ||
| 
						 | 
					8583466c6a | ||
| 
						 | 
					6666604069 | ||
| 
						 | 
					13e7aacc9d | ||
| 
						 | 
					737d502614 | ||
| 
						 | 
					67dd649d00 | ||
| 
						 | 
					b2fc51367b | ||
| 
						 | 
					5771bb4907 | ||
| 
						 | 
					9ba9674437 | ||
| 
						 | 
					acb1532e34 | ||
| 
						 | 
					e2093c34da | ||
| 
						 | 
					a2e4ad90ba | ||
| 
						 | 
					32e69c67f2 | ||
| 
						 | 
					df0b5a187e | ||
| 
						 | 
					cee0e5379b | ||
| 
						 | 
					daf2bd7e66 | ||
| 
						 | 
					4031077f6d | ||
| 
						 | 
					fd72a64053 | ||
| 
						 | 
					959a8b91bd | ||
| 
						 | 
					44f1ff10e6 | ||
| 
						 | 
					64e4589f4e | ||
| 
						 | 
					20aba45cbe | ||
| 
						 | 
					0b1c5b825e | ||
| 
						 | 
					455624105b | ||
| 
						 | 
					7ac5746e0d | ||
| 
						 | 
					12997451f6 | ||
| 
						 | 
					8c77e40695 | ||
| 
						 | 
					2ddd91acf2 | ||
| 
						 | 
					729e49cdc3 | ||
| 
						 | 
					d64b49cc13 | ||
| 
						 | 
					cfa8b3b272 | ||
| 
						 | 
					51981335d5 | ||
| 
						 | 
					70c5e1bbf1 | ||
| 
						 | 
					43e88af28a | ||
| 
						 | 
					ffc66f539f | ||
| 
						 | 
					c4cb694d77 | ||
| 
						 | 
					3fb9577ad9 | ||
| 
						 | 
					34169491ac | ||
| 
						 | 
					8eac859bab | ||
| 
						 | 
					d99e3237f9 | ||
| 
						 | 
					d9a9e0aea3 | ||
| 
						 | 
					0ce03ae26b | ||
| 
						 | 
					18653f8f69 | ||
| 
						 | 
					6e0523109a | ||
| 
						 | 
					b6fa4f641d | ||
| 
						 | 
					ca6295d1bd | ||
| 
						 | 
					18a1d31845 | ||
| 
						 | 
					c5239a63ab | ||
| 
						 | 
					1911269dc9 | ||
| 
						 | 
					04ee1a87e9 | ||
| 
						 | 
					a8fdb6db4d | ||
| 
						 | 
					8860c74f0c | ||
| 
						 | 
					d585440d54 | ||
| 
						 | 
					f74f89c6b5 | ||
| 
						 | 
					7d049a61bb | ||
| 
						 | 
					f2e4dc7907 | ||
| 
						 | 
					0c7589caeb | ||
| 
						 | 
					321411e355 | ||
| 
						 | 
					361de22370 | ||
| 
						 | 
					95a17387a8 | ||
| 
						 | 
					caf9930ff9 | ||
| 
						 | 
					42390faf4a | ||
| 
						 | 
					fdc6c4a219 | ||
| 
						 | 
					6c08f5e343 | ||
| 
						 | 
					e0e4ba9592 | ||
| 
						 | 
					ad20825f31 | ||
| 
						 | 
					e4f3a952d5 | ||
| 
						 | 
					90e3c5bba2 | ||
| 
						 | 
					b1d5ad27f3 | ||
| 
						 | 
					5c54f75b7a | ||
| 
						 | 
					a5f85b4437 | ||
| 
						 | 
					da4e710249 | ||
| 
						 | 
					4ac433fddb | ||
| 
						 | 
					73771d5c50 | ||
| 
						 | 
					af7b1a3a23 | ||
| 
						 | 
					430f63fcbb | ||
| 
						 | 
					5921a9cd68 | ||
| 
						 | 
					ca0037d076 | ||
| 
						 | 
					1e18d0b06c | ||
| 
						 | 
					4b5c3e7e2b | ||
| 
						 | 
					d4c4b75eb3 | ||
| 
						 | 
					9dd4045984 | ||
| 
						 | 
					19e2460af2 | ||
| 
						 | 
					149f787035 | ||
| 
						 | 
					2ab1fe1abf | ||
| 
						 | 
					926b42ba1c | ||
| 
						 | 
					377ed2e212 | ||
| 
						 | 
					42912447fb | ||
| 
						 | 
					25ead44f1c | ||
| 
						 | 
					03b003af47 | ||
| 
						 | 
					5baccf0ce7 | ||
| 
						 | 
					e95c92773c | ||
| 
						 | 
					c23ea384fb | ||
| 
						 | 
					69da17742f | ||
| 
						 | 
					1ec57a74b5 | ||
| 
						 | 
					d1e55252d0 | ||
| 
						 | 
					090feb55e9 | ||
| 
						 | 
					6109acb6f3 | ||
| 
						 | 
					5aa13db815 | ||
| 
						 | 
					1b67dd4232 | ||
| 
						 | 
					ba6efcedcb | ||
| 
						 | 
					bd7c2a680c | ||
| 
						 | 
					1466aa7703 | ||
| 
						 | 
					787f4860db | ||
| 
						 | 
					aeb4e63950 | ||
| 
						 | 
					026f47bfb3 | ||
| 
						 | 
					dd47d063b5 | ||
| 
						 | 
					cdcd1cd292 | ||
| 
						 | 
					a6fa963605 | ||
| 
						 | 
					1cba22175f | ||
| 
						 | 
					f2d7720a4e | ||
| 
						 | 
					801138da27 | ||
| 
						 | 
					51740a2e99 | ||
| 
						 | 
					d68a391e67 | ||
| 
						 | 
					e9d832d64a | ||
| 
						 | 
					f8f09bca02 | ||
| 
						 | 
					756aa13779 | ||
| 
						 | 
					25bbc0c221 | ||
| 
						 | 
					220a14e1f8 | ||
| 
						 | 
					ac74b25c46 | ||
| 
						 | 
					c5d809b3dd | ||
| 
						 | 
					b1cf08b261 | ||
| 
						 | 
					6ae83dfe3d | ||
| 
						 | 
					0932e83b15 | ||
| 
						 | 
					86670c4d39 | ||
| 
						 | 
					4ce55b94ec | ||
| 
						 | 
					1c5dc63eb4 | ||
| 
						 | 
					937fe393a1 | ||
| 
						 | 
					4b552d9fba | ||
| 
						 | 
					aa53d8f1ee | ||
| 
						 | 
					a28932bc29 | ||
| 
						 | 
					afa7414ee1 | ||
| 
						 | 
					aed7ef481e | ||
| 
						 | 
					c820fee1f6 | ||
| 
						 | 
					5244ac4ff6 | ||
| 
						 | 
					89d283eee4 | ||
| 
						 | 
					ef053d23b4 | ||
| 
						 | 
					98470d32f0 | ||
| 
						 | 
					cab6edd800 | ||
| 
						 | 
					ef7a22ff04 | ||
| 
						 | 
					dfda0e5c7c | ||
| 
						 | 
					78c63311c6 | ||
| 
						 | 
					1ac51e7b3e | ||
| 
						 | 
					aaaf9b2b62 | ||
| 
						 | 
					5b552b9ec5 | ||
| 
						 | 
					d36ce7c010 | ||
| 
						 | 
					b8a96f59f0 | ||
| 
						 | 
					2e15ee232d | ||
| 
						 | 
					904495e1b8 | ||
| 
						 | 
					99c4f88c3f | ||
| 
						 | 
					87a9dd18c8 | ||
| 
						 | 
					dbce54477a | ||
| 
						 | 
					38cfd32382 | ||
| 
						 | 
					1b9ae57b9d | ||
| 
						 | 
					4d54cb9b31 | ||
| 
						 | 
					15d0b4355e | ||
| 
						 | 
					316fe2f06c | ||
| 
						 | 
					f8681adec4 | ||
| 
						 | 
					868f5ff20c | ||
| 
						 | 
					59295a615e | ||
| 
						 | 
					d8516cfabb | ||
| 
						 | 
					d847b345b8 | ||
| 
						 | 
					c50e33f531 | ||
| 
						 | 
					5a84bab9ec | ||
| 
						 | 
					41f860c2a3 | ||
| 
						 | 
					c7e62d1279 | ||
| 
						 | 
					2341ff651a | ||
| 
						 | 
					9704de6647 | ||
| 
						 | 
					660030d157 | ||
| 
						 | 
					24fbe602dd | ||
| 
						 | 
					b0c1e0e28c | ||
| 
						 | 
					574aabdede | ||
| 
						 | 
					e47741d471 | ||
| 
						 | 
					a78bea78f9 | ||
| 
						 | 
					44470f31f6 | ||
| 
						 | 
					18ac1b7c54 | ||
| 
						 | 
					e87b659483 | ||
| 
						 | 
					fefcb45e1f | ||
| 
						 | 
					5c92367ca2 | ||
| 
						 | 
					b469a504e4 | ||
| 
						 | 
					218f8e0caf | ||
| 
						 | 
					7965558d5e | ||
| 
						 | 
					d9b860088e | ||
| 
						 | 
					115975c409 | ||
| 
						 | 
					4761ffe023 | ||
| 
						 | 
					88edddf07a | ||
| 
						 | 
					0b77cb1d16 | ||
| 
						 | 
					efa6745a5e | ||
| 
						 | 
					dd8d8ad952 | ||
| 
						 | 
					57284b1ac3 | ||
| 
						 | 
					1a651ce66d | ||
| 
						 | 
					730441c120 | ||
| 
						 | 
					bb1f24ab43 | ||
| 
						 | 
					edb8d187be | ||
| 
						 | 
					e7b6081c5c | ||
| 
						 | 
					97fb8c2cdf | ||
| 
						 | 
					5454500024 | ||
| 
						 | 
					d9839f3a5c | ||
| 
						 | 
					498e3904a9 | ||
| 
						 | 
					7cb01bf842 | ||
| 
						 | 
					c050e8d0fb | ||
| 
						 | 
					4f2643e6e9 | ||
| 
						 | 
					7d0262dd1a | ||
| 
						 | 
					c30ffd0098 | ||
| 
						 | 
					ea31122979 | ||
| 
						 | 
					191afd3e69 | ||
| 
						 | 
					de27ce79dc | ||
| 
						 | 
					a12bd78ceb | ||
| 
						 | 
					ddb986b4fa | ||
| 
						 | 
					c98c78e368 | ||
| 
						 | 
					5570a788fd | ||
| 
						 | 
					42c355e6d7 | ||
| 
						 | 
					a835ab48bc | ||
| 
						 | 
					f28a373898 | ||
| 
						 | 
					28e29efd98 | 
@@ -1,2 +1,4 @@
 | 
			
		||||
[run]
 | 
			
		||||
omit = esphome/components/*
 | 
			
		||||
omit = 
 | 
			
		||||
    esphome/components/*
 | 
			
		||||
    tests/integration/*
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								.devcontainer/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								.devcontainer/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
ARG BUILD_BASE_VERSION=2025.04.0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FROM ghcr.io/esphome/docker-base:debian-${BUILD_BASE_VERSION} AS base
 | 
			
		||||
 | 
			
		||||
RUN git config --system --add safe.directory "*"
 | 
			
		||||
 | 
			
		||||
RUN apt update \
 | 
			
		||||
    && apt install -y \
 | 
			
		||||
      protobuf-compiler
 | 
			
		||||
 | 
			
		||||
RUN pip install uv
 | 
			
		||||
 | 
			
		||||
RUN useradd esphome -m
 | 
			
		||||
 | 
			
		||||
USER esphome
 | 
			
		||||
ENV VIRTUAL_ENV=/home/esphome/.local/esphome-venv
 | 
			
		||||
RUN uv venv $VIRTUAL_ENV
 | 
			
		||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
 | 
			
		||||
# Override this set to true in the docker-base image
 | 
			
		||||
ENV UV_SYSTEM_PYTHON=false
 | 
			
		||||
 | 
			
		||||
WORKDIR /tmp
 | 
			
		||||
 | 
			
		||||
COPY requirements.txt ./
 | 
			
		||||
RUN uv pip install -r requirements.txt
 | 
			
		||||
COPY requirements_dev.txt requirements_test.txt ./
 | 
			
		||||
RUN uv pip install -r requirements_dev.txt -r requirements_test.txt
 | 
			
		||||
 | 
			
		||||
RUN \
 | 
			
		||||
    platformio settings set enable_telemetry No \
 | 
			
		||||
    && platformio settings set check_platformio_interval 1000000
 | 
			
		||||
 | 
			
		||||
COPY script/platformio_install_deps.py platformio.ini ./
 | 
			
		||||
RUN ./platformio_install_deps.py platformio.ini --libraries --platforms --tools
 | 
			
		||||
 | 
			
		||||
WORKDIR /workspaces
 | 
			
		||||
@@ -1,18 +1,17 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "ESPHome Dev",
 | 
			
		||||
  "image": "ghcr.io/esphome/esphome-lint:dev",
 | 
			
		||||
  "context": "..",
 | 
			
		||||
  "dockerFile": "Dockerfile",
 | 
			
		||||
  "postCreateCommand": [
 | 
			
		||||
    "script/devcontainer-post-create"
 | 
			
		||||
  ],
 | 
			
		||||
  "containerEnv": {
 | 
			
		||||
    "DEVCONTAINER": "1",
 | 
			
		||||
    "PIP_BREAK_SYSTEM_PACKAGES": "1",
 | 
			
		||||
    "PIP_ROOT_USER_ACTION": "ignore"
 | 
			
		||||
  "features": {
 | 
			
		||||
    "ghcr.io/devcontainers/features/github-cli:1": {}
 | 
			
		||||
  },
 | 
			
		||||
  "runArgs": [
 | 
			
		||||
    "--privileged",
 | 
			
		||||
    "-e",
 | 
			
		||||
    "ESPHOME_DASHBOARD_USE_PING=1"
 | 
			
		||||
    "GIT_EDITOR=code --wait"
 | 
			
		||||
    // uncomment and edit the path in order to pass though local USB serial to the conatiner
 | 
			
		||||
    // , "--device=/dev/ttyACM0"
 | 
			
		||||
  ],
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/actions/build-image/action.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/actions/build-image/action.yaml
									
									
									
									
										vendored
									
									
								
							@@ -47,7 +47,7 @@ runs:
 | 
			
		||||
 | 
			
		||||
    - name: Build and push to ghcr by digest
 | 
			
		||||
      id: build-ghcr
 | 
			
		||||
      uses: docker/build-push-action@v6.16.0
 | 
			
		||||
      uses: docker/build-push-action@v6.18.0
 | 
			
		||||
      env:
 | 
			
		||||
        DOCKER_BUILD_SUMMARY: false
 | 
			
		||||
        DOCKER_BUILD_RECORD_UPLOAD: false
 | 
			
		||||
@@ -73,7 +73,7 @@ runs:
 | 
			
		||||
 | 
			
		||||
    - name: Build and push to dockerhub by digest
 | 
			
		||||
      id: build-dockerhub
 | 
			
		||||
      uses: docker/build-push-action@v6.16.0
 | 
			
		||||
      uses: docker/build-push-action@v6.18.0
 | 
			
		||||
      env:
 | 
			
		||||
        DOCKER_BUILD_SUMMARY: false
 | 
			
		||||
        DOCKER_BUILD_RECORD_UPLOAD: false
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/ci-api-proto.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-api-proto.yml
									
									
									
									
										vendored
									
									
								
							@@ -21,7 +21,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v5.6.0
 | 
			
		||||
        with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							@@ -43,11 +43,11 @@ jobs:
 | 
			
		||||
          - "docker"
 | 
			
		||||
          # - "lint"
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.1.7
 | 
			
		||||
      - uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v5.6.0
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
          python-version: "3.10"
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.10.0
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										48
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -20,8 +20,8 @@ permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  DEFAULT_PYTHON: "3.9"
 | 
			
		||||
  PYUPGRADE_TARGET: "--py39-plus"
 | 
			
		||||
  DEFAULT_PYTHON: "3.10"
 | 
			
		||||
  PYUPGRADE_TARGET: "--py310-plus"
 | 
			
		||||
 | 
			
		||||
concurrency:
 | 
			
		||||
  # yamllint disable-line rule:line-length
 | 
			
		||||
@@ -36,7 +36,7 @@ jobs:
 | 
			
		||||
      cache-key: ${{ steps.cache-key.outputs.key }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Generate cache-key
 | 
			
		||||
        id: cache-key
 | 
			
		||||
        run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
 | 
			
		||||
@@ -68,7 +68,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -89,7 +89,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -110,7 +110,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -131,7 +131,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -152,7 +152,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -173,10 +173,10 @@ jobs:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        python-version:
 | 
			
		||||
          - "3.9"
 | 
			
		||||
          - "3.10"
 | 
			
		||||
          - "3.11"
 | 
			
		||||
          - "3.12"
 | 
			
		||||
          - "3.13"
 | 
			
		||||
        os:
 | 
			
		||||
          - ubuntu-latest
 | 
			
		||||
          - macOS-latest
 | 
			
		||||
@@ -185,24 +185,24 @@ jobs:
 | 
			
		||||
          # Minimize CI resource usage
 | 
			
		||||
          # by only running the Python version
 | 
			
		||||
          # version used for docker images on Windows and macOS
 | 
			
		||||
          - python-version: "3.13"
 | 
			
		||||
            os: windows-latest
 | 
			
		||||
          - python-version: "3.12"
 | 
			
		||||
            os: windows-latest
 | 
			
		||||
          - python-version: "3.10"
 | 
			
		||||
            os: windows-latest
 | 
			
		||||
          - python-version: "3.9"
 | 
			
		||||
            os: windows-latest
 | 
			
		||||
          - python-version: "3.13"
 | 
			
		||||
            os: macOS-latest
 | 
			
		||||
          - python-version: "3.12"
 | 
			
		||||
            os: macOS-latest
 | 
			
		||||
          - python-version: "3.10"
 | 
			
		||||
            os: macOS-latest
 | 
			
		||||
          - python-version: "3.9"
 | 
			
		||||
            os: macOS-latest
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    needs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -214,14 +214,14 @@ jobs:
 | 
			
		||||
        if: matrix.os == 'windows-latest'
 | 
			
		||||
        run: |
 | 
			
		||||
          ./venv/Scripts/activate
 | 
			
		||||
          pytest -vv --cov-report=xml --tb=native tests
 | 
			
		||||
          pytest -vv --cov-report=xml --tb=native -n auto tests
 | 
			
		||||
      - name: Run pytest
 | 
			
		||||
        if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest'
 | 
			
		||||
        run: |
 | 
			
		||||
          . venv/bin/activate
 | 
			
		||||
          pytest -vv --cov-report=xml --tb=native tests
 | 
			
		||||
          pytest -vv --cov-report=xml --tb=native -n auto tests
 | 
			
		||||
      - name: Upload coverage to Codecov
 | 
			
		||||
        uses: codecov/codecov-action@v5.4.2
 | 
			
		||||
        uses: codecov/codecov-action@v5.4.3
 | 
			
		||||
        with:
 | 
			
		||||
          token: ${{ secrets.CODECOV_TOKEN }}
 | 
			
		||||
 | 
			
		||||
@@ -232,7 +232,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -296,11 +296,11 @@ jobs:
 | 
			
		||||
            name: Run script/clang-tidy for ZEPHYR
 | 
			
		||||
            options: --environment nrf52-tidy --grep USE_ZEPHYR
 | 
			
		||||
            pio_cache_key: tidy-zephyr
 | 
			
		||||
            ignore_errors: true
 | 
			
		||||
            ignore_errors: false
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -356,7 +356,7 @@ jobs:
 | 
			
		||||
      count: ${{ steps.list-components.outputs.count }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
        with:
 | 
			
		||||
          # Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works.
 | 
			
		||||
          fetch-depth: 500
 | 
			
		||||
@@ -406,7 +406,7 @@ jobs:
 | 
			
		||||
          sudo apt-get install libsdl2-dev
 | 
			
		||||
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -432,7 +432,7 @@ jobs:
 | 
			
		||||
      matrix: ${{ steps.split.outputs.components }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Split components into 20 groups
 | 
			
		||||
        id: split
 | 
			
		||||
        run: |
 | 
			
		||||
@@ -462,7 +462,7 @@ jobs:
 | 
			
		||||
          sudo apt-get install libsdl2-dev
 | 
			
		||||
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,8 +18,9 @@ jobs:
 | 
			
		||||
    outputs:
 | 
			
		||||
      tag: ${{ steps.tag.outputs.tag }}
 | 
			
		||||
      branch_build: ${{ steps.tag.outputs.branch_build }}
 | 
			
		||||
      deploy_env: ${{ steps.tag.outputs.deploy_env }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.1.7
 | 
			
		||||
      - uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Get tag
 | 
			
		||||
        id: tag
 | 
			
		||||
        # yamllint disable rule:line-length
 | 
			
		||||
@@ -27,6 +28,11 @@ jobs:
 | 
			
		||||
          if [[ "${{ github.event_name }}" = "release" ]]; then
 | 
			
		||||
            TAG="${{ github.event.release.tag_name}}"
 | 
			
		||||
            BRANCH_BUILD="false"
 | 
			
		||||
            if [[ "${{ github.event.release.prerelease }}" = "true" ]]; then
 | 
			
		||||
              ENVIRONMENT="beta"
 | 
			
		||||
            else
 | 
			
		||||
              ENVIRONMENT="production"
 | 
			
		||||
            fi
 | 
			
		||||
          else
 | 
			
		||||
            TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
 | 
			
		||||
            today="$(date --utc '+%Y%m%d')"
 | 
			
		||||
@@ -35,12 +41,15 @@ jobs:
 | 
			
		||||
            if [[ "$BRANCH" != "dev" ]]; then
 | 
			
		||||
              TAG="${TAG}-${BRANCH}"
 | 
			
		||||
              BRANCH_BUILD="true"
 | 
			
		||||
              ENVIRONMENT=""
 | 
			
		||||
            else
 | 
			
		||||
              BRANCH_BUILD="false"
 | 
			
		||||
              ENVIRONMENT="dev"
 | 
			
		||||
            fi
 | 
			
		||||
          fi
 | 
			
		||||
          echo "tag=${TAG}" >> $GITHUB_OUTPUT
 | 
			
		||||
          echo "branch_build=${BRANCH_BUILD}" >> $GITHUB_OUTPUT
 | 
			
		||||
          echo "deploy_env=${ENVIRONMENT}" >> $GITHUB_OUTPUT
 | 
			
		||||
        # yamllint enable rule:line-length
 | 
			
		||||
 | 
			
		||||
  deploy-pypi:
 | 
			
		||||
@@ -51,21 +60,19 @@ jobs:
 | 
			
		||||
      contents: read
 | 
			
		||||
      id-token: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.1.7
 | 
			
		||||
      - uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v5.6.0
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.x"
 | 
			
		||||
      - name: Set up python environment
 | 
			
		||||
        env:
 | 
			
		||||
          ESPHOME_NO_VENV: 1
 | 
			
		||||
        run: script/setup
 | 
			
		||||
      - name: Build
 | 
			
		||||
        run: |-
 | 
			
		||||
          pip3 install build
 | 
			
		||||
          python3 -m build
 | 
			
		||||
      - name: Publish
 | 
			
		||||
        uses: pypa/gh-action-pypi-publish@v1.12.4
 | 
			
		||||
        with:
 | 
			
		||||
          skip-existing: true
 | 
			
		||||
 | 
			
		||||
  deploy-docker:
 | 
			
		||||
    name: Build ESPHome ${{ matrix.platform.arch }}
 | 
			
		||||
@@ -85,11 +92,11 @@ jobs:
 | 
			
		||||
            os: "ubuntu-24.04-arm"
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.1.7
 | 
			
		||||
      - uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v5.6.0
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
          python-version: "3.10"
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.10.0
 | 
			
		||||
@@ -161,7 +168,7 @@ jobs:
 | 
			
		||||
          - ghcr
 | 
			
		||||
          - dockerhub
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.1.7
 | 
			
		||||
      - uses: actions/checkout@v4.2.2
 | 
			
		||||
 | 
			
		||||
      - name: Download digests
 | 
			
		||||
        uses: actions/download-artifact@v4.3.0
 | 
			
		||||
@@ -235,9 +242,8 @@ jobs:
 | 
			
		||||
  deploy-esphome-schema:
 | 
			
		||||
    if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false'
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs:
 | 
			
		||||
      - init
 | 
			
		||||
      - deploy-manifest
 | 
			
		||||
    needs: [init]
 | 
			
		||||
    environment: ${{ needs.init.outputs.deploy_env }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Trigger Workflow
 | 
			
		||||
        uses: actions/github-script@v7.0.1
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							@@ -13,10 +13,10 @@ jobs:
 | 
			
		||||
    if: github.repository == 'esphome/esphome'
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
 | 
			
		||||
      - name: Checkout Home Assistant
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
        with:
 | 
			
		||||
          repository: home-assistant/core
 | 
			
		||||
          path: lib/home-assistant
 | 
			
		||||
@@ -24,7 +24,7 @@ jobs:
 | 
			
		||||
      - name: Setup Python
 | 
			
		||||
        uses: actions/setup-python@v5.6.0
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: 3.12
 | 
			
		||||
          python-version: 3.13
 | 
			
		||||
 | 
			
		||||
      - name: Install Home Assistant
 | 
			
		||||
        run: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/yaml-lint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/yaml-lint.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,7 +18,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        uses: actions/checkout@v4.2.2
 | 
			
		||||
      - name: Run yamllint
 | 
			
		||||
        uses: frenck/action-yamllint@v1.5.0
 | 
			
		||||
        with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -143,3 +143,4 @@ sdkconfig.*
 | 
			
		||||
/components
 | 
			
		||||
/managed_components
 | 
			
		||||
 | 
			
		||||
api-docs/
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
repos:
 | 
			
		||||
  - repo: https://github.com/astral-sh/ruff-pre-commit
 | 
			
		||||
    # Ruff version.
 | 
			
		||||
    rev: v0.11.9
 | 
			
		||||
    rev: v0.11.10
 | 
			
		||||
    hooks:
 | 
			
		||||
      # Run the linter.
 | 
			
		||||
      - id: ruff
 | 
			
		||||
@@ -28,10 +28,10 @@ repos:
 | 
			
		||||
          - --branch=release
 | 
			
		||||
          - --branch=beta
 | 
			
		||||
  - repo: https://github.com/asottile/pyupgrade
 | 
			
		||||
    rev: v3.15.2
 | 
			
		||||
    rev: v3.20.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: pyupgrade
 | 
			
		||||
        args: [--py39-plus]
 | 
			
		||||
        args: [--py310-plus]
 | 
			
		||||
  - repo: https://github.com/adrienverge/yamllint.git
 | 
			
		||||
    rev: v1.37.1
 | 
			
		||||
    hooks:
 | 
			
		||||
 
 | 
			
		||||
@@ -96,6 +96,7 @@ esphome/components/ch422g/* @clydebarrow @jesterret
 | 
			
		||||
esphome/components/chsc6x/* @kkosik20
 | 
			
		||||
esphome/components/climate/* @esphome/core
 | 
			
		||||
esphome/components/climate_ir/* @glmnet
 | 
			
		||||
esphome/components/cm1106/* @andrewjswan
 | 
			
		||||
esphome/components/color_temperature/* @jesserockz
 | 
			
		||||
esphome/components/combination/* @Cat-Ion @kahrendt
 | 
			
		||||
esphome/components/const/* @esphome/core
 | 
			
		||||
@@ -138,6 +139,7 @@ esphome/components/es7210/* @kahrendt
 | 
			
		||||
esphome/components/es7243e/* @kbx81
 | 
			
		||||
esphome/components/es8156/* @kbx81
 | 
			
		||||
esphome/components/es8311/* @kahrendt @kroimon
 | 
			
		||||
esphome/components/es8388/* @P4uLT
 | 
			
		||||
esphome/components/esp32/* @esphome/core
 | 
			
		||||
esphome/components/esp32_ble/* @Rapsssito @jesserockz
 | 
			
		||||
esphome/components/esp32_ble_client/* @jesserockz
 | 
			
		||||
@@ -148,6 +150,7 @@ esphome/components/esp32_improv/* @jesserockz
 | 
			
		||||
esphome/components/esp32_rmt/* @jesserockz
 | 
			
		||||
esphome/components/esp32_rmt_led_strip/* @jesserockz
 | 
			
		||||
esphome/components/esp8266/* @esphome/core
 | 
			
		||||
esphome/components/esp_ldo/* @clydebarrow
 | 
			
		||||
esphome/components/ethernet_info/* @gtjadsonsantos
 | 
			
		||||
esphome/components/event/* @nohat
 | 
			
		||||
esphome/components/event_emitter/* @Rapsssito
 | 
			
		||||
@@ -169,7 +172,7 @@ esphome/components/gp2y1010au0f/* @zry98
 | 
			
		||||
esphome/components/gp8403/* @jesserockz
 | 
			
		||||
esphome/components/gpio/* @esphome/core
 | 
			
		||||
esphome/components/gpio/one_wire/* @ssieb
 | 
			
		||||
esphome/components/gps/* @coogle
 | 
			
		||||
esphome/components/gps/* @coogle @ximex
 | 
			
		||||
esphome/components/graph/* @synco
 | 
			
		||||
esphome/components/graphical_display_menu/* @MrMDavidson
 | 
			
		||||
esphome/components/gree/* @orestismers
 | 
			
		||||
@@ -233,6 +236,7 @@ esphome/components/kamstrup_kmp/* @cfeenstra1024
 | 
			
		||||
esphome/components/key_collector/* @ssieb
 | 
			
		||||
esphome/components/key_provider/* @ssieb
 | 
			
		||||
esphome/components/kuntze/* @ssieb
 | 
			
		||||
esphome/components/lc709203f/* @ilikecake
 | 
			
		||||
esphome/components/lcd_menu/* @numo68
 | 
			
		||||
esphome/components/ld2410/* @regevbr @sebcaps
 | 
			
		||||
esphome/components/ld2420/* @descipher
 | 
			
		||||
@@ -318,6 +322,7 @@ esphome/components/number/* @esphome/core
 | 
			
		||||
esphome/components/one_wire/* @ssieb
 | 
			
		||||
esphome/components/online_image/* @clydebarrow @guillempages
 | 
			
		||||
esphome/components/opentherm/* @olegtarasov
 | 
			
		||||
esphome/components/openthread/* @mrene
 | 
			
		||||
esphome/components/ota/* @esphome/core
 | 
			
		||||
esphome/components/output/* @esphome/core
 | 
			
		||||
esphome/components/packet_transport/* @clydebarrow
 | 
			
		||||
@@ -478,6 +483,8 @@ esphome/components/ufire_ise/* @pvizeli
 | 
			
		||||
esphome/components/ultrasonic/* @OttoWinter
 | 
			
		||||
esphome/components/update/* @jesserockz
 | 
			
		||||
esphome/components/uponor_smatrix/* @kroimon
 | 
			
		||||
esphome/components/usb_host/* @clydebarrow
 | 
			
		||||
esphome/components/usb_uart/* @clydebarrow
 | 
			
		||||
esphome/components/valve/* @esphome/core
 | 
			
		||||
esphome/components/vbus/* @ssieb
 | 
			
		||||
esphome/components/veml3235/* @kbx81
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,9 @@ FROM base-source-${BUILD_TYPE} AS base
 | 
			
		||||
 | 
			
		||||
RUN git config --system --add safe.directory "*"
 | 
			
		||||
 | 
			
		||||
RUN pip install uv==0.6.14
 | 
			
		||||
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
 | 
			
		||||
 | 
			
		||||
RUN pip install --no-cache-dir -U pip uv==0.6.14
 | 
			
		||||
 | 
			
		||||
COPY requirements.txt /
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ from esphome.const import (
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, EsphomeError, coroutine
 | 
			
		||||
from esphome.helpers import get_bool_env, indent, is_ip_address
 | 
			
		||||
from esphome.log import Fore, color, setup_log
 | 
			
		||||
from esphome.log import AnsiFore, color, setup_log
 | 
			
		||||
from esphome.util import (
 | 
			
		||||
    get_serial_ports,
 | 
			
		||||
    list_yaml_files,
 | 
			
		||||
@@ -83,7 +83,7 @@ def choose_prompt(options, purpose: str = None):
 | 
			
		||||
                raise ValueError
 | 
			
		||||
            break
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            safe_print(color(Fore.RED, f"Invalid option: '{opt}'"))
 | 
			
		||||
            safe_print(color(AnsiFore.RED, f"Invalid option: '{opt}'"))
 | 
			
		||||
    return options[opt - 1][1]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -134,6 +134,7 @@ def get_port_type(port):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run_miniterm(config, port, args):
 | 
			
		||||
    from aioesphomeapi import LogParser
 | 
			
		||||
    import serial
 | 
			
		||||
 | 
			
		||||
    from esphome import platformio_api
 | 
			
		||||
@@ -158,6 +159,7 @@ def run_miniterm(config, port, args):
 | 
			
		||||
        ser.dtr = False
 | 
			
		||||
        ser.rts = False
 | 
			
		||||
 | 
			
		||||
    parser = LogParser()
 | 
			
		||||
    tries = 0
 | 
			
		||||
    while tries < 5:
 | 
			
		||||
        try:
 | 
			
		||||
@@ -174,8 +176,7 @@ def run_miniterm(config, port, args):
 | 
			
		||||
                        .decode("utf8", "backslashreplace")
 | 
			
		||||
                    )
 | 
			
		||||
                    time_str = datetime.now().time().strftime("[%H:%M:%S]")
 | 
			
		||||
                    message = time_str + line
 | 
			
		||||
                    safe_print(message)
 | 
			
		||||
                    safe_print(parser.parse_line(line, time_str))
 | 
			
		||||
 | 
			
		||||
                    backtrace_state = platformio_api.process_stacktrace(
 | 
			
		||||
                        config, line, backtrace_state=backtrace_state
 | 
			
		||||
@@ -593,33 +594,38 @@ def command_update_all(args):
 | 
			
		||||
        middle_text = f" {middle_text} "
 | 
			
		||||
        width = len(click.unstyle(middle_text))
 | 
			
		||||
        half_line = "=" * ((twidth - width) // 2)
 | 
			
		||||
        click.echo(f"{half_line}{middle_text}{half_line}")
 | 
			
		||||
        safe_print(f"{half_line}{middle_text}{half_line}")
 | 
			
		||||
 | 
			
		||||
    for f in files:
 | 
			
		||||
        print(f"Updating {color(Fore.CYAN, f)}")
 | 
			
		||||
        print("-" * twidth)
 | 
			
		||||
        print()
 | 
			
		||||
        rc = run_external_process(
 | 
			
		||||
            "esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
 | 
			
		||||
        )
 | 
			
		||||
        safe_print(f"Updating {color(AnsiFore.CYAN, f)}")
 | 
			
		||||
        safe_print("-" * twidth)
 | 
			
		||||
        safe_print()
 | 
			
		||||
        if CORE.dashboard:
 | 
			
		||||
            rc = run_external_process(
 | 
			
		||||
                "esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            rc = run_external_process(
 | 
			
		||||
                "esphome", "run", f, "--no-logs", "--device", "OTA"
 | 
			
		||||
            )
 | 
			
		||||
        if rc == 0:
 | 
			
		||||
            print_bar(f"[{color(Fore.BOLD_GREEN, 'SUCCESS')}] {f}")
 | 
			
		||||
            print_bar(f"[{color(AnsiFore.BOLD_GREEN, 'SUCCESS')}] {f}")
 | 
			
		||||
            success[f] = True
 | 
			
		||||
        else:
 | 
			
		||||
            print_bar(f"[{color(Fore.BOLD_RED, 'ERROR')}] {f}")
 | 
			
		||||
            print_bar(f"[{color(AnsiFore.BOLD_RED, 'ERROR')}] {f}")
 | 
			
		||||
            success[f] = False
 | 
			
		||||
 | 
			
		||||
        print()
 | 
			
		||||
        print()
 | 
			
		||||
        print()
 | 
			
		||||
        safe_print()
 | 
			
		||||
        safe_print()
 | 
			
		||||
        safe_print()
 | 
			
		||||
 | 
			
		||||
    print_bar(f"[{color(Fore.BOLD_WHITE, 'SUMMARY')}]")
 | 
			
		||||
    print_bar(f"[{color(AnsiFore.BOLD_WHITE, 'SUMMARY')}]")
 | 
			
		||||
    failed = 0
 | 
			
		||||
    for f in files:
 | 
			
		||||
        if success[f]:
 | 
			
		||||
            print(f"  - {f}: {color(Fore.GREEN, 'SUCCESS')}")
 | 
			
		||||
            safe_print(f"  - {f}: {color(AnsiFore.GREEN, 'SUCCESS')}")
 | 
			
		||||
        else:
 | 
			
		||||
            print(f"  - {f}: {color(Fore.BOLD_RED, 'FAILED')}")
 | 
			
		||||
            safe_print(f"  - {f}: {color(AnsiFore.BOLD_RED, 'FAILED')}")
 | 
			
		||||
            failed += 1
 | 
			
		||||
    return failed
 | 
			
		||||
 | 
			
		||||
@@ -645,7 +651,7 @@ def command_rename(args, config):
 | 
			
		||||
        if c not in ALLOWED_NAME_CHARS:
 | 
			
		||||
            print(
 | 
			
		||||
                color(
 | 
			
		||||
                    Fore.BOLD_RED,
 | 
			
		||||
                    AnsiFore.BOLD_RED,
 | 
			
		||||
                    f"'{c}' is an invalid character for names. Valid characters are: "
 | 
			
		||||
                    f"{ALLOWED_NAME_CHARS} (lowercase, no spaces)",
 | 
			
		||||
                )
 | 
			
		||||
@@ -658,7 +664,9 @@ def command_rename(args, config):
 | 
			
		||||
    yaml = yaml_util.load_yaml(CORE.config_path)
 | 
			
		||||
    if CONF_ESPHOME not in yaml or CONF_NAME not in yaml[CONF_ESPHOME]:
 | 
			
		||||
        print(
 | 
			
		||||
            color(Fore.BOLD_RED, "Complex YAML files cannot be automatically renamed.")
 | 
			
		||||
            color(
 | 
			
		||||
                AnsiFore.BOLD_RED, "Complex YAML files cannot be automatically renamed."
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        return 1
 | 
			
		||||
    old_name = yaml[CONF_ESPHOME][CONF_NAME]
 | 
			
		||||
@@ -681,7 +689,7 @@ def command_rename(args, config):
 | 
			
		||||
            )
 | 
			
		||||
            > 1
 | 
			
		||||
        ):
 | 
			
		||||
            print(color(Fore.BOLD_RED, "Too many matches in YAML to safely rename"))
 | 
			
		||||
            print(color(AnsiFore.BOLD_RED, "Too many matches in YAML to safely rename"))
 | 
			
		||||
            return 1
 | 
			
		||||
 | 
			
		||||
        new_raw = re.sub(
 | 
			
		||||
@@ -693,7 +701,7 @@ def command_rename(args, config):
 | 
			
		||||
 | 
			
		||||
    new_path = os.path.join(CORE.config_dir, args.name + ".yaml")
 | 
			
		||||
    print(
 | 
			
		||||
        f"Updating {color(Fore.CYAN, CORE.config_path)} to {color(Fore.CYAN, new_path)}"
 | 
			
		||||
        f"Updating {color(AnsiFore.CYAN, CORE.config_path)} to {color(AnsiFore.CYAN, new_path)}"
 | 
			
		||||
    )
 | 
			
		||||
    print()
 | 
			
		||||
 | 
			
		||||
@@ -702,7 +710,7 @@ def command_rename(args, config):
 | 
			
		||||
 | 
			
		||||
    rc = run_external_process("esphome", "config", new_path)
 | 
			
		||||
    if rc != 0:
 | 
			
		||||
        print(color(Fore.BOLD_RED, "Rename failed. Reverting changes."))
 | 
			
		||||
        print(color(AnsiFore.BOLD_RED, "Rename failed. Reverting changes."))
 | 
			
		||||
        os.remove(new_path)
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
@@ -728,7 +736,7 @@ def command_rename(args, config):
 | 
			
		||||
    if CORE.config_path != new_path:
 | 
			
		||||
        os.remove(CORE.config_path)
 | 
			
		||||
 | 
			
		||||
    print(color(Fore.BOLD_GREEN, "SUCCESS"))
 | 
			
		||||
    print(color(AnsiFore.BOLD_GREEN, "SUCCESS"))
 | 
			
		||||
    print()
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ namespace a4988 {
 | 
			
		||||
static const char *const TAG = "a4988.stepper";
 | 
			
		||||
 | 
			
		||||
void A4988::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up A4988...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  if (this->sleep_pin_ != nullptr) {
 | 
			
		||||
    this->sleep_pin_->setup();
 | 
			
		||||
    this->sleep_pin_->digital_write(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ namespace absolute_humidity {
 | 
			
		||||
static const char *const TAG = "absolute_humidity.sensor";
 | 
			
		||||
 | 
			
		||||
void AbsoluteHumidityComponent::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up absolute humidity '%s'...", this->get_name().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "  Added callback for temperature '%s'", this->temperature_sensor_->get_name().c_str());
 | 
			
		||||
  this->temperature_sensor_->add_on_state_callback([this](float state) { this->temperature_callback_(state); });
 | 
			
		||||
@@ -40,9 +40,11 @@ void AbsoluteHumidityComponent::dump_config() {
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Sources");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Temperature: '%s'", this->temperature_sensor_->get_name().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Relative Humidity: '%s'", this->humidity_sensor_->get_name().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "Sources\n"
 | 
			
		||||
                "  Temperature: '%s'\n"
 | 
			
		||||
                "  Relative Humidity: '%s'",
 | 
			
		||||
                this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float AbsoluteHumidityComponent::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 
 | 
			
		||||
@@ -214,8 +214,10 @@ void AcDimmer::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "AcDimmer:");
 | 
			
		||||
  LOG_PIN("  Output Pin: ", this->gate_pin_);
 | 
			
		||||
  LOG_PIN("  Zero-Cross Pin: ", this->zero_cross_pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "   Min Power: %.1f%%", this->store_.min_power / 10.0f);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "   Init with half cycle: %s", YESNO(this->init_with_half_cycle_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "   Min Power: %.1f%%\n"
 | 
			
		||||
                "   Init with half cycle: %s",
 | 
			
		||||
                this->store_.min_power / 10.0f, YESNO(this->init_with_half_cycle_));
 | 
			
		||||
  if (method_ == DIM_METHOD_LEADING_PULSE) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "   Method: leading pulse");
 | 
			
		||||
  } else if (method_ == DIM_METHOD_LEADING) {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1;
 | 
			
		||||
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1;
 | 
			
		||||
 | 
			
		||||
void ADCSensor::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
 | 
			
		||||
 | 
			
		||||
  if (this->channel1_ != ADC1_CHANNEL_MAX) {
 | 
			
		||||
    adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
 | 
			
		||||
@@ -77,8 +77,10 @@ void ADCSensor::dump_config() {
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Samples: %i", this->sample_count_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Samples: %i\n"
 | 
			
		||||
                "  Sampling mode: %s",
 | 
			
		||||
                this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ namespace adc {
 | 
			
		||||
static const char *const TAG = "adc.esp8266";
 | 
			
		||||
 | 
			
		||||
void ADCSensor::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
 | 
			
		||||
#ifndef USE_ADC_SENSOR_VCC
 | 
			
		||||
  this->pin_->setup();
 | 
			
		||||
#endif
 | 
			
		||||
@@ -30,8 +30,10 @@ void ADCSensor::dump_config() {
 | 
			
		||||
#else
 | 
			
		||||
  LOG_PIN("  Pin: ", this->pin_);
 | 
			
		||||
#endif  // USE_ADC_SENSOR_VCC
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Samples: %i", this->sample_count_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Samples: %i\n"
 | 
			
		||||
                "  Sampling mode: %s",
 | 
			
		||||
                this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ namespace adc {
 | 
			
		||||
static const char *const TAG = "adc.libretiny";
 | 
			
		||||
 | 
			
		||||
void ADCSensor::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
 | 
			
		||||
#ifndef USE_ADC_SENSOR_VCC
 | 
			
		||||
  this->pin_->setup();
 | 
			
		||||
#endif  // !USE_ADC_SENSOR_VCC
 | 
			
		||||
@@ -22,8 +22,10 @@ void ADCSensor::dump_config() {
 | 
			
		||||
#else   // USE_ADC_SENSOR_VCC
 | 
			
		||||
  LOG_PIN("  Pin: ", this->pin_);
 | 
			
		||||
#endif  // USE_ADC_SENSOR_VCC
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Samples: %i", this->sample_count_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Samples: %i\n"
 | 
			
		||||
                "  Sampling mode: %s",
 | 
			
		||||
                this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ namespace adc {
 | 
			
		||||
static const char *const TAG = "adc.rp2040";
 | 
			
		||||
 | 
			
		||||
void ADCSensor::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
 | 
			
		||||
  static bool initialized = false;
 | 
			
		||||
  if (!initialized) {
 | 
			
		||||
    adc_init();
 | 
			
		||||
@@ -33,8 +33,10 @@ void ADCSensor::dump_config() {
 | 
			
		||||
    LOG_PIN("  Pin: ", this->pin_);
 | 
			
		||||
#endif  // USE_ADC_SENSOR_VCC
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Samples: %i", this->sample_count_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Samples: %i\n"
 | 
			
		||||
                "  Sampling mode: %s",
 | 
			
		||||
                this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ 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");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  this->spi_setup();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -177,11 +177,14 @@ void ADE7880::dump_config() {
 | 
			
		||||
    LOG_SENSOR("    ", "Power Factor", this->channel_a_->power_factor);
 | 
			
		||||
    LOG_SENSOR("    ", "Forward Active Energy", this->channel_a_->forward_active_energy);
 | 
			
		||||
    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_a_->reverse_active_energy);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_a_->current_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_a_->voltage_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Power: %" PRId32, this->channel_a_->power_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Phase Angle: %u", this->channel_a_->phase_angle_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG,
 | 
			
		||||
                  "    Calibration:\n"
 | 
			
		||||
                  "     Current: %" PRId32 "\n"
 | 
			
		||||
                  "     Voltage: %" PRId32 "\n"
 | 
			
		||||
                  "     Power: %" PRId32 "\n"
 | 
			
		||||
                  "     Phase Angle: %u",
 | 
			
		||||
                  this->channel_a_->current_gain_calibration, this->channel_a_->voltage_gain_calibration,
 | 
			
		||||
                  this->channel_a_->power_gain_calibration, this->channel_a_->phase_angle_calibration);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->channel_b_ != nullptr) {
 | 
			
		||||
@@ -193,11 +196,14 @@ void ADE7880::dump_config() {
 | 
			
		||||
    LOG_SENSOR("    ", "Power Factor", this->channel_b_->power_factor);
 | 
			
		||||
    LOG_SENSOR("    ", "Forward Active Energy", this->channel_b_->forward_active_energy);
 | 
			
		||||
    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_b_->reverse_active_energy);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_b_->current_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_b_->voltage_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Power: %" PRId32, this->channel_b_->power_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Phase Angle: %u", this->channel_b_->phase_angle_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG,
 | 
			
		||||
                  "    Calibration:\n"
 | 
			
		||||
                  "     Current: %" PRId32 "\n"
 | 
			
		||||
                  "     Voltage: %" PRId32 "\n"
 | 
			
		||||
                  "     Power: %" PRId32 "\n"
 | 
			
		||||
                  "     Phase Angle: %u",
 | 
			
		||||
                  this->channel_b_->current_gain_calibration, this->channel_b_->voltage_gain_calibration,
 | 
			
		||||
                  this->channel_b_->power_gain_calibration, this->channel_b_->phase_angle_calibration);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->channel_c_ != nullptr) {
 | 
			
		||||
@@ -209,18 +215,23 @@ void ADE7880::dump_config() {
 | 
			
		||||
    LOG_SENSOR("    ", "Power Factor", this->channel_c_->power_factor);
 | 
			
		||||
    LOG_SENSOR("    ", "Forward Active Energy", this->channel_c_->forward_active_energy);
 | 
			
		||||
    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_c_->reverse_active_energy);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_c_->current_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_c_->voltage_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Power: %" PRId32, this->channel_c_->power_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Phase Angle: %u", this->channel_c_->phase_angle_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG,
 | 
			
		||||
                  "    Calibration:\n"
 | 
			
		||||
                  "     Current: %" PRId32 "\n"
 | 
			
		||||
                  "     Voltage: %" PRId32 "\n"
 | 
			
		||||
                  "     Power: %" PRId32 "\n"
 | 
			
		||||
                  "     Phase Angle: %u",
 | 
			
		||||
                  this->channel_c_->current_gain_calibration, this->channel_c_->voltage_gain_calibration,
 | 
			
		||||
                  this->channel_c_->power_gain_calibration, this->channel_c_->phase_angle_calibration);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->channel_n_ != nullptr) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Neutral:");
 | 
			
		||||
    LOG_SENSOR("    ", "Current", this->channel_n_->current);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_n_->current_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG,
 | 
			
		||||
                  "    Calibration:\n"
 | 
			
		||||
                  "     Current: %" PRId32,
 | 
			
		||||
                  this->channel_n_->current_gain_calibration);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
 
 | 
			
		||||
@@ -58,15 +58,18 @@ void ADE7953::dump_config() {
 | 
			
		||||
  LOG_SENSOR("  ", "Active Power B Sensor", this->active_power_b_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Rective Power A Sensor", this->reactive_power_a_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Reactive Power B Sensor", this->reactive_power_b_sensor_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  USE_ACC_ENERGY_REGS: %d", this->use_acc_energy_regs_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  PGA_V_8: 0x%X", pga_v_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  PGA_IA_8: 0x%X", pga_ia_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  PGA_IB_8: 0x%X", pga_ib_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  VGAIN_32: 0x%08jX", (uintmax_t) vgain_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  AIGAIN_32: 0x%08jX", (uintmax_t) aigain_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  BIGAIN_32: 0x%08jX", (uintmax_t) bigain_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  AWGAIN_32: 0x%08jX", (uintmax_t) awgain_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  BWGAIN_32: 0x%08jX", (uintmax_t) bwgain_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  USE_ACC_ENERGY_REGS: %d\n"
 | 
			
		||||
                "  PGA_V_8: 0x%X\n"
 | 
			
		||||
                "  PGA_IA_8: 0x%X\n"
 | 
			
		||||
                "  PGA_IB_8: 0x%X\n"
 | 
			
		||||
                "  VGAIN_32: 0x%08jX\n"
 | 
			
		||||
                "  AIGAIN_32: 0x%08jX\n"
 | 
			
		||||
                "  BIGAIN_32: 0x%08jX\n"
 | 
			
		||||
                "  AWGAIN_32: 0x%08jX\n"
 | 
			
		||||
                "  BWGAIN_32: 0x%08jX",
 | 
			
		||||
                this->use_acc_energy_regs_, pga_v_, pga_ia_, pga_ib_, (uintmax_t) vgain_, (uintmax_t) aigain_,
 | 
			
		||||
                (uintmax_t) bigain_, (uintmax_t) awgain_, (uintmax_t) bwgain_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define ADE_PUBLISH_(name, val, factor) \
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#include "ade7953_i2c.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ade7953_i2c {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#include "ade7953_spi.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ade7953_spi {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,15 +10,13 @@ static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
 | 
			
		||||
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
 | 
			
		||||
 | 
			
		||||
void ADS1115Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  uint16_t value;
 | 
			
		||||
  if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &value)) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Configuring ADS1115...");
 | 
			
		||||
 | 
			
		||||
  uint16_t config = 0;
 | 
			
		||||
  // Clear single-shot bit
 | 
			
		||||
  //        0b0xxxxxxxxxxxxxxx
 | 
			
		||||
@@ -68,10 +66,10 @@ void ADS1115Component::setup() {
 | 
			
		||||
  this->prev_config_ = config;
 | 
			
		||||
}
 | 
			
		||||
void ADS1115Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "ADS1115:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with ADS1115 failed!");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "ads1118.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -8,7 +9,7 @@ static const char *const TAG = "ads1118";
 | 
			
		||||
static const uint8_t ADS1118_DATA_RATE_860_SPS = 0b111;
 | 
			
		||||
 | 
			
		||||
void ADS1118::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ads1118");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  this->spi_setup();
 | 
			
		||||
 | 
			
		||||
  this->config_ = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "ags10.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
@@ -23,7 +24,7 @@ static const uint16_t ZP_CURRENT = 0x0000;
 | 
			
		||||
static const uint16_t ZP_DEFAULT = 0xFFFF;
 | 
			
		||||
 | 
			
		||||
void AGS10Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ags10...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
 | 
			
		||||
  auto version = this->read_version_();
 | 
			
		||||
  if (version) {
 | 
			
		||||
@@ -65,7 +66,7 @@ void AGS10Component::dump_config() {
 | 
			
		||||
    case NONE:
 | 
			
		||||
      break;
 | 
			
		||||
    case COMMUNICATION_FAILED:
 | 
			
		||||
      ESP_LOGE(TAG, "Communication with AGS10 failed!");
 | 
			
		||||
      ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
      break;
 | 
			
		||||
    case CRC_CHECK_FAILED:
 | 
			
		||||
      ESP_LOGE(TAG, "The crc check failed");
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,9 @@
 | 
			
		||||
// results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time.
 | 
			
		||||
 | 
			
		||||
#include "aht10.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace aht10 {
 | 
			
		||||
@@ -34,57 +35,59 @@ static const uint8_t AHT10_INIT_ATTEMPTS = 10;
 | 
			
		||||
 | 
			
		||||
static const uint8_t AHT10_STATUS_BUSY = 0x80;
 | 
			
		||||
 | 
			
		||||
static const float AHT10_DIVISOR = 1048576.0f;  // 2^20, used for temperature and humidity calculations
 | 
			
		||||
 | 
			
		||||
void AHT10Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
 | 
			
		||||
  if (this->write(AHT10_SOFTRESET_CMD, sizeof(AHT10_SOFTRESET_CMD)) != i2c::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Reset AHT10 failed!");
 | 
			
		||||
    ESP_LOGE(TAG, "Reset failed");
 | 
			
		||||
  }
 | 
			
		||||
  delay(AHT10_SOFTRESET_DELAY);
 | 
			
		||||
 | 
			
		||||
  i2c::ErrorCode error_code = i2c::ERROR_INVALID_ARGUMENT;
 | 
			
		||||
  switch (this->variant_) {
 | 
			
		||||
    case AHT10Variant::AHT20:
 | 
			
		||||
      ESP_LOGCONFIG(TAG, "Setting up AHT20");
 | 
			
		||||
      error_code = this->write(AHT20_INITIALIZE_CMD, sizeof(AHT20_INITIALIZE_CMD));
 | 
			
		||||
      break;
 | 
			
		||||
    case AHT10Variant::AHT10:
 | 
			
		||||
      ESP_LOGCONFIG(TAG, "Setting up AHT10");
 | 
			
		||||
      error_code = this->write(AHT10_INITIALIZE_CMD, sizeof(AHT10_INITIALIZE_CMD));
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  if (error_code != i2c::ERROR_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with AHT10 failed!");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  uint8_t cal_attempts = 0;
 | 
			
		||||
  uint8_t data = AHT10_STATUS_BUSY;
 | 
			
		||||
  int cal_attempts = 0;
 | 
			
		||||
  while (data & AHT10_STATUS_BUSY) {
 | 
			
		||||
    delay(AHT10_DEFAULT_DELAY);
 | 
			
		||||
    if (this->read(&data, 1) != i2c::ERROR_OK) {
 | 
			
		||||
      ESP_LOGE(TAG, "Communication with AHT10 failed!");
 | 
			
		||||
      ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
      this->mark_failed();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    ++cal_attempts;
 | 
			
		||||
    if (cal_attempts > AHT10_INIT_ATTEMPTS) {
 | 
			
		||||
      ESP_LOGE(TAG, "AHT10 initialization timed out!");
 | 
			
		||||
      ESP_LOGE(TAG, "Initialization timed out");
 | 
			
		||||
      this->mark_failed();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if ((data & 0x68) != 0x08) {  // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED
 | 
			
		||||
    ESP_LOGE(TAG, "AHT10 initialization failed!");
 | 
			
		||||
    ESP_LOGE(TAG, "Initialization failed");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "AHT10 initialization");
 | 
			
		||||
  ESP_LOGV(TAG, "Initialization complete");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AHT10Component::restart_read_() {
 | 
			
		||||
  if (this->read_count_ == AHT10_ATTEMPTS) {
 | 
			
		||||
    this->read_count_ = 0;
 | 
			
		||||
    this->status_set_error("Measurements reading timed-out!");
 | 
			
		||||
    this->status_set_error("Reading timed out");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->read_count_++;
 | 
			
		||||
@@ -97,24 +100,24 @@ void AHT10Component::read_data_() {
 | 
			
		||||
    ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_));
 | 
			
		||||
  }
 | 
			
		||||
  if (this->read(data, 6) != i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning("AHT10 read failed, retrying soon");
 | 
			
		||||
    this->status_set_warning("Read failed, will retry");
 | 
			
		||||
    this->restart_read_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((data[0] & 0x80) == 0x80) {  // Bit[7] = 0b1, device is busy
 | 
			
		||||
    ESP_LOGD(TAG, "AHT10 is busy, waiting...");
 | 
			
		||||
    ESP_LOGD(TAG, "Device busy, will retry");
 | 
			
		||||
    this->restart_read_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
 | 
			
		||||
    // Unrealistic humidity (0x0)
 | 
			
		||||
    // Invalid humidity (0x0)
 | 
			
		||||
    if (this->humidity_sensor_ == nullptr) {
 | 
			
		||||
      ESP_LOGV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required");
 | 
			
		||||
      ESP_LOGV(TAG, "Invalid humidity (reading not required)");
 | 
			
		||||
    } else {
 | 
			
		||||
      ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying...");
 | 
			
		||||
      ESP_LOGD(TAG, "Invalid humidity, retrying");
 | 
			
		||||
      if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
 | 
			
		||||
        this->status_set_warning("Communication with AHT10 failed!");
 | 
			
		||||
        this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
      }
 | 
			
		||||
      this->restart_read_();
 | 
			
		||||
      return;
 | 
			
		||||
@@ -123,22 +126,17 @@ void AHT10Component::read_data_() {
 | 
			
		||||
  if (this->read_count_ > 1) {
 | 
			
		||||
    ESP_LOGD(TAG, "Success at %ums", (unsigned) (millis() - this->start_time_));
 | 
			
		||||
  }
 | 
			
		||||
  uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
 | 
			
		||||
  uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
 | 
			
		||||
  uint32_t raw_temperature = encode_uint24(data[3] & 0xF, data[4], data[5]);
 | 
			
		||||
  uint32_t raw_humidity = encode_uint24(data[1], data[2], data[3]) >> 4;
 | 
			
		||||
 | 
			
		||||
  if (this->temperature_sensor_ != nullptr) {
 | 
			
		||||
    float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f;
 | 
			
		||||
    float temperature = ((200.0f * static_cast<float>(raw_temperature)) / AHT10_DIVISOR) - 50.0f;
 | 
			
		||||
    this->temperature_sensor_->publish_state(temperature);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->humidity_sensor_ != nullptr) {
 | 
			
		||||
    float humidity;
 | 
			
		||||
    if (raw_humidity == 0) {  // unrealistic value
 | 
			
		||||
      humidity = NAN;
 | 
			
		||||
    } else {
 | 
			
		||||
      humidity = (float) raw_humidity * 100.0f / 1048576.0f;
 | 
			
		||||
    }
 | 
			
		||||
    float humidity = raw_humidity == 0 ? NAN : static_cast<float>(raw_humidity) * 100.0f / AHT10_DIVISOR;
 | 
			
		||||
    if (std::isnan(humidity)) {
 | 
			
		||||
      ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
 | 
			
		||||
      ESP_LOGW(TAG, "Invalid humidity reading (0%%), ");
 | 
			
		||||
    }
 | 
			
		||||
    this->humidity_sensor_->publish_state(humidity);
 | 
			
		||||
  }
 | 
			
		||||
@@ -150,7 +148,7 @@ void AHT10Component::update() {
 | 
			
		||||
    return;
 | 
			
		||||
  this->start_time_ = millis();
 | 
			
		||||
  if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning("Communication with AHT10 failed!");
 | 
			
		||||
    this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->restart_read_();
 | 
			
		||||
@@ -162,7 +160,7 @@ void AHT10Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "AHT10:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with AHT10 failed!");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
  }
 | 
			
		||||
  LOG_SENSOR("  ", "Temperature", this->temperature_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Humidity", this->humidity_sensor_);
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ static const char *const TAG = "aic3204";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
void AIC3204::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up AIC3204...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
 | 
			
		||||
  // Set register page to 0
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x00), "Set page 0 failed");
 | 
			
		||||
@@ -113,7 +113,7 @@ void AIC3204::dump_config() {
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with AIC3204 failed");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -235,6 +235,7 @@ async def register_alarm_control_panel(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
        var = cg.Pvariable(config[CONF_ID], var)
 | 
			
		||||
    cg.add(cg.App.register_alarm_control_panel(var))
 | 
			
		||||
    CORE.register_platform_component("alarm_control_panel", var)
 | 
			
		||||
    await setup_alarm_control_panel_core_(var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -90,7 +90,7 @@ bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AM2315C::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up AM2315C...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
 | 
			
		||||
  // get status
 | 
			
		||||
  uint8_t status = 0;
 | 
			
		||||
@@ -188,7 +188,7 @@ void AM2315C::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "AM2315C:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with AM2315C failed!");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
  }
 | 
			
		||||
  LOG_SENSOR("  ", "Temperature", this->temperature_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Humidity", this->humidity_sensor_);
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ void AM2320Component::update() {
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
}
 | 
			
		||||
void AM2320Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up AM2320...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  uint8_t data[8];
 | 
			
		||||
  data[0] = 0;
 | 
			
		||||
  data[1] = 4;
 | 
			
		||||
@@ -47,7 +47,7 @@ void AM2320Component::dump_config() {
 | 
			
		||||
  ESP_LOGD(TAG, "AM2320:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with AM2320 failed!");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
  }
 | 
			
		||||
  LOG_SENSOR("  ", "Temperature", this->temperature_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Humidity", this->humidity_sensor_);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace am43 {
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,10 @@ using namespace esphome::cover;
 | 
			
		||||
 | 
			
		||||
void Am43Component::dump_config() {
 | 
			
		||||
  LOG_COVER("", "AM43 Cover", this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Device Pin: %d", this->pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Invert Position: %d", (int) this->invert_position_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Device Pin: %d\n"
 | 
			
		||||
                "  Invert Position: %d",
 | 
			
		||||
                this->pin_, (int) this->invert_position_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Am43Component::setup() {
 | 
			
		||||
 
 | 
			
		||||
@@ -34,8 +34,10 @@ void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
 | 
			
		||||
void AnalogThresholdBinarySensor::dump_config() {
 | 
			
		||||
  LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this);
 | 
			
		||||
  LOG_SENSOR("  ", "Sensor", this->sensor_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Upper threshold: %.11f", this->upper_threshold_.value());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Lower threshold: %.11f", this->lower_threshold_.value());
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Upper threshold: %.11f\n"
 | 
			
		||||
                "  Lower threshold: %.11f",
 | 
			
		||||
                this->upper_threshold_.value(), this->lower_threshold_.value());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace analog_threshold
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import ble_client, climate
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT
 | 
			
		||||
from esphome.const import CONF_UNIT_OF_MEASUREMENT
 | 
			
		||||
 | 
			
		||||
UNITS = {
 | 
			
		||||
    "f": "f",
 | 
			
		||||
@@ -17,9 +17,9 @@ Anova = anova_ns.class_(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    climate.CLIMATE_SCHEMA.extend(
 | 
			
		||||
    climate.climate_schema(Anova)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(Anova),
 | 
			
		||||
            cv.Required(CONF_UNIT_OF_MEASUREMENT): cv.enum(UNITS),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
@@ -29,8 +29,7 @@ CONFIG_SCHEMA = (
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    var = await climate.new_climate(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await climate.register_climate(var, config)
 | 
			
		||||
    await ble_client.register_ble_node(var, config)
 | 
			
		||||
    cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ enum {  // APDS9306 registers
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
void APDS9306::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up APDS9306...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
 | 
			
		||||
  uint8_t id;
 | 
			
		||||
  if (!this->read_byte(APDS9306_PART_ID, &id)) {  // Part ID register
 | 
			
		||||
@@ -97,7 +97,7 @@ void APDS9306::dump_config() {
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    switch (this->error_code_) {
 | 
			
		||||
      case COMMUNICATION_FAILED:
 | 
			
		||||
        ESP_LOGE(TAG, "Communication with APDS9306 failed!");
 | 
			
		||||
        ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
        break;
 | 
			
		||||
      case WRONG_ID:
 | 
			
		||||
        ESP_LOGE(TAG, "APDS9306 has invalid id!");
 | 
			
		||||
@@ -108,9 +108,12 @@ void APDS9306::dump_config() {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Gain: %u", AMBIENT_LIGHT_GAIN_VALUES[this->gain_]);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Measurement rate: %u", MEASUREMENT_RATE_VALUES[this->measurement_rate_]);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Measurement Resolution/Bit width: %d", MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Gain: %u\n"
 | 
			
		||||
                "  Measurement rate: %u\n"
 | 
			
		||||
                "  Measurement Resolution/Bit width: %d",
 | 
			
		||||
                AMBIENT_LIGHT_GAIN_VALUES[this->gain_], MEASUREMENT_RATE_VALUES[this->measurement_rate_],
 | 
			
		||||
                MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]);
 | 
			
		||||
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ static const char *const TAG = "apds9960";
 | 
			
		||||
#define APDS9960_WRITE_BYTE(reg, value) APDS9960_ERROR_CHECK(this->write_byte(reg, value));
 | 
			
		||||
 | 
			
		||||
void APDS9960::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up APDS9960...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  uint8_t id;
 | 
			
		||||
  if (!this->read_byte(0x92, &id)) {  // ID register
 | 
			
		||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
			
		||||
@@ -141,7 +141,7 @@ void APDS9960::dump_config() {
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    switch (this->error_code_) {
 | 
			
		||||
      case COMMUNICATION_FAILED:
 | 
			
		||||
        ESP_LOGE(TAG, "Communication with APDS9960 failed!");
 | 
			
		||||
        ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
        break;
 | 
			
		||||
      case WRONG_ID:
 | 
			
		||||
        ESP_LOGE(TAG, "APDS9960 has invalid id!");
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,7 @@ SERVICE_ARG_NATIVE_TYPES = {
 | 
			
		||||
    "string[]": cg.std_vector.template(cg.std_string),
 | 
			
		||||
}
 | 
			
		||||
CONF_ENCRYPTION = "encryption"
 | 
			
		||||
CONF_BATCH_DELAY = "batch_delay"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_encryption_key(value):
 | 
			
		||||
@@ -109,6 +110,9 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            ): ACTIONS_SCHEMA,
 | 
			
		||||
            cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
 | 
			
		||||
            cv.Optional(CONF_ENCRYPTION): _encryption_schema,
 | 
			
		||||
            cv.Optional(
 | 
			
		||||
                CONF_BATCH_DELAY, default="100ms"
 | 
			
		||||
            ): cv.positive_time_period_milliseconds,
 | 
			
		||||
            cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
 | 
			
		||||
                single=True
 | 
			
		||||
            ),
 | 
			
		||||
@@ -129,6 +133,7 @@ async def to_code(config):
 | 
			
		||||
    cg.add(var.set_port(config[CONF_PORT]))
 | 
			
		||||
    cg.add(var.set_password(config[CONF_PASSWORD]))
 | 
			
		||||
    cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
 | 
			
		||||
    cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY]))
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ACTIONS, []):
 | 
			
		||||
        template_args = []
 | 
			
		||||
 
 | 
			
		||||
@@ -432,7 +432,8 @@ message FanCommandRequest {
 | 
			
		||||
enum ColorMode {
 | 
			
		||||
  COLOR_MODE_UNKNOWN = 0;
 | 
			
		||||
  COLOR_MODE_ON_OFF = 1;
 | 
			
		||||
  COLOR_MODE_BRIGHTNESS = 2;
 | 
			
		||||
  COLOR_MODE_LEGACY_BRIGHTNESS = 2;
 | 
			
		||||
  COLOR_MODE_BRIGHTNESS = 3;
 | 
			
		||||
  COLOR_MODE_WHITE = 7;
 | 
			
		||||
  COLOR_MODE_COLOR_TEMPERATURE = 11;
 | 
			
		||||
  COLOR_MODE_COLD_WARM_WHITE = 19;
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -8,54 +8,20 @@
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/entity_base.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
using send_message_t = bool(APIConnection *, void *);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  This class holds a pointer to the source component that wants to publish a message, and a pointer to a function that
 | 
			
		||||
  will lazily publish that message.  The two pointers allow dedup in the deferred queue if multiple publishes for the
 | 
			
		||||
  same component are backed up, and take up only 8 bytes of memory.  The entry in the deferred queue (a std::vector) is
 | 
			
		||||
  the DeferredMessage instance itself (not a pointer to one elsewhere in heap) so still only 8 bytes per entry.  Even
 | 
			
		||||
  100 backed up messages (you'd have to have at least 100 sensors publishing because of dedup) would take up only 0.8
 | 
			
		||||
  kB.
 | 
			
		||||
*/
 | 
			
		||||
class DeferredMessageQueue {
 | 
			
		||||
  struct DeferredMessage {
 | 
			
		||||
    friend class DeferredMessageQueue;
 | 
			
		||||
 | 
			
		||||
   protected:
 | 
			
		||||
    void *source_;
 | 
			
		||||
    send_message_t *send_message_;
 | 
			
		||||
 | 
			
		||||
   public:
 | 
			
		||||
    DeferredMessage(void *source, send_message_t *send_message) : source_(source), send_message_(send_message) {}
 | 
			
		||||
    bool operator==(const DeferredMessage &test) const {
 | 
			
		||||
      return (source_ == test.source_ && send_message_ == test.send_message_);
 | 
			
		||||
    }
 | 
			
		||||
  } __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  // vector is used very specifically for its zero memory overhead even though items are popped from the front (memory
 | 
			
		||||
  // footprint is more important than speed here)
 | 
			
		||||
  std::vector<DeferredMessage> deferred_queue_;
 | 
			
		||||
  APIConnection *api_connection_;
 | 
			
		||||
 | 
			
		||||
  // helper for allowing only unique entries in the queue
 | 
			
		||||
  void dmq_push_back_with_dedup_(void *source, send_message_t *send_message);
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  DeferredMessageQueue(APIConnection *api_connection) : api_connection_(api_connection) {}
 | 
			
		||||
  void process_queue();
 | 
			
		||||
  void defer(void *source, send_message_t *send_message);
 | 
			
		||||
};
 | 
			
		||||
// Keepalive timeout in milliseconds
 | 
			
		||||
static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000;
 | 
			
		||||
 | 
			
		||||
class APIConnection : public APIServerConnection {
 | 
			
		||||
 public:
 | 
			
		||||
  friend class APIServer;
 | 
			
		||||
  APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent);
 | 
			
		||||
  virtual ~APIConnection();
 | 
			
		||||
 | 
			
		||||
@@ -63,149 +29,105 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  void loop();
 | 
			
		||||
 | 
			
		||||
  bool send_list_info_done() {
 | 
			
		||||
    ListEntitiesDoneResponse resp;
 | 
			
		||||
    return this->send_list_entities_done_response(resp);
 | 
			
		||||
    return this->schedule_message_(nullptr, &APIConnection::try_send_list_info_done,
 | 
			
		||||
                                   ListEntitiesDoneResponse::MESSAGE_TYPE);
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state);
 | 
			
		||||
  bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor);
 | 
			
		||||
  void send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor);
 | 
			
		||||
  static bool try_send_binary_sensor_state(APIConnection *api, void *v_binary_sensor);
 | 
			
		||||
  static bool try_send_binary_sensor_state(APIConnection *api, binary_sensor::BinarySensor *binary_sensor, bool state);
 | 
			
		||||
  static bool try_send_binary_sensor_info(APIConnection *api, void *v_binary_sensor);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  bool send_cover_state(cover::Cover *cover);
 | 
			
		||||
  void send_cover_info(cover::Cover *cover);
 | 
			
		||||
  static bool try_send_cover_state(APIConnection *api, void *v_cover);
 | 
			
		||||
  static bool try_send_cover_info(APIConnection *api, void *v_cover);
 | 
			
		||||
  void cover_command(const CoverCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool send_fan_state(fan::Fan *fan);
 | 
			
		||||
  void send_fan_info(fan::Fan *fan);
 | 
			
		||||
  static bool try_send_fan_state(APIConnection *api, void *v_fan);
 | 
			
		||||
  static bool try_send_fan_info(APIConnection *api, void *v_fan);
 | 
			
		||||
  void fan_command(const FanCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  bool send_light_state(light::LightState *light);
 | 
			
		||||
  void send_light_info(light::LightState *light);
 | 
			
		||||
  static bool try_send_light_state(APIConnection *api, void *v_light);
 | 
			
		||||
  static bool try_send_light_info(APIConnection *api, void *v_light);
 | 
			
		||||
  void light_command(const LightCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  bool send_sensor_state(sensor::Sensor *sensor, float state);
 | 
			
		||||
  bool send_sensor_state(sensor::Sensor *sensor);
 | 
			
		||||
  void send_sensor_info(sensor::Sensor *sensor);
 | 
			
		||||
  static bool try_send_sensor_state(APIConnection *api, void *v_sensor);
 | 
			
		||||
  static bool try_send_sensor_state(APIConnection *api, sensor::Sensor *sensor, float state);
 | 
			
		||||
  static bool try_send_sensor_info(APIConnection *api, void *v_sensor);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  bool send_switch_state(switch_::Switch *a_switch, bool state);
 | 
			
		||||
  bool send_switch_state(switch_::Switch *a_switch);
 | 
			
		||||
  void send_switch_info(switch_::Switch *a_switch);
 | 
			
		||||
  static bool try_send_switch_state(APIConnection *api, void *v_a_switch);
 | 
			
		||||
  static bool try_send_switch_state(APIConnection *api, switch_::Switch *a_switch, bool state);
 | 
			
		||||
  static bool try_send_switch_info(APIConnection *api, void *v_a_switch);
 | 
			
		||||
  void switch_command(const SwitchCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state);
 | 
			
		||||
  bool send_text_sensor_state(text_sensor::TextSensor *text_sensor);
 | 
			
		||||
  void send_text_sensor_info(text_sensor::TextSensor *text_sensor);
 | 
			
		||||
  static bool try_send_text_sensor_state(APIConnection *api, void *v_text_sensor);
 | 
			
		||||
  static bool try_send_text_sensor_state(APIConnection *api, text_sensor::TextSensor *text_sensor, std::string state);
 | 
			
		||||
  static bool try_send_text_sensor_info(APIConnection *api, void *v_text_sensor);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  void set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image);
 | 
			
		||||
  void send_camera_info(esp32_camera::ESP32Camera *camera);
 | 
			
		||||
  static bool try_send_camera_info(APIConnection *api, void *v_camera);
 | 
			
		||||
  void camera_image(const CameraImageRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  bool send_climate_state(climate::Climate *climate);
 | 
			
		||||
  void send_climate_info(climate::Climate *climate);
 | 
			
		||||
  static bool try_send_climate_state(APIConnection *api, void *v_climate);
 | 
			
		||||
  static bool try_send_climate_info(APIConnection *api, void *v_climate);
 | 
			
		||||
  void climate_command(const ClimateCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_NUMBER
 | 
			
		||||
  bool send_number_state(number::Number *number, float state);
 | 
			
		||||
  bool send_number_state(number::Number *number);
 | 
			
		||||
  void send_number_info(number::Number *number);
 | 
			
		||||
  static bool try_send_number_state(APIConnection *api, void *v_number);
 | 
			
		||||
  static bool try_send_number_state(APIConnection *api, number::Number *number, float state);
 | 
			
		||||
  static bool try_send_number_info(APIConnection *api, void *v_number);
 | 
			
		||||
  void number_command(const NumberCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATE
 | 
			
		||||
  bool send_date_state(datetime::DateEntity *date);
 | 
			
		||||
  void send_date_info(datetime::DateEntity *date);
 | 
			
		||||
  static bool try_send_date_state(APIConnection *api, void *v_date);
 | 
			
		||||
  static bool try_send_date_info(APIConnection *api, void *v_date);
 | 
			
		||||
  void date_command(const DateCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_TIME
 | 
			
		||||
  bool send_time_state(datetime::TimeEntity *time);
 | 
			
		||||
  void send_time_info(datetime::TimeEntity *time);
 | 
			
		||||
  static bool try_send_time_state(APIConnection *api, void *v_time);
 | 
			
		||||
  static bool try_send_time_info(APIConnection *api, void *v_time);
 | 
			
		||||
  void time_command(const TimeCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
  bool send_datetime_state(datetime::DateTimeEntity *datetime);
 | 
			
		||||
  void send_datetime_info(datetime::DateTimeEntity *datetime);
 | 
			
		||||
  static bool try_send_datetime_state(APIConnection *api, void *v_datetime);
 | 
			
		||||
  static bool try_send_datetime_info(APIConnection *api, void *v_datetime);
 | 
			
		||||
  void datetime_command(const DateTimeCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT
 | 
			
		||||
  bool send_text_state(text::Text *text, std::string state);
 | 
			
		||||
  bool send_text_state(text::Text *text);
 | 
			
		||||
  void send_text_info(text::Text *text);
 | 
			
		||||
  static bool try_send_text_state(APIConnection *api, void *v_text);
 | 
			
		||||
  static bool try_send_text_state(APIConnection *api, text::Text *text, std::string state);
 | 
			
		||||
  static bool try_send_text_info(APIConnection *api, void *v_text);
 | 
			
		||||
  void text_command(const TextCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  bool send_select_state(select::Select *select, std::string state);
 | 
			
		||||
  bool send_select_state(select::Select *select);
 | 
			
		||||
  void send_select_info(select::Select *select);
 | 
			
		||||
  static bool try_send_select_state(APIConnection *api, void *v_select);
 | 
			
		||||
  static bool try_send_select_state(APIConnection *api, select::Select *select, std::string state);
 | 
			
		||||
  static bool try_send_select_info(APIConnection *api, void *v_select);
 | 
			
		||||
  void select_command(const SelectCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  void send_button_info(button::Button *button);
 | 
			
		||||
  static bool try_send_button_info(APIConnection *api, void *v_button);
 | 
			
		||||
  void button_command(const ButtonCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
 | 
			
		||||
  bool send_lock_state(lock::Lock *a_lock);
 | 
			
		||||
  void send_lock_info(lock::Lock *a_lock);
 | 
			
		||||
  static bool try_send_lock_state(APIConnection *api, void *v_a_lock);
 | 
			
		||||
  static bool try_send_lock_state(APIConnection *api, lock::Lock *a_lock, lock::LockState state);
 | 
			
		||||
  static bool try_send_lock_info(APIConnection *api, void *v_a_lock);
 | 
			
		||||
  void lock_command(const LockCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VALVE
 | 
			
		||||
  bool send_valve_state(valve::Valve *valve);
 | 
			
		||||
  void send_valve_info(valve::Valve *valve);
 | 
			
		||||
  static bool try_send_valve_state(APIConnection *api, void *v_valve);
 | 
			
		||||
  static bool try_send_valve_info(APIConnection *api, void *v_valve);
 | 
			
		||||
  void valve_command(const ValveCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  bool send_media_player_state(media_player::MediaPlayer *media_player);
 | 
			
		||||
  void send_media_player_info(media_player::MediaPlayer *media_player);
 | 
			
		||||
  static bool try_send_media_player_state(APIConnection *api, void *v_media_player);
 | 
			
		||||
  static bool try_send_media_player_info(APIConnection *api, void *v_media_player);
 | 
			
		||||
  void media_player_command(const MediaPlayerCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
  bool try_send_log_message(int level, const char *tag, const char *line);
 | 
			
		||||
  void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
 | 
			
		||||
    if (!this->service_call_subscription_)
 | 
			
		||||
      return;
 | 
			
		||||
    this->send_homeassistant_service_response(call);
 | 
			
		||||
    this->send_message(call);
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
 | 
			
		||||
@@ -227,7 +149,7 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
  void send_time_request() {
 | 
			
		||||
    GetTimeRequest req;
 | 
			
		||||
    this->send_get_time_request(req);
 | 
			
		||||
    this->send_message(req);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -246,24 +168,17 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
  bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
 | 
			
		||||
  void send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
 | 
			
		||||
  static bool try_send_alarm_control_panel_state(APIConnection *api, void *v_a_alarm_control_panel);
 | 
			
		||||
  static bool try_send_alarm_control_panel_info(APIConnection *api, void *v_a_alarm_control_panel);
 | 
			
		||||
  void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_EVENT
 | 
			
		||||
  void send_event(event::Event *event, std::string event_type);
 | 
			
		||||
  void send_event(event::Event *event, const std::string &event_type);
 | 
			
		||||
  void send_event_info(event::Event *event);
 | 
			
		||||
  static bool try_send_event(APIConnection *api, void *v_event);
 | 
			
		||||
  static bool try_send_event(APIConnection *api, event::Event *event, std::string event_type);
 | 
			
		||||
  static bool try_send_event_info(APIConnection *api, void *v_event);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  bool send_update_state(update::UpdateEntity *update);
 | 
			
		||||
  void send_update_info(update::UpdateEntity *update);
 | 
			
		||||
  static bool try_send_update_state(APIConnection *api, void *v_update);
 | 
			
		||||
  static bool try_send_update_info(APIConnection *api, void *v_update);
 | 
			
		||||
  void update_command(const UpdateCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -314,18 +229,209 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  void on_no_setup_connection() override;
 | 
			
		||||
  ProtoWriteBuffer create_buffer(uint32_t reserve_size) override {
 | 
			
		||||
    // FIXME: ensure no recursive writes can happen
 | 
			
		||||
    this->proto_write_buffer_.clear();
 | 
			
		||||
    this->proto_write_buffer_.reserve(reserve_size);
 | 
			
		||||
    return {&this->proto_write_buffer_};
 | 
			
		||||
 | 
			
		||||
    // Get header padding size - used for both reserve and insert
 | 
			
		||||
    uint8_t header_padding = this->helper_->frame_header_padding();
 | 
			
		||||
 | 
			
		||||
    // Get shared buffer from parent server
 | 
			
		||||
    std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
 | 
			
		||||
    shared_buf.clear();
 | 
			
		||||
    // Reserve space for header padding + message + footer
 | 
			
		||||
    // - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
 | 
			
		||||
    // - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
 | 
			
		||||
    shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
 | 
			
		||||
    // Insert header padding bytes so message encoding starts at the correct position
 | 
			
		||||
    shared_buf.insert(shared_buf.begin(), header_padding, 0);
 | 
			
		||||
    return {&shared_buf};
 | 
			
		||||
  }
 | 
			
		||||
  bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;
 | 
			
		||||
 | 
			
		||||
  // Prepare buffer for next message in batch
 | 
			
		||||
  ProtoWriteBuffer prepare_message_buffer(uint16_t message_size, bool is_first_message) {
 | 
			
		||||
    // Get reference to shared buffer (it maintains state between batch messages)
 | 
			
		||||
    std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
 | 
			
		||||
    size_t current_size = shared_buf.size();
 | 
			
		||||
 | 
			
		||||
    if (is_first_message) {
 | 
			
		||||
      // For first message, initialize buffer with header padding
 | 
			
		||||
      uint8_t header_padding = this->helper_->frame_header_padding();
 | 
			
		||||
      shared_buf.clear();
 | 
			
		||||
      shared_buf.reserve(message_size + header_padding);
 | 
			
		||||
      shared_buf.resize(header_padding);
 | 
			
		||||
      // Fill header padding with zeros
 | 
			
		||||
      std::fill(shared_buf.begin(), shared_buf.end(), 0);
 | 
			
		||||
    } else {
 | 
			
		||||
      // For subsequent messages, add footer space for previous message and header for this message
 | 
			
		||||
      uint8_t footer_size = this->helper_->frame_footer_size();
 | 
			
		||||
      uint8_t header_padding = this->helper_->frame_header_padding();
 | 
			
		||||
 | 
			
		||||
      // Reserve additional space for everything
 | 
			
		||||
      shared_buf.reserve(current_size + footer_size + header_padding + message_size);
 | 
			
		||||
 | 
			
		||||
      // Single resize to add both footer and header padding
 | 
			
		||||
      size_t new_size = current_size + footer_size + header_padding;
 | 
			
		||||
      shared_buf.resize(new_size);
 | 
			
		||||
 | 
			
		||||
      // Fill the newly added bytes with zeros (footer + header padding)
 | 
			
		||||
      std::fill(shared_buf.begin() + current_size, shared_buf.end(), 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {&shared_buf};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool try_to_clear_buffer(bool log_out_of_space);
 | 
			
		||||
  bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) override;
 | 
			
		||||
 | 
			
		||||
  std::string get_client_combined_info() const { return this->client_combined_info_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  friend APIServer;
 | 
			
		||||
  // Buffer allocator methods for batch processing
 | 
			
		||||
  ProtoWriteBuffer allocate_single_message_buffer(uint16_t size);
 | 
			
		||||
  ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size);
 | 
			
		||||
 | 
			
		||||
  bool send_(const void *buf, size_t len, bool force);
 | 
			
		||||
 protected:
 | 
			
		||||
  // Helper function to fill common entity fields
 | 
			
		||||
  template<typename ResponseT> static void fill_entity_info_base(esphome::EntityBase *entity, ResponseT &response) {
 | 
			
		||||
    // Set common fields that are shared by all entity types
 | 
			
		||||
    response.key = entity->get_object_id_hash();
 | 
			
		||||
    response.object_id = entity->get_object_id();
 | 
			
		||||
 | 
			
		||||
    if (entity->has_own_name())
 | 
			
		||||
      response.name = entity->get_name();
 | 
			
		||||
 | 
			
		||||
    // Set common EntityBase properties
 | 
			
		||||
    response.icon = entity->get_icon();
 | 
			
		||||
    response.disabled_by_default = entity->is_disabled_by_default();
 | 
			
		||||
    response.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Non-template helper to encode any ProtoMessage
 | 
			
		||||
  static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
 | 
			
		||||
                                           uint32_t remaining_size, bool is_single);
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                               bool is_single);
 | 
			
		||||
  static uint16_t try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                              bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  static uint16_t try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                       bool is_single);
 | 
			
		||||
  static uint16_t try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  static uint16_t try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
  static uint16_t try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  static uint16_t try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                       bool is_single);
 | 
			
		||||
  static uint16_t try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  static uint16_t try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                        bool is_single);
 | 
			
		||||
  static uint16_t try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                       bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  static uint16_t try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                        bool is_single);
 | 
			
		||||
  static uint16_t try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                       bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  static uint16_t try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                             bool is_single);
 | 
			
		||||
  static uint16_t try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                            bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  static uint16_t try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                         bool is_single);
 | 
			
		||||
  static uint16_t try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                        bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_NUMBER
 | 
			
		||||
  static uint16_t try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                        bool is_single);
 | 
			
		||||
  static uint16_t try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                       bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATE
 | 
			
		||||
  static uint16_t try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
  static uint16_t try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_TIME
 | 
			
		||||
  static uint16_t try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
  static uint16_t try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
  static uint16_t try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                          bool is_single);
 | 
			
		||||
  static uint16_t try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                         bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT
 | 
			
		||||
  static uint16_t try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
  static uint16_t try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  static uint16_t try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                        bool is_single);
 | 
			
		||||
  static uint16_t try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                       bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  static uint16_t try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                       bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  static uint16_t try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
  static uint16_t try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VALVE
 | 
			
		||||
  static uint16_t try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                       bool is_single);
 | 
			
		||||
  static uint16_t try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  static uint16_t try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                              bool is_single);
 | 
			
		||||
  static uint16_t try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                             bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
  static uint16_t try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                                     bool is_single);
 | 
			
		||||
  static uint16_t try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                                    bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_EVENT
 | 
			
		||||
  static uint16_t try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
 | 
			
		||||
                                          uint32_t remaining_size, bool is_single);
 | 
			
		||||
  static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  static uint16_t try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                        bool is_single);
 | 
			
		||||
  static uint16_t try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                       bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                       bool is_single);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // Method for ListEntitiesDone batching
 | 
			
		||||
  static uint16_t try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                          bool is_single);
 | 
			
		||||
 | 
			
		||||
  // Method for DisconnectRequest batching
 | 
			
		||||
  static uint16_t try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
 | 
			
		||||
                                              bool is_single);
 | 
			
		||||
 | 
			
		||||
  // Helper function to get estimated message size for buffer pre-allocation
 | 
			
		||||
  static uint16_t get_estimated_message_size(uint16_t message_type);
 | 
			
		||||
 | 
			
		||||
  enum class ConnectionState {
 | 
			
		||||
    WAITING_FOR_HELLO,
 | 
			
		||||
@@ -335,9 +441,6 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
 | 
			
		||||
  bool remove_{false};
 | 
			
		||||
 | 
			
		||||
  // Buffer used to encode proto messages
 | 
			
		||||
  // Re-use to prevent allocations
 | 
			
		||||
  std::vector<uint8_t> proto_write_buffer_;
 | 
			
		||||
  std::unique_ptr<APIFrameHelper> helper_;
 | 
			
		||||
 | 
			
		||||
  std::string client_info_;
 | 
			
		||||
@@ -358,10 +461,160 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  bool service_call_subscription_{false};
 | 
			
		||||
  bool next_close_ = false;
 | 
			
		||||
  APIServer *parent_;
 | 
			
		||||
  DeferredMessageQueue deferred_message_queue_;
 | 
			
		||||
  InitialStateIterator initial_state_iterator_;
 | 
			
		||||
  ListEntitiesIterator list_entities_iterator_;
 | 
			
		||||
  int state_subs_at_ = -1;
 | 
			
		||||
 | 
			
		||||
  // Function pointer type for message encoding
 | 
			
		||||
  using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
 | 
			
		||||
 | 
			
		||||
  // Optimized MessageCreator class using union dispatch
 | 
			
		||||
  class MessageCreator {
 | 
			
		||||
   public:
 | 
			
		||||
    // Constructor for function pointer (message_type = 0)
 | 
			
		||||
    MessageCreator(MessageCreatorPtr ptr) : message_type_(0) { data_.ptr = ptr; }
 | 
			
		||||
 | 
			
		||||
    // Constructor for string state capture
 | 
			
		||||
    MessageCreator(const std::string &value, uint16_t msg_type) : message_type_(msg_type) {
 | 
			
		||||
      data_.string_ptr = new std::string(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Destructor
 | 
			
		||||
    ~MessageCreator() {
 | 
			
		||||
      // Clean up string data for string-based message types
 | 
			
		||||
      if (uses_string_data_()) {
 | 
			
		||||
        delete data_.string_ptr;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Copy constructor
 | 
			
		||||
    MessageCreator(const MessageCreator &other) : message_type_(other.message_type_) {
 | 
			
		||||
      if (message_type_ == 0) {
 | 
			
		||||
        data_.ptr = other.data_.ptr;
 | 
			
		||||
      } else if (uses_string_data_()) {
 | 
			
		||||
        data_.string_ptr = new std::string(*other.data_.string_ptr);
 | 
			
		||||
      } else {
 | 
			
		||||
        data_ = other.data_;  // For POD types
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Move constructor
 | 
			
		||||
    MessageCreator(MessageCreator &&other) noexcept : data_(other.data_), message_type_(other.message_type_) {
 | 
			
		||||
      other.message_type_ = 0;  // Reset other to function pointer type
 | 
			
		||||
      other.data_.ptr = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Assignment operators (needed for batch deduplication)
 | 
			
		||||
    MessageCreator &operator=(const MessageCreator &other) {
 | 
			
		||||
      if (this != &other) {
 | 
			
		||||
        // Clean up current string data if needed
 | 
			
		||||
        if (uses_string_data_()) {
 | 
			
		||||
          delete data_.string_ptr;
 | 
			
		||||
        }
 | 
			
		||||
        // Copy new data
 | 
			
		||||
        message_type_ = other.message_type_;
 | 
			
		||||
        if (other.message_type_ == 0) {
 | 
			
		||||
          data_.ptr = other.data_.ptr;
 | 
			
		||||
        } else if (other.uses_string_data_()) {
 | 
			
		||||
          data_.string_ptr = new std::string(*other.data_.string_ptr);
 | 
			
		||||
        } else {
 | 
			
		||||
          data_ = other.data_;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MessageCreator &operator=(MessageCreator &&other) noexcept {
 | 
			
		||||
      if (this != &other) {
 | 
			
		||||
        // Clean up current string data if needed
 | 
			
		||||
        if (uses_string_data_()) {
 | 
			
		||||
          delete data_.string_ptr;
 | 
			
		||||
        }
 | 
			
		||||
        // Move data
 | 
			
		||||
        message_type_ = other.message_type_;
 | 
			
		||||
        data_ = other.data_;
 | 
			
		||||
        // Reset other to safe state
 | 
			
		||||
        other.message_type_ = 0;
 | 
			
		||||
        other.data_.ptr = nullptr;
 | 
			
		||||
      }
 | 
			
		||||
      return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Call operator
 | 
			
		||||
    uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) const;
 | 
			
		||||
 | 
			
		||||
   private:
 | 
			
		||||
    // Helper to check if this message type uses heap-allocated strings
 | 
			
		||||
    bool uses_string_data_() const { return message_type_ == EventResponse::MESSAGE_TYPE; }
 | 
			
		||||
    union CreatorData {
 | 
			
		||||
      MessageCreatorPtr ptr;    // 8 bytes
 | 
			
		||||
      std::string *string_ptr;  // 8 bytes
 | 
			
		||||
    } data_;                    // 8 bytes
 | 
			
		||||
    uint16_t message_type_;     // 2 bytes (0 = function ptr, >0 = state capture)
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Generic batching mechanism for both state updates and entity info
 | 
			
		||||
  struct DeferredBatch {
 | 
			
		||||
    struct BatchItem {
 | 
			
		||||
      EntityBase *entity;      // Entity pointer
 | 
			
		||||
      MessageCreator creator;  // Function that creates the message when needed
 | 
			
		||||
      uint16_t message_type;   // Message type for overhead calculation
 | 
			
		||||
 | 
			
		||||
      // Constructor for creating BatchItem
 | 
			
		||||
      BatchItem(EntityBase *entity, MessageCreator creator, uint16_t message_type)
 | 
			
		||||
          : entity(entity), creator(std::move(creator)), message_type(message_type) {}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::vector<BatchItem> items;
 | 
			
		||||
    uint32_t batch_start_time{0};
 | 
			
		||||
    bool batch_scheduled{false};
 | 
			
		||||
 | 
			
		||||
    DeferredBatch() {
 | 
			
		||||
      // Pre-allocate capacity for typical batch sizes to avoid reallocation
 | 
			
		||||
      items.reserve(8);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Add item to the batch
 | 
			
		||||
    void add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type);
 | 
			
		||||
    void clear() {
 | 
			
		||||
      items.clear();
 | 
			
		||||
      batch_scheduled = false;
 | 
			
		||||
      batch_start_time = 0;
 | 
			
		||||
    }
 | 
			
		||||
    bool empty() const { return items.empty(); }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  DeferredBatch deferred_batch_;
 | 
			
		||||
  uint32_t get_batch_delay_ms_() const;
 | 
			
		||||
  // Message will use 8 more bytes than the minimum size, and typical
 | 
			
		||||
  // MTU is 1500. Sometimes users will see as low as 1460 MTU.
 | 
			
		||||
  // If its IPv6 the header is 40 bytes, and if its IPv4
 | 
			
		||||
  // the header is 20 bytes. So we have 1460 - 40 = 1420 bytes
 | 
			
		||||
  // available for the payload. But we also need to add the size of
 | 
			
		||||
  // the protobuf overhead, which is 8 bytes.
 | 
			
		||||
  //
 | 
			
		||||
  // To be safe we pick 1390 bytes as the maximum size
 | 
			
		||||
  // to send in one go. This is the maximum size of a single packet
 | 
			
		||||
  // that can be sent over the network.
 | 
			
		||||
  // This is to avoid fragmentation of the packet.
 | 
			
		||||
  static constexpr size_t MAX_PACKET_SIZE = 1390;  // MTU
 | 
			
		||||
 | 
			
		||||
  bool schedule_batch_();
 | 
			
		||||
  void process_batch_();
 | 
			
		||||
 | 
			
		||||
  // State for batch buffer allocation
 | 
			
		||||
  bool batch_first_message_{false};
 | 
			
		||||
 | 
			
		||||
  // Helper function to schedule a deferred message with known message type
 | 
			
		||||
  bool schedule_message_(EntityBase *entity, MessageCreator creator, uint16_t message_type) {
 | 
			
		||||
    this->deferred_batch_.add_item(entity, std::move(creator), message_type);
 | 
			
		||||
    return this->schedule_batch_();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Overload for function pointers (for info messages and current state reads)
 | 
			
		||||
  bool schedule_message_(EntityBase *entity, MessageCreatorPtr function_ptr, uint16_t message_type) {
 | 
			
		||||
    return schedule_message_(entity, MessageCreator(function_ptr), message_type);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,19 @@
 | 
			
		||||
#include "api_frame_helper.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "proto.h"
 | 
			
		||||
#include "api_pb2_size.h"
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "api.socket";
 | 
			
		||||
 | 
			
		||||
/// Is the given return value (from write syscalls) a wouldblock error?
 | 
			
		||||
bool is_would_block(ssize_t ret) {
 | 
			
		||||
  if (ret == -1) {
 | 
			
		||||
    return errno == EWOULDBLOCK || errno == EAGAIN;
 | 
			
		||||
  }
 | 
			
		||||
  return ret == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *api_error_to_str(APIError err) {
 | 
			
		||||
  // not using switch to ensure compiler doesn't try to build a big table out of it
 | 
			
		||||
  if (err == APIError::OK) {
 | 
			
		||||
@@ -73,92 +66,154 @@ const char *api_error_to_str(APIError err) {
 | 
			
		||||
  return "UNKNOWN";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Common implementation for writing raw data to socket
 | 
			
		||||
template<typename StateEnum>
 | 
			
		||||
APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket,
 | 
			
		||||
                                    std::vector<uint8_t> &tx_buf, const std::string &info, StateEnum &state,
 | 
			
		||||
                                    StateEnum failed_state) {
 | 
			
		||||
  // This method writes data to socket or buffers it
 | 
			
		||||
// Helper method to buffer data from IOVs
 | 
			
		||||
void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len) {
 | 
			
		||||
  SendBuffer buffer;
 | 
			
		||||
  buffer.data.reserve(total_write_len);
 | 
			
		||||
  for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
    const uint8_t *data = reinterpret_cast<uint8_t *>(iov[i].iov_base);
 | 
			
		||||
    buffer.data.insert(buffer.data.end(), data, data + iov[i].iov_len);
 | 
			
		||||
  }
 | 
			
		||||
  this->tx_buf_.push_back(std::move(buffer));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This method writes data to socket or buffers it
 | 
			
		||||
APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
 | 
			
		||||
  // Returns APIError::OK if successful (or would block, but data has been buffered)
 | 
			
		||||
  // Returns APIError::SOCKET_WRITE_FAILED if socket write failed, and sets state to failed_state
 | 
			
		||||
  // Returns APIError::SOCKET_WRITE_FAILED if socket write failed, and sets state to FAILED
 | 
			
		||||
 | 
			
		||||
  if (iovcnt == 0)
 | 
			
		||||
    return APIError::OK;  // Nothing to do, success
 | 
			
		||||
 | 
			
		||||
  size_t total_write_len = 0;
 | 
			
		||||
  uint16_t total_write_len = 0;
 | 
			
		||||
  for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
#ifdef HELPER_LOG_PACKETS
 | 
			
		||||
    ESP_LOGVV(TAG, "Sending raw: %s",
 | 
			
		||||
              format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
 | 
			
		||||
#endif
 | 
			
		||||
    total_write_len += iov[i].iov_len;
 | 
			
		||||
    total_write_len += static_cast<uint16_t>(iov[i].iov_len);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!tx_buf.empty()) {
 | 
			
		||||
    // try to empty tx_buf first
 | 
			
		||||
    while (!tx_buf.empty()) {
 | 
			
		||||
      ssize_t sent = socket->write(tx_buf.data(), tx_buf.size());
 | 
			
		||||
      if (is_would_block(sent)) {
 | 
			
		||||
        break;
 | 
			
		||||
      } else if (sent == -1) {
 | 
			
		||||
        ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", info.c_str(), errno);
 | 
			
		||||
        state = failed_state;
 | 
			
		||||
        return APIError::SOCKET_WRITE_FAILED;  // Socket write failed
 | 
			
		||||
      }
 | 
			
		||||
      // TODO: inefficient if multiple packets in txbuf
 | 
			
		||||
      // replace with deque of buffers
 | 
			
		||||
      tx_buf.erase(tx_buf.begin(), tx_buf.begin() + sent);
 | 
			
		||||
  // Try to send any existing buffered data first if there is any
 | 
			
		||||
  if (!this->tx_buf_.empty()) {
 | 
			
		||||
    APIError send_result = try_send_tx_buf_();
 | 
			
		||||
    // If real error occurred (not just WOULD_BLOCK), return it
 | 
			
		||||
    if (send_result != APIError::OK && send_result != APIError::WOULD_BLOCK) {
 | 
			
		||||
      return send_result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If there is still data in the buffer, we can't send, buffer
 | 
			
		||||
    // the new data and return
 | 
			
		||||
    if (!this->tx_buf_.empty()) {
 | 
			
		||||
      this->buffer_data_from_iov_(iov, iovcnt, total_write_len);
 | 
			
		||||
      return APIError::OK;  // Success, data buffered
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!tx_buf.empty()) {
 | 
			
		||||
    // tx buf not empty, can't write now because then stream would be inconsistent
 | 
			
		||||
    // Reserve space upfront to avoid multiple reallocations
 | 
			
		||||
    tx_buf.reserve(tx_buf.size() + total_write_len);
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      tx_buf.insert(tx_buf.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
 | 
			
		||||
                    reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
 | 
			
		||||
    }
 | 
			
		||||
    return APIError::OK;  // Success, data buffered
 | 
			
		||||
  }
 | 
			
		||||
  // Try to send directly if no buffered data
 | 
			
		||||
  ssize_t sent = this->socket_->writev(iov, iovcnt);
 | 
			
		||||
 | 
			
		||||
  ssize_t sent = socket->writev(iov, iovcnt);
 | 
			
		||||
  if (is_would_block(sent)) {
 | 
			
		||||
    // operation would block, add buffer to tx_buf
 | 
			
		||||
    // Reserve space upfront to avoid multiple reallocations
 | 
			
		||||
    tx_buf.reserve(tx_buf.size() + total_write_len);
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      tx_buf.insert(tx_buf.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
 | 
			
		||||
                    reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
 | 
			
		||||
  if (sent == -1) {
 | 
			
		||||
    if (errno == EWOULDBLOCK || errno == EAGAIN) {
 | 
			
		||||
      // Socket would block, buffer the data
 | 
			
		||||
      this->buffer_data_from_iov_(iov, iovcnt, total_write_len);
 | 
			
		||||
      return APIError::OK;  // Success, data buffered
 | 
			
		||||
    }
 | 
			
		||||
    return APIError::OK;  // Success, data buffered
 | 
			
		||||
  } else if (sent == -1) {
 | 
			
		||||
    // an error occurred
 | 
			
		||||
    ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", info.c_str(), errno);
 | 
			
		||||
    state = failed_state;
 | 
			
		||||
    // Socket error
 | 
			
		||||
    ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
 | 
			
		||||
    this->state_ = State::FAILED;
 | 
			
		||||
    return APIError::SOCKET_WRITE_FAILED;  // Socket write failed
 | 
			
		||||
  } else if ((size_t) sent != total_write_len) {
 | 
			
		||||
    // partially sent, add end to tx_buf
 | 
			
		||||
    size_t remaining = total_write_len - sent;
 | 
			
		||||
    // Reserve space upfront to avoid multiple reallocations
 | 
			
		||||
    tx_buf.reserve(tx_buf.size() + remaining);
 | 
			
		||||
  } else if (static_cast<uint16_t>(sent) < total_write_len) {
 | 
			
		||||
    // Partially sent, buffer the remaining data
 | 
			
		||||
    SendBuffer buffer;
 | 
			
		||||
    uint16_t to_consume = static_cast<uint16_t>(sent);
 | 
			
		||||
    uint16_t remaining = total_write_len - static_cast<uint16_t>(sent);
 | 
			
		||||
 | 
			
		||||
    buffer.data.reserve(remaining);
 | 
			
		||||
 | 
			
		||||
    size_t to_consume = sent;
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      if (to_consume >= iov[i].iov_len) {
 | 
			
		||||
        to_consume -= iov[i].iov_len;
 | 
			
		||||
        // This segment was fully sent
 | 
			
		||||
        to_consume -= static_cast<uint16_t>(iov[i].iov_len);
 | 
			
		||||
      } else {
 | 
			
		||||
        tx_buf.insert(tx_buf.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
 | 
			
		||||
                      reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
 | 
			
		||||
        // This segment was partially sent or not sent at all
 | 
			
		||||
        const uint8_t *data = reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume;
 | 
			
		||||
        uint16_t len = static_cast<uint16_t>(iov[i].iov_len) - to_consume;
 | 
			
		||||
        buffer.data.insert(buffer.data.end(), data, data + len);
 | 
			
		||||
        to_consume = 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return APIError::OK;  // Success, data buffered
 | 
			
		||||
 | 
			
		||||
    this->tx_buf_.push_back(std::move(buffer));
 | 
			
		||||
  }
 | 
			
		||||
  return APIError::OK;  // Success, all data sent
 | 
			
		||||
 | 
			
		||||
  return APIError::OK;  // Success, all data sent or buffered
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, info_.c_str(), ##__VA_ARGS__)
 | 
			
		||||
// Common implementation for trying to send buffered data
 | 
			
		||||
// IMPORTANT: Caller MUST ensure tx_buf_ is not empty before calling this method
 | 
			
		||||
APIError APIFrameHelper::try_send_tx_buf_() {
 | 
			
		||||
  // Try to send from tx_buf - we assume it's not empty as it's the caller's responsibility to check
 | 
			
		||||
  bool tx_buf_empty = false;
 | 
			
		||||
  while (!tx_buf_empty) {
 | 
			
		||||
    // Get the first buffer in the queue
 | 
			
		||||
    SendBuffer &front_buffer = this->tx_buf_.front();
 | 
			
		||||
 | 
			
		||||
    // Try to send the remaining data in this buffer
 | 
			
		||||
    ssize_t sent = this->socket_->write(front_buffer.current_data(), front_buffer.remaining());
 | 
			
		||||
 | 
			
		||||
    if (sent == -1) {
 | 
			
		||||
      if (errno != EWOULDBLOCK && errno != EAGAIN) {
 | 
			
		||||
        // Real socket error (not just would block)
 | 
			
		||||
        ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
 | 
			
		||||
        this->state_ = State::FAILED;
 | 
			
		||||
        return APIError::SOCKET_WRITE_FAILED;  // Socket write failed
 | 
			
		||||
      }
 | 
			
		||||
      // Socket would block, we'll try again later
 | 
			
		||||
      return APIError::WOULD_BLOCK;
 | 
			
		||||
    } else if (sent == 0) {
 | 
			
		||||
      // Nothing sent but not an error
 | 
			
		||||
      return APIError::WOULD_BLOCK;
 | 
			
		||||
    } else if (static_cast<uint16_t>(sent) < front_buffer.remaining()) {
 | 
			
		||||
      // Partially sent, update offset
 | 
			
		||||
      // Cast to ensure no overflow issues with uint16_t
 | 
			
		||||
      front_buffer.offset += static_cast<uint16_t>(sent);
 | 
			
		||||
      return APIError::WOULD_BLOCK;  // Stop processing more buffers if we couldn't send a complete buffer
 | 
			
		||||
    } else {
 | 
			
		||||
      // Buffer completely sent, remove it from the queue
 | 
			
		||||
      this->tx_buf_.pop_front();
 | 
			
		||||
      // Update empty status for the loop condition
 | 
			
		||||
      tx_buf_empty = this->tx_buf_.empty();
 | 
			
		||||
      // Continue loop to try sending the next buffer
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return APIError::OK;  // All buffers sent successfully
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
APIError APIFrameHelper::init_common_() {
 | 
			
		||||
  if (state_ != State::INITIALIZE || this->socket_ == nullptr) {
 | 
			
		||||
    ESP_LOGVV(TAG, "%s: Bad state for init %d", this->info_.c_str(), (int) state_);
 | 
			
		||||
    return APIError::BAD_STATE;
 | 
			
		||||
  }
 | 
			
		||||
  int err = this->socket_->setblocking(false);
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    ESP_LOGVV(TAG, "%s: Setting nonblocking failed with errno %d", this->info_.c_str(), errno);
 | 
			
		||||
    return APIError::TCP_NONBLOCKING_FAILED;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int enable = 1;
 | 
			
		||||
  err = this->socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    ESP_LOGVV(TAG, "%s: Setting nodelay failed with errno %d", this->info_.c_str(), errno);
 | 
			
		||||
    return APIError::TCP_NODELAY_FAILED;
 | 
			
		||||
  }
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->info_.c_str(), ##__VA_ARGS__)
 | 
			
		||||
// uncomment to log raw packets
 | 
			
		||||
//#define HELPER_LOG_PACKETS
 | 
			
		||||
 | 
			
		||||
@@ -206,23 +261,9 @@ std::string noise_err_to_str(int err) {
 | 
			
		||||
 | 
			
		||||
/// Initialize the frame helper, returns OK if successful.
 | 
			
		||||
APIError APINoiseFrameHelper::init() {
 | 
			
		||||
  if (state_ != State::INITIALIZE || socket_ == nullptr) {
 | 
			
		||||
    HELPER_LOG("Bad state for init %d", (int) state_);
 | 
			
		||||
    return APIError::BAD_STATE;
 | 
			
		||||
  }
 | 
			
		||||
  int err = socket_->setblocking(false);
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    HELPER_LOG("Setting nonblocking failed with errno %d", errno);
 | 
			
		||||
    return APIError::TCP_NONBLOCKING_FAILED;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int enable = 1;
 | 
			
		||||
  err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    HELPER_LOG("Setting nodelay failed with errno %d", errno);
 | 
			
		||||
    return APIError::TCP_NODELAY_FAILED;
 | 
			
		||||
  APIError err = init_common_();
 | 
			
		||||
  if (err != APIError::OK) {
 | 
			
		||||
    return err;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // init prologue
 | 
			
		||||
@@ -234,17 +275,16 @@ APIError APINoiseFrameHelper::init() {
 | 
			
		||||
/// Run through handshake messages (if in that phase)
 | 
			
		||||
APIError APINoiseFrameHelper::loop() {
 | 
			
		||||
  APIError err = state_action_();
 | 
			
		||||
  if (err == APIError::WOULD_BLOCK)
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  if (err != APIError::OK)
 | 
			
		||||
  if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
 | 
			
		||||
    return err;
 | 
			
		||||
  if (!tx_buf_.empty()) {
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->tx_buf_.empty()) {
 | 
			
		||||
    err = try_send_tx_buf_();
 | 
			
		||||
    if (err != APIError::OK) {
 | 
			
		||||
    if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
 | 
			
		||||
      return err;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
  return APIError::OK;  // Convert WOULD_BLOCK to OK to avoid connection termination
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
 | 
			
		||||
@@ -270,8 +310,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
  // read header
 | 
			
		||||
  if (rx_header_buf_len_ < 3) {
 | 
			
		||||
    // no header information yet
 | 
			
		||||
    size_t to_read = 3 - rx_header_buf_len_;
 | 
			
		||||
    ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
 | 
			
		||||
    uint8_t to_read = 3 - rx_header_buf_len_;
 | 
			
		||||
    ssize_t received = this->socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
 | 
			
		||||
    if (received == -1) {
 | 
			
		||||
      if (errno == EWOULDBLOCK || errno == EAGAIN) {
 | 
			
		||||
        return APIError::WOULD_BLOCK;
 | 
			
		||||
@@ -284,8 +324,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
      HELPER_LOG("Connection closed");
 | 
			
		||||
      return APIError::CONNECTION_CLOSED;
 | 
			
		||||
    }
 | 
			
		||||
    rx_header_buf_len_ += received;
 | 
			
		||||
    if ((size_t) received != to_read) {
 | 
			
		||||
    rx_header_buf_len_ += static_cast<uint8_t>(received);
 | 
			
		||||
    if (static_cast<uint8_t>(received) != to_read) {
 | 
			
		||||
      // not a full read
 | 
			
		||||
      return APIError::WOULD_BLOCK;
 | 
			
		||||
    }
 | 
			
		||||
@@ -317,8 +357,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
 | 
			
		||||
  if (rx_buf_len_ < msg_size) {
 | 
			
		||||
    // more data to read
 | 
			
		||||
    size_t to_read = msg_size - rx_buf_len_;
 | 
			
		||||
    ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
 | 
			
		||||
    uint16_t to_read = msg_size - rx_buf_len_;
 | 
			
		||||
    ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
 | 
			
		||||
    if (received == -1) {
 | 
			
		||||
      if (errno == EWOULDBLOCK || errno == EAGAIN) {
 | 
			
		||||
        return APIError::WOULD_BLOCK;
 | 
			
		||||
@@ -331,8 +371,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
      HELPER_LOG("Connection closed");
 | 
			
		||||
      return APIError::CONNECTION_CLOSED;
 | 
			
		||||
    }
 | 
			
		||||
    rx_buf_len_ += received;
 | 
			
		||||
    if ((size_t) received != to_read) {
 | 
			
		||||
    rx_buf_len_ += static_cast<uint16_t>(received);
 | 
			
		||||
    if (static_cast<uint16_t>(received) != to_read) {
 | 
			
		||||
      // not all read
 | 
			
		||||
      return APIError::WOULD_BLOCK;
 | 
			
		||||
    }
 | 
			
		||||
@@ -381,6 +421,8 @@ APIError APINoiseFrameHelper::state_action_() {
 | 
			
		||||
    if (aerr != APIError::OK)
 | 
			
		||||
      return aerr;
 | 
			
		||||
    // ignore contents, may be used in future for flags
 | 
			
		||||
    // Reserve space for: existing prologue + 2 size bytes + frame data
 | 
			
		||||
    prologue_.reserve(prologue_.size() + 2 + frame.msg.size());
 | 
			
		||||
    prologue_.push_back((uint8_t) (frame.msg.size() >> 8));
 | 
			
		||||
    prologue_.push_back((uint8_t) frame.msg.size());
 | 
			
		||||
    prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end());
 | 
			
		||||
@@ -389,16 +431,20 @@ APIError APINoiseFrameHelper::state_action_() {
 | 
			
		||||
  }
 | 
			
		||||
  if (state_ == State::SERVER_HELLO) {
 | 
			
		||||
    // send server hello
 | 
			
		||||
    const std::string &name = App.get_name();
 | 
			
		||||
    const std::string &mac = get_mac_address();
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> msg;
 | 
			
		||||
    // Reserve space for: 1 byte proto + name + null + mac + null
 | 
			
		||||
    msg.reserve(1 + name.size() + 1 + mac.size() + 1);
 | 
			
		||||
 | 
			
		||||
    // chosen proto
 | 
			
		||||
    msg.push_back(0x01);
 | 
			
		||||
 | 
			
		||||
    // node name, terminated by null byte
 | 
			
		||||
    const std::string &name = App.get_name();
 | 
			
		||||
    const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
 | 
			
		||||
    msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
 | 
			
		||||
    // node mac, terminated by null byte
 | 
			
		||||
    const std::string &mac = get_mac_address();
 | 
			
		||||
    const uint8_t *mac_ptr = reinterpret_cast<const uint8_t *>(mac.c_str());
 | 
			
		||||
    msg.insert(msg.end(), mac_ptr, mac_ptr + mac.size() + 1);
 | 
			
		||||
 | 
			
		||||
@@ -493,16 +539,18 @@ void APINoiseFrameHelper::send_explicit_handshake_reject_(const std::string &rea
 | 
			
		||||
  std::vector<uint8_t> data;
 | 
			
		||||
  data.resize(reason.length() + 1);
 | 
			
		||||
  data[0] = 0x01;  // failure
 | 
			
		||||
  for (size_t i = 0; i < reason.length(); i++) {
 | 
			
		||||
    data[i + 1] = (uint8_t) reason[i];
 | 
			
		||||
 | 
			
		||||
  // Copy error message in bulk
 | 
			
		||||
  if (!reason.empty()) {
 | 
			
		||||
    std::memcpy(data.data() + 1, reason.c_str(), reason.length());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // temporarily remove failed state
 | 
			
		||||
  auto orig_state = state_;
 | 
			
		||||
  state_ = State::EXPLICIT_REJECT;
 | 
			
		||||
  write_frame_(data.data(), data.size());
 | 
			
		||||
  state_ = orig_state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
 | 
			
		||||
  int err;
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
@@ -530,7 +578,7 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
 | 
			
		||||
    return APIError::CIPHERSTATE_DECRYPT_FAILED;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  size_t msg_size = mbuf.size;
 | 
			
		||||
  uint16_t msg_size = mbuf.size;
 | 
			
		||||
  uint8_t *msg_data = frame.msg.data();
 | 
			
		||||
  if (msg_size < 4) {
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
@@ -556,11 +604,22 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
 | 
			
		||||
  buffer->type = type;
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
}
 | 
			
		||||
bool APINoiseFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
 | 
			
		||||
APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
 | 
			
		||||
  int err;
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
  aerr = state_action_();
 | 
			
		||||
APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
 | 
			
		||||
  std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
 | 
			
		||||
  uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_);
 | 
			
		||||
 | 
			
		||||
  // Resize to include MAC space (required for Noise encryption)
 | 
			
		||||
  raw_buffer->resize(raw_buffer->size() + frame_footer_size_);
 | 
			
		||||
 | 
			
		||||
  // Use write_protobuf_packets with a single packet
 | 
			
		||||
  std::vector<PacketInfo> packets;
 | 
			
		||||
  packets.emplace_back(type, 0, payload_len);
 | 
			
		||||
 | 
			
		||||
  return write_protobuf_packets(buffer, packets);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) {
 | 
			
		||||
  APIError aerr = state_action_();
 | 
			
		||||
  if (aerr != APIError::OK) {
 | 
			
		||||
    return aerr;
 | 
			
		||||
  }
 | 
			
		||||
@@ -569,70 +628,67 @@ APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload
 | 
			
		||||
    return APIError::WOULD_BLOCK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  size_t padding = 0;
 | 
			
		||||
  size_t msg_len = 4 + payload_len + padding;
 | 
			
		||||
  size_t frame_len = 3 + msg_len + noise_cipherstate_get_mac_length(send_cipher_);
 | 
			
		||||
  auto tmpbuf = std::unique_ptr<uint8_t[]>{new (std::nothrow) uint8_t[frame_len]};
 | 
			
		||||
  if (tmpbuf == nullptr) {
 | 
			
		||||
    HELPER_LOG("Could not allocate for writing packet");
 | 
			
		||||
    return APIError::OUT_OF_MEMORY;
 | 
			
		||||
  if (packets.empty()) {
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  tmpbuf[0] = 0x01;  // indicator
 | 
			
		||||
  // tmpbuf[1], tmpbuf[2] to be set later
 | 
			
		||||
  const uint8_t msg_offset = 3;
 | 
			
		||||
  const uint8_t payload_offset = msg_offset + 4;
 | 
			
		||||
  tmpbuf[msg_offset + 0] = (uint8_t) (type >> 8);  // type
 | 
			
		||||
  tmpbuf[msg_offset + 1] = (uint8_t) type;
 | 
			
		||||
  tmpbuf[msg_offset + 2] = (uint8_t) (payload_len >> 8);  // data_len
 | 
			
		||||
  tmpbuf[msg_offset + 3] = (uint8_t) payload_len;
 | 
			
		||||
  // copy data
 | 
			
		||||
  std::copy(payload, payload + payload_len, &tmpbuf[payload_offset]);
 | 
			
		||||
  // fill padding with zeros
 | 
			
		||||
  std::fill(&tmpbuf[payload_offset + payload_len], &tmpbuf[frame_len], 0);
 | 
			
		||||
  std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
 | 
			
		||||
  this->reusable_iovs_.clear();
 | 
			
		||||
  this->reusable_iovs_.reserve(packets.size());
 | 
			
		||||
 | 
			
		||||
  NoiseBuffer mbuf;
 | 
			
		||||
  noise_buffer_init(mbuf);
 | 
			
		||||
  noise_buffer_set_inout(mbuf, &tmpbuf[msg_offset], msg_len, frame_len - msg_offset);
 | 
			
		||||
  err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
 | 
			
		||||
    return APIError::CIPHERSTATE_ENCRYPT_FAILED;
 | 
			
		||||
  }
 | 
			
		||||
  // We need to encrypt each packet in place
 | 
			
		||||
  for (const auto &packet : packets) {
 | 
			
		||||
    uint16_t type = packet.message_type;
 | 
			
		||||
    uint16_t offset = packet.offset;
 | 
			
		||||
    uint16_t payload_len = packet.payload_size;
 | 
			
		||||
    uint16_t msg_len = 4 + payload_len;  // type(2) + data_len(2) + payload
 | 
			
		||||
 | 
			
		||||
  size_t total_len = 3 + mbuf.size;
 | 
			
		||||
  tmpbuf[1] = (uint8_t) (mbuf.size >> 8);
 | 
			
		||||
  tmpbuf[2] = (uint8_t) mbuf.size;
 | 
			
		||||
    // The buffer already has padding at offset
 | 
			
		||||
    uint8_t *buf_start = raw_buffer->data() + offset;
 | 
			
		||||
 | 
			
		||||
  struct iovec iov;
 | 
			
		||||
  iov.iov_base = &tmpbuf[0];
 | 
			
		||||
  iov.iov_len = total_len;
 | 
			
		||||
    // Write noise header
 | 
			
		||||
    buf_start[0] = 0x01;  // indicator
 | 
			
		||||
    // buf_start[1], buf_start[2] to be set after encryption
 | 
			
		||||
 | 
			
		||||
  // write raw to not have two packets sent if NAGLE disabled
 | 
			
		||||
  return write_raw_(&iov, 1);
 | 
			
		||||
}
 | 
			
		||||
APIError APINoiseFrameHelper::try_send_tx_buf_() {
 | 
			
		||||
  // try send from tx_buf
 | 
			
		||||
  while (state_ != State::CLOSED && !tx_buf_.empty()) {
 | 
			
		||||
    ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
 | 
			
		||||
    if (sent == -1) {
 | 
			
		||||
      if (errno == EWOULDBLOCK || errno == EAGAIN)
 | 
			
		||||
        break;
 | 
			
		||||
    // Write message header (to be encrypted)
 | 
			
		||||
    const uint8_t msg_offset = 3;
 | 
			
		||||
    buf_start[msg_offset + 0] = (uint8_t) (type >> 8);         // type high byte
 | 
			
		||||
    buf_start[msg_offset + 1] = (uint8_t) type;                // type low byte
 | 
			
		||||
    buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8);  // data_len high byte
 | 
			
		||||
    buf_start[msg_offset + 3] = (uint8_t) payload_len;         // data_len low byte
 | 
			
		||||
    // payload data is already in the buffer starting at offset + 7
 | 
			
		||||
 | 
			
		||||
    // Make sure we have space for MAC
 | 
			
		||||
    // The buffer should already have been sized appropriately
 | 
			
		||||
 | 
			
		||||
    // Encrypt the message in place
 | 
			
		||||
    NoiseBuffer mbuf;
 | 
			
		||||
    noise_buffer_init(mbuf);
 | 
			
		||||
    noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_);
 | 
			
		||||
 | 
			
		||||
    int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
 | 
			
		||||
    if (err != 0) {
 | 
			
		||||
      state_ = State::FAILED;
 | 
			
		||||
      HELPER_LOG("Socket write failed with errno %d", errno);
 | 
			
		||||
      return APIError::SOCKET_WRITE_FAILED;
 | 
			
		||||
    } else if (sent == 0) {
 | 
			
		||||
      break;
 | 
			
		||||
      HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
 | 
			
		||||
      return APIError::CIPHERSTATE_ENCRYPT_FAILED;
 | 
			
		||||
    }
 | 
			
		||||
    // TODO: inefficient if multiple packets in txbuf
 | 
			
		||||
    // replace with deque of buffers
 | 
			
		||||
    tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
 | 
			
		||||
 | 
			
		||||
    // Fill in the encrypted size
 | 
			
		||||
    buf_start[1] = (uint8_t) (mbuf.size >> 8);
 | 
			
		||||
    buf_start[2] = (uint8_t) mbuf.size;
 | 
			
		||||
 | 
			
		||||
    // Add iovec for this encrypted packet
 | 
			
		||||
    struct iovec iov;
 | 
			
		||||
    iov.iov_base = buf_start;
 | 
			
		||||
    iov.iov_len = 3 + mbuf.size;  // indicator + size + encrypted data
 | 
			
		||||
    this->reusable_iovs_.push_back(iov);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
  // Send all encrypted packets in one writev call
 | 
			
		||||
  return this->write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
 | 
			
		||||
}
 | 
			
		||||
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
 | 
			
		||||
 | 
			
		||||
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {
 | 
			
		||||
  uint8_t header[3];
 | 
			
		||||
  header[0] = 0x01;  // indicator
 | 
			
		||||
  header[1] = (uint8_t) (len >> 8);
 | 
			
		||||
@@ -642,12 +698,12 @@ APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
 | 
			
		||||
  iov[0].iov_base = header;
 | 
			
		||||
  iov[0].iov_len = 3;
 | 
			
		||||
  if (len == 0) {
 | 
			
		||||
    return write_raw_(iov, 1);
 | 
			
		||||
    return this->write_raw_(iov, 1);
 | 
			
		||||
  }
 | 
			
		||||
  iov[1].iov_base = const_cast<uint8_t *>(data);
 | 
			
		||||
  iov[1].iov_len = len;
 | 
			
		||||
 | 
			
		||||
  return write_raw_(iov, 2);
 | 
			
		||||
  return this->write_raw_(iov, 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Initiate the data structures for the handshake.
 | 
			
		||||
@@ -718,6 +774,8 @@ APIError APINoiseFrameHelper::check_handshake_finished_() {
 | 
			
		||||
    return APIError::HANDSHAKESTATE_SPLIT_FAILED;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  frame_footer_size_ = noise_cipherstate_get_mac_length(send_cipher_);
 | 
			
		||||
 | 
			
		||||
  HELPER_LOG("Handshake complete!");
 | 
			
		||||
  noise_handshakestate_free(handshake_);
 | 
			
		||||
  handshake_ = nullptr;
 | 
			
		||||
@@ -740,58 +798,25 @@ APINoiseFrameHelper::~APINoiseFrameHelper() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
APIError APINoiseFrameHelper::close() {
 | 
			
		||||
  state_ = State::CLOSED;
 | 
			
		||||
  int err = socket_->close();
 | 
			
		||||
  if (err == -1)
 | 
			
		||||
    return APIError::CLOSE_FAILED;
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
}
 | 
			
		||||
APIError APINoiseFrameHelper::shutdown(int how) {
 | 
			
		||||
  int err = socket_->shutdown(how);
 | 
			
		||||
  if (err == -1)
 | 
			
		||||
    return APIError::SHUTDOWN_FAILED;
 | 
			
		||||
  if (how == SHUT_RDWR) {
 | 
			
		||||
    state_ = State::CLOSED;
 | 
			
		||||
  }
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
 | 
			
		||||
void noise_rand_bytes(void *output, size_t len) {
 | 
			
		||||
  if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!");
 | 
			
		||||
    ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting");
 | 
			
		||||
    arch_restart();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Explicit template instantiation for Noise
 | 
			
		||||
template APIError APIFrameHelper::write_raw_<APINoiseFrameHelper::State>(
 | 
			
		||||
    const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf_, const std::string &info,
 | 
			
		||||
    APINoiseFrameHelper::State &state, APINoiseFrameHelper::State failed_state);
 | 
			
		||||
#endif  // USE_API_NOISE
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API_PLAINTEXT
 | 
			
		||||
 | 
			
		||||
/// Initialize the frame helper, returns OK if successful.
 | 
			
		||||
APIError APIPlaintextFrameHelper::init() {
 | 
			
		||||
  if (state_ != State::INITIALIZE || socket_ == nullptr) {
 | 
			
		||||
    HELPER_LOG("Bad state for init %d", (int) state_);
 | 
			
		||||
    return APIError::BAD_STATE;
 | 
			
		||||
  }
 | 
			
		||||
  int err = socket_->setblocking(false);
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    HELPER_LOG("Setting nonblocking failed with errno %d", errno);
 | 
			
		||||
    return APIError::TCP_NONBLOCKING_FAILED;
 | 
			
		||||
  }
 | 
			
		||||
  int enable = 1;
 | 
			
		||||
  err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
 | 
			
		||||
  if (err != 0) {
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    HELPER_LOG("Setting nodelay failed with errno %d", errno);
 | 
			
		||||
    return APIError::TCP_NODELAY_FAILED;
 | 
			
		||||
  APIError err = init_common_();
 | 
			
		||||
  if (err != APIError::OK) {
 | 
			
		||||
    return err;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  state_ = State::DATA;
 | 
			
		||||
@@ -802,14 +827,13 @@ APIError APIPlaintextFrameHelper::loop() {
 | 
			
		||||
  if (state_ != State::DATA) {
 | 
			
		||||
    return APIError::BAD_STATE;
 | 
			
		||||
  }
 | 
			
		||||
  // try send pending TX data
 | 
			
		||||
  if (!tx_buf_.empty()) {
 | 
			
		||||
  if (!this->tx_buf_.empty()) {
 | 
			
		||||
    APIError err = try_send_tx_buf_();
 | 
			
		||||
    if (err != APIError::OK) {
 | 
			
		||||
    if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
 | 
			
		||||
      return err;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
  return APIError::OK;  // Convert WOULD_BLOCK to OK to avoid connection termination
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
 | 
			
		||||
@@ -829,8 +853,15 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
 | 
			
		||||
  // read header
 | 
			
		||||
  while (!rx_header_parsed_) {
 | 
			
		||||
    uint8_t data;
 | 
			
		||||
    ssize_t received = socket_->read(&data, 1);
 | 
			
		||||
    // Now that we know when the socket is ready, we can read up to 3 bytes
 | 
			
		||||
    // into the rx_header_buf_ before we have to switch back to reading
 | 
			
		||||
    // one byte at a time to ensure we don't read past the message and
 | 
			
		||||
    // into the next one.
 | 
			
		||||
 | 
			
		||||
    // Read directly into rx_header_buf_ at the current position
 | 
			
		||||
    // Try to get to at least 3 bytes total (indicator + 2 varint bytes), then read one byte at a time
 | 
			
		||||
    ssize_t received =
 | 
			
		||||
        this->socket_->read(&rx_header_buf_[rx_header_buf_pos_], rx_header_buf_pos_ < 3 ? 3 - rx_header_buf_pos_ : 1);
 | 
			
		||||
    if (received == -1) {
 | 
			
		||||
      if (errno == EWOULDBLOCK || errno == EAGAIN) {
 | 
			
		||||
        return APIError::WOULD_BLOCK;
 | 
			
		||||
@@ -843,32 +874,75 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
      HELPER_LOG("Connection closed");
 | 
			
		||||
      return APIError::CONNECTION_CLOSED;
 | 
			
		||||
    }
 | 
			
		||||
    rx_header_buf_.push_back(data);
 | 
			
		||||
 | 
			
		||||
    // try parse header
 | 
			
		||||
    if (rx_header_buf_[0] != 0x00) {
 | 
			
		||||
      state_ = State::FAILED;
 | 
			
		||||
      HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
 | 
			
		||||
      return APIError::BAD_INDICATOR;
 | 
			
		||||
    // If this was the first read, validate the indicator byte
 | 
			
		||||
    if (rx_header_buf_pos_ == 0 && received > 0) {
 | 
			
		||||
      if (rx_header_buf_[0] != 0x00) {
 | 
			
		||||
        state_ = State::FAILED;
 | 
			
		||||
        HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
 | 
			
		||||
        return APIError::BAD_INDICATOR;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t i = 1;
 | 
			
		||||
    rx_header_buf_pos_ += received;
 | 
			
		||||
 | 
			
		||||
    // Check for buffer overflow
 | 
			
		||||
    if (rx_header_buf_pos_ >= sizeof(rx_header_buf_)) {
 | 
			
		||||
      state_ = State::FAILED;
 | 
			
		||||
      HELPER_LOG("Header buffer overflow");
 | 
			
		||||
      return APIError::BAD_DATA_PACKET;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Need at least 3 bytes total (indicator + 2 varint bytes) before trying to parse
 | 
			
		||||
    if (rx_header_buf_pos_ < 3) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // At this point, we have at least 3 bytes total:
 | 
			
		||||
    //   - Validated indicator byte (0x00) stored at position 0
 | 
			
		||||
    //   - At least 2 bytes in the buffer for the varints
 | 
			
		||||
    // Buffer layout:
 | 
			
		||||
    //   [0]: indicator byte (0x00)
 | 
			
		||||
    //   [1-3]: Message size varint (variable length)
 | 
			
		||||
    //     - 2 bytes would only allow up to 16383, which is less than noise's UINT16_MAX (65535)
 | 
			
		||||
    //     - 3 bytes allows up to 2097151, ensuring we support at least as much as noise
 | 
			
		||||
    //   [2-5]: Message type varint (variable length)
 | 
			
		||||
    // We now attempt to parse both varints. If either is incomplete,
 | 
			
		||||
    // we'll continue reading more bytes.
 | 
			
		||||
 | 
			
		||||
    // Skip indicator byte at position 0
 | 
			
		||||
    uint8_t varint_pos = 1;
 | 
			
		||||
    uint32_t consumed = 0;
 | 
			
		||||
    auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[i], rx_header_buf_.size() - i, &consumed);
 | 
			
		||||
 | 
			
		||||
    auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
 | 
			
		||||
    if (!msg_size_varint.has_value()) {
 | 
			
		||||
      // not enough data there yet
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    i += consumed;
 | 
			
		||||
    rx_header_parsed_len_ = msg_size_varint->as_uint32();
 | 
			
		||||
    if (msg_size_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
 | 
			
		||||
      state_ = State::FAILED;
 | 
			
		||||
      HELPER_LOG("Bad packet: message size %" PRIu32 " exceeds maximum %u", msg_size_varint->as_uint32(),
 | 
			
		||||
                 std::numeric_limits<uint16_t>::max());
 | 
			
		||||
      return APIError::BAD_DATA_PACKET;
 | 
			
		||||
    }
 | 
			
		||||
    rx_header_parsed_len_ = msg_size_varint->as_uint16();
 | 
			
		||||
 | 
			
		||||
    auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[i], rx_header_buf_.size() - i, &consumed);
 | 
			
		||||
    // Move to next varint position
 | 
			
		||||
    varint_pos += consumed;
 | 
			
		||||
 | 
			
		||||
    auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
 | 
			
		||||
    if (!msg_type_varint.has_value()) {
 | 
			
		||||
      // not enough data there yet
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    rx_header_parsed_type_ = msg_type_varint->as_uint32();
 | 
			
		||||
    if (msg_type_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
 | 
			
		||||
      state_ = State::FAILED;
 | 
			
		||||
      HELPER_LOG("Bad packet: message type %" PRIu32 " exceeds maximum %u", msg_type_varint->as_uint32(),
 | 
			
		||||
                 std::numeric_limits<uint16_t>::max());
 | 
			
		||||
      return APIError::BAD_DATA_PACKET;
 | 
			
		||||
    }
 | 
			
		||||
    rx_header_parsed_type_ = msg_type_varint->as_uint16();
 | 
			
		||||
    rx_header_parsed_ = true;
 | 
			
		||||
  }
 | 
			
		||||
  // header reading done
 | 
			
		||||
@@ -880,8 +954,8 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
 | 
			
		||||
  if (rx_buf_len_ < rx_header_parsed_len_) {
 | 
			
		||||
    // more data to read
 | 
			
		||||
    size_t to_read = rx_header_parsed_len_ - rx_buf_len_;
 | 
			
		||||
    ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
 | 
			
		||||
    uint16_t to_read = rx_header_parsed_len_ - rx_buf_len_;
 | 
			
		||||
    ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
 | 
			
		||||
    if (received == -1) {
 | 
			
		||||
      if (errno == EWOULDBLOCK || errno == EAGAIN) {
 | 
			
		||||
        return APIError::WOULD_BLOCK;
 | 
			
		||||
@@ -894,8 +968,8 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
      HELPER_LOG("Connection closed");
 | 
			
		||||
      return APIError::CONNECTION_CLOSED;
 | 
			
		||||
    }
 | 
			
		||||
    rx_buf_len_ += received;
 | 
			
		||||
    if ((size_t) received != to_read) {
 | 
			
		||||
    rx_buf_len_ += static_cast<uint16_t>(received);
 | 
			
		||||
    if (static_cast<uint16_t>(received) != to_read) {
 | 
			
		||||
      // not all read
 | 
			
		||||
      return APIError::WOULD_BLOCK;
 | 
			
		||||
    }
 | 
			
		||||
@@ -909,11 +983,10 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
  // consume msg
 | 
			
		||||
  rx_buf_ = {};
 | 
			
		||||
  rx_buf_len_ = 0;
 | 
			
		||||
  rx_header_buf_.clear();
 | 
			
		||||
  rx_header_buf_pos_ = 0;
 | 
			
		||||
  rx_header_parsed_ = false;
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
 | 
			
		||||
@@ -941,7 +1014,7 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
 | 
			
		||||
                         "Bad indicator byte";
 | 
			
		||||
      iov[0].iov_base = (void *) msg;
 | 
			
		||||
      iov[0].iov_len = 19;
 | 
			
		||||
      write_raw_(iov, 1);
 | 
			
		||||
      this->write_raw_(iov, 1);
 | 
			
		||||
    }
 | 
			
		||||
    return aerr;
 | 
			
		||||
  }
 | 
			
		||||
@@ -952,70 +1025,89 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
 | 
			
		||||
  buffer->type = rx_header_parsed_type_;
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
}
 | 
			
		||||
bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
 | 
			
		||||
APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
 | 
			
		||||
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
 | 
			
		||||
  std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
 | 
			
		||||
  uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_);
 | 
			
		||||
 | 
			
		||||
  // Use write_protobuf_packets with a single packet
 | 
			
		||||
  std::vector<PacketInfo> packets;
 | 
			
		||||
  packets.emplace_back(type, 0, payload_len);
 | 
			
		||||
 | 
			
		||||
  return write_protobuf_packets(buffer, packets);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer,
 | 
			
		||||
                                                         const std::vector<PacketInfo> &packets) {
 | 
			
		||||
  if (state_ != State::DATA) {
 | 
			
		||||
    return APIError::BAD_STATE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> header;
 | 
			
		||||
  header.reserve(1 + api::ProtoSize::varint(static_cast<uint32_t>(payload_len)) +
 | 
			
		||||
                 api::ProtoSize::varint(static_cast<uint32_t>(type)));
 | 
			
		||||
  header.push_back(0x00);
 | 
			
		||||
  ProtoVarInt(payload_len).encode(header);
 | 
			
		||||
  ProtoVarInt(type).encode(header);
 | 
			
		||||
 | 
			
		||||
  struct iovec iov[2];
 | 
			
		||||
  iov[0].iov_base = &header[0];
 | 
			
		||||
  iov[0].iov_len = header.size();
 | 
			
		||||
  if (payload_len == 0) {
 | 
			
		||||
    return write_raw_(iov, 1);
 | 
			
		||||
  }
 | 
			
		||||
  iov[1].iov_base = const_cast<uint8_t *>(payload);
 | 
			
		||||
  iov[1].iov_len = payload_len;
 | 
			
		||||
 | 
			
		||||
  return write_raw_(iov, 2);
 | 
			
		||||
}
 | 
			
		||||
APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
 | 
			
		||||
  // try send from tx_buf
 | 
			
		||||
  while (state_ != State::CLOSED && !tx_buf_.empty()) {
 | 
			
		||||
    ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
 | 
			
		||||
    if (is_would_block(sent)) {
 | 
			
		||||
      break;
 | 
			
		||||
    } else if (sent == -1) {
 | 
			
		||||
      state_ = State::FAILED;
 | 
			
		||||
      HELPER_LOG("Socket write failed with errno %d", errno);
 | 
			
		||||
      return APIError::SOCKET_WRITE_FAILED;
 | 
			
		||||
    }
 | 
			
		||||
    // TODO: inefficient if multiple packets in txbuf
 | 
			
		||||
    // replace with deque of buffers
 | 
			
		||||
    tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
 | 
			
		||||
  if (packets.empty()) {
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
}
 | 
			
		||||
  std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
 | 
			
		||||
  this->reusable_iovs_.clear();
 | 
			
		||||
  this->reusable_iovs_.reserve(packets.size());
 | 
			
		||||
 | 
			
		||||
APIError APIPlaintextFrameHelper::close() {
 | 
			
		||||
  state_ = State::CLOSED;
 | 
			
		||||
  int err = socket_->close();
 | 
			
		||||
  if (err == -1)
 | 
			
		||||
    return APIError::CLOSE_FAILED;
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
}
 | 
			
		||||
APIError APIPlaintextFrameHelper::shutdown(int how) {
 | 
			
		||||
  int err = socket_->shutdown(how);
 | 
			
		||||
  if (err == -1)
 | 
			
		||||
    return APIError::SHUTDOWN_FAILED;
 | 
			
		||||
  if (how == SHUT_RDWR) {
 | 
			
		||||
    state_ = State::CLOSED;
 | 
			
		||||
  for (const auto &packet : packets) {
 | 
			
		||||
    uint16_t type = packet.message_type;
 | 
			
		||||
    uint16_t offset = packet.offset;
 | 
			
		||||
    uint16_t payload_len = packet.payload_size;
 | 
			
		||||
 | 
			
		||||
    // Calculate varint sizes for header layout
 | 
			
		||||
    uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(payload_len));
 | 
			
		||||
    uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(type));
 | 
			
		||||
    uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
 | 
			
		||||
 | 
			
		||||
    // Calculate where to start writing the header
 | 
			
		||||
    // The header starts at the latest possible position to minimize unused padding
 | 
			
		||||
    //
 | 
			
		||||
    // Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3
 | 
			
		||||
    // [0-2]  - Unused padding
 | 
			
		||||
    // [3]    - 0x00 indicator byte
 | 
			
		||||
    // [4]    - Payload size varint (1 byte, for sizes 0-127)
 | 
			
		||||
    // [5]    - Message type varint (1 byte, for types 0-127)
 | 
			
		||||
    // [6...] - Actual payload data
 | 
			
		||||
    //
 | 
			
		||||
    // Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2
 | 
			
		||||
    // [0-1]  - Unused padding
 | 
			
		||||
    // [2]    - 0x00 indicator byte
 | 
			
		||||
    // [3-4]  - Payload size varint (2 bytes, for sizes 128-16383)
 | 
			
		||||
    // [5]    - Message type varint (1 byte, for types 0-127)
 | 
			
		||||
    // [6...] - Actual payload data
 | 
			
		||||
    //
 | 
			
		||||
    // Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0
 | 
			
		||||
    // [0]    - 0x00 indicator byte
 | 
			
		||||
    // [1-3]  - Payload size varint (3 bytes, for sizes 16384-2097151)
 | 
			
		||||
    // [4-5]  - Message type varint (2 bytes, for types 128-32767)
 | 
			
		||||
    // [6...] - Actual payload data
 | 
			
		||||
    //
 | 
			
		||||
    // The message starts at offset + frame_header_padding_
 | 
			
		||||
    // So we write the header starting at offset + frame_header_padding_ - total_header_len
 | 
			
		||||
    uint8_t *buf_start = raw_buffer->data() + offset;
 | 
			
		||||
    uint32_t header_offset = frame_header_padding_ - total_header_len;
 | 
			
		||||
 | 
			
		||||
    // Write the plaintext header
 | 
			
		||||
    buf_start[header_offset] = 0x00;  // indicator
 | 
			
		||||
 | 
			
		||||
    // Encode size varint directly into buffer
 | 
			
		||||
    ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
 | 
			
		||||
 | 
			
		||||
    // Encode type varint directly into buffer
 | 
			
		||||
    ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
 | 
			
		||||
 | 
			
		||||
    // Add iovec for this packet (header + payload)
 | 
			
		||||
    struct iovec iov;
 | 
			
		||||
    iov.iov_base = buf_start + header_offset;
 | 
			
		||||
    iov.iov_len = total_header_len + payload_len;
 | 
			
		||||
    this->reusable_iovs_.push_back(iov);
 | 
			
		||||
  }
 | 
			
		||||
  return APIError::OK;
 | 
			
		||||
 | 
			
		||||
  // Send all packets in one writev call
 | 
			
		||||
  return write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Explicit template instantiation for Plaintext
 | 
			
		||||
template APIError APIFrameHelper::write_raw_<APIPlaintextFrameHelper::State>(
 | 
			
		||||
    const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf_, const std::string &info,
 | 
			
		||||
    APIPlaintextFrameHelper::State &state, APIPlaintextFrameHelper::State failed_state);
 | 
			
		||||
#endif  // USE_API_PLAINTEXT
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <deque>
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -12,22 +13,29 @@
 | 
			
		||||
 | 
			
		||||
#include "api_noise_context.h"
 | 
			
		||||
#include "esphome/components/socket/socket.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class ProtoWriteBuffer;
 | 
			
		||||
 | 
			
		||||
struct ReadPacketBuffer {
 | 
			
		||||
  std::vector<uint8_t> container;
 | 
			
		||||
  uint16_t type;
 | 
			
		||||
  size_t data_offset;
 | 
			
		||||
  size_t data_len;
 | 
			
		||||
  uint16_t data_offset;
 | 
			
		||||
  uint16_t data_len;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct PacketBuffer {
 | 
			
		||||
  const std::vector<uint8_t> container;
 | 
			
		||||
  uint16_t type;
 | 
			
		||||
  uint8_t data_offset;
 | 
			
		||||
  uint8_t data_len;
 | 
			
		||||
// Packed packet info structure to minimize memory usage
 | 
			
		||||
struct PacketInfo {
 | 
			
		||||
  uint16_t message_type;  // 2 bytes
 | 
			
		||||
  uint16_t offset;        // 2 bytes (sufficient for packet size ~1460 bytes)
 | 
			
		||||
  uint16_t payload_size;  // 2 bytes (up to 65535 bytes)
 | 
			
		||||
  uint16_t padding;       // 2 byte (for alignment)
 | 
			
		||||
 | 
			
		||||
  PacketInfo(uint16_t type, uint16_t off, uint16_t size)
 | 
			
		||||
      : message_type(type), offset(off), payload_size(size), padding(0) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class APIError : int {
 | 
			
		||||
@@ -60,71 +68,157 @@ const char *api_error_to_str(APIError err);
 | 
			
		||||
 | 
			
		||||
class APIFrameHelper {
 | 
			
		||||
 public:
 | 
			
		||||
  APIFrameHelper() = default;
 | 
			
		||||
  explicit APIFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_owned_(std::move(socket)) {
 | 
			
		||||
    socket_ = socket_owned_.get();
 | 
			
		||||
  }
 | 
			
		||||
  virtual ~APIFrameHelper() = default;
 | 
			
		||||
  virtual APIError init() = 0;
 | 
			
		||||
  virtual APIError loop() = 0;
 | 
			
		||||
  virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
 | 
			
		||||
  virtual bool can_write_without_blocking() = 0;
 | 
			
		||||
  virtual APIError write_packet(uint16_t type, const uint8_t *data, size_t len) = 0;
 | 
			
		||||
  virtual std::string getpeername() = 0;
 | 
			
		||||
  virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen) = 0;
 | 
			
		||||
  virtual APIError close() = 0;
 | 
			
		||||
  virtual APIError shutdown(int how) = 0;
 | 
			
		||||
  bool can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
 | 
			
		||||
  std::string getpeername() { return socket_->getpeername(); }
 | 
			
		||||
  int getpeername(struct sockaddr *addr, socklen_t *addrlen) { return socket_->getpeername(addr, addrlen); }
 | 
			
		||||
  APIError close() {
 | 
			
		||||
    state_ = State::CLOSED;
 | 
			
		||||
    int err = this->socket_->close();
 | 
			
		||||
    if (err == -1)
 | 
			
		||||
      return APIError::CLOSE_FAILED;
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  }
 | 
			
		||||
  APIError shutdown(int how) {
 | 
			
		||||
    int err = this->socket_->shutdown(how);
 | 
			
		||||
    if (err == -1)
 | 
			
		||||
      return APIError::SHUTDOWN_FAILED;
 | 
			
		||||
    if (how == SHUT_RDWR) {
 | 
			
		||||
      state_ = State::CLOSED;
 | 
			
		||||
    }
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  }
 | 
			
		||||
  // Give this helper a name for logging
 | 
			
		||||
  virtual void set_log_info(std::string info) = 0;
 | 
			
		||||
  void set_log_info(std::string info) { info_ = std::move(info); }
 | 
			
		||||
  virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) = 0;
 | 
			
		||||
  // Write multiple protobuf packets in a single operation
 | 
			
		||||
  // packets contains (message_type, offset, length) for each message in the buffer
 | 
			
		||||
  // The buffer contains all messages with appropriate padding before each
 | 
			
		||||
  virtual APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) = 0;
 | 
			
		||||
  // Get the frame header padding required by this protocol
 | 
			
		||||
  virtual uint8_t frame_header_padding() = 0;
 | 
			
		||||
  // Get the frame footer size required by this protocol
 | 
			
		||||
  virtual uint8_t frame_footer_size() = 0;
 | 
			
		||||
  // Check if socket has data ready to read
 | 
			
		||||
  bool is_socket_ready() const { return socket_ != nullptr && socket_->ready(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  // Struct for holding parsed frame data
 | 
			
		||||
  struct ParsedFrame {
 | 
			
		||||
    std::vector<uint8_t> msg;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Buffer containing data to be sent
 | 
			
		||||
  struct SendBuffer {
 | 
			
		||||
    std::vector<uint8_t> data;
 | 
			
		||||
    uint16_t offset{0};  // Current offset within the buffer (uint16_t to reduce memory usage)
 | 
			
		||||
 | 
			
		||||
    // Using uint16_t reduces memory usage since ESPHome API messages are limited to UINT16_MAX (65535) bytes
 | 
			
		||||
    uint16_t remaining() const { return static_cast<uint16_t>(data.size()) - offset; }
 | 
			
		||||
    const uint8_t *current_data() const { return data.data() + offset; }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Queue of data buffers to be sent
 | 
			
		||||
  std::deque<SendBuffer> tx_buf_;
 | 
			
		||||
 | 
			
		||||
  // Common state enum for all frame helpers
 | 
			
		||||
  // Note: Not all states are used by all implementations
 | 
			
		||||
  // - INITIALIZE: Used by both Noise and Plaintext
 | 
			
		||||
  // - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol
 | 
			
		||||
  // - DATA: Used by both Noise and Plaintext
 | 
			
		||||
  // - CLOSED: Used by both Noise and Plaintext
 | 
			
		||||
  // - FAILED: Used by both Noise and Plaintext
 | 
			
		||||
  // - EXPLICIT_REJECT: Only used by Noise protocol
 | 
			
		||||
  enum class State {
 | 
			
		||||
    INITIALIZE = 1,
 | 
			
		||||
    CLIENT_HELLO = 2,  // Noise only
 | 
			
		||||
    SERVER_HELLO = 3,  // Noise only
 | 
			
		||||
    HANDSHAKE = 4,     // Noise only
 | 
			
		||||
    DATA = 5,
 | 
			
		||||
    CLOSED = 6,
 | 
			
		||||
    FAILED = 7,
 | 
			
		||||
    EXPLICIT_REJECT = 8,  // Noise only
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Current state of the frame helper
 | 
			
		||||
  State state_{State::INITIALIZE};
 | 
			
		||||
 | 
			
		||||
  // Helper name for logging
 | 
			
		||||
  std::string info_;
 | 
			
		||||
 | 
			
		||||
  // Socket for communication
 | 
			
		||||
  socket::Socket *socket_{nullptr};
 | 
			
		||||
  std::unique_ptr<socket::Socket> socket_owned_;
 | 
			
		||||
 | 
			
		||||
  // Common implementation for writing raw data to socket
 | 
			
		||||
  APIError write_raw_(const struct iovec *iov, int iovcnt);
 | 
			
		||||
 | 
			
		||||
  // Try to send data from the tx buffer
 | 
			
		||||
  APIError try_send_tx_buf_();
 | 
			
		||||
 | 
			
		||||
  // Helper method to buffer data from IOVs
 | 
			
		||||
  void buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len);
 | 
			
		||||
  template<typename StateEnum>
 | 
			
		||||
  APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf,
 | 
			
		||||
                      const std::string &info, StateEnum &state, StateEnum failed_state);
 | 
			
		||||
 | 
			
		||||
  uint8_t frame_header_padding_{0};
 | 
			
		||||
  uint8_t frame_footer_size_{0};
 | 
			
		||||
 | 
			
		||||
  // Reusable IOV array for write_protobuf_packets to avoid repeated allocations
 | 
			
		||||
  std::vector<struct iovec> reusable_iovs_;
 | 
			
		||||
 | 
			
		||||
  // Receive buffer for reading frame data
 | 
			
		||||
  std::vector<uint8_t> rx_buf_;
 | 
			
		||||
  uint16_t rx_buf_len_ = 0;
 | 
			
		||||
 | 
			
		||||
  // Common initialization for both plaintext and noise protocols
 | 
			
		||||
  APIError init_common_();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
class APINoiseFrameHelper : public APIFrameHelper {
 | 
			
		||||
 public:
 | 
			
		||||
  APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx)
 | 
			
		||||
      : socket_(std::move(socket)), ctx_(std::move(std::move(ctx))) {}
 | 
			
		||||
      : APIFrameHelper(std::move(socket)), ctx_(std::move(ctx)) {
 | 
			
		||||
    // Noise header structure:
 | 
			
		||||
    // Pos 0: indicator (0x01)
 | 
			
		||||
    // Pos 1-2: encrypted payload size (16-bit big-endian)
 | 
			
		||||
    // Pos 3-6: encrypted type (16-bit) + data_len (16-bit)
 | 
			
		||||
    // Pos 7+: actual payload data
 | 
			
		||||
    frame_header_padding_ = 7;
 | 
			
		||||
  }
 | 
			
		||||
  ~APINoiseFrameHelper() override;
 | 
			
		||||
  APIError init() override;
 | 
			
		||||
  APIError loop() override;
 | 
			
		||||
  APIError read_packet(ReadPacketBuffer *buffer) override;
 | 
			
		||||
  bool can_write_without_blocking() override;
 | 
			
		||||
  APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
 | 
			
		||||
  std::string getpeername() override { return this->socket_->getpeername(); }
 | 
			
		||||
  int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
 | 
			
		||||
    return this->socket_->getpeername(addr, addrlen);
 | 
			
		||||
  }
 | 
			
		||||
  APIError close() override;
 | 
			
		||||
  APIError shutdown(int how) override;
 | 
			
		||||
  // Give this helper a name for logging
 | 
			
		||||
  void set_log_info(std::string info) override { info_ = std::move(info); }
 | 
			
		||||
  APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
 | 
			
		||||
  APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) override;
 | 
			
		||||
  // Get the frame header padding required by this protocol
 | 
			
		||||
  uint8_t frame_header_padding() override { return frame_header_padding_; }
 | 
			
		||||
  // Get the frame footer size required by this protocol
 | 
			
		||||
  uint8_t frame_footer_size() override { return frame_footer_size_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  struct ParsedFrame {
 | 
			
		||||
    std::vector<uint8_t> msg;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  APIError state_action_();
 | 
			
		||||
  APIError try_read_frame_(ParsedFrame *frame);
 | 
			
		||||
  APIError try_send_tx_buf_();
 | 
			
		||||
  APIError write_frame_(const uint8_t *data, size_t len);
 | 
			
		||||
  inline APIError write_raw_(const struct iovec *iov, int iovcnt) {
 | 
			
		||||
    return APIFrameHelper::write_raw_(iov, iovcnt, socket_.get(), tx_buf_, info_, state_, State::FAILED);
 | 
			
		||||
  }
 | 
			
		||||
  APIError write_frame_(const uint8_t *data, uint16_t len);
 | 
			
		||||
  APIError init_handshake_();
 | 
			
		||||
  APIError check_handshake_finished_();
 | 
			
		||||
  void send_explicit_handshake_reject_(const std::string &reason);
 | 
			
		||||
 | 
			
		||||
  std::unique_ptr<socket::Socket> socket_;
 | 
			
		||||
 | 
			
		||||
  std::string info_;
 | 
			
		||||
  // Fixed-size header buffer for noise protocol:
 | 
			
		||||
  // 1 byte for indicator + 2 bytes for message size (16-bit value, not varint)
 | 
			
		||||
  // Note: Maximum message size is UINT16_MAX (65535), with a limit of 128 bytes during handshake phase
 | 
			
		||||
  uint8_t rx_header_buf_[3];
 | 
			
		||||
  size_t rx_header_buf_len_ = 0;
 | 
			
		||||
  std::vector<uint8_t> rx_buf_;
 | 
			
		||||
  size_t rx_buf_len_ = 0;
 | 
			
		||||
  uint8_t rx_header_buf_len_ = 0;
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> tx_buf_;
 | 
			
		||||
  std::vector<uint8_t> prologue_;
 | 
			
		||||
 | 
			
		||||
  std::shared_ptr<APINoiseContext> ctx_;
 | 
			
		||||
@@ -132,69 +226,45 @@ class APINoiseFrameHelper : public APIFrameHelper {
 | 
			
		||||
  NoiseCipherState *send_cipher_{nullptr};
 | 
			
		||||
  NoiseCipherState *recv_cipher_{nullptr};
 | 
			
		||||
  NoiseProtocolId nid_;
 | 
			
		||||
 | 
			
		||||
  enum class State {
 | 
			
		||||
    INITIALIZE = 1,
 | 
			
		||||
    CLIENT_HELLO = 2,
 | 
			
		||||
    SERVER_HELLO = 3,
 | 
			
		||||
    HANDSHAKE = 4,
 | 
			
		||||
    DATA = 5,
 | 
			
		||||
    CLOSED = 6,
 | 
			
		||||
    FAILED = 7,
 | 
			
		||||
    EXPLICIT_REJECT = 8,
 | 
			
		||||
  } state_ = State::INITIALIZE;
 | 
			
		||||
};
 | 
			
		||||
#endif  // USE_API_NOISE
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API_PLAINTEXT
 | 
			
		||||
class APIPlaintextFrameHelper : public APIFrameHelper {
 | 
			
		||||
 public:
 | 
			
		||||
  APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
 | 
			
		||||
  APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : APIFrameHelper(std::move(socket)) {
 | 
			
		||||
    // Plaintext header structure (worst case):
 | 
			
		||||
    // Pos 0: indicator (0x00)
 | 
			
		||||
    // Pos 1-3: payload size varint (up to 3 bytes)
 | 
			
		||||
    // Pos 4-5: message type varint (up to 2 bytes)
 | 
			
		||||
    // Pos 6+: actual payload data
 | 
			
		||||
    frame_header_padding_ = 6;
 | 
			
		||||
  }
 | 
			
		||||
  ~APIPlaintextFrameHelper() override = default;
 | 
			
		||||
  APIError init() override;
 | 
			
		||||
  APIError loop() override;
 | 
			
		||||
  APIError read_packet(ReadPacketBuffer *buffer) override;
 | 
			
		||||
  bool can_write_without_blocking() override;
 | 
			
		||||
  APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
 | 
			
		||||
  std::string getpeername() override { return this->socket_->getpeername(); }
 | 
			
		||||
  int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
 | 
			
		||||
    return this->socket_->getpeername(addr, addrlen);
 | 
			
		||||
  }
 | 
			
		||||
  APIError close() override;
 | 
			
		||||
  APIError shutdown(int how) override;
 | 
			
		||||
  // Give this helper a name for logging
 | 
			
		||||
  void set_log_info(std::string info) override { info_ = std::move(info); }
 | 
			
		||||
  APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
 | 
			
		||||
  APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) override;
 | 
			
		||||
  uint8_t frame_header_padding() override { return frame_header_padding_; }
 | 
			
		||||
  // Get the frame footer size required by this protocol
 | 
			
		||||
  uint8_t frame_footer_size() override { return frame_footer_size_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  struct ParsedFrame {
 | 
			
		||||
    std::vector<uint8_t> msg;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  APIError try_read_frame_(ParsedFrame *frame);
 | 
			
		||||
  APIError try_send_tx_buf_();
 | 
			
		||||
  inline APIError write_raw_(const struct iovec *iov, int iovcnt) {
 | 
			
		||||
    return APIFrameHelper::write_raw_(iov, iovcnt, socket_.get(), tx_buf_, info_, state_, State::FAILED);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::unique_ptr<socket::Socket> socket_;
 | 
			
		||||
 | 
			
		||||
  std::string info_;
 | 
			
		||||
  std::vector<uint8_t> rx_header_buf_;
 | 
			
		||||
  // Fixed-size header buffer for plaintext protocol:
 | 
			
		||||
  // We now store the indicator byte + the two varints.
 | 
			
		||||
  // To match noise protocol's maximum message size (UINT16_MAX = 65535), we need:
 | 
			
		||||
  // 1 byte for indicator + 3 bytes for message size varint (supports up to 2097151) + 2 bytes for message type varint
 | 
			
		||||
  //
 | 
			
		||||
  // While varints could theoretically be up to 10 bytes each for 64-bit values,
 | 
			
		||||
  // attempting to process messages with headers that large would likely crash the
 | 
			
		||||
  // ESP32 due to memory constraints.
 | 
			
		||||
  uint8_t rx_header_buf_[6];  // 1 byte indicator + 5 bytes for varints (3 for size + 2 for type)
 | 
			
		||||
  uint8_t rx_header_buf_pos_ = 0;
 | 
			
		||||
  bool rx_header_parsed_ = false;
 | 
			
		||||
  uint32_t rx_header_parsed_type_ = 0;
 | 
			
		||||
  uint32_t rx_header_parsed_len_ = 0;
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> rx_buf_;
 | 
			
		||||
  size_t rx_buf_len_ = 0;
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> tx_buf_;
 | 
			
		||||
 | 
			
		||||
  enum class State {
 | 
			
		||||
    INITIALIZE = 1,
 | 
			
		||||
    DATA = 2,
 | 
			
		||||
    CLOSED = 3,
 | 
			
		||||
    FAILED = 4,
 | 
			
		||||
  } state_ = State::INITIALIZE;
 | 
			
		||||
  uint16_t rx_header_parsed_type_ = 0;
 | 
			
		||||
  uint16_t rx_header_parsed_len_ = 0;
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -96,6 +96,8 @@ template<> const char *proto_enum_to_string<enums::ColorMode>(enums::ColorMode v
 | 
			
		||||
      return "COLOR_MODE_UNKNOWN";
 | 
			
		||||
    case enums::COLOR_MODE_ON_OFF:
 | 
			
		||||
      return "COLOR_MODE_ON_OFF";
 | 
			
		||||
    case enums::COLOR_MODE_LEGACY_BRIGHTNESS:
 | 
			
		||||
      return "COLOR_MODE_LEGACY_BRIGHTNESS";
 | 
			
		||||
    case enums::COLOR_MODE_BRIGHTNESS:
 | 
			
		||||
      return "COLOR_MODE_BRIGHTNESS";
 | 
			
		||||
    case enums::COLOR_MODE_WHITE:
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -8,688 +8,12 @@ namespace api {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "api.service";
 | 
			
		||||
 | 
			
		||||
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<HelloResponse>(msg, 2);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ConnectResponse>(msg, 4);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<DisconnectRequest>(msg, 5);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<DisconnectResponse>(msg, 6);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<PingRequest>(msg, 7);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<PingResponse>(msg, 8);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<DeviceInfoResponse>(msg, 10);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
 | 
			
		||||
}
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BinarySensorStateResponse>(msg, 21);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<CoverStateResponse>(msg, 22);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesFanResponse>(msg, 14);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<FanStateResponse>(msg, 23);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesLightResponse>(msg, 15);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<LightStateResponse>(msg, 24);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<SensorStateResponse>(msg, 25);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<SwitchStateResponse>(msg, 26);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<TextSensorStateResponse>(msg, 27);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
 | 
			
		||||
  return this->send_message_<SubscribeLogsResponse>(msg, 29);
 | 
			
		||||
}
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
bool APIServerConnectionBase::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_noise_encryption_set_key_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<NoiseEncryptionSetKeyResponse>(msg, 125);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<HomeassistantServiceResponse>(msg, 35);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
 | 
			
		||||
    const SubscribeHomeAssistantStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<GetTimeRequest>(msg, 36);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<GetTimeResponse>(msg, 37);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
 | 
			
		||||
}
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<CameraImageResponse>(msg, 44);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ClimateStateResponse>(msg, 47);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_NUMBER
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesNumberResponse>(msg, 49);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_NUMBER
 | 
			
		||||
bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<NumberStateResponse>(msg, 50);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_NUMBER
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_select_response(const ListEntitiesSelectResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_select_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesSelectResponse>(msg, 52);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
bool APIServerConnectionBase::send_select_state_response(const SelectStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_select_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<SelectStateResponse>(msg, 53);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SIREN
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_siren_response(const ListEntitiesSirenResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_siren_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesSirenResponse>(msg, 55);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SIREN
 | 
			
		||||
bool APIServerConnectionBase::send_siren_state_response(const SirenStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_siren_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<SirenStateResponse>(msg, 56);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SIREN
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_lock_response(const ListEntitiesLockResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_lock_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesLockResponse>(msg, 58);
 | 
			
		||||
void APIServerConnectionBase::log_send_message_(const char *name, const std::string &dump) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_message %s: %s", name, dump.c_str());
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
bool APIServerConnectionBase::send_lock_state_response(const LockStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_lock_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<LockStateResponse>(msg, 59);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_button_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesButtonResponse>(msg, 61);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_media_player_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesMediaPlayerResponse>(msg, 63);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayerStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_media_player_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<MediaPlayerStateResponse>(msg, 64);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothLEAdvertisementResponse>(msg, 67);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_le_raw_advertisements_response(
 | 
			
		||||
    const BluetoothLERawAdvertisementsResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_le_raw_advertisements_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothLERawAdvertisementsResponse>(msg, 93);
 | 
			
		||||
}
 | 
			
		||||
#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
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_device_pairing_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothDevicePairingResponse>(msg, 85);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_device_unpairing_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothDeviceUnpairingResponse>(msg, 86);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_scanner_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothScannerStateResponse>(msg, 126);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantRequest &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_voice_assistant_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<VoiceAssistantRequest>(msg, 90);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAudio &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_voice_assistant_audio: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<VoiceAssistantAudio>(msg, 106);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIServerConnectionBase::send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_voice_assistant_announce_finished: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<VoiceAssistantAnnounceFinished>(msg, 120);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIServerConnectionBase::send_voice_assistant_configuration_response(
 | 
			
		||||
    const VoiceAssistantConfigurationResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_voice_assistant_configuration_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<VoiceAssistantConfigurationResponse>(msg, 122);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
 | 
			
		||||
    const ListEntitiesAlarmControlPanelResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_alarm_control_panel_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesAlarmControlPanelResponse>(msg, 94);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
bool APIServerConnectionBase::send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_alarm_control_panel_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<AlarmControlPanelStateResponse>(msg, 95);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_text_response(const ListEntitiesTextResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_text_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesTextResponse>(msg, 97);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT
 | 
			
		||||
bool APIServerConnectionBase::send_text_state_response(const TextStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_text_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<TextStateResponse>(msg, 98);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATE
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_date_response(const ListEntitiesDateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_date_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesDateResponse>(msg, 100);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATE
 | 
			
		||||
bool APIServerConnectionBase::send_date_state_response(const DateStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_date_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<DateStateResponse>(msg, 101);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATE
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_TIME
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_time_response(const ListEntitiesTimeResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_time_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesTimeResponse>(msg, 103);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_TIME
 | 
			
		||||
bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_time_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<TimeStateResponse>(msg, 104);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_TIME
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_EVENT
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_event_response(const ListEntitiesEventResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_event_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesEventResponse>(msg, 107);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_EVENT
 | 
			
		||||
bool APIServerConnectionBase::send_event_response(const EventResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_event_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<EventResponse>(msg, 108);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VALVE
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_valve_response(const ListEntitiesValveResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_valve_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesValveResponse>(msg, 109);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VALVE
 | 
			
		||||
bool APIServerConnectionBase::send_valve_state_response(const ValveStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_valve_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ValveStateResponse>(msg, 110);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VALVE
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_date_time_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesDateTimeResponse>(msg, 112);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
bool APIServerConnectionBase::send_date_time_state_response(const DateTimeStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_date_time_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<DateTimeStateResponse>(msg, 113);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_update_response(const ListEntitiesUpdateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_update_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesUpdateResponse>(msg, 116);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
bool APIServerConnectionBase::send_update_state_response(const UpdateStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_update_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<UpdateStateResponse>(msg, 117);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
 | 
			
		||||
  switch (msg_type) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
@@ -1273,25 +597,25 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
			
		||||
 | 
			
		||||
void APIServerConnection::on_hello_request(const HelloRequest &msg) {
 | 
			
		||||
  HelloResponse ret = this->hello(msg);
 | 
			
		||||
  if (!this->send_hello_response(ret)) {
 | 
			
		||||
  if (!this->send_message(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_connect_request(const ConnectRequest &msg) {
 | 
			
		||||
  ConnectResponse ret = this->connect(msg);
 | 
			
		||||
  if (!this->send_connect_response(ret)) {
 | 
			
		||||
  if (!this->send_message(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) {
 | 
			
		||||
  DisconnectResponse ret = this->disconnect(msg);
 | 
			
		||||
  if (!this->send_disconnect_response(ret)) {
 | 
			
		||||
  if (!this->send_message(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_ping_request(const PingRequest &msg) {
 | 
			
		||||
  PingResponse ret = this->ping(msg);
 | 
			
		||||
  if (!this->send_ping_response(ret)) {
 | 
			
		||||
  if (!this->send_message(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1301,7 +625,7 @@ void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  DeviceInfoResponse ret = this->device_info(msg);
 | 
			
		||||
  if (!this->send_device_info_response(ret)) {
 | 
			
		||||
  if (!this->send_message(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1367,7 +691,7 @@ void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  GetTimeResponse ret = this->get_time(msg);
 | 
			
		||||
  if (!this->send_get_time_response(ret)) {
 | 
			
		||||
  if (!this->send_message(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1393,7 +717,7 @@ void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncrypt
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
 | 
			
		||||
  if (!this->send_noise_encryption_set_key_response(ret)) {
 | 
			
		||||
  if (!this->send_message(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1749,7 +1073,7 @@ void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
 | 
			
		||||
  if (!this->send_bluetooth_connections_free_response(ret)) {
 | 
			
		||||
  if (!this->send_message(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1805,7 +1129,7 @@ void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAs
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg);
 | 
			
		||||
  if (!this->send_voice_assistant_configuration_response(ret)) {
 | 
			
		||||
  if (!this->send_message(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,162 +10,94 @@ namespace api {
 | 
			
		||||
 | 
			
		||||
class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
 public:
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
 protected:
 | 
			
		||||
  void log_send_message_(const char *name, const std::string &dump);
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  template<typename T> bool send_message(const T &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
    this->log_send_message_(T::message_name(), msg.dump());
 | 
			
		||||
#endif
 | 
			
		||||
    return this->send_message_(msg, T::MESSAGE_TYPE);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  virtual void on_hello_request(const HelloRequest &value){};
 | 
			
		||||
  bool send_hello_response(const HelloResponse &msg);
 | 
			
		||||
 | 
			
		||||
  virtual void on_connect_request(const ConnectRequest &value){};
 | 
			
		||||
  bool send_connect_response(const ConnectResponse &msg);
 | 
			
		||||
  bool send_disconnect_request(const DisconnectRequest &msg);
 | 
			
		||||
 | 
			
		||||
  virtual void on_disconnect_request(const DisconnectRequest &value){};
 | 
			
		||||
  bool send_disconnect_response(const DisconnectResponse &msg);
 | 
			
		||||
  virtual void on_disconnect_response(const DisconnectResponse &value){};
 | 
			
		||||
  bool send_ping_request(const PingRequest &msg);
 | 
			
		||||
  virtual void on_ping_request(const PingRequest &value){};
 | 
			
		||||
  bool send_ping_response(const PingResponse &msg);
 | 
			
		||||
  virtual void on_ping_response(const PingResponse &value){};
 | 
			
		||||
  virtual void on_device_info_request(const DeviceInfoRequest &value){};
 | 
			
		||||
  bool send_device_info_response(const DeviceInfoResponse &msg);
 | 
			
		||||
 | 
			
		||||
  virtual void on_list_entities_request(const ListEntitiesRequest &value){};
 | 
			
		||||
  bool send_list_entities_done_response(const ListEntitiesDoneResponse &msg);
 | 
			
		||||
 | 
			
		||||
  virtual void on_subscribe_states_request(const SubscribeStatesRequest &value){};
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  bool send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  bool send_binary_sensor_state_response(const BinarySensorStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  bool send_list_entities_cover_response(const ListEntitiesCoverResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  bool send_cover_state_response(const CoverStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  virtual void on_cover_command_request(const CoverCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool send_list_entities_fan_response(const ListEntitiesFanResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool send_fan_state_response(const FanStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  virtual void on_fan_command_request(const FanCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  bool send_list_entities_light_response(const ListEntitiesLightResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  bool send_light_state_response(const LightStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  virtual void on_light_command_request(const LightCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  bool send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  bool send_sensor_state_response(const SensorStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  bool send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  bool send_switch_state_response(const SwitchStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  virtual void on_switch_command_request(const SwitchCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  bool send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  bool send_text_sensor_state_response(const TextSensorStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){};
 | 
			
		||||
  bool send_subscribe_logs_response(const SubscribeLogsResponse &msg);
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
  virtual void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
  bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
 | 
			
		||||
  bool send_homeassistant_service_response(const HomeassistantServiceResponse &msg);
 | 
			
		||||
 | 
			
		||||
  virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
 | 
			
		||||
  bool send_subscribe_home_assistant_state_response(const SubscribeHomeAssistantStateResponse &msg);
 | 
			
		||||
 | 
			
		||||
  virtual void on_home_assistant_state_response(const HomeAssistantStateResponse &value){};
 | 
			
		||||
  bool send_get_time_request(const GetTimeRequest &msg);
 | 
			
		||||
  virtual void on_get_time_request(const GetTimeRequest &value){};
 | 
			
		||||
  bool send_get_time_response(const GetTimeResponse &msg);
 | 
			
		||||
  virtual void on_get_time_response(const GetTimeResponse &value){};
 | 
			
		||||
  bool send_list_entities_services_response(const ListEntitiesServicesResponse &msg);
 | 
			
		||||
 | 
			
		||||
  virtual void on_execute_service_request(const ExecuteServiceRequest &value){};
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  bool send_list_entities_camera_response(const ListEntitiesCameraResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  bool send_camera_image_response(const CameraImageResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  virtual void on_camera_image_request(const CameraImageRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  bool send_list_entities_climate_response(const ListEntitiesClimateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  bool send_climate_state_response(const ClimateStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  virtual void on_climate_command_request(const ClimateCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_NUMBER
 | 
			
		||||
  bool send_list_entities_number_response(const ListEntitiesNumberResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_NUMBER
 | 
			
		||||
  bool send_number_state_response(const NumberStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_NUMBER
 | 
			
		||||
  virtual void on_number_command_request(const NumberCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  bool send_list_entities_select_response(const ListEntitiesSelectResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  bool send_select_state_response(const SelectStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  virtual void on_select_command_request(const SelectCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SIREN
 | 
			
		||||
  bool send_list_entities_siren_response(const ListEntitiesSirenResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SIREN
 | 
			
		||||
  bool send_siren_state_response(const SirenStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SIREN
 | 
			
		||||
  virtual void on_siren_command_request(const SirenCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  bool send_list_entities_lock_response(const ListEntitiesLockResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  bool send_lock_state_response(const LockStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
  virtual void on_lock_command_request(const LockCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  virtual void on_button_command_request(const ButtonCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  bool send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  bool send_media_player_state_response(const MediaPlayerStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
@@ -173,33 +105,19 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
  virtual void on_subscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
      const SubscribeBluetoothLEAdvertisementsRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_le_raw_advertisements_response(const BluetoothLERawAdvertisementsResponse &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
 | 
			
		||||
@@ -212,49 +130,23 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#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
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_unsubscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
      const UnsubscribeBluetoothLEAdvertisementsRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool send_voice_assistant_request(const VoiceAssistantRequest &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){};
 | 
			
		||||
#endif
 | 
			
		||||
@@ -262,7 +154,6 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
  virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
 | 
			
		||||
  virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
@@ -271,84 +162,39 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool send_voice_assistant_configuration_response(const VoiceAssistantConfigurationResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
  bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
  bool send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
  virtual void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT
 | 
			
		||||
  bool send_list_entities_text_response(const ListEntitiesTextResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT
 | 
			
		||||
  bool send_text_state_response(const TextStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TEXT
 | 
			
		||||
  virtual void on_text_command_request(const TextCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATE
 | 
			
		||||
  bool send_list_entities_date_response(const ListEntitiesDateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATE
 | 
			
		||||
  bool send_date_state_response(const DateStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_DATETIME_DATE
 | 
			
		||||
  virtual void on_date_command_request(const DateCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_TIME
 | 
			
		||||
  bool send_list_entities_time_response(const ListEntitiesTimeResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_TIME
 | 
			
		||||
  bool send_time_state_response(const TimeStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_DATETIME_TIME
 | 
			
		||||
  virtual void on_time_command_request(const TimeCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_EVENT
 | 
			
		||||
  bool send_list_entities_event_response(const ListEntitiesEventResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_EVENT
 | 
			
		||||
  bool send_event_response(const EventResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VALVE
 | 
			
		||||
  bool send_list_entities_valve_response(const ListEntitiesValveResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VALVE
 | 
			
		||||
  bool send_valve_state_response(const ValveStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VALVE
 | 
			
		||||
  virtual void on_valve_command_request(const ValveCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
  bool send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
  bool send_date_time_state_response(const DateTimeStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
  virtual void on_date_time_command_request(const DateTimeCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  bool send_list_entities_update_response(const ListEntitiesUpdateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  bool send_update_state_response(const UpdateStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  virtual void on_update_command_request(const UpdateCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -24,10 +24,14 @@ static const char *const TAG = "api";
 | 
			
		||||
// APIServer
 | 
			
		||||
APIServer *global_api_server = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 | 
			
		||||
APIServer::APIServer() { global_api_server = this; }
 | 
			
		||||
APIServer::APIServer() {
 | 
			
		||||
  global_api_server = this;
 | 
			
		||||
  // Pre-allocate shared write buffer
 | 
			
		||||
  shared_write_buffer_.reserve(64);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIServer::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  this->setup_controller();
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
@@ -43,7 +47,7 @@ void APIServer::setup() {
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  this->socket_ = socket::socket_ip(SOCK_STREAM, 0);
 | 
			
		||||
  this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0);  // monitored for incoming connections
 | 
			
		||||
  if (this->socket_ == nullptr) {
 | 
			
		||||
    ESP_LOGW(TAG, "Could not create socket");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
@@ -88,6 +92,12 @@ void APIServer::setup() {
 | 
			
		||||
#ifdef USE_LOGGER
 | 
			
		||||
  if (logger::global_logger != nullptr) {
 | 
			
		||||
    logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
 | 
			
		||||
      if (this->shutting_down_) {
 | 
			
		||||
        // Don't try to send logs during shutdown
 | 
			
		||||
        // as it could result in a recursion and
 | 
			
		||||
        // we would be filling a buffer we are trying to clear
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      for (auto &c : this->clients_) {
 | 
			
		||||
        if (!c->remove_)
 | 
			
		||||
          c->try_send_log_message(level, tag, message);
 | 
			
		||||
@@ -112,18 +122,20 @@ void APIServer::setup() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIServer::loop() {
 | 
			
		||||
  // Accept new clients
 | 
			
		||||
  while (true) {
 | 
			
		||||
    struct sockaddr_storage source_addr;
 | 
			
		||||
    socklen_t addr_len = sizeof(source_addr);
 | 
			
		||||
    auto sock = this->socket_->accept((struct sockaddr *) &source_addr, &addr_len);
 | 
			
		||||
    if (!sock)
 | 
			
		||||
      break;
 | 
			
		||||
    ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
 | 
			
		||||
  // Accept new clients only if the socket exists and has incoming connections
 | 
			
		||||
  if (this->socket_ && this->socket_->ready()) {
 | 
			
		||||
    while (true) {
 | 
			
		||||
      struct sockaddr_storage source_addr;
 | 
			
		||||
      socklen_t addr_len = sizeof(source_addr);
 | 
			
		||||
      auto sock = this->socket_->accept_loop_monitored((struct sockaddr *) &source_addr, &addr_len);
 | 
			
		||||
      if (!sock)
 | 
			
		||||
        break;
 | 
			
		||||
      ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
 | 
			
		||||
 | 
			
		||||
    auto *conn = new APIConnection(std::move(sock), this);
 | 
			
		||||
    this->clients_.emplace_back(conn);
 | 
			
		||||
    conn->start();
 | 
			
		||||
      auto *conn = new APIConnection(std::move(sock), this);
 | 
			
		||||
      this->clients_.emplace_back(conn);
 | 
			
		||||
      conn->start();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Process clients and remove disconnected ones in a single pass
 | 
			
		||||
@@ -155,7 +167,7 @@ void APIServer::loop() {
 | 
			
		||||
    const uint32_t now = millis();
 | 
			
		||||
    if (!this->is_connected()) {
 | 
			
		||||
      if (now - this->last_connected_ > this->reboot_timeout_) {
 | 
			
		||||
        ESP_LOGE(TAG, "No client connected to API. Rebooting...");
 | 
			
		||||
        ESP_LOGE(TAG, "No client connected; rebooting");
 | 
			
		||||
        App.reboot();
 | 
			
		||||
      }
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
@@ -167,8 +179,10 @@ void APIServer::loop() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIServer::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "API Server:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Address: %s:%u", network::get_use_address().c_str(), this->port_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "API Server:\n"
 | 
			
		||||
                "  Address: %s:%u",
 | 
			
		||||
                network::get_use_address().c_str(), this->port_);
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Using noise encryption: %s", YESNO(this->noise_ctx_->has_psk()));
 | 
			
		||||
  if (!this->noise_ctx_->has_psk()) {
 | 
			
		||||
@@ -213,11 +227,11 @@ bool APIServer::check_password(const std::string &password) const {
 | 
			
		||||
void APIServer::handle_disconnect(APIConnection *conn) {}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
 | 
			
		||||
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto &c : this->clients_)
 | 
			
		||||
    c->send_binary_sensor_state(obj, state);
 | 
			
		||||
    c->send_binary_sensor_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -253,7 +267,7 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto &c : this->clients_)
 | 
			
		||||
    c->send_sensor_state(obj, state);
 | 
			
		||||
    c->send_sensor_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -262,7 +276,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto &c : this->clients_)
 | 
			
		||||
    c->send_switch_state(obj, state);
 | 
			
		||||
    c->send_switch_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -271,7 +285,7 @@ void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto &c : this->clients_)
 | 
			
		||||
    c->send_text_sensor_state(obj, state);
 | 
			
		||||
    c->send_text_sensor_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -289,7 +303,7 @@ void APIServer::on_number_update(number::Number *obj, float state) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto &c : this->clients_)
 | 
			
		||||
    c->send_number_state(obj, state);
 | 
			
		||||
    c->send_number_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -325,7 +339,7 @@ void APIServer::on_text_update(text::Text *obj, const std::string &state) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto &c : this->clients_)
 | 
			
		||||
    c->send_text_state(obj, state);
 | 
			
		||||
    c->send_text_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -334,7 +348,7 @@ void APIServer::on_select_update(select::Select *obj, const std::string &state,
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto &c : this->clients_)
 | 
			
		||||
    c->send_select_state(obj, state);
 | 
			
		||||
    c->send_select_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -343,7 +357,7 @@ void APIServer::on_lock_update(lock::Lock *obj) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto &c : this->clients_)
 | 
			
		||||
    c->send_lock_state(obj, obj->state);
 | 
			
		||||
    c->send_lock_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -394,6 +408,8 @@ void APIServer::set_port(uint16_t port) { this->port_ = port; }
 | 
			
		||||
 | 
			
		||||
void APIServer::set_password(const std::string &password) { this->password_ = password; }
 | 
			
		||||
 | 
			
		||||
void APIServer::set_batch_delay(uint32_t batch_delay) { this->batch_delay_ = batch_delay; }
 | 
			
		||||
 | 
			
		||||
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_homeassistant_service_call(call);
 | 
			
		||||
@@ -452,7 +468,7 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
 | 
			
		||||
      ESP_LOGW(TAG, "Disconnecting all clients to reset connections");
 | 
			
		||||
      this->set_noise_psk(psk);
 | 
			
		||||
      for (auto &c : this->clients_) {
 | 
			
		||||
        c->send_disconnect_request(DisconnectRequest());
 | 
			
		||||
        c->send_message(DisconnectRequest());
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
@@ -472,10 +488,36 @@ void APIServer::request_time() {
 | 
			
		||||
bool APIServer::is_connected() const { return !this->clients_.empty(); }
 | 
			
		||||
 | 
			
		||||
void APIServer::on_shutdown() {
 | 
			
		||||
  for (auto &c : this->clients_) {
 | 
			
		||||
    c->send_disconnect_request(DisconnectRequest());
 | 
			
		||||
  this->shutting_down_ = true;
 | 
			
		||||
 | 
			
		||||
  // Close the listening socket to prevent new connections
 | 
			
		||||
  if (this->socket_) {
 | 
			
		||||
    this->socket_->close();
 | 
			
		||||
    this->socket_ = nullptr;
 | 
			
		||||
  }
 | 
			
		||||
  delay(10);
 | 
			
		||||
 | 
			
		||||
  // Change batch delay to 5ms for quick flushing during shutdown
 | 
			
		||||
  this->batch_delay_ = 5;
 | 
			
		||||
 | 
			
		||||
  // Send disconnect requests to all connected clients
 | 
			
		||||
  for (auto &c : this->clients_) {
 | 
			
		||||
    if (!c->send_message(DisconnectRequest())) {
 | 
			
		||||
      // If we can't send the disconnect request directly (tx_buffer full),
 | 
			
		||||
      // schedule it in the batch so it will be sent with the 5ms timer
 | 
			
		||||
      c->schedule_message_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool APIServer::teardown() {
 | 
			
		||||
  // If network is disconnected, no point trying to flush buffers
 | 
			
		||||
  if (!network::is_connected()) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  this->loop();
 | 
			
		||||
 | 
			
		||||
  // Return true only when all clients have been torn down
 | 
			
		||||
  return this->clients_.empty();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
 
 | 
			
		||||
@@ -34,11 +34,17 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void on_shutdown() override;
 | 
			
		||||
  bool teardown() override;
 | 
			
		||||
  bool check_password(const std::string &password) const;
 | 
			
		||||
  bool uses_password() const;
 | 
			
		||||
  void set_port(uint16_t port);
 | 
			
		||||
  void set_password(const std::string &password);
 | 
			
		||||
  void set_reboot_timeout(uint32_t reboot_timeout);
 | 
			
		||||
  void set_batch_delay(uint32_t batch_delay);
 | 
			
		||||
  uint32_t get_batch_delay() const { return batch_delay_; }
 | 
			
		||||
 | 
			
		||||
  // Get reference to shared buffer for API connections
 | 
			
		||||
  std::vector<uint8_t> &get_shared_buffer_ref() { return shared_write_buffer_; }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
  bool save_noise_psk(psk_t psk, bool make_active = true);
 | 
			
		||||
@@ -48,7 +54,7 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
 | 
			
		||||
  void handle_disconnect(APIConnection *conn);
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override;
 | 
			
		||||
  void on_binary_sensor_update(binary_sensor::BinarySensor *obj) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  void on_cover_update(cover::Cover *obj) override;
 | 
			
		||||
@@ -136,12 +142,15 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool shutting_down_ = false;
 | 
			
		||||
  std::unique_ptr<socket::Socket> socket_ = nullptr;
 | 
			
		||||
  uint16_t port_{6053};
 | 
			
		||||
  uint32_t reboot_timeout_{300000};
 | 
			
		||||
  uint32_t batch_delay_{100};
 | 
			
		||||
  uint32_t last_connected_{0};
 | 
			
		||||
  std::vector<std::unique_ptr<APIConnection>> clients_;
 | 
			
		||||
  std::string password_;
 | 
			
		||||
  std::vector<uint8_t> shared_write_buffer_;  // Shared proto write buffer for all connections
 | 
			
		||||
  std::vector<HomeAssistantStateSubscription> state_subs_;
 | 
			
		||||
  std::vector<UserServiceDescriptor *> user_services_;
 | 
			
		||||
  Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ from datetime import datetime
 | 
			
		||||
import logging
 | 
			
		||||
from typing import TYPE_CHECKING, Any
 | 
			
		||||
 | 
			
		||||
from aioesphomeapi import APIClient
 | 
			
		||||
from aioesphomeapi import APIClient, parse_log_message
 | 
			
		||||
from aioesphomeapi.log_runner import async_run
 | 
			
		||||
 | 
			
		||||
from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__
 | 
			
		||||
@@ -46,9 +46,10 @@ async def async_run_logs(config: dict[str, Any], address: str) -> None:
 | 
			
		||||
        time_ = datetime.now()
 | 
			
		||||
        message: bytes = msg.message
 | 
			
		||||
        text = message.decode("utf8", "backslashreplace")
 | 
			
		||||
        if dashboard:
 | 
			
		||||
            text = text.replace("\033", "\\033")
 | 
			
		||||
        print(f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]{text}")
 | 
			
		||||
        for parsed_msg in parse_log_message(
 | 
			
		||||
            text, f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]"
 | 
			
		||||
        ):
 | 
			
		||||
            print(parsed_msg.replace("\033", "\\033") if dashboard else parsed_msg)
 | 
			
		||||
 | 
			
		||||
    stop = await async_run(cli, on_log, name=name)
 | 
			
		||||
    try:
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,8 @@
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,7 @@ bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(
 | 
			
		||||
ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {}
 | 
			
		||||
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
 | 
			
		||||
  auto resp = service->encode_list_service_response();
 | 
			
		||||
  return this->client_->send_list_entities_services_response(resp);
 | 
			
		||||
  return this->client_->send_message(resp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#include "proto.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -20,16 +20,26 @@ class ProtoVarInt {
 | 
			
		||||
  explicit ProtoVarInt(uint64_t value) : value_(value) {}
 | 
			
		||||
 | 
			
		||||
  static optional<ProtoVarInt> parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed) {
 | 
			
		||||
    if (consumed != nullptr)
 | 
			
		||||
      *consumed = 0;
 | 
			
		||||
 | 
			
		||||
    if (len == 0)
 | 
			
		||||
    if (len == 0) {
 | 
			
		||||
      if (consumed != nullptr)
 | 
			
		||||
        *consumed = 0;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint64_t result = 0;
 | 
			
		||||
    uint8_t bitpos = 0;
 | 
			
		||||
    // Most common case: single-byte varint (values 0-127)
 | 
			
		||||
    if ((buffer[0] & 0x80) == 0) {
 | 
			
		||||
      if (consumed != nullptr)
 | 
			
		||||
        *consumed = 1;
 | 
			
		||||
      return ProtoVarInt(buffer[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (uint32_t i = 0; i < len; i++) {
 | 
			
		||||
    // General case for multi-byte varints
 | 
			
		||||
    // Since we know buffer[0]'s high bit is set, initialize with its value
 | 
			
		||||
    uint64_t result = buffer[0] & 0x7F;
 | 
			
		||||
    uint8_t bitpos = 7;
 | 
			
		||||
 | 
			
		||||
    // Start from the second byte since we've already processed the first
 | 
			
		||||
    for (uint32_t i = 1; i < len; i++) {
 | 
			
		||||
      uint8_t val = buffer[i];
 | 
			
		||||
      result |= uint64_t(val & 0x7F) << uint64_t(bitpos);
 | 
			
		||||
      bitpos += 7;
 | 
			
		||||
@@ -40,9 +50,12 @@ class ProtoVarInt {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
    if (consumed != nullptr)
 | 
			
		||||
      *consumed = 0;
 | 
			
		||||
    return {};  // Incomplete or invalid varint
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint16_t as_uint16() const { return this->value_; }
 | 
			
		||||
  uint32_t as_uint32() const { return this->value_; }
 | 
			
		||||
  uint64_t as_uint64() const { return this->value_; }
 | 
			
		||||
  bool as_bool() const { return this->value_; }
 | 
			
		||||
@@ -71,6 +84,34 @@ class ProtoVarInt {
 | 
			
		||||
      return static_cast<int64_t>(this->value_ >> 1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  /**
 | 
			
		||||
   * Encode the varint value to a pre-allocated buffer without bounds checking.
 | 
			
		||||
   *
 | 
			
		||||
   * @param buffer The pre-allocated buffer to write the encoded varint to
 | 
			
		||||
   * @param len The size of the buffer in bytes
 | 
			
		||||
   *
 | 
			
		||||
   * @note The caller is responsible for ensuring the buffer is large enough
 | 
			
		||||
   *       to hold the encoded value. Use ProtoSize::varint() to calculate
 | 
			
		||||
   *       the exact size needed before calling this method.
 | 
			
		||||
   * @note No bounds checking is performed for performance reasons.
 | 
			
		||||
   */
 | 
			
		||||
  void encode_to_buffer_unchecked(uint8_t *buffer, size_t len) {
 | 
			
		||||
    uint64_t val = this->value_;
 | 
			
		||||
    if (val <= 0x7F) {
 | 
			
		||||
      buffer[0] = val;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    size_t i = 0;
 | 
			
		||||
    while (val && i < len) {
 | 
			
		||||
      uint8_t temp = val & 0x7F;
 | 
			
		||||
      val >>= 7;
 | 
			
		||||
      if (val) {
 | 
			
		||||
        buffer[i++] = temp | 0x80;
 | 
			
		||||
      } else {
 | 
			
		||||
        buffer[i++] = temp;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  void encode(std::vector<uint8_t> &out) {
 | 
			
		||||
    uint64_t val = this->value_;
 | 
			
		||||
    if (val <= 0x7F) {
 | 
			
		||||
@@ -319,11 +360,11 @@ class ProtoService {
 | 
			
		||||
   * @return A ProtoWriteBuffer object with the reserved size.
 | 
			
		||||
   */
 | 
			
		||||
  virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0;
 | 
			
		||||
  virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0;
 | 
			
		||||
  virtual bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) = 0;
 | 
			
		||||
  virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
 | 
			
		||||
 | 
			
		||||
  // Optimized method that pre-allocates buffer based on message size
 | 
			
		||||
  template<class C> bool send_message_(const C &msg, uint32_t message_type) {
 | 
			
		||||
  bool send_message_(const ProtoMessage &msg, uint16_t message_type) {
 | 
			
		||||
    uint32_t msg_size = 0;
 | 
			
		||||
    msg.calculate_size(msg_size);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ namespace api {
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
 | 
			
		||||
  return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state);
 | 
			
		||||
  return this->client_->send_binary_sensor_state(binary_sensor);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
@@ -21,27 +21,21 @@ bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fa
 | 
			
		||||
bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) {
 | 
			
		||||
  return this->client_->send_sensor_state(sensor, sensor->state);
 | 
			
		||||
}
 | 
			
		||||
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_state(sensor); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
bool InitialStateIterator::on_switch(switch_::Switch *a_switch) {
 | 
			
		||||
  return this->client_->send_switch_state(a_switch, a_switch->state);
 | 
			
		||||
}
 | 
			
		||||
bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_state(a_switch); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
 | 
			
		||||
  return this->client_->send_text_sensor_state(text_sensor, text_sensor->state);
 | 
			
		||||
  return this->client_->send_text_sensor_state(text_sensor);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_NUMBER
 | 
			
		||||
bool InitialStateIterator::on_number(number::Number *number) {
 | 
			
		||||
  return this->client_->send_number_state(number, number->state);
 | 
			
		||||
}
 | 
			
		||||
bool InitialStateIterator::on_number(number::Number *number) { return this->client_->send_number_state(number); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATE
 | 
			
		||||
bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); }
 | 
			
		||||
@@ -55,15 +49,13 @@ bool InitialStateIterator::on_datetime(datetime::DateTimeEntity *datetime) {
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT
 | 
			
		||||
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); }
 | 
			
		||||
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
bool InitialStateIterator::on_select(select::Select *select) {
 | 
			
		||||
  return this->client_->send_select_state(select, select->state);
 | 
			
		||||
}
 | 
			
		||||
bool InitialStateIterator::on_select(select::Select *select) { return this->client_->send_select_state(select); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOCK
 | 
			
		||||
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); }
 | 
			
		||||
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VALVE
 | 
			
		||||
bool InitialStateIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_state(valve); }
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ namespace as3935 {
 | 
			
		||||
static const char *const TAG = "as3935";
 | 
			
		||||
 | 
			
		||||
void AS3935Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up AS3935...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
 | 
			
		||||
  this->irq_pin_->setup();
 | 
			
		||||
  LOG_PIN("  IRQ Pin: ", this->irq_pin_);
 | 
			
		||||
@@ -282,7 +282,7 @@ void AS3935Component::display_oscillator(bool state, uint8_t osc) {
 | 
			
		||||
// based on the resonance frequency of the antenna and so it should be trimmed
 | 
			
		||||
// before the calibration is done.
 | 
			
		||||
bool AS3935Component::calibrate_oscillator() {
 | 
			
		||||
  ESP_LOGI(TAG, "Starting oscillators calibration...");
 | 
			
		||||
  ESP_LOGI(TAG, "Starting oscillators calibration");
 | 
			
		||||
  this->write_register(CALIB_RCO, WIPE_ALL, DIRECT_COMMAND, 0);  // Send command to calibrate the oscillators
 | 
			
		||||
 | 
			
		||||
  this->display_oscillator(true, 2);
 | 
			
		||||
@@ -307,7 +307,7 @@ bool AS3935Component::calibrate_oscillator() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AS3935Component::tune_antenna() {
 | 
			
		||||
  ESP_LOGI(TAG, "Starting antenna tuning...");
 | 
			
		||||
  ESP_LOGI(TAG, "Starting antenna tuning");
 | 
			
		||||
  uint8_t div_ratio = this->read_div_ratio();
 | 
			
		||||
  uint8_t tune_val = this->read_capacitance();
 | 
			
		||||
  ESP_LOGI(TAG, "Division Ratio is set to: %d", div_ratio);
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ static const uint8_t REGISTER_AGC = 0x1A;        // 8 bytes  / R
 | 
			
		||||
static const uint8_t REGISTER_MAGNITUDE = 0x1B;  // 16 bytes / R
 | 
			
		||||
 | 
			
		||||
void AS5600Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up AS5600...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
 | 
			
		||||
  if (!this->read_byte(REGISTER_STATUS).has_value()) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
@@ -91,15 +91,17 @@ void AS5600Component::dump_config() {
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with AS5600 failed!");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Watchdog: %d", this->watchdog_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Fast Filter: %d", this->fast_filter_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Slow Filter: %d", this->slow_filter_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Hysteresis: %d", this->hysteresis_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Start Position: %d", this->start_position_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Watchdog: %d\n"
 | 
			
		||||
                "  Fast Filter: %d\n"
 | 
			
		||||
                "  Slow Filter: %d\n"
 | 
			
		||||
                "  Hysteresis: %d\n"
 | 
			
		||||
                "  Start Position: %d",
 | 
			
		||||
                this->watchdog_, this->fast_filter_, this->slow_filter_, this->hysteresis_, this->start_position_);
 | 
			
		||||
  if (this->end_mode_ == END_MODE_POSITION) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  End Position: %d", this->end_position_);
 | 
			
		||||
  } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ namespace as7341 {
 | 
			
		||||
static const char *const TAG = "as7341";
 | 
			
		||||
 | 
			
		||||
void AS7341Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up AS7341...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
 | 
			
		||||
  // Verify device ID
 | 
			
		||||
@@ -38,12 +38,14 @@ void AS7341Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "AS7341:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with AS7341 failed!");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
  }
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Gain: %u", get_gain());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  ATIME: %u", get_atime());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  ASTEP: %u", get_astep());
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Gain: %u\n"
 | 
			
		||||
                "  ATIME: %u\n"
 | 
			
		||||
                "  ASTEP: %u",
 | 
			
		||||
                get_gain(), get_atime(), get_astep());
 | 
			
		||||
 | 
			
		||||
  LOG_SENSOR("  ", "F1", this->f1_);
 | 
			
		||||
  LOG_SENSOR("  ", "F2", this->f2_);
 | 
			
		||||
 
 | 
			
		||||
@@ -71,19 +71,22 @@ bool AT581XComponent::i2c_read_reg(uint8_t addr, uint8_t &data) {
 | 
			
		||||
  return this->read_register(addr, &data, 1) == esphome::i2c::NO_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AT581XComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up AT581X..."); }
 | 
			
		||||
void AT581XComponent::setup() { ESP_LOGCONFIG(TAG, "Running setup"); }
 | 
			
		||||
void AT581XComponent::dump_config() { LOG_I2C_DEVICE(this); }
 | 
			
		||||
#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
 | 
			
		||||
bool AT581XComponent::i2c_write_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Writing new config for AT581X...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Frequency: %dMHz", this->freq_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Sensing distance: %d", this->delta_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Power: %dµA", this->power_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Gain: %d", this->gain_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Trigger base time: %dms", this->trigger_base_time_ms_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Trigger keep time: %dms", this->trigger_keep_time_ms_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Protect time: %dms", this->protect_time_ms_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Self check time: %dms", this->self_check_time_ms_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "Writing new config for AT581X\n"
 | 
			
		||||
                "Frequency: %dMHz\n"
 | 
			
		||||
                "Sensing distance: %d\n"
 | 
			
		||||
                "Power: %dµA\n"
 | 
			
		||||
                "Gain: %d\n"
 | 
			
		||||
                "Trigger base time: %dms\n"
 | 
			
		||||
                "Trigger keep time: %dms\n"
 | 
			
		||||
                "Protect time: %dms\n"
 | 
			
		||||
                "Self check time: %dms",
 | 
			
		||||
                this->freq_, this->delta_, this->power_, this->gain_, this->trigger_base_time_ms_,
 | 
			
		||||
                this->trigger_keep_time_ms_, this->protect_time_ms_, this->self_check_time_ms_);
 | 
			
		||||
 | 
			
		||||
  // Set frequency point
 | 
			
		||||
  if (!this->i2c_write_reg(FREQ_ADDR, GAIN61_VALUE)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,11 +14,8 @@ namespace esphome {
 | 
			
		||||
namespace at581x {
 | 
			
		||||
 | 
			
		||||
class AT581XComponent : public Component, public i2c::I2CDevice {
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
 protected:
 | 
			
		||||
  switch_::Switch *rf_power_switch_{nullptr};
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  void set_rf_power_switch(switch_::Switch *s) {
 | 
			
		||||
    this->rf_power_switch_ = s;
 | 
			
		||||
    s->turn_on();
 | 
			
		||||
@@ -48,6 +45,9 @@ class AT581XComponent : public Component, public i2c::I2CDevice {
 | 
			
		||||
  bool i2c_read_reg(uint8_t addr, uint8_t &data);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  switch_::Switch *rf_power_switch_{nullptr};
 | 
			
		||||
#endif
 | 
			
		||||
  int freq_;
 | 
			
		||||
  int self_check_time_ms_;   /*!< Power-on self-test time, range: 0 ~ 65536 ms */
 | 
			
		||||
  int protect_time_ms_;      /*!< Protection time, recommended 1000 ms */
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@ void ATM90E26Component::update() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ATM90E26Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ATM90E26 Component...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  this->spi_setup();
 | 
			
		||||
 | 
			
		||||
  uint16_t mmode = 0x422;  // default values for everything but L/N line current gains
 | 
			
		||||
@@ -135,7 +135,7 @@ void ATM90E26Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG("", "ATM90E26:");
 | 
			
		||||
  LOG_PIN("  CS Pin: ", this->cs_);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with ATM90E26 failed!");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
  }
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
  LOG_SENSOR("  ", "Voltage A", this->voltage_sensor_);
 | 
			
		||||
 
 | 
			
		||||
@@ -108,7 +108,7 @@ void ATM90E32Component::update() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ATM90E32Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  this->spi_setup();
 | 
			
		||||
 | 
			
		||||
  uint16_t mmode0 = 0x87;  // 3P4W 50Hz
 | 
			
		||||
@@ -217,7 +217,7 @@ void ATM90E32Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG("", "ATM90E32:");
 | 
			
		||||
  LOG_PIN("  CS Pin: ", this->cs_);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with ATM90E32 failed!");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
  }
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
  LOG_SENSOR("  ", "Voltage A", this->phase_[PHASEA].voltage_sensor_);
 | 
			
		||||
@@ -686,7 +686,7 @@ void ATM90E32Component::restore_power_offset_calibrations_() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ATM90E32Component::clear_gain_calibrations() {
 | 
			
		||||
  ESP_LOGI(TAG, "[CALIBRATION] Clearing stored gain calibrations and restoring config-defined values...");
 | 
			
		||||
  ESP_LOGI(TAG, "[CALIBRATION] Clearing stored gain calibrations and restoring config-defined values");
 | 
			
		||||
 | 
			
		||||
  for (int phase = 0; phase < 3; phase++) {
 | 
			
		||||
    gain_phase_[phase].voltage_gain = this->phase_[phase].voltage_gain_;
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ constexpr static const uint8_t AXS_READ_TOUCHPAD[11] = {0xb5, 0xab, 0xa5, 0x5a,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
void AXS15231Touchscreen::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up AXS15231 Touchscreen...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  if (this->reset_pin_ != nullptr) {
 | 
			
		||||
    this->reset_pin_->setup();
 | 
			
		||||
    this->reset_pin_->digital_write(false);
 | 
			
		||||
@@ -60,8 +60,10 @@ void AXS15231Touchscreen::dump_config() {
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  LOG_PIN("  Interrupt Pin: ", this->interrupt_pin_);
 | 
			
		||||
  LOG_PIN("  Reset Pin: ", this->reset_pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Width: %d", this->x_raw_max_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Height: %d", this->y_raw_max_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Width: %d\n"
 | 
			
		||||
                "  Height: %d",
 | 
			
		||||
                this->x_raw_max_, this->y_raw_max_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace axs15231
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import climate_ir
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["climate_ir"]
 | 
			
		||||
CODEOWNERS = ["@bazuchan"]
 | 
			
		||||
@@ -9,13 +7,8 @@ CODEOWNERS = ["@bazuchan"]
 | 
			
		||||
ballu_ns = cg.esphome_ns.namespace("ballu")
 | 
			
		||||
BalluClimate = ballu_ns.class_("BalluClimate", climate_ir.ClimateIR)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(BalluClimate),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(BalluClimate)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await climate_ir.register_climate_ir(var, config)
 | 
			
		||||
    await climate_ir.new_climate_ir(config)
 | 
			
		||||
 
 | 
			
		||||
@@ -194,11 +194,14 @@ Trigger<> *BangBangClimate::get_heat_trigger() const { return this->heat_trigger
 | 
			
		||||
void BangBangClimate::set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; }
 | 
			
		||||
void BangBangClimate::dump_config() {
 | 
			
		||||
  LOG_CLIMATE("", "Bang Bang Climate", this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Supports HEAT: %s", YESNO(this->supports_heat_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Supports COOL: %s", YESNO(this->supports_cool_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Supports AWAY mode: %s", YESNO(this->supports_away_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Default Target Temperature Low: %.2f°C", this->normal_config_.default_temperature_low);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Default Target Temperature High: %.2f°C", this->normal_config_.default_temperature_high);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Supports HEAT: %s\n"
 | 
			
		||||
                "  Supports COOL: %s\n"
 | 
			
		||||
                "  Supports AWAY mode: %s\n"
 | 
			
		||||
                "  Default Target Temperature Low: %.2f°C\n"
 | 
			
		||||
                "  Default Target Temperature High: %.2f°C",
 | 
			
		||||
                YESNO(this->supports_heat_), YESNO(this->supports_cool_), YESNO(this->supports_away_),
 | 
			
		||||
                this->normal_config_.default_temperature_low, this->normal_config_.default_temperature_high);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig() = default;
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_DEFAULT_TARGET_TEMPERATURE_LOW,
 | 
			
		||||
    CONF_HEAT_ACTION,
 | 
			
		||||
    CONF_HUMIDITY_SENSOR,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_IDLE_ACTION,
 | 
			
		||||
    CONF_SENSOR,
 | 
			
		||||
)
 | 
			
		||||
@@ -19,9 +18,9 @@ BangBangClimate = bang_bang_ns.class_("BangBangClimate", climate.Climate, cg.Com
 | 
			
		||||
BangBangClimateTargetTempConfig = bang_bang_ns.struct("BangBangClimateTargetTempConfig")
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    climate.CLIMATE_SCHEMA.extend(
 | 
			
		||||
    climate.climate_schema(BangBangClimate)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BangBangClimate),
 | 
			
		||||
            cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
 | 
			
		||||
            cv.Optional(CONF_HUMIDITY_SENSOR): cv.use_id(sensor.Sensor),
 | 
			
		||||
            cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
 | 
			
		||||
@@ -36,15 +35,15 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    ).extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
    cv.has_at_least_one_key(CONF_COOL_ACTION, CONF_HEAT_ACTION),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    var = await climate.new_climate(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await climate.register_climate(var, config)
 | 
			
		||||
 | 
			
		||||
    sens = await cg.get_variable(config[CONF_SENSOR])
 | 
			
		||||
    cg.add(var.set_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#include "bedjet_hub.h"
 | 
			
		||||
#include "bedjet_child.h"
 | 
			
		||||
#include "bedjet_const.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -483,9 +484,11 @@ void BedJetHub::loop() {}
 | 
			
		||||
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()->get_conn_id());
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "BedJet Hub '%s'\n"
 | 
			
		||||
                "  ble_client.app_id: %d\n"
 | 
			
		||||
                "  ble_client.conn_id: %d",
 | 
			
		||||
                this->get_name().c_str(), this->parent()->app_id, this->parent()->get_conn_id());
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this)
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Child components (%d):", this->children_.size());
 | 
			
		||||
  for (auto *child : this->children_) {
 | 
			
		||||
@@ -526,7 +529,7 @@ void BedJetHub::dispatch_status_() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this->timeout_ > 0 && diff > this->timeout_ && this->parent()->enabled) {
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] Timed out after %" PRId32 " sec. Retrying...", this->get_name().c_str(), this->timeout_);
 | 
			
		||||
      ESP_LOGW(TAG, "[%s] Timed out after %" PRId32 " sec. Retrying", this->get_name().c_str(), this->timeout_);
 | 
			
		||||
      // set_enabled(false) will only close the connection if state != IDLE.
 | 
			
		||||
      this->parent()->set_state(espbt::ClientState::CONNECTING);
 | 
			
		||||
      this->parent()->set_enabled(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,8 @@
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import ble_client, climate
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_HEAT_MODE,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_RECEIVE_TIMEOUT,
 | 
			
		||||
    CONF_TEMPERATURE_SOURCE,
 | 
			
		||||
    CONF_TIME_ID,
 | 
			
		||||
@@ -13,7 +10,6 @@ from esphome.const import (
 | 
			
		||||
 | 
			
		||||
from .. import BEDJET_CLIENT_SCHEMA, bedjet_ns, register_bedjet_child
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
CODEOWNERS = ["@jhansche"]
 | 
			
		||||
DEPENDENCIES = ["bedjet"]
 | 
			
		||||
 | 
			
		||||
@@ -30,9 +26,9 @@ BEDJET_TEMPERATURE_SOURCES = {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    climate.CLIMATE_SCHEMA.extend(
 | 
			
		||||
    climate.climate_schema(BedJetClimate)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BedJetClimate),
 | 
			
		||||
            cv.Optional(CONF_HEAT_MODE, default="heat"): cv.enum(
 | 
			
		||||
                BEDJET_HEAT_MODES, lower=True
 | 
			
		||||
            ),
 | 
			
		||||
@@ -63,9 +59,8 @@ CONFIG_SCHEMA = (
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    var = await climate.new_climate(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await climate.register_climate(var, config)
 | 
			
		||||
    await register_bedjet_child(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_heating_mode(config[CONF_HEAT_MODE]))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +1,22 @@
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import fan
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
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),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    fan.fan_schema(BedJetFan)
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(BEDJET_CLIENT_SCHEMA)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    var = await fan.new_fan(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await fan.register_fan(var, config)
 | 
			
		||||
    await register_bedjet_child(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -119,7 +119,7 @@ void spi_dma_tx_finish_callback(unsigned int param) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BekenSPILEDStripLightOutput::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Beken SPI LED Strip...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
 | 
			
		||||
  size_t buffer_size = this->get_buffer_size_();
 | 
			
		||||
  size_t dma_buffer_size = (buffer_size * 8) + (2 * 64);
 | 
			
		||||
@@ -256,7 +256,7 @@ void BekenSPILEDStripLightOutput::write_state(light::LightState *state) {
 | 
			
		||||
  this->last_refresh_ = now;
 | 
			
		||||
  this->mark_shown_();
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, "Writing RGB values to bus...");
 | 
			
		||||
  ESP_LOGVV(TAG, "Writing RGB values to bus");
 | 
			
		||||
 | 
			
		||||
  if (spi_data == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "SPI not initialized");
 | 
			
		||||
@@ -345,8 +345,10 @@ light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BekenSPILEDStripLightOutput::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Pin: %u", this->pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "Beken SPI LED Strip:\n"
 | 
			
		||||
                "  Pin: %u",
 | 
			
		||||
                this->pin_);
 | 
			
		||||
  const char *rgb_order;
 | 
			
		||||
  switch (this->rgb_order_) {
 | 
			
		||||
    case ORDER_RGB:
 | 
			
		||||
@@ -371,9 +373,11 @@ void BekenSPILEDStripLightOutput::dump_config() {
 | 
			
		||||
      rgb_order = "UNKNOWN";
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  RGB Order: %s", rgb_order);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Max refresh rate: %" PRIu32, *this->max_refresh_rate_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Number of LEDs: %u", this->num_leds_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  RGB Order: %s\n"
 | 
			
		||||
                "  Max refresh rate: %" PRIu32 "\n"
 | 
			
		||||
                "  Number of LEDs: %u",
 | 
			
		||||
                rgb_order, *this->max_refresh_rate_, this->num_leds_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ MTreg:
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
void BH1750Sensor::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up BH1750 '%s'...", this->name_.c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str());
 | 
			
		||||
  uint8_t turn_on = BH1750_COMMAND_POWER_ON;
 | 
			
		||||
  if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
@@ -118,7 +118,7 @@ void BH1750Sensor::dump_config() {
 | 
			
		||||
  LOG_SENSOR("", "BH1750", this);
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with BH1750 failed!");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, this->get_name().c_str());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +1,28 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import fan, output
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_DIRECTION_OUTPUT,
 | 
			
		||||
    CONF_OSCILLATION_OUTPUT,
 | 
			
		||||
    CONF_OUTPUT,
 | 
			
		||||
    CONF_OUTPUT_ID,
 | 
			
		||||
)
 | 
			
		||||
from esphome.const import CONF_DIRECTION_OUTPUT, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT
 | 
			
		||||
 | 
			
		||||
from .. import binary_ns
 | 
			
		||||
 | 
			
		||||
BinaryFan = binary_ns.class_("BinaryFan", fan.Fan, cg.Component)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryFan),
 | 
			
		||||
        cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
 | 
			
		||||
        cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
 | 
			
		||||
        cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    fan.fan_schema(BinaryFan)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
 | 
			
		||||
            cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
 | 
			
		||||
            cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
 | 
			
		||||
    var = await fan.new_fan(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await fan.register_fan(var, config)
 | 
			
		||||
 | 
			
		||||
    output_ = await cg.get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    cg.add(var.set_output(output_))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,10 @@
 | 
			
		||||
from logging import getLogger
 | 
			
		||||
 | 
			
		||||
from esphome import automation, core
 | 
			
		||||
from esphome.automation import Condition, maybe_simple_id
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import mqtt, web_server
 | 
			
		||||
from esphome.components.const import CONF_ON_STATE_CHANGE
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_DELAY,
 | 
			
		||||
@@ -98,6 +101,7 @@ IS_PLATFORM_COMPONENT = True
 | 
			
		||||
 | 
			
		||||
CONF_TIME_OFF = "time_off"
 | 
			
		||||
CONF_TIME_ON = "time_on"
 | 
			
		||||
CONF_TRIGGER_ON_INITIAL_STATE = "trigger_on_initial_state"
 | 
			
		||||
 | 
			
		||||
DEFAULT_DELAY = "1s"
 | 
			
		||||
DEFAULT_TIME_OFF = "100ms"
 | 
			
		||||
@@ -127,9 +131,17 @@ MultiClickTriggerEvent = binary_sensor_ns.struct("MultiClickTriggerEvent")
 | 
			
		||||
StateTrigger = binary_sensor_ns.class_(
 | 
			
		||||
    "StateTrigger", automation.Trigger.template(bool)
 | 
			
		||||
)
 | 
			
		||||
StateChangeTrigger = binary_sensor_ns.class_(
 | 
			
		||||
    "StateChangeTrigger",
 | 
			
		||||
    automation.Trigger.template(cg.optional.template(bool), cg.optional.template(bool)),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
BinarySensorPublishAction = binary_sensor_ns.class_(
 | 
			
		||||
    "BinarySensorPublishAction", automation.Action
 | 
			
		||||
)
 | 
			
		||||
BinarySensorInvalidateAction = binary_sensor_ns.class_(
 | 
			
		||||
    "BinarySensorInvalidateAction", automation.Action
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Condition
 | 
			
		||||
BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Condition)
 | 
			
		||||
@@ -144,6 +156,8 @@ AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Compon
 | 
			
		||||
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
 | 
			
		||||
SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component)
 | 
			
		||||
 | 
			
		||||
_LOGGER = getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
FILTER_REGISTRY = Registry()
 | 
			
		||||
validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
 | 
			
		||||
 | 
			
		||||
@@ -386,6 +400,14 @@ def validate_click_timing(value):
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_publish_initial_state(value):
 | 
			
		||||
    value = cv.boolean(value)
 | 
			
		||||
    _LOGGER.warning(
 | 
			
		||||
        "The 'publish_initial_state' option has been replaced by 'trigger_on_initial_state' and will be removed in a future release"
 | 
			
		||||
    )
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_BINARY_SENSOR_SCHEMA = (
 | 
			
		||||
    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
			
		||||
    .extend(cv.MQTT_COMPONENT_SCHEMA)
 | 
			
		||||
@@ -395,7 +417,12 @@ _BINARY_SENSOR_SCHEMA = (
 | 
			
		||||
            cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
 | 
			
		||||
                mqtt.MQTTBinarySensorComponent
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean,
 | 
			
		||||
            cv.Exclusive(
 | 
			
		||||
                CONF_PUBLISH_INITIAL_STATE, CONF_TRIGGER_ON_INITIAL_STATE
 | 
			
		||||
            ): validate_publish_initial_state,
 | 
			
		||||
            cv.Exclusive(
 | 
			
		||||
                CONF_TRIGGER_ON_INITIAL_STATE, CONF_TRIGGER_ON_INITIAL_STATE
 | 
			
		||||
            ): cv.boolean,
 | 
			
		||||
            cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
			
		||||
            cv.Optional(CONF_FILTERS): validate_filters,
 | 
			
		||||
            cv.Optional(CONF_ON_PRESS): automation.validate_automation(
 | 
			
		||||
@@ -454,6 +481,11 @@ _BINARY_SENSOR_SCHEMA = (
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateChangeTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
)
 | 
			
		||||
@@ -493,8 +525,10 @@ async def setup_binary_sensor_core_(var, config):
 | 
			
		||||
 | 
			
		||||
    if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
 | 
			
		||||
        cg.add(var.set_device_class(device_class))
 | 
			
		||||
    if publish_initial_state := config.get(CONF_PUBLISH_INITIAL_STATE):
 | 
			
		||||
        cg.add(var.set_publish_initial_state(publish_initial_state))
 | 
			
		||||
    trigger = config.get(CONF_TRIGGER_ON_INITIAL_STATE, False) or config.get(
 | 
			
		||||
        CONF_PUBLISH_INITIAL_STATE, False
 | 
			
		||||
    )
 | 
			
		||||
    cg.add(var.set_trigger_on_initial_state(trigger))
 | 
			
		||||
    if inverted := config.get(CONF_INVERTED):
 | 
			
		||||
        cg.add(var.set_inverted(inverted))
 | 
			
		||||
    if filters_config := config.get(CONF_FILTERS):
 | 
			
		||||
@@ -542,6 +576,17 @@ async def setup_binary_sensor_core_(var, config):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [(bool, "x")], conf)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_STATE_CHANGE, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            trigger,
 | 
			
		||||
            [
 | 
			
		||||
                (cg.optional.template(bool), "x_previous"),
 | 
			
		||||
                (cg.optional.template(bool), "x"),
 | 
			
		||||
            ],
 | 
			
		||||
            conf,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if mqtt_id := config.get(CONF_MQTT_ID):
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
@@ -554,6 +599,7 @@ async def register_binary_sensor(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
        var = cg.Pvariable(config[CONF_ID], var)
 | 
			
		||||
    cg.add(cg.App.register_binary_sensor(var))
 | 
			
		||||
    CORE.register_platform_component("binary_sensor", var)
 | 
			
		||||
    await setup_binary_sensor_core_(var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -590,3 +636,18 @@ async def binary_sensor_is_off_to_code(config, condition_id, template_arg, args)
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    cg.add_define("USE_BINARY_SENSOR")
 | 
			
		||||
    cg.add_global(binary_sensor_ns.using)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "binary_sensor.invalidate_state",
 | 
			
		||||
    BinarySensorInvalidateAction,
 | 
			
		||||
    cv.maybe_simple_value(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_ID): cv.use_id(BinarySensor),
 | 
			
		||||
        },
 | 
			
		||||
        key=CONF_ID,
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
async def binary_sensor_invalidate_state_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
 
 | 
			
		||||
@@ -68,8 +68,7 @@ void binary_sensor::MultiClickTrigger::on_state_(bool state) {
 | 
			
		||||
  *this->at_index_ = *this->at_index_ + 1;
 | 
			
		||||
}
 | 
			
		||||
void binary_sensor::MultiClickTrigger::schedule_cooldown_() {
 | 
			
		||||
  ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms...",
 | 
			
		||||
           this->invalid_cooldown_);
 | 
			
		||||
  ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms", this->invalid_cooldown_);
 | 
			
		||||
  this->is_in_cooldown_ = true;
 | 
			
		||||
  this->set_timeout("cooldown", this->invalid_cooldown_, [this]() {
 | 
			
		||||
    ESP_LOGV(TAG, "Multi Click: Cooldown ended, matching is now enabled again.");
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,7 @@ class MultiClickTrigger : public Trigger<>, public Component {
 | 
			
		||||
      : parent_(parent), timing_(std::move(timing)) {}
 | 
			
		||||
 | 
			
		||||
  void setup() override {
 | 
			
		||||
    this->last_state_ = this->parent_->state;
 | 
			
		||||
    this->last_state_ = this->parent_->get_state_default(false);
 | 
			
		||||
    auto f = std::bind(&MultiClickTrigger::on_state_, this, std::placeholders::_1);
 | 
			
		||||
    this->parent_->add_on_state_callback(f);
 | 
			
		||||
  }
 | 
			
		||||
@@ -130,6 +130,14 @@ class StateTrigger : public Trigger<bool> {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class StateChangeTrigger : public Trigger<optional<bool>, optional<bool> > {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit StateChangeTrigger(BinarySensor *parent) {
 | 
			
		||||
    parent->add_full_state_callback(
 | 
			
		||||
        [this](optional<bool> old_state, optional<bool> state) { this->trigger(old_state, state); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class BinarySensorCondition : public Condition<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  BinarySensorCondition(BinarySensor *parent, bool state) : parent_(parent), state_(state) {}
 | 
			
		||||
@@ -154,5 +162,15 @@ template<typename... Ts> class BinarySensorPublishAction : public Action<Ts...>
 | 
			
		||||
  BinarySensor *sensor_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class BinarySensorInvalidateAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit BinarySensorInvalidateAction(BinarySensor *sensor) : sensor_(sensor) {}
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override { this->sensor_->invalidate_state(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  BinarySensor *sensor_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace binary_sensor
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -7,39 +7,26 @@ namespace binary_sensor {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "binary_sensor";
 | 
			
		||||
 | 
			
		||||
void BinarySensor::add_on_state_callback(std::function<void(bool)> &&callback) {
 | 
			
		||||
  this->state_callback_.add(std::move(callback));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BinarySensor::publish_state(bool state) {
 | 
			
		||||
  if (!this->publish_dedup_.next(state))
 | 
			
		||||
    return;
 | 
			
		||||
void BinarySensor::publish_state(bool new_state) {
 | 
			
		||||
  if (this->filter_list_ == nullptr) {
 | 
			
		||||
    this->send_state_internal(state);
 | 
			
		||||
    this->send_state_internal(new_state);
 | 
			
		||||
  } else {
 | 
			
		||||
    this->filter_list_->input(state);
 | 
			
		||||
    this->filter_list_->input(new_state);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void BinarySensor::publish_initial_state(bool state) {
 | 
			
		||||
  this->has_state_ = false;
 | 
			
		||||
  this->publish_state(state);
 | 
			
		||||
void BinarySensor::publish_initial_state(bool new_state) {
 | 
			
		||||
  this->invalidate_state();
 | 
			
		||||
  this->publish_state(new_state);
 | 
			
		||||
}
 | 
			
		||||
void BinarySensor::send_state_internal(bool state) {
 | 
			
		||||
  bool is_initial = !this->has_state_;
 | 
			
		||||
  if (is_initial) {
 | 
			
		||||
    ESP_LOGD(TAG, "'%s': Sending initial state %s", this->get_name().c_str(), ONOFF(state));
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGD(TAG, "'%s': Sending state %s", this->get_name().c_str(), ONOFF(state));
 | 
			
		||||
  }
 | 
			
		||||
  this->has_state_ = true;
 | 
			
		||||
  this->state = state;
 | 
			
		||||
  if (!is_initial || this->publish_initial_state_) {
 | 
			
		||||
    this->state_callback_.call(state);
 | 
			
		||||
void BinarySensor::send_state_internal(bool new_state) {
 | 
			
		||||
  // copy the new state to the visible property for backwards compatibility, before any callbacks
 | 
			
		||||
  this->state = new_state;
 | 
			
		||||
  // Note that set_state_ de-dups and will only trigger callbacks if the state has actually changed
 | 
			
		||||
  if (this->set_state_(new_state)) {
 | 
			
		||||
    ESP_LOGD(TAG, "'%s': New state is %s", this->get_name().c_str(), ONOFF(new_state));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BinarySensor::BinarySensor() : state(false) {}
 | 
			
		||||
 | 
			
		||||
void BinarySensor::add_filter(Filter *filter) {
 | 
			
		||||
  filter->parent_ = this;
 | 
			
		||||
  if (this->filter_list_ == nullptr) {
 | 
			
		||||
@@ -56,7 +43,6 @@ void BinarySensor::add_filters(const std::vector<Filter *> &filters) {
 | 
			
		||||
    this->add_filter(filter);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool BinarySensor::has_state() const { return this->has_state_; }
 | 
			
		||||
bool BinarySensor::is_status_binary_sensor() const { return false; }
 | 
			
		||||
 | 
			
		||||
}  // namespace binary_sensor
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/entity_base.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/components/binary_sensor/filter.h"
 | 
			
		||||
@@ -34,52 +33,39 @@ namespace binary_sensor {
 | 
			
		||||
 * The sub classes should notify the front-end of new states via the publish_state() method which
 | 
			
		||||
 * handles inverted inputs for you.
 | 
			
		||||
 */
 | 
			
		||||
class BinarySensor : public EntityBase, public EntityBase_DeviceClass {
 | 
			
		||||
class BinarySensor : public StatefulEntityBase<bool>, public EntityBase_DeviceClass {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit BinarySensor();
 | 
			
		||||
 | 
			
		||||
  /** Add a callback to be notified of state changes.
 | 
			
		||||
   *
 | 
			
		||||
   * @param callback The void(bool) callback.
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_state_callback(std::function<void(bool)> &&callback);
 | 
			
		||||
  explicit BinarySensor(){};
 | 
			
		||||
 | 
			
		||||
  /** Publish a new state to the front-end.
 | 
			
		||||
   *
 | 
			
		||||
   * @param state The new state.
 | 
			
		||||
   * @param new_state The new state.
 | 
			
		||||
   */
 | 
			
		||||
  void publish_state(bool state);
 | 
			
		||||
  void publish_state(bool new_state);
 | 
			
		||||
 | 
			
		||||
  /** Publish the initial state, this will not make the callback manager send callbacks
 | 
			
		||||
   * and is meant only for the initial state on boot.
 | 
			
		||||
   *
 | 
			
		||||
   * @param state The new state.
 | 
			
		||||
   * @param new_state The new state.
 | 
			
		||||
   */
 | 
			
		||||
  void publish_initial_state(bool state);
 | 
			
		||||
 | 
			
		||||
  /// The current reported state of the binary sensor.
 | 
			
		||||
  bool state{false};
 | 
			
		||||
  void publish_initial_state(bool new_state);
 | 
			
		||||
 | 
			
		||||
  void add_filter(Filter *filter);
 | 
			
		||||
  void add_filters(const std::vector<Filter *> &filters);
 | 
			
		||||
 | 
			
		||||
  void set_publish_initial_state(bool publish_initial_state) { this->publish_initial_state_ = publish_initial_state; }
 | 
			
		||||
 | 
			
		||||
  // ========== INTERNAL METHODS ==========
 | 
			
		||||
  // (In most use cases you won't need these)
 | 
			
		||||
  void send_state_internal(bool state);
 | 
			
		||||
  void send_state_internal(bool new_state);
 | 
			
		||||
 | 
			
		||||
  /// Return whether this binary sensor has outputted a state.
 | 
			
		||||
  virtual bool has_state() const;
 | 
			
		||||
 | 
			
		||||
  virtual bool is_status_binary_sensor() const;
 | 
			
		||||
 | 
			
		||||
  // For backward compatibility, provide an accessible property
 | 
			
		||||
 | 
			
		||||
  bool state{};
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  CallbackManager<void(bool)> state_callback_{};
 | 
			
		||||
  Filter *filter_list_{nullptr};
 | 
			
		||||
  bool has_state_{false};
 | 
			
		||||
  bool publish_initial_state_{false};
 | 
			
		||||
  Deduplicator<bool> publish_dedup_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BinarySensorInitiallyOff : public BinarySensor {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,9 +10,6 @@ namespace binary_sensor {
 | 
			
		||||
static const char *const TAG = "sensor.filter";
 | 
			
		||||
 | 
			
		||||
void Filter::output(bool value) {
 | 
			
		||||
  if (!this->dedup_.next(value))
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (this->next_ == nullptr) {
 | 
			
		||||
    this->parent_->send_state_internal(value);
 | 
			
		||||
  } else {
 | 
			
		||||
@@ -20,6 +17,8 @@ void Filter::output(bool value) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void Filter::input(bool value) {
 | 
			
		||||
  if (!this->dedup_.next(value))
 | 
			
		||||
    return;
 | 
			
		||||
  auto b = this->new_value(value);
 | 
			
		||||
  if (b.has_value()) {
 | 
			
		||||
    this->output(*b);
 | 
			
		||||
@@ -101,7 +100,7 @@ void AutorepeatFilter::next_timing_() {
 | 
			
		||||
 | 
			
		||||
void AutorepeatFilter::next_value_(bool val) {
 | 
			
		||||
  const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2];
 | 
			
		||||
  this->output(val);
 | 
			
		||||
  this->output(val);  // This is at least the second one so not initial
 | 
			
		||||
  this->set_timeout("ON_OFF", val ? timing.time_on : timing.time_off, [this, val]() { this->next_value_(!val); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,7 @@ void BL0906::handle_actions_() {
 | 
			
		||||
  for (int i = 0; i < this->action_queue_.size(); i++) {
 | 
			
		||||
    ptr_func = this->action_queue_[i];
 | 
			
		||||
    if (ptr_func) {
 | 
			
		||||
      ESP_LOGI(TAG, "HandleActionCallback[%d]...", i);
 | 
			
		||||
      ESP_LOGI(TAG, "HandleActionCallback[%d]", i);
 | 
			
		||||
      (this->*ptr_func)();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -196,14 +196,17 @@ void BL0942::received_package_(DataPacket *data) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0942::dump_config() {  // NOLINT(readability-function-cognitive-complexity)
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BL0942:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Reset: %s", TRUEFALSE(this->reset_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Address: %d", this->address_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Nominal line frequency: %d Hz", this->line_freq_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Current reference: %f", this->current_reference_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Energy reference: %f", this->energy_reference_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Power reference: %f", this->power_reference_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Voltage reference: %f", this->voltage_reference_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "BL0942:\n"
 | 
			
		||||
                "  Reset: %s\n"
 | 
			
		||||
                "  Address: %d\n"
 | 
			
		||||
                "  Nominal line frequency: %d Hz\n"
 | 
			
		||||
                "  Current reference: %f\n"
 | 
			
		||||
                "  Energy reference: %f\n"
 | 
			
		||||
                "  Power reference: %f\n"
 | 
			
		||||
                "  Voltage reference: %f",
 | 
			
		||||
                TRUEFALSE(this->reset_), this->address_, this->line_freq_, this->current_reference_,
 | 
			
		||||
                this->energy_reference_, this->power_reference_, this->voltage_reference_);
 | 
			
		||||
  LOG_SENSOR("", "Voltage", this->voltage_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Current", this->current_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Power", this->power_sensor_);
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_LINE_FREQUENCY,
 | 
			
		||||
    CONF_POWER,
 | 
			
		||||
    CONF_RESET,
 | 
			
		||||
    CONF_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_CURRENT,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
@@ -27,7 +28,6 @@ from esphome.const import (
 | 
			
		||||
CONF_CURRENT_REFERENCE = "current_reference"
 | 
			
		||||
CONF_ENERGY_REFERENCE = "energy_reference"
 | 
			
		||||
CONF_POWER_REFERENCE = "power_reference"
 | 
			
		||||
CONF_RESET = "reset"
 | 
			
		||||
CONF_VOLTAGE_REFERENCE = "voltage_reference"
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["uart"]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.automation import maybe_simple_id
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import esp32_ble_client, esp32_ble_tracker
 | 
			
		||||
from esphome.components import esp32_ble, esp32_ble_client, esp32_ble_tracker
 | 
			
		||||
from esphome.components.esp32_ble import BTLoggers
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CHARACTERISTIC_UUID,
 | 
			
		||||
@@ -287,6 +288,9 @@ async def remove_bond_to_code(config, action_id, template_arg, args):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    # Register the loggers this component needs
 | 
			
		||||
    esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP)
 | 
			
		||||
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await esp32_ble_tracker.register_client(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,9 +10,12 @@ static const char *const TAG = "ble_binary_output";
 | 
			
		||||
 | 
			
		||||
void BLEBinaryOutput::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BLE Binary Output:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  MAC address        : %s", this->parent_->address_str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Service UUID       : %s", this->service_uuid_.to_string().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  MAC address        : %s\n"
 | 
			
		||||
                "  Service UUID       : %s\n"
 | 
			
		||||
                "  Characteristic UUID: %s",
 | 
			
		||||
                this->parent_->address_str().c_str(), this->service_uuid_.to_string().c_str(),
 | 
			
		||||
                this->char_uuid_.to_string().c_str());
 | 
			
		||||
  LOG_BINARY_OUTPUT(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
#include "ble_sensor.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
@@ -15,11 +15,14 @@ void BLESensor::loop() {}
 | 
			
		||||
 | 
			
		||||
void BLESensor::dump_config() {
 | 
			
		||||
  LOG_SENSOR("", "BLE Sensor", this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  MAC address        : %s", this->parent()->address_str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Service UUID       : %s", this->service_uuid_.to_string().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Descriptor UUID    : %s", this->descr_uuid_.to_string().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Notifications      : %s", YESNO(this->notify_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  MAC address        : %s\n"
 | 
			
		||||
                "  Service UUID       : %s\n"
 | 
			
		||||
                "  Characteristic UUID: %s\n"
 | 
			
		||||
                "  Descriptor UUID    : %s\n"
 | 
			
		||||
                "  Notifications      : %s",
 | 
			
		||||
                this->parent()->address_str().c_str(), this->service_uuid_.to_string().c_str(),
 | 
			
		||||
                this->char_uuid_.to_string().c_str(), this->descr_uuid_.to_string().c_str(), YESNO(this->notify_));
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,11 +18,14 @@ void BLETextSensor::loop() {}
 | 
			
		||||
 | 
			
		||||
void BLETextSensor::dump_config() {
 | 
			
		||||
  LOG_TEXT_SENSOR("", "BLE Text Sensor", this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  MAC address        : %s", this->parent()->address_str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Service UUID       : %s", this->service_uuid_.to_string().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Descriptor UUID    : %s", this->descr_uuid_.to_string().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Notifications      : %s", YESNO(this->notify_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  MAC address        : %s\n"
 | 
			
		||||
                "  Service UUID       : %s\n"
 | 
			
		||||
                "  Characteristic UUID: %s\n"
 | 
			
		||||
                "  Descriptor UUID    : %s\n"
 | 
			
		||||
                "  Notifications      : %s",
 | 
			
		||||
                this->parent()->address_str().c_str(), this->service_uuid_.to_string().c_str(),
 | 
			
		||||
                this->char_uuid_.to_string().c_str(), this->descr_uuid_.to_string().c_str(), YESNO(this->notify_));
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import esp32_ble_client, esp32_ble_tracker
 | 
			
		||||
from esphome.components import esp32_ble, esp32_ble_client, esp32_ble_tracker
 | 
			
		||||
from esphome.components.esp32 import add_idf_sdkconfig_option
 | 
			
		||||
from esphome.components.esp32_ble import BTLoggers
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import CONF_ACTIVE, CONF_ID
 | 
			
		||||
 | 
			
		||||
@@ -77,6 +78,9 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    # Register the loggers this component needs
 | 
			
		||||
    esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.L2CAP, BTLoggers.SMP)
 | 
			
		||||
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -75,7 +75,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
 | 
			
		||||
      resp.data.reserve(param->read.value_len);
 | 
			
		||||
      // Use bulk insert instead of individual push_backs
 | 
			
		||||
      resp.data.insert(resp.data.end(), param->read.value, param->read.value + param->read.value_len);
 | 
			
		||||
      this->proxy_->get_api_connection()->send_bluetooth_gatt_read_response(resp);
 | 
			
		||||
      this->proxy_->get_api_connection()->send_message(resp);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_WRITE_CHAR_EVT:
 | 
			
		||||
@@ -89,7 +89,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
 | 
			
		||||
      api::BluetoothGATTWriteResponse resp;
 | 
			
		||||
      resp.address = this->address_;
 | 
			
		||||
      resp.handle = param->write.handle;
 | 
			
		||||
      this->proxy_->get_api_connection()->send_bluetooth_gatt_write_response(resp);
 | 
			
		||||
      this->proxy_->get_api_connection()->send_message(resp);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
 | 
			
		||||
@@ -103,7 +103,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
 | 
			
		||||
      api::BluetoothGATTNotifyResponse resp;
 | 
			
		||||
      resp.address = this->address_;
 | 
			
		||||
      resp.handle = param->unreg_for_notify.handle;
 | 
			
		||||
      this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp);
 | 
			
		||||
      this->proxy_->get_api_connection()->send_message(resp);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
 | 
			
		||||
@@ -116,7 +116,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
 | 
			
		||||
      api::BluetoothGATTNotifyResponse resp;
 | 
			
		||||
      resp.address = this->address_;
 | 
			
		||||
      resp.handle = param->reg_for_notify.handle;
 | 
			
		||||
      this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp);
 | 
			
		||||
      this->proxy_->get_api_connection()->send_message(resp);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ESP_GATTC_NOTIFY_EVT: {
 | 
			
		||||
@@ -128,7 +128,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
 | 
			
		||||
      resp.data.reserve(param->notify.value_len);
 | 
			
		||||
      // Use bulk insert instead of individual push_backs
 | 
			
		||||
      resp.data.insert(resp.data.end(), param->notify.value, param->notify.value + param->notify.value_len);
 | 
			
		||||
      this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_data_response(resp);
 | 
			
		||||
      this->proxy_->get_api_connection()->send_message(resp);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/macros.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
@@ -38,7 +39,7 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta
 | 
			
		||||
  resp.state = static_cast<api::enums::BluetoothScannerState>(state);
 | 
			
		||||
  resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
 | 
			
		||||
                                               : api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
 | 
			
		||||
  this->api_connection_->send_bluetooth_scanner_state_response(resp);
 | 
			
		||||
  this->api_connection_->send_message(resp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
 | 
			
		||||
@@ -51,35 +52,60 @@ bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static constexpr size_t FLUSH_BATCH_SIZE = 8;
 | 
			
		||||
static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() {
 | 
			
		||||
  static std::vector<api::BluetoothLERawAdvertisement> batch_buffer;
 | 
			
		||||
  return batch_buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
 | 
			
		||||
  if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  api::BluetoothLERawAdvertisementsResponse resp;
 | 
			
		||||
  // Pre-allocate the advertisements vector to avoid reallocations
 | 
			
		||||
  resp.advertisements.reserve(count);
 | 
			
		||||
  // Get the batch buffer reference
 | 
			
		||||
  auto &batch_buffer = get_batch_buffer();
 | 
			
		||||
 | 
			
		||||
  // Reserve additional capacity if needed
 | 
			
		||||
  size_t new_size = batch_buffer.size() + count;
 | 
			
		||||
  if (batch_buffer.capacity() < new_size) {
 | 
			
		||||
    batch_buffer.reserve(new_size);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Add new advertisements to the batch buffer
 | 
			
		||||
  for (size_t i = 0; i < count; i++) {
 | 
			
		||||
    auto &result = advertisements[i];
 | 
			
		||||
    api::BluetoothLERawAdvertisement adv;
 | 
			
		||||
    uint8_t length = result.adv_data_len + result.scan_rsp_len;
 | 
			
		||||
 | 
			
		||||
    batch_buffer.emplace_back();
 | 
			
		||||
    auto &adv = batch_buffer.back();
 | 
			
		||||
    adv.address = esp32_ble::ble_addr_to_uint64(result.bda);
 | 
			
		||||
    adv.rssi = result.rssi;
 | 
			
		||||
    adv.address_type = result.ble_addr_type;
 | 
			
		||||
    adv.data.assign(&result.ble_adv[0], &result.ble_adv[length]);
 | 
			
		||||
 | 
			
		||||
    uint8_t length = result.adv_data_len + result.scan_rsp_len;
 | 
			
		||||
    adv.data.reserve(length);
 | 
			
		||||
    // Use a bulk insert instead of individual push_backs
 | 
			
		||||
    adv.data.insert(adv.data.end(), &result.ble_adv[0], &result.ble_adv[length]);
 | 
			
		||||
 | 
			
		||||
    resp.advertisements.push_back(std::move(adv));
 | 
			
		||||
 | 
			
		||||
    ESP_LOGV(TAG, "Proxying raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0],
 | 
			
		||||
    ESP_LOGV(TAG, "Queuing raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0],
 | 
			
		||||
             result.bda[1], result.bda[2], result.bda[3], result.bda[4], result.bda[5], length, result.rssi);
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGV(TAG, "Proxying %d packets", count);
 | 
			
		||||
  this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
 | 
			
		||||
 | 
			
		||||
  // Only send if we've accumulated a good batch size to maximize batching efficiency
 | 
			
		||||
  // https://github.com/esphome/backlog/issues/21
 | 
			
		||||
  if (batch_buffer.size() >= FLUSH_BATCH_SIZE) {
 | 
			
		||||
    this->flush_pending_advertisements();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::flush_pending_advertisements() {
 | 
			
		||||
  auto &batch_buffer = get_batch_buffer();
 | 
			
		||||
  if (batch_buffer.empty() || !api::global_api_server->is_connected() || this->api_connection_ == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  api::BluetoothLERawAdvertisementsResponse resp;
 | 
			
		||||
  resp.advertisements.swap(batch_buffer);
 | 
			
		||||
  this->api_connection_->send_message(resp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
 | 
			
		||||
  api::BluetoothLEAdvertisementResponse resp;
 | 
			
		||||
  resp.address = device.address_uint64();
 | 
			
		||||
@@ -91,38 +117,40 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
 | 
			
		||||
  // Pre-allocate vectors based on known sizes
 | 
			
		||||
  auto service_uuids = device.get_service_uuids();
 | 
			
		||||
  resp.service_uuids.reserve(service_uuids.size());
 | 
			
		||||
  for (auto uuid : service_uuids) {
 | 
			
		||||
    resp.service_uuids.push_back(uuid.to_string());
 | 
			
		||||
  for (auto &uuid : service_uuids) {
 | 
			
		||||
    resp.service_uuids.emplace_back(uuid.to_string());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Pre-allocate service data vector
 | 
			
		||||
  auto service_datas = device.get_service_datas();
 | 
			
		||||
  resp.service_data.reserve(service_datas.size());
 | 
			
		||||
  for (auto &data : service_datas) {
 | 
			
		||||
    api::BluetoothServiceData service_data;
 | 
			
		||||
    resp.service_data.emplace_back();
 | 
			
		||||
    auto &service_data = resp.service_data.back();
 | 
			
		||||
    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));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Pre-allocate manufacturer data vector
 | 
			
		||||
  auto manufacturer_datas = device.get_manufacturer_datas();
 | 
			
		||||
  resp.manufacturer_data.reserve(manufacturer_datas.size());
 | 
			
		||||
  for (auto &data : manufacturer_datas) {
 | 
			
		||||
    api::BluetoothServiceData manufacturer_data;
 | 
			
		||||
    resp.manufacturer_data.emplace_back();
 | 
			
		||||
    auto &manufacturer_data = resp.manufacturer_data.back();
 | 
			
		||||
    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));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->api_connection_->send_bluetooth_le_advertisement(resp);
 | 
			
		||||
  this->api_connection_->send_message(resp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Active: %s", YESNO(this->active_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Connections: %d", this->connections_.size());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Raw advertisements: %s", YESNO(this->raw_advertisements_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Active: %s\n"
 | 
			
		||||
                "  Connections: %d\n"
 | 
			
		||||
                "  Raw advertisements: %s",
 | 
			
		||||
                YESNO(this->active_), this->connections_.size(), YESNO(this->raw_advertisements_));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int BluetoothProxy::get_bluetooth_connections_free() {
 | 
			
		||||
@@ -148,6 +176,18 @@ void BluetoothProxy::loop() {
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Flush any pending BLE advertisements that have been accumulated but not yet sent
 | 
			
		||||
  if (this->raw_advertisements_) {
 | 
			
		||||
    static uint32_t last_flush_time = 0;
 | 
			
		||||
    uint32_t now = App.get_loop_component_start_time();
 | 
			
		||||
 | 
			
		||||
    // Flush accumulated advertisements every 100ms
 | 
			
		||||
    if (now - last_flush_time >= 100) {
 | 
			
		||||
      this->flush_pending_advertisements();
 | 
			
		||||
      last_flush_time = now;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  for (auto *connection : this->connections_) {
 | 
			
		||||
    if (connection->send_service_ == connection->service_count_) {
 | 
			
		||||
      connection->send_service_ = DONE_SENDING_SERVICES;
 | 
			
		||||
@@ -262,7 +302,7 @@ void BluetoothProxy::loop() {
 | 
			
		||||
        service_resp.characteristics.push_back(std::move(characteristic_resp));
 | 
			
		||||
      }
 | 
			
		||||
      resp.services.push_back(std::move(service_resp));
 | 
			
		||||
      this->api_connection_->send_bluetooth_gatt_get_services_response(resp);
 | 
			
		||||
      this->api_connection_->send_message(resp);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -415,7 +455,7 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
 | 
			
		||||
      call.success = ret == ESP_OK;
 | 
			
		||||
      call.error = ret;
 | 
			
		||||
 | 
			
		||||
      this->api_connection_->send_bluetooth_device_clear_cache_response(call);
 | 
			
		||||
      this->api_connection_->send_message(call);
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -539,7 +579,7 @@ void BluetoothProxy::send_device_connection(uint64_t address, bool connected, ui
 | 
			
		||||
  call.connected = connected;
 | 
			
		||||
  call.mtu = mtu;
 | 
			
		||||
  call.error = error;
 | 
			
		||||
  this->api_connection_->send_bluetooth_device_connection_response(call);
 | 
			
		||||
  this->api_connection_->send_message(call);
 | 
			
		||||
}
 | 
			
		||||
void BluetoothProxy::send_connections_free() {
 | 
			
		||||
  if (this->api_connection_ == nullptr)
 | 
			
		||||
@@ -552,7 +592,7 @@ void BluetoothProxy::send_connections_free() {
 | 
			
		||||
      call.allocated.push_back(connection->address_);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  this->api_connection_->send_bluetooth_connections_free_response(call);
 | 
			
		||||
  this->api_connection_->send_message(call);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::send_gatt_services_done(uint64_t address) {
 | 
			
		||||
@@ -560,7 +600,7 @@ void BluetoothProxy::send_gatt_services_done(uint64_t address) {
 | 
			
		||||
    return;
 | 
			
		||||
  api::BluetoothGATTGetServicesDoneResponse call;
 | 
			
		||||
  call.address = address;
 | 
			
		||||
  this->api_connection_->send_bluetooth_gatt_get_services_done_response(call);
 | 
			
		||||
  this->api_connection_->send_message(call);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) {
 | 
			
		||||
@@ -570,7 +610,7 @@ void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_
 | 
			
		||||
  call.address = address;
 | 
			
		||||
  call.handle = handle;
 | 
			
		||||
  call.error = error;
 | 
			
		||||
  this->api_connection_->send_bluetooth_gatt_error_response(call);
 | 
			
		||||
  this->api_connection_->send_message(call);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_t error) {
 | 
			
		||||
@@ -579,7 +619,7 @@ void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_
 | 
			
		||||
  call.paired = paired;
 | 
			
		||||
  call.error = error;
 | 
			
		||||
 | 
			
		||||
  this->api_connection_->send_bluetooth_device_pairing_response(call);
 | 
			
		||||
  this->api_connection_->send_message(call);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_err_t error) {
 | 
			
		||||
@@ -588,7 +628,7 @@ void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_e
 | 
			
		||||
  call.success = success;
 | 
			
		||||
  call.error = error;
 | 
			
		||||
 | 
			
		||||
  this->api_connection_->send_bluetooth_device_unpairing_response(call);
 | 
			
		||||
  this->api_connection_->send_message(call);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BluetoothProxy::bluetooth_scanner_set_mode(bool active) {
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  void flush_pending_advertisements();
 | 
			
		||||
  esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
 | 
			
		||||
 | 
			
		||||
  void register_connection(BluetoothConnection *connection) {
 | 
			
		||||
 
 | 
			
		||||
@@ -88,7 +88,7 @@ const char *oversampling_to_str(BME280Oversampling oversampling) {  // NOLINT
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BME280Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up BME280...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  uint8_t chip_id = 0;
 | 
			
		||||
 | 
			
		||||
  // Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
 | 
			
		||||
@@ -182,7 +182,7 @@ void BME280Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BME280:");
 | 
			
		||||
  switch (this->error_code_) {
 | 
			
		||||
    case COMMUNICATION_FAILED:
 | 
			
		||||
      ESP_LOGE(TAG, "Communication with BME280 failed!");
 | 
			
		||||
      ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
      break;
 | 
			
		||||
    case WRONG_CHIP_ID:
 | 
			
		||||
      ESP_LOGE(TAG, "BME280 has wrong chip ID! Is it a BME280?");
 | 
			
		||||
@@ -207,7 +207,7 @@ inline uint8_t oversampling_to_time(BME280Oversampling over_sampling) { return (
 | 
			
		||||
 | 
			
		||||
void BME280Component::update() {
 | 
			
		||||
  // Enable sensor
 | 
			
		||||
  ESP_LOGV(TAG, "Sending conversion request...");
 | 
			
		||||
  ESP_LOGV(TAG, "Sending conversion request");
 | 
			
		||||
  uint8_t meas_value = 0;
 | 
			
		||||
  meas_value |= (this->temperature_oversampling_ & 0b111) << 5;
 | 
			
		||||
  meas_value |= (this->pressure_oversampling_ & 0b111) << 2;
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ static const char *iir_filter_to_str(BME680IIRFilter filter) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BME680Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up BME680...");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup");
 | 
			
		||||
  uint8_t chip_id;
 | 
			
		||||
  if (!this->read_byte(BME680_REGISTER_CHIPID, &chip_id) || chip_id != 0x61) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
@@ -215,7 +215,7 @@ void BME680Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BME680:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with BME680 failed!");
 | 
			
		||||
    ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  IIR Filter: %s", iir_filter_to_str(this->iir_filter_));
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
@@ -307,7 +307,7 @@ void BME680Component::read_data_() {
 | 
			
		||||
      this->humidity_sensor_->publish_state(NAN);
 | 
			
		||||
    if (this->gas_resistance_sensor_ != nullptr)
 | 
			
		||||
      this->gas_resistance_sensor_->publish_state(NAN);
 | 
			
		||||
    ESP_LOGW(TAG, "Communication with BME680 failed!");
 | 
			
		||||
    ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_PRESSURE,
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
    DEVICE_CLASS_PRESSURE,
 | 
			
		||||
    DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    ICON_GAS_CYLINDER,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
@@ -71,7 +71,7 @@ CONFIG_SCHEMA = (
 | 
			
		||||
            cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_HECTOPASCAL,
 | 
			
		||||
                accuracy_decimals=1,
 | 
			
		||||
                device_class=DEVICE_CLASS_PRESSURE,
 | 
			
		||||
                device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#include "bme680_bsec.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -15,7 +15,7 @@ std::vector<BME680BSECComponent *>
 | 
			
		||||
uint8_t BME680BSECComponent::work_buffer_[BSEC_MAX_WORKBUFFER_SIZE] = {0};
 | 
			
		||||
 | 
			
		||||
void BME680BSECComponent::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up BME680(%s) via BSEC...", this->device_id_.c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->device_id_.c_str());
 | 
			
		||||
 | 
			
		||||
  uint8_t new_idx = BME680BSECComponent::instances.size();
 | 
			
		||||
  BME680BSECComponent::instances.push_back(this);
 | 
			
		||||
@@ -159,11 +159,15 @@ void BME680BSECComponent::dump_config() {
 | 
			
		||||
             this->bme680_status_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Temperature Offset: %.2f", this->temperature_offset_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Supply Voltage: %sV", this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  State Save Interval: %ims", this->state_save_interval_ms_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG,
 | 
			
		||||
                "  Temperature Offset: %.2f\n"
 | 
			
		||||
                "  IAQ Mode: %s\n"
 | 
			
		||||
                "  Supply Voltage: %sV\n"
 | 
			
		||||
                "  Sample Rate: %s\n"
 | 
			
		||||
                "  State Save Interval: %ims",
 | 
			
		||||
                this->temperature_offset_, this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile",
 | 
			
		||||
                this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8",
 | 
			
		||||
                BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_), this->state_save_interval_ms_);
 | 
			
		||||
 | 
			
		||||
  LOG_SENSOR("  ", "Temperature", this->temperature_sensor_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "    Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->temperature_sample_rate_));
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,8 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
 | 
			
		||||
    ICON_GAS_CYLINDER,
 | 
			
		||||
    ICON_GAUGE,
 | 
			
		||||
    ICON_THERMOMETER,
 | 
			
		||||
    ICON_WATER_PERCENT,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_HECTOPASCAL,
 | 
			
		||||
@@ -27,11 +29,11 @@ from . import CONF_BME680_BSEC_ID, SAMPLE_RATE_OPTIONS, BME680BSECComponent
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["bme680_bsec"]
 | 
			
		||||
 | 
			
		||||
CONF_IAQ = "iaq"
 | 
			
		||||
CONF_CO2_EQUIVALENT = "co2_equivalent"
 | 
			
		||||
CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent"
 | 
			
		||||
UNIT_IAQ = "IAQ"
 | 
			
		||||
CONF_CO2_EQUIVALENT = "co2_equivalent"
 | 
			
		||||
CONF_IAQ = "iaq"
 | 
			
		||||
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
 | 
			
		||||
UNIT_IAQ = "IAQ"
 | 
			
		||||
 | 
			
		||||
TYPES = [
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
@@ -49,6 +51,7 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
        cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent),
 | 
			
		||||
        cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
            unit_of_measurement=UNIT_CELSIUS,
 | 
			
		||||
            icon=ICON_THERMOMETER,
 | 
			
		||||
            accuracy_decimals=1,
 | 
			
		||||
            device_class=DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
            state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
@@ -65,6 +68,7 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
			
		||||
            unit_of_measurement=UNIT_PERCENT,
 | 
			
		||||
            icon=ICON_WATER_PERCENT,
 | 
			
		||||
            accuracy_decimals=1,
 | 
			
		||||
            device_class=DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
            state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ CODEOWNERS = ["@neffs", "@kbx81"]
 | 
			
		||||
 | 
			
		||||
DOMAIN = "bme68x_bsec2"
 | 
			
		||||
 | 
			
		||||
BSEC2_LIBRARY_VERSION = "v1.8.2610"
 | 
			
		||||
BSEC2_LIBRARY_VERSION = "1.10.2610"
 | 
			
		||||
 | 
			
		||||
CONF_ALGORITHM_OUTPUT = "algorithm_output"
 | 
			
		||||
CONF_BME68X_BSEC2_ID = "bme68x_bsec2_id"
 | 
			
		||||
@@ -145,7 +145,6 @@ CONFIG_SCHEMA_BASE = (
 | 
			
		||||
            ): cv.positive_time_period_minutes,
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
    .add_extra(cv.only_with_arduino)
 | 
			
		||||
    .add_extra(validate_bme68x)
 | 
			
		||||
    .add_extra(download_bme68x_blob)
 | 
			
		||||
)
 | 
			
		||||
@@ -179,11 +178,13 @@ async def to_code_base(config):
 | 
			
		||||
    bsec2_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
 | 
			
		||||
    cg.add(var.set_bsec2_configuration(bsec2_arr, len(rhs)))
 | 
			
		||||
 | 
			
		||||
    # Although this component does not use SPI, the BSEC2 library requires the SPI library
 | 
			
		||||
    cg.add_library("SPI", None)
 | 
			
		||||
    # Although this component does not use SPI, the BSEC2 Arduino library requires the SPI library
 | 
			
		||||
    if core.CORE.using_arduino:
 | 
			
		||||
        cg.add_library("SPI", None)
 | 
			
		||||
    cg.add_library(
 | 
			
		||||
        "BME68x Sensor library",
 | 
			
		||||
        "1.1.40407",
 | 
			
		||||
        "1.3.40408",
 | 
			
		||||
        "https://github.com/boschsensortec/Bosch-BME68x-Library",
 | 
			
		||||
    )
 | 
			
		||||
    cg.add_library(
 | 
			
		||||
        "BSEC2 Software Library",
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user