mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 09:18:00 +01:00
Merge branch 'dev' into daikin-enhancements
This commit is contained in:
commit
a1bbdf7d2a
2002 changed files with 62099 additions and 4111 deletions
|
@ -7,8 +7,21 @@
|
||||||
"PIP_BREAK_SYSTEM_PACKAGES": "1",
|
"PIP_BREAK_SYSTEM_PACKAGES": "1",
|
||||||
"PIP_ROOT_USER_ACTION": "ignore"
|
"PIP_ROOT_USER_ACTION": "ignore"
|
||||||
},
|
},
|
||||||
"runArgs": ["--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1"],
|
"runArgs": [
|
||||||
|
"--privileged",
|
||||||
|
"-e",
|
||||||
|
"ESPHOME_DASHBOARD_USE_PING=1"
|
||||||
|
// uncomment and edit the path in order to pass though local USB serial to the conatiner
|
||||||
|
// , "--device=/dev/ttyACM0"
|
||||||
|
],
|
||||||
"appPort": 6052,
|
"appPort": 6052,
|
||||||
|
// if you are using avahi in the host device, uncomment these to allow the
|
||||||
|
// devcontainer to find devices via mdns
|
||||||
|
//"mounts": [
|
||||||
|
// "type=bind,source=/dev/bus/usb,target=/dev/bus/usb",
|
||||||
|
// "type=bind,source=/var/run/dbus,target=/var/run/dbus",
|
||||||
|
// "type=bind,source=/var/run/avahi-daemon/socket,target=/var/run/avahi-daemon/socket"
|
||||||
|
//],
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
|
|
4
.github/actions/build-image/action.yaml
vendored
4
.github/actions/build-image/action.yaml
vendored
|
@ -36,7 +36,7 @@ runs:
|
||||||
|
|
||||||
- name: Build and push to ghcr by digest
|
- name: Build and push to ghcr by digest
|
||||||
id: build-ghcr
|
id: build-ghcr
|
||||||
uses: docker/build-push-action@v5.0.0
|
uses: docker/build-push-action@v5.3.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./docker/Dockerfile
|
file: ./docker/Dockerfile
|
||||||
|
@ -67,7 +67,7 @@ runs:
|
||||||
|
|
||||||
- name: Build and push to dockerhub by digest
|
- name: Build and push to dockerhub by digest
|
||||||
id: build-dockerhub
|
id: build-dockerhub
|
||||||
uses: docker/build-push-action@v5.0.0
|
uses: docker/build-push-action@v5.3.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./docker/Dockerfile
|
file: ./docker/Dockerfile
|
||||||
|
|
17
.github/actions/restore-python/action.yml
vendored
17
.github/actions/restore-python/action.yml
vendored
|
@ -17,22 +17,31 @@ runs:
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Python ${{ inputs.python-version }}
|
- name: Set up Python ${{ inputs.python-version }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ inputs.python-version }}
|
python-version: ${{ inputs.python-version }}
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
id: cache-venv
|
id: cache-venv
|
||||||
uses: actions/cache/restore@v3.3.2
|
uses: actions/cache/restore@v4.0.2
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ inputs.cache-key }}
|
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ inputs.cache-key }}
|
||||||
- name: Create Python virtual environment
|
- name: Create Python virtual environment
|
||||||
if: steps.cache-venv.outputs.cache-hit != 'true'
|
if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os != 'Windows'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
python -m venv venv
|
python -m venv venv
|
||||||
. venv/bin/activate
|
source venv/bin/activate
|
||||||
|
python --version
|
||||||
|
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
||||||
|
pip install -e .
|
||||||
|
- name: Create Python virtual environment
|
||||||
|
if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os == 'Windows'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
python -m venv venv
|
||||||
|
./venv/Scripts/activate
|
||||||
python --version
|
python --version
|
||||||
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
||||||
pip install -e .
|
pip install -e .
|
||||||
|
|
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
|
@ -13,3 +13,13 @@ updates:
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: "/.github/actions/build-image"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: "/.github/actions/restore-python"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
|
80
.github/workflows/ci-api-proto.yml
vendored
Normal file
80
.github/workflows/ci-api-proto.yml
vendored
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
name: API Proto CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "esphome/components/api/api.proto"
|
||||||
|
- "esphome/components/api/api_pb2.cpp"
|
||||||
|
- "esphome/components/api/api_pb2.h"
|
||||||
|
- "esphome/components/api/api_pb2_service.cpp"
|
||||||
|
- "esphome/components/api/api_pb2_service.h"
|
||||||
|
- "script/api_protobuf/api_protobuf.py"
|
||||||
|
- ".github/workflows/ci-api-proto.yml"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Check generated files
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4.1.1
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5.1.0
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
|
||||||
|
- name: Install apt dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt-cache show protobuf-compiler
|
||||||
|
sudo apt install -y protobuf-compiler
|
||||||
|
protoc --version
|
||||||
|
- name: Install python dependencies
|
||||||
|
run: pip install aioesphomeapi -c requirements.txt -r requirements_dev.txt
|
||||||
|
- name: Generate files
|
||||||
|
run: script/api_protobuf/api_protobuf.py
|
||||||
|
- name: Check for changes
|
||||||
|
run: |
|
||||||
|
if ! git diff --quiet; then
|
||||||
|
echo "## Job Failed" | tee -a $GITHUB_STEP_SUMMARY
|
||||||
|
echo "You have altered the generated proto files but they do not match what is expected." | tee -a $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Please run 'script/api_protobuf/api_protobuf.py' and commit the changes." | tee -a $GITHUB_STEP_SUMMARY
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- if: failure()
|
||||||
|
name: Review PR
|
||||||
|
uses: actions/github-script@v7.0.1
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
await github.rest.pulls.createReview({
|
||||||
|
pull_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
event: 'REQUEST_CHANGES',
|
||||||
|
body: 'You have altered the generated proto files but they do not match what is expected.\nPlease run "script/api_protobuf/api_protobuf.py" and commit the changes.'
|
||||||
|
})
|
||||||
|
- if: success()
|
||||||
|
name: Dismiss review
|
||||||
|
uses: actions/github-script@v7.0.1
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
let reviews = await github.rest.pulls.listReviews({
|
||||||
|
pull_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo
|
||||||
|
});
|
||||||
|
for (let review of reviews.data) {
|
||||||
|
if (review.user.login === 'github-actions[bot]' && review.state === 'CHANGES_REQUESTED') {
|
||||||
|
await github.rest.pulls.dismissReview({
|
||||||
|
pull_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
review_id: review.id,
|
||||||
|
message: 'Files now match the expected proto files.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
6
.github/workflows/ci-docker.yml
vendored
6
.github/workflows/ci-docker.yml
vendored
|
@ -2,7 +2,7 @@
|
||||||
name: CI for docker images
|
name: CI for docker images
|
||||||
|
|
||||||
# Only run when docker paths change
|
# Only run when docker paths change
|
||||||
# yamllint disable-line rule:truthy
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [dev, beta, release]
|
branches: [dev, beta, release]
|
||||||
|
@ -42,11 +42,11 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.1
|
- uses: actions/checkout@v4.1.1
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.0.0
|
uses: docker/setup-buildx-action@v3.3.0
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3.0.0
|
uses: docker/setup-qemu-action@v3.0.0
|
||||||
|
|
||||||
|
|
125
.github/workflows/ci.yml
vendored
125
.github/workflows/ci.yml
vendored
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
# yamllint disable-line rule:truthy
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [dev, beta, release]
|
branches: [dev, beta, release]
|
||||||
|
@ -11,6 +10,8 @@ on:
|
||||||
- "**"
|
- "**"
|
||||||
- "!.github/workflows/*.yml"
|
- "!.github/workflows/*.yml"
|
||||||
- ".github/workflows/ci.yml"
|
- ".github/workflows/ci.yml"
|
||||||
|
- "!.yamllint"
|
||||||
|
- "!.github/dependabot.yml"
|
||||||
merge_group:
|
merge_group:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
@ -40,12 +41,12 @@ jobs:
|
||||||
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
|
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
id: cache-venv
|
id: cache-venv
|
||||||
uses: actions/cache@v3.3.2
|
uses: actions/cache@v4.0.2
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
|
@ -166,7 +167,35 @@ jobs:
|
||||||
|
|
||||||
pytest:
|
pytest:
|
||||||
name: Run pytest
|
name: Run pytest
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
python-version:
|
||||||
|
- "3.9"
|
||||||
|
- "3.10"
|
||||||
|
- "3.11"
|
||||||
|
- "3.12"
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
- macOS-latest
|
||||||
|
- windows-latest
|
||||||
|
exclude:
|
||||||
|
# Minimize CI resource usage
|
||||||
|
# by only running the Python version
|
||||||
|
# version used for docker images on Windows and macOS
|
||||||
|
- python-version: "3.12"
|
||||||
|
os: windows-latest
|
||||||
|
- python-version: "3.10"
|
||||||
|
os: windows-latest
|
||||||
|
- python-version: "3.9"
|
||||||
|
os: windows-latest
|
||||||
|
- python-version: "3.12"
|
||||||
|
os: macOS-latest
|
||||||
|
- python-version: "3.10"
|
||||||
|
os: macOS-latest
|
||||||
|
- python-version: "3.9"
|
||||||
|
os: macOS-latest
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
|
@ -175,14 +204,24 @@ jobs:
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
- name: Register matcher
|
- name: Register matcher
|
||||||
run: echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
run: echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
||||||
- name: Run pytest
|
- name: Run pytest
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
run: |
|
||||||
|
./venv/Scripts/activate
|
||||||
|
pytest -vv --cov-report=xml --tb=native tests
|
||||||
|
- name: Run pytest
|
||||||
|
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest'
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
pytest -vv --tb=native tests
|
pytest -vv --cov-report=xml --tb=native tests
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
clang-format:
|
clang-format:
|
||||||
name: Check clang-format
|
name: Check clang-format
|
||||||
|
@ -327,7 +366,7 @@ jobs:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
uses: actions/cache@v3.3.2
|
uses: actions/cache@v4.0.2
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
|
@ -354,6 +393,70 @@ jobs:
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
|
list-components:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- common
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
|
steps:
|
||||||
|
- name: Check out code from GitHub
|
||||||
|
uses: actions/checkout@v4.1.1
|
||||||
|
with:
|
||||||
|
# Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works.
|
||||||
|
fetch-depth: 500
|
||||||
|
- name: Get target branch
|
||||||
|
id: target-branch
|
||||||
|
run: |
|
||||||
|
echo "branch=${{ github.event.pull_request.base.ref }}" >> $GITHUB_OUTPUT
|
||||||
|
- name: Fetch ${{ steps.target-branch.outputs.branch }} branch
|
||||||
|
run: |
|
||||||
|
git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +refs/heads/${{ steps.target-branch.outputs.branch }}:refs/remotes/origin/${{ steps.target-branch.outputs.branch }}
|
||||||
|
git merge-base refs/remotes/origin/${{ steps.target-branch.outputs.branch }} HEAD
|
||||||
|
- name: Restore Python
|
||||||
|
uses: ./.github/actions/restore-python
|
||||||
|
with:
|
||||||
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
|
- name: Find changed components
|
||||||
|
id: set-matrix
|
||||||
|
run: |
|
||||||
|
. venv/bin/activate
|
||||||
|
echo "matrix=$(script/list-components.py --changed --branch ${{ steps.target-branch.outputs.branch }} | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
test-build-components:
|
||||||
|
name: Component test ${{ matrix.file }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- common
|
||||||
|
- list-components
|
||||||
|
if: ${{ github.event_name == 'pull_request' && needs.list-components.outputs.matrix != '[]' && needs.list-components.outputs.matrix != '' }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
max-parallel: 2
|
||||||
|
matrix:
|
||||||
|
file: ${{ fromJson(needs.list-components.outputs.matrix) }}
|
||||||
|
steps:
|
||||||
|
- name: Install libsodium
|
||||||
|
run: sudo apt-get install libsodium-dev
|
||||||
|
|
||||||
|
- name: Check out code from GitHub
|
||||||
|
uses: actions/checkout@v4.1.1
|
||||||
|
- name: Restore Python
|
||||||
|
uses: ./.github/actions/restore-python
|
||||||
|
with:
|
||||||
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
|
- name: test_build_components -e config -c ${{ matrix.file }}
|
||||||
|
run: |
|
||||||
|
. venv/bin/activate
|
||||||
|
./script/test_build_components -e config -c ${{ matrix.file }}
|
||||||
|
- name: test_build_components -e compile -c ${{ matrix.file }}
|
||||||
|
run: |
|
||||||
|
. venv/bin/activate
|
||||||
|
./script/test_build_components -e compile -c ${{ matrix.file }}
|
||||||
|
|
||||||
ci-status:
|
ci-status:
|
||||||
name: CI Status
|
name: CI Status
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -368,6 +471,8 @@ jobs:
|
||||||
- pyupgrade
|
- pyupgrade
|
||||||
- compile-tests
|
- compile-tests
|
||||||
- clang-tidy
|
- clang-tidy
|
||||||
|
- test-build-components
|
||||||
|
- list-components
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- name: Success
|
- name: Success
|
||||||
|
@ -375,4 +480,8 @@ jobs:
|
||||||
run: exit 0
|
run: exit 0
|
||||||
- name: Failure
|
- name: Failure
|
||||||
if: ${{ contains(needs.*.result, 'failure') }}
|
if: ${{ contains(needs.*.result, 'failure') }}
|
||||||
run: exit 1
|
env:
|
||||||
|
JSON_DOC: ${{ toJSON(needs) }}
|
||||||
|
run: |
|
||||||
|
echo $JSON_DOC | jq
|
||||||
|
exit 1
|
||||||
|
|
1
.github/workflows/lock.yml
vendored
1
.github/workflows/lock.yml
vendored
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
name: Lock
|
name: Lock
|
||||||
|
|
||||||
# yamllint disable-line rule:truthy
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "30 0 * * *"
|
- cron: "30 0 * * *"
|
||||||
|
|
55
.github/workflows/release.yml
vendored
55
.github/workflows/release.yml
vendored
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
name: Publish Release
|
name: Publish Release
|
||||||
|
|
||||||
# yamllint disable-line rule:truthy
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
|
@ -18,14 +17,16 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
tag: ${{ steps.tag.outputs.tag }}
|
tag: ${{ steps.tag.outputs.tag }}
|
||||||
|
branch_build: ${{ steps.tag.outputs.branch_build }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.1
|
- uses: actions/checkout@v4.1.1
|
||||||
- name: Get tag
|
- name: Get tag
|
||||||
id: tag
|
id: tag
|
||||||
# yamllint disable rule:line-length
|
# yamllint disable rule:line-length
|
||||||
run: |
|
run: |
|
||||||
if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then
|
if [[ "${{ github.event_name }}" = "release" ]]; then
|
||||||
TAG="${GITHUB_REF#refs/tags/}"
|
TAG="${{ github.event.release.tag_name}}"
|
||||||
|
BRANCH_BUILD="false"
|
||||||
else
|
else
|
||||||
TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
|
TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
|
||||||
today="$(date --utc '+%Y%m%d')"
|
today="$(date --utc '+%Y%m%d')"
|
||||||
|
@ -33,34 +34,36 @@ jobs:
|
||||||
BRANCH=${GITHUB_REF#refs/heads/}
|
BRANCH=${GITHUB_REF#refs/heads/}
|
||||||
if [[ "$BRANCH" != "dev" ]]; then
|
if [[ "$BRANCH" != "dev" ]]; then
|
||||||
TAG="${TAG}-${BRANCH}"
|
TAG="${TAG}-${BRANCH}"
|
||||||
|
BRANCH_BUILD="true"
|
||||||
|
else
|
||||||
|
BRANCH_BUILD="false"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||||
|
echo "branch_build=${BRANCH_BUILD}" >> $GITHUB_OUTPUT
|
||||||
# yamllint enable rule:line-length
|
# yamllint enable rule:line-length
|
||||||
|
|
||||||
deploy-pypi:
|
deploy-pypi:
|
||||||
name: Build and publish to PyPi
|
name: Build and publish to PyPi
|
||||||
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.1
|
- uses: actions/checkout@v4.1.1
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Set up python environment
|
- name: Set up python environment
|
||||||
env:
|
env:
|
||||||
ESPHOME_NO_VENV: 1
|
ESPHOME_NO_VENV: 1
|
||||||
run: |
|
run: script/setup
|
||||||
script/setup
|
|
||||||
pip install twine
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: python setup.py sdist bdist_wheel
|
run: python setup.py sdist bdist_wheel
|
||||||
- name: Upload
|
- name: Publish
|
||||||
env:
|
uses: pypa/gh-action-pypi-publish@v1.8.14
|
||||||
TWINE_USERNAME: __token__
|
|
||||||
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
|
||||||
run: twine upload dist/*
|
|
||||||
|
|
||||||
deploy-docker:
|
deploy-docker:
|
||||||
name: Build ESPHome ${{ matrix.platform }}
|
name: Build ESPHome ${{ matrix.platform }}
|
||||||
|
@ -80,23 +83,23 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.1
|
- uses: actions/checkout@v4.1.1
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.0.0
|
uses: docker/setup-buildx-action@v3.3.0
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
if: matrix.platform != 'linux/amd64'
|
if: matrix.platform != 'linux/amd64'
|
||||||
uses: docker/setup-qemu-action@v3.0.0
|
uses: docker/setup-qemu-action@v3.0.0
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
uses: docker/login-action@v3.0.0
|
uses: docker/login-action@v3.1.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
uses: docker/login-action@v3.0.0
|
uses: docker/login-action@v3.1.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
@ -163,17 +166,17 @@ jobs:
|
||||||
name: digests-${{ matrix.image.target }}-${{ matrix.registry }}
|
name: digests-${{ matrix.image.target }}-${{ matrix.registry }}
|
||||||
path: /tmp/digests
|
path: /tmp/digests
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.0.0
|
uses: docker/setup-buildx-action@v3.3.0
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
if: matrix.registry == 'dockerhub'
|
if: matrix.registry == 'dockerhub'
|
||||||
uses: docker/login-action@v3.0.0
|
uses: docker/login-action@v3.1.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
if: matrix.registry == 'ghcr'
|
if: matrix.registry == 'ghcr'
|
||||||
uses: docker/login-action@v3.0.0
|
uses: docker/login-action@v3.1.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
@ -198,22 +201,28 @@ jobs:
|
||||||
$(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *)
|
$(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *)
|
||||||
|
|
||||||
deploy-ha-addon-repo:
|
deploy-ha-addon-repo:
|
||||||
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [deploy-manifest]
|
needs:
|
||||||
|
- init
|
||||||
|
- deploy-manifest
|
||||||
steps:
|
steps:
|
||||||
- name: Trigger Workflow
|
- name: Trigger Workflow
|
||||||
uses: actions/github-script@v7.0.1
|
uses: actions/github-script@v7.0.1
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
|
github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
|
let description = "ESPHome";
|
||||||
|
if (context.eventName == "release") {
|
||||||
|
description = ${{ toJSON(github.event.release.body) }};
|
||||||
|
}
|
||||||
github.rest.actions.createWorkflowDispatch({
|
github.rest.actions.createWorkflowDispatch({
|
||||||
owner: "esphome",
|
owner: "esphome",
|
||||||
repo: "home-assistant-addon",
|
repo: "home-assistant-addon",
|
||||||
workflow_id: "bump-version.yml",
|
workflow_id: "bump-version.yml",
|
||||||
ref: "main",
|
ref: "main",
|
||||||
inputs: {
|
inputs: {
|
||||||
version: "${{ github.event.release.tag_name }}",
|
version: "${{ needs.init.outputs.tag }}",
|
||||||
content: ${{ toJSON(github.event.release.body) }}
|
content: description
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
1
.github/workflows/stale.yml
vendored
1
.github/workflows/stale.yml
vendored
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
name: Stale
|
name: Stale
|
||||||
|
|
||||||
# yamllint disable-line rule:truthy
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "30 0 * * *"
|
- cron: "30 0 * * *"
|
||||||
|
|
6
.github/workflows/sync-device-classes.yml
vendored
6
.github/workflows/sync-device-classes.yml
vendored
|
@ -22,9 +22,9 @@ jobs:
|
||||||
path: lib/home-assistant
|
path: lib/home-assistant
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.11
|
python-version: 3.12
|
||||||
|
|
||||||
- name: Install Home Assistant
|
- name: Install Home Assistant
|
||||||
run: |
|
run: |
|
||||||
|
@ -36,7 +36,7 @@ jobs:
|
||||||
python ./script/sync-device_class.py
|
python ./script/sync-device_class.py
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: peter-evans/create-pull-request@v5.0.2
|
uses: peter-evans/create-pull-request@v6.0.4
|
||||||
with:
|
with:
|
||||||
commit-message: "Synchronise Device Classes from Home Assistant"
|
commit-message: "Synchronise Device Classes from Home Assistant"
|
||||||
committer: esphomebot <esphome@nabucasa.com>
|
committer: esphomebot <esphome@nabucasa.com>
|
||||||
|
|
5
.github/workflows/yaml-lint.yml
vendored
5
.github/workflows/yaml-lint.yml
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
---
|
||||||
name: YAML lint
|
name: YAML lint
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
@ -19,4 +20,6 @@ jobs:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Run yamllint
|
- name: Run yamllint
|
||||||
uses: frenck/action-yamllint@v1.4.2
|
uses: frenck/action-yamllint@v1.5.0
|
||||||
|
with:
|
||||||
|
strict: true
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||||
rev: 23.12.0
|
rev: 24.2.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
args:
|
args:
|
||||||
|
@ -27,7 +27,7 @@ repos:
|
||||||
- --branch=release
|
- --branch=release
|
||||||
- --branch=beta
|
- --branch=beta
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v3.15.0
|
rev: v3.15.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py39-plus]
|
args: [--py39-plus]
|
||||||
|
|
20
.yamllint
20
.yamllint
|
@ -1,3 +1,19 @@
|
||||||
---
|
---
|
||||||
ignore: |
|
extends: default
|
||||||
venv/
|
|
||||||
|
ignore-from-file: .gitignore
|
||||||
|
|
||||||
|
rules:
|
||||||
|
document-start: disable
|
||||||
|
empty-lines:
|
||||||
|
level: error
|
||||||
|
max: 1
|
||||||
|
max-start: 0
|
||||||
|
max-end: 1
|
||||||
|
indentation:
|
||||||
|
level: error
|
||||||
|
spaces: 2
|
||||||
|
indent-sequences: true
|
||||||
|
check-multi-line-strings: false
|
||||||
|
line-length: disable
|
||||||
|
truthy: disable
|
||||||
|
|
41
CODEOWNERS
41
CODEOWNERS
|
@ -18,15 +18,19 @@ esphome/components/ac_dimmer/* @glmnet
|
||||||
esphome/components/adc/* @esphome/core
|
esphome/components/adc/* @esphome/core
|
||||||
esphome/components/adc128s102/* @DeerMaximum
|
esphome/components/adc128s102/* @DeerMaximum
|
||||||
esphome/components/addressable_light/* @justfalter
|
esphome/components/addressable_light/* @justfalter
|
||||||
|
esphome/components/ade7880/* @kpfleming
|
||||||
esphome/components/ade7953/* @angelnu
|
esphome/components/ade7953/* @angelnu
|
||||||
esphome/components/ade7953_i2c/* @angelnu
|
esphome/components/ade7953_i2c/* @angelnu
|
||||||
esphome/components/ade7953_spi/* @angelnu
|
esphome/components/ade7953_spi/* @angelnu
|
||||||
|
esphome/components/ads1118/* @solomondg1
|
||||||
|
esphome/components/ags10/* @mak-42
|
||||||
esphome/components/airthings_ble/* @jeromelaban
|
esphome/components/airthings_ble/* @jeromelaban
|
||||||
esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau
|
esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau
|
||||||
esphome/components/airthings_wave_mini/* @ncareau
|
esphome/components/airthings_wave_mini/* @ncareau
|
||||||
esphome/components/airthings_wave_plus/* @jeromelaban
|
esphome/components/airthings_wave_plus/* @jeromelaban
|
||||||
esphome/components/alarm_control_panel/* @grahambrown11 @hwstar
|
esphome/components/alarm_control_panel/* @grahambrown11 @hwstar
|
||||||
esphome/components/alpha3/* @jan-hofmeier
|
esphome/components/alpha3/* @jan-hofmeier
|
||||||
|
esphome/components/am2315c/* @swoboda1337
|
||||||
esphome/components/am43/* @buxtronix
|
esphome/components/am43/* @buxtronix
|
||||||
esphome/components/am43/cover/* @buxtronix
|
esphome/components/am43/cover/* @buxtronix
|
||||||
esphome/components/am43/sensor/* @buxtronix
|
esphome/components/am43/sensor/* @buxtronix
|
||||||
|
@ -38,6 +42,7 @@ esphome/components/as5600/* @ammmze
|
||||||
esphome/components/as5600/sensor/* @ammmze
|
esphome/components/as5600/sensor/* @ammmze
|
||||||
esphome/components/as7341/* @mrgnr
|
esphome/components/as7341/* @mrgnr
|
||||||
esphome/components/async_tcp/* @OttoWinter
|
esphome/components/async_tcp/* @OttoWinter
|
||||||
|
esphome/components/at581x/* @X-Ryl669
|
||||||
esphome/components/atc_mithermometer/* @ahpohl
|
esphome/components/atc_mithermometer/* @ahpohl
|
||||||
esphome/components/atm90e26/* @danieltwagner
|
esphome/components/atm90e26/* @danieltwagner
|
||||||
esphome/components/b_parasite/* @rbaron
|
esphome/components/b_parasite/* @rbaron
|
||||||
|
@ -54,6 +59,8 @@ esphome/components/bl0940/* @tobias-
|
||||||
esphome/components/bl0942/* @dbuezas
|
esphome/components/bl0942/* @dbuezas
|
||||||
esphome/components/ble_client/* @buxtronix @clydebarrow
|
esphome/components/ble_client/* @buxtronix @clydebarrow
|
||||||
esphome/components/bluetooth_proxy/* @jesserockz
|
esphome/components/bluetooth_proxy/* @jesserockz
|
||||||
|
esphome/components/bme280_base/* @esphome/core
|
||||||
|
esphome/components/bme280_spi/* @apbodrov
|
||||||
esphome/components/bme680_bsec/* @trvrnrth
|
esphome/components/bme680_bsec/* @trvrnrth
|
||||||
esphome/components/bmi160/* @flaviut
|
esphome/components/bmi160/* @flaviut
|
||||||
esphome/components/bmp3xx/* @martgras
|
esphome/components/bmp3xx/* @martgras
|
||||||
|
@ -69,17 +76,22 @@ esphome/components/cd74hc4067/* @asoehlke
|
||||||
esphome/components/climate/* @esphome/core
|
esphome/components/climate/* @esphome/core
|
||||||
esphome/components/climate_ir/* @glmnet
|
esphome/components/climate_ir/* @glmnet
|
||||||
esphome/components/color_temperature/* @jesserockz
|
esphome/components/color_temperature/* @jesserockz
|
||||||
|
esphome/components/combination/* @Cat-Ion @kahrendt
|
||||||
esphome/components/coolix/* @glmnet
|
esphome/components/coolix/* @glmnet
|
||||||
esphome/components/copy/* @OttoWinter
|
esphome/components/copy/* @OttoWinter
|
||||||
esphome/components/cover/* @esphome/core
|
esphome/components/cover/* @esphome/core
|
||||||
esphome/components/cs5460a/* @balrog-kun
|
esphome/components/cs5460a/* @balrog-kun
|
||||||
esphome/components/cse7761/* @berfenger
|
esphome/components/cse7761/* @berfenger
|
||||||
|
esphome/components/cst226/* @clydebarrow
|
||||||
|
esphome/components/cst816/* @clydebarrow
|
||||||
esphome/components/ct_clamp/* @jesserockz
|
esphome/components/ct_clamp/* @jesserockz
|
||||||
esphome/components/current_based/* @djwmarcx
|
esphome/components/current_based/* @djwmarcx
|
||||||
esphome/components/dac7678/* @NickB1
|
esphome/components/dac7678/* @NickB1
|
||||||
|
esphome/components/daikin_arc/* @MagicBear
|
||||||
esphome/components/daikin_brc/* @hagak
|
esphome/components/daikin_brc/* @hagak
|
||||||
esphome/components/daly_bms/* @s1lvi0
|
esphome/components/daly_bms/* @s1lvi0
|
||||||
esphome/components/dashboard_import/* @esphome/core
|
esphome/components/dashboard_import/* @esphome/core
|
||||||
|
esphome/components/datetime/* @jesserockz @rfdarter
|
||||||
esphome/components/debug/* @OttoWinter
|
esphome/components/debug/* @OttoWinter
|
||||||
esphome/components/delonghi/* @grob6000
|
esphome/components/delonghi/* @grob6000
|
||||||
esphome/components/dfplayer/* @glmnet
|
esphome/components/dfplayer/* @glmnet
|
||||||
|
@ -93,6 +105,7 @@ esphome/components/duty_time/* @dudanov
|
||||||
esphome/components/ee895/* @Stock-M
|
esphome/components/ee895/* @Stock-M
|
||||||
esphome/components/ektf2232/touchscreen/* @jesserockz
|
esphome/components/ektf2232/touchscreen/* @jesserockz
|
||||||
esphome/components/emc2101/* @ellull
|
esphome/components/emc2101/* @ellull
|
||||||
|
esphome/components/emmeti/* @E440QF
|
||||||
esphome/components/ens160/* @vincentscode
|
esphome/components/ens160/* @vincentscode
|
||||||
esphome/components/ens210/* @itn3rd77
|
esphome/components/ens210/* @itn3rd77
|
||||||
esphome/components/esp32/* @esphome/core
|
esphome/components/esp32/* @esphome/core
|
||||||
|
@ -102,6 +115,7 @@ esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz
|
||||||
esphome/components/esp32_camera_web_server/* @ayufan
|
esphome/components/esp32_camera_web_server/* @ayufan
|
||||||
esphome/components/esp32_can/* @Sympatron
|
esphome/components/esp32_can/* @Sympatron
|
||||||
esphome/components/esp32_improv/* @jesserockz
|
esphome/components/esp32_improv/* @jesserockz
|
||||||
|
esphome/components/esp32_rmt/* @jesserockz
|
||||||
esphome/components/esp32_rmt_led_strip/* @jesserockz
|
esphome/components/esp32_rmt_led_strip/* @jesserockz
|
||||||
esphome/components/esp8266/* @esphome/core
|
esphome/components/esp8266/* @esphome/core
|
||||||
esphome/components/ethernet_info/* @gtjadsonsantos
|
esphome/components/ethernet_info/* @gtjadsonsantos
|
||||||
|
@ -111,7 +125,8 @@ esphome/components/ezo_pmp/* @carlos-sarmiento
|
||||||
esphome/components/factory_reset/* @anatoly-savchenkov
|
esphome/components/factory_reset/* @anatoly-savchenkov
|
||||||
esphome/components/fastled_base/* @OttoWinter
|
esphome/components/fastled_base/* @OttoWinter
|
||||||
esphome/components/feedback/* @ianchi
|
esphome/components/feedback/* @ianchi
|
||||||
esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
esphome/components/fingerprint_grow/* @OnFreund @alexborro @loongyh
|
||||||
|
esphome/components/font/* @clydebarrow @esphome/core
|
||||||
esphome/components/fs3000/* @kahrendt
|
esphome/components/fs3000/* @kahrendt
|
||||||
esphome/components/ft5x06/* @clydebarrow
|
esphome/components/ft5x06/* @clydebarrow
|
||||||
esphome/components/ft63x6/* @gpambrozio
|
esphome/components/ft63x6/* @gpambrozio
|
||||||
|
@ -135,11 +150,13 @@ esphome/components/heatpumpir/* @rob-deutsch
|
||||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||||
esphome/components/hm3301/* @freekode
|
esphome/components/hm3301/* @freekode
|
||||||
esphome/components/homeassistant/* @OttoWinter
|
esphome/components/homeassistant/* @OttoWinter
|
||||||
|
esphome/components/honeywell_hih_i2c/* @Benichou34
|
||||||
esphome/components/honeywellabp/* @RubyBailey
|
esphome/components/honeywellabp/* @RubyBailey
|
||||||
esphome/components/honeywellabp2_i2c/* @jpfaff
|
esphome/components/honeywellabp2_i2c/* @jpfaff
|
||||||
esphome/components/host/* @esphome/core
|
esphome/components/host/* @esphome/core
|
||||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||||
esphome/components/hte501/* @Stock-M
|
esphome/components/hte501/* @Stock-M
|
||||||
|
esphome/components/htu31d/* @betterengineering
|
||||||
esphome/components/hydreon_rgxx/* @functionpointer
|
esphome/components/hydreon_rgxx/* @functionpointer
|
||||||
esphome/components/hyt271/* @Philippe12
|
esphome/components/hyt271/* @Philippe12
|
||||||
esphome/components/i2c/* @esphome/core
|
esphome/components/i2c/* @esphome/core
|
||||||
|
@ -151,14 +168,16 @@ esphome/components/iaqcore/* @yozik04
|
||||||
esphome/components/ili9xxx/* @clydebarrow @nielsnl68
|
esphome/components/ili9xxx/* @clydebarrow @nielsnl68
|
||||||
esphome/components/improv_base/* @esphome/core
|
esphome/components/improv_base/* @esphome/core
|
||||||
esphome/components/improv_serial/* @esphome/core
|
esphome/components/improv_serial/* @esphome/core
|
||||||
|
esphome/components/ina226/* @Sergio303 @latonita
|
||||||
esphome/components/ina260/* @mreditor97
|
esphome/components/ina260/* @mreditor97
|
||||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
||||||
esphome/components/inkplate6/* @jesserockz
|
esphome/components/inkplate6/* @jesserockz
|
||||||
esphome/components/integration/* @OttoWinter
|
esphome/components/integration/* @OttoWinter
|
||||||
esphome/components/internal_temperature/* @Mat931
|
esphome/components/internal_temperature/* @Mat931
|
||||||
esphome/components/interval/* @esphome/core
|
esphome/components/interval/* @esphome/core
|
||||||
|
esphome/components/jsn_sr04t/* @Mafus1
|
||||||
esphome/components/json/* @OttoWinter
|
esphome/components/json/* @OttoWinter
|
||||||
esphome/components/kalman_combinator/* @Cat-Ion
|
esphome/components/kamstrup_kmp/* @cfeenstra1024
|
||||||
esphome/components/key_collector/* @ssieb
|
esphome/components/key_collector/* @ssieb
|
||||||
esphome/components/key_provider/* @ssieb
|
esphome/components/key_provider/* @ssieb
|
||||||
esphome/components/kuntze/* @ssieb
|
esphome/components/kuntze/* @ssieb
|
||||||
|
@ -196,6 +215,7 @@ esphome/components/mcp9808/* @k7hpn
|
||||||
esphome/components/md5/* @esphome/core
|
esphome/components/md5/* @esphome/core
|
||||||
esphome/components/mdns/* @esphome/core
|
esphome/components/mdns/* @esphome/core
|
||||||
esphome/components/media_player/* @jesserockz
|
esphome/components/media_player/* @jesserockz
|
||||||
|
esphome/components/micro_wake_word/* @jesserockz @kahrendt
|
||||||
esphome/components/micronova/* @jorre05
|
esphome/components/micronova/* @jorre05
|
||||||
esphome/components/microphone/* @jesserockz
|
esphome/components/microphone/* @jesserockz
|
||||||
esphome/components/mics_4514/* @jesserockz
|
esphome/components/mics_4514/* @jesserockz
|
||||||
|
@ -219,13 +239,14 @@ esphome/components/mopeka_pro_check/* @spbrogan
|
||||||
esphome/components/mopeka_std_check/* @Fabian-Schmidt
|
esphome/components/mopeka_std_check/* @Fabian-Schmidt
|
||||||
esphome/components/mpl3115a2/* @kbickar
|
esphome/components/mpl3115a2/* @kbickar
|
||||||
esphome/components/mpu6886/* @fabaff
|
esphome/components/mpu6886/* @fabaff
|
||||||
|
esphome/components/ms8607/* @e28eta
|
||||||
esphome/components/network/* @esphome/core
|
esphome/components/network/* @esphome/core
|
||||||
esphome/components/nextion/* @senexcrenshaw
|
esphome/components/nextion/* @senexcrenshaw
|
||||||
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
||||||
esphome/components/nextion/sensor/* @senexcrenshaw
|
esphome/components/nextion/sensor/* @senexcrenshaw
|
||||||
esphome/components/nextion/switch/* @senexcrenshaw
|
esphome/components/nextion/switch/* @senexcrenshaw
|
||||||
esphome/components/nextion/text_sensor/* @senexcrenshaw
|
esphome/components/nextion/text_sensor/* @senexcrenshaw
|
||||||
esphome/components/nfc/* @jesserockz
|
esphome/components/nfc/* @jesserockz @kbx81
|
||||||
esphome/components/noblex/* @AGalfra
|
esphome/components/noblex/* @AGalfra
|
||||||
esphome/components/number/* @esphome/core
|
esphome/components/number/* @esphome/core
|
||||||
esphome/components/ota/* @esphome/core
|
esphome/components/ota/* @esphome/core
|
||||||
|
@ -255,6 +276,7 @@ esphome/components/pvvx_mithermometer/* @pasiz
|
||||||
esphome/components/pylontech/* @functionpointer
|
esphome/components/pylontech/* @functionpointer
|
||||||
esphome/components/qmp6988/* @andrewpc
|
esphome/components/qmp6988/* @andrewpc
|
||||||
esphome/components/qr_code/* @wjtje
|
esphome/components/qr_code/* @wjtje
|
||||||
|
esphome/components/qspi_amoled/* @clydebarrow
|
||||||
esphome/components/qwiic_pir/* @kahrendt
|
esphome/components/qwiic_pir/* @kahrendt
|
||||||
esphome/components/radon_eye_ble/* @jeffeb3
|
esphome/components/radon_eye_ble/* @jeffeb3
|
||||||
esphome/components/radon_eye_rd200/* @jeffeb3
|
esphome/components/radon_eye_rd200/* @jeffeb3
|
||||||
|
@ -268,6 +290,7 @@ esphome/components/rgbct/* @jesserockz
|
||||||
esphome/components/rp2040/* @jesserockz
|
esphome/components/rp2040/* @jesserockz
|
||||||
esphome/components/rp2040_pio_led_strip/* @Papa-DMan
|
esphome/components/rp2040_pio_led_strip/* @Papa-DMan
|
||||||
esphome/components/rp2040_pwm/* @jesserockz
|
esphome/components/rp2040_pwm/* @jesserockz
|
||||||
|
esphome/components/rpi_dpi_rgb/* @clydebarrow
|
||||||
esphome/components/rtl87xx/* @kuba2k2
|
esphome/components/rtl87xx/* @kuba2k2
|
||||||
esphome/components/rtttl/* @glmnet
|
esphome/components/rtttl/* @glmnet
|
||||||
esphome/components/safe_mode/* @jsuanet @paulmonigatti
|
esphome/components/safe_mode/* @jsuanet @paulmonigatti
|
||||||
|
@ -275,6 +298,7 @@ esphome/components/scd4x/* @martgras @sjtrny
|
||||||
esphome/components/script/* @esphome/core
|
esphome/components/script/* @esphome/core
|
||||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||||
esphome/components/sdp3x/* @Azimath
|
esphome/components/sdp3x/* @Azimath
|
||||||
|
esphome/components/seeed_mr24hpc1/* @limengdu
|
||||||
esphome/components/selec_meter/* @sourabhjaiswal
|
esphome/components/selec_meter/* @sourabhjaiswal
|
||||||
esphome/components/select/* @esphome/core
|
esphome/components/select/* @esphome/core
|
||||||
esphome/components/sen0321/* @notjj
|
esphome/components/sen0321/* @notjj
|
||||||
|
@ -286,6 +310,7 @@ esphome/components/sfa30/* @ghsensdev
|
||||||
esphome/components/sgp40/* @SenexCrenshaw
|
esphome/components/sgp40/* @SenexCrenshaw
|
||||||
esphome/components/sgp4x/* @SenexCrenshaw @martgras
|
esphome/components/sgp4x/* @SenexCrenshaw @martgras
|
||||||
esphome/components/shelly_dimmer/* @edge90 @rnauber
|
esphome/components/shelly_dimmer/* @edge90 @rnauber
|
||||||
|
esphome/components/sht3xd/* @mrtoy-me
|
||||||
esphome/components/sht4x/* @sjtrny
|
esphome/components/sht4x/* @sjtrny
|
||||||
esphome/components/shutdown/* @esphome/core @jsuanet
|
esphome/components/shutdown/* @esphome/core @jsuanet
|
||||||
esphome/components/sigma_delta_output/* @Cat-Ion
|
esphome/components/sigma_delta_output/* @Cat-Ion
|
||||||
|
@ -319,11 +344,13 @@ esphome/components/ssd1351_spi/* @kbx81
|
||||||
esphome/components/st7567_base/* @latonita
|
esphome/components/st7567_base/* @latonita
|
||||||
esphome/components/st7567_i2c/* @latonita
|
esphome/components/st7567_i2c/* @latonita
|
||||||
esphome/components/st7567_spi/* @latonita
|
esphome/components/st7567_spi/* @latonita
|
||||||
|
esphome/components/st7701s/* @clydebarrow
|
||||||
esphome/components/st7735/* @SenexCrenshaw
|
esphome/components/st7735/* @SenexCrenshaw
|
||||||
esphome/components/st7789v/* @kbx81
|
esphome/components/st7789v/* @kbx81
|
||||||
esphome/components/st7920/* @marsjan155
|
esphome/components/st7920/* @marsjan155
|
||||||
esphome/components/substitutions/* @esphome/core
|
esphome/components/substitutions/* @esphome/core
|
||||||
esphome/components/sun/* @OttoWinter
|
esphome/components/sun/* @OttoWinter
|
||||||
|
esphome/components/sun_gtil2/* @Mat931
|
||||||
esphome/components/switch/* @esphome/core
|
esphome/components/switch/* @esphome/core
|
||||||
esphome/components/t6615/* @tylermenezes
|
esphome/components/t6615/* @tylermenezes
|
||||||
esphome/components/tca9548a/* @andreashergert1984
|
esphome/components/tca9548a/* @andreashergert1984
|
||||||
|
@ -331,10 +358,13 @@ esphome/components/tcl112/* @glmnet
|
||||||
esphome/components/tee501/* @Stock-M
|
esphome/components/tee501/* @Stock-M
|
||||||
esphome/components/teleinfo/* @0hax
|
esphome/components/teleinfo/* @0hax
|
||||||
esphome/components/template/alarm_control_panel/* @grahambrown11 @hwstar
|
esphome/components/template/alarm_control_panel/* @grahambrown11 @hwstar
|
||||||
|
esphome/components/template/datetime/* @rfdarter
|
||||||
|
esphome/components/template/fan/* @ssieb
|
||||||
esphome/components/text/* @mauritskorse
|
esphome/components/text/* @mauritskorse
|
||||||
esphome/components/thermostat/* @kbx81
|
esphome/components/thermostat/* @kbx81
|
||||||
esphome/components/time/* @OttoWinter
|
esphome/components/time/* @OttoWinter
|
||||||
esphome/components/tlc5947/* @rnauber
|
esphome/components/tlc5947/* @rnauber
|
||||||
|
esphome/components/tlc5971/* @IJIJI
|
||||||
esphome/components/tm1621/* @Philippe12
|
esphome/components/tm1621/* @Philippe12
|
||||||
esphome/components/tm1637/* @glmnet
|
esphome/components/tm1637/* @glmnet
|
||||||
esphome/components/tm1638/* @skykingjwc
|
esphome/components/tm1638/* @skykingjwc
|
||||||
|
@ -359,10 +389,14 @@ esphome/components/uart/button/* @ssieb
|
||||||
esphome/components/ufire_ec/* @pvizeli
|
esphome/components/ufire_ec/* @pvizeli
|
||||||
esphome/components/ufire_ise/* @pvizeli
|
esphome/components/ufire_ise/* @pvizeli
|
||||||
esphome/components/ultrasonic/* @OttoWinter
|
esphome/components/ultrasonic/* @OttoWinter
|
||||||
|
esphome/components/uponor_smatrix/* @kroimon
|
||||||
esphome/components/vbus/* @ssieb
|
esphome/components/vbus/* @ssieb
|
||||||
|
esphome/components/veml3235/* @kbx81
|
||||||
|
esphome/components/veml7700/* @latonita
|
||||||
esphome/components/version/* @esphome/core
|
esphome/components/version/* @esphome/core
|
||||||
esphome/components/voice_assistant/* @jesserockz
|
esphome/components/voice_assistant/* @jesserockz
|
||||||
esphome/components/wake_on_lan/* @willwill2will54
|
esphome/components/wake_on_lan/* @willwill2will54
|
||||||
|
esphome/components/waveshare_epaper/* @clydebarrow
|
||||||
esphome/components/web_server_base/* @OttoWinter
|
esphome/components/web_server_base/* @OttoWinter
|
||||||
esphome/components/web_server_idf/* @dentra
|
esphome/components/web_server_idf/* @dentra
|
||||||
esphome/components/whirlpool/* @glmnet
|
esphome/components/whirlpool/* @glmnet
|
||||||
|
@ -372,6 +406,7 @@ esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard
|
||||||
esphome/components/wl_134/* @hobbypunk90
|
esphome/components/wl_134/* @hobbypunk90
|
||||||
esphome/components/x9c/* @EtienneMD
|
esphome/components/x9c/* @EtienneMD
|
||||||
esphome/components/xgzp68xx/* @gcormier
|
esphome/components/xgzp68xx/* @gcormier
|
||||||
|
esphome/components/xiaomi_hhccjcy10/* @fariouche
|
||||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
||||||
esphome/components/xiaomi_mhoc303/* @drug123
|
esphome/components/xiaomi_mhoc303/* @drug123
|
||||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
||||||
|
|
|
@ -35,7 +35,7 @@ RUN \
|
||||||
iputils-ping=3:20221126-1 \
|
iputils-ping=3:20221126-1 \
|
||||||
git=1:2.39.2-1.1 \
|
git=1:2.39.2-1.1 \
|
||||||
curl=7.88.1-10+deb12u5 \
|
curl=7.88.1-10+deb12u5 \
|
||||||
openssh-client=1:9.2p1-2+deb12u1 \
|
openssh-client=1:9.2p1-2+deb12u2 \
|
||||||
python3-cffi=1.15.1-5 \
|
python3-cffi=1.15.1-5 \
|
||||||
libcairo2=1.16.0-7 \
|
libcairo2=1.16.0-7 \
|
||||||
libmagic1=1:5.44-3 \
|
libmagic1=1:5.44-3 \
|
||||||
|
@ -81,7 +81,7 @@ RUN \
|
||||||
fi; \
|
fi; \
|
||||||
pip3 install \
|
pip3 install \
|
||||||
--break-system-packages --no-cache-dir \
|
--break-system-packages --no-cache-dir \
|
||||||
platformio==6.1.11 \
|
platformio==6.1.13 \
|
||||||
# Change some platformio settings
|
# Change some platformio settings
|
||||||
&& platformio settings set enable_telemetry No \
|
&& platformio settings set enable_telemetry No \
|
||||||
&& platformio settings set check_platformio_interval 1000000 \
|
&& platformio settings set check_platformio_interval 1000000 \
|
||||||
|
|
|
@ -21,4 +21,10 @@ export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
|
||||||
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
|
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
|
||||||
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
|
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
|
||||||
|
|
||||||
|
# If /build is mounted, use that as the build path
|
||||||
|
# otherwise use path in /config (so that builds aren't lost on container restart)
|
||||||
|
if [[ -d /build ]]; then
|
||||||
|
export ESPHOME_BUILD_PATH=/build
|
||||||
|
fi
|
||||||
|
|
||||||
exec esphome "$@"
|
exec esphome "$@"
|
||||||
|
|
47
docker/ha-addon-rootfs/etc/cont-init.d/30-esphome-fork.sh
Executable file
47
docker/ha-addon-rootfs/etc/cont-init.d/30-esphome-fork.sh
Executable file
|
@ -0,0 +1,47 @@
|
||||||
|
#!/usr/bin/with-contenv bashio
|
||||||
|
# ==============================================================================
|
||||||
|
# This file installs the user ESPHome fork if specified.
|
||||||
|
# The fork must be up to date with the latest ESPHome dev branch
|
||||||
|
# and have no conflicts.
|
||||||
|
# This config option only exists in the ESPHome Dev add-on.
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
declare esphome_fork
|
||||||
|
|
||||||
|
if bashio::config.has_value 'esphome_fork'; then
|
||||||
|
esphome_fork=$(bashio::config 'esphome_fork')
|
||||||
|
# format: [username][/repository]:ref
|
||||||
|
if [[ "$esphome_fork" =~ ^(([^/]+)(/([^:]+))?:)?([^:/]+)$ ]]; then
|
||||||
|
username="${BASH_REMATCH[2]:-esphome}"
|
||||||
|
repository="${BASH_REMATCH[4]:-esphome}"
|
||||||
|
ref="${BASH_REMATCH[5]}"
|
||||||
|
else
|
||||||
|
bashio::exit.nok "Invalid esphome_fork format: $esphome_fork"
|
||||||
|
fi
|
||||||
|
full_url="https://github.com/${username}/${repository}/archive/${ref}.tar.gz"
|
||||||
|
bashio::log.info "Checking forked ESPHome"
|
||||||
|
dev_version=$(python3 -c "from esphome.const import __version__; print(__version__)")
|
||||||
|
bashio::log.info "Downloading ESPHome from fork '${esphome_fork}' (${full_url})..."
|
||||||
|
curl -L -o /tmp/esphome.tar.gz "${full_url}" -qq ||
|
||||||
|
bashio::exit.nok "Failed downloading ESPHome fork."
|
||||||
|
bashio::log.info "Installing ESPHome from fork '${esphome_fork}' (${full_url})..."
|
||||||
|
rm -rf /esphome || bashio::exit.nok "Failed to remove ESPHome."
|
||||||
|
mkdir /esphome
|
||||||
|
tar -zxf /tmp/esphome.tar.gz -C /esphome --strip-components=1 ||
|
||||||
|
bashio::exit.nok "Failed installing ESPHome from fork."
|
||||||
|
pip install -U -e /esphome || bashio::exit.nok "Failed installing ESPHome from fork."
|
||||||
|
rm -f /tmp/esphome.tar.gz
|
||||||
|
fork_version=$(python3 -c "from esphome.const import __version__; print(__version__)")
|
||||||
|
|
||||||
|
if [[ "$fork_version" != "$dev_version" ]]; then
|
||||||
|
bashio::log.error "############################"
|
||||||
|
bashio::log.error "Uninstalled fork as version does not match"
|
||||||
|
bashio::log.error "Update (or ask the author to update) the branch"
|
||||||
|
bashio::log.error "This is important as the dev addon and the dev ESPHome"
|
||||||
|
bashio::log.error "branch can have changes that are not compatible with old forks"
|
||||||
|
bashio::log.error "and get reported as bugs which we cannot solve easily."
|
||||||
|
bashio::log.error "############################"
|
||||||
|
bashio::exit.nok
|
||||||
|
fi
|
||||||
|
bashio::log.info "Installed ESPHome from fork '${esphome_fork}' (${full_url})..."
|
||||||
|
fi
|
|
@ -297,8 +297,27 @@ def upload_using_platformio(config, port):
|
||||||
return platformio_api.run_platformio_cli_run(config, CORE.verbose, *upload_args)
|
return platformio_api.run_platformio_cli_run(config, CORE.verbose, *upload_args)
|
||||||
|
|
||||||
|
|
||||||
|
def check_permissions(port):
|
||||||
|
if os.name == "posix" and get_port_type(port) == "SERIAL":
|
||||||
|
# Check if we can open selected serial port
|
||||||
|
if not os.access(port, os.F_OK):
|
||||||
|
raise EsphomeError(
|
||||||
|
"The selected serial port does not exist. To resolve this issue, "
|
||||||
|
"check that the device is connected to this computer with a USB cable and that "
|
||||||
|
"the USB cable can be used for data and is not a power-only cable."
|
||||||
|
)
|
||||||
|
if not (os.access(port, os.R_OK | os.W_OK)):
|
||||||
|
raise EsphomeError(
|
||||||
|
"You do not have read or write permission on the selected serial port. "
|
||||||
|
"To resolve this issue, you can add your user to the dialout group "
|
||||||
|
f"by running the following command: sudo usermod -a -G dialout {os.getlogin()}. "
|
||||||
|
"You will need to log out & back in or reboot to activate the new group access."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def upload_program(config, args, host):
|
def upload_program(config, args, host):
|
||||||
if get_port_type(host) == "SERIAL":
|
if get_port_type(host) == "SERIAL":
|
||||||
|
check_permissions(host)
|
||||||
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
|
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
|
||||||
file = getattr(args, "file", None)
|
file = getattr(args, "file", None)
|
||||||
return upload_using_esptool(config, host, file)
|
return upload_using_esptool(config, host, file)
|
||||||
|
@ -344,6 +363,7 @@ def show_logs(config, args, port):
|
||||||
if "logger" not in config:
|
if "logger" not in config:
|
||||||
raise EsphomeError("Logger is not configured!")
|
raise EsphomeError("Logger is not configured!")
|
||||||
if get_port_type(port) == "SERIAL":
|
if get_port_type(port) == "SERIAL":
|
||||||
|
check_permissions(port)
|
||||||
return run_miniterm(config, port)
|
return run_miniterm(config, port)
|
||||||
if get_port_type(port) == "NETWORK" and "api" in config:
|
if get_port_type(port) == "NETWORK" and "api" in config:
|
||||||
if config[CONF_MDNS][CONF_DISABLED] and CONF_MQTT in config:
|
if config[CONF_MDNS][CONF_DISABLED] and CONF_MQTT in config:
|
||||||
|
@ -765,6 +785,7 @@ def parse_args(argv):
|
||||||
parser_logs = subparsers.add_parser(
|
parser_logs = subparsers.add_parser(
|
||||||
"logs",
|
"logs",
|
||||||
help="Validate the configuration and show all logs.",
|
help="Validate the configuration and show all logs.",
|
||||||
|
aliases=["log"],
|
||||||
parents=[mqtt_options],
|
parents=[mqtt_options],
|
||||||
)
|
)
|
||||||
parser_logs.add_argument(
|
parser_logs.add_argument(
|
||||||
|
|
|
@ -87,4 +87,5 @@ from esphome.cpp_types import ( # noqa
|
||||||
gpio_Flags,
|
gpio_Flags,
|
||||||
EntityCategory,
|
EntityCategory,
|
||||||
Parented,
|
Parented,
|
||||||
|
ESPTime,
|
||||||
)
|
)
|
||||||
|
|
|
@ -139,6 +139,9 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
|
||||||
VARIANT_ESP32C3: {
|
VARIANT_ESP32C3: {
|
||||||
5: adc2_channel_t.ADC2_CHANNEL_0,
|
5: adc2_channel_t.ADC2_CHANNEL_0,
|
||||||
},
|
},
|
||||||
|
VARIANT_ESP32C2: {},
|
||||||
|
VARIANT_ESP32C6: {},
|
||||||
|
VARIANT_ESP32H2: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
1
esphome/components/ade7880/__init__.py
Normal file
1
esphome/components/ade7880/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@kpfleming"]
|
302
esphome/components/ade7880/ade7880.cpp
Normal file
302
esphome/components/ade7880/ade7880.cpp
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
// This component was developed using knowledge gathered by a number
|
||||||
|
// of people who reverse-engineered the Shelly 3EM:
|
||||||
|
//
|
||||||
|
// @AndreKR on GitHub
|
||||||
|
// Axel (@Axel830 on GitHub)
|
||||||
|
// Marko (@goodkiller on GitHub)
|
||||||
|
// Michaël Piron (@michaelpiron on GitHub)
|
||||||
|
// Theo Arends (@arendst on GitHub)
|
||||||
|
|
||||||
|
#include "ade7880.h"
|
||||||
|
#include "ade7880_registers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ade7880 {
|
||||||
|
|
||||||
|
static const char *const TAG = "ade7880";
|
||||||
|
|
||||||
|
void IRAM_ATTR ADE7880Store::gpio_intr(ADE7880Store *arg) { arg->reset_done = true; }
|
||||||
|
|
||||||
|
void ADE7880::setup() {
|
||||||
|
if (this->irq0_pin_ != nullptr) {
|
||||||
|
this->irq0_pin_->setup();
|
||||||
|
}
|
||||||
|
this->irq1_pin_->setup();
|
||||||
|
if (this->reset_pin_ != nullptr) {
|
||||||
|
this->reset_pin_->setup();
|
||||||
|
}
|
||||||
|
this->store_.irq1_pin = this->irq1_pin_->to_isr();
|
||||||
|
this->irq1_pin_->attach_interrupt(ADE7880Store::gpio_intr, &this->store_, gpio::INTERRUPT_FALLING_EDGE);
|
||||||
|
|
||||||
|
// if IRQ1 is already asserted, the cause must be determined
|
||||||
|
if (this->irq1_pin_->digital_read() == 0) {
|
||||||
|
ESP_LOGD(TAG, "IRQ1 found asserted during setup()");
|
||||||
|
auto status1 = read_u32_register16_(STATUS1);
|
||||||
|
if ((status1 & ~STATUS1_RSTDONE) != 0) {
|
||||||
|
// not safe to proceed, must initiate reset
|
||||||
|
ESP_LOGD(TAG, "IRQ1 asserted for !RSTDONE, resetting device");
|
||||||
|
this->reset_device_();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((status1 & STATUS1_RSTDONE) == STATUS1_RSTDONE) {
|
||||||
|
// safe to proceed, device has just completed reset cycle
|
||||||
|
ESP_LOGD(TAG, "Acknowledging RSTDONE");
|
||||||
|
this->write_u32_register16_(STATUS0, 0xFFFF);
|
||||||
|
this->write_u32_register16_(STATUS1, 0xFFFF);
|
||||||
|
this->init_device_();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->reset_device_();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::loop() {
|
||||||
|
// check for completion of a reset cycle
|
||||||
|
if (!this->store_.reset_done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Acknowledging RSTDONE");
|
||||||
|
this->write_u32_register16_(STATUS0, 0xFFFF);
|
||||||
|
this->write_u32_register16_(STATUS1, 0xFFFF);
|
||||||
|
this->init_device_();
|
||||||
|
this->store_.reset_done = false;
|
||||||
|
this->store_.reset_pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void ADE7880::update_sensor_from_s24zp_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f) {
|
||||||
|
if (sensor == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float val = this->read_s24zp_register16_(a_register);
|
||||||
|
sensor->publish_state(f(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void ADE7880::update_sensor_from_s16_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f) {
|
||||||
|
if (sensor == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float val = this->read_s16_register16_(a_register);
|
||||||
|
sensor->publish_state(f(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void ADE7880::update_sensor_from_s32_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f) {
|
||||||
|
if (sensor == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float val = this->read_s32_register16_(a_register);
|
||||||
|
sensor->publish_state(f(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::update() {
|
||||||
|
if (this->store_.reset_pending) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto start = millis();
|
||||||
|
|
||||||
|
if (this->channel_n_ != nullptr) {
|
||||||
|
auto *chan = this->channel_n_;
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->current, NIRMS, [](float val) { return val / 100000.0f; });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->channel_a_ != nullptr) {
|
||||||
|
auto *chan = this->channel_a_;
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->current, AIRMS, [](float val) { return val / 100000.0f; });
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->voltage, BVRMS, [](float val) { return val / 10000.0f; });
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->active_power, AWATT, [](float val) { return val / 100.0f; });
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->apparent_power, AVA, [](float val) { return val / 100.0f; });
|
||||||
|
this->update_sensor_from_s16_register16_(chan->power_factor, APF,
|
||||||
|
[](float val) { return std::abs(val / -327.68f); });
|
||||||
|
this->update_sensor_from_s32_register16_(chan->forward_active_energy, AFWATTHR, [&chan](float val) {
|
||||||
|
return chan->forward_active_energy_total += val / 14400.0f;
|
||||||
|
});
|
||||||
|
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, AFWATTHR, [&chan](float val) {
|
||||||
|
return chan->reverse_active_energy_total += val / 14400.0f;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->channel_b_ != nullptr) {
|
||||||
|
auto *chan = this->channel_b_;
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->current, BIRMS, [](float val) { return val / 100000.0f; });
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->voltage, BVRMS, [](float val) { return val / 10000.0f; });
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->active_power, BWATT, [](float val) { return val / 100.0f; });
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->apparent_power, BVA, [](float val) { return val / 100.0f; });
|
||||||
|
this->update_sensor_from_s16_register16_(chan->power_factor, BPF,
|
||||||
|
[](float val) { return std::abs(val / -327.68f); });
|
||||||
|
this->update_sensor_from_s32_register16_(chan->forward_active_energy, BFWATTHR, [&chan](float val) {
|
||||||
|
return chan->forward_active_energy_total += val / 14400.0f;
|
||||||
|
});
|
||||||
|
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, BFWATTHR, [&chan](float val) {
|
||||||
|
return chan->reverse_active_energy_total += val / 14400.0f;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->channel_c_ != nullptr) {
|
||||||
|
auto *chan = this->channel_c_;
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->current, CIRMS, [](float val) { return val / 100000.0f; });
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->voltage, CVRMS, [](float val) { return val / 10000.0f; });
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->active_power, CWATT, [](float val) { return val / 100.0f; });
|
||||||
|
this->update_sensor_from_s24zp_register16_(chan->apparent_power, CVA, [](float val) { return val / 100.0f; });
|
||||||
|
this->update_sensor_from_s16_register16_(chan->power_factor, CPF,
|
||||||
|
[](float val) { return std::abs(val / -327.68f); });
|
||||||
|
this->update_sensor_from_s32_register16_(chan->forward_active_energy, CFWATTHR, [&chan](float val) {
|
||||||
|
return chan->forward_active_energy_total += val / 14400.0f;
|
||||||
|
});
|
||||||
|
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, CFWATTHR, [&chan](float val) {
|
||||||
|
return chan->reverse_active_energy_total += val / 14400.0f;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "update took %u ms", millis() - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "ADE7880:");
|
||||||
|
LOG_PIN(" IRQ0 Pin: ", this->irq0_pin_);
|
||||||
|
LOG_PIN(" IRQ1 Pin: ", this->irq1_pin_);
|
||||||
|
LOG_PIN(" RESET Pin: ", this->reset_pin_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_);
|
||||||
|
|
||||||
|
if (this->channel_a_ != nullptr) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Phase A:");
|
||||||
|
LOG_SENSOR(" ", "Current", this->channel_a_->current);
|
||||||
|
LOG_SENSOR(" ", "Voltage", this->channel_a_->voltage);
|
||||||
|
LOG_SENSOR(" ", "Active Power", this->channel_a_->active_power);
|
||||||
|
LOG_SENSOR(" ", "Apparent Power", this->channel_a_->apparent_power);
|
||||||
|
LOG_SENSOR(" ", "Power Factor", this->channel_a_->power_factor);
|
||||||
|
LOG_SENSOR(" ", "Forward Active Energy", this->channel_a_->forward_active_energy);
|
||||||
|
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_a_->reverse_active_energy);
|
||||||
|
ESP_LOGCONFIG(TAG, " Calibration:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Current: %u", this->channel_a_->current_gain_calibration);
|
||||||
|
ESP_LOGCONFIG(TAG, " Voltage: %d", this->channel_a_->voltage_gain_calibration);
|
||||||
|
ESP_LOGCONFIG(TAG, " Power: %d", this->channel_a_->power_gain_calibration);
|
||||||
|
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_a_->phase_angle_calibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->channel_b_ != nullptr) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Phase B:");
|
||||||
|
LOG_SENSOR(" ", "Current", this->channel_b_->current);
|
||||||
|
LOG_SENSOR(" ", "Voltage", this->channel_b_->voltage);
|
||||||
|
LOG_SENSOR(" ", "Active Power", this->channel_b_->active_power);
|
||||||
|
LOG_SENSOR(" ", "Apparent Power", this->channel_b_->apparent_power);
|
||||||
|
LOG_SENSOR(" ", "Power Factor", this->channel_b_->power_factor);
|
||||||
|
LOG_SENSOR(" ", "Forward Active Energy", this->channel_b_->forward_active_energy);
|
||||||
|
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_b_->reverse_active_energy);
|
||||||
|
ESP_LOGCONFIG(TAG, " Calibration:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Current: %u", this->channel_b_->current_gain_calibration);
|
||||||
|
ESP_LOGCONFIG(TAG, " Voltage: %d", this->channel_b_->voltage_gain_calibration);
|
||||||
|
ESP_LOGCONFIG(TAG, " Power: %d", this->channel_b_->power_gain_calibration);
|
||||||
|
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_b_->phase_angle_calibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->channel_c_ != nullptr) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Phase C:");
|
||||||
|
LOG_SENSOR(" ", "Current", this->channel_c_->current);
|
||||||
|
LOG_SENSOR(" ", "Voltage", this->channel_c_->voltage);
|
||||||
|
LOG_SENSOR(" ", "Active Power", this->channel_c_->active_power);
|
||||||
|
LOG_SENSOR(" ", "Apparent Power", this->channel_c_->apparent_power);
|
||||||
|
LOG_SENSOR(" ", "Power Factor", this->channel_c_->power_factor);
|
||||||
|
LOG_SENSOR(" ", "Forward Active Energy", this->channel_c_->forward_active_energy);
|
||||||
|
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_c_->reverse_active_energy);
|
||||||
|
ESP_LOGCONFIG(TAG, " Calibration:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Current: %u", this->channel_c_->current_gain_calibration);
|
||||||
|
ESP_LOGCONFIG(TAG, " Voltage: %d", this->channel_c_->voltage_gain_calibration);
|
||||||
|
ESP_LOGCONFIG(TAG, " Power: %d", this->channel_c_->power_gain_calibration);
|
||||||
|
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_c_->phase_angle_calibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->channel_n_ != nullptr) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Neutral:");
|
||||||
|
LOG_SENSOR(" ", "Current", this->channel_n_->current);
|
||||||
|
ESP_LOGCONFIG(TAG, " Calibration:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Current: %u", this->channel_n_->current_gain_calibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::calibrate_s10zp_reading_(uint16_t a_register, int16_t calibration) {
|
||||||
|
if (calibration == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->write_s10zp_register16_(a_register, calibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::calibrate_s24zpse_reading_(uint16_t a_register, int32_t calibration) {
|
||||||
|
if (calibration == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->write_s24zpse_register16_(a_register, calibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::init_device_() {
|
||||||
|
this->write_u8_register16_(CONFIG2, CONFIG2_I2C_LOCK);
|
||||||
|
|
||||||
|
this->write_u16_register16_(GAIN, 0);
|
||||||
|
|
||||||
|
if (this->frequency_ > 55) {
|
||||||
|
this->write_u16_register16_(COMPMODE, COMPMODE_DEFAULT | COMPMODE_SELFREQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->channel_n_ != nullptr) {
|
||||||
|
this->calibrate_s24zpse_reading_(NIGAIN, this->channel_n_->current_gain_calibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->channel_a_ != nullptr) {
|
||||||
|
this->calibrate_s24zpse_reading_(AIGAIN, this->channel_a_->current_gain_calibration);
|
||||||
|
this->calibrate_s24zpse_reading_(AVGAIN, this->channel_a_->voltage_gain_calibration);
|
||||||
|
this->calibrate_s24zpse_reading_(APGAIN, this->channel_a_->power_gain_calibration);
|
||||||
|
this->calibrate_s10zp_reading_(APHCAL, this->channel_a_->phase_angle_calibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->channel_b_ != nullptr) {
|
||||||
|
this->calibrate_s24zpse_reading_(BIGAIN, this->channel_b_->current_gain_calibration);
|
||||||
|
this->calibrate_s24zpse_reading_(BVGAIN, this->channel_b_->voltage_gain_calibration);
|
||||||
|
this->calibrate_s24zpse_reading_(BPGAIN, this->channel_b_->power_gain_calibration);
|
||||||
|
this->calibrate_s10zp_reading_(BPHCAL, this->channel_b_->phase_angle_calibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->channel_c_ != nullptr) {
|
||||||
|
this->calibrate_s24zpse_reading_(CIGAIN, this->channel_c_->current_gain_calibration);
|
||||||
|
this->calibrate_s24zpse_reading_(CVGAIN, this->channel_c_->voltage_gain_calibration);
|
||||||
|
this->calibrate_s24zpse_reading_(CPGAIN, this->channel_c_->power_gain_calibration);
|
||||||
|
this->calibrate_s10zp_reading_(CPHCAL, this->channel_c_->phase_angle_calibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write three default values to data memory RAM to flush the I2C write queue
|
||||||
|
this->write_s32_register16_(VLEVEL, 0);
|
||||||
|
this->write_s32_register16_(VLEVEL, 0);
|
||||||
|
this->write_s32_register16_(VLEVEL, 0);
|
||||||
|
|
||||||
|
this->write_u8_register16_(DSPWP_SEL, DSPWP_SEL_SET);
|
||||||
|
this->write_u8_register16_(DSPWP_SET, DSPWP_SET_RO);
|
||||||
|
this->write_u16_register16_(RUN, RUN_ENABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::reset_device_() {
|
||||||
|
if (this->reset_pin_ != nullptr) {
|
||||||
|
ESP_LOGD(TAG, "Reset device using RESET pin");
|
||||||
|
this->reset_pin_->digital_write(false);
|
||||||
|
delay(1);
|
||||||
|
this->reset_pin_->digital_write(true);
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "Reset device using SWRST command");
|
||||||
|
this->write_u16_register16_(CONFIG, CONFIG_SWRST);
|
||||||
|
}
|
||||||
|
this->store_.reset_pending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ade7880
|
||||||
|
} // namespace esphome
|
131
esphome/components/ade7880/ade7880.h
Normal file
131
esphome/components/ade7880/ade7880.h
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// This component was developed using knowledge gathered by a number
|
||||||
|
// of people who reverse-engineered the Shelly 3EM:
|
||||||
|
//
|
||||||
|
// @AndreKR on GitHub
|
||||||
|
// Axel (@Axel830 on GitHub)
|
||||||
|
// Marko (@goodkiller on GitHub)
|
||||||
|
// Michaël Piron (@michaelpiron on GitHub)
|
||||||
|
// Theo Arends (@arendst on GitHub)
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
|
#include "ade7880_registers.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ade7880 {
|
||||||
|
|
||||||
|
struct NeutralChannel {
|
||||||
|
void set_current(sensor::Sensor *sens) { this->current = sens; }
|
||||||
|
|
||||||
|
void set_current_gain_calibration(int32_t val) { this->current_gain_calibration = val; }
|
||||||
|
|
||||||
|
sensor::Sensor *current{nullptr};
|
||||||
|
int32_t current_gain_calibration{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PowerChannel {
|
||||||
|
void set_current(sensor::Sensor *sens) { this->current = sens; }
|
||||||
|
void set_voltage(sensor::Sensor *sens) { this->voltage = sens; }
|
||||||
|
void set_active_power(sensor::Sensor *sens) { this->active_power = sens; }
|
||||||
|
void set_apparent_power(sensor::Sensor *sens) { this->apparent_power = sens; }
|
||||||
|
void set_power_factor(sensor::Sensor *sens) { this->power_factor = sens; }
|
||||||
|
void set_forward_active_energy(sensor::Sensor *sens) { this->forward_active_energy = sens; }
|
||||||
|
void set_reverse_active_energy(sensor::Sensor *sens) { this->reverse_active_energy = sens; }
|
||||||
|
|
||||||
|
void set_current_gain_calibration(int32_t val) { this->current_gain_calibration = val; }
|
||||||
|
void set_voltage_gain_calibration(int32_t val) { this->voltage_gain_calibration = val; }
|
||||||
|
void set_power_gain_calibration(int32_t val) { this->power_gain_calibration = val; }
|
||||||
|
void set_phase_angle_calibration(int32_t val) { this->phase_angle_calibration = val; }
|
||||||
|
|
||||||
|
sensor::Sensor *current{nullptr};
|
||||||
|
sensor::Sensor *voltage{nullptr};
|
||||||
|
sensor::Sensor *active_power{nullptr};
|
||||||
|
sensor::Sensor *apparent_power{nullptr};
|
||||||
|
sensor::Sensor *power_factor{nullptr};
|
||||||
|
sensor::Sensor *forward_active_energy{nullptr};
|
||||||
|
sensor::Sensor *reverse_active_energy{nullptr};
|
||||||
|
int32_t current_gain_calibration{0};
|
||||||
|
int32_t voltage_gain_calibration{0};
|
||||||
|
int32_t power_gain_calibration{0};
|
||||||
|
uint16_t phase_angle_calibration{0};
|
||||||
|
float forward_active_energy_total{0};
|
||||||
|
float reverse_active_energy_total{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Store data in a class that doesn't use multiple-inheritance (no vtables in flash!)
|
||||||
|
struct ADE7880Store {
|
||||||
|
volatile bool reset_done{false};
|
||||||
|
bool reset_pending{false};
|
||||||
|
ISRInternalGPIOPin irq1_pin;
|
||||||
|
|
||||||
|
static void gpio_intr(ADE7880Store *arg);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ADE7880 : public i2c::I2CDevice, public PollingComponent {
|
||||||
|
public:
|
||||||
|
void set_irq0_pin(InternalGPIOPin *pin) { this->irq0_pin_ = pin; }
|
||||||
|
void set_irq1_pin(InternalGPIOPin *pin) { this->irq1_pin_ = pin; }
|
||||||
|
void set_reset_pin(InternalGPIOPin *pin) { this->reset_pin_ = pin; }
|
||||||
|
void set_frequency(float frequency) { this->frequency_ = frequency; }
|
||||||
|
void set_channel_n(NeutralChannel *channel) { this->channel_n_ = channel; }
|
||||||
|
void set_channel_a(PowerChannel *channel) { this->channel_a_ = channel; }
|
||||||
|
void set_channel_b(PowerChannel *channel) { this->channel_b_ = channel; }
|
||||||
|
void set_channel_c(PowerChannel *channel) { this->channel_c_ = channel; }
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ADE7880Store store_{};
|
||||||
|
InternalGPIOPin *irq0_pin_{nullptr};
|
||||||
|
InternalGPIOPin *irq1_pin_{nullptr};
|
||||||
|
InternalGPIOPin *reset_pin_{nullptr};
|
||||||
|
float frequency_;
|
||||||
|
NeutralChannel *channel_n_{nullptr};
|
||||||
|
PowerChannel *channel_a_{nullptr};
|
||||||
|
PowerChannel *channel_b_{nullptr};
|
||||||
|
PowerChannel *channel_c_{nullptr};
|
||||||
|
|
||||||
|
void calibrate_s10zp_reading_(uint16_t a_register, int16_t calibration);
|
||||||
|
void calibrate_s24zpse_reading_(uint16_t a_register, int32_t calibration);
|
||||||
|
|
||||||
|
void init_device_();
|
||||||
|
|
||||||
|
// each of these functions allow the caller to pass in a lambda (or any other callable)
|
||||||
|
// which modifies the value read from the register before it is passed to the sensor
|
||||||
|
// the callable will be passed a 'float' value and is expected to return a 'float'
|
||||||
|
template<typename F> void update_sensor_from_s24zp_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f);
|
||||||
|
template<typename F> void update_sensor_from_s16_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f);
|
||||||
|
template<typename F> void update_sensor_from_s32_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f);
|
||||||
|
|
||||||
|
void reset_device_();
|
||||||
|
|
||||||
|
uint8_t read_u8_register16_(uint16_t a_register);
|
||||||
|
int16_t read_s16_register16_(uint16_t a_register);
|
||||||
|
uint16_t read_u16_register16_(uint16_t a_register);
|
||||||
|
int32_t read_s24zp_register16_(uint16_t a_register);
|
||||||
|
int32_t read_s32_register16_(uint16_t a_register);
|
||||||
|
uint32_t read_u32_register16_(uint16_t a_register);
|
||||||
|
|
||||||
|
void write_u8_register16_(uint16_t a_register, uint8_t value);
|
||||||
|
void write_s10zp_register16_(uint16_t a_register, int16_t value);
|
||||||
|
void write_u16_register16_(uint16_t a_register, uint16_t value);
|
||||||
|
void write_s24zpse_register16_(uint16_t a_register, int32_t value);
|
||||||
|
void write_s32_register16_(uint16_t a_register, int32_t value);
|
||||||
|
void write_u32_register16_(uint16_t a_register, uint32_t value);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ade7880
|
||||||
|
} // namespace esphome
|
101
esphome/components/ade7880/ade7880_i2c.cpp
Normal file
101
esphome/components/ade7880/ade7880_i2c.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
// This component was developed using knowledge gathered by a number
|
||||||
|
// of people who reverse-engineered the Shelly 3EM:
|
||||||
|
//
|
||||||
|
// @AndreKR on GitHub
|
||||||
|
// Axel (@Axel830 on GitHub)
|
||||||
|
// Marko (@goodkiller on GitHub)
|
||||||
|
// Michaël Piron (@michaelpiron on GitHub)
|
||||||
|
// Theo Arends (@arendst on GitHub)
|
||||||
|
|
||||||
|
#include "ade7880.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ade7880 {
|
||||||
|
|
||||||
|
// adapted from https://stackoverflow.com/a/55912127/1886371
|
||||||
|
template<size_t Bits, typename T> inline T sign_extend(const T &v) noexcept {
|
||||||
|
using S = struct { signed Val : Bits; };
|
||||||
|
return reinterpret_cast<const S *>(&v)->Val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register types
|
||||||
|
// unsigned 8-bit (uint8_t)
|
||||||
|
// signed 10-bit - 16-bit ZP on wire (int16_t, needs sign extension)
|
||||||
|
// unsigned 16-bit (uint16_t)
|
||||||
|
// unsigned 20-bit - 32-bit ZP on wire (uint32_t)
|
||||||
|
// signed 24-bit - 32-bit ZPSE on wire (int32_t, needs sign extension)
|
||||||
|
// signed 24-bit - 32-bit ZP on wire (int32_t, needs sign extension)
|
||||||
|
// signed 24-bit - 32-bit SE on wire (int32_t)
|
||||||
|
// signed 28-bit - 32-bit ZP on wire (int32_t, needs sign extension)
|
||||||
|
// unsigned 32-bit (uint32_t)
|
||||||
|
// signed 32-bit (int32_t)
|
||||||
|
|
||||||
|
uint8_t ADE7880::read_u8_register16_(uint16_t a_register) {
|
||||||
|
uint8_t in;
|
||||||
|
this->read_register16(a_register, &in, sizeof(in));
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t ADE7880::read_s16_register16_(uint16_t a_register) {
|
||||||
|
int16_t in;
|
||||||
|
this->read_register16(a_register, reinterpret_cast<uint8_t *>(&in), sizeof(in));
|
||||||
|
return convert_big_endian(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ADE7880::read_u16_register16_(uint16_t a_register) {
|
||||||
|
uint16_t in;
|
||||||
|
this->read_register16(a_register, reinterpret_cast<uint8_t *>(&in), sizeof(in));
|
||||||
|
return convert_big_endian(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ADE7880::read_s24zp_register16_(uint16_t a_register) {
|
||||||
|
// s24zp means 24 bit signed value in the lower 24 bits of a 32-bit register
|
||||||
|
int32_t in;
|
||||||
|
this->read_register16(a_register, reinterpret_cast<uint8_t *>(&in), sizeof(in));
|
||||||
|
return sign_extend<24>(convert_big_endian(in));
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ADE7880::read_s32_register16_(uint16_t a_register) {
|
||||||
|
int32_t in;
|
||||||
|
this->read_register16(a_register, reinterpret_cast<uint8_t *>(&in), sizeof(in));
|
||||||
|
return convert_big_endian(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ADE7880::read_u32_register16_(uint16_t a_register) {
|
||||||
|
uint32_t in;
|
||||||
|
this->read_register16(a_register, reinterpret_cast<uint8_t *>(&in), sizeof(in));
|
||||||
|
return convert_big_endian(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::write_u8_register16_(uint16_t a_register, uint8_t value) {
|
||||||
|
this->write_register16(a_register, &value, sizeof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::write_s10zp_register16_(uint16_t a_register, int16_t value) {
|
||||||
|
int16_t out = convert_big_endian(value & 0x03FF);
|
||||||
|
this->write_register16(a_register, reinterpret_cast<uint8_t *>(&out), sizeof(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::write_u16_register16_(uint16_t a_register, uint16_t value) {
|
||||||
|
uint16_t out = convert_big_endian(value);
|
||||||
|
this->write_register16(a_register, reinterpret_cast<uint8_t *>(&out), sizeof(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::write_s24zpse_register16_(uint16_t a_register, int32_t value) {
|
||||||
|
// s24zpse means a 24-bit signed value, sign-extended to 28 bits, in the lower 28 bits of a 32-bit register
|
||||||
|
int32_t out = convert_big_endian(value & 0x0FFFFFFF);
|
||||||
|
this->write_register16(a_register, reinterpret_cast<uint8_t *>(&out), sizeof(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::write_s32_register16_(uint16_t a_register, int32_t value) {
|
||||||
|
int32_t out = convert_big_endian(value);
|
||||||
|
this->write_register16(a_register, reinterpret_cast<uint8_t *>(&out), sizeof(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADE7880::write_u32_register16_(uint16_t a_register, uint32_t value) {
|
||||||
|
uint32_t out = convert_big_endian(value);
|
||||||
|
this->write_register16(a_register, reinterpret_cast<uint8_t *>(&out), sizeof(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ade7880
|
||||||
|
} // namespace esphome
|
243
esphome/components/ade7880/ade7880_registers.h
Normal file
243
esphome/components/ade7880/ade7880_registers.h
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// This file is a modified version of the one created by Michaël Piron (@michaelpiron on GitHub)
|
||||||
|
|
||||||
|
// Source: https://www.analog.com/media/en/technical-documentation/application-notes/AN-1127.pdf
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ade7880 {
|
||||||
|
|
||||||
|
// DSP Data Memory RAM registers
|
||||||
|
constexpr uint16_t AIGAIN = 0x4380;
|
||||||
|
constexpr uint16_t AVGAIN = 0x4381;
|
||||||
|
constexpr uint16_t BIGAIN = 0x4382;
|
||||||
|
constexpr uint16_t BVGAIN = 0x4383;
|
||||||
|
constexpr uint16_t CIGAIN = 0x4384;
|
||||||
|
constexpr uint16_t CVGAIN = 0x4385;
|
||||||
|
constexpr uint16_t NIGAIN = 0x4386;
|
||||||
|
|
||||||
|
constexpr uint16_t DICOEFF = 0x4388;
|
||||||
|
|
||||||
|
constexpr uint16_t APGAIN = 0x4389;
|
||||||
|
constexpr uint16_t AWATTOS = 0x438A;
|
||||||
|
constexpr uint16_t BPGAIN = 0x438B;
|
||||||
|
constexpr uint16_t BWATTOS = 0x438C;
|
||||||
|
constexpr uint16_t CPGAIN = 0x438D;
|
||||||
|
constexpr uint16_t CWATTOS = 0x438E;
|
||||||
|
constexpr uint16_t AIRMSOS = 0x438F;
|
||||||
|
constexpr uint16_t AVRMSOS = 0x4390;
|
||||||
|
constexpr uint16_t BIRMSOS = 0x4391;
|
||||||
|
constexpr uint16_t BVRMSOS = 0x4392;
|
||||||
|
constexpr uint16_t CIRMSOS = 0x4393;
|
||||||
|
constexpr uint16_t CVRMSOS = 0x4394;
|
||||||
|
constexpr uint16_t NIRMSOS = 0x4395;
|
||||||
|
constexpr uint16_t HPGAIN = 0x4398;
|
||||||
|
constexpr uint16_t ISUMLVL = 0x4399;
|
||||||
|
|
||||||
|
constexpr uint16_t VLEVEL = 0x439F;
|
||||||
|
|
||||||
|
constexpr uint16_t AFWATTOS = 0x43A2;
|
||||||
|
constexpr uint16_t BFWATTOS = 0x43A3;
|
||||||
|
constexpr uint16_t CFWATTOS = 0x43A4;
|
||||||
|
|
||||||
|
constexpr uint16_t AFVAROS = 0x43A5;
|
||||||
|
constexpr uint16_t BFVAROS = 0x43A6;
|
||||||
|
constexpr uint16_t CFVAROS = 0x43A7;
|
||||||
|
|
||||||
|
constexpr uint16_t AFIRMSOS = 0x43A8;
|
||||||
|
constexpr uint16_t BFIRMSOS = 0x43A9;
|
||||||
|
constexpr uint16_t CFIRMSOS = 0x43AA;
|
||||||
|
|
||||||
|
constexpr uint16_t AFVRMSOS = 0x43AB;
|
||||||
|
constexpr uint16_t BFVRMSOS = 0x43AC;
|
||||||
|
constexpr uint16_t CFVRMSOS = 0x43AD;
|
||||||
|
|
||||||
|
constexpr uint16_t HXWATTOS = 0x43AE;
|
||||||
|
constexpr uint16_t HYWATTOS = 0x43AF;
|
||||||
|
constexpr uint16_t HZWATTOS = 0x43B0;
|
||||||
|
constexpr uint16_t HXVAROS = 0x43B1;
|
||||||
|
constexpr uint16_t HYVAROS = 0x43B2;
|
||||||
|
constexpr uint16_t HZVAROS = 0x43B3;
|
||||||
|
|
||||||
|
constexpr uint16_t HXIRMSOS = 0x43B4;
|
||||||
|
constexpr uint16_t HYIRMSOS = 0x43B5;
|
||||||
|
constexpr uint16_t HZIRMSOS = 0x43B6;
|
||||||
|
constexpr uint16_t HXVRMSOS = 0x43B7;
|
||||||
|
constexpr uint16_t HYVRMSOS = 0x43B8;
|
||||||
|
constexpr uint16_t HZVRMSOS = 0x43B9;
|
||||||
|
|
||||||
|
constexpr uint16_t AIRMS = 0x43C0;
|
||||||
|
constexpr uint16_t AVRMS = 0x43C1;
|
||||||
|
constexpr uint16_t BIRMS = 0x43C2;
|
||||||
|
constexpr uint16_t BVRMS = 0x43C3;
|
||||||
|
constexpr uint16_t CIRMS = 0x43C4;
|
||||||
|
constexpr uint16_t CVRMS = 0x43C5;
|
||||||
|
constexpr uint16_t NIRMS = 0x43C6;
|
||||||
|
|
||||||
|
constexpr uint16_t ISUM = 0x43C7;
|
||||||
|
|
||||||
|
// Internal DSP Memory RAM registers
|
||||||
|
constexpr uint16_t RUN = 0xE228;
|
||||||
|
|
||||||
|
constexpr uint16_t AWATTHR = 0xE400;
|
||||||
|
constexpr uint16_t BWATTHR = 0xE401;
|
||||||
|
constexpr uint16_t CWATTHR = 0xE402;
|
||||||
|
constexpr uint16_t AFWATTHR = 0xE403;
|
||||||
|
constexpr uint16_t BFWATTHR = 0xE404;
|
||||||
|
constexpr uint16_t CFWATTHR = 0xE405;
|
||||||
|
constexpr uint16_t AFVARHR = 0xE409;
|
||||||
|
constexpr uint16_t BFVARHR = 0xE40A;
|
||||||
|
constexpr uint16_t CFVARHR = 0xE40B;
|
||||||
|
|
||||||
|
constexpr uint16_t AVAHR = 0xE40C;
|
||||||
|
constexpr uint16_t BVAHR = 0xE40D;
|
||||||
|
constexpr uint16_t CVAHR = 0xE40E;
|
||||||
|
|
||||||
|
constexpr uint16_t IPEAK = 0xE500;
|
||||||
|
constexpr uint16_t VPEAK = 0xE501;
|
||||||
|
|
||||||
|
constexpr uint16_t STATUS0 = 0xE502;
|
||||||
|
constexpr uint16_t STATUS1 = 0xE503;
|
||||||
|
|
||||||
|
constexpr uint16_t AIMAV = 0xE504;
|
||||||
|
constexpr uint16_t BIMAV = 0xE505;
|
||||||
|
constexpr uint16_t CIMAV = 0xE506;
|
||||||
|
|
||||||
|
constexpr uint16_t OILVL = 0xE507;
|
||||||
|
constexpr uint16_t OVLVL = 0xE508;
|
||||||
|
constexpr uint16_t SAGLVL = 0xE509;
|
||||||
|
constexpr uint16_t MASK0 = 0xE50A;
|
||||||
|
constexpr uint16_t MASK1 = 0xE50B;
|
||||||
|
|
||||||
|
constexpr uint16_t IAWV = 0xE50C;
|
||||||
|
constexpr uint16_t IBWV = 0xE50D;
|
||||||
|
constexpr uint16_t ICWV = 0xE50E;
|
||||||
|
constexpr uint16_t INWV = 0xE50F;
|
||||||
|
constexpr uint16_t VAWV = 0xE510;
|
||||||
|
constexpr uint16_t VBWV = 0xE511;
|
||||||
|
constexpr uint16_t VCWV = 0xE512;
|
||||||
|
|
||||||
|
constexpr uint16_t AWATT = 0xE513;
|
||||||
|
constexpr uint16_t BWATT = 0xE514;
|
||||||
|
constexpr uint16_t CWATT = 0xE515;
|
||||||
|
|
||||||
|
constexpr uint16_t AFVAR = 0xE516;
|
||||||
|
constexpr uint16_t BFVAR = 0xE517;
|
||||||
|
constexpr uint16_t CFVAR = 0xE518;
|
||||||
|
|
||||||
|
constexpr uint16_t AVA = 0xE519;
|
||||||
|
constexpr uint16_t BVA = 0xE51A;
|
||||||
|
constexpr uint16_t CVA = 0xE51B;
|
||||||
|
|
||||||
|
constexpr uint16_t CHECKSUM = 0xE51F;
|
||||||
|
constexpr uint16_t VNOM = 0xE520;
|
||||||
|
constexpr uint16_t LAST_RWDATA_24BIT = 0xE5FF;
|
||||||
|
constexpr uint16_t PHSTATUS = 0xE600;
|
||||||
|
constexpr uint16_t ANGLE0 = 0xE601;
|
||||||
|
constexpr uint16_t ANGLE1 = 0xE602;
|
||||||
|
constexpr uint16_t ANGLE2 = 0xE603;
|
||||||
|
constexpr uint16_t PHNOLOAD = 0xE608;
|
||||||
|
constexpr uint16_t LINECYC = 0xE60C;
|
||||||
|
constexpr uint16_t ZXTOUT = 0xE60D;
|
||||||
|
constexpr uint16_t COMPMODE = 0xE60E;
|
||||||
|
constexpr uint16_t GAIN = 0xE60F;
|
||||||
|
constexpr uint16_t CFMODE = 0xE610;
|
||||||
|
constexpr uint16_t CF1DEN = 0xE611;
|
||||||
|
constexpr uint16_t CF2DEN = 0xE612;
|
||||||
|
constexpr uint16_t CF3DEN = 0xE613;
|
||||||
|
constexpr uint16_t APHCAL = 0xE614;
|
||||||
|
constexpr uint16_t BPHCAL = 0xE615;
|
||||||
|
constexpr uint16_t CPHCAL = 0xE616;
|
||||||
|
constexpr uint16_t PHSIGN = 0xE617;
|
||||||
|
constexpr uint16_t CONFIG = 0xE618;
|
||||||
|
constexpr uint16_t MMODE = 0xE700;
|
||||||
|
constexpr uint16_t ACCMODE = 0xE701;
|
||||||
|
constexpr uint16_t LCYCMODE = 0xE702;
|
||||||
|
constexpr uint16_t PEAKCYC = 0xE703;
|
||||||
|
constexpr uint16_t SAGCYC = 0xE704;
|
||||||
|
constexpr uint16_t CFCYC = 0xE705;
|
||||||
|
constexpr uint16_t HSDC_CFG = 0xE706;
|
||||||
|
constexpr uint16_t VERSION = 0xE707;
|
||||||
|
constexpr uint16_t DSPWP_SET = 0xE7E3;
|
||||||
|
constexpr uint16_t LAST_RWDATA_8BIT = 0xE7FD;
|
||||||
|
constexpr uint16_t DSPWP_SEL = 0xE7FE;
|
||||||
|
constexpr uint16_t FVRMS = 0xE880;
|
||||||
|
constexpr uint16_t FIRMS = 0xE881;
|
||||||
|
constexpr uint16_t FWATT = 0xE882;
|
||||||
|
constexpr uint16_t FVAR = 0xE883;
|
||||||
|
constexpr uint16_t FVA = 0xE884;
|
||||||
|
constexpr uint16_t FPF = 0xE885;
|
||||||
|
constexpr uint16_t VTHDN = 0xE886;
|
||||||
|
constexpr uint16_t ITHDN = 0xE887;
|
||||||
|
constexpr uint16_t HXVRMS = 0xE888;
|
||||||
|
constexpr uint16_t HXIRMS = 0xE889;
|
||||||
|
constexpr uint16_t HXWATT = 0xE88A;
|
||||||
|
constexpr uint16_t HXVAR = 0xE88B;
|
||||||
|
constexpr uint16_t HXVA = 0xE88C;
|
||||||
|
constexpr uint16_t HXPF = 0xE88D;
|
||||||
|
constexpr uint16_t HXVHD = 0xE88E;
|
||||||
|
constexpr uint16_t HXIHD = 0xE88F;
|
||||||
|
constexpr uint16_t HYVRMS = 0xE890;
|
||||||
|
constexpr uint16_t HYIRMS = 0xE891;
|
||||||
|
constexpr uint16_t HYWATT = 0xE892;
|
||||||
|
constexpr uint16_t HYVAR = 0xE893;
|
||||||
|
constexpr uint16_t HYVA = 0xE894;
|
||||||
|
constexpr uint16_t HYPF = 0xE895;
|
||||||
|
constexpr uint16_t HYVHD = 0xE896;
|
||||||
|
constexpr uint16_t HYIHD = 0xE897;
|
||||||
|
constexpr uint16_t HZVRMS = 0xE898;
|
||||||
|
constexpr uint16_t HZIRMS = 0xE899;
|
||||||
|
constexpr uint16_t HZWATT = 0xE89A;
|
||||||
|
constexpr uint16_t HZVAR = 0xE89B;
|
||||||
|
constexpr uint16_t HZVA = 0xE89C;
|
||||||
|
constexpr uint16_t HZPF = 0xE89D;
|
||||||
|
constexpr uint16_t HZVHD = 0xE89E;
|
||||||
|
constexpr uint16_t HZIHD = 0xE89F;
|
||||||
|
constexpr uint16_t HCONFIG = 0xE900;
|
||||||
|
constexpr uint16_t APF = 0xE902;
|
||||||
|
constexpr uint16_t BPF = 0xE903;
|
||||||
|
constexpr uint16_t CPF = 0xE904;
|
||||||
|
constexpr uint16_t APERIOD = 0xE905;
|
||||||
|
constexpr uint16_t BPERIOD = 0xE906;
|
||||||
|
constexpr uint16_t CPERIOD = 0xE907;
|
||||||
|
constexpr uint16_t APNOLOAD = 0xE908;
|
||||||
|
constexpr uint16_t VARNOLOAD = 0xE909;
|
||||||
|
constexpr uint16_t VANOLOAD = 0xE90A;
|
||||||
|
constexpr uint16_t LAST_ADD = 0xE9FE;
|
||||||
|
constexpr uint16_t LAST_RWDATA_16BIT = 0xE9FF;
|
||||||
|
constexpr uint16_t CONFIG3 = 0xEA00;
|
||||||
|
constexpr uint16_t LAST_OP = 0xEA01;
|
||||||
|
constexpr uint16_t WTHR = 0xEA02;
|
||||||
|
constexpr uint16_t VARTHR = 0xEA03;
|
||||||
|
constexpr uint16_t VATHR = 0xEA04;
|
||||||
|
|
||||||
|
constexpr uint16_t HX_REG = 0xEA08;
|
||||||
|
constexpr uint16_t HY_REG = 0xEA09;
|
||||||
|
constexpr uint16_t HZ_REG = 0xEA0A;
|
||||||
|
constexpr uint16_t LPOILVL = 0xEC00;
|
||||||
|
constexpr uint16_t CONFIG2 = 0xEC01;
|
||||||
|
|
||||||
|
// STATUS1 Register Bits
|
||||||
|
constexpr uint32_t STATUS1_RSTDONE = (1 << 15);
|
||||||
|
|
||||||
|
// CONFIG Register Bits
|
||||||
|
constexpr uint16_t CONFIG_SWRST = (1 << 7);
|
||||||
|
|
||||||
|
// CONFIG2 Register Bits
|
||||||
|
constexpr uint8_t CONFIG2_I2C_LOCK = (1 << 1);
|
||||||
|
|
||||||
|
// COMPMODE Register Bits
|
||||||
|
constexpr uint16_t COMPMODE_DEFAULT = 0x01FF;
|
||||||
|
constexpr uint16_t COMPMODE_SELFREQ = (1 << 14);
|
||||||
|
|
||||||
|
// RUN Register Bits
|
||||||
|
constexpr uint16_t RUN_ENABLE = (1 << 0);
|
||||||
|
|
||||||
|
// DSPWP_SET Register Bits
|
||||||
|
constexpr uint8_t DSPWP_SET_RO = (1 << 7);
|
||||||
|
|
||||||
|
// DSPWP_SEL Register Bits
|
||||||
|
constexpr uint8_t DSPWP_SEL_SET = 0xAD;
|
||||||
|
|
||||||
|
} // namespace ade7880
|
||||||
|
} // namespace esphome
|
290
esphome/components/ade7880/sensor.py
Normal file
290
esphome/components/ade7880/sensor.py
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, i2c
|
||||||
|
from esphome import pins
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ACTIVE_POWER,
|
||||||
|
CONF_APPARENT_POWER,
|
||||||
|
CONF_CALIBRATION,
|
||||||
|
CONF_CURRENT,
|
||||||
|
CONF_FORWARD_ACTIVE_ENERGY,
|
||||||
|
CONF_FREQUENCY,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_PHASE_A,
|
||||||
|
CONF_PHASE_ANGLE,
|
||||||
|
CONF_PHASE_B,
|
||||||
|
CONF_PHASE_C,
|
||||||
|
CONF_POWER_FACTOR,
|
||||||
|
CONF_RESET_PIN,
|
||||||
|
CONF_REVERSE_ACTIVE_ENERGY,
|
||||||
|
CONF_VOLTAGE,
|
||||||
|
DEVICE_CLASS_APPARENT_POWER,
|
||||||
|
DEVICE_CLASS_CURRENT,
|
||||||
|
DEVICE_CLASS_ENERGY,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
UNIT_AMPERE,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
UNIT_VOLT,
|
||||||
|
UNIT_VOLT_AMPS,
|
||||||
|
UNIT_VOLT_AMPS_REACTIVE_HOURS,
|
||||||
|
UNIT_WATT,
|
||||||
|
UNIT_WATT_HOURS,
|
||||||
|
)
|
||||||
|
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
|
ade7880_ns = cg.esphome_ns.namespace("ade7880")
|
||||||
|
ADE7880 = ade7880_ns.class_("ADE7880", cg.PollingComponent, i2c.I2CDevice)
|
||||||
|
NeutralChannel = ade7880_ns.struct("NeutralChannel")
|
||||||
|
PowerChannel = ade7880_ns.struct("PowerChannel")
|
||||||
|
|
||||||
|
CONF_CURRENT_GAIN = "current_gain"
|
||||||
|
CONF_IRQ0_PIN = "irq0_pin"
|
||||||
|
CONF_IRQ1_PIN = "irq1_pin"
|
||||||
|
CONF_POWER_GAIN = "power_gain"
|
||||||
|
CONF_VOLTAGE_GAIN = "voltage_gain"
|
||||||
|
|
||||||
|
CONF_NEUTRAL = "neutral"
|
||||||
|
|
||||||
|
NEUTRAL_CHANNEL_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(NeutralChannel),
|
||||||
|
cv.Optional(CONF_NAME): cv.string_strict,
|
||||||
|
cv.Required(CONF_CURRENT): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_AMPERE,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_CURRENT,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Required(CONF_CALIBRATION): cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_CURRENT_GAIN): cv.int_,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
POWER_CHANNEL_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(PowerChannel),
|
||||||
|
cv.Optional(CONF_NAME): cv.string_strict,
|
||||||
|
cv.Optional(CONF_VOLTAGE): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_VOLT,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_VOLTAGE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_CURRENT): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_AMPERE,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_CURRENT,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ACTIVE_POWER): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_WATT,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_APPARENT_POWER): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_VOLT_AMPS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_APPARENT_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_POWER_FACTOR): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_WATT_HOURS,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE_HOURS,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Required(CONF_CALIBRATION): cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_CURRENT_GAIN): cv.int_,
|
||||||
|
cv.Required(CONF_VOLTAGE_GAIN): cv.int_,
|
||||||
|
cv.Required(CONF_POWER_GAIN): cv.int_,
|
||||||
|
cv.Required(CONF_PHASE_ANGLE): cv.int_,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(ADE7880),
|
||||||
|
cv.Optional(CONF_FREQUENCY, default="50Hz"): cv.All(
|
||||||
|
cv.frequency, cv.Range(min=45.0, max=66.0)
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_IRQ0_PIN): pins.internal_gpio_input_pin_schema,
|
||||||
|
cv.Required(CONF_IRQ1_PIN): pins.internal_gpio_input_pin_schema,
|
||||||
|
cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_schema,
|
||||||
|
cv.Optional(CONF_PHASE_A): POWER_CHANNEL_SCHEMA,
|
||||||
|
cv.Optional(CONF_PHASE_B): POWER_CHANNEL_SCHEMA,
|
||||||
|
cv.Optional(CONF_PHASE_C): POWER_CHANNEL_SCHEMA,
|
||||||
|
cv.Optional(CONF_NEUTRAL): NEUTRAL_CHANNEL_SCHEMA,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x38))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def neutral_channel(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
|
||||||
|
current = config[CONF_CURRENT]
|
||||||
|
sens = await sensor.new_sensor(current)
|
||||||
|
cg.add(var.set_current(sens))
|
||||||
|
|
||||||
|
cg.add(
|
||||||
|
var.set_current_gain_calibration(config[CONF_CALIBRATION][CONF_CURRENT_GAIN])
|
||||||
|
)
|
||||||
|
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
async def power_channel(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
|
||||||
|
for sensor_type in [
|
||||||
|
CONF_CURRENT,
|
||||||
|
CONF_VOLTAGE,
|
||||||
|
CONF_ACTIVE_POWER,
|
||||||
|
CONF_APPARENT_POWER,
|
||||||
|
CONF_POWER_FACTOR,
|
||||||
|
CONF_FORWARD_ACTIVE_ENERGY,
|
||||||
|
CONF_REVERSE_ACTIVE_ENERGY,
|
||||||
|
]:
|
||||||
|
if conf := config.get(sensor_type):
|
||||||
|
sens = await sensor.new_sensor(conf)
|
||||||
|
cg.add(getattr(var, f"set_{sensor_type}")(sens))
|
||||||
|
|
||||||
|
for calib_type in [
|
||||||
|
CONF_CURRENT_GAIN,
|
||||||
|
CONF_VOLTAGE_GAIN,
|
||||||
|
CONF_POWER_GAIN,
|
||||||
|
CONF_PHASE_ANGLE,
|
||||||
|
]:
|
||||||
|
cg.add(
|
||||||
|
getattr(var, f"set_{calib_type}_calibration")(
|
||||||
|
config[CONF_CALIBRATION][calib_type]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
def final_validate(config):
|
||||||
|
for channel in [CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]:
|
||||||
|
if channel := config.get(channel):
|
||||||
|
channel_name = channel.get(CONF_NAME)
|
||||||
|
|
||||||
|
for sensor_type in [
|
||||||
|
CONF_CURRENT,
|
||||||
|
CONF_VOLTAGE,
|
||||||
|
CONF_ACTIVE_POWER,
|
||||||
|
CONF_APPARENT_POWER,
|
||||||
|
CONF_POWER_FACTOR,
|
||||||
|
CONF_FORWARD_ACTIVE_ENERGY,
|
||||||
|
CONF_REVERSE_ACTIVE_ENERGY,
|
||||||
|
]:
|
||||||
|
if conf := channel.get(sensor_type):
|
||||||
|
sensor_name = conf.get(CONF_NAME)
|
||||||
|
if (
|
||||||
|
sensor_name
|
||||||
|
and channel_name
|
||||||
|
and not sensor_name.startswith(channel_name)
|
||||||
|
):
|
||||||
|
conf[CONF_NAME] = f"{channel_name} {sensor_name}"
|
||||||
|
|
||||||
|
if channel := config.get(CONF_NEUTRAL):
|
||||||
|
channel_name = channel.get(CONF_NAME)
|
||||||
|
if conf := channel.get(CONF_CURRENT):
|
||||||
|
sensor_name = conf.get(CONF_NAME)
|
||||||
|
if (
|
||||||
|
sensor_name
|
||||||
|
and channel_name
|
||||||
|
and not sensor_name.startswith(channel_name)
|
||||||
|
):
|
||||||
|
conf[CONF_NAME] = f"{channel_name} {sensor_name}"
|
||||||
|
|
||||||
|
|
||||||
|
FINAL_VALIDATE_SCHEMA = final_validate
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
if irq0_pin := config.get(CONF_IRQ0_PIN):
|
||||||
|
pin = await cg.gpio_pin_expression(irq0_pin)
|
||||||
|
cg.add(var.set_irq0_pin(pin))
|
||||||
|
|
||||||
|
pin = await cg.gpio_pin_expression(config[CONF_IRQ1_PIN])
|
||||||
|
cg.add(var.set_irq1_pin(pin))
|
||||||
|
|
||||||
|
if reset_pin := config.get(CONF_RESET_PIN):
|
||||||
|
pin = await cg.gpio_pin_expression(reset_pin)
|
||||||
|
cg.add(var.set_reset_pin(pin))
|
||||||
|
|
||||||
|
if frequency := config.get(CONF_FREQUENCY):
|
||||||
|
cg.add(var.set_frequency(frequency))
|
||||||
|
|
||||||
|
if channel := config.get(CONF_PHASE_A):
|
||||||
|
chan = await power_channel(channel)
|
||||||
|
cg.add(var.set_channel_a(chan))
|
||||||
|
|
||||||
|
if channel := config.get(CONF_PHASE_B):
|
||||||
|
chan = await power_channel(channel)
|
||||||
|
cg.add(var.set_channel_b(chan))
|
||||||
|
|
||||||
|
if channel := config.get(CONF_PHASE_C):
|
||||||
|
chan = await power_channel(channel)
|
||||||
|
cg.add(var.set_channel_c(chan))
|
||||||
|
|
||||||
|
if channel := config.get(CONF_NEUTRAL):
|
||||||
|
chan = await neutral_channel(channel)
|
||||||
|
cg.add(var.set_channel_n(chan))
|
|
@ -41,6 +41,7 @@ CONF_CURRENT_GAIN_A = "current_gain_a"
|
||||||
CONF_CURRENT_GAIN_B = "current_gain_b"
|
CONF_CURRENT_GAIN_B = "current_gain_b"
|
||||||
CONF_ACTIVE_POWER_GAIN_A = "active_power_gain_a"
|
CONF_ACTIVE_POWER_GAIN_A = "active_power_gain_a"
|
||||||
CONF_ACTIVE_POWER_GAIN_B = "active_power_gain_b"
|
CONF_ACTIVE_POWER_GAIN_B = "active_power_gain_b"
|
||||||
|
CONF_USE_ACCUMULATED_ENERGY_REGISTERS = "use_accumulated_energy_registers"
|
||||||
PGA_GAINS = {
|
PGA_GAINS = {
|
||||||
"1x": 0b000,
|
"1x": 0b000,
|
||||||
"2x": 0b001,
|
"2x": 0b001,
|
||||||
|
@ -155,6 +156,7 @@ ADE7953_CONFIG_SCHEMA = cv.Schema(
|
||||||
cv.Optional(CONF_ACTIVE_POWER_GAIN_B, default=0x400000): cv.hex_int_range(
|
cv.Optional(CONF_ACTIVE_POWER_GAIN_B, default=0x400000): cv.hex_int_range(
|
||||||
min=0x100000, max=0x800000
|
min=0x100000, max=0x800000
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_USE_ACCUMULATED_ENERGY_REGISTERS, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
).extend(cv.polling_component_schema("60s"))
|
).extend(cv.polling_component_schema("60s"))
|
||||||
|
|
||||||
|
@ -174,6 +176,9 @@ async def register_ade7953(var, config):
|
||||||
cg.add(var.set_bigain(config.get(CONF_CURRENT_GAIN_B)))
|
cg.add(var.set_bigain(config.get(CONF_CURRENT_GAIN_B)))
|
||||||
cg.add(var.set_awgain(config.get(CONF_ACTIVE_POWER_GAIN_A)))
|
cg.add(var.set_awgain(config.get(CONF_ACTIVE_POWER_GAIN_A)))
|
||||||
cg.add(var.set_bwgain(config.get(CONF_ACTIVE_POWER_GAIN_B)))
|
cg.add(var.set_bwgain(config.get(CONF_ACTIVE_POWER_GAIN_B)))
|
||||||
|
cg.add(
|
||||||
|
var.set_use_acc_energy_regs(config.get(CONF_USE_ACCUMULATED_ENERGY_REGISTERS))
|
||||||
|
)
|
||||||
|
|
||||||
for key in [
|
for key in [
|
||||||
CONF_VOLTAGE,
|
CONF_VOLTAGE,
|
||||||
|
|
|
@ -6,6 +6,9 @@ namespace ade7953_base {
|
||||||
|
|
||||||
static const char *const TAG = "ade7953";
|
static const char *const TAG = "ade7953";
|
||||||
|
|
||||||
|
static const float ADE_POWER_FACTOR = 154.0f;
|
||||||
|
static const float ADE_WATTSEC_POWER_FACTOR = ADE_POWER_FACTOR * ADE_POWER_FACTOR / 3600;
|
||||||
|
|
||||||
void ADE7953::setup() {
|
void ADE7953::setup() {
|
||||||
if (this->irq_pin_ != nullptr) {
|
if (this->irq_pin_ != nullptr) {
|
||||||
this->irq_pin_->setup();
|
this->irq_pin_->setup();
|
||||||
|
@ -34,6 +37,7 @@ void ADE7953::setup() {
|
||||||
this->ade_read_32(BIGAIN_32, &bigain_);
|
this->ade_read_32(BIGAIN_32, &bigain_);
|
||||||
this->ade_read_32(AWGAIN_32, &awgain_);
|
this->ade_read_32(AWGAIN_32, &awgain_);
|
||||||
this->ade_read_32(BWGAIN_32, &bwgain_);
|
this->ade_read_32(BWGAIN_32, &bwgain_);
|
||||||
|
this->last_update_ = millis();
|
||||||
this->is_setup_ = true;
|
this->is_setup_ = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -52,6 +56,7 @@ void ADE7953::dump_config() {
|
||||||
LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_);
|
LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_);
|
||||||
LOG_SENSOR(" ", "Rective Power A Sensor", this->reactive_power_a_sensor_);
|
LOG_SENSOR(" ", "Rective Power A Sensor", this->reactive_power_a_sensor_);
|
||||||
LOG_SENSOR(" ", "Reactive Power B Sensor", this->reactive_power_b_sensor_);
|
LOG_SENSOR(" ", "Reactive Power B Sensor", this->reactive_power_b_sensor_);
|
||||||
|
ESP_LOGCONFIG(TAG, " USE_ACC_ENERGY_REGS: %d", this->use_acc_energy_regs_);
|
||||||
ESP_LOGCONFIG(TAG, " PGA_V_8: 0x%X", pga_v_);
|
ESP_LOGCONFIG(TAG, " PGA_V_8: 0x%X", pga_v_);
|
||||||
ESP_LOGCONFIG(TAG, " PGA_IA_8: 0x%X", pga_ia_);
|
ESP_LOGCONFIG(TAG, " PGA_IA_8: 0x%X", pga_ia_);
|
||||||
ESP_LOGCONFIG(TAG, " PGA_IB_8: 0x%X", pga_ib_);
|
ESP_LOGCONFIG(TAG, " PGA_IB_8: 0x%X", pga_ib_);
|
||||||
|
@ -85,6 +90,7 @@ void ADE7953::update() {
|
||||||
|
|
||||||
uint32_t val;
|
uint32_t val;
|
||||||
uint16_t val_16;
|
uint16_t val_16;
|
||||||
|
uint16_t reg;
|
||||||
|
|
||||||
// Power factor
|
// Power factor
|
||||||
err = this->ade_read_16(0x010A, &val_16);
|
err = this->ade_read_16(0x010A, &val_16);
|
||||||
|
@ -92,23 +98,36 @@ void ADE7953::update() {
|
||||||
err = this->ade_read_16(0x010B, &val_16);
|
err = this->ade_read_16(0x010B, &val_16);
|
||||||
ADE_PUBLISH(power_factor_b, (int16_t) val_16, (0x7FFF / 100.0f));
|
ADE_PUBLISH(power_factor_b, (int16_t) val_16, (0x7FFF / 100.0f));
|
||||||
|
|
||||||
|
float pf = ADE_POWER_FACTOR;
|
||||||
|
if (this->use_acc_energy_regs_) {
|
||||||
|
const uint32_t now = millis();
|
||||||
|
const auto diff = now - this->last_update_;
|
||||||
|
this->last_update_ = now;
|
||||||
|
// prevent DIV/0
|
||||||
|
pf = ADE_WATTSEC_POWER_FACTOR * (diff < 10 ? 10 : diff) / 1000;
|
||||||
|
ESP_LOGVV(TAG, "ADE7953::update() diff=%d pf=%f", diff, pf);
|
||||||
|
}
|
||||||
|
|
||||||
// Apparent power
|
// Apparent power
|
||||||
err = this->ade_read_32(0x0310, &val);
|
reg = this->use_acc_energy_regs_ ? 0x0322 : 0x0310;
|
||||||
ADE_PUBLISH(apparent_power_a, (int32_t) val, 154.0f);
|
err = this->ade_read_32(reg, &val);
|
||||||
err = this->ade_read_32(0x0311, &val);
|
ADE_PUBLISH(apparent_power_a, (int32_t) val, pf);
|
||||||
ADE_PUBLISH(apparent_power_b, (int32_t) val, 154.0f);
|
err = this->ade_read_32(reg + 1, &val);
|
||||||
|
ADE_PUBLISH(apparent_power_b, (int32_t) val, pf);
|
||||||
|
|
||||||
// Active power
|
// Active power
|
||||||
err = this->ade_read_32(0x0312, &val);
|
reg = this->use_acc_energy_regs_ ? 0x031E : 0x0312;
|
||||||
ADE_PUBLISH(active_power_a, (int32_t) val, 154.0f);
|
err = this->ade_read_32(reg, &val);
|
||||||
err = this->ade_read_32(0x0313, &val);
|
ADE_PUBLISH(active_power_a, (int32_t) val, pf);
|
||||||
ADE_PUBLISH(active_power_b, (int32_t) val, 154.0f);
|
err = this->ade_read_32(reg + 1, &val);
|
||||||
|
ADE_PUBLISH(active_power_b, (int32_t) val, pf);
|
||||||
|
|
||||||
// Reactive power
|
// Reactive power
|
||||||
err = this->ade_read_32(0x0314, &val);
|
reg = this->use_acc_energy_regs_ ? 0x0320 : 0x0314;
|
||||||
ADE_PUBLISH(reactive_power_a, (int32_t) val, 154.0f);
|
err = this->ade_read_32(reg, &val);
|
||||||
err = this->ade_read_32(0x0315, &val);
|
ADE_PUBLISH(reactive_power_a, (int32_t) val, pf);
|
||||||
ADE_PUBLISH(reactive_power_b, (int32_t) val, 154.0f);
|
err = this->ade_read_32(reg + 1, &val);
|
||||||
|
ADE_PUBLISH(reactive_power_b, (int32_t) val, pf);
|
||||||
|
|
||||||
// Current
|
// Current
|
||||||
err = this->ade_read_32(0x031A, &val);
|
err = this->ade_read_32(0x031A, &val);
|
||||||
|
|
|
@ -52,6 +52,8 @@ class ADE7953 : public PollingComponent, public sensor::Sensor {
|
||||||
void set_awgain(uint32_t awgain) { awgain_ = awgain; }
|
void set_awgain(uint32_t awgain) { awgain_ = awgain; }
|
||||||
void set_bwgain(uint32_t bwgain) { bwgain_ = bwgain; }
|
void set_bwgain(uint32_t bwgain) { bwgain_ = bwgain; }
|
||||||
|
|
||||||
|
void set_use_acc_energy_regs(bool use_acc_energy_regs) { use_acc_energy_regs_ = use_acc_energy_regs; }
|
||||||
|
|
||||||
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
||||||
void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; }
|
void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; }
|
||||||
|
|
||||||
|
@ -103,6 +105,8 @@ class ADE7953 : public PollingComponent, public sensor::Sensor {
|
||||||
uint32_t bigain_;
|
uint32_t bigain_;
|
||||||
uint32_t awgain_;
|
uint32_t awgain_;
|
||||||
uint32_t bwgain_;
|
uint32_t bwgain_;
|
||||||
|
bool use_acc_energy_regs_{false};
|
||||||
|
uint32_t last_update_;
|
||||||
|
|
||||||
virtual bool ade_write_8(uint16_t reg, uint8_t value) = 0;
|
virtual bool ade_write_8(uint16_t reg, uint8_t value) = 0;
|
||||||
|
|
||||||
|
|
|
@ -13,29 +13,29 @@ void AdE7953I2c::dump_config() {
|
||||||
ade7953_base::ADE7953::dump_config();
|
ade7953_base::ADE7953::dump_config();
|
||||||
}
|
}
|
||||||
bool AdE7953I2c::ade_write_8(uint16_t reg, uint8_t value) {
|
bool AdE7953I2c::ade_write_8(uint16_t reg, uint8_t value) {
|
||||||
std::vector<uint8_t> data(3);
|
uint8_t data[3];
|
||||||
data.push_back(reg >> 8);
|
data[0] = reg >> 8;
|
||||||
data.push_back(reg >> 0);
|
data[1] = reg >> 0;
|
||||||
data.push_back(value);
|
data[2] = value;
|
||||||
return this->write(data.data(), data.size()) != i2c::ERROR_OK;
|
return this->write(data, 3) != i2c::ERROR_OK;
|
||||||
}
|
}
|
||||||
bool AdE7953I2c::ade_write_16(uint16_t reg, uint16_t value) {
|
bool AdE7953I2c::ade_write_16(uint16_t reg, uint16_t value) {
|
||||||
std::vector<uint8_t> data(4);
|
uint8_t data[4];
|
||||||
data.push_back(reg >> 8);
|
data[0] = reg >> 8;
|
||||||
data.push_back(reg >> 0);
|
data[1] = reg >> 0;
|
||||||
data.push_back(value >> 8);
|
data[2] = value >> 8;
|
||||||
data.push_back(value >> 0);
|
data[3] = value >> 0;
|
||||||
return this->write(data.data(), data.size()) != i2c::ERROR_OK;
|
return this->write(data, 4) != i2c::ERROR_OK;
|
||||||
}
|
}
|
||||||
bool AdE7953I2c::ade_write_32(uint16_t reg, uint32_t value) {
|
bool AdE7953I2c::ade_write_32(uint16_t reg, uint32_t value) {
|
||||||
std::vector<uint8_t> data(6);
|
uint8_t data[6];
|
||||||
data.push_back(reg >> 8);
|
data[0] = reg >> 8;
|
||||||
data.push_back(reg >> 0);
|
data[1] = reg >> 0;
|
||||||
data.push_back(value >> 24);
|
data[2] = value >> 24;
|
||||||
data.push_back(value >> 16);
|
data[3] = value >> 16;
|
||||||
data.push_back(value >> 8);
|
data[4] = value >> 8;
|
||||||
data.push_back(value >> 0);
|
data[5] = value >> 0;
|
||||||
return this->write(data.data(), data.size()) != i2c::ERROR_OK;
|
return this->write(data, 6) != i2c::ERROR_OK;
|
||||||
}
|
}
|
||||||
bool AdE7953I2c::ade_read_8(uint16_t reg, uint8_t *value) {
|
bool AdE7953I2c::ade_read_8(uint16_t reg, uint8_t *value) {
|
||||||
uint8_t reg_data[2];
|
uint8_t reg_data[2];
|
||||||
|
|
|
@ -4,13 +4,14 @@ from esphome.components import i2c
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
AUTO_LOAD = ["sensor", "voltage_sampler"]
|
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
ads1115_ns = cg.esphome_ns.namespace("ads1115")
|
ads1115_ns = cg.esphome_ns.namespace("ads1115")
|
||||||
ADS1115Component = ads1115_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice)
|
ADS1115Component = ads1115_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice)
|
||||||
|
|
||||||
CONF_CONTINUOUS_MODE = "continuous_mode"
|
CONF_CONTINUOUS_MODE = "continuous_mode"
|
||||||
|
CONF_ADS1115_ID = "ads1115_id"
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "ads1115.h"
|
#include "ads1115.h"
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ads1115 {
|
namespace ads1115 {
|
||||||
|
@ -75,25 +75,19 @@ void ADS1115Component::dump_config() {
|
||||||
if (this->is_failed()) {
|
if (this->is_failed()) {
|
||||||
ESP_LOGE(TAG, "Communication with ADS1115 failed!");
|
ESP_LOGE(TAG, "Communication with ADS1115 failed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto *sensor : this->sensors_) {
|
|
||||||
LOG_SENSOR(" ", "Sensor", sensor);
|
|
||||||
ESP_LOGCONFIG(TAG, " Multiplexer: %u", sensor->get_multiplexer());
|
|
||||||
ESP_LOGCONFIG(TAG, " Gain: %u", sensor->get_gain());
|
|
||||||
ESP_LOGCONFIG(TAG, " Resolution: %u", sensor->get_resolution());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
|
float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain,
|
||||||
|
ADS1115Resolution resolution) {
|
||||||
uint16_t config = this->prev_config_;
|
uint16_t config = this->prev_config_;
|
||||||
// Multiplexer
|
// Multiplexer
|
||||||
// 0bxBBBxxxxxxxxxxxx
|
// 0bxBBBxxxxxxxxxxxx
|
||||||
config &= 0b1000111111111111;
|
config &= 0b1000111111111111;
|
||||||
config |= (sensor->get_multiplexer() & 0b111) << 12;
|
config |= (multiplexer & 0b111) << 12;
|
||||||
|
|
||||||
// Gain
|
// Gain
|
||||||
// 0bxxxxBBBxxxxxxxxx
|
// 0bxxxxBBBxxxxxxxxx
|
||||||
config &= 0b1111000111111111;
|
config &= 0b1111000111111111;
|
||||||
config |= (sensor->get_gain() & 0b111) << 9;
|
config |= (gain & 0b111) << 9;
|
||||||
|
|
||||||
if (!this->continuous_mode_) {
|
if (!this->continuous_mode_) {
|
||||||
// Start conversion
|
// Start conversion
|
||||||
|
@ -132,7 +126,7 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
|
||||||
return NAN;
|
return NAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sensor->get_resolution() == ADS1015_12_BITS) {
|
if (resolution == ADS1015_12_BITS) {
|
||||||
bool negative = (raw_conversion >> 15) == 1;
|
bool negative = (raw_conversion >> 15) == 1;
|
||||||
|
|
||||||
// shift raw_conversion as it's only 12-bits, left justified
|
// shift raw_conversion as it's only 12-bits, left justified
|
||||||
|
@ -151,8 +145,8 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
|
||||||
auto signed_conversion = static_cast<int16_t>(raw_conversion);
|
auto signed_conversion = static_cast<int16_t>(raw_conversion);
|
||||||
|
|
||||||
float millivolts;
|
float millivolts;
|
||||||
float divider = (sensor->get_resolution() == ADS1115_16_BITS) ? 32768.0f : 2048.0f;
|
float divider = (resolution == ADS1115_16_BITS) ? 32768.0f : 2048.0f;
|
||||||
switch (sensor->get_gain()) {
|
switch (gain) {
|
||||||
case ADS1115_GAIN_6P144:
|
case ADS1115_GAIN_6P144:
|
||||||
millivolts = (signed_conversion * 6144) / divider;
|
millivolts = (signed_conversion * 6144) / divider;
|
||||||
break;
|
break;
|
||||||
|
@ -179,14 +173,5 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
|
||||||
return millivolts / 1e3f;
|
return millivolts / 1e3f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); }
|
|
||||||
void ADS1115Sensor::update() {
|
|
||||||
float v = this->parent_->request_measurement(this);
|
|
||||||
if (!std::isnan(v)) {
|
|
||||||
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
|
|
||||||
this->publish_state(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ads1115
|
} // namespace ads1115
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/components/i2c/i2c.h"
|
#include "esphome/components/i2c/i2c.h"
|
||||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -35,12 +33,8 @@ enum ADS1115Resolution {
|
||||||
ADS1015_12_BITS = 12,
|
ADS1015_12_BITS = 12,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ADS1115Sensor;
|
|
||||||
|
|
||||||
class ADS1115Component : public Component, public i2c::I2CDevice {
|
class ADS1115Component : public Component, public i2c::I2CDevice {
|
||||||
public:
|
public:
|
||||||
void register_sensor(ADS1115Sensor *obj) { this->sensors_.push_back(obj); }
|
|
||||||
/// Set up the internal sensor array.
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
/// HARDWARE_LATE setup priority
|
/// HARDWARE_LATE setup priority
|
||||||
|
@ -48,33 +42,12 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
|
||||||
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
|
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
|
||||||
|
|
||||||
/// Helper method to request a measurement from a sensor.
|
/// Helper method to request a measurement from a sensor.
|
||||||
float request_measurement(ADS1115Sensor *sensor);
|
float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<ADS1115Sensor *> sensors_;
|
|
||||||
uint16_t prev_config_{0};
|
uint16_t prev_config_{0};
|
||||||
bool continuous_mode_;
|
bool continuous_mode_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
|
|
||||||
class ADS1115Sensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
|
||||||
public:
|
|
||||||
ADS1115Sensor(ADS1115Component *parent) : parent_(parent) {}
|
|
||||||
void update() override;
|
|
||||||
void set_multiplexer(ADS1115Multiplexer multiplexer) { multiplexer_ = multiplexer; }
|
|
||||||
void set_gain(ADS1115Gain gain) { gain_ = gain; }
|
|
||||||
void set_resolution(ADS1115Resolution resolution) { resolution_ = resolution; }
|
|
||||||
float sample() override;
|
|
||||||
uint8_t get_multiplexer() const { return multiplexer_; }
|
|
||||||
uint8_t get_gain() const { return gain_; }
|
|
||||||
uint8_t get_resolution() const { return resolution_; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ADS1115Component *parent_;
|
|
||||||
ADS1115Multiplexer multiplexer_;
|
|
||||||
ADS1115Gain gain_;
|
|
||||||
ADS1115Resolution resolution_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ads1115
|
} // namespace ads1115
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -10,8 +10,9 @@ from esphome.const import (
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
)
|
)
|
||||||
from . import ads1115_ns, ADS1115Component
|
from .. import ads1115_ns, ADS1115Component, CONF_ADS1115_ID
|
||||||
|
|
||||||
|
AUTO_LOAD = ["voltage_sampler"]
|
||||||
DEPENDENCIES = ["ads1115"]
|
DEPENDENCIES = ["ads1115"]
|
||||||
|
|
||||||
ADS1115Multiplexer = ads1115_ns.enum("ADS1115Multiplexer")
|
ADS1115Multiplexer = ads1115_ns.enum("ADS1115Multiplexer")
|
||||||
|
@ -43,20 +44,10 @@ RESOLUTION = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def validate_gain(value):
|
|
||||||
if isinstance(value, float):
|
|
||||||
value = f"{value:0.03f}"
|
|
||||||
elif not isinstance(value, str):
|
|
||||||
raise cv.Invalid(f'invalid gain "{value}"')
|
|
||||||
|
|
||||||
return cv.enum(GAIN)(value)
|
|
||||||
|
|
||||||
|
|
||||||
ADS1115Sensor = ads1115_ns.class_(
|
ADS1115Sensor = ads1115_ns.class_(
|
||||||
"ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
|
"ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
|
||||||
)
|
)
|
||||||
|
|
||||||
CONF_ADS1115_ID = "ads1115_id"
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
sensor.sensor_schema(
|
sensor.sensor_schema(
|
||||||
ADS1115Sensor,
|
ADS1115Sensor,
|
||||||
|
@ -69,7 +60,7 @@ CONFIG_SCHEMA = (
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
|
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
|
||||||
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
|
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
|
||||||
cv.Required(CONF_GAIN): validate_gain,
|
cv.Required(CONF_GAIN): cv.enum(GAIN, string=True),
|
||||||
cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum(
|
cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum(
|
||||||
RESOLUTION, upper=True, space="_"
|
RESOLUTION, upper=True, space="_"
|
||||||
),
|
),
|
||||||
|
@ -80,13 +71,11 @@ CONFIG_SCHEMA = (
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
paren = await cg.get_variable(config[CONF_ADS1115_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
|
||||||
await sensor.register_sensor(var, config)
|
await sensor.register_sensor(var, config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
await cg.register_parented(var, config[CONF_ADS1115_ID])
|
||||||
|
|
||||||
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
|
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
|
||||||
cg.add(var.set_gain(config[CONF_GAIN]))
|
cg.add(var.set_gain(config[CONF_GAIN]))
|
||||||
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
|
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
|
||||||
|
|
||||||
cg.add(paren.register_sensor(var))
|
|
30
esphome/components/ads1115/sensor/ads1115_sensor.cpp
Normal file
30
esphome/components/ads1115/sensor/ads1115_sensor.cpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#include "ads1115_sensor.h"
|
||||||
|
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ads1115 {
|
||||||
|
|
||||||
|
static const char *const TAG = "ads1115.sensor";
|
||||||
|
|
||||||
|
float ADS1115Sensor::sample() {
|
||||||
|
return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADS1115Sensor::update() {
|
||||||
|
float v = this->sample();
|
||||||
|
if (!std::isnan(v)) {
|
||||||
|
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
|
||||||
|
this->publish_state(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADS1115Sensor::dump_config() {
|
||||||
|
LOG_SENSOR(" ", "ADS1115 Sensor", this);
|
||||||
|
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ads1115
|
||||||
|
} // namespace esphome
|
35
esphome/components/ads1115/sensor/ads1115_sensor.h
Normal file
35
esphome/components/ads1115/sensor/ads1115_sensor.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||||
|
|
||||||
|
#include "../ads1115.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ads1115 {
|
||||||
|
|
||||||
|
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
|
||||||
|
class ADS1115Sensor : public sensor::Sensor,
|
||||||
|
public PollingComponent,
|
||||||
|
public voltage_sampler::VoltageSampler,
|
||||||
|
public Parented<ADS1115Component> {
|
||||||
|
public:
|
||||||
|
void update() override;
|
||||||
|
void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
|
||||||
|
void set_gain(ADS1115Gain gain) { this->gain_ = gain; }
|
||||||
|
void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; }
|
||||||
|
float sample() override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ADS1115Multiplexer multiplexer_;
|
||||||
|
ADS1115Gain gain_;
|
||||||
|
ADS1115Resolution resolution_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ads1115
|
||||||
|
} // namespace esphome
|
25
esphome/components/ads1118/__init__.py
Normal file
25
esphome/components/ads1118/__init__.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import spi
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
CODEOWNERS = ["@solomondg1"]
|
||||||
|
DEPENDENCIES = ["spi"]
|
||||||
|
MULTI_CONF = True
|
||||||
|
|
||||||
|
CONF_ADS1118_ID = "ads1118_id"
|
||||||
|
|
||||||
|
ads1118_ns = cg.esphome_ns.namespace("ads1118")
|
||||||
|
ADS1118 = ads1118_ns.class_("ADS1118", cg.Component, spi.SPIDevice)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(ADS1118),
|
||||||
|
}
|
||||||
|
).extend(spi.spi_device_schema(cs_pin_required=True))
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await spi.register_spi_device(var, config)
|
126
esphome/components/ads1118/ads1118.cpp
Normal file
126
esphome/components/ads1118/ads1118.cpp
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
#include "ads1118.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ads1118 {
|
||||||
|
|
||||||
|
static const char *const TAG = "ads1118";
|
||||||
|
static const uint8_t ADS1118_DATA_RATE_860_SPS = 0b111;
|
||||||
|
|
||||||
|
void ADS1118::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up ads1118");
|
||||||
|
this->spi_setup();
|
||||||
|
|
||||||
|
this->config_ = 0;
|
||||||
|
// Setup multiplexer
|
||||||
|
// 0bx000xxxxxxxxxxxx
|
||||||
|
this->config_ |= ADS1118_MULTIPLEXER_P0_NG << 12;
|
||||||
|
|
||||||
|
// Setup Gain
|
||||||
|
// 0bxxxx000xxxxxxxxx
|
||||||
|
this->config_ |= ADS1118_GAIN_6P144 << 9;
|
||||||
|
|
||||||
|
// Set singleshot mode
|
||||||
|
// 0bxxxxxxx1xxxxxxxx
|
||||||
|
this->config_ |= 0b0000000100000000;
|
||||||
|
|
||||||
|
// Set data rate - 860 samples per second (we're in singleshot mode)
|
||||||
|
// 0bxxxxxxxx100xxxxx
|
||||||
|
this->config_ |= ADS1118_DATA_RATE_860_SPS << 5;
|
||||||
|
|
||||||
|
// Set temperature sensor mode - ADC
|
||||||
|
// 0bxxxxxxxxxxx0xxxx
|
||||||
|
this->config_ |= 0b0000000000000000;
|
||||||
|
|
||||||
|
// Set DOUT pull up - enable
|
||||||
|
// 0bxxxxxxxxxxxx0xxx
|
||||||
|
this->config_ |= 0b0000000000001000;
|
||||||
|
|
||||||
|
// NOP - must be 01
|
||||||
|
// 0bxxxxxxxxxxxxx01x
|
||||||
|
this->config_ |= 0b0000000000000010;
|
||||||
|
|
||||||
|
// Not used - can be 0 or 1, lets be positive
|
||||||
|
// 0bxxxxxxxxxxxxxxx1
|
||||||
|
this->config_ |= 0b0000000000000001;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADS1118::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "ADS1118:");
|
||||||
|
LOG_PIN(" CS Pin:", this->cs_);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ADS1118::request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain gain, bool temperature_mode) {
|
||||||
|
uint16_t temp_config = this->config_;
|
||||||
|
// Multiplexer
|
||||||
|
// 0bxBBBxxxxxxxxxxxx
|
||||||
|
temp_config &= 0b1000111111111111;
|
||||||
|
temp_config |= (multiplexer & 0b111) << 12;
|
||||||
|
|
||||||
|
// Gain
|
||||||
|
// 0bxxxxBBBxxxxxxxxx
|
||||||
|
temp_config &= 0b1111000111111111;
|
||||||
|
temp_config |= (gain & 0b111) << 9;
|
||||||
|
|
||||||
|
if (temperature_mode) {
|
||||||
|
// Set temperature sensor mode
|
||||||
|
// 0bxxxxxxxxxxx1xxxx
|
||||||
|
temp_config |= 0b0000000000010000;
|
||||||
|
} else {
|
||||||
|
// Set ADC mode
|
||||||
|
// 0bxxxxxxxxxxx0xxxx
|
||||||
|
temp_config &= 0b1111111111101111;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start conversion
|
||||||
|
temp_config |= 0b1000000000000000;
|
||||||
|
|
||||||
|
this->enable();
|
||||||
|
this->write_byte16(temp_config);
|
||||||
|
this->disable();
|
||||||
|
|
||||||
|
// about 1.2 ms with 860 samples per second
|
||||||
|
delay(2);
|
||||||
|
|
||||||
|
this->enable();
|
||||||
|
uint8_t adc_first_byte = this->read_byte();
|
||||||
|
uint8_t adc_second_byte = this->read_byte();
|
||||||
|
this->disable();
|
||||||
|
uint16_t raw_conversion = encode_uint16(adc_first_byte, adc_second_byte);
|
||||||
|
|
||||||
|
auto signed_conversion = static_cast<int16_t>(raw_conversion);
|
||||||
|
|
||||||
|
if (temperature_mode) {
|
||||||
|
return (signed_conversion >> 2) * 0.03125f;
|
||||||
|
} else {
|
||||||
|
float millivolts;
|
||||||
|
float divider = 32768.0f;
|
||||||
|
switch (gain) {
|
||||||
|
case ADS1118_GAIN_6P144:
|
||||||
|
millivolts = (signed_conversion * 6144) / divider;
|
||||||
|
break;
|
||||||
|
case ADS1118_GAIN_4P096:
|
||||||
|
millivolts = (signed_conversion * 4096) / divider;
|
||||||
|
break;
|
||||||
|
case ADS1118_GAIN_2P048:
|
||||||
|
millivolts = (signed_conversion * 2048) / divider;
|
||||||
|
break;
|
||||||
|
case ADS1118_GAIN_1P024:
|
||||||
|
millivolts = (signed_conversion * 1024) / divider;
|
||||||
|
break;
|
||||||
|
case ADS1118_GAIN_0P512:
|
||||||
|
millivolts = (signed_conversion * 512) / divider;
|
||||||
|
break;
|
||||||
|
case ADS1118_GAIN_0P256:
|
||||||
|
millivolts = (signed_conversion * 256) / divider;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
millivolts = NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return millivolts / 1e3f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ads1118
|
||||||
|
} // namespace esphome
|
46
esphome/components/ads1118/ads1118.h
Normal file
46
esphome/components/ads1118/ads1118.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/spi/spi.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ads1118 {
|
||||||
|
|
||||||
|
enum ADS1118Multiplexer {
|
||||||
|
ADS1118_MULTIPLEXER_P0_N1 = 0b000,
|
||||||
|
ADS1118_MULTIPLEXER_P0_N3 = 0b001,
|
||||||
|
ADS1118_MULTIPLEXER_P1_N3 = 0b010,
|
||||||
|
ADS1118_MULTIPLEXER_P2_N3 = 0b011,
|
||||||
|
ADS1118_MULTIPLEXER_P0_NG = 0b100,
|
||||||
|
ADS1118_MULTIPLEXER_P1_NG = 0b101,
|
||||||
|
ADS1118_MULTIPLEXER_P2_NG = 0b110,
|
||||||
|
ADS1118_MULTIPLEXER_P3_NG = 0b111,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ADS1118Gain {
|
||||||
|
ADS1118_GAIN_6P144 = 0b000,
|
||||||
|
ADS1118_GAIN_4P096 = 0b001,
|
||||||
|
ADS1118_GAIN_2P048 = 0b010,
|
||||||
|
ADS1118_GAIN_1P024 = 0b011,
|
||||||
|
ADS1118_GAIN_0P512 = 0b100,
|
||||||
|
ADS1118_GAIN_0P256 = 0b101,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ADS1118 : public Component,
|
||||||
|
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_TRAILING,
|
||||||
|
spi::DATA_RATE_1MHZ> {
|
||||||
|
public:
|
||||||
|
ADS1118() = default;
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
/// Helper method to request a measurement from a sensor.
|
||||||
|
float request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain gain, bool temperature_mode);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint16_t config_{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ads1118
|
||||||
|
} // namespace esphome
|
97
esphome/components/ads1118/sensor/__init__.py
Normal file
97
esphome/components/ads1118/sensor/__init__.py
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, voltage_sampler
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_GAIN,
|
||||||
|
CONF_MULTIPLEXER,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_VOLT,
|
||||||
|
CONF_TYPE,
|
||||||
|
)
|
||||||
|
from .. import ads1118_ns, ADS1118, CONF_ADS1118_ID
|
||||||
|
|
||||||
|
AUTO_LOAD = ["voltage_sampler"]
|
||||||
|
DEPENDENCIES = ["ads1118"]
|
||||||
|
|
||||||
|
ADS1118Multiplexer = ads1118_ns.enum("ADS1118Multiplexer")
|
||||||
|
MUX = {
|
||||||
|
"A0_A1": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P0_N1,
|
||||||
|
"A0_A3": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P0_N3,
|
||||||
|
"A1_A3": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P1_N3,
|
||||||
|
"A2_A3": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P2_N3,
|
||||||
|
"A0_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P0_NG,
|
||||||
|
"A1_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P1_NG,
|
||||||
|
"A2_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P2_NG,
|
||||||
|
"A3_GND": ADS1118Multiplexer.ADS1118_MULTIPLEXER_P3_NG,
|
||||||
|
}
|
||||||
|
|
||||||
|
ADS1118Gain = ads1118_ns.enum("ADS1118Gain")
|
||||||
|
GAIN = {
|
||||||
|
"6.144": ADS1118Gain.ADS1118_GAIN_6P144,
|
||||||
|
"4.096": ADS1118Gain.ADS1118_GAIN_4P096,
|
||||||
|
"2.048": ADS1118Gain.ADS1118_GAIN_2P048,
|
||||||
|
"1.024": ADS1118Gain.ADS1118_GAIN_1P024,
|
||||||
|
"0.512": ADS1118Gain.ADS1118_GAIN_0P512,
|
||||||
|
"0.256": ADS1118Gain.ADS1118_GAIN_0P256,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ADS1118Sensor = ads1118_ns.class_(
|
||||||
|
"ADS1118Sensor",
|
||||||
|
cg.PollingComponent,
|
||||||
|
sensor.Sensor,
|
||||||
|
voltage_sampler.VoltageSampler,
|
||||||
|
cg.Parented.template(ADS1118),
|
||||||
|
)
|
||||||
|
|
||||||
|
TYPE_ADC = "adc"
|
||||||
|
TYPE_TEMPERATURE = "temperature"
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.typed_schema(
|
||||||
|
{
|
||||||
|
TYPE_ADC: sensor.sensor_schema(
|
||||||
|
ADS1118Sensor,
|
||||||
|
unit_of_measurement=UNIT_VOLT,
|
||||||
|
accuracy_decimals=3,
|
||||||
|
device_class=DEVICE_CLASS_VOLTAGE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
)
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_ADS1118_ID): cv.use_id(ADS1118),
|
||||||
|
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
|
||||||
|
cv.Required(CONF_GAIN): cv.enum(GAIN, string=True),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s")),
|
||||||
|
TYPE_TEMPERATURE: sensor.sensor_schema(
|
||||||
|
ADS1118Sensor,
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
)
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_ADS1118_ID): cv.use_id(ADS1118),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s")),
|
||||||
|
},
|
||||||
|
default_type=TYPE_ADC,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = await sensor.new_sensor(config)
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await cg.register_parented(var, config[CONF_ADS1118_ID])
|
||||||
|
|
||||||
|
if config[CONF_TYPE] == TYPE_ADC:
|
||||||
|
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
|
||||||
|
cg.add(var.set_gain(config[CONF_GAIN]))
|
||||||
|
if config[CONF_TYPE] == TYPE_TEMPERATURE:
|
||||||
|
cg.add(var.set_temperature_mode(True))
|
29
esphome/components/ads1118/sensor/ads1118_sensor.cpp
Normal file
29
esphome/components/ads1118/sensor/ads1118_sensor.cpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#include "ads1118_sensor.h"
|
||||||
|
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ads1118 {
|
||||||
|
|
||||||
|
static const char *const TAG = "ads1118.sensor";
|
||||||
|
|
||||||
|
void ADS1118Sensor::dump_config() {
|
||||||
|
LOG_SENSOR(" ", "ADS1118 Sensor", this);
|
||||||
|
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ADS1118Sensor::sample() {
|
||||||
|
return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->temperature_mode_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADS1118Sensor::update() {
|
||||||
|
float v = this->sample();
|
||||||
|
if (!std::isnan(v)) {
|
||||||
|
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
|
||||||
|
this->publish_state(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ads1118
|
||||||
|
} // namespace esphome
|
36
esphome/components/ads1118/sensor/ads1118_sensor.h
Normal file
36
esphome/components/ads1118/sensor/ads1118_sensor.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||||
|
|
||||||
|
#include "../ads1118.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ads1118 {
|
||||||
|
|
||||||
|
class ADS1118Sensor : public PollingComponent,
|
||||||
|
public sensor::Sensor,
|
||||||
|
public voltage_sampler::VoltageSampler,
|
||||||
|
public Parented<ADS1118> {
|
||||||
|
public:
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void set_multiplexer(ADS1118Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
|
||||||
|
void set_gain(ADS1118Gain gain) { this->gain_ = gain; }
|
||||||
|
void set_temperature_mode(bool temp) { this->temperature_mode_ = temp; }
|
||||||
|
|
||||||
|
float sample() override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ADS1118Multiplexer multiplexer_{ADS1118_MULTIPLEXER_P0_NG};
|
||||||
|
ADS1118Gain gain_{ADS1118_GAIN_6P144};
|
||||||
|
bool temperature_mode_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ads1118
|
||||||
|
} // namespace esphome
|
1
esphome/components/ags10/__init__.py
Normal file
1
esphome/components/ags10/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@mak-42"]
|
212
esphome/components/ags10/ags10.cpp
Normal file
212
esphome/components/ags10/ags10.cpp
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
#include "ags10.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ags10 {
|
||||||
|
static const char *const TAG = "ags10";
|
||||||
|
|
||||||
|
// Data acquisition.
|
||||||
|
static const uint8_t REG_TVOC = 0x00;
|
||||||
|
// Zero-point calibration.
|
||||||
|
static const uint8_t REG_CALIBRATION = 0x01;
|
||||||
|
// Read version.
|
||||||
|
static const uint8_t REG_VERSION = 0x11;
|
||||||
|
// Read current resistance.
|
||||||
|
static const uint8_t REG_RESISTANCE = 0x20;
|
||||||
|
// Modify target address.
|
||||||
|
static const uint8_t REG_ADDRESS = 0x21;
|
||||||
|
|
||||||
|
// Zero-point calibration with current resistance.
|
||||||
|
static const uint16_t ZP_CURRENT = 0x0000;
|
||||||
|
// Zero-point reset.
|
||||||
|
static const uint16_t ZP_DEFAULT = 0xFFFF;
|
||||||
|
|
||||||
|
void AGS10Component::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up ags10...");
|
||||||
|
|
||||||
|
auto version = this->read_version_();
|
||||||
|
if (version) {
|
||||||
|
ESP_LOGD(TAG, "AGS10 Sensor Version: 0x%02X", *version);
|
||||||
|
if (this->version_ != nullptr) {
|
||||||
|
this->version_->publish_state(*version);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "AGS10 Sensor Version: unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resistance = this->read_resistance_();
|
||||||
|
if (resistance) {
|
||||||
|
ESP_LOGD(TAG, "AGS10 Sensor Resistance: 0x%08X", *resistance);
|
||||||
|
if (this->resistance_ != nullptr) {
|
||||||
|
this->resistance_->publish_state(*resistance);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "AGS10 Sensor Resistance: unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Sensor initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AGS10Component::update() {
|
||||||
|
auto tvoc = this->read_tvoc_();
|
||||||
|
if (tvoc) {
|
||||||
|
this->tvoc_->publish_state(*tvoc);
|
||||||
|
this->status_clear_warning();
|
||||||
|
} else {
|
||||||
|
this->status_set_warning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AGS10Component::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "AGS10:");
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
switch (this->error_code_) {
|
||||||
|
case NONE:
|
||||||
|
break;
|
||||||
|
case COMMUNICATION_FAILED:
|
||||||
|
ESP_LOGE(TAG, "Communication with AGS10 failed!");
|
||||||
|
break;
|
||||||
|
case CRC_CHECK_FAILED:
|
||||||
|
ESP_LOGE(TAG, "The crc check failed");
|
||||||
|
break;
|
||||||
|
case ILLEGAL_STATUS:
|
||||||
|
ESP_LOGE(TAG, "AGS10 is not ready to return TVOC data or sensor in pre-heat stage.");
|
||||||
|
break;
|
||||||
|
case UNSUPPORTED_UNITS:
|
||||||
|
ESP_LOGE(TAG, "AGS10 returns TVOC data in unsupported units.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "Unknown error: %d", this->error_code_);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_);
|
||||||
|
LOG_SENSOR(" ", "Firmware Version Sensor", this->version_);
|
||||||
|
LOG_SENSOR(" ", "Resistance Sensor", this->resistance_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets new I2C address of AGS10.
|
||||||
|
*/
|
||||||
|
bool AGS10Component::new_i2c_address(uint8_t newaddress) {
|
||||||
|
uint8_t rev_newaddress = ~newaddress;
|
||||||
|
std::array<uint8_t, 5> data{newaddress, rev_newaddress, newaddress, rev_newaddress, 0};
|
||||||
|
data[4] = calc_crc8_(data, 4);
|
||||||
|
if (!this->write_bytes(REG_ADDRESS, data)) {
|
||||||
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
|
this->status_set_warning();
|
||||||
|
ESP_LOGE(TAG, "couldn't write the new I2C address 0x%02X", newaddress);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this->set_i2c_address(newaddress);
|
||||||
|
ESP_LOGW(TAG, "changed I2C address to 0x%02X", newaddress);
|
||||||
|
this->error_code_ = NONE;
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AGS10Component::set_zero_point_with_factory_defaults() { return this->set_zero_point_with(ZP_DEFAULT); }
|
||||||
|
|
||||||
|
bool AGS10Component::set_zero_point_with_current_resistance() { return this->set_zero_point_with(ZP_CURRENT); }
|
||||||
|
|
||||||
|
bool AGS10Component::set_zero_point_with(uint16_t value) {
|
||||||
|
std::array<uint8_t, 5> data{0x00, 0x0C, (uint8_t) ((value >> 8) & 0xFF), (uint8_t) (value & 0xFF), 0};
|
||||||
|
data[4] = calc_crc8_(data, 4);
|
||||||
|
if (!this->write_bytes(REG_CALIBRATION, data)) {
|
||||||
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
|
this->status_set_warning();
|
||||||
|
ESP_LOGE(TAG, "unable to set zero-point calibration with 0x%02X", value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (value == ZP_CURRENT) {
|
||||||
|
ESP_LOGI(TAG, "zero-point calibration has been set with current resistance");
|
||||||
|
} else if (value == ZP_DEFAULT) {
|
||||||
|
ESP_LOGI(TAG, "zero-point calibration has been reset to the factory defaults");
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "zero-point calibration has been set with 0x%02X", value);
|
||||||
|
}
|
||||||
|
this->error_code_ = NONE;
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<uint32_t> AGS10Component::read_tvoc_() {
|
||||||
|
auto data = this->read_and_check_<5>(REG_TVOC);
|
||||||
|
if (!data) {
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = *data;
|
||||||
|
auto status_byte = res[0];
|
||||||
|
|
||||||
|
int units = status_byte & 0x0e;
|
||||||
|
int status_bit = status_byte & 0x01;
|
||||||
|
|
||||||
|
if (status_bit != 0) {
|
||||||
|
this->error_code_ = ILLEGAL_STATUS;
|
||||||
|
ESP_LOGW(TAG, "Reading AGS10 data failed: illegal status (not ready or sensor in pre-heat stage)!");
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (units != 0) {
|
||||||
|
this->error_code_ = UNSUPPORTED_UNITS;
|
||||||
|
ESP_LOGE(TAG, "Reading AGS10 data failed: unsupported units (%d)!", units);
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return encode_uint24(res[1], res[2], res[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<uint8_t> AGS10Component::read_version_() {
|
||||||
|
auto data = this->read_and_check_<5>(REG_VERSION);
|
||||||
|
if (data) {
|
||||||
|
auto res = *data;
|
||||||
|
return res[3];
|
||||||
|
}
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<uint32_t> AGS10Component::read_resistance_() {
|
||||||
|
auto data = this->read_and_check_<5>(REG_RESISTANCE);
|
||||||
|
if (data) {
|
||||||
|
auto res = *data;
|
||||||
|
return encode_uint32(res[0], res[1], res[2], res[3]);
|
||||||
|
}
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_check_(uint8_t a_register) {
|
||||||
|
auto data = this->read_bytes<N>(a_register);
|
||||||
|
if (!data.has_value()) {
|
||||||
|
this->error_code_ = COMMUNICATION_FAILED;
|
||||||
|
ESP_LOGE(TAG, "Reading AGS10 version failed!");
|
||||||
|
return optional<std::array<uint8_t, N>>();
|
||||||
|
}
|
||||||
|
auto len = N - 1;
|
||||||
|
auto res = *data;
|
||||||
|
auto crc_byte = res[len];
|
||||||
|
|
||||||
|
if (crc_byte != calc_crc8_(res, len)) {
|
||||||
|
this->error_code_ = CRC_CHECK_FAILED;
|
||||||
|
ESP_LOGE(TAG, "Reading AGS10 version failed: crc error!");
|
||||||
|
return optional<std::array<uint8_t, N>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t N> uint8_t AGS10Component::calc_crc8_(std::array<uint8_t, N> dat, uint8_t num) {
|
||||||
|
uint8_t i, byte1, crc = 0xFF;
|
||||||
|
for (byte1 = 0; byte1 < num; byte1++) {
|
||||||
|
crc ^= (dat[byte1]);
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
if (crc & 0x80) {
|
||||||
|
crc = (crc << 1) ^ 0x31;
|
||||||
|
} else {
|
||||||
|
crc = (crc << 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
} // namespace ags10
|
||||||
|
} // namespace esphome
|
152
esphome/components/ags10/ags10.h
Normal file
152
esphome/components/ags10/ags10.h
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ags10 {
|
||||||
|
|
||||||
|
class AGS10Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Sets TVOC sensor.
|
||||||
|
*/
|
||||||
|
void set_tvoc(sensor::Sensor *tvoc) { this->tvoc_ = tvoc; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets version info sensor.
|
||||||
|
*/
|
||||||
|
void set_version(sensor::Sensor *version) { this->version_ = version; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets resistance info sensor.
|
||||||
|
*/
|
||||||
|
void set_resistance(sensor::Sensor *resistance) { this->resistance_ = resistance; }
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies target address of AGS10.
|
||||||
|
*
|
||||||
|
* New address is saved and takes effect immediately even after power-off.
|
||||||
|
*/
|
||||||
|
bool new_i2c_address(uint8_t newaddress);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets zero-point with factory defaults.
|
||||||
|
*/
|
||||||
|
bool set_zero_point_with_factory_defaults();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets zero-point with current sensor resistance.
|
||||||
|
*/
|
||||||
|
bool set_zero_point_with_current_resistance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets zero-point with the value.
|
||||||
|
*/
|
||||||
|
bool set_zero_point_with(uint16_t value);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* TVOC.
|
||||||
|
*/
|
||||||
|
sensor::Sensor *tvoc_{nullptr};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Firmvare version.
|
||||||
|
*/
|
||||||
|
sensor::Sensor *version_{nullptr};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resistance.
|
||||||
|
*/
|
||||||
|
sensor::Sensor *resistance_{nullptr};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last operation error code.
|
||||||
|
*/
|
||||||
|
enum ErrorCode {
|
||||||
|
NONE = 0,
|
||||||
|
COMMUNICATION_FAILED,
|
||||||
|
CRC_CHECK_FAILED,
|
||||||
|
ILLEGAL_STATUS,
|
||||||
|
UNSUPPORTED_UNITS,
|
||||||
|
} error_code_{NONE};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads and returns value of TVOC.
|
||||||
|
*/
|
||||||
|
optional<uint32_t> read_tvoc_();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads and returns a firmware version of AGS10.
|
||||||
|
*/
|
||||||
|
optional<uint8_t> read_version_();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads and returns the resistance of AGS10.
|
||||||
|
*/
|
||||||
|
optional<uint32_t> read_resistance_();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read, checks and returns data from the sensor.
|
||||||
|
*/
|
||||||
|
template<size_t N> optional<std::array<uint8_t, N>> read_and_check_(uint8_t a_register);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates CRC8 value.
|
||||||
|
*
|
||||||
|
* CRC8 calculation, initial value: 0xFF, polynomial: 0x31 (x8+ x5+ x4+1)
|
||||||
|
*
|
||||||
|
* @param[in] dat the data buffer
|
||||||
|
* @param num number of bytes in the buffer
|
||||||
|
*/
|
||||||
|
template<size_t N> uint8_t calc_crc8_(std::array<uint8_t, N> dat, uint8_t num);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class AGS10NewI2cAddressAction : public Action<Ts...>, public Parented<AGS10Component> {
|
||||||
|
public:
|
||||||
|
TEMPLATABLE_VALUE(uint8_t, new_address)
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->parent_->new_i2c_address(this->new_address_.value(x...)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AGS10SetZeroPointActionMode {
|
||||||
|
// Zero-point reset.
|
||||||
|
FACTORY_DEFAULT,
|
||||||
|
// Zero-point calibration with current resistance.
|
||||||
|
CURRENT_VALUE,
|
||||||
|
// Zero-point calibration with custom resistance.
|
||||||
|
CUSTOM_VALUE,
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class AGS10SetZeroPointAction : public Action<Ts...>, public Parented<AGS10Component> {
|
||||||
|
public:
|
||||||
|
TEMPLATABLE_VALUE(uint16_t, value)
|
||||||
|
TEMPLATABLE_VALUE(AGS10SetZeroPointActionMode, mode)
|
||||||
|
|
||||||
|
void play(Ts... x) override {
|
||||||
|
switch (this->mode_.value(x...)) {
|
||||||
|
case FACTORY_DEFAULT:
|
||||||
|
this->parent_->set_zero_point_with_factory_defaults();
|
||||||
|
break;
|
||||||
|
case CURRENT_VALUE:
|
||||||
|
this->parent_->set_zero_point_with_current_resistance();
|
||||||
|
break;
|
||||||
|
case CUSTOM_VALUE:
|
||||||
|
this->parent_->set_zero_point_with(this->value_.value(x...));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace ags10
|
||||||
|
} // namespace esphome
|
132
esphome/components/ags10/sensor.py
Normal file
132
esphome/components/ags10/sensor.py
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome import automation
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
ICON_RADIATOR,
|
||||||
|
ICON_RESTART,
|
||||||
|
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
||||||
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_OHM,
|
||||||
|
UNIT_PARTS_PER_BILLION,
|
||||||
|
CONF_ADDRESS,
|
||||||
|
CONF_TVOC,
|
||||||
|
CONF_VERSION,
|
||||||
|
CONF_MODE,
|
||||||
|
CONF_VALUE,
|
||||||
|
)
|
||||||
|
|
||||||
|
CONF_RESISTANCE = "resistance"
|
||||||
|
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
|
ags10_ns = cg.esphome_ns.namespace("ags10")
|
||||||
|
AGS10Component = ags10_ns.class_("AGS10Component", cg.PollingComponent, i2c.I2CDevice)
|
||||||
|
|
||||||
|
# Actions
|
||||||
|
AGS10NewI2cAddressAction = ags10_ns.class_(
|
||||||
|
"AGS10NewI2cAddressAction", automation.Action
|
||||||
|
)
|
||||||
|
AGS10SetZeroPointAction = ags10_ns.class_("AGS10SetZeroPointAction", automation.Action)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(AGS10Component),
|
||||||
|
cv.Optional(CONF_TVOC): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PARTS_PER_BILLION,
|
||||||
|
icon=ICON_RADIATOR,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_VERSION): sensor.sensor_schema(
|
||||||
|
icon=ICON_RESTART,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_RESISTANCE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_OHM,
|
||||||
|
icon=ICON_RESTART,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x1A))
|
||||||
|
)
|
||||||
|
|
||||||
|
FINAL_VALIDATE_SCHEMA = i2c.final_validate_device_schema("ags10", max_frequency="15khz")
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
sens = await sensor.new_sensor(config[CONF_TVOC])
|
||||||
|
cg.add(var.set_tvoc(sens))
|
||||||
|
|
||||||
|
if version_config := config.get(CONF_VERSION):
|
||||||
|
sens = await sensor.new_sensor(version_config)
|
||||||
|
cg.add(var.set_version(sens))
|
||||||
|
|
||||||
|
if resistance_config := config.get(CONF_RESISTANCE):
|
||||||
|
sens = await sensor.new_sensor(resistance_config)
|
||||||
|
cg.add(var.set_resistance(sens))
|
||||||
|
|
||||||
|
|
||||||
|
AGS10_NEW_I2C_ADDRESS_SCHEMA = cv.maybe_simple_value(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(AGS10Component),
|
||||||
|
cv.Required(CONF_ADDRESS): cv.templatable(cv.i2c_address),
|
||||||
|
},
|
||||||
|
key=CONF_ADDRESS,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"ags10.new_i2c_address",
|
||||||
|
AGS10NewI2cAddressAction,
|
||||||
|
AGS10_NEW_I2C_ADDRESS_SCHEMA,
|
||||||
|
)
|
||||||
|
async def ags10newi2caddress_to_code(config, action_id, template_arg, args):
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
|
await cg.register_parented(var, config[CONF_ID])
|
||||||
|
address = await cg.templatable(config[CONF_ADDRESS], args, int)
|
||||||
|
cg.add(var.set_new_address(address))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
AGS10SetZeroPointActionMode = ags10_ns.enum("AGS10SetZeroPointActionMode")
|
||||||
|
AGS10_SET_ZERO_POINT_ACTION_MODE = {
|
||||||
|
"FACTORY_DEFAULT": AGS10SetZeroPointActionMode.FACTORY_DEFAULT,
|
||||||
|
"CURRENT_VALUE": AGS10SetZeroPointActionMode.CURRENT_VALUE,
|
||||||
|
"CUSTOM_VALUE": AGS10SetZeroPointActionMode.CUSTOM_VALUE,
|
||||||
|
}
|
||||||
|
|
||||||
|
AGS10_SET_ZERO_POINT_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(AGS10Component),
|
||||||
|
cv.Required(CONF_MODE): cv.enum(AGS10_SET_ZERO_POINT_ACTION_MODE, upper=True),
|
||||||
|
cv.Optional(CONF_VALUE, default=0xFFFF): cv.templatable(cv.uint16_t),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"ags10.set_zero_point",
|
||||||
|
AGS10SetZeroPointAction,
|
||||||
|
AGS10_SET_ZERO_POINT_SCHEMA,
|
||||||
|
)
|
||||||
|
async def ags10setzeropoint_to_code(config, action_id, template_arg, args):
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
|
await cg.register_parented(var, config[CONF_ID])
|
||||||
|
mode = await cg.templatable(config.get(CONF_MODE), args, enumerate)
|
||||||
|
cg.add(var.set_mode(mode))
|
||||||
|
value = await cg.templatable(config[CONF_VALUE], args, int)
|
||||||
|
cg.add(var.set_value(value))
|
||||||
|
return var
|
|
@ -15,36 +15,43 @@
|
||||||
#include "aht10.h"
|
#include "aht10.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include <cinttypes>
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace aht10 {
|
namespace aht10 {
|
||||||
|
|
||||||
static const char *const TAG = "aht10";
|
static const char *const TAG = "aht10";
|
||||||
static const size_t SIZE_CALIBRATE_CMD = 3;
|
static const uint8_t AHT10_INITIALIZE_CMD[] = {0xE1, 0x08, 0x00};
|
||||||
static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1, 0x08, 0x00};
|
static const uint8_t AHT20_INITIALIZE_CMD[] = {0xBE, 0x08, 0x00};
|
||||||
static const uint8_t AHT20_CALIBRATE_CMD[] = {0xBE, 0x08, 0x00};
|
|
||||||
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
|
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
|
||||||
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement
|
static const uint8_t AHT10_SOFTRESET_CMD[] = {0xBA};
|
||||||
static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms
|
|
||||||
static const uint8_t AHT10_ATTEMPTS = 3; // safety margin, normally 3 attempts are enough: 3*30=90ms
|
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for initialization and temperature measurement
|
||||||
static const uint8_t AHT10_CAL_ATTEMPTS = 10;
|
static const uint8_t AHT10_READ_DELAY = 80; // ms, time to wait for conversion result
|
||||||
|
static const uint8_t AHT10_SOFTRESET_DELAY = 30; // ms
|
||||||
|
|
||||||
|
static const uint8_t AHT10_ATTEMPTS = 3; // safety margin, normally 3 attempts are enough: 3*30=90ms
|
||||||
|
static const uint8_t AHT10_INIT_ATTEMPTS = 10;
|
||||||
|
|
||||||
static const uint8_t AHT10_STATUS_BUSY = 0x80;
|
static const uint8_t AHT10_STATUS_BUSY = 0x80;
|
||||||
|
|
||||||
void AHT10Component::setup() {
|
void AHT10Component::setup() {
|
||||||
const uint8_t *calibrate_cmd;
|
if (this->write(AHT10_SOFTRESET_CMD, sizeof(AHT10_SOFTRESET_CMD)) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGE(TAG, "Reset AHT10 failed!");
|
||||||
|
}
|
||||||
|
delay(AHT10_SOFTRESET_DELAY);
|
||||||
|
|
||||||
|
i2c::ErrorCode error_code = i2c::ERROR_INVALID_ARGUMENT;
|
||||||
switch (this->variant_) {
|
switch (this->variant_) {
|
||||||
case AHT10Variant::AHT20:
|
case AHT10Variant::AHT20:
|
||||||
calibrate_cmd = AHT20_CALIBRATE_CMD;
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up AHT20");
|
ESP_LOGCONFIG(TAG, "Setting up AHT20");
|
||||||
|
error_code = this->write(AHT20_INITIALIZE_CMD, sizeof(AHT20_INITIALIZE_CMD));
|
||||||
break;
|
break;
|
||||||
case AHT10Variant::AHT10:
|
case AHT10Variant::AHT10:
|
||||||
default:
|
|
||||||
calibrate_cmd = AHT10_CALIBRATE_CMD;
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up AHT10");
|
ESP_LOGCONFIG(TAG, "Setting up AHT10");
|
||||||
|
error_code = this->write(AHT10_INITIALIZE_CMD, sizeof(AHT10_INITIALIZE_CMD));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if (error_code != i2c::ERROR_OK) {
|
||||||
if (this->write(calibrate_cmd, SIZE_CALIBRATE_CMD) != i2c::ERROR_OK) {
|
|
||||||
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
|
@ -59,89 +66,92 @@ void AHT10Component::setup() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
++cal_attempts;
|
++cal_attempts;
|
||||||
if (cal_attempts > AHT10_CAL_ATTEMPTS) {
|
if (cal_attempts > AHT10_INIT_ATTEMPTS) {
|
||||||
ESP_LOGE(TAG, "AHT10 calibration timed out!");
|
ESP_LOGE(TAG, "AHT10 initialization timed out!");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((data & 0x68) != 0x08) { // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED
|
if ((data & 0x68) != 0x08) { // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED
|
||||||
ESP_LOGE(TAG, "AHT10 calibration failed!");
|
ESP_LOGE(TAG, "AHT10 initialization failed!");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGV(TAG, "AHT10 calibrated");
|
ESP_LOGV(TAG, "AHT10 initialization");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AHT10Component::update() {
|
void AHT10Component::restart_read_() {
|
||||||
if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
|
if (this->read_count_ == AHT10_ATTEMPTS) {
|
||||||
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
this->read_count_ = 0;
|
||||||
this->status_set_warning();
|
this->status_set_error("Measurements reading timed-out!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this->read_count_++;
|
||||||
|
this->set_timeout(AHT10_READ_DELAY, [this]() { this->read_data_(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void AHT10Component::read_data_() {
|
||||||
uint8_t data[6];
|
uint8_t data[6];
|
||||||
uint8_t delay_ms = AHT10_DEFAULT_DELAY;
|
if (this->read_count_ > 1)
|
||||||
if (this->humidity_sensor_ != nullptr)
|
ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_));
|
||||||
delay_ms = AHT10_HUMIDITY_DELAY;
|
if (this->read(data, 6) != i2c::ERROR_OK) {
|
||||||
bool success = false;
|
this->status_set_warning("AHT10 read failed, retrying soon");
|
||||||
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
|
this->restart_read_();
|
||||||
ESP_LOGVV(TAG, "Attempt %d at %6" PRIu32, i, millis());
|
|
||||||
delay(delay_ms);
|
|
||||||
if (this->read(data, 6) != i2c::ERROR_OK) {
|
|
||||||
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
|
||||||
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
|
|
||||||
} else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
|
|
||||||
// Unrealistic humidity (0x0)
|
|
||||||
if (this->humidity_sensor_ == nullptr) {
|
|
||||||
ESP_LOGVV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required");
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying...");
|
|
||||||
if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
|
|
||||||
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
|
||||||
this->status_set_warning();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// data is valid, we can break the loop
|
|
||||||
ESP_LOGVV(TAG, "Answer at %6" PRIu32, millis());
|
|
||||||
success = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!success || (data[0] & 0x80) == 0x80) {
|
|
||||||
ESP_LOGE(TAG, "Measurements reading timed-out!");
|
|
||||||
this->status_set_warning();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
||||||
|
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
|
||||||
|
this->restart_read_();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
|
||||||
|
// Unrealistic humidity (0x0)
|
||||||
|
if (this->humidity_sensor_ == nullptr) {
|
||||||
|
ESP_LOGV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required");
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying...");
|
||||||
|
if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
|
||||||
|
this->status_set_warning("Communication with AHT10 failed!");
|
||||||
|
}
|
||||||
|
this->restart_read_();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->read_count_ > 1)
|
||||||
|
ESP_LOGD(TAG, "Success at %ums", (unsigned) (millis() - this->start_time_));
|
||||||
uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
|
uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
|
||||||
uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
|
uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
|
||||||
|
|
||||||
float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f;
|
|
||||||
float humidity;
|
|
||||||
if (raw_humidity == 0) { // unrealistic value
|
|
||||||
humidity = NAN;
|
|
||||||
} else {
|
|
||||||
humidity = (float) raw_humidity * 100.0f / 1048576.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->temperature_sensor_ != nullptr) {
|
if (this->temperature_sensor_ != nullptr) {
|
||||||
|
float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f;
|
||||||
this->temperature_sensor_->publish_state(temperature);
|
this->temperature_sensor_->publish_state(temperature);
|
||||||
}
|
}
|
||||||
if (this->humidity_sensor_ != nullptr) {
|
if (this->humidity_sensor_ != nullptr) {
|
||||||
|
float humidity;
|
||||||
|
if (raw_humidity == 0) { // unrealistic value
|
||||||
|
humidity = NAN;
|
||||||
|
} else {
|
||||||
|
humidity = (float) raw_humidity * 100.0f / 1048576.0f;
|
||||||
|
}
|
||||||
if (std::isnan(humidity)) {
|
if (std::isnan(humidity)) {
|
||||||
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
|
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
|
||||||
}
|
}
|
||||||
this->humidity_sensor_->publish_state(humidity);
|
this->humidity_sensor_->publish_state(humidity);
|
||||||
}
|
}
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
|
this->read_count_ = 0;
|
||||||
|
}
|
||||||
|
void AHT10Component::update() {
|
||||||
|
if (this->read_count_ != 0)
|
||||||
|
return;
|
||||||
|
this->start_time_ = millis();
|
||||||
|
if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
|
||||||
|
this->status_set_warning("Communication with AHT10 failed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->restart_read_();
|
||||||
}
|
}
|
||||||
|
|
||||||
float AHT10Component::get_setup_priority() const { return setup_priority::DATA; }
|
float AHT10Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
|
@ -26,6 +26,10 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
sensor::Sensor *temperature_sensor_{nullptr};
|
sensor::Sensor *temperature_sensor_{nullptr};
|
||||||
sensor::Sensor *humidity_sensor_{nullptr};
|
sensor::Sensor *humidity_sensor_{nullptr};
|
||||||
AHT10Variant variant_{};
|
AHT10Variant variant_{};
|
||||||
|
unsigned read_count_{};
|
||||||
|
void read_data_();
|
||||||
|
void restart_read_();
|
||||||
|
uint32_t start_time_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace aht10
|
} // namespace aht10
|
||||||
|
|
1
esphome/components/am2315c/__init__.py
Normal file
1
esphome/components/am2315c/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@swoboda1337"]
|
200
esphome/components/am2315c/am2315c.cpp
Normal file
200
esphome/components/am2315c/am2315c.cpp
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2023-2024 Rob Tillaart
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
#include "am2315c.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace am2315c {
|
||||||
|
|
||||||
|
static const char *const TAG = "am2315c";
|
||||||
|
|
||||||
|
uint8_t AM2315C::crc8_(uint8_t *data, uint8_t len) {
|
||||||
|
uint8_t crc = 0xFF;
|
||||||
|
while (len--) {
|
||||||
|
crc ^= *data++;
|
||||||
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
|
if (crc & 0x80) {
|
||||||
|
crc <<= 1;
|
||||||
|
crc ^= 0x31;
|
||||||
|
} else {
|
||||||
|
crc <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AM2315C::reset_register_(uint8_t reg) {
|
||||||
|
// code based on demo code sent by www.aosong.com
|
||||||
|
// no further documentation.
|
||||||
|
// 0x1B returned 18, 0, 4
|
||||||
|
// 0x1C returned 18, 65, 0
|
||||||
|
// 0x1E returned 18, 8, 0
|
||||||
|
// 18 seems to be status register
|
||||||
|
// other values unknown.
|
||||||
|
uint8_t data[3];
|
||||||
|
data[0] = reg;
|
||||||
|
data[1] = 0;
|
||||||
|
data[2] = 0;
|
||||||
|
ESP_LOGD(TAG, "Reset register: 0x%02x", reg);
|
||||||
|
if (this->write(data, 3) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGE(TAG, "Write failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
delay(5);
|
||||||
|
if (this->read(data, 3) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGE(TAG, "Read failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
delay(10);
|
||||||
|
data[0] = 0xB0 | reg;
|
||||||
|
if (this->write(data, 3) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGE(TAG, "Write failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
delay(5);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) {
|
||||||
|
uint32_t raw;
|
||||||
|
raw = (data[1] << 12) | (data[2] << 4) | (data[3] >> 4);
|
||||||
|
humidity = raw * 9.5367431640625e-5;
|
||||||
|
raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
|
||||||
|
temperature = raw * 1.9073486328125e-4 - 50;
|
||||||
|
return this->crc8_(data, 6) == data[6];
|
||||||
|
}
|
||||||
|
|
||||||
|
void AM2315C::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up AM2315C...");
|
||||||
|
|
||||||
|
// get status
|
||||||
|
uint8_t status = 0;
|
||||||
|
if (this->read(&status, 1) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGE(TAG, "Read failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset registers if required, according to the datasheet
|
||||||
|
// this can be required after power on, although this was
|
||||||
|
// never required during testing
|
||||||
|
if ((status & 0x18) != 0x18) {
|
||||||
|
ESP_LOGD(TAG, "Resetting AM2315C registers");
|
||||||
|
if (!this->reset_register_(0x1B)) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->reset_register_(0x1C)) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->reset_register_(0x1E)) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AM2315C::update() {
|
||||||
|
// request measurement
|
||||||
|
uint8_t data[3];
|
||||||
|
data[0] = 0xAC;
|
||||||
|
data[1] = 0x33;
|
||||||
|
data[2] = 0x00;
|
||||||
|
if (this->write(data, 3) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGE(TAG, "Write failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for hw to complete measurement
|
||||||
|
set_timeout(160, [this]() {
|
||||||
|
// check status
|
||||||
|
uint8_t status = 0;
|
||||||
|
if (this->read(&status, 1) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGE(TAG, "Read failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((status & 0x80) == 0x80) {
|
||||||
|
ESP_LOGE(TAG, "HW still busy!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read
|
||||||
|
uint8_t data[7];
|
||||||
|
if (this->read(data, 7) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGE(TAG, "Read failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for all zeros
|
||||||
|
bool zeros = true;
|
||||||
|
for (uint8_t i : data) {
|
||||||
|
zeros = zeros && (i == 0);
|
||||||
|
}
|
||||||
|
if (zeros) {
|
||||||
|
ESP_LOGW(TAG, "Data all zeros!");
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert
|
||||||
|
float temperature = 0.0;
|
||||||
|
float humidity = 0.0;
|
||||||
|
if (this->convert_(data, humidity, temperature)) {
|
||||||
|
if (this->temperature_sensor_ != nullptr) {
|
||||||
|
this->temperature_sensor_->publish_state(temperature);
|
||||||
|
}
|
||||||
|
if (this->humidity_sensor_ != nullptr) {
|
||||||
|
this->humidity_sensor_->publish_state(humidity);
|
||||||
|
}
|
||||||
|
this->status_clear_warning();
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "CRC failed!");
|
||||||
|
this->status_set_warning();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AM2315C::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "AM2315C:");
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, "Communication with AM2315C failed!");
|
||||||
|
}
|
||||||
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
float AM2315C::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
} // namespace am2315c
|
||||||
|
} // namespace esphome
|
51
esphome/components/am2315c/am2315c.h
Normal file
51
esphome/components/am2315c/am2315c.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2023-2024 Rob Tillaart
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace am2315c {
|
||||||
|
|
||||||
|
class AM2315C : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
void dump_config() override;
|
||||||
|
void update() override;
|
||||||
|
void setup() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
|
||||||
|
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t crc8_(uint8_t *data, uint8_t len);
|
||||||
|
bool convert_(uint8_t *data, float &humidity, float &temperature);
|
||||||
|
bool reset_register_(uint8_t reg);
|
||||||
|
|
||||||
|
sensor::Sensor *temperature_sensor_{nullptr};
|
||||||
|
sensor::Sensor *humidity_sensor_{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace am2315c
|
||||||
|
} // namespace esphome
|
54
esphome/components/am2315c/sensor.py
Normal file
54
esphome/components/am2315c/sensor.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_HUMIDITY,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_HUMIDITY,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
)
|
||||||
|
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
|
am2315c_ns = cg.esphome_ns.namespace("am2315c")
|
||||||
|
AM2315C = am2315c_ns.class_("AM2315C", cg.PollingComponent, i2c.I2CDevice)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(AM2315C),
|
||||||
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_HUMIDITY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x38))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
if temperature_config := config.get(CONF_TEMPERATURE):
|
||||||
|
sens = await sensor.new_sensor(temperature_config)
|
||||||
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
|
|
||||||
|
if humidity_config := config.get(CONF_HUMIDITY):
|
||||||
|
sens = await sensor.new_sensor(humidity_config)
|
||||||
|
cg.add(var.set_humidity_sensor(sens))
|
|
@ -44,6 +44,8 @@ service APIConnection {
|
||||||
rpc button_command (ButtonCommandRequest) returns (void) {}
|
rpc button_command (ButtonCommandRequest) returns (void) {}
|
||||||
rpc lock_command (LockCommandRequest) returns (void) {}
|
rpc lock_command (LockCommandRequest) returns (void) {}
|
||||||
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
|
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
|
||||||
|
rpc date_command (DateCommandRequest) returns (void) {}
|
||||||
|
rpc time_command (TimeCommandRequest) returns (void) {}
|
||||||
|
|
||||||
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
||||||
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
|
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
|
||||||
|
@ -216,7 +218,8 @@ message DeviceInfoResponse {
|
||||||
|
|
||||||
string friendly_name = 13;
|
string friendly_name = 13;
|
||||||
|
|
||||||
uint32 voice_assistant_version = 14;
|
uint32 legacy_voice_assistant_version = 14;
|
||||||
|
uint32 voice_assistant_feature_flags = 17;
|
||||||
|
|
||||||
string suggested_area = 16;
|
string suggested_area = 16;
|
||||||
}
|
}
|
||||||
|
@ -600,6 +603,7 @@ message ListEntitiesTextSensorResponse {
|
||||||
string icon = 5;
|
string icon = 5;
|
||||||
bool disabled_by_default = 6;
|
bool disabled_by_default = 6;
|
||||||
EntityCategory entity_category = 7;
|
EntityCategory entity_category = 7;
|
||||||
|
string device_class = 8;
|
||||||
}
|
}
|
||||||
message TextSensorStateResponse {
|
message TextSensorStateResponse {
|
||||||
option (id) = 27;
|
option (id) = 27;
|
||||||
|
@ -1420,12 +1424,18 @@ message BluetoothDeviceClearCacheResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== PUSH TO TALK ====================
|
// ==================== PUSH TO TALK ====================
|
||||||
|
enum VoiceAssistantSubscribeFlag {
|
||||||
|
VOICE_ASSISTANT_SUBSCRIBE_NONE = 0;
|
||||||
|
VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message SubscribeVoiceAssistantRequest {
|
message SubscribeVoiceAssistantRequest {
|
||||||
option (id) = 89;
|
option (id) = 89;
|
||||||
option (source) = SOURCE_CLIENT;
|
option (source) = SOURCE_CLIENT;
|
||||||
option (ifdef) = "USE_VOICE_ASSISTANT";
|
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||||
|
|
||||||
bool subscribe = 1;
|
bool subscribe = 1;
|
||||||
|
uint32 flags = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum VoiceAssistantRequestFlag {
|
enum VoiceAssistantRequestFlag {
|
||||||
|
@ -1449,6 +1459,7 @@ message VoiceAssistantRequest {
|
||||||
string conversation_id = 2;
|
string conversation_id = 2;
|
||||||
uint32 flags = 3;
|
uint32 flags = 3;
|
||||||
VoiceAssistantAudioSettings audio_settings = 4;
|
VoiceAssistantAudioSettings audio_settings = 4;
|
||||||
|
string wake_word_phrase = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message VoiceAssistantResponse {
|
message VoiceAssistantResponse {
|
||||||
|
@ -1492,6 +1503,16 @@ message VoiceAssistantEventResponse {
|
||||||
repeated VoiceAssistantEventData data = 2;
|
repeated VoiceAssistantEventData data = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message VoiceAssistantAudio {
|
||||||
|
option (id) = 106;
|
||||||
|
option (source) = SOURCE_BOTH;
|
||||||
|
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||||
|
|
||||||
|
bytes data = 1;
|
||||||
|
bool end = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ==================== ALARM CONTROL PANEL ====================
|
// ==================== ALARM CONTROL PANEL ====================
|
||||||
enum AlarmControlPanelState {
|
enum AlarmControlPanelState {
|
||||||
ALARM_STATE_DISARMED = 0;
|
ALARM_STATE_DISARMED = 0;
|
||||||
|
@ -1596,3 +1617,86 @@ message TextCommandRequest {
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
string state = 2;
|
string state = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== DATETIME DATE ====================
|
||||||
|
message ListEntitiesDateResponse {
|
||||||
|
option (id) = 100;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_DATETIME_DATE";
|
||||||
|
|
||||||
|
string object_id = 1;
|
||||||
|
fixed32 key = 2;
|
||||||
|
string name = 3;
|
||||||
|
string unique_id = 4;
|
||||||
|
|
||||||
|
string icon = 5;
|
||||||
|
bool disabled_by_default = 6;
|
||||||
|
EntityCategory entity_category = 7;
|
||||||
|
}
|
||||||
|
message DateStateResponse {
|
||||||
|
option (id) = 101;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_DATETIME_DATE";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
// If the date does not have a valid state yet.
|
||||||
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
|
bool missing_state = 2;
|
||||||
|
uint32 year = 3;
|
||||||
|
uint32 month = 4;
|
||||||
|
uint32 day = 5;
|
||||||
|
}
|
||||||
|
message DateCommandRequest {
|
||||||
|
option (id) = 102;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_DATETIME_DATE";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
uint32 year = 2;
|
||||||
|
uint32 month = 3;
|
||||||
|
uint32 day = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== DATETIME TIME ====================
|
||||||
|
message ListEntitiesTimeResponse {
|
||||||
|
option (id) = 103;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_DATETIME_TIME";
|
||||||
|
|
||||||
|
string object_id = 1;
|
||||||
|
fixed32 key = 2;
|
||||||
|
string name = 3;
|
||||||
|
string unique_id = 4;
|
||||||
|
|
||||||
|
string icon = 5;
|
||||||
|
bool disabled_by_default = 6;
|
||||||
|
EntityCategory entity_category = 7;
|
||||||
|
}
|
||||||
|
message TimeStateResponse {
|
||||||
|
option (id) = 104;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_DATETIME_TIME";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
// If the time does not have a valid state yet.
|
||||||
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
|
bool missing_state = 2;
|
||||||
|
uint32 hour = 3;
|
||||||
|
uint32 minute = 4;
|
||||||
|
uint32 second = 5;
|
||||||
|
}
|
||||||
|
message TimeCommandRequest {
|
||||||
|
option (id) = 105;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_DATETIME_TIME";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
uint32 hour = 2;
|
||||||
|
uint32 minute = 3;
|
||||||
|
uint32 second = 4;
|
||||||
|
}
|
||||||
|
|
|
@ -543,6 +543,7 @@ bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor)
|
||||||
msg.icon = text_sensor->get_icon();
|
msg.icon = text_sensor->get_icon();
|
||||||
msg.disabled_by_default = text_sensor->is_disabled_by_default();
|
msg.disabled_by_default = text_sensor->is_disabled_by_default();
|
||||||
msg.entity_category = static_cast<enums::EntityCategory>(text_sensor->get_entity_category());
|
msg.entity_category = static_cast<enums::EntityCategory>(text_sensor->get_entity_category());
|
||||||
|
msg.device_class = text_sensor->get_device_class();
|
||||||
return this->send_list_entities_text_sensor_response(msg);
|
return this->send_list_entities_text_sensor_response(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -697,6 +698,80 @@ void APIConnection::number_command(const NumberCommandRequest &msg) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
bool APIConnection::send_date_state(datetime::DateEntity *date) {
|
||||||
|
if (!this->state_subscription_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DateStateResponse resp{};
|
||||||
|
resp.key = date->get_object_id_hash();
|
||||||
|
resp.missing_state = !date->has_state();
|
||||||
|
resp.year = date->year;
|
||||||
|
resp.month = date->month;
|
||||||
|
resp.day = date->day;
|
||||||
|
return this->send_date_state_response(resp);
|
||||||
|
}
|
||||||
|
bool APIConnection::send_date_info(datetime::DateEntity *date) {
|
||||||
|
ListEntitiesDateResponse msg;
|
||||||
|
msg.key = date->get_object_id_hash();
|
||||||
|
msg.object_id = date->get_object_id();
|
||||||
|
if (date->has_own_name())
|
||||||
|
msg.name = date->get_name();
|
||||||
|
msg.unique_id = get_default_unique_id("date", date);
|
||||||
|
msg.icon = date->get_icon();
|
||||||
|
msg.disabled_by_default = date->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(date->get_entity_category());
|
||||||
|
|
||||||
|
return this->send_list_entities_date_response(msg);
|
||||||
|
}
|
||||||
|
void APIConnection::date_command(const DateCommandRequest &msg) {
|
||||||
|
datetime::DateEntity *date = App.get_date_by_key(msg.key);
|
||||||
|
if (date == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto call = date->make_call();
|
||||||
|
call.set_date(msg.year, msg.month, msg.day);
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool APIConnection::send_time_state(datetime::TimeEntity *time) {
|
||||||
|
if (!this->state_subscription_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TimeStateResponse resp{};
|
||||||
|
resp.key = time->get_object_id_hash();
|
||||||
|
resp.missing_state = !time->has_state();
|
||||||
|
resp.hour = time->hour;
|
||||||
|
resp.minute = time->minute;
|
||||||
|
resp.second = time->second;
|
||||||
|
return this->send_time_state_response(resp);
|
||||||
|
}
|
||||||
|
bool APIConnection::send_time_info(datetime::TimeEntity *time) {
|
||||||
|
ListEntitiesTimeResponse msg;
|
||||||
|
msg.key = time->get_object_id_hash();
|
||||||
|
msg.object_id = time->get_object_id();
|
||||||
|
if (time->has_own_name())
|
||||||
|
msg.name = time->get_name();
|
||||||
|
msg.unique_id = get_default_unique_id("time", time);
|
||||||
|
msg.icon = time->get_icon();
|
||||||
|
msg.disabled_by_default = time->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(time->get_entity_category());
|
||||||
|
|
||||||
|
return this->send_list_entities_time_response(msg);
|
||||||
|
}
|
||||||
|
void APIConnection::time_command(const TimeCommandRequest &msg) {
|
||||||
|
datetime::TimeEntity *time = App.get_time_by_key(msg.key);
|
||||||
|
if (time == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto call = time->make_call();
|
||||||
|
call.set_time(msg.hour, msg.minute, msg.second);
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool APIConnection::send_text_state(text::Text *text, std::string state) {
|
bool APIConnection::send_text_state(text::Text *text, std::string state) {
|
||||||
if (!this->state_subscription_)
|
if (!this->state_subscription_)
|
||||||
|
@ -1002,10 +1077,15 @@ void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &ms
|
||||||
voice_assistant::global_voice_assistant->failed_to_start();
|
voice_assistant::global_voice_assistant->failed_to_start();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
struct sockaddr_storage storage;
|
if (msg.port == 0) {
|
||||||
socklen_t len = sizeof(storage);
|
// Use API Audio
|
||||||
this->helper_->getpeername((struct sockaddr *) &storage, &len);
|
voice_assistant::global_voice_assistant->start_streaming();
|
||||||
voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port);
|
} else {
|
||||||
|
struct sockaddr_storage storage;
|
||||||
|
socklen_t len = sizeof(storage);
|
||||||
|
this->helper_->getpeername((struct sockaddr *) &storage, &len);
|
||||||
|
voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
|
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
|
||||||
|
@ -1017,6 +1097,15 @@ void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventR
|
||||||
voice_assistant::global_voice_assistant->on_event(msg);
|
voice_assistant::global_voice_assistant->on_event(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void APIConnection::on_voice_assistant_audio(const VoiceAssistantAudio &msg) {
|
||||||
|
if (voice_assistant::global_voice_assistant != nullptr) {
|
||||||
|
if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice_assistant::global_voice_assistant->on_audio(msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1104,7 +1193,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
||||||
|
|
||||||
HelloResponse resp;
|
HelloResponse resp;
|
||||||
resp.api_version_major = 1;
|
resp.api_version_major = 1;
|
||||||
resp.api_version_minor = 9;
|
resp.api_version_minor = 10;
|
||||||
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
||||||
resp.name = App.get_name();
|
resp.name = App.get_name();
|
||||||
|
|
||||||
|
@ -1165,7 +1254,8 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
||||||
resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
|
resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
resp.voice_assistant_version = voice_assistant::global_voice_assistant->get_version();
|
resp.legacy_voice_assistant_version = voice_assistant::global_voice_assistant->get_legacy_version();
|
||||||
|
resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
|
||||||
#endif
|
#endif
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,16 @@ class APIConnection : public APIServerConnection {
|
||||||
bool send_number_info(number::Number *number);
|
bool send_number_info(number::Number *number);
|
||||||
void number_command(const NumberCommandRequest &msg) override;
|
void number_command(const NumberCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
bool send_date_state(datetime::DateEntity *date);
|
||||||
|
bool send_date_info(datetime::DateEntity *date);
|
||||||
|
void date_command(const DateCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool send_time_state(datetime::TimeEntity *time);
|
||||||
|
bool send_time_info(datetime::TimeEntity *time);
|
||||||
|
void time_command(const TimeCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool send_text_state(text::Text *text, std::string state);
|
bool send_text_state(text::Text *text, std::string state);
|
||||||
bool send_text_info(text::Text *text);
|
bool send_text_info(text::Text *text);
|
||||||
|
@ -129,6 +139,7 @@ class APIConnection : public APIServerConnection {
|
||||||
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override;
|
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override;
|
||||||
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
||||||
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
||||||
|
void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
|
|
@ -410,6 +410,19 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
template<>
|
||||||
|
const char *proto_enum_to_string<enums::VoiceAssistantSubscribeFlag>(enums::VoiceAssistantSubscribeFlag value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::VOICE_ASSISTANT_SUBSCRIBE_NONE:
|
||||||
|
return "VOICE_ASSISTANT_SUBSCRIBE_NONE";
|
||||||
|
case enums::VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO:
|
||||||
|
return "VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
template<> const char *proto_enum_to_string<enums::VoiceAssistantRequestFlag>(enums::VoiceAssistantRequestFlag value) {
|
template<> const char *proto_enum_to_string<enums::VoiceAssistantRequestFlag>(enums::VoiceAssistantRequestFlag value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case enums::VOICE_ASSISTANT_REQUEST_NONE:
|
case enums::VOICE_ASSISTANT_REQUEST_NONE:
|
||||||
|
@ -716,7 +729,11 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 14: {
|
case 14: {
|
||||||
this->voice_assistant_version = value.as_uint32();
|
this->legacy_voice_assistant_version = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 17: {
|
||||||
|
this->voice_assistant_feature_flags = value.as_uint32();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -784,7 +801,8 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_uint32(15, this->bluetooth_proxy_feature_flags);
|
buffer.encode_uint32(15, this->bluetooth_proxy_feature_flags);
|
||||||
buffer.encode_string(12, this->manufacturer);
|
buffer.encode_string(12, this->manufacturer);
|
||||||
buffer.encode_string(13, this->friendly_name);
|
buffer.encode_string(13, this->friendly_name);
|
||||||
buffer.encode_uint32(14, this->voice_assistant_version);
|
buffer.encode_uint32(14, this->legacy_voice_assistant_version);
|
||||||
|
buffer.encode_uint32(17, this->voice_assistant_feature_flags);
|
||||||
buffer.encode_string(16, this->suggested_area);
|
buffer.encode_string(16, this->suggested_area);
|
||||||
}
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
@ -850,8 +868,13 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
|
||||||
out.append("'").append(this->friendly_name).append("'");
|
out.append("'").append(this->friendly_name).append("'");
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" voice_assistant_version: ");
|
out.append(" legacy_voice_assistant_version: ");
|
||||||
sprintf(buffer, "%" PRIu32, this->voice_assistant_version);
|
sprintf(buffer, "%" PRIu32, this->legacy_voice_assistant_version);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" voice_assistant_feature_flags: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->voice_assistant_feature_flags);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2721,6 +2744,10 @@ bool ListEntitiesTextSensorResponse::decode_length(uint32_t field_id, ProtoLengt
|
||||||
this->icon = value.as_string();
|
this->icon = value.as_string();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 8: {
|
||||||
|
this->device_class = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2743,6 +2770,7 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_string(5, this->icon);
|
buffer.encode_string(5, this->icon);
|
||||||
buffer.encode_bool(6, this->disabled_by_default);
|
buffer.encode_bool(6, this->disabled_by_default);
|
||||||
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
|
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
|
||||||
|
buffer.encode_string(8, this->device_class);
|
||||||
}
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
|
void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
|
||||||
|
@ -2776,6 +2804,10 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
|
||||||
out.append(" entity_category: ");
|
out.append(" entity_category: ");
|
||||||
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" device_class: ");
|
||||||
|
out.append("'").append(this->device_class).append("'");
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -6505,11 +6537,18 @@ bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarIn
|
||||||
this->subscribe = value.as_bool();
|
this->subscribe = value.as_bool();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 2: {
|
||||||
|
this->flags = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->subscribe); }
|
void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_bool(1, this->subscribe);
|
||||||
|
buffer.encode_uint32(2, this->flags);
|
||||||
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
|
void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
|
@ -6517,6 +6556,11 @@ void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
|
||||||
out.append(" subscribe: ");
|
out.append(" subscribe: ");
|
||||||
out.append(YESNO(this->subscribe));
|
out.append(YESNO(this->subscribe));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" flags: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->flags);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -6594,6 +6638,10 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite
|
||||||
this->audio_settings = value.as_message<VoiceAssistantAudioSettings>();
|
this->audio_settings = value.as_message<VoiceAssistantAudioSettings>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 5: {
|
||||||
|
this->wake_word_phrase = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -6603,6 +6651,7 @@ void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_string(2, this->conversation_id);
|
buffer.encode_string(2, this->conversation_id);
|
||||||
buffer.encode_uint32(3, this->flags);
|
buffer.encode_uint32(3, this->flags);
|
||||||
buffer.encode_message<VoiceAssistantAudioSettings>(4, this->audio_settings);
|
buffer.encode_message<VoiceAssistantAudioSettings>(4, this->audio_settings);
|
||||||
|
buffer.encode_string(5, this->wake_word_phrase);
|
||||||
}
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void VoiceAssistantRequest::dump_to(std::string &out) const {
|
void VoiceAssistantRequest::dump_to(std::string &out) const {
|
||||||
|
@ -6624,6 +6673,10 @@ void VoiceAssistantRequest::dump_to(std::string &out) const {
|
||||||
out.append(" audio_settings: ");
|
out.append(" audio_settings: ");
|
||||||
this->audio_settings.dump_to(out);
|
this->audio_settings.dump_to(out);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" wake_word_phrase: ");
|
||||||
|
out.append("'").append(this->wake_word_phrase).append("'");
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -6734,6 +6787,44 @@ void VoiceAssistantEventResponse::dump_to(std::string &out) const {
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
bool VoiceAssistantAudio::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->end = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool VoiceAssistantAudio::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->data = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void VoiceAssistantAudio::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_string(1, this->data);
|
||||||
|
buffer.encode_bool(2, this->end);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void VoiceAssistantAudio::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("VoiceAssistantAudio {\n");
|
||||||
|
out.append(" data: ");
|
||||||
|
out.append("'").append(this->data).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" end: ");
|
||||||
|
out.append(YESNO(this->end));
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 6: {
|
case 6: {
|
||||||
|
@ -7166,6 +7257,444 @@ void TextCommandRequest::dump_to(std::string &out) const {
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
bool ListEntitiesDateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 6: {
|
||||||
|
this->disabled_by_default = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 7: {
|
||||||
|
this->entity_category = value.as_enum<enums::EntityCategory>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ListEntitiesDateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->object_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->name = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->unique_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
this->icon = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ListEntitiesDateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ListEntitiesDateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_string(1, this->object_id);
|
||||||
|
buffer.encode_fixed32(2, this->key);
|
||||||
|
buffer.encode_string(3, this->name);
|
||||||
|
buffer.encode_string(4, this->unique_id);
|
||||||
|
buffer.encode_string(5, this->icon);
|
||||||
|
buffer.encode_bool(6, this->disabled_by_default);
|
||||||
|
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void ListEntitiesDateResponse::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("ListEntitiesDateResponse {\n");
|
||||||
|
out.append(" object_id: ");
|
||||||
|
out.append("'").append(this->object_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" name: ");
|
||||||
|
out.append("'").append(this->name).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" unique_id: ");
|
||||||
|
out.append("'").append(this->unique_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" icon: ");
|
||||||
|
out.append("'").append(this->icon).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" disabled_by_default: ");
|
||||||
|
out.append(YESNO(this->disabled_by_default));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" entity_category: ");
|
||||||
|
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bool DateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->missing_state = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->year = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->month = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
this->day = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool DateStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void DateStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_fixed32(1, this->key);
|
||||||
|
buffer.encode_bool(2, this->missing_state);
|
||||||
|
buffer.encode_uint32(3, this->year);
|
||||||
|
buffer.encode_uint32(4, this->month);
|
||||||
|
buffer.encode_uint32(5, this->day);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void DateStateResponse::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("DateStateResponse {\n");
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" missing_state: ");
|
||||||
|
out.append(YESNO(this->missing_state));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" year: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->year);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" month: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->month);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" day: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->day);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bool DateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->year = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->month = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->day = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool DateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void DateCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_fixed32(1, this->key);
|
||||||
|
buffer.encode_uint32(2, this->year);
|
||||||
|
buffer.encode_uint32(3, this->month);
|
||||||
|
buffer.encode_uint32(4, this->day);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void DateCommandRequest::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("DateCommandRequest {\n");
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" year: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->year);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" month: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->month);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" day: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->day);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bool ListEntitiesTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 6: {
|
||||||
|
this->disabled_by_default = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 7: {
|
||||||
|
this->entity_category = value.as_enum<enums::EntityCategory>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ListEntitiesTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->object_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->name = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->unique_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
this->icon = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ListEntitiesTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ListEntitiesTimeResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_string(1, this->object_id);
|
||||||
|
buffer.encode_fixed32(2, this->key);
|
||||||
|
buffer.encode_string(3, this->name);
|
||||||
|
buffer.encode_string(4, this->unique_id);
|
||||||
|
buffer.encode_string(5, this->icon);
|
||||||
|
buffer.encode_bool(6, this->disabled_by_default);
|
||||||
|
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void ListEntitiesTimeResponse::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("ListEntitiesTimeResponse {\n");
|
||||||
|
out.append(" object_id: ");
|
||||||
|
out.append("'").append(this->object_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" name: ");
|
||||||
|
out.append("'").append(this->name).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" unique_id: ");
|
||||||
|
out.append("'").append(this->unique_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" icon: ");
|
||||||
|
out.append("'").append(this->icon).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" disabled_by_default: ");
|
||||||
|
out.append(YESNO(this->disabled_by_default));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" entity_category: ");
|
||||||
|
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bool TimeStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->missing_state = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->hour = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->minute = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
this->second = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool TimeStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TimeStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_fixed32(1, this->key);
|
||||||
|
buffer.encode_bool(2, this->missing_state);
|
||||||
|
buffer.encode_uint32(3, this->hour);
|
||||||
|
buffer.encode_uint32(4, this->minute);
|
||||||
|
buffer.encode_uint32(5, this->second);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void TimeStateResponse::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("TimeStateResponse {\n");
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" missing_state: ");
|
||||||
|
out.append(YESNO(this->missing_state));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" hour: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->hour);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" minute: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->minute);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" second: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->second);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bool TimeCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->hour = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->minute = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->second = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TimeCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_fixed32(1, this->key);
|
||||||
|
buffer.encode_uint32(2, this->hour);
|
||||||
|
buffer.encode_uint32(3, this->minute);
|
||||||
|
buffer.encode_uint32(4, this->second);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void TimeCommandRequest::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("TimeCommandRequest {\n");
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" hour: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->hour);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" minute: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->minute);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" second: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->second);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -165,6 +165,10 @@ enum BluetoothDeviceRequestType : uint32_t {
|
||||||
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
|
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
|
||||||
BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
|
BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
|
||||||
};
|
};
|
||||||
|
enum VoiceAssistantSubscribeFlag : uint32_t {
|
||||||
|
VOICE_ASSISTANT_SUBSCRIBE_NONE = 0,
|
||||||
|
VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO = 1,
|
||||||
|
};
|
||||||
enum VoiceAssistantRequestFlag : uint32_t {
|
enum VoiceAssistantRequestFlag : uint32_t {
|
||||||
VOICE_ASSISTANT_REQUEST_NONE = 0,
|
VOICE_ASSISTANT_REQUEST_NONE = 0,
|
||||||
VOICE_ASSISTANT_REQUEST_USE_VAD = 1,
|
VOICE_ASSISTANT_REQUEST_USE_VAD = 1,
|
||||||
|
@ -327,7 +331,8 @@ class DeviceInfoResponse : public ProtoMessage {
|
||||||
uint32_t bluetooth_proxy_feature_flags{0};
|
uint32_t bluetooth_proxy_feature_flags{0};
|
||||||
std::string manufacturer{};
|
std::string manufacturer{};
|
||||||
std::string friendly_name{};
|
std::string friendly_name{};
|
||||||
uint32_t voice_assistant_version{0};
|
uint32_t legacy_voice_assistant_version{0};
|
||||||
|
uint32_t voice_assistant_feature_flags{0};
|
||||||
std::string suggested_area{};
|
std::string suggested_area{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
@ -713,6 +718,7 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
|
||||||
std::string icon{};
|
std::string icon{};
|
||||||
bool disabled_by_default{false};
|
bool disabled_by_default{false};
|
||||||
enums::EntityCategory entity_category{};
|
enums::EntityCategory entity_category{};
|
||||||
|
std::string device_class{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
@ -1673,6 +1679,7 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage {
|
||||||
class SubscribeVoiceAssistantRequest : public ProtoMessage {
|
class SubscribeVoiceAssistantRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
bool subscribe{false};
|
bool subscribe{false};
|
||||||
|
uint32_t flags{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
@ -1701,6 +1708,7 @@ class VoiceAssistantRequest : public ProtoMessage {
|
||||||
std::string conversation_id{};
|
std::string conversation_id{};
|
||||||
uint32_t flags{0};
|
uint32_t flags{0};
|
||||||
VoiceAssistantAudioSettings audio_settings{};
|
VoiceAssistantAudioSettings audio_settings{};
|
||||||
|
std::string wake_word_phrase{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
@ -1747,6 +1755,19 @@ class VoiceAssistantEventResponse : public ProtoMessage {
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
|
class VoiceAssistantAudio : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
std::string data{};
|
||||||
|
bool end{false};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
|
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{};
|
std::string object_id{};
|
||||||
|
@ -1848,6 +1869,106 @@ class TextCommandRequest : public ProtoMessage {
|
||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
};
|
};
|
||||||
|
class ListEntitiesDateResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
std::string object_id{};
|
||||||
|
uint32_t key{0};
|
||||||
|
std::string name{};
|
||||||
|
std::string unique_id{};
|
||||||
|
std::string icon{};
|
||||||
|
bool disabled_by_default{false};
|
||||||
|
enums::EntityCategory entity_category{};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class DateStateResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
uint32_t key{0};
|
||||||
|
bool missing_state{false};
|
||||||
|
uint32_t year{0};
|
||||||
|
uint32_t month{0};
|
||||||
|
uint32_t day{0};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class DateCommandRequest : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
uint32_t key{0};
|
||||||
|
uint32_t year{0};
|
||||||
|
uint32_t month{0};
|
||||||
|
uint32_t day{0};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class ListEntitiesTimeResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
std::string object_id{};
|
||||||
|
uint32_t key{0};
|
||||||
|
std::string name{};
|
||||||
|
std::string unique_id{};
|
||||||
|
std::string icon{};
|
||||||
|
bool disabled_by_default{false};
|
||||||
|
enums::EntityCategory entity_category{};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class TimeStateResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
uint32_t key{0};
|
||||||
|
bool missing_state{false};
|
||||||
|
uint32_t hour{0};
|
||||||
|
uint32_t minute{0};
|
||||||
|
uint32_t second{0};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class TimeCommandRequest : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
uint32_t key{0};
|
||||||
|
uint32_t hour{0};
|
||||||
|
uint32_t minute{0};
|
||||||
|
uint32_t second{0};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -476,6 +476,14 @@ bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantR
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAudio &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_voice_assistant_audio: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<VoiceAssistantAudio>(msg, 106);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
|
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
|
||||||
const ListEntitiesAlarmControlPanelResponse &msg) {
|
const ListEntitiesAlarmControlPanelResponse &msg) {
|
||||||
|
@ -513,6 +521,42 @@ bool APIServerConnectionBase::send_text_state_response(const TextStateResponse &
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
bool APIServerConnectionBase::send_list_entities_date_response(const ListEntitiesDateResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_list_entities_date_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<ListEntitiesDateResponse>(msg, 100);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
bool APIServerConnectionBase::send_date_state_response(const DateStateResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_date_state_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<DateStateResponse>(msg, 101);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool APIServerConnectionBase::send_list_entities_time_response(const ListEntitiesTimeResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_list_entities_time_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<ListEntitiesTimeResponse>(msg, 103);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_time_state_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<TimeStateResponse>(msg, 104);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
#endif
|
||||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
||||||
switch (msg_type) {
|
switch (msg_type) {
|
||||||
case 1: {
|
case 1: {
|
||||||
|
@ -942,6 +986,39 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||||
ESP_LOGVV(TAG, "on_text_command_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_text_command_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
this->on_text_command_request(msg);
|
this->on_text_command_request(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 102: {
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
DateCommandRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "on_date_command_request: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
this->on_date_command_request(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 105: {
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
TimeCommandRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "on_time_command_request: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
this->on_time_command_request(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 106: {
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
VoiceAssistantAudio msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "on_voice_assistant_audio: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
this->on_voice_assistant_audio(msg);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1218,6 +1295,32 @@ void APIServerConnection::on_media_player_command_request(const MediaPlayerComma
|
||||||
this->media_player_command(msg);
|
this->media_player_command(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
void APIServerConnection::on_date_command_request(const DateCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->date_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->time_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||||
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||||
|
|
|
@ -240,6 +240,10 @@ class APIServerConnectionBase : public ProtoService {
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
|
virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
|
||||||
|
virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
|
||||||
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
|
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
|
||||||
#endif
|
#endif
|
||||||
|
@ -257,6 +261,24 @@ class APIServerConnectionBase : public ProtoService {
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
virtual void on_text_command_request(const TextCommandRequest &value){};
|
virtual void on_text_command_request(const TextCommandRequest &value){};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
bool send_list_entities_date_response(const ListEntitiesDateResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
bool send_date_state_response(const DateStateResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
virtual void on_date_command_request(const DateCommandRequest &value){};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool send_list_entities_time_response(const ListEntitiesTimeResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool send_time_state_response(const TimeStateResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
virtual void on_time_command_request(const TimeCommandRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||||
|
@ -312,6 +334,12 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
|
virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
virtual void date_command(const DateCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
virtual void time_command(const TimeCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
|
@ -398,6 +426,12 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
|
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
void on_date_command_request(const DateCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void on_time_command_request(const TimeCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -255,6 +255,24 @@ void APIServer::on_number_update(number::Number *obj, float state) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
void APIServer::on_date_update(datetime::DateEntity *obj) {
|
||||||
|
if (obj->is_internal())
|
||||||
|
return;
|
||||||
|
for (auto &c : this->clients_)
|
||||||
|
c->send_date_state(obj);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void APIServer::on_time_update(datetime::TimeEntity *obj) {
|
||||||
|
if (obj->is_internal())
|
||||||
|
return;
|
||||||
|
for (auto &c : this->clients_)
|
||||||
|
c->send_time_state(obj);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
void APIServer::on_text_update(text::Text *obj, const std::string &state) {
|
void APIServer::on_text_update(text::Text *obj, const std::string &state) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
|
|
|
@ -66,6 +66,12 @@ class APIServer : public Component, public Controller {
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
void on_number_update(number::Number *obj, float state) override;
|
void on_number_update(number::Number *obj, float state) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
void on_date_update(datetime::DateEntity *obj) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void on_time_update(datetime::TimeEntity *obj) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
void on_text_update(text::Text *obj, const std::string &state) override;
|
void on_text_update(text::Text *obj, const std::string &state) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#include "list_entities.h"
|
#include "list_entities.h"
|
||||||
#include "esphome/core/util.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "esphome/core/application.h"
|
|
||||||
#include "api_connection.h"
|
#include "api_connection.h"
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/util.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
@ -60,6 +60,14 @@ bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->
|
||||||
bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); }
|
bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
bool ListEntitiesIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_info(date); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_info(time); }
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool ListEntitiesIterator::on_text(text::Text *text) { return this->client_->send_text_info(text); }
|
bool ListEntitiesIterator::on_text(text::Text *text) { return this->client_->send_text_info(text); }
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -46,6 +46,12 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
bool on_number(number::Number *number) override;
|
bool on_number(number::Number *number) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
bool on_date(datetime::DateEntity *date) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool on_time(datetime::TimeEntity *time) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool on_text(text::Text *text) override;
|
bool on_text(text::Text *text) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -42,6 +42,12 @@ bool InitialStateIterator::on_number(number::Number *number) {
|
||||||
return this->client_->send_number_state(number, number->state);
|
return this->client_->send_number_state(number, number->state);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); }
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool InitialStateIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_state(time); }
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); }
|
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); }
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -43,6 +43,12 @@ class InitialStateIterator : public ComponentIterator {
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
bool on_number(number::Number *number) override;
|
bool on_number(number::Number *number) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
bool on_date(datetime::DateEntity *date) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool on_time(datetime::TimeEntity *time) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool on_text(text::Text *text) override;
|
bool on_text(text::Text *text) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -22,7 +22,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
if CORE.is_esp32 or CORE.is_libretiny:
|
if CORE.is_esp32 or CORE.is_libretiny:
|
||||||
# https://github.com/esphome/AsyncTCP/blob/master/library.json
|
# https://github.com/esphome/AsyncTCP/blob/master/library.json
|
||||||
cg.add_library("esphome/AsyncTCP-esphome", "2.0.1")
|
cg.add_library("esphome/AsyncTCP-esphome", "2.1.3")
|
||||||
elif CORE.is_esp8266:
|
elif CORE.is_esp8266:
|
||||||
# https://github.com/esphome/ESPAsyncTCP
|
# https://github.com/esphome/ESPAsyncTCP
|
||||||
cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0")
|
cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0")
|
||||||
|
|
224
esphome/components/at581x/__init__.py
Normal file
224
esphome/components/at581x/__init__.py
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome import automation, core
|
||||||
|
from esphome.components import i2c
|
||||||
|
from esphome.automation import maybe_simple_id
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_FREQUENCY,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
CODEOWNERS = ["@X-Ryl669"]
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
MULTI_CONF = True
|
||||||
|
|
||||||
|
|
||||||
|
at581x_ns = cg.esphome_ns.namespace("at581x")
|
||||||
|
AT581XComponent = at581x_ns.class_("AT581XComponent", cg.Component, i2c.I2CDevice)
|
||||||
|
|
||||||
|
|
||||||
|
CONF_AT581X_ID = "at581x_id"
|
||||||
|
|
||||||
|
|
||||||
|
CONF_SENSING_DISTANCE = "sensing_distance"
|
||||||
|
CONF_SENSITIVITY = "sensitivity"
|
||||||
|
CONF_POWERON_SELFCHECK_TIME = "poweron_selfcheck_time"
|
||||||
|
CONF_PROTECT_TIME = "protect_time"
|
||||||
|
CONF_TRIGGER_BASE = "trigger_base"
|
||||||
|
CONF_TRIGGER_KEEP = "trigger_keep"
|
||||||
|
CONF_STAGE_GAIN = "stage_gain"
|
||||||
|
CONF_POWER_CONSUMPTION = "power_consumption"
|
||||||
|
CONF_HW_FRONTEND_RESET = "hw_frontend_reset"
|
||||||
|
|
||||||
|
RADAR_ALLOWED_FREQ = [
|
||||||
|
5696e6,
|
||||||
|
5715e6,
|
||||||
|
5730e6,
|
||||||
|
5748e6,
|
||||||
|
5765e6,
|
||||||
|
5784e6,
|
||||||
|
5800e6,
|
||||||
|
5819e6,
|
||||||
|
5836e6,
|
||||||
|
5851e6,
|
||||||
|
5869e6,
|
||||||
|
5888e6,
|
||||||
|
]
|
||||||
|
RADAR_ALLOWED_CUR_CONSUMPTION = [
|
||||||
|
48e-6,
|
||||||
|
56e-6,
|
||||||
|
63e-6,
|
||||||
|
70e-6,
|
||||||
|
77e-6,
|
||||||
|
91e-6,
|
||||||
|
105e-6,
|
||||||
|
115e-6,
|
||||||
|
40e-6,
|
||||||
|
44e-6,
|
||||||
|
47e-6,
|
||||||
|
51e-6,
|
||||||
|
54e-6,
|
||||||
|
61e-6,
|
||||||
|
68e-6,
|
||||||
|
78e-6,
|
||||||
|
]
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(AT581XComponent),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
CONFIG_SCHEMA.extend(i2c.i2c_device_schema(0x28)).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
# Actions
|
||||||
|
AT581XResetAction = at581x_ns.class_("AT581XResetAction", automation.Action)
|
||||||
|
AT581XSettingsAction = at581x_ns.class_("AT581XSettingsAction", automation.Action)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"at581x.reset",
|
||||||
|
AT581XResetAction,
|
||||||
|
maybe_simple_id(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.use_id(AT581XComponent),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def at581x_reset_to_code(config, action_id, template_arg, args):
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
|
await cg.register_parented(var, config[CONF_ID])
|
||||||
|
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
RADAR_SETTINGS_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.use_id(AT581XComponent),
|
||||||
|
cv.Optional(CONF_HW_FRONTEND_RESET): cv.templatable(cv.boolean),
|
||||||
|
cv.Optional(CONF_FREQUENCY, default="5800MHz"): cv.templatable(
|
||||||
|
cv.All(cv.frequency, cv.one_of(*RADAR_ALLOWED_FREQ))
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_SENSING_DISTANCE, default=823): cv.templatable(
|
||||||
|
cv.int_range(min=0, max=1023)
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_POWERON_SELFCHECK_TIME, default="2000ms"): cv.templatable(
|
||||||
|
cv.All(
|
||||||
|
cv.positive_time_period_milliseconds,
|
||||||
|
cv.Range(max=core.TimePeriod(milliseconds=65535)),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_POWER_CONSUMPTION, default="70uA"): cv.templatable(
|
||||||
|
cv.All(cv.current, cv.one_of(*RADAR_ALLOWED_CUR_CONSUMPTION))
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PROTECT_TIME, default="1000ms"): cv.templatable(
|
||||||
|
cv.All(
|
||||||
|
cv.positive_time_period_milliseconds,
|
||||||
|
cv.Range(
|
||||||
|
min=core.TimePeriod(milliseconds=1),
|
||||||
|
max=core.TimePeriod(milliseconds=65535),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TRIGGER_BASE, default="500ms"): cv.templatable(
|
||||||
|
cv.All(
|
||||||
|
cv.positive_time_period_milliseconds,
|
||||||
|
cv.Range(
|
||||||
|
min=core.TimePeriod(milliseconds=1),
|
||||||
|
max=core.TimePeriod(milliseconds=65535),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TRIGGER_KEEP, default="1500ms"): cv.templatable(
|
||||||
|
cv.All(
|
||||||
|
cv.positive_time_period_milliseconds,
|
||||||
|
cv.Range(
|
||||||
|
min=core.TimePeriod(milliseconds=1),
|
||||||
|
max=core.TimePeriod(milliseconds=65535),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_STAGE_GAIN, default=3): cv.templatable(
|
||||||
|
cv.int_range(min=0, max=12)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
).add_extra(
|
||||||
|
cv.has_at_least_one_key(
|
||||||
|
CONF_HW_FRONTEND_RESET,
|
||||||
|
CONF_FREQUENCY,
|
||||||
|
CONF_SENSING_DISTANCE,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"at581x.settings",
|
||||||
|
AT581XSettingsAction,
|
||||||
|
RADAR_SETTINGS_SCHEMA,
|
||||||
|
)
|
||||||
|
async def at581x_settings_to_code(config, action_id, template_arg, args):
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
|
await cg.register_parented(var, config[CONF_ID])
|
||||||
|
|
||||||
|
# Radar configuration
|
||||||
|
if frontend_reset := config.get(CONF_HW_FRONTEND_RESET):
|
||||||
|
template_ = await cg.templatable(frontend_reset, args, int)
|
||||||
|
cg.add(var.set_hw_frontend_reset(template_))
|
||||||
|
|
||||||
|
if freq := config.get(CONF_FREQUENCY):
|
||||||
|
template_ = await cg.templatable(freq, args, float)
|
||||||
|
template_ = int(template_ / 1000000)
|
||||||
|
cg.add(var.set_frequency(template_))
|
||||||
|
|
||||||
|
if sens_dist := config.get(CONF_SENSING_DISTANCE):
|
||||||
|
template_ = await cg.templatable(sens_dist, args, int)
|
||||||
|
cg.add(var.set_sensing_distance(template_))
|
||||||
|
|
||||||
|
if selfcheck := config.get(CONF_POWERON_SELFCHECK_TIME):
|
||||||
|
template_ = await cg.templatable(selfcheck, args, float)
|
||||||
|
if isinstance(template_, cv.TimePeriod):
|
||||||
|
template_ = template_.total_milliseconds
|
||||||
|
template_ = int(template_)
|
||||||
|
cg.add(var.set_poweron_selfcheck_time(template_))
|
||||||
|
|
||||||
|
if protect := config.get(CONF_PROTECT_TIME):
|
||||||
|
template_ = await cg.templatable(protect, args, float)
|
||||||
|
if isinstance(template_, cv.TimePeriod):
|
||||||
|
template_ = template_.total_milliseconds
|
||||||
|
template_ = int(template_)
|
||||||
|
cg.add(var.set_protect_time(template_))
|
||||||
|
|
||||||
|
if trig_base := config.get(CONF_TRIGGER_BASE):
|
||||||
|
template_ = await cg.templatable(trig_base, args, float)
|
||||||
|
if isinstance(template_, cv.TimePeriod):
|
||||||
|
template_ = template_.total_milliseconds
|
||||||
|
template_ = int(template_)
|
||||||
|
cg.add(var.set_trigger_base(template_))
|
||||||
|
|
||||||
|
if trig_keep := config.get(CONF_TRIGGER_KEEP):
|
||||||
|
template_ = await cg.templatable(trig_keep, args, float)
|
||||||
|
if isinstance(template_, cv.TimePeriod):
|
||||||
|
template_ = template_.total_milliseconds
|
||||||
|
template_ = int(template_)
|
||||||
|
cg.add(var.set_trigger_keep(template_))
|
||||||
|
|
||||||
|
if stage_gain := config.get(CONF_STAGE_GAIN):
|
||||||
|
template_ = await cg.templatable(stage_gain, args, int)
|
||||||
|
cg.add(var.set_stage_gain(template_))
|
||||||
|
|
||||||
|
if power := config.get(CONF_POWER_CONSUMPTION):
|
||||||
|
template_ = await cg.templatable(power, args, float)
|
||||||
|
template_ = int(template_ * 1000000)
|
||||||
|
cg.add(var.set_power_consumption(template_))
|
||||||
|
|
||||||
|
return var
|
195
esphome/components/at581x/at581x.cpp
Normal file
195
esphome/components/at581x/at581x.cpp
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
#include "at581x.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
/* Select gain for AT581X (3dB per step for level1, 6dB per step for level 2), high value = small gain. (p12) */
|
||||||
|
const uint8_t GAIN_ADDR_TABLE[] = {0x5c, 0x63};
|
||||||
|
const uint8_t GAIN5C_TABLE[] = {0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, 0xb8, 0xc8};
|
||||||
|
const uint8_t GAIN63_TABLE[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
|
||||||
|
const uint8_t GAIN61_VALUE = 0xCA; // 0xC0 | 0x02 (freq present) | 0x08 (gain present)
|
||||||
|
|
||||||
|
/*!< Power consumption configuration table (p12). */
|
||||||
|
const uint8_t POWER_TABLE[] = {48, 56, 63, 70, 77, 91, 105, 115, 40, 44, 47, 51, 54, 61, 68, 78};
|
||||||
|
const uint8_t POWER67_TABLE[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
|
||||||
|
const uint8_t POWER68_TABLE[] = {0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24}; // See Page 12, shift by 3 bits
|
||||||
|
|
||||||
|
/*!< Frequency Configuration table (p14/15 of datasheet). */
|
||||||
|
const uint8_t FREQ_ADDR = 0x61;
|
||||||
|
const uint16_t FREQ_TABLE[] = {5696, 5715, 5730, 5748, 5765, 5784, 5800, 5819, 5836, 5851, 5869, 5888};
|
||||||
|
const uint8_t FREQ5F_TABLE[] = {0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x40, 0x41, 0x42, 0x43};
|
||||||
|
const uint8_t FREQ60_TABLE[] = {0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e};
|
||||||
|
|
||||||
|
/*!< Value for RF and analog modules switch (p10). */
|
||||||
|
const uint8_t RF_OFF_TABLE[] = {0x46, 0xaa, 0x50};
|
||||||
|
const uint8_t RF_ON_TABLE[] = {0x45, 0x55, 0xA0};
|
||||||
|
const uint8_t RF_REG_ADDR[] = {0x5d, 0x62, 0x51};
|
||||||
|
|
||||||
|
/*!< Registers of Lighting delay time. Unit: ms, min 2s (p8) */
|
||||||
|
const uint8_t HIGH_LEVEL_DELAY_CONTROL_ADDR = 0x41; /*!< Time_flag_out_ctrl 0x01 */
|
||||||
|
const uint8_t HIGH_LEVEL_DELAY_VALUE_ADDR = 0x42; /*!< Time_flag_out_1 Bit<7:0> */
|
||||||
|
|
||||||
|
const uint8_t RESET_ADDR = 0x00;
|
||||||
|
|
||||||
|
/*!< Sensing distance address */
|
||||||
|
const uint8_t SIGNAL_DETECTION_THRESHOLD_ADDR_LO = 0x10;
|
||||||
|
const uint8_t SIGNAL_DETECTION_THRESHOLD_ADDR_HI = 0x11;
|
||||||
|
|
||||||
|
/*!< Bit field value for power registers */
|
||||||
|
const uint8_t POWER_THRESHOLD_ADDR_HI = 0x68;
|
||||||
|
const uint8_t POWER_THRESHOLD_ADDR_LO = 0x67;
|
||||||
|
const uint8_t PWR_WORK_TIME_EN = 8; // Reg 0x67
|
||||||
|
const uint8_t PWR_BURST_TIME_EN = 32; // Reg 0x68
|
||||||
|
const uint8_t PWR_THRESH_EN = 64; // Reg 0x68
|
||||||
|
const uint8_t PWR_THRESH_VAL_EN = 128; // Reg 0x67
|
||||||
|
|
||||||
|
/*!< Times */
|
||||||
|
const uint8_t TRIGGER_BASE_TIME_ADDR = 0x3D; // 4 bytes, so up to 0x40
|
||||||
|
const uint8_t PROTECT_TIME_ADDR = 0x4E; // 2 bytes, up to 0x4F
|
||||||
|
const uint8_t TRIGGER_KEEP_TIME_ADDR = 0x42; // 4 bytes, so up to 0x45
|
||||||
|
const uint8_t TIME41_VALUE = 1;
|
||||||
|
const uint8_t SELF_CHECK_TIME_ADDR = 0x38; // 2 bytes, up to 0x39
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace at581x {
|
||||||
|
|
||||||
|
static const char *const TAG = "at581x";
|
||||||
|
|
||||||
|
bool AT581XComponent::i2c_write_reg(uint8_t addr, uint8_t data) {
|
||||||
|
return this->write_register(addr, &data, 1) == esphome::i2c::NO_ERROR;
|
||||||
|
}
|
||||||
|
bool AT581XComponent::i2c_write_reg(uint8_t addr, uint32_t data) {
|
||||||
|
return this->i2c_write_reg(addr + 0, uint8_t(data & 0xFF)) &&
|
||||||
|
this->i2c_write_reg(addr + 1, uint8_t((data >> 8) & 0xFF)) &&
|
||||||
|
this->i2c_write_reg(addr + 2, uint8_t((data >> 16) & 0xFF)) &&
|
||||||
|
this->i2c_write_reg(addr + 3, uint8_t((data >> 24) & 0xFF));
|
||||||
|
}
|
||||||
|
bool AT581XComponent::i2c_write_reg(uint8_t addr, uint16_t data) {
|
||||||
|
return this->i2c_write_reg(addr, uint8_t(data & 0xFF)) && this->i2c_write_reg(addr + 1, uint8_t((data >> 8) & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AT581XComponent::i2c_read_reg(uint8_t addr, uint8_t &data) {
|
||||||
|
return this->read_register(addr, &data, 1) == esphome::i2c::NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AT581XComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up AT581X..."); }
|
||||||
|
void AT581XComponent::dump_config() { LOG_I2C_DEVICE(this); }
|
||||||
|
#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
|
||||||
|
bool AT581XComponent::i2c_write_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Writing new config for AT581X...");
|
||||||
|
ESP_LOGCONFIG(TAG, "Frequency: %dMHz", this->freq_);
|
||||||
|
ESP_LOGCONFIG(TAG, "Sensing distance: %d", this->delta_);
|
||||||
|
ESP_LOGCONFIG(TAG, "Power: %dµA", this->power_);
|
||||||
|
ESP_LOGCONFIG(TAG, "Gain: %d", this->gain_);
|
||||||
|
ESP_LOGCONFIG(TAG, "Trigger base time: %dms", this->trigger_base_time_ms_);
|
||||||
|
ESP_LOGCONFIG(TAG, "Trigger keep time: %dms", this->trigger_keep_time_ms_);
|
||||||
|
ESP_LOGCONFIG(TAG, "Protect time: %dms", this->protect_time_ms_);
|
||||||
|
ESP_LOGCONFIG(TAG, "Self check time: %dms", this->self_check_time_ms_);
|
||||||
|
|
||||||
|
// Set frequency point
|
||||||
|
if (!this->i2c_write_reg(FREQ_ADDR, GAIN61_VALUE)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write AT581X Freq mode");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Find the current frequency from the table to know what value to write
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(FREQ_TABLE) + 1; i++) {
|
||||||
|
if (i == ARRAY_SIZE(FREQ_TABLE)) {
|
||||||
|
ESP_LOGE(TAG, "Set frequency not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (FREQ_TABLE[i] == this->freq_) {
|
||||||
|
if (!this->i2c_write_reg(0x5F, FREQ5F_TABLE[i]) || !this->i2c_write_reg(0x60, FREQ60_TABLE[i])) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write AT581X Freq value");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set distance
|
||||||
|
if (!this->i2c_write_reg(SIGNAL_DETECTION_THRESHOLD_ADDR_LO, (uint8_t) (this->delta_ & 0xFF)) ||
|
||||||
|
!this->i2c_write_reg(SIGNAL_DETECTION_THRESHOLD_ADDR_HI, (uint8_t) (this->delta_ >> 8))) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write AT581X sensing distance low");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set power setting
|
||||||
|
uint8_t pwr67 = PWR_THRESH_VAL_EN | PWR_WORK_TIME_EN, pwr68 = PWR_BURST_TIME_EN | PWR_THRESH_EN;
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(POWER_TABLE) + 1; i++) {
|
||||||
|
if (i == ARRAY_SIZE(POWER_TABLE)) {
|
||||||
|
ESP_LOGE(TAG, "Set power not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (POWER_TABLE[i] == this->power_) {
|
||||||
|
pwr67 |= POWER67_TABLE[i];
|
||||||
|
pwr68 |= POWER68_TABLE[i]; // See Page 12
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->i2c_write_reg(POWER_THRESHOLD_ADDR_LO, pwr67) || !this->i2c_write_reg(POWER_THRESHOLD_ADDR_HI, pwr68)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write AT581X power registers");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set gain
|
||||||
|
if (!this->i2c_write_reg(GAIN_ADDR_TABLE[0], GAIN5C_TABLE[this->gain_]) ||
|
||||||
|
!this->i2c_write_reg(GAIN_ADDR_TABLE[1], GAIN63_TABLE[this->gain_ >> 1])) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write AT581X gain registers");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set times
|
||||||
|
if (!this->i2c_write_reg(TRIGGER_BASE_TIME_ADDR, (uint32_t) this->trigger_base_time_ms_)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write AT581X trigger base time registers");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this->i2c_write_reg(TRIGGER_KEEP_TIME_ADDR, (uint32_t) this->trigger_keep_time_ms_)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write AT581X trigger keep time registers");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->i2c_write_reg(PROTECT_TIME_ADDR, (uint16_t) this->protect_time_ms_)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write AT581X protect time registers");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this->i2c_write_reg(SELF_CHECK_TIME_ADDR, (uint16_t) this->self_check_time_ms_)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write AT581X self check time registers");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->i2c_write_reg(0x41, TIME41_VALUE)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to enable AT581X time registers");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't know why it's required in other code, it's not in datasheet
|
||||||
|
if (!this->i2c_write_reg(0x55, (uint8_t) 0x04)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to enable AT581X");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, config is written, let's reset the chip so it's using the new config
|
||||||
|
return this->reset_hardware_frontend();
|
||||||
|
}
|
||||||
|
|
||||||
|
// float AT581XComponent::get_setup_priority() const { return 0; }
|
||||||
|
bool AT581XComponent::reset_hardware_frontend() {
|
||||||
|
if (!this->i2c_write_reg(RESET_ADDR, (uint8_t) 0) || !this->i2c_write_reg(RESET_ADDR, (uint8_t) 1)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to reset AT581X hardware frontend");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AT581XComponent::set_rf_mode(bool enable) {
|
||||||
|
const uint8_t *p = enable ? &RF_ON_TABLE[0] : &RF_OFF_TABLE[0];
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(RF_REG_ADDR); i++) {
|
||||||
|
if (!this->i2c_write_reg(RF_REG_ADDR[i], p[i])) {
|
||||||
|
ESP_LOGE(TAG, "Failed to write AT581X RF mode");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace at581x
|
||||||
|
} // namespace esphome
|
62
esphome/components/at581x/at581x.h
Normal file
62
esphome/components/at581x/at581x.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#endif
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace at581x {
|
||||||
|
|
||||||
|
class AT581XComponent : public Component, public i2c::I2CDevice {
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
protected:
|
||||||
|
switch_::Switch *rf_power_switch_{nullptr};
|
||||||
|
|
||||||
|
public:
|
||||||
|
void set_rf_power_switch(switch_::Switch *s) {
|
||||||
|
this->rf_power_switch_ = s;
|
||||||
|
s->turn_on();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
// float get_setup_priority() const override;
|
||||||
|
|
||||||
|
void set_sensing_distance(int distance) { this->delta_ = 1023 - distance; }
|
||||||
|
|
||||||
|
void set_rf_mode(bool enabled);
|
||||||
|
void set_frequency(int frequency) { this->freq_ = frequency; }
|
||||||
|
void set_poweron_selfcheck_time(int value) { this->self_check_time_ms_ = value; }
|
||||||
|
void set_protect_time(int value) { this->protect_time_ms_ = value; }
|
||||||
|
void set_trigger_base(int value) { this->trigger_base_time_ms_ = value; }
|
||||||
|
void set_trigger_keep(int value) { this->trigger_keep_time_ms_ = value; }
|
||||||
|
void set_stage_gain(int value) { this->gain_ = value; }
|
||||||
|
void set_power_consumption(int value) { this->power_ = value; }
|
||||||
|
|
||||||
|
bool i2c_write_config();
|
||||||
|
bool reset_hardware_frontend();
|
||||||
|
bool i2c_write_reg(uint8_t addr, uint8_t data);
|
||||||
|
bool i2c_write_reg(uint8_t addr, uint32_t data);
|
||||||
|
bool i2c_write_reg(uint8_t addr, uint16_t data);
|
||||||
|
bool i2c_read_reg(uint8_t addr, uint8_t &data);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int freq_;
|
||||||
|
int self_check_time_ms_; /*!< Power-on self-test time, range: 0 ~ 65536 ms */
|
||||||
|
int protect_time_ms_; /*!< Protection time, recommended 1000 ms */
|
||||||
|
int trigger_base_time_ms_; /*!< Default: 500 ms */
|
||||||
|
int trigger_keep_time_ms_; /*!< Total trig time = TRIGGER_BASE_TIME + DEF_TRIGGER_KEEP_TIME, minimum: 1 */
|
||||||
|
int delta_; /*!< Delta value: 0 ~ 1023, the larger the value, the shorter the distance */
|
||||||
|
int gain_; /*!< Default: 9dB */
|
||||||
|
int power_; /*!< In µA */
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace at581x
|
||||||
|
} // namespace esphome
|
71
esphome/components/at581x/automation.h
Normal file
71
esphome/components/at581x/automation.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
#include "at581x.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace at581x {
|
||||||
|
|
||||||
|
template<typename... Ts> class AT581XResetAction : public Action<Ts...>, public Parented<AT581XComponent> {
|
||||||
|
public:
|
||||||
|
void play(Ts... x) { this->parent_->reset_hardware_frontend(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class AT581XSettingsAction : public Action<Ts...>, public Parented<AT581XComponent> {
|
||||||
|
public:
|
||||||
|
TEMPLATABLE_VALUE(int8_t, hw_frontend_reset)
|
||||||
|
TEMPLATABLE_VALUE(int, frequency)
|
||||||
|
TEMPLATABLE_VALUE(int, sensing_distance)
|
||||||
|
TEMPLATABLE_VALUE(int, poweron_selfcheck_time)
|
||||||
|
TEMPLATABLE_VALUE(int, power_consumption)
|
||||||
|
TEMPLATABLE_VALUE(int, protect_time)
|
||||||
|
TEMPLATABLE_VALUE(int, trigger_base)
|
||||||
|
TEMPLATABLE_VALUE(int, trigger_keep)
|
||||||
|
TEMPLATABLE_VALUE(int, stage_gain)
|
||||||
|
|
||||||
|
void play(Ts... x) {
|
||||||
|
if (this->frequency_.has_value()) {
|
||||||
|
int v = this->frequency_.value(x...);
|
||||||
|
this->parent_->set_frequency(v);
|
||||||
|
}
|
||||||
|
if (this->sensing_distance_.has_value()) {
|
||||||
|
int v = this->sensing_distance_.value(x...);
|
||||||
|
this->parent_->set_sensing_distance(v);
|
||||||
|
}
|
||||||
|
if (this->poweron_selfcheck_time_.has_value()) {
|
||||||
|
int v = this->poweron_selfcheck_time_.value(x...);
|
||||||
|
this->parent_->set_poweron_selfcheck_time(v);
|
||||||
|
}
|
||||||
|
if (this->power_consumption_.has_value()) {
|
||||||
|
int v = this->power_consumption_.value(x...);
|
||||||
|
this->parent_->set_power_consumption(v);
|
||||||
|
}
|
||||||
|
if (this->protect_time_.has_value()) {
|
||||||
|
int v = this->protect_time_.value(x...);
|
||||||
|
this->parent_->set_protect_time(v);
|
||||||
|
}
|
||||||
|
if (this->trigger_base_.has_value()) {
|
||||||
|
int v = this->trigger_base_.value(x...);
|
||||||
|
this->parent_->set_trigger_base(v);
|
||||||
|
}
|
||||||
|
if (this->trigger_keep_.has_value()) {
|
||||||
|
int v = this->trigger_keep_.value(x...);
|
||||||
|
this->parent_->set_trigger_keep(v);
|
||||||
|
}
|
||||||
|
if (this->stage_gain_.has_value()) {
|
||||||
|
int v = this->stage_gain_.value(x...);
|
||||||
|
this->parent_->set_stage_gain(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This actually perform all the modification on the system
|
||||||
|
this->parent_->i2c_write_config();
|
||||||
|
|
||||||
|
if (this->hw_frontend_reset_.has_value() && this->hw_frontend_reset_.value(x...) == true) {
|
||||||
|
this->parent_->reset_hardware_frontend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace at581x
|
||||||
|
} // namespace esphome
|
31
esphome/components/at581x/switch/__init__.py
Normal file
31
esphome/components/at581x/switch/__init__.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import switch
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
DEVICE_CLASS_SWITCH,
|
||||||
|
ICON_WIFI,
|
||||||
|
)
|
||||||
|
from .. import CONF_AT581X_ID, AT581XComponent, at581x_ns
|
||||||
|
|
||||||
|
DEPENDENCIES = ["at581x"]
|
||||||
|
|
||||||
|
RFSwitch = at581x_ns.class_("RFSwitch", switch.Switch)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = switch.switch_schema(
|
||||||
|
RFSwitch,
|
||||||
|
device_class=DEVICE_CLASS_SWITCH,
|
||||||
|
icon=ICON_WIFI,
|
||||||
|
).extend(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_AT581X_ID): cv.use_id(AT581XComponent),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
at581x_component = await cg.get_variable(config[CONF_AT581X_ID])
|
||||||
|
s = await switch.new_switch(config)
|
||||||
|
await cg.register_parented(s, config[CONF_AT581X_ID])
|
||||||
|
cg.add(at581x_component.set_rf_power_switch(s))
|
12
esphome/components/at581x/switch/rf_switch.cpp
Normal file
12
esphome/components/at581x/switch/rf_switch.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include "rf_switch.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace at581x {
|
||||||
|
|
||||||
|
void RFSwitch::write_state(bool state) {
|
||||||
|
this->publish_state(state);
|
||||||
|
this->parent_->set_rf_mode(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace at581x
|
||||||
|
} // namespace esphome
|
15
esphome/components/at581x/switch/rf_switch.h
Normal file
15
esphome/components/at581x/switch/rf_switch.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#include "../at581x.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace at581x {
|
||||||
|
|
||||||
|
class RFSwitch : public switch_::Switch, public Parented<AT581XComponent> {
|
||||||
|
protected:
|
||||||
|
void write_state(bool state) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace at581x
|
||||||
|
} // namespace esphome
|
|
@ -117,7 +117,7 @@ void ATM90E26Component::setup() {
|
||||||
this->write16_(ATM90E26_REGISTER_ADJSTART,
|
this->write16_(ATM90E26_REGISTER_ADJSTART,
|
||||||
0x8765); // Checks correctness of 31-3A registers and starts normal measurement if ok
|
0x8765); // Checks correctness of 31-3A registers and starts normal measurement if ok
|
||||||
|
|
||||||
uint16_t sys_status = this->read16_(ATM90E26_REGISTER_SYSSTATUS);
|
const uint16_t sys_status = this->read16_(ATM90E26_REGISTER_SYSSTATUS);
|
||||||
if (sys_status & 0xC000) { // Checksum 1 Error
|
if (sys_status & 0xC000) { // Checksum 1 Error
|
||||||
|
|
||||||
ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS1 was incorrect, expected: 0x%04X",
|
ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS1 was incorrect, expected: 0x%04X",
|
||||||
|
@ -177,27 +177,27 @@ void ATM90E26Component::write16_(uint8_t a_register, uint16_t val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float ATM90E26Component::get_line_current_() {
|
float ATM90E26Component::get_line_current_() {
|
||||||
uint16_t current = this->read16_(ATM90E26_REGISTER_IRMS);
|
const uint16_t current = this->read16_(ATM90E26_REGISTER_IRMS);
|
||||||
return current / 1000.0f;
|
return current / 1000.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ATM90E26Component::get_line_voltage_() {
|
float ATM90E26Component::get_line_voltage_() {
|
||||||
uint16_t voltage = this->read16_(ATM90E26_REGISTER_URMS);
|
const uint16_t voltage = this->read16_(ATM90E26_REGISTER_URMS);
|
||||||
return voltage / 100.0f;
|
return voltage / 100.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ATM90E26Component::get_active_power_() {
|
float ATM90E26Component::get_active_power_() {
|
||||||
int16_t val = this->read16_(ATM90E26_REGISTER_PMEAN); // two's complement
|
const int16_t val = this->read16_(ATM90E26_REGISTER_PMEAN); // two's complement
|
||||||
return (float) val;
|
return (float) val;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ATM90E26Component::get_reactive_power_() {
|
float ATM90E26Component::get_reactive_power_() {
|
||||||
int16_t val = this->read16_(ATM90E26_REGISTER_QMEAN); // two's complement
|
const int16_t val = this->read16_(ATM90E26_REGISTER_QMEAN); // two's complement
|
||||||
return (float) val;
|
return (float) val;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ATM90E26Component::get_power_factor_() {
|
float ATM90E26Component::get_power_factor_() {
|
||||||
uint16_t val = this->read16_(ATM90E26_REGISTER_POWERF); // signed
|
const uint16_t val = this->read16_(ATM90E26_REGISTER_POWERF); // signed
|
||||||
if (val & 0x8000) {
|
if (val & 0x8000) {
|
||||||
return -(val & 0x7FF) / 1000.0f;
|
return -(val & 0x7FF) / 1000.0f;
|
||||||
} else {
|
} else {
|
||||||
|
@ -206,7 +206,7 @@ float ATM90E26Component::get_power_factor_() {
|
||||||
}
|
}
|
||||||
|
|
||||||
float ATM90E26Component::get_forward_active_energy_() {
|
float ATM90E26Component::get_forward_active_energy_() {
|
||||||
uint16_t val = this->read16_(ATM90E26_REGISTER_APENERGY);
|
const uint16_t val = this->read16_(ATM90E26_REGISTER_APENERGY);
|
||||||
if ((UINT32_MAX - this->cumulative_forward_active_energy_) > val) {
|
if ((UINT32_MAX - this->cumulative_forward_active_energy_) > val) {
|
||||||
this->cumulative_forward_active_energy_ += val;
|
this->cumulative_forward_active_energy_ += val;
|
||||||
} else {
|
} else {
|
||||||
|
@ -217,7 +217,7 @@ float ATM90E26Component::get_forward_active_energy_() {
|
||||||
}
|
}
|
||||||
|
|
||||||
float ATM90E26Component::get_reverse_active_energy_() {
|
float ATM90E26Component::get_reverse_active_energy_() {
|
||||||
uint16_t val = this->read16_(ATM90E26_REGISTER_ANENERGY);
|
const uint16_t val = this->read16_(ATM90E26_REGISTER_ANENERGY);
|
||||||
if (UINT32_MAX - this->cumulative_reverse_active_energy_ > val) {
|
if (UINT32_MAX - this->cumulative_reverse_active_energy_ > val) {
|
||||||
this->cumulative_reverse_active_energy_ += val;
|
this->cumulative_reverse_active_energy_ += val;
|
||||||
} else {
|
} else {
|
||||||
|
@ -227,7 +227,7 @@ float ATM90E26Component::get_reverse_active_energy_() {
|
||||||
}
|
}
|
||||||
|
|
||||||
float ATM90E26Component::get_frequency_() {
|
float ATM90E26Component::get_frequency_() {
|
||||||
uint16_t freq = this->read16_(ATM90E26_REGISTER_FREQ);
|
const uint16_t freq = this->read16_(ATM90E26_REGISTER_FREQ);
|
||||||
return freq / 100.0f;
|
return freq / 100.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,82 +7,128 @@ namespace esphome {
|
||||||
namespace atm90e32 {
|
namespace atm90e32 {
|
||||||
|
|
||||||
static const char *const TAG = "atm90e32";
|
static const char *const TAG = "atm90e32";
|
||||||
|
void ATM90E32Component::loop() {
|
||||||
|
if (this->get_publish_interval_flag_()) {
|
||||||
|
this->set_publish_interval_flag_(false);
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].voltage_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].voltage_ = this->get_phase_voltage_(phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].current_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].current_ = this->get_phase_current_(phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].power_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].active_power_ = this->get_phase_active_power_(phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].power_factor_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].power_factor_ = this->get_phase_power_factor_(phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].reactive_power_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].reactive_power_ = this->get_phase_reactive_power_(phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].forward_active_energy_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].forward_active_energy_ = this->get_phase_forward_active_energy_(phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].reverse_active_energy_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].reverse_active_energy_ = this->get_phase_reverse_active_energy_(phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].phase_angle_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].phase_angle_ = this->get_phase_angle_(phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].harmonic_active_power_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].harmonic_active_power_ = this->get_phase_harmonic_active_power_(phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].peak_current_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].peak_current_ = this->get_phase_peak_current_(phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// After the local store in collected we can publish them trusting they are withing +-1 haardware sampling
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].voltage_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].voltage_sensor_->publish_state(this->get_local_phase_voltage_(phase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].current_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].current_sensor_->publish_state(this->get_local_phase_current_(phase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].power_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].power_sensor_->publish_state(this->get_local_phase_active_power_(phase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].power_factor_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].power_factor_sensor_->publish_state(this->get_local_phase_power_factor_(phase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].reactive_power_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].reactive_power_sensor_->publish_state(this->get_local_phase_reactive_power_(phase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].forward_active_energy_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].forward_active_energy_sensor_->publish_state(
|
||||||
|
this->get_local_phase_forward_active_energy_(phase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].reverse_active_energy_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].reverse_active_energy_sensor_->publish_state(
|
||||||
|
this->get_local_phase_reverse_active_energy_(phase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].phase_angle_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].phase_angle_sensor_->publish_state(this->get_local_phase_angle_(phase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].harmonic_active_power_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].harmonic_active_power_sensor_->publish_state(
|
||||||
|
this->get_local_phase_harmonic_active_power_(phase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
|
if (this->phase_[phase].peak_current_sensor_ != nullptr) {
|
||||||
|
this->phase_[phase].peak_current_sensor_->publish_state(this->get_local_phase_peak_current_(phase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->freq_sensor_ != nullptr) {
|
||||||
|
this->freq_sensor_->publish_state(this->get_frequency_());
|
||||||
|
}
|
||||||
|
if (this->chip_temperature_sensor_ != nullptr) {
|
||||||
|
this->chip_temperature_sensor_->publish_state(this->get_chip_temperature_());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ATM90E32Component::update() {
|
void ATM90E32Component::update() {
|
||||||
if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) {
|
if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) {
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this->set_publish_interval_flag_(true);
|
||||||
if (this->phase_[0].voltage_sensor_ != nullptr) {
|
|
||||||
this->phase_[0].voltage_sensor_->publish_state(this->get_line_voltage_a_());
|
|
||||||
}
|
|
||||||
if (this->phase_[1].voltage_sensor_ != nullptr) {
|
|
||||||
this->phase_[1].voltage_sensor_->publish_state(this->get_line_voltage_b_());
|
|
||||||
}
|
|
||||||
if (this->phase_[2].voltage_sensor_ != nullptr) {
|
|
||||||
this->phase_[2].voltage_sensor_->publish_state(this->get_line_voltage_c_());
|
|
||||||
}
|
|
||||||
if (this->phase_[0].current_sensor_ != nullptr) {
|
|
||||||
this->phase_[0].current_sensor_->publish_state(this->get_line_current_a_());
|
|
||||||
}
|
|
||||||
if (this->phase_[1].current_sensor_ != nullptr) {
|
|
||||||
this->phase_[1].current_sensor_->publish_state(this->get_line_current_b_());
|
|
||||||
}
|
|
||||||
if (this->phase_[2].current_sensor_ != nullptr) {
|
|
||||||
this->phase_[2].current_sensor_->publish_state(this->get_line_current_c_());
|
|
||||||
}
|
|
||||||
if (this->phase_[0].power_sensor_ != nullptr) {
|
|
||||||
this->phase_[0].power_sensor_->publish_state(this->get_active_power_a_());
|
|
||||||
}
|
|
||||||
if (this->phase_[1].power_sensor_ != nullptr) {
|
|
||||||
this->phase_[1].power_sensor_->publish_state(this->get_active_power_b_());
|
|
||||||
}
|
|
||||||
if (this->phase_[2].power_sensor_ != nullptr) {
|
|
||||||
this->phase_[2].power_sensor_->publish_state(this->get_active_power_c_());
|
|
||||||
}
|
|
||||||
if (this->phase_[0].reactive_power_sensor_ != nullptr) {
|
|
||||||
this->phase_[0].reactive_power_sensor_->publish_state(this->get_reactive_power_a_());
|
|
||||||
}
|
|
||||||
if (this->phase_[1].reactive_power_sensor_ != nullptr) {
|
|
||||||
this->phase_[1].reactive_power_sensor_->publish_state(this->get_reactive_power_b_());
|
|
||||||
}
|
|
||||||
if (this->phase_[2].reactive_power_sensor_ != nullptr) {
|
|
||||||
this->phase_[2].reactive_power_sensor_->publish_state(this->get_reactive_power_c_());
|
|
||||||
}
|
|
||||||
if (this->phase_[0].power_factor_sensor_ != nullptr) {
|
|
||||||
this->phase_[0].power_factor_sensor_->publish_state(this->get_power_factor_a_());
|
|
||||||
}
|
|
||||||
if (this->phase_[1].power_factor_sensor_ != nullptr) {
|
|
||||||
this->phase_[1].power_factor_sensor_->publish_state(this->get_power_factor_b_());
|
|
||||||
}
|
|
||||||
if (this->phase_[2].power_factor_sensor_ != nullptr) {
|
|
||||||
this->phase_[2].power_factor_sensor_->publish_state(this->get_power_factor_c_());
|
|
||||||
}
|
|
||||||
if (this->phase_[0].forward_active_energy_sensor_ != nullptr) {
|
|
||||||
this->phase_[0].forward_active_energy_sensor_->publish_state(this->get_forward_active_energy_a_());
|
|
||||||
}
|
|
||||||
if (this->phase_[1].forward_active_energy_sensor_ != nullptr) {
|
|
||||||
this->phase_[1].forward_active_energy_sensor_->publish_state(this->get_forward_active_energy_b_());
|
|
||||||
}
|
|
||||||
if (this->phase_[2].forward_active_energy_sensor_ != nullptr) {
|
|
||||||
this->phase_[2].forward_active_energy_sensor_->publish_state(this->get_forward_active_energy_c_());
|
|
||||||
}
|
|
||||||
if (this->phase_[0].reverse_active_energy_sensor_ != nullptr) {
|
|
||||||
this->phase_[0].reverse_active_energy_sensor_->publish_state(this->get_reverse_active_energy_a_());
|
|
||||||
}
|
|
||||||
if (this->phase_[1].reverse_active_energy_sensor_ != nullptr) {
|
|
||||||
this->phase_[1].reverse_active_energy_sensor_->publish_state(this->get_reverse_active_energy_b_());
|
|
||||||
}
|
|
||||||
if (this->phase_[2].reverse_active_energy_sensor_ != nullptr) {
|
|
||||||
this->phase_[2].reverse_active_energy_sensor_->publish_state(this->get_reverse_active_energy_c_());
|
|
||||||
}
|
|
||||||
if (this->freq_sensor_ != nullptr) {
|
|
||||||
this->freq_sensor_->publish_state(this->get_frequency_());
|
|
||||||
}
|
|
||||||
if (this->chip_temperature_sensor_ != nullptr) {
|
|
||||||
this->chip_temperature_sensor_->publish_state(this->get_chip_temperature_());
|
|
||||||
}
|
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,29 +147,51 @@ void ATM90E32Component::setup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
this->write16_(ATM90E32_REGISTER_SOFTRESET, 0x789A); // Perform soft reset
|
this->write16_(ATM90E32_REGISTER_SOFTRESET, 0x789A); // Perform soft reset
|
||||||
|
delay(6); // Wait for the minimum 5ms + 1ms
|
||||||
this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA); // enable register config access
|
this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA); // enable register config access
|
||||||
this->write16_(ATM90E32_REGISTER_METEREN, 0x0001); // Enable Metering
|
if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != 0x55AA) {
|
||||||
if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != 0x0001) {
|
|
||||||
ESP_LOGW(TAG, "Could not initialize ATM90E32 IC, check SPI settings");
|
ESP_LOGW(TAG, "Could not initialize ATM90E32 IC, check SPI settings");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->write16_(ATM90E32_REGISTER_PLCONSTH, 0x0861); // PL Constant MSB (default) = 140625000
|
|
||||||
this->write16_(ATM90E32_REGISTER_PLCONSTL, 0xC468); // PL Constant LSB (default)
|
this->write16_(ATM90E32_REGISTER_METEREN, 0x0001); // Enable Metering
|
||||||
this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0xD654); // ZX2, ZX1, ZX0 pin config
|
this->write16_(ATM90E32_REGISTER_SAGPEAKDETCFG, 0xFF3F); // Peak Detector time ms (15:8), Sag Period ms (7:0)
|
||||||
this->write16_(ATM90E32_REGISTER_MMODE0, mmode0); // Mode Config (frequency set in main program)
|
this->write16_(ATM90E32_REGISTER_PLCONSTH, 0x0861); // PL Constant MSB (default) = 140625000
|
||||||
this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_); // PGA Gain Configuration for Current Channels
|
this->write16_(ATM90E32_REGISTER_PLCONSTL, 0xC468); // PL Constant LSB (default)
|
||||||
this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x1D4C); // All Active Startup Power Threshold - 0.02A/0.00032 = 7500
|
this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0xD654); // ZX2, ZX1, ZX0 pin config
|
||||||
this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
|
this->write16_(ATM90E32_REGISTER_MMODE0, mmode0); // Mode Config (frequency set in main program)
|
||||||
this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE); // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750
|
this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_); // PGA Gain Configuration for Current Channels
|
||||||
this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE); // Each phase Reactive Phase Threshold - 10%
|
this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x1D4C); // All Active Startup Power Threshold - 0.02A/0.00032 = 7500
|
||||||
this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[0].volt_gain_); // A Voltage rms gain
|
this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
|
||||||
this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[0].ct_gain_); // A line current gain
|
this->write16_(ATM90E32_REGISTER_SSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
|
||||||
this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[1].volt_gain_); // B Voltage rms gain
|
this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE); // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750
|
||||||
this->write16_(ATM90E32_REGISTER_IGAINB, this->phase_[1].ct_gain_); // B line current gain
|
this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE); // Each phase Reactive Phase Threshold - 10%
|
||||||
this->write16_(ATM90E32_REGISTER_UGAINC, this->phase_[2].volt_gain_); // C Voltage rms gain
|
// Setup voltage and current calibration offsets for PHASE A
|
||||||
this->write16_(ATM90E32_REGISTER_IGAINC, this->phase_[2].ct_gain_); // C line current gain
|
this->phase_[PHASEA].voltage_offset_ = calibrate_voltage_offset_phase(PHASEA);
|
||||||
this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000); // end configuration
|
this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_); // A Voltage offset
|
||||||
|
this->phase_[PHASEA].current_offset_ = calibrate_current_offset_phase(PHASEA);
|
||||||
|
this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_); // A Current offset
|
||||||
|
// Setup voltage and current gain for PHASE A
|
||||||
|
this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[PHASEA].voltage_gain_); // A Voltage rms gain
|
||||||
|
this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[PHASEA].ct_gain_); // A line current gain
|
||||||
|
// Setup voltage and current calibration offsets for PHASE B
|
||||||
|
this->phase_[PHASEB].voltage_offset_ = calibrate_voltage_offset_phase(PHASEB);
|
||||||
|
this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_); // B Voltage offset
|
||||||
|
this->phase_[PHASEB].current_offset_ = calibrate_current_offset_phase(PHASEB);
|
||||||
|
this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_); // B Current offset
|
||||||
|
// Setup voltage and current gain for PHASE B
|
||||||
|
this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[PHASEB].voltage_gain_); // B Voltage rms gain
|
||||||
|
this->write16_(ATM90E32_REGISTER_IGAINB, this->phase_[PHASEB].ct_gain_); // B line current gain
|
||||||
|
// Setup voltage and current calibration offsets for PHASE C
|
||||||
|
this->phase_[PHASEC].voltage_offset_ = calibrate_voltage_offset_phase(PHASEC);
|
||||||
|
this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_); // C Voltage offset
|
||||||
|
this->phase_[PHASEC].current_offset_ = calibrate_current_offset_phase(PHASEC);
|
||||||
|
this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_); // C Current offset
|
||||||
|
// Setup voltage and current gain for PHASE C
|
||||||
|
this->write16_(ATM90E32_REGISTER_UGAINC, this->phase_[PHASEC].voltage_gain_); // C Voltage rms gain
|
||||||
|
this->write16_(ATM90E32_REGISTER_IGAINC, this->phase_[PHASEC].ct_gain_); // C line current gain
|
||||||
|
this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000); // end configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::dump_config() {
|
void ATM90E32Component::dump_config() {
|
||||||
|
@ -133,43 +201,54 @@ void ATM90E32Component::dump_config() {
|
||||||
ESP_LOGE(TAG, "Communication with ATM90E32 failed!");
|
ESP_LOGE(TAG, "Communication with ATM90E32 failed!");
|
||||||
}
|
}
|
||||||
LOG_UPDATE_INTERVAL(this);
|
LOG_UPDATE_INTERVAL(this);
|
||||||
LOG_SENSOR(" ", "Voltage A", this->phase_[0].voltage_sensor_);
|
LOG_SENSOR(" ", "Voltage A", this->phase_[PHASEA].voltage_sensor_);
|
||||||
LOG_SENSOR(" ", "Current A", this->phase_[0].current_sensor_);
|
LOG_SENSOR(" ", "Current A", this->phase_[PHASEA].current_sensor_);
|
||||||
LOG_SENSOR(" ", "Power A", this->phase_[0].power_sensor_);
|
LOG_SENSOR(" ", "Power A", this->phase_[PHASEA].power_sensor_);
|
||||||
LOG_SENSOR(" ", "Reactive Power A", this->phase_[0].reactive_power_sensor_);
|
LOG_SENSOR(" ", "Reactive Power A", this->phase_[PHASEA].reactive_power_sensor_);
|
||||||
LOG_SENSOR(" ", "PF A", this->phase_[0].power_factor_sensor_);
|
LOG_SENSOR(" ", "PF A", this->phase_[PHASEA].power_factor_sensor_);
|
||||||
LOG_SENSOR(" ", "Active Forward Energy A", this->phase_[0].forward_active_energy_sensor_);
|
LOG_SENSOR(" ", "Active Forward Energy A", this->phase_[PHASEA].forward_active_energy_sensor_);
|
||||||
LOG_SENSOR(" ", "Active Reverse Energy A", this->phase_[0].reverse_active_energy_sensor_);
|
LOG_SENSOR(" ", "Active Reverse Energy A", this->phase_[PHASEA].reverse_active_energy_sensor_);
|
||||||
LOG_SENSOR(" ", "Voltage B", this->phase_[1].voltage_sensor_);
|
LOG_SENSOR(" ", "Harmonic Power A", this->phase_[PHASEA].harmonic_active_power_sensor_);
|
||||||
LOG_SENSOR(" ", "Current B", this->phase_[1].current_sensor_);
|
LOG_SENSOR(" ", "Phase Angle A", this->phase_[PHASEA].phase_angle_sensor_);
|
||||||
LOG_SENSOR(" ", "Power B", this->phase_[1].power_sensor_);
|
LOG_SENSOR(" ", "Peak Current A", this->phase_[PHASEA].peak_current_sensor_);
|
||||||
LOG_SENSOR(" ", "Reactive Power B", this->phase_[1].reactive_power_sensor_);
|
LOG_SENSOR(" ", "Voltage B", this->phase_[PHASEB].voltage_sensor_);
|
||||||
LOG_SENSOR(" ", "PF B", this->phase_[1].power_factor_sensor_);
|
LOG_SENSOR(" ", "Current B", this->phase_[PHASEB].current_sensor_);
|
||||||
LOG_SENSOR(" ", "Active Forward Energy B", this->phase_[1].forward_active_energy_sensor_);
|
LOG_SENSOR(" ", "Power B", this->phase_[PHASEB].power_sensor_);
|
||||||
LOG_SENSOR(" ", "Active Reverse Energy B", this->phase_[1].reverse_active_energy_sensor_);
|
LOG_SENSOR(" ", "Reactive Power B", this->phase_[PHASEB].reactive_power_sensor_);
|
||||||
LOG_SENSOR(" ", "Voltage C", this->phase_[2].voltage_sensor_);
|
LOG_SENSOR(" ", "PF B", this->phase_[PHASEB].power_factor_sensor_);
|
||||||
LOG_SENSOR(" ", "Current C", this->phase_[2].current_sensor_);
|
LOG_SENSOR(" ", "Active Forward Energy B", this->phase_[PHASEB].forward_active_energy_sensor_);
|
||||||
LOG_SENSOR(" ", "Power C", this->phase_[2].power_sensor_);
|
LOG_SENSOR(" ", "Active Reverse Energy B", this->phase_[PHASEB].reverse_active_energy_sensor_);
|
||||||
LOG_SENSOR(" ", "Reactive Power C", this->phase_[2].reactive_power_sensor_);
|
LOG_SENSOR(" ", "Harmonic Power A", this->phase_[PHASEB].harmonic_active_power_sensor_);
|
||||||
LOG_SENSOR(" ", "PF C", this->phase_[2].power_factor_sensor_);
|
LOG_SENSOR(" ", "Phase Angle A", this->phase_[PHASEB].phase_angle_sensor_);
|
||||||
LOG_SENSOR(" ", "Active Forward Energy C", this->phase_[2].forward_active_energy_sensor_);
|
LOG_SENSOR(" ", "Peak Current A", this->phase_[PHASEB].peak_current_sensor_);
|
||||||
LOG_SENSOR(" ", "Active Reverse Energy C", this->phase_[2].reverse_active_energy_sensor_);
|
LOG_SENSOR(" ", "Voltage C", this->phase_[PHASEC].voltage_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Current C", this->phase_[PHASEC].current_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Power C", this->phase_[PHASEC].power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Reactive Power C", this->phase_[PHASEC].reactive_power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "PF C", this->phase_[PHASEC].power_factor_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Active Forward Energy C", this->phase_[PHASEC].forward_active_energy_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Active Reverse Energy C", this->phase_[PHASEC].reverse_active_energy_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Harmonic Power A", this->phase_[PHASEC].harmonic_active_power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Phase Angle A", this->phase_[PHASEC].phase_angle_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Peak Current A", this->phase_[PHASEC].peak_current_sensor_);
|
||||||
LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
|
LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
|
||||||
LOG_SENSOR(" ", "Chip Temp", this->chip_temperature_sensor_);
|
LOG_SENSOR(" ", "Chip Temp", this->chip_temperature_sensor_);
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_setup_priority() const { return setup_priority::DATA; }
|
|
||||||
|
|
||||||
|
float ATM90E32Component::get_setup_priority() const { return setup_priority::IO; }
|
||||||
|
|
||||||
|
// R/C registers can conly be cleared after the LastSPIData register is updated (register 78H)
|
||||||
|
// Peakdetect period: 05H. Bit 15:8 are PeakDet_period in ms. 7:0 are Sag_period
|
||||||
|
// Default is 143FH (20ms, 63ms)
|
||||||
uint16_t ATM90E32Component::read16_(uint16_t a_register) {
|
uint16_t ATM90E32Component::read16_(uint16_t a_register) {
|
||||||
uint8_t addrh = (1 << 7) | ((a_register >> 8) & 0x03);
|
uint8_t addrh = (1 << 7) | ((a_register >> 8) & 0x03);
|
||||||
uint8_t addrl = (a_register & 0xFF);
|
uint8_t addrl = (a_register & 0xFF);
|
||||||
uint8_t data[2];
|
uint8_t data[2];
|
||||||
uint16_t output;
|
uint16_t output;
|
||||||
|
|
||||||
this->enable();
|
this->enable();
|
||||||
delayMicroseconds(10);
|
delay_microseconds_safe(10);
|
||||||
this->write_byte(addrh);
|
this->write_byte(addrh);
|
||||||
this->write_byte(addrl);
|
this->write_byte(addrl);
|
||||||
delayMicroseconds(4);
|
|
||||||
this->read_array(data, 2);
|
this->read_array(data, 2);
|
||||||
this->disable();
|
this->disable();
|
||||||
|
|
||||||
|
@ -179,9 +258,9 @@ uint16_t ATM90E32Component::read16_(uint16_t a_register) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int ATM90E32Component::read32_(uint16_t addr_h, uint16_t addr_l) {
|
int ATM90E32Component::read32_(uint16_t addr_h, uint16_t addr_l) {
|
||||||
uint16_t val_h = this->read16_(addr_h);
|
const uint16_t val_h = this->read16_(addr_h);
|
||||||
uint16_t val_l = this->read16_(addr_l);
|
const uint16_t val_l = this->read16_(addr_l);
|
||||||
int32_t val = (val_h << 16) | val_l;
|
const int32_t val = (val_h << 16) | val_l;
|
||||||
|
|
||||||
ESP_LOGVV(TAG,
|
ESP_LOGVV(TAG,
|
||||||
"read32_ addr_h 0x%04" PRIX16 " val_h 0x%04" PRIX16 " addr_l 0x%04" PRIX16 " val_l 0x%04" PRIX16
|
"read32_ addr_h 0x%04" PRIX16 " val_h 0x%04" PRIX16 " addr_l 0x%04" PRIX16 " val_l 0x%04" PRIX16
|
||||||
|
@ -192,141 +271,174 @@ int ATM90E32Component::read32_(uint16_t addr_h, uint16_t addr_l) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::write16_(uint16_t a_register, uint16_t val) {
|
void ATM90E32Component::write16_(uint16_t a_register, uint16_t val) {
|
||||||
uint8_t addrh = (a_register >> 8) & 0x03;
|
|
||||||
uint8_t addrl = (a_register & 0xFF);
|
|
||||||
|
|
||||||
ESP_LOGVV(TAG, "write16_ 0x%04" PRIX16 " val 0x%04" PRIX16, a_register, val);
|
ESP_LOGVV(TAG, "write16_ 0x%04" PRIX16 " val 0x%04" PRIX16, a_register, val);
|
||||||
this->enable();
|
this->enable();
|
||||||
delayMicroseconds(10);
|
this->write_byte16(a_register);
|
||||||
this->write_byte(addrh);
|
this->write_byte16(val);
|
||||||
this->write_byte(addrl);
|
|
||||||
delayMicroseconds(4);
|
|
||||||
this->write_byte((val >> 8) & 0xff);
|
|
||||||
this->write_byte(val & 0xFF);
|
|
||||||
this->disable();
|
this->disable();
|
||||||
|
if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != val)
|
||||||
|
ESP_LOGW(TAG, "SPI write error 0x%04X val 0x%04X", a_register, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
float ATM90E32Component::get_line_voltage_a_() {
|
float ATM90E32Component::get_local_phase_voltage_(uint8_t phase) { return this->phase_[phase].voltage_; }
|
||||||
uint16_t voltage = this->read16_(ATM90E32_REGISTER_URMSA);
|
|
||||||
|
float ATM90E32Component::get_local_phase_current_(uint8_t phase) { return this->phase_[phase].current_; }
|
||||||
|
|
||||||
|
float ATM90E32Component::get_local_phase_active_power_(uint8_t phase) { return this->phase_[phase].active_power_; }
|
||||||
|
|
||||||
|
float ATM90E32Component::get_local_phase_reactive_power_(uint8_t phase) { return this->phase_[phase].reactive_power_; }
|
||||||
|
|
||||||
|
float ATM90E32Component::get_local_phase_power_factor_(uint8_t phase) { return this->phase_[phase].power_factor_; }
|
||||||
|
|
||||||
|
float ATM90E32Component::get_local_phase_forward_active_energy_(uint8_t phase) {
|
||||||
|
return this->phase_[phase].forward_active_energy_;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E32Component::get_local_phase_reverse_active_energy_(uint8_t phase) {
|
||||||
|
return this->phase_[phase].reverse_active_energy_;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E32Component::get_local_phase_angle_(uint8_t phase) { return this->phase_[phase].phase_angle_; }
|
||||||
|
|
||||||
|
float ATM90E32Component::get_local_phase_harmonic_active_power_(uint8_t phase) {
|
||||||
|
return this->phase_[phase].harmonic_active_power_;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E32Component::get_local_phase_peak_current_(uint8_t phase) { return this->phase_[phase].peak_current_; }
|
||||||
|
|
||||||
|
float ATM90E32Component::get_phase_voltage_(uint8_t phase) {
|
||||||
|
const uint16_t voltage = this->read16_(ATM90E32_REGISTER_URMS + phase);
|
||||||
|
if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != voltage)
|
||||||
|
ESP_LOGW(TAG, "SPI URMS voltage register read error.");
|
||||||
return (float) voltage / 100;
|
return (float) voltage / 100;
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_line_voltage_b_() {
|
|
||||||
uint16_t voltage = this->read16_(ATM90E32_REGISTER_URMSB);
|
float ATM90E32Component::get_phase_voltage_avg_(uint8_t phase) {
|
||||||
return (float) voltage / 100;
|
const uint8_t reads = 10;
|
||||||
|
uint32_t accumulation = 0;
|
||||||
|
uint16_t voltage = 0;
|
||||||
|
for (uint8_t i = 0; i < reads; i++) {
|
||||||
|
voltage = this->read16_(ATM90E32_REGISTER_URMS + phase);
|
||||||
|
if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != voltage)
|
||||||
|
ESP_LOGW(TAG, "SPI URMS voltage register read error.");
|
||||||
|
accumulation += voltage;
|
||||||
|
}
|
||||||
|
voltage = accumulation / reads;
|
||||||
|
this->phase_[phase].voltage_ = (float) voltage / 100;
|
||||||
|
return this->phase_[phase].voltage_;
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_line_voltage_c_() {
|
|
||||||
uint16_t voltage = this->read16_(ATM90E32_REGISTER_URMSC);
|
float ATM90E32Component::get_phase_current_avg_(uint8_t phase) {
|
||||||
return (float) voltage / 100;
|
const uint8_t reads = 10;
|
||||||
|
uint32_t accumulation = 0;
|
||||||
|
uint16_t current = 0;
|
||||||
|
for (uint8_t i = 0; i < reads; i++) {
|
||||||
|
current = this->read16_(ATM90E32_REGISTER_IRMS + phase);
|
||||||
|
if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != current)
|
||||||
|
ESP_LOGW(TAG, "SPI IRMS current register read error.");
|
||||||
|
accumulation += current;
|
||||||
|
}
|
||||||
|
current = accumulation / reads;
|
||||||
|
this->phase_[phase].current_ = (float) current / 1000;
|
||||||
|
return this->phase_[phase].current_;
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_line_current_a_() {
|
|
||||||
uint16_t current = this->read16_(ATM90E32_REGISTER_IRMSA);
|
float ATM90E32Component::get_phase_current_(uint8_t phase) {
|
||||||
|
const uint16_t current = this->read16_(ATM90E32_REGISTER_IRMS + phase);
|
||||||
|
if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != current)
|
||||||
|
ESP_LOGW(TAG, "SPI IRMS current register read error.");
|
||||||
return (float) current / 1000;
|
return (float) current / 1000;
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_line_current_b_() {
|
|
||||||
uint16_t current = this->read16_(ATM90E32_REGISTER_IRMSB);
|
float ATM90E32Component::get_phase_active_power_(uint8_t phase) {
|
||||||
return (float) current / 1000;
|
const int val = this->read32_(ATM90E32_REGISTER_PMEAN + phase, ATM90E32_REGISTER_PMEANLSB + phase);
|
||||||
}
|
|
||||||
float ATM90E32Component::get_line_current_c_() {
|
|
||||||
uint16_t current = this->read16_(ATM90E32_REGISTER_IRMSC);
|
|
||||||
return (float) current / 1000;
|
|
||||||
}
|
|
||||||
float ATM90E32Component::get_active_power_a_() {
|
|
||||||
int val = this->read32_(ATM90E32_REGISTER_PMEANA, ATM90E32_REGISTER_PMEANALSB);
|
|
||||||
return val * 0.00032f;
|
return val * 0.00032f;
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_active_power_b_() {
|
|
||||||
int val = this->read32_(ATM90E32_REGISTER_PMEANB, ATM90E32_REGISTER_PMEANBLSB);
|
float ATM90E32Component::get_phase_reactive_power_(uint8_t phase) {
|
||||||
|
const int val = this->read32_(ATM90E32_REGISTER_QMEAN + phase, ATM90E32_REGISTER_QMEANLSB + phase);
|
||||||
return val * 0.00032f;
|
return val * 0.00032f;
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_active_power_c_() {
|
|
||||||
int val = this->read32_(ATM90E32_REGISTER_PMEANC, ATM90E32_REGISTER_PMEANCLSB);
|
float ATM90E32Component::get_phase_power_factor_(uint8_t phase) {
|
||||||
|
const int16_t powerfactor = this->read16_(ATM90E32_REGISTER_PFMEAN + phase);
|
||||||
|
if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != powerfactor)
|
||||||
|
ESP_LOGW(TAG, "SPI power factor read error.");
|
||||||
|
return (float) powerfactor / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E32Component::get_phase_forward_active_energy_(uint8_t phase) {
|
||||||
|
const uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGY + phase);
|
||||||
|
if ((UINT32_MAX - this->phase_[phase].cumulative_forward_active_energy_) > val) {
|
||||||
|
this->phase_[phase].cumulative_forward_active_energy_ += val;
|
||||||
|
} else {
|
||||||
|
this->phase_[phase].cumulative_forward_active_energy_ = val;
|
||||||
|
}
|
||||||
|
return ((float) this->phase_[phase].cumulative_forward_active_energy_ * 10 / 3200);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E32Component::get_phase_reverse_active_energy_(uint8_t phase) {
|
||||||
|
const uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGY);
|
||||||
|
if (UINT32_MAX - this->phase_[phase].cumulative_reverse_active_energy_ > val) {
|
||||||
|
this->phase_[phase].cumulative_reverse_active_energy_ += val;
|
||||||
|
} else {
|
||||||
|
this->phase_[phase].cumulative_reverse_active_energy_ = val;
|
||||||
|
}
|
||||||
|
return ((float) this->phase_[phase].cumulative_reverse_active_energy_ * 10 / 3200);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E32Component::get_phase_harmonic_active_power_(uint8_t phase) {
|
||||||
|
int val = this->read32_(ATM90E32_REGISTER_PMEANH + phase, ATM90E32_REGISTER_PMEANHLSB + phase);
|
||||||
return val * 0.00032f;
|
return val * 0.00032f;
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_reactive_power_a_() {
|
|
||||||
int val = this->read32_(ATM90E32_REGISTER_QMEANA, ATM90E32_REGISTER_QMEANALSB);
|
float ATM90E32Component::get_phase_angle_(uint8_t phase) {
|
||||||
return val * 0.00032f;
|
uint16_t val = this->read16_(ATM90E32_REGISTER_PANGLE + phase) / 10.0;
|
||||||
|
return (float) (val > 180) ? val - 360.0 : val;
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_reactive_power_b_() {
|
|
||||||
int val = this->read32_(ATM90E32_REGISTER_QMEANB, ATM90E32_REGISTER_QMEANBLSB);
|
float ATM90E32Component::get_phase_peak_current_(uint8_t phase) {
|
||||||
return val * 0.00032f;
|
int16_t val = (float) this->read16_(ATM90E32_REGISTER_IPEAK + phase);
|
||||||
}
|
if (!this->peak_current_signed_)
|
||||||
float ATM90E32Component::get_reactive_power_c_() {
|
val = abs(val);
|
||||||
int val = this->read32_(ATM90E32_REGISTER_QMEANC, ATM90E32_REGISTER_QMEANCLSB);
|
// phase register * phase current gain value / 1000 * 2^13
|
||||||
return val * 0.00032f;
|
return (float) (val * this->phase_[phase].ct_gain_ / 8192000.0);
|
||||||
}
|
|
||||||
float ATM90E32Component::get_power_factor_a_() {
|
|
||||||
int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANA);
|
|
||||||
return (float) pf / 1000;
|
|
||||||
}
|
|
||||||
float ATM90E32Component::get_power_factor_b_() {
|
|
||||||
int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANB);
|
|
||||||
return (float) pf / 1000;
|
|
||||||
}
|
|
||||||
float ATM90E32Component::get_power_factor_c_() {
|
|
||||||
int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANC);
|
|
||||||
return (float) pf / 1000;
|
|
||||||
}
|
|
||||||
float ATM90E32Component::get_forward_active_energy_a_() {
|
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYA);
|
|
||||||
if ((UINT32_MAX - this->phase_[0].cumulative_forward_active_energy_) > val) {
|
|
||||||
this->phase_[0].cumulative_forward_active_energy_ += val;
|
|
||||||
} else {
|
|
||||||
this->phase_[0].cumulative_forward_active_energy_ = val;
|
|
||||||
}
|
|
||||||
return ((float) this->phase_[0].cumulative_forward_active_energy_ * 10 / 3200);
|
|
||||||
}
|
|
||||||
float ATM90E32Component::get_forward_active_energy_b_() {
|
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYB);
|
|
||||||
if (UINT32_MAX - this->phase_[1].cumulative_forward_active_energy_ > val) {
|
|
||||||
this->phase_[1].cumulative_forward_active_energy_ += val;
|
|
||||||
} else {
|
|
||||||
this->phase_[1].cumulative_forward_active_energy_ = val;
|
|
||||||
}
|
|
||||||
return ((float) this->phase_[1].cumulative_forward_active_energy_ * 10 / 3200);
|
|
||||||
}
|
|
||||||
float ATM90E32Component::get_forward_active_energy_c_() {
|
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYC);
|
|
||||||
if (UINT32_MAX - this->phase_[2].cumulative_forward_active_energy_ > val) {
|
|
||||||
this->phase_[2].cumulative_forward_active_energy_ += val;
|
|
||||||
} else {
|
|
||||||
this->phase_[2].cumulative_forward_active_energy_ = val;
|
|
||||||
}
|
|
||||||
return ((float) this->phase_[2].cumulative_forward_active_energy_ * 10 / 3200);
|
|
||||||
}
|
|
||||||
float ATM90E32Component::get_reverse_active_energy_a_() {
|
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYA);
|
|
||||||
if (UINT32_MAX - this->phase_[0].cumulative_reverse_active_energy_ > val) {
|
|
||||||
this->phase_[0].cumulative_reverse_active_energy_ += val;
|
|
||||||
} else {
|
|
||||||
this->phase_[0].cumulative_reverse_active_energy_ = val;
|
|
||||||
}
|
|
||||||
return ((float) this->phase_[0].cumulative_reverse_active_energy_ * 10 / 3200);
|
|
||||||
}
|
|
||||||
float ATM90E32Component::get_reverse_active_energy_b_() {
|
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYB);
|
|
||||||
if (UINT32_MAX - this->phase_[1].cumulative_reverse_active_energy_ > val) {
|
|
||||||
this->phase_[1].cumulative_reverse_active_energy_ += val;
|
|
||||||
} else {
|
|
||||||
this->phase_[1].cumulative_reverse_active_energy_ = val;
|
|
||||||
}
|
|
||||||
return ((float) this->phase_[1].cumulative_reverse_active_energy_ * 10 / 3200);
|
|
||||||
}
|
|
||||||
float ATM90E32Component::get_reverse_active_energy_c_() {
|
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYC);
|
|
||||||
if (UINT32_MAX - this->phase_[2].cumulative_reverse_active_energy_ > val) {
|
|
||||||
this->phase_[2].cumulative_reverse_active_energy_ += val;
|
|
||||||
} else {
|
|
||||||
this->phase_[2].cumulative_reverse_active_energy_ = val;
|
|
||||||
}
|
|
||||||
return ((float) this->phase_[2].cumulative_reverse_active_energy_ * 10 / 3200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float ATM90E32Component::get_frequency_() {
|
float ATM90E32Component::get_frequency_() {
|
||||||
uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);
|
const uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);
|
||||||
return (float) freq / 100;
|
return (float) freq / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ATM90E32Component::get_chip_temperature_() {
|
float ATM90E32Component::get_chip_temperature_() {
|
||||||
uint16_t ctemp = this->read16_(ATM90E32_REGISTER_TEMP);
|
const uint16_t ctemp = this->read16_(ATM90E32_REGISTER_TEMP);
|
||||||
return (float) ctemp;
|
return (float) ctemp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t ATM90E32Component::calibrate_voltage_offset_phase(uint8_t phase) {
|
||||||
|
const uint8_t num_reads = 5;
|
||||||
|
uint64_t total_value = 0;
|
||||||
|
for (int i = 0; i < num_reads; ++i) {
|
||||||
|
const uint32_t measurement_value = read32_(ATM90E32_REGISTER_URMS + phase, ATM90E32_REGISTER_URMSLSB + phase);
|
||||||
|
total_value += measurement_value;
|
||||||
|
}
|
||||||
|
const uint32_t average_value = total_value / num_reads;
|
||||||
|
const uint32_t shifted_value = average_value >> 7;
|
||||||
|
const uint32_t voltage_offset = ~shifted_value + 1;
|
||||||
|
return voltage_offset & 0xFFFF; // Take the lower 16 bits
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ATM90E32Component::calibrate_current_offset_phase(uint8_t phase) {
|
||||||
|
const uint8_t num_reads = 5;
|
||||||
|
uint64_t total_value = 0;
|
||||||
|
for (int i = 0; i < num_reads; ++i) {
|
||||||
|
const uint32_t measurement_value = read32_(ATM90E32_REGISTER_IRMS + phase, ATM90E32_REGISTER_IRMSLSB + phase);
|
||||||
|
total_value += measurement_value;
|
||||||
|
}
|
||||||
|
const uint32_t average_value = total_value / num_reads;
|
||||||
|
const uint32_t current_offset = ~average_value + 1;
|
||||||
|
return current_offset & 0xFFFF; // Take the lower 16 bits
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace atm90e32
|
} // namespace atm90e32
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -3,14 +3,19 @@
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/spi/spi.h"
|
#include "esphome/components/spi/spi.h"
|
||||||
|
#include "atm90e32_reg.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace atm90e32 {
|
namespace atm90e32 {
|
||||||
|
|
||||||
class ATM90E32Component : public PollingComponent,
|
class ATM90E32Component : public PollingComponent,
|
||||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH,
|
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH,
|
||||||
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_200KHZ> {
|
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_1MHZ> {
|
||||||
public:
|
public:
|
||||||
|
static const uint8_t PHASEA = 0;
|
||||||
|
static const uint8_t PHASEB = 1;
|
||||||
|
static const uint8_t PHASEC = 2;
|
||||||
|
void loop() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
@ -20,6 +25,7 @@ class ATM90E32Component : public PollingComponent,
|
||||||
void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; }
|
void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; }
|
||||||
void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; }
|
void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; }
|
||||||
void set_reactive_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].reactive_power_sensor_ = obj; }
|
void set_reactive_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].reactive_power_sensor_ = obj; }
|
||||||
|
void set_apparent_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].apparent_power_sensor_ = obj; }
|
||||||
void set_forward_active_energy_sensor(int phase, sensor::Sensor *obj) {
|
void set_forward_active_energy_sensor(int phase, sensor::Sensor *obj) {
|
||||||
this->phase_[phase].forward_active_energy_sensor_ = obj;
|
this->phase_[phase].forward_active_energy_sensor_ = obj;
|
||||||
}
|
}
|
||||||
|
@ -27,64 +33,94 @@ class ATM90E32Component : public PollingComponent,
|
||||||
this->phase_[phase].reverse_active_energy_sensor_ = obj;
|
this->phase_[phase].reverse_active_energy_sensor_ = obj;
|
||||||
}
|
}
|
||||||
void set_power_factor_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_factor_sensor_ = obj; }
|
void set_power_factor_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_factor_sensor_ = obj; }
|
||||||
void set_volt_gain(int phase, uint16_t gain) { this->phase_[phase].volt_gain_ = gain; }
|
void set_phase_angle_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].phase_angle_sensor_ = obj; }
|
||||||
|
void set_harmonic_active_power_sensor(int phase, sensor::Sensor *obj) {
|
||||||
|
this->phase_[phase].harmonic_active_power_sensor_ = obj;
|
||||||
|
}
|
||||||
|
void set_peak_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].peak_current_sensor_ = obj; }
|
||||||
|
void set_volt_gain(int phase, uint16_t gain) { this->phase_[phase].voltage_gain_ = gain; }
|
||||||
void set_ct_gain(int phase, uint16_t gain) { this->phase_[phase].ct_gain_ = gain; }
|
void set_ct_gain(int phase, uint16_t gain) { this->phase_[phase].ct_gain_ = gain; }
|
||||||
|
|
||||||
void set_freq_sensor(sensor::Sensor *freq_sensor) { freq_sensor_ = freq_sensor; }
|
void set_freq_sensor(sensor::Sensor *freq_sensor) { freq_sensor_ = freq_sensor; }
|
||||||
|
void set_peak_current_signed(bool flag) { peak_current_signed_ = flag; }
|
||||||
void set_chip_temperature_sensor(sensor::Sensor *chip_temperature_sensor) {
|
void set_chip_temperature_sensor(sensor::Sensor *chip_temperature_sensor) {
|
||||||
chip_temperature_sensor_ = chip_temperature_sensor;
|
chip_temperature_sensor_ = chip_temperature_sensor;
|
||||||
}
|
}
|
||||||
void set_line_freq(int freq) { line_freq_ = freq; }
|
void set_line_freq(int freq) { line_freq_ = freq; }
|
||||||
void set_current_phases(int phases) { current_phases_ = phases; }
|
void set_current_phases(int phases) { current_phases_ = phases; }
|
||||||
void set_pga_gain(uint16_t gain) { pga_gain_ = gain; }
|
void set_pga_gain(uint16_t gain) { pga_gain_ = gain; }
|
||||||
|
uint16_t calibrate_voltage_offset_phase(uint8_t /*phase*/);
|
||||||
|
uint16_t calibrate_current_offset_phase(uint8_t /*phase*/);
|
||||||
|
|
||||||
|
int32_t last_periodic_millis = millis();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint16_t read16_(uint16_t a_register);
|
uint16_t read16_(uint16_t a_register);
|
||||||
int read32_(uint16_t addr_h, uint16_t addr_l);
|
int read32_(uint16_t addr_h, uint16_t addr_l);
|
||||||
void write16_(uint16_t a_register, uint16_t val);
|
void write16_(uint16_t a_register, uint16_t val);
|
||||||
|
float get_local_phase_voltage_(uint8_t /*phase*/);
|
||||||
float get_line_voltage_a_();
|
float get_local_phase_current_(uint8_t /*phase*/);
|
||||||
float get_line_voltage_b_();
|
float get_local_phase_active_power_(uint8_t /*phase*/);
|
||||||
float get_line_voltage_c_();
|
float get_local_phase_reactive_power_(uint8_t /*phase*/);
|
||||||
float get_line_current_a_();
|
float get_local_phase_power_factor_(uint8_t /*phase*/);
|
||||||
float get_line_current_b_();
|
float get_local_phase_forward_active_energy_(uint8_t /*phase*/);
|
||||||
float get_line_current_c_();
|
float get_local_phase_reverse_active_energy_(uint8_t /*phase*/);
|
||||||
float get_active_power_a_();
|
float get_local_phase_angle_(uint8_t /*phase*/);
|
||||||
float get_active_power_b_();
|
float get_local_phase_harmonic_active_power_(uint8_t /*phase*/);
|
||||||
float get_active_power_c_();
|
float get_local_phase_peak_current_(uint8_t /*phase*/);
|
||||||
float get_reactive_power_a_();
|
float get_phase_voltage_(uint8_t /*phase*/);
|
||||||
float get_reactive_power_b_();
|
float get_phase_voltage_avg_(uint8_t /*phase*/);
|
||||||
float get_reactive_power_c_();
|
float get_phase_current_(uint8_t /*phase*/);
|
||||||
float get_power_factor_a_();
|
float get_phase_current_avg_(uint8_t /*phase*/);
|
||||||
float get_power_factor_b_();
|
float get_phase_active_power_(uint8_t /*phase*/);
|
||||||
float get_power_factor_c_();
|
float get_phase_reactive_power_(uint8_t /*phase*/);
|
||||||
float get_forward_active_energy_a_();
|
float get_phase_power_factor_(uint8_t /*phase*/);
|
||||||
float get_forward_active_energy_b_();
|
float get_phase_forward_active_energy_(uint8_t /*phase*/);
|
||||||
float get_forward_active_energy_c_();
|
float get_phase_reverse_active_energy_(uint8_t /*phase*/);
|
||||||
float get_reverse_active_energy_a_();
|
float get_phase_angle_(uint8_t /*phase*/);
|
||||||
float get_reverse_active_energy_b_();
|
float get_phase_harmonic_active_power_(uint8_t /*phase*/);
|
||||||
float get_reverse_active_energy_c_();
|
float get_phase_peak_current_(uint8_t /*phase*/);
|
||||||
float get_frequency_();
|
float get_frequency_();
|
||||||
float get_chip_temperature_();
|
float get_chip_temperature_();
|
||||||
|
bool get_publish_interval_flag_() { return publish_interval_flag_; };
|
||||||
|
void set_publish_interval_flag_(bool flag) { publish_interval_flag_ = flag; };
|
||||||
|
|
||||||
struct ATM90E32Phase {
|
struct ATM90E32Phase {
|
||||||
uint16_t volt_gain_{7305};
|
uint16_t voltage_gain_{7305};
|
||||||
uint16_t ct_gain_{27961};
|
uint16_t ct_gain_{27961};
|
||||||
|
uint16_t voltage_offset_{0};
|
||||||
|
uint16_t current_offset_{0};
|
||||||
|
float voltage_{0};
|
||||||
|
float current_{0};
|
||||||
|
float active_power_{0};
|
||||||
|
float reactive_power_{0};
|
||||||
|
float power_factor_{0};
|
||||||
|
float forward_active_energy_{0};
|
||||||
|
float reverse_active_energy_{0};
|
||||||
|
float phase_angle_{0};
|
||||||
|
float harmonic_active_power_{0};
|
||||||
|
float peak_current_{0};
|
||||||
sensor::Sensor *voltage_sensor_{nullptr};
|
sensor::Sensor *voltage_sensor_{nullptr};
|
||||||
sensor::Sensor *current_sensor_{nullptr};
|
sensor::Sensor *current_sensor_{nullptr};
|
||||||
sensor::Sensor *power_sensor_{nullptr};
|
sensor::Sensor *power_sensor_{nullptr};
|
||||||
sensor::Sensor *reactive_power_sensor_{nullptr};
|
sensor::Sensor *reactive_power_sensor_{nullptr};
|
||||||
|
sensor::Sensor *apparent_power_sensor_{nullptr};
|
||||||
sensor::Sensor *power_factor_sensor_{nullptr};
|
sensor::Sensor *power_factor_sensor_{nullptr};
|
||||||
sensor::Sensor *forward_active_energy_sensor_{nullptr};
|
sensor::Sensor *forward_active_energy_sensor_{nullptr};
|
||||||
sensor::Sensor *reverse_active_energy_sensor_{nullptr};
|
sensor::Sensor *reverse_active_energy_sensor_{nullptr};
|
||||||
|
sensor::Sensor *phase_angle_sensor_{nullptr};
|
||||||
|
sensor::Sensor *harmonic_active_power_sensor_{nullptr};
|
||||||
|
sensor::Sensor *peak_current_sensor_{nullptr};
|
||||||
uint32_t cumulative_forward_active_energy_{0};
|
uint32_t cumulative_forward_active_energy_{0};
|
||||||
uint32_t cumulative_reverse_active_energy_{0};
|
uint32_t cumulative_reverse_active_energy_{0};
|
||||||
} phase_[3];
|
} phase_[3];
|
||||||
|
|
||||||
sensor::Sensor *freq_sensor_{nullptr};
|
sensor::Sensor *freq_sensor_{nullptr};
|
||||||
sensor::Sensor *chip_temperature_sensor_{nullptr};
|
sensor::Sensor *chip_temperature_sensor_{nullptr};
|
||||||
uint16_t pga_gain_{0x15};
|
uint16_t pga_gain_{0x15};
|
||||||
int line_freq_{60};
|
int line_freq_{60};
|
||||||
int current_phases_{3};
|
int current_phases_{3};
|
||||||
|
bool publish_interval_flag_{true};
|
||||||
|
bool peak_current_signed_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace atm90e32
|
} // namespace atm90e32
|
||||||
|
|
|
@ -131,10 +131,12 @@ static const uint16_t ATM90E32_REGISTER_IOFFSETN = 0x6E; // N Current Offset
|
||||||
|
|
||||||
/* ENERGY REGISTERS */
|
/* ENERGY REGISTERS */
|
||||||
static const uint16_t ATM90E32_REGISTER_APENERGYT = 0x80; // Total Forward Active
|
static const uint16_t ATM90E32_REGISTER_APENERGYT = 0x80; // Total Forward Active
|
||||||
|
static const uint16_t ATM90E32_REGISTER_APENERGY = 0x81; // Forward Active Reg Base
|
||||||
static const uint16_t ATM90E32_REGISTER_APENERGYA = 0x81; // A Forward Active
|
static const uint16_t ATM90E32_REGISTER_APENERGYA = 0x81; // A Forward Active
|
||||||
static const uint16_t ATM90E32_REGISTER_APENERGYB = 0x82; // B Forward Active
|
static const uint16_t ATM90E32_REGISTER_APENERGYB = 0x82; // B Forward Active
|
||||||
static const uint16_t ATM90E32_REGISTER_APENERGYC = 0x83; // C Forward Active
|
static const uint16_t ATM90E32_REGISTER_APENERGYC = 0x83; // C Forward Active
|
||||||
static const uint16_t ATM90E32_REGISTER_ANENERGYT = 0x84; // Total Reverse Active
|
static const uint16_t ATM90E32_REGISTER_ANENERGYT = 0x84; // Total Reverse Active
|
||||||
|
static const uint16_t ATM90E32_REGISTER_ANENERGY = 0x85; // Reverse Active Reg Base
|
||||||
static const uint16_t ATM90E32_REGISTER_ANENERGYA = 0x85; // A Reverse Active
|
static const uint16_t ATM90E32_REGISTER_ANENERGYA = 0x85; // A Reverse Active
|
||||||
static const uint16_t ATM90E32_REGISTER_ANENERGYB = 0x86; // B Reverse Active
|
static const uint16_t ATM90E32_REGISTER_ANENERGYB = 0x86; // B Reverse Active
|
||||||
static const uint16_t ATM90E32_REGISTER_ANENERGYC = 0x87; // C Reverse Active
|
static const uint16_t ATM90E32_REGISTER_ANENERGYC = 0x87; // C Reverse Active
|
||||||
|
@ -172,10 +174,12 @@ static const uint16_t ATM90E32_REGISTER_ANENERGYCH = 0xAF; // C Reverse Harm. E
|
||||||
|
|
||||||
/* POWER & P.F. REGISTERS */
|
/* POWER & P.F. REGISTERS */
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANT = 0xB0; // Total Mean Power (P)
|
static const uint16_t ATM90E32_REGISTER_PMEANT = 0xB0; // Total Mean Power (P)
|
||||||
|
static const uint16_t ATM90E32_REGISTER_PMEAN = 0xB1; // Mean Power Reg Base (P)
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANA = 0xB1; // A Mean Power (P)
|
static const uint16_t ATM90E32_REGISTER_PMEANA = 0xB1; // A Mean Power (P)
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANB = 0xB2; // B Mean Power (P)
|
static const uint16_t ATM90E32_REGISTER_PMEANB = 0xB2; // B Mean Power (P)
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANC = 0xB3; // C Mean Power (P)
|
static const uint16_t ATM90E32_REGISTER_PMEANC = 0xB3; // C Mean Power (P)
|
||||||
static const uint16_t ATM90E32_REGISTER_QMEANT = 0xB4; // Total Mean Power (Q)
|
static const uint16_t ATM90E32_REGISTER_QMEANT = 0xB4; // Total Mean Power (Q)
|
||||||
|
static const uint16_t ATM90E32_REGISTER_QMEAN = 0xB5; // Mean Power Reg Base (Q)
|
||||||
static const uint16_t ATM90E32_REGISTER_QMEANA = 0xB5; // A Mean Power (Q)
|
static const uint16_t ATM90E32_REGISTER_QMEANA = 0xB5; // A Mean Power (Q)
|
||||||
static const uint16_t ATM90E32_REGISTER_QMEANB = 0xB6; // B Mean Power (Q)
|
static const uint16_t ATM90E32_REGISTER_QMEANB = 0xB6; // B Mean Power (Q)
|
||||||
static const uint16_t ATM90E32_REGISTER_QMEANC = 0xB7; // C Mean Power (Q)
|
static const uint16_t ATM90E32_REGISTER_QMEANC = 0xB7; // C Mean Power (Q)
|
||||||
|
@ -184,15 +188,18 @@ static const uint16_t ATM90E32_REGISTER_SMEANA = 0xB9; // A Mean Power (S)
|
||||||
static const uint16_t ATM90E32_REGISTER_SMEANB = 0xBA; // B Mean Power (S)
|
static const uint16_t ATM90E32_REGISTER_SMEANB = 0xBA; // B Mean Power (S)
|
||||||
static const uint16_t ATM90E32_REGISTER_SMEANC = 0xBB; // C Mean Power (S)
|
static const uint16_t ATM90E32_REGISTER_SMEANC = 0xBB; // C Mean Power (S)
|
||||||
static const uint16_t ATM90E32_REGISTER_PFMEANT = 0xBC; // Mean Power Factor
|
static const uint16_t ATM90E32_REGISTER_PFMEANT = 0xBC; // Mean Power Factor
|
||||||
|
static const uint16_t ATM90E32_REGISTER_PFMEAN = 0xBD; // Power Factor Reg Base
|
||||||
static const uint16_t ATM90E32_REGISTER_PFMEANA = 0xBD; // A Power Factor
|
static const uint16_t ATM90E32_REGISTER_PFMEANA = 0xBD; // A Power Factor
|
||||||
static const uint16_t ATM90E32_REGISTER_PFMEANB = 0xBE; // B Power Factor
|
static const uint16_t ATM90E32_REGISTER_PFMEANB = 0xBE; // B Power Factor
|
||||||
static const uint16_t ATM90E32_REGISTER_PFMEANC = 0xBF; // C Power Factor
|
static const uint16_t ATM90E32_REGISTER_PFMEANC = 0xBF; // C Power Factor
|
||||||
|
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANTLSB = 0xC0; // Lower Word (Tot. Act. Power)
|
static const uint16_t ATM90E32_REGISTER_PMEANTLSB = 0xC0; // Lower Word (Tot. Act. Power)
|
||||||
|
static const uint16_t ATM90E32_REGISTER_PMEANLSB = 0xC1; // Lower Word Reg Base (Active Power)
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANALSB = 0xC1; // Lower Word (A Act. Power)
|
static const uint16_t ATM90E32_REGISTER_PMEANALSB = 0xC1; // Lower Word (A Act. Power)
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANBLSB = 0xC2; // Lower Word (B Act. Power)
|
static const uint16_t ATM90E32_REGISTER_PMEANBLSB = 0xC2; // Lower Word (B Act. Power)
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANCLSB = 0xC3; // Lower Word (C Act. Power)
|
static const uint16_t ATM90E32_REGISTER_PMEANCLSB = 0xC3; // Lower Word (C Act. Power)
|
||||||
static const uint16_t ATM90E32_REGISTER_QMEANTLSB = 0xC4; // Lower Word (Tot. React. Power)
|
static const uint16_t ATM90E32_REGISTER_QMEANTLSB = 0xC4; // Lower Word (Tot. React. Power)
|
||||||
|
static const uint16_t ATM90E32_REGISTER_QMEANLSB = 0xC5; // Lower Word Reg Base (Reactive Power)
|
||||||
static const uint16_t ATM90E32_REGISTER_QMEANALSB = 0xC5; // Lower Word (A React. Power)
|
static const uint16_t ATM90E32_REGISTER_QMEANALSB = 0xC5; // Lower Word (A React. Power)
|
||||||
static const uint16_t ATM90E32_REGISTER_QMEANBLSB = 0xC6; // Lower Word (B React. Power)
|
static const uint16_t ATM90E32_REGISTER_QMEANBLSB = 0xC6; // Lower Word (B React. Power)
|
||||||
static const uint16_t ATM90E32_REGISTER_QMEANCLSB = 0xC7; // Lower Word (C React. Power)
|
static const uint16_t ATM90E32_REGISTER_QMEANCLSB = 0xC7; // Lower Word (C React. Power)
|
||||||
|
@ -207,12 +214,15 @@ static const uint16_t ATM90E32_REGISTER_PMEANAF = 0xD1; // A Active Fund. Power
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANBF = 0xD2; // B Active Fund. Power
|
static const uint16_t ATM90E32_REGISTER_PMEANBF = 0xD2; // B Active Fund. Power
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANCF = 0xD3; // C Active Fund. Power
|
static const uint16_t ATM90E32_REGISTER_PMEANCF = 0xD3; // C Active Fund. Power
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANTH = 0xD4; // Total Active Harm. Power
|
static const uint16_t ATM90E32_REGISTER_PMEANTH = 0xD4; // Total Active Harm. Power
|
||||||
|
static const uint16_t ATM90E32_REGISTER_PMEANH = 0xD5; // Active Harm. Power Reg Base
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANAH = 0xD5; // A Active Harm. Power
|
static const uint16_t ATM90E32_REGISTER_PMEANAH = 0xD5; // A Active Harm. Power
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANBH = 0xD6; // B Active Harm. Power
|
static const uint16_t ATM90E32_REGISTER_PMEANBH = 0xD6; // B Active Harm. Power
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANCH = 0xD7; // C Active Harm. Power
|
static const uint16_t ATM90E32_REGISTER_PMEANCH = 0xD7; // C Active Harm. Power
|
||||||
|
static const uint16_t ATM90E32_REGISTER_URMS = 0xD9; // RMS Voltage Reg Base
|
||||||
static const uint16_t ATM90E32_REGISTER_URMSA = 0xD9; // A RMS Voltage
|
static const uint16_t ATM90E32_REGISTER_URMSA = 0xD9; // A RMS Voltage
|
||||||
static const uint16_t ATM90E32_REGISTER_URMSB = 0xDA; // B RMS Voltage
|
static const uint16_t ATM90E32_REGISTER_URMSB = 0xDA; // B RMS Voltage
|
||||||
static const uint16_t ATM90E32_REGISTER_URMSC = 0xDB; // C RMS Voltage
|
static const uint16_t ATM90E32_REGISTER_URMSC = 0xDB; // C RMS Voltage
|
||||||
|
static const uint16_t ATM90E32_REGISTER_IRMS = 0xDD; // RMS Current Reg Base
|
||||||
static const uint16_t ATM90E32_REGISTER_IRMSA = 0xDD; // A RMS Current
|
static const uint16_t ATM90E32_REGISTER_IRMSA = 0xDD; // A RMS Current
|
||||||
static const uint16_t ATM90E32_REGISTER_IRMSB = 0xDE; // B RMS Current
|
static const uint16_t ATM90E32_REGISTER_IRMSB = 0xDE; // B RMS Current
|
||||||
static const uint16_t ATM90E32_REGISTER_IRMSC = 0xDF; // C RMS Current
|
static const uint16_t ATM90E32_REGISTER_IRMSC = 0xDF; // C RMS Current
|
||||||
|
@ -223,12 +233,15 @@ static const uint16_t ATM90E32_REGISTER_PMEANAFLSB = 0xE1; // Lower Word (A Act
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANBFLSB = 0xE2; // Lower Word (B Act. Fund. Power)
|
static const uint16_t ATM90E32_REGISTER_PMEANBFLSB = 0xE2; // Lower Word (B Act. Fund. Power)
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANCFLSB = 0xE3; // Lower Word (C Act. Fund. Power)
|
static const uint16_t ATM90E32_REGISTER_PMEANCFLSB = 0xE3; // Lower Word (C Act. Fund. Power)
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANTHLSB = 0xE4; // Lower Word (Tot. Act. Harm. Power)
|
static const uint16_t ATM90E32_REGISTER_PMEANTHLSB = 0xE4; // Lower Word (Tot. Act. Harm. Power)
|
||||||
|
static const uint16_t ATM90E32_REGISTER_PMEANHLSB = 0xE5; // Lower Word (A Act. Harm. Power) Reg Base
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANAHLSB = 0xE5; // Lower Word (A Act. Harm. Power)
|
static const uint16_t ATM90E32_REGISTER_PMEANAHLSB = 0xE5; // Lower Word (A Act. Harm. Power)
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANBHLSB = 0xE6; // Lower Word (B Act. Harm. Power)
|
static const uint16_t ATM90E32_REGISTER_PMEANBHLSB = 0xE6; // Lower Word (B Act. Harm. Power)
|
||||||
static const uint16_t ATM90E32_REGISTER_PMEANCHLSB = 0xE7; // Lower Word (C Act. Harm. Power)
|
static const uint16_t ATM90E32_REGISTER_PMEANCHLSB = 0xE7; // Lower Word (C Act. Harm. Power)
|
||||||
|
static const uint16_t ATM90E32_REGISTER_URMSLSB = 0xE9; // Lower Word RMS Voltage Reg Base
|
||||||
static const uint16_t ATM90E32_REGISTER_URMSALSB = 0xE9; // Lower Word (A RMS Voltage)
|
static const uint16_t ATM90E32_REGISTER_URMSALSB = 0xE9; // Lower Word (A RMS Voltage)
|
||||||
static const uint16_t ATM90E32_REGISTER_URMSBLSB = 0xEA; // Lower Word (B RMS Voltage)
|
static const uint16_t ATM90E32_REGISTER_URMSBLSB = 0xEA; // Lower Word (B RMS Voltage)
|
||||||
static const uint16_t ATM90E32_REGISTER_URMSCLSB = 0xEB; // Lower Word (C RMS Voltage)
|
static const uint16_t ATM90E32_REGISTER_URMSCLSB = 0xEB; // Lower Word (C RMS Voltage)
|
||||||
|
static const uint16_t ATM90E32_REGISTER_IRMSLSB = 0xED; // Lower Word RMS Current Reg Base
|
||||||
static const uint16_t ATM90E32_REGISTER_IRMSALSB = 0xED; // Lower Word (A RMS Current)
|
static const uint16_t ATM90E32_REGISTER_IRMSALSB = 0xED; // Lower Word (A RMS Current)
|
||||||
static const uint16_t ATM90E32_REGISTER_IRMSBLSB = 0xEE; // Lower Word (B RMS Current)
|
static const uint16_t ATM90E32_REGISTER_IRMSBLSB = 0xEE; // Lower Word (B RMS Current)
|
||||||
static const uint16_t ATM90E32_REGISTER_IRMSCLSB = 0xEF; // Lower Word (C RMS Current)
|
static const uint16_t ATM90E32_REGISTER_IRMSCLSB = 0xEF; // Lower Word (C RMS Current)
|
||||||
|
@ -237,10 +250,12 @@ static const uint16_t ATM90E32_REGISTER_IRMSCLSB = 0xEF; // Lower Word (C RMS
|
||||||
static const uint16_t ATM90E32_REGISTER_UPEAKA = 0xF1; // A Voltage Peak
|
static const uint16_t ATM90E32_REGISTER_UPEAKA = 0xF1; // A Voltage Peak
|
||||||
static const uint16_t ATM90E32_REGISTER_UPEAKB = 0xF2; // B Voltage Peak
|
static const uint16_t ATM90E32_REGISTER_UPEAKB = 0xF2; // B Voltage Peak
|
||||||
static const uint16_t ATM90E32_REGISTER_UPEAKC = 0xF3; // C Voltage Peak
|
static const uint16_t ATM90E32_REGISTER_UPEAKC = 0xF3; // C Voltage Peak
|
||||||
|
static const uint16_t ATM90E32_REGISTER_IPEAK = 0xF5; // Peak Current Reg Base
|
||||||
static const uint16_t ATM90E32_REGISTER_IPEAKA = 0xF5; // A Current Peak
|
static const uint16_t ATM90E32_REGISTER_IPEAKA = 0xF5; // A Current Peak
|
||||||
static const uint16_t ATM90E32_REGISTER_IPEAKB = 0xF6; // B Current Peak
|
static const uint16_t ATM90E32_REGISTER_IPEAKB = 0xF6; // B Current Peak
|
||||||
static const uint16_t ATM90E32_REGISTER_IPEAKC = 0xF7; // C Current Peak
|
static const uint16_t ATM90E32_REGISTER_IPEAKC = 0xF7; // C Current Peak
|
||||||
static const uint16_t ATM90E32_REGISTER_FREQ = 0xF8; // Frequency
|
static const uint16_t ATM90E32_REGISTER_FREQ = 0xF8; // Frequency
|
||||||
|
static const uint16_t ATM90E32_REGISTER_PANGLE = 0xF9; // Mean Phase Angle Reg Base
|
||||||
static const uint16_t ATM90E32_REGISTER_PANGLEA = 0xF9; // A Mean Phase Angle
|
static const uint16_t ATM90E32_REGISTER_PANGLEA = 0xF9; // A Mean Phase Angle
|
||||||
static const uint16_t ATM90E32_REGISTER_PANGLEB = 0xFA; // B Mean Phase Angle
|
static const uint16_t ATM90E32_REGISTER_PANGLEB = 0xFA; // B Mean Phase Angle
|
||||||
static const uint16_t ATM90E32_REGISTER_PANGLEC = 0xFB; // C Mean Phase Angle
|
static const uint16_t ATM90E32_REGISTER_PANGLEC = 0xFB; // C Mean Phase Angle
|
||||||
|
|
|
@ -9,8 +9,10 @@ from esphome.const import (
|
||||||
CONF_PHASE_A,
|
CONF_PHASE_A,
|
||||||
CONF_PHASE_B,
|
CONF_PHASE_B,
|
||||||
CONF_PHASE_C,
|
CONF_PHASE_C,
|
||||||
|
CONF_PHASE_ANGLE,
|
||||||
CONF_POWER,
|
CONF_POWER,
|
||||||
CONF_POWER_FACTOR,
|
CONF_POWER_FACTOR,
|
||||||
|
CONF_APPARENT_POWER,
|
||||||
CONF_FREQUENCY,
|
CONF_FREQUENCY,
|
||||||
CONF_FORWARD_ACTIVE_ENERGY,
|
CONF_FORWARD_ACTIVE_ENERGY,
|
||||||
CONF_REVERSE_ACTIVE_ENERGY,
|
CONF_REVERSE_ACTIVE_ENERGY,
|
||||||
|
@ -25,12 +27,13 @@ from esphome.const import (
|
||||||
ICON_CURRENT_AC,
|
ICON_CURRENT_AC,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_TOTAL_INCREASING,
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
UNIT_AMPERE,
|
||||||
|
UNIT_DEGREES,
|
||||||
|
UNIT_CELSIUS,
|
||||||
UNIT_HERTZ,
|
UNIT_HERTZ,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
UNIT_AMPERE,
|
|
||||||
UNIT_WATT,
|
|
||||||
UNIT_CELSIUS,
|
|
||||||
UNIT_VOLT_AMPS_REACTIVE,
|
UNIT_VOLT_AMPS_REACTIVE,
|
||||||
|
UNIT_WATT,
|
||||||
UNIT_WATT_HOURS,
|
UNIT_WATT_HOURS,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,6 +43,10 @@ CONF_GAIN_PGA = "gain_pga"
|
||||||
CONF_CURRENT_PHASES = "current_phases"
|
CONF_CURRENT_PHASES = "current_phases"
|
||||||
CONF_GAIN_VOLTAGE = "gain_voltage"
|
CONF_GAIN_VOLTAGE = "gain_voltage"
|
||||||
CONF_GAIN_CT = "gain_ct"
|
CONF_GAIN_CT = "gain_ct"
|
||||||
|
CONF_HARMONIC_POWER = "harmonic_power"
|
||||||
|
CONF_PEAK_CURRENT = "peak_current"
|
||||||
|
CONF_PEAK_CURRENT_SIGNED = "peak_current_signed"
|
||||||
|
UNIT_DEG = "degrees"
|
||||||
LINE_FREQS = {
|
LINE_FREQS = {
|
||||||
"50HZ": 50,
|
"50HZ": 50,
|
||||||
"60HZ": 60,
|
"60HZ": 60,
|
||||||
|
@ -85,6 +92,12 @@ ATM90E32_PHASE_SCHEMA = cv.Schema(
|
||||||
accuracy_decimals=2,
|
accuracy_decimals=2,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_APPARENT_POWER): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_WATT,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
|
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
|
||||||
accuracy_decimals=2,
|
accuracy_decimals=2,
|
||||||
device_class=DEVICE_CLASS_POWER_FACTOR,
|
device_class=DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
@ -102,6 +115,24 @@ ATM90E32_PHASE_SCHEMA = cv.Schema(
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_PHASE_ANGLE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_DEGREES,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HARMONIC_POWER): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_WATT,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PEAK_CURRENT): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_AMPERE,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_CURRENT,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
|
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
|
||||||
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,
|
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,
|
||||||
}
|
}
|
||||||
|
@ -132,6 +163,7 @@ CONFIG_SCHEMA = (
|
||||||
CURRENT_PHASES, upper=True
|
CURRENT_PHASES, upper=True
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_GAIN_PGA, default="2X"): cv.enum(PGA_GAINS, upper=True),
|
cv.Optional(CONF_GAIN_PGA, default="2X"): cv.enum(PGA_GAINS, upper=True),
|
||||||
|
cv.Optional(CONF_PEAK_CURRENT_SIGNED, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.polling_component_schema("60s"))
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
@ -162,6 +194,9 @@ async def to_code(config):
|
||||||
if reactive_power_config := conf.get(CONF_REACTIVE_POWER):
|
if reactive_power_config := conf.get(CONF_REACTIVE_POWER):
|
||||||
sens = await sensor.new_sensor(reactive_power_config)
|
sens = await sensor.new_sensor(reactive_power_config)
|
||||||
cg.add(var.set_reactive_power_sensor(i, sens))
|
cg.add(var.set_reactive_power_sensor(i, sens))
|
||||||
|
if apparent_power_config := conf.get(CONF_APPARENT_POWER):
|
||||||
|
sens = await sensor.new_sensor(apparent_power_config)
|
||||||
|
cg.add(var.set_apparent_power_sensor(i, sens))
|
||||||
if power_factor_config := conf.get(CONF_POWER_FACTOR):
|
if power_factor_config := conf.get(CONF_POWER_FACTOR):
|
||||||
sens = await sensor.new_sensor(power_factor_config)
|
sens = await sensor.new_sensor(power_factor_config)
|
||||||
cg.add(var.set_power_factor_sensor(i, sens))
|
cg.add(var.set_power_factor_sensor(i, sens))
|
||||||
|
@ -171,6 +206,15 @@ async def to_code(config):
|
||||||
if reverse_active_energy_config := conf.get(CONF_REVERSE_ACTIVE_ENERGY):
|
if reverse_active_energy_config := conf.get(CONF_REVERSE_ACTIVE_ENERGY):
|
||||||
sens = await sensor.new_sensor(reverse_active_energy_config)
|
sens = await sensor.new_sensor(reverse_active_energy_config)
|
||||||
cg.add(var.set_reverse_active_energy_sensor(i, sens))
|
cg.add(var.set_reverse_active_energy_sensor(i, sens))
|
||||||
|
if phase_angle_config := conf.get(CONF_PHASE_ANGLE):
|
||||||
|
sens = await sensor.new_sensor(phase_angle_config)
|
||||||
|
cg.add(var.set_phase_angle_sensor(i, sens))
|
||||||
|
if harmonic_active_power_config := conf.get(CONF_HARMONIC_POWER):
|
||||||
|
sens = await sensor.new_sensor(harmonic_active_power_config)
|
||||||
|
cg.add(var.set_harmonic_active_power_sensor(i, sens))
|
||||||
|
if peak_current_config := conf.get(CONF_PEAK_CURRENT):
|
||||||
|
sens = await sensor.new_sensor(peak_current_config)
|
||||||
|
cg.add(var.set_peak_current_sensor(i, sens))
|
||||||
|
|
||||||
if frequency_config := config.get(CONF_FREQUENCY):
|
if frequency_config := config.get(CONF_FREQUENCY):
|
||||||
sens = await sensor.new_sensor(frequency_config)
|
sens = await sensor.new_sensor(frequency_config)
|
||||||
|
@ -182,3 +226,4 @@ async def to_code(config):
|
||||||
cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
|
cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
|
||||||
cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
|
cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
|
||||||
cg.add(var.set_pga_gain(config[CONF_GAIN_PGA]))
|
cg.add(var.set_pga_gain(config[CONF_GAIN_PGA]))
|
||||||
|
cg.add(var.set_peak_current_signed(config[CONF_PEAK_CURRENT_SIGNED]))
|
||||||
|
|
|
@ -194,8 +194,8 @@ void BangBangClimate::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_));
|
ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_));
|
||||||
ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_));
|
ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_));
|
||||||
ESP_LOGCONFIG(TAG, " Supports AWAY mode: %s", YESNO(this->supports_away_));
|
ESP_LOGCONFIG(TAG, " Supports AWAY mode: %s", YESNO(this->supports_away_));
|
||||||
ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.1f°C", this->normal_config_.default_temperature_low);
|
ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.2f°C", this->normal_config_.default_temperature_low);
|
||||||
ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.1f°C", this->normal_config_.default_temperature_high);
|
ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.2f°C", this->normal_config_.default_temperature_high);
|
||||||
}
|
}
|
||||||
|
|
||||||
BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig() = default;
|
BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig() = default;
|
||||||
|
|
|
@ -242,7 +242,7 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
|
||||||
this->set_notify_(true);
|
this->set_notify_(true);
|
||||||
|
|
||||||
#ifdef USE_TIME
|
#ifdef USE_TIME
|
||||||
if (this->time_id_.has_value()) {
|
if (this->time_id_ != nullptr) {
|
||||||
this->send_local_time();
|
this->send_local_time();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -441,9 +441,8 @@ uint8_t BedJetHub::write_notify_config_descriptor_(bool enable) {
|
||||||
|
|
||||||
#ifdef USE_TIME
|
#ifdef USE_TIME
|
||||||
void BedJetHub::send_local_time() {
|
void BedJetHub::send_local_time() {
|
||||||
if (this->time_id_.has_value()) {
|
if (this->time_id_ != nullptr) {
|
||||||
auto *time_id = *this->time_id_;
|
ESPTime now = this->time_id_->now();
|
||||||
ESPTime now = time_id->now();
|
|
||||||
if (now.is_valid()) {
|
if (now.is_valid()) {
|
||||||
this->set_clock(now.hour, now.minute);
|
this->set_clock(now.hour, now.minute);
|
||||||
ESP_LOGD(TAG, "Using time component to set BedJet clock: %d:%02d", now.hour, now.minute);
|
ESP_LOGD(TAG, "Using time component to set BedJet clock: %d:%02d", now.hour, now.minute);
|
||||||
|
@ -454,10 +453,9 @@ void BedJetHub::send_local_time() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BedJetHub::setup_time_() {
|
void BedJetHub::setup_time_() {
|
||||||
if (this->time_id_.has_value()) {
|
if (this->time_id_ != nullptr) {
|
||||||
this->send_local_time();
|
this->send_local_time();
|
||||||
auto *time_id = *this->time_id_;
|
this->time_id_->add_on_time_sync_callback([this] { this->send_local_time(); });
|
||||||
time_id->add_on_time_sync_callback([this] { this->send_local_time(); });
|
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "`time_id` is not configured: will not sync BedJet clock.");
|
ESP_LOGI(TAG, "`time_id` is not configured: will not sync BedJet clock.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ class BedJetHub : public esphome::ble_client::BLEClientNode, public PollingCompo
|
||||||
#ifdef USE_TIME
|
#ifdef USE_TIME
|
||||||
/** Initializes time sync callbacks to support syncing current time to the BedJet. */
|
/** Initializes time sync callbacks to support syncing current time to the BedJet. */
|
||||||
void setup_time_();
|
void setup_time_();
|
||||||
optional<time::RealTimeClock *> time_id_{};
|
time::RealTimeClock *time_id_{nullptr};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t timeout_{DEFAULT_STATUS_TIMEOUT};
|
uint32_t timeout_{DEFAULT_STATUS_TIMEOUT};
|
||||||
|
|
|
@ -4,6 +4,7 @@ from esphome.components import sensor, uart
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_CURRENT,
|
CONF_CURRENT,
|
||||||
CONF_ENERGY,
|
CONF_ENERGY,
|
||||||
|
CONF_EXTERNAL_TEMPERATURE,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_POWER,
|
CONF_POWER,
|
||||||
CONF_VOLTAGE,
|
CONF_VOLTAGE,
|
||||||
|
@ -18,12 +19,12 @@ from esphome.const import (
|
||||||
UNIT_KILOWATT_HOURS,
|
UNIT_KILOWATT_HOURS,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
UNIT_WATT,
|
UNIT_WATT,
|
||||||
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
)
|
)
|
||||||
|
|
||||||
DEPENDENCIES = ["uart"]
|
DEPENDENCIES = ["uart"]
|
||||||
|
|
||||||
CONF_INTERNAL_TEMPERATURE = "internal_temperature"
|
CONF_INTERNAL_TEMPERATURE = "internal_temperature"
|
||||||
CONF_EXTERNAL_TEMPERATURE = "external_temperature"
|
|
||||||
|
|
||||||
bl0940_ns = cg.esphome_ns.namespace("bl0940")
|
bl0940_ns = cg.esphome_ns.namespace("bl0940")
|
||||||
BL0940 = bl0940_ns.class_("BL0940", cg.PollingComponent, uart.UARTDevice)
|
BL0940 = bl0940_ns.class_("BL0940", cg.PollingComponent, uart.UARTDevice)
|
||||||
|
@ -54,6 +55,7 @@ CONFIG_SCHEMA = (
|
||||||
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
||||||
accuracy_decimals=0,
|
accuracy_decimals=0,
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_INTERNAL_TEMPERATURE): sensor.sensor_schema(
|
cv.Optional(CONF_INTERNAL_TEMPERATURE): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_CELSIUS,
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
|
|
@ -19,6 +19,7 @@ from esphome.const import (
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
UNIT_WATT,
|
UNIT_WATT,
|
||||||
UNIT_HERTZ,
|
UNIT_HERTZ,
|
||||||
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
)
|
)
|
||||||
|
|
||||||
DEPENDENCIES = ["uart"]
|
DEPENDENCIES = ["uart"]
|
||||||
|
@ -52,6 +53,7 @@ CONFIG_SCHEMA = (
|
||||||
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
||||||
accuracy_decimals=0,
|
accuracy_decimals=0,
|
||||||
device_class=DEVICE_CLASS_ENERGY,
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
|
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_HERTZ,
|
unit_of_measurement=UNIT_HERTZ,
|
||||||
|
|
|
@ -8,8 +8,11 @@ from esphome.const import (
|
||||||
CONF_IBEACON_MINOR,
|
CONF_IBEACON_MINOR,
|
||||||
CONF_IBEACON_UUID,
|
CONF_IBEACON_UUID,
|
||||||
CONF_MIN_RSSI,
|
CONF_MIN_RSSI,
|
||||||
|
CONF_TIMEOUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CONF_IRK = "irk"
|
||||||
|
|
||||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||||
|
|
||||||
ble_presence_ns = cg.esphome_ns.namespace("ble_presence")
|
ble_presence_ns = cg.esphome_ns.namespace("ble_presence")
|
||||||
|
@ -34,10 +37,12 @@ CONFIG_SCHEMA = cv.All(
|
||||||
.extend(
|
.extend(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
|
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
|
||||||
|
cv.Optional(CONF_IRK): cv.uuid,
|
||||||
cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
||||||
cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t,
|
cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t,
|
||||||
cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t,
|
cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t,
|
||||||
cv.Optional(CONF_IBEACON_UUID): cv.uuid,
|
cv.Optional(CONF_IBEACON_UUID): cv.uuid,
|
||||||
|
cv.Optional(CONF_TIMEOUT, default="5min"): cv.positive_time_period,
|
||||||
cv.Optional(CONF_MIN_RSSI): cv.All(
|
cv.Optional(CONF_MIN_RSSI): cv.All(
|
||||||
cv.decibel, cv.int_range(min=-100, max=-30)
|
cv.decibel, cv.int_range(min=-100, max=-30)
|
||||||
),
|
),
|
||||||
|
@ -45,7 +50,9 @@ CONFIG_SCHEMA = cv.All(
|
||||||
)
|
)
|
||||||
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||||
.extend(cv.COMPONENT_SCHEMA),
|
.extend(cv.COMPONENT_SCHEMA),
|
||||||
cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_IBEACON_UUID),
|
cv.has_exactly_one_key(
|
||||||
|
CONF_MAC_ADDRESS, CONF_IRK, CONF_SERVICE_UUID, CONF_IBEACON_UUID
|
||||||
|
),
|
||||||
_validate,
|
_validate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,12 +62,17 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await esp32_ble_tracker.register_ble_device(var, config)
|
await esp32_ble_tracker.register_ble_device(var, config)
|
||||||
|
|
||||||
|
cg.add(var.set_timeout(config[CONF_TIMEOUT].total_milliseconds))
|
||||||
if min_rssi := config.get(CONF_MIN_RSSI):
|
if min_rssi := config.get(CONF_MIN_RSSI):
|
||||||
cg.add(var.set_minimum_rssi(min_rssi))
|
cg.add(var.set_minimum_rssi(min_rssi))
|
||||||
|
|
||||||
if mac_address := config.get(CONF_MAC_ADDRESS):
|
if mac_address := config.get(CONF_MAC_ADDRESS):
|
||||||
cg.add(var.set_address(mac_address.as_hex))
|
cg.add(var.set_address(mac_address.as_hex))
|
||||||
|
|
||||||
|
if irk := config.get(CONF_IRK):
|
||||||
|
irk = esp32_ble_tracker.as_hex_array(str(irk))
|
||||||
|
cg.add(var.set_irk(irk))
|
||||||
|
|
||||||
if service_uuid := config.get(CONF_SERVICE_UUID):
|
if service_uuid := config.get(CONF_SERVICE_UUID):
|
||||||
if len(service_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
|
if len(service_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||||
cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(service_uuid)))
|
cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(service_uuid)))
|
||||||
|
|
|
@ -6,6 +6,16 @@
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#ifdef USE_ARDUINO
|
||||||
|
#include "mbedtls/aes.h"
|
||||||
|
#include "mbedtls/base64.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ESP_IDF
|
||||||
|
#define MBEDTLS_AES_ALT
|
||||||
|
#include <aes_alt.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ble_presence {
|
namespace ble_presence {
|
||||||
|
|
||||||
|
@ -17,6 +27,10 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
||||||
this->match_by_ = MATCH_BY_MAC_ADDRESS;
|
this->match_by_ = MATCH_BY_MAC_ADDRESS;
|
||||||
this->address_ = address;
|
this->address_ = address;
|
||||||
}
|
}
|
||||||
|
void set_irk(uint8_t *irk) {
|
||||||
|
this->match_by_ = MATCH_BY_IRK;
|
||||||
|
this->irk_ = irk;
|
||||||
|
}
|
||||||
void set_service_uuid16(uint16_t uuid) {
|
void set_service_uuid16(uint16_t uuid) {
|
||||||
this->match_by_ = MATCH_BY_SERVICE_UUID;
|
this->match_by_ = MATCH_BY_SERVICE_UUID;
|
||||||
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid);
|
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid);
|
||||||
|
@ -45,11 +59,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
||||||
this->check_minimum_rssi_ = true;
|
this->check_minimum_rssi_ = true;
|
||||||
this->minimum_rssi_ = rssi;
|
this->minimum_rssi_ = rssi;
|
||||||
}
|
}
|
||||||
void on_scan_end() override {
|
void set_timeout(uint32_t timeout) { this->timeout_ = timeout; }
|
||||||
if (!this->found_)
|
|
||||||
this->publish_state(false);
|
|
||||||
this->found_ = false;
|
|
||||||
}
|
|
||||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
|
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
|
||||||
if (this->check_minimum_rssi_ && this->minimum_rssi_ > device.get_rssi()) {
|
if (this->check_minimum_rssi_ && this->minimum_rssi_ > device.get_rssi()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -57,16 +67,20 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
||||||
switch (this->match_by_) {
|
switch (this->match_by_) {
|
||||||
case MATCH_BY_MAC_ADDRESS:
|
case MATCH_BY_MAC_ADDRESS:
|
||||||
if (device.address_uint64() == this->address_) {
|
if (device.address_uint64() == this->address_) {
|
||||||
this->publish_state(true);
|
this->set_found_(true);
|
||||||
this->found_ = true;
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MATCH_BY_IRK:
|
||||||
|
if (resolve_irk_(device.address_uint64(), this->irk_)) {
|
||||||
|
this->set_found_(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MATCH_BY_SERVICE_UUID:
|
case MATCH_BY_SERVICE_UUID:
|
||||||
for (auto uuid : device.get_service_uuids()) {
|
for (auto uuid : device.get_service_uuids()) {
|
||||||
if (this->uuid_ == uuid) {
|
if (this->uuid_ == uuid) {
|
||||||
this->publish_state(true);
|
this->set_found_(true);
|
||||||
this->found_ = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,20 +104,31 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->publish_state(true);
|
this->set_found_(true);
|
||||||
this->found_ = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loop() override {
|
||||||
|
if (this->found_ && this->last_seen_ + this->timeout_ < millis())
|
||||||
|
this->set_found_(false);
|
||||||
|
}
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
|
void set_found_(bool state) {
|
||||||
|
this->found_ = state;
|
||||||
|
if (state)
|
||||||
|
this->last_seen_ = millis();
|
||||||
|
this->publish_state(state);
|
||||||
|
}
|
||||||
|
enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_IRK, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
|
||||||
MatchType match_by_;
|
MatchType match_by_;
|
||||||
|
|
||||||
uint64_t address_;
|
uint64_t address_;
|
||||||
|
uint8_t *irk_;
|
||||||
|
|
||||||
esp32_ble_tracker::ESPBTUUID uuid_;
|
esp32_ble_tracker::ESPBTUUID uuid_;
|
||||||
|
|
||||||
|
@ -117,7 +142,46 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
||||||
bool check_ibeacon_minor_{false};
|
bool check_ibeacon_minor_{false};
|
||||||
bool check_minimum_rssi_{false};
|
bool check_minimum_rssi_{false};
|
||||||
|
|
||||||
|
bool resolve_irk_(uint64_t addr64, const uint8_t *irk) {
|
||||||
|
uint8_t ecb_key[16];
|
||||||
|
uint8_t ecb_plaintext[16];
|
||||||
|
uint8_t ecb_ciphertext[16];
|
||||||
|
|
||||||
|
memcpy(&ecb_key, irk, 16);
|
||||||
|
memset(&ecb_plaintext, 0, 16);
|
||||||
|
|
||||||
|
ecb_plaintext[13] = (addr64 >> 40) & 0xff;
|
||||||
|
ecb_plaintext[14] = (addr64 >> 32) & 0xff;
|
||||||
|
ecb_plaintext[15] = (addr64 >> 24) & 0xff;
|
||||||
|
|
||||||
|
mbedtls_aes_context ctx = {0, 0, {0}};
|
||||||
|
mbedtls_aes_init(&ctx);
|
||||||
|
|
||||||
|
if (mbedtls_aes_setkey_enc(&ctx, ecb_key, 128) != 0) {
|
||||||
|
mbedtls_aes_free(&ctx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mbedtls_aes_crypt_ecb(&ctx,
|
||||||
|
#ifdef USE_ARDUINO
|
||||||
|
MBEDTLS_AES_ENCRYPT,
|
||||||
|
#elif defined(USE_ESP_IDF)
|
||||||
|
ESP_AES_ENCRYPT,
|
||||||
|
#endif
|
||||||
|
ecb_plaintext, ecb_ciphertext) != 0) {
|
||||||
|
mbedtls_aes_free(&ctx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mbedtls_aes_free(&ctx);
|
||||||
|
|
||||||
|
return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) &&
|
||||||
|
ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
bool found_{false};
|
bool found_{false};
|
||||||
|
uint32_t last_seen_{};
|
||||||
|
uint32_t timeout_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ble_presence
|
} // namespace ble_presence
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import i2c, sensor
|
|
||||||
from esphome.const import (
|
|
||||||
CONF_HUMIDITY,
|
|
||||||
CONF_ID,
|
|
||||||
CONF_IIR_FILTER,
|
|
||||||
CONF_OVERSAMPLING,
|
|
||||||
CONF_PRESSURE,
|
|
||||||
CONF_TEMPERATURE,
|
|
||||||
DEVICE_CLASS_HUMIDITY,
|
|
||||||
DEVICE_CLASS_PRESSURE,
|
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
UNIT_CELSIUS,
|
|
||||||
UNIT_HECTOPASCAL,
|
|
||||||
UNIT_PERCENT,
|
|
||||||
)
|
|
||||||
|
|
||||||
DEPENDENCIES = ["i2c"]
|
|
||||||
|
|
||||||
bme280_ns = cg.esphome_ns.namespace("bme280")
|
|
||||||
BME280Oversampling = bme280_ns.enum("BME280Oversampling")
|
|
||||||
OVERSAMPLING_OPTIONS = {
|
|
||||||
"NONE": BME280Oversampling.BME280_OVERSAMPLING_NONE,
|
|
||||||
"1X": BME280Oversampling.BME280_OVERSAMPLING_1X,
|
|
||||||
"2X": BME280Oversampling.BME280_OVERSAMPLING_2X,
|
|
||||||
"4X": BME280Oversampling.BME280_OVERSAMPLING_4X,
|
|
||||||
"8X": BME280Oversampling.BME280_OVERSAMPLING_8X,
|
|
||||||
"16X": BME280Oversampling.BME280_OVERSAMPLING_16X,
|
|
||||||
}
|
|
||||||
|
|
||||||
BME280IIRFilter = bme280_ns.enum("BME280IIRFilter")
|
|
||||||
IIR_FILTER_OPTIONS = {
|
|
||||||
"OFF": BME280IIRFilter.BME280_IIR_FILTER_OFF,
|
|
||||||
"2X": BME280IIRFilter.BME280_IIR_FILTER_2X,
|
|
||||||
"4X": BME280IIRFilter.BME280_IIR_FILTER_4X,
|
|
||||||
"8X": BME280IIRFilter.BME280_IIR_FILTER_8X,
|
|
||||||
"16X": BME280IIRFilter.BME280_IIR_FILTER_16X,
|
|
||||||
}
|
|
||||||
|
|
||||||
BME280Component = bme280_ns.class_(
|
|
||||||
"BME280Component", cg.PollingComponent, i2c.I2CDevice
|
|
||||||
)
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
|
||||||
cv.Schema(
|
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(BME280Component),
|
|
||||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_CELSIUS,
|
|
||||||
accuracy_decimals=1,
|
|
||||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
).extend(
|
|
||||||
{
|
|
||||||
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
|
||||||
OVERSAMPLING_OPTIONS, upper=True
|
|
||||||
),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_HECTOPASCAL,
|
|
||||||
accuracy_decimals=1,
|
|
||||||
device_class=DEVICE_CLASS_PRESSURE,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
).extend(
|
|
||||||
{
|
|
||||||
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
|
||||||
OVERSAMPLING_OPTIONS, upper=True
|
|
||||||
),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_PERCENT,
|
|
||||||
accuracy_decimals=1,
|
|
||||||
device_class=DEVICE_CLASS_HUMIDITY,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
).extend(
|
|
||||||
{
|
|
||||||
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
|
||||||
OVERSAMPLING_OPTIONS, upper=True
|
|
||||||
),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
|
|
||||||
IIR_FILTER_OPTIONS, upper=True
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.extend(cv.polling_component_schema("60s"))
|
|
||||||
.extend(i2c.i2c_device_schema(0x77))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
|
||||||
await cg.register_component(var, config)
|
|
||||||
await i2c.register_i2c_device(var, config)
|
|
||||||
|
|
||||||
if temperature_config := config.get(CONF_TEMPERATURE):
|
|
||||||
sens = await sensor.new_sensor(temperature_config)
|
|
||||||
cg.add(var.set_temperature_sensor(sens))
|
|
||||||
cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING]))
|
|
||||||
|
|
||||||
if pressure_config := config.get(CONF_PRESSURE):
|
|
||||||
sens = await sensor.new_sensor(pressure_config)
|
|
||||||
cg.add(var.set_pressure_sensor(sens))
|
|
||||||
cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING]))
|
|
||||||
|
|
||||||
if humidity_config := config.get(CONF_HUMIDITY):
|
|
||||||
sens = await sensor.new_sensor(humidity_config)
|
|
||||||
cg.add(var.set_humidity_sensor(sens))
|
|
||||||
cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING]))
|
|
||||||
|
|
||||||
cg.add(var.set_iir_filter(config[CONF_IIR_FILTER]))
|
|
1
esphome/components/bme280_base/__init__.py
Normal file
1
esphome/components/bme280_base/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@esphome/core"]
|
|
@ -1,9 +1,14 @@
|
||||||
#include "bme280.h"
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "bme280_base.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include <esphome/components/sensor/sensor.h>
|
||||||
|
#include <esphome/core/component.h>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace bme280 {
|
namespace bme280_base {
|
||||||
|
|
||||||
static const char *const TAG = "bme280.sensor";
|
static const char *const TAG = "bme280.sensor";
|
||||||
|
|
||||||
|
@ -46,7 +51,24 @@ static const uint8_t BME280_STATUS_IM_UPDATE = 0b01;
|
||||||
|
|
||||||
inline uint16_t combine_bytes(uint8_t msb, uint8_t lsb) { return ((msb & 0xFF) << 8) | (lsb & 0xFF); }
|
inline uint16_t combine_bytes(uint8_t msb, uint8_t lsb) { return ((msb & 0xFF) << 8) | (lsb & 0xFF); }
|
||||||
|
|
||||||
static const char *oversampling_to_str(BME280Oversampling oversampling) {
|
const char *iir_filter_to_str(BME280IIRFilter filter) { // NOLINT
|
||||||
|
switch (filter) {
|
||||||
|
case BME280_IIR_FILTER_OFF:
|
||||||
|
return "OFF";
|
||||||
|
case BME280_IIR_FILTER_2X:
|
||||||
|
return "2x";
|
||||||
|
case BME280_IIR_FILTER_4X:
|
||||||
|
return "4x";
|
||||||
|
case BME280_IIR_FILTER_8X:
|
||||||
|
return "8x";
|
||||||
|
case BME280_IIR_FILTER_16X:
|
||||||
|
return "16x";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *oversampling_to_str(BME280Oversampling oversampling) { // NOLINT
|
||||||
switch (oversampling) {
|
switch (oversampling) {
|
||||||
case BME280_OVERSAMPLING_NONE:
|
case BME280_OVERSAMPLING_NONE:
|
||||||
return "None";
|
return "None";
|
||||||
|
@ -65,23 +87,6 @@ static const char *oversampling_to_str(BME280Oversampling oversampling) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *iir_filter_to_str(BME280IIRFilter filter) {
|
|
||||||
switch (filter) {
|
|
||||||
case BME280_IIR_FILTER_OFF:
|
|
||||||
return "OFF";
|
|
||||||
case BME280_IIR_FILTER_2X:
|
|
||||||
return "2x";
|
|
||||||
case BME280_IIR_FILTER_4X:
|
|
||||||
return "4x";
|
|
||||||
case BME280_IIR_FILTER_8X:
|
|
||||||
return "8x";
|
|
||||||
case BME280_IIR_FILTER_16X:
|
|
||||||
return "16x";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BME280Component::setup() {
|
void BME280Component::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up BME280...");
|
ESP_LOGCONFIG(TAG, "Setting up BME280...");
|
||||||
uint8_t chip_id = 0;
|
uint8_t chip_id = 0;
|
||||||
|
@ -112,7 +117,7 @@ void BME280Component::setup() {
|
||||||
// Wait until the NVM data has finished loading.
|
// Wait until the NVM data has finished loading.
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
uint8_t retry = 5;
|
uint8_t retry = 5;
|
||||||
do {
|
do { // NOLINT
|
||||||
delay(2);
|
delay(2);
|
||||||
if (!this->read_byte(BME280_REGISTER_STATUS, &status)) {
|
if (!this->read_byte(BME280_REGISTER_STATUS, &status)) {
|
||||||
ESP_LOGW(TAG, "Error reading status register.");
|
ESP_LOGW(TAG, "Error reading status register.");
|
||||||
|
@ -175,7 +180,6 @@ void BME280Component::setup() {
|
||||||
}
|
}
|
||||||
void BME280Component::dump_config() {
|
void BME280Component::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "BME280:");
|
ESP_LOGCONFIG(TAG, "BME280:");
|
||||||
LOG_I2C_DEVICE(this);
|
|
||||||
switch (this->error_code_) {
|
switch (this->error_code_) {
|
||||||
case COMMUNICATION_FAILED:
|
case COMMUNICATION_FAILED:
|
||||||
ESP_LOGE(TAG, "Communication with BME280 failed!");
|
ESP_LOGE(TAG, "Communication with BME280 failed!");
|
||||||
|
@ -226,14 +230,14 @@ void BME280Component::update() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int32_t t_fine = 0;
|
int32_t t_fine = 0;
|
||||||
float temperature = this->read_temperature_(data, &t_fine);
|
float const temperature = this->read_temperature_(data, &t_fine);
|
||||||
if (std::isnan(temperature)) {
|
if (std::isnan(temperature)) {
|
||||||
ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values.");
|
ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values.");
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float pressure = this->read_pressure_(data, t_fine);
|
float const pressure = this->read_pressure_(data, t_fine);
|
||||||
float humidity = this->read_humidity_(data, t_fine);
|
float const humidity = this->read_humidity_(data, t_fine);
|
||||||
|
|
||||||
ESP_LOGV(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity);
|
ESP_LOGV(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity);
|
||||||
if (this->temperature_sensor_ != nullptr)
|
if (this->temperature_sensor_ != nullptr)
|
||||||
|
@ -257,12 +261,12 @@ float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) {
|
||||||
const int32_t t2 = this->calibration_.t2;
|
const int32_t t2 = this->calibration_.t2;
|
||||||
const int32_t t3 = this->calibration_.t3;
|
const int32_t t3 = this->calibration_.t3;
|
||||||
|
|
||||||
int32_t var1 = (((adc >> 3) - (t1 << 1)) * t2) >> 11;
|
int32_t const var1 = (((adc >> 3) - (t1 << 1)) * t2) >> 11;
|
||||||
int32_t var2 = (((((adc >> 4) - t1) * ((adc >> 4) - t1)) >> 12) * t3) >> 14;
|
int32_t const var2 = (((((adc >> 4) - t1) * ((adc >> 4) - t1)) >> 12) * t3) >> 14;
|
||||||
*t_fine = var1 + var2;
|
*t_fine = var1 + var2;
|
||||||
|
|
||||||
float temperature = (*t_fine * 5 + 128) >> 8;
|
float const temperature = (*t_fine * 5 + 128);
|
||||||
return temperature / 100.0f;
|
return temperature / 25600.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) {
|
float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) {
|
||||||
|
@ -303,11 +307,11 @@ float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float BME280Component::read_humidity_(const uint8_t *data, int32_t t_fine) {
|
float BME280Component::read_humidity_(const uint8_t *data, int32_t t_fine) {
|
||||||
uint16_t raw_adc = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF);
|
uint16_t const raw_adc = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF);
|
||||||
if (raw_adc == 0x8000)
|
if (raw_adc == 0x8000)
|
||||||
return NAN;
|
return NAN;
|
||||||
|
|
||||||
int32_t adc = raw_adc;
|
int32_t const adc = raw_adc;
|
||||||
|
|
||||||
const int32_t h1 = this->calibration_.h1;
|
const int32_t h1 = this->calibration_.h1;
|
||||||
const int32_t h2 = this->calibration_.h2;
|
const int32_t h2 = this->calibration_.h2;
|
||||||
|
@ -325,7 +329,7 @@ float BME280Component::read_humidity_(const uint8_t *data, int32_t t_fine) {
|
||||||
|
|
||||||
v_x1_u32r = v_x1_u32r < 0 ? 0 : v_x1_u32r;
|
v_x1_u32r = v_x1_u32r < 0 ? 0 : v_x1_u32r;
|
||||||
v_x1_u32r = v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r;
|
v_x1_u32r = v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r;
|
||||||
float h = v_x1_u32r >> 12;
|
float const h = v_x1_u32r >> 12;
|
||||||
|
|
||||||
return h / 1024.0f;
|
return h / 1024.0f;
|
||||||
}
|
}
|
||||||
|
@ -351,5 +355,5 @@ uint16_t BME280Component::read_u16_le_(uint8_t a_register) {
|
||||||
}
|
}
|
||||||
int16_t BME280Component::read_s16_le_(uint8_t a_register) { return this->read_u16_le_(a_register); }
|
int16_t BME280Component::read_s16_le_(uint8_t a_register) { return this->read_u16_le_(a_register); }
|
||||||
|
|
||||||
} // namespace bme280
|
} // namespace bme280_base
|
||||||
} // namespace esphome
|
} // namespace esphome
|
|
@ -2,10 +2,9 @@
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/i2c/i2c.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace bme280 {
|
namespace bme280_base {
|
||||||
|
|
||||||
/// Internal struct storing the calibration values of an BME280.
|
/// Internal struct storing the calibration values of an BME280.
|
||||||
struct BME280CalibrationData {
|
struct BME280CalibrationData {
|
||||||
|
@ -57,8 +56,8 @@ enum BME280IIRFilter {
|
||||||
BME280_IIR_FILTER_16X = 0b100,
|
BME280_IIR_FILTER_16X = 0b100,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This class implements support for the BME280 Temperature+Pressure+Humidity i2c sensor.
|
/// This class implements support for the BME280 Temperature+Pressure+Humidity sensor.
|
||||||
class BME280Component : public PollingComponent, public i2c::I2CDevice {
|
class BME280Component : public PollingComponent {
|
||||||
public:
|
public:
|
||||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||||
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
|
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
|
||||||
|
@ -91,6 +90,11 @@ class BME280Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
uint16_t read_u16_le_(uint8_t a_register);
|
uint16_t read_u16_le_(uint8_t a_register);
|
||||||
int16_t read_s16_le_(uint8_t a_register);
|
int16_t read_s16_le_(uint8_t a_register);
|
||||||
|
|
||||||
|
virtual bool read_byte(uint8_t a_register, uint8_t *data) = 0;
|
||||||
|
virtual bool write_byte(uint8_t a_register, uint8_t data) = 0;
|
||||||
|
virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
|
||||||
|
virtual bool read_byte_16(uint8_t a_register, uint16_t *data) = 0;
|
||||||
|
|
||||||
BME280CalibrationData calibration_;
|
BME280CalibrationData calibration_;
|
||||||
BME280Oversampling temperature_oversampling_{BME280_OVERSAMPLING_16X};
|
BME280Oversampling temperature_oversampling_{BME280_OVERSAMPLING_16X};
|
||||||
BME280Oversampling pressure_oversampling_{BME280_OVERSAMPLING_16X};
|
BME280Oversampling pressure_oversampling_{BME280_OVERSAMPLING_16X};
|
||||||
|
@ -106,5 +110,5 @@ class BME280Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
} error_code_{NONE};
|
} error_code_{NONE};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace bme280
|
} // namespace bme280_base
|
||||||
} // namespace esphome
|
} // namespace esphome
|
106
esphome/components/bme280_base/sensor.py
Normal file
106
esphome/components/bme280_base/sensor.py
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_HUMIDITY,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_IIR_FILTER,
|
||||||
|
CONF_OVERSAMPLING,
|
||||||
|
CONF_PRESSURE,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_HUMIDITY,
|
||||||
|
DEVICE_CLASS_PRESSURE,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_HECTOPASCAL,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
)
|
||||||
|
|
||||||
|
bme280_ns = cg.esphome_ns.namespace("bme280_base")
|
||||||
|
BME280Oversampling = bme280_ns.enum("BME280Oversampling")
|
||||||
|
OVERSAMPLING_OPTIONS = {
|
||||||
|
"NONE": BME280Oversampling.BME280_OVERSAMPLING_NONE,
|
||||||
|
"1X": BME280Oversampling.BME280_OVERSAMPLING_1X,
|
||||||
|
"2X": BME280Oversampling.BME280_OVERSAMPLING_2X,
|
||||||
|
"4X": BME280Oversampling.BME280_OVERSAMPLING_4X,
|
||||||
|
"8X": BME280Oversampling.BME280_OVERSAMPLING_8X,
|
||||||
|
"16X": BME280Oversampling.BME280_OVERSAMPLING_16X,
|
||||||
|
}
|
||||||
|
|
||||||
|
BME280IIRFilter = bme280_ns.enum("BME280IIRFilter")
|
||||||
|
IIR_FILTER_OPTIONS = {
|
||||||
|
"OFF": BME280IIRFilter.BME280_IIR_FILTER_OFF,
|
||||||
|
"2X": BME280IIRFilter.BME280_IIR_FILTER_2X,
|
||||||
|
"4X": BME280IIRFilter.BME280_IIR_FILTER_4X,
|
||||||
|
"8X": BME280IIRFilter.BME280_IIR_FILTER_8X,
|
||||||
|
"16X": BME280IIRFilter.BME280_IIR_FILTER_16X,
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG_SCHEMA_BASE = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||||
|
OVERSAMPLING_OPTIONS, upper=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_PRESSURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||||
|
OVERSAMPLING_OPTIONS, upper=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_HUMIDITY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||||
|
OVERSAMPLING_OPTIONS, upper=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
|
||||||
|
IIR_FILTER_OPTIONS, upper=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
).extend(cv.polling_component_schema("60s"))
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config, func=None):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
if func is not None:
|
||||||
|
await func(var, config)
|
||||||
|
|
||||||
|
if temperature_config := config.get(CONF_TEMPERATURE):
|
||||||
|
sens = await sensor.new_sensor(temperature_config)
|
||||||
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
|
cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING]))
|
||||||
|
|
||||||
|
if pressure_config := config.get(CONF_PRESSURE):
|
||||||
|
sens = await sensor.new_sensor(pressure_config)
|
||||||
|
cg.add(var.set_pressure_sensor(sens))
|
||||||
|
cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING]))
|
||||||
|
|
||||||
|
if humidity_config := config.get(CONF_HUMIDITY):
|
||||||
|
sens = await sensor.new_sensor(humidity_config)
|
||||||
|
cg.add(var.set_humidity_sensor(sens))
|
||||||
|
cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING]))
|
||||||
|
|
||||||
|
cg.add(var.set_iir_filter(config[CONF_IIR_FILTER]))
|
30
esphome/components/bme280_i2c/bme280_i2c.cpp
Normal file
30
esphome/components/bme280_i2c/bme280_i2c.cpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "bme280_i2c.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "../bme280_base/bme280_base.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace bme280_i2c {
|
||||||
|
|
||||||
|
bool BME280I2CComponent::read_byte(uint8_t a_register, uint8_t *data) {
|
||||||
|
return I2CDevice::read_byte(a_register, data);
|
||||||
|
};
|
||||||
|
bool BME280I2CComponent::write_byte(uint8_t a_register, uint8_t data) {
|
||||||
|
return I2CDevice::write_byte(a_register, data);
|
||||||
|
};
|
||||||
|
bool BME280I2CComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
|
||||||
|
return I2CDevice::read_bytes(a_register, data, len);
|
||||||
|
};
|
||||||
|
bool BME280I2CComponent::read_byte_16(uint8_t a_register, uint16_t *data) {
|
||||||
|
return I2CDevice::read_byte_16(a_register, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
void BME280I2CComponent::dump_config() {
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
BME280Component::dump_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bme280_i2c
|
||||||
|
} // namespace esphome
|
20
esphome/components/bme280_i2c/bme280_i2c.h
Normal file
20
esphome/components/bme280_i2c/bme280_i2c.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/bme280_base/bme280_base.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace bme280_i2c {
|
||||||
|
|
||||||
|
static const char *const TAG = "bme280_i2c.sensor";
|
||||||
|
|
||||||
|
class BME280I2CComponent : public esphome::bme280_base::BME280Component, public i2c::I2CDevice {
|
||||||
|
bool read_byte(uint8_t a_register, uint8_t *data) override;
|
||||||
|
bool write_byte(uint8_t a_register, uint8_t data) override;
|
||||||
|
bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
|
||||||
|
bool read_byte_16(uint8_t a_register, uint16_t *data) override;
|
||||||
|
void dump_config() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bme280_i2c
|
||||||
|
} // namespace esphome
|
19
esphome/components/bme280_i2c/sensor.py
Normal file
19
esphome/components/bme280_i2c/sensor.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import i2c
|
||||||
|
from ..bme280_base.sensor import to_code as to_code_base, cv, CONFIG_SCHEMA_BASE
|
||||||
|
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
AUTO_LOAD = ["bme280_base"]
|
||||||
|
|
||||||
|
bme280_ns = cg.esphome_ns.namespace("bme280_i2c")
|
||||||
|
BME280I2CComponent = bme280_ns.class_(
|
||||||
|
"BME280I2CComponent", cg.PollingComponent, i2c.I2CDevice
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(
|
||||||
|
i2c.i2c_device_schema(default_address=0x77)
|
||||||
|
).extend({cv.GenerateID(): cv.declare_id(BME280I2CComponent)})
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
await to_code_base(config, func=i2c.register_i2c_device)
|
1
esphome/components/bme280_spi/__init__.py
Normal file
1
esphome/components/bme280_spi/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@apbodrov"]
|
66
esphome/components/bme280_spi/bme280_spi.cpp
Normal file
66
esphome/components/bme280_spi/bme280_spi.cpp
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "bme280_spi.h"
|
||||||
|
#include <esphome/components/bme280_base/bme280_base.h>
|
||||||
|
|
||||||
|
int set_bit(uint8_t num, int position) {
|
||||||
|
int mask = 1 << position;
|
||||||
|
return num | mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
int clear_bit(uint8_t num, int position) {
|
||||||
|
int mask = 1 << position;
|
||||||
|
return num & ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace bme280_spi {
|
||||||
|
|
||||||
|
void BME280SPIComponent::setup() {
|
||||||
|
this->spi_setup();
|
||||||
|
BME280Component::setup();
|
||||||
|
};
|
||||||
|
|
||||||
|
// In SPI mode, only 7 bits of the register addresses are used; the MSB of register address is not used
|
||||||
|
// and replaced by a read/write bit (RW = ‘0’ for write and RW = ‘1’ for read).
|
||||||
|
// Example: address 0xF7 is accessed by using SPI register address 0x77. For write access, the byte
|
||||||
|
// 0x77 is transferred, for read access, the byte 0xF7 is transferred.
|
||||||
|
// https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf
|
||||||
|
|
||||||
|
bool BME280SPIComponent::read_byte(uint8_t a_register, uint8_t *data) {
|
||||||
|
this->enable();
|
||||||
|
// cause: *data = this->delegate_->transfer(tmp) doesnt work
|
||||||
|
this->delegate_->transfer(set_bit(a_register, 7));
|
||||||
|
*data = this->delegate_->transfer(0);
|
||||||
|
this->disable();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BME280SPIComponent::write_byte(uint8_t a_register, uint8_t data) {
|
||||||
|
this->enable();
|
||||||
|
this->delegate_->transfer(clear_bit(a_register, 7));
|
||||||
|
this->delegate_->transfer(data);
|
||||||
|
this->disable();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BME280SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
|
||||||
|
this->enable();
|
||||||
|
this->delegate_->transfer(set_bit(a_register, 7));
|
||||||
|
this->delegate_->read_array(data, len);
|
||||||
|
this->disable();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BME280SPIComponent::read_byte_16(uint8_t a_register, uint16_t *data) {
|
||||||
|
this->enable();
|
||||||
|
this->delegate_->transfer(set_bit(a_register, 7));
|
||||||
|
((uint8_t *) data)[1] = this->delegate_->transfer(0);
|
||||||
|
((uint8_t *) data)[0] = this->delegate_->transfer(0);
|
||||||
|
this->disable();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bme280_spi
|
||||||
|
} // namespace esphome
|
20
esphome/components/bme280_spi/bme280_spi.h
Normal file
20
esphome/components/bme280_spi/bme280_spi.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/bme280_base/bme280_base.h"
|
||||||
|
#include "esphome/components/spi/spi.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace bme280_spi {
|
||||||
|
|
||||||
|
class BME280SPIComponent : public esphome::bme280_base::BME280Component,
|
||||||
|
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||||
|
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_200KHZ> {
|
||||||
|
void setup() override;
|
||||||
|
bool read_byte(uint8_t a_register, uint8_t *data) override;
|
||||||
|
bool write_byte(uint8_t a_register, uint8_t data) override;
|
||||||
|
bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
|
||||||
|
bool read_byte_16(uint8_t a_register, uint16_t *data) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bme280_spi
|
||||||
|
} // namespace esphome
|
24
esphome/components/bme280_spi/sensor.py
Normal file
24
esphome/components/bme280_spi/sensor.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import spi
|
||||||
|
from esphome.components.bme280_base.sensor import (
|
||||||
|
to_code as to_code_base,
|
||||||
|
cv,
|
||||||
|
CONFIG_SCHEMA_BASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
DEPENDENCIES = ["spi"]
|
||||||
|
AUTO_LOAD = ["bme280_base"]
|
||||||
|
|
||||||
|
|
||||||
|
bme280_spi_ns = cg.esphome_ns.namespace("bme280_spi")
|
||||||
|
BME280SPIComponent = bme280_spi_ns.class_(
|
||||||
|
"BME280SPIComponent", cg.PollingComponent, spi.SPIDevice
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(spi.spi_device_schema()).extend(
|
||||||
|
{cv.GenerateID(): cv.declare_id(BME280SPIComponent)}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
await to_code_base(config, func=spi.register_spi_device)
|
|
@ -11,6 +11,7 @@ MULTI_CONF = True
|
||||||
CONF_BME680_BSEC_ID = "bme680_bsec_id"
|
CONF_BME680_BSEC_ID = "bme680_bsec_id"
|
||||||
CONF_TEMPERATURE_OFFSET = "temperature_offset"
|
CONF_TEMPERATURE_OFFSET = "temperature_offset"
|
||||||
CONF_IAQ_MODE = "iaq_mode"
|
CONF_IAQ_MODE = "iaq_mode"
|
||||||
|
CONF_SUPPLY_VOLTAGE = "supply_voltage"
|
||||||
CONF_SAMPLE_RATE = "sample_rate"
|
CONF_SAMPLE_RATE = "sample_rate"
|
||||||
CONF_STATE_SAVE_INTERVAL = "state_save_interval"
|
CONF_STATE_SAVE_INTERVAL = "state_save_interval"
|
||||||
|
|
||||||
|
@ -22,6 +23,12 @@ IAQ_MODE_OPTIONS = {
|
||||||
"MOBILE": IAQMode.IAQ_MODE_MOBILE,
|
"MOBILE": IAQMode.IAQ_MODE_MOBILE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SupplyVoltage = bme680_bsec_ns.enum("SupplyVoltage")
|
||||||
|
SUPPLY_VOLTAGE_OPTIONS = {
|
||||||
|
"1.8V": SupplyVoltage.SUPPLY_VOLTAGE_1V8,
|
||||||
|
"3.3V": SupplyVoltage.SUPPLY_VOLTAGE_3V3,
|
||||||
|
}
|
||||||
|
|
||||||
SampleRate = bme680_bsec_ns.enum("SampleRate")
|
SampleRate = bme680_bsec_ns.enum("SampleRate")
|
||||||
SAMPLE_RATE_OPTIONS = {
|
SAMPLE_RATE_OPTIONS = {
|
||||||
"LP": SampleRate.SAMPLE_RATE_LP,
|
"LP": SampleRate.SAMPLE_RATE_LP,
|
||||||
|
@ -40,6 +47,9 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum(
|
cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum(
|
||||||
IAQ_MODE_OPTIONS, upper=True
|
IAQ_MODE_OPTIONS, upper=True
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_SUPPLY_VOLTAGE, default="3.3V"): cv.enum(
|
||||||
|
SUPPLY_VOLTAGE_OPTIONS, upper=True
|
||||||
|
),
|
||||||
cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum(
|
cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum(
|
||||||
SAMPLE_RATE_OPTIONS, upper=True
|
SAMPLE_RATE_OPTIONS, upper=True
|
||||||
),
|
),
|
||||||
|
@ -67,6 +77,7 @@ async def to_code(config):
|
||||||
cg.add(var.set_device_id(str(config[CONF_ID])))
|
cg.add(var.set_device_id(str(config[CONF_ID])))
|
||||||
cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET]))
|
cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET]))
|
||||||
cg.add(var.set_iaq_mode(config[CONF_IAQ_MODE]))
|
cg.add(var.set_iaq_mode(config[CONF_IAQ_MODE]))
|
||||||
|
cg.add(var.set_supply_voltage(config[CONF_SUPPLY_VOLTAGE]))
|
||||||
cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE]))
|
cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE]))
|
||||||
cg.add(
|
cg.add(
|
||||||
var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds)
|
var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue