mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	| @@ -1,17 +1,13 @@ | |||||||
| { | { | ||||||
|   "name": "ESPHome Dev", |   "name": "ESPHome Dev", | ||||||
|   "image": "ghcr.io/esphome/esphome-lint:dev", |   "image": "ghcr.io/esphome/esphome-lint:dev", | ||||||
|   "postCreateCommand": [ |   "postCreateCommand": ["script/devcontainer-post-create"], | ||||||
|     "script/devcontainer-post-create" |  | ||||||
|   ], |  | ||||||
|   "containerEnv": { |   "containerEnv": { | ||||||
|     "DEVCONTAINER": "1" |     "DEVCONTAINER": "1", | ||||||
|  |     "PIP_BREAK_SYSTEM_PACKAGES": "1", | ||||||
|  |     "PIP_ROOT_USER_ACTION": "ignore" | ||||||
|   }, |   }, | ||||||
|   "runArgs": [ |   "runArgs": ["--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1"], | ||||||
|     "--privileged", |  | ||||||
|     "-e", |  | ||||||
|     "ESPHOME_DASHBOARD_USE_PING=1" |  | ||||||
|   ], |  | ||||||
|   "appPort": 6052, |   "appPort": 6052, | ||||||
|   "customizations": { |   "customizations": { | ||||||
|     "vscode": { |     "vscode": { | ||||||
| @@ -24,7 +20,7 @@ | |||||||
|         // cpp |         // cpp | ||||||
|         "ms-vscode.cpptools", |         "ms-vscode.cpptools", | ||||||
|         // editorconfig |         // editorconfig | ||||||
|         "editorconfig.editorconfig", |         "editorconfig.editorconfig" | ||||||
|       ], |       ], | ||||||
|       "settings": { |       "settings": { | ||||||
|         "python.languageServer": "Pylance", |         "python.languageServer": "Pylance", | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							| @@ -19,6 +19,8 @@ | |||||||
| - [ ] ESP32 IDF | - [ ] ESP32 IDF | ||||||
| - [ ] ESP8266 | - [ ] ESP8266 | ||||||
| - [ ] RP2040 | - [ ] RP2040 | ||||||
|  | - [ ] BK72xx | ||||||
|  | - [ ] RTL87xx | ||||||
|  |  | ||||||
| ## Example entry for `config.yaml`: | ## Example entry for `config.yaml`: | ||||||
| <!-- | <!-- | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							| @@ -40,7 +40,7 @@ jobs: | |||||||
|         arch: [amd64, armv7, aarch64] |         arch: [amd64, armv7, aarch64] | ||||||
|         build_type: ["ha-addon", "docker", "lint"] |         build_type: ["ha-addon", "docker", "lint"] | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4.1.0 |       - uses: actions/checkout@v4.1.1 | ||||||
|       - name: Set up Python |       - name: Set up Python | ||||||
|         uses: actions/setup-python@v4.7.1 |         uses: actions/setup-python@v4.7.1 | ||||||
|         with: |         with: | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -34,7 +34,7 @@ jobs: | |||||||
|       cache-key: ${{ steps.cache-key.outputs.key }} |       cache-key: ${{ steps.cache-key.outputs.key }} | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Generate cache-key |       - name: Generate cache-key | ||||||
|         id: cache-key |         id: cache-key | ||||||
|         run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT |         run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT | ||||||
| @@ -66,7 +66,7 @@ jobs: | |||||||
|       - common |       - common | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Restore Python |       - name: Restore Python | ||||||
|         uses: ./.github/actions/restore-python |         uses: ./.github/actions/restore-python | ||||||
|         with: |         with: | ||||||
| @@ -87,7 +87,7 @@ jobs: | |||||||
|       - common |       - common | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Restore Python |       - name: Restore Python | ||||||
|         uses: ./.github/actions/restore-python |         uses: ./.github/actions/restore-python | ||||||
|         with: |         with: | ||||||
| @@ -108,7 +108,7 @@ jobs: | |||||||
|       - common |       - common | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Restore Python |       - name: Restore Python | ||||||
|         uses: ./.github/actions/restore-python |         uses: ./.github/actions/restore-python | ||||||
|         with: |         with: | ||||||
| @@ -129,7 +129,7 @@ jobs: | |||||||
|       - common |       - common | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Restore Python |       - name: Restore Python | ||||||
|         uses: ./.github/actions/restore-python |         uses: ./.github/actions/restore-python | ||||||
|         with: |         with: | ||||||
| @@ -150,7 +150,7 @@ jobs: | |||||||
|       - common |       - common | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Restore Python |       - name: Restore Python | ||||||
|         uses: ./.github/actions/restore-python |         uses: ./.github/actions/restore-python | ||||||
|         with: |         with: | ||||||
| @@ -171,7 +171,7 @@ jobs: | |||||||
|       - common |       - common | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Restore Python |       - name: Restore Python | ||||||
|         uses: ./.github/actions/restore-python |         uses: ./.github/actions/restore-python | ||||||
|         with: |         with: | ||||||
| @@ -191,7 +191,7 @@ jobs: | |||||||
|       - common |       - common | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Restore Python |       - name: Restore Python | ||||||
|         uses: ./.github/actions/restore-python |         uses: ./.github/actions/restore-python | ||||||
|         with: |         with: | ||||||
| @@ -216,11 +216,34 @@ jobs: | |||||||
|       matrix: ${{ steps.set-matrix.outputs.matrix }} |       matrix: ${{ steps.set-matrix.outputs.matrix }} | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Find all YAML test files |       - name: Find all YAML test files | ||||||
|         id: set-matrix |         id: set-matrix | ||||||
|         run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT |         run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT | ||||||
|  |  | ||||||
|  |   validate-tests: | ||||||
|  |     name: Validate YAML test ${{ matrix.file }} | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     needs: | ||||||
|  |       - common | ||||||
|  |       - compile-tests-list | ||||||
|  |     strategy: | ||||||
|  |       fail-fast: false | ||||||
|  |       matrix: | ||||||
|  |         file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }} | ||||||
|  |     steps: | ||||||
|  |       - name: Check out code from GitHub | ||||||
|  |         uses: actions/checkout@v4.1.1 | ||||||
|  |       - name: Restore Python | ||||||
|  |         uses: ./.github/actions/restore-python | ||||||
|  |         with: | ||||||
|  |           python-version: ${{ env.DEFAULT_PYTHON }} | ||||||
|  |           cache-key: ${{ needs.common.outputs.cache-key }} | ||||||
|  |       - name: Run esphome config ${{ matrix.file }} | ||||||
|  |         run: | | ||||||
|  |           . venv/bin/activate | ||||||
|  |           esphome config ${{ matrix.file }} | ||||||
|  |  | ||||||
|   compile-tests: |   compile-tests: | ||||||
|     name: Run YAML test ${{ matrix.file }} |     name: Run YAML test ${{ matrix.file }} | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
| @@ -234,6 +257,7 @@ jobs: | |||||||
|       - pytest |       - pytest | ||||||
|       - pyupgrade |       - pyupgrade | ||||||
|       - compile-tests-list |       - compile-tests-list | ||||||
|  |       - validate-tests | ||||||
|     strategy: |     strategy: | ||||||
|       fail-fast: false |       fail-fast: false | ||||||
|       max-parallel: 2 |       max-parallel: 2 | ||||||
| @@ -241,7 +265,7 @@ jobs: | |||||||
|         file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }} |         file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }} | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Restore Python |       - name: Restore Python | ||||||
|         uses: ./.github/actions/restore-python |         uses: ./.github/actions/restore-python | ||||||
|         with: |         with: | ||||||
| @@ -296,7 +320,7 @@ jobs: | |||||||
|  |  | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Restore Python |       - name: Restore Python | ||||||
|         uses: ./.github/actions/restore-python |         uses: ./.github/actions/restore-python | ||||||
|         with: |         with: | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								.github/workflows/needs-docs.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/needs-docs.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | name: Needs Docs | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   pull_request: | ||||||
|  |     types: [labeled, unlabeled] | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   check: | ||||||
|  |     name: Check | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - name: Check for needs-docs label | ||||||
|  |         uses: actions/github-script@v6.4.1 | ||||||
|  |         with: | ||||||
|  |           script: | | ||||||
|  |             const { data: labels } = await github.rest.issues.listLabelsOnIssue({ | ||||||
|  |               owner: context.repo.owner, | ||||||
|  |               repo: context.repo.repo, | ||||||
|  |               issue_number: context.issue.number | ||||||
|  |             }); | ||||||
|  |             const needsDocs = labels.find(label => label.name === 'needs-docs'); | ||||||
|  |             if (needsDocs) { | ||||||
|  |               core.setFailed('Pull request needs docs'); | ||||||
|  |             } | ||||||
							
								
								
									
										6
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -19,7 +19,7 @@ jobs: | |||||||
|     outputs: |     outputs: | ||||||
|       tag: ${{ steps.tag.outputs.tag }} |       tag: ${{ steps.tag.outputs.tag }} | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4.1.0 |       - uses: actions/checkout@v4.1.1 | ||||||
|       - name: Get tag |       - name: Get tag | ||||||
|         id: tag |         id: tag | ||||||
|         # yamllint disable rule:line-length |         # yamllint disable rule:line-length | ||||||
| @@ -43,7 +43,7 @@ jobs: | |||||||
|     if: github.repository == 'esphome/esphome' && github.event_name == 'release' |     if: github.repository == 'esphome/esphome' && github.event_name == 'release' | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4.1.0 |       - uses: actions/checkout@v4.1.1 | ||||||
|       - name: Set up Python |       - name: Set up Python | ||||||
|         uses: actions/setup-python@v4.7.1 |         uses: actions/setup-python@v4.7.1 | ||||||
|         with: |         with: | ||||||
| @@ -88,7 +88,7 @@ jobs: | |||||||
|             target: "lint" |             target: "lint" | ||||||
|             baseimg: "docker" |             baseimg: "docker" | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4.1.0 |       - uses: actions/checkout@v4.1.1 | ||||||
|       - name: Set up Python |       - name: Set up Python | ||||||
|         uses: actions/setup-python@v4.7.1 |         uses: actions/setup-python@v4.7.1 | ||||||
|         with: |         with: | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							| @@ -13,10 +13,10 @@ jobs: | |||||||
|     if: github.repository == 'esphome/esphome' |     if: github.repository == 'esphome/esphome' | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|  |  | ||||||
|       - name: Checkout Home Assistant |       - name: Checkout Home Assistant | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|         with: |         with: | ||||||
|           repository: home-assistant/core |           repository: home-assistant/core | ||||||
|           path: lib/home-assistant |           path: lib/home-assistant | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/yaml-lint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/yaml-lint.yml
									
									
									
									
										vendored
									
									
								
							| @@ -17,6 +17,6 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out code from GitHub |       - name: Check out code from GitHub | ||||||
|         uses: actions/checkout@v4.1.0 |         uses: actions/checkout@v4.1.1 | ||||||
|       - name: Run yamllint |       - name: Run yamllint | ||||||
|         uses: frenck/action-yamllint@v1.4.1 |         uses: frenck/action-yamllint@v1.4.1 | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
| # See https://pre-commit.com/hooks.html for more hooks | # See https://pre-commit.com/hooks.html for more hooks | ||||||
| repos: | repos: | ||||||
|   - repo: https://github.com/psf/black-pre-commit-mirror |   - repo: https://github.com/psf/black-pre-commit-mirror | ||||||
|     rev: 23.9.1 |     rev: 23.10.1 | ||||||
|     hooks: |     hooks: | ||||||
|       - id: black |       - id: black | ||||||
|         args: |         args: | ||||||
| @@ -11,7 +11,7 @@ repos: | |||||||
|           - --quiet |           - --quiet | ||||||
|         files: ^((esphome|script|tests)/.+)?[^/]+\.py$ |         files: ^((esphome|script|tests)/.+)?[^/]+\.py$ | ||||||
|   - repo: https://github.com/PyCQA/flake8 |   - repo: https://github.com/PyCQA/flake8 | ||||||
|     rev: 6.0.0 |     rev: 6.1.0 | ||||||
|     hooks: |     hooks: | ||||||
|       - id: flake8 |       - id: flake8 | ||||||
|         additional_dependencies: |         additional_dependencies: | ||||||
| @@ -27,7 +27,7 @@ repos: | |||||||
|           - --branch=release |           - --branch=release | ||||||
|           - --branch=beta |           - --branch=beta | ||||||
|   - repo: https://github.com/asottile/pyupgrade |   - repo: https://github.com/asottile/pyupgrade | ||||||
|     rev: v3.10.1 |     rev: v3.15.0 | ||||||
|     hooks: |     hooks: | ||||||
|       - id: pyupgrade |       - id: pyupgrade | ||||||
|         args: [--py39-plus] |         args: [--py39-plus] | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								CODEOWNERS
									
									
									
									
									
								
							| @@ -17,6 +17,9 @@ esphome/components/ac_dimmer/* @glmnet | |||||||
| esphome/components/adc/* @esphome/core | esphome/components/adc/* @esphome/core | ||||||
| esphome/components/adc128s102/* @DeerMaximum | esphome/components/adc128s102/* @DeerMaximum | ||||||
| esphome/components/addressable_light/* @justfalter | esphome/components/addressable_light/* @justfalter | ||||||
|  | esphome/components/ade7953/* @angelnu | ||||||
|  | esphome/components/ade7953_i2c/* @angelnu | ||||||
|  | esphome/components/ade7953_spi/* @angelnu | ||||||
| esphome/components/airthings_ble/* @jeromelaban | esphome/components/airthings_ble/* @jeromelaban | ||||||
| esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau | esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau | ||||||
| esphome/components/airthings_wave_mini/* @ncareau | esphome/components/airthings_wave_mini/* @ncareau | ||||||
| @@ -77,6 +80,7 @@ esphome/components/dashboard_import/* @esphome/core | |||||||
| esphome/components/debug/* @OttoWinter | esphome/components/debug/* @OttoWinter | ||||||
| esphome/components/delonghi/* @grob6000 | esphome/components/delonghi/* @grob6000 | ||||||
| esphome/components/dfplayer/* @glmnet | esphome/components/dfplayer/* @glmnet | ||||||
|  | esphome/components/dfrobot_sen0395/* @niklasweber | ||||||
| esphome/components/dht/* @OttoWinter | esphome/components/dht/* @OttoWinter | ||||||
| esphome/components/display_menu_base/* @numo68 | esphome/components/display_menu_base/* @numo68 | ||||||
| esphome/components/dps310/* @kbx81 | esphome/components/dps310/* @kbx81 | ||||||
| @@ -85,11 +89,12 @@ esphome/components/dsmr/* @glmnet @zuidwijk | |||||||
| esphome/components/duty_time/* @dudanov | esphome/components/duty_time/* @dudanov | ||||||
| esphome/components/ee895/* @Stock-M | esphome/components/ee895/* @Stock-M | ||||||
| esphome/components/ektf2232/* @jesserockz | esphome/components/ektf2232/* @jesserockz | ||||||
|  | esphome/components/emc2101/* @ellull | ||||||
| esphome/components/ens210/* @itn3rd77 | esphome/components/ens210/* @itn3rd77 | ||||||
| esphome/components/esp32/* @esphome/core | esphome/components/esp32/* @esphome/core | ||||||
| esphome/components/esp32_ble/* @jesserockz | esphome/components/esp32_ble/* @Rapsssito @jesserockz | ||||||
| esphome/components/esp32_ble_client/* @jesserockz | esphome/components/esp32_ble_client/* @jesserockz | ||||||
| esphome/components/esp32_ble_server/* @clydebarrow @jesserockz | esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz | ||||||
| esphome/components/esp32_camera_web_server/* @ayufan | esphome/components/esp32_camera_web_server/* @ayufan | ||||||
| esphome/components/esp32_can/* @Sympatron | esphome/components/esp32_can/* @Sympatron | ||||||
| esphome/components/esp32_improv/* @jesserockz | esphome/components/esp32_improv/* @jesserockz | ||||||
| @@ -110,6 +115,7 @@ esphome/components/gp8403/* @jesserockz | |||||||
| esphome/components/gpio/* @esphome/core | esphome/components/gpio/* @esphome/core | ||||||
| esphome/components/gps/* @coogle | esphome/components/gps/* @coogle | ||||||
| esphome/components/graph/* @synco | esphome/components/graph/* @synco | ||||||
|  | esphome/components/gree/* @orestismers | ||||||
| esphome/components/grove_tb6612fng/* @max246 | esphome/components/grove_tb6612fng/* @max246 | ||||||
| esphome/components/growatt_solar/* @leeuwte | esphome/components/growatt_solar/* @leeuwte | ||||||
| esphome/components/haier/* @paveldn | esphome/components/haier/* @paveldn | ||||||
| @@ -121,6 +127,7 @@ esphome/components/hitachi_ac424/* @sourabhjaiswal | |||||||
| esphome/components/hm3301/* @freekode | esphome/components/hm3301/* @freekode | ||||||
| esphome/components/homeassistant/* @OttoWinter | esphome/components/homeassistant/* @OttoWinter | ||||||
| esphome/components/honeywellabp/* @RubyBailey | esphome/components/honeywellabp/* @RubyBailey | ||||||
|  | esphome/components/honeywellabp2_i2c/* @jpfaff | ||||||
| esphome/components/host/* @esphome/core | esphome/components/host/* @esphome/core | ||||||
| esphome/components/hrxl_maxsonar_wr/* @netmikey | esphome/components/hrxl_maxsonar_wr/* @netmikey | ||||||
| esphome/components/hte501/* @Stock-M | esphome/components/hte501/* @Stock-M | ||||||
| @@ -131,6 +138,7 @@ esphome/components/i2s_audio/* @jesserockz | |||||||
| esphome/components/i2s_audio/media_player/* @jesserockz | esphome/components/i2s_audio/media_player/* @jesserockz | ||||||
| esphome/components/i2s_audio/microphone/* @jesserockz | esphome/components/i2s_audio/microphone/* @jesserockz | ||||||
| esphome/components/i2s_audio/speaker/* @jesserockz | esphome/components/i2s_audio/speaker/* @jesserockz | ||||||
|  | esphome/components/iaqcore/* @yozik04 | ||||||
| esphome/components/ili9xxx/* @clydebarrow @nielsnl68 | esphome/components/ili9xxx/* @clydebarrow @nielsnl68 | ||||||
| esphome/components/improv_base/* @esphome/core | esphome/components/improv_base/* @esphome/core | ||||||
| esphome/components/improv_serial/* @esphome/core | esphome/components/improv_serial/* @esphome/core | ||||||
| @@ -147,6 +155,7 @@ esphome/components/key_provider/* @ssieb | |||||||
| esphome/components/kuntze/* @ssieb | esphome/components/kuntze/* @ssieb | ||||||
| esphome/components/lcd_menu/* @numo68 | esphome/components/lcd_menu/* @numo68 | ||||||
| esphome/components/ld2410/* @regevbr @sebcaps | esphome/components/ld2410/* @regevbr @sebcaps | ||||||
|  | esphome/components/ld2420/* @descipher | ||||||
| esphome/components/ledc/* @OttoWinter | esphome/components/ledc/* @OttoWinter | ||||||
| esphome/components/libretiny/* @kuba2k2 | esphome/components/libretiny/* @kuba2k2 | ||||||
| esphome/components/libretiny_pwm/* @kuba2k2 | esphome/components/libretiny_pwm/* @kuba2k2 | ||||||
| @@ -178,6 +187,7 @@ esphome/components/mcp9808/* @k7hpn | |||||||
| esphome/components/md5/* @esphome/core | esphome/components/md5/* @esphome/core | ||||||
| esphome/components/mdns/* @esphome/core | esphome/components/mdns/* @esphome/core | ||||||
| esphome/components/media_player/* @jesserockz | esphome/components/media_player/* @jesserockz | ||||||
|  | esphome/components/micronova/* @jorre05 | ||||||
| esphome/components/microphone/* @jesserockz | esphome/components/microphone/* @jesserockz | ||||||
| esphome/components/mics_4514/* @jesserockz | esphome/components/mics_4514/* @jesserockz | ||||||
| esphome/components/midea/* @dudanov | esphome/components/midea/* @dudanov | ||||||
| @@ -207,11 +217,12 @@ esphome/components/nextion/sensor/* @senexcrenshaw | |||||||
| esphome/components/nextion/switch/* @senexcrenshaw | esphome/components/nextion/switch/* @senexcrenshaw | ||||||
| esphome/components/nextion/text_sensor/* @senexcrenshaw | esphome/components/nextion/text_sensor/* @senexcrenshaw | ||||||
| esphome/components/nfc/* @jesserockz | esphome/components/nfc/* @jesserockz | ||||||
|  | esphome/components/noblex/* @AGalfra | ||||||
| esphome/components/number/* @esphome/core | esphome/components/number/* @esphome/core | ||||||
| esphome/components/ota/* @esphome/core | esphome/components/ota/* @esphome/core | ||||||
| esphome/components/output/* @esphome/core | esphome/components/output/* @esphome/core | ||||||
| esphome/components/pca6416a/* @Mat931 | esphome/components/pca6416a/* @Mat931 | ||||||
| esphome/components/pca9554/* @hwstar | esphome/components/pca9554/* @clydebarrow @hwstar | ||||||
| esphome/components/pcf85063/* @brogon | esphome/components/pcf85063/* @brogon | ||||||
| esphome/components/pcf8563/* @KoenBreeman | esphome/components/pcf8563/* @KoenBreeman | ||||||
| esphome/components/pid/* @OttoWinter | esphome/components/pid/* @OttoWinter | ||||||
| @@ -229,11 +240,13 @@ esphome/components/pulse_meter/* @TrentHouliston @cstaahl @stevebaxter | |||||||
| esphome/components/pvvx_mithermometer/* @pasiz | esphome/components/pvvx_mithermometer/* @pasiz | ||||||
| esphome/components/qmp6988/* @andrewpc | esphome/components/qmp6988/* @andrewpc | ||||||
| esphome/components/qr_code/* @wjtje | esphome/components/qr_code/* @wjtje | ||||||
|  | esphome/components/qwiic_pir/* @kahrendt | ||||||
| esphome/components/radon_eye_ble/* @jeffeb3 | esphome/components/radon_eye_ble/* @jeffeb3 | ||||||
| esphome/components/radon_eye_rd200/* @jeffeb3 | esphome/components/radon_eye_rd200/* @jeffeb3 | ||||||
| esphome/components/rc522/* @glmnet | esphome/components/rc522/* @glmnet | ||||||
| esphome/components/rc522_i2c/* @glmnet | esphome/components/rc522_i2c/* @glmnet | ||||||
| esphome/components/rc522_spi/* @glmnet | esphome/components/rc522_spi/* @glmnet | ||||||
|  | esphome/components/resistance_sampler/* @jesserockz | ||||||
| esphome/components/restart/* @esphome/core | esphome/components/restart/* @esphome/core | ||||||
| esphome/components/rf_bridge/* @jesserockz | esphome/components/rf_bridge/* @jesserockz | ||||||
| esphome/components/rgbct/* @jesserockz | esphome/components/rgbct/* @jesserockz | ||||||
| @@ -254,6 +267,7 @@ esphome/components/sen21231/* @shreyaskarnik | |||||||
| esphome/components/sen5x/* @martgras | esphome/components/sen5x/* @martgras | ||||||
| esphome/components/sensirion_common/* @martgras | esphome/components/sensirion_common/* @martgras | ||||||
| esphome/components/sensor/* @esphome/core | esphome/components/sensor/* @esphome/core | ||||||
|  | esphome/components/sfa30/* @ghsensdev | ||||||
| esphome/components/sgp40/* @SenexCrenshaw | esphome/components/sgp40/* @SenexCrenshaw | ||||||
| esphome/components/sgp4x/* @SenexCrenshaw @martgras | esphome/components/sgp4x/* @SenexCrenshaw @martgras | ||||||
| esphome/components/shelly_dimmer/* @edge90 @rnauber | esphome/components/shelly_dimmer/* @edge90 @rnauber | ||||||
| @@ -299,6 +313,7 @@ esphome/components/tcl112/* @glmnet | |||||||
| esphome/components/tee501/* @Stock-M | esphome/components/tee501/* @Stock-M | ||||||
| esphome/components/teleinfo/* @0hax | esphome/components/teleinfo/* @0hax | ||||||
| esphome/components/template/alarm_control_panel/* @grahambrown11 | esphome/components/template/alarm_control_panel/* @grahambrown11 | ||||||
|  | esphome/components/text/* @mauritskorse | ||||||
| esphome/components/thermostat/* @kbx81 | esphome/components/thermostat/* @kbx81 | ||||||
| esphome/components/time/* @OttoWinter | esphome/components/time/* @OttoWinter | ||||||
| esphome/components/tlc5947/* @rnauber | esphome/components/tlc5947/* @rnauber | ||||||
| @@ -322,6 +337,7 @@ esphome/components/tuya/sensor/* @jesserockz | |||||||
| esphome/components/tuya/switch/* @jesserockz | esphome/components/tuya/switch/* @jesserockz | ||||||
| esphome/components/tuya/text_sensor/* @dentra | esphome/components/tuya/text_sensor/* @dentra | ||||||
| esphome/components/uart/* @esphome/core | esphome/components/uart/* @esphome/core | ||||||
|  | esphome/components/uart/button/* @ssieb | ||||||
| esphome/components/ufire_ec/* @pvizeli | esphome/components/ufire_ec/* @pvizeli | ||||||
| esphome/components/ufire_ise/* @pvizeli | esphome/components/ufire_ise/* @pvizeli | ||||||
| esphome/components/ultrasonic/* @OttoWinter | esphome/components/ultrasonic/* @OttoWinter | ||||||
| @@ -337,10 +353,12 @@ esphome/components/wiegand/* @ssieb | |||||||
| esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard | esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard | ||||||
| esphome/components/wl_134/* @hobbypunk90 | esphome/components/wl_134/* @hobbypunk90 | ||||||
| esphome/components/x9c/* @EtienneMD | esphome/components/x9c/* @EtienneMD | ||||||
|  | esphome/components/xgzp68xx/* @gcormier | ||||||
| esphome/components/xiaomi_lywsd03mmc/* @ahpohl | esphome/components/xiaomi_lywsd03mmc/* @ahpohl | ||||||
| esphome/components/xiaomi_mhoc303/* @drug123 | esphome/components/xiaomi_mhoc303/* @drug123 | ||||||
| esphome/components/xiaomi_mhoc401/* @vevsvevs | esphome/components/xiaomi_mhoc401/* @vevsvevs | ||||||
| esphome/components/xiaomi_rtcgq02lm/* @jesserockz | esphome/components/xiaomi_rtcgq02lm/* @jesserockz | ||||||
| esphome/components/xl9535/* @mreditor97 | esphome/components/xl9535/* @mreditor97 | ||||||
| esphome/components/xpt2046/* @nielsnl68 @numo68 | esphome/components/xpt2046/* @nielsnl68 @numo68 | ||||||
|  | esphome/components/zhlt01/* @cfeenstra1024 | ||||||
| esphome/components/zio_ultrasonic/* @kahrendt | esphome/components/zio_ultrasonic/* @kahrendt | ||||||
|   | |||||||
| @@ -5,39 +5,52 @@ | |||||||
| # One of "docker", "hassio" | # One of "docker", "hassio" | ||||||
| ARG BASEIMGTYPE=docker | ARG BASEIMGTYPE=docker | ||||||
|  |  | ||||||
|  |  | ||||||
| # https://github.com/hassio-addons/addon-debian-base/releases | # https://github.com/hassio-addons/addon-debian-base/releases | ||||||
| FROM ghcr.io/hassio-addons/debian-base:6.2.3 AS base-hassio | FROM ghcr.io/hassio-addons/debian-base:7.2.0 AS base-hassio | ||||||
| # https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye | # https://hub.docker.com/_/debian?tab=tags&page=1&name=bookworm | ||||||
| FROM debian:bullseye-20230208-slim AS base-docker | FROM debian:12.2-slim AS base-docker | ||||||
|  |  | ||||||
| FROM base-${BASEIMGTYPE} AS base | FROM base-${BASEIMGTYPE} AS base | ||||||
|  |  | ||||||
|  |  | ||||||
| ARG TARGETARCH | ARG TARGETARCH | ||||||
| ARG TARGETVARIANT | ARG TARGETVARIANT | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Note that --break-system-packages is used below because | ||||||
|  | # https://peps.python.org/pep-0668/ added a safety check that prevents | ||||||
|  | # installing packages with the same name as a system package. This is | ||||||
|  | # not a problem for us because we are not concerned about overwriting | ||||||
|  | # system packages because we are running in an isolated container. | ||||||
|  |  | ||||||
| RUN \ | RUN \ | ||||||
|     apt-get update \ |     apt-get update \ | ||||||
|     # Use pinned versions so that we get updates with build caching |     # Use pinned versions so that we get updates with build caching | ||||||
|     && apt-get install -y --no-install-recommends \ |     && apt-get install -y --no-install-recommends \ | ||||||
|         python3=3.9.2-3 \ |         python3-pip=23.0.1+dfsg-1 \ | ||||||
|         python3-pip=20.3.4-4+deb11u1 \ |         python3-setuptools=66.1.1-1 \ | ||||||
|         python3-setuptools=52.0.0-4 \ |         python3-venv=3.11.2-1+b1 \ | ||||||
|         python3-cryptography=3.3.2-1 \ |         python3-wheel=0.38.4-2 \ | ||||||
|         python3-venv=3.9.2-3 \ |         iputils-ping=3:20221126-1 \ | ||||||
|         iputils-ping=3:20210202-1 \ |         git=1:2.39.2-1.1 \ | ||||||
|         git=1:2.30.2-1+deb11u2 \ |         curl=7.88.1-10+deb12u4 \ | ||||||
|         curl=7.74.0-1.3+deb11u10 \ |         openssh-client=1:9.2p1-2+deb12u1 \ | ||||||
|         openssh-client=1:8.4p1-5+deb11u2 \ |         python3-cffi=1.15.1-5 \ | ||||||
|         python3-cffi=1.14.5-1 \ |         libcairo2=1.16.0-7 \ | ||||||
|         libcairo2=1.16.0-5 \ |  | ||||||
|         patch=2.7.6-7; \ |         patch=2.7.6-7; \ | ||||||
|     if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ |     if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ | ||||||
|         apt-get install -y --no-install-recommends \ |         apt-get install -y --no-install-recommends \ | ||||||
|           build-essential=12.9 \ |           build-essential=12.9 \ | ||||||
|           python3-dev=3.9.2-3 \ |           python3-dev=3.11.2-1+b1 \ | ||||||
|           zlib1g-dev=1:1.2.11.dfsg-2+deb11u2 \ |           zlib1g-dev=1:1.2.13.dfsg-1 \ | ||||||
|           libjpeg-dev=1:2.0.6-4 \ |           libjpeg-dev=1:2.1.5-2 \ | ||||||
|           libfreetype-dev=2.10.4+dfsg-1+deb11u1; \ |           libfreetype-dev=2.12.1+dfsg-5 \ | ||||||
|  |           libssl-dev=3.0.11-1~deb12u2 \ | ||||||
|  |           libffi-dev=3.4.4-1 \ | ||||||
|  |           cargo=0.66.0+ds1-1 \ | ||||||
|  |           pkg-config=1.8.1-1 \ | ||||||
|  |           gcc-arm-linux-gnueabihf=4:12.2.0-3; \ | ||||||
|     fi; \ |     fi; \ | ||||||
|     rm -rf \ |     rm -rf \ | ||||||
|         /tmp/* \ |         /tmp/* \ | ||||||
| @@ -60,9 +73,12 @@ RUN \ | |||||||
|  |  | ||||||
| RUN \ | RUN \ | ||||||
|     # Ubuntu python3-pip is missing wheel |     # Ubuntu python3-pip is missing wheel | ||||||
|     pip3 install --no-cache-dir \ |     if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ | ||||||
|         wheel==0.37.1 \ |         export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ | ||||||
|         platformio==6.1.11 \ |     fi; \ | ||||||
|  |     pip3 install \ | ||||||
|  |     --break-system-packages --no-cache-dir \ | ||||||
|  |     platformio==6.1.11 \ | ||||||
|     # Change some platformio settings |     # Change some platformio settings | ||||||
|     && platformio settings set enable_telemetry No \ |     && platformio settings set enable_telemetry No \ | ||||||
|     && platformio settings set check_platformio_interval 1000000 \ |     && platformio settings set check_platformio_interval 1000000 \ | ||||||
| @@ -70,9 +86,15 @@ RUN \ | |||||||
|  |  | ||||||
|  |  | ||||||
| # First install requirements to leverage caching when requirements don't change | # First install requirements to leverage caching when requirements don't change | ||||||
|  | # tmpfs is for https://github.com/rust-lang/cargo/issues/8719 | ||||||
|  |  | ||||||
| COPY requirements.txt requirements_optional.txt script/platformio_install_deps.py platformio.ini / | COPY requirements.txt requirements_optional.txt script/platformio_install_deps.py platformio.ini / | ||||||
| RUN \ | RUN --mount=type=tmpfs,target=/root/.cargo if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ | ||||||
|     pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \ |         export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ | ||||||
|  |     fi; \ | ||||||
|  |     CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo \ | ||||||
|  |     pip3 install \ | ||||||
|  |     --break-system-packages --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \ | ||||||
|     && /platformio_install_deps.py /platformio.ini --libraries |     && /platformio_install_deps.py /platformio.ini --libraries | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -81,7 +103,11 @@ FROM base AS docker | |||||||
|  |  | ||||||
| # Copy esphome and install | # Copy esphome and install | ||||||
| COPY . /esphome | COPY . /esphome | ||||||
| RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome | RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ | ||||||
|  |         export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ | ||||||
|  |   fi; \ | ||||||
|  |   pip3 install \ | ||||||
|  |   --break-system-packages --no-cache-dir --no-use-pep517 -e /esphome | ||||||
|  |  | ||||||
| # Settings for dashboard | # Settings for dashboard | ||||||
| ENV USERNAME="" PASSWORD="" | ENV USERNAME="" PASSWORD="" | ||||||
| @@ -89,6 +115,10 @@ ENV USERNAME="" PASSWORD="" | |||||||
| # Expose the dashboard to Docker | # Expose the dashboard to Docker | ||||||
| EXPOSE 6052 | EXPOSE 6052 | ||||||
|  |  | ||||||
|  | # Run healthcheck (heartbeat) | ||||||
|  | HEALTHCHECK --interval=30s --timeout=30s \ | ||||||
|  |   CMD curl --fail http://localhost:6052/version -A "HealthCheck" || exit 1 | ||||||
|  |  | ||||||
| COPY docker/docker_entrypoint.sh /entrypoint.sh | COPY docker/docker_entrypoint.sh /entrypoint.sh | ||||||
|  |  | ||||||
| # The directory the user should mount their configuration files to | # The directory the user should mount their configuration files to | ||||||
| @@ -110,7 +140,7 @@ RUN \ | |||||||
|     apt-get update \ |     apt-get update \ | ||||||
|     # Use pinned versions so that we get updates with build caching |     # Use pinned versions so that we get updates with build caching | ||||||
|     && apt-get install -y --no-install-recommends \ |     && apt-get install -y --no-install-recommends \ | ||||||
|         nginx-light=1.18.0-6.1+deb11u3 \ |         nginx-light=1.22.1-9 \ | ||||||
|     && rm -rf \ |     && rm -rf \ | ||||||
|         /tmp/* \ |         /tmp/* \ | ||||||
|         /var/{cache,log}/* \ |         /var/{cache,log}/* \ | ||||||
| @@ -123,7 +153,11 @@ COPY docker/ha-addon-rootfs/ / | |||||||
|  |  | ||||||
| # Copy esphome and install | # Copy esphome and install | ||||||
| COPY . /esphome | COPY . /esphome | ||||||
| RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome | RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ | ||||||
|  |         export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ | ||||||
|  |   fi; \ | ||||||
|  |   pip3 install \ | ||||||
|  |   --break-system-packages --no-cache-dir --no-use-pep517 -e /esphome | ||||||
|  |  | ||||||
| # Labels | # Labels | ||||||
| LABEL \ | LABEL \ | ||||||
| @@ -146,20 +180,24 @@ RUN \ | |||||||
|     apt-get update \ |     apt-get update \ | ||||||
|     # Use pinned versions so that we get updates with build caching |     # Use pinned versions so that we get updates with build caching | ||||||
|     && apt-get install -y --no-install-recommends \ |     && apt-get install -y --no-install-recommends \ | ||||||
|         clang-format-13=1:13.0.1-6~deb11u1 \ |         clang-format-13=1:13.0.1-11+b2 \ | ||||||
|         clang-tidy-11=1:11.0.1-2 \ |         clang-tidy-14=1:14.0.6-12 \ | ||||||
|         patch=2.7.6-7 \ |         patch=2.7.6-7 \ | ||||||
|         software-properties-common=0.96.20.2-2.1 \ |         software-properties-common=0.99.30-4 \ | ||||||
|         nano=5.4-2+deb11u2 \ |         nano=7.2-1 \ | ||||||
|         build-essential=12.9 \ |         build-essential=12.9 \ | ||||||
|         python3-dev=3.9.2-3 \ |         python3-dev=3.11.2-1+b1 \ | ||||||
|     && rm -rf \ |     && rm -rf \ | ||||||
|         /tmp/* \ |         /tmp/* \ | ||||||
|         /var/{cache,log}/* \ |         /var/{cache,log}/* \ | ||||||
|         /var/lib/apt/lists/* |         /var/lib/apt/lists/* | ||||||
|  |  | ||||||
| COPY requirements_test.txt / | COPY requirements_test.txt / | ||||||
| RUN pip3 install --no-cache-dir -r /requirements_test.txt | RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ | ||||||
|  |         export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ | ||||||
|  |   fi; \ | ||||||
|  |   pip3 install \ | ||||||
|  |   --break-system-packages --no-cache-dir -r /requirements_test.txt | ||||||
|  |  | ||||||
| VOLUME ["/esphome"] | VOLUME ["/esphome"] | ||||||
| WORKDIR /esphome | WORKDIR /esphome | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | # PYTHON_ARGCOMPLETE_OK | ||||||
| import argparse | import argparse | ||||||
| import functools | import functools | ||||||
| import logging | import logging | ||||||
| @@ -7,6 +8,8 @@ import sys | |||||||
| import time | import time | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
|  |  | ||||||
|  | import argcomplete | ||||||
|  |  | ||||||
| from esphome import const, writer, yaml_util | from esphome import const, writer, yaml_util | ||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| from esphome.config import iter_components, read_config, strip_default_ids | from esphome.config import iter_components, read_config, strip_default_ids | ||||||
| @@ -966,6 +969,7 @@ def parse_args(argv): | |||||||
|     # Finally, run the new-style parser again with the possibly swapped arguments, |     # Finally, run the new-style parser again with the possibly swapped arguments, | ||||||
|     # and let it error out if the command is unparsable. |     # and let it error out if the command is unparsable. | ||||||
|     parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion) |     parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion) | ||||||
|  |     argcomplete.autocomplete(parser) | ||||||
|     return parser.parse_args(arguments) |     return parser.parse_args(arguments) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1 @@ | |||||||
|  | CODEOWNERS = ["@angelnu"] | ||||||
|   | |||||||
| @@ -1,53 +0,0 @@ | |||||||
| #include "ade7953.h" |  | ||||||
| #include "esphome/core/log.h" |  | ||||||
|  |  | ||||||
| namespace esphome { |  | ||||||
| namespace ade7953 { |  | ||||||
|  |  | ||||||
| static const char *const TAG = "ade7953"; |  | ||||||
|  |  | ||||||
| void ADE7953::dump_config() { |  | ||||||
|   ESP_LOGCONFIG(TAG, "ADE7953:"); |  | ||||||
|   LOG_PIN("  IRQ Pin: ", irq_pin_); |  | ||||||
|   LOG_I2C_DEVICE(this); |  | ||||||
|   LOG_UPDATE_INTERVAL(this); |  | ||||||
|   LOG_SENSOR("  ", "Voltage Sensor", this->voltage_sensor_); |  | ||||||
|   LOG_SENSOR("  ", "Current A Sensor", this->current_a_sensor_); |  | ||||||
|   LOG_SENSOR("  ", "Current B Sensor", this->current_b_sensor_); |  | ||||||
|   LOG_SENSOR("  ", "Active Power A Sensor", this->active_power_a_sensor_); |  | ||||||
|   LOG_SENSOR("  ", "Active Power B Sensor", this->active_power_b_sensor_); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define ADE_PUBLISH_(name, val, factor) \ |  | ||||||
|   if (err == i2c::ERROR_OK && this->name##_sensor_) { \ |  | ||||||
|     float value = (val) / (factor); \ |  | ||||||
|     this->name##_sensor_->publish_state(value); \ |  | ||||||
|   } |  | ||||||
| #define ADE_PUBLISH(name, val, factor) ADE_PUBLISH_(name, val, factor) |  | ||||||
|  |  | ||||||
| void ADE7953::update() { |  | ||||||
|   if (!this->is_setup_) |  | ||||||
|     return; |  | ||||||
|  |  | ||||||
|   uint32_t val; |  | ||||||
|   i2c::ErrorCode err = ade_read_32_(0x0312, &val); |  | ||||||
|   ADE_PUBLISH(active_power_a, (int32_t) val, 154.0f); |  | ||||||
|   err = ade_read_32_(0x0313, &val); |  | ||||||
|   ADE_PUBLISH(active_power_b, (int32_t) val, 154.0f); |  | ||||||
|   err = ade_read_32_(0x031A, &val); |  | ||||||
|   ADE_PUBLISH(current_a, (uint32_t) val, 100000.0f); |  | ||||||
|   err = ade_read_32_(0x031B, &val); |  | ||||||
|   ADE_PUBLISH(current_b, (uint32_t) val, 100000.0f); |  | ||||||
|   err = ade_read_32_(0x031C, &val); |  | ||||||
|   ADE_PUBLISH(voltage, (uint32_t) val, 26000.0f); |  | ||||||
|  |  | ||||||
|   //    auto apparent_power_a = this->ade_read_<int32_t>(0x0310); |  | ||||||
|   //    auto apparent_power_b = this->ade_read_<int32_t>(0x0311); |  | ||||||
|   //    auto reactive_power_a = this->ade_read_<int32_t>(0x0314); |  | ||||||
|   //    auto reactive_power_b = this->ade_read_<int32_t>(0x0315); |  | ||||||
|   //    auto power_factor_a = this->ade_read_<int16_t>(0x010A); |  | ||||||
|   //    auto power_factor_b = this->ade_read_<int16_t>(0x010B); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| }  // namespace ade7953 |  | ||||||
| }  // namespace esphome |  | ||||||
| @@ -1,97 +0,0 @@ | |||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" |  | ||||||
| #include "esphome/core/hal.h" |  | ||||||
| #include "esphome/components/i2c/i2c.h" |  | ||||||
| #include "esphome/components/sensor/sensor.h" |  | ||||||
|  |  | ||||||
| #include <vector> |  | ||||||
|  |  | ||||||
| namespace esphome { |  | ||||||
| namespace ade7953 { |  | ||||||
|  |  | ||||||
| class ADE7953 : public i2c::I2CDevice, public PollingComponent { |  | ||||||
|  public: |  | ||||||
|   void set_irq_pin(InternalGPIOPin *irq_pin) { irq_pin_ = irq_pin; } |  | ||||||
|   void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } |  | ||||||
|   void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; } |  | ||||||
|   void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; } |  | ||||||
|   void set_active_power_a_sensor(sensor::Sensor *active_power_a_sensor) { |  | ||||||
|     active_power_a_sensor_ = active_power_a_sensor; |  | ||||||
|   } |  | ||||||
|   void set_active_power_b_sensor(sensor::Sensor *active_power_b_sensor) { |  | ||||||
|     active_power_b_sensor_ = active_power_b_sensor; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void setup() override { |  | ||||||
|     if (this->irq_pin_ != nullptr) { |  | ||||||
|       this->irq_pin_->setup(); |  | ||||||
|     } |  | ||||||
|     this->set_timeout(100, [this]() { |  | ||||||
|       this->ade_write_8_(0x0010, 0x04); |  | ||||||
|       this->ade_write_8_(0x00FE, 0xAD); |  | ||||||
|       this->ade_write_16_(0x0120, 0x0030); |  | ||||||
|       this->is_setup_ = true; |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void dump_config() override; |  | ||||||
|  |  | ||||||
|   void update() override; |  | ||||||
|  |  | ||||||
|  protected: |  | ||||||
|   i2c::ErrorCode ade_write_8_(uint16_t reg, uint8_t value) { |  | ||||||
|     std::vector<uint8_t> data; |  | ||||||
|     data.push_back(reg >> 8); |  | ||||||
|     data.push_back(reg >> 0); |  | ||||||
|     data.push_back(value); |  | ||||||
|     return write(data.data(), data.size()); |  | ||||||
|   } |  | ||||||
|   i2c::ErrorCode ade_write_16_(uint16_t reg, uint16_t value) { |  | ||||||
|     std::vector<uint8_t> data; |  | ||||||
|     data.push_back(reg >> 8); |  | ||||||
|     data.push_back(reg >> 0); |  | ||||||
|     data.push_back(value >> 8); |  | ||||||
|     data.push_back(value >> 0); |  | ||||||
|     return write(data.data(), data.size()); |  | ||||||
|   } |  | ||||||
|   i2c::ErrorCode ade_write_32_(uint16_t reg, uint32_t value) { |  | ||||||
|     std::vector<uint8_t> data; |  | ||||||
|     data.push_back(reg >> 8); |  | ||||||
|     data.push_back(reg >> 0); |  | ||||||
|     data.push_back(value >> 24); |  | ||||||
|     data.push_back(value >> 16); |  | ||||||
|     data.push_back(value >> 8); |  | ||||||
|     data.push_back(value >> 0); |  | ||||||
|     return write(data.data(), data.size()); |  | ||||||
|   } |  | ||||||
|   i2c::ErrorCode ade_read_32_(uint16_t reg, uint32_t *value) { |  | ||||||
|     uint8_t reg_data[2]; |  | ||||||
|     reg_data[0] = reg >> 8; |  | ||||||
|     reg_data[1] = reg >> 0; |  | ||||||
|     i2c::ErrorCode err = write(reg_data, 2); |  | ||||||
|     if (err != i2c::ERROR_OK) |  | ||||||
|       return err; |  | ||||||
|     uint8_t recv[4]; |  | ||||||
|     err = read(recv, 4); |  | ||||||
|     if (err != i2c::ERROR_OK) |  | ||||||
|       return err; |  | ||||||
|     *value = 0; |  | ||||||
|     *value |= ((uint32_t) recv[0]) << 24; |  | ||||||
|     *value |= ((uint32_t) recv[1]) << 16; |  | ||||||
|     *value |= ((uint32_t) recv[2]) << 8; |  | ||||||
|     *value |= ((uint32_t) recv[3]); |  | ||||||
|     return i2c::ERROR_OK; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   InternalGPIOPin *irq_pin_{nullptr}; |  | ||||||
|   bool is_setup_{false}; |  | ||||||
|   sensor::Sensor *voltage_sensor_{nullptr}; |  | ||||||
|   sensor::Sensor *current_a_sensor_{nullptr}; |  | ||||||
|   sensor::Sensor *current_b_sensor_{nullptr}; |  | ||||||
|   sensor::Sensor *active_power_a_sensor_{nullptr}; |  | ||||||
|   sensor::Sensor *active_power_b_sensor_{nullptr}; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| }  // namespace ade7953 |  | ||||||
| }  // namespace esphome |  | ||||||
| @@ -1,90 +1,5 @@ | |||||||
| import esphome.codegen as cg |  | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome.components import sensor, i2c |  | ||||||
| from esphome import pins | CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid( | ||||||
| from esphome.const import ( |     "The ade7953 sensor component has been renamed to ade7953_i2c." | ||||||
|     CONF_ID, |  | ||||||
|     CONF_IRQ_PIN, |  | ||||||
|     CONF_VOLTAGE, |  | ||||||
|     DEVICE_CLASS_CURRENT, |  | ||||||
|     DEVICE_CLASS_POWER, |  | ||||||
|     DEVICE_CLASS_VOLTAGE, |  | ||||||
|     STATE_CLASS_MEASUREMENT, |  | ||||||
|     UNIT_AMPERE, |  | ||||||
|     UNIT_VOLT, |  | ||||||
|     UNIT_WATT, |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| DEPENDENCIES = ["i2c"] |  | ||||||
|  |  | ||||||
| ade7953_ns = cg.esphome_ns.namespace("ade7953") |  | ||||||
| ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice) |  | ||||||
|  |  | ||||||
| CONF_CURRENT_A = "current_a" |  | ||||||
| CONF_CURRENT_B = "current_b" |  | ||||||
| CONF_ACTIVE_POWER_A = "active_power_a" |  | ||||||
| CONF_ACTIVE_POWER_B = "active_power_b" |  | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = ( |  | ||||||
|     cv.Schema( |  | ||||||
|         { |  | ||||||
|             cv.GenerateID(): cv.declare_id(ADE7953), |  | ||||||
|             cv.Optional(CONF_IRQ_PIN): pins.internal_gpio_input_pin_schema, |  | ||||||
|             cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( |  | ||||||
|                 unit_of_measurement=UNIT_VOLT, |  | ||||||
|                 accuracy_decimals=1, |  | ||||||
|                 device_class=DEVICE_CLASS_VOLTAGE, |  | ||||||
|                 state_class=STATE_CLASS_MEASUREMENT, |  | ||||||
|             ), |  | ||||||
|             cv.Optional(CONF_CURRENT_A): sensor.sensor_schema( |  | ||||||
|                 unit_of_measurement=UNIT_AMPERE, |  | ||||||
|                 accuracy_decimals=2, |  | ||||||
|                 device_class=DEVICE_CLASS_CURRENT, |  | ||||||
|                 state_class=STATE_CLASS_MEASUREMENT, |  | ||||||
|             ), |  | ||||||
|             cv.Optional(CONF_CURRENT_B): sensor.sensor_schema( |  | ||||||
|                 unit_of_measurement=UNIT_AMPERE, |  | ||||||
|                 accuracy_decimals=2, |  | ||||||
|                 device_class=DEVICE_CLASS_CURRENT, |  | ||||||
|                 state_class=STATE_CLASS_MEASUREMENT, |  | ||||||
|             ), |  | ||||||
|             cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema( |  | ||||||
|                 unit_of_measurement=UNIT_WATT, |  | ||||||
|                 accuracy_decimals=1, |  | ||||||
|                 device_class=DEVICE_CLASS_POWER, |  | ||||||
|                 state_class=STATE_CLASS_MEASUREMENT, |  | ||||||
|             ), |  | ||||||
|             cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema( |  | ||||||
|                 unit_of_measurement=UNIT_WATT, |  | ||||||
|                 accuracy_decimals=1, |  | ||||||
|                 device_class=DEVICE_CLASS_POWER, |  | ||||||
|                 state_class=STATE_CLASS_MEASUREMENT, |  | ||||||
|             ), |  | ||||||
|         } |  | ||||||
|     ) |  | ||||||
|     .extend(cv.polling_component_schema("60s")) |  | ||||||
|     .extend(i2c.i2c_device_schema(0x38)) |  | ||||||
| ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| async def to_code(config): |  | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |  | ||||||
|     await cg.register_component(var, config) |  | ||||||
|     await i2c.register_i2c_device(var, config) |  | ||||||
|  |  | ||||||
|     if irq_pin_config := config.get(CONF_IRQ_PIN): |  | ||||||
|         irq_pin = await cg.gpio_pin_expression(irq_pin_config) |  | ||||||
|         cg.add(var.set_irq_pin(irq_pin)) |  | ||||||
|  |  | ||||||
|     for key in [ |  | ||||||
|         CONF_VOLTAGE, |  | ||||||
|         CONF_CURRENT_A, |  | ||||||
|         CONF_CURRENT_B, |  | ||||||
|         CONF_ACTIVE_POWER_A, |  | ||||||
|         CONF_ACTIVE_POWER_B, |  | ||||||
|     ]: |  | ||||||
|         if key not in config: |  | ||||||
|             continue |  | ||||||
|         conf = config[key] |  | ||||||
|         sens = await sensor.new_sensor(conf) |  | ||||||
|         cg.add(getattr(var, f"set_{key}_sensor")(sens)) |  | ||||||
|   | |||||||
							
								
								
									
										196
									
								
								esphome/components/ade7953_base/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								esphome/components/ade7953_base/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,196 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import sensor | ||||||
|  | from esphome import pins | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_IRQ_PIN, | ||||||
|  |     CONF_VOLTAGE, | ||||||
|  |     CONF_FREQUENCY, | ||||||
|  |     DEVICE_CLASS_CURRENT, | ||||||
|  |     DEVICE_CLASS_APPARENT_POWER, | ||||||
|  |     DEVICE_CLASS_POWER, | ||||||
|  |     DEVICE_CLASS_REACTIVE_POWER, | ||||||
|  |     DEVICE_CLASS_POWER_FACTOR, | ||||||
|  |     DEVICE_CLASS_VOLTAGE, | ||||||
|  |     DEVICE_CLASS_FREQUENCY, | ||||||
|  |     STATE_CLASS_MEASUREMENT, | ||||||
|  |     UNIT_VOLT, | ||||||
|  |     UNIT_HERTZ, | ||||||
|  |     UNIT_AMPERE, | ||||||
|  |     UNIT_VOLT_AMPS, | ||||||
|  |     UNIT_WATT, | ||||||
|  |     UNIT_VOLT_AMPS_REACTIVE, | ||||||
|  |     UNIT_PERCENT, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONF_CURRENT_A = "current_a" | ||||||
|  | CONF_CURRENT_B = "current_b" | ||||||
|  | CONF_ACTIVE_POWER_A = "active_power_a" | ||||||
|  | CONF_ACTIVE_POWER_B = "active_power_b" | ||||||
|  | CONF_APPARENT_POWER_A = "apparent_power_a" | ||||||
|  | CONF_APPARENT_POWER_B = "apparent_power_b" | ||||||
|  | CONF_REACTIVE_POWER_A = "reactive_power_a" | ||||||
|  | CONF_REACTIVE_POWER_B = "reactive_power_b" | ||||||
|  | CONF_POWER_FACTOR_A = "power_factor_a" | ||||||
|  | CONF_POWER_FACTOR_B = "power_factor_b" | ||||||
|  | CONF_VOLTAGE_PGA_GAIN = "voltage_pga_gain" | ||||||
|  | CONF_CURRENT_PGA_GAIN_A = "current_pga_gain_a" | ||||||
|  | CONF_CURRENT_PGA_GAIN_B = "current_pga_gain_b" | ||||||
|  | CONF_VOLTAGE_GAIN = "voltage_gain" | ||||||
|  | CONF_CURRENT_GAIN_A = "current_gain_a" | ||||||
|  | CONF_CURRENT_GAIN_B = "current_gain_b" | ||||||
|  | CONF_ACTIVE_POWER_GAIN_A = "active_power_gain_a" | ||||||
|  | CONF_ACTIVE_POWER_GAIN_B = "active_power_gain_b" | ||||||
|  | PGA_GAINS = { | ||||||
|  |     "1x": 0b000, | ||||||
|  |     "2x": 0b001, | ||||||
|  |     "4x": 0b010, | ||||||
|  |     "8x": 0b011, | ||||||
|  |     "16x": 0b100, | ||||||
|  |     "22x": 0b101, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ade7953_base_ns = cg.esphome_ns.namespace("ade7953_base") | ||||||
|  | ADE7953 = ade7953_base_ns.class_("ADE7953", cg.PollingComponent) | ||||||
|  |  | ||||||
|  | ADE7953_CONFIG_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.Optional(CONF_IRQ_PIN): pins.internal_gpio_input_pin_schema, | ||||||
|  |         cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_VOLT, | ||||||
|  |             accuracy_decimals=1, | ||||||
|  |             device_class=DEVICE_CLASS_VOLTAGE, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_HERTZ, | ||||||
|  |             accuracy_decimals=2, | ||||||
|  |             device_class=DEVICE_CLASS_FREQUENCY, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_CURRENT_A): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_AMPERE, | ||||||
|  |             accuracy_decimals=2, | ||||||
|  |             device_class=DEVICE_CLASS_CURRENT, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_CURRENT_B): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_AMPERE, | ||||||
|  |             accuracy_decimals=2, | ||||||
|  |             device_class=DEVICE_CLASS_CURRENT, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_WATT, | ||||||
|  |             accuracy_decimals=1, | ||||||
|  |             device_class=DEVICE_CLASS_POWER, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_WATT, | ||||||
|  |             accuracy_decimals=1, | ||||||
|  |             device_class=DEVICE_CLASS_POWER, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_APPARENT_POWER_A): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_VOLT_AMPS, | ||||||
|  |             accuracy_decimals=1, | ||||||
|  |             device_class=DEVICE_CLASS_APPARENT_POWER, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_APPARENT_POWER_B): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_VOLT_AMPS, | ||||||
|  |             accuracy_decimals=1, | ||||||
|  |             device_class=DEVICE_CLASS_APPARENT_POWER, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_REACTIVE_POWER_A): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE, | ||||||
|  |             accuracy_decimals=1, | ||||||
|  |             device_class=DEVICE_CLASS_REACTIVE_POWER, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_REACTIVE_POWER_B): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE, | ||||||
|  |             accuracy_decimals=1, | ||||||
|  |             device_class=DEVICE_CLASS_REACTIVE_POWER, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_POWER_FACTOR_A): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_PERCENT, | ||||||
|  |             accuracy_decimals=2, | ||||||
|  |             device_class=DEVICE_CLASS_POWER_FACTOR, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_POWER_FACTOR_B): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_PERCENT, | ||||||
|  |             accuracy_decimals=2, | ||||||
|  |             device_class=DEVICE_CLASS_POWER_FACTOR, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional( | ||||||
|  |             CONF_VOLTAGE_PGA_GAIN, | ||||||
|  |             default="1x", | ||||||
|  |         ): cv.one_of(*PGA_GAINS, lower=True), | ||||||
|  |         cv.Optional( | ||||||
|  |             CONF_CURRENT_PGA_GAIN_A, | ||||||
|  |             default="1x", | ||||||
|  |         ): cv.one_of(*PGA_GAINS, lower=True), | ||||||
|  |         cv.Optional( | ||||||
|  |             CONF_CURRENT_PGA_GAIN_B, | ||||||
|  |             default="1x", | ||||||
|  |         ): cv.one_of(*PGA_GAINS, lower=True), | ||||||
|  |         cv.Optional(CONF_VOLTAGE_GAIN, default=0x400000): cv.hex_int_range( | ||||||
|  |             min=0x100000, max=0x800000 | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_CURRENT_GAIN_A, default=0x400000): cv.hex_int_range( | ||||||
|  |             min=0x100000, max=0x800000 | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_CURRENT_GAIN_B, default=0x400000): cv.hex_int_range( | ||||||
|  |             min=0x100000, max=0x800000 | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_ACTIVE_POWER_GAIN_A, default=0x400000): cv.hex_int_range( | ||||||
|  |             min=0x100000, max=0x800000 | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_ACTIVE_POWER_GAIN_B, default=0x400000): cv.hex_int_range( | ||||||
|  |             min=0x100000, max=0x800000 | ||||||
|  |         ), | ||||||
|  |     } | ||||||
|  | ).extend(cv.polling_component_schema("60s")) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def register_ade7953(var, config): | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |  | ||||||
|  |     if irq_pin_config := config.get(CONF_IRQ_PIN): | ||||||
|  |         irq_pin = await cg.gpio_pin_expression(irq_pin_config) | ||||||
|  |         cg.add(var.set_irq_pin(irq_pin)) | ||||||
|  |  | ||||||
|  |     cg.add(var.set_pga_v(PGA_GAINS[config.get(CONF_VOLTAGE_PGA_GAIN)])) | ||||||
|  |     cg.add(var.set_pga_ia(PGA_GAINS[config.get(CONF_CURRENT_PGA_GAIN_A)])) | ||||||
|  |     cg.add(var.set_pga_ib(PGA_GAINS[config.get(CONF_CURRENT_PGA_GAIN_B)])) | ||||||
|  |     cg.add(var.set_vgain(config.get(CONF_VOLTAGE_GAIN))) | ||||||
|  |     cg.add(var.set_aigain(config.get(CONF_CURRENT_GAIN_A))) | ||||||
|  |     cg.add(var.set_bigain(config.get(CONF_CURRENT_GAIN_B))) | ||||||
|  |     cg.add(var.set_awgain(config.get(CONF_ACTIVE_POWER_GAIN_A))) | ||||||
|  |     cg.add(var.set_bwgain(config.get(CONF_ACTIVE_POWER_GAIN_B))) | ||||||
|  |  | ||||||
|  |     for key in [ | ||||||
|  |         CONF_VOLTAGE, | ||||||
|  |         CONF_FREQUENCY, | ||||||
|  |         CONF_CURRENT_A, | ||||||
|  |         CONF_CURRENT_B, | ||||||
|  |         CONF_POWER_FACTOR_A, | ||||||
|  |         CONF_POWER_FACTOR_B, | ||||||
|  |         CONF_APPARENT_POWER_A, | ||||||
|  |         CONF_APPARENT_POWER_B, | ||||||
|  |         CONF_ACTIVE_POWER_A, | ||||||
|  |         CONF_ACTIVE_POWER_B, | ||||||
|  |         CONF_REACTIVE_POWER_A, | ||||||
|  |         CONF_REACTIVE_POWER_B, | ||||||
|  |     ]: | ||||||
|  |         if key not in config: | ||||||
|  |             continue | ||||||
|  |         conf = config[key] | ||||||
|  |         sens = await sensor.new_sensor(conf) | ||||||
|  |         cg.add(getattr(var, f"set_{key}_sensor")(sens)) | ||||||
							
								
								
									
										129
									
								
								esphome/components/ade7953_base/ade7953_base.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								esphome/components/ade7953_base/ade7953_base.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | |||||||
|  | #include "ade7953_base.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ade7953_base { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "ade7953"; | ||||||
|  |  | ||||||
|  | void ADE7953::setup() { | ||||||
|  |   if (this->irq_pin_ != nullptr) { | ||||||
|  |     this->irq_pin_->setup(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // The chip might take up to 100ms to initialise | ||||||
|  |   this->set_timeout(100, [this]() { | ||||||
|  |     // this->ade_write_8(0x0010, 0x04); | ||||||
|  |     this->ade_write_8(0x00FE, 0xAD); | ||||||
|  |     this->ade_write_16(0x0120, 0x0030); | ||||||
|  |     // Set gains | ||||||
|  |     this->ade_write_8(PGA_V_8, pga_v_); | ||||||
|  |     this->ade_write_8(PGA_IA_8, pga_ia_); | ||||||
|  |     this->ade_write_8(PGA_IB_8, pga_ib_); | ||||||
|  |     this->ade_write_32(AVGAIN_32, vgain_); | ||||||
|  |     this->ade_write_32(AIGAIN_32, aigain_); | ||||||
|  |     this->ade_write_32(BIGAIN_32, bigain_); | ||||||
|  |     this->ade_write_32(AWGAIN_32, awgain_); | ||||||
|  |     this->ade_write_32(BWGAIN_32, bwgain_); | ||||||
|  |     // Read back gains for debugging | ||||||
|  |     this->ade_read_8(PGA_V_8, &pga_v_); | ||||||
|  |     this->ade_read_8(PGA_IA_8, &pga_ia_); | ||||||
|  |     this->ade_read_8(PGA_IB_8, &pga_ib_); | ||||||
|  |     this->ade_read_32(AVGAIN_32, &vgain_); | ||||||
|  |     this->ade_read_32(AIGAIN_32, &aigain_); | ||||||
|  |     this->ade_read_32(BIGAIN_32, &bigain_); | ||||||
|  |     this->ade_read_32(AWGAIN_32, &awgain_); | ||||||
|  |     this->ade_read_32(BWGAIN_32, &bwgain_); | ||||||
|  |     this->is_setup_ = true; | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ADE7953::dump_config() { | ||||||
|  |   LOG_PIN("  IRQ Pin: ", irq_pin_); | ||||||
|  |   LOG_UPDATE_INTERVAL(this); | ||||||
|  |   LOG_SENSOR("  ", "Voltage Sensor", this->voltage_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Current A Sensor", this->current_a_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Current B Sensor", this->current_b_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Power Factor A Sensor", this->power_factor_a_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Power Factor B Sensor", this->power_factor_b_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Apparent Power A Sensor", this->apparent_power_a_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Apparent Power B Sensor", this->apparent_power_b_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Active Power A Sensor", this->active_power_a_sensor_); | ||||||
|  |   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, "  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_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define ADE_PUBLISH_(name, val, factor) \ | ||||||
|  |   if (err == 0 && this->name##_sensor_) { \ | ||||||
|  |     float value = (val) / (factor); \ | ||||||
|  |     this->name##_sensor_->publish_state(value); \ | ||||||
|  |   } | ||||||
|  | #define ADE_PUBLISH(name, val, factor) ADE_PUBLISH_(name, val, factor) | ||||||
|  |  | ||||||
|  | void ADE7953::update() { | ||||||
|  |   if (!this->is_setup_) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   bool err; | ||||||
|  |  | ||||||
|  |   uint32_t interrupts_a = 0; | ||||||
|  |   uint32_t interrupts_b = 0; | ||||||
|  |   if (this->irq_pin_ != nullptr) { | ||||||
|  |     // Read and reset interrupts | ||||||
|  |     this->ade_read_32(0x032E, &interrupts_a); | ||||||
|  |     this->ade_read_32(0x0331, &interrupts_b); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   uint32_t val; | ||||||
|  |   uint16_t val_16; | ||||||
|  |  | ||||||
|  |   // Power factor | ||||||
|  |   err = this->ade_read_16(0x010A, &val_16); | ||||||
|  |   ADE_PUBLISH(power_factor_a, (int16_t) val_16, (0x7FFF / 100.0f)); | ||||||
|  |   err = this->ade_read_16(0x010B, &val_16); | ||||||
|  |   ADE_PUBLISH(power_factor_b, (int16_t) val_16, (0x7FFF / 100.0f)); | ||||||
|  |  | ||||||
|  |   // Apparent power | ||||||
|  |   err = this->ade_read_32(0x0310, &val); | ||||||
|  |   ADE_PUBLISH(apparent_power_a, (int32_t) val, 154.0f); | ||||||
|  |   err = this->ade_read_32(0x0311, &val); | ||||||
|  |   ADE_PUBLISH(apparent_power_b, (int32_t) val, 154.0f); | ||||||
|  |  | ||||||
|  |   // Active power | ||||||
|  |   err = this->ade_read_32(0x0312, &val); | ||||||
|  |   ADE_PUBLISH(active_power_a, (int32_t) val, 154.0f); | ||||||
|  |   err = this->ade_read_32(0x0313, &val); | ||||||
|  |   ADE_PUBLISH(active_power_b, (int32_t) val, 154.0f); | ||||||
|  |  | ||||||
|  |   // Reactive power | ||||||
|  |   err = this->ade_read_32(0x0314, &val); | ||||||
|  |   ADE_PUBLISH(reactive_power_a, (int32_t) val, 154.0f); | ||||||
|  |   err = this->ade_read_32(0x0315, &val); | ||||||
|  |   ADE_PUBLISH(reactive_power_b, (int32_t) val, 154.0f); | ||||||
|  |  | ||||||
|  |   // Current | ||||||
|  |   err = this->ade_read_32(0x031A, &val); | ||||||
|  |   ADE_PUBLISH(current_a, (uint32_t) val, 100000.0f); | ||||||
|  |   err = this->ade_read_32(0x031B, &val); | ||||||
|  |   ADE_PUBLISH(current_b, (uint32_t) val, 100000.0f); | ||||||
|  |  | ||||||
|  |   // Voltage | ||||||
|  |   err = this->ade_read_32(0x031C, &val); | ||||||
|  |   ADE_PUBLISH(voltage, (uint32_t) val, 26000.0f); | ||||||
|  |  | ||||||
|  |   // Frequency | ||||||
|  |   err = this->ade_read_16(0x010E, &val_16); | ||||||
|  |   ADE_PUBLISH(frequency, 223750.0f, 1 + val_16); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ade7953_base | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										121
									
								
								esphome/components/ade7953_base/ade7953_base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								esphome/components/ade7953_base/ade7953_base.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/hal.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  |  | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ade7953_base { | ||||||
|  |  | ||||||
|  | static const uint8_t PGA_V_8 = | ||||||
|  |     0x007;  // PGA_V,  (R/W) Default: 0x00, Unsigned, Voltage channel gain configuration (Bits[2:0]) | ||||||
|  | static const uint8_t PGA_IA_8 = | ||||||
|  |     0x008;  // PGA_IA, (R/W) Default: 0x00, Unsigned, Current Channel A gain configuration (Bits[2:0]) | ||||||
|  | static const uint8_t PGA_IB_8 = | ||||||
|  |     0x009;  // PGA_IB, (R/W) Default: 0x00, Unsigned, Current Channel B gain configuration (Bits[2:0]) | ||||||
|  |  | ||||||
|  | static const uint32_t AIGAIN_32 = | ||||||
|  |     0x380;  // AIGAIN, (R/W)   Default: 0x400000, Unsigned,Current channel gain (Current Channel A)(32 bit) | ||||||
|  | static const uint32_t AVGAIN_32 = 0x381;  // AVGAIN, (R/W)   Default: 0x400000, Unsigned,Voltage channel gain(32 bit) | ||||||
|  | static const uint32_t AWGAIN_32 = | ||||||
|  |     0x382;  // AWGAIN, (R/W)   Default: 0x400000, Unsigned,Active power gain (Current Channel A)(32 bit) | ||||||
|  | static const uint32_t AVARGAIN_32 = | ||||||
|  |     0x383;  // AVARGAIN, (R/W) Default: 0x400000, Unsigned, Reactive power gain (Current Channel A)(32 bit) | ||||||
|  | static const uint32_t AVAGAIN_32 = | ||||||
|  |     0x384;  // AVAGAIN, (R/W)  Default: 0x400000, Unsigned,Apparent power gain (Current Channel A)(32 bit) | ||||||
|  |  | ||||||
|  | static const uint32_t BIGAIN_32 = | ||||||
|  |     0x38C;  // BIGAIN, (R/W)   Default: 0x400000, Unsigned,Current channel gain (Current Channel B)(32 bit) | ||||||
|  | static const uint32_t BVGAIN_32 = 0x38D;  // BVGAIN, (R/W)   Default: 0x400000, Unsigned,Voltage channel gain(32 bit) | ||||||
|  | static const uint32_t BWGAIN_32 = | ||||||
|  |     0x38E;  // BWGAIN, (R/W)   Default: 0x400000, Unsigned,Active power gain (Current Channel B)(32 bit) | ||||||
|  | static const uint32_t BVARGAIN_32 = | ||||||
|  |     0x38F;  // BVARGAIN, (R/W) Default: 0x400000, Unsigned, Reactive power gain (Current Channel B)(32 bit) | ||||||
|  | static const uint32_t BVAGAIN_32 = | ||||||
|  |     0x390;  // BVAGAIN, (R/W)  Default: 0x400000, Unsigned,Apparent power gain (Current Channel B)(32 bit) | ||||||
|  |  | ||||||
|  | class ADE7953 : public PollingComponent, public sensor::Sensor { | ||||||
|  |  public: | ||||||
|  |   void set_irq_pin(InternalGPIOPin *irq_pin) { irq_pin_ = irq_pin; } | ||||||
|  |  | ||||||
|  |   // Set PGA input gains: 0 1x, 1 2x, 0b10 4x | ||||||
|  |   void set_pga_v(uint8_t pga_v) { pga_v_ = pga_v; } | ||||||
|  |   void set_pga_ia(uint8_t pga_ia) { pga_ia_ = pga_ia; } | ||||||
|  |   void set_pga_ib(uint8_t pga_ib) { pga_ib_ = pga_ib; } | ||||||
|  |  | ||||||
|  |   // Set input gains | ||||||
|  |   void set_vgain(uint32_t vgain) { vgain_ = vgain; } | ||||||
|  |   void set_aigain(uint32_t aigain) { aigain_ = aigain; } | ||||||
|  |   void set_bigain(uint32_t bigain) { bigain_ = bigain; } | ||||||
|  |   void set_awgain(uint32_t awgain) { awgain_ = awgain; } | ||||||
|  |   void set_bwgain(uint32_t bwgain) { bwgain_ = bwgain; } | ||||||
|  |  | ||||||
|  |   void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } | ||||||
|  |   void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; } | ||||||
|  |  | ||||||
|  |   void set_power_factor_a_sensor(sensor::Sensor *power_factor_a) { power_factor_a_sensor_ = power_factor_a; } | ||||||
|  |   void set_power_factor_b_sensor(sensor::Sensor *power_factor_b) { power_factor_b_sensor_ = power_factor_b; } | ||||||
|  |  | ||||||
|  |   void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; } | ||||||
|  |   void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; } | ||||||
|  |  | ||||||
|  |   void set_apparent_power_a_sensor(sensor::Sensor *apparent_power_a) { apparent_power_a_sensor_ = apparent_power_a; } | ||||||
|  |   void set_apparent_power_b_sensor(sensor::Sensor *apparent_power_b) { apparent_power_b_sensor_ = apparent_power_b; } | ||||||
|  |  | ||||||
|  |   void set_active_power_a_sensor(sensor::Sensor *active_power_a_sensor) { | ||||||
|  |     active_power_a_sensor_ = active_power_a_sensor; | ||||||
|  |   } | ||||||
|  |   void set_active_power_b_sensor(sensor::Sensor *active_power_b_sensor) { | ||||||
|  |     active_power_b_sensor_ = active_power_b_sensor; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void set_reactive_power_a_sensor(sensor::Sensor *reactive_power_a) { reactive_power_a_sensor_ = reactive_power_a; } | ||||||
|  |   void set_reactive_power_b_sensor(sensor::Sensor *reactive_power_b) { reactive_power_b_sensor_ = reactive_power_b; } | ||||||
|  |  | ||||||
|  |   void setup() override; | ||||||
|  |  | ||||||
|  |   void dump_config() override; | ||||||
|  |  | ||||||
|  |   void update() override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   InternalGPIOPin *irq_pin_{nullptr}; | ||||||
|  |   bool is_setup_{false}; | ||||||
|  |   sensor::Sensor *voltage_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *frequency_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *current_a_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *current_b_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *apparent_power_a_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *apparent_power_b_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *active_power_a_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *active_power_b_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *reactive_power_a_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *reactive_power_b_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *power_factor_a_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *power_factor_b_sensor_{nullptr}; | ||||||
|  |   uint8_t pga_v_; | ||||||
|  |   uint8_t pga_ia_; | ||||||
|  |   uint8_t pga_ib_; | ||||||
|  |   uint32_t vgain_; | ||||||
|  |   uint32_t aigain_; | ||||||
|  |   uint32_t bigain_; | ||||||
|  |   uint32_t awgain_; | ||||||
|  |   uint32_t bwgain_; | ||||||
|  |  | ||||||
|  |   virtual bool ade_write_8(uint16_t reg, uint8_t value) = 0; | ||||||
|  |  | ||||||
|  |   virtual bool ade_write_16(uint16_t reg, uint16_t value) = 0; | ||||||
|  |  | ||||||
|  |   virtual bool ade_write_32(uint16_t reg, uint32_t value) = 0; | ||||||
|  |  | ||||||
|  |   virtual bool ade_read_8(uint16_t reg, uint8_t *value) = 0; | ||||||
|  |  | ||||||
|  |   virtual bool ade_read_16(uint16_t reg, uint16_t *value) = 0; | ||||||
|  |  | ||||||
|  |   virtual bool ade_read_32(uint16_t reg, uint32_t *value) = 0; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ade7953_base | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										1
									
								
								esphome/components/ade7953_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/ade7953_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | CODEOWNERS = ["@angelnu"] | ||||||
							
								
								
									
										80
									
								
								esphome/components/ade7953_i2c/ade7953_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								esphome/components/ade7953_i2c/ade7953_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | |||||||
|  | #include "ade7953_i2c.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ade7953_i2c { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "ade7953"; | ||||||
|  |  | ||||||
|  | void AdE7953I2c::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "ADE7953_i2c:"); | ||||||
|  |   LOG_I2C_DEVICE(this); | ||||||
|  |   ade7953_base::ADE7953::dump_config(); | ||||||
|  | } | ||||||
|  | bool AdE7953I2c::ade_write_8(uint16_t reg, uint8_t value) { | ||||||
|  |   std::vector<uint8_t> data(3); | ||||||
|  |   data.push_back(reg >> 8); | ||||||
|  |   data.push_back(reg >> 0); | ||||||
|  |   data.push_back(value); | ||||||
|  |   return this->write(data.data(), data.size()) != i2c::ERROR_OK; | ||||||
|  | } | ||||||
|  | bool AdE7953I2c::ade_write_16(uint16_t reg, uint16_t value) { | ||||||
|  |   std::vector<uint8_t> data(4); | ||||||
|  |   data.push_back(reg >> 8); | ||||||
|  |   data.push_back(reg >> 0); | ||||||
|  |   data.push_back(value >> 8); | ||||||
|  |   data.push_back(value >> 0); | ||||||
|  |   return this->write(data.data(), data.size()) != i2c::ERROR_OK; | ||||||
|  | } | ||||||
|  | bool AdE7953I2c::ade_write_32(uint16_t reg, uint32_t value) { | ||||||
|  |   std::vector<uint8_t> data(6); | ||||||
|  |   data.push_back(reg >> 8); | ||||||
|  |   data.push_back(reg >> 0); | ||||||
|  |   data.push_back(value >> 24); | ||||||
|  |   data.push_back(value >> 16); | ||||||
|  |   data.push_back(value >> 8); | ||||||
|  |   data.push_back(value >> 0); | ||||||
|  |   return this->write(data.data(), data.size()) != i2c::ERROR_OK; | ||||||
|  | } | ||||||
|  | bool AdE7953I2c::ade_read_8(uint16_t reg, uint8_t *value) { | ||||||
|  |   uint8_t reg_data[2]; | ||||||
|  |   reg_data[0] = reg >> 8; | ||||||
|  |   reg_data[1] = reg >> 0; | ||||||
|  |   i2c::ErrorCode err = this->write(reg_data, 2); | ||||||
|  |   if (err != i2c::ERROR_OK) | ||||||
|  |     return true; | ||||||
|  |   err = this->read(value, 1); | ||||||
|  |   return (err != i2c::ERROR_OK); | ||||||
|  | } | ||||||
|  | bool AdE7953I2c::ade_read_16(uint16_t reg, uint16_t *value) { | ||||||
|  |   uint8_t reg_data[2]; | ||||||
|  |   reg_data[0] = reg >> 8; | ||||||
|  |   reg_data[1] = reg >> 0; | ||||||
|  |   i2c::ErrorCode err = this->write(reg_data, 2); | ||||||
|  |   if (err != i2c::ERROR_OK) | ||||||
|  |     return true; | ||||||
|  |   uint8_t recv[2]; | ||||||
|  |   err = this->read(recv, 2); | ||||||
|  |   if (err != i2c::ERROR_OK) | ||||||
|  |     return true; | ||||||
|  |   *value = encode_uint16(recv[0], recv[1]); | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  | bool AdE7953I2c::ade_read_32(uint16_t reg, uint32_t *value) { | ||||||
|  |   uint8_t reg_data[2]; | ||||||
|  |   reg_data[0] = reg >> 8; | ||||||
|  |   reg_data[1] = reg >> 0; | ||||||
|  |   i2c::ErrorCode err = this->write(reg_data, 2); | ||||||
|  |   if (err != i2c::ERROR_OK) | ||||||
|  |     return true; | ||||||
|  |   uint8_t recv[4]; | ||||||
|  |   err = this->read(recv, 4); | ||||||
|  |   if (err != i2c::ERROR_OK) | ||||||
|  |     return true; | ||||||
|  |   *value = encode_uint32(recv[0], recv[1], recv[2], recv[3]); | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ade7953_i2c | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										28
									
								
								esphome/components/ade7953_i2c/ade7953_i2c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/ade7953_i2c/ade7953_i2c.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/hal.h" | ||||||
|  | #include "esphome/components/i2c/i2c.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/components/ade7953_base/ade7953_base.h" | ||||||
|  |  | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ade7953_i2c { | ||||||
|  |  | ||||||
|  | class AdE7953I2c : public ade7953_base::ADE7953, public i2c::I2CDevice { | ||||||
|  |  public: | ||||||
|  |   void dump_config() override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool ade_write_8(uint16_t reg, uint8_t value) override; | ||||||
|  |   bool ade_write_16(uint16_t reg, uint16_t value) override; | ||||||
|  |   bool ade_write_32(uint16_t reg, uint32_t value) override; | ||||||
|  |   bool ade_read_8(uint16_t reg, uint8_t *value) override; | ||||||
|  |   bool ade_read_16(uint16_t reg, uint16_t *value) override; | ||||||
|  |   bool ade_read_32(uint16_t reg, uint32_t *value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ade7953_i2c | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										27
									
								
								esphome/components/ade7953_i2c/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/ade7953_i2c/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import i2c, ade7953_base | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  |  | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["i2c"] | ||||||
|  | AUTO_LOAD = ["ade7953_base"] | ||||||
|  |  | ||||||
|  | ade7953_ns = cg.esphome_ns.namespace("ade7953_i2c") | ||||||
|  | ADE7953 = ade7953_ns.class_("AdE7953I2c", ade7953_base.ADE7953, i2c.I2CDevice) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.declare_id(ADE7953), | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     .extend(ade7953_base.ADE7953_CONFIG_SCHEMA) | ||||||
|  |     .extend(i2c.i2c_device_schema(0x38)) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await i2c.register_i2c_device(var, config) | ||||||
|  |     await ade7953_base.register_ade7953(var, config) | ||||||
							
								
								
									
										1
									
								
								esphome/components/ade7953_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/ade7953_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | CODEOWNERS = ["@angelnu"] | ||||||
							
								
								
									
										81
									
								
								esphome/components/ade7953_spi/ade7953_spi.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								esphome/components/ade7953_spi/ade7953_spi.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | #include "ade7953_spi.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ade7953_spi { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "ade7953"; | ||||||
|  |  | ||||||
|  | void AdE7953Spi::setup() { | ||||||
|  |   this->spi_setup(); | ||||||
|  |   ade7953_base::ADE7953::setup(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AdE7953Spi::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "ADE7953_spi:"); | ||||||
|  |   LOG_PIN("  CS Pin: ", this->cs_); | ||||||
|  |   ade7953_base::ADE7953::dump_config(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool AdE7953Spi::ade_write_8(uint16_t reg, uint8_t value) { | ||||||
|  |   this->enable(); | ||||||
|  |   this->write_byte16(reg); | ||||||
|  |   this->transfer_byte(0); | ||||||
|  |   this->transfer_byte(value); | ||||||
|  |   this->disable(); | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool AdE7953Spi::ade_write_16(uint16_t reg, uint16_t value) { | ||||||
|  |   this->enable(); | ||||||
|  |   this->write_byte16(reg); | ||||||
|  |   this->transfer_byte(0); | ||||||
|  |   this->write_byte16(value); | ||||||
|  |   this->disable(); | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool AdE7953Spi::ade_write_32(uint16_t reg, uint32_t value) { | ||||||
|  |   this->enable(); | ||||||
|  |   this->write_byte16(reg); | ||||||
|  |   this->transfer_byte(0); | ||||||
|  |   this->write_byte16(value >> 16); | ||||||
|  |   this->write_byte16(value & 0xFFFF); | ||||||
|  |   this->disable(); | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool AdE7953Spi::ade_read_8(uint16_t reg, uint8_t *value) { | ||||||
|  |   this->enable(); | ||||||
|  |   this->write_byte16(reg); | ||||||
|  |   this->transfer_byte(0x80); | ||||||
|  |   *value = this->read_byte(); | ||||||
|  |   this->disable(); | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool AdE7953Spi::ade_read_16(uint16_t reg, uint16_t *value) { | ||||||
|  |   this->enable(); | ||||||
|  |   this->write_byte16(reg); | ||||||
|  |   this->transfer_byte(0x80); | ||||||
|  |   uint8_t recv[2]; | ||||||
|  |   this->read_array(recv, 4); | ||||||
|  |   *value = encode_uint16(recv[0], recv[1]); | ||||||
|  |   this->disable(); | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool AdE7953Spi::ade_read_32(uint16_t reg, uint32_t *value) { | ||||||
|  |   this->enable(); | ||||||
|  |   this->write_byte16(reg); | ||||||
|  |   this->transfer_byte(0x80); | ||||||
|  |   uint8_t recv[4]; | ||||||
|  |   this->read_array(recv, 4); | ||||||
|  |   *value = encode_uint32(recv[0], recv[1], recv[2], recv[3]); | ||||||
|  |   this->disable(); | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ade7953_spi | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										32
									
								
								esphome/components/ade7953_spi/ade7953_spi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								esphome/components/ade7953_spi/ade7953_spi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/hal.h" | ||||||
|  | #include "esphome/components/spi/spi.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/components/ade7953_base/ade7953_base.h" | ||||||
|  |  | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ade7953_spi { | ||||||
|  |  | ||||||
|  | class AdE7953Spi : public ade7953_base::ADE7953, | ||||||
|  |                    public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_LEADING, | ||||||
|  |                                          spi::DATA_RATE_1MHZ> { | ||||||
|  |  public: | ||||||
|  |   void setup() override; | ||||||
|  |  | ||||||
|  |   void dump_config() override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool ade_write_8(uint16_t reg, uint8_t value) override; | ||||||
|  |   bool ade_write_16(uint16_t reg, uint16_t value) override; | ||||||
|  |   bool ade_write_32(uint16_t reg, uint32_t value) override; | ||||||
|  |   bool ade_read_8(uint16_t reg, uint8_t *value) override; | ||||||
|  |   bool ade_read_16(uint16_t reg, uint16_t *value) override; | ||||||
|  |   bool ade_read_32(uint16_t reg, uint32_t *value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ade7953_spi | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										27
									
								
								esphome/components/ade7953_spi/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/ade7953_spi/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import spi, ade7953_base | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  |  | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["spi"] | ||||||
|  | AUTO_LOAD = ["ade7953_base"] | ||||||
|  |  | ||||||
|  | ade7953_ns = cg.esphome_ns.namespace("ade7953_spi") | ||||||
|  | ADE7953 = ade7953_ns.class_("AdE7953Spi", ade7953_base.ADE7953, spi.SPIDevice) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.declare_id(ADE7953), | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     .extend(ade7953_base.ADE7953_CONFIG_SCHEMA) | ||||||
|  |     .extend(spi.spi_device_schema()) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await spi.register_spi_device(var, config) | ||||||
|  |     await ade7953_base.register_ade7953(var, config) | ||||||
| @@ -15,6 +15,7 @@ | |||||||
| #include "aht10.h" | #include "aht10.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace aht10 { | namespace aht10 { | ||||||
| @@ -72,7 +73,7 @@ void AHT10Component::update() { | |||||||
|     delay_ms = AHT10_HUMIDITY_DELAY; |     delay_ms = AHT10_HUMIDITY_DELAY; | ||||||
|   bool success = false; |   bool success = false; | ||||||
|   for (int i = 0; i < AHT10_ATTEMPTS; ++i) { |   for (int i = 0; i < AHT10_ATTEMPTS; ++i) { | ||||||
|     ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis()); |     ESP_LOGVV(TAG, "Attempt %d at %6" PRIu32, i, millis()); | ||||||
|     delay(delay_ms); |     delay(delay_ms); | ||||||
|     if (this->read(data, 6) != i2c::ERROR_OK) { |     if (this->read(data, 6) != i2c::ERROR_OK) { | ||||||
|       ESP_LOGD(TAG, "Communication with AHT10 failed, waiting..."); |       ESP_LOGD(TAG, "Communication with AHT10 failed, waiting..."); | ||||||
| @@ -96,7 +97,7 @@ void AHT10Component::update() { | |||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       // data is valid, we can break the loop |       // data is valid, we can break the loop | ||||||
|       ESP_LOGVV(TAG, "Answer at %6u", millis()); |       ESP_LOGVV(TAG, "Answer at %6" PRIu32, millis()); | ||||||
|       success = true; |       success = true; | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -39,6 +39,7 @@ service APIConnection { | |||||||
|   rpc camera_image (CameraImageRequest) returns (void) {} |   rpc camera_image (CameraImageRequest) returns (void) {} | ||||||
|   rpc climate_command (ClimateCommandRequest) returns (void) {} |   rpc climate_command (ClimateCommandRequest) returns (void) {} | ||||||
|   rpc number_command (NumberCommandRequest) returns (void) {} |   rpc number_command (NumberCommandRequest) returns (void) {} | ||||||
|  |   rpc text_command (TextCommandRequest) returns (void) {} | ||||||
|   rpc select_command (SelectCommandRequest) returns (void) {} |   rpc select_command (SelectCommandRequest) returns (void) {} | ||||||
|   rpc button_command (ButtonCommandRequest) returns (void) {} |   rpc button_command (ButtonCommandRequest) returns (void) {} | ||||||
|   rpc lock_command (LockCommandRequest) returns (void) {} |   rpc lock_command (LockCommandRequest) returns (void) {} | ||||||
| @@ -216,6 +217,8 @@ message DeviceInfoResponse { | |||||||
|   string friendly_name = 13; |   string friendly_name = 13; | ||||||
|  |  | ||||||
|   uint32 voice_assistant_version = 14; |   uint32 voice_assistant_version = 14; | ||||||
|  |  | ||||||
|  |   string suggested_area = 16; | ||||||
| } | } | ||||||
|  |  | ||||||
| message ListEntitiesRequest { | message ListEntitiesRequest { | ||||||
| @@ -1536,3 +1539,48 @@ message AlarmControlPanelCommandRequest { | |||||||
|   AlarmControlPanelStateCommand command = 2; |   AlarmControlPanelStateCommand command = 2; | ||||||
|   string code = 3; |   string code = 3; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ===================== TEXT ===================== | ||||||
|  | enum TextMode { | ||||||
|  |   TEXT_MODE_TEXT = 0; | ||||||
|  |   TEXT_MODE_PASSWORD = 1; | ||||||
|  | } | ||||||
|  | message ListEntitiesTextResponse { | ||||||
|  |   option (id) = 97; | ||||||
|  |   option (source) = SOURCE_SERVER; | ||||||
|  |   option (ifdef) = "USE_TEXT"; | ||||||
|  |  | ||||||
|  |   string object_id = 1; | ||||||
|  |   fixed32 key = 2; | ||||||
|  |   string name = 3; | ||||||
|  |   string unique_id = 4; | ||||||
|  |   string icon = 5; | ||||||
|  |   bool disabled_by_default = 6; | ||||||
|  |   EntityCategory entity_category = 7; | ||||||
|  |  | ||||||
|  |   uint32 min_length = 8; | ||||||
|  |   uint32 max_length = 9; | ||||||
|  |   string pattern = 10; | ||||||
|  |   TextMode mode = 11; | ||||||
|  | } | ||||||
|  | message TextStateResponse { | ||||||
|  |   option (id) = 98; | ||||||
|  |   option (source) = SOURCE_SERVER; | ||||||
|  |   option (ifdef) = "USE_TEXT"; | ||||||
|  |   option (no_delay) = true; | ||||||
|  |  | ||||||
|  |   fixed32 key = 1; | ||||||
|  |   string state = 2; | ||||||
|  |   // If the Text does not have a valid state yet. | ||||||
|  |   // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller | ||||||
|  |   bool missing_state = 3; | ||||||
|  | } | ||||||
|  | message TextCommandRequest { | ||||||
|  |   option (id) = 99; | ||||||
|  |   option (source) = SOURCE_CLIENT; | ||||||
|  |   option (ifdef) = "USE_TEXT"; | ||||||
|  |   option (no_delay) = true; | ||||||
|  |  | ||||||
|  |   fixed32 key = 1; | ||||||
|  |   string state = 2; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| #include "api_connection.h" | #include "api_connection.h" | ||||||
| #include <cerrno> | #include <cerrno> | ||||||
| #include <cinttypes> | #include <cinttypes> | ||||||
|  | #include <utility> | ||||||
| #include "esphome/components/network/util.h" | #include "esphome/components/network/util.h" | ||||||
| #include "esphome/core/entity_base.h" | #include "esphome/core/entity_base.h" | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
| @@ -664,6 +665,44 @@ void APIConnection::number_command(const NumberCommandRequest &msg) { | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_TEXT | ||||||
|  | bool APIConnection::send_text_state(text::Text *text, std::string state) { | ||||||
|  |   if (!this->state_subscription_) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   TextStateResponse resp{}; | ||||||
|  |   resp.key = text->get_object_id_hash(); | ||||||
|  |   resp.state = std::move(state); | ||||||
|  |   resp.missing_state = !text->has_state(); | ||||||
|  |   return this->send_text_state_response(resp); | ||||||
|  | } | ||||||
|  | bool APIConnection::send_text_info(text::Text *text) { | ||||||
|  |   ListEntitiesTextResponse msg; | ||||||
|  |   msg.key = text->get_object_id_hash(); | ||||||
|  |   msg.object_id = text->get_object_id(); | ||||||
|  |   msg.name = text->get_name(); | ||||||
|  |   msg.icon = text->get_icon(); | ||||||
|  |   msg.disabled_by_default = text->is_disabled_by_default(); | ||||||
|  |   msg.entity_category = static_cast<enums::EntityCategory>(text->get_entity_category()); | ||||||
|  |   msg.mode = static_cast<enums::TextMode>(text->traits.get_mode()); | ||||||
|  |  | ||||||
|  |   msg.min_length = text->traits.get_min_length(); | ||||||
|  |   msg.max_length = text->traits.get_max_length(); | ||||||
|  |   msg.pattern = text->traits.get_pattern(); | ||||||
|  |  | ||||||
|  |   return this->send_list_entities_text_response(msg); | ||||||
|  | } | ||||||
|  | void APIConnection::text_command(const TextCommandRequest &msg) { | ||||||
|  |   text::Text *text = App.get_text_by_key(msg.key); | ||||||
|  |   if (text == nullptr) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   auto call = text->make_call(); | ||||||
|  |   call.set_value(msg.state); | ||||||
|  |   call.perform(); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
| bool APIConnection::send_select_state(select::Select *select, std::string state) { | bool APIConnection::send_select_state(select::Select *select, std::string state) { | ||||||
|   if (!this->state_subscription_) |   if (!this->state_subscription_) | ||||||
| @@ -1063,6 +1102,7 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { | |||||||
|   resp.uses_password = this->parent_->uses_password(); |   resp.uses_password = this->parent_->uses_password(); | ||||||
|   resp.name = App.get_name(); |   resp.name = App.get_name(); | ||||||
|   resp.friendly_name = App.get_friendly_name(); |   resp.friendly_name = App.get_friendly_name(); | ||||||
|  |   resp.suggested_area = App.get_area(); | ||||||
|   resp.mac_address = get_mac_address_pretty(); |   resp.mac_address = get_mac_address_pretty(); | ||||||
|   resp.esphome_version = ESPHOME_VERSION; |   resp.esphome_version = ESPHOME_VERSION; | ||||||
|   resp.compilation_time = App.get_compilation_time(); |   resp.compilation_time = App.get_compilation_time(); | ||||||
|   | |||||||
| @@ -72,6 +72,11 @@ class APIConnection : public APIServerConnection { | |||||||
|   bool send_number_info(number::Number *number); |   bool send_number_info(number::Number *number); | ||||||
|   void number_command(const NumberCommandRequest &msg) override; |   void number_command(const NumberCommandRequest &msg) override; | ||||||
| #endif | #endif | ||||||
|  | #ifdef USE_TEXT | ||||||
|  |   bool send_text_state(text::Text *text, std::string state); | ||||||
|  |   bool send_text_info(text::Text *text); | ||||||
|  |   void text_command(const TextCommandRequest &msg) override; | ||||||
|  | #endif | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
|   bool send_select_state(select::Select *select, std::string state); |   bool send_select_state(select::Select *select, std::string state); | ||||||
|   bool send_select_info(select::Select *select); |   bool send_select_info(select::Select *select); | ||||||
|   | |||||||
| @@ -512,6 +512,18 @@ const char *proto_enum_to_string<enums::AlarmControlPanelStateCommand>(enums::Al | |||||||
|   } |   } | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|  | template<> const char *proto_enum_to_string<enums::TextMode>(enums::TextMode value) { | ||||||
|  |   switch (value) { | ||||||
|  |     case enums::TEXT_MODE_TEXT: | ||||||
|  |       return "TEXT_MODE_TEXT"; | ||||||
|  |     case enums::TEXT_MODE_PASSWORD: | ||||||
|  |       return "TEXT_MODE_PASSWORD"; | ||||||
|  |     default: | ||||||
|  |       return "UNKNOWN"; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | #endif | ||||||
| bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||||
|   switch (field_id) { |   switch (field_id) { | ||||||
|     case 2: { |     case 2: { | ||||||
| @@ -749,6 +761,10 @@ bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited v | |||||||
|       this->friendly_name = value.as_string(); |       this->friendly_name = value.as_string(); | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|  |     case 16: { | ||||||
|  |       this->suggested_area = value.as_string(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|     default: |     default: | ||||||
|       return false; |       return false; | ||||||
|   } |   } | ||||||
| @@ -769,6 +785,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { | |||||||
|   buffer.encode_string(12, this->manufacturer); |   buffer.encode_string(12, this->manufacturer); | ||||||
|   buffer.encode_string(13, this->friendly_name); |   buffer.encode_string(13, this->friendly_name); | ||||||
|   buffer.encode_uint32(14, this->voice_assistant_version); |   buffer.encode_uint32(14, this->voice_assistant_version); | ||||||
|  |   buffer.encode_string(16, this->suggested_area); | ||||||
| } | } | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| void DeviceInfoResponse::dump_to(std::string &out) const { | void DeviceInfoResponse::dump_to(std::string &out) const { | ||||||
| @@ -837,6 +854,10 @@ void DeviceInfoResponse::dump_to(std::string &out) const { | |||||||
|   sprintf(buffer, "%" PRIu32, this->voice_assistant_version); |   sprintf(buffer, "%" PRIu32, this->voice_assistant_version); | ||||||
|   out.append(buffer); |   out.append(buffer); | ||||||
|   out.append("\n"); |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  suggested_area: "); | ||||||
|  |   out.append("'").append(this->suggested_area).append("'"); | ||||||
|  |   out.append("\n"); | ||||||
|   out.append("}"); |   out.append("}"); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| @@ -6795,6 +6816,227 @@ void AlarmControlPanelCommandRequest::dump_to(std::string &out) const { | |||||||
|   out.append("}"); |   out.append("}"); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | bool ListEntitiesTextResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||||
|  |   switch (field_id) { | ||||||
|  |     case 6: { | ||||||
|  |       this->disabled_by_default = value.as_bool(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     case 7: { | ||||||
|  |       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     case 8: { | ||||||
|  |       this->min_length = value.as_uint32(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     case 9: { | ||||||
|  |       this->max_length = value.as_uint32(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     case 11: { | ||||||
|  |       this->mode = value.as_enum<enums::TextMode>(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |       return false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | bool ListEntitiesTextResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { | ||||||
|  |   switch (field_id) { | ||||||
|  |     case 1: { | ||||||
|  |       this->object_id = value.as_string(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     case 3: { | ||||||
|  |       this->name = value.as_string(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     case 4: { | ||||||
|  |       this->unique_id = value.as_string(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     case 5: { | ||||||
|  |       this->icon = value.as_string(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     case 10: { | ||||||
|  |       this->pattern = value.as_string(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |       return false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | bool ListEntitiesTextResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||||
|  |   switch (field_id) { | ||||||
|  |     case 2: { | ||||||
|  |       this->key = value.as_fixed32(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |       return false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | void ListEntitiesTextResponse::encode(ProtoWriteBuffer buffer) const { | ||||||
|  |   buffer.encode_string(1, this->object_id); | ||||||
|  |   buffer.encode_fixed32(2, this->key); | ||||||
|  |   buffer.encode_string(3, this->name); | ||||||
|  |   buffer.encode_string(4, this->unique_id); | ||||||
|  |   buffer.encode_string(5, this->icon); | ||||||
|  |   buffer.encode_bool(6, this->disabled_by_default); | ||||||
|  |   buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); | ||||||
|  |   buffer.encode_uint32(8, this->min_length); | ||||||
|  |   buffer.encode_uint32(9, this->max_length); | ||||||
|  |   buffer.encode_string(10, this->pattern); | ||||||
|  |   buffer.encode_enum<enums::TextMode>(11, this->mode); | ||||||
|  | } | ||||||
|  | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|  | void ListEntitiesTextResponse::dump_to(std::string &out) const { | ||||||
|  |   __attribute__((unused)) char buffer[64]; | ||||||
|  |   out.append("ListEntitiesTextResponse {\n"); | ||||||
|  |   out.append("  object_id: "); | ||||||
|  |   out.append("'").append(this->object_id).append("'"); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  key: "); | ||||||
|  |   sprintf(buffer, "%" PRIu32, this->key); | ||||||
|  |   out.append(buffer); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  name: "); | ||||||
|  |   out.append("'").append(this->name).append("'"); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  unique_id: "); | ||||||
|  |   out.append("'").append(this->unique_id).append("'"); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  icon: "); | ||||||
|  |   out.append("'").append(this->icon).append("'"); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  disabled_by_default: "); | ||||||
|  |   out.append(YESNO(this->disabled_by_default)); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  entity_category: "); | ||||||
|  |   out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  min_length: "); | ||||||
|  |   sprintf(buffer, "%" PRIu32, this->min_length); | ||||||
|  |   out.append(buffer); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  max_length: "); | ||||||
|  |   sprintf(buffer, "%" PRIu32, this->max_length); | ||||||
|  |   out.append(buffer); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  pattern: "); | ||||||
|  |   out.append("'").append(this->pattern).append("'"); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  mode: "); | ||||||
|  |   out.append(proto_enum_to_string<enums::TextMode>(this->mode)); | ||||||
|  |   out.append("\n"); | ||||||
|  |   out.append("}"); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | bool TextStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||||
|  |   switch (field_id) { | ||||||
|  |     case 3: { | ||||||
|  |       this->missing_state = value.as_bool(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |       return false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | bool TextStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { | ||||||
|  |   switch (field_id) { | ||||||
|  |     case 2: { | ||||||
|  |       this->state = value.as_string(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |       return false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | bool TextStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||||
|  |   switch (field_id) { | ||||||
|  |     case 1: { | ||||||
|  |       this->key = value.as_fixed32(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |       return false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | void TextStateResponse::encode(ProtoWriteBuffer buffer) const { | ||||||
|  |   buffer.encode_fixed32(1, this->key); | ||||||
|  |   buffer.encode_string(2, this->state); | ||||||
|  |   buffer.encode_bool(3, this->missing_state); | ||||||
|  | } | ||||||
|  | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|  | void TextStateResponse::dump_to(std::string &out) const { | ||||||
|  |   __attribute__((unused)) char buffer[64]; | ||||||
|  |   out.append("TextStateResponse {\n"); | ||||||
|  |   out.append("  key: "); | ||||||
|  |   sprintf(buffer, "%" PRIu32, this->key); | ||||||
|  |   out.append(buffer); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  state: "); | ||||||
|  |   out.append("'").append(this->state).append("'"); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  missing_state: "); | ||||||
|  |   out.append(YESNO(this->missing_state)); | ||||||
|  |   out.append("\n"); | ||||||
|  |   out.append("}"); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | bool TextCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { | ||||||
|  |   switch (field_id) { | ||||||
|  |     case 2: { | ||||||
|  |       this->state = value.as_string(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |       return false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | bool TextCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||||
|  |   switch (field_id) { | ||||||
|  |     case 1: { | ||||||
|  |       this->key = value.as_fixed32(); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |       return false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | void TextCommandRequest::encode(ProtoWriteBuffer buffer) const { | ||||||
|  |   buffer.encode_fixed32(1, this->key); | ||||||
|  |   buffer.encode_string(2, this->state); | ||||||
|  | } | ||||||
|  | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|  | void TextCommandRequest::dump_to(std::string &out) const { | ||||||
|  |   __attribute__((unused)) char buffer[64]; | ||||||
|  |   out.append("TextCommandRequest {\n"); | ||||||
|  |   out.append("  key: "); | ||||||
|  |   sprintf(buffer, "%" PRIu32, this->key); | ||||||
|  |   out.append(buffer); | ||||||
|  |   out.append("\n"); | ||||||
|  |  | ||||||
|  |   out.append("  state: "); | ||||||
|  |   out.append("'").append(this->state).append("'"); | ||||||
|  |   out.append("\n"); | ||||||
|  |   out.append("}"); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| }  // namespace api | }  // namespace api | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -208,6 +208,10 @@ enum AlarmControlPanelStateCommand : uint32_t { | |||||||
|   ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS = 5, |   ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS = 5, | ||||||
|   ALARM_CONTROL_PANEL_TRIGGER = 6, |   ALARM_CONTROL_PANEL_TRIGGER = 6, | ||||||
| }; | }; | ||||||
|  | enum TextMode : uint32_t { | ||||||
|  |   TEXT_MODE_TEXT = 0, | ||||||
|  |   TEXT_MODE_PASSWORD = 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
| }  // namespace enums | }  // namespace enums | ||||||
|  |  | ||||||
| @@ -324,6 +328,7 @@ class DeviceInfoResponse : public ProtoMessage { | |||||||
|   std::string manufacturer{}; |   std::string manufacturer{}; | ||||||
|   std::string friendly_name{}; |   std::string friendly_name{}; | ||||||
|   uint32_t voice_assistant_version{0}; |   uint32_t voice_assistant_version{0}; | ||||||
|  |   std::string suggested_area{}; | ||||||
|   void encode(ProtoWriteBuffer buffer) const override; |   void encode(ProtoWriteBuffer buffer) const override; | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|   void dump_to(std::string &out) const override; |   void dump_to(std::string &out) const override; | ||||||
| @@ -1778,6 +1783,57 @@ class AlarmControlPanelCommandRequest : public ProtoMessage { | |||||||
|   bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; |   bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; | ||||||
|   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; |   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; | ||||||
| }; | }; | ||||||
|  | class ListEntitiesTextResponse : public ProtoMessage { | ||||||
|  |  public: | ||||||
|  |   std::string object_id{}; | ||||||
|  |   uint32_t key{0}; | ||||||
|  |   std::string name{}; | ||||||
|  |   std::string unique_id{}; | ||||||
|  |   std::string icon{}; | ||||||
|  |   bool disabled_by_default{false}; | ||||||
|  |   enums::EntityCategory entity_category{}; | ||||||
|  |   uint32_t min_length{0}; | ||||||
|  |   uint32_t max_length{0}; | ||||||
|  |   std::string pattern{}; | ||||||
|  |   enums::TextMode mode{}; | ||||||
|  |   void encode(ProtoWriteBuffer buffer) const override; | ||||||
|  | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|  |   void dump_to(std::string &out) const override; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||||
|  |   bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; | ||||||
|  |   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; | ||||||
|  | }; | ||||||
|  | class TextStateResponse : public ProtoMessage { | ||||||
|  |  public: | ||||||
|  |   uint32_t key{0}; | ||||||
|  |   std::string state{}; | ||||||
|  |   bool missing_state{false}; | ||||||
|  |   void encode(ProtoWriteBuffer buffer) const override; | ||||||
|  | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|  |   void dump_to(std::string &out) const override; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||||
|  |   bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; | ||||||
|  |   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; | ||||||
|  | }; | ||||||
|  | class TextCommandRequest : public ProtoMessage { | ||||||
|  |  public: | ||||||
|  |   uint32_t key{0}; | ||||||
|  |   std::string state{}; | ||||||
|  |   void encode(ProtoWriteBuffer buffer) const override; | ||||||
|  | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|  |   void dump_to(std::string &out) const override; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||||
|  |   bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
| }  // namespace api | }  // namespace api | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -495,6 +495,24 @@ bool APIServerConnectionBase::send_alarm_control_panel_state_response(const Alar | |||||||
| #endif | #endif | ||||||
| #ifdef USE_ALARM_CONTROL_PANEL | #ifdef USE_ALARM_CONTROL_PANEL | ||||||
| #endif | #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 | ||||||
| bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { | bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { | ||||||
|   switch (msg_type) { |   switch (msg_type) { | ||||||
|     case 1: { |     case 1: { | ||||||
| @@ -913,6 +931,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, | |||||||
|       ESP_LOGVV(TAG, "on_alarm_control_panel_command_request: %s", msg.dump().c_str()); |       ESP_LOGVV(TAG, "on_alarm_control_panel_command_request: %s", msg.dump().c_str()); | ||||||
| #endif | #endif | ||||||
|       this->on_alarm_control_panel_command_request(msg); |       this->on_alarm_control_panel_command_request(msg); | ||||||
|  | #endif | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case 99: { | ||||||
|  | #ifdef USE_TEXT | ||||||
|  |       TextCommandRequest msg; | ||||||
|  |       msg.decode(msg_data, msg_size); | ||||||
|  | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|  |       ESP_LOGVV(TAG, "on_text_command_request: %s", msg.dump().c_str()); | ||||||
|  | #endif | ||||||
|  |       this->on_text_command_request(msg); | ||||||
| #endif | #endif | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
| @@ -1124,6 +1153,19 @@ void APIServerConnection::on_number_command_request(const NumberCommandRequest & | |||||||
|   this->number_command(msg); |   this->number_command(msg); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | #ifdef USE_TEXT | ||||||
|  | void APIServerConnection::on_text_command_request(const TextCommandRequest &msg) { | ||||||
|  |   if (!this->is_connection_setup()) { | ||||||
|  |     this->on_no_setup_connection(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   if (!this->is_authenticated()) { | ||||||
|  |     this->on_unauthenticated_access(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   this->text_command(msg); | ||||||
|  | } | ||||||
|  | #endif | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
| void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) { | void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) { | ||||||
|   if (!this->is_connection_setup()) { |   if (!this->is_connection_setup()) { | ||||||
|   | |||||||
| @@ -248,6 +248,15 @@ class APIServerConnectionBase : public ProtoService { | |||||||
| #endif | #endif | ||||||
| #ifdef USE_ALARM_CONTROL_PANEL | #ifdef USE_ALARM_CONTROL_PANEL | ||||||
|   virtual void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &value){}; |   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 | #endif | ||||||
|  protected: |  protected: | ||||||
|   bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; |   bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; | ||||||
| @@ -288,6 +297,9 @@ class APIServerConnection : public APIServerConnectionBase { | |||||||
| #ifdef USE_NUMBER | #ifdef USE_NUMBER | ||||||
|   virtual void number_command(const NumberCommandRequest &msg) = 0; |   virtual void number_command(const NumberCommandRequest &msg) = 0; | ||||||
| #endif | #endif | ||||||
|  | #ifdef USE_TEXT | ||||||
|  |   virtual void text_command(const TextCommandRequest &msg) = 0; | ||||||
|  | #endif | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
|   virtual void select_command(const SelectCommandRequest &msg) = 0; |   virtual void select_command(const SelectCommandRequest &msg) = 0; | ||||||
| #endif | #endif | ||||||
| @@ -371,6 +383,9 @@ class APIServerConnection : public APIServerConnectionBase { | |||||||
| #ifdef USE_NUMBER | #ifdef USE_NUMBER | ||||||
|   void on_number_command_request(const NumberCommandRequest &msg) override; |   void on_number_command_request(const NumberCommandRequest &msg) override; | ||||||
| #endif | #endif | ||||||
|  | #ifdef USE_TEXT | ||||||
|  |   void on_text_command_request(const TextCommandRequest &msg) override; | ||||||
|  | #endif | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
|   void on_select_command_request(const SelectCommandRequest &msg) override; |   void on_select_command_request(const SelectCommandRequest &msg) override; | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -255,6 +255,15 @@ void APIServer::on_number_update(number::Number *obj, float state) { | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_TEXT | ||||||
|  | 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); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
| void APIServer::on_select_update(select::Select *obj, const std::string &state, size_t index) { | void APIServer::on_select_update(select::Select *obj, const std::string &state, size_t index) { | ||||||
|   if (obj->is_internal()) |   if (obj->is_internal()) | ||||||
|   | |||||||
| @@ -66,6 +66,9 @@ class APIServer : public Component, public Controller { | |||||||
| #ifdef USE_NUMBER | #ifdef USE_NUMBER | ||||||
|   void on_number_update(number::Number *obj, float state) override; |   void on_number_update(number::Number *obj, float state) override; | ||||||
| #endif | #endif | ||||||
|  | #ifdef USE_TEXT | ||||||
|  |   void on_text_update(text::Text *obj, const std::string &state) override; | ||||||
|  | #endif | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
|   void on_select_update(select::Select *obj, const std::string &state, size_t index) override; |   void on_select_update(select::Select *obj, const std::string &state, size_t index) override; | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -1,71 +1,65 @@ | |||||||
|  | from __future__ import annotations | ||||||
|  |  | ||||||
| import asyncio | import asyncio | ||||||
| import logging | import logging | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
| from typing import Optional | from typing import Any | ||||||
|  |  | ||||||
| from aioesphomeapi import APIClient, ReconnectLogic, APIConnectionError, LogLevel | from aioesphomeapi import APIClient | ||||||
| import zeroconf | from aioesphomeapi.api_pb2 import SubscribeLogsResponse | ||||||
|  | from aioesphomeapi.log_runner import async_run | ||||||
|  | from zeroconf.asyncio import AsyncZeroconf | ||||||
|  |  | ||||||
|  | from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__ | ||||||
|  | from esphome.core import CORE | ||||||
|  |  | ||||||
| from esphome.const import CONF_KEY, CONF_PORT, CONF_PASSWORD, __version__ |  | ||||||
| from esphome.util import safe_print |  | ||||||
| from . import CONF_ENCRYPTION | from . import CONF_ENCRYPTION | ||||||
|  |  | ||||||
| _LOGGER = logging.getLogger(__name__) | _LOGGER = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
| async def async_run_logs(config, address): | async def async_run_logs(config, address): | ||||||
|  |     """Run the logs command in the event loop.""" | ||||||
|     conf = config["api"] |     conf = config["api"] | ||||||
|     port: int = int(conf[CONF_PORT]) |     port: int = int(conf[CONF_PORT]) | ||||||
|     password: str = conf[CONF_PASSWORD] |     password: str = conf[CONF_PASSWORD] | ||||||
|     noise_psk: Optional[str] = None |     noise_psk: str | None = None | ||||||
|     if CONF_ENCRYPTION in conf: |     if CONF_ENCRYPTION in conf: | ||||||
|         noise_psk = conf[CONF_ENCRYPTION][CONF_KEY] |         noise_psk = conf[CONF_ENCRYPTION][CONF_KEY] | ||||||
|     _LOGGER.info("Starting log output from %s using esphome API", address) |     _LOGGER.info("Starting log output from %s using esphome API", address) | ||||||
|  |     aiozc = AsyncZeroconf() | ||||||
|  |  | ||||||
|     cli = APIClient( |     cli = APIClient( | ||||||
|         address, |         address, | ||||||
|         port, |         port, | ||||||
|         password, |         password, | ||||||
|         client_info=f"ESPHome Logs {__version__}", |         client_info=f"ESPHome Logs {__version__}", | ||||||
|         noise_psk=noise_psk, |         noise_psk=noise_psk, | ||||||
|  |         zeroconf_instance=aiozc.zeroconf, | ||||||
|     ) |     ) | ||||||
|     first_connect = True |     dashboard = CORE.dashboard | ||||||
|  |  | ||||||
|     def on_log(msg): |     def on_log(msg: SubscribeLogsResponse) -> None: | ||||||
|         time_ = datetime.now().time().strftime("[%H:%M:%S]") |         """Handle a new log message.""" | ||||||
|         text = msg.message.decode("utf8", "backslashreplace") |         time_ = datetime.now() | ||||||
|         safe_print(time_ + text) |         message: bytes = msg.message | ||||||
|  |         text = message.decode("utf8", "backslashreplace") | ||||||
|     async def on_connect(): |         if dashboard: | ||||||
|         nonlocal first_connect |             text = text.replace("\033", "\\033") | ||||||
|         try: |         print(f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]{text}") | ||||||
|             await cli.subscribe_logs( |  | ||||||
|                 on_log, |  | ||||||
|                 log_level=LogLevel.LOG_LEVEL_VERY_VERBOSE, |  | ||||||
|                 dump_config=first_connect, |  | ||||||
|             ) |  | ||||||
|             first_connect = False |  | ||||||
|         except APIConnectionError: |  | ||||||
|             cli.disconnect() |  | ||||||
|  |  | ||||||
|     async def on_disconnect(expected_disconnect: bool) -> None: |  | ||||||
|         _LOGGER.warning("Disconnected from API") |  | ||||||
|  |  | ||||||
|     zc = zeroconf.Zeroconf() |  | ||||||
|     reconnect = ReconnectLogic( |  | ||||||
|         client=cli, |  | ||||||
|         on_connect=on_connect, |  | ||||||
|         on_disconnect=on_disconnect, |  | ||||||
|         zeroconf_instance=zc, |  | ||||||
|     ) |  | ||||||
|     await reconnect.start() |  | ||||||
|  |  | ||||||
|  |     stop = await async_run(cli, on_log, aio_zeroconf_instance=aiozc) | ||||||
|     try: |     try: | ||||||
|         while True: |         while True: | ||||||
|             await asyncio.sleep(60) |             await asyncio.sleep(60) | ||||||
|  |     finally: | ||||||
|  |         await aiozc.async_close() | ||||||
|  |         await stop() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def run_logs(config: dict[str, Any], address: str) -> None: | ||||||
|  |     """Run the logs command.""" | ||||||
|  |     try: | ||||||
|  |         asyncio.run(async_run_logs(config, address)) | ||||||
|     except KeyboardInterrupt: |     except KeyboardInterrupt: | ||||||
|         await reconnect.stop() |         pass | ||||||
|         zc.close() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def run_logs(config, address): |  | ||||||
|     asyncio.run(async_run_logs(config, address)) |  | ||||||
|   | |||||||
| @@ -60,6 +60,10 @@ bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this-> | |||||||
| bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); } | bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifdef USE_TEXT | ||||||
|  | bool ListEntitiesIterator::on_text(text::Text *text) { return this->client_->send_text_info(text); } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
| bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); } | bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); } | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -46,6 +46,9 @@ class ListEntitiesIterator : public ComponentIterator { | |||||||
| #ifdef USE_NUMBER | #ifdef USE_NUMBER | ||||||
|   bool on_number(number::Number *number) override; |   bool on_number(number::Number *number) override; | ||||||
| #endif | #endif | ||||||
|  | #ifdef USE_TEXT | ||||||
|  |   bool on_text(text::Text *text) override; | ||||||
|  | #endif | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
|   bool on_select(select::Select *select) override; |   bool on_select(select::Select *select) override; | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -42,6 +42,9 @@ bool InitialStateIterator::on_number(number::Number *number) { | |||||||
|   return this->client_->send_number_state(number, number->state); |   return this->client_->send_number_state(number, number->state); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | #ifdef USE_TEXT | ||||||
|  | bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); } | ||||||
|  | #endif | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
| bool InitialStateIterator::on_select(select::Select *select) { | bool InitialStateIterator::on_select(select::Select *select) { | ||||||
|   return this->client_->send_select_state(select, select->state); |   return this->client_->send_select_state(select, select->state); | ||||||
|   | |||||||
| @@ -43,6 +43,9 @@ class InitialStateIterator : public ComponentIterator { | |||||||
| #ifdef USE_NUMBER | #ifdef USE_NUMBER | ||||||
|   bool on_number(number::Number *number) override; |   bool on_number(number::Number *number) override; | ||||||
| #endif | #endif | ||||||
|  | #ifdef USE_TEXT | ||||||
|  |   bool on_text(text::Text *text) override; | ||||||
|  | #endif | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
|   bool on_select(select::Select *select) override; |   bool on_select(select::Select *select) override; | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ namespace esphome { | |||||||
| namespace api { | namespace api { | ||||||
|  |  | ||||||
| template<> bool get_execute_arg_value<bool>(const ExecuteServiceArgument &arg) { return arg.bool_; } | template<> bool get_execute_arg_value<bool>(const ExecuteServiceArgument &arg) { return arg.bool_; } | ||||||
| template<> int get_execute_arg_value<int>(const ExecuteServiceArgument &arg) { | template<> int32_t get_execute_arg_value<int32_t>(const ExecuteServiceArgument &arg) { | ||||||
|   if (arg.legacy_int != 0) |   if (arg.legacy_int != 0) | ||||||
|     return arg.legacy_int; |     return arg.legacy_int; | ||||||
|   return arg.int_; |   return arg.int_; | ||||||
| @@ -26,11 +26,13 @@ template<> std::vector<std::string> get_execute_arg_value<std::vector<std::strin | |||||||
| } | } | ||||||
|  |  | ||||||
| template<> enums::ServiceArgType to_service_arg_type<bool>() { return enums::SERVICE_ARG_TYPE_BOOL; } | template<> enums::ServiceArgType to_service_arg_type<bool>() { return enums::SERVICE_ARG_TYPE_BOOL; } | ||||||
| template<> enums::ServiceArgType to_service_arg_type<int>() { return enums::SERVICE_ARG_TYPE_INT; } | template<> enums::ServiceArgType to_service_arg_type<int32_t>() { return enums::SERVICE_ARG_TYPE_INT; } | ||||||
| template<> enums::ServiceArgType to_service_arg_type<float>() { return enums::SERVICE_ARG_TYPE_FLOAT; } | template<> enums::ServiceArgType to_service_arg_type<float>() { return enums::SERVICE_ARG_TYPE_FLOAT; } | ||||||
| template<> enums::ServiceArgType to_service_arg_type<std::string>() { return enums::SERVICE_ARG_TYPE_STRING; } | template<> enums::ServiceArgType to_service_arg_type<std::string>() { return enums::SERVICE_ARG_TYPE_STRING; } | ||||||
| template<> enums::ServiceArgType to_service_arg_type<std::vector<bool>>() { return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; } | template<> enums::ServiceArgType to_service_arg_type<std::vector<bool>>() { return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; } | ||||||
| template<> enums::ServiceArgType to_service_arg_type<std::vector<int>>() { return enums::SERVICE_ARG_TYPE_INT_ARRAY; } | template<> enums::ServiceArgType to_service_arg_type<std::vector<int32_t>>() { | ||||||
|  |   return enums::SERVICE_ARG_TYPE_INT_ARRAY; | ||||||
|  | } | ||||||
| template<> enums::ServiceArgType to_service_arg_type<std::vector<float>>() { | template<> enums::ServiceArgType to_service_arg_type<std::vector<float>>() { | ||||||
|   return enums::SERVICE_ARG_TYPE_FLOAT_ARRAY; |   return enums::SERVICE_ARG_TYPE_FLOAT_ARRAY; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| #include "atm90e32.h" | #include "atm90e32.h" | ||||||
| #include "atm90e32_reg.h" | #include "atm90e32_reg.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace atm90e32 { | namespace atm90e32 { | ||||||
| @@ -173,7 +174,7 @@ uint16_t ATM90E32Component::read16_(uint16_t a_register) { | |||||||
|   this->disable(); |   this->disable(); | ||||||
|  |  | ||||||
|   output = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF); |   output = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF); | ||||||
|   ESP_LOGVV(TAG, "read16_ 0x%04X output 0x%04X", a_register, output); |   ESP_LOGVV(TAG, "read16_ 0x%04" PRIX16 " output 0x%04" PRIX16, a_register, output); | ||||||
|   return output; |   return output; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -182,8 +183,10 @@ int ATM90E32Component::read32_(uint16_t addr_h, uint16_t addr_l) { | |||||||
|   uint16_t val_l = this->read16_(addr_l); |   uint16_t val_l = this->read16_(addr_l); | ||||||
|   int32_t val = (val_h << 16) | val_l; |   int32_t val = (val_h << 16) | val_l; | ||||||
|  |  | ||||||
|   ESP_LOGVV(TAG, "read32_ addr_h 0x%04X val_h 0x%04X addr_l 0x%04X val_l 0x%04X = %d", addr_h, val_h, addr_l, val_l, |   ESP_LOGVV(TAG, | ||||||
|             val); |             "read32_ addr_h 0x%04" PRIX16 " val_h 0x%04" PRIX16 " addr_l 0x%04" PRIX16 " val_l 0x%04" PRIX16 | ||||||
|  |             " = %" PRId32, | ||||||
|  |             addr_h, val_h, addr_l, val_l, val); | ||||||
|  |  | ||||||
|   return val; |   return val; | ||||||
| } | } | ||||||
| @@ -192,7 +195,7 @@ void ATM90E32Component::write16_(uint16_t a_register, uint16_t val) { | |||||||
|   uint8_t addrh = (a_register >> 8) & 0x03; |   uint8_t addrh = (a_register >> 8) & 0x03; | ||||||
|   uint8_t addrl = (a_register & 0xFF); |   uint8_t addrl = (a_register & 0xFF); | ||||||
|  |  | ||||||
|   ESP_LOGVV(TAG, "write16_ 0x%04X val 0x%04X", a_register, val); |   ESP_LOGVV(TAG, "write16_ 0x%04" PRIX16 " val 0x%04" PRIX16, a_register, val); | ||||||
|   this->enable(); |   this->enable(); | ||||||
|   delayMicroseconds(10); |   delayMicroseconds(10); | ||||||
|   this->write_byte(addrh); |   this->write_byte(addrh); | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ void binary_sensor::MultiClickTrigger::on_state_(bool state) { | |||||||
|     // Start matching |     // Start matching | ||||||
|     MultiClickTriggerEvent evt = this->timing_[0]; |     MultiClickTriggerEvent evt = this->timing_[0]; | ||||||
|     if (evt.state == state) { |     if (evt.state == state) { | ||||||
|       ESP_LOGV(TAG, "START min=%u max=%u", evt.min_length, evt.max_length); |       ESP_LOGV(TAG, "START min=%" PRIu32 " max=%" PRIu32, evt.min_length, evt.max_length); | ||||||
|       ESP_LOGV(TAG, "Multi Click: Starting multi click action!"); |       ESP_LOGV(TAG, "Multi Click: Starting multi click action!"); | ||||||
|       this->at_index_ = 1; |       this->at_index_ = 1; | ||||||
|       if (this->timing_.size() == 1 && evt.max_length == 4294967294UL) { |       if (this->timing_.size() == 1 && evt.max_length == 4294967294UL) { | ||||||
| @@ -51,15 +51,15 @@ void binary_sensor::MultiClickTrigger::on_state_(bool state) { | |||||||
|   MultiClickTriggerEvent evt = this->timing_[*this->at_index_]; |   MultiClickTriggerEvent evt = this->timing_[*this->at_index_]; | ||||||
|  |  | ||||||
|   if (evt.max_length != 4294967294UL) { |   if (evt.max_length != 4294967294UL) { | ||||||
|     ESP_LOGV(TAG, "A i=%u min=%u max=%u", *this->at_index_, evt.min_length, evt.max_length);  // NOLINT |     ESP_LOGV(TAG, "A i=%u min=%" PRIu32 " max=%" PRIu32, *this->at_index_, evt.min_length, evt.max_length);  // NOLINT | ||||||
|     this->schedule_is_valid_(evt.min_length); |     this->schedule_is_valid_(evt.min_length); | ||||||
|     this->schedule_is_not_valid_(evt.max_length); |     this->schedule_is_not_valid_(evt.max_length); | ||||||
|   } else if (*this->at_index_ + 1 != this->timing_.size()) { |   } else if (*this->at_index_ + 1 != this->timing_.size()) { | ||||||
|     ESP_LOGV(TAG, "B i=%u min=%u", *this->at_index_, evt.min_length);  // NOLINT |     ESP_LOGV(TAG, "B i=%u min=%" PRIu32, *this->at_index_, evt.min_length);  // NOLINT | ||||||
|     this->cancel_timeout("is_not_valid"); |     this->cancel_timeout("is_not_valid"); | ||||||
|     this->schedule_is_valid_(evt.min_length); |     this->schedule_is_valid_(evt.min_length); | ||||||
|   } else { |   } else { | ||||||
|     ESP_LOGV(TAG, "C i=%u min=%u", *this->at_index_, evt.min_length);  // NOLINT |     ESP_LOGV(TAG, "C i=%u min=%" PRIu32, *this->at_index_, evt.min_length);  // NOLINT | ||||||
|     this->is_valid_ = false; |     this->is_valid_ = false; | ||||||
|     this->cancel_timeout("is_not_valid"); |     this->cancel_timeout("is_not_valid"); | ||||||
|     this->set_timeout("trigger", evt.min_length, [this]() { this->trigger_(); }); |     this->set_timeout("trigger", evt.min_length, [this]() { this->trigger_(); }); | ||||||
| @@ -68,7 +68,8 @@ void binary_sensor::MultiClickTrigger::on_state_(bool state) { | |||||||
|   *this->at_index_ = *this->at_index_ + 1; |   *this->at_index_ = *this->at_index_ + 1; | ||||||
| } | } | ||||||
| void binary_sensor::MultiClickTrigger::schedule_cooldown_() { | void binary_sensor::MultiClickTrigger::schedule_cooldown_() { | ||||||
|   ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %u 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->is_in_cooldown_ = true; | ||||||
|   this->set_timeout("cooldown", this->invalid_cooldown_, [this]() { |   this->set_timeout("cooldown", this->invalid_cooldown_, [this]() { | ||||||
|     ESP_LOGV(TAG, "Multi Click: Cooldown ended, matching is now enabled again."); |     ESP_LOGV(TAG, "Multi Click: Cooldown ended, matching is now enabled again."); | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <cinttypes> | ||||||
| #include <utility> | #include <utility> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #include "bl0939.h" | #include "bl0939.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace bl0939 { | namespace bl0939 { | ||||||
| @@ -80,7 +81,7 @@ void BL0939::setup() { | |||||||
| void BL0939::received_package_(const DataPacket *data) const { | void BL0939::received_package_(const DataPacket *data) const { | ||||||
|   // Bad header |   // Bad header | ||||||
|   if (data->frame_header != BL0939_PACKET_HEADER) { |   if (data->frame_header != BL0939_PACKET_HEADER) { | ||||||
|     ESP_LOGI("bl0939", "Invalid data. Header mismatch: %d", data->frame_header); |     ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -120,8 +121,9 @@ void BL0939::received_package_(const DataPacket *data) const { | |||||||
|     energy_sensor_sum_->publish_state(total_energy_consumption); |     energy_sensor_sum_->publish_state(total_energy_consumption); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGV("bl0939", "BL0939: U %fV, I1 %fA, I2 %fA, P1 %fW, P2 %fW, CntA %d, CntB %d, ∫P1 %fkWh, ∫P2 %fkWh", v_rms, |   ESP_LOGV(TAG, | ||||||
|            ia_rms, ib_rms, a_watt, b_watt, cfa_cnt, cfb_cnt, a_energy_consumption, b_energy_consumption); |            "BL0939: U %fV, I1 %fA, I2 %fA, P1 %fW, P2 %fW, CntA %" PRId32 ", CntB %" PRId32 ", ∫P1 %fkWh, ∫P2 %fkWh", | ||||||
|  |            v_rms, ia_rms, ib_rms, a_watt, b_watt, cfa_cnt, cfb_cnt, a_energy_consumption, b_energy_consumption); | ||||||
| } | } | ||||||
|  |  | ||||||
| void BL0939::dump_config() {  // NOLINT(readability-function-cognitive-complexity) | void BL0939::dump_config() {  // NOLINT(readability-function-cognitive-complexity) | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #include "bl0940.h" | #include "bl0940.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace bl0940 { | namespace bl0940 { | ||||||
| @@ -77,7 +78,7 @@ float BL0940::update_temp_(sensor::Sensor *sensor, ube16_t temperature) const { | |||||||
|   float converted_temp = ((float) 170 / 448) * (tb / 2 - 32) - 45; |   float converted_temp = ((float) 170 / 448) * (tb / 2 - 32) - 45; | ||||||
|   if (sensor != nullptr) { |   if (sensor != nullptr) { | ||||||
|     if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) { |     if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) { | ||||||
|       ESP_LOGD("bl0940", "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f", |       ESP_LOGD(TAG, "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f", | ||||||
|                sensor->get_name().c_str(), sensor->get_state(), converted_temp); |                sensor->get_name().c_str(), sensor->get_state(), converted_temp); | ||||||
|       return 0.0f; |       return 0.0f; | ||||||
|     } |     } | ||||||
| @@ -89,7 +90,7 @@ float BL0940::update_temp_(sensor::Sensor *sensor, ube16_t temperature) const { | |||||||
| void BL0940::received_package_(const DataPacket *data) const { | void BL0940::received_package_(const DataPacket *data) const { | ||||||
|   // Bad header |   // Bad header | ||||||
|   if (data->frame_header != BL0940_PACKET_HEADER) { |   if (data->frame_header != BL0940_PACKET_HEADER) { | ||||||
|     ESP_LOGI("bl0940", "Invalid data. Header mismatch: %d", data->frame_header); |     ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -115,7 +116,7 @@ void BL0940::received_package_(const DataPacket *data) const { | |||||||
|     energy_sensor_->publish_state(total_energy_consumption); |     energy_sensor_->publish_state(total_energy_consumption); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGV("bl0940", "BL0940: U %fV, I %fA, P %fW, Cnt %d, ∫P %fkWh, T1 %f°C, T2 %f°C", v_rms, i_rms, watt, cf_cnt, |   ESP_LOGV(TAG, "BL0940: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, T1 %f°C, T2 %f°C", v_rms, i_rms, watt, cf_cnt, | ||||||
|            total_energy_consumption, tps1, tps2); |            total_energy_consumption, tps1, tps2); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #include "bl0942.h" | #include "bl0942.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace bl0942 { | namespace bl0942 { | ||||||
| @@ -104,8 +105,8 @@ void BL0942::received_package_(DataPacket *data) { | |||||||
|     frequency_sensor_->publish_state(frequency); |     frequency_sensor_->publish_state(frequency); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %d, ∫P %fkWh, frequency %f°Hz, status 0x%08X", v_rms, i_rms, watt, |   ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, frequency %fHz, status 0x%08X", v_rms, i_rms, | ||||||
|            cf_cnt, total_energy_consumption, frequency, data->status); |            watt, cf_cnt, total_energy_consumption, frequency, data->status); | ||||||
| } | } | ||||||
|  |  | ||||||
| void BL0942::dump_config() {  // NOLINT(readability-function-cognitive-complexity) | void BL0942::dump_config() {  // NOLINT(readability-function-cognitive-complexity) | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #include "ble_rssi_sensor.h" | #include "ble_rssi_sensor.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | ||||||
| #include "esphome/core/application.h" | #include "esphome/core/application.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
| #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
|  |  | ||||||
| @@ -37,6 +37,10 @@ void BLEClientRSSISensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga | |||||||
|     } |     } | ||||||
|     case ESP_GATTC_SEARCH_CMPL_EVT: |     case ESP_GATTC_SEARCH_CMPL_EVT: | ||||||
|       this->node_state = espbt::ClientState::ESTABLISHED; |       this->node_state = espbt::ClientState::ESTABLISHED; | ||||||
|  |       if (this->should_update_) { | ||||||
|  |         this->should_update_ = false; | ||||||
|  |         this->get_rssi_(); | ||||||
|  |       } | ||||||
|       break; |       break; | ||||||
|     default: |     default: | ||||||
|       break; |       break; | ||||||
| @@ -50,6 +54,7 @@ void BLEClientRSSISensor::gap_event_handler(esp_gap_ble_cb_event_t event, esp_bl | |||||||
|       if (param->read_rssi_cmpl.status == ESP_BT_STATUS_SUCCESS) { |       if (param->read_rssi_cmpl.status == ESP_BT_STATUS_SUCCESS) { | ||||||
|         int8_t rssi = param->read_rssi_cmpl.rssi; |         int8_t rssi = param->read_rssi_cmpl.rssi; | ||||||
|         ESP_LOGI(TAG, "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT RSSI: %d", rssi); |         ESP_LOGI(TAG, "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT RSSI: %d", rssi); | ||||||
|  |         this->status_clear_warning(); | ||||||
|         this->publish_state(rssi); |         this->publish_state(rssi); | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
| @@ -61,9 +66,12 @@ void BLEClientRSSISensor::gap_event_handler(esp_gap_ble_cb_event_t event, esp_bl | |||||||
| void BLEClientRSSISensor::update() { | void BLEClientRSSISensor::update() { | ||||||
|   if (this->node_state != espbt::ClientState::ESTABLISHED) { |   if (this->node_state != espbt::ClientState::ESTABLISHED) { | ||||||
|     ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str()); |     ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str()); | ||||||
|  |     this->should_update_ = true; | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |   this->get_rssi_(); | ||||||
|  | } | ||||||
|  | void BLEClientRSSISensor::get_rssi_() { | ||||||
|   ESP_LOGV(TAG, "requesting rssi from %s", this->parent()->address_str().c_str()); |   ESP_LOGV(TAG, "requesting rssi from %s", this->parent()->address_str().c_str()); | ||||||
|   auto status = esp_ble_gap_read_rssi(this->parent()->get_remote_bda()); |   auto status = esp_ble_gap_read_rssi(this->parent()->get_remote_bda()); | ||||||
|   if (status != ESP_OK) { |   if (status != ESP_OK) { | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" |  | ||||||
| #include "esphome/components/ble_client/ble_client.h" | #include "esphome/components/ble_client/ble_client.h" | ||||||
| #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | ||||||
| #include "esphome/components/sensor/sensor.h" | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
| #include <esp_gattc_api.h> | #include <esp_gattc_api.h> | ||||||
| @@ -24,6 +24,10 @@ class BLEClientRSSISensor : public sensor::Sensor, public PollingComponent, publ | |||||||
|  |  | ||||||
|   void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, |   void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | ||||||
|                            esp_ble_gattc_cb_param_t *param) override; |                            esp_ble_gattc_cb_param_t *param) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void get_rssi_(); | ||||||
|  |   bool should_update_{false}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace ble_client | }  // namespace ble_client | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
| #include "bmp3xx.h" | #include "bmp3xx.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace bmp3xx { | namespace bmp3xx { | ||||||
| @@ -198,8 +199,9 @@ void BMP3XXComponent::update() { | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGVV(TAG, "measurement time %d", uint32_t(ceilf(meas_time))); |   const uint32_t meas_timeout = uint32_t(ceilf(meas_time)); | ||||||
|   this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() { |   ESP_LOGVV(TAG, "measurement time %" PRIu32, meas_timeout); | ||||||
|  |   this->set_timeout("data", meas_timeout, [this]() { | ||||||
|     float temperature = 0.0f; |     float temperature = 0.0f; | ||||||
|     float pressure = 0.0f; |     float pressure = 0.0f; | ||||||
|     if (this->pressure_sensor_ != nullptr) { |     if (this->pressure_sensor_ != nullptr) { | ||||||
|   | |||||||
| @@ -16,9 +16,9 @@ void Canbus::setup() { | |||||||
|  |  | ||||||
| void Canbus::dump_config() { | void Canbus::dump_config() { | ||||||
|   if (this->use_extended_id_) { |   if (this->use_extended_id_) { | ||||||
|     ESP_LOGCONFIG(TAG, "config extended id=0x%08x", this->can_id_); |     ESP_LOGCONFIG(TAG, "config extended id=0x%08" PRIx32, this->can_id_); | ||||||
|   } else { |   } else { | ||||||
|     ESP_LOGCONFIG(TAG, "config standard id=0x%03x", this->can_id_); |     ESP_LOGCONFIG(TAG, "config standard id=0x%03" PRIx32, this->can_id_); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -28,9 +28,11 @@ void Canbus::send_data(uint32_t can_id, bool use_extended_id, bool remote_transm | |||||||
|  |  | ||||||
|   uint8_t size = static_cast<uint8_t>(data.size()); |   uint8_t size = static_cast<uint8_t>(data.size()); | ||||||
|   if (use_extended_id) { |   if (use_extended_id) { | ||||||
|     ESP_LOGD(TAG, "send extended id=0x%08x rtr=%s size=%d", can_id, TRUEFALSE(remote_transmission_request), size); |     ESP_LOGD(TAG, "send extended id=0x%08" PRIx32 " rtr=%s size=%d", can_id, TRUEFALSE(remote_transmission_request), | ||||||
|  |              size); | ||||||
|   } else { |   } else { | ||||||
|     ESP_LOGD(TAG, "send standard id=0x%03x rtr=%s size=%d", can_id, TRUEFALSE(remote_transmission_request), size); |     ESP_LOGD(TAG, "send standard id=0x%03" PRIx32 " rtr=%s size=%d", can_id, TRUEFALSE(remote_transmission_request), | ||||||
|  |              size); | ||||||
|   } |   } | ||||||
|   if (size > CAN_MAX_DATA_LENGTH) |   if (size > CAN_MAX_DATA_LENGTH) | ||||||
|     size = CAN_MAX_DATA_LENGTH; |     size = CAN_MAX_DATA_LENGTH; | ||||||
| @@ -49,9 +51,9 @@ void Canbus::send_data(uint32_t can_id, bool use_extended_id, bool remote_transm | |||||||
|  |  | ||||||
| void Canbus::add_trigger(CanbusTrigger *trigger) { | void Canbus::add_trigger(CanbusTrigger *trigger) { | ||||||
|   if (trigger->use_extended_id_) { |   if (trigger->use_extended_id_) { | ||||||
|     ESP_LOGVV(TAG, "add trigger for extended canid=0x%08x", trigger->can_id_); |     ESP_LOGVV(TAG, "add trigger for extended canid=0x%08" PRIx32, trigger->can_id_); | ||||||
|   } else { |   } else { | ||||||
|     ESP_LOGVV(TAG, "add trigger for std canid=0x%03x", trigger->can_id_); |     ESP_LOGVV(TAG, "add trigger for std canid=0x%03" PRIx32, trigger->can_id_); | ||||||
|   } |   } | ||||||
|   this->triggers_.push_back(trigger); |   this->triggers_.push_back(trigger); | ||||||
| }; | }; | ||||||
| @@ -63,10 +65,10 @@ void Canbus::loop() { | |||||||
|   while (this->read_message(&can_message) == canbus::ERROR_OK) { |   while (this->read_message(&can_message) == canbus::ERROR_OK) { | ||||||
|     message_counter++; |     message_counter++; | ||||||
|     if (can_message.use_extended_id) { |     if (can_message.use_extended_id) { | ||||||
|       ESP_LOGD(TAG, "received can message (#%d) extended can_id=0x%x size=%d", message_counter, can_message.can_id, |       ESP_LOGD(TAG, "received can message (#%d) extended can_id=0x%" PRIx32 " size=%d", message_counter, | ||||||
|                can_message.can_data_length_code); |                can_message.can_id, can_message.can_data_length_code); | ||||||
|     } else { |     } else { | ||||||
|       ESP_LOGD(TAG, "received can message (#%d) std can_id=0x%x size=%d", message_counter, can_message.can_id, |       ESP_LOGD(TAG, "received can message (#%d) std can_id=0x%" PRIx32 " size=%d", message_counter, can_message.can_id, | ||||||
|                can_message.can_data_length_code); |                can_message.can_data_length_code); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ | |||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/optional.h" | #include "esphome/core/optional.h" | ||||||
|  |  | ||||||
|  | #include <cinttypes> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #include "cd74hc4067.h" | #include "cd74hc4067.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace cd74hc4067 { | namespace cd74hc4067 { | ||||||
| @@ -27,7 +28,7 @@ void CD74HC4067Component::dump_config() { | |||||||
|   LOG_PIN("  S1 Pin: ", this->pin_s1_); |   LOG_PIN("  S1 Pin: ", this->pin_s1_); | ||||||
|   LOG_PIN("  S2 Pin: ", this->pin_s2_); |   LOG_PIN("  S2 Pin: ", this->pin_s2_); | ||||||
|   LOG_PIN("  S3 Pin: ", this->pin_s3_); |   LOG_PIN("  S3 Pin: ", this->pin_s3_); | ||||||
|   ESP_LOGCONFIG(TAG, "switch delay: %d", this->switch_delay_); |   ESP_LOGCONFIG(TAG, "switch delay: %" PRIu32, this->switch_delay_); | ||||||
| } | } | ||||||
|  |  | ||||||
| void CD74HC4067Component::activate_pin(uint8_t pin) { | void CD74HC4067Component::activate_pin(uint8_t pin) { | ||||||
|   | |||||||
| @@ -121,7 +121,7 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGD(TAG, "Decoded 0x%02X", remote_state); |   ESP_LOGD(TAG, "Decoded 0x%02" PRIX32, remote_state); | ||||||
|   if ((remote_state & 0xFF00000) != 0x8800000) |   if ((remote_state & 0xFF00000) != 0x8800000) | ||||||
|     return false; |     return false; | ||||||
|  |  | ||||||
| @@ -173,7 +173,7 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) { | |||||||
| } | } | ||||||
| void LgIrClimate::transmit_(uint32_t value) { | void LgIrClimate::transmit_(uint32_t value) { | ||||||
|   calc_checksum_(value); |   calc_checksum_(value); | ||||||
|   ESP_LOGD(TAG, "Sending climate_lg_ir code: 0x%02X", value); |   ESP_LOGD(TAG, "Sending climate_lg_ir code: 0x%02" PRIX32, value); | ||||||
|  |  | ||||||
|   auto transmit = this->transmitter_->transmit(); |   auto transmit = this->transmitter_->transmit(); | ||||||
|   auto *data = transmit.get_data(); |   auto *data = transmit.get_data(); | ||||||
|   | |||||||
| @@ -2,6 +2,8 @@ | |||||||
|  |  | ||||||
| #include "esphome/components/climate_ir/climate_ir.h" | #include "esphome/components/climate_ir/climate_ir.h" | ||||||
|  |  | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace climate_ir_lg { | namespace climate_ir_lg { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -101,7 +101,7 @@ void CoolixClimate::transmit_state() { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   ESP_LOGV(TAG, "Sending coolix code: 0x%06X", remote_state); |   ESP_LOGV(TAG, "Sending coolix code: 0x%06" PRIX32, remote_state); | ||||||
|  |  | ||||||
|   auto transmit = this->transmitter_->transmit(); |   auto transmit = this->transmitter_->transmit(); | ||||||
|   auto *data = transmit.get_data(); |   auto *data = transmit.get_data(); | ||||||
| @@ -115,7 +115,7 @@ bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteRecei | |||||||
|     return false; |     return false; | ||||||
|   // Decoded remote state y 3 bytes long code. |   // Decoded remote state y 3 bytes long code. | ||||||
|   uint32_t remote_state = (*decoded).second; |   uint32_t remote_state = (*decoded).second; | ||||||
|   ESP_LOGV(TAG, "Decoded 0x%06X", remote_state); |   ESP_LOGV(TAG, "Decoded 0x%06" PRIX32, remote_state); | ||||||
|   if ((remote_state & 0xFF0000) != 0xB20000) |   if ((remote_state & 0xFF0000) != 0xB20000) | ||||||
|     return false; |     return false; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,8 @@ | |||||||
|  |  | ||||||
| #include "esphome/components/climate_ir/climate_ir.h" | #include "esphome/components/climate_ir/climate_ir.h" | ||||||
|  |  | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace coolix { | namespace coolix { | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								esphome/components/copy/text/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								esphome/components/copy/text/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import text | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_ENTITY_CATEGORY, | ||||||
|  |     CONF_ICON, | ||||||
|  |     CONF_MODE, | ||||||
|  |     CONF_SOURCE_ID, | ||||||
|  | ) | ||||||
|  | from esphome.core.entity_helpers import inherit_property_from | ||||||
|  |  | ||||||
|  | from .. import copy_ns | ||||||
|  |  | ||||||
|  | CopyText = copy_ns.class_("CopyText", text.Text, cg.Component) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = text.TEXT_SCHEMA.extend( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(): cv.declare_id(CopyText), | ||||||
|  |         cv.Required(CONF_SOURCE_ID): cv.use_id(text.Text), | ||||||
|  |     } | ||||||
|  | ).extend(cv.COMPONENT_SCHEMA) | ||||||
|  |  | ||||||
|  | FINAL_VALIDATE_SCHEMA = cv.All( | ||||||
|  |     inherit_property_from(CONF_ICON, CONF_SOURCE_ID), | ||||||
|  |     inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID), | ||||||
|  |     inherit_property_from(CONF_MODE, CONF_SOURCE_ID), | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = await text.new_text(config) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |  | ||||||
|  |     source = await cg.get_variable(config[CONF_SOURCE_ID]) | ||||||
|  |     cg.add(var.set_source(source)) | ||||||
							
								
								
									
										25
									
								
								esphome/components/copy/text/copy_text.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								esphome/components/copy/text/copy_text.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | #include "copy_text.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace copy { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "copy.text"; | ||||||
|  |  | ||||||
|  | void CopyText::setup() { | ||||||
|  |   source_->add_on_state_callback([this](const std::string &value) { this->publish_state(value); }); | ||||||
|  |  | ||||||
|  |   if (source_->has_state()) | ||||||
|  |     this->publish_state(source_->state); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CopyText::dump_config() { LOG_TEXT("", "Copy Text", this); } | ||||||
|  |  | ||||||
|  | void CopyText::control(const std::string &value) { | ||||||
|  |   auto call2 = source_->make_call(); | ||||||
|  |   call2.set_value(value); | ||||||
|  |   call2.perform(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace copy | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										23
									
								
								esphome/components/copy/text/copy_text.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								esphome/components/copy/text/copy_text.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/text/text.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace copy { | ||||||
|  |  | ||||||
|  | class CopyText : public text::Text, public Component { | ||||||
|  |  public: | ||||||
|  |   void set_source(text::Text *source) { source_ = source; } | ||||||
|  |   void setup() override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override { return setup_priority::DATA; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void control(const std::string &value) override; | ||||||
|  |  | ||||||
|  |   text::Text *source_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace copy | ||||||
|  | }  // namespace esphome | ||||||
| @@ -86,7 +86,7 @@ void CS5460AComponent::hw_init_() { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   uint32_t status = this->read_register_(REG_STATUS); |   uint32_t status = this->read_register_(REG_STATUS); | ||||||
|   ESP_LOGCONFIG(TAG, "  Version: %x", (status >> 6) & 7); |   ESP_LOGCONFIG(TAG, "  Version: %" PRIx32, (status >> 6) & 7); | ||||||
|  |  | ||||||
|   this->write_register_(REG_CYCLE_COUNT, samples_); |   this->write_register_(REG_CYCLE_COUNT, samples_); | ||||||
|   this->write_register_(REG_PULSE_RATE, lroundf(pulse_freq_ * 32.0f)); |   this->write_register_(REG_PULSE_RATE, lroundf(pulse_freq_ * 32.0f)); | ||||||
| @@ -323,7 +323,7 @@ void CS5460AComponent::dump_config() { | |||||||
|   ESP_LOGCONFIG(TAG, "  Init status: %s", |   ESP_LOGCONFIG(TAG, "  Init status: %s", | ||||||
|                 state == COMPONENT_STATE_LOOP ? "OK" : (state == COMPONENT_STATE_FAILED ? "failed" : "other")); |                 state == COMPONENT_STATE_LOOP ? "OK" : (state == COMPONENT_STATE_FAILED ? "failed" : "other")); | ||||||
|   LOG_PIN("  CS Pin: ", cs_); |   LOG_PIN("  CS Pin: ", cs_); | ||||||
|   ESP_LOGCONFIG(TAG, "  Samples / cycle: %u", samples_); |   ESP_LOGCONFIG(TAG, "  Samples / cycle: %" PRIu32, samples_); | ||||||
|   ESP_LOGCONFIG(TAG, "  Phase offset: %i", phase_offset_); |   ESP_LOGCONFIG(TAG, "  Phase offset: %i", phase_offset_); | ||||||
|   ESP_LOGCONFIG(TAG, "  PGA Gain: %s", pga_gain_ == CS5460A_PGA_GAIN_50X ? "50x" : "10x"); |   ESP_LOGCONFIG(TAG, "  PGA Gain: %s", pga_gain_ == CS5460A_PGA_GAIN_50X ? "50x" : "10x"); | ||||||
|   ESP_LOGCONFIG(TAG, "  Current gain: %.5f", current_gain_); |   ESP_LOGCONFIG(TAG, "  Current gain: %.5f", current_gain_); | ||||||
|   | |||||||
| @@ -5,6 +5,8 @@ | |||||||
| #include "esphome/components/sensor/sensor.h" | #include "esphome/components/sensor/sensor.h" | ||||||
| #include "esphome/components/spi/spi.h" | #include "esphome/components/spi/spi.h" | ||||||
|  |  | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace cs5460a { | namespace cs5460a { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -217,7 +217,7 @@ void CSE7761Component::get_data_() { | |||||||
|     this->voltage_sensor_->publish_state(voltage); |     this->voltage_sensor_->publish_state(voltage); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   for (uint32_t channel = 0; channel < 2; channel++) { |   for (uint8_t channel = 0; channel < 2; channel++) { | ||||||
|     // Active power = PowerPA * PowerPAC * 1000 / 0x80000000 |     // Active power = PowerPA * PowerPAC * 1000 / 0x80000000 | ||||||
|     float active_power = (float) this->data_.active_power[channel] / this->coefficient_by_unit_(POWER_PAC);  // W |     float active_power = (float) this->data_.active_power[channel] / this->coefficient_by_unit_(POWER_PAC);  // W | ||||||
|     float amps = (float) this->data_.current_rms[channel] / this->coefficient_by_unit_(RMS_IAC);             // A |     float amps = (float) this->data_.current_rms[channel] / this->coefficient_by_unit_(RMS_IAC);             // A | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #include "cse7766.h" | #include "cse7766.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace cse7766 { | namespace cse7766 { | ||||||
| @@ -162,7 +163,7 @@ void CSE7766Component::update() { | |||||||
|     if (counts != 0) { |     if (counts != 0) { | ||||||
|       const auto avg = acc / counts; |       const auto avg = acc / counts; | ||||||
|  |  | ||||||
|       ESP_LOGV(TAG, "Got %s_acc=%.2f %s_counts=%d %s=%.1f", name, acc, name, counts, name, avg); |       ESP_LOGV(TAG, "Got %s_acc=%.2f %s_counts=%" PRIu32 " %s=%.1f", name, acc, name, counts, name, avg); | ||||||
|  |  | ||||||
|       if (sensor != nullptr) { |       if (sensor != nullptr) { | ||||||
|         sensor->publish_state(avg); |         sensor->publish_state(avg); | ||||||
| @@ -178,7 +179,8 @@ void CSE7766Component::update() { | |||||||
|   publish_state("power", this->power_sensor_, this->power_acc_, this->power_counts_); |   publish_state("power", this->power_sensor_, this->power_acc_, this->power_counts_); | ||||||
|  |  | ||||||
|   if (this->energy_total_counts_ != 0) { |   if (this->energy_total_counts_ != 0) { | ||||||
|     ESP_LOGV(TAG, "Got energy_total=%.2f energy_total_counts=%d", this->energy_total_, this->energy_total_counts_); |     ESP_LOGV(TAG, "Got energy_total=%.2f energy_total_counts=%" PRIu32, this->energy_total_, | ||||||
|  |              this->energy_total_counts_); | ||||||
|  |  | ||||||
|     if (this->energy_sensor_ != nullptr) { |     if (this->energy_sensor_ != nullptr) { | ||||||
|       this->energy_sensor_->publish_state(this->energy_total_); |       this->energy_sensor_->publish_state(this->energy_total_); | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ void DeepSleepComponent::setup() { | |||||||
|  |  | ||||||
|   const optional<uint32_t> run_duration = get_run_duration_(); |   const optional<uint32_t> run_duration = get_run_duration_(); | ||||||
|   if (run_duration.has_value()) { |   if (run_duration.has_value()) { | ||||||
|     ESP_LOGI(TAG, "Scheduling Deep Sleep to start in %u ms", *run_duration); |     ESP_LOGI(TAG, "Scheduling Deep Sleep to start in %" PRIu32 " ms", *run_duration); | ||||||
|     this->set_timeout(*run_duration, [this]() { this->begin_sleep(); }); |     this->set_timeout(*run_duration, [this]() { this->begin_sleep(); }); | ||||||
|   } else { |   } else { | ||||||
|     ESP_LOGD(TAG, "Not scheduling Deep Sleep, as no run duration is configured."); |     ESP_LOGD(TAG, "Not scheduling Deep Sleep, as no run duration is configured."); | ||||||
| @@ -49,19 +49,20 @@ void DeepSleepComponent::dump_config() { | |||||||
|   ESP_LOGCONFIG(TAG, "Setting up Deep Sleep..."); |   ESP_LOGCONFIG(TAG, "Setting up Deep Sleep..."); | ||||||
|   if (this->sleep_duration_.has_value()) { |   if (this->sleep_duration_.has_value()) { | ||||||
|     uint32_t duration = *this->sleep_duration_ / 1000; |     uint32_t duration = *this->sleep_duration_ / 1000; | ||||||
|     ESP_LOGCONFIG(TAG, "  Sleep Duration: %u ms", duration); |     ESP_LOGCONFIG(TAG, "  Sleep Duration: %" PRIu32 " ms", duration); | ||||||
|   } |   } | ||||||
|   if (this->run_duration_.has_value()) { |   if (this->run_duration_.has_value()) { | ||||||
|     ESP_LOGCONFIG(TAG, "  Run Duration: %u ms", *this->run_duration_); |     ESP_LOGCONFIG(TAG, "  Run Duration: %" PRIu32 " ms", *this->run_duration_); | ||||||
|   } |   } | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
|   if (wakeup_pin_ != nullptr) { |   if (wakeup_pin_ != nullptr) { | ||||||
|     LOG_PIN("  Wakeup Pin: ", this->wakeup_pin_); |     LOG_PIN("  Wakeup Pin: ", this->wakeup_pin_); | ||||||
|   } |   } | ||||||
|   if (this->wakeup_cause_to_run_duration_.has_value()) { |   if (this->wakeup_cause_to_run_duration_.has_value()) { | ||||||
|     ESP_LOGCONFIG(TAG, "  Default Wakeup Run Duration: %u ms", this->wakeup_cause_to_run_duration_->default_cause); |     ESP_LOGCONFIG(TAG, "  Default Wakeup Run Duration: %" PRIu32 " ms", | ||||||
|     ESP_LOGCONFIG(TAG, "  Touch Wakeup Run Duration: %u ms", this->wakeup_cause_to_run_duration_->touch_cause); |                   this->wakeup_cause_to_run_duration_->default_cause); | ||||||
|     ESP_LOGCONFIG(TAG, "  GPIO Wakeup Run Duration: %u ms", this->wakeup_cause_to_run_duration_->gpio_cause); |     ESP_LOGCONFIG(TAG, "  Touch Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->touch_cause); | ||||||
|  |     ESP_LOGCONFIG(TAG, "  GPIO Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->gpio_cause); | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,6 +14,8 @@ | |||||||
| #include "esphome/core/time.h" | #include "esphome/core/time.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace deep_sleep { | namespace deep_sleep { | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										208
									
								
								esphome/components/dfrobot_sen0395/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								esphome/components/dfrobot_sen0395/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,208 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome import automation | ||||||
|  | from esphome import core | ||||||
|  | from esphome.automation import maybe_simple_id | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  | from esphome.components import uart | ||||||
|  |  | ||||||
|  | CODEOWNERS = ["@niklasweber"] | ||||||
|  | DEPENDENCIES = ["uart"] | ||||||
|  | MULTI_CONF = True | ||||||
|  |  | ||||||
|  | dfrobot_sen0395_ns = cg.esphome_ns.namespace("dfrobot_sen0395") | ||||||
|  | DfrobotSen0395Component = dfrobot_sen0395_ns.class_( | ||||||
|  |     "DfrobotSen0395Component", cg.Component | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | # Actions | ||||||
|  | DfrobotSen0395ResetAction = dfrobot_sen0395_ns.class_( | ||||||
|  |     "DfrobotSen0395ResetAction", automation.Action | ||||||
|  | ) | ||||||
|  | DfrobotSen0395SettingsAction = dfrobot_sen0395_ns.class_( | ||||||
|  |     "DfrobotSen0395SettingsAction", automation.Action | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONF_DFROBOT_SEN0395_ID = "dfrobot_sen0395_id" | ||||||
|  |  | ||||||
|  | CONF_DELAY_AFTER_DETECT = "delay_after_detect" | ||||||
|  | CONF_DELAY_AFTER_DISAPPEAR = "delay_after_disappear" | ||||||
|  | CONF_DETECTION_SEGMENTS = "detection_segments" | ||||||
|  | CONF_OUTPUT_LATENCY = "output_latency" | ||||||
|  | CONF_FACTORY_RESET = "factory_reset" | ||||||
|  | CONF_SENSITIVITY = "sensitivity" | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.All( | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.declare_id(DfrobotSen0395Component), | ||||||
|  |         } | ||||||
|  |     ).extend(uart.UART_DEVICE_SCHEMA) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     await uart.register_uart_device(var, config) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action( | ||||||
|  |     "dfrobot_sen0395.reset", | ||||||
|  |     DfrobotSen0395ResetAction, | ||||||
|  |     maybe_simple_id( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.use_id(DfrobotSen0395Component), | ||||||
|  |         } | ||||||
|  |     ), | ||||||
|  | ) | ||||||
|  | async def dfrobot_sen0395_reset_to_code(config, action_id, template_arg, args): | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg) | ||||||
|  |     await cg.register_parented(var, config[CONF_ID]) | ||||||
|  |  | ||||||
|  |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def range_segment_list(input): | ||||||
|  |     """Validate input is a list of ranges which can be used to configure the dfrobot mmwave radar | ||||||
|  |  | ||||||
|  |     A list of segments should be provided. A minimum of one segment is required and a maximum of | ||||||
|  |     four segments is allowed. A segment describes a range of distances. E.g. from 0mm to 1m. | ||||||
|  |     The distances need to be defined in an ascending order and they cannot contain / intersect | ||||||
|  |     each other. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     # Flatten input to one dimensional list | ||||||
|  |     flat_list = [] | ||||||
|  |     if isinstance(input, list): | ||||||
|  |         for list_item in input: | ||||||
|  |             if isinstance(list_item, list): | ||||||
|  |                 for item in list_item: | ||||||
|  |                     flat_list.append(item) | ||||||
|  |             else: | ||||||
|  |                 flat_list.append(list_item) | ||||||
|  |     else: | ||||||
|  |         flat_list.append(input) | ||||||
|  |  | ||||||
|  |     input = flat_list | ||||||
|  |  | ||||||
|  |     if len(input) < 2: | ||||||
|  |         raise cv.Invalid( | ||||||
|  |             "At least two values need to be specified (start + stop distances)" | ||||||
|  |         ) | ||||||
|  |     if len(input) % 2 != 0: | ||||||
|  |         raise cv.Invalid( | ||||||
|  |             "An even number of arguments must be specified (pairs of min + max)" | ||||||
|  |         ) | ||||||
|  |     if len(input) > 8: | ||||||
|  |         raise cv.Invalid( | ||||||
|  |             "Maximum four segments can be specified (8 values: 4 * min + max)" | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     largest_distance = -1 | ||||||
|  |     for distance in input: | ||||||
|  |         if isinstance(distance, core.Lambda): | ||||||
|  |             continue | ||||||
|  |         m = cv.distance(distance) | ||||||
|  |         if m > 9: | ||||||
|  |             raise cv.Invalid("Maximum distance is 9m") | ||||||
|  |         if m < 0: | ||||||
|  |             raise cv.Invalid("Minimum distance is 0m") | ||||||
|  |         if m <= largest_distance: | ||||||
|  |             raise cv.Invalid( | ||||||
|  |                 "Distances must be delared from small to large " | ||||||
|  |                 "and they cannot contain each other" | ||||||
|  |             ) | ||||||
|  |         largest_distance = m | ||||||
|  |         # Replace distance object with meters float | ||||||
|  |         input[input.index(distance)] = m | ||||||
|  |  | ||||||
|  |     return input | ||||||
|  |  | ||||||
|  |  | ||||||
|  | MMWAVE_SETTINGS_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(): cv.use_id(DfrobotSen0395Component), | ||||||
|  |         cv.Optional(CONF_FACTORY_RESET): cv.templatable(cv.boolean), | ||||||
|  |         cv.Optional(CONF_DETECTION_SEGMENTS): range_segment_list, | ||||||
|  |         cv.Optional(CONF_OUTPUT_LATENCY): { | ||||||
|  |             cv.Required(CONF_DELAY_AFTER_DETECT): cv.templatable( | ||||||
|  |                 cv.All( | ||||||
|  |                     cv.positive_time_period, | ||||||
|  |                     cv.Range(max=core.TimePeriod(seconds=1638.375)), | ||||||
|  |                 ) | ||||||
|  |             ), | ||||||
|  |             cv.Required(CONF_DELAY_AFTER_DISAPPEAR): cv.templatable( | ||||||
|  |                 cv.All( | ||||||
|  |                     cv.positive_time_period, | ||||||
|  |                     cv.Range(max=core.TimePeriod(seconds=1638.375)), | ||||||
|  |                 ) | ||||||
|  |             ), | ||||||
|  |         }, | ||||||
|  |         cv.Optional(CONF_SENSITIVITY): cv.templatable(cv.int_range(min=0, max=9)), | ||||||
|  |     } | ||||||
|  | ).add_extra( | ||||||
|  |     cv.has_at_least_one_key( | ||||||
|  |         CONF_FACTORY_RESET, | ||||||
|  |         CONF_DETECTION_SEGMENTS, | ||||||
|  |         CONF_OUTPUT_LATENCY, | ||||||
|  |         CONF_SENSITIVITY, | ||||||
|  |     ) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action( | ||||||
|  |     "dfrobot_sen0395.settings", | ||||||
|  |     DfrobotSen0395SettingsAction, | ||||||
|  |     MMWAVE_SETTINGS_SCHEMA, | ||||||
|  | ) | ||||||
|  | async def dfrobot_sen0395_settings_to_code(config, action_id, template_arg, args): | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg) | ||||||
|  |     await cg.register_parented(var, config[CONF_ID]) | ||||||
|  |  | ||||||
|  |     if factory_reset_config := config.get(CONF_FACTORY_RESET): | ||||||
|  |         template_ = await cg.templatable(factory_reset_config, args, int) | ||||||
|  |         cg.add(var.set_factory_reset(template_)) | ||||||
|  |  | ||||||
|  |     if CONF_DETECTION_SEGMENTS in config: | ||||||
|  |         segments = config[CONF_DETECTION_SEGMENTS] | ||||||
|  |  | ||||||
|  |         if len(segments) >= 2: | ||||||
|  |             template_ = await cg.templatable(segments[0], args, float) | ||||||
|  |             cg.add(var.set_det_min1(template_)) | ||||||
|  |             template_ = await cg.templatable(segments[1], args, float) | ||||||
|  |             cg.add(var.set_det_max1(template_)) | ||||||
|  |         if len(segments) >= 4: | ||||||
|  |             template_ = await cg.templatable(segments[2], args, float) | ||||||
|  |             cg.add(var.set_det_min2(template_)) | ||||||
|  |             template_ = await cg.templatable(segments[3], args, float) | ||||||
|  |             cg.add(var.set_det_max2(template_)) | ||||||
|  |         if len(segments) >= 6: | ||||||
|  |             template_ = await cg.templatable(segments[4], args, float) | ||||||
|  |             cg.add(var.set_det_min3(template_)) | ||||||
|  |             template_ = await cg.templatable(segments[5], args, float) | ||||||
|  |             cg.add(var.set_det_max3(template_)) | ||||||
|  |         if len(segments) >= 8: | ||||||
|  |             template_ = await cg.templatable(segments[6], args, float) | ||||||
|  |             cg.add(var.set_det_min4(template_)) | ||||||
|  |             template_ = await cg.templatable(segments[7], args, float) | ||||||
|  |             cg.add(var.set_det_max4(template_)) | ||||||
|  |     if CONF_OUTPUT_LATENCY in config: | ||||||
|  |         template_ = await cg.templatable( | ||||||
|  |             config[CONF_OUTPUT_LATENCY][CONF_DELAY_AFTER_DETECT], args, float | ||||||
|  |         ) | ||||||
|  |         if isinstance(template_, cv.TimePeriod): | ||||||
|  |             template_ = template_.total_milliseconds / 1000 | ||||||
|  |         cg.add(var.set_delay_after_detect(template_)) | ||||||
|  |  | ||||||
|  |         template_ = await cg.templatable( | ||||||
|  |             config[CONF_OUTPUT_LATENCY][CONF_DELAY_AFTER_DISAPPEAR], args, float | ||||||
|  |         ) | ||||||
|  |         if isinstance(template_, cv.TimePeriod): | ||||||
|  |             template_ = template_.total_milliseconds / 1000 | ||||||
|  |         cg.add(var.set_delay_after_disappear(template_)) | ||||||
|  |     if CONF_SENSITIVITY in config: | ||||||
|  |         template_ = await cg.templatable(config[CONF_SENSITIVITY], args, int) | ||||||
|  |         cg.add(var.set_sensitivity(template_)) | ||||||
|  |  | ||||||
|  |     return var | ||||||
							
								
								
									
										89
									
								
								esphome/components/dfrobot_sen0395/automation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								esphome/components/dfrobot_sen0395/automation.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/automation.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
|  | #include "dfrobot_sen0395.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace dfrobot_sen0395 { | ||||||
|  |  | ||||||
|  | template<typename... Ts> | ||||||
|  | class DfrobotSen0395ResetAction : public Action<Ts...>, public Parented<DfrobotSen0395Component> { | ||||||
|  |  public: | ||||||
|  |   void play(Ts... x) { this->parent_->enqueue(make_unique<ResetSystemCommand>()); } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template<typename... Ts> | ||||||
|  | class DfrobotSen0395SettingsAction : public Action<Ts...>, public Parented<DfrobotSen0395Component> { | ||||||
|  |  public: | ||||||
|  |   TEMPLATABLE_VALUE(int8_t, factory_reset) | ||||||
|  |   TEMPLATABLE_VALUE(int8_t, start_after_power_on) | ||||||
|  |   TEMPLATABLE_VALUE(int8_t, turn_on_led) | ||||||
|  |   TEMPLATABLE_VALUE(int8_t, presence_via_uart) | ||||||
|  |   TEMPLATABLE_VALUE(int8_t, sensitivity) | ||||||
|  |   TEMPLATABLE_VALUE(float, delay_after_detect) | ||||||
|  |   TEMPLATABLE_VALUE(float, delay_after_disappear) | ||||||
|  |   TEMPLATABLE_VALUE(float, det_min1) | ||||||
|  |   TEMPLATABLE_VALUE(float, det_max1) | ||||||
|  |   TEMPLATABLE_VALUE(float, det_min2) | ||||||
|  |   TEMPLATABLE_VALUE(float, det_max2) | ||||||
|  |   TEMPLATABLE_VALUE(float, det_min3) | ||||||
|  |   TEMPLATABLE_VALUE(float, det_max3) | ||||||
|  |   TEMPLATABLE_VALUE(float, det_min4) | ||||||
|  |   TEMPLATABLE_VALUE(float, det_max4) | ||||||
|  |  | ||||||
|  |   void play(Ts... x) { | ||||||
|  |     this->parent_->enqueue(make_unique<PowerCommand>(0)); | ||||||
|  |     if (this->factory_reset_.has_value() && this->factory_reset_.value(x...) == true) { | ||||||
|  |       this->parent_->enqueue(make_unique<FactoryResetCommand>()); | ||||||
|  |     } | ||||||
|  |     if (this->det_min1_.has_value() && this->det_max1_.has_value()) { | ||||||
|  |       if (this->det_min1_.value() >= 0 && this->det_max1_.value() >= 0) { | ||||||
|  |         this->parent_->enqueue(make_unique<DetRangeCfgCommand>( | ||||||
|  |             this->det_min1_.value_or(-1), this->det_max1_.value_or(-1), this->det_min2_.value_or(-1), | ||||||
|  |             this->det_max2_.value_or(-1), this->det_min3_.value_or(-1), this->det_max3_.value_or(-1), | ||||||
|  |             this->det_min4_.value_or(-1), this->det_max4_.value_or(-1))); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (this->delay_after_detect_.has_value() && this->delay_after_disappear_.has_value()) { | ||||||
|  |       float detect = this->delay_after_detect_.value(x...); | ||||||
|  |       float disappear = this->delay_after_disappear_.value(x...); | ||||||
|  |       if (detect >= 0 && disappear >= 0) { | ||||||
|  |         this->parent_->enqueue(make_unique<OutputLatencyCommand>(detect, disappear)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (this->start_after_power_on_.has_value()) { | ||||||
|  |       int8_t val = this->start_after_power_on_.value(x...); | ||||||
|  |       if (val >= 0) { | ||||||
|  |         this->parent_->enqueue(make_unique<SensorCfgStartCommand>(val)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (this->turn_on_led_.has_value()) { | ||||||
|  |       int8_t val = this->turn_on_led_.value(x...); | ||||||
|  |       if (val >= 0) { | ||||||
|  |         this->parent_->enqueue(make_unique<LedModeCommand>(val)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (this->presence_via_uart_.has_value()) { | ||||||
|  |       int8_t val = this->presence_via_uart_.value(x...); | ||||||
|  |       if (val >= 0) { | ||||||
|  |         this->parent_->enqueue(make_unique<UartOutputCommand>(val)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (this->sensitivity_.has_value()) { | ||||||
|  |       int8_t val = this->sensitivity_.value(x...); | ||||||
|  |       if (val >= 0) { | ||||||
|  |         if (val > 9) { | ||||||
|  |           val = 9; | ||||||
|  |         } | ||||||
|  |         this->parent_->enqueue(make_unique<SensitivityCommand>(val)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     this->parent_->enqueue(make_unique<SaveCfgCommand>()); | ||||||
|  |     this->parent_->enqueue(make_unique<PowerCommand>(1)); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace dfrobot_sen0395 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										22
									
								
								esphome/components/dfrobot_sen0395/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								esphome/components/dfrobot_sen0395/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import binary_sensor | ||||||
|  | from esphome.const import DEVICE_CLASS_MOTION | ||||||
|  | from . import CONF_DFROBOT_SEN0395_ID, DfrobotSen0395Component | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["dfrobot_sen0395"] | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = binary_sensor.binary_sensor_schema( | ||||||
|  |     device_class=DEVICE_CLASS_MOTION | ||||||
|  | ).extend( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(CONF_DFROBOT_SEN0395_ID): cv.use_id(DfrobotSen0395Component), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     parent = await cg.get_variable(config[CONF_DFROBOT_SEN0395_ID]) | ||||||
|  |     binary_sens = await binary_sensor.new_binary_sensor(config) | ||||||
|  |  | ||||||
|  |     cg.add(parent.set_detected_binary_sensor(binary_sens)) | ||||||
							
								
								
									
										329
									
								
								esphome/components/dfrobot_sen0395/commands.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										329
									
								
								esphome/components/dfrobot_sen0395/commands.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,329 @@ | |||||||
|  | #include "commands.h" | ||||||
|  |  | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | #include "dfrobot_sen0395.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace dfrobot_sen0395 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "dfrobot_sen0395.commands"; | ||||||
|  |  | ||||||
|  | uint8_t Command::execute(DfrobotSen0395Component *parent) { | ||||||
|  |   this->parent_ = parent; | ||||||
|  |   if (this->cmd_sent_) { | ||||||
|  |     if (this->parent_->read_message_()) { | ||||||
|  |       std::string message(this->parent_->read_buffer_); | ||||||
|  |       if (message.rfind("is not recognized as a CLI command") != std::string::npos) { | ||||||
|  |         ESP_LOGD(TAG, "Command not recognized properly by sensor"); | ||||||
|  |         if (this->retries_left_ > 0) { | ||||||
|  |           this->retries_left_ -= 1; | ||||||
|  |           this->cmd_sent_ = false; | ||||||
|  |           ESP_LOGD(TAG, "Retrying..."); | ||||||
|  |           return 0; | ||||||
|  |         } else { | ||||||
|  |           this->parent_->find_prompt_(); | ||||||
|  |           return 1;  // Command done | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       uint8_t rc = on_message(message); | ||||||
|  |       if (rc == 2) { | ||||||
|  |         if (this->retries_left_ > 0) { | ||||||
|  |           this->retries_left_ -= 1; | ||||||
|  |           this->cmd_sent_ = false; | ||||||
|  |           ESP_LOGD(TAG, "Retrying..."); | ||||||
|  |           return 0; | ||||||
|  |         } else { | ||||||
|  |           this->parent_->find_prompt_(); | ||||||
|  |           return 1;  // Command done | ||||||
|  |         } | ||||||
|  |       } else if (rc == 0) { | ||||||
|  |         return 0; | ||||||
|  |       } else { | ||||||
|  |         this->parent_->find_prompt_(); | ||||||
|  |         return 1; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (millis() - this->parent_->ts_last_cmd_sent_ > this->timeout_ms_) { | ||||||
|  |       ESP_LOGD(TAG, "Command timeout"); | ||||||
|  |       if (this->retries_left_ > 0) { | ||||||
|  |         this->retries_left_ -= 1; | ||||||
|  |         this->cmd_sent_ = false; | ||||||
|  |         ESP_LOGD(TAG, "Retrying..."); | ||||||
|  |       } else { | ||||||
|  |         return 1;  // Command done | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } else if (this->parent_->send_cmd_(this->cmd_.c_str(), this->cmd_duration_ms_)) { | ||||||
|  |     this->cmd_sent_ = true; | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t ReadStateCommand::execute(DfrobotSen0395Component *parent) { | ||||||
|  |   this->parent_ = parent; | ||||||
|  |   if (this->parent_->read_message_()) { | ||||||
|  |     std::string message(this->parent_->read_buffer_); | ||||||
|  |     if (message.rfind("$JYBSS,0, , , *") != std::string::npos) { | ||||||
|  |       this->parent_->set_detected_(false); | ||||||
|  |       this->parent_->set_active(true); | ||||||
|  |       return 1;  // Command done | ||||||
|  |     } else if (message.rfind("$JYBSS,1, , , *") != std::string::npos) { | ||||||
|  |       this->parent_->set_detected_(true); | ||||||
|  |       this->parent_->set_active(true); | ||||||
|  |       return 1;  // Command done | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (millis() - this->parent_->ts_last_cmd_sent_ > this->timeout_ms_) { | ||||||
|  |     return 1;  // Command done, timeout | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t ReadStateCommand::on_message(std::string &message) { return 1; } | ||||||
|  |  | ||||||
|  | uint8_t PowerCommand::on_message(std::string &message) { | ||||||
|  |   if (message == "sensor stopped already") { | ||||||
|  |     this->parent_->set_active(false); | ||||||
|  |     ESP_LOGI(TAG, "Stopped sensor (already stopped)"); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } else if (message == "sensor started already") { | ||||||
|  |     this->parent_->set_active(true); | ||||||
|  |     ESP_LOGI(TAG, "Started sensor (already started)"); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } else if (message == "new parameter isn't save, can't startSensor") { | ||||||
|  |     this->parent_->set_active(false); | ||||||
|  |     ESP_LOGE(TAG, "Can't start sensor! (Use SaveCfgCommand to save config first)"); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } else if (message == "Done") { | ||||||
|  |     this->parent_->set_active(this->power_on_); | ||||||
|  |     if (this->power_on_) { | ||||||
|  |       ESP_LOGI(TAG, "Started sensor"); | ||||||
|  |     } else { | ||||||
|  |       ESP_LOGI(TAG, "Stopped sensor"); | ||||||
|  |     } | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DetRangeCfgCommand::DetRangeCfgCommand(float min1, float max1, float min2, float max2, float min3, float max3, | ||||||
|  |                                        float min4, float max4) { | ||||||
|  |   // TODO: Print warning when values are rounded | ||||||
|  |   if (min1 < 0 || max1 < 0) { | ||||||
|  |     this->min1_ = min1 = 0; | ||||||
|  |     this->max1_ = max1 = 0; | ||||||
|  |     this->min2_ = min2 = this->max2_ = max2 = this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 = | ||||||
|  |         this->max4_ = max4 = -1; | ||||||
|  |  | ||||||
|  |     ESP_LOGW(TAG, "DetRangeCfgCommand invalid input parameters. Using range config 0 0."); | ||||||
|  |  | ||||||
|  |     this->cmd_ = "detRangeCfg -1 0 0"; | ||||||
|  |   } else if (min2 < 0 || max2 < 0) { | ||||||
|  |     this->min1_ = min1 = round(min1 / 0.15) * 0.15; | ||||||
|  |     this->max1_ = max1 = round(max1 / 0.15) * 0.15; | ||||||
|  |     this->min2_ = min2 = this->max2_ = max2 = this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 = | ||||||
|  |         this->max4_ = max4 = -1; | ||||||
|  |  | ||||||
|  |     this->cmd_ = str_sprintf("detRangeCfg -1 %.0f %.0f", min1 / 0.15, max1 / 0.15); | ||||||
|  |   } else if (min3 < 0 || max3 < 0) { | ||||||
|  |     this->min1_ = min1 = round(min1 / 0.15) * 0.15; | ||||||
|  |     this->max1_ = max1 = round(max1 / 0.15) * 0.15; | ||||||
|  |     this->min2_ = min2 = round(min2 / 0.15) * 0.15; | ||||||
|  |     this->max2_ = max2 = round(max2 / 0.15) * 0.15; | ||||||
|  |     this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 = this->max4_ = max4 = -1; | ||||||
|  |  | ||||||
|  |     this->cmd_ = str_sprintf("detRangeCfg -1 %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15, min2 / 0.15, max2 / 0.15); | ||||||
|  |   } else if (min4 < 0 || max4 < 0) { | ||||||
|  |     this->min1_ = min1 = round(min1 / 0.15) * 0.15; | ||||||
|  |     this->max1_ = max1 = round(max1 / 0.15) * 0.15; | ||||||
|  |     this->min2_ = min2 = round(min2 / 0.15) * 0.15; | ||||||
|  |     this->max2_ = max2 = round(max2 / 0.15) * 0.15; | ||||||
|  |     this->min3_ = min3 = round(min3 / 0.15) * 0.15; | ||||||
|  |     this->max3_ = max3 = round(max3 / 0.15) * 0.15; | ||||||
|  |     this->min4_ = min4 = this->max4_ = max4 = -1; | ||||||
|  |  | ||||||
|  |     this->cmd_ = str_sprintf("detRangeCfg -1 " | ||||||
|  |                              "%.0f %.0f %.0f %.0f %.0f %.0f", | ||||||
|  |                              min1 / 0.15, max1 / 0.15, min2 / 0.15, max2 / 0.15, min3 / 0.15, max3 / 0.15); | ||||||
|  |   } else { | ||||||
|  |     this->min1_ = min1 = round(min1 / 0.15) * 0.15; | ||||||
|  |     this->max1_ = max1 = round(max1 / 0.15) * 0.15; | ||||||
|  |     this->min2_ = min2 = round(min2 / 0.15) * 0.15; | ||||||
|  |     this->max2_ = max2 = round(max2 / 0.15) * 0.15; | ||||||
|  |     this->min3_ = min3 = round(min3 / 0.15) * 0.15; | ||||||
|  |     this->max3_ = max3 = round(max3 / 0.15) * 0.15; | ||||||
|  |     this->min4_ = min4 = round(min4 / 0.15) * 0.15; | ||||||
|  |     this->max4_ = max4 = round(max4 / 0.15) * 0.15; | ||||||
|  |  | ||||||
|  |     this->cmd_ = str_sprintf("detRangeCfg -1 " | ||||||
|  |                              "%.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f", | ||||||
|  |                              min1 / 0.15, max1 / 0.15, min2 / 0.15, max2 / 0.15, min3 / 0.15, max3 / 0.15, min4 / 0.15, | ||||||
|  |                              max4 / 0.15); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   this->min1_ = min1; | ||||||
|  |   this->max1_ = max1; | ||||||
|  |   this->min2_ = min2; | ||||||
|  |   this->max2_ = max2; | ||||||
|  |   this->min3_ = min3; | ||||||
|  |   this->max3_ = max3; | ||||||
|  |   this->min4_ = min4; | ||||||
|  |   this->max4_ = max4; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | uint8_t DetRangeCfgCommand::on_message(std::string &message) { | ||||||
|  |   if (message == "sensor is not stopped") { | ||||||
|  |     ESP_LOGE(TAG, "Cannot configure range config. Sensor is not stopped!"); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } else if (message == "Done") { | ||||||
|  |     ESP_LOGI(TAG, "Updated detection area config:"); | ||||||
|  |     ESP_LOGI(TAG, "Detection area 1 from %.02fm to %.02fm.", this->min1_, this->max1_); | ||||||
|  |     if (this->min2_ >= 0 && this->max2_ >= 0) { | ||||||
|  |       ESP_LOGI(TAG, "Detection area 2 from %.02fm to %.02fm.", this->min2_, this->max2_); | ||||||
|  |     } | ||||||
|  |     if (this->min3_ >= 0 && this->max3_ >= 0) { | ||||||
|  |       ESP_LOGI(TAG, "Detection area 3 from %.02fm to %.02fm.", this->min3_, this->max3_); | ||||||
|  |     } | ||||||
|  |     if (this->min4_ >= 0 && this->max4_ >= 0) { | ||||||
|  |       ESP_LOGI(TAG, "Detection area 4 from %.02fm to %.02fm.", this->min4_, this->max4_); | ||||||
|  |     } | ||||||
|  |     ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str()); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | OutputLatencyCommand::OutputLatencyCommand(float delay_after_detection, float delay_after_disappear) { | ||||||
|  |   delay_after_detection = round(delay_after_detection / 0.025) * 0.025; | ||||||
|  |   delay_after_disappear = round(delay_after_disappear / 0.025) * 0.025; | ||||||
|  |   if (delay_after_detection < 0) | ||||||
|  |     delay_after_detection = 0; | ||||||
|  |   if (delay_after_detection > 1638.375) | ||||||
|  |     delay_after_detection = 1638.375; | ||||||
|  |   if (delay_after_disappear < 0) | ||||||
|  |     delay_after_disappear = 0; | ||||||
|  |   if (delay_after_disappear > 1638.375) | ||||||
|  |     delay_after_disappear = 1638.375; | ||||||
|  |  | ||||||
|  |   this->delay_after_detection_ = delay_after_detection; | ||||||
|  |   this->delay_after_disappear_ = delay_after_disappear; | ||||||
|  |  | ||||||
|  |   this->cmd_ = str_sprintf("outputLatency -1 %.0f %.0f", delay_after_detection / 0.025, delay_after_disappear / 0.025); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | uint8_t OutputLatencyCommand::on_message(std::string &message) { | ||||||
|  |   if (message == "sensor is not stopped") { | ||||||
|  |     ESP_LOGE(TAG, "Cannot configure output latency. Sensor is not stopped!"); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } else if (message == "Done") { | ||||||
|  |     ESP_LOGI(TAG, "Updated output latency config:"); | ||||||
|  |     ESP_LOGI(TAG, "Signal that someone was detected is delayed by %.02fs.", this->delay_after_detection_); | ||||||
|  |     ESP_LOGI(TAG, "Signal that nobody is detected anymore is delayed by %.02fs.", this->delay_after_disappear_); | ||||||
|  |     ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str()); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t SensorCfgStartCommand::on_message(std::string &message) { | ||||||
|  |   if (message == "sensor is not stopped") { | ||||||
|  |     ESP_LOGE(TAG, "Cannot configure sensor startup behavior. Sensor is not stopped!"); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } else if (message == "Done") { | ||||||
|  |     ESP_LOGI(TAG, "Updated sensor startup behavior:"); | ||||||
|  |     if (startup_mode_) { | ||||||
|  |       this->parent_->set_start_after_boot(true); | ||||||
|  |       ESP_LOGI(TAG, "Sensor will start automatically after power-on."); | ||||||
|  |     } else { | ||||||
|  |       this->parent_->set_start_after_boot(false); | ||||||
|  |       ESP_LOGI(TAG, "Sensor needs to be started manually after power-on."); | ||||||
|  |     } | ||||||
|  |     ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str()); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t FactoryResetCommand::on_message(std::string &message) { | ||||||
|  |   if (message == "sensor is not stopped") { | ||||||
|  |     ESP_LOGE(TAG, "Cannot factory reset. Sensor is not stopped!"); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } else if (message == "Done") { | ||||||
|  |     ESP_LOGI(TAG, "Sensor factory reset done."); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t ResetSystemCommand::on_message(std::string &message) { | ||||||
|  |   if (message == "leapMMW:/>") { | ||||||
|  |     ESP_LOGI(TAG, "Restarted sensor."); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t SaveCfgCommand::on_message(std::string &message) { | ||||||
|  |   if (message == "no parameter has changed") { | ||||||
|  |     ESP_LOGI(TAG, "Not saving config (no parameter changed)."); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } else if (message == "Done") { | ||||||
|  |     ESP_LOGI(TAG, "Saved config. Saving a lot may damage the sensor."); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t LedModeCommand::on_message(std::string &message) { | ||||||
|  |   if (message == "sensor is not stopped") { | ||||||
|  |     ESP_LOGE(TAG, "Cannot set led mode. Sensor is not stopped!"); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } else if (message == "Done") { | ||||||
|  |     ESP_LOGI(TAG, "Set led mode done."); | ||||||
|  |     if (this->active_) { | ||||||
|  |       this->parent_->set_led_active(true); | ||||||
|  |       ESP_LOGI(TAG, "Sensor LED will blink."); | ||||||
|  |     } else { | ||||||
|  |       this->parent_->set_led_active(false); | ||||||
|  |       ESP_LOGI(TAG, "Turned off LED."); | ||||||
|  |     } | ||||||
|  |     ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str()); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t UartOutputCommand::on_message(std::string &message) { | ||||||
|  |   if (message == "sensor is not stopped") { | ||||||
|  |     ESP_LOGE(TAG, "Cannot set uart output mode. Sensor is not stopped!"); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } else if (message == "Done") { | ||||||
|  |     ESP_LOGI(TAG, "Set uart mode done."); | ||||||
|  |     if (this->active_) { | ||||||
|  |       this->parent_->set_uart_presence_active(true); | ||||||
|  |       ESP_LOGI(TAG, "Presence information is sent via UART and GPIO."); | ||||||
|  |     } else { | ||||||
|  |       this->parent_->set_uart_presence_active(false); | ||||||
|  |       ESP_LOGI(TAG, "Presence information is only sent via GPIO."); | ||||||
|  |     } | ||||||
|  |     ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str()); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t SensitivityCommand::on_message(std::string &message) { | ||||||
|  |   if (message == "sensor is not stopped") { | ||||||
|  |     ESP_LOGE(TAG, "Cannot set sensitivity. Sensor is not stopped!"); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } else if (message == "Done") { | ||||||
|  |     ESP_LOGI(TAG, "Set sensitivity done. Set to value %d.", this->sensitivity_); | ||||||
|  |     ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str()); | ||||||
|  |     return 1;  // Command done | ||||||
|  |   } | ||||||
|  |   return 0;  // Command not done yet | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace dfrobot_sen0395 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										156
									
								
								esphome/components/dfrobot_sen0395/commands.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								esphome/components/dfrobot_sen0395/commands.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace dfrobot_sen0395 { | ||||||
|  |  | ||||||
|  | class DfrobotSen0395Component; | ||||||
|  |  | ||||||
|  | // Use command queue and time stamps to avoid blocking. | ||||||
|  | // When component has run time, check if minimum time (1s) between | ||||||
|  | // commands has passed. After that run a command from the queue. | ||||||
|  | class Command { | ||||||
|  |  public: | ||||||
|  |   virtual ~Command() = default; | ||||||
|  |   virtual uint8_t execute(DfrobotSen0395Component *parent); | ||||||
|  |   virtual uint8_t on_message(std::string &message) = 0; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   DfrobotSen0395Component *parent_{nullptr}; | ||||||
|  |   std::string cmd_; | ||||||
|  |   bool cmd_sent_{false}; | ||||||
|  |   int8_t retries_left_{2}; | ||||||
|  |   uint32_t cmd_duration_ms_{1000}; | ||||||
|  |   uint32_t timeout_ms_{1500}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class ReadStateCommand : public Command { | ||||||
|  |  public: | ||||||
|  |   uint8_t execute(DfrobotSen0395Component *parent) override; | ||||||
|  |   uint8_t on_message(std::string &message) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint32_t timeout_ms_{500}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class PowerCommand : public Command { | ||||||
|  |  public: | ||||||
|  |   PowerCommand(bool power_on) : power_on_(power_on) { | ||||||
|  |     if (power_on) { | ||||||
|  |       cmd_ = "sensorStart"; | ||||||
|  |     } else { | ||||||
|  |       cmd_ = "sensorStop"; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   uint8_t on_message(std::string &message) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool power_on_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class DetRangeCfgCommand : public Command { | ||||||
|  |  public: | ||||||
|  |   DetRangeCfgCommand(float min1, float max1, float min2, float max2, float min3, float max3, float min4, float max4); | ||||||
|  |   uint8_t on_message(std::string &message) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   float min1_, max1_, min2_, max2_, min3_, max3_, min4_, max4_; | ||||||
|  |   // TODO: Set min max values in component, so they can be published as sensor. | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class OutputLatencyCommand : public Command { | ||||||
|  |  public: | ||||||
|  |   OutputLatencyCommand(float delay_after_detection, float delay_after_disappear); | ||||||
|  |   uint8_t on_message(std::string &message) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   float delay_after_detection_; | ||||||
|  |   float delay_after_disappear_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class SensorCfgStartCommand : public Command { | ||||||
|  |  public: | ||||||
|  |   SensorCfgStartCommand(bool startup_mode) : startup_mode_(startup_mode) { | ||||||
|  |     char tmp_cmd[20] = {0}; | ||||||
|  |     sprintf(tmp_cmd, "sensorCfgStart %d", startup_mode); | ||||||
|  |     cmd_ = std::string(tmp_cmd); | ||||||
|  |   } | ||||||
|  |   uint8_t on_message(std::string &message) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool startup_mode_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class FactoryResetCommand : public Command { | ||||||
|  |  public: | ||||||
|  |   FactoryResetCommand() { cmd_ = "factoryReset 0x45670123 0xCDEF89AB 0x956128C6 0xDF54AC89"; }; | ||||||
|  |   uint8_t on_message(std::string &message) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class ResetSystemCommand : public Command { | ||||||
|  |  public: | ||||||
|  |   ResetSystemCommand() { cmd_ = "resetSystem"; } | ||||||
|  |   uint8_t on_message(std::string &message) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class SaveCfgCommand : public Command { | ||||||
|  |  public: | ||||||
|  |   SaveCfgCommand() { cmd_ = "saveCfg 0x45670123 0xCDEF89AB 0x956128C6 0xDF54AC89"; } | ||||||
|  |   uint8_t on_message(std::string &message) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint32_t cmd_duration_ms_{3000}; | ||||||
|  |   uint32_t timeout_ms_{3500}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class LedModeCommand : public Command { | ||||||
|  |  public: | ||||||
|  |   LedModeCommand(bool active) : active_(active) { | ||||||
|  |     if (active) { | ||||||
|  |       cmd_ = "setLedMode 1 0"; | ||||||
|  |     } else { | ||||||
|  |       cmd_ = "setLedMode 1 1"; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   uint8_t on_message(std::string &message) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool active_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class UartOutputCommand : public Command { | ||||||
|  |  public: | ||||||
|  |   UartOutputCommand(bool active) : active_(active) { | ||||||
|  |     if (active) { | ||||||
|  |       cmd_ = "setUartOutput 1 1"; | ||||||
|  |     } else { | ||||||
|  |       cmd_ = "setUartOutput 1 0"; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   uint8_t on_message(std::string &message) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool active_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class SensitivityCommand : public Command { | ||||||
|  |  public: | ||||||
|  |   SensitivityCommand(uint8_t sensitivity) : sensitivity_(sensitivity) { | ||||||
|  |     if (sensitivity > 9) | ||||||
|  |       sensitivity_ = sensitivity = 9; | ||||||
|  |     char tmp_cmd[20] = {0}; | ||||||
|  |     sprintf(tmp_cmd, "setSensitivity %d", sensitivity); | ||||||
|  |     cmd_ = std::string(tmp_cmd); | ||||||
|  |   }; | ||||||
|  |   uint8_t on_message(std::string &message) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint8_t sensitivity_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace dfrobot_sen0395 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										142
									
								
								esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | |||||||
|  | #include "dfrobot_sen0395.h" | ||||||
|  |  | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace dfrobot_sen0395 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "dfrobot_sen0395"; | ||||||
|  | const char ASCII_CR = 0x0D; | ||||||
|  | const char ASCII_LF = 0x0A; | ||||||
|  |  | ||||||
|  | void DfrobotSen0395Component::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Dfrobot Mmwave Radar:"); | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  |   LOG_BINARY_SENSOR("  ", "Registered", this->detected_binary_sensor_); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |   LOG_SWITCH("  ", "Sensor Active Switch", this->sensor_active_switch_); | ||||||
|  |   LOG_SWITCH("  ", "Turn on LED Switch", this->turn_on_led_switch_); | ||||||
|  |   LOG_SWITCH("  ", "Presence via UART Switch", this->presence_via_uart_switch_); | ||||||
|  |   LOG_SWITCH("  ", "Start after Boot Switch", this->start_after_boot_switch_); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void DfrobotSen0395Component::loop() { | ||||||
|  |   if (cmd_queue_.is_empty()) { | ||||||
|  |     // Command queue empty. Read sensor state. | ||||||
|  |     cmd_queue_.enqueue(make_unique<ReadStateCommand>()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Commands are non-blocking and need to be called repeatedly. | ||||||
|  |   if (cmd_queue_.process(this)) { | ||||||
|  |     // Dequeue if command is done | ||||||
|  |     cmd_queue_.dequeue(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int8_t DfrobotSen0395Component::enqueue(std::unique_ptr<Command> cmd) { | ||||||
|  |   return cmd_queue_.enqueue(std::move(cmd));  // Transfer ownership using std::move | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t DfrobotSen0395Component::read_message_() { | ||||||
|  |   while (this->available()) { | ||||||
|  |     uint8_t byte; | ||||||
|  |     this->read_byte(&byte); | ||||||
|  |  | ||||||
|  |     if (this->read_pos_ == MMWAVE_READ_BUFFER_LENGTH) | ||||||
|  |       this->read_pos_ = 0; | ||||||
|  |  | ||||||
|  |     ESP_LOGVV(TAG, "Buffer pos: %u %d", this->read_pos_, byte); | ||||||
|  |  | ||||||
|  |     if (byte == ASCII_CR) | ||||||
|  |       continue; | ||||||
|  |     if (byte >= 0x7F) | ||||||
|  |       byte = '?';  // needs to be valid utf8 string for log functions. | ||||||
|  |     this->read_buffer_[this->read_pos_] = byte; | ||||||
|  |  | ||||||
|  |     if (this->read_pos_ == 9 && byte == '>') | ||||||
|  |       this->read_buffer_[++this->read_pos_] = ASCII_LF; | ||||||
|  |  | ||||||
|  |     if (this->read_buffer_[this->read_pos_] == ASCII_LF) { | ||||||
|  |       this->read_buffer_[this->read_pos_] = 0; | ||||||
|  |       this->read_pos_ = 0; | ||||||
|  |       ESP_LOGV(TAG, "Message: %s", this->read_buffer_); | ||||||
|  |       return 1;  // Full message in buffer | ||||||
|  |     } else { | ||||||
|  |       this->read_pos_++; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 0;  // No full message yet | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t DfrobotSen0395Component::find_prompt_() { | ||||||
|  |   if (this->read_message_()) { | ||||||
|  |     std::string message(this->read_buffer_); | ||||||
|  |     if (message.rfind("leapMMW:/>") != std::string::npos) { | ||||||
|  |       return 1;  // Prompt found | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 0;  // Not found yet | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t DfrobotSen0395Component::send_cmd_(const char *cmd, uint32_t duration) { | ||||||
|  |   // The interval between two commands must be larger than the specified duration (in ms). | ||||||
|  |   if (millis() - ts_last_cmd_sent_ > duration) { | ||||||
|  |     this->write_str(cmd); | ||||||
|  |     ts_last_cmd_sent_ = millis(); | ||||||
|  |     return 1;  // Command sent | ||||||
|  |   } | ||||||
|  |   // Could not send command yet as command duration did not fully pass yet. | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void DfrobotSen0395Component::set_detected_(bool detected) { | ||||||
|  |   this->detected_ = detected; | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  |   if (this->detected_binary_sensor_ != nullptr) | ||||||
|  |     this->detected_binary_sensor_->publish_state(detected); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int8_t CircularCommandQueue::enqueue(std::unique_ptr<Command> cmd) { | ||||||
|  |   if (this->is_full()) { | ||||||
|  |     ESP_LOGE(TAG, "Command queue is full"); | ||||||
|  |     return -1; | ||||||
|  |   } else if (this->is_empty()) | ||||||
|  |     front_++; | ||||||
|  |   rear_ = (rear_ + 1) % COMMAND_QUEUE_SIZE; | ||||||
|  |   commands_[rear_] = std::move(cmd);  // Transfer ownership using std::move | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::unique_ptr<Command> CircularCommandQueue::dequeue() { | ||||||
|  |   if (this->is_empty()) | ||||||
|  |     return nullptr; | ||||||
|  |   std::unique_ptr<Command> dequeued_cmd = std::move(commands_[front_]); | ||||||
|  |   if (front_ == rear_) { | ||||||
|  |     front_ = -1; | ||||||
|  |     rear_ = -1; | ||||||
|  |   } else | ||||||
|  |     front_ = (front_ + 1) % COMMAND_QUEUE_SIZE; | ||||||
|  |  | ||||||
|  |   return dequeued_cmd; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool CircularCommandQueue::is_empty() { return front_ == -1; } | ||||||
|  |  | ||||||
|  | bool CircularCommandQueue::is_full() { return (rear_ + 1) % COMMAND_QUEUE_SIZE == front_; } | ||||||
|  |  | ||||||
|  | // Run execute method of first in line command. | ||||||
|  | // Execute is non-blocking and has to be called until it returns 1. | ||||||
|  | uint8_t CircularCommandQueue::process(DfrobotSen0395Component *parent) { | ||||||
|  |   if (!is_empty()) { | ||||||
|  |     return commands_[front_]->execute(parent); | ||||||
|  |   } else { | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace dfrobot_sen0395 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										125
									
								
								esphome/components/dfrobot_sen0395/dfrobot_sen0395.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								esphome/components/dfrobot_sen0395/dfrobot_sen0395.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/uart/uart.h" | ||||||
|  | #include "esphome/core/automation.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  | #include "esphome/components/binary_sensor/binary_sensor.h" | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  | #include "esphome/components/switch/switch.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include "commands.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace dfrobot_sen0395 { | ||||||
|  |  | ||||||
|  | const uint8_t MMWAVE_READ_BUFFER_LENGTH = 255; | ||||||
|  |  | ||||||
|  | // forward declaration due to circular dependency | ||||||
|  | class DfrobotSen0395Component; | ||||||
|  |  | ||||||
|  | static const uint8_t COMMAND_QUEUE_SIZE = 20; | ||||||
|  |  | ||||||
|  | class CircularCommandQueue { | ||||||
|  |  public: | ||||||
|  |   int8_t enqueue(std::unique_ptr<Command> cmd); | ||||||
|  |   std::unique_ptr<Command> dequeue(); | ||||||
|  |   bool is_empty(); | ||||||
|  |   bool is_full(); | ||||||
|  |   uint8_t process(DfrobotSen0395Component *parent); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   int front_{-1}; | ||||||
|  |   int rear_{-1}; | ||||||
|  |   std::unique_ptr<Command> commands_[COMMAND_QUEUE_SIZE]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class DfrobotSen0395Component : public uart::UARTDevice, public Component { | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |   SUB_SWITCH(sensor_active) | ||||||
|  |   SUB_SWITCH(turn_on_led) | ||||||
|  |   SUB_SWITCH(presence_via_uart) | ||||||
|  |   SUB_SWITCH(start_after_boot) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   void dump_config() override; | ||||||
|  |   void loop() override; | ||||||
|  |   void set_active(bool active) { | ||||||
|  |     if (active != active_) { | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |       if (this->sensor_active_switch_ != nullptr) | ||||||
|  |         this->sensor_active_switch_->publish_state(active); | ||||||
|  | #endif | ||||||
|  |       active_ = active; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   bool is_active() { return active_; } | ||||||
|  |  | ||||||
|  |   void set_led_active(bool active) { | ||||||
|  |     if (led_active_ != active) { | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |       if (this->turn_on_led_switch_ != nullptr) | ||||||
|  |         this->turn_on_led_switch_->publish_state(active); | ||||||
|  | #endif | ||||||
|  |       led_active_ = active; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   bool is_led_active() { return led_active_; } | ||||||
|  |  | ||||||
|  |   void set_uart_presence_active(bool active) { | ||||||
|  |     uart_presence_active_ = active; | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |     if (this->presence_via_uart_switch_ != nullptr) | ||||||
|  |       this->presence_via_uart_switch_->publish_state(active); | ||||||
|  | #endif | ||||||
|  |   } | ||||||
|  |   bool is_uart_presence_active() { return uart_presence_active_; } | ||||||
|  |  | ||||||
|  |   void set_start_after_boot(bool start) { | ||||||
|  |     start_after_boot_ = start; | ||||||
|  | #ifdef USE_SWITCH | ||||||
|  |     if (this->start_after_boot_switch_ != nullptr) | ||||||
|  |       this->start_after_boot_switch_->publish_state(start); | ||||||
|  | #endif | ||||||
|  |   } | ||||||
|  |   bool does_start_after_boot() { return start_after_boot_; } | ||||||
|  |  | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  |   void set_detected_binary_sensor(binary_sensor::BinarySensor *detected_binary_sensor) { | ||||||
|  |     detected_binary_sensor_ = detected_binary_sensor; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |   int8_t enqueue(std::unique_ptr<Command> cmd); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  |   binary_sensor::BinarySensor *detected_binary_sensor_{nullptr}; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |   bool detected_{false}; | ||||||
|  |   bool active_{false}; | ||||||
|  |   bool led_active_{false}; | ||||||
|  |   bool uart_presence_active_{false}; | ||||||
|  |   bool start_after_boot_{false}; | ||||||
|  |   char read_buffer_[MMWAVE_READ_BUFFER_LENGTH]; | ||||||
|  |   size_t read_pos_{0}; | ||||||
|  |   CircularCommandQueue cmd_queue_; | ||||||
|  |   uint32_t ts_last_cmd_sent_{0}; | ||||||
|  |  | ||||||
|  |   uint8_t read_message_(); | ||||||
|  |   uint8_t find_prompt_(); | ||||||
|  |   uint8_t send_cmd_(const char *cmd, uint32_t duration); | ||||||
|  |  | ||||||
|  |   void set_detected_(bool detected); | ||||||
|  |  | ||||||
|  |   friend class Command; | ||||||
|  |   friend class ReadStateCommand; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace dfrobot_sen0395 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										65
									
								
								esphome/components/dfrobot_sen0395/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								esphome/components/dfrobot_sen0395/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import switch | ||||||
|  | from esphome.const import ENTITY_CATEGORY_CONFIG, CONF_TYPE | ||||||
|  |  | ||||||
|  | from .. import CONF_DFROBOT_SEN0395_ID, DfrobotSen0395Component | ||||||
|  |  | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["dfrobot_sen0395"] | ||||||
|  |  | ||||||
|  | dfrobot_sen0395_ns = cg.esphome_ns.namespace("dfrobot_sen0395") | ||||||
|  | DfrobotSen0395Switch = dfrobot_sen0395_ns.class_( | ||||||
|  |     "DfrobotSen0395Switch", | ||||||
|  |     switch.Switch, | ||||||
|  |     cg.Component, | ||||||
|  |     cg.Parented.template(DfrobotSen0395Component), | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | Sen0395PowerSwitch = dfrobot_sen0395_ns.class_( | ||||||
|  |     "Sen0395PowerSwitch", DfrobotSen0395Switch | ||||||
|  | ) | ||||||
|  | Sen0395LedSwitch = dfrobot_sen0395_ns.class_("Sen0395LedSwitch", DfrobotSen0395Switch) | ||||||
|  | Sen0395UartPresenceSwitch = dfrobot_sen0395_ns.class_( | ||||||
|  |     "Sen0395UartPresenceSwitch", DfrobotSen0395Switch | ||||||
|  | ) | ||||||
|  | Sen0395StartAfterBootSwitch = dfrobot_sen0395_ns.class_( | ||||||
|  |     "Sen0395StartAfterBootSwitch", DfrobotSen0395Switch | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | _SWITCH_SCHEMA = ( | ||||||
|  |     switch.switch_schema( | ||||||
|  |         entity_category=ENTITY_CATEGORY_CONFIG, | ||||||
|  |     ) | ||||||
|  |     .extend( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(CONF_DFROBOT_SEN0395_ID): cv.use_id(DfrobotSen0395Component), | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     .extend(cv.COMPONENT_SCHEMA) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.typed_schema( | ||||||
|  |     { | ||||||
|  |         "sensor_active": _SWITCH_SCHEMA.extend( | ||||||
|  |             {cv.GenerateID(): cv.declare_id(Sen0395PowerSwitch)} | ||||||
|  |         ), | ||||||
|  |         "turn_on_led": _SWITCH_SCHEMA.extend( | ||||||
|  |             {cv.GenerateID(): cv.declare_id(Sen0395LedSwitch)} | ||||||
|  |         ), | ||||||
|  |         "presence_via_uart": _SWITCH_SCHEMA.extend( | ||||||
|  |             {cv.GenerateID(): cv.declare_id(Sen0395UartPresenceSwitch)} | ||||||
|  |         ), | ||||||
|  |         "start_after_boot": _SWITCH_SCHEMA.extend( | ||||||
|  |             {cv.GenerateID(): cv.declare_id(Sen0395StartAfterBootSwitch)} | ||||||
|  |         ), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     parent = await cg.get_variable(config[CONF_DFROBOT_SEN0395_ID]) | ||||||
|  |     var = await switch.new_switch(config) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     await cg.register_parented(var, parent) | ||||||
|  |     cg.add(getattr(parent, f"set_{config[CONF_TYPE]}_switch")(var)) | ||||||
| @@ -0,0 +1,48 @@ | |||||||
|  | #include "dfrobot_sen0395_switch.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace dfrobot_sen0395 { | ||||||
|  |  | ||||||
|  | void Sen0395PowerSwitch::write_state(bool state) { this->parent_->enqueue(make_unique<PowerCommand>(state)); } | ||||||
|  |  | ||||||
|  | void Sen0395LedSwitch::write_state(bool state) { | ||||||
|  |   bool was_active = false; | ||||||
|  |   if (this->parent_->is_active()) { | ||||||
|  |     was_active = true; | ||||||
|  |     this->parent_->enqueue(make_unique<PowerCommand>(false)); | ||||||
|  |   } | ||||||
|  |   this->parent_->enqueue(make_unique<LedModeCommand>(state)); | ||||||
|  |   this->parent_->enqueue(make_unique<SaveCfgCommand>()); | ||||||
|  |   if (was_active) { | ||||||
|  |     this->parent_->enqueue(make_unique<PowerCommand>(true)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Sen0395UartPresenceSwitch::write_state(bool state) { | ||||||
|  |   bool was_active = false; | ||||||
|  |   if (this->parent_->is_active()) { | ||||||
|  |     was_active = true; | ||||||
|  |     this->parent_->enqueue(make_unique<PowerCommand>(false)); | ||||||
|  |   } | ||||||
|  |   this->parent_->enqueue(make_unique<UartOutputCommand>(state)); | ||||||
|  |   this->parent_->enqueue(make_unique<SaveCfgCommand>()); | ||||||
|  |   if (was_active) { | ||||||
|  |     this->parent_->enqueue(make_unique<PowerCommand>(true)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Sen0395StartAfterBootSwitch::write_state(bool state) { | ||||||
|  |   bool was_active = false; | ||||||
|  |   if (this->parent_->is_active()) { | ||||||
|  |     was_active = true; | ||||||
|  |     this->parent_->enqueue(make_unique<PowerCommand>(false)); | ||||||
|  |   } | ||||||
|  |   this->parent_->enqueue(make_unique<SensorCfgStartCommand>(state)); | ||||||
|  |   this->parent_->enqueue(make_unique<SaveCfgCommand>()); | ||||||
|  |   if (was_active) { | ||||||
|  |     this->parent_->enqueue(make_unique<PowerCommand>(true)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace dfrobot_sen0395 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -0,0 +1,34 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/switch/switch.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
|  | #include "../dfrobot_sen0395.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace dfrobot_sen0395 { | ||||||
|  |  | ||||||
|  | class DfrobotSen0395Switch : public switch_::Switch, public Component, public Parented<DfrobotSen0395Component> {}; | ||||||
|  |  | ||||||
|  | class Sen0395PowerSwitch : public DfrobotSen0395Switch { | ||||||
|  |  public: | ||||||
|  |   void write_state(bool state) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class Sen0395LedSwitch : public DfrobotSen0395Switch { | ||||||
|  |  public: | ||||||
|  |   void write_state(bool state) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class Sen0395UartPresenceSwitch : public DfrobotSen0395Switch { | ||||||
|  |  public: | ||||||
|  |   void write_state(bool state) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class Sen0395StartAfterBootSwitch : public DfrobotSen0395Switch { | ||||||
|  |  public: | ||||||
|  |   void write_state(bool state) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace dfrobot_sen0395 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -95,7 +95,7 @@ void DutyTimeSensor::publish_and_save_(const uint32_t sec, const uint32_t ms) { | |||||||
|  |  | ||||||
| void DutyTimeSensor::dump_config() { | void DutyTimeSensor::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "Duty Time:"); |   ESP_LOGCONFIG(TAG, "Duty Time:"); | ||||||
|   ESP_LOGCONFIG(TAG, "  Update Interval: %dms", this->get_update_interval()); |   ESP_LOGCONFIG(TAG, "  Update Interval: %" PRId32 "ms", this->get_update_interval()); | ||||||
|   ESP_LOGCONFIG(TAG, "  Restore: %s", ONOFF(this->restore_)); |   ESP_LOGCONFIG(TAG, "  Restore: %s", ONOFF(this->restore_)); | ||||||
|   LOG_SENSOR("  ", "Duty Time Sensor:", this); |   LOG_SENSOR("  ", "Duty Time Sensor:", this); | ||||||
|   LOG_SENSOR("  ", "Last Duty Time Sensor:", this->last_duty_time_sensor_); |   LOG_SENSOR("  ", "Last Duty Time Sensor:", this->last_duty_time_sensor_); | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
| #include "esphome/core/automation.h" | #include "esphome/core/automation.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/preferences.h" | #include "esphome/core/preferences.h" | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
| #include "esphome/components/socket/socket.h" | #include "esphome/components/socket/socket.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
|  | #include <cinttypes> | ||||||
| #include <map> | #include <map> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <set> | #include <set> | ||||||
|   | |||||||
| @@ -57,8 +57,8 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet | |||||||
|       std::min(it->size(), std::min(output_offset + get_lights_per_universe(), output_offset + packet.count - 1)); |       std::min(it->size(), std::min(output_offset + get_lights_per_universe(), output_offset + packet.count - 1)); | ||||||
|   auto *input_data = packet.values + 1; |   auto *input_data = packet.values + 1; | ||||||
|  |  | ||||||
|   ESP_LOGV(TAG, "Applying data for '%s' on %d universe, for %d-%d.", get_name().c_str(), universe, output_offset, |   ESP_LOGV(TAG, "Applying data for '%s' on %d universe, for %" PRId32 "-%d.", get_name().c_str(), universe, | ||||||
|            output_end); |            output_offset, output_end); | ||||||
|  |  | ||||||
|   switch (channels_) { |   switch (channels_) { | ||||||
|     case E131_MONO: |     case E131_MONO: | ||||||
|   | |||||||
							
								
								
									
										81
									
								
								esphome/components/emc2101/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								esphome/components/emc2101/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import i2c | ||||||
|  | from esphome.const import CONF_ID, CONF_INVERTED, CONF_RESOLUTION | ||||||
|  |  | ||||||
|  | CODEOWNERS = ["@ellull"] | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["i2c"] | ||||||
|  |  | ||||||
|  | CONF_PWM = "pwm" | ||||||
|  | CONF_DIVIDER = "divider" | ||||||
|  | CONF_DAC = "dac" | ||||||
|  | CONF_CONVERSION_RATE = "conversion_rate" | ||||||
|  |  | ||||||
|  | CONF_EMC2101_ID = "emc2101_id" | ||||||
|  |  | ||||||
|  | emc2101_ns = cg.esphome_ns.namespace("emc2101") | ||||||
|  |  | ||||||
|  | Emc2101DACConversionRate = emc2101_ns.enum("Emc2101DACConversionRate") | ||||||
|  | CONVERSIONS_PER_SECOND = { | ||||||
|  |     "1/16": Emc2101DACConversionRate.Emc2101_DAC_1_EVERY_16S, | ||||||
|  |     "1/8": Emc2101DACConversionRate.Emc2101_DAC_1_EVERY_8S, | ||||||
|  |     "1/4": Emc2101DACConversionRate.Emc2101_DAC_1_EVERY_4S, | ||||||
|  |     "1/2": Emc2101DACConversionRate.Emc2101_DAC_1_EVERY_2S, | ||||||
|  |     "1": Emc2101DACConversionRate.Emc2101_DAC_1_EVERY_SECOND, | ||||||
|  |     "2": Emc2101DACConversionRate.Emc2101_DAC_2_EVERY_SECOND, | ||||||
|  |     "4": Emc2101DACConversionRate.Emc2101_DAC_4_EVERY_SECOND, | ||||||
|  |     "8": Emc2101DACConversionRate.Emc2101_DAC_8_EVERY_SECOND, | ||||||
|  |     "16": Emc2101DACConversionRate.Emc2101_DAC_16_EVERY_SECOND, | ||||||
|  |     "32": Emc2101DACConversionRate.Emc2101_DAC_32_EVERY_SECOND, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Emc2101Component = emc2101_ns.class_("Emc2101Component", cg.Component, i2c.I2CDevice) | ||||||
|  |  | ||||||
|  | EMC2101_COMPONENT_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(CONF_EMC2101_ID): cv.use_id(Emc2101Component), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.All( | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.declare_id(Emc2101Component), | ||||||
|  |             cv.Optional(CONF_PWM): cv.Schema( | ||||||
|  |                 { | ||||||
|  |                     cv.Optional(CONF_RESOLUTION, default=23): cv.int_range( | ||||||
|  |                         min=0, max=31 | ||||||
|  |                     ), | ||||||
|  |                     cv.Optional(CONF_DIVIDER, default=1): cv.uint8_t, | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_DAC): cv.Schema( | ||||||
|  |                 { | ||||||
|  |                     cv.Optional(CONF_CONVERSION_RATE, default="16"): cv.enum( | ||||||
|  |                         CONVERSIONS_PER_SECOND | ||||||
|  |                     ), | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_INVERTED, default=False): cv.boolean, | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     .extend(cv.COMPONENT_SCHEMA) | ||||||
|  |     .extend(i2c.i2c_device_schema(0x4C)), | ||||||
|  |     cv.has_exactly_one_key(CONF_PWM, CONF_DAC), | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     await i2c.register_i2c_device(var, config) | ||||||
|  |  | ||||||
|  |     if pwm_config := config.get(CONF_PWM): | ||||||
|  |         cg.add(var.set_dac_mode(False)) | ||||||
|  |         cg.add(var.set_pwm_resolution(pwm_config[CONF_RESOLUTION])) | ||||||
|  |         cg.add(var.set_pwm_divider(pwm_config[CONF_DIVIDER])) | ||||||
|  |     if dac_config := config.get(CONF_DAC): | ||||||
|  |         cg.add(var.set_dac_mode(True)) | ||||||
|  |         cg.add(var.set_dac_conversion_rate(dac_config[CONF_CONVERSION_RATE])) | ||||||
|  |     cg.add(var.set_inverted(config[CONF_INVERTED])) | ||||||
							
								
								
									
										169
									
								
								esphome/components/emc2101/emc2101.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								esphome/components/emc2101/emc2101.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | |||||||
|  | // Implementation based on: | ||||||
|  | //  - Adafruit_EMC2101: https://github.com/adafruit/Adafruit_EMC2101 | ||||||
|  | //  - Official Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/2101.pdf | ||||||
|  |  | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | #include "emc2101.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace emc2101 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "EMC2101"; | ||||||
|  |  | ||||||
|  | static const uint8_t EMC2101_CHIP_ID = 0x16;      // EMC2101 default device id from part id | ||||||
|  | static const uint8_t EMC2101_ALT_CHIP_ID = 0x28;  // EMC2101 alternate device id from part id | ||||||
|  |  | ||||||
|  | // EMC2101 registers from the datasheet. We only define what we use. | ||||||
|  | static const uint8_t EMC2101_REGISTER_INTERNAL_TEMP = 0x00;      // The internal temperature register | ||||||
|  | static const uint8_t EMC2101_REGISTER_EXTERNAL_TEMP_MSB = 0x01;  // high byte for the external temperature reading | ||||||
|  | static const uint8_t EMC2101_REGISTER_DAC_CONV_RATE = 0x04;      // DAC convesion rate config | ||||||
|  | static const uint8_t EMC2101_REGISTER_EXTERNAL_TEMP_LSB = 0x10;  // low byte for the external temperature reading | ||||||
|  | static const uint8_t EMC2101_REGISTER_CONFIG = 0x03;             // configuration register | ||||||
|  | static const uint8_t EMC2101_REGISTER_TACH_LSB = 0x46;           // Tach RPM data low byte | ||||||
|  | static const uint8_t EMC2101_REGISTER_TACH_MSB = 0x47;           // Tach RPM data high byte | ||||||
|  | static const uint8_t EMC2101_REGISTER_FAN_CONFIG = 0x4A;         // General fan config register | ||||||
|  | static const uint8_t EMC2101_REGISTER_FAN_SETTING = 0x4C;        // Fan speed for non-LUT settings | ||||||
|  | static const uint8_t EMC2101_REGISTER_PWM_FREQ = 0x4D;           // PWM frequency setting | ||||||
|  | static const uint8_t EMC2101_REGISTER_PWM_DIV = 0x4E;            // PWM frequency divisor | ||||||
|  | static const uint8_t EMC2101_REGISTER_WHOAMI = 0xFD;             // Chip ID register | ||||||
|  |  | ||||||
|  | // EMC2101 configuration bits from the datasheet. We only define what we use. | ||||||
|  |  | ||||||
|  | // Determines the funcionallity of the ALERT/TACH pin. | ||||||
|  | // 0 (default): The ALERT/TECH pin will function as an open drain, active low interrupt. | ||||||
|  | // 1: The ALERT/TECH pin will function as a high impedance TACH input. This may require an | ||||||
|  | // external pull-up resistor to set the proper signaling levels. | ||||||
|  | static const uint8_t EMC2101_ALT_TCH_BIT = 1 << 2; | ||||||
|  |  | ||||||
|  | // Determines the FAN output mode. | ||||||
|  | // 0 (default): PWM output enabled at FAN pin. | ||||||
|  | // 1: DAC output enabled at FAN ping. | ||||||
|  | static const uint8_t EMC2101_DAC_BIT = 1 << 4; | ||||||
|  |  | ||||||
|  | // Overrides the CLK_SEL bit and uses the Frequency Divide Register to determine | ||||||
|  | // the base PWM frequency. It is recommended that this bit be set for maximum PWM resolution. | ||||||
|  | // 0 (default): The base clock frequency for the PWM is determined by the CLK_SEL bit. | ||||||
|  | // 1 (recommended): The base clock that is used to determine the PWM frequency is set by the | ||||||
|  | // Frequency Divide Register | ||||||
|  | static const uint8_t EMC2101_CLK_OVR_BIT = 1 << 2; | ||||||
|  |  | ||||||
|  | // Sets the polarity of the Fan output driver. | ||||||
|  | // 0 (default): The polarity of the Fan output driver is non-inverted. A '00h' setting will | ||||||
|  | // correspond to a 0% duty cycle or a minimum DAC output voltage. | ||||||
|  | // 1: The polarity of the Fan output driver is inverted. A '00h' setting will correspond to a | ||||||
|  | // 100% duty cycle or a maximum DAC output voltage. | ||||||
|  | static const uint8_t EMC2101_POLARITY_BIT = 1 << 4; | ||||||
|  |  | ||||||
|  | float Emc2101Component::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||||
|  |  | ||||||
|  | void Emc2101Component::setup() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Setting up Emc2101 sensor..."); | ||||||
|  |  | ||||||
|  |   // make sure we're talking to the right chip | ||||||
|  |   uint8_t chip_id = reg(EMC2101_REGISTER_WHOAMI).get(); | ||||||
|  |   if ((chip_id != EMC2101_CHIP_ID) && (chip_id != EMC2101_ALT_CHIP_ID)) { | ||||||
|  |     ESP_LOGE(TAG, "Wrong chip ID %02X", chip_id); | ||||||
|  |     this->mark_failed(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Configure EMC2101 | ||||||
|  |   i2c::I2CRegister config = reg(EMC2101_REGISTER_CONFIG); | ||||||
|  |   config |= EMC2101_ALT_TCH_BIT; | ||||||
|  |   if (this->dac_mode_) { | ||||||
|  |     config |= EMC2101_DAC_BIT; | ||||||
|  |   } | ||||||
|  |   if (this->inverted_) { | ||||||
|  |     config |= EMC2101_POLARITY_BIT; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->dac_mode_) {  // DAC mode configurations | ||||||
|  |     // set DAC conversion rate | ||||||
|  |     reg(EMC2101_REGISTER_DAC_CONV_RATE) = this->dac_conversion_rate_; | ||||||
|  |   } else {  // PWM mode configurations | ||||||
|  |     // set PWM divider | ||||||
|  |     reg(EMC2101_REGISTER_FAN_CONFIG) |= EMC2101_CLK_OVR_BIT; | ||||||
|  |     reg(EMC2101_REGISTER_PWM_DIV) = this->pwm_divider_; | ||||||
|  |  | ||||||
|  |     // set PWM resolution | ||||||
|  |     reg(EMC2101_REGISTER_PWM_FREQ) = this->pwm_resolution_; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Emc2101Component::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Emc2101 component:"); | ||||||
|  |   LOG_I2C_DEVICE(this); | ||||||
|  |   if (this->is_failed()) { | ||||||
|  |     ESP_LOGE(TAG, "Communication with EMC2101 failed!"); | ||||||
|  |   } | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Mode: %s", this->dac_mode_ ? "DAC" : "PWM"); | ||||||
|  |   if (this->dac_mode_) { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  DAC Conversion Rate: %X", this->dac_conversion_rate_); | ||||||
|  |   } else { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  PWM Resolution: %02X", this->pwm_resolution_); | ||||||
|  |     ESP_LOGCONFIG(TAG, "  PWM Divider: %02X", this->pwm_divider_); | ||||||
|  |   } | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Inverted: %s", YESNO(this->inverted_)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Emc2101Component::set_duty_cycle(float value) { | ||||||
|  |   uint8_t duty_cycle = remap(value, 0.0f, 1.0f, (uint8_t) 0, this->max_output_value_); | ||||||
|  |   ESP_LOGD(TAG, "Setting duty fan setting to %02X", duty_cycle); | ||||||
|  |   if (!this->write_byte(EMC2101_REGISTER_FAN_SETTING, duty_cycle)) { | ||||||
|  |     ESP_LOGE(TAG, "Communication with EMC2101 failed!"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float Emc2101Component::get_duty_cycle() { | ||||||
|  |   uint8_t duty_cycle; | ||||||
|  |   if (!this->read_byte(EMC2101_REGISTER_FAN_SETTING, &duty_cycle)) { | ||||||
|  |     ESP_LOGE(TAG, "Communication with EMC2101 failed!"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |     return NAN; | ||||||
|  |   } | ||||||
|  |   return remap(duty_cycle, (uint8_t) 0, this->max_output_value_, 0.0f, 1.0f); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float Emc2101Component::get_internal_temperature() { | ||||||
|  |   uint8_t temperature; | ||||||
|  |   if (!this->read_byte(EMC2101_REGISTER_INTERNAL_TEMP, &temperature)) { | ||||||
|  |     ESP_LOGE(TAG, "Communication with EMC2101 failed!"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |     return NAN; | ||||||
|  |   } | ||||||
|  |   return temperature; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float Emc2101Component::get_external_temperature() { | ||||||
|  |   // Read **MSB** first to match 'Data Read Interlock' behavior from 6.1 of datasheet | ||||||
|  |   uint8_t lsb, msb; | ||||||
|  |   if (!this->read_byte(EMC2101_REGISTER_EXTERNAL_TEMP_MSB, &msb) || | ||||||
|  |       !this->read_byte(EMC2101_REGISTER_EXTERNAL_TEMP_LSB, &lsb)) { | ||||||
|  |     ESP_LOGE(TAG, "Communication with EMC2101 failed!"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |     return NAN; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // join msb and lsb (5 least significant bits are not used) | ||||||
|  |   uint16_t raw = (msb << 8 | lsb) >> 5; | ||||||
|  |   return raw * 0.125; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float Emc2101Component::get_speed() { | ||||||
|  |   // Read **LSB** first to match 'Data Read Interlock' behavior from 6.1 of datasheet | ||||||
|  |   uint8_t lsb, msb; | ||||||
|  |   if (!this->read_byte(EMC2101_REGISTER_TACH_LSB, &lsb) || !this->read_byte(EMC2101_REGISTER_TACH_MSB, &msb)) { | ||||||
|  |     ESP_LOGE(TAG, "Communication with EMC2101 failed!"); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |     return NAN; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // calculate RPMs | ||||||
|  |   uint16_t tach = msb << 8 | lsb; | ||||||
|  |   return tach == 0xFFFF ? 0.0f : 5400000.0f / tach; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace emc2101 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										115
									
								
								esphome/components/emc2101/emc2101.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								esphome/components/emc2101/emc2101.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/i2c/i2c.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace emc2101 { | ||||||
|  |  | ||||||
|  | /** Enum listing all DAC conversion rates for the EMC2101. | ||||||
|  |  * | ||||||
|  |  * Specific values of the enum constants are register values taken from the EMC2101 datasheet. | ||||||
|  |  */ | ||||||
|  | enum Emc2101DACConversionRate { | ||||||
|  |   EMC2101_DAC_1_EVERY_16_S, | ||||||
|  |   EMC2101_DAC_1_EVERY_8_S, | ||||||
|  |   EMC2101_DAC_1_EVERY_4_S, | ||||||
|  |   EMC2101_DAC_1_EVERY_2_S, | ||||||
|  |   EMC2101_DAC_1_EVERY_SECOND, | ||||||
|  |   EMC2101_DAC_2_EVERY_SECOND, | ||||||
|  |   EMC2101_DAC_4_EVERY_SECOND, | ||||||
|  |   EMC2101_DAC_8_EVERY_SECOND, | ||||||
|  |   EMC2101_DAC_16_EVERY_SECOND, | ||||||
|  |   EMC2101_DAC_32_EVERY_SECOND, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /// This class includes support for the EMC2101 i2c fan controller. | ||||||
|  | /// The device has an output (PWM or DAC) and several sensors and this | ||||||
|  | /// class is for the EMC2101 configuration. | ||||||
|  | class Emc2101Component : public Component, public i2c::I2CDevice { | ||||||
|  |  public: | ||||||
|  |   /** Sets the mode of the output. | ||||||
|  |    * | ||||||
|  |    * @param dac_mode false for PWM output and true for DAC mode. | ||||||
|  |    */ | ||||||
|  |   void set_dac_mode(bool dac_mode) { | ||||||
|  |     this->dac_mode_ = dac_mode; | ||||||
|  |     this->max_output_value_ = 63; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** Sets the PWM resolution. | ||||||
|  |    * | ||||||
|  |    * @param resolution the PWM resolution. | ||||||
|  |    */ | ||||||
|  |   void set_pwm_resolution(uint8_t resolution) { | ||||||
|  |     this->pwm_resolution_ = resolution; | ||||||
|  |     this->max_output_value_ = 2 * resolution; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** Sets the PWM divider used to derive the PWM frequency. | ||||||
|  |    * | ||||||
|  |    * @param divider The PWM divider. | ||||||
|  |    */ | ||||||
|  |   void set_pwm_divider(uint8_t divider) { this->pwm_divider_ = divider; } | ||||||
|  |  | ||||||
|  |   /** Sets the DAC conversion rate (how many conversions per second). | ||||||
|  |    * | ||||||
|  |    * @param conversion_rate The DAC conversion rate. | ||||||
|  |    */ | ||||||
|  |   void set_dac_conversion_rate(Emc2101DACConversionRate conversion_rate) { | ||||||
|  |     this->dac_conversion_rate_ = conversion_rate; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** Inverts the polarity of the Fan output. | ||||||
|  |    * | ||||||
|  |    * @param inverted Invert or not the Fan output. | ||||||
|  |    */ | ||||||
|  |   void set_inverted(bool inverted) { this->inverted_ = inverted; } | ||||||
|  |  | ||||||
|  |   /** Sets the Fan output duty cycle | ||||||
|  |    * | ||||||
|  |    * @param value The duty cycle value, from 0.0f to 1.0f. | ||||||
|  |    */ | ||||||
|  |   void set_duty_cycle(float value); | ||||||
|  |  | ||||||
|  |   /** Gets the Fan output duty cycle | ||||||
|  |    * | ||||||
|  |    * @return The duty cycle percentage from 0.0f to 1.0f. | ||||||
|  |    */ | ||||||
|  |   float get_duty_cycle(); | ||||||
|  |  | ||||||
|  |   /** Gets the internal temperature sensor reading. | ||||||
|  |    * | ||||||
|  |    * @return The temperature in degrees celsius. | ||||||
|  |    */ | ||||||
|  |   float get_internal_temperature(); | ||||||
|  |  | ||||||
|  |   /** Gets the external temperature sensor reading. | ||||||
|  |    * | ||||||
|  |    * @return The temperature in degrees celsius. | ||||||
|  |    */ | ||||||
|  |   float get_external_temperature(); | ||||||
|  |  | ||||||
|  |   /** Gets the tachometer speed sensor reading. | ||||||
|  |    * | ||||||
|  |    * @return The fan speed in RPMs. | ||||||
|  |    */ | ||||||
|  |   float get_speed(); | ||||||
|  |  | ||||||
|  |   /** Used by ESPHome framework. */ | ||||||
|  |   void setup() override; | ||||||
|  |   /** Used by ESPHome framework. */ | ||||||
|  |   void dump_config() override; | ||||||
|  |   /** Used by ESPHome framework. */ | ||||||
|  |   float get_setup_priority() const override; | ||||||
|  |  | ||||||
|  |   bool dac_mode_{false}; | ||||||
|  |   bool inverted_{false}; | ||||||
|  |   uint8_t max_output_value_; | ||||||
|  |   uint8_t pwm_resolution_; | ||||||
|  |   uint8_t pwm_divider_; | ||||||
|  |   Emc2101DACConversionRate dac_conversion_rate_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace emc2101 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										21
									
								
								esphome/components/emc2101/output/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								esphome/components/emc2101/output/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import output | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  | from .. import EMC2101_COMPONENT_SCHEMA, CONF_EMC2101_ID, emc2101_ns | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["emc2101"] | ||||||
|  |  | ||||||
|  | EMC2101Output = emc2101_ns.class_("EMC2101Output", output.FloatOutput) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = EMC2101_COMPONENT_SCHEMA.extend( | ||||||
|  |     { | ||||||
|  |         cv.Required(CONF_ID): cv.declare_id(EMC2101Output), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     paren = await cg.get_variable(config[CONF_EMC2101_ID]) | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID], paren) | ||||||
|  |     await output.register_output(var, config) | ||||||
							
								
								
									
										9
									
								
								esphome/components/emc2101/output/emc2101_output.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								esphome/components/emc2101/output/emc2101_output.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | #include "emc2101_output.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace emc2101 { | ||||||
|  |  | ||||||
|  | void EMC2101Output::write_state(float state) { this->parent_->set_duty_cycle(state); } | ||||||
|  |  | ||||||
|  | }  // namespace emc2101 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										22
									
								
								esphome/components/emc2101/output/emc2101_output.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								esphome/components/emc2101/output/emc2101_output.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "../emc2101.h" | ||||||
|  | #include "esphome/components/output/float_output.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace emc2101 { | ||||||
|  |  | ||||||
|  | /// This class allows to control the EMC2101 output. | ||||||
|  | class EMC2101Output : public output::FloatOutput { | ||||||
|  |  public: | ||||||
|  |   EMC2101Output(Emc2101Component *parent) : parent_(parent) {} | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   /** Used by ESPHome framework. */ | ||||||
|  |   void write_state(float state) override; | ||||||
|  |  | ||||||
|  |   Emc2101Component *parent_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace emc2101 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										74
									
								
								esphome/components/emc2101/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								esphome/components/emc2101/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import sensor | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_ID, | ||||||
|  |     CONF_SPEED, | ||||||
|  |     DEVICE_CLASS_TEMPERATURE, | ||||||
|  |     STATE_CLASS_MEASUREMENT, | ||||||
|  |     UNIT_CELSIUS, | ||||||
|  |     UNIT_PERCENT, | ||||||
|  |     UNIT_REVOLUTIONS_PER_MINUTE, | ||||||
|  |     ICON_PERCENT, | ||||||
|  | ) | ||||||
|  | from .. import EMC2101_COMPONENT_SCHEMA, CONF_EMC2101_ID, emc2101_ns | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["emc2101"] | ||||||
|  |  | ||||||
|  | CONF_INTERNAL_TEMPERATURE = "internal_temperature" | ||||||
|  | CONF_EXTERNAL_TEMPERATURE = "external_temperature" | ||||||
|  | CONF_DUTY_CYCLE = "duty_cycle" | ||||||
|  |  | ||||||
|  | EMC2101Sensor = emc2101_ns.class_("EMC2101Sensor", cg.PollingComponent) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = EMC2101_COMPONENT_SCHEMA.extend( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(): cv.declare_id(EMC2101Sensor), | ||||||
|  |         cv.Optional(CONF_INTERNAL_TEMPERATURE): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_CELSIUS, | ||||||
|  |             accuracy_decimals=0, | ||||||
|  |             device_class=DEVICE_CLASS_TEMPERATURE, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_EXTERNAL_TEMPERATURE): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_CELSIUS, | ||||||
|  |             accuracy_decimals=3, | ||||||
|  |             device_class=DEVICE_CLASS_TEMPERATURE, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_SPEED): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_REVOLUTIONS_PER_MINUTE, | ||||||
|  |             accuracy_decimals=2, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |             icon="mdi:fan", | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_DUTY_CYCLE): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_PERCENT, | ||||||
|  |             accuracy_decimals=2, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |             icon=ICON_PERCENT, | ||||||
|  |         ), | ||||||
|  |     } | ||||||
|  | ).extend(cv.polling_component_schema("60s")) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     paren = await cg.get_variable(config[CONF_EMC2101_ID]) | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID], paren) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |  | ||||||
|  |     if CONF_INTERNAL_TEMPERATURE in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_INTERNAL_TEMPERATURE]) | ||||||
|  |         cg.add(var.set_internal_temperature_sensor(sens)) | ||||||
|  |  | ||||||
|  |     if CONF_EXTERNAL_TEMPERATURE in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_EXTERNAL_TEMPERATURE]) | ||||||
|  |         cg.add(var.set_external_temperature_sensor(sens)) | ||||||
|  |  | ||||||
|  |     if CONF_SPEED in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_SPEED]) | ||||||
|  |         cg.add(var.set_speed_sensor(sens)) | ||||||
|  |  | ||||||
|  |     if CONF_DUTY_CYCLE in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_DUTY_CYCLE]) | ||||||
|  |         cg.add(var.set_duty_cycle_sensor(sens)) | ||||||
							
								
								
									
										43
									
								
								esphome/components/emc2101/sensor/emc2101_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								esphome/components/emc2101/sensor/emc2101_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | #include "emc2101_sensor.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace emc2101 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "EMC2101.sensor"; | ||||||
|  |  | ||||||
|  | float EMC2101Sensor::get_setup_priority() const { return setup_priority::DATA; } | ||||||
|  |  | ||||||
|  | void EMC2101Sensor::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Emc2101 sensor:"); | ||||||
|  |   LOG_SENSOR("  ", "Internal temperature", this->internal_temperature_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "External temperature", this->external_temperature_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Speed", this->speed_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Duty cycle", this->duty_cycle_sensor_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void EMC2101Sensor::update() { | ||||||
|  |   if (this->internal_temperature_sensor_ != nullptr) { | ||||||
|  |     float internal_temperature = this->parent_->get_internal_temperature(); | ||||||
|  |     this->internal_temperature_sensor_->publish_state(internal_temperature); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->external_temperature_sensor_ != nullptr) { | ||||||
|  |     float external_temperature = this->parent_->get_external_temperature(); | ||||||
|  |     this->external_temperature_sensor_->publish_state(external_temperature); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->speed_sensor_ != nullptr) { | ||||||
|  |     float speed = this->parent_->get_speed(); | ||||||
|  |     this->speed_sensor_->publish_state(speed); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->duty_cycle_sensor_ != nullptr) { | ||||||
|  |     float duty_cycle = this->parent_->get_duty_cycle(); | ||||||
|  |     this->duty_cycle_sensor_->publish_state(duty_cycle * 100.0f); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace emc2101 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										39
									
								
								esphome/components/emc2101/sensor/emc2101_sensor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								esphome/components/emc2101/sensor/emc2101_sensor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "../emc2101.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace emc2101 { | ||||||
|  |  | ||||||
|  | /// This class exposes the EMC2101 sensors. | ||||||
|  | class EMC2101Sensor : public PollingComponent { | ||||||
|  |  public: | ||||||
|  |   EMC2101Sensor(Emc2101Component *parent) : parent_(parent) {} | ||||||
|  |   /** Used by ESPHome framework. */ | ||||||
|  |   void dump_config() override; | ||||||
|  |   /** Used by ESPHome framework. */ | ||||||
|  |   void update() override; | ||||||
|  |   /** Used by ESPHome framework. */ | ||||||
|  |   float get_setup_priority() const override; | ||||||
|  |  | ||||||
|  |   /** Used by ESPHome framework. */ | ||||||
|  |   void set_internal_temperature_sensor(sensor::Sensor *sensor) { this->internal_temperature_sensor_ = sensor; } | ||||||
|  |   /** Used by ESPHome framework. */ | ||||||
|  |   void set_external_temperature_sensor(sensor::Sensor *sensor) { this->external_temperature_sensor_ = sensor; } | ||||||
|  |   /** Used by ESPHome framework. */ | ||||||
|  |   void set_speed_sensor(sensor::Sensor *sensor) { this->speed_sensor_ = sensor; } | ||||||
|  |   /** Used by ESPHome framework. */ | ||||||
|  |   void set_duty_cycle_sensor(sensor::Sensor *sensor) { this->duty_cycle_sensor_ = sensor; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   Emc2101Component *parent_; | ||||||
|  |   sensor::Sensor *internal_temperature_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *external_temperature_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *speed_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *duty_cycle_sensor_{nullptr}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace emc2101 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -386,10 +386,21 @@ FRAMEWORK_SCHEMA = cv.typed_schema( | |||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | FLASH_SIZES = [ | ||||||
|  |     "4MB", | ||||||
|  |     "8MB", | ||||||
|  |     "16MB", | ||||||
|  |     "32MB", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | CONF_FLASH_SIZE = "flash_size" | ||||||
| CONFIG_SCHEMA = cv.All( | CONFIG_SCHEMA = cv.All( | ||||||
|     cv.Schema( |     cv.Schema( | ||||||
|         { |         { | ||||||
|             cv.Required(CONF_BOARD): cv.string_strict, |             cv.Required(CONF_BOARD): cv.string_strict, | ||||||
|  |             cv.Optional(CONF_FLASH_SIZE, default="4MB"): cv.one_of( | ||||||
|  |                 *FLASH_SIZES, upper=True | ||||||
|  |             ), | ||||||
|             cv.Optional(CONF_VARIANT): cv.one_of(*VARIANTS, upper=True), |             cv.Optional(CONF_VARIANT): cv.one_of(*VARIANTS, upper=True), | ||||||
|             cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA, |             cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA, | ||||||
|         } |         } | ||||||
| @@ -401,6 +412,7 @@ CONFIG_SCHEMA = cv.All( | |||||||
|  |  | ||||||
| async def to_code(config): | async def to_code(config): | ||||||
|     cg.add_platformio_option("board", config[CONF_BOARD]) |     cg.add_platformio_option("board", config[CONF_BOARD]) | ||||||
|  |     cg.add_platformio_option("board_upload.flash_size", config[CONF_FLASH_SIZE]) | ||||||
|     cg.add_build_flag("-DUSE_ESP32") |     cg.add_build_flag("-DUSE_ESP32") | ||||||
|     cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) |     cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) | ||||||
|     cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}") |     cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}") | ||||||
| @@ -505,24 +517,46 @@ async def to_code(config): | |||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |  | ||||||
| ARDUINO_PARTITIONS_CSV = """\ | APP_PARTITION_SIZES = { | ||||||
| nvs,      data, nvs,     0x009000, 0x005000, |     "4MB": 0x1C0000,  # 1792 KB | ||||||
| otadata,  data, ota,     0x00e000, 0x002000, |     "8MB": 0x3C0000,  # 3840 KB | ||||||
| app0,     app,  ota_0,   0x010000, 0x1C0000, |     "16MB": 0x7C0000,  # 7936 KB | ||||||
| app1,     app,  ota_1,   0x1D0000, 0x1C0000, |     "32MB": 0xFC0000,  # 16128 KB | ||||||
| eeprom,   data, 0x99,    0x390000, 0x001000, | } | ||||||
| spiffs,   data, spiffs,  0x391000, 0x00F000 |  | ||||||
|  |  | ||||||
|  | def get_arduino_partition_csv(flash_size): | ||||||
|  |     app_partition_size = APP_PARTITION_SIZES[flash_size] | ||||||
|  |     eeprom_partition_size = 0x1000  # 4 KB | ||||||
|  |     spiffs_partition_size = 0xF000  # 60 KB | ||||||
|  |  | ||||||
|  |     app0_partition_start = 0x010000  # 64 KB | ||||||
|  |     app1_partition_start = app0_partition_start + app_partition_size | ||||||
|  |     eeprom_partition_start = app1_partition_start + app_partition_size | ||||||
|  |     spiffs_partition_start = eeprom_partition_start + eeprom_partition_size | ||||||
|  |  | ||||||
|  |     partition_csv = f"""\ | ||||||
|  | nvs,      data, nvs,     0x9000, 0x5000, | ||||||
|  | otadata,  data, ota,     0xE000, 0x2000, | ||||||
|  | app0,     app,  ota_0,   0x{app0_partition_start:X}, 0x{app_partition_size:X}, | ||||||
|  | app1,     app,  ota_1,   0x{app1_partition_start:X}, 0x{app_partition_size:X}, | ||||||
|  | eeprom,   data, 0x99,    0x{eeprom_partition_start:X}, 0x{eeprom_partition_size:X}, | ||||||
|  | spiffs,   data, spiffs,  0x{spiffs_partition_start:X}, 0x{spiffs_partition_size:X} | ||||||
| """ | """ | ||||||
|  |     return partition_csv | ||||||
|  |  | ||||||
|  |  | ||||||
| IDF_PARTITIONS_CSV = """\ | def get_idf_partition_csv(flash_size): | ||||||
| # Name,   Type, SubType, Offset,   Size, Flags |     app_partition_size = APP_PARTITION_SIZES[flash_size] | ||||||
|  |  | ||||||
|  |     partition_csv = f"""\ | ||||||
| otadata,  data, ota,     ,        0x2000, | otadata,  data, ota,     ,        0x2000, | ||||||
| phy_init, data, phy,     ,        0x1000, | phy_init, data, phy,     ,        0x1000, | ||||||
| app0,     app,  ota_0,   ,      0x1C0000, | app0,     app,  ota_0,   ,        0x{app_partition_size:X}, | ||||||
| app1,     app,  ota_1,   ,      0x1C0000, | app1,     app,  ota_1,   ,        0x{app_partition_size:X}, | ||||||
| nvs,      data, nvs,     ,       0x6d000, | nvs,      data, nvs,     ,        0x6D000, | ||||||
| """ | """ | ||||||
|  |     return partition_csv | ||||||
|  |  | ||||||
|  |  | ||||||
| def _format_sdkconfig_val(value: SdkconfigValueType) -> str: | def _format_sdkconfig_val(value: SdkconfigValueType) -> str: | ||||||
| @@ -565,13 +599,17 @@ def copy_files(): | |||||||
|     if CORE.using_arduino: |     if CORE.using_arduino: | ||||||
|         write_file_if_changed( |         write_file_if_changed( | ||||||
|             CORE.relative_build_path("partitions.csv"), |             CORE.relative_build_path("partitions.csv"), | ||||||
|             ARDUINO_PARTITIONS_CSV, |             get_arduino_partition_csv( | ||||||
|  |                 CORE.platformio_options.get("board_upload.flash_size") | ||||||
|  |             ), | ||||||
|         ) |         ) | ||||||
|     if CORE.using_esp_idf: |     if CORE.using_esp_idf: | ||||||
|         _write_sdkconfig() |         _write_sdkconfig() | ||||||
|         write_file_if_changed( |         write_file_if_changed( | ||||||
|             CORE.relative_build_path("partitions.csv"), |             CORE.relative_build_path("partitions.csv"), | ||||||
|             IDF_PARTITIONS_CSV, |             get_idf_partition_csv( | ||||||
|  |                 CORE.platformio_options.get("board_upload.flash_size") | ||||||
|  |             ), | ||||||
|         ) |         ) | ||||||
|         # IDF build scripts look for version string to put in the build. |         # IDF build scripts look for version string to put in the build. | ||||||
|         # However, if the build path does not have an initialized git repo, |         # However, if the build path does not have an initialized git repo, | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ from esphome.const import ( | |||||||
|     CONF_OUTPUT, |     CONF_OUTPUT, | ||||||
|     CONF_PULLDOWN, |     CONF_PULLDOWN, | ||||||
|     CONF_PULLUP, |     CONF_PULLUP, | ||||||
|  |     CONF_IGNORE_STRAPPING_WARNING, | ||||||
| ) | ) | ||||||
| from esphome import pins | from esphome import pins | ||||||
| from esphome.core import CORE | from esphome.core import CORE | ||||||
| @@ -176,6 +177,7 @@ ESP32_PIN_SCHEMA = cv.All( | |||||||
|             } |             } | ||||||
|         ), |         ), | ||||||
|         cv.Optional(CONF_INVERTED, default=False): cv.boolean, |         cv.Optional(CONF_INVERTED, default=False): cv.boolean, | ||||||
|  |         cv.Optional(CONF_IGNORE_STRAPPING_WARNING, default=False): cv.boolean, | ||||||
|         cv.Optional(CONF_DRIVE_STRENGTH, default="20mA"): cv.All( |         cv.Optional(CONF_DRIVE_STRENGTH, default="20mA"): cv.All( | ||||||
|             cv.float_with_unit("current", "mA", optional_unit=True), |             cv.float_with_unit("current", "mA", optional_unit=True), | ||||||
|             cv.enum(DRIVE_STRENGTHS), |             cv.enum(DRIVE_STRENGTHS), | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ from esphome.const import ( | |||||||
|     CONF_PULLUP, |     CONF_PULLUP, | ||||||
| ) | ) | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
|  | from esphome.pins import check_strapping_pin | ||||||
|  |  | ||||||
|  |  | ||||||
| _ESP_SDIO_PINS = { | _ESP_SDIO_PINS = { | ||||||
| @@ -35,13 +36,6 @@ def esp32_validate_gpio_pin(value): | |||||||
|             "flash interface in QUAD IO flash mode.", |             "flash interface in QUAD IO flash mode.", | ||||||
|             value, |             value, | ||||||
|         ) |         ) | ||||||
|     if value in _ESP32_STRAPPING_PINS: |  | ||||||
|         _LOGGER.warning( |  | ||||||
|             "GPIO%d is a Strapping PIN and should be avoided.\n" |  | ||||||
|             "Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n" |  | ||||||
|             "See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins", |  | ||||||
|             value, |  | ||||||
|         ) |  | ||||||
|     if value in (24, 28, 29, 30, 31): |     if value in (24, 28, 29, 30, 31): | ||||||
|         # These pins are not exposed in GPIO mux (reason unknown) |         # These pins are not exposed in GPIO mux (reason unknown) | ||||||
|         # but they're missing from IO_MUX list in datasheet |         # but they're missing from IO_MUX list in datasheet | ||||||
| @@ -74,4 +68,5 @@ def esp32_validate_supports(value): | |||||||
|             f"GPIO{num} (34-39) does not support pulldowns.", [CONF_MODE, CONF_PULLDOWN] |             f"GPIO{num} (34-39) does not support pulldowns.", [CONF_MODE, CONF_PULLDOWN] | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     check_strapping_pin(value, _ESP32_STRAPPING_PINS, _LOGGER) | ||||||
|     return value |     return value | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import logging | import logging | ||||||
|  |  | ||||||
| from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER | from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER | ||||||
|  | from esphome.pins import check_strapping_pin | ||||||
|  |  | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
|  |  | ||||||
| @@ -12,13 +13,6 @@ _LOGGER = logging.getLogger(__name__) | |||||||
| def esp32_c2_validate_gpio_pin(value): | def esp32_c2_validate_gpio_pin(value): | ||||||
|     if value < 0 or value > 20: |     if value < 0 or value > 20: | ||||||
|         raise cv.Invalid(f"Invalid pin number: {value} (must be 0-20)") |         raise cv.Invalid(f"Invalid pin number: {value} (must be 0-20)") | ||||||
|     if value in _ESP32C2_STRAPPING_PINS: |  | ||||||
|         _LOGGER.warning( |  | ||||||
|             "GPIO%d is a Strapping PIN and should be avoided.\n" |  | ||||||
|             "Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n" |  | ||||||
|             "See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins", |  | ||||||
|             value, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     return value |     return value | ||||||
|  |  | ||||||
| @@ -34,4 +28,6 @@ def esp32_c2_validate_supports(value): | |||||||
|     if is_input: |     if is_input: | ||||||
|         # All ESP32 pins support input mode |         # All ESP32 pins support input mode | ||||||
|         pass |         pass | ||||||
|  |  | ||||||
|  |     check_strapping_pin(value, _ESP32C2_STRAPPING_PINS, _LOGGER) | ||||||
|     return value |     return value | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ from esphome.const import ( | |||||||
|     CONF_NUMBER, |     CONF_NUMBER, | ||||||
| ) | ) | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
|  | from esphome.pins import check_strapping_pin | ||||||
|  |  | ||||||
| _ESP32C3_SPI_PSRAM_PINS = { | _ESP32C3_SPI_PSRAM_PINS = { | ||||||
|     12: "SPIHD", |     12: "SPIHD", | ||||||
| @@ -28,13 +29,6 @@ def esp32_c3_validate_gpio_pin(value): | |||||||
|         raise cv.Invalid( |         raise cv.Invalid( | ||||||
|             f"This pin cannot be used on ESP32-C3s and is already used by the SPI/PSRAM interface (function: {_ESP32C3_SPI_PSRAM_PINS[value]})" |             f"This pin cannot be used on ESP32-C3s and is already used by the SPI/PSRAM interface (function: {_ESP32C3_SPI_PSRAM_PINS[value]})" | ||||||
|         ) |         ) | ||||||
|     if value in _ESP32C3_STRAPPING_PINS: |  | ||||||
|         _LOGGER.warning( |  | ||||||
|             "GPIO%d is a Strapping PIN and should be avoided.\n" |  | ||||||
|             "Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n" |  | ||||||
|             "See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins", |  | ||||||
|             value, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     return value |     return value | ||||||
|  |  | ||||||
| @@ -50,4 +44,6 @@ def esp32_c3_validate_supports(value): | |||||||
|     if is_input: |     if is_input: | ||||||
|         # All ESP32 pins support input mode |         # All ESP32 pins support input mode | ||||||
|         pass |         pass | ||||||
|  |  | ||||||
|  |     check_strapping_pin(value, _ESP32C3_STRAPPING_PINS, _LOGGER) | ||||||
|     return value |     return value | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ import logging | |||||||
| from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER | from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER | ||||||
|  |  | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
|  | from esphome.pins import check_strapping_pin | ||||||
|  |  | ||||||
| _ESP32C6_SPI_PSRAM_PINS = { | _ESP32C6_SPI_PSRAM_PINS = { | ||||||
|     24: "SPICS0", |     24: "SPICS0", | ||||||
| @@ -26,13 +27,6 @@ def esp32_c6_validate_gpio_pin(value): | |||||||
|         raise cv.Invalid( |         raise cv.Invalid( | ||||||
|             f"This pin cannot be used on ESP32-C6s and is already used by the SPI/PSRAM interface (function: {_ESP32C6_SPI_PSRAM_PINS[value]})" |             f"This pin cannot be used on ESP32-C6s and is already used by the SPI/PSRAM interface (function: {_ESP32C6_SPI_PSRAM_PINS[value]})" | ||||||
|         ) |         ) | ||||||
|     if value in _ESP32C6_STRAPPING_PINS: |  | ||||||
|         _LOGGER.warning( |  | ||||||
|             "GPIO%d is a Strapping PIN and should be avoided.\n" |  | ||||||
|             "Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n" |  | ||||||
|             "See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins", |  | ||||||
|             value, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     return value |     return value | ||||||
|  |  | ||||||
| @@ -47,4 +41,6 @@ def esp32_c6_validate_supports(value): | |||||||
|     if is_input: |     if is_input: | ||||||
|         # All ESP32 pins support input mode |         # All ESP32 pins support input mode | ||||||
|         pass |         pass | ||||||
|  |  | ||||||
|  |     check_strapping_pin(value, _ESP32C6_STRAPPING_PINS, _LOGGER) | ||||||
|     return value |     return value | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ from esphome.const import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
|  | from esphome.pins import check_strapping_pin | ||||||
|  |  | ||||||
| _ESP32S2_SPI_PSRAM_PINS = { | _ESP32S2_SPI_PSRAM_PINS = { | ||||||
|     26: "SPICS1", |     26: "SPICS1", | ||||||
| @@ -34,13 +35,6 @@ def esp32_s2_validate_gpio_pin(value): | |||||||
|         raise cv.Invalid( |         raise cv.Invalid( | ||||||
|             f"This pin cannot be used on ESP32-S2s and is already used by the SPI/PSRAM interface (function: {_ESP32S2_SPI_PSRAM_PINS[value]})" |             f"This pin cannot be used on ESP32-S2s and is already used by the SPI/PSRAM interface (function: {_ESP32S2_SPI_PSRAM_PINS[value]})" | ||||||
|         ) |         ) | ||||||
|     if value in _ESP32S2_STRAPPING_PINS: |  | ||||||
|         _LOGGER.warning( |  | ||||||
|             "GPIO%d is a Strapping PIN and should be avoided.\n" |  | ||||||
|             "Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n" |  | ||||||
|             "See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins", |  | ||||||
|             value, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     if value in (22, 23, 24, 25): |     if value in (22, 23, 24, 25): | ||||||
|         # These pins are not exposed in GPIO mux (reason unknown) |         # These pins are not exposed in GPIO mux (reason unknown) | ||||||
| @@ -77,4 +71,5 @@ def esp32_s2_validate_supports(value): | |||||||
|             f"GPIO{num} does not support pulldowns.", [CONF_MODE, CONF_PULLDOWN] |             f"GPIO{num} does not support pulldowns.", [CONF_MODE, CONF_PULLDOWN] | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     check_strapping_pin(value, _ESP32S2_STRAPPING_PINS, _LOGGER) | ||||||
|     return value |     return value | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ from esphome.const import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
|  | from esphome.pins import check_strapping_pin | ||||||
|  |  | ||||||
| _ESP_32S3_SPI_PSRAM_PINS = { | _ESP_32S3_SPI_PSRAM_PINS = { | ||||||
|     26: "SPICS1", |     26: "SPICS1", | ||||||
| @@ -45,14 +46,6 @@ def esp32_s3_validate_gpio_pin(value): | |||||||
|             value, |             value, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     if value in _ESP_32S3_STRAPPING_PINS: |  | ||||||
|         _LOGGER.warning( |  | ||||||
|             "GPIO%d is a Strapping PIN and should be avoided.\n" |  | ||||||
|             "Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n" |  | ||||||
|             "See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins", |  | ||||||
|             value, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     if value in (22, 23, 24, 25): |     if value in (22, 23, 24, 25): | ||||||
|         # These pins are not exposed in GPIO mux (reason unknown) |         # These pins are not exposed in GPIO mux (reason unknown) | ||||||
|         # but they're missing from IO_MUX list in datasheet |         # but they're missing from IO_MUX list in datasheet | ||||||
| @@ -71,4 +64,6 @@ def esp32_s3_validate_supports(value): | |||||||
|     if is_input: |     if is_input: | ||||||
|         # All ESP32 pins support input mode |         # All ESP32 pins support input mode | ||||||
|         pass |         pass | ||||||
|  |  | ||||||
|  |     check_strapping_pin(value, _ESP_32S3_STRAPPING_PINS, _LOGGER) | ||||||
|     return value |     return value | ||||||
|   | |||||||
| @@ -1,15 +1,17 @@ | |||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
|  | from esphome import automation | ||||||
| from esphome.const import CONF_ID | from esphome.const import CONF_ID | ||||||
| from esphome.core import CORE | from esphome.core import CORE | ||||||
| from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const | from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const | ||||||
|  |  | ||||||
| DEPENDENCIES = ["esp32"] | DEPENDENCIES = ["esp32"] | ||||||
| CODEOWNERS = ["@jesserockz"] | CODEOWNERS = ["@jesserockz", "@Rapsssito"] | ||||||
| CONFLICTS_WITH = ["esp32_ble_beacon"] | CONFLICTS_WITH = ["esp32_ble_beacon"] | ||||||
|  |  | ||||||
| CONF_BLE_ID = "ble_id" | CONF_BLE_ID = "ble_id" | ||||||
| CONF_IO_CAPABILITY = "io_capability" | CONF_IO_CAPABILITY = "io_capability" | ||||||
|  | CONF_ENABLE_ON_BOOT = "enable_on_boot" | ||||||
|  |  | ||||||
| NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] | NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] | ||||||
|  |  | ||||||
| @@ -20,6 +22,10 @@ GAPEventHandler = esp32_ble_ns.class_("GAPEventHandler") | |||||||
| GATTcEventHandler = esp32_ble_ns.class_("GATTcEventHandler") | GATTcEventHandler = esp32_ble_ns.class_("GATTcEventHandler") | ||||||
| GATTsEventHandler = esp32_ble_ns.class_("GATTsEventHandler") | GATTsEventHandler = esp32_ble_ns.class_("GATTsEventHandler") | ||||||
|  |  | ||||||
|  | BLEEnabledCondition = esp32_ble_ns.class_("BLEEnabledCondition", automation.Condition) | ||||||
|  | BLEEnableAction = esp32_ble_ns.class_("BLEEnableAction", automation.Action) | ||||||
|  | BLEDisableAction = esp32_ble_ns.class_("BLEDisableAction", automation.Action) | ||||||
|  |  | ||||||
| IoCapability = esp32_ble_ns.enum("IoCapability") | IoCapability = esp32_ble_ns.enum("IoCapability") | ||||||
| IO_CAPABILITY = { | IO_CAPABILITY = { | ||||||
|     "none": IoCapability.IO_CAP_NONE, |     "none": IoCapability.IO_CAP_NONE, | ||||||
| @@ -35,6 +41,7 @@ CONFIG_SCHEMA = cv.Schema( | |||||||
|         cv.Optional(CONF_IO_CAPABILITY, default="none"): cv.enum( |         cv.Optional(CONF_IO_CAPABILITY, default="none"): cv.enum( | ||||||
|             IO_CAPABILITY, lower=True |             IO_CAPABILITY, lower=True | ||||||
|         ), |         ), | ||||||
|  |         cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean, | ||||||
|     } |     } | ||||||
| ).extend(cv.COMPONENT_SCHEMA) | ).extend(cv.COMPONENT_SCHEMA) | ||||||
|  |  | ||||||
| @@ -50,9 +57,25 @@ FINAL_VALIDATE_SCHEMA = validate_variant | |||||||
|  |  | ||||||
| async def to_code(config): | async def to_code(config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     await cg.register_component(var, config) |     cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT])) | ||||||
|     cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY])) |     cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY])) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |  | ||||||
|     if CORE.using_esp_idf: |     if CORE.using_esp_idf: | ||||||
|         add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) |         add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) | ||||||
|         add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True) |         add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_condition("ble.enabled", BLEEnabledCondition, cv.Schema({})) | ||||||
|  | async def ble_enabled_to_code(config, condition_id, template_arg, args): | ||||||
|  |     return cg.new_Pvariable(condition_id, template_arg) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action("ble.enable", BLEEnableAction, cv.Schema({})) | ||||||
|  | async def ble_enable_to_code(config, action_id, template_arg, args): | ||||||
|  |     return cg.new_Pvariable(action_id, template_arg) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action("ble.disable", BLEDisableAction, cv.Schema({})) | ||||||
|  | async def ble_disable_to_code(config, action_id, template_arg, args): | ||||||
|  |     return cg.new_Pvariable(action_id, template_arg) | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user