mirror of
https://github.com/esphome/esphome.git
synced 2026-01-26 22:42:06 -07:00
Compare commits
329 Commits
action_cal
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aaf10b6a8 | ||
|
|
33f545a8e3 | ||
|
|
d056e1040b | ||
|
|
75a78b2bf3 | ||
|
|
cd6314dc96 | ||
|
|
f91bffff9a | ||
|
|
5cbe9af485 | ||
|
|
a7fbecb25c | ||
|
|
bf92d94863 | ||
|
|
9c3817f544 | ||
|
|
ee9e3315b6 | ||
|
|
67dea1e538 | ||
|
|
003b9c6c3f | ||
|
|
2f1a345905 | ||
|
|
7ef933abec | ||
|
|
4ddd40bcfb | ||
|
|
8ae901b3f1 | ||
|
|
bc49174920 | ||
|
|
123ee02d39 | ||
|
|
0cc8055757 | ||
|
|
27a212c14d | ||
|
|
65dc182526 | ||
|
|
dd91039ff1 | ||
|
|
1c9a9c7536 | ||
|
|
011407ea8b | ||
|
|
1141e83a7c | ||
|
|
214ce95cf3 | ||
|
|
3a7b83ba93 | ||
|
|
cc2f3d85dc | ||
|
|
723f67d5e2 | ||
|
|
70e45706d9 | ||
|
|
56a2a2269f | ||
|
|
d6841ba33a | ||
|
|
10cbd0164a | ||
|
|
d285706b41 | ||
|
|
ef469c20df | ||
|
|
6870d3dc50 | ||
|
|
9cc39621a6 | ||
|
|
c4f7d09553 | ||
|
|
ab1661ef22 | ||
|
|
ccbf17d5ab | ||
|
|
bac96086be | ||
|
|
c32e4bc65b | ||
|
|
993765d732 | ||
|
|
8d84fe0113 | ||
|
|
58746b737f | ||
|
|
f93e843972 | ||
|
|
60968d311b | ||
|
|
30584e2e96 | ||
|
|
468ae39a9e | ||
|
|
beb9c8d328 | ||
|
|
cdda3fb7cc | ||
|
|
bba00a3906 | ||
|
|
42e50ca178 | ||
|
|
165e362a1b | ||
|
|
e4763f8e71 | ||
|
|
9fddd0659e | ||
|
|
faea546a0e | ||
|
|
069db2e128 | ||
|
|
5f2203b915 | ||
|
|
5c67e04fef | ||
|
|
0cdcacc7fc | ||
|
|
cfb61bc50a | ||
|
|
547c985672 | ||
|
|
44e624d7a7 | ||
|
|
5779e3e6e4 | ||
|
|
3184717607 | ||
|
|
e8972c65c8 | ||
|
|
71cda05073 | ||
|
|
3dbebb728d | ||
|
|
f938de16af | ||
|
|
ec791063b3 | ||
|
|
fb984cd052 | ||
|
|
85181779d1 | ||
|
|
95b23702e4 | ||
|
|
95eebcd74f | ||
|
|
3c3d5c2fca | ||
|
|
811ac81320 | ||
|
|
f01bd68a4b | ||
|
|
5433c0f707 | ||
|
|
b06cce9eeb | ||
|
|
65bcfee035 | ||
|
|
9261b9ecaa | ||
|
|
6725e6c01e | ||
|
|
effbcece49 | ||
|
|
98a926f37f | ||
|
|
110c173eac | ||
|
|
6008abae62 | ||
|
|
04e102f344 | ||
|
|
bb67b1ca1e | ||
|
|
6d7956a062 | ||
|
|
afbbdd1492 | ||
|
|
b06568c132 | ||
|
|
3c5fc638d5 | ||
|
|
ddb762f8f5 | ||
|
|
4ac7fe84b4 | ||
|
|
d6a41ed51e | ||
|
|
8d1379a275 | ||
|
|
5bbf9153ca | ||
|
|
a1c4d56268 | ||
|
|
a9ce3df04c | ||
|
|
99aa83564e | ||
|
|
aa5092bdc2 | ||
|
|
645832a070 | ||
|
|
19c1d3aee7 | ||
|
|
ce5ec7a78f | ||
|
|
ebf589560d | ||
|
|
8dd1aec606 | ||
|
|
9d967b01c8 | ||
|
|
11e0d536e4 | ||
|
|
673f46f761 | ||
|
|
4abae8d445 | ||
|
|
e62368e058 | ||
|
|
5345c96ff3 | ||
|
|
333ace25c9 | ||
|
|
6014bba3d1 | ||
|
|
5f2394ef80 | ||
|
|
29555c0ddc | ||
|
|
37eaf10f75 | ||
|
|
0b60fd0c8c | ||
|
|
fc16ad806a | ||
|
|
7e43abd86f | ||
|
|
7a2734fae9 | ||
|
|
346f3d38d5 | ||
|
|
fbde91358c | ||
|
|
54d6825323 | ||
|
|
307c3e1061 | ||
|
|
df74d307c8 | ||
|
|
acdc7bd892 | ||
|
|
1095bde2db | ||
|
|
258b73d7f6 | ||
|
|
31608543c2 | ||
|
|
41a060668c | ||
|
|
6bad697fc6 | ||
|
|
3ca5e5e4e4 | ||
|
|
cd4cb8b3ec | ||
|
|
1f3a0490a7 | ||
|
|
b08d871add | ||
|
|
15f0986a59 | ||
|
|
90edf32acf | ||
|
|
3c0f43db9e | ||
|
|
6edecd3d45 | ||
|
|
055c00f1ac | ||
|
|
7dc40881e2 | ||
|
|
b04373687e | ||
|
|
b89c127f62 | ||
|
|
47dc5d0a1f | ||
|
|
21886dd3ac | ||
|
|
85a5a26519 | ||
|
|
79ccacd6d6 | ||
|
|
e2319ba651 | ||
|
|
ed4ebffa74 | ||
|
|
c213de4861 | ||
|
|
6cf320fd60 | ||
|
|
aeea340bc6 | ||
|
|
d0e50ed030 | ||
|
|
280d460025 | ||
|
|
ea70faf642 | ||
|
|
5d7b38b261 | ||
|
|
e88093ca60 | ||
|
|
b48d4ab785 | ||
|
|
8ade9dfc10 | ||
|
|
4e0e7796de | ||
|
|
62b6c9bf7c | ||
|
|
b5fe271d6b | ||
|
|
5d787e2512 | ||
|
|
8998ef0bc3 | ||
|
|
8ec31dd769 | ||
|
|
0193464f92 | ||
|
|
1996bc425f | ||
|
|
a0d3d54d69 | ||
|
|
ee264d0fd4 | ||
|
|
892e9b006f | ||
|
|
f8bd4ef57d | ||
|
|
bfcc0e26a3 | ||
|
|
86a1b4cf69 | ||
|
|
d8a28f6fba | ||
|
|
e80a940222 | ||
|
|
e99dbe05f7 | ||
|
|
f453a8d9a1 | ||
|
|
126190d26a | ||
|
|
e40201a98d | ||
|
|
8142f5db44 | ||
|
|
98ccab87a7 | ||
|
|
b9e72a8774 | ||
|
|
d9fc625c6a | ||
|
|
dfbf79d6d6 | ||
|
|
ea0fac96cb | ||
|
|
3182222d60 | ||
|
|
d8849b16f2 | ||
|
|
635983f163 | ||
|
|
6cbe672004 | ||
|
|
226867b05c | ||
|
|
67871a1683 | ||
|
|
f60c03e350 | ||
|
|
eb66429144 | ||
|
|
0f3bac5dd6 | ||
|
|
5b92d0b89e | ||
|
|
052b05df56 | ||
|
|
7b0db659d1 | ||
|
|
2f7270cf8f | ||
|
|
b44727aee6 | ||
|
|
1a55254258 | ||
|
|
baf2b0e3c9 | ||
|
|
680e92a226 | ||
|
|
db0b32bfc9 | ||
|
|
21794e28e5 | ||
|
|
728236270c | ||
|
|
01cdc4ed58 | ||
|
|
d6a0c8ffbb | ||
|
|
4cc0f874f7 | ||
|
|
ed58b9372f | ||
|
|
ee2a81923b | ||
|
|
0a1e7ee50b | ||
|
|
4d4283bcfa | ||
|
|
e4fb6988ff | ||
|
|
d31b733dce | ||
|
|
b25a2f8d8e | ||
|
|
3f892711c7 | ||
|
|
798d3bd956 | ||
|
|
77df3933db | ||
|
|
19514ccdf4 | ||
|
|
2947642ca5 | ||
|
|
60e333db08 | ||
|
|
d8463f4813 | ||
|
|
e1800d2fe2 | ||
|
|
50aa4b1992 | ||
|
|
edb303e495 | ||
|
|
973fc4c5dc | ||
|
|
f88e8fc43b | ||
|
|
d830787c71 | ||
|
|
c4c31a2e8e | ||
|
|
e6790f0042 | ||
|
|
ec7f72e280 | ||
|
|
6f29dbd6f1 | ||
|
|
9caf78aa7e | ||
|
|
1f4221abfa | ||
|
|
92808a09c7 | ||
|
|
e54d5ee898 | ||
|
|
bbe1155518 | ||
|
|
69d7b6e921 | ||
|
|
510c874061 | ||
|
|
f7ad324d81 | ||
|
|
58a9e30017 | ||
|
|
52ac9e1861 | ||
|
|
c5e4a60884 | ||
|
|
a680884138 | ||
|
|
6832efbacc | ||
|
|
3057a0484f | ||
|
|
bc78f80f77 | ||
|
|
916b028fb2 | ||
|
|
16adae7359 | ||
|
|
4906f87751 | ||
|
|
5b37d2fb27 | ||
|
|
68affe0b9c | ||
|
|
8263a8273f | ||
|
|
14b7539094 | ||
|
|
b37cb812a7 | ||
|
|
42491569c8 | ||
|
|
b1230ec6bb | ||
|
|
4eda9e965f | ||
|
|
d2528af649 | ||
|
|
2eabc1b96b | ||
|
|
535c3eb2a2 | ||
|
|
20f937692e | ||
|
|
c2737ca3bb | ||
|
|
00cc9e44b6 | ||
|
|
c151b2da67 | ||
|
|
dacd185afb | ||
|
|
f88cf1b83a | ||
|
|
3f6412ba07 | ||
|
|
1ad0969099 | ||
|
|
737c2b8732 | ||
|
|
9030dc9d4e | ||
|
|
0b5a3506cc | ||
|
|
3c63ff5e36 | ||
|
|
0427350101 | ||
|
|
41dceb76ec | ||
|
|
6380458d78 | ||
|
|
0dc5a7c9a4 | ||
|
|
9003844eda | ||
|
|
22a4ec69c2 | ||
|
|
9d42bfd161 | ||
|
|
49c881d067 | ||
|
|
78aee4f498 | ||
|
|
9da2c08f36 | ||
|
|
03f3deff41 | ||
|
|
f1e5d3a39a | ||
|
|
2f6863230d | ||
|
|
0de91e6648 | ||
|
|
f44036310c | ||
|
|
66b4af1777 | ||
|
|
068b497b9b | ||
|
|
d6fa1d6e5f | ||
|
|
d5f557ad1c | ||
|
|
9c5f4e5288 | ||
|
|
c8cc29a991 | ||
|
|
8b49d465f8 | ||
|
|
47ee2f4ad9 | ||
|
|
2793e33baf | ||
|
|
5dfdd05122 | ||
|
|
be12e3667a | ||
|
|
52c631384a | ||
|
|
45e000f091 | ||
|
|
e45cad45fe | ||
|
|
3d74d1e7f0 | ||
|
|
a060d1d044 | ||
|
|
733f57da50 | ||
|
|
4d96c60696 | ||
|
|
3d40979c96 | ||
|
|
7fed9144a6 | ||
|
|
7abb374f2a | ||
|
|
5d90f170e5 | ||
|
|
6e01c4f86e | ||
|
|
f4c17e15ea | ||
|
|
d6507ce329 | ||
|
|
9504e92458 | ||
|
|
3911991de2 | ||
|
|
dede47477b | ||
|
|
dca8def0f2 | ||
|
|
a1727a8901 | ||
|
|
48f5296d24 | ||
|
|
1327776d5b | ||
|
|
df4a3e8915 | ||
|
|
6823e17b3b | ||
|
|
675103bed0 | ||
|
|
6c043be4d3 | ||
|
|
e9469cbe48 | ||
|
|
5890cdf69a |
@@ -1 +1 @@
|
|||||||
d272a88e8ca28ae9340a9a03295a566432a52cb696501908f57764475bf7ca65
|
a172e2f65981e98354cc6b5ecf69bdb055dd13602226042ab2c7acd037a2bf41
|
||||||
|
|||||||
96
.claude/skills/pr-workflow/SKILL.md
Normal file
96
.claude/skills/pr-workflow/SKILL.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
name: pr-workflow
|
||||||
|
description: Create pull requests for esphome. Use when creating PRs, submitting changes, or preparing contributions.
|
||||||
|
allowed-tools: Read, Bash, Glob, Grep
|
||||||
|
---
|
||||||
|
|
||||||
|
# ESPHome PR Workflow
|
||||||
|
|
||||||
|
When creating a pull request for esphome, follow these steps:
|
||||||
|
|
||||||
|
## 1. Create Branch from Upstream
|
||||||
|
|
||||||
|
Always base your branch on **upstream** (not origin/fork) to ensure you have the latest code:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git fetch upstream
|
||||||
|
git checkout -b <branch-name> upstream/dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Read the PR Template
|
||||||
|
|
||||||
|
Before creating a PR, read `.github/PULL_REQUEST_TEMPLATE.md` to understand required fields.
|
||||||
|
|
||||||
|
## 3. Create the PR
|
||||||
|
|
||||||
|
Use `gh pr create` with the **full template** filled in. Never skip or abbreviate sections.
|
||||||
|
|
||||||
|
Required fields:
|
||||||
|
- **What does this implement/fix?**: Brief description of changes
|
||||||
|
- **Types of changes**: Check ONE appropriate box (Bugfix, New feature, Breaking change, etc.)
|
||||||
|
- **Related issue**: Use `fixes <link>` syntax if applicable
|
||||||
|
- **Pull request in esphome-docs**: Link if docs are needed
|
||||||
|
- **Test Environment**: Check platforms you tested on
|
||||||
|
- **Example config.yaml**: Include working example YAML
|
||||||
|
- **Checklist**: Verify code is tested and tests added
|
||||||
|
|
||||||
|
## 4. Example PR Body
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# What does this implement/fix?
|
||||||
|
|
||||||
|
<describe your changes here>
|
||||||
|
|
||||||
|
## Types of changes
|
||||||
|
|
||||||
|
- [ ] Bugfix (non-breaking change which fixes an issue)
|
||||||
|
- [x] New feature (non-breaking change which adds functionality)
|
||||||
|
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||||
|
- [ ] Developer breaking change (an API change that could break external components)
|
||||||
|
- [ ] Code quality improvements to existing code or addition of tests
|
||||||
|
- [ ] Other
|
||||||
|
|
||||||
|
**Related issue or feature (if applicable):**
|
||||||
|
|
||||||
|
- fixes https://github.com/esphome/esphome/issues/XXX
|
||||||
|
|
||||||
|
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):**
|
||||||
|
|
||||||
|
- esphome/esphome-docs#XXX
|
||||||
|
|
||||||
|
## Test Environment
|
||||||
|
|
||||||
|
- [x] ESP32
|
||||||
|
- [x] ESP32 IDF
|
||||||
|
- [ ] ESP8266
|
||||||
|
- [ ] RP2040
|
||||||
|
- [ ] BK72xx
|
||||||
|
- [ ] RTL87xx
|
||||||
|
- [ ] LN882x
|
||||||
|
- [ ] nRF52840
|
||||||
|
|
||||||
|
## Example entry for `config.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Example config.yaml
|
||||||
|
component_name:
|
||||||
|
id: my_component
|
||||||
|
option: value
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checklist:
|
||||||
|
- [x] The code change is tested and works locally.
|
||||||
|
- [x] Tests have been added to verify that the new code works (under `tests/` folder).
|
||||||
|
|
||||||
|
If user exposed functionality or configuration variables are added/changed:
|
||||||
|
- [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Push and Create PR
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push -u origin <branch-name>
|
||||||
|
gh pr create --repo esphome/esphome --base dev --title "[component] Brief description"
|
||||||
|
```
|
||||||
|
|
||||||
|
Title should be prefixed with the component name in brackets, e.g. `[safe_mode] Add feature`.
|
||||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -27,6 +27,7 @@
|
|||||||
- [ ] RP2040
|
- [ ] RP2040
|
||||||
- [ ] BK72xx
|
- [ ] BK72xx
|
||||||
- [ ] RTL87xx
|
- [ ] RTL87xx
|
||||||
|
- [ ] LN882x
|
||||||
- [ ] nRF52840
|
- [ ] nRF52840
|
||||||
|
|
||||||
## Example entry for `config.yaml`:
|
## Example entry for `config.yaml`:
|
||||||
|
|||||||
4
.github/actions/restore-python/action.yml
vendored
4
.github/actions/restore-python/action.yml
vendored
@@ -17,12 +17,12 @@ 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@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.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@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
|
|||||||
2
.github/workflows/auto-label-pr.yml
vendored
2
.github/workflows/auto-label-pr.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
if: github.event.action != 'labeled' || github.event.sender.type != 'Bot'
|
if: github.event.action != 'labeled' || github.event.sender.type != 'Bot'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
|
|||||||
4
.github/workflows/ci-api-proto.yml
vendored
4
.github/workflows/ci-api-proto.yml
vendored
@@ -21,9 +21,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/ci-clang-tidy-hash.yml
vendored
4
.github/workflows/ci-clang-tidy-hash.yml
vendored
@@ -21,10 +21,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/ci-docker.yml
vendored
4
.github/workflows/ci-docker.yml
vendored
@@ -43,9 +43,9 @@ jobs:
|
|||||||
- "docker"
|
- "docker"
|
||||||
# - "lint"
|
# - "lint"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Check out code from base repository
|
- name: Check out code from base repository
|
||||||
if: steps.pr.outputs.skip != 'true'
|
if: steps.pr.outputs.skip != 'true'
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
# Always check out from the base repository (esphome/esphome), never from forks
|
# Always check out from the base repository (esphome/esphome), never from forks
|
||||||
# Use the PR's target branch to ensure we run trusted code from the main repo
|
# Use the PR's target branch to ensure we run trusted code from the main repo
|
||||||
|
|||||||
64
.github/workflows/ci.yml
vendored
64
.github/workflows/ci.yml
vendored
@@ -36,18 +36,18 @@ jobs:
|
|||||||
cache-key: ${{ steps.cache-key.outputs.key }}
|
cache-key: ${{ steps.cache-key.outputs.key }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Generate cache-key
|
- name: Generate cache-key
|
||||||
id: cache-key
|
id: cache-key
|
||||||
run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
|
run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.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@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
if: needs.determine-jobs.outputs.python-linters == 'true'
|
if: needs.determine-jobs.outputs.python-linters == 'true'
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
@@ -91,7 +91,7 @@ jobs:
|
|||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
@@ -132,7 +132,7 @@ jobs:
|
|||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
id: restore-python
|
id: restore-python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
@@ -157,7 +157,7 @@ jobs:
|
|||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
- name: Save Python virtual environment cache
|
- name: Save Python virtual environment cache
|
||||||
if: github.ref == 'refs/heads/dev'
|
if: github.ref == 'refs/heads/dev'
|
||||||
uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache/save@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
key: ${{ runner.os }}-${{ steps.restore-python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
|
key: ${{ runner.os }}-${{ steps.restore-python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
|
||||||
@@ -183,7 +183,7 @@ jobs:
|
|||||||
component-test-batches: ${{ steps.determine.outputs.component-test-batches }}
|
component-test-batches: ${{ steps.determine.outputs.component-test-batches }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
# Fetch enough history to find the merge base
|
# Fetch enough history to find the merge base
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
@@ -193,7 +193,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: Restore components graph cache
|
- name: Restore components graph cache
|
||||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: .temp/components_graph.json
|
path: .temp/components_graph.json
|
||||||
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
|
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
|
||||||
@@ -223,7 +223,7 @@ jobs:
|
|||||||
echo "component-test-batches=$(echo "$output" | jq -c '.component_test_batches')" >> $GITHUB_OUTPUT
|
echo "component-test-batches=$(echo "$output" | jq -c '.component_test_batches')" >> $GITHUB_OUTPUT
|
||||||
- name: Save components graph cache
|
- name: Save components graph cache
|
||||||
if: github.ref == 'refs/heads/dev'
|
if: github.ref == 'refs/heads/dev'
|
||||||
uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache/save@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: .temp/components_graph.json
|
path: .temp/components_graph.json
|
||||||
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
|
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
|
||||||
@@ -237,15 +237,15 @@ jobs:
|
|||||||
if: needs.determine-jobs.outputs.integration-tests == 'true'
|
if: needs.determine-jobs.outputs.integration-tests == 'true'
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Set up Python 3.13
|
- name: Set up Python 3.13
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.13"
|
python-version: "3.13"
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
id: cache-venv
|
id: cache-venv
|
||||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
|
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
|
||||||
@@ -273,7 +273,7 @@ jobs:
|
|||||||
if: github.event_name == 'pull_request' && (needs.determine-jobs.outputs.cpp-unit-tests-run-all == 'true' || needs.determine-jobs.outputs.cpp-unit-tests-components != '[]')
|
if: github.event_name == 'pull_request' && (needs.determine-jobs.outputs.cpp-unit-tests-run-all == 'true' || needs.determine-jobs.outputs.cpp-unit-tests-components != '[]')
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
@@ -321,7 +321,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
# Need history for HEAD~1 to work for checking changed files
|
# Need history for HEAD~1 to work for checking changed files
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
@@ -334,14 +334,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: github.ref == 'refs/heads/dev'
|
if: github.ref == 'refs/heads/dev'
|
||||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
||||||
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: github.ref != 'refs/heads/dev'
|
if: github.ref != 'refs/heads/dev'
|
||||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
||||||
@@ -400,7 +400,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
# Need history for HEAD~1 to work for checking changed files
|
# Need history for HEAD~1 to work for checking changed files
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
@@ -413,14 +413,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: github.ref == 'refs/heads/dev'
|
if: github.ref == 'refs/heads/dev'
|
||||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
||||||
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: github.ref != 'refs/heads/dev'
|
if: github.ref != 'refs/heads/dev'
|
||||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
||||||
@@ -489,7 +489,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
# Need history for HEAD~1 to work for checking changed files
|
# Need history for HEAD~1 to work for checking changed files
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
@@ -502,14 +502,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: github.ref == 'refs/heads/dev'
|
if: github.ref == 'refs/heads/dev'
|
||||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
||||||
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: github.ref != 'refs/heads/dev'
|
if: github.ref != 'refs/heads/dev'
|
||||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
|
||||||
@@ -577,7 +577,7 @@ jobs:
|
|||||||
version: 1.0
|
version: 1.0
|
||||||
|
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
@@ -662,7 +662,7 @@ jobs:
|
|||||||
if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release')
|
if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release')
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
@@ -688,7 +688,7 @@ jobs:
|
|||||||
skip: ${{ steps.check-script.outputs.skip }}
|
skip: ${{ steps.check-script.outputs.skip }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out target branch
|
- name: Check out target branch
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.base_ref }}
|
ref: ${{ github.base_ref }}
|
||||||
|
|
||||||
@@ -735,7 +735,7 @@ jobs:
|
|||||||
- name: Restore cached memory analysis
|
- name: Restore cached memory analysis
|
||||||
id: cache-memory-analysis
|
id: cache-memory-analysis
|
||||||
if: steps.check-script.outputs.skip != 'true'
|
if: steps.check-script.outputs.skip != 'true'
|
||||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: memory-analysis-target.json
|
path: memory-analysis-target.json
|
||||||
key: ${{ steps.cache-key.outputs.cache-key }}
|
key: ${{ steps.cache-key.outputs.cache-key }}
|
||||||
@@ -759,7 +759,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: steps.check-script.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true'
|
if: steps.check-script.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true'
|
||||||
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
|
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
|
||||||
@@ -800,7 +800,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Save memory analysis to cache
|
- name: Save memory analysis to cache
|
||||||
if: steps.check-script.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' && steps.build.outcome == 'success'
|
if: steps.check-script.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' && steps.build.outcome == 'success'
|
||||||
uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache/save@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: memory-analysis-target.json
|
path: memory-analysis-target.json
|
||||||
key: ${{ steps.cache-key.outputs.cache-key }}
|
key: ${{ steps.cache-key.outputs.cache-key }}
|
||||||
@@ -840,14 +840,14 @@ jobs:
|
|||||||
flash_usage: ${{ steps.extract.outputs.flash_usage }}
|
flash_usage: ${{ steps.extract.outputs.flash_usage }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out PR branch
|
- name: Check out PR branch
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- 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: ${{ 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/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
|
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
|
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
|
||||||
@@ -908,7 +908,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
|
|||||||
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
@@ -54,11 +54,11 @@ jobs:
|
|||||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
|
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
build-mode: ${{ matrix.build-mode }}
|
build-mode: ${{ matrix.build-mode }}
|
||||||
@@ -86,6 +86,6 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
|
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||||
with:
|
with:
|
||||||
category: "/language:${{matrix.language}}"
|
category: "/language:${{matrix.language}}"
|
||||||
|
|||||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
branch_build: ${{ steps.tag.outputs.branch_build }}
|
branch_build: ${{ steps.tag.outputs.branch_build }}
|
||||||
deploy_env: ${{ steps.tag.outputs.deploy_env }}
|
deploy_env: ${{ steps.tag.outputs.deploy_env }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Get tag
|
- name: Get tag
|
||||||
id: tag
|
id: tag
|
||||||
# yamllint disable rule:line-length
|
# yamllint disable rule:line-length
|
||||||
@@ -60,9 +60,9 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -92,9 +92,9 @@ jobs:
|
|||||||
os: "ubuntu-24.04-arm"
|
os: "ubuntu-24.04-arm"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ jobs:
|
|||||||
- ghcr
|
- ghcr
|
||||||
- dockerhub
|
- dockerhub
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||||
|
|||||||
8
.github/workflows/sync-device-classes.yml
vendored
8
.github/workflows/sync-device-classes.yml
vendored
@@ -13,16 +13,16 @@ jobs:
|
|||||||
if: github.repository == 'esphome/esphome'
|
if: github.repository == 'esphome/esphome'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- name: Checkout Home Assistant
|
- name: Checkout Home Assistant
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
repository: home-assistant/core
|
repository: home-assistant/core
|
||||||
path: lib/home-assistant
|
path: lib/home-assistant
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.13
|
python-version: 3.13
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
python script/run-in-env.py pre-commit run --all-files
|
python script/run-in-env.py pre-commit run --all-files
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||||
with:
|
with:
|
||||||
commit-message: "Synchronise Device Classes from Home Assistant"
|
commit-message: "Synchronise Device Classes from Home Assistant"
|
||||||
committer: esphomebot <esphome@openhomefoundation.org>
|
committer: esphomebot <esphome@openhomefoundation.org>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ ci:
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.14.11
|
rev: v0.14.14
|
||||||
hooks:
|
hooks:
|
||||||
# Run the linter.
|
# Run the linter.
|
||||||
- id: ruff
|
- id: ruff
|
||||||
|
|||||||
@@ -88,7 +88,8 @@ esphome/components/bmp3xx/* @latonita
|
|||||||
esphome/components/bmp3xx_base/* @latonita @martgras
|
esphome/components/bmp3xx_base/* @latonita @martgras
|
||||||
esphome/components/bmp3xx_i2c/* @latonita
|
esphome/components/bmp3xx_i2c/* @latonita
|
||||||
esphome/components/bmp3xx_spi/* @latonita
|
esphome/components/bmp3xx_spi/* @latonita
|
||||||
esphome/components/bmp581/* @kahrendt
|
esphome/components/bmp581_base/* @danielkent-net @kahrendt
|
||||||
|
esphome/components/bmp581_i2c/* @danielkent-net @kahrendt
|
||||||
esphome/components/bp1658cj/* @Cossid
|
esphome/components/bp1658cj/* @Cossid
|
||||||
esphome/components/bp5758d/* @Cossid
|
esphome/components/bp5758d/* @Cossid
|
||||||
esphome/components/bthome_mithermometer/* @nagyrobi
|
esphome/components/bthome_mithermometer/* @nagyrobi
|
||||||
@@ -255,6 +256,7 @@ esphome/components/inkplate/* @jesserockz @JosipKuci
|
|||||||
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/ir_rf_proxy/* @kbx81
|
||||||
esphome/components/jsn_sr04t/* @Mafus1
|
esphome/components/jsn_sr04t/* @Mafus1
|
||||||
esphome/components/json/* @esphome/core
|
esphome/components/json/* @esphome/core
|
||||||
esphome/components/kamstrup_kmp/* @cfeenstra1024
|
esphome/components/kamstrup_kmp/* @cfeenstra1024
|
||||||
@@ -480,6 +482,7 @@ esphome/components/switch/* @esphome/core
|
|||||||
esphome/components/switch/binary_sensor/* @ssieb
|
esphome/components/switch/binary_sensor/* @ssieb
|
||||||
esphome/components/sx126x/* @swoboda1337
|
esphome/components/sx126x/* @swoboda1337
|
||||||
esphome/components/sx127x/* @swoboda1337
|
esphome/components/sx127x/* @swoboda1337
|
||||||
|
esphome/components/sy6970/* @linkedupbits
|
||||||
esphome/components/syslog/* @clydebarrow
|
esphome/components/syslog/* @clydebarrow
|
||||||
esphome/components/t6615/* @tylermenezes
|
esphome/components/t6615/* @tylermenezes
|
||||||
esphome/components/tc74/* @sethgirvan
|
esphome/components/tc74/* @sethgirvan
|
||||||
|
|||||||
2
Doxyfile
2
Doxyfile
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = 2026.1.0-dev
|
PROJECT_NUMBER = 2026.2.0-dev
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||||
# for a project that appears at the top of each page and should give viewer a
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# PYTHON_ARGCOMPLETE_OK
|
# PYTHON_ARGCOMPLETE_OK
|
||||||
import argparse
|
import argparse
|
||||||
|
from collections.abc import Callable
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import functools
|
import functools
|
||||||
import getpass
|
import getpass
|
||||||
@@ -42,6 +43,7 @@ from esphome.const import (
|
|||||||
CONF_SUBSTITUTIONS,
|
CONF_SUBSTITUTIONS,
|
||||||
CONF_TOPIC,
|
CONF_TOPIC,
|
||||||
ENV_NOGITIGNORE,
|
ENV_NOGITIGNORE,
|
||||||
|
KEY_NATIVE_IDF,
|
||||||
PLATFORM_ESP32,
|
PLATFORM_ESP32,
|
||||||
PLATFORM_ESP8266,
|
PLATFORM_ESP8266,
|
||||||
PLATFORM_RP2040,
|
PLATFORM_RP2040,
|
||||||
@@ -115,6 +117,7 @@ class ArgsProtocol(Protocol):
|
|||||||
configuration: str
|
configuration: str
|
||||||
name: str
|
name: str
|
||||||
upload_speed: str | None
|
upload_speed: str | None
|
||||||
|
native_idf: bool
|
||||||
|
|
||||||
|
|
||||||
def choose_prompt(options, purpose: str = None):
|
def choose_prompt(options, purpose: str = None):
|
||||||
@@ -222,8 +225,13 @@ def choose_upload_log_host(
|
|||||||
else:
|
else:
|
||||||
resolved.append(device)
|
resolved.append(device)
|
||||||
if not resolved:
|
if not resolved:
|
||||||
|
if CORE.dashboard:
|
||||||
|
hint = "If you know the IP, set 'use_address' in your network config."
|
||||||
|
else:
|
||||||
|
hint = "If you know the IP, try --device <IP>"
|
||||||
raise EsphomeError(
|
raise EsphomeError(
|
||||||
f"All specified devices {defaults} could not be resolved. Is the device connected to the network?"
|
f"All specified devices {defaults} could not be resolved. "
|
||||||
|
f"Is the device connected to the network? {hint}"
|
||||||
)
|
)
|
||||||
return resolved
|
return resolved
|
||||||
|
|
||||||
@@ -494,12 +502,15 @@ def wrap_to_code(name, comp):
|
|||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
def write_cpp(config: ConfigType) -> int:
|
def write_cpp(config: ConfigType, native_idf: bool = False) -> int:
|
||||||
if not get_bool_env(ENV_NOGITIGNORE):
|
if not get_bool_env(ENV_NOGITIGNORE):
|
||||||
writer.write_gitignore()
|
writer.write_gitignore()
|
||||||
|
|
||||||
|
# Store native_idf flag so esp32 component can check it
|
||||||
|
CORE.data[KEY_NATIVE_IDF] = native_idf
|
||||||
|
|
||||||
generate_cpp_contents(config)
|
generate_cpp_contents(config)
|
||||||
return write_cpp_file()
|
return write_cpp_file(native_idf=native_idf)
|
||||||
|
|
||||||
|
|
||||||
def generate_cpp_contents(config: ConfigType) -> None:
|
def generate_cpp_contents(config: ConfigType) -> None:
|
||||||
@@ -513,10 +524,15 @@ def generate_cpp_contents(config: ConfigType) -> None:
|
|||||||
CORE.flush_tasks()
|
CORE.flush_tasks()
|
||||||
|
|
||||||
|
|
||||||
def write_cpp_file() -> int:
|
def write_cpp_file(native_idf: bool = False) -> int:
|
||||||
code_s = indent(CORE.cpp_main_section)
|
code_s = indent(CORE.cpp_main_section)
|
||||||
writer.write_cpp(code_s)
|
writer.write_cpp(code_s)
|
||||||
|
|
||||||
|
if native_idf and CORE.is_esp32 and CORE.target_framework == "esp-idf":
|
||||||
|
from esphome.build_gen import espidf
|
||||||
|
|
||||||
|
espidf.write_project()
|
||||||
|
else:
|
||||||
from esphome.build_gen import platformio
|
from esphome.build_gen import platformio
|
||||||
|
|
||||||
platformio.write_project()
|
platformio.write_project()
|
||||||
@@ -525,20 +541,37 @@ def write_cpp_file() -> int:
|
|||||||
|
|
||||||
|
|
||||||
def compile_program(args: ArgsProtocol, config: ConfigType) -> int:
|
def compile_program(args: ArgsProtocol, config: ConfigType) -> int:
|
||||||
from esphome import platformio_api
|
native_idf = getattr(args, "native_idf", False)
|
||||||
|
|
||||||
# NOTE: "Build path:" format is parsed by script/ci_memory_impact_extract.py
|
# NOTE: "Build path:" format is parsed by script/ci_memory_impact_extract.py
|
||||||
# If you change this format, update the regex in that script as well
|
# If you change this format, update the regex in that script as well
|
||||||
_LOGGER.info("Compiling app... Build path: %s", CORE.build_path)
|
_LOGGER.info("Compiling app... Build path: %s", CORE.build_path)
|
||||||
|
|
||||||
|
if native_idf and CORE.is_esp32 and CORE.target_framework == "esp-idf":
|
||||||
|
from esphome import espidf_api
|
||||||
|
|
||||||
|
rc = espidf_api.run_compile(config, CORE.verbose)
|
||||||
|
if rc != 0:
|
||||||
|
return rc
|
||||||
|
|
||||||
|
# Create factory.bin and ota.bin
|
||||||
|
espidf_api.create_factory_bin()
|
||||||
|
espidf_api.create_ota_bin()
|
||||||
|
else:
|
||||||
|
from esphome import platformio_api
|
||||||
|
|
||||||
rc = platformio_api.run_compile(config, CORE.verbose)
|
rc = platformio_api.run_compile(config, CORE.verbose)
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
|
idedata = platformio_api.get_idedata(config)
|
||||||
|
if idedata is None:
|
||||||
|
return 1
|
||||||
|
|
||||||
# Check if firmware was rebuilt and emit build_info + create manifest
|
# Check if firmware was rebuilt and emit build_info + create manifest
|
||||||
_check_and_emit_build_info()
|
_check_and_emit_build_info()
|
||||||
|
|
||||||
idedata = platformio_api.get_idedata(config)
|
return 0
|
||||||
return 0 if idedata is not None else 1
|
|
||||||
|
|
||||||
|
|
||||||
def _check_and_emit_build_info() -> None:
|
def _check_and_emit_build_info() -> None:
|
||||||
@@ -795,7 +828,8 @@ def command_vscode(args: ArgsProtocol) -> int | None:
|
|||||||
|
|
||||||
|
|
||||||
def command_compile(args: ArgsProtocol, config: ConfigType) -> int | None:
|
def command_compile(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||||
exit_code = write_cpp(config)
|
native_idf = getattr(args, "native_idf", False)
|
||||||
|
exit_code = write_cpp(config, native_idf=native_idf)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return exit_code
|
return exit_code
|
||||||
if args.only_generate:
|
if args.only_generate:
|
||||||
@@ -850,7 +884,8 @@ def command_logs(args: ArgsProtocol, config: ConfigType) -> int | None:
|
|||||||
|
|
||||||
|
|
||||||
def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
|
def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||||
exit_code = write_cpp(config)
|
native_idf = getattr(args, "native_idf", False)
|
||||||
|
exit_code = write_cpp(config, native_idf=native_idf)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return exit_code
|
return exit_code
|
||||||
exit_code = compile_program(args, config)
|
exit_code = compile_program(args, config)
|
||||||
@@ -931,11 +966,21 @@ def command_dashboard(args: ArgsProtocol) -> int | None:
|
|||||||
return dashboard.start_dashboard(args)
|
return dashboard.start_dashboard(args)
|
||||||
|
|
||||||
|
|
||||||
def command_update_all(args: ArgsProtocol) -> int | None:
|
def run_multiple_configs(
|
||||||
|
files: list, command_builder: Callable[[str], list[str]]
|
||||||
|
) -> int:
|
||||||
|
"""Run a command for each configuration file in a subprocess.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
files: List of configuration files to process.
|
||||||
|
command_builder: Callable that takes a file path and returns a command list.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Number of failed files.
|
||||||
|
"""
|
||||||
import click
|
import click
|
||||||
|
|
||||||
success = {}
|
success = {}
|
||||||
files = list_yaml_files(args.configuration)
|
|
||||||
twidth = 60
|
twidth = 60
|
||||||
|
|
||||||
def print_bar(middle_text):
|
def print_bar(middle_text):
|
||||||
@@ -945,17 +990,19 @@ def command_update_all(args: ArgsProtocol) -> int | None:
|
|||||||
safe_print(f"{half_line}{middle_text}{half_line}")
|
safe_print(f"{half_line}{middle_text}{half_line}")
|
||||||
|
|
||||||
for f in files:
|
for f in files:
|
||||||
safe_print(f"Updating {color(AnsiFore.CYAN, str(f))}")
|
f_path = Path(f) if not isinstance(f, Path) else f
|
||||||
|
|
||||||
|
if any(f_path.name == x for x in SECRETS_FILES):
|
||||||
|
_LOGGER.warning("Skipping secrets file %s", f_path)
|
||||||
|
continue
|
||||||
|
|
||||||
|
safe_print(f"Processing {color(AnsiFore.CYAN, str(f))}")
|
||||||
safe_print("-" * twidth)
|
safe_print("-" * twidth)
|
||||||
safe_print()
|
safe_print()
|
||||||
if CORE.dashboard:
|
|
||||||
rc = run_external_process(
|
cmd = command_builder(f)
|
||||||
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
|
rc = run_external_process(*cmd)
|
||||||
)
|
|
||||||
else:
|
|
||||||
rc = run_external_process(
|
|
||||||
"esphome", "run", f, "--no-logs", "--device", "OTA"
|
|
||||||
)
|
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
print_bar(f"[{color(AnsiFore.BOLD_GREEN, 'SUCCESS')}] {str(f)}")
|
print_bar(f"[{color(AnsiFore.BOLD_GREEN, 'SUCCESS')}] {str(f)}")
|
||||||
success[f] = True
|
success[f] = True
|
||||||
@@ -970,6 +1017,8 @@ def command_update_all(args: ArgsProtocol) -> int | None:
|
|||||||
print_bar(f"[{color(AnsiFore.BOLD_WHITE, 'SUMMARY')}]")
|
print_bar(f"[{color(AnsiFore.BOLD_WHITE, 'SUMMARY')}]")
|
||||||
failed = 0
|
failed = 0
|
||||||
for f in files:
|
for f in files:
|
||||||
|
if f not in success:
|
||||||
|
continue # Skipped file
|
||||||
if success[f]:
|
if success[f]:
|
||||||
safe_print(f" - {str(f)}: {color(AnsiFore.GREEN, 'SUCCESS')}")
|
safe_print(f" - {str(f)}: {color(AnsiFore.GREEN, 'SUCCESS')}")
|
||||||
else:
|
else:
|
||||||
@@ -978,6 +1027,17 @@ def command_update_all(args: ArgsProtocol) -> int | None:
|
|||||||
return failed
|
return failed
|
||||||
|
|
||||||
|
|
||||||
|
def command_update_all(args: ArgsProtocol) -> int | None:
|
||||||
|
files = list_yaml_files(args.configuration)
|
||||||
|
|
||||||
|
def build_command(f):
|
||||||
|
if CORE.dashboard:
|
||||||
|
return ["esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"]
|
||||||
|
return ["esphome", "run", f, "--no-logs", "--device", "OTA"]
|
||||||
|
|
||||||
|
return run_multiple_configs(files, build_command)
|
||||||
|
|
||||||
|
|
||||||
def command_idedata(args: ArgsProtocol, config: ConfigType) -> int:
|
def command_idedata(args: ArgsProtocol, config: ConfigType) -> int:
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@@ -1279,6 +1339,11 @@ def parse_args(argv):
|
|||||||
help="Only generate source code, do not compile.",
|
help="Only generate source code, do not compile.",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
)
|
)
|
||||||
|
parser_compile.add_argument(
|
||||||
|
"--native-idf",
|
||||||
|
help="Build with native ESP-IDF instead of PlatformIO (ESP32 esp-idf framework only).",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
|
||||||
parser_upload = subparsers.add_parser(
|
parser_upload = subparsers.add_parser(
|
||||||
"upload",
|
"upload",
|
||||||
@@ -1360,6 +1425,11 @@ def parse_args(argv):
|
|||||||
help="Reset the device before starting serial logs.",
|
help="Reset the device before starting serial logs.",
|
||||||
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
|
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
|
||||||
)
|
)
|
||||||
|
parser_run.add_argument(
|
||||||
|
"--native-idf",
|
||||||
|
help="Build with native ESP-IDF instead of PlatformIO (ESP32 esp-idf framework only).",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
|
||||||
parser_clean = subparsers.add_parser(
|
parser_clean = subparsers.add_parser(
|
||||||
"clean-mqtt",
|
"clean-mqtt",
|
||||||
@@ -1528,11 +1598,25 @@ def run_esphome(argv):
|
|||||||
|
|
||||||
_LOGGER.info("ESPHome %s", const.__version__)
|
_LOGGER.info("ESPHome %s", const.__version__)
|
||||||
|
|
||||||
for conf_path in args.configuration:
|
# Multiple configurations: use subprocesses to avoid state leakage
|
||||||
conf_path = Path(conf_path)
|
# between compilations (e.g., LVGL touchscreen state in module globals)
|
||||||
|
if len(args.configuration) > 1:
|
||||||
|
# Build command by reusing argv, replacing all configs with single file
|
||||||
|
# argv[0] is the program path, skip it since we prefix with "esphome"
|
||||||
|
def build_command(f):
|
||||||
|
return (
|
||||||
|
["esphome"]
|
||||||
|
+ [arg for arg in argv[1:] if arg not in args.configuration]
|
||||||
|
+ [str(f)]
|
||||||
|
)
|
||||||
|
|
||||||
|
return run_multiple_configs(args.configuration, build_command)
|
||||||
|
|
||||||
|
# Single configuration
|
||||||
|
conf_path = Path(args.configuration[0])
|
||||||
if any(conf_path.name == x for x in SECRETS_FILES):
|
if any(conf_path.name == x for x in SECRETS_FILES):
|
||||||
_LOGGER.warning("Skipping secrets file %s", conf_path)
|
_LOGGER.warning("Skipping secrets file %s", conf_path)
|
||||||
continue
|
return 0
|
||||||
|
|
||||||
CORE.config_path = conf_path
|
CORE.config_path = conf_path
|
||||||
CORE.dashboard = args.dashboard
|
CORE.dashboard = args.dashboard
|
||||||
@@ -1549,17 +1633,13 @@ def run_esphome(argv):
|
|||||||
|
|
||||||
if args.command not in POST_CONFIG_ACTIONS:
|
if args.command not in POST_CONFIG_ACTIONS:
|
||||||
safe_print(f"Unknown command {args.command}")
|
safe_print(f"Unknown command {args.command}")
|
||||||
|
return 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rc = POST_CONFIG_ACTIONS[args.command](args, config)
|
return POST_CONFIG_ACTIONS[args.command](args, config)
|
||||||
except EsphomeError as e:
|
except EsphomeError as e:
|
||||||
_LOGGER.error(e, exc_info=args.verbose)
|
_LOGGER.error(e, exc_info=args.verbose)
|
||||||
return 1
|
return 1
|
||||||
if rc != 0:
|
|
||||||
return rc
|
|
||||||
|
|
||||||
CORE.reset()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from .helpers import (
|
|||||||
map_section_name,
|
map_section_name,
|
||||||
parse_symbol_line,
|
parse_symbol_line,
|
||||||
)
|
)
|
||||||
from .toolchain import find_tool, run_tool
|
from .toolchain import find_tool, resolve_tool_path, run_tool
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esphome.platformio_api import IDEData
|
from esphome.platformio_api import IDEData
|
||||||
@@ -132,6 +132,12 @@ class MemoryAnalyzer:
|
|||||||
readelf_path = readelf_path or idedata.readelf_path
|
readelf_path = readelf_path or idedata.readelf_path
|
||||||
_LOGGER.debug("Using toolchain paths from PlatformIO idedata")
|
_LOGGER.debug("Using toolchain paths from PlatformIO idedata")
|
||||||
|
|
||||||
|
# Validate paths exist, fall back to find_tool if they don't
|
||||||
|
# This handles cases like Zephyr where cc_path doesn't include full path
|
||||||
|
# and the toolchain prefix may differ (e.g., arm-zephyr-eabi- vs arm-none-eabi-)
|
||||||
|
objdump_path = resolve_tool_path("objdump", objdump_path, objdump_path)
|
||||||
|
readelf_path = resolve_tool_path("readelf", readelf_path, objdump_path)
|
||||||
|
|
||||||
self.objdump_path = objdump_path or "objdump"
|
self.objdump_path = objdump_path or "objdump"
|
||||||
self.readelf_path = readelf_path or "readelf"
|
self.readelf_path = readelf_path or "readelf"
|
||||||
self.external_components = external_components or set()
|
self.external_components = external_components or set()
|
||||||
|
|||||||
@@ -9,11 +9,61 @@ ESPHOME_COMPONENT_PATTERN = re.compile(r"esphome::([a-zA-Z0-9_]+)::")
|
|||||||
# Maps standard section names to their various platform-specific variants
|
# Maps standard section names to their various platform-specific variants
|
||||||
# Note: Order matters! More specific patterns (.bss) must come before general ones (.dram)
|
# Note: Order matters! More specific patterns (.bss) must come before general ones (.dram)
|
||||||
# because ESP-IDF uses names like ".dram0.bss" which would match ".dram" otherwise
|
# because ESP-IDF uses names like ".dram0.bss" which would match ".dram" otherwise
|
||||||
|
#
|
||||||
|
# Platform-specific sections:
|
||||||
|
# - ESP8266/ESP32: .iram*, .dram*
|
||||||
|
# - LibreTiny RTL87xx: .xip.code_* (flash), .ram.code_* (RAM)
|
||||||
|
# - LibreTiny BK7231: .itcm.code (fast RAM), .vectors (interrupt vectors)
|
||||||
|
# - LibreTiny LN882X: .flash_text, .flash_copy* (flash code)
|
||||||
|
# - Zephyr/nRF52: text, rodata, datas, bss (no leading dots)
|
||||||
SECTION_MAPPING = {
|
SECTION_MAPPING = {
|
||||||
".text": frozenset([".text", ".iram"]),
|
".text": frozenset(
|
||||||
".rodata": frozenset([".rodata"]),
|
[
|
||||||
".bss": frozenset([".bss"]), # Must be before .data to catch ".dram0.bss"
|
".text",
|
||||||
".data": frozenset([".data", ".dram"]),
|
".iram",
|
||||||
|
# LibreTiny RTL87xx XIP (eXecute In Place) flash code
|
||||||
|
".xip.code",
|
||||||
|
# LibreTiny RTL87xx RAM code
|
||||||
|
".ram.code_text",
|
||||||
|
# LibreTiny BK7231 fast RAM code and vectors
|
||||||
|
".itcm.code",
|
||||||
|
".vectors",
|
||||||
|
# LibreTiny LN882X flash code
|
||||||
|
".flash_text",
|
||||||
|
".flash_copy",
|
||||||
|
# Zephyr/nRF52 sections (no leading dots)
|
||||||
|
"text",
|
||||||
|
"rom_start",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
".rodata": frozenset(
|
||||||
|
[
|
||||||
|
".rodata",
|
||||||
|
# LibreTiny RTL87xx read-only data in RAM
|
||||||
|
".ram.code_rodata",
|
||||||
|
# Zephyr/nRF52 sections (no leading dots)
|
||||||
|
"rodata",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
# .bss patterns - must be before .data to catch ".dram0.bss"
|
||||||
|
".bss": frozenset(
|
||||||
|
[
|
||||||
|
".bss",
|
||||||
|
# LibreTiny LN882X BSS
|
||||||
|
".bss_ram",
|
||||||
|
# Zephyr/nRF52 sections (no leading dots)
|
||||||
|
"bss",
|
||||||
|
"noinit",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
".data": frozenset(
|
||||||
|
[
|
||||||
|
".data",
|
||||||
|
".dram",
|
||||||
|
# Zephyr/nRF52 sections (no leading dots)
|
||||||
|
"datas",
|
||||||
|
]
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Section to ComponentMemory attribute mapping
|
# Section to ComponentMemory attribute mapping
|
||||||
|
|||||||
@@ -94,13 +94,13 @@ def parse_symbol_line(line: str) -> tuple[str, str, int, str] | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# Find section, size, and name
|
# Find section, size, and name
|
||||||
|
# Try each part as a potential section name
|
||||||
for i, part in enumerate(parts):
|
for i, part in enumerate(parts):
|
||||||
if not part.startswith("."):
|
# Skip parts that are clearly flags, addresses, or other metadata
|
||||||
continue
|
# Sections start with '.' (standard ELF) or are known section names (Zephyr)
|
||||||
|
|
||||||
section = map_section_name(part)
|
section = map_section_name(part)
|
||||||
if not section:
|
if not section:
|
||||||
break
|
continue
|
||||||
|
|
||||||
# Need at least size field after section
|
# Need at least size field after section
|
||||||
if i + 1 >= len(parts):
|
if i + 1 >= len(parts):
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import subprocess
|
import subprocess
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
@@ -17,10 +18,82 @@ TOOLCHAIN_PREFIXES = [
|
|||||||
"xtensa-lx106-elf-", # ESP8266
|
"xtensa-lx106-elf-", # ESP8266
|
||||||
"xtensa-esp32-elf-", # ESP32
|
"xtensa-esp32-elf-", # ESP32
|
||||||
"xtensa-esp-elf-", # ESP32 (newer IDF)
|
"xtensa-esp-elf-", # ESP32 (newer IDF)
|
||||||
|
"arm-zephyr-eabi-", # nRF52/Zephyr SDK
|
||||||
|
"arm-none-eabi-", # Generic ARM (RP2040, etc.)
|
||||||
"", # System default (no prefix)
|
"", # System default (no prefix)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _find_in_platformio_packages(tool_name: str) -> str | None:
|
||||||
|
"""Search for a tool in PlatformIO package directories.
|
||||||
|
|
||||||
|
This handles cases like Zephyr SDK where tools are installed in nested
|
||||||
|
directories that aren't in PATH.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tool_name: Name of the tool (e.g., "readelf", "objdump")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Full path to the tool or None if not found
|
||||||
|
"""
|
||||||
|
# Get PlatformIO packages directory
|
||||||
|
platformio_home = Path(os.path.expanduser("~/.platformio/packages"))
|
||||||
|
if not platformio_home.exists():
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Search patterns for toolchains that might contain the tool
|
||||||
|
# Order matters - more specific patterns first
|
||||||
|
search_patterns = [
|
||||||
|
# Zephyr SDK deeply nested structure (4 levels)
|
||||||
|
# e.g., toolchain-gccarmnoneeabi/zephyr-sdk-0.17.4/arm-zephyr-eabi/bin/arm-zephyr-eabi-objdump
|
||||||
|
f"toolchain-*/*/*/bin/*-{tool_name}",
|
||||||
|
# Zephyr SDK nested structure (3 levels)
|
||||||
|
f"toolchain-*/*/bin/*-{tool_name}",
|
||||||
|
f"toolchain-*/bin/*-{tool_name}",
|
||||||
|
# Standard PlatformIO toolchain structure
|
||||||
|
f"toolchain-*/bin/*{tool_name}",
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in search_patterns:
|
||||||
|
matches = list(platformio_home.glob(pattern))
|
||||||
|
if matches:
|
||||||
|
# Sort to get consistent results, prefer arm-zephyr-eabi over arm-none-eabi
|
||||||
|
matches.sort(key=lambda p: ("zephyr" not in str(p), str(p)))
|
||||||
|
tool_path = str(matches[0])
|
||||||
|
_LOGGER.debug("Found %s in PlatformIO packages: %s", tool_name, tool_path)
|
||||||
|
return tool_path
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_tool_path(
|
||||||
|
tool_name: str,
|
||||||
|
derived_path: str | None,
|
||||||
|
objdump_path: str | None = None,
|
||||||
|
) -> str | None:
|
||||||
|
"""Resolve a tool path, falling back to find_tool if derived path doesn't exist.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tool_name: Name of the tool (e.g., "objdump", "readelf")
|
||||||
|
derived_path: Path derived from idedata (may not exist for some platforms)
|
||||||
|
objdump_path: Path to objdump binary to derive other tool paths from
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Resolved path to the tool, or the original derived_path if it exists
|
||||||
|
"""
|
||||||
|
if derived_path and not Path(derived_path).exists():
|
||||||
|
found = find_tool(tool_name, objdump_path)
|
||||||
|
if found:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Derived %s path %s not found, using %s",
|
||||||
|
tool_name,
|
||||||
|
derived_path,
|
||||||
|
found,
|
||||||
|
)
|
||||||
|
return found
|
||||||
|
return derived_path
|
||||||
|
|
||||||
|
|
||||||
def find_tool(
|
def find_tool(
|
||||||
tool_name: str,
|
tool_name: str,
|
||||||
objdump_path: str | None = None,
|
objdump_path: str | None = None,
|
||||||
@@ -28,7 +101,8 @@ def find_tool(
|
|||||||
"""Find a toolchain tool by name.
|
"""Find a toolchain tool by name.
|
||||||
|
|
||||||
First tries to derive the tool path from objdump_path (if provided),
|
First tries to derive the tool path from objdump_path (if provided),
|
||||||
then falls back to searching for platform-specific tools.
|
then searches PlatformIO package directories (for cross-compile toolchains),
|
||||||
|
and finally falls back to searching for platform-specific tools in PATH.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tool_name: Name of the tool (e.g., "objdump", "nm", "c++filt")
|
tool_name: Name of the tool (e.g., "objdump", "nm", "c++filt")
|
||||||
@@ -47,7 +121,13 @@ def find_tool(
|
|||||||
_LOGGER.debug("Found %s at: %s", tool_name, potential_path)
|
_LOGGER.debug("Found %s at: %s", tool_name, potential_path)
|
||||||
return potential_path
|
return potential_path
|
||||||
|
|
||||||
# Try platform-specific tools
|
# Search in PlatformIO packages directory first (handles Zephyr SDK, etc.)
|
||||||
|
# This must come before PATH search because system tools (e.g., /usr/bin/objdump)
|
||||||
|
# are for the host architecture, not the target (ARM, Xtensa, etc.)
|
||||||
|
if found := _find_in_platformio_packages(tool_name):
|
||||||
|
return found
|
||||||
|
|
||||||
|
# Try platform-specific tools in PATH (fallback for when tools are installed globally)
|
||||||
for prefix in TOOLCHAIN_PREFIXES:
|
for prefix in TOOLCHAIN_PREFIXES:
|
||||||
cmd = f"{prefix}{tool_name}"
|
cmd = f"{prefix}{tool_name}"
|
||||||
try:
|
try:
|
||||||
|
|||||||
139
esphome/build_gen/espidf.py
Normal file
139
esphome/build_gen/espidf.py
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
"""ESP-IDF direct build generator for ESPHome."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
|
from esphome.core import CORE
|
||||||
|
from esphome.helpers import mkdir_p, write_file_if_changed
|
||||||
|
|
||||||
|
|
||||||
|
def get_available_components() -> list[str] | None:
|
||||||
|
"""Get list of available ESP-IDF components from project_description.json.
|
||||||
|
|
||||||
|
Returns only internal ESP-IDF components, excluding external/managed
|
||||||
|
components (from idf_component.yml).
|
||||||
|
"""
|
||||||
|
project_desc = Path(CORE.build_path) / "build" / "project_description.json"
|
||||||
|
if not project_desc.exists():
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(project_desc, encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
component_info = data.get("build_component_info", {})
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for name, info in component_info.items():
|
||||||
|
# Exclude our own src component
|
||||||
|
if name == "src":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Exclude managed/external components
|
||||||
|
comp_dir = info.get("dir", "")
|
||||||
|
if "managed_components" in comp_dir:
|
||||||
|
continue
|
||||||
|
|
||||||
|
result.append(name)
|
||||||
|
|
||||||
|
return result
|
||||||
|
except (json.JSONDecodeError, OSError):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def has_discovered_components() -> bool:
|
||||||
|
"""Check if we have discovered components from a previous configure."""
|
||||||
|
return get_available_components() is not None
|
||||||
|
|
||||||
|
|
||||||
|
def get_project_cmakelists() -> str:
|
||||||
|
"""Generate the top-level CMakeLists.txt for ESP-IDF project."""
|
||||||
|
# Get IDF target from ESP32 variant (e.g., ESP32S3 -> esp32s3)
|
||||||
|
variant = get_esp32_variant()
|
||||||
|
idf_target = variant.lower().replace("-", "")
|
||||||
|
|
||||||
|
return f"""\
|
||||||
|
# Auto-generated by ESPHome
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
set(IDF_TARGET {idf_target})
|
||||||
|
set(EXTRA_COMPONENT_DIRS ${{CMAKE_SOURCE_DIR}}/src)
|
||||||
|
|
||||||
|
include($ENV{{IDF_PATH}}/tools/cmake/project.cmake)
|
||||||
|
project({CORE.name})
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_component_cmakelists(minimal: bool = False) -> str:
|
||||||
|
"""Generate the main component CMakeLists.txt."""
|
||||||
|
idf_requires = [] if minimal else (get_available_components() or [])
|
||||||
|
requires_str = " ".join(idf_requires)
|
||||||
|
|
||||||
|
# Extract compile definitions from build flags (-DXXX -> XXX)
|
||||||
|
compile_defs = [flag[2:] for flag in CORE.build_flags if flag.startswith("-D")]
|
||||||
|
compile_defs_str = "\n ".join(compile_defs) if compile_defs else ""
|
||||||
|
|
||||||
|
# Extract compile options (-W flags, excluding linker flags)
|
||||||
|
compile_opts = [
|
||||||
|
flag
|
||||||
|
for flag in CORE.build_flags
|
||||||
|
if flag.startswith("-W") and not flag.startswith("-Wl,")
|
||||||
|
]
|
||||||
|
compile_opts_str = "\n ".join(compile_opts) if compile_opts else ""
|
||||||
|
|
||||||
|
# Extract linker options (-Wl, flags)
|
||||||
|
link_opts = [flag for flag in CORE.build_flags if flag.startswith("-Wl,")]
|
||||||
|
link_opts_str = "\n ".join(link_opts) if link_opts else ""
|
||||||
|
|
||||||
|
return f"""\
|
||||||
|
# Auto-generated by ESPHome
|
||||||
|
file(GLOB_RECURSE app_sources
|
||||||
|
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.cpp"
|
||||||
|
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.c"
|
||||||
|
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cpp"
|
||||||
|
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c"
|
||||||
|
)
|
||||||
|
|
||||||
|
idf_component_register(
|
||||||
|
SRCS ${{app_sources}}
|
||||||
|
INCLUDE_DIRS "." "esphome"
|
||||||
|
REQUIRES {requires_str}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Apply C++ standard
|
||||||
|
target_compile_features(${{COMPONENT_LIB}} PUBLIC cxx_std_20)
|
||||||
|
|
||||||
|
# ESPHome compile definitions
|
||||||
|
target_compile_definitions(${{COMPONENT_LIB}} PUBLIC
|
||||||
|
{compile_defs_str}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ESPHome compile options
|
||||||
|
target_compile_options(${{COMPONENT_LIB}} PUBLIC
|
||||||
|
{compile_opts_str}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ESPHome linker options
|
||||||
|
target_link_options(${{COMPONENT_LIB}} PUBLIC
|
||||||
|
{link_opts_str}
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def write_project(minimal: bool = False) -> None:
|
||||||
|
"""Write ESP-IDF project files."""
|
||||||
|
mkdir_p(CORE.build_path)
|
||||||
|
mkdir_p(CORE.relative_src_path())
|
||||||
|
|
||||||
|
# Write top-level CMakeLists.txt
|
||||||
|
write_file_if_changed(
|
||||||
|
CORE.relative_build_path("CMakeLists.txt"),
|
||||||
|
get_project_cmakelists(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Write component CMakeLists.txt in src/
|
||||||
|
write_file_if_changed(
|
||||||
|
CORE.relative_src_path("CMakeLists.txt"),
|
||||||
|
get_component_cmakelists(minimal=minimal),
|
||||||
|
)
|
||||||
@@ -69,6 +69,7 @@ from esphome.cpp_types import ( # noqa: F401
|
|||||||
JsonObjectConst,
|
JsonObjectConst,
|
||||||
Parented,
|
Parented,
|
||||||
PollingComponent,
|
PollingComponent,
|
||||||
|
StringRef,
|
||||||
arduino_json_ns,
|
arduino_json_ns,
|
||||||
bool_,
|
bool_,
|
||||||
const_char_ptr,
|
const_char_ptr,
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
|
|||||||
this->last_update_ = millis();
|
this->last_update_ = millis();
|
||||||
if (state != this->current_state_) {
|
if (state != this->current_state_) {
|
||||||
auto prev_state = this->current_state_;
|
auto prev_state = this->current_state_;
|
||||||
ESP_LOGD(TAG, "Set state to: %s, previous: %s", LOG_STR_ARG(alarm_control_panel_state_to_string(state)),
|
ESP_LOGD(TAG, "'%s' >> %s (was %s)", this->get_name().c_str(),
|
||||||
|
LOG_STR_ARG(alarm_control_panel_state_to_string(state)),
|
||||||
LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state)));
|
LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state)));
|
||||||
this->current_state_ = state;
|
this->current_state_ = state;
|
||||||
// Single state callback - triggers check get_state() for specific states
|
// Single state callback - triggers check get_state() for specific states
|
||||||
@@ -66,52 +67,29 @@ void AlarmControlPanel::add_on_ready_callback(std::function<void()> &&callback)
|
|||||||
this->ready_callback_.add(std::move(callback));
|
this->ready_callback_.add(std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmControlPanel::arm_away(optional<std::string> code) {
|
void AlarmControlPanel::arm_with_code_(AlarmControlPanelCall &(AlarmControlPanelCall::*arm_method)(),
|
||||||
|
const char *code) {
|
||||||
auto call = this->make_call();
|
auto call = this->make_call();
|
||||||
call.arm_away();
|
(call.*arm_method)();
|
||||||
if (code.has_value())
|
if (code != nullptr)
|
||||||
call.set_code(code.value());
|
call.set_code(code);
|
||||||
call.perform();
|
call.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmControlPanel::arm_home(optional<std::string> code) {
|
void AlarmControlPanel::arm_away(const char *code) { this->arm_with_code_(&AlarmControlPanelCall::arm_away, code); }
|
||||||
auto call = this->make_call();
|
|
||||||
call.arm_home();
|
void AlarmControlPanel::arm_home(const char *code) { this->arm_with_code_(&AlarmControlPanelCall::arm_home, code); }
|
||||||
if (code.has_value())
|
|
||||||
call.set_code(code.value());
|
void AlarmControlPanel::arm_night(const char *code) { this->arm_with_code_(&AlarmControlPanelCall::arm_night, code); }
|
||||||
call.perform();
|
|
||||||
|
void AlarmControlPanel::arm_vacation(const char *code) {
|
||||||
|
this->arm_with_code_(&AlarmControlPanelCall::arm_vacation, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmControlPanel::arm_night(optional<std::string> code) {
|
void AlarmControlPanel::arm_custom_bypass(const char *code) {
|
||||||
auto call = this->make_call();
|
this->arm_with_code_(&AlarmControlPanelCall::arm_custom_bypass, code);
|
||||||
call.arm_night();
|
|
||||||
if (code.has_value())
|
|
||||||
call.set_code(code.value());
|
|
||||||
call.perform();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmControlPanel::arm_vacation(optional<std::string> code) {
|
void AlarmControlPanel::disarm(const char *code) { this->arm_with_code_(&AlarmControlPanelCall::disarm, code); }
|
||||||
auto call = this->make_call();
|
|
||||||
call.arm_vacation();
|
|
||||||
if (code.has_value())
|
|
||||||
call.set_code(code.value());
|
|
||||||
call.perform();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AlarmControlPanel::arm_custom_bypass(optional<std::string> code) {
|
|
||||||
auto call = this->make_call();
|
|
||||||
call.arm_custom_bypass();
|
|
||||||
if (code.has_value())
|
|
||||||
call.set_code(code.value());
|
|
||||||
call.perform();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AlarmControlPanel::disarm(optional<std::string> code) {
|
|
||||||
auto call = this->make_call();
|
|
||||||
call.disarm();
|
|
||||||
if (code.has_value())
|
|
||||||
call.set_code(code.value());
|
|
||||||
call.perform();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace esphome::alarm_control_panel
|
} // namespace esphome::alarm_control_panel
|
||||||
|
|||||||
@@ -76,37 +76,53 @@ class AlarmControlPanel : public EntityBase {
|
|||||||
*
|
*
|
||||||
* @param code The code
|
* @param code The code
|
||||||
*/
|
*/
|
||||||
void arm_away(optional<std::string> code = nullopt);
|
void arm_away(const char *code = nullptr);
|
||||||
|
void arm_away(const optional<std::string> &code) {
|
||||||
|
this->arm_away(code.has_value() ? code.value().c_str() : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
/** arm the alarm in home mode
|
/** arm the alarm in home mode
|
||||||
*
|
*
|
||||||
* @param code The code
|
* @param code The code
|
||||||
*/
|
*/
|
||||||
void arm_home(optional<std::string> code = nullopt);
|
void arm_home(const char *code = nullptr);
|
||||||
|
void arm_home(const optional<std::string> &code) {
|
||||||
|
this->arm_home(code.has_value() ? code.value().c_str() : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
/** arm the alarm in night mode
|
/** arm the alarm in night mode
|
||||||
*
|
*
|
||||||
* @param code The code
|
* @param code The code
|
||||||
*/
|
*/
|
||||||
void arm_night(optional<std::string> code = nullopt);
|
void arm_night(const char *code = nullptr);
|
||||||
|
void arm_night(const optional<std::string> &code) {
|
||||||
|
this->arm_night(code.has_value() ? code.value().c_str() : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
/** arm the alarm in vacation mode
|
/** arm the alarm in vacation mode
|
||||||
*
|
*
|
||||||
* @param code The code
|
* @param code The code
|
||||||
*/
|
*/
|
||||||
void arm_vacation(optional<std::string> code = nullopt);
|
void arm_vacation(const char *code = nullptr);
|
||||||
|
void arm_vacation(const optional<std::string> &code) {
|
||||||
|
this->arm_vacation(code.has_value() ? code.value().c_str() : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
/** arm the alarm in custom bypass mode
|
/** arm the alarm in custom bypass mode
|
||||||
*
|
*
|
||||||
* @param code The code
|
* @param code The code
|
||||||
*/
|
*/
|
||||||
void arm_custom_bypass(optional<std::string> code = nullopt);
|
void arm_custom_bypass(const char *code = nullptr);
|
||||||
|
void arm_custom_bypass(const optional<std::string> &code) {
|
||||||
|
this->arm_custom_bypass(code.has_value() ? code.value().c_str() : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
/** disarm the alarm
|
/** disarm the alarm
|
||||||
*
|
*
|
||||||
* @param code The code
|
* @param code The code
|
||||||
*/
|
*/
|
||||||
void disarm(optional<std::string> code = nullopt);
|
void disarm(const char *code = nullptr);
|
||||||
|
void disarm(const optional<std::string> &code) { this->disarm(code.has_value() ? code.value().c_str() : nullptr); }
|
||||||
|
|
||||||
/** Get the state
|
/** Get the state
|
||||||
*
|
*
|
||||||
@@ -118,6 +134,8 @@ class AlarmControlPanel : public EntityBase {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend AlarmControlPanelCall;
|
friend AlarmControlPanelCall;
|
||||||
|
// Helper to reduce code duplication for arm/disarm methods
|
||||||
|
void arm_with_code_(AlarmControlPanelCall &(AlarmControlPanelCall::*arm_method)(), const char *code);
|
||||||
// in order to store last panel state in flash
|
// in order to store last panel state in flash
|
||||||
ESPPreferenceObject pref_;
|
ESPPreferenceObject pref_;
|
||||||
// current state
|
// current state
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ static const char *const TAG = "alarm_control_panel";
|
|||||||
|
|
||||||
AlarmControlPanelCall::AlarmControlPanelCall(AlarmControlPanel *parent) : parent_(parent) {}
|
AlarmControlPanelCall::AlarmControlPanelCall(AlarmControlPanel *parent) : parent_(parent) {}
|
||||||
|
|
||||||
AlarmControlPanelCall &AlarmControlPanelCall::set_code(const std::string &code) {
|
AlarmControlPanelCall &AlarmControlPanelCall::set_code(const char *code) {
|
||||||
this->code_ = code;
|
if (code != nullptr) {
|
||||||
|
this->code_ = std::string(code);
|
||||||
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ class AlarmControlPanelCall {
|
|||||||
public:
|
public:
|
||||||
AlarmControlPanelCall(AlarmControlPanel *parent);
|
AlarmControlPanelCall(AlarmControlPanel *parent);
|
||||||
|
|
||||||
AlarmControlPanelCall &set_code(const std::string &code);
|
AlarmControlPanelCall &set_code(const char *code);
|
||||||
|
AlarmControlPanelCall &set_code(const std::string &code) { return this->set_code(code.c_str()); }
|
||||||
AlarmControlPanelCall &arm_away();
|
AlarmControlPanelCall &arm_away();
|
||||||
AlarmControlPanelCall &arm_home();
|
AlarmControlPanelCall &arm_home();
|
||||||
AlarmControlPanelCall &arm_night();
|
AlarmControlPanelCall &arm_night();
|
||||||
|
|||||||
@@ -66,15 +66,7 @@ template<typename... Ts> class ArmAwayAction : public Action<Ts...> {
|
|||||||
|
|
||||||
TEMPLATABLE_VALUE(std::string, code)
|
TEMPLATABLE_VALUE(std::string, code)
|
||||||
|
|
||||||
void play(const Ts &...x) override {
|
void play(const Ts &...x) override { this->alarm_control_panel_->arm_away(this->code_.optional_value(x...)); }
|
||||||
auto call = this->alarm_control_panel_->make_call();
|
|
||||||
auto code = this->code_.optional_value(x...);
|
|
||||||
if (code.has_value()) {
|
|
||||||
call.set_code(code.value());
|
|
||||||
}
|
|
||||||
call.arm_away();
|
|
||||||
call.perform();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AlarmControlPanel *alarm_control_panel_;
|
AlarmControlPanel *alarm_control_panel_;
|
||||||
@@ -86,15 +78,7 @@ template<typename... Ts> class ArmHomeAction : public Action<Ts...> {
|
|||||||
|
|
||||||
TEMPLATABLE_VALUE(std::string, code)
|
TEMPLATABLE_VALUE(std::string, code)
|
||||||
|
|
||||||
void play(const Ts &...x) override {
|
void play(const Ts &...x) override { this->alarm_control_panel_->arm_home(this->code_.optional_value(x...)); }
|
||||||
auto call = this->alarm_control_panel_->make_call();
|
|
||||||
auto code = this->code_.optional_value(x...);
|
|
||||||
if (code.has_value()) {
|
|
||||||
call.set_code(code.value());
|
|
||||||
}
|
|
||||||
call.arm_home();
|
|
||||||
call.perform();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AlarmControlPanel *alarm_control_panel_;
|
AlarmControlPanel *alarm_control_panel_;
|
||||||
@@ -106,15 +90,7 @@ template<typename... Ts> class ArmNightAction : public Action<Ts...> {
|
|||||||
|
|
||||||
TEMPLATABLE_VALUE(std::string, code)
|
TEMPLATABLE_VALUE(std::string, code)
|
||||||
|
|
||||||
void play(const Ts &...x) override {
|
void play(const Ts &...x) override { this->alarm_control_panel_->arm_night(this->code_.optional_value(x...)); }
|
||||||
auto call = this->alarm_control_panel_->make_call();
|
|
||||||
auto code = this->code_.optional_value(x...);
|
|
||||||
if (code.has_value()) {
|
|
||||||
call.set_code(code.value());
|
|
||||||
}
|
|
||||||
call.arm_night();
|
|
||||||
call.perform();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AlarmControlPanel *alarm_control_panel_;
|
AlarmControlPanel *alarm_control_panel_;
|
||||||
|
|||||||
@@ -1,21 +1,12 @@
|
|||||||
#include "am43_base.h"
|
#include "am43_base.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace am43 {
|
namespace am43 {
|
||||||
|
|
||||||
const uint8_t START_PACKET[5] = {0x00, 0xff, 0x00, 0x00, 0x9a};
|
const uint8_t START_PACKET[5] = {0x00, 0xff, 0x00, 0x00, 0x9a};
|
||||||
|
|
||||||
std::string pkt_to_hex(const uint8_t *data, uint16_t len) {
|
|
||||||
char buf[64];
|
|
||||||
memset(buf, 0, 64);
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
sprintf(&buf[i * 2], "%02x", data[i]);
|
|
||||||
std::string ret = buf;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
Am43Packet *Am43Encoder::get_battery_level_request() {
|
Am43Packet *Am43Encoder::get_battery_level_request() {
|
||||||
uint8_t data = 0x1;
|
uint8_t data = 0x1;
|
||||||
return this->encode_(0xA2, &data, 1);
|
return this->encode_(0xA2, &data, 1);
|
||||||
@@ -73,7 +64,9 @@ Am43Packet *Am43Encoder::encode_(uint8_t command, uint8_t *data, uint8_t length)
|
|||||||
memcpy(&this->packet_.data[7], data, length);
|
memcpy(&this->packet_.data[7], data, length);
|
||||||
this->packet_.length = length + 7;
|
this->packet_.length = length + 7;
|
||||||
this->checksum_();
|
this->checksum_();
|
||||||
ESP_LOGV("am43", "ENC(%d): 0x%s", packet_.length, pkt_to_hex(packet_.data, packet_.length).c_str());
|
char hex_buf[format_hex_size(sizeof(this->packet_.data))];
|
||||||
|
ESP_LOGV("am43", "ENC(%d): 0x%s", this->packet_.length,
|
||||||
|
format_hex_to(hex_buf, this->packet_.data, this->packet_.length));
|
||||||
return &this->packet_;
|
return &this->packet_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +81,8 @@ void Am43Decoder::decode(const uint8_t *data, uint16_t length) {
|
|||||||
this->has_set_state_response_ = false;
|
this->has_set_state_response_ = false;
|
||||||
this->has_position_ = false;
|
this->has_position_ = false;
|
||||||
this->has_pin_response_ = false;
|
this->has_pin_response_ = false;
|
||||||
ESP_LOGV("am43", "DEC(%d): 0x%s", length, pkt_to_hex(data, length).c_str());
|
char hex_buf[format_hex_size(24)]; // Max expected packet size
|
||||||
|
ESP_LOGV("am43", "DEC(%d): 0x%s", length, format_hex_to(hex_buf, data, length));
|
||||||
|
|
||||||
if (length < 2 || data[0] != 0x9a)
|
if (length < 2 || data[0] != 0x9a)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -18,31 +18,31 @@ AnovaPacket *AnovaCodec::clean_packet_() {
|
|||||||
|
|
||||||
AnovaPacket *AnovaCodec::get_read_device_status_request() {
|
AnovaPacket *AnovaCodec::get_read_device_status_request() {
|
||||||
this->current_query_ = READ_DEVICE_STATUS;
|
this->current_query_ = READ_DEVICE_STATUS;
|
||||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_DEVICE_STATUS);
|
snprintf((char *) this->packet_.data, sizeof(this->packet_.data), "%s", CMD_READ_DEVICE_STATUS);
|
||||||
return this->clean_packet_();
|
return this->clean_packet_();
|
||||||
}
|
}
|
||||||
|
|
||||||
AnovaPacket *AnovaCodec::get_read_target_temp_request() {
|
AnovaPacket *AnovaCodec::get_read_target_temp_request() {
|
||||||
this->current_query_ = READ_TARGET_TEMPERATURE;
|
this->current_query_ = READ_TARGET_TEMPERATURE;
|
||||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_TARGET_TEMP);
|
snprintf((char *) this->packet_.data, sizeof(this->packet_.data), "%s", CMD_READ_TARGET_TEMP);
|
||||||
return this->clean_packet_();
|
return this->clean_packet_();
|
||||||
}
|
}
|
||||||
|
|
||||||
AnovaPacket *AnovaCodec::get_read_current_temp_request() {
|
AnovaPacket *AnovaCodec::get_read_current_temp_request() {
|
||||||
this->current_query_ = READ_CURRENT_TEMPERATURE;
|
this->current_query_ = READ_CURRENT_TEMPERATURE;
|
||||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_CURRENT_TEMP);
|
snprintf((char *) this->packet_.data, sizeof(this->packet_.data), "%s", CMD_READ_CURRENT_TEMP);
|
||||||
return this->clean_packet_();
|
return this->clean_packet_();
|
||||||
}
|
}
|
||||||
|
|
||||||
AnovaPacket *AnovaCodec::get_read_unit_request() {
|
AnovaPacket *AnovaCodec::get_read_unit_request() {
|
||||||
this->current_query_ = READ_UNIT;
|
this->current_query_ = READ_UNIT;
|
||||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_UNIT);
|
snprintf((char *) this->packet_.data, sizeof(this->packet_.data), "%s", CMD_READ_UNIT);
|
||||||
return this->clean_packet_();
|
return this->clean_packet_();
|
||||||
}
|
}
|
||||||
|
|
||||||
AnovaPacket *AnovaCodec::get_read_data_request() {
|
AnovaPacket *AnovaCodec::get_read_data_request() {
|
||||||
this->current_query_ = READ_DATA;
|
this->current_query_ = READ_DATA;
|
||||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_DATA);
|
snprintf((char *) this->packet_.data, sizeof(this->packet_.data), "%s", CMD_READ_DATA);
|
||||||
return this->clean_packet_();
|
return this->clean_packet_();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,25 +50,25 @@ AnovaPacket *AnovaCodec::get_set_target_temp_request(float temperature) {
|
|||||||
this->current_query_ = SET_TARGET_TEMPERATURE;
|
this->current_query_ = SET_TARGET_TEMPERATURE;
|
||||||
if (this->fahrenheit_)
|
if (this->fahrenheit_)
|
||||||
temperature = ctof(temperature);
|
temperature = ctof(temperature);
|
||||||
sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature);
|
snprintf((char *) this->packet_.data, sizeof(this->packet_.data), CMD_SET_TARGET_TEMP, temperature);
|
||||||
return this->clean_packet_();
|
return this->clean_packet_();
|
||||||
}
|
}
|
||||||
|
|
||||||
AnovaPacket *AnovaCodec::get_set_unit_request(char unit) {
|
AnovaPacket *AnovaCodec::get_set_unit_request(char unit) {
|
||||||
this->current_query_ = SET_UNIT;
|
this->current_query_ = SET_UNIT;
|
||||||
sprintf((char *) this->packet_.data, CMD_SET_TEMP_UNIT, unit);
|
snprintf((char *) this->packet_.data, sizeof(this->packet_.data), CMD_SET_TEMP_UNIT, unit);
|
||||||
return this->clean_packet_();
|
return this->clean_packet_();
|
||||||
}
|
}
|
||||||
|
|
||||||
AnovaPacket *AnovaCodec::get_start_request() {
|
AnovaPacket *AnovaCodec::get_start_request() {
|
||||||
this->current_query_ = START;
|
this->current_query_ = START;
|
||||||
sprintf((char *) this->packet_.data, CMD_START);
|
snprintf((char *) this->packet_.data, sizeof(this->packet_.data), "%s", CMD_START);
|
||||||
return this->clean_packet_();
|
return this->clean_packet_();
|
||||||
}
|
}
|
||||||
|
|
||||||
AnovaPacket *AnovaCodec::get_stop_request() {
|
AnovaPacket *AnovaCodec::get_stop_request() {
|
||||||
this->current_query_ = STOP;
|
this->current_query_ = STOP;
|
||||||
sprintf((char *) this->packet_.data, CMD_STOP);
|
snprintf((char *) this->packet_.data, sizeof(this->packet_.data), "%s", CMD_STOP);
|
||||||
return this->clean_packet_();
|
return this->clean_packet_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import logging
|
|||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import Condition
|
from esphome.automation import Condition
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components.logger import request_log_listener
|
||||||
from esphome.config_helpers import get_logger_level
|
from esphome.config_helpers import get_logger_level
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
@@ -326,6 +327,9 @@ async def to_code(config: ConfigType) -> None:
|
|||||||
# Track controller registration for StaticVector sizing
|
# Track controller registration for StaticVector sizing
|
||||||
CORE.register_controller()
|
CORE.register_controller()
|
||||||
|
|
||||||
|
# Request a log listener slot for API log streaming
|
||||||
|
request_log_listener()
|
||||||
|
|
||||||
cg.add(var.set_port(config[CONF_PORT]))
|
cg.add(var.set_port(config[CONF_PORT]))
|
||||||
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
||||||
cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY]))
|
cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY]))
|
||||||
|
|||||||
@@ -265,8 +265,7 @@ void APIConnection::loop() {
|
|||||||
// If we can't send the ping request directly (tx_buffer full),
|
// If we can't send the ping request directly (tx_buffer full),
|
||||||
// schedule it at the front of the batch so it will be sent with priority
|
// schedule it at the front of the batch so it will be sent with priority
|
||||||
ESP_LOGW(TAG, "Buffer full, ping queued");
|
ESP_LOGW(TAG, "Buffer full, ping queued");
|
||||||
this->schedule_message_front_(nullptr, &APIConnection::try_send_ping_request, PingRequest::MESSAGE_TYPE,
|
this->schedule_message_front_(nullptr, PingRequest::MESSAGE_TYPE, PingRequest::ESTIMATED_SIZE);
|
||||||
PingRequest::ESTIMATED_SIZE);
|
|
||||||
this->flags_.sent_ping = true; // Mark as sent to avoid scheduling multiple pings
|
this->flags_.sent_ping = true; // Mark as sent to avoid scheduling multiple pings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,7 +304,8 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
// If in log-only mode, just log and return
|
// If in log-only mode, just log and return
|
||||||
if (conn->flags_.log_only_mode) {
|
if (conn->flags_.log_only_mode) {
|
||||||
conn->log_send_message_(msg.message_name(), msg.dump());
|
DumpBuffer dump_buf;
|
||||||
|
conn->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));
|
||||||
return 1; // Return non-zero to indicate "success" for logging
|
return 1; // Return non-zero to indicate "success" for logging
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -361,8 +361,8 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
|
|||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor) {
|
bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor) {
|
||||||
return this->send_message_smart_(binary_sensor, &APIConnection::try_send_binary_sensor_state,
|
return this->send_message_smart_(binary_sensor, BinarySensorStateResponse::MESSAGE_TYPE,
|
||||||
BinarySensorStateResponse::MESSAGE_TYPE, BinarySensorStateResponse::ESTIMATED_SIZE);
|
BinarySensorStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -388,8 +388,7 @@ uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConne
|
|||||||
|
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
bool APIConnection::send_cover_state(cover::Cover *cover) {
|
bool APIConnection::send_cover_state(cover::Cover *cover) {
|
||||||
return this->send_message_smart_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(cover, CoverStateResponse::MESSAGE_TYPE, CoverStateResponse::ESTIMATED_SIZE);
|
||||||
CoverStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
@@ -429,8 +428,7 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool APIConnection::send_fan_state(fan::Fan *fan) {
|
bool APIConnection::send_fan_state(fan::Fan *fan) {
|
||||||
return this->send_message_smart_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(fan, FanStateResponse::MESSAGE_TYPE, FanStateResponse::ESTIMATED_SIZE);
|
||||||
FanStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
@@ -481,8 +479,7 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool APIConnection::send_light_state(light::LightState *light) {
|
bool APIConnection::send_light_state(light::LightState *light) {
|
||||||
return this->send_message_smart_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(light, LightStateResponse::MESSAGE_TYPE, LightStateResponse::ESTIMATED_SIZE);
|
||||||
LightStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
@@ -568,8 +565,7 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
bool APIConnection::send_sensor_state(sensor::Sensor *sensor) {
|
bool APIConnection::send_sensor_state(sensor::Sensor *sensor) {
|
||||||
return this->send_message_smart_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(sensor, SensorStateResponse::MESSAGE_TYPE, SensorStateResponse::ESTIMATED_SIZE);
|
||||||
SensorStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -597,8 +593,7 @@ uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *
|
|||||||
|
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
bool APIConnection::send_switch_state(switch_::Switch *a_switch) {
|
bool APIConnection::send_switch_state(switch_::Switch *a_switch) {
|
||||||
return this->send_message_smart_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(a_switch, SwitchStateResponse::MESSAGE_TYPE, SwitchStateResponse::ESTIMATED_SIZE);
|
||||||
SwitchStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -632,8 +627,8 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor) {
|
bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor) {
|
||||||
return this->send_message_smart_(text_sensor, &APIConnection::try_send_text_sensor_state,
|
return this->send_message_smart_(text_sensor, TextSensorStateResponse::MESSAGE_TYPE,
|
||||||
TextSensorStateResponse::MESSAGE_TYPE, TextSensorStateResponse::ESTIMATED_SIZE);
|
TextSensorStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -657,8 +652,7 @@ uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnect
|
|||||||
|
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
bool APIConnection::send_climate_state(climate::Climate *climate) {
|
bool APIConnection::send_climate_state(climate::Climate *climate) {
|
||||||
return this->send_message_smart_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(climate, ClimateStateResponse::MESSAGE_TYPE, ClimateStateResponse::ESTIMATED_SIZE);
|
||||||
ClimateStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
@@ -753,8 +747,7 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
bool APIConnection::send_number_state(number::Number *number) {
|
bool APIConnection::send_number_state(number::Number *number) {
|
||||||
return this->send_message_smart_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(number, NumberStateResponse::MESSAGE_TYPE, NumberStateResponse::ESTIMATED_SIZE);
|
||||||
NumberStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -788,8 +781,7 @@ void APIConnection::number_command(const NumberCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
bool APIConnection::send_date_state(datetime::DateEntity *date) {
|
bool APIConnection::send_date_state(datetime::DateEntity *date) {
|
||||||
return this->send_message_smart_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(date, DateStateResponse::MESSAGE_TYPE, DateStateResponse::ESTIMATED_SIZE);
|
||||||
DateStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
@@ -817,8 +809,7 @@ void APIConnection::date_command(const DateCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_DATETIME_TIME
|
#ifdef USE_DATETIME_TIME
|
||||||
bool APIConnection::send_time_state(datetime::TimeEntity *time) {
|
bool APIConnection::send_time_state(datetime::TimeEntity *time) {
|
||||||
return this->send_message_smart_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(time, TimeStateResponse::MESSAGE_TYPE, TimeStateResponse::ESTIMATED_SIZE);
|
||||||
TimeStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
@@ -846,8 +837,8 @@ void APIConnection::time_command(const TimeCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) {
|
bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) {
|
||||||
return this->send_message_smart_(datetime, &APIConnection::try_send_datetime_state,
|
return this->send_message_smart_(datetime, DateTimeStateResponse::MESSAGE_TYPE,
|
||||||
DateTimeStateResponse::MESSAGE_TYPE, DateTimeStateResponse::ESTIMATED_SIZE);
|
DateTimeStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
@@ -877,8 +868,7 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool APIConnection::send_text_state(text::Text *text) {
|
bool APIConnection::send_text_state(text::Text *text) {
|
||||||
return this->send_message_smart_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(text, TextStateResponse::MESSAGE_TYPE, TextStateResponse::ESTIMATED_SIZE);
|
||||||
TextStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -910,8 +900,7 @@ void APIConnection::text_command(const TextCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
bool APIConnection::send_select_state(select::Select *select) {
|
bool APIConnection::send_select_state(select::Select *select) {
|
||||||
return this->send_message_smart_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(select, SelectStateResponse::MESSAGE_TYPE, SelectStateResponse::ESTIMATED_SIZE);
|
||||||
SelectStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -955,8 +944,7 @@ void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg
|
|||||||
|
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
bool APIConnection::send_lock_state(lock::Lock *a_lock) {
|
bool APIConnection::send_lock_state(lock::Lock *a_lock) {
|
||||||
return this->send_message_smart_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(a_lock, LockStateResponse::MESSAGE_TYPE, LockStateResponse::ESTIMATED_SIZE);
|
||||||
LockStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -996,8 +984,7 @@ void APIConnection::lock_command(const LockCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
bool APIConnection::send_valve_state(valve::Valve *valve) {
|
bool APIConnection::send_valve_state(valve::Valve *valve) {
|
||||||
return this->send_message_smart_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(valve, ValveStateResponse::MESSAGE_TYPE, ValveStateResponse::ESTIMATED_SIZE);
|
||||||
ValveStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
@@ -1031,8 +1018,8 @@ void APIConnection::valve_command(const ValveCommandRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) {
|
bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) {
|
||||||
return this->send_message_smart_(media_player, &APIConnection::try_send_media_player_state,
|
return this->send_message_smart_(media_player, MediaPlayerStateResponse::MESSAGE_TYPE,
|
||||||
MediaPlayerStateResponse::MESSAGE_TYPE, MediaPlayerStateResponse::ESTIMATED_SIZE);
|
MediaPlayerStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
@@ -1314,8 +1301,7 @@ void APIConnection::zwave_proxy_request(const ZWaveProxyRequest &msg) {
|
|||||||
|
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||||
return this->send_message_smart_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_state,
|
return this->send_message_smart_(a_alarm_control_panel, AlarmControlPanelStateResponse::MESSAGE_TYPE,
|
||||||
AlarmControlPanelStateResponse::MESSAGE_TYPE,
|
|
||||||
AlarmControlPanelStateResponse::ESTIMATED_SIZE);
|
AlarmControlPanelStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn,
|
uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn,
|
||||||
@@ -1368,8 +1354,8 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
|
|||||||
|
|
||||||
#ifdef USE_WATER_HEATER
|
#ifdef USE_WATER_HEATER
|
||||||
bool APIConnection::send_water_heater_state(water_heater::WaterHeater *water_heater) {
|
bool APIConnection::send_water_heater_state(water_heater::WaterHeater *water_heater) {
|
||||||
return this->send_message_smart_(water_heater, &APIConnection::try_send_water_heater_state,
|
return this->send_message_smart_(water_heater, WaterHeaterStateResponse::MESSAGE_TYPE,
|
||||||
WaterHeaterStateResponse::MESSAGE_TYPE, WaterHeaterStateResponse::ESTIMATED_SIZE);
|
WaterHeaterStateResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_water_heater_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_water_heater_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
@@ -1418,10 +1404,11 @@ void APIConnection::on_water_heater_command_request(const WaterHeaterCommandRequ
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
void APIConnection::send_event(event::Event *event, StringRef event_type) {
|
// Event is a special case - unlike other entities with simple state fields,
|
||||||
// get_last_event_type() returns StringRef pointing to null-terminated string literals from codegen
|
// events store their state in a member accessed via obj->get_last_event_type()
|
||||||
this->send_message_smart_(event, MessageCreator(event_type.c_str()), EventResponse::MESSAGE_TYPE,
|
void APIConnection::send_event(event::Event *event) {
|
||||||
EventResponse::ESTIMATED_SIZE);
|
this->send_message_smart_(event, EventResponse::MESSAGE_TYPE, EventResponse::ESTIMATED_SIZE,
|
||||||
|
event->get_last_event_type_index());
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_event_response(event::Event *event, StringRef event_type, APIConnection *conn,
|
uint16_t APIConnection::try_send_event_response(event::Event *event, StringRef event_type, APIConnection *conn,
|
||||||
uint32_t remaining_size, bool is_single) {
|
uint32_t remaining_size, bool is_single) {
|
||||||
@@ -1472,8 +1459,7 @@ uint16_t APIConnection::try_send_infrared_info(EntityBase *entity, APIConnection
|
|||||||
|
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
bool APIConnection::send_update_state(update::UpdateEntity *update) {
|
bool APIConnection::send_update_state(update::UpdateEntity *update) {
|
||||||
return this->send_message_smart_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE,
|
return this->send_message_smart_(update, UpdateStateResponse::MESSAGE_TYPE, UpdateStateResponse::ESTIMATED_SIZE);
|
||||||
UpdateStateResponse::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
bool is_single) {
|
bool is_single) {
|
||||||
@@ -1726,17 +1712,16 @@ void APIConnection::on_home_assistant_state_response(const HomeAssistantStateRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create null-terminated state for callback (parse_number needs null-termination)
|
// Create null-terminated state for callback (parse_number needs null-termination)
|
||||||
// HA state max length is 255, so 256 byte buffer covers all cases
|
// HA state max length is 255 characters, but attributes can be much longer
|
||||||
char state_buf[256];
|
// Use stack buffer for common case (states), heap fallback for large attributes
|
||||||
size_t copy_len = msg.state.size();
|
size_t state_len = msg.state.size();
|
||||||
if (copy_len >= sizeof(state_buf)) {
|
SmallBufferWithHeapFallback<MAX_STATE_LEN + 1> state_buf_alloc(state_len + 1);
|
||||||
copy_len = sizeof(state_buf) - 1; // Truncate to leave space for null terminator
|
char *state_buf = reinterpret_cast<char *>(state_buf_alloc.get());
|
||||||
|
if (state_len > 0) {
|
||||||
|
memcpy(state_buf, msg.state.c_str(), state_len);
|
||||||
}
|
}
|
||||||
if (copy_len > 0) {
|
state_buf[state_len] = '\0';
|
||||||
memcpy(state_buf, msg.state.c_str(), copy_len);
|
it.callback(StringRef(state_buf, state_len));
|
||||||
}
|
|
||||||
state_buf[copy_len] = '\0';
|
|
||||||
it.callback(StringRef(state_buf, copy_len));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1859,23 +1844,8 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle Nagle's algorithm based on message type to prevent log messages from
|
// Set TCP_NODELAY based on message type - see set_nodelay_for_message() for details
|
||||||
// filling the TCP send buffer and crowding out important state updates.
|
this->helper_->set_nodelay_for_message(is_log_message);
|
||||||
//
|
|
||||||
// This honors the `no_delay` proto option - SubscribeLogsResponse is the only
|
|
||||||
// message with `option (no_delay) = false;` in api.proto, indicating it should
|
|
||||||
// allow Nagle coalescing. This option existed since 2019 but was never implemented.
|
|
||||||
//
|
|
||||||
// - Log messages: Enable Nagle (NODELAY=false) so small log packets coalesce
|
|
||||||
// into fewer, larger packets. They flush naturally via TCP delayed ACK timer
|
|
||||||
// (~200ms), buffer filling, or when a state update triggers a flush.
|
|
||||||
//
|
|
||||||
// - All other messages (state updates, responses): Disable Nagle (NODELAY=true)
|
|
||||||
// for immediate delivery. These are time-sensitive and should not be delayed.
|
|
||||||
//
|
|
||||||
// This must be done proactively BEFORE the buffer fills up - checking buffer
|
|
||||||
// state here would be too late since we'd already be in a degraded state.
|
|
||||||
this->helper_->set_nodelay(!is_log_message);
|
|
||||||
|
|
||||||
APIError err = this->helper_->write_protobuf_packet(message_type, buffer);
|
APIError err = this->helper_->write_protobuf_packet(message_type, buffer);
|
||||||
if (err == APIError::WOULD_BLOCK)
|
if (err == APIError::WOULD_BLOCK)
|
||||||
@@ -1896,30 +1866,31 @@ void APIConnection::on_fatal_error() {
|
|||||||
this->flags_.remove = true;
|
this->flags_.remove = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint8_t message_type,
|
void APIConnection::DeferredBatch::add_item(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
|
||||||
uint8_t estimated_size) {
|
uint8_t aux_data_index) {
|
||||||
// Check if we already have a message of this type for this entity
|
// Check if we already have a message of this type for this entity
|
||||||
// This provides deduplication per entity/message_type combination
|
// This provides deduplication per entity/message_type combination
|
||||||
// O(n) but optimized for RAM and not performance.
|
// O(n) but optimized for RAM and not performance.
|
||||||
for (auto &item : items) {
|
// Skip deduplication for events - they are edge-triggered, every occurrence matters
|
||||||
if (item.entity == entity && item.message_type == message_type) {
|
#ifdef USE_EVENT
|
||||||
// Replace with new creator
|
if (message_type != EventResponse::MESSAGE_TYPE)
|
||||||
item.creator = creator;
|
#endif
|
||||||
return;
|
{
|
||||||
|
for (const auto &item : items) {
|
||||||
|
if (item.entity == entity && item.message_type == message_type)
|
||||||
|
return; // Already queued
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// No existing item found (or event), add new one
|
||||||
|
items.push_back({entity, message_type, estimated_size, aux_data_index});
|
||||||
|
}
|
||||||
|
|
||||||
// No existing item found, add new one
|
void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, uint8_t message_type, uint8_t estimated_size) {
|
||||||
items.emplace_back(entity, creator, message_type, estimated_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, MessageCreator creator, uint8_t message_type,
|
|
||||||
uint8_t estimated_size) {
|
|
||||||
// Add high priority message and swap to front
|
// Add high priority message and swap to front
|
||||||
// This avoids expensive vector::insert which shifts all elements
|
// This avoids expensive vector::insert which shifts all elements
|
||||||
// Note: We only ever have one high-priority message at a time (ping OR disconnect)
|
// Note: We only ever have one high-priority message at a time (ping OR disconnect)
|
||||||
// If we're disconnecting, pings are blocked, so this simple swap is sufficient
|
// If we're disconnecting, pings are blocked, so this simple swap is sufficient
|
||||||
items.emplace_back(entity, creator, message_type, estimated_size);
|
items.push_back({entity, message_type, estimated_size, AUX_DATA_UNUSED});
|
||||||
if (items.size() > 1) {
|
if (items.size() > 1) {
|
||||||
// Swap the new high-priority item to the front
|
// Swap the new high-priority item to the front
|
||||||
std::swap(items.front(), items.back());
|
std::swap(items.front(), items.back());
|
||||||
@@ -1958,19 +1929,17 @@ void APIConnection::process_batch_() {
|
|||||||
if (num_items == 1) {
|
if (num_items == 1) {
|
||||||
const auto &item = this->deferred_batch_[0];
|
const auto &item = this->deferred_batch_[0];
|
||||||
|
|
||||||
// Let the creator calculate size and encode if it fits
|
// Let dispatch_message_ calculate size and encode if it fits
|
||||||
uint16_t payload_size =
|
uint16_t payload_size = this->dispatch_message_(item, std::numeric_limits<uint16_t>::max(), true);
|
||||||
item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true, item.message_type);
|
|
||||||
|
|
||||||
if (payload_size > 0 && this->send_buffer(ProtoWriteBuffer{&shared_buf}, item.message_type)) {
|
if (payload_size > 0 && this->send_buffer(ProtoWriteBuffer{&shared_buf}, item.message_type)) {
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
// Log messages after send attempt for VV debugging
|
// Log message after send attempt for VV debugging
|
||||||
// It's safe to use the buffer for logging at this point regardless of send result
|
|
||||||
this->log_batch_item_(item);
|
this->log_batch_item_(item);
|
||||||
#endif
|
#endif
|
||||||
this->clear_batch_();
|
this->clear_batch_();
|
||||||
} else if (payload_size == 0) {
|
} else if (payload_size == 0) {
|
||||||
// Message too large
|
// Message too large to fit in available space
|
||||||
ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type);
|
ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type);
|
||||||
this->clear_batch_();
|
this->clear_batch_();
|
||||||
}
|
}
|
||||||
@@ -2015,9 +1984,9 @@ void APIConnection::process_batch_() {
|
|||||||
// Process items and encode directly to buffer (up to our limit)
|
// Process items and encode directly to buffer (up to our limit)
|
||||||
for (size_t i = 0; i < messages_to_process; i++) {
|
for (size_t i = 0; i < messages_to_process; i++) {
|
||||||
const auto &item = this->deferred_batch_[i];
|
const auto &item = this->deferred_batch_[i];
|
||||||
// Try to encode message
|
// Try to encode message via dispatch
|
||||||
// The creator will calculate overhead to determine if the message fits
|
// The dispatch function calculates overhead to determine if the message fits
|
||||||
uint16_t payload_size = item.creator(item.entity, this, remaining_size, false, item.message_type);
|
uint16_t payload_size = this->dispatch_message_(item, remaining_size, false);
|
||||||
|
|
||||||
if (payload_size == 0) {
|
if (payload_size == 0) {
|
||||||
// Message won't fit, stop processing
|
// Message won't fit, stop processing
|
||||||
@@ -2083,18 +2052,129 @@ void APIConnection::process_batch_() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
// Dispatch message encoding based on message_type
|
||||||
bool is_single, uint8_t message_type) const {
|
// Switch assigns function pointer, single call site for smaller code size
|
||||||
|
uint16_t APIConnection::dispatch_message_(const DeferredBatch::BatchItem &item, uint32_t remaining_size,
|
||||||
|
bool is_single) {
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
// Special case: EventResponse uses const char * pointer
|
// Events need aux_data_index to look up event type from entity
|
||||||
if (message_type == EventResponse::MESSAGE_TYPE) {
|
if (item.message_type == EventResponse::MESSAGE_TYPE) {
|
||||||
auto *e = static_cast<event::Event *>(entity);
|
// Skip if aux_data_index is invalid (should never happen in normal operation)
|
||||||
return APIConnection::try_send_event_response(e, StringRef(data_.const_char_ptr), conn, remaining_size, is_single);
|
if (item.aux_data_index == DeferredBatch::AUX_DATA_UNUSED)
|
||||||
|
return 0;
|
||||||
|
auto *event = static_cast<event::Event *>(item.entity);
|
||||||
|
return try_send_event_response(event, StringRef::from_maybe_nullptr(event->get_event_type(item.aux_data_index)),
|
||||||
|
this, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// All other message types use function pointers
|
// All other message types use function pointer lookup via switch
|
||||||
return data_.function_ptr(entity, conn, remaining_size, is_single);
|
MessageCreatorPtr func = nullptr;
|
||||||
|
|
||||||
|
// Macros to reduce repetitive switch cases
|
||||||
|
#define CASE_STATE_INFO(entity_name, StateResp, InfoResp) \
|
||||||
|
case StateResp::MESSAGE_TYPE: \
|
||||||
|
func = &try_send_##entity_name##_state; \
|
||||||
|
break; \
|
||||||
|
case InfoResp::MESSAGE_TYPE: \
|
||||||
|
func = &try_send_##entity_name##_info; \
|
||||||
|
break;
|
||||||
|
#define CASE_INFO_ONLY(entity_name, InfoResp) \
|
||||||
|
case InfoResp::MESSAGE_TYPE: \
|
||||||
|
func = &try_send_##entity_name##_info; \
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (item.message_type) {
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
CASE_STATE_INFO(binary_sensor, BinarySensorStateResponse, ListEntitiesBinarySensorResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_COVER
|
||||||
|
CASE_STATE_INFO(cover, CoverStateResponse, ListEntitiesCoverResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_FAN
|
||||||
|
CASE_STATE_INFO(fan, FanStateResponse, ListEntitiesFanResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_LIGHT
|
||||||
|
CASE_STATE_INFO(light, LightStateResponse, ListEntitiesLightResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
CASE_STATE_INFO(sensor, SensorStateResponse, ListEntitiesSensorResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
CASE_STATE_INFO(switch, SwitchStateResponse, ListEntitiesSwitchResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BUTTON
|
||||||
|
CASE_INFO_ONLY(button, ListEntitiesButtonResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_TEXT_SENSOR
|
||||||
|
CASE_STATE_INFO(text_sensor, TextSensorStateResponse, ListEntitiesTextSensorResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_CLIMATE
|
||||||
|
CASE_STATE_INFO(climate, ClimateStateResponse, ListEntitiesClimateResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
CASE_STATE_INFO(number, NumberStateResponse, ListEntitiesNumberResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
CASE_STATE_INFO(date, DateStateResponse, ListEntitiesDateResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
CASE_STATE_INFO(time, TimeStateResponse, ListEntitiesTimeResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATETIME
|
||||||
|
CASE_STATE_INFO(datetime, DateTimeStateResponse, ListEntitiesDateTimeResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_TEXT
|
||||||
|
CASE_STATE_INFO(text, TextStateResponse, ListEntitiesTextResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
CASE_STATE_INFO(select, SelectStateResponse, ListEntitiesSelectResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_LOCK
|
||||||
|
CASE_STATE_INFO(lock, LockStateResponse, ListEntitiesLockResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_VALVE
|
||||||
|
CASE_STATE_INFO(valve, ValveStateResponse, ListEntitiesValveResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_MEDIA_PLAYER
|
||||||
|
CASE_STATE_INFO(media_player, MediaPlayerStateResponse, ListEntitiesMediaPlayerResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
CASE_STATE_INFO(alarm_control_panel, AlarmControlPanelStateResponse, ListEntitiesAlarmControlPanelResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_WATER_HEATER
|
||||||
|
CASE_STATE_INFO(water_heater, WaterHeaterStateResponse, ListEntitiesWaterHeaterResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_CAMERA
|
||||||
|
CASE_INFO_ONLY(camera, ListEntitiesCameraResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_INFRARED
|
||||||
|
CASE_INFO_ONLY(infrared, ListEntitiesInfraredResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_EVENT
|
||||||
|
CASE_INFO_ONLY(event, ListEntitiesEventResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_UPDATE
|
||||||
|
CASE_STATE_INFO(update, UpdateStateResponse, ListEntitiesUpdateResponse)
|
||||||
|
#endif
|
||||||
|
// Special messages (not entity state/info)
|
||||||
|
case ListEntitiesDoneResponse::MESSAGE_TYPE:
|
||||||
|
func = &try_send_list_info_done;
|
||||||
|
break;
|
||||||
|
case DisconnectRequest::MESSAGE_TYPE:
|
||||||
|
func = &try_send_disconnect_request;
|
||||||
|
break;
|
||||||
|
case PingRequest::MESSAGE_TYPE:
|
||||||
|
func = &try_send_ping_request;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CASE_STATE_INFO
|
||||||
|
#undef CASE_INFO_ONLY
|
||||||
|
|
||||||
|
return func(item.entity, this, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "esphome/core/string_ref.h"
|
#include "esphome/core/string_ref.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace esphome::api {
|
namespace esphome::api {
|
||||||
@@ -38,8 +39,8 @@ class APIConnection final : public APIServerConnection {
|
|||||||
void loop();
|
void loop();
|
||||||
|
|
||||||
bool send_list_info_done() {
|
bool send_list_info_done() {
|
||||||
return this->schedule_message_(nullptr, &APIConnection::try_send_list_info_done,
|
return this->schedule_message_(nullptr, ListEntitiesDoneResponse::MESSAGE_TYPE,
|
||||||
ListEntitiesDoneResponse::MESSAGE_TYPE, ListEntitiesDoneResponse::ESTIMATED_SIZE);
|
ListEntitiesDoneResponse::ESTIMATED_SIZE);
|
||||||
}
|
}
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor);
|
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor);
|
||||||
@@ -178,7 +179,7 @@ class APIConnection final : public APIServerConnection {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
void send_event(event::Event *event, StringRef event_type);
|
void send_event(event::Event *event);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
@@ -540,33 +541,17 @@ class APIConnection final : public APIServerConnection {
|
|||||||
// Function pointer type for message encoding
|
// Function pointer type for message encoding
|
||||||
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
|
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
|
||||||
|
|
||||||
class MessageCreator {
|
|
||||||
public:
|
|
||||||
MessageCreator(MessageCreatorPtr ptr) { data_.function_ptr = ptr; }
|
|
||||||
explicit MessageCreator(const char *str_value) { data_.const_char_ptr = str_value; }
|
|
||||||
|
|
||||||
// Call operator - uses message_type to determine union type
|
|
||||||
uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single,
|
|
||||||
uint8_t message_type) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
union Data {
|
|
||||||
MessageCreatorPtr function_ptr;
|
|
||||||
const char *const_char_ptr;
|
|
||||||
} data_; // 4 bytes on 32-bit, 8 bytes on 64-bit
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generic batching mechanism for both state updates and entity info
|
// Generic batching mechanism for both state updates and entity info
|
||||||
struct DeferredBatch {
|
struct DeferredBatch {
|
||||||
struct BatchItem {
|
// Sentinel value for unused aux_data_index
|
||||||
EntityBase *entity; // Entity pointer
|
static constexpr uint8_t AUX_DATA_UNUSED = std::numeric_limits<uint8_t>::max();
|
||||||
MessageCreator creator; // Function that creates the message when needed
|
|
||||||
uint8_t message_type; // Message type for overhead calculation (max 255)
|
|
||||||
uint8_t estimated_size; // Estimated message size (max 255 bytes)
|
|
||||||
|
|
||||||
// Constructor for creating BatchItem
|
struct BatchItem {
|
||||||
BatchItem(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size)
|
EntityBase *entity; // 4 bytes - Entity pointer
|
||||||
: entity(entity), creator(creator), message_type(message_type), estimated_size(estimated_size) {}
|
uint8_t message_type; // 1 byte - Message type for protocol and dispatch
|
||||||
|
uint8_t estimated_size; // 1 byte - Estimated message size (max 255 bytes)
|
||||||
|
uint8_t aux_data_index{AUX_DATA_UNUSED}; // 1 byte - For events: index into entity's event_types
|
||||||
|
// 1 byte padding
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<BatchItem> items;
|
std::vector<BatchItem> items;
|
||||||
@@ -575,10 +560,11 @@ class APIConnection final : public APIServerConnection {
|
|||||||
// No pre-allocation - log connections never use batching, and for
|
// No pre-allocation - log connections never use batching, and for
|
||||||
// connections that do, buffers are released after initial sync anyway
|
// connections that do, buffers are released after initial sync anyway
|
||||||
|
|
||||||
// Add item to the batch
|
// Add item to the batch (with deduplication)
|
||||||
void add_item(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size);
|
void add_item(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
|
||||||
|
uint8_t aux_data_index = AUX_DATA_UNUSED);
|
||||||
// Add item to the front of the batch (for high priority messages like ping)
|
// Add item to the front of the batch (for high priority messages like ping)
|
||||||
void add_item_front(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size);
|
void add_item_front(EntityBase *entity, uint8_t message_type, uint8_t estimated_size);
|
||||||
|
|
||||||
// Clear all items
|
// Clear all items
|
||||||
void clear() {
|
void clear() {
|
||||||
@@ -592,6 +578,7 @@ class APIConnection final : public APIServerConnection {
|
|||||||
bool empty() const { return items.empty(); }
|
bool empty() const { return items.empty(); }
|
||||||
size_t size() const { return items.size(); }
|
size_t size() const { return items.size(); }
|
||||||
const BatchItem &operator[](size_t index) const { return items[index]; }
|
const BatchItem &operator[](size_t index) const { return items[index]; }
|
||||||
|
|
||||||
// Release excess capacity - only releases if items already empty
|
// Release excess capacity - only releases if items already empty
|
||||||
void release_buffer() {
|
void release_buffer() {
|
||||||
// Safe to call: batch is processed before release_buffer is called,
|
// Safe to call: batch is processed before release_buffer is called,
|
||||||
@@ -663,17 +650,15 @@ class APIConnection final : public APIServerConnection {
|
|||||||
this->flags_.batch_scheduled = false;
|
this->flags_.batch_scheduled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
// Dispatch message encoding based on message_type - replaces function pointer storage
|
||||||
// Helper to log a proto message from a MessageCreator object
|
// Switch assigns pointer, single call site for smaller code size
|
||||||
void log_proto_message_(EntityBase *entity, const MessageCreator &creator, uint8_t message_type) {
|
uint16_t dispatch_message_(const DeferredBatch::BatchItem &item, uint32_t remaining_size, bool is_single);
|
||||||
this->flags_.log_only_mode = true;
|
|
||||||
creator(entity, this, MAX_BATCH_PACKET_SIZE, true, message_type);
|
|
||||||
this->flags_.log_only_mode = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void log_batch_item_(const DeferredBatch::BatchItem &item) {
|
void log_batch_item_(const DeferredBatch::BatchItem &item) {
|
||||||
// Use the helper to log the message
|
this->flags_.log_only_mode = true;
|
||||||
this->log_proto_message_(item.entity, item.creator, item.message_type);
|
this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE, true);
|
||||||
|
this->flags_.log_only_mode = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -698,63 +683,31 @@ class APIConnection final : public APIServerConnection {
|
|||||||
// Helper method to send a message either immediately or via batching
|
// Helper method to send a message either immediately or via batching
|
||||||
// Tries immediate send if should_send_immediately_() returns true and buffer has space
|
// Tries immediate send if should_send_immediately_() returns true and buffer has space
|
||||||
// Falls back to batching if immediate send fails or isn't applicable
|
// Falls back to batching if immediate send fails or isn't applicable
|
||||||
bool send_message_smart_(EntityBase *entity, MessageCreatorPtr creator, uint8_t message_type,
|
bool send_message_smart_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
|
||||||
uint8_t estimated_size) {
|
uint8_t aux_data_index = DeferredBatch::AUX_DATA_UNUSED) {
|
||||||
if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) {
|
if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) {
|
||||||
// Now actually encode and send
|
DeferredBatch::BatchItem item{entity, message_type, estimated_size, aux_data_index};
|
||||||
if (creator(entity, this, MAX_BATCH_PACKET_SIZE, true) &&
|
if (this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE, true) &&
|
||||||
this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) {
|
this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) {
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
// Log the message in verbose mode
|
this->log_batch_item_(item);
|
||||||
this->log_proto_message_(entity, MessageCreator(creator), message_type);
|
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If immediate send failed, fall through to batching
|
|
||||||
}
|
}
|
||||||
|
return this->schedule_message_(entity, message_type, estimated_size, aux_data_index);
|
||||||
// Fall back to scheduled batching
|
|
||||||
return this->schedule_message_(entity, creator, message_type, estimated_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overload for MessageCreator (used by events which need to capture event_type)
|
|
||||||
bool send_message_smart_(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size) {
|
|
||||||
// Try to send immediately if message type should bypass batching and buffer has space
|
|
||||||
if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) {
|
|
||||||
// Now actually encode and send
|
|
||||||
if (creator(entity, this, MAX_BATCH_PACKET_SIZE, true, message_type) &&
|
|
||||||
this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) {
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
|
||||||
// Log the message in verbose mode
|
|
||||||
this->log_proto_message_(entity, creator, message_type);
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If immediate send failed, fall through to batching
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to scheduled batching
|
|
||||||
return this->schedule_message_(entity, creator, message_type, estimated_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to schedule a deferred message with known message type
|
// Helper function to schedule a deferred message with known message type
|
||||||
bool schedule_message_(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size) {
|
bool schedule_message_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
|
||||||
this->deferred_batch_.add_item(entity, creator, message_type, estimated_size);
|
uint8_t aux_data_index = DeferredBatch::AUX_DATA_UNUSED) {
|
||||||
|
this->deferred_batch_.add_item(entity, message_type, estimated_size, aux_data_index);
|
||||||
return this->schedule_batch_();
|
return this->schedule_batch_();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overload for function pointers (for info messages and current state reads)
|
|
||||||
bool schedule_message_(EntityBase *entity, MessageCreatorPtr function_ptr, uint8_t message_type,
|
|
||||||
uint8_t estimated_size) {
|
|
||||||
return schedule_message_(entity, MessageCreator(function_ptr), message_type, estimated_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to schedule a high priority message at the front of the batch
|
// Helper function to schedule a high priority message at the front of the batch
|
||||||
bool schedule_message_front_(EntityBase *entity, MessageCreatorPtr function_ptr, uint8_t message_type,
|
bool schedule_message_front_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size) {
|
||||||
uint8_t estimated_size) {
|
this->deferred_batch_.add_item_front(entity, message_type, estimated_size);
|
||||||
this->deferred_batch_.add_item_front(entity, MessageCreator(function_ptr), message_type, estimated_size);
|
|
||||||
return this->schedule_batch_();
|
return this->schedule_batch_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,26 +120,39 @@ class APIFrameHelper {
|
|||||||
}
|
}
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
/// Toggle TCP_NODELAY socket option to control Nagle's algorithm.
|
// Manage TCP_NODELAY (Nagle's algorithm) based on message type.
|
||||||
///
|
//
|
||||||
/// This is used to allow log messages to coalesce (Nagle enabled) while keeping
|
// For non-log messages (sensor data, state updates): Always disable Nagle
|
||||||
/// state updates low-latency (NODELAY enabled). Without this, many small log
|
// (NODELAY on) for immediate delivery - these are time-sensitive.
|
||||||
/// packets fill the TCP send buffer, crowding out important state updates.
|
//
|
||||||
///
|
// For log messages: Use Nagle to coalesce multiple small log packets into
|
||||||
/// State is tracked to minimize setsockopt() overhead - on lwip_raw (ESP8266/RP2040)
|
// fewer larger packets, reducing WiFi overhead. However, we limit batching
|
||||||
/// this is just a boolean assignment; on other platforms it's a lightweight syscall.
|
// to 3 messages to avoid excessive LWIP buffer pressure on memory-constrained
|
||||||
///
|
// devices like ESP8266. LWIP's TCP_OVERSIZE option coalesces the data into
|
||||||
/// @param enable true to enable NODELAY (disable Nagle), false to enable Nagle
|
// shared pbufs, but holding data too long waiting for Nagle's timer causes
|
||||||
/// @return true if successful or already in desired state
|
// buffer exhaustion and dropped messages.
|
||||||
bool set_nodelay(bool enable) {
|
//
|
||||||
if (this->nodelay_enabled_ == enable)
|
// Flow: Log 1 (Nagle on) -> Log 2 (Nagle on) -> Log 3 (NODELAY, flush all)
|
||||||
return true;
|
//
|
||||||
int val = enable ? 1 : 0;
|
void set_nodelay_for_message(bool is_log_message) {
|
||||||
int err = this->socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
if (!is_log_message) {
|
||||||
if (err == 0) {
|
if (this->nodelay_state_ != NODELAY_ON) {
|
||||||
this->nodelay_enabled_ = enable;
|
this->set_nodelay_raw_(true);
|
||||||
|
this->nodelay_state_ = NODELAY_ON;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log messages 1-3: state transitions -1 -> 1 -> 2 -> -1 (flush on 3rd)
|
||||||
|
if (this->nodelay_state_ == NODELAY_ON) {
|
||||||
|
this->set_nodelay_raw_(false);
|
||||||
|
this->nodelay_state_ = 1;
|
||||||
|
} else if (this->nodelay_state_ >= LOG_NAGLE_COUNT) {
|
||||||
|
this->set_nodelay_raw_(true);
|
||||||
|
this->nodelay_state_ = NODELAY_ON;
|
||||||
|
} else {
|
||||||
|
this->nodelay_state_++;
|
||||||
}
|
}
|
||||||
return err == 0;
|
|
||||||
}
|
}
|
||||||
virtual APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) = 0;
|
virtual APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) = 0;
|
||||||
// Write multiple protobuf messages in a single operation
|
// Write multiple protobuf messages in a single operation
|
||||||
@@ -229,10 +242,18 @@ class APIFrameHelper {
|
|||||||
uint8_t tx_buf_head_{0};
|
uint8_t tx_buf_head_{0};
|
||||||
uint8_t tx_buf_tail_{0};
|
uint8_t tx_buf_tail_{0};
|
||||||
uint8_t tx_buf_count_{0};
|
uint8_t tx_buf_count_{0};
|
||||||
// Tracks TCP_NODELAY state to minimize setsockopt() calls. Initialized to true
|
// Nagle batching state for log messages. NODELAY_ON (-1) means NODELAY is enabled
|
||||||
// since init_common_() enables NODELAY. Used by set_nodelay() to allow log
|
// (immediate send). Values 1-2 count log messages in the current Nagle batch.
|
||||||
// messages to coalesce while keeping state updates low-latency.
|
// After LOG_NAGLE_COUNT logs, we switch to NODELAY to flush and reset.
|
||||||
bool nodelay_enabled_{true};
|
static constexpr int8_t NODELAY_ON = -1;
|
||||||
|
static constexpr int8_t LOG_NAGLE_COUNT = 2;
|
||||||
|
int8_t nodelay_state_{NODELAY_ON};
|
||||||
|
|
||||||
|
// Internal helper to set TCP_NODELAY socket option
|
||||||
|
void set_nodelay_raw_(bool enable) {
|
||||||
|
int val = enable ? 1 : 0;
|
||||||
|
this->socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
// Common initialization for both plaintext and noise protocols
|
// Common initialization for both plaintext and noise protocols
|
||||||
APIError init_common_();
|
APIError init_common_();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
#include "api_connection.h" // For ClientInfo struct
|
#include "api_connection.h" // For ClientInfo struct
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/core/entity_base.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
@@ -256,28 +257,30 @@ APIError APINoiseFrameHelper::state_action_() {
|
|||||||
}
|
}
|
||||||
if (state_ == State::SERVER_HELLO) {
|
if (state_ == State::SERVER_HELLO) {
|
||||||
// send server hello
|
// send server hello
|
||||||
constexpr size_t mac_len = 13; // 12 hex chars + null terminator
|
|
||||||
const std::string &name = App.get_name();
|
const std::string &name = App.get_name();
|
||||||
char mac[mac_len];
|
char mac[MAC_ADDRESS_BUFFER_SIZE];
|
||||||
get_mac_address_into_buffer(mac);
|
get_mac_address_into_buffer(mac);
|
||||||
|
|
||||||
// Calculate positions and sizes
|
// Calculate positions and sizes
|
||||||
size_t name_len = name.size() + 1; // including null terminator
|
size_t name_len = name.size() + 1; // including null terminator
|
||||||
size_t name_offset = 1;
|
size_t name_offset = 1;
|
||||||
size_t mac_offset = name_offset + name_len;
|
size_t mac_offset = name_offset + name_len;
|
||||||
size_t total_size = 1 + name_len + mac_len;
|
size_t total_size = 1 + name_len + MAC_ADDRESS_BUFFER_SIZE;
|
||||||
|
|
||||||
auto msg = std::make_unique<uint8_t[]>(total_size);
|
// 1 (proto) + name (max ESPHOME_DEVICE_NAME_MAX_LEN) + 1 (name null)
|
||||||
|
// + mac (MAC_ADDRESS_BUFFER_SIZE - 1) + 1 (mac null)
|
||||||
|
constexpr size_t max_msg_size = 1 + ESPHOME_DEVICE_NAME_MAX_LEN + 1 + MAC_ADDRESS_BUFFER_SIZE;
|
||||||
|
uint8_t msg[max_msg_size];
|
||||||
|
|
||||||
// chosen proto
|
// chosen proto
|
||||||
msg[0] = 0x01;
|
msg[0] = 0x01;
|
||||||
|
|
||||||
// node name, terminated by null byte
|
// node name, terminated by null byte
|
||||||
std::memcpy(msg.get() + name_offset, name.c_str(), name_len);
|
std::memcpy(msg + name_offset, name.c_str(), name_len);
|
||||||
// node mac, terminated by null byte
|
// node mac, terminated by null byte
|
||||||
std::memcpy(msg.get() + mac_offset, mac, mac_len);
|
std::memcpy(msg + mac_offset, mac, MAC_ADDRESS_BUFFER_SIZE);
|
||||||
|
|
||||||
aerr = write_frame_(msg.get(), total_size);
|
aerr = write_frame_(msg, total_size);
|
||||||
if (aerr != APIError::OK)
|
if (aerr != APIError::OK)
|
||||||
return aerr;
|
return aerr;
|
||||||
|
|
||||||
@@ -353,35 +356,32 @@ APIError APINoiseFrameHelper::state_action_() {
|
|||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
void APINoiseFrameHelper::send_explicit_handshake_reject_(const LogString *reason) {
|
void APINoiseFrameHelper::send_explicit_handshake_reject_(const LogString *reason) {
|
||||||
|
// Max reject message: "Bad handshake packet len" (24) + 1 (failure byte) = 25 bytes
|
||||||
|
uint8_t data[32];
|
||||||
|
data[0] = 0x01; // failure
|
||||||
|
|
||||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||||
// On ESP8266 with flash strings, we need to use PROGMEM-aware functions
|
// On ESP8266 with flash strings, we need to use PROGMEM-aware functions
|
||||||
size_t reason_len = strlen_P(reinterpret_cast<PGM_P>(reason));
|
size_t reason_len = strlen_P(reinterpret_cast<PGM_P>(reason));
|
||||||
size_t data_size = reason_len + 1;
|
|
||||||
auto data = std::make_unique<uint8_t[]>(data_size);
|
|
||||||
data[0] = 0x01; // failure
|
|
||||||
|
|
||||||
// Copy error message from PROGMEM
|
|
||||||
if (reason_len > 0) {
|
if (reason_len > 0) {
|
||||||
memcpy_P(data.get() + 1, reinterpret_cast<PGM_P>(reason), reason_len);
|
memcpy_P(data + 1, reinterpret_cast<PGM_P>(reason), reason_len);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// Normal memory access
|
// Normal memory access
|
||||||
const char *reason_str = LOG_STR_ARG(reason);
|
const char *reason_str = LOG_STR_ARG(reason);
|
||||||
size_t reason_len = strlen(reason_str);
|
size_t reason_len = strlen(reason_str);
|
||||||
size_t data_size = reason_len + 1;
|
|
||||||
auto data = std::make_unique<uint8_t[]>(data_size);
|
|
||||||
data[0] = 0x01; // failure
|
|
||||||
|
|
||||||
// Copy error message in bulk
|
|
||||||
if (reason_len > 0) {
|
if (reason_len > 0) {
|
||||||
std::memcpy(data.get() + 1, reason_str, reason_len);
|
// NOLINTNEXTLINE(bugprone-not-null-terminated-result) - binary protocol, not a C string
|
||||||
|
std::memcpy(data + 1, reason_str, reason_len);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
size_t data_size = reason_len + 1;
|
||||||
|
|
||||||
// temporarily remove failed state
|
// temporarily remove failed state
|
||||||
auto orig_state = state_;
|
auto orig_state = state_;
|
||||||
state_ = State::EXPLICIT_REJECT;
|
state_ = State::EXPLICIT_REJECT;
|
||||||
write_frame_(data.get(), data_size);
|
write_frame_(data, data_size);
|
||||||
state_ = orig_state;
|
state_ = orig_state;
|
||||||
}
|
}
|
||||||
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,12 @@ namespace esphome::api {
|
|||||||
static const char *const TAG = "api.service";
|
static const char *const TAG = "api.service";
|
||||||
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void APIServerConnectionBase::log_send_message_(const char *name, const std::string &dump) {
|
void APIServerConnectionBase::log_send_message_(const char *name, const char *dump) {
|
||||||
ESP_LOGVV(TAG, "send_message %s: %s", name, dump.c_str());
|
ESP_LOGVV(TAG, "send_message %s: %s", name, dump);
|
||||||
|
}
|
||||||
|
void APIServerConnectionBase::log_receive_message_(const LogString *name, const ProtoMessage &msg) {
|
||||||
|
DumpBuffer dump_buf;
|
||||||
|
ESP_LOGVV(TAG, "%s: %s", LOG_STR_ARG(name), msg.dump_to(dump_buf));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -19,7 +23,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
HelloRequest msg;
|
HelloRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_hello_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_hello_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_hello_request(msg);
|
this->on_hello_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -28,7 +32,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
DisconnectRequest msg;
|
DisconnectRequest msg;
|
||||||
// Empty message: no decode needed
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_disconnect_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_disconnect_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_disconnect_request(msg);
|
this->on_disconnect_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -37,7 +41,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
DisconnectResponse msg;
|
DisconnectResponse msg;
|
||||||
// Empty message: no decode needed
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_disconnect_response: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_disconnect_response"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_disconnect_response(msg);
|
this->on_disconnect_response(msg);
|
||||||
break;
|
break;
|
||||||
@@ -46,7 +50,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
PingRequest msg;
|
PingRequest msg;
|
||||||
// Empty message: no decode needed
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_ping_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_ping_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_ping_request(msg);
|
this->on_ping_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -55,7 +59,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
PingResponse msg;
|
PingResponse msg;
|
||||||
// Empty message: no decode needed
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_ping_response: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_ping_response"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_ping_response(msg);
|
this->on_ping_response(msg);
|
||||||
break;
|
break;
|
||||||
@@ -64,7 +68,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
DeviceInfoRequest msg;
|
DeviceInfoRequest msg;
|
||||||
// Empty message: no decode needed
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_device_info_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_device_info_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_device_info_request(msg);
|
this->on_device_info_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -73,7 +77,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
ListEntitiesRequest msg;
|
ListEntitiesRequest msg;
|
||||||
// Empty message: no decode needed
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_list_entities_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_list_entities_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_list_entities_request(msg);
|
this->on_list_entities_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -82,7 +86,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
SubscribeStatesRequest msg;
|
SubscribeStatesRequest msg;
|
||||||
// Empty message: no decode needed
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_subscribe_states_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_subscribe_states_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_states_request(msg);
|
this->on_subscribe_states_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -91,7 +95,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
SubscribeLogsRequest msg;
|
SubscribeLogsRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_subscribe_logs_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_subscribe_logs_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_logs_request(msg);
|
this->on_subscribe_logs_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -101,7 +105,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
CoverCommandRequest msg;
|
CoverCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_cover_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_cover_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_cover_command_request(msg);
|
this->on_cover_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -112,7 +116,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
FanCommandRequest msg;
|
FanCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_fan_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_fan_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_fan_command_request(msg);
|
this->on_fan_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -123,7 +127,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
LightCommandRequest msg;
|
LightCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_light_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_light_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_light_command_request(msg);
|
this->on_light_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -134,7 +138,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
SwitchCommandRequest msg;
|
SwitchCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_switch_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_switch_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_switch_command_request(msg);
|
this->on_switch_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -145,7 +149,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
SubscribeHomeassistantServicesRequest msg;
|
SubscribeHomeassistantServicesRequest msg;
|
||||||
// Empty message: no decode needed
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_subscribe_homeassistant_services_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_subscribe_homeassistant_services_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_homeassistant_services_request(msg);
|
this->on_subscribe_homeassistant_services_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -155,7 +159,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
GetTimeResponse msg;
|
GetTimeResponse msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_get_time_response: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_get_time_response"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_get_time_response(msg);
|
this->on_get_time_response(msg);
|
||||||
break;
|
break;
|
||||||
@@ -165,7 +169,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
SubscribeHomeAssistantStatesRequest msg;
|
SubscribeHomeAssistantStatesRequest msg;
|
||||||
// Empty message: no decode needed
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_subscribe_home_assistant_states_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_subscribe_home_assistant_states_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_home_assistant_states_request(msg);
|
this->on_subscribe_home_assistant_states_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -176,7 +180,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
HomeAssistantStateResponse msg;
|
HomeAssistantStateResponse msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_home_assistant_state_response: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_home_assistant_state_response"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_home_assistant_state_response(msg);
|
this->on_home_assistant_state_response(msg);
|
||||||
break;
|
break;
|
||||||
@@ -187,7 +191,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
ExecuteServiceRequest msg;
|
ExecuteServiceRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_execute_service_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_execute_service_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_execute_service_request(msg);
|
this->on_execute_service_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -198,7 +202,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
CameraImageRequest msg;
|
CameraImageRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_camera_image_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_camera_image_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_camera_image_request(msg);
|
this->on_camera_image_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -209,7 +213,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
ClimateCommandRequest msg;
|
ClimateCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_climate_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_climate_command_request(msg);
|
this->on_climate_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -220,7 +224,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
NumberCommandRequest msg;
|
NumberCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_number_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_number_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_number_command_request(msg);
|
this->on_number_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -231,7 +235,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
SelectCommandRequest msg;
|
SelectCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_select_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_select_command_request(msg);
|
this->on_select_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -242,7 +246,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
SirenCommandRequest msg;
|
SirenCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_siren_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_siren_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_siren_command_request(msg);
|
this->on_siren_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -253,7 +257,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
LockCommandRequest msg;
|
LockCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_lock_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_lock_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_lock_command_request(msg);
|
this->on_lock_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -264,7 +268,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
ButtonCommandRequest msg;
|
ButtonCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_button_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_button_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_button_command_request(msg);
|
this->on_button_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -275,7 +279,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
MediaPlayerCommandRequest msg;
|
MediaPlayerCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_media_player_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_media_player_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_media_player_command_request(msg);
|
this->on_media_player_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -286,7 +290,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
SubscribeBluetoothLEAdvertisementsRequest msg;
|
SubscribeBluetoothLEAdvertisementsRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_subscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_subscribe_bluetooth_le_advertisements_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_bluetooth_le_advertisements_request(msg);
|
this->on_subscribe_bluetooth_le_advertisements_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -297,7 +301,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
BluetoothDeviceRequest msg;
|
BluetoothDeviceRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_bluetooth_device_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_bluetooth_device_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_bluetooth_device_request(msg);
|
this->on_bluetooth_device_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -308,7 +312,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
BluetoothGATTGetServicesRequest msg;
|
BluetoothGATTGetServicesRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_get_services_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_bluetooth_gatt_get_services_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_bluetooth_gatt_get_services_request(msg);
|
this->on_bluetooth_gatt_get_services_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -319,7 +323,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
BluetoothGATTReadRequest msg;
|
BluetoothGATTReadRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_read_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_bluetooth_gatt_read_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_bluetooth_gatt_read_request(msg);
|
this->on_bluetooth_gatt_read_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -330,7 +334,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
BluetoothGATTWriteRequest msg;
|
BluetoothGATTWriteRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_write_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_bluetooth_gatt_write_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_bluetooth_gatt_write_request(msg);
|
this->on_bluetooth_gatt_write_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -341,7 +345,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
BluetoothGATTReadDescriptorRequest msg;
|
BluetoothGATTReadDescriptorRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_read_descriptor_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_bluetooth_gatt_read_descriptor_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_bluetooth_gatt_read_descriptor_request(msg);
|
this->on_bluetooth_gatt_read_descriptor_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -352,7 +356,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
BluetoothGATTWriteDescriptorRequest msg;
|
BluetoothGATTWriteDescriptorRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_write_descriptor_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_bluetooth_gatt_write_descriptor_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_bluetooth_gatt_write_descriptor_request(msg);
|
this->on_bluetooth_gatt_write_descriptor_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -363,7 +367,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
BluetoothGATTNotifyRequest msg;
|
BluetoothGATTNotifyRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_notify_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_bluetooth_gatt_notify_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_bluetooth_gatt_notify_request(msg);
|
this->on_bluetooth_gatt_notify_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -374,7 +378,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
SubscribeBluetoothConnectionsFreeRequest msg;
|
SubscribeBluetoothConnectionsFreeRequest msg;
|
||||||
// Empty message: no decode needed
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_subscribe_bluetooth_connections_free_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_subscribe_bluetooth_connections_free_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_bluetooth_connections_free_request(msg);
|
this->on_subscribe_bluetooth_connections_free_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -385,7 +389,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
UnsubscribeBluetoothLEAdvertisementsRequest msg;
|
UnsubscribeBluetoothLEAdvertisementsRequest msg;
|
||||||
// Empty message: no decode needed
|
// Empty message: no decode needed
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_unsubscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_unsubscribe_bluetooth_le_advertisements_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_unsubscribe_bluetooth_le_advertisements_request(msg);
|
this->on_unsubscribe_bluetooth_le_advertisements_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -396,7 +400,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
SubscribeVoiceAssistantRequest msg;
|
SubscribeVoiceAssistantRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_subscribe_voice_assistant_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_subscribe_voice_assistant_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_subscribe_voice_assistant_request(msg);
|
this->on_subscribe_voice_assistant_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -407,7 +411,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
VoiceAssistantResponse msg;
|
VoiceAssistantResponse msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_voice_assistant_response: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_voice_assistant_response"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_voice_assistant_response(msg);
|
this->on_voice_assistant_response(msg);
|
||||||
break;
|
break;
|
||||||
@@ -418,7 +422,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
VoiceAssistantEventResponse msg;
|
VoiceAssistantEventResponse msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_voice_assistant_event_response: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_voice_assistant_event_response"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_voice_assistant_event_response(msg);
|
this->on_voice_assistant_event_response(msg);
|
||||||
break;
|
break;
|
||||||
@@ -429,7 +433,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
AlarmControlPanelCommandRequest msg;
|
AlarmControlPanelCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_alarm_control_panel_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_alarm_control_panel_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_alarm_control_panel_command_request(msg);
|
this->on_alarm_control_panel_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -440,7 +444,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
TextCommandRequest msg;
|
TextCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_text_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_text_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_text_command_request(msg);
|
this->on_text_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -451,7 +455,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
DateCommandRequest msg;
|
DateCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_date_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_date_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_date_command_request(msg);
|
this->on_date_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -462,7 +466,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
TimeCommandRequest msg;
|
TimeCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_time_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_time_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_time_command_request(msg);
|
this->on_time_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -473,7 +477,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
VoiceAssistantAudio msg;
|
VoiceAssistantAudio msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_voice_assistant_audio: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_voice_assistant_audio"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_voice_assistant_audio(msg);
|
this->on_voice_assistant_audio(msg);
|
||||||
break;
|
break;
|
||||||
@@ -484,7 +488,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
ValveCommandRequest msg;
|
ValveCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_valve_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_valve_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_valve_command_request(msg);
|
this->on_valve_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -495,7 +499,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
DateTimeCommandRequest msg;
|
DateTimeCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_date_time_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_date_time_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_date_time_command_request(msg);
|
this->on_date_time_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -506,7 +510,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
VoiceAssistantTimerEventResponse msg;
|
VoiceAssistantTimerEventResponse msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_voice_assistant_timer_event_response: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_voice_assistant_timer_event_response"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_voice_assistant_timer_event_response(msg);
|
this->on_voice_assistant_timer_event_response(msg);
|
||||||
break;
|
break;
|
||||||
@@ -517,7 +521,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
UpdateCommandRequest msg;
|
UpdateCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_update_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_update_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_update_command_request(msg);
|
this->on_update_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -528,7 +532,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
VoiceAssistantAnnounceRequest msg;
|
VoiceAssistantAnnounceRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_voice_assistant_announce_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_voice_assistant_announce_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_voice_assistant_announce_request(msg);
|
this->on_voice_assistant_announce_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -539,7 +543,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
VoiceAssistantConfigurationRequest msg;
|
VoiceAssistantConfigurationRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_voice_assistant_configuration_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_voice_assistant_configuration_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_voice_assistant_configuration_request(msg);
|
this->on_voice_assistant_configuration_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -550,7 +554,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
VoiceAssistantSetConfiguration msg;
|
VoiceAssistantSetConfiguration msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_voice_assistant_set_configuration: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_voice_assistant_set_configuration"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_voice_assistant_set_configuration(msg);
|
this->on_voice_assistant_set_configuration(msg);
|
||||||
break;
|
break;
|
||||||
@@ -561,7 +565,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
NoiseEncryptionSetKeyRequest msg;
|
NoiseEncryptionSetKeyRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_noise_encryption_set_key_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_noise_encryption_set_key_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_noise_encryption_set_key_request(msg);
|
this->on_noise_encryption_set_key_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -572,7 +576,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
BluetoothScannerSetModeRequest msg;
|
BluetoothScannerSetModeRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_bluetooth_scanner_set_mode_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_bluetooth_scanner_set_mode_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_bluetooth_scanner_set_mode_request(msg);
|
this->on_bluetooth_scanner_set_mode_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -583,7 +587,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
ZWaveProxyFrame msg;
|
ZWaveProxyFrame msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_z_wave_proxy_frame: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_z_wave_proxy_frame"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_z_wave_proxy_frame(msg);
|
this->on_z_wave_proxy_frame(msg);
|
||||||
break;
|
break;
|
||||||
@@ -594,7 +598,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
ZWaveProxyRequest msg;
|
ZWaveProxyRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_z_wave_proxy_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_z_wave_proxy_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_z_wave_proxy_request(msg);
|
this->on_z_wave_proxy_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -605,7 +609,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
HomeassistantActionResponse msg;
|
HomeassistantActionResponse msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_homeassistant_action_response: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_homeassistant_action_response"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_homeassistant_action_response(msg);
|
this->on_homeassistant_action_response(msg);
|
||||||
break;
|
break;
|
||||||
@@ -616,7 +620,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
WaterHeaterCommandRequest msg;
|
WaterHeaterCommandRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_water_heater_command_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_water_heater_command_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_water_heater_command_request(msg);
|
this->on_water_heater_command_request(msg);
|
||||||
break;
|
break;
|
||||||
@@ -627,7 +631,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
InfraredRFTransmitRawTimingsRequest msg;
|
InfraredRFTransmitRawTimingsRequest msg;
|
||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "on_infrared_rf_transmit_raw_timings_request: %s", msg.dump().c_str());
|
this->log_receive_message_(LOG_STR("on_infrared_rf_transmit_raw_timings_request"), msg);
|
||||||
#endif
|
#endif
|
||||||
this->on_infrared_rf_transmit_raw_timings_request(msg);
|
this->on_infrared_rf_transmit_raw_timings_request(msg);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -12,14 +12,16 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
public:
|
public:
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
protected:
|
protected:
|
||||||
void log_send_message_(const char *name, const std::string &dump);
|
void log_send_message_(const char *name, const char *dump);
|
||||||
|
void log_receive_message_(const LogString *name, const ProtoMessage &msg);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool send_message(const ProtoMessage &msg, uint8_t message_type) {
|
bool send_message(const ProtoMessage &msg, uint8_t message_type) {
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_send_message_(msg.message_name(), msg.dump());
|
DumpBuffer dump_buf;
|
||||||
|
this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));
|
||||||
#endif
|
#endif
|
||||||
return this->send_message_(msg, message_type);
|
return this->send_message_(msg, message_type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,8 +241,10 @@ void APIServer::handle_disconnect(APIConnection *conn) {}
|
|||||||
void APIServer::on_##entity_name##_update(entity_type *obj) { /* NOLINT(bugprone-macro-parentheses) */ \
|
void APIServer::on_##entity_name##_update(entity_type *obj) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||||
if (obj->is_internal()) \
|
if (obj->is_internal()) \
|
||||||
return; \
|
return; \
|
||||||
for (auto &c : this->clients_) \
|
for (auto &c : this->clients_) { \
|
||||||
|
if (c->flags_.state_subscription) \
|
||||||
c->send_##entity_name##_state(obj); \
|
c->send_##entity_name##_state(obj); \
|
||||||
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
@@ -318,13 +320,13 @@ API_DISPATCH_UPDATE(water_heater::WaterHeater, water_heater)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
// Event is a special case - unlike other entities with simple state fields,
|
|
||||||
// events store their state in a member accessed via obj->get_last_event_type()
|
|
||||||
void APIServer::on_event(event::Event *obj) {
|
void APIServer::on_event(event::Event *obj) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto &c : this->clients_)
|
for (auto &c : this->clients_) {
|
||||||
c->send_event(obj, obj->get_last_event_type());
|
if (c->flags_.state_subscription)
|
||||||
|
c->send_event(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -333,9 +335,11 @@ void APIServer::on_event(event::Event *obj) {
|
|||||||
void APIServer::on_update(update::UpdateEntity *obj) {
|
void APIServer::on_update(update::UpdateEntity *obj) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto &c : this->clients_)
|
for (auto &c : this->clients_) {
|
||||||
|
if (c->flags_.state_subscription)
|
||||||
c->send_update_state(obj);
|
c->send_update_state(obj);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ZWAVE_PROXY
|
#ifdef USE_ZWAVE_PROXY
|
||||||
@@ -554,8 +558,10 @@ bool APIServer::clear_noise_psk(bool make_active) {
|
|||||||
#ifdef USE_HOMEASSISTANT_TIME
|
#ifdef USE_HOMEASSISTANT_TIME
|
||||||
void APIServer::request_time() {
|
void APIServer::request_time() {
|
||||||
for (auto &client : this->clients_) {
|
for (auto &client : this->clients_) {
|
||||||
if (!client->flags_.remove && client->is_authenticated())
|
if (!client->flags_.remove && client->is_authenticated()) {
|
||||||
client->send_time_request();
|
client->send_time_request();
|
||||||
|
return; // Only request from one client to avoid clock conflicts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -615,8 +621,7 @@ void APIServer::on_shutdown() {
|
|||||||
if (!c->send_message(req, DisconnectRequest::MESSAGE_TYPE)) {
|
if (!c->send_message(req, DisconnectRequest::MESSAGE_TYPE)) {
|
||||||
// If we can't send the disconnect request directly (tx_buffer full),
|
// If we can't send the disconnect request directly (tx_buffer full),
|
||||||
// schedule it at the front of the batch so it will be sent with priority
|
// schedule it at the front of the batch so it will be sent with priority
|
||||||
c->schedule_message_front_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE,
|
c->schedule_message_front_(nullptr, DisconnectRequest::MESSAGE_TYPE, DisconnectRequest::ESTIMATED_SIZE);
|
||||||
DisconnectRequest::ESTIMATED_SIZE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -639,14 +644,6 @@ bool APIServer::teardown() {
|
|||||||
#define USE_API_ACTION_CALL_TIMEOUT_MS 30000 // NOLINT
|
#define USE_API_ACTION_CALL_TIMEOUT_MS 30000 // NOLINT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// SSO-friendly action call key - hex format guarantees max 11 chars ("ac_ffffffff")
|
|
||||||
// which fits in any std::string SSO buffer (typically 12-15 bytes)
|
|
||||||
static inline std::string make_action_call_key(uint32_t id) {
|
|
||||||
char buf[12];
|
|
||||||
size_t len = snprintf(buf, sizeof(buf), "ac_%x", id);
|
|
||||||
return std::string(buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t APIServer::register_active_action_call(uint32_t client_call_id, APIConnection *conn) {
|
uint32_t APIServer::register_active_action_call(uint32_t client_call_id, APIConnection *conn) {
|
||||||
uint32_t action_call_id = this->next_action_call_id_++;
|
uint32_t action_call_id = this->next_action_call_id_++;
|
||||||
// Handle wraparound (skip 0 as it means "no call")
|
// Handle wraparound (skip 0 as it means "no call")
|
||||||
@@ -656,7 +653,8 @@ uint32_t APIServer::register_active_action_call(uint32_t client_call_id, APIConn
|
|||||||
this->active_action_calls_.push_back({action_call_id, client_call_id, conn});
|
this->active_action_calls_.push_back({action_call_id, client_call_id, conn});
|
||||||
|
|
||||||
// Schedule automatic cleanup after timeout (client will have given up by then)
|
// Schedule automatic cleanup after timeout (client will have given up by then)
|
||||||
this->set_timeout(make_action_call_key(action_call_id), USE_API_ACTION_CALL_TIMEOUT_MS, [this, action_call_id]() {
|
// Uses numeric ID overload to avoid heap allocation from str_sprintf
|
||||||
|
this->set_timeout(action_call_id, USE_API_ACTION_CALL_TIMEOUT_MS, [this, action_call_id]() {
|
||||||
ESP_LOGD(TAG, "Action call %u timed out", action_call_id);
|
ESP_LOGD(TAG, "Action call %u timed out", action_call_id);
|
||||||
this->unregister_active_action_call(action_call_id);
|
this->unregister_active_action_call(action_call_id);
|
||||||
});
|
});
|
||||||
@@ -665,8 +663,8 @@ uint32_t APIServer::register_active_action_call(uint32_t client_call_id, APIConn
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APIServer::unregister_active_action_call(uint32_t action_call_id) {
|
void APIServer::unregister_active_action_call(uint32_t action_call_id) {
|
||||||
// Cancel the timeout for this action call
|
// Cancel the timeout for this action call (uses numeric ID overload)
|
||||||
this->cancel_timeout(make_action_call_key(action_call_id));
|
this->cancel_timeout(action_call_id);
|
||||||
|
|
||||||
// Swap-and-pop is more efficient than remove_if for unordered vectors
|
// Swap-and-pop is more efficient than remove_if for unordered vectors
|
||||||
for (size_t i = 0; i < this->active_action_calls_.size(); i++) {
|
for (size_t i = 0; i < this->active_action_calls_.size(); i++) {
|
||||||
@@ -682,8 +680,8 @@ void APIServer::unregister_active_action_calls_for_connection(APIConnection *con
|
|||||||
// Remove all active action calls for disconnected connection using swap-and-pop
|
// Remove all active action calls for disconnected connection using swap-and-pop
|
||||||
for (size_t i = 0; i < this->active_action_calls_.size();) {
|
for (size_t i = 0; i < this->active_action_calls_.size();) {
|
||||||
if (this->active_action_calls_[i].connection == conn) {
|
if (this->active_action_calls_[i].connection == conn) {
|
||||||
// Cancel the timeout for this action call
|
// Cancel the timeout for this action call (uses numeric ID overload)
|
||||||
this->cancel_timeout(make_action_call_key(this->active_action_calls_[i].action_call_id));
|
this->cancel_timeout(this->active_action_calls_[i].action_call_id);
|
||||||
|
|
||||||
std::swap(this->active_action_calls_[i], this->active_action_calls_.back());
|
std::swap(this->active_action_calls_[i], this->active_action_calls_.back());
|
||||||
this->active_action_calls_.pop_back();
|
this->active_action_calls_.pop_back();
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ namespace esphome::api {
|
|||||||
class APIConnection;
|
class APIConnection;
|
||||||
|
|
||||||
// Macro for generating ListEntitiesIterator handlers
|
// Macro for generating ListEntitiesIterator handlers
|
||||||
// Calls schedule_message_ with try_send_*_info
|
// Calls schedule_message_ which dispatches to try_send_*_info
|
||||||
#define LIST_ENTITIES_HANDLER(entity_type, EntityClass, ResponseType) \
|
#define LIST_ENTITIES_HANDLER(entity_type, EntityClass, ResponseType) \
|
||||||
bool ListEntitiesIterator::on_##entity_type(EntityClass *entity) { /* NOLINT(bugprone-macro-parentheses) */ \
|
bool ListEntitiesIterator::on_##entity_type(EntityClass *entity) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||||
return this->client_->schedule_message_(entity, &APIConnection::try_send_##entity_type##_info, \
|
return this->client_->schedule_message_(entity, ResponseType::MESSAGE_TYPE, ResponseType::ESTIMATED_SIZE); \
|
||||||
ResponseType::MESSAGE_TYPE, ResponseType::ESTIMATED_SIZE); \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListEntitiesIterator : public ComponentIterator {
|
class ListEntitiesIterator : public ComponentIterator {
|
||||||
|
|||||||
@@ -48,14 +48,14 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
|
|||||||
}
|
}
|
||||||
uint32_t field_length = res->as_uint32();
|
uint32_t field_length = res->as_uint32();
|
||||||
ptr += consumed;
|
ptr += consumed;
|
||||||
if (ptr + field_length > end) {
|
if (field_length > static_cast<size_t>(end - ptr)) {
|
||||||
return count; // Out of bounds
|
return count; // Out of bounds
|
||||||
}
|
}
|
||||||
ptr += field_length;
|
ptr += field_length;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WIRE_TYPE_FIXED32: { // 32-bit - skip 4 bytes
|
case WIRE_TYPE_FIXED32: { // 32-bit - skip 4 bytes
|
||||||
if (ptr + 4 > end) {
|
if (end - ptr < 4) {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
ptr += 4;
|
ptr += 4;
|
||||||
@@ -110,7 +110,7 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
|||||||
}
|
}
|
||||||
uint32_t field_length = res->as_uint32();
|
uint32_t field_length = res->as_uint32();
|
||||||
ptr += consumed;
|
ptr += consumed;
|
||||||
if (ptr + field_length > end) {
|
if (field_length > static_cast<size_t>(end - ptr)) {
|
||||||
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at offset %ld", (long) (ptr - buffer));
|
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at offset %ld", (long) (ptr - buffer));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,7 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WIRE_TYPE_FIXED32: { // 32-bit
|
case WIRE_TYPE_FIXED32: { // 32-bit
|
||||||
if (ptr + 4 > end) {
|
if (end - ptr < 4) {
|
||||||
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at offset %ld", (long) (ptr - buffer));
|
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at offset %ld", (long) (ptr - buffer));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -139,12 +139,4 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
|
||||||
std::string ProtoMessage::dump() const {
|
|
||||||
std::string out;
|
|
||||||
this->dump_to(out);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace esphome::api
|
} // namespace esphome::api
|
||||||
|
|||||||
@@ -362,6 +362,63 @@ class ProtoWriteBuffer {
|
|||||||
std::vector<uint8_t> *buffer_;
|
std::vector<uint8_t> *buffer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
/**
|
||||||
|
* Fixed-size buffer for message dumps - avoids heap allocation.
|
||||||
|
* Sized to match the logger's default tx_buffer_size (512 bytes)
|
||||||
|
* since anything larger gets truncated anyway.
|
||||||
|
*/
|
||||||
|
class DumpBuffer {
|
||||||
|
public:
|
||||||
|
// Matches default tx_buffer_size in logger component
|
||||||
|
static constexpr size_t CAPACITY = 512;
|
||||||
|
|
||||||
|
DumpBuffer() : pos_(0) { buf_[0] = '\0'; }
|
||||||
|
|
||||||
|
DumpBuffer &append(const char *str) {
|
||||||
|
if (str) {
|
||||||
|
append_impl_(str, strlen(str));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DumpBuffer &append(const char *str, size_t len) {
|
||||||
|
append_impl_(str, len);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DumpBuffer &append(size_t n, char c) {
|
||||||
|
size_t space = CAPACITY - 1 - pos_;
|
||||||
|
if (n > space)
|
||||||
|
n = space;
|
||||||
|
if (n > 0) {
|
||||||
|
memset(buf_ + pos_, c, n);
|
||||||
|
pos_ += n;
|
||||||
|
buf_[pos_] = '\0';
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *c_str() const { return buf_; }
|
||||||
|
size_t size() const { return pos_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void append_impl_(const char *str, size_t len) {
|
||||||
|
size_t space = CAPACITY - 1 - pos_;
|
||||||
|
if (len > space)
|
||||||
|
len = space;
|
||||||
|
if (len > 0) {
|
||||||
|
memcpy(buf_ + pos_, str, len);
|
||||||
|
pos_ += len;
|
||||||
|
buf_[pos_] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf_[CAPACITY];
|
||||||
|
size_t pos_;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
class ProtoMessage {
|
class ProtoMessage {
|
||||||
public:
|
public:
|
||||||
virtual ~ProtoMessage() = default;
|
virtual ~ProtoMessage() = default;
|
||||||
@@ -370,8 +427,7 @@ class ProtoMessage {
|
|||||||
// Default implementation for messages with no fields
|
// Default implementation for messages with no fields
|
||||||
virtual void calculate_size(ProtoSize &size) const {}
|
virtual void calculate_size(ProtoSize &size) const {}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
std::string dump() const;
|
virtual const char *dump_to(DumpBuffer &out) const = 0;
|
||||||
virtual void dump_to(std::string &out) const = 0;
|
|
||||||
virtual const char *message_name() const { return "unknown"; }
|
virtual const char *message_name() const { return "unknown"; }
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,14 +13,11 @@ from . import AQI_CALCULATION_TYPE, CONF_CALCULATION_TYPE, aqi_ns
|
|||||||
CODEOWNERS = ["@jasstrong"]
|
CODEOWNERS = ["@jasstrong"]
|
||||||
DEPENDENCIES = ["sensor"]
|
DEPENDENCIES = ["sensor"]
|
||||||
|
|
||||||
UNIT_INDEX = "index"
|
|
||||||
|
|
||||||
AQISensor = aqi_ns.class_("AQISensor", sensor.Sensor, cg.Component)
|
AQISensor = aqi_ns.class_("AQISensor", sensor.Sensor, cg.Component)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
sensor.sensor_schema(
|
sensor.sensor_schema(
|
||||||
AQISensor,
|
AQISensor,
|
||||||
unit_of_measurement=UNIT_INDEX,
|
|
||||||
accuracy_decimals=0,
|
accuracy_decimals=0,
|
||||||
device_class=DEVICE_CLASS_AQI,
|
device_class=DEVICE_CLASS_AQI,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
|||||||
@@ -108,10 +108,14 @@ void ATM90E32Component::update() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ATM90E32Component::get_cs_summary_(std::span<char, GPIO_SUMMARY_MAX_LEN> buffer) {
|
||||||
|
this->cs_->dump_summary(buffer.data(), buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
void ATM90E32Component::setup() {
|
void ATM90E32Component::setup() {
|
||||||
this->spi_setup();
|
this->spi_setup();
|
||||||
this->cs_summary_ = this->cs_->dump_summary();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
const char *cs = this->cs_summary_.c_str();
|
this->get_cs_summary_(cs);
|
||||||
|
|
||||||
uint16_t mmode0 = 0x87; // 3P4W 50Hz
|
uint16_t mmode0 = 0x87; // 3P4W 50Hz
|
||||||
uint16_t high_thresh = 0;
|
uint16_t high_thresh = 0;
|
||||||
@@ -158,12 +162,14 @@ void ATM90E32Component::setup() {
|
|||||||
|
|
||||||
if (this->enable_offset_calibration_) {
|
if (this->enable_offset_calibration_) {
|
||||||
// Initialize flash storage for offset calibrations
|
// Initialize flash storage for offset calibrations
|
||||||
uint32_t o_hash = fnv1_hash(std::string("_offset_calibration_") + this->cs_summary_);
|
uint32_t o_hash = fnv1_hash("_offset_calibration_");
|
||||||
|
o_hash = fnv1_hash_extend(o_hash, cs);
|
||||||
this->offset_pref_ = global_preferences->make_preference<OffsetCalibration[3]>(o_hash, true);
|
this->offset_pref_ = global_preferences->make_preference<OffsetCalibration[3]>(o_hash, true);
|
||||||
this->restore_offset_calibrations_();
|
this->restore_offset_calibrations_();
|
||||||
|
|
||||||
// Initialize flash storage for power offset calibrations
|
// Initialize flash storage for power offset calibrations
|
||||||
uint32_t po_hash = fnv1_hash(std::string("_power_offset_calibration_") + this->cs_summary_);
|
uint32_t po_hash = fnv1_hash("_power_offset_calibration_");
|
||||||
|
po_hash = fnv1_hash_extend(po_hash, cs);
|
||||||
this->power_offset_pref_ = global_preferences->make_preference<PowerOffsetCalibration[3]>(po_hash, true);
|
this->power_offset_pref_ = global_preferences->make_preference<PowerOffsetCalibration[3]>(po_hash, true);
|
||||||
this->restore_power_offset_calibrations_();
|
this->restore_power_offset_calibrations_();
|
||||||
} else {
|
} else {
|
||||||
@@ -183,7 +189,8 @@ void ATM90E32Component::setup() {
|
|||||||
|
|
||||||
if (this->enable_gain_calibration_) {
|
if (this->enable_gain_calibration_) {
|
||||||
// Initialize flash storage for gain calibration
|
// Initialize flash storage for gain calibration
|
||||||
uint32_t g_hash = fnv1_hash(std::string("_gain_calibration_") + this->cs_summary_);
|
uint32_t g_hash = fnv1_hash("_gain_calibration_");
|
||||||
|
g_hash = fnv1_hash_extend(g_hash, cs);
|
||||||
this->gain_calibration_pref_ = global_preferences->make_preference<GainCalibration[3]>(g_hash, true);
|
this->gain_calibration_pref_ = global_preferences->make_preference<GainCalibration[3]>(g_hash, true);
|
||||||
this->restore_gain_calibrations_();
|
this->restore_gain_calibrations_();
|
||||||
|
|
||||||
@@ -214,7 +221,8 @@ void ATM90E32Component::setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::log_calibration_status_() {
|
void ATM90E32Component::log_calibration_status_() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
|
|
||||||
bool offset_mismatch = false;
|
bool offset_mismatch = false;
|
||||||
bool power_mismatch = false;
|
bool power_mismatch = false;
|
||||||
@@ -565,7 +573,8 @@ float ATM90E32Component::get_chip_temperature_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::run_gain_calibrations() {
|
void ATM90E32Component::run_gain_calibrations() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
if (!this->enable_gain_calibration_) {
|
if (!this->enable_gain_calibration_) {
|
||||||
ESP_LOGW(TAG, "[CALIBRATION][%s] Gain calibration is disabled! Enable it first with enable_gain_calibration: true",
|
ESP_LOGW(TAG, "[CALIBRATION][%s] Gain calibration is disabled! Enable it first with enable_gain_calibration: true",
|
||||||
cs);
|
cs);
|
||||||
@@ -665,7 +674,8 @@ void ATM90E32Component::run_gain_calibrations() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::save_gain_calibration_to_memory_() {
|
void ATM90E32Component::save_gain_calibration_to_memory_() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
bool success = this->gain_calibration_pref_.save(&this->gain_phase_);
|
bool success = this->gain_calibration_pref_.save(&this->gain_phase_);
|
||||||
global_preferences->sync();
|
global_preferences->sync();
|
||||||
if (success) {
|
if (success) {
|
||||||
@@ -678,7 +688,8 @@ void ATM90E32Component::save_gain_calibration_to_memory_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::save_offset_calibration_to_memory_() {
|
void ATM90E32Component::save_offset_calibration_to_memory_() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
bool success = this->offset_pref_.save(&this->offset_phase_);
|
bool success = this->offset_pref_.save(&this->offset_phase_);
|
||||||
global_preferences->sync();
|
global_preferences->sync();
|
||||||
if (success) {
|
if (success) {
|
||||||
@@ -694,7 +705,8 @@ void ATM90E32Component::save_offset_calibration_to_memory_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::save_power_offset_calibration_to_memory_() {
|
void ATM90E32Component::save_power_offset_calibration_to_memory_() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
bool success = this->power_offset_pref_.save(&this->power_offset_phase_);
|
bool success = this->power_offset_pref_.save(&this->power_offset_phase_);
|
||||||
global_preferences->sync();
|
global_preferences->sync();
|
||||||
if (success) {
|
if (success) {
|
||||||
@@ -710,7 +722,8 @@ void ATM90E32Component::save_power_offset_calibration_to_memory_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::run_offset_calibrations() {
|
void ATM90E32Component::run_offset_calibrations() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
if (!this->enable_offset_calibration_) {
|
if (!this->enable_offset_calibration_) {
|
||||||
ESP_LOGW(TAG,
|
ESP_LOGW(TAG,
|
||||||
"[CALIBRATION][%s] Offset calibration is disabled! Enable it first with enable_offset_calibration: true",
|
"[CALIBRATION][%s] Offset calibration is disabled! Enable it first with enable_offset_calibration: true",
|
||||||
@@ -740,7 +753,8 @@ void ATM90E32Component::run_offset_calibrations() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::run_power_offset_calibrations() {
|
void ATM90E32Component::run_power_offset_calibrations() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
if (!this->enable_offset_calibration_) {
|
if (!this->enable_offset_calibration_) {
|
||||||
ESP_LOGW(
|
ESP_LOGW(
|
||||||
TAG,
|
TAG,
|
||||||
@@ -813,7 +827,8 @@ void ATM90E32Component::write_power_offsets_to_registers_(uint8_t phase, int16_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::restore_gain_calibrations_() {
|
void ATM90E32Component::restore_gain_calibrations_() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
for (uint8_t i = 0; i < 3; ++i) {
|
for (uint8_t i = 0; i < 3; ++i) {
|
||||||
this->config_gain_phase_[i].voltage_gain = this->phase_[i].voltage_gain_;
|
this->config_gain_phase_[i].voltage_gain = this->phase_[i].voltage_gain_;
|
||||||
this->config_gain_phase_[i].current_gain = this->phase_[i].ct_gain_;
|
this->config_gain_phase_[i].current_gain = this->phase_[i].ct_gain_;
|
||||||
@@ -867,7 +882,8 @@ void ATM90E32Component::restore_gain_calibrations_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::restore_offset_calibrations_() {
|
void ATM90E32Component::restore_offset_calibrations_() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
for (uint8_t i = 0; i < 3; ++i)
|
for (uint8_t i = 0; i < 3; ++i)
|
||||||
this->config_offset_phase_[i] = this->offset_phase_[i];
|
this->config_offset_phase_[i] = this->offset_phase_[i];
|
||||||
|
|
||||||
@@ -909,7 +925,8 @@ void ATM90E32Component::restore_offset_calibrations_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::restore_power_offset_calibrations_() {
|
void ATM90E32Component::restore_power_offset_calibrations_() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
for (uint8_t i = 0; i < 3; ++i)
|
for (uint8_t i = 0; i < 3; ++i)
|
||||||
this->config_power_offset_phase_[i] = this->power_offset_phase_[i];
|
this->config_power_offset_phase_[i] = this->power_offset_phase_[i];
|
||||||
|
|
||||||
@@ -951,7 +968,8 @@ void ATM90E32Component::restore_power_offset_calibrations_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::clear_gain_calibrations() {
|
void ATM90E32Component::clear_gain_calibrations() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
if (!this->using_saved_calibrations_) {
|
if (!this->using_saved_calibrations_) {
|
||||||
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored gain calibrations to clear. Current values:", cs);
|
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored gain calibrations to clear. Current values:", cs);
|
||||||
ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs);
|
ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs);
|
||||||
@@ -1000,7 +1018,8 @@ void ATM90E32Component::clear_gain_calibrations() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::clear_offset_calibrations() {
|
void ATM90E32Component::clear_offset_calibrations() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
if (!this->restored_offset_calibration_) {
|
if (!this->restored_offset_calibration_) {
|
||||||
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored offset calibrations to clear. Current values:", cs);
|
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored offset calibrations to clear. Current values:", cs);
|
||||||
ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
|
ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
|
||||||
@@ -1042,7 +1061,8 @@ void ATM90E32Component::clear_offset_calibrations() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::clear_power_offset_calibrations() {
|
void ATM90E32Component::clear_power_offset_calibrations() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
if (!this->restored_power_offset_calibration_) {
|
if (!this->restored_power_offset_calibration_) {
|
||||||
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored power offsets to clear. Current values:", cs);
|
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored power offsets to clear. Current values:", cs);
|
||||||
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
|
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
|
||||||
@@ -1117,7 +1137,8 @@ int16_t ATM90E32Component::calibrate_power_offset(uint8_t phase, bool reactive)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ATM90E32Component::verify_gain_writes_() {
|
bool ATM90E32Component::verify_gain_writes_() {
|
||||||
const char *cs = this->cs_summary_.c_str();
|
char cs[GPIO_SUMMARY_MAX_LEN];
|
||||||
|
this->get_cs_summary_(cs);
|
||||||
bool success = true;
|
bool success = true;
|
||||||
for (uint8_t phase = 0; phase < 3; phase++) {
|
for (uint8_t phase = 0; phase < 3; phase++) {
|
||||||
uint16_t read_voltage = this->read16_(voltage_gain_registers[phase]);
|
uint16_t read_voltage = this->read16_(voltage_gain_registers[phase]);
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <span>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "atm90e32_reg.h"
|
#include "atm90e32_reg.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 "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/gpio.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/preferences.h"
|
#include "esphome/core/preferences.h"
|
||||||
|
|
||||||
@@ -182,6 +184,7 @@ class ATM90E32Component : public PollingComponent,
|
|||||||
bool verify_gain_writes_();
|
bool verify_gain_writes_();
|
||||||
bool validate_spi_read_(uint16_t expected, const char *context = nullptr);
|
bool validate_spi_read_(uint16_t expected, const char *context = nullptr);
|
||||||
void log_calibration_status_();
|
void log_calibration_status_();
|
||||||
|
void get_cs_summary_(std::span<char, GPIO_SUMMARY_MAX_LEN> buffer);
|
||||||
|
|
||||||
struct ATM90E32Phase {
|
struct ATM90E32Phase {
|
||||||
uint16_t voltage_gain_{0};
|
uint16_t voltage_gain_{0};
|
||||||
@@ -247,7 +250,6 @@ class ATM90E32Component : public PollingComponent,
|
|||||||
ESPPreferenceObject offset_pref_;
|
ESPPreferenceObject offset_pref_;
|
||||||
ESPPreferenceObject power_offset_pref_;
|
ESPPreferenceObject power_offset_pref_;
|
||||||
ESPPreferenceObject gain_calibration_pref_;
|
ESPPreferenceObject gain_calibration_pref_;
|
||||||
std::string cs_summary_;
|
|
||||||
|
|
||||||
sensor::Sensor *freq_sensor_{nullptr};
|
sensor::Sensor *freq_sensor_{nullptr};
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components.esp32 import add_idf_component
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_NUM_CHANNELS, CONF_SAMPLE_RATE
|
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_NUM_CHANNELS, CONF_SAMPLE_RATE
|
||||||
import esphome.final_validate as fv
|
import esphome.final_validate as fv
|
||||||
@@ -165,4 +166,7 @@ def final_validate_audio_schema(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_library("esphome/esp-audio-libs", "2.0.1")
|
add_idf_component(
|
||||||
|
name="esphome/esp-audio-libs",
|
||||||
|
ref="2.0.3",
|
||||||
|
)
|
||||||
|
|||||||
@@ -300,7 +300,7 @@ FileDecoderState AudioDecoder::decode_mp3_() {
|
|||||||
|
|
||||||
// Advance read pointer to match the offset for the syncword
|
// Advance read pointer to match the offset for the syncword
|
||||||
this->input_transfer_buffer_->decrease_buffer_length(offset);
|
this->input_transfer_buffer_->decrease_buffer_length(offset);
|
||||||
uint8_t *buffer_start = this->input_transfer_buffer_->get_buffer_start();
|
const uint8_t *buffer_start = this->input_transfer_buffer_->get_buffer_start();
|
||||||
|
|
||||||
buffer_length = (int) this->input_transfer_buffer_->available();
|
buffer_length = (int) this->input_transfer_buffer_->available();
|
||||||
int err = esp_audio_libs::helix_decoder::MP3Decode(this->mp3_decoder_, &buffer_start, &buffer_length,
|
int err = esp_audio_libs::helix_decoder::MP3Decode(this->mp3_decoder_, &buffer_start, &buffer_length,
|
||||||
|
|||||||
@@ -185,18 +185,16 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string url_string = str_lower_case(url);
|
if (str_endswith_ignore_case(url, ".wav")) {
|
||||||
|
|
||||||
if (str_endswith(url_string, ".wav")) {
|
|
||||||
file_type = AudioFileType::WAV;
|
file_type = AudioFileType::WAV;
|
||||||
}
|
}
|
||||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||||
else if (str_endswith(url_string, ".mp3")) {
|
else if (str_endswith_ignore_case(url, ".mp3")) {
|
||||||
file_type = AudioFileType::MP3;
|
file_type = AudioFileType::MP3;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_AUDIO_FLAC_SUPPORT
|
#ifdef USE_AUDIO_FLAC_SUPPORT
|
||||||
else if (str_endswith(url_string, ".flac")) {
|
else if (str_endswith_ignore_case(url, ".flac")) {
|
||||||
file_type = AudioFileType::FLAC;
|
file_type = AudioFileType::FLAC;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#include "bh1750.h"
|
#include "bh1750.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome::bh1750 {
|
||||||
namespace bh1750 {
|
|
||||||
|
|
||||||
static const char *const TAG = "bh1750.sensor";
|
static const char *const TAG = "bh1750.sensor";
|
||||||
|
|
||||||
@@ -13,6 +13,31 @@ static const uint8_t BH1750_COMMAND_ONE_TIME_L = 0b00100011;
|
|||||||
static const uint8_t BH1750_COMMAND_ONE_TIME_H = 0b00100000;
|
static const uint8_t BH1750_COMMAND_ONE_TIME_H = 0b00100000;
|
||||||
static const uint8_t BH1750_COMMAND_ONE_TIME_H2 = 0b00100001;
|
static const uint8_t BH1750_COMMAND_ONE_TIME_H2 = 0b00100001;
|
||||||
|
|
||||||
|
static constexpr uint32_t MEASUREMENT_TIMEOUT_MS = 2000;
|
||||||
|
static constexpr float HIGH_LIGHT_THRESHOLD_LX = 7000.0f;
|
||||||
|
|
||||||
|
// Measurement time constants (datasheet values)
|
||||||
|
static constexpr uint16_t MTREG_DEFAULT = 69;
|
||||||
|
static constexpr uint16_t MTREG_MIN = 31;
|
||||||
|
static constexpr uint16_t MTREG_MAX = 254;
|
||||||
|
static constexpr uint16_t MEAS_TIME_L_MS = 24; // L-resolution max measurement time @ mtreg=69
|
||||||
|
static constexpr uint16_t MEAS_TIME_H_MS = 180; // H/H2-resolution max measurement time @ mtreg=69
|
||||||
|
|
||||||
|
// Conversion constants (datasheet formulas)
|
||||||
|
static constexpr float RESOLUTION_DIVISOR = 1.2f; // counts to lux conversion divisor
|
||||||
|
static constexpr float MODE_H2_DIVISOR = 2.0f; // H2 mode has 2x higher resolution
|
||||||
|
|
||||||
|
// MTreg calculation constants
|
||||||
|
static constexpr int COUNTS_TARGET = 50000; // Target counts for optimal range (avoid saturation)
|
||||||
|
static constexpr int COUNTS_NUMERATOR = 10;
|
||||||
|
static constexpr int COUNTS_DENOMINATOR = 12;
|
||||||
|
|
||||||
|
// MTreg register bit manipulation constants
|
||||||
|
static constexpr uint8_t MTREG_HI_SHIFT = 5; // High 3 bits start at bit 5
|
||||||
|
static constexpr uint8_t MTREG_HI_MASK = 0b111; // 3-bit mask for high bits
|
||||||
|
static constexpr uint8_t MTREG_LO_SHIFT = 0; // Low 5 bits start at bit 0
|
||||||
|
static constexpr uint8_t MTREG_LO_MASK = 0b11111; // 5-bit mask for low bits
|
||||||
|
|
||||||
/*
|
/*
|
||||||
bh1750 properties:
|
bh1750 properties:
|
||||||
|
|
||||||
@@ -43,74 +68,7 @@ void BH1750Sensor::setup() {
|
|||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
this->state_ = IDLE;
|
||||||
|
|
||||||
void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f) {
|
|
||||||
// turn on (after one-shot sensor automatically powers down)
|
|
||||||
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
|
|
||||||
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
|
|
||||||
ESP_LOGW(TAG, "Power on failed");
|
|
||||||
f(NAN);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (active_mtreg_ != mtreg) {
|
|
||||||
// set mtreg
|
|
||||||
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111);
|
|
||||||
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111);
|
|
||||||
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
|
|
||||||
ESP_LOGW(TAG, "Set measurement time failed");
|
|
||||||
active_mtreg_ = 0;
|
|
||||||
f(NAN);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
active_mtreg_ = mtreg;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t cmd;
|
|
||||||
uint16_t meas_time;
|
|
||||||
switch (mode) {
|
|
||||||
case BH1750_MODE_L:
|
|
||||||
cmd = BH1750_COMMAND_ONE_TIME_L;
|
|
||||||
meas_time = 24 * mtreg / 69;
|
|
||||||
break;
|
|
||||||
case BH1750_MODE_H:
|
|
||||||
cmd = BH1750_COMMAND_ONE_TIME_H;
|
|
||||||
meas_time = 180 * mtreg / 69;
|
|
||||||
break;
|
|
||||||
case BH1750_MODE_H2:
|
|
||||||
cmd = BH1750_COMMAND_ONE_TIME_H2;
|
|
||||||
meas_time = 180 * mtreg / 69;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
f(NAN);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this->write(&cmd, 1) != i2c::ERROR_OK) {
|
|
||||||
ESP_LOGW(TAG, "Start measurement failed");
|
|
||||||
f(NAN);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// probably not needed, but adjust for rounding
|
|
||||||
meas_time++;
|
|
||||||
|
|
||||||
this->set_timeout("read", meas_time, [this, mode, mtreg, f]() {
|
|
||||||
uint16_t raw_value;
|
|
||||||
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
|
|
||||||
ESP_LOGW(TAG, "Read data failed");
|
|
||||||
f(NAN);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
raw_value = i2c::i2ctohs(raw_value);
|
|
||||||
|
|
||||||
float lx = float(raw_value) / 1.2f;
|
|
||||||
lx *= 69.0f / mtreg;
|
|
||||||
if (mode == BH1750_MODE_H2)
|
|
||||||
lx /= 2.0f;
|
|
||||||
|
|
||||||
f(lx);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BH1750Sensor::dump_config() {
|
void BH1750Sensor::dump_config() {
|
||||||
@@ -124,45 +82,189 @@ void BH1750Sensor::dump_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BH1750Sensor::update() {
|
void BH1750Sensor::update() {
|
||||||
// first do a quick measurement in L-mode with full range
|
const uint32_t now = millis();
|
||||||
// to find right range
|
|
||||||
this->read_lx_(BH1750_MODE_L, 31, [this](float val) {
|
// Start coarse measurement to determine optimal mode/mtreg
|
||||||
if (std::isnan(val)) {
|
if (this->state_ != IDLE) {
|
||||||
|
// Safety timeout: reset if stuck
|
||||||
|
if (now - this->measurement_start_time_ > MEASUREMENT_TIMEOUT_MS) {
|
||||||
|
ESP_LOGW(TAG, "Measurement timeout, resetting state");
|
||||||
|
this->state_ = IDLE;
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Previous measurement not complete, skipping update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->start_measurement_(BH1750_MODE_L, MTREG_MIN, now)) {
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
this->publish_state(NAN);
|
this->publish_state(NAN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BH1750Mode use_mode;
|
this->state_ = WAITING_COARSE_MEASUREMENT;
|
||||||
uint8_t use_mtreg;
|
this->enable_loop(); // Enable loop while measurement in progress
|
||||||
if (val <= 7000) {
|
}
|
||||||
use_mode = BH1750_MODE_H2;
|
|
||||||
use_mtreg = 254;
|
void BH1750Sensor::loop() {
|
||||||
|
const uint32_t now = App.get_loop_component_start_time();
|
||||||
|
|
||||||
|
switch (this->state_) {
|
||||||
|
case IDLE:
|
||||||
|
// Disable loop when idle to save cycles
|
||||||
|
this->disable_loop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WAITING_COARSE_MEASUREMENT:
|
||||||
|
if (now - this->measurement_start_time_ >= this->measurement_duration_) {
|
||||||
|
this->state_ = READING_COARSE_RESULT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READING_COARSE_RESULT: {
|
||||||
|
float lx;
|
||||||
|
if (!this->read_measurement_(lx)) {
|
||||||
|
this->fail_and_reset_();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->process_coarse_result_(lx);
|
||||||
|
|
||||||
|
// Start fine measurement with optimal settings
|
||||||
|
// fetch millis() again since the read can take a bit
|
||||||
|
if (!this->start_measurement_(this->fine_mode_, this->fine_mtreg_, millis())) {
|
||||||
|
this->fail_and_reset_();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->state_ = WAITING_FINE_MEASUREMENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WAITING_FINE_MEASUREMENT:
|
||||||
|
if (now - this->measurement_start_time_ >= this->measurement_duration_) {
|
||||||
|
this->state_ = READING_FINE_RESULT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READING_FINE_RESULT: {
|
||||||
|
float lx;
|
||||||
|
if (!this->read_measurement_(lx)) {
|
||||||
|
this->fail_and_reset_();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), lx);
|
||||||
|
this->status_clear_warning();
|
||||||
|
this->publish_state(lx);
|
||||||
|
this->state_ = IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BH1750Sensor::start_measurement_(BH1750Mode mode, uint8_t mtreg, uint32_t now) {
|
||||||
|
// Power on
|
||||||
|
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
|
||||||
|
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGW(TAG, "Power on failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set MTreg if changed
|
||||||
|
if (this->active_mtreg_ != mtreg) {
|
||||||
|
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> MTREG_HI_SHIFT) & MTREG_HI_MASK);
|
||||||
|
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> MTREG_LO_SHIFT) & MTREG_LO_MASK);
|
||||||
|
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGW(TAG, "Set measurement time failed");
|
||||||
|
this->active_mtreg_ = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this->active_mtreg_ = mtreg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start measurement
|
||||||
|
uint8_t cmd;
|
||||||
|
uint16_t meas_time;
|
||||||
|
switch (mode) {
|
||||||
|
case BH1750_MODE_L:
|
||||||
|
cmd = BH1750_COMMAND_ONE_TIME_L;
|
||||||
|
meas_time = MEAS_TIME_L_MS * mtreg / MTREG_DEFAULT;
|
||||||
|
break;
|
||||||
|
case BH1750_MODE_H:
|
||||||
|
cmd = BH1750_COMMAND_ONE_TIME_H;
|
||||||
|
meas_time = MEAS_TIME_H_MS * mtreg / MTREG_DEFAULT;
|
||||||
|
break;
|
||||||
|
case BH1750_MODE_H2:
|
||||||
|
cmd = BH1750_COMMAND_ONE_TIME_H2;
|
||||||
|
meas_time = MEAS_TIME_H_MS * mtreg / MTREG_DEFAULT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->write(&cmd, 1) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGW(TAG, "Start measurement failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store current measurement parameters
|
||||||
|
this->current_mode_ = mode;
|
||||||
|
this->current_mtreg_ = mtreg;
|
||||||
|
this->measurement_start_time_ = now;
|
||||||
|
this->measurement_duration_ = meas_time + 1; // Add 1ms for safety
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BH1750Sensor::read_measurement_(float &lx_out) {
|
||||||
|
uint16_t raw_value;
|
||||||
|
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGW(TAG, "Read data failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
raw_value = i2c::i2ctohs(raw_value);
|
||||||
|
|
||||||
|
float lx = float(raw_value) / RESOLUTION_DIVISOR;
|
||||||
|
lx *= float(MTREG_DEFAULT) / this->current_mtreg_;
|
||||||
|
if (this->current_mode_ == BH1750_MODE_H2) {
|
||||||
|
lx /= MODE_H2_DIVISOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
lx_out = lx;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BH1750Sensor::process_coarse_result_(float lx) {
|
||||||
|
if (std::isnan(lx)) {
|
||||||
|
// Use defaults if coarse measurement failed
|
||||||
|
this->fine_mode_ = BH1750_MODE_H2;
|
||||||
|
this->fine_mtreg_ = MTREG_MAX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lx <= HIGH_LIGHT_THRESHOLD_LX) {
|
||||||
|
this->fine_mode_ = BH1750_MODE_H2;
|
||||||
|
this->fine_mtreg_ = MTREG_MAX;
|
||||||
} else {
|
} else {
|
||||||
use_mode = BH1750_MODE_H;
|
this->fine_mode_ = BH1750_MODE_H;
|
||||||
// lx = counts / 1.2 * (69 / mtreg)
|
// lx = counts / 1.2 * (69 / mtreg)
|
||||||
// -> mtreg = counts / 1.2 * (69 / lx)
|
// -> mtreg = counts / 1.2 * (69 / lx)
|
||||||
// calculate for counts=50000 (allow some range to not saturate, but maximize mtreg)
|
// calculate for counts=50000 (allow some range to not saturate, but maximize mtreg)
|
||||||
// -> mtreg = 50000*(10/12)*(69/lx)
|
// -> mtreg = 50000*(10/12)*(69/lx)
|
||||||
int ideal_mtreg = 50000 * 10 * 69 / (12 * (int) val);
|
int ideal_mtreg = COUNTS_TARGET * COUNTS_NUMERATOR * MTREG_DEFAULT / (COUNTS_DENOMINATOR * (int) lx);
|
||||||
use_mtreg = std::min(254, std::max(31, ideal_mtreg));
|
this->fine_mtreg_ = std::min((int) MTREG_MAX, std::max((int) MTREG_MIN, ideal_mtreg));
|
||||||
}
|
}
|
||||||
ESP_LOGV(TAG, "L result: %f -> Calculated mode=%d, mtreg=%d", val, (int) use_mode, use_mtreg);
|
|
||||||
|
|
||||||
this->read_lx_(use_mode, use_mtreg, [this](float val) {
|
ESP_LOGV(TAG, "L result: %.1f -> Calculated mode=%d, mtreg=%d", lx, (int) this->fine_mode_, this->fine_mtreg_);
|
||||||
if (std::isnan(val)) {
|
}
|
||||||
|
|
||||||
|
void BH1750Sensor::fail_and_reset_() {
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
this->publish_state(NAN);
|
this->publish_state(NAN);
|
||||||
return;
|
this->state_ = IDLE;
|
||||||
}
|
|
||||||
ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), val);
|
|
||||||
this->status_clear_warning();
|
|
||||||
this->publish_state(val);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
} // namespace bh1750
|
} // namespace esphome::bh1750
|
||||||
} // namespace esphome
|
|
||||||
|
|||||||
@@ -4,10 +4,9 @@
|
|||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/i2c/i2c.h"
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome::bh1750 {
|
||||||
namespace bh1750 {
|
|
||||||
|
|
||||||
enum BH1750Mode {
|
enum BH1750Mode : uint8_t {
|
||||||
BH1750_MODE_L,
|
BH1750_MODE_L,
|
||||||
BH1750_MODE_H,
|
BH1750_MODE_H,
|
||||||
BH1750_MODE_H2,
|
BH1750_MODE_H2,
|
||||||
@@ -21,13 +20,36 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
|
|||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
void loop() override;
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f);
|
// State machine states
|
||||||
|
enum State : uint8_t {
|
||||||
uint8_t active_mtreg_{0};
|
IDLE,
|
||||||
|
WAITING_COARSE_MEASUREMENT,
|
||||||
|
READING_COARSE_RESULT,
|
||||||
|
WAITING_FINE_MEASUREMENT,
|
||||||
|
READING_FINE_RESULT,
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace bh1750
|
// 4-byte aligned members
|
||||||
} // namespace esphome
|
uint32_t measurement_start_time_{0};
|
||||||
|
uint32_t measurement_duration_{0};
|
||||||
|
|
||||||
|
// 1-byte members grouped together to minimize padding
|
||||||
|
State state_{IDLE};
|
||||||
|
BH1750Mode current_mode_{BH1750_MODE_L};
|
||||||
|
uint8_t current_mtreg_{31};
|
||||||
|
BH1750Mode fine_mode_{BH1750_MODE_H2};
|
||||||
|
uint8_t fine_mtreg_{254};
|
||||||
|
uint8_t active_mtreg_{0};
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
bool start_measurement_(BH1750Mode mode, uint8_t mtreg, uint32_t now);
|
||||||
|
bool read_measurement_(float &lx_out);
|
||||||
|
void process_coarse_result_(float lx);
|
||||||
|
void fail_and_reset_();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esphome::bh1750
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ bool BinarySensor::set_new_state(const optional<bool> &new_state) {
|
|||||||
#if defined(USE_BINARY_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
#if defined(USE_BINARY_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
||||||
ControllerRegistry::notify_binary_sensor_update(this);
|
ControllerRegistry::notify_binary_sensor_update(this);
|
||||||
#endif
|
#endif
|
||||||
ESP_LOGD(TAG, "'%s': %s", this->get_name().c_str(), ONOFFMAYBE(new_state));
|
ESP_LOGD(TAG, "'%s' >> %s", this->get_name().c_str(), ONOFFMAYBE(new_state));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,9 +1,23 @@
|
|||||||
# This file was auto-generated by libretiny/generate_components.py
|
"""
|
||||||
# Do not modify its contents.
|
██╗ ██╗ █████╗ ██████╗ ███╗ ██╗██╗███╗ ██╗ ██████╗
|
||||||
# For custom pin validators, put validate_pin() or validate_usage()
|
██║ ██║██╔══██╗██╔══██╗████╗ ██║██║████╗ ██║██╔════╝
|
||||||
# in gpio.py file in this directory.
|
██║ █╗ ██║███████║██████╔╝██╔██╗ ██║██║██╔██╗ ██║██║ ███╗
|
||||||
# For changing schema/pin schema, put COMPONENT_SCHEMA or COMPONENT_PIN_SCHEMA
|
██║███╗██║██╔══██║██╔══██╗██║╚██╗██║██║██║╚██╗██║██║ ██║
|
||||||
# in schema.py file in this directory.
|
╚███╔███╔╝██║ ██║██║ ██║██║ ╚████║██║██║ ╚████║╚██████╔╝
|
||||||
|
╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||||
|
|
||||||
|
AUTO-GENERATED FILE - DO NOT EDIT!
|
||||||
|
|
||||||
|
This file was auto-generated by libretiny/generate_components.py.
|
||||||
|
Any manual changes WILL BE LOST on regeneration.
|
||||||
|
|
||||||
|
To customize this component:
|
||||||
|
- Pin validators: Create gpio.py with validate_pin() or validate_usage()
|
||||||
|
- Schema extensions: Create schema.py with COMPONENT_SCHEMA or COMPONENT_PIN_SCHEMA
|
||||||
|
|
||||||
|
Platform-specific code should be added to the main libretiny component
|
||||||
|
(__init__.py in esphome/components/libretiny/) rather than here.
|
||||||
|
"""
|
||||||
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.components import libretiny
|
from esphome.components import libretiny
|
||||||
@@ -27,6 +41,7 @@ COMPONENT_DATA = LibreTinyComponent(
|
|||||||
board_pins=BK72XX_BOARD_PINS,
|
board_pins=BK72XX_BOARD_PINS,
|
||||||
pin_validation=None,
|
pin_validation=None,
|
||||||
usage_validation=None,
|
usage_validation=None,
|
||||||
|
supports_atomics=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ static const char *const TAG = "bl0940.number";
|
|||||||
void CalibrationNumber::setup() {
|
void CalibrationNumber::setup() {
|
||||||
float value = 0.0f;
|
float value = 0.0f;
|
||||||
if (this->restore_value_) {
|
if (this->restore_value_) {
|
||||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
this->pref_ = this->make_entity_preference<float>();
|
||||||
if (!this->pref_.load(&value)) {
|
if (!this->pref_.load(&value)) {
|
||||||
value = 0.0f;
|
value = 0.0f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components.logger import request_log_listener
|
||||||
from esphome.components.zephyr import zephyr_add_prj_conf
|
from esphome.components.zephyr import zephyr_add_prj_conf
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_LOGS, CONF_TYPE
|
from esphome.const import CONF_ID, CONF_LOGS, CONF_TYPE
|
||||||
@@ -25,5 +26,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
zephyr_add_prj_conf("BT_NUS", True)
|
zephyr_add_prj_conf("BT_NUS", True)
|
||||||
cg.add(var.set_expose_log(config[CONF_TYPE] == CONF_LOGS))
|
expose_log = config[CONF_TYPE] == CONF_LOGS
|
||||||
|
cg.add(var.set_expose_log(expose_log))
|
||||||
|
if expose_log:
|
||||||
|
request_log_listener() # Request a log listener slot for BLE NUS log streaming
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ void BluetoothConnection::loop() {
|
|||||||
// - For V3_WITH_CACHE: Services are never sent, disable after INIT state
|
// - For V3_WITH_CACHE: Services are never sent, disable after INIT state
|
||||||
// - For V3_WITHOUT_CACHE: Disable only after service discovery is complete
|
// - For V3_WITHOUT_CACHE: Disable only after service discovery is complete
|
||||||
// (send_service_ == DONE_SENDING_SERVICES, which is only set after services are sent)
|
// (send_service_ == DONE_SENDING_SERVICES, which is only set after services are sent)
|
||||||
if (this->state_ != espbt::ClientState::INIT && (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE ||
|
if (this->state() != espbt::ClientState::INIT && (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE ||
|
||||||
this->send_service_ == DONE_SENDING_SERVICES)) {
|
this->send_service_ == DONE_SENDING_SERVICES)) {
|
||||||
this->disable_loop();
|
this->disable_loop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ TYPES = [
|
|||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema(
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(cg.Component),
|
cv.GenerateID(): cv.declare_id(cg.EntityBase),
|
||||||
cv.GenerateID(CONF_BME68X_BSEC2_ID): cv.use_id(BME68xBSEC2Component),
|
cv.GenerateID(CONF_BME68X_BSEC2_ID): cv.use_id(BME68xBSEC2Component),
|
||||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_CELSIUS,
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
|||||||
@@ -1,164 +1,5 @@
|
|||||||
import math
|
|
||||||
|
|
||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import i2c, sensor
|
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
|
||||||
CONF_ID,
|
CONFIG_SCHEMA = cv.invalid(
|
||||||
CONF_IIR_FILTER,
|
"The bmp581 sensor component has been renamed to bmp581_i2c."
|
||||||
CONF_OVERSAMPLING,
|
|
||||||
CONF_PRESSURE,
|
|
||||||
CONF_TEMPERATURE,
|
|
||||||
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
|
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
UNIT_CELSIUS,
|
|
||||||
UNIT_PASCAL,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
CODEOWNERS = ["@kahrendt"]
|
|
||||||
DEPENDENCIES = ["i2c"]
|
|
||||||
|
|
||||||
bmp581_ns = cg.esphome_ns.namespace("bmp581")
|
|
||||||
|
|
||||||
Oversampling = bmp581_ns.enum("Oversampling")
|
|
||||||
OVERSAMPLING_OPTIONS = {
|
|
||||||
"NONE": Oversampling.OVERSAMPLING_NONE,
|
|
||||||
"2X": Oversampling.OVERSAMPLING_X2,
|
|
||||||
"4X": Oversampling.OVERSAMPLING_X4,
|
|
||||||
"8X": Oversampling.OVERSAMPLING_X8,
|
|
||||||
"16X": Oversampling.OVERSAMPLING_X16,
|
|
||||||
"32X": Oversampling.OVERSAMPLING_X32,
|
|
||||||
"64X": Oversampling.OVERSAMPLING_X64,
|
|
||||||
"128X": Oversampling.OVERSAMPLING_X128,
|
|
||||||
}
|
|
||||||
|
|
||||||
IIRFilter = bmp581_ns.enum("IIRFilter")
|
|
||||||
IIR_FILTER_OPTIONS = {
|
|
||||||
"OFF": IIRFilter.IIR_FILTER_OFF,
|
|
||||||
"2X": IIRFilter.IIR_FILTER_2,
|
|
||||||
"4X": IIRFilter.IIR_FILTER_4,
|
|
||||||
"8X": IIRFilter.IIR_FILTER_8,
|
|
||||||
"16X": IIRFilter.IIR_FILTER_16,
|
|
||||||
"32X": IIRFilter.IIR_FILTER_32,
|
|
||||||
"64X": IIRFilter.IIR_FILTER_64,
|
|
||||||
"128X": IIRFilter.IIR_FILTER_128,
|
|
||||||
}
|
|
||||||
|
|
||||||
BMP581Component = bmp581_ns.class_(
|
|
||||||
"BMP581Component", cg.PollingComponent, i2c.I2CDevice
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def compute_measurement_conversion_time(config):
|
|
||||||
# - adds up sensor conversion time based on temperature and pressure oversampling rates given in datasheet
|
|
||||||
# - returns a rounded up time in ms
|
|
||||||
|
|
||||||
# Page 12 of datasheet
|
|
||||||
PRESSURE_OVERSAMPLING_CONVERSION_TIMES = {
|
|
||||||
"NONE": 1.0,
|
|
||||||
"2X": 1.7,
|
|
||||||
"4X": 2.9,
|
|
||||||
"8X": 5.4,
|
|
||||||
"16X": 10.4,
|
|
||||||
"32X": 20.4,
|
|
||||||
"64X": 40.4,
|
|
||||||
"128X": 80.4,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Page 12 of datasheet
|
|
||||||
TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES = {
|
|
||||||
"NONE": 1.0,
|
|
||||||
"2X": 1.1,
|
|
||||||
"4X": 1.5,
|
|
||||||
"8X": 2.1,
|
|
||||||
"16X": 3.3,
|
|
||||||
"32X": 5.8,
|
|
||||||
"64X": 10.8,
|
|
||||||
"128X": 20.8,
|
|
||||||
}
|
|
||||||
|
|
||||||
pressure_conversion_time = (
|
|
||||||
0.0 # No conversion time necessary without a pressure sensor
|
|
||||||
)
|
|
||||||
if pressure_config := config.get(CONF_PRESSURE):
|
|
||||||
pressure_conversion_time = PRESSURE_OVERSAMPLING_CONVERSION_TIMES[
|
|
||||||
pressure_config.get(CONF_OVERSAMPLING)
|
|
||||||
]
|
|
||||||
|
|
||||||
temperature_conversion_time = (
|
|
||||||
1.0 # BMP581 always samples the temperature even if only reading pressure
|
|
||||||
)
|
|
||||||
if temperature_config := config.get(CONF_TEMPERATURE):
|
|
||||||
temperature_conversion_time = TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES[
|
|
||||||
temperature_config.get(CONF_OVERSAMPLING)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Datasheet indicates a 5% possible error in each conversion time listed
|
|
||||||
return math.ceil(1.05 * (pressure_conversion_time + temperature_conversion_time))
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
|
||||||
cv.Schema(
|
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(BMP581Component),
|
|
||||||
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="NONE"): cv.enum(
|
|
||||||
OVERSAMPLING_OPTIONS, upper=True
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
|
|
||||||
IIR_FILTER_OPTIONS, upper=True
|
|
||||||
),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_PASCAL,
|
|
||||||
accuracy_decimals=0,
|
|
||||||
device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
|
|
||||||
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(0x46))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
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_config(
|
|
||||||
temperature_config[CONF_OVERSAMPLING]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
cg.add(
|
|
||||||
var.set_temperature_iir_filter_config(temperature_config[CONF_IIR_FILTER])
|
|
||||||
)
|
|
||||||
|
|
||||||
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_config(pressure_config[CONF_OVERSAMPLING]))
|
|
||||||
cg.add(var.set_pressure_iir_filter_config(pressure_config[CONF_IIR_FILTER]))
|
|
||||||
|
|
||||||
cg.add(var.set_conversion_time(compute_measurement_conversion_time(config)))
|
|
||||||
|
|||||||
157
esphome/components/bmp581_base/__init__.py
Normal file
157
esphome/components/bmp581_base/__init__.py
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_IIR_FILTER,
|
||||||
|
CONF_OVERSAMPLING,
|
||||||
|
CONF_PRESSURE,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_PASCAL,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@kahrendt", "@danielkent-net"]
|
||||||
|
|
||||||
|
bmp581_ns = cg.esphome_ns.namespace("bmp581_base")
|
||||||
|
|
||||||
|
Oversampling = bmp581_ns.enum("Oversampling")
|
||||||
|
OVERSAMPLING_OPTIONS = {
|
||||||
|
"NONE": Oversampling.OVERSAMPLING_NONE,
|
||||||
|
"2X": Oversampling.OVERSAMPLING_X2,
|
||||||
|
"4X": Oversampling.OVERSAMPLING_X4,
|
||||||
|
"8X": Oversampling.OVERSAMPLING_X8,
|
||||||
|
"16X": Oversampling.OVERSAMPLING_X16,
|
||||||
|
"32X": Oversampling.OVERSAMPLING_X32,
|
||||||
|
"64X": Oversampling.OVERSAMPLING_X64,
|
||||||
|
"128X": Oversampling.OVERSAMPLING_X128,
|
||||||
|
}
|
||||||
|
|
||||||
|
IIRFilter = bmp581_ns.enum("IIRFilter")
|
||||||
|
IIR_FILTER_OPTIONS = {
|
||||||
|
"OFF": IIRFilter.IIR_FILTER_OFF,
|
||||||
|
"2X": IIRFilter.IIR_FILTER_2,
|
||||||
|
"4X": IIRFilter.IIR_FILTER_4,
|
||||||
|
"8X": IIRFilter.IIR_FILTER_8,
|
||||||
|
"16X": IIRFilter.IIR_FILTER_16,
|
||||||
|
"32X": IIRFilter.IIR_FILTER_32,
|
||||||
|
"64X": IIRFilter.IIR_FILTER_64,
|
||||||
|
"128X": IIRFilter.IIR_FILTER_128,
|
||||||
|
}
|
||||||
|
|
||||||
|
BMP581Component = bmp581_ns.class_("BMP581Component", cg.PollingComponent)
|
||||||
|
|
||||||
|
|
||||||
|
def compute_measurement_conversion_time(config):
|
||||||
|
# - adds up sensor conversion time based on temperature and pressure oversampling rates given in datasheet
|
||||||
|
# - returns a rounded up time in ms
|
||||||
|
|
||||||
|
# Page 12 of datasheet
|
||||||
|
PRESSURE_OVERSAMPLING_CONVERSION_TIMES = {
|
||||||
|
"NONE": 1.0,
|
||||||
|
"2X": 1.7,
|
||||||
|
"4X": 2.9,
|
||||||
|
"8X": 5.4,
|
||||||
|
"16X": 10.4,
|
||||||
|
"32X": 20.4,
|
||||||
|
"64X": 40.4,
|
||||||
|
"128X": 80.4,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Page 12 of datasheet
|
||||||
|
TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES = {
|
||||||
|
"NONE": 1.0,
|
||||||
|
"2X": 1.1,
|
||||||
|
"4X": 1.5,
|
||||||
|
"8X": 2.1,
|
||||||
|
"16X": 3.3,
|
||||||
|
"32X": 5.8,
|
||||||
|
"64X": 10.8,
|
||||||
|
"128X": 20.8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pressure_conversion_time = (
|
||||||
|
0.0 # No conversion time necessary without a pressure sensor
|
||||||
|
)
|
||||||
|
if pressure_config := config.get(CONF_PRESSURE):
|
||||||
|
pressure_conversion_time = PRESSURE_OVERSAMPLING_CONVERSION_TIMES[
|
||||||
|
pressure_config.get(CONF_OVERSAMPLING)
|
||||||
|
]
|
||||||
|
|
||||||
|
temperature_conversion_time = (
|
||||||
|
1.0 # BMP581 always samples the temperature even if only reading pressure
|
||||||
|
)
|
||||||
|
if temperature_config := config.get(CONF_TEMPERATURE):
|
||||||
|
temperature_conversion_time = TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES[
|
||||||
|
temperature_config.get(CONF_OVERSAMPLING)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Datasheet indicates a 5% possible error in each conversion time listed
|
||||||
|
return math.ceil(1.05 * (pressure_conversion_time + temperature_conversion_time))
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA_BASE = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(BMP581Component),
|
||||||
|
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="NONE"): cv.enum(
|
||||||
|
OVERSAMPLING_OPTIONS, upper=True
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
|
||||||
|
IIR_FILTER_OPTIONS, upper=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PASCAL,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
|
||||||
|
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_base(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(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_config(
|
||||||
|
temperature_config[CONF_OVERSAMPLING]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cg.add(
|
||||||
|
var.set_temperature_iir_filter_config(temperature_config[CONF_IIR_FILTER])
|
||||||
|
)
|
||||||
|
|
||||||
|
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_config(pressure_config[CONF_OVERSAMPLING]))
|
||||||
|
cg.add(var.set_pressure_iir_filter_config(pressure_config[CONF_IIR_FILTER]))
|
||||||
|
|
||||||
|
cg.add(var.set_conversion_time(compute_measurement_conversion_time(config)))
|
||||||
|
return var
|
||||||
@@ -10,12 +10,11 @@
|
|||||||
* - All datasheet page references refer to Bosch Document Number BST-BMP581-DS004-04 (revision number 1.4)
|
* - All datasheet page references refer to Bosch Document Number BST-BMP581-DS004-04 (revision number 1.4)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "bmp581.h"
|
#include "bmp581_base.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome::bmp581_base {
|
||||||
namespace bmp581 {
|
|
||||||
|
|
||||||
static const char *const TAG = "bmp581";
|
static const char *const TAG = "bmp581";
|
||||||
|
|
||||||
@@ -91,7 +90,6 @@ void BMP581Component::dump_config() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_I2C_DEVICE(this);
|
|
||||||
LOG_UPDATE_INTERVAL(this);
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
|
||||||
ESP_LOGCONFIG(TAG, " Measurement conversion time: %ums", this->conversion_time_);
|
ESP_LOGCONFIG(TAG, " Measurement conversion time: %ums", this->conversion_time_);
|
||||||
@@ -149,7 +147,7 @@ void BMP581Component::setup() {
|
|||||||
uint8_t chip_id;
|
uint8_t chip_id;
|
||||||
|
|
||||||
// read chip id from sensor
|
// read chip id from sensor
|
||||||
if (!this->read_byte(BMP581_CHIP_ID, &chip_id)) {
|
if (!this->bmp_read_byte(BMP581_CHIP_ID, &chip_id)) {
|
||||||
ESP_LOGE(TAG, "Read chip ID failed");
|
ESP_LOGE(TAG, "Read chip ID failed");
|
||||||
|
|
||||||
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||||
@@ -172,7 +170,7 @@ void BMP581Component::setup() {
|
|||||||
// 3) Verify sensor status (check if NVM is okay) //
|
// 3) Verify sensor status (check if NVM is okay) //
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
|
|
||||||
if (!this->read_byte(BMP581_STATUS, &this->status_.reg)) {
|
if (!this->bmp_read_byte(BMP581_STATUS, &this->status_.reg)) {
|
||||||
ESP_LOGE(TAG, "Failed to read status register");
|
ESP_LOGE(TAG, "Failed to read status register");
|
||||||
|
|
||||||
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
this->error_code_ = ERROR_COMMUNICATION_FAILED;
|
||||||
@@ -359,7 +357,7 @@ bool BMP581Component::check_data_readiness_() {
|
|||||||
|
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
|
|
||||||
if (!this->read_byte(BMP581_INT_STATUS, &status)) {
|
if (!this->bmp_read_byte(BMP581_INT_STATUS, &status)) {
|
||||||
ESP_LOGE(TAG, "Failed to read interrupt status register");
|
ESP_LOGE(TAG, "Failed to read interrupt status register");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -400,7 +398,7 @@ bool BMP581Component::prime_iir_filter_() {
|
|||||||
|
|
||||||
// flush the IIR filter with forced measurements (we will only flush once)
|
// flush the IIR filter with forced measurements (we will only flush once)
|
||||||
this->dsp_config_.bit.iir_flush_forced_en = true;
|
this->dsp_config_.bit.iir_flush_forced_en = true;
|
||||||
if (!this->write_byte(BMP581_DSP, this->dsp_config_.reg)) {
|
if (!this->bmp_write_byte(BMP581_DSP, this->dsp_config_.reg)) {
|
||||||
ESP_LOGE(TAG, "Failed to write IIR source register");
|
ESP_LOGE(TAG, "Failed to write IIR source register");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -430,7 +428,7 @@ bool BMP581Component::prime_iir_filter_() {
|
|||||||
|
|
||||||
// disable IIR filter flushings on future forced measurements
|
// disable IIR filter flushings on future forced measurements
|
||||||
this->dsp_config_.bit.iir_flush_forced_en = false;
|
this->dsp_config_.bit.iir_flush_forced_en = false;
|
||||||
if (!this->write_byte(BMP581_DSP, this->dsp_config_.reg)) {
|
if (!this->bmp_write_byte(BMP581_DSP, this->dsp_config_.reg)) {
|
||||||
ESP_LOGE(TAG, "Failed to write IIR source register");
|
ESP_LOGE(TAG, "Failed to write IIR source register");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -454,7 +452,7 @@ bool BMP581Component::read_temperature_(float &temperature) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t data[3];
|
uint8_t data[3];
|
||||||
if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 3)) {
|
if (!this->bmp_read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 3)) {
|
||||||
ESP_LOGW(TAG, "Failed to read measurement");
|
ESP_LOGW(TAG, "Failed to read measurement");
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
|
|
||||||
@@ -483,7 +481,7 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float &
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t data[6];
|
uint8_t data[6];
|
||||||
if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 6)) {
|
if (!this->bmp_read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 6)) {
|
||||||
ESP_LOGW(TAG, "Failed to read measurement");
|
ESP_LOGW(TAG, "Failed to read measurement");
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
|
|
||||||
@@ -507,7 +505,7 @@ bool BMP581Component::reset_() {
|
|||||||
// - returns the Power-On-Reboot interrupt status, which is asserted if successful
|
// - returns the Power-On-Reboot interrupt status, which is asserted if successful
|
||||||
|
|
||||||
// writes reset command to BMP's command register
|
// writes reset command to BMP's command register
|
||||||
if (!this->write_byte(BMP581_COMMAND, RESET_COMMAND)) {
|
if (!this->bmp_write_byte(BMP581_COMMAND, RESET_COMMAND)) {
|
||||||
ESP_LOGE(TAG, "Failed to write reset command");
|
ESP_LOGE(TAG, "Failed to write reset command");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -518,7 +516,7 @@ bool BMP581Component::reset_() {
|
|||||||
delay(3);
|
delay(3);
|
||||||
|
|
||||||
// read interrupt status register
|
// read interrupt status register
|
||||||
if (!this->read_byte(BMP581_INT_STATUS, &this->int_status_.reg)) {
|
if (!this->bmp_read_byte(BMP581_INT_STATUS, &this->int_status_.reg)) {
|
||||||
ESP_LOGE(TAG, "Failed to read interrupt status register");
|
ESP_LOGE(TAG, "Failed to read interrupt status register");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -562,7 +560,7 @@ bool BMP581Component::write_iir_settings_(IIRFilter temperature_iir, IIRFilter p
|
|||||||
// BMP581_DSP register and BMP581_DSP_IIR registers are successive
|
// BMP581_DSP register and BMP581_DSP_IIR registers are successive
|
||||||
// - allows us to write the IIR configuration with one command to both registers
|
// - allows us to write the IIR configuration with one command to both registers
|
||||||
uint8_t register_data[2] = {this->dsp_config_.reg, this->iir_config_.reg};
|
uint8_t register_data[2] = {this->dsp_config_.reg, this->iir_config_.reg};
|
||||||
return this->write_bytes(BMP581_DSP, register_data, sizeof(register_data));
|
return this->bmp_write_bytes(BMP581_DSP, register_data, sizeof(register_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BMP581Component::write_interrupt_source_settings_(bool data_ready_enable) {
|
bool BMP581Component::write_interrupt_source_settings_(bool data_ready_enable) {
|
||||||
@@ -572,7 +570,7 @@ bool BMP581Component::write_interrupt_source_settings_(bool data_ready_enable) {
|
|||||||
this->int_source_.bit.drdy_data_reg_en = data_ready_enable;
|
this->int_source_.bit.drdy_data_reg_en = data_ready_enable;
|
||||||
|
|
||||||
// write interrupt source register
|
// write interrupt source register
|
||||||
return this->write_byte(BMP581_INT_SOURCE, this->int_source_.reg);
|
return this->bmp_write_byte(BMP581_INT_SOURCE, this->int_source_.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BMP581Component::write_oversampling_settings_(Oversampling temperature_oversampling,
|
bool BMP581Component::write_oversampling_settings_(Oversampling temperature_oversampling,
|
||||||
@@ -583,7 +581,7 @@ bool BMP581Component::write_oversampling_settings_(Oversampling temperature_over
|
|||||||
this->osr_config_.bit.osr_t = temperature_oversampling;
|
this->osr_config_.bit.osr_t = temperature_oversampling;
|
||||||
this->osr_config_.bit.osr_p = pressure_oversampling;
|
this->osr_config_.bit.osr_p = pressure_oversampling;
|
||||||
|
|
||||||
return this->write_byte(BMP581_OSR, this->osr_config_.reg);
|
return this->bmp_write_byte(BMP581_OSR, this->osr_config_.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BMP581Component::write_power_mode_(OperationMode mode) {
|
bool BMP581Component::write_power_mode_(OperationMode mode) {
|
||||||
@@ -593,8 +591,7 @@ bool BMP581Component::write_power_mode_(OperationMode mode) {
|
|||||||
this->odr_config_.bit.pwr_mode = mode;
|
this->odr_config_.bit.pwr_mode = mode;
|
||||||
|
|
||||||
// write odr register
|
// write odr register
|
||||||
return this->write_byte(BMP581_ODR, this->odr_config_.reg);
|
return this->bmp_write_byte(BMP581_ODR, this->odr_config_.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace bmp581
|
} // namespace esphome::bmp581_base
|
||||||
} // namespace esphome
|
|
||||||
@@ -3,11 +3,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/components/i2c/i2c.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome::bmp581_base {
|
||||||
namespace bmp581 {
|
|
||||||
|
|
||||||
static const uint8_t BMP581_ASIC_ID = 0x50; // BMP581's ASIC chip ID (page 51 of datasheet)
|
static const uint8_t BMP581_ASIC_ID = 0x50; // BMP581's ASIC chip ID (page 51 of datasheet)
|
||||||
static const uint8_t RESET_COMMAND = 0xB6; // Soft reset command
|
static const uint8_t RESET_COMMAND = 0xB6; // Soft reset command
|
||||||
@@ -59,7 +57,7 @@ enum IIRFilter {
|
|||||||
IIR_FILTER_128 = 0x7
|
IIR_FILTER_128 = 0x7
|
||||||
};
|
};
|
||||||
|
|
||||||
class BMP581Component : public PollingComponent, public i2c::I2CDevice {
|
class BMP581Component : public PollingComponent {
|
||||||
public:
|
public:
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
@@ -84,6 +82,11 @@ class BMP581Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
void set_conversion_time(uint8_t conversion_time) { this->conversion_time_ = conversion_time; }
|
void set_conversion_time(uint8_t conversion_time) { this->conversion_time_ = conversion_time; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual bool bmp_read_byte(uint8_t a_register, uint8_t *data) = 0;
|
||||||
|
virtual bool bmp_write_byte(uint8_t a_register, uint8_t data) = 0;
|
||||||
|
virtual bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
|
||||||
|
virtual bool bmp_write_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
|
||||||
|
|
||||||
sensor::Sensor *temperature_sensor_{nullptr};
|
sensor::Sensor *temperature_sensor_{nullptr};
|
||||||
sensor::Sensor *pressure_sensor_{nullptr};
|
sensor::Sensor *pressure_sensor_{nullptr};
|
||||||
|
|
||||||
@@ -216,5 +219,4 @@ class BMP581Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
} odr_config_ = {.reg = 0};
|
} odr_config_ = {.reg = 0};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace bmp581
|
} // namespace esphome::bmp581_base
|
||||||
} // namespace esphome
|
|
||||||
0
esphome/components/bmp581_i2c/__init__.py
Normal file
0
esphome/components/bmp581_i2c/__init__.py
Normal file
12
esphome/components/bmp581_i2c/bmp581_i2c.cpp
Normal file
12
esphome/components/bmp581_i2c/bmp581_i2c.cpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include "bmp581_i2c.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome::bmp581_i2c {
|
||||||
|
|
||||||
|
void BMP581I2CComponent::dump_config() {
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
BMP581Component::dump_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace esphome::bmp581_i2c
|
||||||
24
esphome/components/bmp581_i2c/bmp581_i2c.h
Normal file
24
esphome/components/bmp581_i2c/bmp581_i2c.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/bmp581_base/bmp581_base.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome::bmp581_i2c {
|
||||||
|
|
||||||
|
static const char *const TAG = "bmp581_i2c.sensor";
|
||||||
|
|
||||||
|
/// This class implements support for the BMP581 Temperature+Pressure i2c sensor.
|
||||||
|
class BMP581I2CComponent : public esphome::bmp581_base::BMP581Component, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
bool bmp_read_byte(uint8_t a_register, uint8_t *data) override { return read_byte(a_register, data); }
|
||||||
|
bool bmp_write_byte(uint8_t a_register, uint8_t data) override { return write_byte(a_register, data); }
|
||||||
|
bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) override {
|
||||||
|
return read_bytes(a_register, data, len);
|
||||||
|
}
|
||||||
|
bool bmp_write_bytes(uint8_t a_register, uint8_t *data, size_t len) override {
|
||||||
|
return write_bytes(a_register, data, len);
|
||||||
|
}
|
||||||
|
void dump_config() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esphome::bmp581_i2c
|
||||||
23
esphome/components/bmp581_i2c/sensor.py
Normal file
23
esphome/components/bmp581_i2c/sensor.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import i2c
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
|
||||||
|
from ..bmp581_base import CONFIG_SCHEMA_BASE, to_code_base
|
||||||
|
|
||||||
|
AUTO_LOAD = ["bmp581_base"]
|
||||||
|
CODEOWNERS = ["@kahrendt", "@danielkent-net"]
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
|
bmp581_ns = cg.esphome_ns.namespace("bmp581_i2c")
|
||||||
|
BMP581I2CComponent = bmp581_ns.class_(
|
||||||
|
"BMP581I2CComponent", cg.PollingComponent, i2c.I2CDevice
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(
|
||||||
|
i2c.i2c_device_schema(default_address=0x46)
|
||||||
|
).extend({cv.GenerateID(): cv.declare_id(BMP581I2CComponent)})
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = await to_code_base(config)
|
||||||
|
await i2c.register_i2c_device(var, config)
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import esp32_ble_tracker
|
from esphome.components import esp32_ble_tracker
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_MAC_ADDRESS
|
from esphome.const import CONF_BINDKEY, CONF_ID, CONF_MAC_ADDRESS
|
||||||
|
from esphome.core import HexInt
|
||||||
|
|
||||||
CODEOWNERS = ["@nagyrobi"]
|
CODEOWNERS = ["@nagyrobi"]
|
||||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||||
@@ -22,6 +23,7 @@ def bthome_mithermometer_base_schema(extra_schema=None):
|
|||||||
{
|
{
|
||||||
cv.GenerateID(CONF_ID): cv.declare_id(BTHomeMiThermometer),
|
cv.GenerateID(CONF_ID): cv.declare_id(BTHomeMiThermometer),
|
||||||
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||||
|
cv.Optional(CONF_BINDKEY): cv.bind_key,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(BLE_DEVICE_SCHEMA)
|
.extend(BLE_DEVICE_SCHEMA)
|
||||||
@@ -34,3 +36,9 @@ async def setup_bthome_mithermometer(var, 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_address(config[CONF_MAC_ADDRESS].as_hex))
|
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||||
|
if bindkey := config.get(CONF_BINDKEY):
|
||||||
|
bindkey_bytes = [
|
||||||
|
HexInt(int(bindkey[index : index + 2], 16))
|
||||||
|
for index in range(0, len(bindkey), 2)
|
||||||
|
]
|
||||||
|
cg.add(var.set_bindkey(cg.ArrayInitializer(*bindkey_bytes)))
|
||||||
|
|||||||
@@ -3,15 +3,23 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include "mbedtls/ccm.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace bthome_mithermometer {
|
namespace bthome_mithermometer {
|
||||||
|
|
||||||
static const char *const TAG = "bthome_mithermometer";
|
static const char *const TAG = "bthome_mithermometer";
|
||||||
|
static constexpr size_t BTHOME_BINDKEY_SIZE = 16;
|
||||||
|
static constexpr size_t BTHOME_NONCE_SIZE = 13;
|
||||||
|
static constexpr size_t BTHOME_MIC_SIZE = 4;
|
||||||
|
static constexpr size_t BTHOME_COUNTER_SIZE = 4;
|
||||||
|
|
||||||
static const char *format_mac_address(std::span<char, MAC_ADDRESS_PRETTY_BUFFER_SIZE> buffer, uint64_t address) {
|
static const char *format_mac_address(std::span<char, MAC_ADDRESS_PRETTY_BUFFER_SIZE> buffer, uint64_t address) {
|
||||||
std::array<uint8_t, MAC_ADDRESS_SIZE> mac{};
|
std::array<uint8_t, MAC_ADDRESS_SIZE> mac{};
|
||||||
@@ -130,6 +138,10 @@ void BTHomeMiThermometer::dump_config() {
|
|||||||
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||||
ESP_LOGCONFIG(TAG, "BTHome MiThermometer");
|
ESP_LOGCONFIG(TAG, "BTHome MiThermometer");
|
||||||
ESP_LOGCONFIG(TAG, " MAC Address: %s", format_mac_address(addr_buf, this->address_));
|
ESP_LOGCONFIG(TAG, " MAC Address: %s", format_mac_address(addr_buf, this->address_));
|
||||||
|
if (this->has_bindkey_) {
|
||||||
|
char bindkey_hex[format_hex_pretty_size(BTHOME_BINDKEY_SIZE)];
|
||||||
|
ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty_to(bindkey_hex, this->bindkey_, BTHOME_BINDKEY_SIZE, '.'));
|
||||||
|
}
|
||||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||||
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||||
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||||
@@ -150,6 +162,60 @@ bool BTHomeMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &dev
|
|||||||
return matched;
|
return matched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BTHomeMiThermometer::set_bindkey(std::initializer_list<uint8_t> bindkey) {
|
||||||
|
if (bindkey.size() != sizeof(this->bindkey_)) {
|
||||||
|
ESP_LOGW(TAG, "BTHome bindkey size mismatch: %zu", bindkey.size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::copy(bindkey.begin(), bindkey.end(), this->bindkey_);
|
||||||
|
this->has_bindkey_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BTHomeMiThermometer::decrypt_bthome_payload_(const std::vector<uint8_t> &data, uint64_t source_address,
|
||||||
|
std::vector<uint8_t> &payload) const {
|
||||||
|
if (data.size() <= 1 + BTHOME_COUNTER_SIZE + BTHOME_MIC_SIZE) {
|
||||||
|
ESP_LOGVV(TAG, "Encrypted BTHome payload too short: %zu", data.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t ciphertext_size = data.size() - 1 - BTHOME_COUNTER_SIZE - BTHOME_MIC_SIZE;
|
||||||
|
payload.resize(ciphertext_size);
|
||||||
|
|
||||||
|
std::array<uint8_t, MAC_ADDRESS_SIZE> mac{};
|
||||||
|
for (size_t i = 0; i < MAC_ADDRESS_SIZE; i++) {
|
||||||
|
mac[i] = (source_address >> ((MAC_ADDRESS_SIZE - 1 - i) * 8)) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint8_t, BTHOME_NONCE_SIZE> nonce{};
|
||||||
|
memcpy(nonce.data(), mac.data(), mac.size());
|
||||||
|
nonce[6] = 0xD2;
|
||||||
|
nonce[7] = 0xFC;
|
||||||
|
nonce[8] = data[0];
|
||||||
|
memcpy(nonce.data() + 9, &data[data.size() - BTHOME_COUNTER_SIZE - BTHOME_MIC_SIZE], BTHOME_COUNTER_SIZE);
|
||||||
|
|
||||||
|
const uint8_t *ciphertext = data.data() + 1;
|
||||||
|
const uint8_t *mic = data.data() + data.size() - BTHOME_MIC_SIZE;
|
||||||
|
|
||||||
|
mbedtls_ccm_context ctx;
|
||||||
|
mbedtls_ccm_init(&ctx);
|
||||||
|
|
||||||
|
int ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, this->bindkey_, BTHOME_BINDKEY_SIZE * 8);
|
||||||
|
if (ret) {
|
||||||
|
ESP_LOGVV(TAG, "mbedtls_ccm_setkey() failed.");
|
||||||
|
mbedtls_ccm_free(&ctx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mbedtls_ccm_auth_decrypt(&ctx, ciphertext_size, nonce.data(), nonce.size(), nullptr, 0, ciphertext,
|
||||||
|
payload.data(), mic, BTHOME_MIC_SIZE);
|
||||||
|
mbedtls_ccm_free(&ctx);
|
||||||
|
if (ret) {
|
||||||
|
ESP_LOGVV(TAG, "BTHome decryption failed (ret=%d).", ret);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool BTHomeMiThermometer::handle_service_data_(const esp32_ble_tracker::ServiceData &service_data,
|
bool BTHomeMiThermometer::handle_service_data_(const esp32_ble_tracker::ServiceData &service_data,
|
||||||
const esp32_ble_tracker::ESPBTDevice &device) {
|
const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
if (!service_data.uuid.contains(0xD2, 0xFC)) {
|
if (!service_data.uuid.contains(0xD2, 0xFC)) {
|
||||||
@@ -173,51 +239,88 @@ bool BTHomeMiThermometer::handle_service_data_(const esp32_ble_tracker::ServiceD
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t source_address = device.address_uint64();
|
||||||
|
bool address_matches = source_address == this->address_;
|
||||||
|
if (!is_encrypted && mac_included && data.size() >= 7) {
|
||||||
|
uint64_t advertised_address = 0;
|
||||||
|
for (int i = 5; i >= 0; i--) {
|
||||||
|
advertised_address = (advertised_address << 8) | data[1 + i];
|
||||||
|
}
|
||||||
|
address_matches = address_matches || advertised_address == this->address_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_encrypted && !this->has_bindkey_) {
|
||||||
|
if (address_matches) {
|
||||||
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||||
if (is_encrypted) {
|
ESP_LOGE(TAG, "Encrypted BTHome frame received but no bindkey configured for %s",
|
||||||
ESP_LOGV(TAG, "Ignoring encrypted BTHome frame from %s", device.address_str_to(addr_buf));
|
device.address_str_to(addr_buf));
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t payload_index = 1;
|
if (!is_encrypted && this->has_bindkey_) {
|
||||||
uint64_t source_address = device.address_uint64();
|
if (address_matches) {
|
||||||
|
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||||
|
ESP_LOGE(TAG, "Unencrypted BTHome frame received with bindkey configured for %s",
|
||||||
|
device.address_str_to(addr_buf));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> decrypted_payload;
|
||||||
|
const uint8_t *payload = nullptr;
|
||||||
|
size_t payload_size = 0;
|
||||||
|
|
||||||
|
if (is_encrypted) {
|
||||||
|
if (!this->decrypt_bthome_payload_(data, source_address, decrypted_payload)) {
|
||||||
|
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||||
|
ESP_LOGVV(TAG, "Failed to decrypt BTHome frame from %s", device.address_str_to(addr_buf));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
payload = decrypted_payload.data();
|
||||||
|
payload_size = decrypted_payload.size();
|
||||||
|
} else {
|
||||||
|
payload = data.data() + 1;
|
||||||
|
payload_size = data.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (mac_included) {
|
if (mac_included) {
|
||||||
if (data.size() < 7) {
|
if (payload_size < 6) {
|
||||||
ESP_LOGVV(TAG, "BTHome payload missing MAC address");
|
ESP_LOGVV(TAG, "BTHome payload missing MAC address");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
source_address = 0;
|
source_address = 0;
|
||||||
for (int i = 5; i >= 0; i--) {
|
for (int i = 5; i >= 0; i--) {
|
||||||
source_address = (source_address << 8) | data[1 + i];
|
source_address = (source_address << 8) | payload[i];
|
||||||
}
|
}
|
||||||
payload_index = 7;
|
payload += 6;
|
||||||
|
payload_size -= 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||||
if (source_address != this->address_) {
|
if (source_address != this->address_) {
|
||||||
ESP_LOGVV(TAG, "BTHome frame from unexpected device %s", format_mac_address(addr_buf, source_address));
|
ESP_LOGVV(TAG, "BTHome frame from unexpected device %s", format_mac_address(addr_buf, source_address));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload_index >= data.size()) {
|
if (payload_size == 0) {
|
||||||
ESP_LOGVV(TAG, "BTHome payload empty after header");
|
ESP_LOGVV(TAG, "BTHome payload empty after header");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool reported = false;
|
bool reported = false;
|
||||||
size_t offset = payload_index;
|
size_t offset = 0;
|
||||||
uint8_t last_type = 0;
|
uint8_t last_type = 0;
|
||||||
|
|
||||||
while (offset < data.size()) {
|
while (offset < payload_size) {
|
||||||
const uint8_t obj_type = data[offset++];
|
const uint8_t obj_type = payload[offset++];
|
||||||
size_t value_length = 0;
|
size_t value_length = 0;
|
||||||
bool has_length_byte = obj_type == 0x53; // text objects include explicit length
|
bool has_length_byte = obj_type == 0x53; // text objects include explicit length
|
||||||
|
|
||||||
if (has_length_byte) {
|
if (has_length_byte) {
|
||||||
if (offset >= data.size()) {
|
if (offset >= payload_size) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
value_length = data[offset++];
|
value_length = payload[offset++];
|
||||||
} else {
|
} else {
|
||||||
if (!get_bthome_value_length(obj_type, value_length)) {
|
if (!get_bthome_value_length(obj_type, value_length)) {
|
||||||
ESP_LOGVV(TAG, "Unknown BTHome object 0x%02X", obj_type);
|
ESP_LOGVV(TAG, "Unknown BTHome object 0x%02X", obj_type);
|
||||||
@@ -229,12 +332,12 @@ bool BTHomeMiThermometer::handle_service_data_(const esp32_ble_tracker::ServiceD
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset + value_length > data.size()) {
|
if (offset + value_length > payload_size) {
|
||||||
ESP_LOGVV(TAG, "BTHome object length exceeds payload");
|
ESP_LOGVV(TAG, "BTHome object length exceeds payload");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t *value = &data[offset];
|
const uint8_t *value = &payload[offset];
|
||||||
offset += value_length;
|
offset += value_length;
|
||||||
|
|
||||||
if (obj_type < last_type) {
|
if (obj_type < last_type) {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
@@ -14,6 +16,7 @@ namespace bthome_mithermometer {
|
|||||||
class BTHomeMiThermometer : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
|
class BTHomeMiThermometer : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
|
||||||
public:
|
public:
|
||||||
void set_address(uint64_t address) { this->address_ = address; }
|
void set_address(uint64_t address) { this->address_ = address; }
|
||||||
|
void set_bindkey(std::initializer_list<uint8_t> bindkey);
|
||||||
|
|
||||||
void set_temperature(sensor::Sensor *temperature) { this->temperature_ = temperature; }
|
void set_temperature(sensor::Sensor *temperature) { this->temperature_ = temperature; }
|
||||||
void set_humidity(sensor::Sensor *humidity) { this->humidity_ = humidity; }
|
void set_humidity(sensor::Sensor *humidity) { this->humidity_ = humidity; }
|
||||||
@@ -27,9 +30,13 @@ class BTHomeMiThermometer : public esp32_ble_tracker::ESPBTDeviceListener, publi
|
|||||||
protected:
|
protected:
|
||||||
bool handle_service_data_(const esp32_ble_tracker::ServiceData &service_data,
|
bool handle_service_data_(const esp32_ble_tracker::ServiceData &service_data,
|
||||||
const esp32_ble_tracker::ESPBTDevice &device);
|
const esp32_ble_tracker::ESPBTDevice &device);
|
||||||
|
bool decrypt_bthome_payload_(const std::vector<uint8_t> &data, uint64_t source_address,
|
||||||
|
std::vector<uint8_t> &payload) const;
|
||||||
|
|
||||||
uint64_t address_{0};
|
uint64_t address_{0};
|
||||||
optional<uint8_t> last_packet_id_{};
|
optional<uint8_t> last_packet_id_{};
|
||||||
|
bool has_bindkey_{false};
|
||||||
|
uint8_t bindkey_[16];
|
||||||
|
|
||||||
sensor::Sensor *temperature_{nullptr};
|
sensor::Sensor *temperature_{nullptr};
|
||||||
sensor::Sensor *humidity_{nullptr};
|
sensor::Sensor *humidity_{nullptr};
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
|
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
|
||||||
web_server_base.WebServerBase
|
web_server_base.WebServerBase
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_COMPRESSION, default="br"): cv.one_of("br", "gzip"),
|
cv.Optional(CONF_COMPRESSION, default="gzip"): cv.one_of("gzip", "br"),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
cv.only_on(
|
cv.only_on(
|
||||||
|
|||||||
@@ -152,6 +152,13 @@ void CC1101Component::setup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CC1101Component::call_listeners_(const std::vector<uint8_t> &packet, float freq_offset, float rssi, uint8_t lqi) {
|
||||||
|
for (auto &listener : this->listeners_) {
|
||||||
|
listener->on_packet(packet, freq_offset, rssi, lqi);
|
||||||
|
}
|
||||||
|
this->packet_trigger_->trigger(packet, freq_offset, rssi, lqi);
|
||||||
|
}
|
||||||
|
|
||||||
void CC1101Component::loop() {
|
void CC1101Component::loop() {
|
||||||
if (this->state_.PKT_FORMAT != static_cast<uint8_t>(PacketFormat::PACKET_FORMAT_FIFO) || this->gdo0_pin_ == nullptr ||
|
if (this->state_.PKT_FORMAT != static_cast<uint8_t>(PacketFormat::PACKET_FORMAT_FIFO) || this->gdo0_pin_ == nullptr ||
|
||||||
!this->gdo0_pin_->digital_read()) {
|
!this->gdo0_pin_->digital_read()) {
|
||||||
@@ -198,7 +205,7 @@ void CC1101Component::loop() {
|
|||||||
bool crc_ok = (this->state_.LQI & STATUS_CRC_OK_MASK) != 0;
|
bool crc_ok = (this->state_.LQI & STATUS_CRC_OK_MASK) != 0;
|
||||||
uint8_t lqi = this->state_.LQI & STATUS_LQI_MASK;
|
uint8_t lqi = this->state_.LQI & STATUS_LQI_MASK;
|
||||||
if (this->state_.CRC_EN == 0 || crc_ok) {
|
if (this->state_.CRC_EN == 0 || crc_ok) {
|
||||||
this->packet_trigger_->trigger(this->packet_, freq_offset, rssi, lqi);
|
this->call_listeners_(this->packet_, freq_offset, rssi, lqi);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return to rx
|
// Return to rx
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ namespace esphome::cc1101 {
|
|||||||
|
|
||||||
enum class CC1101Error { NONE = 0, TIMEOUT, PARAMS, CRC_ERROR, FIFO_OVERFLOW, PLL_LOCK };
|
enum class CC1101Error { NONE = 0, TIMEOUT, PARAMS, CRC_ERROR, FIFO_OVERFLOW, PLL_LOCK };
|
||||||
|
|
||||||
|
class CC1101Listener {
|
||||||
|
public:
|
||||||
|
virtual void on_packet(const std::vector<uint8_t> &packet, float freq_offset, float rssi, uint8_t lqi) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class CC1101Component : public Component,
|
class CC1101Component : public Component,
|
||||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||||
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
|
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
|
||||||
@@ -73,6 +78,7 @@ class CC1101Component : public Component,
|
|||||||
|
|
||||||
// Packet mode operations
|
// Packet mode operations
|
||||||
CC1101Error transmit_packet(const std::vector<uint8_t> &packet);
|
CC1101Error transmit_packet(const std::vector<uint8_t> &packet);
|
||||||
|
void register_listener(CC1101Listener *listener) { this->listeners_.push_back(listener); }
|
||||||
Trigger<std::vector<uint8_t>, float, float, uint8_t> *get_packet_trigger() const { return this->packet_trigger_; }
|
Trigger<std::vector<uint8_t>, float, float, uint8_t> *get_packet_trigger() const { return this->packet_trigger_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -89,9 +95,11 @@ class CC1101Component : public Component,
|
|||||||
InternalGPIOPin *gdo0_pin_{nullptr};
|
InternalGPIOPin *gdo0_pin_{nullptr};
|
||||||
|
|
||||||
// Packet handling
|
// Packet handling
|
||||||
|
void call_listeners_(const std::vector<uint8_t> &packet, float freq_offset, float rssi, uint8_t lqi);
|
||||||
Trigger<std::vector<uint8_t>, float, float, uint8_t> *packet_trigger_{
|
Trigger<std::vector<uint8_t>, float, float, uint8_t> *packet_trigger_{
|
||||||
new Trigger<std::vector<uint8_t>, float, float, uint8_t>()};
|
new Trigger<std::vector<uint8_t>, float, float, uint8_t>()};
|
||||||
std::vector<uint8_t> packet_;
|
std::vector<uint8_t> packet_;
|
||||||
|
std::vector<CC1101Listener *> listeners_;
|
||||||
|
|
||||||
// Low-level Helpers
|
// Low-level Helpers
|
||||||
uint8_t strobe_(Command cmd);
|
uint8_t strobe_(Command cmd);
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ void CCS811Component::setup() {
|
|||||||
bootloader_version, application_version);
|
bootloader_version, application_version);
|
||||||
if (this->version_ != nullptr) {
|
if (this->version_ != nullptr) {
|
||||||
char version[20]; // "15.15.15 (0xffff)" is 17 chars, plus NUL, plus wiggle room
|
char version[20]; // "15.15.15 (0xffff)" is 17 chars, plus NUL, plus wiggle room
|
||||||
sprintf(version, "%d.%d.%d (0x%02x)", (application_version >> 12 & 15), (application_version >> 8 & 15),
|
buf_append_printf(version, sizeof(version), 0, "%d.%d.%d (0x%02x)", (application_version >> 12 & 15),
|
||||||
(application_version >> 4 & 15), application_version);
|
(application_version >> 8 & 15), (application_version >> 4 & 15), application_version);
|
||||||
ESP_LOGD(TAG, "publishing version state: %s", version);
|
ESP_LOGD(TAG, "publishing version state: %s", version);
|
||||||
this->version_->publish_state(version);
|
this->version_->publish_state(version);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pi
|
|||||||
|
|
||||||
void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value ^ this->inverted_); }
|
void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value ^ this->inverted_); }
|
||||||
size_t CH422GGPIOPin::dump_summary(char *buffer, size_t len) const {
|
size_t CH422GGPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||||
return snprintf(buffer, len, "EXIO%u via CH422G", this->pin_);
|
return buf_append_printf(buffer, len, 0, "EXIO%u via CH422G", this->pin_);
|
||||||
}
|
}
|
||||||
void CH422GGPIOPin::set_flags(gpio::Flags flags) {
|
void CH422GGPIOPin::set_flags(gpio::Flags flags) {
|
||||||
flags_ = flags;
|
flags_ = flags;
|
||||||
|
|||||||
@@ -360,8 +360,7 @@ void Climate::add_on_control_callback(std::function<void(ClimateCall &)> &&callb
|
|||||||
static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL;
|
static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL;
|
||||||
|
|
||||||
optional<ClimateDeviceRestoreState> Climate::restore_state_() {
|
optional<ClimateDeviceRestoreState> Climate::restore_state_() {
|
||||||
this->rtc_ = global_preferences->make_preference<ClimateDeviceRestoreState>(this->get_preference_hash() ^
|
this->rtc_ = this->make_entity_preference<ClimateDeviceRestoreState>(RESTORE_STATE_VERSION);
|
||||||
RESTORE_STATE_VERSION);
|
|
||||||
ClimateDeviceRestoreState recovered{};
|
ClimateDeviceRestoreState recovered{};
|
||||||
if (!this->rtc_.load(&recovered))
|
if (!this->rtc_.load(&recovered))
|
||||||
return {};
|
return {};
|
||||||
@@ -436,7 +435,7 @@ void Climate::save_state_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Climate::publish_state() {
|
void Climate::publish_state() {
|
||||||
ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str());
|
ESP_LOGD(TAG, "'%s' >>", this->name_.c_str());
|
||||||
auto traits = this->get_traits();
|
auto traits = this->get_traits();
|
||||||
|
|
||||||
ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode)));
|
ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode)));
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#include "cover.h"
|
#include "cover.h"
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
#include "esphome/core/controller_registry.h"
|
#include "esphome/core/controller_registry.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/progmem.h"
|
||||||
|
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
namespace esphome::cover {
|
namespace esphome::cover {
|
||||||
|
|
||||||
static const char *const TAG = "cover";
|
static const char *const TAG = "cover";
|
||||||
@@ -39,13 +39,13 @@ Cover::Cover() : position{COVER_OPEN} {}
|
|||||||
|
|
||||||
CoverCall::CoverCall(Cover *parent) : parent_(parent) {}
|
CoverCall::CoverCall(Cover *parent) : parent_(parent) {}
|
||||||
CoverCall &CoverCall::set_command(const char *command) {
|
CoverCall &CoverCall::set_command(const char *command) {
|
||||||
if (strcasecmp(command, "OPEN") == 0) {
|
if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("OPEN")) == 0) {
|
||||||
this->set_command_open();
|
this->set_command_open();
|
||||||
} else if (strcasecmp(command, "CLOSE") == 0) {
|
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("CLOSE")) == 0) {
|
||||||
this->set_command_close();
|
this->set_command_close();
|
||||||
} else if (strcasecmp(command, "STOP") == 0) {
|
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("STOP")) == 0) {
|
||||||
this->set_command_stop();
|
this->set_command_stop();
|
||||||
} else if (strcasecmp(command, "TOGGLE") == 0) {
|
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("TOGGLE")) == 0) {
|
||||||
this->set_command_toggle();
|
this->set_command_toggle();
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
|
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
|
||||||
@@ -153,7 +153,7 @@ void Cover::publish_state(bool save) {
|
|||||||
this->position = clamp(this->position, 0.0f, 1.0f);
|
this->position = clamp(this->position, 0.0f, 1.0f);
|
||||||
this->tilt = clamp(this->tilt, 0.0f, 1.0f);
|
this->tilt = clamp(this->tilt, 0.0f, 1.0f);
|
||||||
|
|
||||||
ESP_LOGD(TAG, "'%s' - Publishing:", this->name_.c_str());
|
ESP_LOGD(TAG, "'%s' >>", this->name_.c_str());
|
||||||
auto traits = this->get_traits();
|
auto traits = this->get_traits();
|
||||||
if (traits.get_supports_position()) {
|
if (traits.get_supports_position()) {
|
||||||
ESP_LOGD(TAG, " Position: %.0f%%", this->position * 100.0f);
|
ESP_LOGD(TAG, " Position: %.0f%%", this->position * 100.0f);
|
||||||
@@ -187,7 +187,7 @@ void Cover::publish_state(bool save) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
optional<CoverRestoreState> Cover::restore_state_() {
|
optional<CoverRestoreState> Cover::restore_state_() {
|
||||||
this->rtc_ = global_preferences->make_preference<CoverRestoreState>(this->get_preference_hash());
|
this->rtc_ = this->make_entity_preference<CoverRestoreState>();
|
||||||
CoverRestoreState recovered{};
|
CoverRestoreState recovered{};
|
||||||
if (!this->rtc_.load(&recovered))
|
if (!this->rtc_.load(&recovered))
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ class CS5460AComponent : public Component,
|
|||||||
void restart() { restart_(); }
|
void restart() { restart_(); }
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void loop() override {}
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -207,20 +207,24 @@ void CSE7766Component::parse_data_() {
|
|||||||
|
|
||||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||||
{
|
{
|
||||||
std::string buf = "Parsed:";
|
// Buffer: 7 + 15 + 33 + 15 + 25 = 95 chars max + null, rounded to 128 for safety margin.
|
||||||
|
// Float sizes with %.4f can be up to 11 chars for large values (e.g., 999999.9999).
|
||||||
|
char buf[128];
|
||||||
|
size_t pos = buf_append_printf(buf, sizeof(buf), 0, "Parsed:");
|
||||||
if (have_voltage) {
|
if (have_voltage) {
|
||||||
buf += str_sprintf(" V=%fV", voltage);
|
pos = buf_append_printf(buf, sizeof(buf), pos, " V=%.4fV", voltage);
|
||||||
}
|
}
|
||||||
if (have_current) {
|
if (have_current) {
|
||||||
buf += str_sprintf(" I=%fmA (~%fmA)", current * 1000.0f, calculated_current * 1000.0f);
|
pos = buf_append_printf(buf, sizeof(buf), pos, " I=%.4fmA (~%.4fmA)", current * 1000.0f,
|
||||||
|
calculated_current * 1000.0f);
|
||||||
}
|
}
|
||||||
if (have_power) {
|
if (have_power) {
|
||||||
buf += str_sprintf(" P=%fW", power);
|
pos = buf_append_printf(buf, sizeof(buf), pos, " P=%.4fW", power);
|
||||||
}
|
}
|
||||||
if (energy != 0.0f) {
|
if (energy != 0.0f) {
|
||||||
buf += str_sprintf(" E=%fkWh (%u)", energy, cf_pulses);
|
buf_append_printf(buf, sizeof(buf), pos, " E=%.4fkWh (%u)", energy, cf_pulses);
|
||||||
}
|
}
|
||||||
ESP_LOGVV(TAG, "%s", buf.c_str());
|
ESP_LOGVV(TAG, "%s", buf);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,8 +258,9 @@ bool DaikinArcClimate::parse_state_frame_(const uint8_t frame[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char buf[DAIKIN_STATE_FRAME_SIZE * 3 + 1] = {0};
|
char buf[DAIKIN_STATE_FRAME_SIZE * 3 + 1] = {0};
|
||||||
|
size_t pos = 0;
|
||||||
for (size_t i = 0; i < DAIKIN_STATE_FRAME_SIZE; i++) {
|
for (size_t i = 0; i < DAIKIN_STATE_FRAME_SIZE; i++) {
|
||||||
sprintf(buf, "%s%02x ", buf, frame[i]);
|
pos = buf_append_printf(buf, sizeof(buf), pos, "%02x ", frame[i]);
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG, "FRAME %s", buf);
|
ESP_LOGD(TAG, "FRAME %s", buf);
|
||||||
|
|
||||||
@@ -349,8 +350,9 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
|||||||
if (data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
|
if (data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
|
||||||
valid_daikin_frame = true;
|
valid_daikin_frame = true;
|
||||||
size_t bytes_count = data.size() / 2 / 8;
|
size_t bytes_count = data.size() / 2 / 8;
|
||||||
std::unique_ptr<char[]> buf(new char[bytes_count * 3 + 1]);
|
size_t buf_size = bytes_count * 3 + 1;
|
||||||
buf[0] = '\0';
|
std::unique_ptr<char[]> buf(new char[buf_size]()); // value-initialize (zero-fill)
|
||||||
|
size_t buf_pos = 0;
|
||||||
for (size_t i = 0; i < bytes_count; i++) {
|
for (size_t i = 0; i < bytes_count; i++) {
|
||||||
uint8_t byte = 0;
|
uint8_t byte = 0;
|
||||||
for (int8_t bit = 0; bit < 8; bit++) {
|
for (int8_t bit = 0; bit < 8; bit++) {
|
||||||
@@ -361,19 +363,19 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sprintf(buf.get(), "%s%02x ", buf.get(), byte);
|
buf_pos = buf_append_printf(buf.get(), buf_size, buf_pos, "%02x ", byte);
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG, "WHOLE FRAME %s size: %d", buf.get(), data.size());
|
ESP_LOGD(TAG, "WHOLE FRAME %s size: %d", buf.get(), data.size());
|
||||||
}
|
}
|
||||||
if (!valid_daikin_frame) {
|
if (!valid_daikin_frame) {
|
||||||
char sbuf[16 * 10 + 1];
|
char sbuf[16 * 10 + 1] = {0};
|
||||||
sbuf[0] = '\0';
|
size_t sbuf_pos = 0;
|
||||||
for (size_t j = 0; j < static_cast<size_t>(data.size()); j++) {
|
for (size_t j = 0; j < static_cast<size_t>(data.size()); j++) {
|
||||||
if ((j - 2) % 16 == 0) {
|
if ((j - 2) % 16 == 0) {
|
||||||
if (j > 0) {
|
if (j > 0) {
|
||||||
ESP_LOGD(TAG, "DATA %04x: %s", (j - 16 > 0xffff ? 0 : j - 16), sbuf);
|
ESP_LOGD(TAG, "DATA %04x: %s", (j - 16 > 0xffff ? 0 : j - 16), sbuf);
|
||||||
}
|
}
|
||||||
sbuf[0] = '\0';
|
sbuf_pos = 0;
|
||||||
}
|
}
|
||||||
char type_ch = ' ';
|
char type_ch = ' ';
|
||||||
// debug_tolerance = 25%
|
// debug_tolerance = 25%
|
||||||
@@ -401,9 +403,10 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
|||||||
type_ch = '0';
|
type_ch = '0';
|
||||||
|
|
||||||
if (abs(data[j]) > 100000) {
|
if (abs(data[j]) > 100000) {
|
||||||
sprintf(sbuf, "%s%-5d[%c] ", sbuf, data[j] > 0 ? 99999 : -99999, type_ch);
|
sbuf_pos = buf_append_printf(sbuf, sizeof(sbuf), sbuf_pos, "%-5d[%c] ", data[j] > 0 ? 99999 : -99999, type_ch);
|
||||||
} else {
|
} else {
|
||||||
sprintf(sbuf, "%s%-5d[%c] ", sbuf, (int) (round(data[j] / 10.) * 10), type_ch);
|
sbuf_pos =
|
||||||
|
buf_append_printf(sbuf, sizeof(sbuf), sbuf_pos, "%-5d[%c] ", (int) (round(data[j] / 10.) * 10), type_ch);
|
||||||
}
|
}
|
||||||
if (j + 1 == static_cast<size_t>(data.size())) {
|
if (j + 1 == static_cast<size_t>(data.size())) {
|
||||||
ESP_LOGD(TAG, "DATA %04x: %s", (j - 8 > 0xffff ? 0 : j - 8), sbuf);
|
ESP_LOGD(TAG, "DATA %04x: %s", (j - 8 > 0xffff ? 0 : j - 8), sbuf);
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ void DallasTemperatureSensor::update() {
|
|||||||
|
|
||||||
this->send_command_(DALLAS_COMMAND_START_CONVERSION);
|
this->send_command_(DALLAS_COMMAND_START_CONVERSION);
|
||||||
|
|
||||||
this->set_timeout(this->get_address_name(), this->millis_to_wait_for_conversion_(), [this] {
|
this->set_timeout(this->get_address_name().c_str(), this->millis_to_wait_for_conversion_(), [this] {
|
||||||
if (!this->read_scratch_pad_() || !this->check_scratch_pad_()) {
|
if (!this->read_scratch_pad_() || !this->check_scratch_pad_()) {
|
||||||
this->publish_state(NAN);
|
this->publish_state(NAN);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ void DateEntity::publish_state() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->set_has_state(true);
|
this->set_has_state(true);
|
||||||
ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
|
ESP_LOGD(TAG, "'%s' >> %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
|
||||||
this->state_callback_.call();
|
this->state_callback_.call();
|
||||||
#if defined(USE_DATETIME_DATE) && defined(USE_CONTROLLER_REGISTRY)
|
#if defined(USE_DATETIME_DATE) && defined(USE_CONTROLLER_REGISTRY)
|
||||||
ControllerRegistry::notify_date_update(this);
|
ControllerRegistry::notify_date_update(this);
|
||||||
@@ -106,9 +106,9 @@ DateCall &DateCall::set_date(uint16_t year, uint8_t month, uint8_t day) {
|
|||||||
|
|
||||||
DateCall &DateCall::set_date(ESPTime time) { return this->set_date(time.year, time.month, time.day_of_month); };
|
DateCall &DateCall::set_date(ESPTime time) { return this->set_date(time.year, time.month, time.day_of_month); };
|
||||||
|
|
||||||
DateCall &DateCall::set_date(const std::string &date) {
|
DateCall &DateCall::set_date(const char *date, size_t len) {
|
||||||
ESPTime val{};
|
ESPTime val{};
|
||||||
if (!ESPTime::strptime(date, val)) {
|
if (!ESPTime::strptime(date, len, val)) {
|
||||||
ESP_LOGE(TAG, "Could not convert the date string to an ESPTime object");
|
ESP_LOGE(TAG, "Could not convert the date string to an ESPTime object");
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,9 @@ class DateCall {
|
|||||||
void perform();
|
void perform();
|
||||||
DateCall &set_date(uint16_t year, uint8_t month, uint8_t day);
|
DateCall &set_date(uint16_t year, uint8_t month, uint8_t day);
|
||||||
DateCall &set_date(ESPTime time);
|
DateCall &set_date(ESPTime time);
|
||||||
DateCall &set_date(const std::string &date);
|
DateCall &set_date(const char *date, size_t len);
|
||||||
|
DateCall &set_date(const char *date) { return this->set_date(date, strlen(date)); }
|
||||||
|
DateCall &set_date(const std::string &date) { return this->set_date(date.c_str(), date.size()); }
|
||||||
|
|
||||||
DateCall &set_year(uint16_t year) {
|
DateCall &set_year(uint16_t year) {
|
||||||
this->year_ = year;
|
this->year_ = year;
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ void DateTimeEntity::publish_state() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->set_has_state(true);
|
this->set_has_state(true);
|
||||||
ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_,
|
ESP_LOGD(TAG, "'%s' >> %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_, this->month_,
|
||||||
this->month_, this->day_, this->hour_, this->minute_, this->second_);
|
this->day_, this->hour_, this->minute_, this->second_);
|
||||||
this->state_callback_.call();
|
this->state_callback_.call();
|
||||||
#if defined(USE_DATETIME_DATETIME) && defined(USE_CONTROLLER_REGISTRY)
|
#if defined(USE_DATETIME_DATETIME) && defined(USE_CONTROLLER_REGISTRY)
|
||||||
ControllerRegistry::notify_datetime_update(this);
|
ControllerRegistry::notify_datetime_update(this);
|
||||||
@@ -163,9 +163,9 @@ DateTimeCall &DateTimeCall::set_datetime(ESPTime datetime) {
|
|||||||
datetime.second);
|
datetime.second);
|
||||||
};
|
};
|
||||||
|
|
||||||
DateTimeCall &DateTimeCall::set_datetime(const std::string &datetime) {
|
DateTimeCall &DateTimeCall::set_datetime(const char *datetime, size_t len) {
|
||||||
ESPTime val{};
|
ESPTime val{};
|
||||||
if (!ESPTime::strptime(datetime, val)) {
|
if (!ESPTime::strptime(datetime, len, val)) {
|
||||||
ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object");
|
ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object");
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,11 @@ class DateTimeCall {
|
|||||||
void perform();
|
void perform();
|
||||||
DateTimeCall &set_datetime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);
|
DateTimeCall &set_datetime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);
|
||||||
DateTimeCall &set_datetime(ESPTime datetime);
|
DateTimeCall &set_datetime(ESPTime datetime);
|
||||||
DateTimeCall &set_datetime(const std::string &datetime);
|
DateTimeCall &set_datetime(const char *datetime, size_t len);
|
||||||
|
DateTimeCall &set_datetime(const char *datetime) { return this->set_datetime(datetime, strlen(datetime)); }
|
||||||
|
DateTimeCall &set_datetime(const std::string &datetime) {
|
||||||
|
return this->set_datetime(datetime.c_str(), datetime.size());
|
||||||
|
}
|
||||||
DateTimeCall &set_datetime(time_t epoch_seconds);
|
DateTimeCall &set_datetime(time_t epoch_seconds);
|
||||||
|
|
||||||
DateTimeCall &set_year(uint16_t year) {
|
DateTimeCall &set_year(uint16_t year) {
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ void TimeEntity::publish_state() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->set_has_state(true);
|
this->set_has_state(true);
|
||||||
ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_,
|
ESP_LOGD(TAG, "'%s' >> %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_, this->second_);
|
||||||
this->second_);
|
|
||||||
this->state_callback_.call();
|
this->state_callback_.call();
|
||||||
#if defined(USE_DATETIME_TIME) && defined(USE_CONTROLLER_REGISTRY)
|
#if defined(USE_DATETIME_TIME) && defined(USE_CONTROLLER_REGISTRY)
|
||||||
ControllerRegistry::notify_time_update(this);
|
ControllerRegistry::notify_time_update(this);
|
||||||
@@ -75,9 +74,9 @@ TimeCall &TimeCall::set_time(uint8_t hour, uint8_t minute, uint8_t second) {
|
|||||||
|
|
||||||
TimeCall &TimeCall::set_time(ESPTime time) { return this->set_time(time.hour, time.minute, time.second); };
|
TimeCall &TimeCall::set_time(ESPTime time) { return this->set_time(time.hour, time.minute, time.second); };
|
||||||
|
|
||||||
TimeCall &TimeCall::set_time(const std::string &time) {
|
TimeCall &TimeCall::set_time(const char *time, size_t len) {
|
||||||
ESPTime val{};
|
ESPTime val{};
|
||||||
if (!ESPTime::strptime(time, val)) {
|
if (!ESPTime::strptime(time, len, val)) {
|
||||||
ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object");
|
ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object");
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,9 @@ class TimeCall {
|
|||||||
void perform();
|
void perform();
|
||||||
TimeCall &set_time(uint8_t hour, uint8_t minute, uint8_t second);
|
TimeCall &set_time(uint8_t hour, uint8_t minute, uint8_t second);
|
||||||
TimeCall &set_time(ESPTime time);
|
TimeCall &set_time(ESPTime time);
|
||||||
TimeCall &set_time(const std::string &time);
|
TimeCall &set_time(const char *time, size_t len);
|
||||||
|
TimeCall &set_time(const char *time) { return this->set_time(time, strlen(time)); }
|
||||||
|
TimeCall &set_time(const std::string &time) { return this->set_time(time.c_str(), time.size()); }
|
||||||
|
|
||||||
TimeCall &set_hour(uint8_t hour) {
|
TimeCall &set_hour(uint8_t hour) {
|
||||||
this->hour_ = hour;
|
this->hour_ = hour;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ void DebugComponent::dump_config() {
|
|||||||
|
|
||||||
char device_info_buffer[DEVICE_INFO_BUFFER_SIZE];
|
char device_info_buffer[DEVICE_INFO_BUFFER_SIZE];
|
||||||
ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
|
ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
|
||||||
size_t pos = buf_append(device_info_buffer, DEVICE_INFO_BUFFER_SIZE, 0, "%s", ESPHOME_VERSION);
|
size_t pos = buf_append_printf(device_info_buffer, DEVICE_INFO_BUFFER_SIZE, 0, "%s", ESPHOME_VERSION);
|
||||||
|
|
||||||
this->free_heap_ = get_free_heap_();
|
this->free_heap_ = get_free_heap_();
|
||||||
ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
|
ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
|
||||||
|
|||||||
@@ -5,12 +5,6 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/macros.h"
|
#include "esphome/core/macros.h"
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <cstdarg>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <algorithm>
|
|
||||||
#ifdef USE_ESP8266
|
|
||||||
#include <pgmspace.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
@@ -25,40 +19,7 @@ namespace debug {
|
|||||||
static constexpr size_t DEVICE_INFO_BUFFER_SIZE = 256;
|
static constexpr size_t DEVICE_INFO_BUFFER_SIZE = 256;
|
||||||
static constexpr size_t RESET_REASON_BUFFER_SIZE = 128;
|
static constexpr size_t RESET_REASON_BUFFER_SIZE = 128;
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
// buf_append_printf is now provided by esphome/core/helpers.h
|
||||||
// ESP8266: Use vsnprintf_P to keep format strings in flash (PROGMEM)
|
|
||||||
// Format strings must be wrapped with PSTR() macro
|
|
||||||
inline size_t buf_append_p(char *buf, size_t size, size_t pos, PGM_P fmt, ...) {
|
|
||||||
if (pos >= size) {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
int written = vsnprintf_P(buf + pos, size - pos, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
if (written < 0) {
|
|
||||||
return pos; // encoding error
|
|
||||||
}
|
|
||||||
return std::min(pos + static_cast<size_t>(written), size);
|
|
||||||
}
|
|
||||||
#define buf_append(buf, size, pos, fmt, ...) buf_append_p(buf, size, pos, PSTR(fmt), ##__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
/// Safely append formatted string to buffer, returning new position (capped at size)
|
|
||||||
__attribute__((format(printf, 4, 5))) inline size_t buf_append(char *buf, size_t size, size_t pos, const char *fmt,
|
|
||||||
...) {
|
|
||||||
if (pos >= size) {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
int written = vsnprintf(buf + pos, size - pos, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
if (written < 0) {
|
|
||||||
return pos; // encoding error
|
|
||||||
}
|
|
||||||
return std::min(pos + static_cast<size_t>(written), size);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class DebugComponent : public PollingComponent {
|
class DebugComponent : public PollingComponent {
|
||||||
public:
|
public:
|
||||||
@@ -74,8 +35,11 @@ class DebugComponent : public PollingComponent {
|
|||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
void set_free_sensor(sensor::Sensor *free_sensor) { free_sensor_ = free_sensor; }
|
void set_free_sensor(sensor::Sensor *free_sensor) { free_sensor_ = free_sensor; }
|
||||||
void set_block_sensor(sensor::Sensor *block_sensor) { block_sensor_ = block_sensor; }
|
void set_block_sensor(sensor::Sensor *block_sensor) { block_sensor_ = block_sensor; }
|
||||||
#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
|
#if (defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)) || defined(USE_ESP32)
|
||||||
void set_fragmentation_sensor(sensor::Sensor *fragmentation_sensor) { fragmentation_sensor_ = fragmentation_sensor; }
|
void set_fragmentation_sensor(sensor::Sensor *fragmentation_sensor) { fragmentation_sensor_ = fragmentation_sensor; }
|
||||||
|
#endif
|
||||||
|
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||||
|
void set_min_free_sensor(sensor::Sensor *min_free_sensor) { min_free_sensor_ = min_free_sensor; }
|
||||||
#endif
|
#endif
|
||||||
void set_loop_time_sensor(sensor::Sensor *loop_time_sensor) { loop_time_sensor_ = loop_time_sensor; }
|
void set_loop_time_sensor(sensor::Sensor *loop_time_sensor) { loop_time_sensor_ = loop_time_sensor; }
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
@@ -97,8 +61,11 @@ class DebugComponent : public PollingComponent {
|
|||||||
|
|
||||||
sensor::Sensor *free_sensor_{nullptr};
|
sensor::Sensor *free_sensor_{nullptr};
|
||||||
sensor::Sensor *block_sensor_{nullptr};
|
sensor::Sensor *block_sensor_{nullptr};
|
||||||
#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
|
#if (defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)) || defined(USE_ESP32)
|
||||||
sensor::Sensor *fragmentation_sensor_{nullptr};
|
sensor::Sensor *fragmentation_sensor_{nullptr};
|
||||||
|
#endif
|
||||||
|
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||||
|
sensor::Sensor *min_free_sensor_{nullptr};
|
||||||
#endif
|
#endif
|
||||||
sensor::Sensor *loop_time_sensor_{nullptr};
|
sensor::Sensor *loop_time_sensor_{nullptr};
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
|||||||
uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT
|
uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT
|
||||||
uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT
|
uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT
|
||||||
ESP_LOGD(TAG, "Flash Chip: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode);
|
ESP_LOGD(TAG, "Flash Chip: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode);
|
||||||
pos = buf_append(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed,
|
pos = buf_append_printf(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed,
|
||||||
flash_mode);
|
flash_mode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -182,60 +182,71 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
|||||||
const char *model = ESPHOME_VARIANT;
|
const char *model = ESPHOME_VARIANT;
|
||||||
|
|
||||||
// Build features string
|
// Build features string
|
||||||
pos = buf_append(buf, size, pos, "|Chip: %s Features:", model);
|
pos = buf_append_printf(buf, size, pos, "|Chip: %s Features:", model);
|
||||||
bool first_feature = true;
|
bool first_feature = true;
|
||||||
for (const auto &feature : CHIP_FEATURES) {
|
for (const auto &feature : CHIP_FEATURES) {
|
||||||
if (info.features & feature.bit) {
|
if (info.features & feature.bit) {
|
||||||
pos = buf_append(buf, size, pos, "%s%s", first_feature ? "" : ", ", feature.name);
|
pos = buf_append_printf(buf, size, pos, "%s%s", first_feature ? "" : ", ", feature.name);
|
||||||
first_feature = false;
|
first_feature = false;
|
||||||
info.features &= ~feature.bit;
|
info.features &= ~feature.bit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (info.features != 0) {
|
if (info.features != 0) {
|
||||||
pos = buf_append(buf, size, pos, "%sOther:0x%" PRIx32, first_feature ? "" : ", ", info.features);
|
pos = buf_append_printf(buf, size, pos, "%sOther:0x%" PRIx32, first_feature ? "" : ", ", info.features);
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG, "Chip: Model=%s, Cores=%u, Revision=%u", model, info.cores, info.revision);
|
ESP_LOGD(TAG, "Chip: Model=%s, Cores=%u, Revision=%u", model, info.cores, info.revision);
|
||||||
pos = buf_append(buf, size, pos, " Cores:%u Revision:%u", info.cores, info.revision);
|
pos = buf_append_printf(buf, size, pos, " Cores:%u Revision:%u", info.cores, info.revision);
|
||||||
|
|
||||||
uint32_t cpu_freq_mhz = arch_get_cpu_freq_hz() / 1000000;
|
uint32_t cpu_freq_mhz = arch_get_cpu_freq_hz() / 1000000;
|
||||||
ESP_LOGD(TAG, "CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
|
ESP_LOGD(TAG, "CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
|
||||||
pos = buf_append(buf, size, pos, "|CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
|
pos = buf_append_printf(buf, size, pos, "|CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
|
||||||
|
|
||||||
// Framework detection
|
// Framework detection
|
||||||
#ifdef USE_ARDUINO
|
#ifdef USE_ARDUINO
|
||||||
ESP_LOGD(TAG, "Framework: Arduino");
|
ESP_LOGD(TAG, "Framework: Arduino");
|
||||||
pos = buf_append(buf, size, pos, "|Framework: Arduino");
|
pos = buf_append_printf(buf, size, pos, "|Framework: Arduino");
|
||||||
#elif defined(USE_ESP32)
|
#elif defined(USE_ESP32)
|
||||||
ESP_LOGD(TAG, "Framework: ESP-IDF");
|
ESP_LOGD(TAG, "Framework: ESP-IDF");
|
||||||
pos = buf_append(buf, size, pos, "|Framework: ESP-IDF");
|
pos = buf_append_printf(buf, size, pos, "|Framework: ESP-IDF");
|
||||||
#else
|
#else
|
||||||
ESP_LOGW(TAG, "Framework: UNKNOWN");
|
ESP_LOGW(TAG, "Framework: UNKNOWN");
|
||||||
pos = buf_append(buf, size, pos, "|Framework: UNKNOWN");
|
pos = buf_append_printf(buf, size, pos, "|Framework: UNKNOWN");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
|
ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
|
||||||
pos = buf_append(buf, size, pos, "|ESP-IDF: %s", esp_get_idf_version());
|
pos = buf_append_printf(buf, size, pos, "|ESP-IDF: %s", esp_get_idf_version());
|
||||||
|
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
get_mac_address_raw(mac);
|
get_mac_address_raw(mac);
|
||||||
ESP_LOGD(TAG, "EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
ESP_LOGD(TAG, "EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
pos = buf_append(buf, size, pos, "|EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4],
|
pos = buf_append_printf(buf, size, pos, "|EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3],
|
||||||
mac[5]);
|
mac[4], mac[5]);
|
||||||
|
|
||||||
char reason_buffer[RESET_REASON_BUFFER_SIZE];
|
char reason_buffer[RESET_REASON_BUFFER_SIZE];
|
||||||
const char *reset_reason = get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE>(reason_buffer));
|
const char *reset_reason = get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE>(reason_buffer));
|
||||||
pos = buf_append(buf, size, pos, "|Reset: %s", reset_reason);
|
pos = buf_append_printf(buf, size, pos, "|Reset: %s", reset_reason);
|
||||||
|
|
||||||
const char *wakeup_cause = get_wakeup_cause_(std::span<char, RESET_REASON_BUFFER_SIZE>(reason_buffer));
|
const char *wakeup_cause = get_wakeup_cause_(std::span<char, RESET_REASON_BUFFER_SIZE>(reason_buffer));
|
||||||
pos = buf_append(buf, size, pos, "|Wakeup: %s", wakeup_cause);
|
pos = buf_append_printf(buf, size, pos, "|Wakeup: %s", wakeup_cause);
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugComponent::update_platform_() {
|
void DebugComponent::update_platform_() {
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
|
uint32_t max_alloc = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
|
||||||
if (this->block_sensor_ != nullptr) {
|
if (this->block_sensor_ != nullptr) {
|
||||||
this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
|
this->block_sensor_->publish_state(max_alloc);
|
||||||
|
}
|
||||||
|
if (this->min_free_sensor_ != nullptr) {
|
||||||
|
this->min_free_sensor_->publish_state(heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL));
|
||||||
|
}
|
||||||
|
if (this->fragmentation_sensor_ != nullptr) {
|
||||||
|
uint32_t free_heap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||||
|
if (free_heap > 0) {
|
||||||
|
float fragmentation = 100.0f - (100.0f * max_alloc / free_heap);
|
||||||
|
this->fragmentation_sensor_->publish_state(fragmentation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this->psram_sensor_ != nullptr) {
|
if (this->psram_sensor_ != nullptr) {
|
||||||
this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
|
this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
|
||||||
|
|||||||
@@ -3,21 +3,80 @@
|
|||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include <Esp.h>
|
#include <Esp.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <user_interface.h>
|
||||||
|
|
||||||
|
// Global reset info struct populated by SDK at boot
|
||||||
|
extern struct rst_info resetInfo;
|
||||||
|
|
||||||
|
// Core version - either a string pointer or a version number to format as hex
|
||||||
|
extern uint32_t core_version;
|
||||||
|
extern const char *core_release;
|
||||||
|
}
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace debug {
|
namespace debug {
|
||||||
|
|
||||||
static const char *const TAG = "debug";
|
static const char *const TAG = "debug";
|
||||||
|
|
||||||
|
// Get reset reason string from reason code (no heap allocation)
|
||||||
|
// Returns LogString* pointing to flash (PROGMEM) on ESP8266
|
||||||
|
static const LogString *get_reset_reason_str(uint32_t reason) {
|
||||||
|
switch (reason) {
|
||||||
|
case REASON_DEFAULT_RST:
|
||||||
|
return LOG_STR("Power On");
|
||||||
|
case REASON_WDT_RST:
|
||||||
|
return LOG_STR("Hardware Watchdog");
|
||||||
|
case REASON_EXCEPTION_RST:
|
||||||
|
return LOG_STR("Exception");
|
||||||
|
case REASON_SOFT_WDT_RST:
|
||||||
|
return LOG_STR("Software Watchdog");
|
||||||
|
case REASON_SOFT_RESTART:
|
||||||
|
return LOG_STR("Software/System restart");
|
||||||
|
case REASON_DEEP_SLEEP_AWAKE:
|
||||||
|
return LOG_STR("Deep-Sleep Wake");
|
||||||
|
case REASON_EXT_SYS_RST:
|
||||||
|
return LOG_STR("External System");
|
||||||
|
default:
|
||||||
|
return LOG_STR("Unknown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size for core version hex buffer
|
||||||
|
static constexpr size_t CORE_VERSION_BUFFER_SIZE = 12;
|
||||||
|
|
||||||
|
// Get core version string (no heap allocation)
|
||||||
|
// Returns either core_release directly or formats core_version as hex into provided buffer
|
||||||
|
static const char *get_core_version_str(std::span<char, CORE_VERSION_BUFFER_SIZE> buffer) {
|
||||||
|
if (core_release != nullptr) {
|
||||||
|
return core_release;
|
||||||
|
}
|
||||||
|
snprintf_P(buffer.data(), CORE_VERSION_BUFFER_SIZE, PSTR("%08x"), core_version);
|
||||||
|
return buffer.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size for reset info buffer
|
||||||
|
static constexpr size_t RESET_INFO_BUFFER_SIZE = 200;
|
||||||
|
|
||||||
|
// Get detailed reset info string (no heap allocation)
|
||||||
|
// For watchdog/exception resets, includes detailed exception info
|
||||||
|
static const char *get_reset_info_str(std::span<char, RESET_INFO_BUFFER_SIZE> buffer, uint32_t reason) {
|
||||||
|
if (reason >= REASON_WDT_RST && reason <= REASON_SOFT_WDT_RST) {
|
||||||
|
snprintf_P(buffer.data(), RESET_INFO_BUFFER_SIZE,
|
||||||
|
PSTR("Fatal exception:%d flag:%d (%s) epc1:0x%08x epc2:0x%08x epc3:0x%08x excvaddr:0x%08x depc:0x%08x"),
|
||||||
|
static_cast<int>(resetInfo.exccause), static_cast<int>(reason),
|
||||||
|
LOG_STR_ARG(get_reset_reason_str(reason)), resetInfo.epc1, resetInfo.epc2, resetInfo.epc3,
|
||||||
|
resetInfo.excvaddr, resetInfo.depc);
|
||||||
|
return buffer.data();
|
||||||
|
}
|
||||||
|
return LOG_STR_ARG(get_reset_reason_str(reason));
|
||||||
|
}
|
||||||
|
|
||||||
const char *DebugComponent::get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
|
const char *DebugComponent::get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
|
||||||
char *buf = buffer.data();
|
// Copy from flash to provided buffer
|
||||||
#if !defined(CLANG_TIDY)
|
strncpy_P(buffer.data(), (PGM_P) get_reset_reason_str(resetInfo.reason), RESET_REASON_BUFFER_SIZE - 1);
|
||||||
String reason = ESP.getResetReason(); // NOLINT
|
buffer[RESET_REASON_BUFFER_SIZE - 1] = '\0';
|
||||||
snprintf_P(buf, RESET_REASON_BUFFER_SIZE, PSTR("%s"), reason.c_str());
|
return buffer.data();
|
||||||
return buf;
|
|
||||||
#else
|
|
||||||
buf[0] = '\0';
|
|
||||||
return buf;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *DebugComponent::get_wakeup_cause_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
|
const char *DebugComponent::get_wakeup_cause_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
|
||||||
@@ -33,37 +92,42 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
|||||||
constexpr size_t size = DEVICE_INFO_BUFFER_SIZE;
|
constexpr size_t size = DEVICE_INFO_BUFFER_SIZE;
|
||||||
char *buf = buffer.data();
|
char *buf = buffer.data();
|
||||||
|
|
||||||
const char *flash_mode;
|
const LogString *flash_mode;
|
||||||
switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
|
switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
|
||||||
case FM_QIO:
|
case FM_QIO:
|
||||||
flash_mode = "QIO";
|
flash_mode = LOG_STR("QIO");
|
||||||
break;
|
break;
|
||||||
case FM_QOUT:
|
case FM_QOUT:
|
||||||
flash_mode = "QOUT";
|
flash_mode = LOG_STR("QOUT");
|
||||||
break;
|
break;
|
||||||
case FM_DIO:
|
case FM_DIO:
|
||||||
flash_mode = "DIO";
|
flash_mode = LOG_STR("DIO");
|
||||||
break;
|
break;
|
||||||
case FM_DOUT:
|
case FM_DOUT:
|
||||||
flash_mode = "DOUT";
|
flash_mode = LOG_STR("DOUT");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
flash_mode = "UNKNOWN";
|
flash_mode = LOG_STR("UNKNOWN");
|
||||||
}
|
}
|
||||||
uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT
|
uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT(readability-static-accessed-through-instance)
|
||||||
uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT
|
uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT(readability-static-accessed-through-instance)
|
||||||
ESP_LOGD(TAG, "Flash Chip: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode);
|
ESP_LOGD(TAG, "Flash Chip: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed,
|
||||||
pos = buf_append(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed,
|
LOG_STR_ARG(flash_mode));
|
||||||
flash_mode);
|
pos = buf_append_printf(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed,
|
||||||
|
LOG_STR_ARG(flash_mode));
|
||||||
|
|
||||||
#if !defined(CLANG_TIDY)
|
|
||||||
char reason_buffer[RESET_REASON_BUFFER_SIZE];
|
char reason_buffer[RESET_REASON_BUFFER_SIZE];
|
||||||
const char *reset_reason = get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE>(reason_buffer));
|
const char *reset_reason = get_reset_reason_(reason_buffer);
|
||||||
|
char core_version_buffer[CORE_VERSION_BUFFER_SIZE];
|
||||||
|
char reset_info_buffer[RESET_INFO_BUFFER_SIZE];
|
||||||
|
// NOLINTBEGIN(readability-static-accessed-through-instance)
|
||||||
uint32_t chip_id = ESP.getChipId();
|
uint32_t chip_id = ESP.getChipId();
|
||||||
uint8_t boot_version = ESP.getBootVersion();
|
uint8_t boot_version = ESP.getBootVersion();
|
||||||
uint8_t boot_mode = ESP.getBootMode();
|
uint8_t boot_mode = ESP.getBootMode();
|
||||||
uint8_t cpu_freq = ESP.getCpuFreqMHz();
|
uint8_t cpu_freq = ESP.getCpuFreqMHz();
|
||||||
uint32_t flash_chip_id = ESP.getFlashChipId();
|
uint32_t flash_chip_id = ESP.getFlashChipId();
|
||||||
|
const char *sdk_version = ESP.getSdkVersion();
|
||||||
|
// NOLINTEND(readability-static-accessed-through-instance)
|
||||||
|
|
||||||
ESP_LOGD(TAG,
|
ESP_LOGD(TAG,
|
||||||
"Chip ID: 0x%08" PRIX32 "\n"
|
"Chip ID: 0x%08" PRIX32 "\n"
|
||||||
@@ -74,19 +138,18 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
|||||||
"Flash Chip ID=0x%08" PRIX32 "\n"
|
"Flash Chip ID=0x%08" PRIX32 "\n"
|
||||||
"Reset Reason: %s\n"
|
"Reset Reason: %s\n"
|
||||||
"Reset Info: %s",
|
"Reset Info: %s",
|
||||||
chip_id, ESP.getSdkVersion(), ESP.getCoreVersion().c_str(), boot_version, boot_mode, cpu_freq, flash_chip_id,
|
chip_id, sdk_version, get_core_version_str(core_version_buffer), boot_version, boot_mode, cpu_freq,
|
||||||
reset_reason, ESP.getResetInfo().c_str());
|
flash_chip_id, reset_reason, get_reset_info_str(reset_info_buffer, resetInfo.reason));
|
||||||
|
|
||||||
pos = buf_append(buf, size, pos, "|Chip: 0x%08" PRIX32, chip_id);
|
pos = buf_append_printf(buf, size, pos, "|Chip: 0x%08" PRIX32, chip_id);
|
||||||
pos = buf_append(buf, size, pos, "|SDK: %s", ESP.getSdkVersion());
|
pos = buf_append_printf(buf, size, pos, "|SDK: %s", sdk_version);
|
||||||
pos = buf_append(buf, size, pos, "|Core: %s", ESP.getCoreVersion().c_str());
|
pos = buf_append_printf(buf, size, pos, "|Core: %s", get_core_version_str(core_version_buffer));
|
||||||
pos = buf_append(buf, size, pos, "|Boot: %u", boot_version);
|
pos = buf_append_printf(buf, size, pos, "|Boot: %u", boot_version);
|
||||||
pos = buf_append(buf, size, pos, "|Mode: %u", boot_mode);
|
pos = buf_append_printf(buf, size, pos, "|Mode: %u", boot_mode);
|
||||||
pos = buf_append(buf, size, pos, "|CPU: %u", cpu_freq);
|
pos = buf_append_printf(buf, size, pos, "|CPU: %u", cpu_freq);
|
||||||
pos = buf_append(buf, size, pos, "|Flash: 0x%08" PRIX32, flash_chip_id);
|
pos = buf_append_printf(buf, size, pos, "|Flash: 0x%08" PRIX32, flash_chip_id);
|
||||||
pos = buf_append(buf, size, pos, "|Reset: %s", reset_reason);
|
pos = buf_append_printf(buf, size, pos, "|Reset: %s", reset_reason);
|
||||||
pos = buf_append(buf, size, pos, "|%s", ESP.getResetInfo().c_str());
|
pos = buf_append_printf(buf, size, pos, "|%s", get_reset_info_str(reset_info_buffer, resetInfo.reason));
|
||||||
#endif
|
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,12 +36,12 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
|||||||
lt_get_version(), lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz(), mac_id,
|
lt_get_version(), lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz(), mac_id,
|
||||||
lt_get_board_code(), flash_kib, ram_kib, reset_reason);
|
lt_get_board_code(), flash_kib, ram_kib, reset_reason);
|
||||||
|
|
||||||
pos = buf_append(buf, size, pos, "|Version: %s", LT_BANNER_STR + 10);
|
pos = buf_append_printf(buf, size, pos, "|Version: %s", LT_BANNER_STR + 10);
|
||||||
pos = buf_append(buf, size, pos, "|Reset Reason: %s", reset_reason);
|
pos = buf_append_printf(buf, size, pos, "|Reset Reason: %s", reset_reason);
|
||||||
pos = buf_append(buf, size, pos, "|Chip Name: %s", lt_cpu_get_model_name());
|
pos = buf_append_printf(buf, size, pos, "|Chip Name: %s", lt_cpu_get_model_name());
|
||||||
pos = buf_append(buf, size, pos, "|Chip ID: 0x%06" PRIX32, mac_id);
|
pos = buf_append_printf(buf, size, pos, "|Chip ID: 0x%06" PRIX32, mac_id);
|
||||||
pos = buf_append(buf, size, pos, "|Flash: %" PRIu32 " KiB", flash_kib);
|
pos = buf_append_printf(buf, size, pos, "|Flash: %" PRIu32 " KiB", flash_kib);
|
||||||
pos = buf_append(buf, size, pos, "|RAM: %" PRIu32 " KiB", ram_kib);
|
pos = buf_append_printf(buf, size, pos, "|RAM: %" PRIu32 " KiB", ram_kib);
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
@@ -51,6 +51,9 @@ void DebugComponent::update_platform_() {
|
|||||||
if (this->block_sensor_ != nullptr) {
|
if (this->block_sensor_ != nullptr) {
|
||||||
this->block_sensor_->publish_state(lt_heap_get_max_alloc());
|
this->block_sensor_->publish_state(lt_heap_get_max_alloc());
|
||||||
}
|
}
|
||||||
|
if (this->min_free_sensor_ != nullptr) {
|
||||||
|
this->min_free_sensor_->publish_state(lt_heap_get_min_free());
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
|||||||
|
|
||||||
uint32_t cpu_freq = rp2040.f_cpu();
|
uint32_t cpu_freq = rp2040.f_cpu();
|
||||||
ESP_LOGD(TAG, "CPU Frequency: %" PRIu32, cpu_freq);
|
ESP_LOGD(TAG, "CPU Frequency: %" PRIu32, cpu_freq);
|
||||||
pos = buf_append(buf, size, pos, "|CPU Frequency: %" PRIu32, cpu_freq);
|
pos = buf_append_printf(buf, size, pos, "|CPU Frequency: %" PRIu32, cpu_freq);
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ static size_t append_reset_reason(char *buf, size_t size, size_t pos, bool set,
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
if (pos > 0) {
|
if (pos > 0) {
|
||||||
pos = buf_append(buf, size, pos, ", ");
|
pos = buf_append_printf(buf, size, pos, ", ");
|
||||||
}
|
}
|
||||||
return buf_append(buf, size, pos, "%s", reason);
|
return buf_append_printf(buf, size, pos, "%s", reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint32_t read_mem_u32(uintptr_t addr) {
|
static inline uint32_t read_mem_u32(uintptr_t addr) {
|
||||||
@@ -132,6 +132,26 @@ void DebugComponent::log_partition_info_() {
|
|||||||
flash_area_foreach(fa_cb, nullptr);
|
flash_area_foreach(fa_cb, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *regout0_to_str(uint32_t value) {
|
||||||
|
switch (value) {
|
||||||
|
case (UICR_REGOUT0_VOUT_DEFAULT):
|
||||||
|
return "1.8V (default)";
|
||||||
|
case (UICR_REGOUT0_VOUT_1V8):
|
||||||
|
return "1.8V";
|
||||||
|
case (UICR_REGOUT0_VOUT_2V1):
|
||||||
|
return "2.1V";
|
||||||
|
case (UICR_REGOUT0_VOUT_2V4):
|
||||||
|
return "2.4V";
|
||||||
|
case (UICR_REGOUT0_VOUT_2V7):
|
||||||
|
return "2.7V";
|
||||||
|
case (UICR_REGOUT0_VOUT_3V0):
|
||||||
|
return "3.0V";
|
||||||
|
case (UICR_REGOUT0_VOUT_3V3):
|
||||||
|
return "3.3V";
|
||||||
|
}
|
||||||
|
return "???V";
|
||||||
|
}
|
||||||
|
|
||||||
size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE> buffer, size_t pos) {
|
size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE> buffer, size_t pos) {
|
||||||
constexpr size_t size = DEVICE_INFO_BUFFER_SIZE;
|
constexpr size_t size = DEVICE_INFO_BUFFER_SIZE;
|
||||||
char *buf = buffer.data();
|
char *buf = buffer.data();
|
||||||
@@ -140,48 +160,28 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
|||||||
const char *supply_status =
|
const char *supply_status =
|
||||||
(nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_NORMAL) ? "Normal voltage." : "High voltage.";
|
(nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_NORMAL) ? "Normal voltage." : "High voltage.";
|
||||||
ESP_LOGD(TAG, "Main supply status: %s", supply_status);
|
ESP_LOGD(TAG, "Main supply status: %s", supply_status);
|
||||||
pos = buf_append(buf, size, pos, "|Main supply status: %s", supply_status);
|
pos = buf_append_printf(buf, size, pos, "|Main supply status: %s", supply_status);
|
||||||
|
|
||||||
// Regulator stage 0
|
// Regulator stage 0
|
||||||
if (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_HIGH) {
|
if (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_HIGH) {
|
||||||
const char *reg0_type = nrf_power_dcdcen_vddh_get(NRF_POWER) ? "DC/DC" : "LDO";
|
const char *reg0_type = nrf_power_dcdcen_vddh_get(NRF_POWER) ? "DC/DC" : "LDO";
|
||||||
const char *reg0_voltage;
|
const char *reg0_voltage = regout0_to_str((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) >> UICR_REGOUT0_VOUT_Pos);
|
||||||
switch (NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) {
|
|
||||||
case (UICR_REGOUT0_VOUT_DEFAULT << UICR_REGOUT0_VOUT_Pos):
|
|
||||||
reg0_voltage = "1.8V (default)";
|
|
||||||
break;
|
|
||||||
case (UICR_REGOUT0_VOUT_1V8 << UICR_REGOUT0_VOUT_Pos):
|
|
||||||
reg0_voltage = "1.8V";
|
|
||||||
break;
|
|
||||||
case (UICR_REGOUT0_VOUT_2V1 << UICR_REGOUT0_VOUT_Pos):
|
|
||||||
reg0_voltage = "2.1V";
|
|
||||||
break;
|
|
||||||
case (UICR_REGOUT0_VOUT_2V4 << UICR_REGOUT0_VOUT_Pos):
|
|
||||||
reg0_voltage = "2.4V";
|
|
||||||
break;
|
|
||||||
case (UICR_REGOUT0_VOUT_2V7 << UICR_REGOUT0_VOUT_Pos):
|
|
||||||
reg0_voltage = "2.7V";
|
|
||||||
break;
|
|
||||||
case (UICR_REGOUT0_VOUT_3V0 << UICR_REGOUT0_VOUT_Pos):
|
|
||||||
reg0_voltage = "3.0V";
|
|
||||||
break;
|
|
||||||
case (UICR_REGOUT0_VOUT_3V3 << UICR_REGOUT0_VOUT_Pos):
|
|
||||||
reg0_voltage = "3.3V";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
reg0_voltage = "???V";
|
|
||||||
}
|
|
||||||
ESP_LOGD(TAG, "Regulator stage 0: %s, %s", reg0_type, reg0_voltage);
|
ESP_LOGD(TAG, "Regulator stage 0: %s, %s", reg0_type, reg0_voltage);
|
||||||
pos = buf_append(buf, size, pos, "|Regulator stage 0: %s, %s", reg0_type, reg0_voltage);
|
pos = buf_append_printf(buf, size, pos, "|Regulator stage 0: %s, %s", reg0_type, reg0_voltage);
|
||||||
|
#ifdef USE_NRF52_REG0_VOUT
|
||||||
|
if ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) >> UICR_REGOUT0_VOUT_Pos != USE_NRF52_REG0_VOUT) {
|
||||||
|
ESP_LOGE(TAG, "Regulator stage 0: expected %s", regout0_to_str(USE_NRF52_REG0_VOUT));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGD(TAG, "Regulator stage 0: disabled");
|
ESP_LOGD(TAG, "Regulator stage 0: disabled");
|
||||||
pos = buf_append(buf, size, pos, "|Regulator stage 0: disabled");
|
pos = buf_append_printf(buf, size, pos, "|Regulator stage 0: disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regulator stage 1
|
// Regulator stage 1
|
||||||
const char *reg1_type = nrf_power_dcdcen_get(NRF_POWER) ? "DC/DC" : "LDO";
|
const char *reg1_type = nrf_power_dcdcen_get(NRF_POWER) ? "DC/DC" : "LDO";
|
||||||
ESP_LOGD(TAG, "Regulator stage 1: %s", reg1_type);
|
ESP_LOGD(TAG, "Regulator stage 1: %s", reg1_type);
|
||||||
pos = buf_append(buf, size, pos, "|Regulator stage 1: %s", reg1_type);
|
pos = buf_append_printf(buf, size, pos, "|Regulator stage 1: %s", reg1_type);
|
||||||
|
|
||||||
// USB power state
|
// USB power state
|
||||||
const char *usb_state;
|
const char *usb_state;
|
||||||
@@ -195,7 +195,7 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
|||||||
usb_state = "disconnected";
|
usb_state = "disconnected";
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG, "USB power state: %s", usb_state);
|
ESP_LOGD(TAG, "USB power state: %s", usb_state);
|
||||||
pos = buf_append(buf, size, pos, "|USB power state: %s", usb_state);
|
pos = buf_append_printf(buf, size, pos, "|USB power state: %s", usb_state);
|
||||||
|
|
||||||
// Power-fail comparator
|
// Power-fail comparator
|
||||||
bool enabled;
|
bool enabled;
|
||||||
@@ -300,14 +300,14 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG, "Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage);
|
ESP_LOGD(TAG, "Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage);
|
||||||
pos = buf_append(buf, size, pos, "|Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage);
|
pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: %s, VDDH: %s", pof_voltage, vddh_voltage);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGD(TAG, "Power-fail comparator: %s", pof_voltage);
|
ESP_LOGD(TAG, "Power-fail comparator: %s", pof_voltage);
|
||||||
pos = buf_append(buf, size, pos, "|Power-fail comparator: %s", pof_voltage);
|
pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: %s", pof_voltage);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGD(TAG, "Power-fail comparator: disabled");
|
ESP_LOGD(TAG, "Power-fail comparator: disabled");
|
||||||
pos = buf_append(buf, size, pos, "|Power-fail comparator: disabled");
|
pos = buf_append_printf(buf, size, pos, "|Power-fail comparator: disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto package = [](uint32_t value) {
|
auto package = [](uint32_t value) {
|
||||||
|
|||||||
@@ -11,16 +11,24 @@ from esphome.const import (
|
|||||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
ICON_COUNTER,
|
ICON_COUNTER,
|
||||||
ICON_TIMER,
|
ICON_TIMER,
|
||||||
|
PLATFORM_BK72XX,
|
||||||
|
PLATFORM_LN882X,
|
||||||
|
PLATFORM_RTL87XX,
|
||||||
UNIT_BYTES,
|
UNIT_BYTES,
|
||||||
UNIT_HERTZ,
|
UNIT_HERTZ,
|
||||||
UNIT_MILLISECOND,
|
UNIT_MILLISECOND,
|
||||||
UNIT_PERCENT,
|
UNIT_PERCENT,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import CONF_DEBUG_ID, DebugComponent
|
from . import ( # noqa: F401 pylint: disable=unused-import
|
||||||
|
CONF_DEBUG_ID,
|
||||||
|
FILTER_SOURCE_FILES,
|
||||||
|
DebugComponent,
|
||||||
|
)
|
||||||
|
|
||||||
DEPENDENCIES = ["debug"]
|
DEPENDENCIES = ["debug"]
|
||||||
|
|
||||||
|
CONF_MIN_FREE = "min_free"
|
||||||
CONF_PSRAM = "psram"
|
CONF_PSRAM = "psram"
|
||||||
|
|
||||||
CONFIG_SCHEMA = {
|
CONFIG_SCHEMA = {
|
||||||
@@ -38,8 +46,14 @@ CONFIG_SCHEMA = {
|
|||||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_FRAGMENTATION): cv.All(
|
cv.Optional(CONF_FRAGMENTATION): cv.All(
|
||||||
|
cv.Any(
|
||||||
|
cv.All(
|
||||||
cv.only_on_esp8266,
|
cv.only_on_esp8266,
|
||||||
cv.require_framework_version(esp8266_arduino=cv.Version(2, 5, 2)),
|
cv.require_framework_version(esp8266_arduino=cv.Version(2, 5, 2)),
|
||||||
|
),
|
||||||
|
cv.only_on_esp32,
|
||||||
|
msg="This feature is only available on ESP8266 (Arduino 2.5.2+) and ESP32",
|
||||||
|
),
|
||||||
sensor.sensor_schema(
|
sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_PERCENT,
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
icon=ICON_COUNTER,
|
icon=ICON_COUNTER,
|
||||||
@@ -47,6 +61,19 @@ CONFIG_SCHEMA = {
|
|||||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_MIN_FREE): cv.All(
|
||||||
|
cv.Any(
|
||||||
|
cv.only_on_esp32,
|
||||||
|
cv.only_on([PLATFORM_BK72XX, PLATFORM_LN882X, PLATFORM_RTL87XX]),
|
||||||
|
msg="This feature is only available on ESP32 and LibreTiny (BK72xx, LN882x, RTL87xx)",
|
||||||
|
),
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_BYTES,
|
||||||
|
icon=ICON_COUNTER,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
),
|
||||||
cv.Optional(CONF_LOOP_TIME): sensor.sensor_schema(
|
cv.Optional(CONF_LOOP_TIME): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_MILLISECOND,
|
unit_of_measurement=UNIT_MILLISECOND,
|
||||||
icon=ICON_TIMER,
|
icon=ICON_TIMER,
|
||||||
@@ -89,6 +116,10 @@ async def to_code(config):
|
|||||||
sens = await sensor.new_sensor(fragmentation_conf)
|
sens = await sensor.new_sensor(fragmentation_conf)
|
||||||
cg.add(debug_component.set_fragmentation_sensor(sens))
|
cg.add(debug_component.set_fragmentation_sensor(sens))
|
||||||
|
|
||||||
|
if min_free_conf := config.get(CONF_MIN_FREE):
|
||||||
|
sens = await sensor.new_sensor(min_free_conf)
|
||||||
|
cg.add(debug_component.set_min_free_sensor(sens))
|
||||||
|
|
||||||
if loop_time_conf := config.get(CONF_LOOP_TIME):
|
if loop_time_conf := config.get(CONF_LOOP_TIME):
|
||||||
sens = await sensor.new_sensor(loop_time_conf)
|
sens = await sensor.new_sensor(loop_time_conf)
|
||||||
cg.add(debug_component.set_loop_time_sensor(sens))
|
cg.add(debug_component.set_loop_time_sensor(sens))
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ from esphome.const import (
|
|||||||
ICON_RESTART,
|
ICON_RESTART,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import CONF_DEBUG_ID, DebugComponent
|
from . import ( # noqa: F401 pylint: disable=unused-import
|
||||||
|
CONF_DEBUG_ID,
|
||||||
|
FILTER_SOURCE_FILES,
|
||||||
|
DebugComponent,
|
||||||
|
)
|
||||||
|
|
||||||
DEPENDENCIES = ["debug"]
|
DEPENDENCIES = ["debug"]
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,9 @@ DetRangeCfgCommand::DetRangeCfgCommand(float min1, float max1, float min2, float
|
|||||||
this->min2_ = min2 = this->max2_ = max2 = this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 =
|
this->min2_ = min2 = this->max2_ = max2 = this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 =
|
||||||
this->max4_ = max4 = -1;
|
this->max4_ = max4 = -1;
|
||||||
|
|
||||||
this->cmd_ = str_sprintf("detRangeCfg -1 %.0f %.0f", min1 / 0.15, max1 / 0.15);
|
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||||
|
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f", min1 / 0.15, max1 / 0.15);
|
||||||
|
this->cmd_ = buf;
|
||||||
} else if (min3 < 0 || max3 < 0) {
|
} else if (min3 < 0 || max3 < 0) {
|
||||||
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
|
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
|
||||||
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
|
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
|
||||||
@@ -135,7 +137,10 @@ DetRangeCfgCommand::DetRangeCfgCommand(float min1, float max1, float min2, float
|
|||||||
this->max2_ = max2 = round(max2 / 0.15) * 0.15;
|
this->max2_ = max2 = round(max2 / 0.15) * 0.15;
|
||||||
this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 = this->max4_ = max4 = -1;
|
this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 = this->max4_ = max4 = -1;
|
||||||
|
|
||||||
this->cmd_ = str_sprintf("detRangeCfg -1 %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15, min2 / 0.15, max2 / 0.15);
|
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||||
|
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15, min2 / 0.15,
|
||||||
|
max2 / 0.15);
|
||||||
|
this->cmd_ = buf;
|
||||||
} else if (min4 < 0 || max4 < 0) {
|
} else if (min4 < 0 || max4 < 0) {
|
||||||
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
|
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
|
||||||
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
|
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
|
||||||
@@ -145,9 +150,10 @@ DetRangeCfgCommand::DetRangeCfgCommand(float min1, float max1, float min2, float
|
|||||||
this->max3_ = max3 = round(max3 / 0.15) * 0.15;
|
this->max3_ = max3 = round(max3 / 0.15) * 0.15;
|
||||||
this->min4_ = min4 = this->max4_ = max4 = -1;
|
this->min4_ = min4 = this->max4_ = max4 = -1;
|
||||||
|
|
||||||
this->cmd_ = str_sprintf("detRangeCfg -1 "
|
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||||
"%.0f %.0f %.0f %.0f %.0f %.0f",
|
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15, min2 / 0.15,
|
||||||
min1 / 0.15, max1 / 0.15, min2 / 0.15, max2 / 0.15, min3 / 0.15, max3 / 0.15);
|
max2 / 0.15, min3 / 0.15, max3 / 0.15);
|
||||||
|
this->cmd_ = buf;
|
||||||
} else {
|
} else {
|
||||||
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
|
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
|
||||||
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
|
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
|
||||||
@@ -158,10 +164,10 @@ DetRangeCfgCommand::DetRangeCfgCommand(float min1, float max1, float min2, float
|
|||||||
this->min4_ = min4 = round(min4 / 0.15) * 0.15;
|
this->min4_ = min4 = round(min4 / 0.15) * 0.15;
|
||||||
this->max4_ = max4 = round(max4 / 0.15) * 0.15;
|
this->max4_ = max4 = round(max4 / 0.15) * 0.15;
|
||||||
|
|
||||||
this->cmd_ = str_sprintf("detRangeCfg -1 "
|
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
|
||||||
"%.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f",
|
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15,
|
||||||
min1 / 0.15, max1 / 0.15, min2 / 0.15, max2 / 0.15, min3 / 0.15, max3 / 0.15, min4 / 0.15,
|
min2 / 0.15, max2 / 0.15, min3 / 0.15, max3 / 0.15, min4 / 0.15, max4 / 0.15);
|
||||||
max4 / 0.15);
|
this->cmd_ = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->min1_ = min1;
|
this->min1_ = min1;
|
||||||
@@ -203,7 +209,10 @@ SetLatencyCommand::SetLatencyCommand(float delay_after_detection, float delay_af
|
|||||||
delay_after_disappear = std::round(delay_after_disappear / 0.025f) * 0.025f;
|
delay_after_disappear = std::round(delay_after_disappear / 0.025f) * 0.025f;
|
||||||
this->delay_after_detection_ = clamp(delay_after_detection, 0.0f, 1638.375f);
|
this->delay_after_detection_ = clamp(delay_after_detection, 0.0f, 1638.375f);
|
||||||
this->delay_after_disappear_ = clamp(delay_after_disappear, 0.0f, 1638.375f);
|
this->delay_after_disappear_ = clamp(delay_after_disappear, 0.0f, 1638.375f);
|
||||||
this->cmd_ = str_sprintf("setLatency %.03f %.03f", this->delay_after_detection_, this->delay_after_disappear_);
|
// max 32: "setLatency "(11) + float(8) + " "(1) + float(8) + null, rounded to 32
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof(buf), "setLatency %.03f %.03f", this->delay_after_detection_, this->delay_after_disappear_);
|
||||||
|
this->cmd_ = buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8_t SetLatencyCommand::on_message(std::string &message) {
|
uint8_t SetLatencyCommand::on_message(std::string &message) {
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ class SetLatencyCommand : public Command {
|
|||||||
class SensorCfgStartCommand : public Command {
|
class SensorCfgStartCommand : public Command {
|
||||||
public:
|
public:
|
||||||
SensorCfgStartCommand(bool startup_mode) : startup_mode_(startup_mode) {
|
SensorCfgStartCommand(bool startup_mode) : startup_mode_(startup_mode) {
|
||||||
char tmp_cmd[20] = {0};
|
char tmp_cmd[20]; // "sensorCfgStart " (15) + "0/1" (1) + null = 17
|
||||||
sprintf(tmp_cmd, "sensorCfgStart %d", startup_mode);
|
buf_append_printf(tmp_cmd, sizeof(tmp_cmd), 0, "sensorCfgStart %d", startup_mode);
|
||||||
cmd_ = std::string(tmp_cmd);
|
cmd_ = std::string(tmp_cmd);
|
||||||
}
|
}
|
||||||
uint8_t on_message(std::string &message) override;
|
uint8_t on_message(std::string &message) override;
|
||||||
@@ -142,8 +142,8 @@ class SensitivityCommand : public Command {
|
|||||||
SensitivityCommand(uint8_t sensitivity) : sensitivity_(sensitivity) {
|
SensitivityCommand(uint8_t sensitivity) : sensitivity_(sensitivity) {
|
||||||
if (sensitivity > 9)
|
if (sensitivity > 9)
|
||||||
sensitivity_ = sensitivity = 9;
|
sensitivity_ = sensitivity = 9;
|
||||||
char tmp_cmd[20] = {0};
|
char tmp_cmd[20]; // "setSensitivity " (15) + "0-9" (1) + null = 17
|
||||||
sprintf(tmp_cmd, "setSensitivity %d", sensitivity);
|
buf_append_printf(tmp_cmd, sizeof(tmp_cmd), 0, "setSensitivity %d", sensitivity);
|
||||||
cmd_ = std::string(tmp_cmd);
|
cmd_ = std::string(tmp_cmd);
|
||||||
};
|
};
|
||||||
uint8_t on_message(std::string &message) override;
|
uint8_t on_message(std::string &message) override;
|
||||||
|
|||||||
@@ -89,10 +89,8 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
|
|||||||
delayMicroseconds(500);
|
delayMicroseconds(500);
|
||||||
} else if (this->model_ == DHT_MODEL_DHT22_TYPE2) {
|
} else if (this->model_ == DHT_MODEL_DHT22_TYPE2) {
|
||||||
delayMicroseconds(2000);
|
delayMicroseconds(2000);
|
||||||
} else if (this->model_ == DHT_MODEL_AM2120 || this->model_ == DHT_MODEL_AM2302) {
|
|
||||||
delayMicroseconds(1000);
|
|
||||||
} else {
|
} else {
|
||||||
delayMicroseconds(800);
|
delayMicroseconds(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|||||||
@@ -25,29 +25,13 @@ dsmr_ns = cg.esphome_ns.namespace("esphome::dsmr")
|
|||||||
Dsmr = dsmr_ns.class_("Dsmr", cg.Component, uart.UARTDevice)
|
Dsmr = dsmr_ns.class_("Dsmr", cg.Component, uart.UARTDevice)
|
||||||
|
|
||||||
|
|
||||||
def _validate_key(value):
|
|
||||||
value = cv.string_strict(value)
|
|
||||||
parts = [value[i : i + 2] for i in range(0, len(value), 2)]
|
|
||||||
if len(parts) != 16:
|
|
||||||
raise cv.Invalid("Decryption key must consist of 16 hexadecimal numbers")
|
|
||||||
parts_int = []
|
|
||||||
if any(len(part) != 2 for part in parts):
|
|
||||||
raise cv.Invalid("Decryption key must be format XX")
|
|
||||||
for part in parts:
|
|
||||||
try:
|
|
||||||
parts_int.append(int(part, 16))
|
|
||||||
except ValueError:
|
|
||||||
# pylint: disable=raise-missing-from
|
|
||||||
raise cv.Invalid("Decryption key must be hex values from 00 to FF")
|
|
||||||
|
|
||||||
return "".join(f"{part:02X}" for part in parts_int)
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(Dsmr),
|
cv.GenerateID(): cv.declare_id(Dsmr),
|
||||||
cv.Optional(CONF_DECRYPTION_KEY): _validate_key,
|
cv.Optional(CONF_DECRYPTION_KEY): lambda value: cv.bind_key(
|
||||||
|
value, name="Decryption key"
|
||||||
|
),
|
||||||
cv.Optional(CONF_CRC_CHECK, default=True): cv.boolean,
|
cv.Optional(CONF_CRC_CHECK, default=True): cv.boolean,
|
||||||
cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_,
|
cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_,
|
||||||
cv.Optional(CONF_WATER_MBUS_ID, default=2): cv.int_,
|
cv.Optional(CONF_WATER_MBUS_ID, default=2): cv.int_,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "dsmr.h"
|
#include "dsmr.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#include <AES.h>
|
#include <AES.h>
|
||||||
@@ -294,8 +295,8 @@ void Dsmr::dump_config() {
|
|||||||
DSMR_TEXT_SENSOR_LIST(DSMR_LOG_TEXT_SENSOR, )
|
DSMR_TEXT_SENSOR_LIST(DSMR_LOG_TEXT_SENSOR, )
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dsmr::set_decryption_key(const std::string &decryption_key) {
|
void Dsmr::set_decryption_key(const char *decryption_key) {
|
||||||
if (decryption_key.empty()) {
|
if (decryption_key == nullptr || decryption_key[0] == '\0') {
|
||||||
ESP_LOGI(TAG, "Disabling decryption");
|
ESP_LOGI(TAG, "Disabling decryption");
|
||||||
this->decryption_key_.clear();
|
this->decryption_key_.clear();
|
||||||
if (this->crypt_telegram_ != nullptr) {
|
if (this->crypt_telegram_ != nullptr) {
|
||||||
@@ -305,21 +306,15 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decryption_key.length() != 32) {
|
if (!parse_hex(decryption_key, this->decryption_key_, 16)) {
|
||||||
ESP_LOGE(TAG, "Error, decryption key must be 32 character long");
|
ESP_LOGE(TAG, "Error, decryption key must be 32 hex characters");
|
||||||
|
this->decryption_key_.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->decryption_key_.clear();
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Decryption key is set");
|
ESP_LOGI(TAG, "Decryption key is set");
|
||||||
// Verbose level prints decryption key
|
// Verbose level prints decryption key
|
||||||
ESP_LOGV(TAG, "Using decryption key: %s", decryption_key.c_str());
|
ESP_LOGV(TAG, "Using decryption key: %s", decryption_key);
|
||||||
|
|
||||||
char temp[3] = {0};
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
strncpy(temp, &(decryption_key.c_str()[i * 2]), 2);
|
|
||||||
this->decryption_key_.push_back(std::strtoul(temp, nullptr, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->crypt_telegram_ == nullptr) {
|
if (this->crypt_telegram_ == nullptr) {
|
||||||
this->crypt_telegram_ = new uint8_t[this->max_telegram_len_]; // NOLINT
|
this->crypt_telegram_ = new uint8_t[this->max_telegram_len_]; // NOLINT
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user