mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 00:51:49 +00:00 
			
		
		
		
	Compare commits
	
		
			236 Commits
		
	
	
		
			jesserockz
			...
			2023.4.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					19fc1417ae | ||
| 
						 | 
					e2fefa51f5 | ||
| 
						 | 
					f668d5617f | ||
| 
						 | 
					47c4ff15d6 | ||
| 
						 | 
					ccf1bdc0b4 | ||
| 
						 | 
					e993fcf80c | ||
| 
						 | 
					1bdc30a09e | ||
| 
						 | 
					f56e89597f | ||
| 
						 | 
					9460fb28c4 | ||
| 
						 | 
					7207b9734f | ||
| 
						 | 
					3be3267d06 | ||
| 
						 | 
					98db604dba | ||
| 
						 | 
					ebf6f8c6de | ||
| 
						 | 
					2ebacad398 | ||
| 
						 | 
					53c59cf675 | ||
| 
						 | 
					9da261cb39 | ||
| 
						 | 
					9fbbcd6d8a | ||
| 
						 | 
					1a86167a47 | ||
| 
						 | 
					614ed7fd0c | ||
| 
						 | 
					4eb69d6af5 | ||
| 
						 | 
					0547f2a931 | ||
| 
						 | 
					b5fbe0b145 | ||
| 
						 | 
					443c3c2a56 | ||
| 
						 | 
					d75daa9644 | ||
| 
						 | 
					7963abb27a | ||
| 
						 | 
					0b9e8fda34 | ||
| 
						 | 
					a3cacc0c8b | ||
| 
						 | 
					5a4840f641 | ||
| 
						 | 
					3d7d689040 | ||
| 
						 | 
					b60c08dd28 | ||
| 
						 | 
					80bc567c31 | ||
| 
						 | 
					888ac2e180 | ||
| 
						 | 
					421ebcc8b2 | ||
| 
						 | 
					b56fa8c50a | ||
| 
						 | 
					42401775e1 | ||
| 
						 | 
					9c9bc58c16 | ||
| 
						 | 
					fbc129cccc | ||
| 
						 | 
					99638190cb | ||
| 
						 | 
					d78e9e6aa8 | ||
| 
						 | 
					878155a03d | ||
| 
						 | 
					9922eb83e2 | ||
| 
						 | 
					79f861f012 | ||
| 
						 | 
					4faa9d109e | ||
| 
						 | 
					28534ecc61 | ||
| 
						 | 
					616e0a21d8 | ||
| 
						 | 
					a546ffd490 | ||
| 
						 | 
					b5d0aede38 | ||
| 
						 | 
					a014d853a4 | ||
| 
						 | 
					c4ddf7697d | ||
| 
						 | 
					a2931b6774 | ||
| 
						 | 
					3ac7bf3761 | ||
| 
						 | 
					922344811f | ||
| 
						 | 
					cb2fcaa9b1 | ||
| 
						 | 
					1f50bd0649 | ||
| 
						 | 
					e4b2de5c68 | ||
| 
						 | 
					f862b479e7 | ||
| 
						 | 
					358c59bd8d | ||
| 
						 | 
					74fe135c9c | ||
| 
						 | 
					8d3896172d | ||
| 
						 | 
					9d9725144d | ||
| 
						 | 
					06f83bf1c0 | ||
| 
						 | 
					c2756d57d8 | ||
| 
						 | 
					56504692af | ||
| 
						 | 
					e542e75b9e | ||
| 
						 | 
					806e43c34c | ||
| 
						 | 
					29e7d00894 | ||
| 
						 | 
					9ee661c1e4 | ||
| 
						 | 
					36c0e2416d | ||
| 
						 | 
					e4ba3ff1db | ||
| 
						 | 
					be69b49880 | ||
| 
						 | 
					cc317d27f5 | ||
| 
						 | 
					c16709ed95 | ||
| 
						 | 
					e13eaf6706 | ||
| 
						 | 
					a1eb3b8475 | ||
| 
						 | 
					d52e425ba2 | ||
| 
						 | 
					dd8dc1ef1d | ||
| 
						 | 
					bc427de16a | ||
| 
						 | 
					db5988bbe1 | ||
| 
						 | 
					a3875af4b4 | ||
| 
						 | 
					d70e7da0ef | ||
| 
						 | 
					d42f35de5d | ||
| 
						 | 
					e6737479f7 | ||
| 
						 | 
					7f75832bf1 | ||
| 
						 | 
					33339e3bd8 | ||
| 
						 | 
					c298c1166f | ||
| 
						 | 
					c3d9eef01f | ||
| 
						 | 
					5ffdc66864 | ||
| 
						 | 
					9922c1503a | ||
| 
						 | 
					fce99d4b17 | ||
| 
						 | 
					11567085d8 | ||
| 
						 | 
					83f8e84247 | ||
| 
						 | 
					11eb5cb0fa | ||
| 
						 | 
					9a7af97b2d | ||
| 
						 | 
					5e11469f50 | ||
| 
						 | 
					0c7a3d1fff | ||
| 
						 | 
					8a705bf4b0 | ||
| 
						 | 
					6a89180deb | ||
| 
						 | 
					65d2b806cc | ||
| 
						 | 
					c149a3033c | ||
| 
						 | 
					4b7c233f1a | ||
| 
						 | 
					6e8e9c2aa9 | ||
| 
						 | 
					b6f628ee40 | ||
| 
						 | 
					bf79a700b7 | ||
| 
						 | 
					cdeb6e750f | ||
| 
						 | 
					dd226360bb | ||
| 
						 | 
					1a9aedf152 | ||
| 
						 | 
					d82c6df57e | ||
| 
						 | 
					7c91b4474a | ||
| 
						 | 
					a4f21db272 | ||
| 
						 | 
					58a8e1859e | ||
| 
						 | 
					2bed5b18c1 | ||
| 
						 | 
					fb5eb57345 | ||
| 
						 | 
					63db42a1d4 | ||
| 
						 | 
					f58ffe41f8 | ||
| 
						 | 
					445d2e372c | ||
| 
						 | 
					c037e95861 | ||
| 
						 | 
					2e1b35959f | ||
| 
						 | 
					7f46d9e0f9 | ||
| 
						 | 
					069b5f81a0 | ||
| 
						 | 
					3a36d0b13f | ||
| 
						 | 
					f0760e99b7 | ||
| 
						 | 
					18fecf8c09 | ||
| 
						 | 
					414cf1b333 | ||
| 
						 | 
					d10f891f51 | ||
| 
						 | 
					b5927322e6 | ||
| 
						 | 
					1cf4107e1c | ||
| 
						 | 
					c12408326c | ||
| 
						 | 
					4434e59e5a | ||
| 
						 | 
					45180d98f6 | ||
| 
						 | 
					44494ad18e | ||
| 
						 | 
					1447536906 | ||
| 
						 | 
					27ec517084 | ||
| 
						 | 
					ce1f034bac | ||
| 
						 | 
					f1f96f16e9 | ||
| 
						 | 
					7665e9b076 | ||
| 
						 | 
					227d94f38d | ||
| 
						 | 
					b724ae9e0e | ||
| 
						 | 
					df6cc14201 | ||
| 
						 | 
					d981d7859d | ||
| 
						 | 
					0f1ec515c1 | ||
| 
						 | 
					78e18256f7 | ||
| 
						 | 
					4899dfe642 | ||
| 
						 | 
					d6b6e94059 | ||
| 
						 | 
					310355a00b | ||
| 
						 | 
					8cf26d6f3c | ||
| 
						 | 
					b15a10f905 | ||
| 
						 | 
					5dcf1debd7 | ||
| 
						 | 
					9b57e1ac1d | ||
| 
						 | 
					68683e3a50 | ||
| 
						 | 
					d83324c4dc | ||
| 
						 | 
					ecde4c1d2d | ||
| 
						 | 
					bd8e470726 | ||
| 
						 | 
					d2913fe627 | ||
| 
						 | 
					43acc7dc2c | ||
| 
						 | 
					e2a16d758b | ||
| 
						 | 
					4af4649e23 | ||
| 
						 | 
					8bcddef39d | ||
| 
						 | 
					4ac96ccea2 | ||
| 
						 | 
					3c5de77ae9 | ||
| 
						 | 
					a2925b1d37 | ||
| 
						 | 
					73748e9e20 | ||
| 
						 | 
					75c9823899 | ||
| 
						 | 
					c8c0bd3351 | ||
| 
						 | 
					e1cdeb7c8f | ||
| 
						 | 
					7f97f42552 | ||
| 
						 | 
					aa7f3569ec | ||
| 
						 | 
					2d0a08442e | ||
| 
						 | 
					d2380756b2 | ||
| 
						 | 
					925e3cb6c9 | ||
| 
						 | 
					6757acba56 | ||
| 
						 | 
					5cc91cdd95 | ||
| 
						 | 
					2b41886819 | ||
| 
						 | 
					72c6efd6a0 | ||
| 
						 | 
					a1f1804112 | ||
| 
						 | 
					a8b1ceb4e9 | ||
| 
						 | 
					4fb0f7f8c6 | ||
| 
						 | 
					958cadeca8 | ||
| 
						 | 
					00f2655f1a | ||
| 
						 | 
					074f5029eb | ||
| 
						 | 
					1691976587 | ||
| 
						 | 
					60e6b4d21e | ||
| 
						 | 
					5750591df2 | ||
| 
						 | 
					a75da54455 | ||
| 
						 | 
					de7f6c3f5f | ||
| 
						 | 
					4245480656 | ||
| 
						 | 
					1824c8131e | ||
| 
						 | 
					4e9606d2e0 | ||
| 
						 | 
					78500fa933 | ||
| 
						 | 
					9c69b98a49 | ||
| 
						 | 
					e6d8ef98d3 | ||
| 
						 | 
					3f1af1690b | ||
| 
						 | 
					84374b6b1e | ||
| 
						 | 
					391316c9b5 | ||
| 
						 | 
					705c62ebd7 | ||
| 
						 | 
					cb520c00a5 | ||
| 
						 | 
					2f24138345 | ||
| 
						 | 
					96512b80cc | ||
| 
						 | 
					fcb9b51978 | ||
| 
						 | 
					f408f1a368 | ||
| 
						 | 
					7d8d563c62 | ||
| 
						 | 
					0a1f705fda | ||
| 
						 | 
					1952c1880b | ||
| 
						 | 
					b03967dac1 | ||
| 
						 | 
					bcae2596a6 | ||
| 
						 | 
					fc0347c86c | ||
| 
						 | 
					d9563d4de1 | ||
| 
						 | 
					cc7e2bf8db | ||
| 
						 | 
					5d98e2923b | ||
| 
						 | 
					07197d12f6 | ||
| 
						 | 
					7b0a298497 | ||
| 
						 | 
					21679cf2ba | ||
| 
						 | 
					4be7cd12a1 | ||
| 
						 | 
					dee4d0ccb7 | ||
| 
						 | 
					7209dd8bae | ||
| 
						 | 
					ab736c89bb | ||
| 
						 | 
					6911639617 | ||
| 
						 | 
					b9720d0715 | ||
| 
						 | 
					47b3267ed4 | ||
| 
						 | 
					e16ba2adb5 | ||
| 
						 | 
					0a19b1e32c | ||
| 
						 | 
					bae9a950c0 | ||
| 
						 | 
					72b2943332 | ||
| 
						 | 
					4ec0ef7548 | ||
| 
						 | 
					25bc6761f6 | ||
| 
						 | 
					81b6562c25 | ||
| 
						 | 
					ae74189fc2 | ||
| 
						 | 
					9e516efe10 | ||
| 
						 | 
					366e29439e | ||
| 
						 | 
					1c9c700d7f | ||
| 
						 | 
					b2e6b9d31f | ||
| 
						 | 
					7623f63846 | ||
| 
						 | 
					2bfaf9dce3 | ||
| 
						 | 
					5c2c1560bb | ||
| 
						 | 
					f7096ab78e | ||
| 
						 | 
					98f8feb625 | ||
| 
						 | 
					9944ca414e | 
@@ -4,53 +4,60 @@
 | 
			
		||||
  "postCreateCommand": [
 | 
			
		||||
    "script/devcontainer-post-create"
 | 
			
		||||
  ],
 | 
			
		||||
  "containerEnv": {
 | 
			
		||||
    "DEVCONTAINER": "1"
 | 
			
		||||
  },
 | 
			
		||||
  "runArgs": [
 | 
			
		||||
    "--privileged",
 | 
			
		||||
    "-e",
 | 
			
		||||
    "ESPHOME_DASHBOARD_USE_PING=1"
 | 
			
		||||
  ],
 | 
			
		||||
  "appPort": 6052,
 | 
			
		||||
  "extensions": [
 | 
			
		||||
    // python
 | 
			
		||||
    "ms-python.python",
 | 
			
		||||
    "visualstudioexptteam.vscodeintellicode",
 | 
			
		||||
    // yaml
 | 
			
		||||
    "redhat.vscode-yaml",
 | 
			
		||||
    // cpp
 | 
			
		||||
    "ms-vscode.cpptools",
 | 
			
		||||
    // editorconfig
 | 
			
		||||
    "editorconfig.editorconfig",
 | 
			
		||||
  ],
 | 
			
		||||
  "settings": {
 | 
			
		||||
    "python.languageServer": "Pylance",
 | 
			
		||||
    "python.pythonPath": "/usr/bin/python3",
 | 
			
		||||
    "python.linting.pylintEnabled": true,
 | 
			
		||||
    "python.linting.enabled": true,
 | 
			
		||||
    "python.formatting.provider": "black",
 | 
			
		||||
    "editor.formatOnPaste": false,
 | 
			
		||||
    "editor.formatOnSave": true,
 | 
			
		||||
    "editor.formatOnType": true,
 | 
			
		||||
    "files.trimTrailingWhitespace": true,
 | 
			
		||||
    "terminal.integrated.defaultProfile.linux": "bash",
 | 
			
		||||
    "yaml.customTags": [
 | 
			
		||||
      "!secret scalar",
 | 
			
		||||
      "!lambda scalar",
 | 
			
		||||
      "!include_dir_named scalar",
 | 
			
		||||
      "!include_dir_list scalar",
 | 
			
		||||
      "!include_dir_merge_list scalar",
 | 
			
		||||
      "!include_dir_merge_named scalar"
 | 
			
		||||
    ],
 | 
			
		||||
    "files.exclude": {
 | 
			
		||||
      "**/.git": true,
 | 
			
		||||
      "**/.DS_Store": true,
 | 
			
		||||
      "**/*.pyc": {
 | 
			
		||||
        "when": "$(basename).py"
 | 
			
		||||
      },
 | 
			
		||||
      "**/__pycache__": true
 | 
			
		||||
    },
 | 
			
		||||
    "files.associations": {
 | 
			
		||||
      "**/.vscode/*.json": "jsonc"
 | 
			
		||||
    },
 | 
			
		||||
    "C_Cpp.clang_format_path": "/usr/bin/clang-format-13",
 | 
			
		||||
  "customizations": {
 | 
			
		||||
    "vscode": {
 | 
			
		||||
      "extensions": [
 | 
			
		||||
        // python
 | 
			
		||||
        "ms-python.python",
 | 
			
		||||
        "visualstudioexptteam.vscodeintellicode",
 | 
			
		||||
        // yaml
 | 
			
		||||
        "redhat.vscode-yaml",
 | 
			
		||||
        // cpp
 | 
			
		||||
        "ms-vscode.cpptools",
 | 
			
		||||
        // editorconfig
 | 
			
		||||
        "editorconfig.editorconfig",
 | 
			
		||||
      ],
 | 
			
		||||
      "settings": {
 | 
			
		||||
        "python.languageServer": "Pylance",
 | 
			
		||||
        "python.pythonPath": "/usr/bin/python3",
 | 
			
		||||
        "python.linting.pylintEnabled": true,
 | 
			
		||||
        "python.linting.enabled": true,
 | 
			
		||||
        "python.formatting.provider": "black",
 | 
			
		||||
        "editor.formatOnPaste": false,
 | 
			
		||||
        "editor.formatOnSave": true,
 | 
			
		||||
        "editor.formatOnType": true,
 | 
			
		||||
        "files.trimTrailingWhitespace": true,
 | 
			
		||||
        "terminal.integrated.defaultProfile.linux": "bash",
 | 
			
		||||
        "yaml.customTags": [
 | 
			
		||||
          "!secret scalar",
 | 
			
		||||
          "!lambda scalar",
 | 
			
		||||
          "!include_dir_named scalar",
 | 
			
		||||
          "!include_dir_list scalar",
 | 
			
		||||
          "!include_dir_merge_list scalar",
 | 
			
		||||
          "!include_dir_merge_named scalar"
 | 
			
		||||
        ],
 | 
			
		||||
        "files.exclude": {
 | 
			
		||||
          "**/.git": true,
 | 
			
		||||
          "**/.DS_Store": true,
 | 
			
		||||
          "**/*.pyc": {
 | 
			
		||||
            "when": "$(basename).py"
 | 
			
		||||
          },
 | 
			
		||||
          "**/__pycache__": true
 | 
			
		||||
        },
 | 
			
		||||
        "files.associations": {
 | 
			
		||||
          "**/.vscode/*.json": "jsonc"
 | 
			
		||||
        },
 | 
			
		||||
        "C_Cpp.clang_format_path": "/usr/bin/clang-format-13"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,7 +18,7 @@ jobs:
 | 
			
		||||
  stale:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/stale@v7
 | 
			
		||||
      - uses: actions/stale@v8
 | 
			
		||||
        with:
 | 
			
		||||
          days-before-pr-stale: 90
 | 
			
		||||
          days-before-pr-close: 7
 | 
			
		||||
@@ -38,7 +38,7 @@ jobs:
 | 
			
		||||
  close-issues:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/stale@v7
 | 
			
		||||
      - uses: actions/stale@v8
 | 
			
		||||
        with:
 | 
			
		||||
          days-before-pr-stale: -1
 | 
			
		||||
          days-before-pr-close: -1
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
---
 | 
			
		||||
name: Synchronise Device Classes from Home Assistant
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: '45 6 * * *'
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: write
 | 
			
		||||
  pull-requests: write
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  sync:
 | 
			
		||||
    name: Sync Device Classes
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
 | 
			
		||||
      - name: Checkout Home Assistant
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
        with:
 | 
			
		||||
          repository: home-assistant/core
 | 
			
		||||
          path: lib/home-assistant
 | 
			
		||||
 | 
			
		||||
      - name: Setup Python
 | 
			
		||||
        uses: actions/setup-python@v4
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: 3.11
 | 
			
		||||
 | 
			
		||||
      - name: Install Home Assistant
 | 
			
		||||
        run: |
 | 
			
		||||
          python -m pip install --upgrade pip
 | 
			
		||||
          pip install -e lib/home-assistant
 | 
			
		||||
 | 
			
		||||
      - name: Sync
 | 
			
		||||
        run: |
 | 
			
		||||
          python ./script/sync-device_class.py
 | 
			
		||||
 | 
			
		||||
      - name: Get PR template
 | 
			
		||||
        id: pr-template-body
 | 
			
		||||
        run: |
 | 
			
		||||
          body=$(cat .github/PULL_REQUEST_TEMPLATE.md)
 | 
			
		||||
          delimiter="$(openssl rand -hex 8)"
 | 
			
		||||
          echo "body<<$delimiter" >> $GITHUB_OUTPUT
 | 
			
		||||
          echo "$body" >> $GITHUB_OUTPUT
 | 
			
		||||
          echo "$delimiter" >> $GITHUB_OUTPUT
 | 
			
		||||
 | 
			
		||||
      - name: Commit changes
 | 
			
		||||
        uses: peter-evans/create-pull-request@v4
 | 
			
		||||
        with:
 | 
			
		||||
          commit-message: "Synchronise Device Classes from Home Assistant"
 | 
			
		||||
          committer: esphomebot <esphome@nabucasa.com>
 | 
			
		||||
          author: esphomebot <esphome@nabucasa.com>
 | 
			
		||||
          branch: sync/device-classes/
 | 
			
		||||
          branch-suffix: timestamp
 | 
			
		||||
          delete-branch: true
 | 
			
		||||
          title: "Synchronise Device Classes from Home Assistant"
 | 
			
		||||
          body: ${{ steps.pr-template-body.outputs.body }}
 | 
			
		||||
@@ -2,8 +2,8 @@
 | 
			
		||||
# See https://pre-commit.com for more information
 | 
			
		||||
# See https://pre-commit.com/hooks.html for more hooks
 | 
			
		||||
repos:
 | 
			
		||||
  - repo: https://github.com/ambv/black
 | 
			
		||||
    rev: 23.1.0
 | 
			
		||||
  - repo: https://github.com/psf/black
 | 
			
		||||
    rev: 23.3.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: black
 | 
			
		||||
        args:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							@@ -2,15 +2,24 @@
 | 
			
		||||
  "version": "2.0.0",
 | 
			
		||||
  "tasks": [
 | 
			
		||||
    {
 | 
			
		||||
      "label": "run",
 | 
			
		||||
      "label": "Run Dashboard",
 | 
			
		||||
      "type": "shell",
 | 
			
		||||
      "command": "python3 -m esphome dashboard config/",
 | 
			
		||||
      "command": "${command:python.interpreterPath}",
 | 
			
		||||
      "args": [
 | 
			
		||||
        "-m",
 | 
			
		||||
        "esphome",
 | 
			
		||||
        "dashboard",
 | 
			
		||||
        "config/"
 | 
			
		||||
      ],
 | 
			
		||||
      "problemMatcher": []
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "label": "clang-tidy",
 | 
			
		||||
      "type": "shell",
 | 
			
		||||
      "command": "./script/clang-tidy",
 | 
			
		||||
      "command": "${command:python.interpreterPath}",
 | 
			
		||||
      "args": [
 | 
			
		||||
        "./script/clang-tidy"
 | 
			
		||||
      ],
 | 
			
		||||
      "problemMatcher": [
 | 
			
		||||
        {
 | 
			
		||||
          "owner": "clang-tidy",
 | 
			
		||||
 
 | 
			
		||||
@@ -111,6 +111,8 @@ esphome/components/hte501/* @Stock-M
 | 
			
		||||
esphome/components/hydreon_rgxx/* @functionpointer
 | 
			
		||||
esphome/components/i2c/* @esphome/core
 | 
			
		||||
esphome/components/i2s_audio/* @jesserockz
 | 
			
		||||
esphome/components/i2s_audio/media_player/* @jesserockz
 | 
			
		||||
esphome/components/i2s_audio/microphone/* @jesserockz
 | 
			
		||||
esphome/components/ili9xxx/* @nielsnl68
 | 
			
		||||
esphome/components/improv_base/* @esphome/core
 | 
			
		||||
esphome/components/improv_serial/* @esphome/core
 | 
			
		||||
@@ -154,11 +156,13 @@ esphome/components/mcp9808/* @k7hpn
 | 
			
		||||
esphome/components/md5/* @esphome/core
 | 
			
		||||
esphome/components/mdns/* @esphome/core
 | 
			
		||||
esphome/components/media_player/* @jesserockz
 | 
			
		||||
esphome/components/microphone/* @jesserockz
 | 
			
		||||
esphome/components/mics_4514/* @jesserockz
 | 
			
		||||
esphome/components/midea/* @dudanov
 | 
			
		||||
esphome/components/midea_ir/* @dudanov
 | 
			
		||||
esphome/components/mitsubishi/* @RubyBailey
 | 
			
		||||
esphome/components/mlx90393/* @functionpointer
 | 
			
		||||
esphome/components/mmc5603/* @benhoff
 | 
			
		||||
esphome/components/modbus_controller/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/binary_sensor/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/number/* @martgras
 | 
			
		||||
@@ -286,6 +290,7 @@ esphome/components/ufire_ise/* @pvizeli
 | 
			
		||||
esphome/components/ultrasonic/* @OttoWinter
 | 
			
		||||
esphome/components/vbus/* @ssieb
 | 
			
		||||
esphome/components/version/* @esphome/core
 | 
			
		||||
esphome/components/voice_assistant/* @jesserockz
 | 
			
		||||
esphome/components/wake_on_lan/* @willwill2will54
 | 
			
		||||
esphome/components/web_server_base/* @OttoWinter
 | 
			
		||||
esphome/components/whirlpool/* @glmnet
 | 
			
		||||
 
 | 
			
		||||
@@ -152,6 +152,8 @@ def run_miniterm(config, port):
 | 
			
		||||
        _LOGGER.error("Could not connect to serial port %s", port)
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def wrap_to_code(name, comp):
 | 
			
		||||
    coro = coroutine(comp.to_code)
 | 
			
		||||
 
 | 
			
		||||
@@ -76,8 +76,6 @@ async def to_code(config):
 | 
			
		||||
        pos = 0
 | 
			
		||||
        for frameIndex in range(frames):
 | 
			
		||||
            image.seek(frameIndex)
 | 
			
		||||
            if CONF_RESIZE in config:
 | 
			
		||||
                image.thumbnail(config[CONF_RESIZE])
 | 
			
		||||
            frame = image.convert("RGB")
 | 
			
		||||
            if CONF_RESIZE in config:
 | 
			
		||||
                frame = frame.resize([width, height])
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,9 @@ service APIConnection {
 | 
			
		||||
  rpc bluetooth_gatt_write_descriptor(BluetoothGATTWriteDescriptorRequest) returns (void) {}
 | 
			
		||||
  rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {}
 | 
			
		||||
  rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {}
 | 
			
		||||
  rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
 | 
			
		||||
 | 
			
		||||
  rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -208,6 +211,8 @@ message DeviceInfoResponse {
 | 
			
		||||
  string manufacturer = 12;
 | 
			
		||||
 | 
			
		||||
  string friendly_name = 13;
 | 
			
		||||
 | 
			
		||||
  uint32 voice_assistant_version = 14;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ListEntitiesRequest {
 | 
			
		||||
@@ -1125,6 +1130,7 @@ message MediaPlayerCommandRequest {
 | 
			
		||||
message SubscribeBluetoothLEAdvertisementsRequest {
 | 
			
		||||
  option (id) = 66;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothServiceData {
 | 
			
		||||
@@ -1156,6 +1162,7 @@ enum BluetoothDeviceRequestType {
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3;
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4;
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5;
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothDeviceRequest {
 | 
			
		||||
@@ -1359,3 +1366,71 @@ message BluetoothDeviceUnpairingResponse {
 | 
			
		||||
  bool success = 2;
 | 
			
		||||
  int32 error = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message UnsubscribeBluetoothLEAdvertisementsRequest {
 | 
			
		||||
  option (id) = 87;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message BluetoothDeviceClearCacheResponse {
 | 
			
		||||
  option (id) = 88;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BLUETOOTH_PROXY";
 | 
			
		||||
 | 
			
		||||
  uint64 address = 1;
 | 
			
		||||
  bool success = 2;
 | 
			
		||||
  int32 error = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== PUSH TO TALK ====================
 | 
			
		||||
message SubscribeVoiceAssistantRequest {
 | 
			
		||||
  option (id) = 89;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
 | 
			
		||||
  bool subscribe = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantRequest {
 | 
			
		||||
  option (id) = 90;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
 | 
			
		||||
  bool start = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantResponse {
 | 
			
		||||
  option (id) = 91;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
 | 
			
		||||
  uint32 port = 1;
 | 
			
		||||
  bool error = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum VoiceAssistantEvent {
 | 
			
		||||
  VOICE_ASSISTANT_ERROR = 0;
 | 
			
		||||
  VOICE_ASSISTANT_RUN_START = 1;
 | 
			
		||||
  VOICE_ASSISTANT_RUN_END = 2;
 | 
			
		||||
  VOICE_ASSISTANT_STT_START = 3;
 | 
			
		||||
  VOICE_ASSISTANT_STT_END = 4;
 | 
			
		||||
  VOICE_ASSISTANT_INTENT_START = 5;
 | 
			
		||||
  VOICE_ASSISTANT_INTENT_END = 6;
 | 
			
		||||
  VOICE_ASSISTANT_TTS_START = 7;
 | 
			
		||||
  VOICE_ASSISTANT_TTS_END = 8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantEventData {
 | 
			
		||||
  string name = 1;
 | 
			
		||||
  string value = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantEventResponse {
 | 
			
		||||
  option (id) = 92;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
 | 
			
		||||
  VoiceAssistantEvent event_type = 1;
 | 
			
		||||
  repeated VoiceAssistantEventData data = 2;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
#include "esphome/core/entity_base.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
@@ -15,6 +16,9 @@
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#include "esphome/components/voice_assistant/voice_assistant.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
@@ -180,7 +184,8 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
 | 
			
		||||
  ListEntitiesBinarySensorResponse msg;
 | 
			
		||||
  msg.object_id = binary_sensor->get_object_id();
 | 
			
		||||
  msg.key = binary_sensor->get_object_id_hash();
 | 
			
		||||
  msg.name = binary_sensor->get_name();
 | 
			
		||||
  if (binary_sensor->has_own_name())
 | 
			
		||||
    msg.name = binary_sensor->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
 | 
			
		||||
  msg.device_class = binary_sensor->get_device_class();
 | 
			
		||||
  msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
 | 
			
		||||
@@ -212,7 +217,8 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
 | 
			
		||||
  ListEntitiesCoverResponse msg;
 | 
			
		||||
  msg.key = cover->get_object_id_hash();
 | 
			
		||||
  msg.object_id = cover->get_object_id();
 | 
			
		||||
  msg.name = cover->get_name();
 | 
			
		||||
  if (cover->has_own_name())
 | 
			
		||||
    msg.name = cover->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("cover", cover);
 | 
			
		||||
  msg.assumed_state = traits.get_is_assumed_state();
 | 
			
		||||
  msg.supports_position = traits.get_supports_position();
 | 
			
		||||
@@ -275,7 +281,8 @@ bool APIConnection::send_fan_info(fan::Fan *fan) {
 | 
			
		||||
  ListEntitiesFanResponse msg;
 | 
			
		||||
  msg.key = fan->get_object_id_hash();
 | 
			
		||||
  msg.object_id = fan->get_object_id();
 | 
			
		||||
  msg.name = fan->get_name();
 | 
			
		||||
  if (fan->has_own_name())
 | 
			
		||||
    msg.name = fan->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("fan", fan);
 | 
			
		||||
  msg.supports_oscillation = traits.supports_oscillation();
 | 
			
		||||
  msg.supports_speed = traits.supports_speed();
 | 
			
		||||
@@ -337,7 +344,8 @@ bool APIConnection::send_light_info(light::LightState *light) {
 | 
			
		||||
  ListEntitiesLightResponse msg;
 | 
			
		||||
  msg.key = light->get_object_id_hash();
 | 
			
		||||
  msg.object_id = light->get_object_id();
 | 
			
		||||
  msg.name = light->get_name();
 | 
			
		||||
  if (light->has_own_name())
 | 
			
		||||
    msg.name = light->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("light", light);
 | 
			
		||||
 | 
			
		||||
  msg.disabled_by_default = light->is_disabled_by_default();
 | 
			
		||||
@@ -418,7 +426,8 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
 | 
			
		||||
  ListEntitiesSensorResponse msg;
 | 
			
		||||
  msg.key = sensor->get_object_id_hash();
 | 
			
		||||
  msg.object_id = sensor->get_object_id();
 | 
			
		||||
  msg.name = sensor->get_name();
 | 
			
		||||
  if (sensor->has_own_name())
 | 
			
		||||
    msg.name = sensor->get_name();
 | 
			
		||||
  msg.unique_id = sensor->unique_id();
 | 
			
		||||
  if (msg.unique_id.empty())
 | 
			
		||||
    msg.unique_id = get_default_unique_id("sensor", sensor);
 | 
			
		||||
@@ -448,7 +457,8 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
 | 
			
		||||
  ListEntitiesSwitchResponse msg;
 | 
			
		||||
  msg.key = a_switch->get_object_id_hash();
 | 
			
		||||
  msg.object_id = a_switch->get_object_id();
 | 
			
		||||
  msg.name = a_switch->get_name();
 | 
			
		||||
  if (a_switch->has_own_name())
 | 
			
		||||
    msg.name = a_switch->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("switch", a_switch);
 | 
			
		||||
  msg.icon = a_switch->get_icon();
 | 
			
		||||
  msg.assumed_state = a_switch->assumed_state();
 | 
			
		||||
@@ -533,7 +543,8 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
 | 
			
		||||
  ListEntitiesClimateResponse msg;
 | 
			
		||||
  msg.key = climate->get_object_id_hash();
 | 
			
		||||
  msg.object_id = climate->get_object_id();
 | 
			
		||||
  msg.name = climate->get_name();
 | 
			
		||||
  if (climate->has_own_name())
 | 
			
		||||
    msg.name = climate->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("climate", climate);
 | 
			
		||||
 | 
			
		||||
  msg.disabled_by_default = climate->is_disabled_by_default();
 | 
			
		||||
@@ -611,7 +622,8 @@ bool APIConnection::send_number_info(number::Number *number) {
 | 
			
		||||
  ListEntitiesNumberResponse msg;
 | 
			
		||||
  msg.key = number->get_object_id_hash();
 | 
			
		||||
  msg.object_id = number->get_object_id();
 | 
			
		||||
  msg.name = number->get_name();
 | 
			
		||||
  if (number->has_own_name())
 | 
			
		||||
    msg.name = number->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("number", number);
 | 
			
		||||
  msg.icon = number->get_icon();
 | 
			
		||||
  msg.disabled_by_default = number->is_disabled_by_default();
 | 
			
		||||
@@ -652,7 +664,8 @@ bool APIConnection::send_select_info(select::Select *select) {
 | 
			
		||||
  ListEntitiesSelectResponse msg;
 | 
			
		||||
  msg.key = select->get_object_id_hash();
 | 
			
		||||
  msg.object_id = select->get_object_id();
 | 
			
		||||
  msg.name = select->get_name();
 | 
			
		||||
  if (select->has_own_name())
 | 
			
		||||
    msg.name = select->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("select", select);
 | 
			
		||||
  msg.icon = select->get_icon();
 | 
			
		||||
  msg.disabled_by_default = select->is_disabled_by_default();
 | 
			
		||||
@@ -679,7 +692,8 @@ bool APIConnection::send_button_info(button::Button *button) {
 | 
			
		||||
  ListEntitiesButtonResponse msg;
 | 
			
		||||
  msg.key = button->get_object_id_hash();
 | 
			
		||||
  msg.object_id = button->get_object_id();
 | 
			
		||||
  msg.name = button->get_name();
 | 
			
		||||
  if (button->has_own_name())
 | 
			
		||||
    msg.name = button->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("button", button);
 | 
			
		||||
  msg.icon = button->get_icon();
 | 
			
		||||
  msg.disabled_by_default = button->is_disabled_by_default();
 | 
			
		||||
@@ -710,7 +724,8 @@ bool APIConnection::send_lock_info(lock::Lock *a_lock) {
 | 
			
		||||
  ListEntitiesLockResponse msg;
 | 
			
		||||
  msg.key = a_lock->get_object_id_hash();
 | 
			
		||||
  msg.object_id = a_lock->get_object_id();
 | 
			
		||||
  msg.name = a_lock->get_name();
 | 
			
		||||
  if (a_lock->has_own_name())
 | 
			
		||||
    msg.name = a_lock->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("lock", a_lock);
 | 
			
		||||
  msg.icon = a_lock->get_icon();
 | 
			
		||||
  msg.assumed_state = a_lock->traits.get_assumed_state();
 | 
			
		||||
@@ -755,7 +770,8 @@ bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_play
 | 
			
		||||
  ListEntitiesMediaPlayerResponse msg;
 | 
			
		||||
  msg.key = media_player->get_object_id_hash();
 | 
			
		||||
  msg.object_id = media_player->get_object_id();
 | 
			
		||||
  msg.name = media_player->get_name();
 | 
			
		||||
  if (media_player->has_own_name())
 | 
			
		||||
    msg.name = media_player->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("media_player", media_player);
 | 
			
		||||
  msg.icon = media_player->get_icon();
 | 
			
		||||
  msg.disabled_by_default = media_player->is_disabled_by_default();
 | 
			
		||||
@@ -799,7 +815,8 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
 | 
			
		||||
  ListEntitiesCameraResponse msg;
 | 
			
		||||
  msg.key = camera->get_object_id_hash();
 | 
			
		||||
  msg.object_id = camera->get_object_id();
 | 
			
		||||
  msg.name = camera->get_name();
 | 
			
		||||
  if (camera->has_own_name())
 | 
			
		||||
    msg.name = camera->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("camera", camera);
 | 
			
		||||
  msg.disabled_by_default = camera->is_disabled_by_default();
 | 
			
		||||
  msg.icon = camera->get_icon();
 | 
			
		||||
@@ -879,6 +896,30 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIConnection::request_voice_assistant(bool start) {
 | 
			
		||||
  if (!this->voice_assistant_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
  VoiceAssistantRequest msg;
 | 
			
		||||
  msg.start = start;
 | 
			
		||||
  return this->send_voice_assistant_request(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {
 | 
			
		||||
  if (voice_assistant::global_voice_assistant != nullptr) {
 | 
			
		||||
    struct sockaddr_storage storage;
 | 
			
		||||
    socklen_t len = sizeof(storage);
 | 
			
		||||
    this->helper_->getpeername((struct sockaddr *) &storage, &len);
 | 
			
		||||
    voice_assistant::global_voice_assistant->start(&storage, msg.port);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
 | 
			
		||||
  if (voice_assistant::global_voice_assistant != nullptr) {
 | 
			
		||||
    voice_assistant::global_voice_assistant->on_event(msg);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
 | 
			
		||||
  if (this->log_subscription_ < level)
 | 
			
		||||
    return false;
 | 
			
		||||
@@ -898,7 +939,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
 | 
			
		||||
  this->helper_->set_log_info(client_info_);
 | 
			
		||||
  this->client_api_version_major_ = msg.api_version_major;
 | 
			
		||||
  this->client_api_version_minor_ = msg.api_version_minor;
 | 
			
		||||
  ESP_LOGV(TAG, "Hello from client: '%s' | API Version %d.%d", this->client_info_.c_str(),
 | 
			
		||||
  ESP_LOGV(TAG, "Hello from client: '%s' | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
 | 
			
		||||
           this->client_api_version_major_, this->client_api_version_minor_);
 | 
			
		||||
 | 
			
		||||
  HelloResponse resp;
 | 
			
		||||
@@ -953,7 +994,12 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
 | 
			
		||||
  resp.webserver_port = USE_WEBSERVER_PORT;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 4 : 1;
 | 
			
		||||
  resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active()
 | 
			
		||||
                                     ? bluetooth_proxy::ACTIVE_CONNECTIONS_VERSION
 | 
			
		||||
                                     : bluetooth_proxy::PASSIVE_ONLY_VERSION;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  resp.voice_assistant_version = 1;
 | 
			
		||||
#endif
 | 
			
		||||
  return resp;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -97,6 +98,12 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
    this->send_homeassistant_service_response(call);
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override {
 | 
			
		||||
    this->bluetooth_le_advertisement_subscription_ = true;
 | 
			
		||||
  }
 | 
			
		||||
  void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override {
 | 
			
		||||
    this->bluetooth_le_advertisement_subscription_ = false;
 | 
			
		||||
  }
 | 
			
		||||
  bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg);
 | 
			
		||||
 | 
			
		||||
  void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
 | 
			
		||||
@@ -117,6 +124,15 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override {
 | 
			
		||||
    this->voice_assistant_subscription_ = msg.subscribe;
 | 
			
		||||
  }
 | 
			
		||||
  bool request_voice_assistant(bool start);
 | 
			
		||||
  void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
 | 
			
		||||
  void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  void on_disconnect_response(const DisconnectResponse &value) override;
 | 
			
		||||
  void on_ping_response(const PingResponse &value) override {
 | 
			
		||||
    // we initiated ping
 | 
			
		||||
@@ -150,9 +166,7 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  void execute_service(const ExecuteServiceRequest &msg) override;
 | 
			
		||||
  void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override {
 | 
			
		||||
    this->bluetooth_le_advertisement_subscription_ = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; }
 | 
			
		||||
  bool is_connection_setup() override {
 | 
			
		||||
    return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated();
 | 
			
		||||
@@ -197,7 +211,12 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  uint32_t last_traffic_;
 | 
			
		||||
  bool sent_ping_{false};
 | 
			
		||||
  bool service_call_subscription_{false};
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool bluetooth_le_advertisement_subscription_{false};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool voice_assistant_subscription_{false};
 | 
			
		||||
#endif
 | 
			
		||||
  bool next_close_ = false;
 | 
			
		||||
  APIServer *parent_;
 | 
			
		||||
  InitialStateIterator initial_state_iterator_;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,8 +10,8 @@
 | 
			
		||||
#include "noise/protocol.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/socket/socket.h"
 | 
			
		||||
#include "api_noise_context.h"
 | 
			
		||||
#include "esphome/components/socket/socket.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
@@ -67,6 +67,7 @@ class APIFrameHelper {
 | 
			
		||||
  virtual bool can_write_without_blocking() = 0;
 | 
			
		||||
  virtual APIError write_packet(uint16_t type, const uint8_t *data, size_t len) = 0;
 | 
			
		||||
  virtual std::string getpeername() = 0;
 | 
			
		||||
  virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen) = 0;
 | 
			
		||||
  virtual APIError close() = 0;
 | 
			
		||||
  virtual APIError shutdown(int how) = 0;
 | 
			
		||||
  // Give this helper a name for logging
 | 
			
		||||
@@ -84,7 +85,10 @@ class APINoiseFrameHelper : public APIFrameHelper {
 | 
			
		||||
  APIError read_packet(ReadPacketBuffer *buffer) override;
 | 
			
		||||
  bool can_write_without_blocking() override;
 | 
			
		||||
  APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
 | 
			
		||||
  std::string getpeername() override { return socket_->getpeername(); }
 | 
			
		||||
  std::string getpeername() override { return this->socket_->getpeername(); }
 | 
			
		||||
  int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
 | 
			
		||||
    return this->socket_->getpeername(addr, addrlen);
 | 
			
		||||
  }
 | 
			
		||||
  APIError close() override;
 | 
			
		||||
  APIError shutdown(int how) override;
 | 
			
		||||
  // Give this helper a name for logging
 | 
			
		||||
@@ -144,7 +148,10 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
 | 
			
		||||
  APIError read_packet(ReadPacketBuffer *buffer) override;
 | 
			
		||||
  bool can_write_without_blocking() override;
 | 
			
		||||
  APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
 | 
			
		||||
  std::string getpeername() override { return socket_->getpeername(); }
 | 
			
		||||
  std::string getpeername() override { return this->socket_->getpeername(); }
 | 
			
		||||
  int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
 | 
			
		||||
    return this->socket_->getpeername(addr, addrlen);
 | 
			
		||||
  }
 | 
			
		||||
  APIError close() override;
 | 
			
		||||
  APIError shutdown(int how) override;
 | 
			
		||||
  // Give this helper a name for logging
 | 
			
		||||
 
 | 
			
		||||
@@ -400,6 +400,34 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet
 | 
			
		||||
      return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE";
 | 
			
		||||
    case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE:
 | 
			
		||||
      return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE";
 | 
			
		||||
    case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE:
 | 
			
		||||
      return "BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::VoiceAssistantEvent value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::VOICE_ASSISTANT_ERROR:
 | 
			
		||||
      return "VOICE_ASSISTANT_ERROR";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_RUN_START:
 | 
			
		||||
      return "VOICE_ASSISTANT_RUN_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_RUN_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_RUN_END";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_STT_START:
 | 
			
		||||
      return "VOICE_ASSISTANT_STT_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_STT_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_STT_END";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_INTENT_START:
 | 
			
		||||
      return "VOICE_ASSISTANT_INTENT_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_INTENT_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_INTENT_END";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_TTS_START:
 | 
			
		||||
      return "VOICE_ASSISTANT_TTS_START";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_TTS_END:
 | 
			
		||||
      return "VOICE_ASSISTANT_TTS_END";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
@@ -592,6 +620,10 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
      this->bluetooth_proxy_version = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 14: {
 | 
			
		||||
      this->voice_assistant_version = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -652,6 +684,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_uint32(11, this->bluetooth_proxy_version);
 | 
			
		||||
  buffer.encode_string(12, this->manufacturer);
 | 
			
		||||
  buffer.encode_string(13, this->friendly_name);
 | 
			
		||||
  buffer.encode_uint32(14, this->voice_assistant_version);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
			
		||||
@@ -710,6 +743,11 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  friendly_name: ");
 | 
			
		||||
  out.append("'").append(this->friendly_name).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  voice_assistant_version: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->voice_assistant_version);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -6060,6 +6098,204 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
void UnsubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool BluetoothDeviceClearCacheResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->address = value.as_uint64();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->success = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->error = value.as_int32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_uint64(1, this->address);
 | 
			
		||||
  buffer.encode_bool(2, this->success);
 | 
			
		||||
  buffer.encode_int32(3, this->error);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("BluetoothDeviceClearCacheResponse {\n");
 | 
			
		||||
  out.append("  address: ");
 | 
			
		||||
  sprintf(buffer, "%llu", this->address);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  success: ");
 | 
			
		||||
  out.append(YESNO(this->success));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  error: ");
 | 
			
		||||
  sprintf(buffer, "%d", this->error);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->subscribe = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->subscribe); }
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("SubscribeVoiceAssistantRequest {\n");
 | 
			
		||||
  out.append("  subscribe: ");
 | 
			
		||||
  out.append(YESNO(this->subscribe));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->start = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->start); }
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantRequest::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantRequest {\n");
 | 
			
		||||
  out.append("  start: ");
 | 
			
		||||
  out.append(YESNO(this->start));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->port = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->error = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_uint32(1, this->port);
 | 
			
		||||
  buffer.encode_bool(2, this->error);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantResponse {\n");
 | 
			
		||||
  out.append("  port: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->port);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  error: ");
 | 
			
		||||
  out.append(YESNO(this->error));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantEventData::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->name = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->value = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantEventData::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_string(1, this->name);
 | 
			
		||||
  buffer.encode_string(2, this->value);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantEventData::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantEventData {\n");
 | 
			
		||||
  out.append("  name: ");
 | 
			
		||||
  out.append("'").append(this->name).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  value: ");
 | 
			
		||||
  out.append("'").append(this->value).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->event_type = value.as_enum<enums::VoiceAssistantEvent>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool VoiceAssistantEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->data.push_back(value.as_message<VoiceAssistantEventData>());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantEventResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_enum<enums::VoiceAssistantEvent>(1, this->event_type);
 | 
			
		||||
  for (auto &it : this->data) {
 | 
			
		||||
    buffer.encode_message<VoiceAssistantEventData>(2, it, true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantEventResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantEventResponse {\n");
 | 
			
		||||
  out.append("  event_type: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::VoiceAssistantEvent>(this->event_type));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  for (const auto &it : this->data) {
 | 
			
		||||
    out.append("  data: ");
 | 
			
		||||
    it.dump_to(out);
 | 
			
		||||
    out.append("\n");
 | 
			
		||||
  }
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -163,6 +163,18 @@ enum BluetoothDeviceRequestType : uint32_t {
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3,
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4,
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
 | 
			
		||||
  BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
 | 
			
		||||
};
 | 
			
		||||
enum VoiceAssistantEvent : uint32_t {
 | 
			
		||||
  VOICE_ASSISTANT_ERROR = 0,
 | 
			
		||||
  VOICE_ASSISTANT_RUN_START = 1,
 | 
			
		||||
  VOICE_ASSISTANT_RUN_END = 2,
 | 
			
		||||
  VOICE_ASSISTANT_STT_START = 3,
 | 
			
		||||
  VOICE_ASSISTANT_STT_END = 4,
 | 
			
		||||
  VOICE_ASSISTANT_INTENT_START = 5,
 | 
			
		||||
  VOICE_ASSISTANT_INTENT_END = 6,
 | 
			
		||||
  VOICE_ASSISTANT_TTS_START = 7,
 | 
			
		||||
  VOICE_ASSISTANT_TTS_END = 8,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace enums
 | 
			
		||||
@@ -278,6 +290,7 @@ class DeviceInfoResponse : public ProtoMessage {
 | 
			
		||||
  uint32_t bluetooth_proxy_version{0};
 | 
			
		||||
  std::string manufacturer{};
 | 
			
		||||
  std::string friendly_name{};
 | 
			
		||||
  uint32_t voice_assistant_version{0};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
@@ -1554,6 +1567,87 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage {
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class BluetoothDeviceClearCacheResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint64_t address{0};
 | 
			
		||||
  bool success{false};
 | 
			
		||||
  int32_t error{0};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class SubscribeVoiceAssistantRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool subscribe{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool start{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t port{0};
 | 
			
		||||
  bool error{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantEventData : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string value{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantEventResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  enums::VoiceAssistantEvent event_type{};
 | 
			
		||||
  std::vector<VoiceAssistantEventData> data{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -329,6 +329,8 @@ bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayer
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str());
 | 
			
		||||
@@ -441,6 +443,30 @@ bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const Blu
 | 
			
		||||
  return this->send_message_<BluetoothDeviceUnpairingResponse>(msg, 86);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantRequest &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_voice_assistant_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<VoiceAssistantRequest>(msg, 90);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
 | 
			
		||||
  switch (msg_type) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
@@ -709,12 +735,14 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 66: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      SubscribeBluetoothLEAdvertisementsRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_subscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_subscribe_bluetooth_le_advertisements_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 68: {
 | 
			
		||||
@@ -802,6 +830,50 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
			
		||||
      ESP_LOGVV(TAG, "on_subscribe_bluetooth_connections_free_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_subscribe_bluetooth_connections_free_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 87: {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
      UnsubscribeBluetoothLEAdvertisementsRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_unsubscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_unsubscribe_bluetooth_le_advertisements_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 89: {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
      SubscribeVoiceAssistantRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_subscribe_voice_assistant_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_subscribe_voice_assistant_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 91: {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
      VoiceAssistantResponse msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_voice_assistant_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_voice_assistant_response(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 92: {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
      VoiceAssistantEventResponse msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_voice_assistant_event_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_voice_assistant_event_response(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1065,6 +1137,7 @@ void APIServerConnection::on_media_player_command_request(const MediaPlayerComma
 | 
			
		||||
  this->media_player_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
    const SubscribeBluetoothLEAdvertisementsRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
@@ -1077,6 +1150,7 @@ void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
  }
 | 
			
		||||
  this->subscribe_bluetooth_le_advertisements(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
@@ -1185,6 +1259,33 @@ void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
    const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->unsubscribe_bluetooth_le_advertisements(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->subscribe_voice_assistant(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -154,8 +154,10 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_subscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
      const SubscribeBluetoothLEAdvertisementsRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -215,6 +217,25 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void on_unsubscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
      const UnsubscribeBluetoothLEAdvertisementsRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool send_voice_assistant_request(const VoiceAssistantRequest &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
 | 
			
		||||
@@ -267,7 +288,9 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void bluetooth_device_request(const BluetoothDeviceRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -292,6 +315,12 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
 | 
			
		||||
      const SubscribeBluetoothConnectionsFreeRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  void on_hello_request(const HelloRequest &msg) override;
 | 
			
		||||
@@ -339,7 +368,9 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_MEDIA_PLAYER
 | 
			
		||||
  void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -364,6 +395,13 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_unsubscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
      const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ void APIServer::setup() {
 | 
			
		||||
 | 
			
		||||
  struct sockaddr_storage server;
 | 
			
		||||
 | 
			
		||||
  socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), htons(this->port_));
 | 
			
		||||
  socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
 | 
			
		||||
  if (sl == 0) {
 | 
			
		||||
    ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
@@ -331,6 +331,17 @@ void APIServer::send_bluetooth_device_unpairing(uint64_t address, bool success,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIServer::send_bluetooth_device_clear_cache(uint64_t address, bool success, esp_err_t error) {
 | 
			
		||||
  BluetoothDeviceClearCacheResponse call;
 | 
			
		||||
  call.address = address;
 | 
			
		||||
  call.success = success;
 | 
			
		||||
  call.error = error;
 | 
			
		||||
 | 
			
		||||
  for (auto &client : this->clients_) {
 | 
			
		||||
    client->send_bluetooth_device_clear_cache_response(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIServer::send_bluetooth_connections_free(uint8_t free, uint8_t limit) {
 | 
			
		||||
  BluetoothConnectionsFreeResponse call;
 | 
			
		||||
  call.free = free;
 | 
			
		||||
@@ -416,5 +427,20 @@ void APIServer::on_shutdown() {
 | 
			
		||||
  delay(10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIServer::start_voice_assistant() {
 | 
			
		||||
  bool result = false;
 | 
			
		||||
  for (auto &c : this->clients_) {
 | 
			
		||||
    result |= c->request_voice_assistant(true);
 | 
			
		||||
  }
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
void APIServer::stop_voice_assistant() {
 | 
			
		||||
  for (auto &c : this->clients_) {
 | 
			
		||||
    c->request_voice_assistant(false);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,7 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
  void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu = 0, esp_err_t error = ESP_OK);
 | 
			
		||||
  void send_bluetooth_device_pairing(uint64_t address, bool paired, esp_err_t error = ESP_OK);
 | 
			
		||||
  void send_bluetooth_device_unpairing(uint64_t address, bool success, esp_err_t error = ESP_OK);
 | 
			
		||||
  void send_bluetooth_device_clear_cache(uint64_t address, bool success, esp_err_t error = ESP_OK);
 | 
			
		||||
  void send_bluetooth_connections_free(uint8_t free, uint8_t limit);
 | 
			
		||||
  void send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call);
 | 
			
		||||
  void send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call);
 | 
			
		||||
@@ -94,6 +95,11 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
  void request_time();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool start_voice_assistant();
 | 
			
		||||
  void stop_voice_assistant();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  bool is_connected() const;
 | 
			
		||||
 | 
			
		||||
  struct HomeAssistantStateSubscription {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "proto.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -13,7 +14,7 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
 | 
			
		||||
    uint32_t consumed;
 | 
			
		||||
    auto res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
 | 
			
		||||
    if (!res.has_value()) {
 | 
			
		||||
      ESP_LOGV(TAG, "Invalid field start at %u", i);
 | 
			
		||||
      ESP_LOGV(TAG, "Invalid field start at %" PRIu32, i);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -25,12 +26,12 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
 | 
			
		||||
      case 0: {  // VarInt
 | 
			
		||||
        res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
 | 
			
		||||
        if (!res.has_value()) {
 | 
			
		||||
          ESP_LOGV(TAG, "Invalid VarInt at %u", i);
 | 
			
		||||
          ESP_LOGV(TAG, "Invalid VarInt at %" PRIu32, i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this->decode_varint(field_id, *res)) {
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode VarInt field %u with value %u!", field_id, res->as_uint32());
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu32 "!", field_id, res->as_uint32());
 | 
			
		||||
        }
 | 
			
		||||
        i += consumed;
 | 
			
		||||
        break;
 | 
			
		||||
@@ -38,38 +39,38 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
 | 
			
		||||
      case 2: {  // Length-delimited
 | 
			
		||||
        res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
 | 
			
		||||
        if (!res.has_value()) {
 | 
			
		||||
          ESP_LOGV(TAG, "Invalid Length Delimited at %u", i);
 | 
			
		||||
          ESP_LOGV(TAG, "Invalid Length Delimited at %" PRIu32, i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        uint32_t field_length = res->as_uint32();
 | 
			
		||||
        i += consumed;
 | 
			
		||||
        if (field_length > length - i) {
 | 
			
		||||
          ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %u", i);
 | 
			
		||||
          ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %" PRIu32, i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this->decode_length(field_id, ProtoLengthDelimited(&buffer[i], field_length))) {
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode Length Delimited field %u!", field_id);
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode Length Delimited field %" PRIu32 "!", field_id);
 | 
			
		||||
        }
 | 
			
		||||
        i += field_length;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 5: {  // 32-bit
 | 
			
		||||
        if (length - i < 4) {
 | 
			
		||||
          ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %u", i);
 | 
			
		||||
          ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %" PRIu32, i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]);
 | 
			
		||||
        if (!this->decode_32bit(field_id, Proto32Bit(val))) {
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val);
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode 32-bit field %" PRIu32 " with value %" PRIu32 "!", field_id, val);
 | 
			
		||||
        }
 | 
			
		||||
        i += 4;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      default:
 | 
			
		||||
        ESP_LOGV(TAG, "Invalid field type at %u", i);
 | 
			
		||||
        ESP_LOGV(TAG, "Invalid field type at %" PRIu32, i);
 | 
			
		||||
        error = true;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_CAPACITANCE,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["sensor", "binary_sensor"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
CONF_AS3935_ID = "as3935_id"
 | 
			
		||||
 
 | 
			
		||||
@@ -26,9 +26,13 @@ void AS3935Component::setup() {
 | 
			
		||||
void AS3935Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "AS3935:");
 | 
			
		||||
  LOG_PIN("  Interrupt Pin: ", this->irq_pin_);
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  LOG_BINARY_SENSOR("  ", "Thunder alert", this->thunder_alert_binary_sensor_);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  LOG_SENSOR("  ", "Distance", this->distance_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Lightning energy", this->energy_sensor_);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
@@ -44,16 +48,22 @@ void AS3935Component::loop() {
 | 
			
		||||
    ESP_LOGI(TAG, "Disturber was detected - try increasing the spike rejection value!");
 | 
			
		||||
  } else if (int_value == LIGHTNING_INT) {
 | 
			
		||||
    ESP_LOGI(TAG, "Lightning has been detected!");
 | 
			
		||||
    if (this->thunder_alert_binary_sensor_ != nullptr)
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
    if (this->thunder_alert_binary_sensor_ != nullptr) {
 | 
			
		||||
      this->thunder_alert_binary_sensor_->publish_state(true);
 | 
			
		||||
      this->set_timeout(10, [this]() { this->thunder_alert_binary_sensor_->publish_state(false); });
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
    uint8_t distance = this->get_distance_to_storm_();
 | 
			
		||||
    if (this->distance_sensor_ != nullptr)
 | 
			
		||||
      this->distance_sensor_->publish_state(distance);
 | 
			
		||||
 | 
			
		||||
    uint32_t energy = this->get_lightning_energy_();
 | 
			
		||||
    if (this->energy_sensor_ != nullptr)
 | 
			
		||||
      this->energy_sensor_->publish_state(energy);
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
  this->thunder_alert_binary_sensor_->publish_state(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AS3935Component::write_indoor(bool indoor) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,14 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace as3935 {
 | 
			
		||||
@@ -52,6 +57,15 @@ enum AS3935Values {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AS3935Component : public Component {
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  SUB_SENSOR(distance)
 | 
			
		||||
  SUB_SENSOR(energy)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  SUB_BINARY_SENSOR(thunder_alert)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
@@ -59,11 +73,7 @@ class AS3935Component : public Component {
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void set_irq_pin(GPIOPin *irq_pin) { irq_pin_ = irq_pin; }
 | 
			
		||||
  void set_distance_sensor(sensor::Sensor *distance_sensor) { distance_sensor_ = distance_sensor; }
 | 
			
		||||
  void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
 | 
			
		||||
  void set_thunder_alert_binary_sensor(binary_sensor::BinarySensor *thunder_alert_binary_sensor) {
 | 
			
		||||
    thunder_alert_binary_sensor_ = thunder_alert_binary_sensor;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_indoor(bool indoor) { indoor_ = indoor; }
 | 
			
		||||
  void write_indoor(bool indoor);
 | 
			
		||||
  void set_noise_level(uint8_t noise_level) { noise_level_ = noise_level; }
 | 
			
		||||
@@ -92,9 +102,6 @@ class AS3935Component : public Component {
 | 
			
		||||
 | 
			
		||||
  virtual void write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position) = 0;
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *distance_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *energy_sensor_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *thunder_alert_binary_sensor_{nullptr};
 | 
			
		||||
  GPIOPin *irq_pin_;
 | 
			
		||||
 | 
			
		||||
  bool indoor_;
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,7 @@ class BedJetHub : public esphome::ble_client::BLEClientNode, public PollingCompo
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void setup() override { this->codec_ = make_unique<BedjetCodec>(); }
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::BLUETOOTH; }
 | 
			
		||||
 | 
			
		||||
  /** @return The BedJet's configured name, or the MAC address if not discovered yet. */
 | 
			
		||||
  std::string get_name() {
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ void BinarySensorMap::process_group_() {
 | 
			
		||||
    if (bs.binary_sensor->state) {
 | 
			
		||||
      num_active_sensors++;
 | 
			
		||||
      total_current_value += bs.sensor_value;
 | 
			
		||||
      mask |= 1 << i;
 | 
			
		||||
      mask |= 1ULL << i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // check if the sensor map was touched
 | 
			
		||||
@@ -38,12 +38,11 @@ void BinarySensorMap::process_group_() {
 | 
			
		||||
    // did the bit_mask change or is it a new sensor touch
 | 
			
		||||
    if (this->last_mask_ != mask) {
 | 
			
		||||
      float publish_value = total_current_value / num_active_sensors;
 | 
			
		||||
      ESP_LOGD(TAG, "'%s' - Publishing %.2f", this->name_.c_str(), publish_value);
 | 
			
		||||
      this->publish_state(publish_value);
 | 
			
		||||
    }
 | 
			
		||||
  } else if (this->last_mask_ != 0ULL) {
 | 
			
		||||
    // is this a new sensor release
 | 
			
		||||
    ESP_LOGD(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str());
 | 
			
		||||
    ESP_LOGV(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str());
 | 
			
		||||
    this->publish_state(NAN);
 | 
			
		||||
  }
 | 
			
		||||
  this->last_mask_ = mask;
 | 
			
		||||
@@ -52,28 +51,22 @@ void BinarySensorMap::process_group_() {
 | 
			
		||||
void BinarySensorMap::process_sum_() {
 | 
			
		||||
  float total_current_value = 0.0;
 | 
			
		||||
  uint64_t mask = 0x00;
 | 
			
		||||
  // check all binary_sensors for its state. when active add its value to total_current_value.
 | 
			
		||||
  // create a bitmask for the binary_sensor status on all channels
 | 
			
		||||
  // - check all binary_sensor states
 | 
			
		||||
  // - if active, add its value to total_current_value
 | 
			
		||||
  // - creates a bitmask for the binary_sensor status on all channels
 | 
			
		||||
  for (size_t i = 0; i < this->channels_.size(); i++) {
 | 
			
		||||
    auto bs = this->channels_[i];
 | 
			
		||||
    if (bs.binary_sensor->state) {
 | 
			
		||||
      total_current_value += bs.sensor_value;
 | 
			
		||||
      mask |= 1 << i;
 | 
			
		||||
      mask |= 1ULL << i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // check if the sensor map was touched
 | 
			
		||||
  if (mask != 0ULL) {
 | 
			
		||||
    // did the bit_mask change or is it a new sensor touch
 | 
			
		||||
    if (this->last_mask_ != mask) {
 | 
			
		||||
      float publish_value = total_current_value;
 | 
			
		||||
      ESP_LOGD(TAG, "'%s' - Publishing %.2f", this->name_.c_str(), publish_value);
 | 
			
		||||
      this->publish_state(publish_value);
 | 
			
		||||
    }
 | 
			
		||||
  } else if (this->last_mask_ != 0ULL) {
 | 
			
		||||
    // is this a new sensor release
 | 
			
		||||
    ESP_LOGD(TAG, "'%s' - No binary sensor active, publishing 0", this->name_.c_str());
 | 
			
		||||
    this->publish_state(0.0);
 | 
			
		||||
 | 
			
		||||
  // update state only if the binary sensor states have changed or if no state has ever been sent on boot
 | 
			
		||||
  if ((this->last_mask_ != mask) || (!this->has_state())) {
 | 
			
		||||
    this->publish_state(total_current_value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->last_mask_ = mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ CONFIG_SCHEMA = cv.typed_schema(
 | 
			
		||||
        ).extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Required(CONF_CHANNELS): cv.All(
 | 
			
		||||
                    cv.ensure_list(entry), cv.Length(min=1)
 | 
			
		||||
                    cv.ensure_list(entry), cv.Length(min=1, max=64)
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
@@ -50,7 +50,7 @@ CONFIG_SCHEMA = cv.typed_schema(
 | 
			
		||||
        ).extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Required(CONF_CHANNELS): cv.All(
 | 
			
		||||
                    cv.ensure_list(entry), cv.Length(min=1)
 | 
			
		||||
                    cv.ensure_list(entry), cv.Length(min=1, max=64)
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
 
 | 
			
		||||
@@ -306,6 +306,13 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
 | 
			
		||||
      api::global_api_server->send_bluetooth_device_unpairing(msg.address, ret == ESP_OK, ret);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: {
 | 
			
		||||
      esp_bd_addr_t address;
 | 
			
		||||
      uint64_to_bd_addr(msg.address, address);
 | 
			
		||||
      esp_err_t ret = esp_ble_gattc_cache_clean(address);
 | 
			
		||||
      api::global_api_server->send_bluetooth_device_clear_cache(msg.address, ret == ESP_OK, ret);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -68,6 +68,14 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
 | 
			
		||||
 | 
			
		||||
extern BluetoothProxy *global_bluetooth_proxy;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 | 
			
		||||
// Version 1: Initial version without active connections
 | 
			
		||||
// Version 2: Support for active connections
 | 
			
		||||
// Version 3: New connection API
 | 
			
		||||
// Version 4: Pairing support
 | 
			
		||||
// Version 5: Cache clear support
 | 
			
		||||
static const uint32_t ACTIVE_CONNECTIONS_VERSION = 5;
 | 
			
		||||
static const uint32_t PASSIVE_ONLY_VERSION = 1;
 | 
			
		||||
 | 
			
		||||
}  // namespace bluetooth_proxy
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c
 | 
			
		||||
from esphome.components import i2c, esp32
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@trvrnrth"]
 | 
			
		||||
@@ -32,22 +32,31 @@ BME680BSECComponent = bme680_bsec_ns.class_(
 | 
			
		||||
    "BME680BSECComponent", cg.Component, i2c.I2CDevice
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(BME680BSECComponent),
 | 
			
		||||
        cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature,
 | 
			
		||||
        cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum(
 | 
			
		||||
            IAQ_MODE_OPTIONS, upper=True
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum(
 | 
			
		||||
            SAMPLE_RATE_OPTIONS, upper=True
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(
 | 
			
		||||
            CONF_STATE_SAVE_INTERVAL, default="6hours"
 | 
			
		||||
        ): cv.positive_time_period_minutes,
 | 
			
		||||
    },
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BME680BSECComponent),
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature,
 | 
			
		||||
            cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum(
 | 
			
		||||
                IAQ_MODE_OPTIONS, upper=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum(
 | 
			
		||||
                SAMPLE_RATE_OPTIONS, upper=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(
 | 
			
		||||
                CONF_STATE_SAVE_INTERVAL, default="6hours"
 | 
			
		||||
            ): cv.positive_time_period_minutes,
 | 
			
		||||
        }
 | 
			
		||||
    ).extend(i2c.i2c_device_schema(0x76)),
 | 
			
		||||
    cv.only_with_arduino,
 | 
			
		||||
).extend(i2c.i2c_device_schema(0x76))
 | 
			
		||||
    cv.Any(
 | 
			
		||||
        cv.only_on_esp8266,
 | 
			
		||||
        cv.All(
 | 
			
		||||
            cv.only_on_esp32,
 | 
			
		||||
            esp32.only_on_variant(supported=[esp32.const.VARIANT_ESP32]),
 | 
			
		||||
        ),
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,6 @@ namespace button {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "button";
 | 
			
		||||
 | 
			
		||||
Button::Button(const std::string &name) : EntityBase(name) {}
 | 
			
		||||
Button::Button() : Button("") {}
 | 
			
		||||
 | 
			
		||||
void Button::press() {
 | 
			
		||||
  ESP_LOGD(TAG, "'%s' Pressed.", this->get_name().c_str());
 | 
			
		||||
  this->press_action();
 | 
			
		||||
 
 | 
			
		||||
@@ -28,9 +28,6 @@ namespace button {
 | 
			
		||||
 */
 | 
			
		||||
class Button : public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit Button();
 | 
			
		||||
  explicit Button(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /** Press this button. This is called by the front-end.
 | 
			
		||||
   *
 | 
			
		||||
   * For implementing buttons, please override press_action.
 | 
			
		||||
 
 | 
			
		||||
@@ -453,12 +453,7 @@ void Climate::set_visual_temperature_step_override(float target, float current)
 | 
			
		||||
  this->visual_target_temperature_step_override_ = target;
 | 
			
		||||
  this->visual_current_temperature_step_override_ = current;
 | 
			
		||||
}
 | 
			
		||||
#pragma GCC diagnostic push
 | 
			
		||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 | 
			
		||||
Climate::Climate(const std::string &name) : EntityBase(name) {}
 | 
			
		||||
#pragma GCC diagnostic pop
 | 
			
		||||
 | 
			
		||||
Climate::Climate() : Climate("") {}
 | 
			
		||||
ClimateCall Climate::make_call() { return ClimateCall(this); }
 | 
			
		||||
 | 
			
		||||
ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) {
 | 
			
		||||
 
 | 
			
		||||
@@ -166,11 +166,6 @@ struct ClimateDeviceRestoreState {
 | 
			
		||||
 */
 | 
			
		||||
class Climate : public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  /// Construct a climate device with empty name (will be set later).
 | 
			
		||||
  Climate();
 | 
			
		||||
  /// Construct a climate device with a name.
 | 
			
		||||
  Climate(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /// The active mode of the climate device.
 | 
			
		||||
  ClimateMode mode{CLIMATE_MODE_OFF};
 | 
			
		||||
  /// The active state of the climate device.
 | 
			
		||||
 
 | 
			
		||||
@@ -14,12 +14,15 @@ from .. import copy_ns
 | 
			
		||||
CopySelect = copy_ns.class_("CopySelect", select.Select, cg.Component)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = select.SELECT_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(CopySelect),
 | 
			
		||||
        cv.Required(CONF_SOURCE_ID): cv.use_id(select.Select),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    select.select_schema(CopySelect)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_SOURCE_ID): cv.use_id(select.Select),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = cv.All(
 | 
			
		||||
    inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ const char *cover_operation_to_str(CoverOperation op) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Cover::Cover(const std::string &name) : EntityBase(name), position{COVER_OPEN} {}
 | 
			
		||||
Cover::Cover() : position{COVER_OPEN} {}
 | 
			
		||||
 | 
			
		||||
CoverCall::CoverCall(Cover *parent) : parent_(parent) {}
 | 
			
		||||
CoverCall &CoverCall::set_command(const char *command) {
 | 
			
		||||
@@ -204,7 +204,6 @@ optional<CoverRestoreState> Cover::restore_state_() {
 | 
			
		||||
    return {};
 | 
			
		||||
  return recovered;
 | 
			
		||||
}
 | 
			
		||||
Cover::Cover() : Cover("") {}
 | 
			
		||||
std::string Cover::get_device_class() {
 | 
			
		||||
  if (this->device_class_override_.has_value())
 | 
			
		||||
    return *this->device_class_override_;
 | 
			
		||||
 
 | 
			
		||||
@@ -111,7 +111,6 @@ const char *cover_operation_to_str(CoverOperation op);
 | 
			
		||||
class Cover : public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit Cover();
 | 
			
		||||
  explicit Cover(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /// The current operation of the cover (idle, opening, closing).
 | 
			
		||||
  CoverOperation current_operation{COVER_OPERATION_IDLE};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,11 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.final_validate as fv
 | 
			
		||||
from esphome.components import logger
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_BLOCK,
 | 
			
		||||
    CONF_DEVICE,
 | 
			
		||||
    CONF_FRAGMENTATION,
 | 
			
		||||
    CONF_FREE,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_LEVEL,
 | 
			
		||||
    CONF_LOGGER,
 | 
			
		||||
    CONF_LOOP_TIME,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -43,18 +39,6 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
).extend(cv.polling_component_schema("60s"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _final_validate(_):
 | 
			
		||||
    logger_conf = fv.full_config.get()[CONF_LOGGER]
 | 
			
		||||
    severity = logger.LOG_LEVEL_SEVERITY.index(logger_conf[CONF_LEVEL])
 | 
			
		||||
    if severity < logger.LOG_LEVEL_SEVERITY.index("DEBUG"):
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            "The debug component requires the logger to be at least at DEBUG level"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = _final_validate
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,10 @@ static uint32_t get_free_heap() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebugComponent::dump_config() {
 | 
			
		||||
#ifndef ESPHOME_LOG_HAS_DEBUG
 | 
			
		||||
  return;  // Can't log below if debug logging is disabled
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  std::string device_info;
 | 
			
		||||
  std::string reset_reason;
 | 
			
		||||
  device_info.reserve(256);
 | 
			
		||||
 
 | 
			
		||||
@@ -163,7 +163,7 @@ RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 0, 5)
 | 
			
		||||
# The platformio/espressif32 version to use for arduino frameworks
 | 
			
		||||
#  - https://github.com/platformio/platform-espressif32/releases
 | 
			
		||||
#  - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
 | 
			
		||||
ARDUINO_PLATFORM_VERSION = cv.Version(5, 2, 0)
 | 
			
		||||
ARDUINO_PLATFORM_VERSION = cv.Version(5, 3, 0)
 | 
			
		||||
 | 
			
		||||
# The default/recommended esp-idf framework version
 | 
			
		||||
#  - https://github.com/espressif/esp-idf/releases
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
#include "gpio.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32 {
 | 
			
		||||
@@ -74,7 +75,7 @@ void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpi
 | 
			
		||||
 | 
			
		||||
std::string ESP32InternalGPIOPin::dump_summary() const {
 | 
			
		||||
  char buffer[32];
 | 
			
		||||
  snprintf(buffer, sizeof(buffer), "GPIO%u", static_cast<uint32_t>(pin_));
 | 
			
		||||
  snprintf(buffer, sizeof(buffer), "GPIO%" PRIu32, static_cast<uint32_t>(pin_));
 | 
			
		||||
  return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <nvs_flash.h>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
@@ -101,7 +102,7 @@ class ESP32Preferences : public ESPPreferences {
 | 
			
		||||
    pref->nvs_handle = nvs_handle;
 | 
			
		||||
 | 
			
		||||
    uint32_t keyval = type;
 | 
			
		||||
    pref->key = str_sprintf("%u", keyval);
 | 
			
		||||
    pref->key = str_sprintf("%" PRIu32, keyval);
 | 
			
		||||
 | 
			
		||||
    return ESPPreferenceObject(pref);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,8 @@ CONF_BLE_ID = "ble_id"
 | 
			
		||||
 | 
			
		||||
NO_BLUTOOTH_VARIANTS = [const.VARIANT_ESP32S2]
 | 
			
		||||
 | 
			
		||||
NO_BLUTOOTH_VARIANTS = [const.VARIANT_ESP32S2]
 | 
			
		||||
 | 
			
		||||
esp32_ble_ns = cg.esphome_ns.namespace("esp32_ble")
 | 
			
		||||
ESP32BLE = esp32_ble_ns.class_("ESP32BLE", cg.Component)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
 | 
			
		||||
#include <esp_bt.h>
 | 
			
		||||
#include <esp_bt_main.h>
 | 
			
		||||
#include <esp_bt_device.h>
 | 
			
		||||
#include <esp_gap_ble_api.h>
 | 
			
		||||
#include <freertos/FreeRTOS.h>
 | 
			
		||||
#include <freertos/FreeRTOSConfig.h>
 | 
			
		||||
@@ -211,7 +212,16 @@ void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if
 | 
			
		||||
 | 
			
		||||
float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; }
 | 
			
		||||
 | 
			
		||||
void ESP32BLE::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 BLE:"); }
 | 
			
		||||
void ESP32BLE::dump_config() {
 | 
			
		||||
  const uint8_t *mac_address = esp_bt_dev_get_address();
 | 
			
		||||
  if (mac_address) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "ESP32 BLE:");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  MAC address: %02X:%02X:%02X:%02X:%02X:%02X", mac_address[0], mac_address[1], mac_address[2],
 | 
			
		||||
                  mac_address[3], mac_address[4], mac_address[5]);
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ESP32BLE *global_ble = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -156,7 +156,7 @@ CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number,
 | 
			
		||||
                cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All(
 | 
			
		||||
                    cv.frequency, cv.Range(min=10e6, max=20e6)
 | 
			
		||||
                    cv.frequency, cv.Range(min=8e6, max=20e6)
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
 
 | 
			
		||||
@@ -202,7 +202,7 @@ void ESP32Camera::loop() {
 | 
			
		||||
float ESP32Camera::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
/* ---------------- constructors ---------------- */
 | 
			
		||||
ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) {
 | 
			
		||||
ESP32Camera::ESP32Camera() {
 | 
			
		||||
  this->config_.pin_pwdn = -1;
 | 
			
		||||
  this->config_.pin_reset = -1;
 | 
			
		||||
  this->config_.pin_xclk = -1;
 | 
			
		||||
@@ -215,7 +215,6 @@ ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) {
 | 
			
		||||
 | 
			
		||||
  global_esp32_camera = this;
 | 
			
		||||
}
 | 
			
		||||
ESP32Camera::ESP32Camera() : ESP32Camera("") {}
 | 
			
		||||
 | 
			
		||||
/* ---------------- setters ---------------- */
 | 
			
		||||
/* set pin assignment */
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,6 @@ class CameraImageReader {
 | 
			
		||||
/* ---------------- ESP32Camera class ---------------- */
 | 
			
		||||
class ESP32Camera : public Component, public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  ESP32Camera(const std::string &name);
 | 
			
		||||
  ESP32Camera();
 | 
			
		||||
 | 
			
		||||
  /* setters */
 | 
			
		||||
 
 | 
			
		||||
@@ -22,20 +22,12 @@ ESP32ImprovComponent = esp32_improv_ns.class_(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_none_(value):
 | 
			
		||||
    if value in ("none", "None"):
 | 
			
		||||
        return None
 | 
			
		||||
    if cv.boolean(value) is False:
 | 
			
		||||
        return None
 | 
			
		||||
    raise cv.Invalid("Must be none")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(ESP32ImprovComponent),
 | 
			
		||||
        cv.GenerateID(CONF_BLE_SERVER_ID): cv.use_id(esp32_ble_server.BLEServer),
 | 
			
		||||
        cv.Required(CONF_AUTHORIZER): cv.Any(
 | 
			
		||||
            validate_none_, cv.use_id(binary_sensor.BinarySensor)
 | 
			
		||||
            cv.none, cv.use_id(binary_sensor.BinarySensor)
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_STATUS_INDICATOR): cv.use_id(output.BinaryOutput),
 | 
			
		||||
        cv.Optional(
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ ETHERNET_TYPES = {
 | 
			
		||||
    "DP83848": EthernetType.ETHERNET_TYPE_DP83848,
 | 
			
		||||
    "IP101": EthernetType.ETHERNET_TYPE_IP101,
 | 
			
		||||
    "JL1101": EthernetType.ETHERNET_TYPE_JL1101,
 | 
			
		||||
    "KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t")
 | 
			
		||||
 
 | 
			
		||||
@@ -26,8 +26,10 @@ EthernetComponent::EthernetComponent() { global_eth_component = this; }
 | 
			
		||||
 | 
			
		||||
void EthernetComponent::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Ethernet...");
 | 
			
		||||
  // Delay here to allow power to stabilise before Ethernet is initialised.
 | 
			
		||||
  delay(300);  // NOLINT
 | 
			
		||||
  if (esp_reset_reason() != ESP_RST_DEEPSLEEP) {
 | 
			
		||||
    // Delay here to allow power to stabilise before Ethernet is initialized.
 | 
			
		||||
    delay(300);  // NOLINT
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  esp_err_t err;
 | 
			
		||||
  err = esp_netif_init();
 | 
			
		||||
@@ -52,26 +54,29 @@ void EthernetComponent::setup() {
 | 
			
		||||
 | 
			
		||||
  esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
 | 
			
		||||
 | 
			
		||||
  esp_eth_phy_t *phy;
 | 
			
		||||
  switch (this->type_) {
 | 
			
		||||
    case ETHERNET_TYPE_LAN8720: {
 | 
			
		||||
      phy = esp_eth_phy_new_lan87xx(&phy_config);
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_lan87xx(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ETHERNET_TYPE_RTL8201: {
 | 
			
		||||
      phy = esp_eth_phy_new_rtl8201(&phy_config);
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_rtl8201(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ETHERNET_TYPE_DP83848: {
 | 
			
		||||
      phy = esp_eth_phy_new_dp83848(&phy_config);
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_dp83848(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ETHERNET_TYPE_IP101: {
 | 
			
		||||
      phy = esp_eth_phy_new_ip101(&phy_config);
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_ip101(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ETHERNET_TYPE_JL1101: {
 | 
			
		||||
      phy = esp_eth_phy_new_jl1101(&phy_config);
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_jl1101(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ETHERNET_TYPE_KSZ8081: {
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_ksz8081(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    default: {
 | 
			
		||||
@@ -80,7 +85,7 @@ void EthernetComponent::setup() {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
 | 
			
		||||
  esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, this->phy_);
 | 
			
		||||
  this->eth_handle_ = nullptr;
 | 
			
		||||
  err = esp_eth_driver_install(ð_config, &this->eth_handle_);
 | 
			
		||||
  ESPHL_ERROR_CHECK(err, "ETH driver install error");
 | 
			
		||||
@@ -140,7 +145,7 @@ void EthernetComponent::loop() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EthernetComponent::dump_config() {
 | 
			
		||||
  std::string eth_type;
 | 
			
		||||
  const char *eth_type;
 | 
			
		||||
  switch (this->type_) {
 | 
			
		||||
    case ETHERNET_TYPE_LAN8720:
 | 
			
		||||
      eth_type = "LAN8720";
 | 
			
		||||
@@ -158,6 +163,14 @@ void EthernetComponent::dump_config() {
 | 
			
		||||
      eth_type = "IP101";
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case ETHERNET_TYPE_JL1101:
 | 
			
		||||
      eth_type = "JL1101";
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case ETHERNET_TYPE_KSZ8081:
 | 
			
		||||
      eth_type = "KSZ8081";
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      eth_type = "Unknown";
 | 
			
		||||
      break;
 | 
			
		||||
@@ -170,7 +183,8 @@ void EthernetComponent::dump_config() {
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  MDC Pin: %u", this->mdc_pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  MDIO Pin: %u", this->mdio_pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Type: %s", eth_type.c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Type: %s", eth_type);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  PHY addr: %u", this->phy_addr_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; }
 | 
			
		||||
@@ -255,14 +269,22 @@ void EthernetComponent::start_connect_() {
 | 
			
		||||
  if (this->manual_ip_.has_value()) {
 | 
			
		||||
    if (uint32_t(this->manual_ip_->dns1) != 0) {
 | 
			
		||||
      ip_addr_t d;
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
      d.type = IPADDR_TYPE_V4;
 | 
			
		||||
      d.u_addr.ip4.addr = static_cast<uint32_t>(this->manual_ip_->dns1);
 | 
			
		||||
#else
 | 
			
		||||
      d.addr = static_cast<uint32_t>(this->manual_ip_->dns1);
 | 
			
		||||
#endif
 | 
			
		||||
      dns_setserver(0, &d);
 | 
			
		||||
    }
 | 
			
		||||
    if (uint32_t(this->manual_ip_->dns1) != 0) {
 | 
			
		||||
    if (uint32_t(this->manual_ip_->dns2) != 0) {
 | 
			
		||||
      ip_addr_t d;
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
      d.type = IPADDR_TYPE_V4;
 | 
			
		||||
      d.u_addr.ip4.addr = static_cast<uint32_t>(this->manual_ip_->dns2);
 | 
			
		||||
#else
 | 
			
		||||
      d.addr = static_cast<uint32_t>(this->manual_ip_->dns2);
 | 
			
		||||
#endif
 | 
			
		||||
      dns_setserver(1, &d);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
@@ -289,8 +311,13 @@ void EthernetComponent::dump_connect_params_() {
 | 
			
		||||
  const ip_addr_t *dns_ip1 = dns_getserver(0);
 | 
			
		||||
  const ip_addr_t *dns_ip2 = dns_getserver(1);
 | 
			
		||||
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  DNS1: %s", network::IPAddress(dns_ip1->u_addr.ip4.addr).str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  DNS2: %s", network::IPAddress(dns_ip2->u_addr.ip4.addr).str().c_str());
 | 
			
		||||
#else
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  DNS1: %s", network::IPAddress(dns_ip1->addr).str().c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  DNS2: %s", network::IPAddress(dns_ip2->addr).str().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  esp_err_t err;
 | 
			
		||||
 | 
			
		||||
@@ -330,6 +357,21 @@ std::string EthernetComponent::get_use_address() const {
 | 
			
		||||
 | 
			
		||||
void EthernetComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; }
 | 
			
		||||
 | 
			
		||||
bool EthernetComponent::powerdown() {
 | 
			
		||||
  ESP_LOGI(TAG, "Powering down ethernet PHY");
 | 
			
		||||
  if (this->phy_ == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "Ethernet PHY not assigned");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  this->connected_ = false;
 | 
			
		||||
  this->started_ = false;
 | 
			
		||||
  if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Error powering down ethernet PHY");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ethernet
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,11 +14,13 @@ namespace esphome {
 | 
			
		||||
namespace ethernet {
 | 
			
		||||
 | 
			
		||||
enum EthernetType {
 | 
			
		||||
  ETHERNET_TYPE_LAN8720 = 0,
 | 
			
		||||
  ETHERNET_TYPE_UNKNOWN = 0,
 | 
			
		||||
  ETHERNET_TYPE_LAN8720,
 | 
			
		||||
  ETHERNET_TYPE_RTL8201,
 | 
			
		||||
  ETHERNET_TYPE_DP83848,
 | 
			
		||||
  ETHERNET_TYPE_IP101,
 | 
			
		||||
  ETHERNET_TYPE_JL1101,
 | 
			
		||||
  ETHERNET_TYPE_KSZ8081,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ManualIP {
 | 
			
		||||
@@ -43,6 +45,7 @@ class EthernetComponent : public Component {
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  bool can_proceed() override;
 | 
			
		||||
  void on_shutdown() override { powerdown(); }
 | 
			
		||||
  bool is_connected();
 | 
			
		||||
 | 
			
		||||
  void set_phy_addr(uint8_t phy_addr);
 | 
			
		||||
@@ -56,6 +59,7 @@ class EthernetComponent : public Component {
 | 
			
		||||
  network::IPAddress get_ip_address();
 | 
			
		||||
  std::string get_use_address() const;
 | 
			
		||||
  void set_use_address(const std::string &use_address);
 | 
			
		||||
  bool powerdown();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
 | 
			
		||||
@@ -69,7 +73,7 @@ class EthernetComponent : public Component {
 | 
			
		||||
  int power_pin_{-1};
 | 
			
		||||
  uint8_t mdc_pin_{23};
 | 
			
		||||
  uint8_t mdio_pin_{18};
 | 
			
		||||
  EthernetType type_{ETHERNET_TYPE_LAN8720};
 | 
			
		||||
  EthernetType type_{ETHERNET_TYPE_UNKNOWN};
 | 
			
		||||
  emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN};
 | 
			
		||||
  emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO};
 | 
			
		||||
  optional<ManualIP> manual_ip_{};
 | 
			
		||||
@@ -80,6 +84,7 @@ class EthernetComponent : public Component {
 | 
			
		||||
  uint32_t connect_begin_;
 | 
			
		||||
  esp_netif_t *eth_netif_{nullptr};
 | 
			
		||||
  esp_eth_handle_t eth_handle_;
 | 
			
		||||
  esp_eth_phy_t *phy_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 
 | 
			
		||||
@@ -12,14 +12,14 @@ static const char *const TAG = "ezo.sensor";
 | 
			
		||||
 | 
			
		||||
enum EzoCommandType : uint8_t {
 | 
			
		||||
  EZO_READ = 0,
 | 
			
		||||
  EZO_LED = 1,
 | 
			
		||||
  EZO_DEVICE_INFORMATION = 2,
 | 
			
		||||
  EZO_SLOPE = 3,
 | 
			
		||||
  EZO_LED,
 | 
			
		||||
  EZO_DEVICE_INFORMATION,
 | 
			
		||||
  EZO_SLOPE,
 | 
			
		||||
  EZO_CALIBRATION,
 | 
			
		||||
  EZO_SLEEP = 4,
 | 
			
		||||
  EZO_I2C = 5,
 | 
			
		||||
  EZO_T = 6,
 | 
			
		||||
  EZO_CUSTOM = 7
 | 
			
		||||
  EZO_SLEEP,
 | 
			
		||||
  EZO_I2C,
 | 
			
		||||
  EZO_T,
 | 
			
		||||
  EZO_CUSTOM
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum EzoCalibrationType : uint8_t { EZO_CAL_LOW = 0, EZO_CAL_MID = 1, EZO_CAL_HIGH = 2 };
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ FanIsOffCondition = fan_ns.class_("FanIsOffCondition", automation.Condition.temp
 | 
			
		||||
FAN_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(Fan),
 | 
			
		||||
        cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum(
 | 
			
		||||
        cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
 | 
			
		||||
            RESTORE_MODES, upper=True, space="_"
 | 
			
		||||
        ),
 | 
			
		||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent),
 | 
			
		||||
 
 | 
			
		||||
@@ -80,9 +80,6 @@ void FanRestoreState::apply(Fan &fan) {
 | 
			
		||||
  fan.publish_state();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Fan::Fan() : EntityBase("") {}
 | 
			
		||||
Fan::Fan(const std::string &name) : EntityBase(name) {}
 | 
			
		||||
 | 
			
		||||
FanCall Fan::turn_on() { return this->make_call().set_state(true); }
 | 
			
		||||
FanCall Fan::turn_off() { return this->make_call().set_state(false); }
 | 
			
		||||
FanCall Fan::toggle() { return this->make_call().set_state(!this->state); }
 | 
			
		||||
 
 | 
			
		||||
@@ -99,10 +99,6 @@ struct FanRestoreState {
 | 
			
		||||
 | 
			
		||||
class Fan : public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  Fan();
 | 
			
		||||
  /// Construct the fan with name.
 | 
			
		||||
  explicit Fan(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /// The current on/off state of the fan.
 | 
			
		||||
  bool state{false};
 | 
			
		||||
  /// The current oscillation state of the fan.
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@ enum ESPDEPRECATED("LegacyFanDirection members are deprecated, use FanDirection
 | 
			
		||||
class ESPDEPRECATED("FanState is deprecated, use Fan instead.", "2022.2") FanState : public Fan, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  FanState() = default;
 | 
			
		||||
  explicit FanState(const std::string &name) : Fan(name) {}
 | 
			
		||||
 | 
			
		||||
  /// Get the traits of this fan.
 | 
			
		||||
  FanTraits get_traits() override { return this->traits_; }
 | 
			
		||||
 
 | 
			
		||||
@@ -122,11 +122,18 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Adjust limits to nice y_per_div boundaries
 | 
			
		||||
  int yn = int(ymin / y_per_div);
 | 
			
		||||
  int ym = int(ymax / y_per_div) + int(1 * (fmodf(ymax, y_per_div) != 0));
 | 
			
		||||
  ymin = yn * y_per_div;
 | 
			
		||||
  ymax = ym * y_per_div;
 | 
			
		||||
  yrange = ymax - ymin;
 | 
			
		||||
  int yn = 0;
 | 
			
		||||
  int ym = 1;
 | 
			
		||||
  if (!std::isnan(ymin) && !std::isnan(ymax)) {
 | 
			
		||||
    yn = (int) floorf(ymin / y_per_div);
 | 
			
		||||
    ym = (int) ceilf(ymax / y_per_div);
 | 
			
		||||
    if (yn == ym) {
 | 
			
		||||
      ym++;
 | 
			
		||||
    }
 | 
			
		||||
    ymin = yn * y_per_div;
 | 
			
		||||
    ymax = ym * y_per_div;
 | 
			
		||||
    yrange = ymax - ymin;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Draw grid
 | 
			
		||||
  if (!std::isnan(this->gridspacing_y_)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -154,18 +154,25 @@ ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cn
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  uint8_t status = wire_->endTransmission(stop);
 | 
			
		||||
  if (status == 0) {
 | 
			
		||||
    return ERROR_OK;
 | 
			
		||||
  } else if (status == 1) {
 | 
			
		||||
    // transmit buffer not large enough
 | 
			
		||||
    ESP_LOGVV(TAG, "TX failed: buffer not large enough");
 | 
			
		||||
    return ERROR_UNKNOWN;
 | 
			
		||||
  } else if (status == 2 || status == 3) {
 | 
			
		||||
    ESP_LOGVV(TAG, "TX failed: not acknowledged");
 | 
			
		||||
    return ERROR_NOT_ACKNOWLEDGED;
 | 
			
		||||
  switch (status) {
 | 
			
		||||
    case 0:
 | 
			
		||||
      return ERROR_OK;
 | 
			
		||||
    case 1:
 | 
			
		||||
      // transmit buffer not large enough
 | 
			
		||||
      ESP_LOGVV(TAG, "TX failed: buffer not large enough");
 | 
			
		||||
      return ERROR_UNKNOWN;
 | 
			
		||||
    case 2:
 | 
			
		||||
    case 3:
 | 
			
		||||
      ESP_LOGVV(TAG, "TX failed: not acknowledged");
 | 
			
		||||
      return ERROR_NOT_ACKNOWLEDGED;
 | 
			
		||||
    case 5:
 | 
			
		||||
      ESP_LOGVV(TAG, "TX failed: timeout");
 | 
			
		||||
      return ERROR_UNKNOWN;
 | 
			
		||||
    case 4:
 | 
			
		||||
    default:
 | 
			
		||||
      ESP_LOGVV(TAG, "TX failed: unknown error %u", status);
 | 
			
		||||
      return ERROR_UNKNOWN;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGVV(TAG, "TX failed: unknown error %u", status);
 | 
			
		||||
  return ERROR_UNKNOWN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Perform I2C bus recovery, see:
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace i2c {
 | 
			
		||||
@@ -47,7 +48,7 @@ void IDFI2CBus::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "I2C Bus:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  SDA Pin: GPIO%u", this->sda_pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  SCL Pin: GPIO%u", this->scl_pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Frequency: %u Hz", this->frequency_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Frequency: %" PRIu32 " Hz", this->frequency_);
 | 
			
		||||
  switch (this->recovery_result_) {
 | 
			
		||||
    case RECOVERY_COMPLETED:
 | 
			
		||||
      ESP_LOGCONFIG(TAG, "  Recovery: bus successfully recovered");
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.final_validate as fv
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
from esphome.components.esp32 import get_esp32_variant
 | 
			
		||||
from esphome.components.esp32.const import (
 | 
			
		||||
    VARIANT_ESP32,
 | 
			
		||||
    VARIANT_ESP32S2,
 | 
			
		||||
    VARIANT_ESP32S3,
 | 
			
		||||
    VARIANT_ESP32C3,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
DEPENDENCIES = ["esp32"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
CONF_I2S_DOUT_PIN = "i2s_dout_pin"
 | 
			
		||||
CONF_I2S_DIN_PIN = "i2s_din_pin"
 | 
			
		||||
CONF_I2S_BCLK_PIN = "i2s_bclk_pin"
 | 
			
		||||
CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin"
 | 
			
		||||
 | 
			
		||||
CONF_I2S_AUDIO = "i2s_audio"
 | 
			
		||||
CONF_I2S_AUDIO_ID = "i2s_audio_id"
 | 
			
		||||
 | 
			
		||||
i2s_audio_ns = cg.esphome_ns.namespace("i2s_audio")
 | 
			
		||||
I2SAudioComponent = i2s_audio_ns.class_("I2SAudioComponent", cg.Component)
 | 
			
		||||
I2SAudioIn = i2s_audio_ns.class_("I2SAudioIn", cg.Parented.template(I2SAudioComponent))
 | 
			
		||||
I2SAudioOut = i2s_audio_ns.class_(
 | 
			
		||||
    "I2SAudioOut", cg.Parented.template(I2SAudioComponent)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/{variant}/include/soc/soc_caps.h
 | 
			
		||||
I2S_PORTS = {
 | 
			
		||||
    VARIANT_ESP32: 2,
 | 
			
		||||
    VARIANT_ESP32S2: 1,
 | 
			
		||||
    VARIANT_ESP32S3: 2,
 | 
			
		||||
    VARIANT_ESP32C3: 1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(I2SAudioComponent),
 | 
			
		||||
        cv.Required(CONF_I2S_BCLK_PIN): pins.internal_gpio_output_pin_number,
 | 
			
		||||
        cv.Required(CONF_I2S_LRCLK_PIN): pins.internal_gpio_output_pin_number,
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _final_validate(_):
 | 
			
		||||
    i2s_audio_configs = fv.full_config.get()[CONF_I2S_AUDIO]
 | 
			
		||||
    variant = get_esp32_variant()
 | 
			
		||||
    if variant not in I2S_PORTS:
 | 
			
		||||
        raise cv.Invalid(f"Unsupported variant {variant}")
 | 
			
		||||
    if len(i2s_audio_configs) > I2S_PORTS[variant]:
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            f"Only {I2S_PORTS[variant]} I2S audio ports are supported on {variant}"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = _final_validate
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_bclk_pin(config[CONF_I2S_BCLK_PIN]))
 | 
			
		||||
    cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN]))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								esphome/components/i2s_audio/i2s_audio.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								esphome/components/i2s_audio/i2s_audio.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
#include "i2s_audio.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace i2s_audio {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "i2s_audio";
 | 
			
		||||
 | 
			
		||||
void I2SAudioComponent::setup() {
 | 
			
		||||
  static i2s_port_t next_port_num = I2S_NUM_0;
 | 
			
		||||
 | 
			
		||||
  if (next_port_num >= I2S_NUM_MAX) {
 | 
			
		||||
    ESP_LOGE(TAG, "Too many I2S Audio components!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->port_ = next_port_num;
 | 
			
		||||
  next_port_num = (i2s_port_t) (next_port_num + 1);
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up I2S Audio...");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace i2s_audio
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
							
								
								
									
										64
									
								
								esphome/components/i2s_audio/i2s_audio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								esphome/components/i2s_audio/i2s_audio.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include <driver/i2s.h>
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace i2s_audio {
 | 
			
		||||
 | 
			
		||||
class I2SAudioComponent;
 | 
			
		||||
 | 
			
		||||
class I2SAudioIn : public Parented<I2SAudioComponent> {};
 | 
			
		||||
 | 
			
		||||
class I2SAudioOut : public Parented<I2SAudioComponent> {};
 | 
			
		||||
 | 
			
		||||
class I2SAudioComponent : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
 | 
			
		||||
  void register_audio_in(I2SAudioIn *in) {
 | 
			
		||||
    this->audio_in_ = in;
 | 
			
		||||
    in->set_parent(this);
 | 
			
		||||
  }
 | 
			
		||||
  void register_audio_out(I2SAudioOut *out) {
 | 
			
		||||
    this->audio_out_ = out;
 | 
			
		||||
    out->set_parent(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  i2s_pin_config_t get_pin_config() const {
 | 
			
		||||
    return {
 | 
			
		||||
        .mck_io_num = I2S_PIN_NO_CHANGE,
 | 
			
		||||
        .bck_io_num = this->bclk_pin_,
 | 
			
		||||
        .ws_io_num = this->lrclk_pin_,
 | 
			
		||||
        .data_out_num = I2S_PIN_NO_CHANGE,
 | 
			
		||||
        .data_in_num = I2S_PIN_NO_CHANGE,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_bclk_pin(uint8_t pin) { this->bclk_pin_ = pin; }
 | 
			
		||||
  void set_lrclk_pin(uint8_t pin) { this->lrclk_pin_ = pin; }
 | 
			
		||||
 | 
			
		||||
  void lock() { this->lock_.lock(); }
 | 
			
		||||
  bool try_lock() { return this->lock_.try_lock(); }
 | 
			
		||||
  void unlock() { this->lock_.unlock(); }
 | 
			
		||||
 | 
			
		||||
  i2s_port_t get_port() const { return this->port_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  Mutex lock_;
 | 
			
		||||
 | 
			
		||||
  I2SAudioIn *audio_in_{nullptr};
 | 
			
		||||
  I2SAudioOut *audio_out_{nullptr};
 | 
			
		||||
 | 
			
		||||
  uint8_t bclk_pin_;
 | 
			
		||||
  uint8_t lrclk_pin_;
 | 
			
		||||
  i2s_port_t port_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace i2s_audio
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
@@ -5,22 +5,25 @@ import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
 | 
			
		||||
from esphome.const import CONF_ID, CONF_MODE
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
 | 
			
		||||
from .. import (
 | 
			
		||||
    i2s_audio_ns,
 | 
			
		||||
    I2SAudioComponent,
 | 
			
		||||
    I2SAudioOut,
 | 
			
		||||
    CONF_I2S_AUDIO_ID,
 | 
			
		||||
    CONF_I2S_DOUT_PIN,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
DEPENDENCIES = ["esp32"]
 | 
			
		||||
 | 
			
		||||
i2s_audio_ns = cg.esphome_ns.namespace("i2s_audio")
 | 
			
		||||
DEPENDENCIES = ["i2s_audio"]
 | 
			
		||||
 | 
			
		||||
I2SAudioMediaPlayer = i2s_audio_ns.class_(
 | 
			
		||||
    "I2SAudioMediaPlayer", cg.Component, media_player.MediaPlayer
 | 
			
		||||
    "I2SAudioMediaPlayer", cg.Component, media_player.MediaPlayer, I2SAudioOut
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t")
 | 
			
		||||
 | 
			
		||||
CONF_I2S_DOUT_PIN = "i2s_dout_pin"
 | 
			
		||||
CONF_I2S_BCLK_PIN = "i2s_bclk_pin"
 | 
			
		||||
CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin"
 | 
			
		||||
 | 
			
		||||
CONF_MUTE_PIN = "mute_pin"
 | 
			
		||||
CONF_AUDIO_ID = "audio_id"
 | 
			
		||||
CONF_DAC_TYPE = "dac_type"
 | 
			
		||||
@@ -48,34 +51,26 @@ def validate_esp32_variant(config):
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    cv.typed_schema(
 | 
			
		||||
        {
 | 
			
		||||
            "internal": cv.Schema(
 | 
			
		||||
            "internal": media_player.MEDIA_PLAYER_SCHEMA.extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer),
 | 
			
		||||
                    cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
 | 
			
		||||
                    cv.Required(CONF_MODE): cv.enum(INTERNAL_DAC_OPTIONS, lower=True),
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
            .extend(media_player.MEDIA_PLAYER_SCHEMA)
 | 
			
		||||
            .extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
            "external": cv.Schema(
 | 
			
		||||
            ).extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
            "external": media_player.MEDIA_PLAYER_SCHEMA.extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer),
 | 
			
		||||
                    cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
 | 
			
		||||
                    cv.Required(
 | 
			
		||||
                        CONF_I2S_DOUT_PIN
 | 
			
		||||
                    ): pins.internal_gpio_output_pin_number,
 | 
			
		||||
                    cv.Required(
 | 
			
		||||
                        CONF_I2S_BCLK_PIN
 | 
			
		||||
                    ): pins.internal_gpio_output_pin_number,
 | 
			
		||||
                    cv.Required(
 | 
			
		||||
                        CONF_I2S_LRCLK_PIN
 | 
			
		||||
                    ): pins.internal_gpio_output_pin_number,
 | 
			
		||||
                    cv.Optional(CONF_MUTE_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
                    cv.Optional(CONF_MODE, default="mono"): cv.one_of(
 | 
			
		||||
                        *EXTERNAL_DAC_OPTIONS, lower=True
 | 
			
		||||
                    ),
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
            .extend(media_player.MEDIA_PLAYER_SCHEMA)
 | 
			
		||||
            .extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
            ).extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
        },
 | 
			
		||||
        key=CONF_DAC_TYPE,
 | 
			
		||||
    ),
 | 
			
		||||
@@ -89,19 +84,19 @@ async def to_code(config):
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await media_player.register_media_player(var, config)
 | 
			
		||||
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_I2S_AUDIO_ID])
 | 
			
		||||
    cg.add(parent.register_audio_out(var))
 | 
			
		||||
 | 
			
		||||
    if config[CONF_DAC_TYPE] == "internal":
 | 
			
		||||
        cg.add(var.set_internal_dac_mode(config[CONF_MODE]))
 | 
			
		||||
    else:
 | 
			
		||||
        cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN]))
 | 
			
		||||
        cg.add(var.set_bclk_pin(config[CONF_I2S_BCLK_PIN]))
 | 
			
		||||
        cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN]))
 | 
			
		||||
        if CONF_MUTE_PIN in config:
 | 
			
		||||
            pin = await cg.gpio_pin_expression(config[CONF_MUTE_PIN])
 | 
			
		||||
            cg.add(var.set_mute_pin(pin))
 | 
			
		||||
        cg.add(var.set_external_dac_channels(2 if config[CONF_MODE] == "stereo" else 1))
 | 
			
		||||
 | 
			
		||||
    if CORE.is_esp32:
 | 
			
		||||
        cg.add_library("WiFiClientSecure", None)
 | 
			
		||||
        cg.add_library("HTTPClient", None)
 | 
			
		||||
        cg.add_library("esphome/ESP32-audioI2S", "2.0.6")
 | 
			
		||||
        cg.add_build_flag("-DAUDIO_NO_SD_FS")
 | 
			
		||||
    cg.add_library("WiFiClientSecure", None)
 | 
			
		||||
    cg.add_library("HTTPClient", None)
 | 
			
		||||
    cg.add_library("esphome/ESP32-audioI2S", "2.0.6")
 | 
			
		||||
    cg.add_build_flag("-DAUDIO_NO_SD_FS")
 | 
			
		||||
@@ -11,11 +11,19 @@ static const char *const TAG = "audio";
 | 
			
		||||
 | 
			
		||||
void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
 | 
			
		||||
  if (call.get_media_url().has_value()) {
 | 
			
		||||
    if (this->audio_->isRunning())
 | 
			
		||||
      this->audio_->stopSong();
 | 
			
		||||
    this->high_freq_.start();
 | 
			
		||||
    this->audio_->connecttohost(call.get_media_url().value().c_str());
 | 
			
		||||
    this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
 | 
			
		||||
    this->current_url_ = call.get_media_url();
 | 
			
		||||
 | 
			
		||||
    if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && this->audio_ != nullptr) {
 | 
			
		||||
      if (this->audio_->isRunning()) {
 | 
			
		||||
        this->audio_->stopSong();
 | 
			
		||||
      }
 | 
			
		||||
      this->audio_->connecttohost(this->current_url_.value().c_str());
 | 
			
		||||
    } else {
 | 
			
		||||
      this->start();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (this->i2s_state_ != I2S_STATE_RUNNING) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (call.get_volume().has_value()) {
 | 
			
		||||
    this->volume = call.get_volume().value();
 | 
			
		||||
@@ -35,7 +43,7 @@ void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
 | 
			
		||||
        this->state = media_player::MEDIA_PLAYER_STATE_PAUSED;
 | 
			
		||||
        break;
 | 
			
		||||
      case media_player::MEDIA_PLAYER_COMMAND_STOP:
 | 
			
		||||
        this->stop_();
 | 
			
		||||
        this->stop();
 | 
			
		||||
        break;
 | 
			
		||||
      case media_player::MEDIA_PLAYER_COMMAND_MUTE:
 | 
			
		||||
        this->mute_();
 | 
			
		||||
@@ -94,22 +102,51 @@ void I2SAudioMediaPlayer::set_volume_(float volume, bool publish) {
 | 
			
		||||
    this->volume = volume;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I2SAudioMediaPlayer::stop_() {
 | 
			
		||||
  if (this->audio_->isRunning())
 | 
			
		||||
    this->audio_->stopSong();
 | 
			
		||||
  this->high_freq_.stop();
 | 
			
		||||
void I2SAudioMediaPlayer::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Audio...");
 | 
			
		||||
  this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I2SAudioMediaPlayer::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Audio...");
 | 
			
		||||
void I2SAudioMediaPlayer::loop() {
 | 
			
		||||
  switch (this->i2s_state_) {
 | 
			
		||||
    case I2S_STATE_STARTING:
 | 
			
		||||
      this->start_();
 | 
			
		||||
      break;
 | 
			
		||||
    case I2S_STATE_RUNNING:
 | 
			
		||||
      this->play_();
 | 
			
		||||
      break;
 | 
			
		||||
    case I2S_STATE_STOPPING:
 | 
			
		||||
      this->stop_();
 | 
			
		||||
      break;
 | 
			
		||||
    case I2S_STATE_STOPPED:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I2SAudioMediaPlayer::play_() {
 | 
			
		||||
  this->audio_->loop();
 | 
			
		||||
  if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && !this->audio_->isRunning()) {
 | 
			
		||||
    this->stop();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I2SAudioMediaPlayer::start() { this->i2s_state_ = I2S_STATE_STARTING; }
 | 
			
		||||
void I2SAudioMediaPlayer::start_() {
 | 
			
		||||
  if (this->parent_->try_lock()) {
 | 
			
		||||
    return;  // Waiting for another i2s to return lock
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#if SOC_I2S_SUPPORTS_DAC
 | 
			
		||||
  if (this->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) {
 | 
			
		||||
    this->audio_ = make_unique<Audio>(true, this->internal_dac_mode_);
 | 
			
		||||
    this->audio_ = make_unique<Audio>(true, this->internal_dac_mode_, this->parent_->get_port());
 | 
			
		||||
  } else {
 | 
			
		||||
#endif
 | 
			
		||||
    this->audio_ = make_unique<Audio>(false);
 | 
			
		||||
    this->audio_->setPinout(this->bclk_pin_, this->lrclk_pin_, this->dout_pin_);
 | 
			
		||||
    this->audio_ = make_unique<Audio>(false, I2S_DAC_CHANNEL_BOTH_EN, this->parent_->get_port());
 | 
			
		||||
 | 
			
		||||
    i2s_pin_config_t pin_config = this->parent_->get_pin_config();
 | 
			
		||||
    pin_config.data_out_num = this->dout_pin_;
 | 
			
		||||
    i2s_set_pin(this->parent_->get_port(), &pin_config);
 | 
			
		||||
 | 
			
		||||
    this->audio_->forceMono(this->external_dac_channels_ == 1);
 | 
			
		||||
    if (this->mute_pin_ != nullptr) {
 | 
			
		||||
      this->mute_pin_->setup();
 | 
			
		||||
@@ -118,16 +155,30 @@ void I2SAudioMediaPlayer::setup() {
 | 
			
		||||
#if SOC_I2S_SUPPORTS_DAC
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I2SAudioMediaPlayer::loop() {
 | 
			
		||||
  this->audio_->loop();
 | 
			
		||||
  if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && !this->audio_->isRunning()) {
 | 
			
		||||
    this->stop_();
 | 
			
		||||
  this->i2s_state_ = I2S_STATE_RUNNING;
 | 
			
		||||
  this->high_freq_.start();
 | 
			
		||||
  if (this->current_url_.has_value()) {
 | 
			
		||||
    this->audio_->connecttohost(this->current_url_.value().c_str());
 | 
			
		||||
    this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
 | 
			
		||||
    this->publish_state();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void I2SAudioMediaPlayer::stop() { this->i2s_state_ = I2S_STATE_STOPPING; }
 | 
			
		||||
void I2SAudioMediaPlayer::stop_() {
 | 
			
		||||
  if (this->audio_->isRunning()) {
 | 
			
		||||
    this->audio_->stopSong();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->audio_ = nullptr;
 | 
			
		||||
  this->current_url_ = {};
 | 
			
		||||
  this->parent_->unlock();
 | 
			
		||||
  this->i2s_state_ = I2S_STATE_STOPPED;
 | 
			
		||||
 | 
			
		||||
  this->high_freq_.stop();
 | 
			
		||||
  this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
 | 
			
		||||
  this->publish_state();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
media_player::MediaPlayerTraits I2SAudioMediaPlayer::get_traits() {
 | 
			
		||||
  auto traits = media_player::MediaPlayerTraits();
 | 
			
		||||
@@ -2,6 +2,10 @@
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
 | 
			
		||||
 | 
			
		||||
#include "../i2s_audio.h"
 | 
			
		||||
 | 
			
		||||
#include <driver/i2s.h>
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/media_player/media_player.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/gpio.h"
 | 
			
		||||
@@ -12,7 +16,14 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace i2s_audio {
 | 
			
		||||
 | 
			
		||||
class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer {
 | 
			
		||||
enum I2SState : uint8_t {
 | 
			
		||||
  I2S_STATE_STOPPED = 0,
 | 
			
		||||
  I2S_STATE_STARTING,
 | 
			
		||||
  I2S_STATE_RUNNING,
 | 
			
		||||
  I2S_STATE_STOPPING,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer, public I2SAudioOut {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  float get_setup_priority() const override { return esphome::setup_priority::LATE; }
 | 
			
		||||
@@ -22,8 +33,6 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer {
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  void set_dout_pin(uint8_t pin) { this->dout_pin_ = pin; }
 | 
			
		||||
  void set_bclk_pin(uint8_t pin) { this->bclk_pin_ = pin; }
 | 
			
		||||
  void set_lrclk_pin(uint8_t pin) { this->lrclk_pin_ = pin; }
 | 
			
		||||
  void set_mute_pin(GPIOPin *mute_pin) { this->mute_pin_ = mute_pin; }
 | 
			
		||||
#if SOC_I2S_SUPPORTS_DAC
 | 
			
		||||
  void set_internal_dac_mode(i2s_dac_mode_t mode) { this->internal_dac_mode_ = mode; }
 | 
			
		||||
@@ -34,20 +43,24 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer {
 | 
			
		||||
 | 
			
		||||
  bool is_muted() const override { return this->muted_; }
 | 
			
		||||
 | 
			
		||||
  void start();
 | 
			
		||||
  void stop();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void control(const media_player::MediaPlayerCall &call) override;
 | 
			
		||||
 | 
			
		||||
  void mute_();
 | 
			
		||||
  void unmute_();
 | 
			
		||||
  void set_volume_(float volume, bool publish = true);
 | 
			
		||||
  void stop_();
 | 
			
		||||
 | 
			
		||||
  void start_();
 | 
			
		||||
  void stop_();
 | 
			
		||||
  void play_();
 | 
			
		||||
 | 
			
		||||
  I2SState i2s_state_{I2S_STATE_STOPPED};
 | 
			
		||||
  std::unique_ptr<Audio> audio_;
 | 
			
		||||
 | 
			
		||||
  uint8_t dout_pin_{0};
 | 
			
		||||
  uint8_t din_pin_{0};
 | 
			
		||||
  uint8_t bclk_pin_;
 | 
			
		||||
  uint8_t lrclk_pin_;
 | 
			
		||||
 | 
			
		||||
  GPIOPin *mute_pin_{nullptr};
 | 
			
		||||
  bool muted_{false};
 | 
			
		||||
@@ -59,6 +72,8 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer {
 | 
			
		||||
  uint8_t external_dac_channels_;
 | 
			
		||||
 | 
			
		||||
  HighFrequencyLoopRequester high_freq_;
 | 
			
		||||
 | 
			
		||||
  optional<std::string> current_url_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace i2s_audio
 | 
			
		||||
							
								
								
									
										41
									
								
								esphome/components/i2s_audio/microphone/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								esphome/components/i2s_audio/microphone/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
from esphome.components import microphone
 | 
			
		||||
 | 
			
		||||
from .. import (
 | 
			
		||||
    i2s_audio_ns,
 | 
			
		||||
    I2SAudioComponent,
 | 
			
		||||
    I2SAudioIn,
 | 
			
		||||
    CONF_I2S_AUDIO_ID,
 | 
			
		||||
    CONF_I2S_DIN_PIN,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
DEPENDENCIES = ["i2s_audio"]
 | 
			
		||||
 | 
			
		||||
I2SAudioMicrophone = i2s_audio_ns.class_(
 | 
			
		||||
    "I2SAudioMicrophone", I2SAudioIn, microphone.Microphone, cg.Component
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = microphone.MICROPHONE_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(I2SAudioMicrophone),
 | 
			
		||||
        cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
 | 
			
		||||
        cv.Required(CONF_I2S_DIN_PIN): pins.internal_gpio_input_pin_number,
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_I2S_AUDIO_ID])
 | 
			
		||||
    cg.add(parent.register_audio_in(var))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN]))
 | 
			
		||||
 | 
			
		||||
    await microphone.register_microphone(var, config)
 | 
			
		||||
							
								
								
									
										101
									
								
								esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
			
		||||
#include "i2s_audio_microphone.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include <driver/i2s.h>
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace i2s_audio {
 | 
			
		||||
 | 
			
		||||
static const size_t BUFFER_SIZE = 512;
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "i2s_audio.microphone";
 | 
			
		||||
 | 
			
		||||
void I2SAudioMicrophone::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone...");
 | 
			
		||||
  this->buffer_.resize(BUFFER_SIZE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I2SAudioMicrophone::start() { this->state_ = microphone::STATE_STARTING; }
 | 
			
		||||
void I2SAudioMicrophone::start_() {
 | 
			
		||||
  if (!this->parent_->try_lock()) {
 | 
			
		||||
    return;  // Waiting for another i2s to return lock
 | 
			
		||||
  }
 | 
			
		||||
  i2s_driver_config_t config = {
 | 
			
		||||
      .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
 | 
			
		||||
      .sample_rate = 16000,
 | 
			
		||||
      .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
 | 
			
		||||
      .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
 | 
			
		||||
      .communication_format = I2S_COMM_FORMAT_STAND_I2S,
 | 
			
		||||
      .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
 | 
			
		||||
      .dma_buf_count = 4,
 | 
			
		||||
      .dma_buf_len = 256,
 | 
			
		||||
      .use_apll = false,
 | 
			
		||||
      .tx_desc_auto_clear = false,
 | 
			
		||||
      .fixed_mclk = 0,
 | 
			
		||||
      .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT,
 | 
			
		||||
      .bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
 | 
			
		||||
 | 
			
		||||
  i2s_pin_config_t pin_config = this->parent_->get_pin_config();
 | 
			
		||||
  pin_config.data_in_num = this->din_pin_;
 | 
			
		||||
 | 
			
		||||
  i2s_set_pin(this->parent_->get_port(), &pin_config);
 | 
			
		||||
  this->state_ = microphone::STATE_RUNNING;
 | 
			
		||||
  this->high_freq_.start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I2SAudioMicrophone::stop() {
 | 
			
		||||
  if (this->state_ == microphone::STATE_STOPPED)
 | 
			
		||||
    return;
 | 
			
		||||
  this->state_ = microphone::STATE_STOPPING;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I2SAudioMicrophone::stop_() {
 | 
			
		||||
  i2s_stop(this->parent_->get_port());
 | 
			
		||||
  i2s_driver_uninstall(this->parent_->get_port());
 | 
			
		||||
  this->parent_->unlock();
 | 
			
		||||
  this->state_ = microphone::STATE_STOPPED;
 | 
			
		||||
  this->high_freq_.stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I2SAudioMicrophone::read_() {
 | 
			
		||||
  size_t bytes_read = 0;
 | 
			
		||||
  esp_err_t err =
 | 
			
		||||
      i2s_read(this->parent_->get_port(), this->buffer_.data(), BUFFER_SIZE, &bytes_read, (100 / portTICK_PERIOD_MS));
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
 | 
			
		||||
  this->data_callbacks_.call(this->buffer_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void I2SAudioMicrophone::loop() {
 | 
			
		||||
  switch (this->state_) {
 | 
			
		||||
    case microphone::STATE_STOPPED:
 | 
			
		||||
      break;
 | 
			
		||||
    case microphone::STATE_STARTING:
 | 
			
		||||
      this->start_();
 | 
			
		||||
      break;
 | 
			
		||||
    case microphone::STATE_RUNNING:
 | 
			
		||||
      this->read_();
 | 
			
		||||
      break;
 | 
			
		||||
    case microphone::STATE_STOPPING:
 | 
			
		||||
      this->stop_();
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace i2s_audio
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
@@ -0,0 +1,37 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include "../i2s_audio.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/microphone/microphone.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace i2s_audio {
 | 
			
		||||
 | 
			
		||||
class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void start() override;
 | 
			
		||||
  void stop() override;
 | 
			
		||||
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void set_din_pin(uint8_t pin) { this->din_pin_ = pin; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void start_();
 | 
			
		||||
  void stop_();
 | 
			
		||||
  void read_();
 | 
			
		||||
 | 
			
		||||
  uint8_t din_pin_{0};
 | 
			
		||||
  std::vector<uint8_t> buffer_;
 | 
			
		||||
 | 
			
		||||
  HighFrequencyLoopRequester high_freq_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace i2s_audio
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
@@ -21,7 +21,7 @@ std::string ImprovBase::get_formatted_next_url_() {
 | 
			
		||||
  // Ip address
 | 
			
		||||
  pos = this->next_url_.find("{{ip_address}}");
 | 
			
		||||
  if (pos != std::string::npos) {
 | 
			
		||||
    std::string ip = network::IPAddress(network::get_ip_address()).str();
 | 
			
		||||
    std::string ip = network::get_ip_address().str();
 | 
			
		||||
    copy.replace(pos, 14, ip);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ LIGHT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ex
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(LightState),
 | 
			
		||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTJSONLightComponent),
 | 
			
		||||
        cv.Optional(CONF_RESTORE_MODE, default="restore_default_off"): cv.enum(
 | 
			
		||||
        cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
 | 
			
		||||
            RESTORE_MODES, upper=True, space="_"
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_TURN_ON): auto.validate_automation(
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ namespace light {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "light";
 | 
			
		||||
 | 
			
		||||
LightState::LightState(const std::string &name, LightOutput *output) : EntityBase(name), output_(output) {}
 | 
			
		||||
LightState::LightState(LightOutput *output) : output_(output) {}
 | 
			
		||||
 | 
			
		||||
LightTraits LightState::get_traits() { return this->output_->get_traits(); }
 | 
			
		||||
 
 | 
			
		||||
@@ -33,9 +33,6 @@ enum LightRestoreMode {
 | 
			
		||||
 */
 | 
			
		||||
class LightState : public EntityBase, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  /// Construct this LightState using the provided traits and name.
 | 
			
		||||
  LightState(const std::string &name, LightOutput *output);
 | 
			
		||||
 | 
			
		||||
  LightState(LightOutput *output);
 | 
			
		||||
 | 
			
		||||
  LightTraits get_traits();
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,7 @@ const char *lock_state_to_string(LockState state) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Lock::Lock(const std::string &name) : EntityBase(name), state(LOCK_STATE_NONE) {}
 | 
			
		||||
Lock::Lock() : Lock("") {}
 | 
			
		||||
Lock::Lock() : state(LOCK_STATE_NONE) {}
 | 
			
		||||
LockCall Lock::make_call() { return LockCall(this); }
 | 
			
		||||
 | 
			
		||||
void Lock::lock() {
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,6 @@ class LockCall {
 | 
			
		||||
class Lock : public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit Lock();
 | 
			
		||||
  explicit Lock(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /** Make a lock device control call, this is used to control the lock device, see the LockCall description
 | 
			
		||||
   * for more info.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "logger.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP_IDF
 | 
			
		||||
#include <driver/uart.h>
 | 
			
		||||
@@ -292,7 +293,7 @@ const char *const UART_SELECTIONS[] = {"UART0", "UART1", "USB_CDC"};
 | 
			
		||||
void Logger::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Logger:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Log Baud Rate: %u", this->baud_rate_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Log Baud Rate: %" PRIu32, this->baud_rate_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Hardware UART: %s", UART_SELECTIONS[this->uart_]);
 | 
			
		||||
  for (auto &it : this->log_levels_) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Level for '%s': %s", it.tag.c_str(), LOG_LEVELS[it.level]);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								esphome/components/microphone/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								esphome/components/microphone/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
from esphome import automation
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
 | 
			
		||||
from esphome.automation import maybe_simple_id
 | 
			
		||||
from esphome.const import CONF_ID, CONF_TRIGGER_ID
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
from esphome.coroutine import coroutine_with_priority
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
 | 
			
		||||
IS_PLATFORM_COMPONENT = True
 | 
			
		||||
 | 
			
		||||
CONF_ON_DATA = "on_data"
 | 
			
		||||
 | 
			
		||||
microphone_ns = cg.esphome_ns.namespace("microphone")
 | 
			
		||||
 | 
			
		||||
Microphone = microphone_ns.class_("Microphone")
 | 
			
		||||
 | 
			
		||||
CaptureAction = microphone_ns.class_(
 | 
			
		||||
    "CaptureAction", automation.Action, cg.Parented.template(Microphone)
 | 
			
		||||
)
 | 
			
		||||
StopCaptureAction = microphone_ns.class_(
 | 
			
		||||
    "StopCaptureAction", automation.Action, cg.Parented.template(Microphone)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DataTrigger = microphone_ns.class_(
 | 
			
		||||
    "DataTrigger",
 | 
			
		||||
    automation.Trigger.template(cg.std_vector.template(cg.int16).operator("ref")),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
IsCapturingCondition = microphone_ns.class_(
 | 
			
		||||
    "IsCapturingCondition", automation.Condition
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def setup_microphone_core_(var, config):
 | 
			
		||||
    for conf in config.get(CONF_ON_DATA, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            trigger,
 | 
			
		||||
            [(cg.std_vector.template(cg.uint8).operator("ref").operator("const"), "x")],
 | 
			
		||||
            conf,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_microphone(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
        var = cg.Pvariable(config[CONF_ID], var)
 | 
			
		||||
    await setup_microphone_core_(var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MICROPHONE_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Optional(CONF_ON_DATA): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DataTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MICROPHONE_ACTION_SCHEMA = maybe_simple_id({cv.GenerateID(): cv.use_id(Microphone)})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def media_player_action(config, action_id, template_arg, args):
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg)
 | 
			
		||||
    await cg.register_parented(var, config[CONF_ID])
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
automation.register_action(
 | 
			
		||||
    "microphone.capture", CaptureAction, MICROPHONE_ACTION_SCHEMA
 | 
			
		||||
)(media_player_action)
 | 
			
		||||
 | 
			
		||||
automation.register_action(
 | 
			
		||||
    "microphone.stop_capture", StopCaptureAction, MICROPHONE_ACTION_SCHEMA
 | 
			
		||||
)(media_player_action)
 | 
			
		||||
 | 
			
		||||
automation.register_condition(
 | 
			
		||||
    "microphone.is_capturing", IsCapturingCondition, MICROPHONE_ACTION_SCHEMA
 | 
			
		||||
)(media_player_action)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(100.0)
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    cg.add_global(microphone_ns.using)
 | 
			
		||||
    cg.add_define("USE_MICROPHONE")
 | 
			
		||||
							
								
								
									
										32
									
								
								esphome/components/microphone/automation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								esphome/components/microphone/automation.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "microphone.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace microphone {
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class CaptureAction : public Action<Ts...>, public Parented<Microphone> {
 | 
			
		||||
  void play(Ts... x) override { this->parent_->start(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class StopCaptureAction : public Action<Ts...>, public Parented<Microphone> {
 | 
			
		||||
  void play(Ts... x) override { this->parent_->stop(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DataTrigger : public Trigger<const std::vector<uint8_t> &> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit DataTrigger(Microphone *mic) {
 | 
			
		||||
    mic->add_data_callback([this](const std::vector<uint8_t> &data) { this->trigger(data); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class IsCapturingActon : public Condition<Ts...>, public Parented<Microphone> {
 | 
			
		||||
 public:
 | 
			
		||||
  bool check(Ts... x) override { return this->parent_->is_running(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace microphone
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										33
									
								
								esphome/components/microphone/microphone.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								esphome/components/microphone/microphone.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/entity_base.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace microphone {
 | 
			
		||||
 | 
			
		||||
enum State : uint8_t {
 | 
			
		||||
  STATE_STOPPED = 0,
 | 
			
		||||
  STATE_STARTING,
 | 
			
		||||
  STATE_RUNNING,
 | 
			
		||||
  STATE_STOPPING,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Microphone {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual void start() = 0;
 | 
			
		||||
  virtual void stop() = 0;
 | 
			
		||||
  void add_data_callback(std::function<void(const std::vector<uint8_t> &)> &&data_callback) {
 | 
			
		||||
    this->data_callbacks_.add(std::move(data_callback));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool is_running() const { return this->state_ == STATE_RUNNING; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  State state_{STATE_STOPPED};
 | 
			
		||||
 | 
			
		||||
  CallbackManager<void(const std::vector<uint8_t> &)> data_callbacks_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace microphone
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										1
									
								
								esphome/components/mmc5603/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/mmc5603/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
CODEOWNERS = ["@benhoff"]
 | 
			
		||||
							
								
								
									
										162
									
								
								esphome/components/mmc5603/mmc5603.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								esphome/components/mmc5603/mmc5603.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
			
		||||
#include "mmc5603.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace mmc5603 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "mmc5603";
 | 
			
		||||
static const uint8_t MMC5603_ADDRESS = 0x30;
 | 
			
		||||
static const uint8_t MMC56X3_PRODUCT_ID = 0x39;
 | 
			
		||||
 | 
			
		||||
static const uint8_t MMC56X3_DEFAULT_ADDRESS = 0x30;
 | 
			
		||||
static const uint8_t MMC56X3_CHIP_ID = 0x10;
 | 
			
		||||
 | 
			
		||||
static const uint8_t MMC56X3_ADDR_XOUT0 = 0x00;
 | 
			
		||||
static const uint8_t MMC56X3_ADDR_XOUT1 = 0x01;
 | 
			
		||||
static const uint8_t MMC56X3_ADDR_XOUT2 = 0x06;
 | 
			
		||||
 | 
			
		||||
static const uint8_t MMC56X3_ADDR_YOUT0 = 0x02;
 | 
			
		||||
static const uint8_t MMC56X3_ADDR_YOUT1 = 0x03;
 | 
			
		||||
static const uint8_t MMC56X3_ADDR_YOUT2 = 0x07;
 | 
			
		||||
 | 
			
		||||
static const uint8_t MMC56X3_ADDR_ZOUT0 = 0x04;
 | 
			
		||||
static const uint8_t MMC56X3_ADDR_ZOUT1 = 0x05;
 | 
			
		||||
static const uint8_t MMC56X3_ADDR_ZOUT2 = 0x08;
 | 
			
		||||
 | 
			
		||||
static const uint8_t MMC56X3_OUT_TEMP = 0x09;
 | 
			
		||||
static const uint8_t MMC56X3_STATUS_REG = 0x18;
 | 
			
		||||
static const uint8_t MMC56X3_CTRL0_REG = 0x1B;
 | 
			
		||||
static const uint8_t MMC56X3_CTRL1_REG = 0x1C;
 | 
			
		||||
static const uint8_t MMC56X3_CTRL2_REG = 0x1D;
 | 
			
		||||
static const uint8_t MMC5603_ODR_REG = 0x1A;
 | 
			
		||||
 | 
			
		||||
void MMC5603Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up MMC5603...");
 | 
			
		||||
  uint8_t id = 0;
 | 
			
		||||
  if (!this->read_byte(MMC56X3_PRODUCT_ID, &id)) {
 | 
			
		||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (id != MMC56X3_CHIP_ID) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "Chip Wrong");
 | 
			
		||||
    this->error_code_ = ID_REGISTERS;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->write_byte(MMC56X3_CTRL1_REG, 0x80)) {  // turn on set bit
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "Control 1 Failed for set bit");
 | 
			
		||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->write_byte(MMC56X3_CTRL0_REG, 0x08)) {  // turn on set bit
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "Control 0 Failed for set bit");
 | 
			
		||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->write_byte(MMC56X3_CTRL0_REG, 0x10)) {
 | 
			
		||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t ctrl_2 = 0;
 | 
			
		||||
 | 
			
		||||
  ctrl_2 &= ~0x10;  // turn off cmm_en bit
 | 
			
		||||
  if (!this->write_byte(MMC56X3_CTRL2_REG, ctrl_2)) {
 | 
			
		||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void MMC5603Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "MMC5603:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  if (this->error_code_ == COMMUNICATION_FAILED) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with MMC5603 failed!");
 | 
			
		||||
  } else if (this->error_code_ == ID_REGISTERS) {
 | 
			
		||||
    ESP_LOGE(TAG, "The ID registers don't match - Is this really an MMC5603?");
 | 
			
		||||
  }
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
 | 
			
		||||
  LOG_SENSOR("  ", "X Axis", this->x_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Y Axis", this->y_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Z Axis", this->z_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Heading", this->heading_sensor_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float MMC5603Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
void MMC5603Component::update() {
 | 
			
		||||
  if (!this->write_byte(MMC56X3_CTRL0_REG, 0x01)) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  uint8_t status = 0;
 | 
			
		||||
  if (!this->read_byte(MMC56X3_STATUS_REG, &status)) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t buffer[9] = {0};
 | 
			
		||||
 | 
			
		||||
  if (!this->read_byte(MMC56X3_ADDR_XOUT0, &buffer[0]) || !this->read_byte(MMC56X3_ADDR_XOUT1, &buffer[1]) ||
 | 
			
		||||
      !this->read_byte(MMC56X3_ADDR_XOUT2, &buffer[2])) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->read_byte(MMC56X3_ADDR_YOUT0, &buffer[3]) || !this->read_byte(MMC56X3_ADDR_YOUT1, &buffer[4]) ||
 | 
			
		||||
      !this->read_byte(MMC56X3_ADDR_YOUT2, &buffer[5])) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->read_byte(MMC56X3_ADDR_ZOUT0, &buffer[6]) || !this->read_byte(MMC56X3_ADDR_ZOUT1, &buffer[7]) ||
 | 
			
		||||
      !this->read_byte(MMC56X3_ADDR_ZOUT2, &buffer[8])) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int32_t raw_x = 0;
 | 
			
		||||
  raw_x |= buffer[0] << 12;
 | 
			
		||||
  raw_x |= buffer[1] << 4;
 | 
			
		||||
  raw_x |= buffer[2] << 0;
 | 
			
		||||
 | 
			
		||||
  const float x = 0.0625 * (raw_x - 524288);
 | 
			
		||||
 | 
			
		||||
  int32_t raw_y = 0;
 | 
			
		||||
  raw_y |= buffer[3] << 12;
 | 
			
		||||
  raw_y |= buffer[4] << 4;
 | 
			
		||||
  raw_y |= buffer[5] << 0;
 | 
			
		||||
 | 
			
		||||
  const float y = 0.0625 * (raw_y - 524288);
 | 
			
		||||
 | 
			
		||||
  int32_t raw_z = 0;
 | 
			
		||||
  raw_z |= buffer[6] << 12;
 | 
			
		||||
  raw_z |= buffer[7] << 4;
 | 
			
		||||
  raw_z |= buffer[8] << 0;
 | 
			
		||||
 | 
			
		||||
  const float z = 0.0625 * (raw_z - 524288);
 | 
			
		||||
 | 
			
		||||
  const float heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
 | 
			
		||||
  ESP_LOGD(TAG, "Got x=%0.02fµT y=%0.02fµT z=%0.02fµT heading=%0.01f°", x, y, z, heading);
 | 
			
		||||
 | 
			
		||||
  if (this->x_sensor_ != nullptr)
 | 
			
		||||
    this->x_sensor_->publish_state(x);
 | 
			
		||||
  if (this->y_sensor_ != nullptr)
 | 
			
		||||
    this->y_sensor_->publish_state(y);
 | 
			
		||||
  if (this->z_sensor_ != nullptr)
 | 
			
		||||
    this->z_sensor_->publish_state(z);
 | 
			
		||||
  if (this->heading_sensor_ != nullptr)
 | 
			
		||||
    this->heading_sensor_->publish_state(heading);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace mmc5603
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										43
									
								
								esphome/components/mmc5603/mmc5603.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								esphome/components/mmc5603/mmc5603.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace mmc5603 {
 | 
			
		||||
 | 
			
		||||
enum MMC5603Datarate {
 | 
			
		||||
  MMC5603_DATARATE_75_0_HZ,
 | 
			
		||||
  MMC5603_DATARATE_150_0_HZ,
 | 
			
		||||
  MMC5603_DATARATE_255_0_HZ,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class MMC5603Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void update() override;
 | 
			
		||||
 | 
			
		||||
  void set_datarate(MMC5603Datarate datarate) { datarate_ = datarate; }
 | 
			
		||||
  void set_x_sensor(sensor::Sensor *x_sensor) { x_sensor_ = x_sensor; }
 | 
			
		||||
  void set_y_sensor(sensor::Sensor *y_sensor) { y_sensor_ = y_sensor; }
 | 
			
		||||
  void set_z_sensor(sensor::Sensor *z_sensor) { z_sensor_ = z_sensor; }
 | 
			
		||||
  void set_heading_sensor(sensor::Sensor *heading_sensor) { heading_sensor_ = heading_sensor; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  MMC5603Datarate datarate_;
 | 
			
		||||
  sensor::Sensor *x_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *y_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *z_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *heading_sensor_{nullptr};
 | 
			
		||||
  enum ErrorCode {
 | 
			
		||||
    NONE = 0,
 | 
			
		||||
    COMMUNICATION_FAILED,
 | 
			
		||||
    ID_REGISTERS,
 | 
			
		||||
  } error_code_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace mmc5603
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										91
									
								
								esphome/components/mmc5603/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								esphome/components/mmc5603/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ADDRESS,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    ICON_MAGNET,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_MICROTESLA,
 | 
			
		||||
    UNIT_DEGREES,
 | 
			
		||||
    ICON_SCREEN_ROTATION,
 | 
			
		||||
    CONF_UPDATE_INTERVAL,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
mmc5603_ns = cg.esphome_ns.namespace("mmc5603")
 | 
			
		||||
 | 
			
		||||
CONF_FIELD_STRENGTH_X = "field_strength_x"
 | 
			
		||||
CONF_FIELD_STRENGTH_Y = "field_strength_y"
 | 
			
		||||
CONF_FIELD_STRENGTH_Z = "field_strength_z"
 | 
			
		||||
CONF_HEADING = "heading"
 | 
			
		||||
 | 
			
		||||
MMC5603Component = mmc5603_ns.class_(
 | 
			
		||||
    "MMC5603Component", cg.PollingComponent, i2c.I2CDevice
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MMC5603Datarate = mmc5603_ns.enum("MMC5603Datarate")
 | 
			
		||||
MMC5603Datarates = {
 | 
			
		||||
    75: MMC5603Datarate.MMC5603_DATARATE_75_0_HZ,
 | 
			
		||||
    150: MMC5603Datarate.MMC5603_DATARATE_150_0_HZ,
 | 
			
		||||
    255: MMC5603Datarate.MMC5603_DATARATE_255_0_HZ,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
field_strength_schema = sensor.sensor_schema(
 | 
			
		||||
    unit_of_measurement=UNIT_MICROTESLA,
 | 
			
		||||
    icon=ICON_MAGNET,
 | 
			
		||||
    accuracy_decimals=1,
 | 
			
		||||
    state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
)
 | 
			
		||||
heading_schema = sensor.sensor_schema(
 | 
			
		||||
    unit_of_measurement=UNIT_DEGREES,
 | 
			
		||||
    icon=ICON_SCREEN_ROTATION,
 | 
			
		||||
    accuracy_decimals=1,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(MMC5603Component),
 | 
			
		||||
            cv.Optional(CONF_ADDRESS): cv.i2c_address,
 | 
			
		||||
            cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema,
 | 
			
		||||
            cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema,
 | 
			
		||||
            cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
 | 
			
		||||
            cv.Optional(CONF_HEADING): heading_schema,
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(i2c.i2c_device_schema(0x1E))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def auto_data_rate(config):
 | 
			
		||||
    interval_msec = config[CONF_UPDATE_INTERVAL].total_milliseconds
 | 
			
		||||
    interval_hz = 1000.0 / interval_msec
 | 
			
		||||
    for datarate in sorted(MMC5603Datarates.keys()):
 | 
			
		||||
        if float(datarate) >= interval_hz:
 | 
			
		||||
            return MMC5603Datarates[datarate]
 | 
			
		||||
    return MMC5603Datarates[75]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_datarate(auto_data_rate(config)))
 | 
			
		||||
    if CONF_FIELD_STRENGTH_X in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_FIELD_STRENGTH_X])
 | 
			
		||||
        cg.add(var.set_x_sensor(sens))
 | 
			
		||||
    if CONF_FIELD_STRENGTH_Y in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_FIELD_STRENGTH_Y])
 | 
			
		||||
        cg.add(var.set_y_sensor(sens))
 | 
			
		||||
    if CONF_FIELD_STRENGTH_Z in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_FIELD_STRENGTH_Z])
 | 
			
		||||
        cg.add(var.set_z_sensor(sens))
 | 
			
		||||
    if CONF_HEADING in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_HEADING])
 | 
			
		||||
        cg.add(var.set_heading_sensor(sens))
 | 
			
		||||
@@ -64,9 +64,10 @@ INTEGER_SENSOR_VALUE_TYPE = {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    select.SELECT_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend(
 | 
			
		||||
    select.select_schema(ModbusSelect)
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(ModbusSelect),
 | 
			
		||||
            cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController),
 | 
			
		||||
            cv.Required(CONF_ADDRESS): cv.positive_int,
 | 
			
		||||
            cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum(
 | 
			
		||||
 
 | 
			
		||||
@@ -2,16 +2,16 @@
 | 
			
		||||
 | 
			
		||||
#ifdef USE_MQTT
 | 
			
		||||
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
#include <utility>
 | 
			
		||||
#ifdef USE_LOGGER
 | 
			
		||||
#include "esphome/components/logger/logger.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "lwip/err.h"
 | 
			
		||||
#include "lwip/dns.h"
 | 
			
		||||
#include "lwip/err.h"
 | 
			
		||||
#include "mqtt_component.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -104,7 +104,11 @@ void MQTTClientComponent::start_dnslookup_() {
 | 
			
		||||
      // Got IP immediately
 | 
			
		||||
      this->dns_resolved_ = true;
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
      this->ip_ = addr.u_addr.ip4.addr;
 | 
			
		||||
#else
 | 
			
		||||
      this->ip_ = addr.addr;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
      this->ip_ = addr.addr;
 | 
			
		||||
@@ -160,8 +164,12 @@ void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t *
 | 
			
		||||
    a_this->dns_resolve_error_ = true;
 | 
			
		||||
  } else {
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
    a_this->ip_ = ipaddr->u_addr.ip4.addr;
 | 
			
		||||
#else
 | 
			
		||||
    a_this->ip_ = ipaddr->addr;
 | 
			
		||||
#endif
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
    a_this->ip_ = ipaddr->addr;
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,8 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    if CONF_ENABLE_IPV6 in config and config[CONF_ENABLE_IPV6]:
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_LWIP_IPV6", True)
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_LWIP_IPV6_AUTOCONFIG", True)
 | 
			
		||||
    if CONF_ENABLE_IPV6 in config:
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_LWIP_IPV6", config[CONF_ENABLE_IPV6])
 | 
			
		||||
        add_idf_sdkconfig_option(
 | 
			
		||||
            "CONFIG_LWIP_IPV6_AUTOCONFIG", config[CONF_ENABLE_IPV6]
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,11 @@ NdefMessage::NdefMessage(std::vector<uint8_t> &data) {
 | 
			
		||||
      index += id_length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((data.begin() + index > data.end()) || (data.begin() + index + payload_length > data.end())) {
 | 
			
		||||
      ESP_LOGE(TAG, "Corrupt record encountered; NdefMessage constructor aborting");
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<uint8_t> payload_data(data.begin() + index, data.begin() + index + payload_length);
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<NdefRecord> record;
 | 
			
		||||
 
 | 
			
		||||
@@ -42,8 +42,8 @@ class NdefRecord {
 | 
			
		||||
  virtual const std::string &get_payload() const { return this->payload_; };
 | 
			
		||||
 | 
			
		||||
  virtual std::vector<uint8_t> get_encoded_payload() {
 | 
			
		||||
    std::vector<uint8_t> empty_payload;
 | 
			
		||||
    return empty_payload;
 | 
			
		||||
    std::vector<uint8_t> payload(this->payload_.begin(), this->payload_.end());
 | 
			
		||||
    return payload;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
 
 | 
			
		||||
@@ -89,18 +89,18 @@ uint32_t get_mifare_classic_buffer_size(uint32_t message_length) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mifare_classic_is_first_block(uint8_t block_num) {
 | 
			
		||||
  if (block_num < 128) {
 | 
			
		||||
    return (block_num % 4 == 0);
 | 
			
		||||
  if (block_num < MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW * MIFARE_CLASSIC_16BLOCK_SECT_START) {
 | 
			
		||||
    return (block_num % MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW == 0);
 | 
			
		||||
  } else {
 | 
			
		||||
    return (block_num % 16 == 0);
 | 
			
		||||
    return (block_num % MIFARE_CLASSIC_BLOCKS_PER_SECT_HIGH == 0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mifare_classic_is_trailer_block(uint8_t block_num) {
 | 
			
		||||
  if (block_num < 128) {
 | 
			
		||||
    return ((block_num + 1) % 4 == 0);
 | 
			
		||||
  if (block_num < MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW * MIFARE_CLASSIC_16BLOCK_SECT_START) {
 | 
			
		||||
    return ((block_num + 1) % MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW == 0);
 | 
			
		||||
  } else {
 | 
			
		||||
    return ((block_num + 1) % 16 == 0);
 | 
			
		||||
    return ((block_num + 1) % MIFARE_CLASSIC_BLOCKS_PER_SECT_HIGH == 0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,9 @@ namespace nfc {
 | 
			
		||||
static const uint8_t MIFARE_CLASSIC_BLOCK_SIZE = 16;
 | 
			
		||||
static const uint8_t MIFARE_CLASSIC_LONG_TLV_SIZE = 4;
 | 
			
		||||
static const uint8_t MIFARE_CLASSIC_SHORT_TLV_SIZE = 2;
 | 
			
		||||
static const uint8_t MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW = 4;
 | 
			
		||||
static const uint8_t MIFARE_CLASSIC_BLOCKS_PER_SECT_HIGH = 16;
 | 
			
		||||
static const uint8_t MIFARE_CLASSIC_16BLOCK_SECT_START = 32;
 | 
			
		||||
 | 
			
		||||
static const uint8_t MIFARE_ULTRALIGHT_PAGE_SIZE = 4;
 | 
			
		||||
static const uint8_t MIFARE_ULTRALIGHT_READ_SIZE = 4;
 | 
			
		||||
@@ -30,10 +33,18 @@ static const uint8_t TAG_TYPE_UNKNOWN = 99;
 | 
			
		||||
// Mifare Commands
 | 
			
		||||
static const uint8_t MIFARE_CMD_AUTH_A = 0x60;
 | 
			
		||||
static const uint8_t MIFARE_CMD_AUTH_B = 0x61;
 | 
			
		||||
static const uint8_t MIFARE_CMD_HALT = 0x50;
 | 
			
		||||
static const uint8_t MIFARE_CMD_READ = 0x30;
 | 
			
		||||
static const uint8_t MIFARE_CMD_WRITE = 0xA0;
 | 
			
		||||
static const uint8_t MIFARE_CMD_WRITE_ULTRALIGHT = 0xA2;
 | 
			
		||||
 | 
			
		||||
// Mifare Ack/Nak
 | 
			
		||||
static const uint8_t MIFARE_CMD_ACK = 0x0A;
 | 
			
		||||
static const uint8_t MIFARE_CMD_NAK_INVALID_XFER_BUFF_VALID = 0x00;
 | 
			
		||||
static const uint8_t MIFARE_CMD_NAK_CRC_ERROR_XFER_BUFF_VALID = 0x01;
 | 
			
		||||
static const uint8_t MIFARE_CMD_NAK_INVALID_XFER_BUFF_INVALID = 0x04;
 | 
			
		||||
static const uint8_t MIFARE_CMD_NAK_CRC_ERROR_XFER_BUFF_INVALID = 0x05;
 | 
			
		||||
 | 
			
		||||
static const char *const MIFARE_CLASSIC = "Mifare Classic";
 | 
			
		||||
static const char *const NFC_FORUM_TYPE_2 = "NFC Forum Type 2";
 | 
			
		||||
static const char *const ERROR = "Error";
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
from typing import Optional
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import automation
 | 
			
		||||
@@ -31,6 +30,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_DISTANCE,
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_ENERGY_STORAGE,
 | 
			
		||||
    DEVICE_CLASS_FREQUENCY,
 | 
			
		||||
    DEVICE_CLASS_GAS,
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
@@ -59,6 +59,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_VOLUME,
 | 
			
		||||
    DEVICE_CLASS_VOLUME_STORAGE,
 | 
			
		||||
    DEVICE_CLASS_WATER,
 | 
			
		||||
    DEVICE_CLASS_WEIGHT,
 | 
			
		||||
    DEVICE_CLASS_WIND_SPEED,
 | 
			
		||||
@@ -81,6 +82,7 @@ DEVICE_CLASSES = [
 | 
			
		||||
    DEVICE_CLASS_DISTANCE,
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_ENERGY_STORAGE,
 | 
			
		||||
    DEVICE_CLASS_FREQUENCY,
 | 
			
		||||
    DEVICE_CLASS_GAS,
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
@@ -109,6 +111,7 @@ DEVICE_CLASSES = [
 | 
			
		||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_VOLUME,
 | 
			
		||||
    DEVICE_CLASS_VOLUME_STORAGE,
 | 
			
		||||
    DEVICE_CLASS_WATER,
 | 
			
		||||
    DEVICE_CLASS_WEIGHT,
 | 
			
		||||
    DEVICE_CLASS_WIND_SPEED,
 | 
			
		||||
@@ -204,14 +207,13 @@ def number_schema(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def setup_number_core_(
 | 
			
		||||
    var, config, *, min_value: float, max_value: float, step: Optional[float]
 | 
			
		||||
    var, config, *, min_value: float, max_value: float, step: float
 | 
			
		||||
):
 | 
			
		||||
    await setup_entity(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.traits.set_min_value(min_value))
 | 
			
		||||
    cg.add(var.traits.set_max_value(max_value))
 | 
			
		||||
    if step is not None:
 | 
			
		||||
        cg.add(var.traits.set_step(step))
 | 
			
		||||
    cg.add(var.traits.set_step(step))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.traits.set_mode(config[CONF_MODE]))
 | 
			
		||||
 | 
			
		||||
@@ -239,7 +241,7 @@ async def setup_number_core_(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_number(
 | 
			
		||||
    var, config, *, min_value: float, max_value: float, step: Optional[float] = None
 | 
			
		||||
    var, config, *, min_value: float, max_value: float, step: float
 | 
			
		||||
):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
        var = cg.Pvariable(config[CONF_ID], var)
 | 
			
		||||
@@ -249,9 +251,7 @@ async def register_number(
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def new_number(
 | 
			
		||||
    config, *, min_value: float, max_value: float, step: Optional[float] = None
 | 
			
		||||
):
 | 
			
		||||
async def new_number(config, *, min_value: float, max_value: float, step: float):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await register_number(
 | 
			
		||||
        var, config, min_value=min_value, max_value=max_value, step=step
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ void OTAComponent::setup() {
 | 
			
		||||
 | 
			
		||||
  struct sockaddr_storage server;
 | 
			
		||||
 | 
			
		||||
  socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), htons(this->port_));
 | 
			
		||||
  socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
 | 
			
		||||
  if (sl == 0) {
 | 
			
		||||
    ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
 
 | 
			
		||||
@@ -102,7 +102,7 @@ async def to_code(config):
 | 
			
		||||
                conf[CONF_ADDRESSABLE_LIGHT_ID],
 | 
			
		||||
                await cg.get_variable(conf[CONF_SINGLE_LIGHT_ID]),
 | 
			
		||||
            )
 | 
			
		||||
            light_state = cg.new_Pvariable(conf[CONF_LIGHT_ID], "", wrapper)
 | 
			
		||||
            light_state = cg.new_Pvariable(conf[CONF_LIGHT_ID], wrapper)
 | 
			
		||||
            await cg.register_component(light_state, conf)
 | 
			
		||||
            segments.append(AddressableSegment(light_state, 0, 1, False))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,9 +19,12 @@ void PN532::setup() {
 | 
			
		||||
 | 
			
		||||
  // Get version data
 | 
			
		||||
  if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) {
 | 
			
		||||
    ESP_LOGE(TAG, "Error sending version command");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
    ESP_LOGW(TAG, "Error sending version command, trying again...");
 | 
			
		||||
    if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) {
 | 
			
		||||
      ESP_LOGE(TAG, "Error sending version command");
 | 
			
		||||
      this->mark_failed();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> version_data;
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,13 @@ std::unique_ptr<nfc::NfcTag> PN532::read_mifare_classic_tag_(std::vector<uint8_t
 | 
			
		||||
      current_block++;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  buffer.erase(buffer.begin(), buffer.begin() + message_start_index);
 | 
			
		||||
 | 
			
		||||
  if (buffer.begin() + message_start_index < buffer.end()) {
 | 
			
		||||
    buffer.erase(buffer.begin(), buffer.begin() + message_start_index);
 | 
			
		||||
  } else {
 | 
			
		||||
    return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC, buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TOLERANCE,
 | 
			
		||||
    CONF_MEMORY_BLOCKS,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
from esphome.core import CORE, TimePeriod
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["remote_base"]
 | 
			
		||||
remote_receiver_ns = cg.esphome_ns.namespace("remote_receiver")
 | 
			
		||||
@@ -33,9 +33,10 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
 | 
			
		||||
            cv.SplitDefault(
 | 
			
		||||
                CONF_BUFFER_SIZE, esp32="10000b", esp8266="1000b"
 | 
			
		||||
            ): cv.validate_bytes,
 | 
			
		||||
            cv.Optional(
 | 
			
		||||
                CONF_FILTER, default="50us"
 | 
			
		||||
            ): cv.positive_time_period_microseconds,
 | 
			
		||||
            cv.Optional(CONF_FILTER, default="50us"): cv.All(
 | 
			
		||||
                cv.positive_time_period_microseconds,
 | 
			
		||||
                cv.Range(max=TimePeriod(microseconds=255)),
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(
 | 
			
		||||
                CONF_IDLE, default="10ms"
 | 
			
		||||
            ): cv.positive_time_period_microseconds,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,8 @@ import esphome.config_validation as cv
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.components import mqtt
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ENTITY_CATEGORY,
 | 
			
		||||
    CONF_ICON,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_ON_VALUE,
 | 
			
		||||
    CONF_OPTION,
 | 
			
		||||
@@ -14,6 +16,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_INDEX,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
from esphome.cpp_generator import MockObjClass
 | 
			
		||||
from esphome.cpp_helpers import setup_entity
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@esphome/core"]
 | 
			
		||||
@@ -43,8 +46,6 @@ SELECT_OPERATION_OPTIONS = {
 | 
			
		||||
    "LAST": SelectOperation.SELECT_OP_LAST,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
icon = cv.icon
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
 | 
			
		||||
    {
 | 
			
		||||
@@ -58,6 +59,30 @@ SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).e
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
_UNDEF = object()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def select_schema(
 | 
			
		||||
    class_: MockObjClass = _UNDEF,
 | 
			
		||||
    *,
 | 
			
		||||
    entity_category: str = _UNDEF,
 | 
			
		||||
    icon: str = _UNDEF,
 | 
			
		||||
):
 | 
			
		||||
    schema = cv.Schema({})
 | 
			
		||||
    if class_ is not _UNDEF:
 | 
			
		||||
        schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)})
 | 
			
		||||
    if entity_category is not _UNDEF:
 | 
			
		||||
        schema = schema.extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Optional(
 | 
			
		||||
                    CONF_ENTITY_CATEGORY, default=entity_category
 | 
			
		||||
                ): cv.entity_category
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    if icon is not _UNDEF:
 | 
			
		||||
        schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
 | 
			
		||||
    return SELECT_SCHEMA.extend(schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def setup_select_core_(var, config, *, options: list[str]):
 | 
			
		||||
    await setup_entity(var, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -86,7 +86,7 @@ void SenseAirComponent::background_calibration_result() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check if 5th bit (register CI6) is set
 | 
			
		||||
  ESP_LOGD(TAG, "SenseAir Result=%s (%02x%02x%02x %02x%02x %02x%02x)", (response[4] & 0b100000) != 0 ? "OK" : "NOT_OK",
 | 
			
		||||
  ESP_LOGI(TAG, "SenseAir Result=%s (%02x%02x%02x %02x%02x %02x%02x)", (response[4] & 0b100000) != 0 ? "OK" : "NOT_OK",
 | 
			
		||||
           response[0], response[1], response[2], response[3], response[4], response[5], response[6]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_DURATION,
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_ENERGY_STORAGE,
 | 
			
		||||
    DEVICE_CLASS_FREQUENCY,
 | 
			
		||||
    DEVICE_CLASS_GAS,
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
@@ -72,6 +73,7 @@ from esphome.const import (
 | 
			
		||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_VOLUME,
 | 
			
		||||
    DEVICE_CLASS_VOLUME_STORAGE,
 | 
			
		||||
    DEVICE_CLASS_WATER,
 | 
			
		||||
    DEVICE_CLASS_WEIGHT,
 | 
			
		||||
    DEVICE_CLASS_WIND_SPEED,
 | 
			
		||||
@@ -97,6 +99,7 @@ DEVICE_CLASSES = [
 | 
			
		||||
    DEVICE_CLASS_DURATION,
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_ENERGY_STORAGE,
 | 
			
		||||
    DEVICE_CLASS_FREQUENCY,
 | 
			
		||||
    DEVICE_CLASS_GAS,
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
@@ -126,6 +129,7 @@ DEVICE_CLASSES = [
 | 
			
		||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_VOLUME,
 | 
			
		||||
    DEVICE_CLASS_VOLUME_STORAGE,
 | 
			
		||||
    DEVICE_CLASS_WATER,
 | 
			
		||||
    DEVICE_CLASS_WEIGHT,
 | 
			
		||||
    DEVICE_CLASS_WIND_SPEED,
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,7 @@ std::string state_class_to_string(StateClass state_class) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Sensor::Sensor(const std::string &name) : EntityBase(name), state(NAN), raw_state(NAN) {}
 | 
			
		||||
Sensor::Sensor() : Sensor("") {}
 | 
			
		||||
Sensor::Sensor() : state(NAN), raw_state(NAN) {}
 | 
			
		||||
 | 
			
		||||
std::string Sensor::get_unit_of_measurement() {
 | 
			
		||||
  if (this->unit_of_measurement_.has_value())
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,6 @@ std::string state_class_to_string(StateClass state_class);
 | 
			
		||||
class Sensor : public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit Sensor();
 | 
			
		||||
  explicit Sensor(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /// Get the unit of measurement, using the manual override if set.
 | 
			
		||||
  std::string get_unit_of_measurement();
 | 
			
		||||
@@ -162,7 +161,6 @@ class Sensor : public EntityBase {
 | 
			
		||||
  CallbackManager<void(float)> raw_callback_;  ///< Storage for raw state callbacks.
 | 
			
		||||
  CallbackManager<void(float)> callback_;      ///< Storage for filtered state callbacks.
 | 
			
		||||
 | 
			
		||||
  bool has_state_{false};
 | 
			
		||||
  Filter *filter_list_{nullptr};  ///< Store all active filters.
 | 
			
		||||
 | 
			
		||||
  optional<std::string> unit_of_measurement_;           ///< Unit of measurement override
 | 
			
		||||
@@ -170,6 +168,7 @@ class Sensor : public EntityBase {
 | 
			
		||||
  optional<std::string> device_class_;                  ///< Device class override
 | 
			
		||||
  optional<StateClass> state_class_{STATE_CLASS_NONE};  ///< State class override
 | 
			
		||||
  bool force_update_{false};                            ///< Force update mode
 | 
			
		||||
  bool has_state_{false};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace sensor
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,9 @@ std::string format_sockaddr(const struct sockaddr_storage &storage) {
 | 
			
		||||
    char buf[INET_ADDRSTRLEN];
 | 
			
		||||
    if (inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf)) != nullptr)
 | 
			
		||||
      return std::string{buf};
 | 
			
		||||
  } else if (storage.ss_family == AF_INET6) {
 | 
			
		||||
  }
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
  else if (storage.ss_family == AF_INET6) {
 | 
			
		||||
    const struct sockaddr_in6 *addr = reinterpret_cast<const struct sockaddr_in6 *>(&storage);
 | 
			
		||||
    char buf[INET6_ADDRSTRLEN];
 | 
			
		||||
    // Format IPv4-mapped IPv6 addresses as regular IPv4 addresses
 | 
			
		||||
@@ -32,6 +34,7 @@ std::string format_sockaddr(const struct sockaddr_storage &storage) {
 | 
			
		||||
    if (inet_ntop(AF_INET6, &addr->sin6_addr, buf, sizeof(buf)) != nullptr)
 | 
			
		||||
      return std::string{buf};
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -136,6 +139,11 @@ class BSDSocketImpl : public Socket {
 | 
			
		||||
    return ::writev(fd_, iov, iovcnt);
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ssize_t sendto(const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) override {
 | 
			
		||||
    return ::sendto(fd_, buf, len, flags, to, tolen);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int setblocking(bool blocking) override {
 | 
			
		||||
    int fl = ::fcntl(fd_, F_GETFL, 0);
 | 
			
		||||
    if (blocking) {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,19 +15,28 @@
 | 
			
		||||
/* Address families.  */
 | 
			
		||||
#define AF_UNSPEC 0
 | 
			
		||||
#define AF_INET 2
 | 
			
		||||
#define AF_INET6 10
 | 
			
		||||
#define PF_INET AF_INET
 | 
			
		||||
#define PF_INET6 AF_INET6
 | 
			
		||||
#define PF_UNSPEC AF_UNSPEC
 | 
			
		||||
 | 
			
		||||
#define IPPROTO_IP 0
 | 
			
		||||
#define IPPROTO_TCP 6
 | 
			
		||||
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
#define AF_INET6 10
 | 
			
		||||
#define PF_INET6 AF_INET6
 | 
			
		||||
 | 
			
		||||
#define IPPROTO_IPV6 41
 | 
			
		||||
#define IPPROTO_ICMPV6 58
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define TCP_NODELAY 0x01
 | 
			
		||||
 | 
			
		||||
#define F_GETFL 3
 | 
			
		||||
#define F_SETFL 4
 | 
			
		||||
 | 
			
		||||
#ifdef O_NONBLOCK
 | 
			
		||||
#undef O_NONBLOCK
 | 
			
		||||
#endif
 | 
			
		||||
#define O_NONBLOCK 1
 | 
			
		||||
 | 
			
		||||
#define SHUT_RD 0
 | 
			
		||||
@@ -58,6 +67,7 @@ struct sockaddr_in {
 | 
			
		||||
  char sin_zero[SIN_ZERO_LEN];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
// NOLINTNEXTLINE(readability-identifier-naming)
 | 
			
		||||
struct sockaddr_in6 {
 | 
			
		||||
  uint8_t sin6_len;          /* length of this structure    */
 | 
			
		||||
@@ -67,6 +77,7 @@ struct sockaddr_in6 {
 | 
			
		||||
  struct in6_addr sin6_addr; /* IPv6 address                */
 | 
			
		||||
  uint32_t sin6_scope_id;    /* Set of interfaces for scope */
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// NOLINTNEXTLINE(readability-identifier-naming)
 | 
			
		||||
struct sockaddr {
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user