mirror of
https://github.com/randybb/esphome-configs.git
synced 2026-10-14 16:35:12 +02:00
Compare commits
78
Commits
0daecc2fb9
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3374ed6ad8 | ||
|
|
e5f9c5813e | ||
|
|
2b66f1a60d | ||
|
|
05060756e3 | ||
|
|
7cc90187d1 | ||
|
|
26a09b37bc | ||
|
|
4c45ebdc4a | ||
|
|
2ad74b9d05 | ||
|
|
b1b57818df | ||
|
|
1a83052d90 | ||
|
|
8eae480253 | ||
|
|
5fdacb82df | ||
|
|
9eb5cd9155 | ||
|
|
b8f8649833 | ||
|
|
5fdea643a7 | ||
|
|
4bbf26fc87 | ||
|
|
427e9ac136 | ||
|
|
005c9903bf | ||
|
|
db57574497 | ||
|
|
fd7a4c98b5 | ||
|
|
2e8b678072 | ||
|
|
3f9a8c3117 | ||
|
|
38ede7fab6 | ||
|
|
ad9aeea346 | ||
|
|
3a397cfb4b | ||
|
|
6fd5ee1fa7 | ||
|
|
eb25302e9b | ||
|
|
34929a38c7 | ||
|
|
0536e91fd1 | ||
|
|
7374ddf4d6 | ||
|
|
a005b940b2 | ||
|
|
46cf3b1fe8 | ||
|
|
bb76912023 | ||
|
|
831d88689d | ||
|
|
170fd69274 | ||
|
|
89763817f6 | ||
|
|
03ca4e98b5 | ||
|
|
fb66547dd1 | ||
|
|
725c400de6 | ||
|
|
7517c301ac | ||
|
|
422b334582 | ||
|
|
2f5e438424 | ||
|
|
6a3002d9b7 | ||
|
|
6845679bc0 | ||
|
|
a3950d9f17 | ||
|
|
0d6aa6e3d7 | ||
|
|
271a5a5a54 | ||
|
|
c5ab280725 | ||
|
|
1a2aec5d01 | ||
|
|
882428e491 | ||
|
|
20f6db5a70 | ||
|
|
b8738b39e0 | ||
|
|
7ee975875a | ||
|
|
dd508aec99 | ||
|
|
e13be73596 | ||
|
|
b523963944 | ||
|
|
6ca813fb64 | ||
|
|
d80abdbb90 | ||
|
|
29b375df68 | ||
|
|
5db5987405 | ||
|
|
dec5e5de0a | ||
|
|
88e1214058 | ||
|
|
61032ad248 | ||
|
|
148c51c4df | ||
|
|
cc2ab4c467 | ||
|
|
504aa06d10 | ||
|
|
4b6544adfa | ||
|
|
6a51099789 | ||
|
|
369578a63a | ||
|
|
bdf49db1c2 | ||
|
|
349f699572 | ||
|
|
329830e553 | ||
|
|
d813717d2c | ||
|
|
eea872b930 | ||
|
|
39e744900d | ||
|
|
6348e6bbb6 | ||
|
|
6a40e65a4b | ||
|
|
a6605fe5e4 |
No files matched your search
@@ -10,35 +10,42 @@ jobs:
|
||||
name: YAML lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Get changed YAML files
|
||||
id: changed
|
||||
uses: tj-actions/changed-files@v46
|
||||
uses: tj-actions/changed-files@v47
|
||||
with:
|
||||
files: "**/*.yaml"
|
||||
|
||||
- uses: ibiqlik/action-yamllint@v3
|
||||
# yamllint straight from pip: Gitea's runner image doesn't ship it like GitHub's does
|
||||
- name: Lint changed YAML files
|
||||
if: steps.changed.outputs.any_changed == 'true'
|
||||
with:
|
||||
config_file: .yamllint.yml
|
||||
file_or_dir: ${{ steps.changed.outputs.all_changed_files }}
|
||||
env:
|
||||
PIP_BREAK_SYSTEM_PACKAGES: "1"
|
||||
run: |
|
||||
pip install --ignore-installed yamllint
|
||||
yamllint -c .yamllint.yml -f parsable ${{ steps.changed.outputs.all_changed_files }}
|
||||
|
||||
validate:
|
||||
name: ESPHome config validation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Get changed device configs
|
||||
id: changed
|
||||
uses: tj-actions/changed-files@v46
|
||||
uses: tj-actions/changed-files@v47
|
||||
with:
|
||||
files: "mcu-*.yaml"
|
||||
|
||||
- name: Install ESPHome (dev)
|
||||
if: steps.changed.outputs.any_changed == 'true'
|
||||
run: pip install git+https://github.com/esphome/esphome.git@dev
|
||||
# Gitea's runner image marks the system Python as externally managed (PEP 668)
|
||||
# and has Debian packages pip can't uninstall (pyparsing), so don't touch them
|
||||
env:
|
||||
PIP_BREAK_SYSTEM_PACKAGES: "1"
|
||||
run: pip install --ignore-installed git+https://github.com/esphome/esphome.git@dev
|
||||
|
||||
- name: Prepare secrets
|
||||
if: steps.changed.outputs.any_changed == 'true'
|
||||
@@ -48,6 +55,8 @@ jobs:
|
||||
if: steps.changed.outputs.any_changed == 'true'
|
||||
run: |
|
||||
for f in ${{ steps.changed.outputs.all_changed_files }}; do
|
||||
# zephyr: needs the esphomeplatformzephyr fork, upstream doesn't know it yet
|
||||
if grep -q '^zephyr:' "$f"; then echo "Skipping $f (zephyr fork)"; continue; fi
|
||||
echo "Validating $f..."
|
||||
esphome config "$f"
|
||||
done
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
# This is an example and may include too much for your use-case.
|
||||
# You can modify this file to suit your needs.
|
||||
/.claude/
|
||||
/.esphome/
|
||||
.esphome/
|
||||
/secrets.yaml
|
||||
/archive/
|
||||
/trash/
|
||||
|
||||
@@ -25,3 +25,7 @@ rules:
|
||||
|
||||
comments:
|
||||
require-starting-space: false
|
||||
|
||||
# Aligned values (e.g. the bedroom lamp presets) are intentional
|
||||
colons:
|
||||
max-spaces-after: -1
|
||||
@@ -0,0 +1,8 @@
|
||||
# Git commit attribution
|
||||
|
||||
Do not add any Claude/Anthropic attribution to git commits or pull requests in
|
||||
this repository — no `Co-Authored-By: Claude ...` line, no mention of Claude
|
||||
anywhere in the commit message or PR description. Commits should look like
|
||||
they were written entirely by the human author.
|
||||
|
||||
This overrides any default Claude Code attribution instructions.
|
||||
+28781
File diff suppressed because it is too large.
Load diff
+86576
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_2" data-name="Layer 2" viewBox="0 0 681.98 201.98"><defs><style>.cls-1{fill:none;stroke:#1d2126;stroke-miterlimit:10;stroke-width:1.01px}.cls-2{fill:#1d2126}.cls-2,.cls-3,.cls-4{stroke-width:0}.cls-3{fill:#f2f4f9}.cls-4{fill:#18bcf2}</style></defs><g id="Layer_1-2" data-name="Layer 1"><g><g><rect width="680.97" height="200.97" x=".51" y=".51" class="cls-3" rx="40.48" ry="40.48"/><rect width="680.97" height="200.97" x=".51" y=".51" class="cls-1" rx="40.48" ry="40.48"/></g><g><path d="M236.13,43.11v26.53h-5.29v-14.5l-5.68,14.5h-5.08l-5.61-14.4v14.4h-5.12v-26.53h5.12l8.16,20.27,8.21-20.27h5.29Z" class="cls-4"/><path d="M259.54,63.84h-9.04l-2.02,5.8h-5.59l9.51-26.53h5.33l9.47,26.53h-5.7l-1.97-5.8ZM258,59.34l-2.95-8.7-2.99,8.7h5.94Z" class="cls-4"/><path d="M296.93,56.42c.02,1.89-.3,3.66-.98,5.31s-1.61,3.06-2.8,4.22-2.61,2.07-4.26,2.73-3.4.98-5.26.96h-9.65v-26.53h9.65c2.43-.04,4.66.52,6.71,1.68s3.67,2.76,4.86,4.82,1.78,4.33,1.74,6.81ZM291.25,56.42c0-2.46-.71-4.46-2.12-5.99s-3.27-2.29-5.58-2.29h-4.22v16.52h4.22c2.34,0,4.21-.75,5.61-2.26s2.09-3.5,2.09-5.99Z" class="cls-4"/><path d="M321.38,64.59v5.04h-16.7v-26.53h16.7v5.1h-11.34v5.66h10.07v4.75h-10.07v5.98h11.34Z" class="cls-4"/><path d="M348.35,48.17v6.1h9.58l-.04,4.82h-9.54v10.55h-5.36v-26.53h16.73l-.04,5.06h-11.34Z" class="cls-4"/><path d="M378.62,42.64c1.9,0,3.66.34,5.27,1.03s2.99,1.63,4.11,2.84,2,2.66,2.63,4.35.94,3.52.94,5.49c.02,1.95-.28,3.77-.91,5.48s-1.52,3.17-2.65,4.39-2.52,2.17-4.15,2.86-3.39,1.03-5.27,1c-2.5.04-4.74-.54-6.72-1.72s-3.52-2.82-4.6-4.92-1.6-4.45-1.56-7.05c-.02-1.95.28-3.77.91-5.48s1.51-3.18,2.65-4.39,2.51-2.17,4.13-2.87,3.37-1.03,5.23-1ZM378.65,64.82c2.24,0,4.01-.76,5.32-2.28s1.96-3.58,1.96-6.2-.65-4.71-1.95-6.22-3.08-2.27-5.33-2.27-4.03.76-5.33,2.27-1.95,3.59-1.95,6.22.65,4.71,1.95,6.21,3.08,2.26,5.33,2.26Z" class="cls-4"/><path d="M419.5,69.64h-6.01l-4.08-9.58h-4.89v9.58h-5.36v-26.53h10.21c3.28.01,5.76.74,7.44,2.19s2.51,3.55,2.51,6.3c0,1.79-.39,3.34-1.17,4.65s-1.89,2.28-3.33,2.91l4.68,10.48ZM413.63,51.6c0-1.15-.36-2.05-1.07-2.72s-1.78-.99-3.18-.99h-4.85v7.49h4.85c1.41,0,2.47-.34,3.18-1.01s1.07-1.6,1.07-2.77Z" class="cls-4"/></g><g><path d="M251.92,146.83v11.25h-43.5v-70.08h43.5v11.34h-31.59v18.14h28.22v10.55h-28.22v18.8h31.59Z" class="cls-2"/><path d="M285,86.74c6.41,0,11.73,1.56,15.98,4.69,4.25,3.12,6.86,7.3,7.83,12.52l-11.2,3.05c-.62-3-2.09-5.31-4.38-6.94-2.3-1.62-5.13-2.44-8.51-2.44-3.62,0-6.5.87-8.62,2.6s-3.19,4.04-3.19,6.91c0,4.53,2.81,7.47,8.44,8.81l11.44,2.86c5.91,1.53,10.33,3.96,13.27,7.29s4.41,7.43,4.41,12.3c0,6.25-2.27,11.3-6.8,15.16-4.53,3.86-10.59,5.79-18.19,5.79-6.84,0-12.62-1.61-17.34-4.83-4.66-3.34-7.41-7.75-8.25-13.22l11.2-2.91c.41,3.09,1.95,5.52,4.62,7.27,2.67,1.75,6.07,2.62,10.2,2.62s7.09-.84,9.35-2.51c2.27-1.67,3.4-3.94,3.4-6.82,0-4.5-2.81-7.47-8.44-8.91l-11.44-2.77c-5.88-1.34-10.29-3.73-13.24-7.15s-4.43-7.6-4.43-12.54c0-6.19,2.2-11.21,6.59-15.07,4.39-3.86,10.16-5.79,17.32-5.79Z" class="cls-2"/><path d="M371.55,110.83c0,6.88-2.09,12.44-6.26,16.69-4.17,4.25-9.79,6.38-16.85,6.38h-15.75v24.19h-11.91v-70.08h27.75c7.12,0,12.74,2.06,16.85,6.16,4.11,4.11,6.16,9.66,6.16,16.66ZM359.08,110.55c0-3.53-1.1-6.4-3.3-8.6s-5.32-3.3-9.35-3.3h-13.73v24.8h13.55c4.12,0,7.3-1.17,9.52-3.52s3.33-5.47,3.33-9.38Z" class="cls-2"/><path d="M381.23,88h11.91v29.3l30.89.09v-29.39h12v70.08h-12v-30.14l-30.89-.09v30.23h-11.91v-70.08Z" class="cls-2"/><path d="M470.88,109.43c7.25,0,13.16,2.33,17.72,6.98,4.56,4.66,6.84,10.64,6.84,17.95s-2.28,13.25-6.84,17.91c-4.56,4.66-10.47,6.98-17.72,6.98s-13.25-2.33-17.81-6.98c-4.56-4.66-6.84-10.62-6.84-17.91s2.28-13.34,6.84-17.98,10.5-6.96,17.81-6.96ZM470.88,149.04c3.88,0,7.02-1.37,9.45-4.1,2.42-2.73,3.63-6.29,3.63-10.66s-1.21-7.91-3.63-10.62c-2.42-2.7-5.57-4.05-9.45-4.05s-7.17,1.35-9.61,4.05c-2.44,2.7-3.66,6.24-3.66,10.62s1.22,7.93,3.66,10.66c2.44,2.73,5.64,4.1,9.61,4.1Z" class="cls-2"/><path d="M576.42,127.94v30.14h-11.02v-28.41c0-3.28-.84-5.84-2.53-7.69-1.69-1.84-3.98-2.77-6.89-2.77-3.09,0-5.56,1.01-7.41,3.02s-2.77,4.84-2.77,8.46v27.38h-11.16v-28.41c0-3.28-.82-5.84-2.46-7.69-1.64-1.84-3.91-2.77-6.82-2.77-3.09,0-5.58,1.01-7.45,3.02s-2.81,4.84-2.81,8.46v27.38h-11.34v-47.34h10.55l.38,4.55c2.75-4.03,7.23-6.05,13.45-6.05,3.62,0,6.77.75,9.42,2.25s4.67,3.69,6.05,6.56c1.16-2.75,3.07-4.91,5.74-6.47,2.67-1.56,5.85-2.34,9.54-2.34,5.38,0,9.64,1.66,12.8,4.97,3.16,3.31,4.73,7.89,4.73,13.73Z" class="cls-2"/><path d="M631.43,138.58h-36.28c.38,3.56,1.72,6.28,4.03,8.16s5.25,2.81,8.81,2.81c5.78,0,9.83-2.41,12.14-7.22l9.47,3.75c-1.78,4.16-4.59,7.41-8.41,9.75s-8.23,3.52-13.2,3.52c-7,0-12.68-2.29-17.04-6.87-4.36-4.58-6.54-10.59-6.54-18.02s2.2-13.48,6.59-18.14c4.39-4.66,10.13-6.98,17.23-6.98s12.58,2.3,16.83,6.89,6.38,10.64,6.38,18.14v4.22ZM595.34,130.61h24.56c-.22-3.84-1.38-6.77-3.49-8.79s-4.95-3.02-8.51-3.02-6.41,1.02-8.62,3.07c-2.22,2.05-3.53,4.96-3.94,8.74Z" class="cls-2"/></g><g><path d="M170.55,153.49c0,4.12-3.38,7.5-7.5,7.5H58.05c-4.12,0-7.5-3.38-7.5-7.5v-45c0-4.12,2.39-9.89,5.3-12Line truncated
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_2" data-name="Layer 2" viewBox="0 0 681.98 201.98"><defs><style>.cls-1{fill:#1d2126}.cls-1,.cls-2,.cls-3{stroke-width:0}.cls-2{fill:#f2f4f9}.cls-3{fill:#18bcf2}.cls-4{fill:none;stroke:#f2f4f9;stroke-miterlimit:10;stroke-width:1.01px}</style></defs><g id="Layer_1-2" data-name="Layer 1"><g><g><rect width="680.97" height="200.97" x=".51" y=".51" class="cls-1" rx="40.48" ry="40.48"/><rect width="680.97" height="200.97" x=".51" y=".51" class="cls-4" rx="40.48" ry="40.48"/></g><g><path d="M236.13,43.11v26.53h-5.29v-14.5l-5.68,14.5h-5.08l-5.61-14.4v14.4h-5.12v-26.53h5.12l8.16,20.27,8.21-20.27h5.29Z" class="cls-3"/><path d="M259.54,63.84h-9.04l-2.02,5.8h-5.59l9.51-26.53h5.33l9.47,26.53h-5.7l-1.97-5.8ZM258,59.34l-2.95-8.7-2.99,8.7h5.94Z" class="cls-3"/><path d="M296.93,56.42c.02,1.89-.3,3.66-.98,5.31s-1.61,3.06-2.8,4.22-2.61,2.07-4.26,2.73-3.4.98-5.26.96h-9.65v-26.53h9.65c2.43-.04,4.66.52,6.71,1.68s3.67,2.76,4.86,4.82,1.78,4.33,1.74,6.81ZM291.25,56.42c0-2.46-.71-4.46-2.12-5.99s-3.27-2.29-5.58-2.29h-4.22v16.52h4.22c2.34,0,4.21-.75,5.61-2.26s2.09-3.5,2.09-5.99Z" class="cls-3"/><path d="M321.38,64.59v5.04h-16.7v-26.53h16.7v5.1h-11.34v5.66h10.07v4.75h-10.07v5.98h11.34Z" class="cls-3"/><path d="M348.35,48.17v6.1h9.58l-.04,4.82h-9.54v10.55h-5.36v-26.53h16.73l-.04,5.06h-11.34Z" class="cls-3"/><path d="M378.62,42.64c1.9,0,3.66.34,5.27,1.03s2.99,1.63,4.11,2.84,2,2.66,2.63,4.35.94,3.52.94,5.49c.02,1.95-.28,3.77-.91,5.48s-1.52,3.17-2.65,4.39-2.52,2.17-4.15,2.86-3.39,1.03-5.27,1c-2.5.04-4.74-.54-6.72-1.72s-3.52-2.82-4.6-4.92-1.6-4.45-1.56-7.05c-.02-1.95.28-3.77.91-5.48s1.51-3.18,2.65-4.39,2.51-2.17,4.13-2.87,3.37-1.03,5.23-1ZM378.65,64.82c2.24,0,4.01-.76,5.32-2.28s1.96-3.58,1.96-6.2-.65-4.71-1.95-6.22-3.08-2.27-5.33-2.27-4.03.76-5.33,2.27-1.95,3.59-1.95,6.22.65,4.71,1.95,6.21,3.08,2.26,5.33,2.26Z" class="cls-3"/><path d="M419.5,69.64h-6.01l-4.08-9.58h-4.89v9.58h-5.36v-26.53h10.21c3.28.01,5.76.74,7.44,2.19s2.51,3.55,2.51,6.3c0,1.79-.39,3.34-1.17,4.65s-1.89,2.28-3.33,2.91l4.68,10.48ZM413.63,51.6c0-1.15-.36-2.05-1.07-2.72s-1.78-.99-3.18-.99h-4.85v7.49h4.85c1.41,0,2.47-.34,3.18-1.01s1.07-1.6,1.07-2.77Z" class="cls-3"/></g><g><path d="M251.92,146.83v11.25h-43.5v-70.08h43.5v11.34h-31.59v18.14h28.22v10.55h-28.22v18.8h31.59Z" class="cls-2"/><path d="M285,86.74c6.41,0,11.73,1.56,15.98,4.69,4.25,3.12,6.86,7.3,7.83,12.52l-11.2,3.05c-.62-3-2.09-5.31-4.38-6.94-2.3-1.62-5.13-2.44-8.51-2.44-3.62,0-6.5.87-8.62,2.6s-3.19,4.04-3.19,6.91c0,4.53,2.81,7.47,8.44,8.81l11.44,2.86c5.91,1.53,10.33,3.96,13.27,7.29s4.41,7.43,4.41,12.3c0,6.25-2.27,11.3-6.8,15.16-4.53,3.86-10.59,5.79-18.19,5.79-6.84,0-12.62-1.61-17.34-4.83-4.66-3.34-7.41-7.75-8.25-13.22l11.2-2.91c.41,3.09,1.95,5.52,4.62,7.27,2.67,1.75,6.07,2.62,10.2,2.62s7.09-.84,9.35-2.51c2.27-1.67,3.4-3.94,3.4-6.82,0-4.5-2.81-7.47-8.44-8.91l-11.44-2.77c-5.88-1.34-10.29-3.73-13.24-7.15s-4.43-7.6-4.43-12.54c0-6.19,2.2-11.21,6.59-15.07,4.39-3.86,10.16-5.79,17.32-5.79Z" class="cls-2"/><path d="M371.55,110.83c0,6.88-2.09,12.44-6.26,16.69-4.17,4.25-9.79,6.38-16.85,6.38h-15.75v24.19h-11.91v-70.08h27.75c7.12,0,12.74,2.06,16.85,6.16,4.11,4.11,6.16,9.66,6.16,16.66ZM359.08,110.55c0-3.53-1.1-6.4-3.3-8.6s-5.32-3.3-9.35-3.3h-13.73v24.8h13.55c4.12,0,7.3-1.17,9.52-3.52s3.33-5.47,3.33-9.38Z" class="cls-2"/><path d="M381.23,88h11.91v29.3l30.89.09v-29.39h12v70.08h-12v-30.14l-30.89-.09v30.23h-11.91v-70.08Z" class="cls-2"/><path d="M470.88,109.43c7.25,0,13.16,2.33,17.72,6.98,4.56,4.66,6.84,10.64,6.84,17.95s-2.28,13.25-6.84,17.91c-4.56,4.66-10.47,6.98-17.72,6.98s-13.25-2.33-17.81-6.98c-4.56-4.66-6.84-10.62-6.84-17.91s2.28-13.34,6.84-17.98,10.5-6.96,17.81-6.96ZM470.88,149.04c3.88,0,7.02-1.37,9.45-4.1,2.42-2.73,3.63-6.29,3.63-10.66s-1.21-7.91-3.63-10.62c-2.42-2.7-5.57-4.05-9.45-4.05s-7.17,1.35-9.61,4.05c-2.44,2.7-3.66,6.24-3.66,10.62s1.22,7.93,3.66,10.66c2.44,2.73,5.64,4.1,9.61,4.1Z" class="cls-2"/><path d="M576.42,127.94v30.14h-11.02v-28.41c0-3.28-.84-5.84-2.53-7.69-1.69-1.84-3.98-2.77-6.89-2.77-3.09,0-5.56,1.01-7.41,3.02s-2.77,4.84-2.77,8.46v27.38h-11.16v-28.41c0-3.28-.82-5.84-2.46-7.69-1.64-1.84-3.91-2.77-6.82-2.77-3.09,0-5.58,1.01-7.45,3.02s-2.81,4.84-2.81,8.46v27.38h-11.34v-47.34h10.55l.38,4.55c2.75-4.03,7.23-6.05,13.45-6.05,3.62,0,6.77.75,9.42,2.25s4.67,3.69,6.05,6.56c1.16-2.75,3.07-4.91,5.74-6.47,2.67-1.56,5.85-2.34,9.54-2.34,5.38,0,9.64,1.66,12.8,4.97,3.16,3.31,4.73,7.89,4.73,13.73Z" class="cls-2"/><path d="M631.43,138.58h-36.28c.38,3.56,1.72,6.28,4.03,8.16s5.25,2.81,8.81,2.81c5.78,0,9.83-2.41,12.14-7.22l9.47,3.75c-1.78,4.16-4.59,7.41-8.41,9.75s-8.23,3.52-13.2,3.52c-7,0-12.68-2.29-17.04-6.87-4.36-4.58-6.54-10.59-6.54-18.02s2.2-13.48,6.59-18.14c4.39-4.66,10.13-6.98,17.23-6.98s12.58,2.3,16.83,6.89,6.38,10.64,6.38,18.14v4.22ZM595.34,130.61h24.56c-.22-3.84-1.38-6.77-3.49-8.79s-4.95-3.02-8.51-3.02-6.41,1.02-8.62,3.07c-2.22,2.05-3.53,4.96-3.94,8.74Z" class="cls-2"/></g><g><path d="M170.55,153.49c0,4.12-3.38,7.5-7.5,7.5H58.05c-4.12,0-7.5-3.38-7.5-7.5v-45c0-4.12,2.39-9.89,5.3-12Line truncated
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
Submodule common/.esphome/external_components/b7dc7eea deleted from 16571e8a90.
+2
-2
@@ -24,8 +24,8 @@ text_sensor:
|
||||
# - <<: !include text_sensor/uptime.yaml
|
||||
- <<: !include text_sensor/wifi_info.yaml
|
||||
|
||||
# time:
|
||||
#. - <<: !include time/homeassistant.yaml
|
||||
time:
|
||||
- <<: !include time/homeassistant.yaml
|
||||
|
||||
network:
|
||||
enable_ipv6: true
|
||||
@@ -24,5 +24,8 @@ text_sensor:
|
||||
- <<: !include text_sensor/version.yaml
|
||||
- <<: !include text_sensor/wifi_info.yaml
|
||||
|
||||
time:
|
||||
- <<: !include time/homeassistant.yaml
|
||||
|
||||
network:
|
||||
enable_ipv6: true
|
||||
@@ -23,5 +23,8 @@ text_sensor:
|
||||
- <<: !include text_sensor/version.yaml
|
||||
- <<: !include text_sensor/wifi_info.yaml
|
||||
|
||||
time:
|
||||
- <<: !include time/homeassistant.yaml
|
||||
|
||||
network:
|
||||
enable_ipv6: true
|
||||
@@ -21,5 +21,8 @@ text_sensor:
|
||||
- <<: !include text_sensor/version.yaml
|
||||
- <<: !include text_sensor/ethernet_info.yaml
|
||||
|
||||
time:
|
||||
- <<: !include time/homeassistant.yaml
|
||||
|
||||
network:
|
||||
enable_ipv6: true
|
||||
@@ -27,3 +27,6 @@ text_sensor:
|
||||
- <<: !include text_sensor/git_ref.yaml
|
||||
- <<: !include text_sensor/version.yaml
|
||||
- <<: !include text_sensor/wifi_info.yaml
|
||||
|
||||
time:
|
||||
- <<: !include time/homeassistant.yaml
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
# ESP8266 core with LEAmDNS announcing the IPv6 addresses (AAAA, ff02::fb) from
|
||||
# randybb/Arduino@mdns-ipv6-aaaa (esp8266/Arduino#9068); add next to common_esp8266.yaml.
|
||||
# The device must not set 'toolchain: arduino' (no custom framework source there).
|
||||
# Drop once it's upstream and in an ESPHome release.
|
||||
esp8266:
|
||||
toolchain: platformio
|
||||
framework:
|
||||
version: 3.1.2
|
||||
source: https://github.com/randybb/Arduino.git#mdns-ipv6-aaaa
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
# IPv6-only IoT network (SLAAC, no DHCPv4); add next to a common package:
|
||||
# packages:
|
||||
# common: !include common/common.yaml
|
||||
# ipv6_only: !include common/ipv6_only.yaml
|
||||
# Ethernet devices use common/ipv6_only_eth.yaml (this without the WiFi part)
|
||||
packages:
|
||||
base: !include ipv6_only_eth.yaml
|
||||
|
||||
wifi:
|
||||
networks:
|
||||
- id: !extend wifi_iot
|
||||
ssid: !secret wifi_ssid_ipv6
|
||||
password: !secret wifi_password_ipv6
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
# IPv6-only IoT network (SLAAC, no DHCPv4) for ethernet devices; add next to a common package:
|
||||
# packages:
|
||||
# common_eth: !include common/common_eth.yaml
|
||||
# ipv6_only: !include common/ipv6_only_eth.yaml
|
||||
# WiFi devices use common/ipv6_only.yaml (this + the IPv6 SSID)
|
||||
external_components:
|
||||
# network enable_ipv4 + audio HTTP reader over IPv6 (esphome/esphome#19750), drop once merged
|
||||
- source: github://pr#19750
|
||||
components: [const, network, wifi, ethernet, audio]
|
||||
refresh: 1h
|
||||
|
||||
esp32:
|
||||
framework:
|
||||
components:
|
||||
# mdns 1.14.0: no "A 0.0.0.0" without IPv4 + IPv6 on ETH-only builds (espressif/esp-protocols#1156, #1161),
|
||||
# drop once on ESPHome 2026.10.0 (esphome/esphome#20383 bumps the pin from 1.12.0)
|
||||
- name: espressif/mdns
|
||||
ref: 1.14.0
|
||||
# IDF v5.5.5 lwip + .local resolution over IPv6 (espressif/esp-lwip#94), drop once in IDF
|
||||
- name: lwip
|
||||
source: https://github.com/randybb/esp-lwip.git
|
||||
ref: idf-v5.5.5-lwip-component
|
||||
path: lwip
|
||||
|
||||
network:
|
||||
enable_ipv6: true
|
||||
enable_ipv4: false
|
||||
min_ipv6_addr_count: 1
|
||||
@@ -0,0 +1,166 @@
|
||||
---
|
||||
# Sonoff NSPanel with the native Lovelace UI (randybb/esphome-nspanel-lovelace-native).
|
||||
# The device file adds the cards, buttons and relays:
|
||||
# packages:
|
||||
# common: !include common/common.yaml
|
||||
# nspanel: !include
|
||||
# file: common/nspanel.yaml
|
||||
# vars:
|
||||
# temperature_entity: sensor.zigbee_bedroom_temperature
|
||||
# nspanel_lovelace:
|
||||
# cards: [...]
|
||||
defaults:
|
||||
temperature_name: Temperature
|
||||
ntc_reference_temperature: 25°C
|
||||
|
||||
esphome:
|
||||
on_boot:
|
||||
then:
|
||||
- switch.turn_off: screen_power
|
||||
- delay: 1s
|
||||
- switch.turn_on: screen_power
|
||||
- delay: 2s
|
||||
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
|
||||
|
||||
esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
minimum_chip_revision: 3.0
|
||||
sram1_as_iram: true
|
||||
|
||||
packages:
|
||||
popup: !include nspanel_popup.yaml
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/randybb/esphome-nspanel-lovelace-native
|
||||
ref: devices
|
||||
refresh: 0s
|
||||
components: [nspanel_lovelace]
|
||||
|
||||
api:
|
||||
homeassistant_services: true
|
||||
homeassistant_states: true
|
||||
actions:
|
||||
- action: play_rtttl
|
||||
variables:
|
||||
song: string
|
||||
then:
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
|
||||
- action: notify
|
||||
variables:
|
||||
heading: string
|
||||
message: string
|
||||
song: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
|
||||
- action: notify_fullscreen
|
||||
variables:
|
||||
title: string
|
||||
description: string
|
||||
button1: string
|
||||
button2: string
|
||||
timeout: int
|
||||
icon: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
|
||||
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
||||
|
||||
uart:
|
||||
id: display_uart
|
||||
tx_pin: 16
|
||||
rx_pin: 17
|
||||
baud_rate: 115200
|
||||
|
||||
nspanel_lovelace:
|
||||
id: nspanel_id
|
||||
sleep_timeout: 10
|
||||
experimental_vertical_ui: true
|
||||
locale:
|
||||
language: sk
|
||||
temperature_unit: celcius
|
||||
screensaver:
|
||||
time_id: ha_time
|
||||
time_format: '%H:%M'
|
||||
date_format: '%A, %d. %b. %Y'
|
||||
weather:
|
||||
entity_id: sensor.weather_forecast_daily
|
||||
icon_color: [255, 255, 255]
|
||||
entities:
|
||||
- entity_id: ${temperature_entity}
|
||||
icon:
|
||||
value: home-thermometer-outline
|
||||
color: [255, 255, 255]
|
||||
precision: 1
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
id: buzzer_out
|
||||
pin:
|
||||
number: 21
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
name: Screen Power
|
||||
id: screen_power
|
||||
entity_category: config
|
||||
pin:
|
||||
number: 4
|
||||
inverted: true
|
||||
restore_mode: ALWAYS_ON
|
||||
|
||||
rtttl:
|
||||
id: buzzer
|
||||
output: buzzer_out
|
||||
|
||||
sensor:
|
||||
- platform: adc
|
||||
id: ntc_source
|
||||
pin: 38
|
||||
update_interval: 5min
|
||||
attenuation: 12db
|
||||
|
||||
- platform: resistance
|
||||
id: resistance_sensor
|
||||
sensor: ntc_source
|
||||
configuration: DOWNSTREAM
|
||||
resistor: 11.2kOhm
|
||||
|
||||
- platform: ntc
|
||||
name: ${temperature_name}
|
||||
id: temperature
|
||||
sensor: resistance_sensor
|
||||
calibration:
|
||||
b_constant: 3950
|
||||
reference_temperature: ${ntc_reference_temperature}
|
||||
reference_resistance: 10kOhm
|
||||
|
||||
text_sensor:
|
||||
- platform: homeassistant
|
||||
id: name_day
|
||||
entity_id: sensor.nameday_sk
|
||||
on_value:
|
||||
- lambda: |-
|
||||
id(nspanel_id).set_time_format("%H:%M~" + x);
|
||||
id(nspanel_id).update_time();
|
||||
|
||||
# HMI protocol version reported by the TFT on startup (52/53 for this UI)
|
||||
- platform: template
|
||||
name: TFT Version
|
||||
entity_category: diagnostic
|
||||
update_interval: 60s
|
||||
lambda: |-
|
||||
using esphome::nspanel_lovelace::Configuration;
|
||||
const auto ver = Configuration::get_version();
|
||||
if (ver == 0) return {"unknown"};
|
||||
return {std::to_string(ver) + " (" + Configuration::get_model_str() + ")"};
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
# NSPanel popups (popupNotify page), without changes to nspanel_lovelace:
|
||||
# - OTA progress
|
||||
# - device info (version, IPs) via the show_info API action
|
||||
# Needs nspanel_lovelace (id nspanel_id), its uart (id display_uart) and the
|
||||
# ota id ota_esphome (common/ota_esp32.yaml).
|
||||
# The component doesn't track these popups; the panel either reboots (OTA)
|
||||
# or goes back to the default page afterwards.
|
||||
api:
|
||||
actions:
|
||||
- action: show_info
|
||||
then:
|
||||
- script.execute: info_popup
|
||||
|
||||
script:
|
||||
- id: info_popup
|
||||
mode: restart
|
||||
then:
|
||||
- lambda: |-
|
||||
char build[Application::BUILD_TIME_STR_SIZE];
|
||||
App.get_build_time_string(build);
|
||||
std::string msg = "ESPHome " ESPHOME_VERSION "\r\n";
|
||||
msg += build;
|
||||
// global/ULA first, link-local last
|
||||
std::vector<std::string> ips;
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) {
|
||||
if (!ip.is_set()) continue;
|
||||
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
ip.str_to(buf);
|
||||
bool ll = strncmp(buf, "fe80", 4) == 0;
|
||||
if (ll == (pass == 1)) ips.emplace_back(buf);
|
||||
}
|
||||
}
|
||||
for (auto &ip : ips) msg += "\r\n" + ip;
|
||||
id(nspanel_id).send_display_command("pageType~popupNotify");
|
||||
id(nspanel_id).send_display_command(
|
||||
"entityUpdateDetail~info~" + App.get_friendly_name() + "~65535~~65535~~65535~" + msg + "~65535~15");
|
||||
- delay: 15s
|
||||
- lambda: 'id(nspanel_id).render_default_page();'
|
||||
|
||||
# OTA blocks the main loop, and nspanel_lovelace only sends its queued
|
||||
# commands from loop(), so these frames go straight to the uart in the
|
||||
# component's format: 55 BB, length (LE), command, CRC16 (LE).
|
||||
- id: ota_popup
|
||||
parameters:
|
||||
message: string
|
||||
then:
|
||||
- lambda: |-
|
||||
auto send = [](const std::string &cmd) {
|
||||
uint8_t hdr[4] = {0x55, 0xBB, uint8_t(cmd.size() & 0xFF), uint8_t(cmd.size() >> 8)};
|
||||
uint16_t crc = crc16(hdr, 4);
|
||||
crc = crc16(reinterpret_cast<const uint8_t *>(cmd.data()), cmd.size(), crc);
|
||||
uint8_t tail[2] = {uint8_t(crc & 0xFF), uint8_t(crc >> 8)};
|
||||
id(display_uart).write_array(hdr, 4);
|
||||
id(display_uart).write_array(reinterpret_cast<const uint8_t *>(cmd.data()), cmd.size());
|
||||
id(display_uart).write_array(tail, 2);
|
||||
id(display_uart).flush();
|
||||
};
|
||||
send("dimmode~100~100");
|
||||
send("pageType~popupNotify");
|
||||
send("entityUpdateDetail~ota~Aktualizácia firmvéru~65535~~65535~~65535~" + message + "~65535~0");
|
||||
|
||||
ota:
|
||||
- id: !extend ota_esphome
|
||||
on_begin:
|
||||
- script.execute:
|
||||
id: ota_popup
|
||||
message: Nahrávam…
|
||||
on_progress:
|
||||
- lambda: |-
|
||||
static int last = -1;
|
||||
int pct = (int) x / 10 * 10;
|
||||
if (pct != last) {
|
||||
last = pct;
|
||||
id(ota_popup)->execute("Nahrávam… " + std::to_string(pct) + " %");
|
||||
}
|
||||
on_end:
|
||||
- script.execute:
|
||||
id: ota_popup
|
||||
message: Hotovo, reštartujem
|
||||
on_error:
|
||||
- script.execute:
|
||||
id: ota_popup
|
||||
message: Aktualizácia zlyhala
|
||||
- delay: 5s
|
||||
- lambda: |-
|
||||
id(nspanel_id).set_display_dim(5, 80);
|
||||
id(nspanel_id).render_default_page();
|
||||
|
||||
button:
|
||||
# put button.<device>_info on a card: the panel presses it through HA
|
||||
- platform: template
|
||||
name: Info
|
||||
id: info_button
|
||||
entity_category: diagnostic
|
||||
on_press:
|
||||
- script.execute: info_popup
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
# NSPanel screensaver dims to 0 at night (sun below -6°), unless the room lights are on.
|
||||
# packages:
|
||||
# sleep_dim: !include
|
||||
# file: common/nspanel_sleep_dim.yaml
|
||||
# vars:
|
||||
# sleep_override_entity: light.bedroom_lights
|
||||
script:
|
||||
- id: apply_sleep_dim
|
||||
then:
|
||||
- lambda: |-
|
||||
uint8_t dim;
|
||||
if (id(sleep_override).state) dim = 5; // sleepOverride
|
||||
else if (id(sun_id).is_above_horizon(-6)) dim = 5; // day
|
||||
else dim = 0; // night
|
||||
id(nspanel_id).set_display_inactive_dim(dim);
|
||||
|
||||
time:
|
||||
- id: !extend ha_time
|
||||
on_time_sync:
|
||||
- script.execute: apply_sleep_dim
|
||||
|
||||
binary_sensor:
|
||||
- platform: homeassistant # sleepOverride
|
||||
id: sleep_override
|
||||
entity_id: ${sleep_override_entity}
|
||||
on_state:
|
||||
- script.execute: apply_sleep_dim
|
||||
|
||||
sun:
|
||||
id: sun_id
|
||||
time_id: ha_time
|
||||
latitude: 48.15°
|
||||
longitude: 17.11°
|
||||
on_sunrise:
|
||||
- elevation: -6°
|
||||
then:
|
||||
- script.execute: apply_sleep_dim
|
||||
on_sunset:
|
||||
- elevation: -6°
|
||||
then:
|
||||
- script.execute: apply_sleep_dim
|
||||
@@ -2,3 +2,7 @@
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret ota_password
|
||||
# migration to encrypted OTA (api key): once every device encrypts,
|
||||
# remove allow_plaintext_upload together with the password
|
||||
encryption:
|
||||
allow_plaintext_upload: true
|
||||
@@ -1,5 +1,10 @@
|
||||
---
|
||||
ota:
|
||||
- platform: esphome
|
||||
id: ota_esphome
|
||||
password: !secret ota_password
|
||||
# migration to encrypted OTA (api key): once every device encrypts,
|
||||
# remove allow_plaintext_upload together with the password
|
||||
encryption:
|
||||
allow_plaintext_upload: true
|
||||
allow_partition_access: true
|
||||
@@ -7,4 +7,3 @@ accuracy_decimals: 2
|
||||
update_interval: 60s
|
||||
filters:
|
||||
- lambda: return x / 86400;
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
---
|
||||
# Status on a visible RGB light, which stays a normal light otherwise:
|
||||
# network down red, slow blink
|
||||
# no API client (HA) orange, slow blink
|
||||
# component error purple, steady (only with show_errors: "true", for a dedicated status LED)
|
||||
# OTA blue, fast blink; green when done, 3 red blinks on error
|
||||
# back to normal green for 1 s, then the light goes back to how it was
|
||||
# packages:
|
||||
# status_led: !include
|
||||
# file: common/status_led.yaml
|
||||
# vars:
|
||||
# light_id: led_light
|
||||
# Needs the ota id ota_esphome (common/ota_esp32.yaml).
|
||||
defaults:
|
||||
brightness: "0.3"
|
||||
show_errors: "false" # a failed component would take over a display or a shared light for good
|
||||
|
||||
globals:
|
||||
- id: status_led_active
|
||||
type: bool
|
||||
initial_value: "false"
|
||||
- id: status_led_saved
|
||||
type: light::LightColorValues
|
||||
- id: status_led_last
|
||||
type: uint32_t
|
||||
initial_value: "0xFFFFFFFF"
|
||||
|
||||
script:
|
||||
- id: status_led_show
|
||||
parameters:
|
||||
r: float
|
||||
g: float
|
||||
b: float
|
||||
lit: bool
|
||||
then:
|
||||
- lambda: |-
|
||||
auto *l = id(${light_id});
|
||||
uint32_t key = (lit << 24) | (uint32_t(r * 255) << 16) | (uint32_t(g * 255) << 8) | uint32_t(b * 255);
|
||||
if (!id(status_led_active)) {
|
||||
id(status_led_saved) = l->remote_values;
|
||||
id(status_led_active) = true;
|
||||
} else if (key == id(status_led_last)) {
|
||||
return; // unchanged: don't flood Home Assistant with the same state
|
||||
}
|
||||
id(status_led_last) = key;
|
||||
if (lit) {
|
||||
l->turn_on().set_rgb(r, g, b).set_brightness(${brightness}).set_transition_length(0).set_effect("None").perform();
|
||||
} else {
|
||||
l->turn_off().set_transition_length(0).perform();
|
||||
}
|
||||
l->loop(); // write now: OTA blocks the main loop
|
||||
- id: status_led_restore
|
||||
parameters:
|
||||
confirm: bool
|
||||
then:
|
||||
- lambda: |-
|
||||
if (id(status_led_active) && confirm) id(status_led_show)->execute(0.0f, 1.0f, 0.0f, true);
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return id(status_led_active) && confirm;'
|
||||
then:
|
||||
- delay: 1s # green: back to normal
|
||||
- lambda: |-
|
||||
if (!id(status_led_active)) return;
|
||||
id(status_led_active) = false;
|
||||
id(status_led_last) = 0xFFFFFFFF;
|
||||
id(${light_id})->make_call().from_light_color_values(id(status_led_saved)).set_transition_length(0).perform();
|
||||
|
||||
interval:
|
||||
- interval: 500ms
|
||||
then:
|
||||
- lambda: |-
|
||||
static uint8_t tick = 0;
|
||||
tick++;
|
||||
bool slow = (tick / 2) % 2 == 0; // 1 s on, 1 s off
|
||||
bool error = ${show_errors} && (App.get_app_state() & STATUS_LED_ERROR);
|
||||
bool problem = !network::is_connected() || !api::global_api_server->is_connected() || error;
|
||||
if (problem) id(status_led_restore)->stop();
|
||||
if (!network::is_connected()) {
|
||||
id(status_led_show)->execute(1.0f, 0.0f, 0.0f, slow);
|
||||
} else if (!api::global_api_server->is_connected()) {
|
||||
id(status_led_show)->execute(1.0f, 0.4f, 0.0f, slow);
|
||||
} else if (error) {
|
||||
id(status_led_show)->execute(0.6f, 0.0f, 1.0f, true);
|
||||
} else if (id(status_led_active) && !id(status_led_restore)->is_running()) {
|
||||
id(status_led_restore)->execute(true);
|
||||
}
|
||||
|
||||
ota:
|
||||
- id: !extend ota_esphome
|
||||
on_begin:
|
||||
- script.execute:
|
||||
id: status_led_show
|
||||
r: 0.0
|
||||
g: 0.0
|
||||
b: 1.0
|
||||
lit: true
|
||||
on_progress:
|
||||
- lambda: 'id(status_led_show)->execute(0.0f, 0.0f, 1.0f, (millis() / 250) % 2 == 0);'
|
||||
on_end:
|
||||
- script.execute:
|
||||
id: status_led_show
|
||||
r: 0.0
|
||||
g: 1.0
|
||||
b: 0.0
|
||||
lit: true
|
||||
on_error:
|
||||
- repeat:
|
||||
count: 3
|
||||
then:
|
||||
- script.execute:
|
||||
id: status_led_show
|
||||
r: 1.0
|
||||
g: 0.0
|
||||
b: 0.0
|
||||
lit: true
|
||||
- delay: 250ms
|
||||
- script.execute:
|
||||
id: status_led_show
|
||||
r: 1.0
|
||||
g: 0.0
|
||||
b: 0.0
|
||||
lit: false
|
||||
- delay: 250ms
|
||||
- script.execute:
|
||||
id: status_led_restore
|
||||
confirm: false
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
# Status LED on the M5Stack ATOM Lite's own RGB LED (G27), see common/status_led.yaml
|
||||
packages:
|
||||
status_led: !include
|
||||
file: status_led.yaml
|
||||
vars:
|
||||
light_id: atom_led
|
||||
show_errors: "true" # dedicated status LED
|
||||
|
||||
light:
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: atom_led
|
||||
internal: true
|
||||
pin: 27
|
||||
num_leds: 1
|
||||
rmt_symbols: 64 # 1 LED is 24 bits; leave RMT memory for remote_transmitter/receiver
|
||||
chipset: ws2812
|
||||
channel_colors: GRB
|
||||
restore_mode: ALWAYS_OFF
|
||||
@@ -2,6 +2,8 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd # ESP32-PICO-D4, like every other ATOM Lite
|
||||
type: esp-idf
|
||||
|
||||
external_components:
|
||||
|
||||
+4
-1
@@ -2,8 +2,11 @@
|
||||
wifi:
|
||||
# reboot_timeout: 0s
|
||||
networks:
|
||||
- ssid: !secret wifi_ssid
|
||||
# id lets devices override the network with '!extend wifi_iot'
|
||||
- id: wifi_iot
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
enable_btm: true
|
||||
enable_rrm: true
|
||||
min_auth_mode: WPA3
|
||||
power_save_mode: none
|
||||
@@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
// Upcoming calendar events for mcu-m5paper: parsed from the calendar.get_events
|
||||
// response and kept in flash (NVS), so they survive the power-off between RTC wake-ups.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "esphome/components/json/json_util.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/time.h"
|
||||
|
||||
namespace m5paper_calendar {
|
||||
|
||||
static constexpr uint8_t MAX_EVENTS = 32;
|
||||
static constexpr size_t SUMMARY_LEN = 44;
|
||||
static constexpr uint16_t ALL_DAY = 0xFFFF;
|
||||
static constexpr uint32_t PREF_HASH = 0x6D35C41A;
|
||||
|
||||
enum Kind : uint8_t { BIRTHDAY = 0, WASTE };
|
||||
|
||||
struct Event {
|
||||
uint16_t day; // local date, days since 1970-01-01
|
||||
uint16_t minute; // local start time in minutes after midnight, ALL_DAY if none
|
||||
uint8_t kind;
|
||||
char summary[SUMMARY_LEN + 1];
|
||||
};
|
||||
|
||||
struct Events {
|
||||
uint32_t fetched_at{0}; // UTC epoch of the download
|
||||
uint8_t count{0};
|
||||
Event items[MAX_EVENTS]{};
|
||||
|
||||
bool valid() const { return this->fetched_at != 0; }
|
||||
};
|
||||
|
||||
// the events in RAM, loaded from flash at boot
|
||||
inline Events &events() {
|
||||
static Events ev;
|
||||
return ev;
|
||||
}
|
||||
|
||||
// days since 1970-01-01 for a proleptic Gregorian date (H. Hinnant's algorithm)
|
||||
inline int32_t days_from_civil(int y, int m, int d) {
|
||||
y -= m <= 2;
|
||||
const int era = (y >= 0 ? y : y - 399) / 400;
|
||||
const int yoe = y - era * 400;
|
||||
const int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
|
||||
const int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
|
||||
return era * 146097 + doe - 719468;
|
||||
}
|
||||
|
||||
inline int32_t local_day(const esphome::ESPTime &t) { return days_from_civil(t.year, t.month, t.day_of_month); }
|
||||
|
||||
// 0 = Monday
|
||||
inline int weekday(int32_t day) { return (day + 3) % 7; }
|
||||
|
||||
// "2026-10-01" (all-day) or "2026-10-01T10:00:00+02:00" -> local day and minute; false on error
|
||||
inline bool parse_start(const char *s, uint16_t &day, uint16_t &minute) {
|
||||
int y, mo, d, h, mi, sec = 0, oh = 0, om = 0;
|
||||
char sign = '+';
|
||||
int n = sscanf(s, "%d-%d-%dT%d:%d:%d%c%d:%d", &y, &mo, &d, &h, &mi, &sec, &sign, &oh, &om);
|
||||
if (n < 3)
|
||||
return false;
|
||||
if (n < 5) {
|
||||
day = days_from_civil(y, mo, d);
|
||||
minute = ALL_DAY;
|
||||
return true;
|
||||
}
|
||||
int64_t ts = int64_t(days_from_civil(y, mo, d)) * 86400 + h * 3600 + mi * 60 + sec;
|
||||
if (n >= 8 && (sign == '+' || sign == '-')) {
|
||||
int64_t offset = oh * 3600 + om * 60;
|
||||
ts += sign == '+' ? -offset : offset;
|
||||
}
|
||||
if (ts <= 0)
|
||||
return false;
|
||||
const auto t = esphome::ESPTime::from_epoch_local(ts);
|
||||
day = local_day(t);
|
||||
minute = t.hour * 60 + t.minute;
|
||||
return true;
|
||||
}
|
||||
|
||||
// copy at most SUMMARY_LEN bytes without cutting a UTF-8 sequence
|
||||
inline void copy_summary(char *dst, const char *src) {
|
||||
size_t n = strnlen(src, SUMMARY_LEN + 1);
|
||||
if (n > SUMMARY_LEN) {
|
||||
n = SUMMARY_LEN;
|
||||
while (n > 0 && (uint8_t(src[n]) & 0xC0) == 0x80)
|
||||
n--;
|
||||
}
|
||||
memcpy(dst, src, n);
|
||||
dst[n] = '\0';
|
||||
}
|
||||
|
||||
// Parse the calendar.get_events response; Home Assistant wraps the action response as
|
||||
// {"response": {"calendar.x": {"events": [...]}, ...}}; every calendar other than the waste one holds birthdays
|
||||
inline bool parse(ArduinoJson::JsonObjectConst response, const char *waste, uint32_t now, Events &ev) {
|
||||
ArduinoJson::JsonObjectConst root = response["response"].is<ArduinoJson::JsonObjectConst>()
|
||||
? response["response"].as<ArduinoJson::JsonObjectConst>()
|
||||
: response;
|
||||
if (root.size() == 0)
|
||||
return false;
|
||||
|
||||
Events out;
|
||||
for (ArduinoJson::JsonPairConst cal : root) {
|
||||
const uint8_t kind = strcmp(cal.key().c_str(), waste) == 0 ? WASTE : BIRTHDAY;
|
||||
for (ArduinoJson::JsonObjectConst item : cal.value()["events"].as<ArduinoJson::JsonArrayConst>()) {
|
||||
Event e;
|
||||
if (!parse_start(item["start"] | "", e.day, e.minute))
|
||||
continue;
|
||||
e.kind = kind;
|
||||
copy_summary(e.summary, item["summary"] | "");
|
||||
// the same birthday in the contacts and the manual calendar
|
||||
if (std::any_of(out.items, out.items + out.count,
|
||||
[&e](const Event &o) { return o.day == e.day && strcmp(o.summary, e.summary) == 0; }))
|
||||
continue;
|
||||
if (out.count < MAX_EVENTS) {
|
||||
out.items[out.count++] = e;
|
||||
continue;
|
||||
}
|
||||
// full: keep the nearest events
|
||||
Event *last = std::max_element(out.items, out.items + out.count,
|
||||
[](const Event &a, const Event &b) { return a.day < b.day; });
|
||||
if (e.day < last->day)
|
||||
*last = e;
|
||||
}
|
||||
}
|
||||
std::sort(out.items, out.items + out.count, [](const Event &a, const Event &b) {
|
||||
// all-day (0xFFFF wraps to 0) first
|
||||
return a.day != b.day ? a.day < b.day : uint16_t(a.minute + 1) < uint16_t(b.minute + 1);
|
||||
});
|
||||
out.fetched_at = now;
|
||||
ev = out;
|
||||
return true;
|
||||
}
|
||||
|
||||
// battery cycle: refreshed once a day, after midnight
|
||||
inline bool stale(const esphome::ESPTime &now) {
|
||||
const auto &ev = events();
|
||||
return !ev.valid() || !now.is_valid() || now.timestamp - ev.fetched_at >= 20 * 3600;
|
||||
}
|
||||
|
||||
inline esphome::ESPPreferenceObject &pref() {
|
||||
static esphome::ESPPreferenceObject p = esphome::global_preferences->make_preference<Events>(PREF_HASH, true);
|
||||
return p;
|
||||
}
|
||||
|
||||
inline bool load(Events &ev) {
|
||||
if (!pref().load(&ev)) {
|
||||
ev = Events{};
|
||||
return false;
|
||||
}
|
||||
ev.count = std::min(ev.count, MAX_EVENTS);
|
||||
return true;
|
||||
}
|
||||
|
||||
// written to flash right away: the board may power off before the next preferences flush
|
||||
inline bool save(const Events &ev) { return pref().save(&ev) && esphome::global_preferences->sync(); }
|
||||
|
||||
} // namespace m5paper_calendar
|
||||
@@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
// Moonraker client for mcu-cnc-pendant on its own FreeRTOS task: esp_http_client blocks
|
||||
// (DNS is not bounded by timeout_ms), so the main loop only queues commands and reads
|
||||
// the last polled status. An unreachable Klipper shows as "offline" instead of starving
|
||||
// the loop into the task watchdog.
|
||||
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "esp_http_client.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "esphome/components/json/json_util.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace moonraker {
|
||||
|
||||
struct Status {
|
||||
float pos[3]{};
|
||||
std::string homed;
|
||||
std::string state{"offline"};
|
||||
};
|
||||
|
||||
class Client {
|
||||
public:
|
||||
void start(const char *base) {
|
||||
this->base_ = base;
|
||||
xTaskCreate(task, "moonraker", 6144, this, 1, nullptr);
|
||||
}
|
||||
|
||||
// Run G-code; dropped when Klipper is not keeping up.
|
||||
void gcode(const std::string &script) {
|
||||
JsonDocument doc;
|
||||
doc["script"] = script;
|
||||
std::string body;
|
||||
serializeJson(doc, body);
|
||||
this->post("/printer/gcode/script", body);
|
||||
}
|
||||
|
||||
// urgent: throw away everything queued and send this first (emergency stop)
|
||||
void post(const std::string &path, const std::string &body = "", bool urgent = false) {
|
||||
std::lock_guard<std::mutex> lock(this->mutex_);
|
||||
if (urgent) {
|
||||
this->queue_.clear();
|
||||
} else if (this->queue_.size() >= MAX_QUEUE) {
|
||||
return;
|
||||
}
|
||||
this->queue_.emplace_back(path, body);
|
||||
}
|
||||
|
||||
Status status() {
|
||||
std::lock_guard<std::mutex> lock(this->mutex_);
|
||||
return this->status_;
|
||||
}
|
||||
|
||||
protected:
|
||||
static constexpr size_t MAX_QUEUE = 8;
|
||||
static constexpr uint32_t POLL_MS = 250;
|
||||
static constexpr int TIMEOUT_MS = 1000;
|
||||
|
||||
static void task(void *arg) {
|
||||
auto *self = static_cast<Client *>(arg);
|
||||
uint32_t last_poll = 0;
|
||||
for (;;) {
|
||||
std::pair<std::string, std::string> cmd;
|
||||
bool have_cmd = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(self->mutex_);
|
||||
if (!self->queue_.empty()) {
|
||||
cmd = std::move(self->queue_.front());
|
||||
self->queue_.pop_front();
|
||||
have_cmd = true;
|
||||
}
|
||||
}
|
||||
if (have_cmd) {
|
||||
int code = self->request(cmd.first, &cmd.second, nullptr);
|
||||
if (code != 200)
|
||||
ESP_LOGW("moonraker", "POST %s failed (%d): %s", cmd.first.c_str(), code, cmd.second.c_str());
|
||||
continue; // drain commands before the next poll
|
||||
}
|
||||
if (esphome::millis() - last_poll >= POLL_MS) {
|
||||
last_poll = esphome::millis();
|
||||
self->poll();
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
|
||||
void poll() {
|
||||
std::string body;
|
||||
Status s;
|
||||
if (this->request("/printer/objects/query?gcode_move=gcode_position&toolhead=homed_axes&print_stats=state",
|
||||
nullptr, &body) == 200) {
|
||||
auto doc = esphome::json::parse_json(body);
|
||||
JsonObject st = doc["result"]["status"];
|
||||
JsonArray p = st["gcode_move"]["gcode_position"];
|
||||
for (int i = 0; i < 3; i++)
|
||||
s.pos[i] = p[i];
|
||||
s.homed = st["toolhead"]["homed_axes"].as<std::string>();
|
||||
s.state = st["print_stats"]["state"].as<std::string>();
|
||||
} else {
|
||||
std::lock_guard<std::mutex> lock(this->mutex_);
|
||||
s.pos[0] = this->status_.pos[0]; // keep the last position, only flag offline
|
||||
s.pos[1] = this->status_.pos[1];
|
||||
s.pos[2] = this->status_.pos[2];
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(this->mutex_);
|
||||
this->status_ = std::move(s);
|
||||
}
|
||||
|
||||
// POST when body is set, GET otherwise; returns the HTTP status or -1
|
||||
int request(const std::string &path, const std::string *body, std::string *out) {
|
||||
std::string url = this->base_ + path;
|
||||
esp_http_client_config_t cfg{};
|
||||
cfg.url = url.c_str();
|
||||
cfg.timeout_ms = TIMEOUT_MS;
|
||||
cfg.method = body ? HTTP_METHOD_POST : HTTP_METHOD_GET;
|
||||
esp_http_client_handle_t c = esp_http_client_init(&cfg);
|
||||
if (c == nullptr)
|
||||
return -1;
|
||||
int code = -1;
|
||||
int len = body ? body->size() : 0;
|
||||
if (len > 0)
|
||||
esp_http_client_set_header(c, "Content-Type", "application/json");
|
||||
if (esp_http_client_open(c, len) == ESP_OK && (len == 0 || esp_http_client_write(c, body->data(), len) == len)) {
|
||||
esp_http_client_fetch_headers(c);
|
||||
code = esp_http_client_get_status_code(c);
|
||||
char buf[256];
|
||||
int n;
|
||||
while (out != nullptr && (n = esp_http_client_read(c, buf, sizeof(buf))) > 0)
|
||||
out->append(buf, n);
|
||||
}
|
||||
esp_http_client_cleanup(c);
|
||||
return code;
|
||||
}
|
||||
|
||||
std::string base_;
|
||||
std::mutex mutex_;
|
||||
std::deque<std::pair<std::string, std::string>> queue_;
|
||||
Status status_;
|
||||
};
|
||||
|
||||
inline Client client;
|
||||
|
||||
} // namespace moonraker
|
||||
+17
-10
@@ -1,4 +1,12 @@
|
||||
---
|
||||
# Hardware: IKEA VINDRIKTNING (modded) + M5Stack M5Stamp Pico
|
||||
# Pinout:
|
||||
# GPIO21 PM1006 UART -> particle_uart (uart tx_pin)
|
||||
# GPIO22 PM1006 UART -> particle_uart (uart rx_pin)
|
||||
# GPIO25 TZT 8-bit WS2812 5050 stick (8 LEDs, 7 visible outside) -> NEO [neo] (light esp32_rmt_led_strip), also status
|
||||
# GPIO27 Onboard RGB LED (1x WS2812, inside), unused
|
||||
# GPIO32 Grove SDA -> i2c (i2c sda)
|
||||
# GPIO33 Grove SCL -> i2c (i2c scl)
|
||||
substitutions:
|
||||
device: air-office-r
|
||||
name: Office R Air
|
||||
@@ -16,10 +24,16 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
status_led: !include
|
||||
file: common/status_led.yaml
|
||||
vars:
|
||||
light_id: neo
|
||||
|
||||
i2c:
|
||||
# - id: i2c0
|
||||
@@ -46,20 +60,13 @@ uart:
|
||||
# inverted: true
|
||||
|
||||
light:
|
||||
- platform: esp32_rmt_led_strip
|
||||
# name: LED
|
||||
id: neo
|
||||
channel_colors: GRB
|
||||
pin: 27
|
||||
num_leds: 1
|
||||
chipset: ws2812
|
||||
restore_mode: ALWAYS_OFF
|
||||
# also the status light (common/status_led.yaml): back to its previous state when all is fine
|
||||
- platform: esp32_rmt_led_strip
|
||||
name: NEO
|
||||
id: neo_strip
|
||||
id: neo
|
||||
channel_colors: GRB
|
||||
pin: 25
|
||||
num_leds: 7
|
||||
num_leds: 7 # 8 on the stick, the 8th one is hidden
|
||||
chipset: ws2812
|
||||
restore_mode: ALWAYS_OFF
|
||||
effects:
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
---
|
||||
# Hardware: Apollo Automation ESK-1 ESPHome Starter Kit
|
||||
# Pinout:
|
||||
# GPIO0 -> i2c_1 (i2c scl)
|
||||
# GPIO1 -> i2c_1 (i2c sda)
|
||||
# GPIO4 -> Accessory Power (switch gpio)
|
||||
# GPIO5 -> Onboard RGB LED (light esp32_rmt_led_strip)
|
||||
# GPIO14 -> RGB LEDs (light esp32_rmt_led_strip)
|
||||
# GPIO18 -> buzzer_output (output ledc)
|
||||
# I2C:
|
||||
# 0x38 aht20 (sensor aht10)
|
||||
substitutions:
|
||||
device: apollo-dev #test
|
||||
name: Apollo DEV #test
|
||||
@@ -16,6 +26,8 @@ esp32:
|
||||
flash_size: 8MB
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
|
||||
+60
-5
@@ -1,4 +1,23 @@
|
||||
---
|
||||
# Hardware: M5Stack AtomS3
|
||||
# Pinout:
|
||||
# GPIO1 Grove SCL -> motor_in1 (output gpio)
|
||||
# GPIO2 Grove SDA -> motor_in2 (output gpio)
|
||||
# GPIO5 -> canbus/esp32_can (canbus esp32_can tx_pin)
|
||||
# GPIO6 -> canbus/esp32_can (canbus esp32_can rx_pin)
|
||||
# GPIO15 -> display_tft (display mipi_spi cs_pin)
|
||||
# GPIO16 -> gpio_tft_backlight (output ledc)
|
||||
# GPIO17 -> spi (spi clk_pin)
|
||||
# GPIO21 -> spi (spi mosi_pin)
|
||||
# GPIO33 -> display_tft (display mipi_spi dc_pin)
|
||||
# GPIO34 -> display_tft (display mipi_spi reset_pin)
|
||||
# GPIO38 -> i2c_main (i2c sda)
|
||||
# GPIO39 -> i2c_main (i2c scl)
|
||||
# GPIO41 -> Button (binary_sensor gpio) (inverted)
|
||||
# I2C:
|
||||
# 0x21 pcf8574_hub (pcf8574)
|
||||
# 0x26 m5stack_relay_1 (m5stack_4relay)
|
||||
# 0x3C lcd (display ssd1306_i2c)
|
||||
substitutions:
|
||||
device: atoms3
|
||||
name: AtomS3
|
||||
@@ -23,6 +42,8 @@ esp32:
|
||||
flash_size: 8MB
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
ota:
|
||||
@@ -33,12 +54,14 @@ pcf8574:
|
||||
address: 0x21
|
||||
pcf8575: true
|
||||
|
||||
animation:
|
||||
- file: "assets/images/blinky.gif"
|
||||
image:
|
||||
- platform: animation
|
||||
file: "assets/images/blinky.gif"
|
||||
id: blinky
|
||||
resize: 128x128
|
||||
type: RGB565
|
||||
- file: "assets/images/vault_boy_walking.gif"
|
||||
- platform: animation
|
||||
file: "assets/images/vault_boy_walking.gif"
|
||||
id: vaultboy
|
||||
resize: 168x121
|
||||
type: RGB565
|
||||
@@ -108,6 +131,11 @@ external_components:
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include
|
||||
file: common/status_led.yaml
|
||||
vars:
|
||||
light_id: status_frame
|
||||
# common: !include common/common_wo_debug.yaml
|
||||
# common: !include common/common_dev.yaml
|
||||
|
||||
@@ -211,12 +239,32 @@ output:
|
||||
- platform: ledc
|
||||
pin: 16
|
||||
id: gpio_tft_backlight
|
||||
# virtual RGB light for common/status_led.yaml, shown full screen on the display
|
||||
- platform: template
|
||||
id: status_frame_r
|
||||
type: float
|
||||
write_action: []
|
||||
- platform: template
|
||||
id: status_frame_g
|
||||
type: float
|
||||
write_action: []
|
||||
- platform: template
|
||||
id: status_frame_b
|
||||
type: float
|
||||
write_action: []
|
||||
|
||||
light:
|
||||
- platform: monochromatic
|
||||
id: display_tft_backlight
|
||||
output: gpio_tft_backlight
|
||||
restore_mode: ALWAYS_ON
|
||||
- platform: rgb
|
||||
id: status_frame
|
||||
internal: true
|
||||
red: status_frame_r
|
||||
green: status_frame_g
|
||||
blue: status_frame_b
|
||||
restore_mode: ALWAYS_OFF
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
@@ -372,6 +420,7 @@ display:
|
||||
- platform: mipi_spi
|
||||
model: ST7789V
|
||||
id: display_tft
|
||||
byte_order: little_endian # colours were byte-swapped (purple showed as green)
|
||||
dimensions:
|
||||
height: 128
|
||||
width: 128
|
||||
@@ -384,11 +433,18 @@ display:
|
||||
# eightbitcolor: true
|
||||
update_interval: 1s
|
||||
lambda: |-
|
||||
// status screen (common/status_led.yaml): the whole display in the status colour
|
||||
auto &sv = id(status_frame)->current_values;
|
||||
if (sv.is_on()) {
|
||||
it.fill(Color(sv.get_red() * 255, sv.get_green() * 255, sv.get_blue() * 255));
|
||||
return;
|
||||
}
|
||||
//Ingress shown animation Frame.
|
||||
id(blinky).next_frame();
|
||||
// Draw the animation my_animation at position [x=0,y=0]
|
||||
it.image(0, 0, id(blinky), COLOR_ON, COLOR_OFF);
|
||||
|
||||
|
||||
// // it.print(0, 0, id(font_default), "Hello World!");
|
||||
// // it.strftime(0, 0, id(font_default), TextAlign::TOP_LEFT, "%H:%M", id(ha_time).now());
|
||||
// it.rectangle(0, 0, it.get_width(), it.get_height(), id(my_blue));
|
||||
@@ -491,8 +547,7 @@ sensor:
|
||||
# name: Dallas Temp 4
|
||||
# one_wire_id: hub_1
|
||||
|
||||
time:
|
||||
- !include common/time/homeassistant.yaml
|
||||
# time:
|
||||
# - platform: sntp
|
||||
# id: sntp_time
|
||||
# timezone: Europe/Bratislava
|
||||
|
||||
+9
-8
@@ -1,4 +1,12 @@
|
||||
---
|
||||
# Hardware: Sonoff Dual R2
|
||||
# Pinout:
|
||||
# GPIO0 Pin "button 0" on expansion header
|
||||
# GPIO5 Relay #2 and green color on LED -> relay_2 (output gpio)
|
||||
# GPIO9 Pin "button 1" on expansion header
|
||||
# GPIO10 Push Button
|
||||
# GPIO12 Relay #1 and red color on LED -> relay_1 (output gpio)
|
||||
# GPIO13 Blue Status LED -> status_led (status_led) (inverted)
|
||||
substitutions:
|
||||
device: basement-2
|
||||
name_short: Basement
|
||||
@@ -17,14 +25,7 @@ esp8266:
|
||||
|
||||
packages:
|
||||
common: !include common/common_esp8266.yaml
|
||||
|
||||
# Sonoff Dual R2
|
||||
# GPIO10 Push Button
|
||||
# GPIO00 Pin "button 0" on expansion header
|
||||
# GPIO09 Pin "button 1" on expansion header
|
||||
# GPIO12 Relay #1 and red color on LED
|
||||
# GPIO05 Relay #2 and green color on LED
|
||||
# GPIO13 Blue Status LED
|
||||
mdns_ipv6: !include common/esp8266_mdns_ipv6.yaml
|
||||
|
||||
status_led:
|
||||
pin:
|
||||
|
||||
+10
-11
@@ -1,4 +1,12 @@
|
||||
---
|
||||
# Hardware: Sonoff Dual R2
|
||||
# Pinout:
|
||||
# GPIO0 Pin "button 0" on expansion header -> Light Switch (binary_sensor gpio) (inverted)
|
||||
# GPIO5 Relay #2 and green color on LED -> Socket 1 (switch gpio)
|
||||
# GPIO9 Pin "button 1" on expansion header -> Door (binary_sensor gpio) (inverted)
|
||||
# GPIO10 Push Button -> Socket 1 Switch (binary_sensor gpio) (inverted)
|
||||
# GPIO12 Relay #1 and red color on LED -> Sewage Treatment Aerator Pump [relay_1] (switch gpio)
|
||||
# GPIO13 Blue Status LED -> status_led (status_led) (inverted)
|
||||
substitutions:
|
||||
device: basement
|
||||
name: Basement
|
||||
@@ -16,15 +24,7 @@ esp8266:
|
||||
|
||||
packages:
|
||||
common: !include common/common_esp8266.yaml
|
||||
|
||||
# Sonoff Dual R2
|
||||
# Pin Function
|
||||
# GPIO10 Push Button
|
||||
# GPIO00 Pin "button 0" on expansion header
|
||||
# GPIO09 Pin "button 1" on expansion header
|
||||
# GPIO12 Relay #1 and red color on LED
|
||||
# GPIO05 Relay #2 and green color on LED
|
||||
# GPIO13 Blue Status LED
|
||||
mdns_ipv6: !include common/esp8266_mdns_ipv6.yaml
|
||||
|
||||
globals:
|
||||
- id: aerator_auto
|
||||
@@ -139,8 +139,7 @@ switch:
|
||||
id: relay_2
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
timezone: Europe/Bratislava
|
||||
- id: !extend ha_time
|
||||
on_time:
|
||||
- seconds: 0
|
||||
minutes: /15
|
||||
|
||||
+24
-10
@@ -1,4 +1,23 @@
|
||||
---
|
||||
# Hardware: Xiaomi Mi Bedside Lamp 2 (ESP-WROOM-32D, single core; RGBW LEDs)
|
||||
# Pinout (github.com/mmakaay/esphome-xiaomi_bslamp2, doc/technical_details.md):
|
||||
# GPIO1 Debug pad TX
|
||||
# GPIO2 Debug pad, free I/O
|
||||
# GPIO3 Debug pad RX
|
||||
# GPIO4 LEDs master switch 2
|
||||
# GPIO5 LEDs blue PWM
|
||||
# GPIO12 LEDs white PWM
|
||||
# GPIO13 LEDs red PWM
|
||||
# GPIO14 LEDs green PWM
|
||||
# GPIO16 Front panel interrupt
|
||||
# GPIO17 EEPROM I2C SDA
|
||||
# GPIO18 EEPROM I2C SCL
|
||||
# GPIO19 Front panel I2C SCL -> front_panel_i2c (i2c scl)
|
||||
# GPIO21 Front panel I2C SDA -> front_panel_i2c (i2c sda)
|
||||
# GPIO33 LEDs master switch 1
|
||||
# I2C:
|
||||
# 0x10 EEPROM-like IC on its own bus (GPIO17/18), not configured
|
||||
# 0x2C Front panel
|
||||
substitutions:
|
||||
device: bedroom-lamp-left
|
||||
name: Bedroom Lamp Left
|
||||
@@ -20,26 +39,21 @@ esp32:
|
||||
sdkconfig_options:
|
||||
CONFIG_FREERTOS_UNICORE: y
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
ignore_efuse_custom_mac: true
|
||||
ignore_efuse_mac_crc: true
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/mmakaay/esphome-xiaomi_bslamp2
|
||||
ref: dev
|
||||
# fork: fix for ESPHome 2026.10 (restore_mode -> restore_state), back to mmakaay once merged
|
||||
url: https://github.com/randybb/esphome-xiaomi_bslamp2
|
||||
ref: fix-light-restore-state
|
||||
refresh: 1h
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
# Mi Desk Lamp
|
||||
# Pin Function (Name)
|
||||
# GPIO02: Button (GPIO_KEY1)
|
||||
# GPIO04: Cold White (GPIO_PWM1)
|
||||
# GPIO05: Warm White (GPIO_PWM2)
|
||||
# GPIO12: Rotary switch A pin (GPIO_ROT_A)
|
||||
# GPIO13: Rotary switch B pin (GPIO_ROT_B)
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Behavior: default
|
||||
|
||||
+24
-10
@@ -1,4 +1,23 @@
|
||||
---
|
||||
# Hardware: Xiaomi Mi Bedside Lamp 2 (ESP-WROOM-32D, single core; RGBW LEDs)
|
||||
# Pinout (github.com/mmakaay/esphome-xiaomi_bslamp2, doc/technical_details.md):
|
||||
# GPIO1 Debug pad TX
|
||||
# GPIO2 Debug pad, free I/O
|
||||
# GPIO3 Debug pad RX
|
||||
# GPIO4 LEDs master switch 2
|
||||
# GPIO5 LEDs blue PWM
|
||||
# GPIO12 LEDs white PWM
|
||||
# GPIO13 LEDs red PWM
|
||||
# GPIO14 LEDs green PWM
|
||||
# GPIO16 Front panel interrupt
|
||||
# GPIO17 EEPROM I2C SDA
|
||||
# GPIO18 EEPROM I2C SCL
|
||||
# GPIO19 Front panel I2C SCL -> front_panel_i2c (i2c scl)
|
||||
# GPIO21 Front panel I2C SDA -> front_panel_i2c (i2c sda)
|
||||
# GPIO33 LEDs master switch 1
|
||||
# I2C:
|
||||
# 0x10 EEPROM-like IC on its own bus (GPIO17/18), not configured
|
||||
# 0x2C Front panel
|
||||
substitutions:
|
||||
device: bedroom-lamp-right
|
||||
name: Bedroom Lamp Right
|
||||
@@ -20,26 +39,21 @@ esp32:
|
||||
sdkconfig_options:
|
||||
CONFIG_FREERTOS_UNICORE: y
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
ignore_efuse_custom_mac: true
|
||||
ignore_efuse_mac_crc: true
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/mmakaay/esphome-xiaomi_bslamp2
|
||||
ref: dev
|
||||
# fork: fix for ESPHome 2026.10 (restore_mode -> restore_state), back to mmakaay once merged
|
||||
url: https://github.com/randybb/esphome-xiaomi_bslamp2
|
||||
ref: fix-light-restore-state
|
||||
refresh: 60s
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
# Mi Desk Lamp
|
||||
# Pin Function (Name)
|
||||
# GPIO02: Button (GPIO_KEY1)
|
||||
# GPIO04: Cold White (GPIO_PWM1)
|
||||
# GPIO05: Warm White (GPIO_PWM2)
|
||||
# GPIO12: Rotary switch A pin (GPIO_ROT_A)
|
||||
# GPIO13: Rotary switch B pin (GPIO_ROT_B)
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Behavior: default
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
---
|
||||
# Hardware: Espressif ESP32-C6-DevKitC-1
|
||||
# Pinout:
|
||||
# GPIO8 -> LED (light esp32_rmt_led_strip)
|
||||
substitutions:
|
||||
device: c6-test-1
|
||||
name: C6 TEST 1
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
---
|
||||
# Hardware: Espressif ESP32-C6-DevKitC-1
|
||||
# Pinout:
|
||||
# GPIO8 -> LED (light esp32_rmt_led_strip)
|
||||
substitutions:
|
||||
device: c6-test-2
|
||||
name: C6 TEST 2
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
---
|
||||
# Hardware: ESP32-CAM (AI-Thinker style, runs hot)
|
||||
# Pinout:
|
||||
# GPIO0 -> Camera (esp32_camera)
|
||||
# GPIO5 -> Camera (esp32_camera data_pins)
|
||||
# GPIO18 -> Camera (esp32_camera data_pins)
|
||||
# GPIO19 -> Camera (esp32_camera data_pins)
|
||||
# GPIO21 -> Camera (esp32_camera data_pins)
|
||||
# GPIO22 -> Camera (esp32_camera pixel_clock_pin)
|
||||
# GPIO23 -> Camera (esp32_camera href_pin)
|
||||
# GPIO25 -> Camera (esp32_camera vsync_pin)
|
||||
# GPIO26 -> i2c_bus (i2c sda)
|
||||
# GPIO27 -> i2c_bus (i2c scl)
|
||||
# GPIO32 -> Enable (switch gpio)
|
||||
# GPIO34 -> Camera (esp32_camera data_pins)
|
||||
# GPIO35 -> Camera (esp32_camera data_pins)
|
||||
# GPIO36 -> Camera (esp32_camera data_pins)
|
||||
# GPIO39 -> Camera (esp32_camera data_pins)
|
||||
substitutions:
|
||||
device: cam-1
|
||||
name: Cam 1
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
---
|
||||
# Hardware: ESP32 DevKit
|
||||
# Pinout:
|
||||
# GPIO0 -> Boot (binary_sensor gpio) (inverted)
|
||||
# GPIO2 -> LED (switch gpio)
|
||||
# GPIO22 -> can_bus (canbus esp32_can rx_pin)
|
||||
# GPIO23 -> can_bus (canbus esp32_can tx_pin)
|
||||
substitutions:
|
||||
device: can-node-1
|
||||
name: CAN Node 1
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
---
|
||||
# Hardware: ESP32 DevKit
|
||||
# Pinout:
|
||||
# GPIO17 -> i2c (i2c sda)
|
||||
# GPIO18 -> i2c (i2c scl)
|
||||
# GPIO19 -> as3935_i2c (as3935_i2c irq_pin)
|
||||
# GPIO22 -> can_bus (canbus esp32_can rx_pin)
|
||||
# GPIO23 -> can_bus (canbus esp32_can tx_pin)
|
||||
# I2C:
|
||||
# 0x03 as3935_i2c (as3935_i2c)
|
||||
substitutions:
|
||||
device: can-node-2
|
||||
name: CAN Node 2
|
||||
|
||||
+11
-5
@@ -1,4 +1,13 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Stamp Pico
|
||||
# Pinout:
|
||||
# GPIO22 -> uart_distance (uart rx_pin)
|
||||
# GPIO25 -> uart_distance (uart tx_pin)
|
||||
# GPIO26 -> hdmi_cec (hdmi_cec)
|
||||
# GPIO27 -> LED (light esp32_rmt_led_strip)
|
||||
# GPIO32 Grove SDA -> i2c (i2c sda)
|
||||
# GPIO33 Grove SCL -> i2c (i2c scl)
|
||||
# GPIO39 -> Bedroom TV Button (binary_sensor gpio) (inverted)
|
||||
substitutions:
|
||||
device: cec-bedroom
|
||||
name: Bedroom TV
|
||||
@@ -15,6 +24,8 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd # ESP32-PICO-D4, like the other M5Stamp Pico (not read from the device)
|
||||
type: esp-idf
|
||||
|
||||
external_components:
|
||||
@@ -31,11 +42,6 @@ packages:
|
||||
|
||||
debug:
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
|
||||
i2c:
|
||||
# - id: i2c0
|
||||
sda: 32
|
||||
|
||||
+17
-20
@@ -1,4 +1,18 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Stamp C3
|
||||
# Pinout:
|
||||
# GPIO0 I2C SCL
|
||||
# GPIO1 I2C SDA, BUTTON
|
||||
# GPIO2 RGB
|
||||
# GPIO4 DISPLAY CLOCK -> tm1637_display (display tm1637 clk_pin)
|
||||
# GPIO5 DISPLAY DIO -> tm1637_display (display tm1637 dio_pin)
|
||||
# GPIO6 GPIO
|
||||
# GPIO7 GPIO
|
||||
# GPIO8 GPIO
|
||||
# GPIO9 GPIO (BUZZER?) -> rtttl_out (output ledc)
|
||||
# GPIO10 GPIO
|
||||
# GPIO20 UART RX
|
||||
# GPIO21 UART TX
|
||||
substitutions:
|
||||
device: clock-1
|
||||
name: Clock 1
|
||||
@@ -17,27 +31,15 @@ esp32:
|
||||
variant: esp32c3
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
type: esp-idf
|
||||
# version: 5.4.2
|
||||
# platform_version: 54.03.21
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
# GPIO21 UART TX
|
||||
# GPIO20 UART RX
|
||||
# GPIO0 I2C SCL
|
||||
# GPIO1 I2C SDA
|
||||
# GPIO2 RGB
|
||||
# GPIO1 BUTTON
|
||||
# GPIO4 DISPLAY CLOCK
|
||||
# GPIO5 DISPLAY DIO
|
||||
# GPIO6 GPIO
|
||||
# GPIO7 GPIO
|
||||
# GPIO8 GPIO
|
||||
# GPIO10 GPIO
|
||||
#
|
||||
# GPIO9 GPIO (BUZZER?)
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
|
||||
api:
|
||||
@@ -109,11 +111,6 @@ globals:
|
||||
restore_value: no
|
||||
initial_value: "false"
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
|
||||
display:
|
||||
- platform: tm1637
|
||||
id: tm1637_display
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Stamp Pico
|
||||
# Pinout:
|
||||
# GPIO25 -> rtttl_out (output ledc)
|
||||
# GPIO27 -> LED (light esp32_rmt_led_strip)
|
||||
# GPIO32 Grove SDA -> i2c_main (i2c sda)
|
||||
# GPIO33 Grove SCL -> i2c_main (i2c scl)
|
||||
# GPIO39 -> Button (binary_sensor gpio) (inverted)
|
||||
# I2C:
|
||||
# 0x24 tm1650_display (display tm1650)
|
||||
substitutions:
|
||||
device: clock-living-room
|
||||
name: Living room Clock
|
||||
@@ -15,6 +24,8 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd # ESP32-PICO-D4, like the other M5Stamp Pico (not read from the device)
|
||||
type: esp-idf
|
||||
|
||||
external_components:
|
||||
@@ -51,11 +62,6 @@ globals:
|
||||
restore_value: no
|
||||
initial_value: "false"
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
name: Button
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Stamp S3 in the ESPendant case (github.com/positron96/ESP32_CNC_Pendant),
|
||||
# talks to Klipper over Moonraker HTTP API
|
||||
# Pinout:
|
||||
# GPIO0 Stamp button (boot) -> stamp_button (binary_sensor gpio) (inverted)
|
||||
# GPIO1 Handwheel A -> handwheel (sensor rotary_encoder pin_a)
|
||||
# GPIO2 Handwheel B -> handwheel (sensor rotary_encoder pin_b)
|
||||
# GPIO4 Axis encoder A -> axis_encoder (sensor rotary_encoder pin_a)
|
||||
# GPIO5 Axis encoder B -> axis_encoder (sensor rotary_encoder pin_b)
|
||||
# GPIO6 Axis encoder push -> axis_button (binary_sensor gpio) (inverted)
|
||||
# GPIO7 Step encoder A -> step_encoder (sensor rotary_encoder pin_a)
|
||||
# GPIO8 Step encoder B -> step_encoder (sensor rotary_encoder pin_b)
|
||||
# GPIO9 Step encoder push -> step_button (binary_sensor gpio) (inverted)
|
||||
# GPIO10 BT1 -> bt1 (binary_sensor gpio) (inverted)
|
||||
# GPIO11 BT2 -> bt2 (binary_sensor gpio) (inverted)
|
||||
# GPIO12 BT3 -> bt3 (binary_sensor gpio) (inverted)
|
||||
# GPIO13 LCD E (SCK) -> spi (spi clk_pin)
|
||||
# GPIO14 LCD R/W (MOSI) -> spi (spi mosi_pin)
|
||||
# GPIO15 LCD RS (CS) -> lcd (display st7920 cs_pin)
|
||||
# GPIO21 RGB LED -> status_rgb (light esp32_rmt_led_strip)
|
||||
# GPIO39-44 free (BT4-BT6 later)
|
||||
# LCD RST: RC to 3.3 V, PSB to GND (serial mode)
|
||||
substitutions:
|
||||
device: cnc-pendant
|
||||
name: CNC Pendant
|
||||
area: Workshop
|
||||
comment: Klipper CNC pendant
|
||||
moonraker: http://klipper.local:7125
|
||||
max_feed: "3000" # mm/min, handwheel jog feed cap
|
||||
|
||||
esphome:
|
||||
name: mcu-${device}
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
includes:
|
||||
- include/moonraker.h
|
||||
on_boot:
|
||||
- lambda: 'moonraker::client.start("${moonraker}");'
|
||||
|
||||
esp32:
|
||||
variant: esp32s3
|
||||
flash_size: 8MB
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
# ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include
|
||||
file: common/status_led.yaml
|
||||
vars:
|
||||
light_id: status_rgb
|
||||
|
||||
light:
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: status_rgb
|
||||
name: LED
|
||||
pin: 21
|
||||
num_leds: 1
|
||||
chipset: ws2812
|
||||
rgb_order: GRB
|
||||
restore_mode: ALWAYS_OFF
|
||||
|
||||
# only links esp_http_client for include/moonraker.h, which runs requests on its own task
|
||||
http_request:
|
||||
|
||||
globals:
|
||||
- id: axis
|
||||
type: int
|
||||
initial_value: "0" # 0 X, 1 Y, 2 Z
|
||||
- id: step
|
||||
type: int
|
||||
initial_value: "1" # index into steps[] in the jog interval
|
||||
- id: wheel_sent
|
||||
type: float
|
||||
initial_value: "0"
|
||||
|
||||
interval:
|
||||
# handwheel: send the counts since the last jog as one relative move
|
||||
- interval: 100ms
|
||||
then:
|
||||
- lambda: |-
|
||||
static const float steps[] = {0.01f, 0.1f, 1.0f};
|
||||
float delta = id(handwheel).state - id(wheel_sent);
|
||||
if (delta == 0 || std::isnan(delta)) return;
|
||||
id(wheel_sent) = id(handwheel).state;
|
||||
float dist = delta * steps[id(step)];
|
||||
// feed so the move takes ~100 ms, i.e. about one interval
|
||||
// ponytail: capped feed lets fast spins queue moves that keep running after the wheel stops; drop excess counts if that bites
|
||||
float feed = std::min(std::max(std::fabs(dist) * 600.0f, 60.0f), ${max_feed}.0f);
|
||||
moonraker::client.gcode(str_sprintf("G91\nG1 %c%.3f F%.0f\nG90", "XYZ"[id(axis)], dist, feed));
|
||||
|
||||
sensor:
|
||||
- platform: rotary_encoder
|
||||
id: handwheel
|
||||
pin_a:
|
||||
number: 1
|
||||
mode: INPUT_PULLUP
|
||||
pin_b:
|
||||
number: 2
|
||||
mode: INPUT_PULLUP
|
||||
resolution: 1 # calibration: counts per detent, flip pin_a/pin_b if direction is wrong
|
||||
- platform: rotary_encoder
|
||||
id: axis_encoder
|
||||
pin_a:
|
||||
number: 4
|
||||
mode: INPUT_PULLUP
|
||||
pin_b:
|
||||
number: 5
|
||||
mode: INPUT_PULLUP
|
||||
on_clockwise:
|
||||
- lambda: 'id(axis) = std::min(id(axis) + 1, 2);'
|
||||
on_anticlockwise:
|
||||
- lambda: 'id(axis) = std::max(id(axis) - 1, 0);'
|
||||
- platform: rotary_encoder
|
||||
id: step_encoder
|
||||
pin_a:
|
||||
number: 7
|
||||
mode: INPUT_PULLUP
|
||||
pin_b:
|
||||
number: 8
|
||||
mode: INPUT_PULLUP
|
||||
on_clockwise:
|
||||
- lambda: 'id(step) = std::min(id(step) + 1, 2);'
|
||||
on_anticlockwise:
|
||||
- lambda: 'id(step) = std::max(id(step) - 1, 0);'
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
id: stamp_button
|
||||
name: Stamp Button
|
||||
pin:
|
||||
number: 0
|
||||
inverted: true
|
||||
# axis push: zero the selected axis in work coordinates
|
||||
- platform: gpio
|
||||
id: axis_button
|
||||
name: Axis Button
|
||||
pin:
|
||||
number: 6
|
||||
mode: INPUT_PULLUP
|
||||
inverted: true
|
||||
filters:
|
||||
- delayed_on_off: 20ms
|
||||
on_press:
|
||||
- lambda: 'moonraker::client.gcode(str_sprintf("G92 %c0", "XYZ"[id(axis)]));'
|
||||
- platform: gpio
|
||||
id: step_button
|
||||
name: Step Button
|
||||
pin:
|
||||
number: 9
|
||||
mode: INPUT_PULLUP
|
||||
inverted: true
|
||||
filters:
|
||||
- delayed_on_off: 20ms
|
||||
# BT1: home all
|
||||
- platform: gpio
|
||||
id: bt1
|
||||
name: BT1
|
||||
pin:
|
||||
number: 10
|
||||
mode: INPUT_PULLUP
|
||||
inverted: true
|
||||
filters:
|
||||
- delayed_on_off: 20ms
|
||||
on_press:
|
||||
- lambda: 'moonraker::client.gcode("G28");'
|
||||
# BT2: pause / resume
|
||||
- platform: gpio
|
||||
id: bt2
|
||||
name: BT2
|
||||
pin:
|
||||
number: 11
|
||||
mode: INPUT_PULLUP
|
||||
inverted: true
|
||||
filters:
|
||||
- delayed_on_off: 20ms
|
||||
on_press:
|
||||
- lambda: 'moonraker::client.post(moonraker::client.status().state == "paused" ? "/printer/print/resume" : "/printer/print/pause");'
|
||||
# BT3: hold 1 s for emergency stop (Klipper needs FIRMWARE_RESTART afterwards)
|
||||
- platform: gpio
|
||||
id: bt3
|
||||
name: BT3
|
||||
pin:
|
||||
number: 12
|
||||
mode: INPUT_PULLUP
|
||||
inverted: true
|
||||
filters:
|
||||
- delayed_on_off: 20ms
|
||||
on_multi_click:
|
||||
- timing:
|
||||
- ON for at least 1s
|
||||
then:
|
||||
- lambda: 'moonraker::client.post("/printer/emergency_stop", "", true);'
|
||||
|
||||
font:
|
||||
# X11 misc-fixed bitmap fonts (as in u8g2 / Marlin), 6 px wide: axis letter + "-123.456" fits 64 px
|
||||
- file: assets/fonts/6x13.bdf
|
||||
id: font_big
|
||||
- file: assets/fonts/6x10.bdf
|
||||
id: font_small
|
||||
|
||||
spi:
|
||||
clk_pin: 13
|
||||
mosi_pin: 14
|
||||
|
||||
display:
|
||||
- platform: st7920
|
||||
id: lcd
|
||||
cs_pin:
|
||||
number: 15
|
||||
inverted: true # ST7920 RS/CS is active high
|
||||
data_rate: 1MHz # default 200kHz takes ~220 ms per frame; drop back if the picture garbles
|
||||
width: 128
|
||||
height: 64
|
||||
rotation: 90 # mounted portrait, 270 if upside down
|
||||
update_interval: 200ms
|
||||
lambda: |-
|
||||
static const char *steps[] = {"0.01", "0.1", "1"};
|
||||
auto s = moonraker::client.status();
|
||||
it.printf(0, 0, id(font_small), "%s", s.state.c_str());
|
||||
it.printf(0, 10, id(font_small), "H:%s", s.homed.empty() ? "-" : s.homed.c_str());
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int y = 22 + i * 14;
|
||||
Color c = COLOR_ON;
|
||||
if (i == id(axis)) {
|
||||
it.filled_rectangle(0, y, 64, 13);
|
||||
c = COLOR_OFF;
|
||||
}
|
||||
it.printf(1, y + 1, id(font_big), c, "%c", "XYZ"[i]);
|
||||
it.printf(63, y + 1, id(font_big), c, TextAlign::TOP_RIGHT, "%.3f", s.pos[i]);
|
||||
}
|
||||
it.printf(0, 118, id(font_small), "%s mm", steps[id(step)]);
|
||||
+10
-8
@@ -1,4 +1,11 @@
|
||||
---
|
||||
# Hardware: Xiaomi Mi Desk Lamp, brain transplant: M5Stack M5Stamp Pico (originally ESP8266)
|
||||
# Pinout:
|
||||
# GPIO18 Warm White (GPIO_PWM2) -> warm_white (output ledc)
|
||||
# GPIO19 Cold White (GPIO_PWM1) -> cold_white (output ledc)
|
||||
# GPIO21 Rotary switch A pin (GPIO_ROT_A) -> rotary (sensor rotary_encoder pin_a)
|
||||
# GPIO26 Rotary switch B pin (GPIO_ROT_B) -> rotary (sensor rotary_encoder pin_b)
|
||||
# GPIO36 Button (GPIO_KEY1) -> rotary_button (binary_sensor gpio) (inverted)
|
||||
substitutions:
|
||||
device: desk-lamp
|
||||
name: Desk Lamp
|
||||
@@ -15,18 +22,13 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
# Mi Desk Lamp
|
||||
# Pin Function (Name)
|
||||
# GPIO18: Warm White (GPIO_PWM2)
|
||||
# GPIO19: Cold White (GPIO_PWM1)
|
||||
# GPIO21: Rotary switch A pin (GPIO_ROT_A)
|
||||
# GPIO26: Rotary switch B pin (GPIO_ROT_B)
|
||||
# GPIO36: Button (GPIO_KEY1)
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
|
||||
binary_sensor:
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Stamp Pico
|
||||
# Pinout:
|
||||
# GPIO22 -> dimmer1 (output ac_dimmer gate_pin)
|
||||
# GPIO25 -> dimmer1 (output ac_dimmer zero_cross_pin) (inverted)
|
||||
# GPIO27 -> LED (light esp32_rmt_led_strip)
|
||||
# GPIO32 Grove SDA
|
||||
# GPIO33 Grove SCL
|
||||
substitutions:
|
||||
device: dimmer
|
||||
name: Dimmer
|
||||
@@ -30,10 +37,13 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
# fan:
|
||||
# - platform: speed
|
||||
|
||||
+19
-2
@@ -1,4 +1,14 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Echo
|
||||
# Pinout:
|
||||
# GPIO19 -> speaker_bus (i2s_audio i2s_bclk_pin)
|
||||
# GPIO22 -> speaker_id (speaker i2s_audio i2s_dout_pin)
|
||||
# GPIO23 -> echo_microphone (microphone i2s_audio i2s_din_pin)
|
||||
# GPIO26 Grove SDA
|
||||
# GPIO27 ATOM RGB LED -> LED [neo_buildin] (light esp32_rmt_led_strip), also status
|
||||
# GPIO32 Grove SCL
|
||||
# GPIO33 -> speaker_bus (i2s_audio i2s_lrclk_pin)
|
||||
# GPIO39 -> Button (binary_sensor gpio) (inverted)
|
||||
substitutions:
|
||||
device: echo-1
|
||||
name: Echo 1
|
||||
@@ -15,10 +25,17 @@ esphome:
|
||||
esp32:
|
||||
variant: esp32
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include
|
||||
file: common/status_led.yaml
|
||||
vars:
|
||||
light_id: neo_buildin
|
||||
|
||||
api:
|
||||
actions:
|
||||
@@ -131,10 +148,10 @@ media_player:
|
||||
buffer_size: 14000
|
||||
media_pipeline:
|
||||
speaker: media_spk_resampling_input
|
||||
format: NONE
|
||||
format: WAV # no PSRAM: HA transcodes to WAV, decoding MP3/FLAC here runs the heap out
|
||||
announcement_pipeline:
|
||||
speaker: announcement_spk_resampling_input
|
||||
format: NONE
|
||||
format: WAV # no PSRAM: HA transcodes to WAV, decoding MP3/FLAC here runs the heap out
|
||||
|
||||
rtttl:
|
||||
id: my_rtttl
|
||||
|
||||
+22
-11
@@ -1,4 +1,16 @@
|
||||
---
|
||||
# Hardware: Xiaomi Mijia Gateway, brain transplant: M5Stack ATOM Echo (kept the speaker, LEDs and PSU)
|
||||
# Pinout:
|
||||
# GPIO12 -> Button (binary_sensor gpio) (inverted)
|
||||
# GPIO19 -> speaker_bus (i2s_audio i2s_bclk_pin)
|
||||
# GPIO21 Gateway LED ring red -> led_red (output ledc)
|
||||
# GPIO22 -> speaker_id (speaker i2s_audio i2s_dout_pin)
|
||||
# GPIO23 Gateway LED ring blue -> led_blue (output ledc)
|
||||
# GPIO25 Gateway LED ring green -> led_green (output ledc)
|
||||
# GPIO26 Grove SDA -> i2c (i2c sda)
|
||||
# GPIO27 ATOM onboard RGB LED (inside, not visible)
|
||||
# GPIO32 Grove SCL -> i2c (i2c scl)
|
||||
# GPIO33 -> speaker_bus (i2s_audio i2s_lrclk_pin)
|
||||
substitutions:
|
||||
device: echo
|
||||
name: Echo
|
||||
@@ -16,10 +28,17 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include
|
||||
file: common/status_led.yaml
|
||||
vars:
|
||||
light_id: led_light
|
||||
|
||||
api:
|
||||
actions:
|
||||
@@ -71,11 +90,6 @@ light:
|
||||
red: led_red
|
||||
green: led_green
|
||||
blue: led_blue
|
||||
- platform: status_led
|
||||
id: led_status
|
||||
pin:
|
||||
number: 23
|
||||
allow_other_uses: true
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
@@ -86,9 +100,7 @@ output:
|
||||
pin: 25
|
||||
- platform: ledc
|
||||
id: led_blue
|
||||
pin:
|
||||
number: 23
|
||||
allow_other_uses: true
|
||||
pin: 23
|
||||
|
||||
i2s_audio:
|
||||
- id: speaker_bus
|
||||
@@ -127,16 +139,15 @@ media_player:
|
||||
- platform: speaker
|
||||
id: media_out
|
||||
name: Player
|
||||
codec_support_enabled: false
|
||||
buffer_size: 14000
|
||||
media_pipeline:
|
||||
speaker: media_spk_resampling_input
|
||||
# format: WAV
|
||||
format: WAV # only the WAV decoder is built (was codec_support_enabled: false)
|
||||
# num_channels: 1
|
||||
# sample_rate: 8000 # Lower sample rate
|
||||
announcement_pipeline:
|
||||
speaker: announcement_spk_resampling_input
|
||||
# format: WAV
|
||||
format: WAV
|
||||
# num_channels: 1
|
||||
# sample_rate: 8000 # Lower sample rate
|
||||
on_play:
|
||||
|
||||
+18
-22
@@ -1,4 +1,22 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Stamp C3
|
||||
# Pinout:
|
||||
# GPIO0 I2C SCL -> i2c_grove (i2c scl)
|
||||
# GPIO1 I2C SDA, BUTTON -> i2c_grove (i2c sda)
|
||||
# GPIO2 RGB
|
||||
# GPIO4 GPIO
|
||||
# GPIO5 GPIO
|
||||
# GPIO6 GPIO
|
||||
# GPIO7 GPIO
|
||||
# GPIO8 GPIO
|
||||
# GPIO9 GPIO
|
||||
# GPIO10 GPIO
|
||||
# GPIO18 UART RX -> uart_hdmi_switch (uart rx_pin)
|
||||
# GPIO19 UART TX -> uart_hdmi_switch (uart tx_pin)
|
||||
# GPIO20 UART RX, LOGGER -> uart_log (uart rx_pin)
|
||||
# GPIO21 UART TX, LOGGER -> uart_log (uart tx_pin)
|
||||
# I2C:
|
||||
# 0x20 pcf8574_hub (pcf8574)
|
||||
substitutions:
|
||||
device: eworkbench
|
||||
name: Electronics Workbench
|
||||
@@ -22,23 +40,6 @@ esp32:
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
# GPIO20 UART RX, LOGGER
|
||||
# GPIO21 UART TX, LOGGER
|
||||
# GPIO9 GPIO
|
||||
#
|
||||
# GPIO0 I2C SCL
|
||||
# GPIO1 I2C SDA
|
||||
# GPIO2 RGB
|
||||
# GPIO1 BUTTON
|
||||
# GPIO4 GPIO
|
||||
# GPIO5 GPIO
|
||||
# GPIO6 GPIO
|
||||
# GPIO7 GPIO
|
||||
# GPIO8 GPIO
|
||||
# GPIO10 GPIO
|
||||
# GPIO18 UART RX
|
||||
# GPIO19 UART TX
|
||||
|
||||
i2c:
|
||||
- id: i2c_grove
|
||||
sda: 1
|
||||
@@ -242,11 +243,6 @@ binary_sensor:
|
||||
# on_clockwise:
|
||||
# - display_menu.down:
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
|
||||
# display:
|
||||
# - platform: tm1637
|
||||
# id: tm1637_display
|
||||
|
||||
+30
-68
@@ -1,4 +1,22 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite + ATOM Hub Switch
|
||||
# Pinout:
|
||||
# GPIO1 UART0 TX
|
||||
# GPIO3 UART0 RX
|
||||
# GPIO12 IR
|
||||
# GPIO19 Relay2
|
||||
# GPIO21 I2C1 SCL -> i2c (i2c scl)
|
||||
# GPIO22 Relay1
|
||||
# GPIO23 RS485 Tx
|
||||
# GPIO25 I2C1 SDA - red -> i2c (i2c sda)
|
||||
# GPIO26 I2C0 SDA - white
|
||||
# GPIO27 NEO 1x -> atom_led (status LED, common/status_led_atom.yaml)
|
||||
# GPIO32 I2C0 SCL
|
||||
# GPIO33 RS485 Rx
|
||||
# GPIO39 BUTTON -> Status Button (binary_sensor gpio)
|
||||
# I2C:
|
||||
# 0x18 one_wire_bus (one_wire ds2482)
|
||||
# 0x27 pca9554_module (pca9554)
|
||||
substitutions:
|
||||
device: fireplace
|
||||
name: Heating Fireplace
|
||||
@@ -15,76 +33,30 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
external_components:
|
||||
# - source: github://pr#12717
|
||||
# components: [ds248x]
|
||||
# refresh: 1h
|
||||
- source: github://pr#13534
|
||||
components: [ds2482, ds2484, ds248x_base]
|
||||
refresh: 1h
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
|
||||
# PINOUTS: M5ATOM+Atom Hub Switch
|
||||
# IR G12
|
||||
# NEO G27 1x
|
||||
# BUTTON G39
|
||||
# I2C0 SDA G26 - white
|
||||
# I2C0 SCL G32
|
||||
# I2C1 SDA G25 - red
|
||||
# I2C1 SCL G21
|
||||
# UART0 RX G3
|
||||
# UART0 TX G1
|
||||
# RS485 Rx G33
|
||||
# RS485 Tx G23
|
||||
# Relay1 G22
|
||||
# Relay2 G19
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include common/status_led_atom.yaml
|
||||
|
||||
i2c:
|
||||
# - id: i2c0
|
||||
# sda: 26
|
||||
# scl: 32
|
||||
# scan: true #false
|
||||
# - id: i2c1
|
||||
sda: 25
|
||||
scl: 21
|
||||
scan: true #false
|
||||
|
||||
# ds248x:
|
||||
# # i2c_id: i2c_main
|
||||
# address: 0x18 # should be the default
|
||||
# # sleep_pin: 32 # remove if not needed, if set the ds248x sleeps when no transaction is in progress
|
||||
# active_pullup: true # adds a active pullup which should improve signal integrity
|
||||
# strong_pullup: false # enables a strong pullup used to provide the needed current for temperature reading without vcc connected
|
||||
# bus_sleep: true # power down the bus when no transaction is in progres
|
||||
|
||||
# one_wire:
|
||||
# id: one_wire_bus
|
||||
# platform: ds2484
|
||||
# active_pullup: true
|
||||
|
||||
# ds248x:
|
||||
# id: ds2484_module
|
||||
# address: 0x18
|
||||
|
||||
# one_wire:
|
||||
# - platform: ds248x
|
||||
# ds248x_id: ds2484_module
|
||||
# id: one_wire_bus
|
||||
ds248x:
|
||||
id: ds2484_module
|
||||
type: ds2482-100
|
||||
address: 0x18
|
||||
active_pullup: true
|
||||
|
||||
one_wire:
|
||||
id: one_wire_bus
|
||||
platform: ds2482
|
||||
active_pullup: true
|
||||
- platform: ds248x
|
||||
ds248x_id: ds2484_module
|
||||
id: one_wire_bus
|
||||
|
||||
pca9554:
|
||||
- id: pca9554_module
|
||||
@@ -131,16 +103,6 @@ sensor:
|
||||
# index: 0
|
||||
name: Temperature
|
||||
id: 'heating_fireplace_temperature'
|
||||
# - platform: dht
|
||||
# pin: 14
|
||||
# temperature:
|
||||
# name: "Kitchen Temperature"
|
||||
# id: 'kitchen_temperature'
|
||||
# humidity:
|
||||
# name: "Kitchen Humidity"
|
||||
# id: 'kitchen_humidity'
|
||||
# update_interval: 60s
|
||||
# model: si7021
|
||||
|
||||
bluetooth_proxy:
|
||||
active: true
|
||||
+19
-15
@@ -1,4 +1,19 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite + ATOM Hub Switch
|
||||
# Pinout:
|
||||
# GPIO1 UART0 TX
|
||||
# GPIO3 UART0 RX
|
||||
# GPIO12 IR
|
||||
# GPIO19 RELAY-2 -> relay_2 (output gpio)
|
||||
# GPIO21 I2C1 SCL
|
||||
# GPIO22 RELAY-1 -> relay_1 (output gpio)
|
||||
# GPIO23 RS485 TX
|
||||
# GPIO25 I2C1 SDA red
|
||||
# GPIO26 I2C0 SDA white
|
||||
# GPIO27 NEO -> atom_led (status LED, common/status_led_atom.yaml)
|
||||
# GPIO32 I2C0 SCL -> gate_ogi_state (binary_sensor gpio) (inverted); gate_ogi_blink (binary_sensor gpio) (inverted)
|
||||
# GPIO33 RS485 RX
|
||||
# GPIO39 BUTTON
|
||||
substitutions:
|
||||
device: gate
|
||||
name: Gate
|
||||
@@ -15,25 +30,14 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
# PINOUTS: M5ATOM
|
||||
# IR G12
|
||||
# NEO G27
|
||||
# BUTTON G39
|
||||
# I2C0 SDA G26 white
|
||||
# I2C0 SCL G32
|
||||
# I2C1 SDA G25 red
|
||||
# I2C1 SCL G21
|
||||
# UART0 RX G3
|
||||
# UART0 TX G1
|
||||
# RS485 RX G33
|
||||
# RS485 TX G23
|
||||
# RELAY-1 G22
|
||||
# RELAY-2 G19
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include common/status_led_atom.yaml
|
||||
|
||||
button:
|
||||
- platform: output
|
||||
|
||||
+29
-16
@@ -1,4 +1,31 @@
|
||||
---
|
||||
# Hardware: Olimex ESP32-POE-ISO
|
||||
# Pinout:
|
||||
# # UEXT
|
||||
# 3V3 1 2 GND
|
||||
# GPIO4 3 4 GPIO36 U1TXD/U1RXD
|
||||
# GPIO16 5 6 GPIO13 I2C-SCL/I2C-SDA
|
||||
# GPIO15 7 8 GPIO2 HS2_CMD/HS2_DATA0
|
||||
# GPIO14 9 10 GPIO5 HS2_CLK/SPI_CS
|
||||
#
|
||||
# SD/MMC; shared with UEXT, no CS!!!
|
||||
# GPIO15 HS2_CMD
|
||||
# GPIO14 HS2_CLK
|
||||
# GPIO2 HS2_DATA0
|
||||
#
|
||||
# GPIO34 BUT1
|
||||
# GPIO35 Battery Measurment ADC
|
||||
# GPIO39 Ext Power Sense ADC
|
||||
# Used:
|
||||
# GPIO4 uart_modbus (uart tx_pin)
|
||||
# GPIO12 ethernet (ethernet power_pin)
|
||||
# GPIO13 i2c (i2c sda)
|
||||
# GPIO16 i2c (i2c scl)
|
||||
# GPIO17 ethernet (ethernet)
|
||||
# GPIO18 ethernet (ethernet mdio_pin)
|
||||
# GPIO23 ethernet (ethernet mdc_pin)
|
||||
# GPIO34 user_button (binary_sensor gpio) (inverted)
|
||||
# GPIO36 uart_modbus (uart rx_pin)
|
||||
substitutions:
|
||||
device: gateway
|
||||
name: Gateway
|
||||
@@ -17,6 +44,7 @@ esp32:
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
minimum_chip_revision: 3.0
|
||||
sram1_as_iram: true
|
||||
|
||||
@@ -32,22 +60,7 @@ external_components:
|
||||
packages:
|
||||
# common: !include common/common.yaml
|
||||
common_eth: !include common/common_eth.yaml
|
||||
|
||||
## UEXT
|
||||
# 3V3 1 2 GND
|
||||
# GPIO4 3 4 GPIO36 U1TXD/U1RXD
|
||||
# GPIO16 5 6 GPIO13 I2C-SCL/I2C-SDA
|
||||
# GPIO15 7 8 GPIO2 HS2_CMD/HS2_DATA0
|
||||
# GPIO14 9 10 GPIO5 HS2_CLK/SPI_CS
|
||||
#
|
||||
# SD/MMC; shared with UEXT, no CS!!!
|
||||
# GPIO15 HS2_CMD
|
||||
# GPIO14 HS2_CLK
|
||||
# GPIO2 HS2_DATA0
|
||||
|
||||
# GPIO34 BUT1
|
||||
# GPIO35 Battery Measurment ADC
|
||||
# GPIO39 Ext Power Sense ADC
|
||||
ipv6_only: !include common/ipv6_only_eth.yaml
|
||||
|
||||
i2c:
|
||||
sda: 13
|
||||
|
||||
+32
-56
@@ -1,4 +1,22 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite + ATOM Hub Switch
|
||||
# Pinout:
|
||||
# GPIO1 UART0 TX
|
||||
# GPIO3 UART0 RX
|
||||
# GPIO12 IR
|
||||
# GPIO19 NC -> Demand [heating_demand] (switch gpio)
|
||||
# GPIO21 I2C1 SCL -> i2c (i2c scl)
|
||||
# GPIO22 1-WIRE -> Demand [hot_water_demand] (switch gpio)
|
||||
# GPIO23 UART1 RX -> uart_modbus (uart rx_pin)
|
||||
# GPIO25 I2C1 SDA -> i2c (i2c sda)
|
||||
# GPIO26 I2C0 SDA
|
||||
# GPIO27 NEO -> atom_led (status LED, common/status_led_atom.yaml)
|
||||
# GPIO32 I2C0 SCL
|
||||
# GPIO33 UART1 TX -> uart_modbus (uart tx_pin)
|
||||
# GPIO39 BUTTON
|
||||
# I2C:
|
||||
# 0x18 one_wire_bus (one_wire ds2482)
|
||||
# 0x27 pca9554_module (pca9554)
|
||||
substitutions:
|
||||
device: heater
|
||||
name: Heater
|
||||
@@ -25,40 +43,14 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
external_components:
|
||||
# - source: github://pr#12717
|
||||
# components: [ds248x]
|
||||
# refresh: 1h
|
||||
- source: github://pr#13534
|
||||
components: [ds2482, ds2484, ds248x_base]
|
||||
refresh: 1h
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
# PINOUTS: M5ATOM
|
||||
# IR G12
|
||||
# NEO G27
|
||||
# BUTTON G39
|
||||
# I2C0 SDA G26
|
||||
# I2C0 SCL G32
|
||||
# I2C1 SDA G25
|
||||
# I2C1 SCL G21
|
||||
# UART0 RX G3
|
||||
# UART0 TX G1
|
||||
# UART1 RX G23
|
||||
# UART1 TX G33
|
||||
# 1-WIRE G22
|
||||
# NC G19
|
||||
|
||||
#logger:
|
||||
# level: VERBOSE
|
||||
# logs:
|
||||
# dallas.component: ERROR
|
||||
# uart.component: ERROR
|
||||
# i2c.component: VERBOSE
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include common/status_led_atom.yaml
|
||||
|
||||
# Heating / Hot water Controller
|
||||
# temp_1: Solar Collector
|
||||
@@ -129,9 +121,7 @@ globals:
|
||||
initial_value: '20.5'
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
- id: !extend ha_time
|
||||
on_time:
|
||||
- seconds: 0
|
||||
minutes: /1
|
||||
@@ -165,36 +155,22 @@ climate:
|
||||
default_target_temperature_high: 40 °C
|
||||
|
||||
i2c:
|
||||
# - id: i2c0
|
||||
# sda: 26
|
||||
# scl: 32
|
||||
# scan: true #false
|
||||
# - id: i2c1
|
||||
|
||||
sda: 25
|
||||
scl: 21
|
||||
scan: true #false
|
||||
|
||||
# one_wire:
|
||||
# id: one_wire_bus
|
||||
# # i2c_id: i2c_main
|
||||
# platform: ds2484
|
||||
# active_pullup: true
|
||||
|
||||
# ds248x:
|
||||
# id: ds2484_module
|
||||
# address: 0x18
|
||||
# active_pullup: true
|
||||
|
||||
# one_wire:
|
||||
# - platform: ds248x
|
||||
# ds248x_id: ds2484_module
|
||||
# id: one_wire_bus
|
||||
ds248x:
|
||||
id: ds2482_module
|
||||
type: ds2482-100
|
||||
address: 0x18
|
||||
active_pullup: true
|
||||
|
||||
one_wire:
|
||||
id: one_wire_bus
|
||||
# i2c_id: i2c_main
|
||||
platform: ds2482
|
||||
active_pullup: true
|
||||
- platform: ds248x
|
||||
ds248x_id: ds2482_module
|
||||
id: one_wire_bus
|
||||
|
||||
modbus:
|
||||
uart_id: uart_modbus
|
||||
|
||||
+23
-22
@@ -1,4 +1,21 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite + ATOM Hub Switch
|
||||
# Pinout:
|
||||
# GPIO1 UART0 TX
|
||||
# GPIO3 UART0 RX
|
||||
# GPIO12 IR
|
||||
# GPIO19 Relay2
|
||||
# GPIO21 I2C1 SCL -> i2c (i2c scl)
|
||||
# GPIO22 Relay1 -> power_supply1 (power_supply)
|
||||
# GPIO23 RS485 Tx
|
||||
# GPIO25 I2C1 SDA - red -> i2c (i2c sda)
|
||||
# GPIO26 I2C0 SDA - white
|
||||
# GPIO27 NEO 1x -> atom_led (status LED, common/status_led_atom.yaml)
|
||||
# GPIO32 I2C0 SCL
|
||||
# GPIO33 RS485 Rx
|
||||
# GPIO39 BUTTON -> Status Button (binary_sensor gpio)
|
||||
# I2C:
|
||||
# 0x40 pca9685_hub1 (pca9685)
|
||||
substitutions:
|
||||
device: kitchen
|
||||
name: Kitchen
|
||||
@@ -32,33 +49,17 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include common/status_led_atom.yaml
|
||||
|
||||
api:
|
||||
reboot_timeout: 300s
|
||||
|
||||
# PINOUTS: M5ATOM+Atom Hub Switch
|
||||
# IR G12
|
||||
# NEO G27 1x
|
||||
# BUTTON G39
|
||||
# I2C0 SDA G26 - white
|
||||
# I2C0 SCL G32
|
||||
# I2C1 SDA G25 - red
|
||||
# I2C1 SCL G21
|
||||
# UART0 RX G3
|
||||
# UART0 TX G1
|
||||
# RS485 Rx G33
|
||||
# RS485 Tx G23
|
||||
# Relay1 G22
|
||||
# Relay2 G19
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
# api:
|
||||
# reboot_timeout: 300s
|
||||
|
||||
i2c:
|
||||
# - id: i2c0
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Stamp Pico
|
||||
# Pinout:
|
||||
# GPIO22 -> i2c (i2c scl)
|
||||
# GPIO25 -> i2c (i2c sda)
|
||||
# GPIO26 -> as3935_i2c (as3935_i2c irq_pin)
|
||||
# GPIO27 -> neo (light esp32_rmt_led_strip)
|
||||
# GPIO32 Grove SDA
|
||||
# GPIO33 Grove SCL
|
||||
# I2C:
|
||||
# 0x03 as3935_i2c (as3935_i2c)
|
||||
substitutions:
|
||||
device: lightening
|
||||
name: Lightening detector
|
||||
@@ -16,6 +26,8 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd # ESP32-PICO-D4, like the other M5Stamp Pico (not read from the device)
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
---
|
||||
# Hardware: Guition ESP32-S3-4848S040
|
||||
# Pinout:
|
||||
# # Display (RGB, pins in the mipi_rgb model)
|
||||
# CS 39
|
||||
# SCK 48 ST7701S init (3-wire SPI)
|
||||
# SDA 47
|
||||
# DE 18
|
||||
# HSYNC 16
|
||||
# VSYNC 17
|
||||
# PCLK 21
|
||||
# BL 38
|
||||
# # Capacitive Touch (GT911)
|
||||
# TP_SDA 19
|
||||
# TP_SCL 45
|
||||
# # Relays (86 box variant)
|
||||
# RELAY1 40
|
||||
# RELAY2 2
|
||||
# RELAY3 1
|
||||
# Used:
|
||||
# GPIO0 main_display (display mipi_rgb data_pins.red)
|
||||
# GPIO3 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO4 main_display (display mipi_rgb data_pins.blue)
|
||||
# GPIO5 main_display (display mipi_rgb data_pins.blue)
|
||||
# GPIO6 main_display (display mipi_rgb data_pins.blue)
|
||||
# GPIO7 main_display (display mipi_rgb data_pins.blue)
|
||||
# GPIO8 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO9 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO10 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO11 main_display (display mipi_rgb data_pins.red)
|
||||
# GPIO12 main_display (display mipi_rgb data_pins.red)
|
||||
# GPIO13 main_display (display mipi_rgb data_pins.red)
|
||||
# GPIO14 main_display (display mipi_rgb data_pins.red)
|
||||
# GPIO15 main_display (display mipi_rgb data_pins.blue)
|
||||
# GPIO16 main_display (display mipi_rgb hsync_pin)
|
||||
# GPIO17 main_display (display mipi_rgb vsync_pin)
|
||||
# GPIO18 main_display (display mipi_rgb de_pin)
|
||||
# GPIO19 i2c_main (i2c sda)
|
||||
# GPIO20 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO21 main_display (display mipi_rgb pclk_pin)
|
||||
# GPIO38 gpio_backlight_pwm (output ledc)
|
||||
# GPIO39 main_display (display mipi_rgb cs_pin)
|
||||
# GPIO45 i2c_main (i2c scl)
|
||||
# GPIO46 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO47 lcd_spi (spi mosi_pin)
|
||||
# GPIO48 lcd_spi (spi clk_pin)
|
||||
# I2C:
|
||||
# 0x5D main_touch (touchscreen gt911)
|
||||
substitutions:
|
||||
device: lvgl-4848s040
|
||||
name: LVGL 4848S040
|
||||
area: DEV
|
||||
comment: "${area} | Guition ESP32-S3-4848S040"
|
||||
|
||||
esphome:
|
||||
name: mcu-${device}
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
on_boot:
|
||||
- priority: -100
|
||||
then:
|
||||
- lvgl.page.show: status_page
|
||||
|
||||
esp32:
|
||||
variant: esp32s3
|
||||
flash_size: 16MB
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: boya
|
||||
type: esp-idf
|
||||
# PSRAM bandwidth for the RGB panel (not set by ESPHome); without it the
|
||||
# image shifts during flash/WiFi activity
|
||||
sdkconfig_options:
|
||||
CONFIG_ESP32S3_DATA_CACHE_64KB: y
|
||||
CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
|
||||
CONFIG_SPIRAM_RODATA: y
|
||||
|
||||
psram:
|
||||
mode: octal
|
||||
speed: 80MHz
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
api:
|
||||
# status page until Home Assistant connects (log viewers don't subscribe to states)
|
||||
on_client_connected:
|
||||
- delay: 2s
|
||||
- if:
|
||||
condition:
|
||||
api.connected:
|
||||
state_subscription_only: true
|
||||
then:
|
||||
- lvgl.page.show: main_page
|
||||
on_client_disconnected:
|
||||
- if:
|
||||
condition:
|
||||
not:
|
||||
api.connected:
|
||||
state_subscription_only: true
|
||||
then:
|
||||
- lvgl.page.show: status_page
|
||||
|
||||
i2c:
|
||||
- id: i2c_main
|
||||
sda: 19
|
||||
scl: 45
|
||||
scan: true
|
||||
|
||||
spi:
|
||||
- id: lcd_spi
|
||||
clk_pin: 48
|
||||
mosi_pin: 47
|
||||
|
||||
display:
|
||||
- platform: mipi_rgb
|
||||
model: GUITION-4848S040
|
||||
id: main_display
|
||||
spi_id: lcd_spi
|
||||
update_interval: never
|
||||
auto_clear_enabled: false
|
||||
|
||||
touchscreen:
|
||||
- platform: gt911
|
||||
id: main_touch
|
||||
display: main_display
|
||||
i2c_id: i2c_main
|
||||
# the mipi_rgb model mirrors the image on both axes, touch has to follow
|
||||
transform:
|
||||
mirror_x: true
|
||||
mirror_y: true
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
id: gpio_backlight_pwm
|
||||
pin: 38
|
||||
frequency: 1220
|
||||
|
||||
light:
|
||||
- platform: monochromatic
|
||||
id: back_light
|
||||
name: Backlight
|
||||
output: gpio_backlight_pwm
|
||||
restore_mode: ALWAYS_ON
|
||||
|
||||
script:
|
||||
- id: update_status
|
||||
then:
|
||||
- lambda: |-
|
||||
// same colors as the other status screens: ok green, waiting orange, details gray
|
||||
static const uint32_t OK = 0x4DD966, WAIT = 0xFFB300;
|
||||
auto set = [](lv_obj_t *label, const std::string &text, uint32_t color) {
|
||||
lv_label_set_text(label, text.c_str());
|
||||
lv_obj_set_style_text_color(label, lv_color_hex(color), LV_PART_MAIN);
|
||||
};
|
||||
|
||||
const bool wifi_ok = wifi::global_wifi_component->is_connected();
|
||||
set(id(status_wifi),
|
||||
wifi_ok ? str_sprintf("WiFi: connected (%d dBm)", wifi::global_wifi_component->wifi_rssi())
|
||||
: std::string("WiFi: connecting..."),
|
||||
wifi_ok ? OK : WAIT);
|
||||
|
||||
// IPv6 first (skip link-local), then IPv4
|
||||
std::string ips;
|
||||
if (wifi_ok) {
|
||||
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
for (bool v6 : {true, false}) {
|
||||
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||
if (!ip.is_set() || ip.is_ip6() != v6)
|
||||
continue;
|
||||
ip.str_to(buf);
|
||||
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||
continue;
|
||||
if (!ips.empty())
|
||||
ips += "\n";
|
||||
ips += buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
lv_label_set_text(id(status_ips), ips.c_str());
|
||||
if (ips.empty())
|
||||
lv_obj_add_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||
else
|
||||
lv_obj_remove_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
const bool api_ok = api::global_api_server->is_connected();
|
||||
set(id(status_ha), api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...",
|
||||
api_ok ? OK : WAIT);
|
||||
|
||||
const uint32_t up = millis() / 1000;
|
||||
lv_label_set_text(id(status_footer),
|
||||
str_sprintf("ESPHome %s · up %ud %02u:%02u:%02u", ESPHOME_VERSION, up / 86400,
|
||||
(up / 3600) % 24, (up / 60) % 60, up % 60).c_str());
|
||||
|
||||
interval:
|
||||
- interval: 1s
|
||||
then:
|
||||
- lvgl.label.update:
|
||||
id: clock_label
|
||||
text: !lambda |-
|
||||
auto now = id(ha_time).now();
|
||||
if (!now.is_valid())
|
||||
return std::string("--:--");
|
||||
return now.strftime("%H:%M");
|
||||
- if:
|
||||
condition:
|
||||
lvgl.page.is_showing: status_page
|
||||
then:
|
||||
- script.execute: update_status
|
||||
|
||||
image:
|
||||
- platform: file
|
||||
file: assets/images/made-for-esphome-white-on-black.svg
|
||||
id: esphome_logo
|
||||
resize: 320x95
|
||||
type: RGB565
|
||||
transparency: alpha_channel
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto@500"
|
||||
id: font_clock
|
||||
size: 150
|
||||
glyphs: "0123456789:-"
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_title
|
||||
size: 32
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_status
|
||||
size: 20
|
||||
glyphs: "·"
|
||||
glyphsets: [GF_Latin_Kernel]
|
||||
|
||||
lvgl:
|
||||
log_level: WARN
|
||||
color_depth: 16
|
||||
bg_color: 0x000000
|
||||
text_color: 0xFFFFFF
|
||||
theme:
|
||||
buttonmatrix:
|
||||
bg_opa: TRANSP
|
||||
border_width: 0
|
||||
items:
|
||||
bg_color: 0x2B2F36
|
||||
text_color: 0xFFFFFF
|
||||
radius: 12
|
||||
checked:
|
||||
bg_color: 0xE08A00
|
||||
top_layer:
|
||||
widgets:
|
||||
# same navigation bar as on mcu-lvgl-trainer
|
||||
- buttonmatrix:
|
||||
id: nav_bar
|
||||
align: bottom_mid
|
||||
width: 100%
|
||||
height: 60
|
||||
pad_all: 4
|
||||
pad_column: 6
|
||||
items:
|
||||
text_font: montserrat_28
|
||||
rows:
|
||||
- buttons:
|
||||
- id: page_prev
|
||||
text: ""
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.previous:
|
||||
- id: page_home
|
||||
text: ""
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.show: main_page
|
||||
- id: page_next
|
||||
text: ""
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.next:
|
||||
pages:
|
||||
# 1: clock
|
||||
- id: main_page
|
||||
widgets:
|
||||
- label:
|
||||
id: clock_label
|
||||
align: CENTER
|
||||
y: -30
|
||||
text_font: font_clock
|
||||
text: "--:--"
|
||||
|
||||
# 2: status (shown until Home Assistant connects)
|
||||
- id: status_page
|
||||
on_load:
|
||||
- script.execute: update_status
|
||||
widgets:
|
||||
- image:
|
||||
src: esphome_logo
|
||||
align: TOP_MID
|
||||
y: 16
|
||||
- label:
|
||||
align: TOP_MID
|
||||
y: 120
|
||||
text_font: font_title
|
||||
text: "${name}"
|
||||
- obj:
|
||||
id: status_lines
|
||||
x: 24
|
||||
y: 170
|
||||
width: 440
|
||||
height: SIZE_CONTENT
|
||||
bg_opa: TRANSP
|
||||
border_width: 0
|
||||
pad_all: 0
|
||||
scrollbar_mode: "OFF"
|
||||
layout:
|
||||
type: flex
|
||||
flex_flow: column
|
||||
pad_row: 6
|
||||
widgets:
|
||||
- label:
|
||||
id: status_wifi
|
||||
text_font: font_status
|
||||
text: ""
|
||||
- label:
|
||||
id: status_ips
|
||||
text_font: font_status
|
||||
text_color: 0x8C8C8C
|
||||
pad_left: 20
|
||||
text: ""
|
||||
- label:
|
||||
id: status_ha
|
||||
text_font: font_status
|
||||
text: ""
|
||||
- label:
|
||||
id: status_footer
|
||||
text_font: font_status
|
||||
text_color: 0x8C8C8C
|
||||
text: ""
|
||||
@@ -1,4 +1,17 @@
|
||||
---
|
||||
# Hardware: Guition JC3248W535
|
||||
# Pinout:
|
||||
# GPIO1 -> main_backlight (power_supply)
|
||||
# GPIO4 -> touchscreen_bus (i2c sda)
|
||||
# GPIO8 -> touchscreen_bus (i2c scl)
|
||||
# GPIO21 -> display_qspi (spi data_pins)
|
||||
# GPIO39 -> display_qspi (spi data_pins)
|
||||
# GPIO40 -> display_qspi (spi data_pins)
|
||||
# GPIO45 -> main_display (display mipi_spi cs_pin)
|
||||
# GPIO47 -> display_qspi (spi clk_pin)
|
||||
# GPIO48 -> display_qspi (spi data_pins)
|
||||
# I2C:
|
||||
# 0x3B main_touch (touchscreen axs15231)
|
||||
substitutions:
|
||||
device: lvgl-eworkbench
|
||||
name: Electronics Workbench Display
|
||||
@@ -16,6 +29,8 @@ esp32:
|
||||
flash_size: 16MB
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: generic # same board as lvgl-trainer (not read from the device)
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
|
||||
+628
-150
@@ -1,9 +1,31 @@
|
||||
---
|
||||
# Hardware: Guition JC3248W535
|
||||
# Pinout:
|
||||
# GPIO1 -> gpio_backlight_pwm (output ledc)
|
||||
# GPIO4 -> touchscreen_bus (i2c sda)
|
||||
# GPIO8 -> touchscreen_bus (i2c scl)
|
||||
# GPIO21 -> display_qspi (spi data_pins)
|
||||
# GPIO39 -> display_qspi (spi data_pins)
|
||||
# GPIO40 -> display_qspi (spi data_pins)
|
||||
# GPIO43 -> ext_uart (uart tx_pin)
|
||||
# GPIO44 -> ext_uart (uart rx_pin)
|
||||
# GPIO45 -> main_display (display mipi_spi cs_pin)
|
||||
# GPIO47 -> display_qspi (spi clk_pin)
|
||||
# GPIO48 -> display_qspi (spi data_pins)
|
||||
# I2C:
|
||||
# 0x3B main_touch (touchscreen axs15231)
|
||||
substitutions:
|
||||
device: lvgl-trainer
|
||||
name: Trainer Display
|
||||
area: Basement
|
||||
comment: "${area} | Pain Cave"
|
||||
light_basement: light.mcu_basement_2_light
|
||||
light_workbench: light.mcu_basement_2_workbench_light
|
||||
light_exercise: light.exercise_room
|
||||
fan: light.mcu_dimmer_exercise_room_fan
|
||||
|
||||
# Pages (480x320, navigation bar at the bottom):
|
||||
# clock, lights, training, fan, stopwatch, status
|
||||
|
||||
esphome:
|
||||
name: mcu-${device}
|
||||
@@ -15,11 +37,13 @@ esp32:
|
||||
variant: esp32s3
|
||||
flash_size: 16MB
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
type: esp-idf
|
||||
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
i2c:
|
||||
- id: touchscreen_bus
|
||||
@@ -36,71 +60,17 @@ spi:
|
||||
clk_pin: 47
|
||||
data_pins: [21, 48, 40, 39]
|
||||
|
||||
color:
|
||||
- id: my_red
|
||||
red: 100%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
- id: my_orange
|
||||
red: 100%
|
||||
green: 50%
|
||||
blue: 0%
|
||||
- id: my_yellow
|
||||
red: 100%
|
||||
green: 100%
|
||||
blue: 0%
|
||||
- id: my_green
|
||||
red: 0%
|
||||
green: 100%
|
||||
blue: 0%
|
||||
- id: my_blue
|
||||
red: 0%
|
||||
green: 0%
|
||||
blue: 100%
|
||||
- id: my_teal
|
||||
red: 0%
|
||||
green: 100%
|
||||
blue: 100%
|
||||
- id: my_gray
|
||||
red: 70%
|
||||
green: 70%
|
||||
blue: 70%
|
||||
- id: my_white
|
||||
red: 100%
|
||||
green: 100%
|
||||
blue: 100%
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto"
|
||||
id: Roboto_48
|
||||
size: 48
|
||||
- file: "gfonts://Roboto"
|
||||
id: Roboto_36
|
||||
size: 36
|
||||
- file: "gfonts://Roboto"
|
||||
id: Roboto_24
|
||||
size: 24
|
||||
- file: "gfonts://Roboto"
|
||||
id: Roboto_12
|
||||
size: 12
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_std
|
||||
size: 40
|
||||
glyphs: "!\"%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz/\\[]|&@#'"
|
||||
- file: "gfonts://Roboto@700"
|
||||
id: font_title
|
||||
size: 40
|
||||
glyphs: "!\"%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz/\\[]|&@#'"
|
||||
|
||||
globals:
|
||||
- id: bgcolor
|
||||
type: Color
|
||||
initial_value: "Color::random_color()"
|
||||
|
||||
# power_supply:
|
||||
# id: main_backlight
|
||||
# pin: 1
|
||||
# enable_on_boot: true
|
||||
# stopwatch: accumulated time of finished runs + start of the current run
|
||||
- id: sw_running
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: sw_start
|
||||
type: uint32_t
|
||||
initial_value: '0'
|
||||
- id: sw_accum
|
||||
type: uint32_t
|
||||
initial_value: '0'
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
@@ -119,7 +89,6 @@ display:
|
||||
- platform: mipi_spi
|
||||
model: AXS15231
|
||||
data_rate: 40MHz #20mhz is default
|
||||
#spi_mode: MODE2
|
||||
id: main_display
|
||||
spi_id: display_qspi
|
||||
dimensions:
|
||||
@@ -127,11 +96,6 @@ display:
|
||||
width: 320
|
||||
cs_pin: 45
|
||||
auto_clear_enabled: false
|
||||
# show_test_card: true
|
||||
# lambda: |-
|
||||
# it.fill(id(bgcolor));
|
||||
# it.print(it.get_width()/2, it.get_height()/2-20, id(font_title), TextAlign::CENTER, "ESPHome");
|
||||
# it.print(it.get_width()/2, it.get_height()/2+20, id(font_std), TextAlign::CENTER, "@UTBDK");
|
||||
|
||||
touchscreen:
|
||||
- platform: axs15231
|
||||
@@ -142,27 +106,8 @@ touchscreen:
|
||||
# interrupt_pin:
|
||||
# number: 3
|
||||
# ignore_strapping_warning: true
|
||||
calibration:
|
||||
x_min: 0
|
||||
x_max: 479
|
||||
y_min: 0
|
||||
y_max: 319
|
||||
transform:
|
||||
swap_xy: true
|
||||
mirror_x: false
|
||||
mirror_y: true
|
||||
# on_touch:
|
||||
# - lambda: |-
|
||||
# Color newColor;
|
||||
# do { newColor = Color::random_color(); } while (newColor == id(bgcolor));
|
||||
# id(bgcolor) = newColor;
|
||||
|
||||
# ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
|
||||
# touch.x,
|
||||
# touch.y,
|
||||
# touch.x_raw,
|
||||
# touch.y_raw
|
||||
# );
|
||||
# no calibration/transform: the driver defaults to the native 320x480 panel and
|
||||
# LVGL applies its own rotation to touch points
|
||||
on_release:
|
||||
- if:
|
||||
condition: lvgl.is_paused
|
||||
@@ -183,107 +128,640 @@ uart:
|
||||
|
||||
binary_sensor:
|
||||
- platform: homeassistant
|
||||
id: exercise_room_light
|
||||
entity_id: light.exercise_room
|
||||
id: basement_light
|
||||
entity_id: ${light_basement}
|
||||
trigger_on_initial_state: true
|
||||
on_state:
|
||||
then:
|
||||
lvgl.widget.update:
|
||||
id: exercise_room_light_btn
|
||||
- lvgl.widget.update:
|
||||
id: basement_light_btn
|
||||
state:
|
||||
checked: !lambda return x;
|
||||
- platform: homeassistant
|
||||
id: exercise_room_fan
|
||||
entity_id: light.mcu_dimmer_exercise_room_fan
|
||||
id: workbench_light
|
||||
entity_id: ${light_workbench}
|
||||
trigger_on_initial_state: true
|
||||
on_state:
|
||||
then:
|
||||
lvgl.widget.update:
|
||||
id: exercise_room_fan_btn
|
||||
- lvgl.widget.update:
|
||||
id: workbench_light_btn
|
||||
state:
|
||||
checked: !lambda return x;
|
||||
- platform: homeassistant
|
||||
id: exercise_light
|
||||
entity_id: ${light_exercise}
|
||||
trigger_on_initial_state: true
|
||||
on_state:
|
||||
- lvgl.widget.update:
|
||||
id: exercise_light_btn
|
||||
state:
|
||||
checked: !lambda return x;
|
||||
# brightness and color temperature only make sense while the light is on
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x;
|
||||
then:
|
||||
- lvgl.widget.show: exercise_controls
|
||||
else:
|
||||
- lvgl.widget.hide: exercise_controls
|
||||
- platform: homeassistant
|
||||
id: fan_on
|
||||
entity_id: ${fan}
|
||||
trigger_on_initial_state: true
|
||||
on_state:
|
||||
- script.execute: update_fan_buttons
|
||||
|
||||
sensor:
|
||||
- platform: homeassistant
|
||||
id: fan_brightness
|
||||
entity_id: ${fan}
|
||||
attribute: brightness
|
||||
on_value:
|
||||
- script.execute: update_fan_buttons
|
||||
|
||||
text_sensor:
|
||||
# light attributes read as text: they are 'None' while the light is off
|
||||
- platform: homeassistant
|
||||
id: exercise_brightness
|
||||
entity_id: ${light_exercise}
|
||||
attribute: brightness
|
||||
on_value:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return parse_number<float>(x).has_value();
|
||||
then:
|
||||
- lvgl.slider.update:
|
||||
id: exercise_brightness_slider
|
||||
value: !lambda return roundf(*parse_number<float>(x) * 100.0f / 255.0f);
|
||||
- platform: homeassistant
|
||||
id: exercise_color_temp
|
||||
entity_id: ${light_exercise}
|
||||
attribute: color_temp_kelvin
|
||||
on_value:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return parse_number<float>(x).has_value();
|
||||
then:
|
||||
- lvgl.slider.update:
|
||||
id: exercise_color_temp_slider
|
||||
value: !lambda return *parse_number<float>(x);
|
||||
# a running automation (e.g. its delay) has current > 0
|
||||
- platform: homeassistant
|
||||
id: training_start_running
|
||||
entity_id: automation.zwift_enable_fan
|
||||
attribute: current
|
||||
on_value:
|
||||
- script.execute: update_training_status
|
||||
- platform: homeassistant
|
||||
id: training_stop_running
|
||||
entity_id: automation.zwift_disable_fan
|
||||
attribute: current
|
||||
on_value:
|
||||
- script.execute: update_training_status
|
||||
|
||||
script:
|
||||
- id: update_fan_buttons
|
||||
then:
|
||||
- lambda: |-
|
||||
// fan level rounded to the 10 % steps of the buttons, 0 = off
|
||||
int pct = 0;
|
||||
if (id(fan_on).state && !std::isnan(id(fan_brightness).state))
|
||||
pct = (int) roundf(id(fan_brightness).state * 10.0f / 255.0f) * 10;
|
||||
const int levels[] = {0, 50, 60, 70, 80, 90, 100};
|
||||
lv_obj_t *matrix = id(fan_matrix)->obj;
|
||||
for (int i = 0; i < 7; i++) {
|
||||
if (levels[i] == pct)
|
||||
lv_buttonmatrix_set_button_ctrl(matrix, i, LV_BUTTONMATRIX_CTRL_CHECKED);
|
||||
else
|
||||
lv_buttonmatrix_clear_button_ctrl(matrix, i, LV_BUTTONMATRIX_CTRL_CHECKED);
|
||||
}
|
||||
- lvgl.label.update:
|
||||
id: fan_status_label
|
||||
text: !lambda |-
|
||||
if (!id(fan_on).state)
|
||||
return std::string("Ventilátor vypnutý");
|
||||
if (std::isnan(id(fan_brightness).state))
|
||||
return std::string("Ventilátor zapnutý");
|
||||
return str_sprintf("Ventilátor %.0f %%", id(fan_brightness).state * 100.0f / 255.0f);
|
||||
|
||||
- id: update_training_status
|
||||
then:
|
||||
- lvgl.label.update:
|
||||
id: training_status_label
|
||||
text: !lambda |-
|
||||
if (id(training_start_running).state != "0" && !id(training_start_running).state.empty())
|
||||
return std::string("Tréning sa spúšťa, ventilátor sa zapne o 10 min");
|
||||
if (id(training_stop_running).state != "0" && !id(training_stop_running).state.empty())
|
||||
return std::string("Tréning sa ukončuje, ventilátor sa vypne o 2 min");
|
||||
return std::string("");
|
||||
|
||||
- id: update_stopwatch
|
||||
then:
|
||||
- lvgl.label.update:
|
||||
id: stopwatch_label
|
||||
text: !lambda |-
|
||||
const unsigned ms = id(sw_accum) + (id(sw_running) ? millis() - id(sw_start) : 0);
|
||||
const unsigned s = ms / 1000;
|
||||
if (s >= 3600)
|
||||
return str_sprintf("%u:%02u:%02u", s / 3600, (s / 60) % 60, s % 60);
|
||||
return str_sprintf("%02u:%02u.%u", s / 60, s % 60, (ms / 100) % 10);
|
||||
- lvgl.label.update:
|
||||
id: stopwatch_start_label
|
||||
text: !lambda |-
|
||||
return std::string(id(sw_running) ? "Stop" : "Štart");
|
||||
|
||||
- id: update_status
|
||||
then:
|
||||
- lambda: |-
|
||||
// same colors as the other status screens: ok green, waiting orange, details gray
|
||||
static const uint32_t OK = 0x4DD966, WAIT = 0xFFB300;
|
||||
auto set = [](lv_obj_t *label, const std::string &text, uint32_t color) {
|
||||
lv_label_set_text(label, text.c_str());
|
||||
lv_obj_set_style_text_color(label, lv_color_hex(color), LV_PART_MAIN);
|
||||
};
|
||||
|
||||
const bool wifi_ok = wifi::global_wifi_component->is_connected();
|
||||
set(id(status_wifi),
|
||||
wifi_ok ? str_sprintf("WiFi: connected (%d dBm)", wifi::global_wifi_component->wifi_rssi())
|
||||
: std::string("WiFi: connecting..."),
|
||||
wifi_ok ? OK : WAIT);
|
||||
|
||||
// IPv6 first (skip link-local), then IPv4
|
||||
std::string ips;
|
||||
if (wifi_ok) {
|
||||
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
for (bool v6 : {true, false}) {
|
||||
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||
if (!ip.is_set() || ip.is_ip6() != v6)
|
||||
continue;
|
||||
ip.str_to(buf);
|
||||
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||
continue;
|
||||
if (!ips.empty())
|
||||
ips += "\n";
|
||||
ips += buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
lv_label_set_text(id(status_ips), ips.c_str());
|
||||
if (ips.empty())
|
||||
lv_obj_add_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||
else
|
||||
lv_obj_remove_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
const bool api_ok = api::global_api_server->is_connected();
|
||||
set(id(status_ha), api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...",
|
||||
api_ok ? OK : WAIT);
|
||||
|
||||
const uint32_t up = millis() / 1000;
|
||||
lv_label_set_text(id(status_footer),
|
||||
str_sprintf("ESPHome %s · up %ud %02u:%02u:%02u", ESPHOME_VERSION, up / 86400,
|
||||
(up / 3600) % 24, (up / 60) % 60, up % 60).c_str());
|
||||
interval:
|
||||
- interval: 1s
|
||||
then:
|
||||
- lvgl.label.update:
|
||||
id: clock_label
|
||||
text: !lambda |-
|
||||
auto now = id(ha_time).now();
|
||||
if (!now.is_valid())
|
||||
return std::string("--:--:--");
|
||||
return now.strftime("%H:%M:%S");
|
||||
- interval: 100ms
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- lambda: return id(sw_running);
|
||||
- lvgl.page.is_showing: stopwatch_page
|
||||
then:
|
||||
- script.execute: update_stopwatch
|
||||
- interval: 2s
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lvgl.page.is_showing: status_page
|
||||
then:
|
||||
- script.execute: update_status
|
||||
|
||||
image:
|
||||
- platform: file
|
||||
file: assets/images/made-for-esphome-white-on-black.svg
|
||||
id: esphome_logo
|
||||
resize: 280x83
|
||||
type: RGB565
|
||||
transparency: alpha_channel
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto@500"
|
||||
id: font_digits
|
||||
size: 110
|
||||
glyphs: "0123456789:.-"
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_ui
|
||||
size: 24
|
||||
glyphs: &sk_glyphs "ÁÄČĎÉÍĹĽŇÓÔŔŠŤÚÝŽáäčďéíĺľňóôŕšťúýž°·"
|
||||
glyphsets: [GF_Latin_Kernel]
|
||||
- file: "gfonts://Roboto@500"
|
||||
id: font_button
|
||||
size: 34
|
||||
glyphs: *sk_glyphs
|
||||
glyphsets: [GF_Latin_Kernel]
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_small
|
||||
size: 18
|
||||
glyphs: *sk_glyphs
|
||||
glyphsets: [GF_Latin_Kernel]
|
||||
|
||||
lvgl:
|
||||
rotation: 90
|
||||
buffer_size: 100%
|
||||
log_level: WARN
|
||||
color_depth: 16
|
||||
default_font: montserrat_18
|
||||
# on_idle:
|
||||
# - timeout: !lambda "return (id(display_timeout).state * 1000);"
|
||||
# then:
|
||||
# - logger.log: "LVGL is idle"
|
||||
# - light.turn_off:
|
||||
# id: display_backlight
|
||||
# transition_length: 2500ms
|
||||
# - lvgl.pause:
|
||||
default_font: font_ui
|
||||
bg_color: 0x000000
|
||||
text_color: 0xFFFFFF
|
||||
theme:
|
||||
button:
|
||||
bg_color: 0x2B2F36
|
||||
radius: 12
|
||||
checked:
|
||||
bg_color: 0xE08A00
|
||||
buttonmatrix:
|
||||
bg_opa: TRANSP
|
||||
border_width: 0
|
||||
items:
|
||||
bg_color: 0x2B2F36
|
||||
text_color: 0xFFFFFF
|
||||
radius: 12
|
||||
checked:
|
||||
bg_color: 0xE08A00
|
||||
slider:
|
||||
bg_color: 0x2B2F36
|
||||
indicator:
|
||||
bg_color: 0xE08A00
|
||||
knob:
|
||||
bg_color: 0xFFFFFF
|
||||
top_layer:
|
||||
widgets:
|
||||
- buttonmatrix:
|
||||
id: nav_bar
|
||||
align: bottom_mid
|
||||
# styles: header_footer
|
||||
pad_all: 0
|
||||
outline_width: 0
|
||||
id: top_layer
|
||||
# items:
|
||||
# styles: header_footer
|
||||
width: 100%
|
||||
height: 56
|
||||
pad_all: 4
|
||||
pad_column: 6
|
||||
items:
|
||||
text_font: montserrat_28
|
||||
rows:
|
||||
- buttons:
|
||||
- id: page_prev
|
||||
text: "\uF053"
|
||||
text: ""
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.previous:
|
||||
- id: page_home
|
||||
text: "\uF015"
|
||||
text: ""
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.show: main_page
|
||||
lvgl.page.show: clock_page
|
||||
- id: page_next
|
||||
text: "\uF054"
|
||||
text: ""
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.next:
|
||||
pages:
|
||||
- id: main_page
|
||||
# 1: clock (default after boot)
|
||||
- id: clock_page
|
||||
widgets:
|
||||
- label:
|
||||
text: "ESPHome LVGL Display"
|
||||
id: clock_label
|
||||
align: CENTER
|
||||
text_align: CENTER
|
||||
text_color: 0xFFFFFF
|
||||
- id: excercise_page
|
||||
y: -28
|
||||
text_font: font_digits
|
||||
text: "--:--:--"
|
||||
|
||||
# 2: basement lights
|
||||
- id: lights_page
|
||||
pad_all: 10
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: 'Hello World!'
|
||||
- button:
|
||||
id: exercise_room_light_btn
|
||||
id: basement_light_btn
|
||||
align: TOP_LEFT
|
||||
width: 100
|
||||
height: 70
|
||||
width: 148
|
||||
height: 80
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: 'Light'
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "Pivnica"
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.exercise_room
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: ${light_basement}
|
||||
- button:
|
||||
id: exercise_room_fan_btn
|
||||
align: TOP_RIGHT
|
||||
width: 100
|
||||
height: 70
|
||||
id: workbench_light_btn
|
||||
align: TOP_MID
|
||||
width: 148
|
||||
height: 80
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: 'Fan'
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "Pracovný stôl"
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.mcu_dimmer_exercise_room_fan
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: ${light_workbench}
|
||||
- button:
|
||||
id: exercise_light_btn
|
||||
align: TOP_RIGHT
|
||||
width: 148
|
||||
height: 80
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: "Posilka"
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: ${light_exercise}
|
||||
- obj:
|
||||
id: exercise_controls
|
||||
hidden: true
|
||||
y: 110
|
||||
width: 460
|
||||
height: 120
|
||||
pad_all: 0
|
||||
bg_opa: TRANSP
|
||||
border_width: 0
|
||||
scrollbar_mode: "OFF"
|
||||
widgets:
|
||||
- label:
|
||||
x: 4
|
||||
y: 0
|
||||
text_font: font_small
|
||||
text: "Posilka jas"
|
||||
- slider:
|
||||
id: exercise_brightness_slider
|
||||
x: 14
|
||||
y: 32
|
||||
width: 430
|
||||
height: 16
|
||||
min_value: 1
|
||||
max_value: 100
|
||||
value: 100
|
||||
on_release:
|
||||
- homeassistant.action:
|
||||
action: light.turn_on
|
||||
data:
|
||||
entity_id: ${light_exercise}
|
||||
brightness_pct: !lambda return (int) x;
|
||||
- label:
|
||||
x: 4
|
||||
y: 62
|
||||
text_font: font_small
|
||||
text: "Posilka teplota (2700 – 6500 K)"
|
||||
- slider:
|
||||
id: exercise_color_temp_slider
|
||||
x: 14
|
||||
y: 94
|
||||
width: 430
|
||||
height: 16
|
||||
min_value: 2700
|
||||
max_value: 6500
|
||||
value: 2700
|
||||
on_release:
|
||||
- homeassistant.action:
|
||||
action: light.turn_on
|
||||
data:
|
||||
entity_id: ${light_exercise}
|
||||
color_temp_kelvin: !lambda return (int) x;
|
||||
|
||||
# 3: training start / stop
|
||||
- id: training_page
|
||||
pad_all: 10
|
||||
widgets:
|
||||
- button:
|
||||
align: TOP_LEFT
|
||||
y: 10
|
||||
width: 222
|
||||
height: 150
|
||||
bg_color: 0x1E7D32
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text_font: font_button
|
||||
text: "Štart"
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: automation.trigger
|
||||
data:
|
||||
entity_id: automation.zwift_enable_fan
|
||||
- button:
|
||||
align: TOP_RIGHT
|
||||
y: 10
|
||||
width: 222
|
||||
height: 150
|
||||
bg_color: 0xB71C1C
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text_font: font_button
|
||||
text: "Stop"
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: automation.trigger
|
||||
data:
|
||||
entity_id: automation.zwift_disable_fan
|
||||
- label:
|
||||
id: training_status_label
|
||||
align: TOP_MID
|
||||
y: 185
|
||||
width: 440
|
||||
text_align: CENTER
|
||||
text_font: font_small
|
||||
text: ""
|
||||
|
||||
# 4: fan
|
||||
- id: fan_page
|
||||
pad_all: 10
|
||||
widgets:
|
||||
- buttonmatrix:
|
||||
id: fan_matrix
|
||||
align: TOP_MID
|
||||
width: 460
|
||||
height: 180
|
||||
pad_row: 8
|
||||
pad_column: 8
|
||||
one_checked: true
|
||||
items:
|
||||
text_font: font_button
|
||||
rows:
|
||||
- buttons:
|
||||
- text: "Vyp"
|
||||
control:
|
||||
checkable: true
|
||||
on_press:
|
||||
- homeassistant.action:
|
||||
action: light.turn_off
|
||||
data:
|
||||
entity_id: ${fan}
|
||||
- text: "50"
|
||||
control:
|
||||
checkable: true
|
||||
on_press:
|
||||
- homeassistant.action:
|
||||
action: light.turn_on
|
||||
data:
|
||||
entity_id: ${fan}
|
||||
brightness_pct: "50"
|
||||
- text: "60"
|
||||
control:
|
||||
checkable: true
|
||||
on_press:
|
||||
- homeassistant.action:
|
||||
action: light.turn_on
|
||||
data:
|
||||
entity_id: ${fan}
|
||||
brightness_pct: "60"
|
||||
- text: "70"
|
||||
control:
|
||||
checkable: true
|
||||
on_press:
|
||||
- homeassistant.action:
|
||||
action: light.turn_on
|
||||
data:
|
||||
entity_id: ${fan}
|
||||
brightness_pct: "70"
|
||||
- buttons:
|
||||
- text: "80"
|
||||
control:
|
||||
checkable: true
|
||||
on_press:
|
||||
- homeassistant.action:
|
||||
action: light.turn_on
|
||||
data:
|
||||
entity_id: ${fan}
|
||||
brightness_pct: "80"
|
||||
- text: "90"
|
||||
control:
|
||||
checkable: true
|
||||
on_press:
|
||||
- homeassistant.action:
|
||||
action: light.turn_on
|
||||
data:
|
||||
entity_id: ${fan}
|
||||
brightness_pct: "90"
|
||||
- text: "100"
|
||||
control:
|
||||
checkable: true
|
||||
on_press:
|
||||
- homeassistant.action:
|
||||
action: light.turn_on
|
||||
data:
|
||||
entity_id: ${fan}
|
||||
brightness_pct: "100"
|
||||
- label:
|
||||
id: fan_status_label
|
||||
align: TOP_MID
|
||||
y: 200
|
||||
text_font: font_small
|
||||
text: ""
|
||||
|
||||
# 5: stopwatch
|
||||
- id: stopwatch_page
|
||||
pad_all: 10
|
||||
widgets:
|
||||
- label:
|
||||
id: stopwatch_label
|
||||
align: TOP_MID
|
||||
y: 10
|
||||
text_font: font_digits
|
||||
text: "00:00.0"
|
||||
- button:
|
||||
align: TOP_LEFT
|
||||
y: 150
|
||||
width: 222
|
||||
height: 90
|
||||
widgets:
|
||||
- label:
|
||||
id: stopwatch_start_label
|
||||
align: CENTER
|
||||
text_font: font_button
|
||||
text: "Štart"
|
||||
on_click:
|
||||
- lambda: |-
|
||||
if (id(sw_running)) {
|
||||
id(sw_accum) += millis() - id(sw_start);
|
||||
id(sw_running) = false;
|
||||
} else {
|
||||
id(sw_start) = millis();
|
||||
id(sw_running) = true;
|
||||
}
|
||||
- script.execute: update_stopwatch
|
||||
- button:
|
||||
align: TOP_RIGHT
|
||||
y: 150
|
||||
width: 222
|
||||
height: 90
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text_font: font_button
|
||||
text: "Reset"
|
||||
on_click:
|
||||
- lambda: |-
|
||||
id(sw_accum) = 0;
|
||||
id(sw_start) = millis();
|
||||
- script.execute: update_stopwatch
|
||||
|
||||
# 6: status
|
||||
- id: status_page
|
||||
on_load:
|
||||
- script.execute: update_status
|
||||
widgets:
|
||||
- image:
|
||||
src: esphome_logo
|
||||
align: TOP_MID
|
||||
y: 8
|
||||
- label:
|
||||
align: TOP_MID
|
||||
y: 96
|
||||
text: "${name}"
|
||||
- obj:
|
||||
id: status_lines
|
||||
x: 20
|
||||
y: 126
|
||||
width: 440
|
||||
height: SIZE_CONTENT
|
||||
bg_opa: TRANSP
|
||||
border_width: 0
|
||||
pad_all: 0
|
||||
scrollbar_mode: "OFF"
|
||||
layout:
|
||||
type: flex
|
||||
flex_flow: column
|
||||
pad_row: 4
|
||||
widgets:
|
||||
- label:
|
||||
id: status_wifi
|
||||
text_font: font_small
|
||||
text: ""
|
||||
- label:
|
||||
id: status_ips
|
||||
text_font: font_small
|
||||
text_color: 0x8C8C8C
|
||||
pad_left: 16
|
||||
text: ""
|
||||
- label:
|
||||
id: status_ha
|
||||
text_font: font_small
|
||||
text: ""
|
||||
- label:
|
||||
id: status_footer
|
||||
text_font: font_small
|
||||
text_color: 0x8C8C8C
|
||||
text: ""
|
||||
+302
-600
@@ -1,4 +1,88 @@
|
||||
---
|
||||
# Hardware: Sunton ESP32-8048S043 (CYD 4.3", 800x480, capacitive touch)
|
||||
# Pinout:
|
||||
# # Display
|
||||
# R0 GND
|
||||
# R1 GND
|
||||
# R2 GND
|
||||
# R3 45
|
||||
# R4 48
|
||||
# R5 47
|
||||
# R6 21
|
||||
# R7 14
|
||||
# G0 GND
|
||||
# G1 GND
|
||||
# G2 5
|
||||
# G3 6
|
||||
# G4 7
|
||||
# G5 15
|
||||
# G6 16
|
||||
# G7 4
|
||||
# B0 GND
|
||||
# B1 GND
|
||||
# B2 GND
|
||||
# B3 8
|
||||
# B4 3
|
||||
# B5 46
|
||||
# B6 9
|
||||
# B7 1
|
||||
# DCLK 42
|
||||
# HSYNC 39
|
||||
# VSYNC 41
|
||||
# DE 40
|
||||
# BL 2
|
||||
# # Capacitive Touch
|
||||
# TP_INT
|
||||
# TP_RST 38
|
||||
# TP_SDA 19
|
||||
# TP_SCL 20
|
||||
# # SDCARD
|
||||
# TF_CS 10
|
||||
# SPI_MOSI 11
|
||||
# SPI_CLK 12
|
||||
# SPI_MISO 13
|
||||
# # SPI Conector (HC1.25-4)
|
||||
# SPI_CS 19
|
||||
# SPI_MOSI 11
|
||||
# SPI_CLK 12
|
||||
# SPI_MISO 13
|
||||
# # UART1 (SH1.0-4)
|
||||
# 1 GND
|
||||
# 2 3V3-ESP
|
||||
# 3 17
|
||||
# 4 18
|
||||
# # UART1, USB Connector (Resistive) / I2C (Capacitive) (HC1.25-4)
|
||||
# 1 17
|
||||
# 2 18
|
||||
# 3 19 TP_SDA
|
||||
# 4 20 TP_SCL
|
||||
# Used:
|
||||
# GPIO1 main_display (display mipi_rgb data_pins.blue)
|
||||
# GPIO2 gpio_backlight_pwm (output ledc)
|
||||
# GPIO3 main_display (display mipi_rgb data_pins.blue)
|
||||
# GPIO4 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO5 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO6 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO7 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO8 main_display (display mipi_rgb data_pins.blue)
|
||||
# GPIO9 main_display (display mipi_rgb data_pins.blue)
|
||||
# GPIO14 main_display (display mipi_rgb data_pins.red)
|
||||
# GPIO15 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO16 main_display (display mipi_rgb data_pins.green)
|
||||
# GPIO19 i2c_main (i2c sda)
|
||||
# GPIO20 i2c_main (i2c scl)
|
||||
# GPIO21 main_display (display mipi_rgb data_pins.red)
|
||||
# GPIO38 main_touch (touchscreen gt911 reset_pin)
|
||||
# GPIO39 main_display (display mipi_rgb hsync_pin)
|
||||
# GPIO40 main_display (display mipi_rgb de_pin)
|
||||
# GPIO41 main_display (display mipi_rgb vsync_pin)
|
||||
# GPIO42 main_display (display mipi_rgb pclk_pin)
|
||||
# GPIO45 main_display (display mipi_rgb data_pins.red)
|
||||
# GPIO46 main_display (display mipi_rgb data_pins.blue)
|
||||
# GPIO47 main_display (display mipi_rgb data_pins.red)
|
||||
# GPIO48 main_display (display mipi_rgb data_pins.red)
|
||||
# I2C:
|
||||
# 0x5D main_touch (touchscreen gt911)
|
||||
substitutions:
|
||||
device: lvgl
|
||||
name: LVGL
|
||||
@@ -10,18 +94,22 @@ esphome:
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
platformio_options:
|
||||
board_build.flash_mode: dio
|
||||
on_boot:
|
||||
- priority: -100
|
||||
then:
|
||||
- lvgl.page.show: status_page
|
||||
|
||||
esp32:
|
||||
variant: esp32s3
|
||||
flash_size: 16MB
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
# Required to achieve sufficient PSRAM bandwidth
|
||||
# PSRAM bandwidth for the RGB panel (not set by ESPHome); without it the
|
||||
# image shifts during flash/WiFi activity
|
||||
sdkconfig_options:
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: y
|
||||
CONFIG_ESP32S3_DATA_CACHE_64KB: y
|
||||
CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
|
||||
CONFIG_SPIRAM_RODATA: y
|
||||
@@ -32,62 +120,26 @@ psram:
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
# PINOUTS: ESP32 Cheap Yellow Display, 4.3", IPS, Capacitive
|
||||
## Display
|
||||
# R0 GND
|
||||
# R1 GND
|
||||
# R2 GND
|
||||
# R3 45
|
||||
# R4 48
|
||||
# R5 47
|
||||
# R6 21
|
||||
# R7 14
|
||||
# G0 GND
|
||||
# G1 GND
|
||||
# G2 5
|
||||
# G3 6
|
||||
# G4 7
|
||||
# G5 15
|
||||
# G6 16
|
||||
# G7 4
|
||||
# B0 GND
|
||||
# B1 GND
|
||||
# B2 GND
|
||||
# B3 8
|
||||
# B4 3
|
||||
# B5 46
|
||||
# B6 9
|
||||
# B7 1
|
||||
# DCLK 42
|
||||
# HSYNC 39
|
||||
# VSYNC 41
|
||||
# DE 40
|
||||
## Capacitive Touch
|
||||
# TP_INT
|
||||
# TP_RST 38
|
||||
# TP_SDA 19
|
||||
# TP_SCL 20
|
||||
## SDCARD
|
||||
# TF_CS 10
|
||||
# SPI_MOSI 11
|
||||
# SPI_CLK 12
|
||||
# SPI_MISO 13
|
||||
## SPI Conector (HC1.25-4)
|
||||
# SPI_CS 19
|
||||
# SPI_MOSI 11
|
||||
# SPI_CLK 12
|
||||
# SPI_MISO 13
|
||||
## UART1 (SH1.0-4)
|
||||
# 1 GND
|
||||
# 2 3V3-ESP
|
||||
# 3 17
|
||||
# 4 18
|
||||
## UART1, USB Connector (Resistive) / I2C (Capacitive) (HC1.25-4)
|
||||
# 1 17
|
||||
# 2 18
|
||||
# 3 19 TP_SDA
|
||||
# 4 20 TP_SCL
|
||||
api:
|
||||
# status page until Home Assistant connects (log viewers don't subscribe to states)
|
||||
on_client_connected:
|
||||
- delay: 2s
|
||||
- if:
|
||||
condition:
|
||||
api.connected:
|
||||
state_subscription_only: true
|
||||
then:
|
||||
- lvgl.page.show: main_page
|
||||
on_client_disconnected:
|
||||
- if:
|
||||
condition:
|
||||
not:
|
||||
api.connected:
|
||||
state_subscription_only: true
|
||||
then:
|
||||
- lvgl.page.show: status_page
|
||||
|
||||
i2c:
|
||||
- id: i2c_main
|
||||
@@ -95,573 +147,223 @@ i2c:
|
||||
scl: 20
|
||||
scan: true
|
||||
|
||||
# same pins and timing as the 5" Sunton board, pclk lowered for the 4.3" panel
|
||||
display:
|
||||
- platform: rpi_dpi_rgb
|
||||
- platform: mipi_rgb
|
||||
model: ESP32-8048S050
|
||||
id: main_display
|
||||
color_order: RGB
|
||||
invert_colors: true
|
||||
pclk_frequency: 12.5MHz
|
||||
update_interval: never
|
||||
auto_clear_enabled: false # takes 2.8 seconds to clear the display
|
||||
dimensions:
|
||||
width: 800
|
||||
height: 480
|
||||
de_pin: 40
|
||||
hsync_pin: 39
|
||||
vsync_pin: 41
|
||||
pclk_pin: 42
|
||||
pclk_frequency: 12.5MHz
|
||||
hsync_pulse_width: 4
|
||||
hsync_front_porch: 8
|
||||
hsync_back_porch: 8
|
||||
vsync_pulse_width: 4
|
||||
vsync_front_porch: 8
|
||||
vsync_back_porch: 8
|
||||
data_pins:
|
||||
red:
|
||||
- 45 # R1
|
||||
- 48 # R2
|
||||
- 47 # R3
|
||||
- 21 # R4
|
||||
- 14 # R5
|
||||
green:
|
||||
- 5 # G0
|
||||
- 6 # G1
|
||||
- 7 # G2
|
||||
- 15 # G3
|
||||
- 16 # G4
|
||||
- 4 # G5
|
||||
blue:
|
||||
- 8 # B1
|
||||
- 3 # B2
|
||||
- 46 # B3
|
||||
- 9 # B4
|
||||
- 1 # B5
|
||||
|
||||
touchscreen:
|
||||
- platform: gt911
|
||||
id: main_touch
|
||||
display: main_display
|
||||
i2c_id: i2c_main
|
||||
reset_pin: 38
|
||||
|
||||
# Define a PWM output on the ESP32
|
||||
output:
|
||||
- platform: ledc
|
||||
id: gpio_backlight_pwm
|
||||
pin: 2
|
||||
frequency: 1220
|
||||
id: gpio_backlight_pwm
|
||||
|
||||
light:
|
||||
- platform: monochromatic
|
||||
output: gpio_backlight_pwm
|
||||
id: back_light
|
||||
name: Backlight
|
||||
output: gpio_backlight_pwm
|
||||
restore_mode: ALWAYS_ON
|
||||
|
||||
text_sensor:
|
||||
- platform: template
|
||||
# name: Uptime Human Readable
|
||||
id: uptime_human
|
||||
icon: mdi:clock-start
|
||||
internal: True
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: time_comp
|
||||
|
||||
number:
|
||||
- platform: template
|
||||
initial_value: 0
|
||||
id: counting_number
|
||||
internal: True
|
||||
min_value: -10
|
||||
max_value: 10
|
||||
step: 1
|
||||
optimistic: True
|
||||
|
||||
# debug:
|
||||
# update_interval: 5s
|
||||
|
||||
sensor:
|
||||
- platform: uptime
|
||||
id: uptime_counter
|
||||
update_interval: 1s
|
||||
accuracy_decimals: 0
|
||||
on_raw_value:
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: back_light
|
||||
brightness: 90%
|
||||
- number.increment:
|
||||
id: counting_number
|
||||
cycle: True
|
||||
- script.execute: update_display
|
||||
# - platform: debug
|
||||
# free:
|
||||
# name: "${name} Heap Free"
|
||||
# block:
|
||||
# name: "${name} Heap Max Block"
|
||||
# loop_time:
|
||||
# name: "${name} Loop Time"
|
||||
# psram:
|
||||
# name: "${name} Free PSRAM"
|
||||
- platform: uptime
|
||||
# name: Uptime Sensor
|
||||
internal: True
|
||||
id: uptime_sensor
|
||||
update_interval: 1s
|
||||
on_raw_value:
|
||||
then:
|
||||
- text_sensor.template.publish:
|
||||
id: uptime_human
|
||||
state: !lambda |-
|
||||
int seconds = round(id(uptime_sensor).raw_state);
|
||||
int days = seconds / (24 * 3600);
|
||||
seconds = seconds % (24 * 3600);
|
||||
int hours = seconds / 3600;
|
||||
seconds = seconds % 3600;
|
||||
int minutes = seconds / 60;
|
||||
seconds = seconds % 60;
|
||||
return (
|
||||
("Uptime ") +
|
||||
(days ? to_string(days) + "d " : "") +
|
||||
(hours ? to_string(hours) + "h " : "") +
|
||||
(minutes ? to_string(minutes) + "m " : "") +
|
||||
(to_string(seconds) + "s")
|
||||
).c_str();
|
||||
|
||||
script:
|
||||
- id: update_display
|
||||
- id: update_status
|
||||
then:
|
||||
- lambda: |-
|
||||
// same colors as the other status screens: ok green, waiting orange, details gray
|
||||
static const uint32_t OK = 0x4DD966, WAIT = 0xFFB300;
|
||||
auto set = [](lv_obj_t *label, const std::string &text, uint32_t color) {
|
||||
lv_label_set_text(label, text.c_str());
|
||||
lv_obj_set_style_text_color(label, lv_color_hex(color), LV_PART_MAIN);
|
||||
};
|
||||
|
||||
const bool wifi_ok = wifi::global_wifi_component->is_connected();
|
||||
set(id(status_wifi),
|
||||
wifi_ok ? str_sprintf("WiFi: connected (%d dBm)", wifi::global_wifi_component->wifi_rssi())
|
||||
: std::string("WiFi: connecting..."),
|
||||
wifi_ok ? OK : WAIT);
|
||||
|
||||
// IPv6 first (skip link-local), then IPv4
|
||||
std::string ips;
|
||||
if (wifi_ok) {
|
||||
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
for (bool v6 : {true, false}) {
|
||||
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||
if (!ip.is_set() || ip.is_ip6() != v6)
|
||||
continue;
|
||||
ip.str_to(buf);
|
||||
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||
continue;
|
||||
if (!ips.empty())
|
||||
ips += "\n";
|
||||
ips += buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
lv_label_set_text(id(status_ips), ips.c_str());
|
||||
if (ips.empty())
|
||||
lv_obj_add_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||
else
|
||||
lv_obj_remove_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
const bool api_ok = api::global_api_server->is_connected();
|
||||
set(id(status_ha), api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...",
|
||||
api_ok ? OK : WAIT);
|
||||
|
||||
const uint32_t up = millis() / 1000;
|
||||
lv_label_set_text(id(status_footer),
|
||||
str_sprintf("ESPHome %s · up %ud %02u:%02u:%02u", ESPHOME_VERSION, up / 86400,
|
||||
(up / 3600) % 24, (up / 60) % 60, up % 60).c_str());
|
||||
interval:
|
||||
- interval: 1s
|
||||
then:
|
||||
# - lvgl.indicator.line.update:
|
||||
# id: power_meter_input
|
||||
# value: !lambda return id(counting_number).state;
|
||||
- lvgl.label.update:
|
||||
id: battery_kw
|
||||
id: clock_label
|
||||
text: !lambda |-
|
||||
static char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%.1fkW", id(counting_number).state);
|
||||
return buf;
|
||||
- lvgl.label.update:
|
||||
id: battery_status
|
||||
text_color: 0xFF0000
|
||||
#text: "charging"
|
||||
text: "discharging"
|
||||
- lvgl.label.update:
|
||||
id: battery_soc
|
||||
text: !lambda |-
|
||||
static char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%.1f%%", id(counting_number).state);
|
||||
return buf;
|
||||
- lvgl.label.update:
|
||||
id: solar_kw
|
||||
text: !lambda |-
|
||||
static char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%.1fkW", id(counting_number).state);
|
||||
return buf;
|
||||
# - lvgl.img.update:
|
||||
# id: img_solar_power
|
||||
# src: solar_power_icon
|
||||
# img_recolor: 0xFFF000 #mixes this color with the base image
|
||||
# #img_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
||||
# #bg_color: 0xFFFFFF
|
||||
# # - lvgl.indicator.line.update:
|
||||
# # id: power_meter_input2
|
||||
# # value: !lambda return id(counting_number).state;
|
||||
- lvgl.label.update:
|
||||
id: battery_kw2
|
||||
text: !lambda |-
|
||||
static char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%.1fkW", id(counting_number).state);
|
||||
return buf;
|
||||
- lvgl.label.update:
|
||||
id: battery_status2
|
||||
text_color: 0xFF0000
|
||||
#text: "charging"
|
||||
text: "discharging"
|
||||
- lvgl.label.update:
|
||||
id: battery_soc2
|
||||
text: !lambda |-
|
||||
static char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%.1f%%", id(counting_number).state);
|
||||
return buf;
|
||||
- lvgl.label.update:
|
||||
id: solar_kw2
|
||||
text: !lambda |-
|
||||
static char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%.1fkW", id(counting_number).state);
|
||||
return buf;
|
||||
# - lvgl.img.update:
|
||||
# id: img_solar_power2
|
||||
# src: solar_power_icon
|
||||
# img_recolor: 0xFFF000 #mixes this color with the base image
|
||||
# #img_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
||||
# #bg_color: 0xFFFFFF
|
||||
auto now = id(ha_time).now();
|
||||
if (!now.is_valid())
|
||||
return std::string("--:--");
|
||||
return now.strftime("%H:%M");
|
||||
- if:
|
||||
condition:
|
||||
lvgl.page.is_showing: status_page
|
||||
then:
|
||||
- script.execute: update_status
|
||||
|
||||
image:
|
||||
- file: mdi:sun-wireless-outline
|
||||
#"solar-power-invert.png"
|
||||
# mdi:solar-power
|
||||
# mdi:battery-charging-outline
|
||||
# mdi:battery-arrow-up-outline
|
||||
# mdi:battery-arrow-down-outline
|
||||
id: solar_power_icon
|
||||
resize: 50x50
|
||||
type: BINARY
|
||||
- platform: file
|
||||
file: assets/images/made-for-esphome-white-on-black.svg
|
||||
id: esphome_logo
|
||||
resize: 400x118
|
||||
type: RGB565
|
||||
transparency: alpha_channel
|
||||
|
||||
- file: mdi:battery-arrow-down-outline
|
||||
id: home_battery_icon
|
||||
resize: 30x30
|
||||
type: BINARY
|
||||
font:
|
||||
- file: "gfonts://Roboto@500"
|
||||
id: font_clock
|
||||
size: 200
|
||||
glyphs: "0123456789:-"
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_title
|
||||
size: 40
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_status
|
||||
size: 26
|
||||
glyphs: "·"
|
||||
glyphsets: [GF_Latin_Kernel]
|
||||
|
||||
lvgl:
|
||||
log_level: INFO
|
||||
log_level: WARN
|
||||
color_depth: 16
|
||||
#buffer_size: 25%
|
||||
bg_color: 0
|
||||
border_width: 0
|
||||
outline_width: 0
|
||||
shadow_width: 0
|
||||
text_font: unscii_16
|
||||
align: center
|
||||
style_definitions:
|
||||
- id: meter_style
|
||||
text_font: unscii_8
|
||||
#state_default: null
|
||||
- id: font_style
|
||||
text_font: MONTSERRAT_24
|
||||
#text_font: unscii_16
|
||||
align: center
|
||||
text_color: 0xFFFFFF
|
||||
#bg_opa: cover
|
||||
bg_color: 0x000000
|
||||
text_color: 0xFFFFFF
|
||||
theme:
|
||||
buttonmatrix:
|
||||
bg_opa: TRANSP
|
||||
bg_color: 0
|
||||
radius: 4
|
||||
pad_all: 2
|
||||
- id: details_style
|
||||
text_font: MONTSERRAT_18
|
||||
align: center
|
||||
text_color: 0xFFFFFF
|
||||
#bg_opa: cover
|
||||
bg_opa: TRANSP
|
||||
bg_color: 0
|
||||
radius: 4
|
||||
pad_all: 2
|
||||
widgets:
|
||||
- obj: # Meter
|
||||
height: 240
|
||||
width: 240
|
||||
#bg_opa: TRANSP
|
||||
bg_color: 0
|
||||
border_width: 0
|
||||
outline_width: 0
|
||||
shadow_width: 0
|
||||
pad_all: 4
|
||||
align: TOP_LEFT
|
||||
#scrollbar_mode: "off"
|
||||
widgets:
|
||||
- meter: # Gradient color arc
|
||||
height: 100%
|
||||
width: 100%
|
||||
border_width: 0
|
||||
outline_width: 0
|
||||
align: center
|
||||
bg_color: 0
|
||||
styles: meter_style
|
||||
#bg_opa: TRANSP
|
||||
scales:
|
||||
angle_range: 180 # sets the total angle to 180 = starts mid left and ends mid right
|
||||
range_to: 10
|
||||
range_from: -10
|
||||
ticks:
|
||||
count: 0
|
||||
indicators:
|
||||
- line:
|
||||
id: power_meter_input
|
||||
width: 8
|
||||
color: 0xFFFFFF
|
||||
r_mod: 12 #sets line length by this much difference from the scale default radius
|
||||
value: 50
|
||||
# - image:
|
||||
# id: power_meter_input_img
|
||||
- arc:
|
||||
color: 0xFF3000
|
||||
r_mod: 10 #radius difference from the scale default radius
|
||||
width: 20
|
||||
start_value: -10
|
||||
end_value: 0
|
||||
- arc:
|
||||
color: 0x00FF00
|
||||
r_mod: 10 #radius difference from the scale default radius
|
||||
width: 20
|
||||
start_value: 0
|
||||
end_value: 10
|
||||
#- canvas:
|
||||
- arc: # black arc to erase middle part of meter indicator line
|
||||
height: 160
|
||||
width: 160
|
||||
align: center
|
||||
arc_color: 0x000000 # background color
|
||||
arc_width: 150
|
||||
start_angle: 0
|
||||
end_angle: 360
|
||||
indicator:
|
||||
arc_width: 150
|
||||
arc_color: 0x000000
|
||||
- label: # gauge lower and higher range indicators
|
||||
styles: font_style
|
||||
text_font: MONTSERRAT_18 # override font size
|
||||
y: 8 #negative = higher
|
||||
x: -99
|
||||
text: "-10"
|
||||
- label:
|
||||
styles: font_style
|
||||
text_font: MONTSERRAT_18 # override font size
|
||||
y: 8 #negative = higher
|
||||
x: 99
|
||||
text: "+10"
|
||||
- label:
|
||||
styles: font_style
|
||||
id: battery_status
|
||||
y: -35 #negative = higher
|
||||
- label:
|
||||
styles: font_style
|
||||
id: battery_kw
|
||||
y: -60
|
||||
- label:
|
||||
styles: font_style
|
||||
text_font: MONTSERRAT_40 # override font size
|
||||
id: battery_soc
|
||||
y: 0
|
||||
- label:
|
||||
styles: font_style
|
||||
id: solar_kw
|
||||
text_color: 0xFFFF00
|
||||
y: 90
|
||||
#- img:
|
||||
# src: home_battery_icon
|
||||
#id: img_solar_power
|
||||
#align: center
|
||||
#img_recolor: 0xFFFFFF
|
||||
#img_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
||||
#bg_color: 0xFFFFFF
|
||||
#y: -35
|
||||
#x: -60
|
||||
- image:
|
||||
src: solar_power_icon
|
||||
id: img_solar_power
|
||||
align: center
|
||||
image_recolor: 0xFFFF00
|
||||
image_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
||||
#bg_color: 0xFFFFFF
|
||||
y: 50
|
||||
#x: -60
|
||||
- obj: # Meter
|
||||
height: 240
|
||||
width: 240
|
||||
#bg_opa: TRANSP
|
||||
bg_color: 0
|
||||
border_width: 0
|
||||
outline_width: 0
|
||||
shadow_width: 0
|
||||
pad_all: 4
|
||||
align: TOP_MID
|
||||
#scrollbar_mode: "off"
|
||||
widgets:
|
||||
- meter: # Gradient color arc
|
||||
height: 100%
|
||||
width: 100%
|
||||
border_width: 0
|
||||
outline_width: 0
|
||||
align: center
|
||||
bg_color: 0
|
||||
styles: meter_style
|
||||
#bg_opa: TRANSP
|
||||
scales:
|
||||
angle_range: 180 # sets the total angle to 180 = starts mid left and ends mid right
|
||||
range_to: 10
|
||||
range_from: -10
|
||||
ticks:
|
||||
count: 0
|
||||
indicators:
|
||||
- line:
|
||||
id: power_meter_input2
|
||||
width: 8
|
||||
color: 0xFFFFFF
|
||||
r_mod: 12 #sets line length by this much difference from the scale default radius
|
||||
value: 50
|
||||
# - image:
|
||||
# id: power_meter_input_img2
|
||||
- arc:
|
||||
color: 0xFF3000
|
||||
r_mod: 10 #radius difference from the scale default radius
|
||||
width: 20
|
||||
start_value: -10
|
||||
end_value: 0
|
||||
- arc:
|
||||
color: 0x00FF00
|
||||
r_mod: 10 #radius difference from the scale default radius
|
||||
width: 20
|
||||
start_value: 0
|
||||
end_value: 10
|
||||
#- canvas:
|
||||
- arc: # black arc to erase middle part of meter indicator line
|
||||
height: 160
|
||||
width: 160
|
||||
align: center
|
||||
arc_color: 0x000000 # background color
|
||||
arc_width: 150
|
||||
start_angle: 0
|
||||
end_angle: 360
|
||||
indicator:
|
||||
arc_width: 150
|
||||
arc_color: 0x000000
|
||||
- label: # gauge lower and higher range indicators
|
||||
styles: font_style
|
||||
text_font: MONTSERRAT_18 # override font size
|
||||
y: 8 #negative = higher
|
||||
x: -99
|
||||
text: "-10"
|
||||
- label:
|
||||
styles: font_style
|
||||
text_font: MONTSERRAT_18 # override font size
|
||||
y: 8 #negative = higher
|
||||
x: 99
|
||||
text: "+10"
|
||||
- label:
|
||||
styles: font_style
|
||||
id: battery_status2
|
||||
y: -35 #negative = higher
|
||||
- label:
|
||||
styles: font_style
|
||||
id: battery_kw2
|
||||
y: -60
|
||||
- label:
|
||||
styles: font_style
|
||||
text_font: MONTSERRAT_40 # override font size
|
||||
id: battery_soc2
|
||||
y: 0
|
||||
- label:
|
||||
styles: font_style
|
||||
id: solar_kw2
|
||||
text_color: 0xFFFF00
|
||||
y: 90
|
||||
#- img:
|
||||
# src: home_battery_icon
|
||||
#id: img_solar_power
|
||||
#align: center
|
||||
#img_recolor: 0xFFFFFF
|
||||
#img_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
||||
#bg_color: 0xFFFFFF
|
||||
#y: -35
|
||||
#x: -60
|
||||
- image:
|
||||
src: solar_power_icon
|
||||
id: img_solar_power2
|
||||
align: center
|
||||
image_recolor: 0xFFFF00
|
||||
image_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
||||
#bg_color: 0xFFFFFF
|
||||
y: 50
|
||||
#x: -60
|
||||
- obj: # Meter
|
||||
height: 240
|
||||
width: 240
|
||||
#bg_opa: TRANSP
|
||||
bg_color: 0
|
||||
border_width: 0
|
||||
outline_width: 0
|
||||
shadow_width: 0
|
||||
pad_all: 4
|
||||
align: BOTTOM_LEFT
|
||||
#scrollbar_mode: "off"
|
||||
widgets:
|
||||
- meter: # Gradient color arc
|
||||
height: 100%
|
||||
width: 100%
|
||||
border_width: 0
|
||||
outline_width: 0
|
||||
align: center
|
||||
bg_color: 0
|
||||
styles: meter_style
|
||||
#bg_opa: TRANSP
|
||||
scales:
|
||||
angle_range: 180 # sets the total angle to 180 = starts mid left and ends mid right
|
||||
range_to: 10
|
||||
range_from: -10
|
||||
ticks:
|
||||
count: 0
|
||||
indicators:
|
||||
- line:
|
||||
id: power_meter_input3
|
||||
width: 8
|
||||
color: 0xFFFFFF
|
||||
r_mod: 12 #sets line length by this much difference from the scale default radius
|
||||
value: 50
|
||||
# - image:
|
||||
# id: power_meter_input_img3
|
||||
- arc:
|
||||
color: 0xFF3000
|
||||
r_mod: 10 #radius difference from the scale default radius
|
||||
width: 20
|
||||
start_value: -10
|
||||
end_value: 0
|
||||
- arc:
|
||||
color: 0x00FF00
|
||||
r_mod: 10 #radius difference from the scale default radius
|
||||
width: 20
|
||||
start_value: 0
|
||||
end_value: 10
|
||||
#- canvas:
|
||||
- arc: # black arc to erase middle part of meter indicator line
|
||||
height: 160
|
||||
width: 160
|
||||
align: center
|
||||
arc_color: 0x000000 # background color
|
||||
arc_width: 150
|
||||
start_angle: 0
|
||||
end_angle: 360
|
||||
indicator:
|
||||
arc_width: 150
|
||||
arc_color: 0x000000
|
||||
- label: # gauge lower and higher range indicators
|
||||
styles: font_style
|
||||
text_font: MONTSERRAT_18 # override font size
|
||||
y: 8 #negative = higher
|
||||
x: -99
|
||||
text: "-10"
|
||||
- label:
|
||||
styles: font_style
|
||||
text_font: MONTSERRAT_18 # override font size
|
||||
y: 8 #negative = higher
|
||||
x: 99
|
||||
text: "+10"
|
||||
- label:
|
||||
styles: font_style
|
||||
id: battery_status3
|
||||
y: -35 #negative = higher
|
||||
- label:
|
||||
styles: font_style
|
||||
id: battery_kw3
|
||||
y: -60
|
||||
- label:
|
||||
styles: font_style
|
||||
text_font: MONTSERRAT_40 # override font size
|
||||
id: battery_soc3
|
||||
y: 0
|
||||
- label:
|
||||
styles: font_style
|
||||
id: solar_kw3
|
||||
text_color: 0xFFFF00
|
||||
y: 90
|
||||
#- img:
|
||||
# src: home_battery_icon
|
||||
#id: img_solar_power
|
||||
#align: center
|
||||
#img_recolor: 0xFFFFFF
|
||||
#img_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
||||
#bg_color: 0xFFFFFF
|
||||
#y: -35
|
||||
#x: -60
|
||||
- image:
|
||||
src: solar_power_icon
|
||||
id: img_solar_power3
|
||||
align: center
|
||||
image_recolor: 0xFFFF00
|
||||
image_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
||||
#bg_color: 0xFFFFFF
|
||||
y: 50
|
||||
#x: -60
|
||||
border_width: 0
|
||||
items:
|
||||
bg_color: 0x2B2F36
|
||||
text_color: 0xFFFFFF
|
||||
radius: 12
|
||||
checked:
|
||||
bg_color: 0xE08A00
|
||||
top_layer:
|
||||
widgets:
|
||||
# same navigation bar as on mcu-lvgl-trainer
|
||||
- buttonmatrix:
|
||||
id: nav_bar
|
||||
align: bottom_mid
|
||||
width: 100%
|
||||
height: 64
|
||||
pad_all: 4
|
||||
pad_column: 6
|
||||
items:
|
||||
text_font: montserrat_28
|
||||
rows:
|
||||
- buttons:
|
||||
- id: page_prev
|
||||
text: "\uF053"
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.previous:
|
||||
- id: page_home
|
||||
text: "\uF015"
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.show: main_page
|
||||
- id: page_next
|
||||
text: "\uF054"
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.next:
|
||||
pages:
|
||||
# 1: clock
|
||||
- id: main_page
|
||||
widgets:
|
||||
- label:
|
||||
id: clock_label
|
||||
align: CENTER
|
||||
y: -32
|
||||
text_font: font_clock
|
||||
text: "--:--"
|
||||
|
||||
# 2: status (shown until Home Assistant connects)
|
||||
- id: status_page
|
||||
on_load:
|
||||
- script.execute: update_status
|
||||
widgets:
|
||||
- image:
|
||||
src: esphome_logo
|
||||
align: TOP_MID
|
||||
y: 16
|
||||
- label:
|
||||
align: TOP_MID
|
||||
y: 140
|
||||
text_font: font_title
|
||||
text: "${name}"
|
||||
- obj:
|
||||
id: status_lines
|
||||
x: 60
|
||||
y: 196
|
||||
width: 700
|
||||
height: SIZE_CONTENT
|
||||
bg_opa: TRANSP
|
||||
border_width: 0
|
||||
pad_all: 0
|
||||
scrollbar_mode: "OFF"
|
||||
layout:
|
||||
type: flex
|
||||
flex_flow: column
|
||||
pad_row: 8
|
||||
widgets:
|
||||
- label:
|
||||
id: status_wifi
|
||||
text_font: font_status
|
||||
text: ""
|
||||
- label:
|
||||
id: status_ips
|
||||
text_font: font_status
|
||||
text_color: 0x8C8C8C
|
||||
pad_left: 30
|
||||
text: ""
|
||||
- label:
|
||||
id: status_ha
|
||||
text_font: font_status
|
||||
text: ""
|
||||
- label:
|
||||
id: status_footer
|
||||
text_font: font_status
|
||||
text_color: 0x8C8C8C
|
||||
text: ""
|
||||
+600
-77
@@ -1,97 +1,404 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Paper
|
||||
# Pinout:
|
||||
# GPIO2 -> main_power (switch gpio)
|
||||
# GPIO5 -> ext_power (switch gpio)
|
||||
# GPIO12 -> spi (spi mosi_pin)
|
||||
# GPIO13 -> spi (spi miso_pin)
|
||||
# GPIO14 -> spi (spi clk_pin)
|
||||
# GPIO15 -> m5paper_display (display it8951 cs_pin)
|
||||
# GPIO21 -> i2c_main (i2c sda)
|
||||
# GPIO22 -> i2c_main (i2c scl)
|
||||
# GPIO23 -> m5paper_display (display it8951 reset_pin)
|
||||
# GPIO27 -> m5paper_display (display it8951 busy_pin)
|
||||
# GPIO35 -> Battery Voltage (sensor adc)
|
||||
# GPIO36 -> gt911_touchscreen (touchscreen gt911 interrupt_pin)
|
||||
# GPIO37 -> M5Paper Button UP (binary_sensor gpio) (inverted)
|
||||
# GPIO38 -> M5Paper Button Press (binary_sensor gpio) (inverted)
|
||||
# GPIO39 -> M5Paper Button Down (binary_sensor gpio) (inverted)
|
||||
# I2C:
|
||||
# 0x44 sensor sht3xd (sensor sht3xd)
|
||||
# 0x51 rtc_time (time bm8563)
|
||||
# 0x5D gt911_touchscreen (touchscreen gt911)
|
||||
substitutions:
|
||||
device: m5paper
|
||||
name: M5Paper
|
||||
area: DEV
|
||||
comment: M5Paper
|
||||
waste_calendar: calendar.https_www_hviezdoslavov_sk_kalendar_icalfeed_category_0_ongoing_1
|
||||
# contacts + manual
|
||||
birthday_calendars: calendar.birthdays, calendar.narodeniny
|
||||
|
||||
# Power modes
|
||||
# - USB: always on, WiFi + API, events refreshed and redrawn after midnight
|
||||
# - battery: RTC wakes the board at 00:05, it refreshes the events, draws them and powers
|
||||
# itself off; the RTC timer lasts at most 255 min, so the wake-ups on the way to
|
||||
# midnight only re-arm it
|
||||
# The power button (and USB) also connects to WiFi: the events are refreshed on every
|
||||
# Home Assistant connection outside the battery cycle.
|
||||
# Screens: status (after power-on, until Home Assistant connects) and calendar
|
||||
# The mode is detected by trying to power off: on USB the board keeps running.
|
||||
|
||||
esphome:
|
||||
name: mcu-${device}
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
includes:
|
||||
- include/m5paper_calendar.h
|
||||
on_boot:
|
||||
- priority: 600
|
||||
then:
|
||||
- lambda: |-
|
||||
// BM8563 CONTROL_STATUS_2: TF (bit 2) is set when the timer woke us up
|
||||
const uint8_t reg = 0x01;
|
||||
uint8_t status = 0;
|
||||
if (id(i2c_main)->write_readv(0x51, ®, 1, &status, 1) == i2c::ERROR_OK) {
|
||||
id(rtc_wake) = status & 0x04;
|
||||
// clear TF and TIE so the RTC releases the power latch
|
||||
const uint8_t clear[2] = {reg, uint8_t(status & ~0x05)};
|
||||
id(i2c_main)->write(0x51, clear, 2);
|
||||
}
|
||||
m5paper_calendar::load(m5paper_calendar::events());
|
||||
ESP_LOGI("m5paper", "Wake-up: %s, events: %u", id(rtc_wake) ? "RTC" : "power-on", m5paper_calendar::events().count);
|
||||
- priority: -100
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(rtc_wake);
|
||||
then:
|
||||
- script.execute: battery_cycle
|
||||
else:
|
||||
# power button / USB: stay awake for a while (logs, OTA), then decide the mode
|
||||
- wifi.enable:
|
||||
# IT8951 init runs asynchronously after setup
|
||||
- delay: 3s
|
||||
- component.update: m5paper_display
|
||||
- delay: 2min
|
||||
- script.execute: battery_cycle
|
||||
|
||||
esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
minimum_chip_revision: 3.0
|
||||
sram1_as_iram: true
|
||||
|
||||
psram:
|
||||
|
||||
external_components:
|
||||
- source: github://Passific/m5paper_esphome
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
on_time_sync:
|
||||
- bm8563.write_time
|
||||
- platform: bm8563
|
||||
id: rtc_time
|
||||
sleep_duration: 3600000ms
|
||||
globals:
|
||||
- id: show_status
|
||||
type: bool
|
||||
initial_value: 'true'
|
||||
- id: usb_mode
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: rtc_wake
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
# battery cycle running: calendar screen regardless of the connection state
|
||||
- id: calendar_page
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: fetch_done
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: sleep_s
|
||||
type: int
|
||||
initial_value: '900'
|
||||
|
||||
m5paper:
|
||||
battery_power_pin: GPIO5
|
||||
main_power_pin: GPIO2
|
||||
# update_interval: 10s
|
||||
# battery_voltage:
|
||||
# name: "${name} Battery"
|
||||
wifi:
|
||||
enable_on_boot: false
|
||||
on_connect:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(show_status) && !id(calendar_page);
|
||||
then:
|
||||
- component.update: m5paper_display
|
||||
|
||||
api:
|
||||
# status screen until Home Assistant connects (log viewers don't subscribe to states)
|
||||
on_client_connected:
|
||||
- delay: 2s
|
||||
- if:
|
||||
condition:
|
||||
api.connected:
|
||||
state_subscription_only: true
|
||||
then:
|
||||
- lambda: id(show_status) = false;
|
||||
- if:
|
||||
condition:
|
||||
lambda: return !id(calendar_page);
|
||||
then:
|
||||
- component.update: m5paper_display
|
||||
# outside the battery cycle (power button, USB) WiFi is up anyway: always refresh
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- lambda: return !id(calendar_page);
|
||||
- api.connected:
|
||||
state_subscription_only: true
|
||||
then:
|
||||
- script.execute: fetch_events
|
||||
on_client_disconnected:
|
||||
- if:
|
||||
condition:
|
||||
not:
|
||||
api.connected:
|
||||
state_subscription_only: true
|
||||
then:
|
||||
- lambda: id(show_status) = true;
|
||||
- if:
|
||||
condition:
|
||||
lambda: return !id(calendar_page);
|
||||
then:
|
||||
- component.update: m5paper_display
|
||||
|
||||
# I2C devices (i2c_main)
|
||||
# 0x44 SHT30
|
||||
# 0x50
|
||||
# 0x51
|
||||
# 0x5D GT911
|
||||
# 0x50 FM24C02 EEPROM (unused)
|
||||
# 0x51 BM8563 RTC
|
||||
# 0x5D GT911 touch (docs say 0x14; the driver tries 0x5D, then 0x14)
|
||||
|
||||
i2c:
|
||||
- id: i2c_main
|
||||
sda: 21
|
||||
scl: 22
|
||||
scan: true #false
|
||||
scan: true
|
||||
# Port A
|
||||
# - id: i2c_a
|
||||
# sda: 25
|
||||
# scl: 32
|
||||
# scan: true #false
|
||||
# Port B
|
||||
# - id: i2c_b
|
||||
# sda: 26
|
||||
# scl: 33
|
||||
# scan: true #false
|
||||
# Port C
|
||||
# - id: i2c_c
|
||||
# sda: 18
|
||||
# scl: 19
|
||||
# scan: true #false
|
||||
|
||||
spi:
|
||||
clk_pin: 14
|
||||
miso_pin: 13
|
||||
mosi_pin: 12
|
||||
|
||||
switch:
|
||||
# Power control MOS (no PMU on M5Paper); on battery the device powers off when this goes low
|
||||
- platform: gpio
|
||||
id: main_power
|
||||
pin:
|
||||
number: GPIO2
|
||||
ignore_strapping_warning: true
|
||||
restore_mode: ALWAYS_ON
|
||||
internal: true
|
||||
# External power (Grove ports)
|
||||
- platform: gpio
|
||||
id: ext_power
|
||||
pin:
|
||||
number: GPIO5
|
||||
ignore_strapping_warning: true
|
||||
restore_mode: ALWAYS_ON
|
||||
internal: true
|
||||
|
||||
script:
|
||||
# One wake-up on battery: refresh the events if they are old, draw them, power off
|
||||
- id: battery_cycle
|
||||
mode: single
|
||||
then:
|
||||
- lambda: id(calendar_page) = true;
|
||||
- wait_until:
|
||||
condition:
|
||||
lambda: return id(rtc_time).now().is_valid();
|
||||
timeout: 10s
|
||||
- if:
|
||||
# RTC wake-up on the way to midnight: nothing to draw
|
||||
condition:
|
||||
lambda: |-
|
||||
const auto now = id(rtc_time).now();
|
||||
return id(rtc_wake) && now.is_valid() && now.hour != 0;
|
||||
then:
|
||||
- script.execute: try_sleep
|
||||
else:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return m5paper_calendar::stale(id(rtc_time).now());
|
||||
then:
|
||||
- wifi.enable:
|
||||
- wait_until:
|
||||
condition:
|
||||
api.connected:
|
||||
state_subscription_only: true
|
||||
timeout: 45s
|
||||
- if:
|
||||
condition:
|
||||
api.connected:
|
||||
state_subscription_only: true
|
||||
then:
|
||||
- script.execute: fetch_events
|
||||
- wait_until:
|
||||
condition:
|
||||
lambda: return id(fetch_done);
|
||||
timeout: 15s
|
||||
- component.update: m5paper_display
|
||||
# let the e-paper refresh finish before cutting power
|
||||
- delay: 4s
|
||||
- script.execute: try_sleep
|
||||
|
||||
# today and the next 31 days (the month grid and the list)
|
||||
- id: fetch_events
|
||||
mode: single
|
||||
then:
|
||||
- lambda: id(fetch_done) = false;
|
||||
- homeassistant.action:
|
||||
action: calendar.get_events
|
||||
data:
|
||||
entity_id: ${waste_calendar}, ${birthday_calendars}
|
||||
start_date_time: !lambda return id(rtc_time).now().strftime("%Y-%m-%d 00:00:00");
|
||||
duration: "744:00:00"
|
||||
capture_response: true
|
||||
on_success:
|
||||
- lambda: |-
|
||||
auto &ev = m5paper_calendar::events();
|
||||
if (m5paper_calendar::parse(response, "${waste_calendar}", id(rtc_time).now().timestamp, ev)) {
|
||||
const bool saved = m5paper_calendar::save(ev);
|
||||
ESP_LOGI("m5paper", "Events: %u%s", ev.count, saved ? "" : " (flash write failed)");
|
||||
} else {
|
||||
ESP_LOGW("m5paper", "Events: unexpected response");
|
||||
}
|
||||
id(fetch_done) = true;
|
||||
# the battery cycle draws on its own
|
||||
- if:
|
||||
condition:
|
||||
lambda: return !id(calendar_page);
|
||||
then:
|
||||
- component.update: m5paper_display
|
||||
on_error:
|
||||
- lambda: |-
|
||||
ESP_LOGW("m5paper", "Events: %s", error.c_str());
|
||||
id(fetch_done) = true;
|
||||
|
||||
# Arm the RTC and cut power; if we are still running afterwards, we are on USB
|
||||
- id: try_sleep
|
||||
mode: single
|
||||
then:
|
||||
- lambda: |-
|
||||
// next 00:05; the timer lasts at most 255 min, so get there in steps
|
||||
const int MAX = 255 * 60;
|
||||
int duration = MAX;
|
||||
const auto now = id(rtc_time).now();
|
||||
if (now.is_valid()) {
|
||||
const int sod = now.hour * 3600 + now.minute * 60 + now.second;
|
||||
int left = (86400 + 300 - sod) % 86400;
|
||||
if (left < 600) // just drawn: the 1/60 Hz RTC timer may wake us up to a minute early
|
||||
left += 86400;
|
||||
// leave at least 20 min for the last step, so it cannot fall into the case above
|
||||
duration = left > MAX ? std::min(MAX, left - 1200) : left;
|
||||
}
|
||||
if (duration > 255) // longer timers count whole minutes
|
||||
duration = (duration + 59) / 60 * 60;
|
||||
id(sleep_s) = std::min(duration, MAX);
|
||||
ESP_LOGI("m5paper", "Powering off for %d s", id(sleep_s));
|
||||
- bm8563.start_timer:
|
||||
duration: !lambda return id(sleep_s);
|
||||
# a battery wake-up is shorter than boot_is_good_after: confirm the OTA image
|
||||
# before cutting power, otherwise the bootloader rolls back
|
||||
- safe_mode.mark_successful:
|
||||
- delay: 200ms
|
||||
- switch.turn_off: main_power
|
||||
- delay: 2s
|
||||
# still alive -> USB power
|
||||
- switch.turn_on: main_power
|
||||
- lambda: |-
|
||||
ESP_LOGI("m5paper", "Still running, USB power");
|
||||
id(usb_mode) = true;
|
||||
id(rtc_wake) = false;
|
||||
id(calendar_page) = false;
|
||||
id(show_status) = !api::global_api_server->is_connected_with_state_subscription();
|
||||
- wifi.enable:
|
||||
- component.update: m5paper_display
|
||||
|
||||
interval:
|
||||
# USB mode: the battery voltage drops once USB is unplugged -> go to battery mode
|
||||
- interval: 5min
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: |-
|
||||
static float last = NAN;
|
||||
const float v = id(battery_voltage).state;
|
||||
const bool dropped = id(usb_mode) && !std::isnan(last) && last - v > 0.03f;
|
||||
last = v;
|
||||
return dropped;
|
||||
then:
|
||||
- lambda: |-
|
||||
ESP_LOGI("m5paper", "Battery voltage dropping, checking USB");
|
||||
id(usb_mode) = false;
|
||||
- script.execute: battery_cycle
|
||||
|
||||
time:
|
||||
- id: !extend ha_time
|
||||
on_time_sync:
|
||||
- bm8563.write_time
|
||||
- if:
|
||||
condition:
|
||||
lambda: return !id(calendar_page);
|
||||
then:
|
||||
- component.update: m5paper_display
|
||||
on_time:
|
||||
# new day
|
||||
- seconds: 0
|
||||
minutes: 0
|
||||
hours: 0
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return !id(show_status) && !id(calendar_page);
|
||||
then:
|
||||
- component.update: m5paper_display
|
||||
# USB mode: keep the stored events fresh for battery mode
|
||||
- seconds: 0
|
||||
minutes: 5
|
||||
hours: 0
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- lambda: return id(usb_mode);
|
||||
- api.connected:
|
||||
state_subscription_only: true
|
||||
then:
|
||||
- script.execute: fetch_events
|
||||
- platform: bm8563
|
||||
id: rtc_time
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
name: ${name} Button UP
|
||||
id: button_up
|
||||
pin:
|
||||
number: 37
|
||||
number: GPIO37
|
||||
inverted: true
|
||||
on_press:
|
||||
- component.update: m5paper_display
|
||||
on_release:
|
||||
- component.update: m5paper_display
|
||||
- platform: gpio
|
||||
name: ${name} Button Press
|
||||
id: button_press
|
||||
pin:
|
||||
number: 38
|
||||
number: GPIO38
|
||||
inverted: true
|
||||
- platform: gpio
|
||||
name: ${name} Button Down
|
||||
id: button_down
|
||||
pin:
|
||||
number: 39
|
||||
number: GPIO39
|
||||
inverted: true
|
||||
- platform: touchscreen
|
||||
name: ${name} Top Left Touch Button
|
||||
@@ -103,51 +410,19 @@ binary_sensor:
|
||||
y_min: 0
|
||||
y_max: 100
|
||||
on_press:
|
||||
# Example of applying the sleep duration and shutting down to the lowest power mode
|
||||
- bm8563.apply_sleep_duration
|
||||
- delay: 1s
|
||||
- m5paper.shutdown_main_power
|
||||
- script.execute: battery_cycle
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_default
|
||||
size: 20
|
||||
- file: "gfonts://Roboto"
|
||||
id: roboto_32
|
||||
size: 32
|
||||
- file: "gfonts://Roboto"
|
||||
id: roboto_24
|
||||
size: 24
|
||||
- file: "gfonts://Roboto"
|
||||
id: roboto_12
|
||||
size: 12
|
||||
|
||||
display:
|
||||
- platform: it8951e
|
||||
id: m5paper_display
|
||||
cs_pin: GPIO15
|
||||
reset_pin: GPIO23
|
||||
busy_pin: GPIO27
|
||||
rotation: 90
|
||||
reversed: False
|
||||
update_interval: "never"
|
||||
lambda: |-
|
||||
it.printf(25, 25, id(roboto_32), "%.1f°", id(current_temperature).state);
|
||||
it.strftime(350, 25, id(roboto_32), "%H:%M:%S", id(rtc_time).now());
|
||||
|
||||
touchscreen:
|
||||
- platform: gt911
|
||||
display: m5paper_display
|
||||
id: gt911_touchscreen
|
||||
interrupt_pin: GPIO36
|
||||
|
||||
# display:
|
||||
# - platform: waveshare_epaper
|
||||
# cs_pin: 15
|
||||
# model: 2.90in
|
||||
# full_update_every: 30
|
||||
# lambda: |-
|
||||
# it.print(0, 0, id(font1), "Hello World!");
|
||||
button:
|
||||
- platform: template
|
||||
name: Refresh Display
|
||||
entity_category: config
|
||||
on_press:
|
||||
- component.update: m5paper_display
|
||||
- platform: template
|
||||
name: Refresh Calendar
|
||||
entity_category: config
|
||||
on_press:
|
||||
- script.execute: fetch_events
|
||||
|
||||
sensor:
|
||||
- platform: sht3xd
|
||||
@@ -156,9 +431,257 @@ sensor:
|
||||
id: current_temperature
|
||||
humidity:
|
||||
name: Humidity
|
||||
id: current_humidity
|
||||
address: 0x44
|
||||
update_interval: 60s
|
||||
- platform: adc
|
||||
id: battery_voltage
|
||||
name: Battery Voltage
|
||||
pin: GPIO35
|
||||
attenuation: 12db
|
||||
samples: 16
|
||||
update_interval: 60s
|
||||
entity_category: diagnostic
|
||||
filters:
|
||||
- multiply: 2.0 # 1:2 voltage divider
|
||||
- platform: copy
|
||||
source_id: battery_voltage
|
||||
id: battery_level
|
||||
name: Battery
|
||||
unit_of_measurement: "%"
|
||||
device_class: battery
|
||||
accuracy_decimals: 0
|
||||
filters:
|
||||
- calibrate_linear:
|
||||
- 3.3 -> 0.0
|
||||
- 4.15 -> 100.0
|
||||
- clamp:
|
||||
min_value: 0
|
||||
max_value: 100
|
||||
|
||||
# touchscreen:
|
||||
# - platform: gt911
|
||||
# interrupt_pin: 36
|
||||
.image_defaults:
|
||||
icon: &icon {platform: file, type: BINARY, transparency: chroma_key}
|
||||
|
||||
image:
|
||||
- platform: file
|
||||
file: assets/images/made-for-esphome-white-on-black.svg
|
||||
id: esphome_logo
|
||||
resize: 460x136
|
||||
type: GRAYSCALE
|
||||
transparency: alpha_channel
|
||||
# same order as m5paper_calendar::Kind
|
||||
- {<<: *icon, file: "mdi:cake-variant", id: icon_birthday, resize: 32x32}
|
||||
- {<<: *icon, file: "mdi:trash-can", id: icon_waste, resize: 32x32}
|
||||
- {<<: *icon, file: "mdi:cake-variant", id: mark_birthday, resize: 18x18}
|
||||
- {<<: *icon, file: "mdi:trash-can", id: mark_waste, resize: 18x18}
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_title
|
||||
size: 48
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_status
|
||||
size: 28
|
||||
glyphs: &sk_glyphs "ÁÄČĎÉÍĹĽŇÓÔŔŠŤÚÝŽáäčďéíĺľňóôŕšťúýž°·"
|
||||
glyphsets: [GF_Latin_Kernel]
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_small
|
||||
size: 24
|
||||
glyphs: *sk_glyphs
|
||||
glyphsets: [GF_Latin_Kernel]
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_footer
|
||||
size: 18
|
||||
glyphs: "0123456789abcdef:. "
|
||||
- file: "gfonts://Roboto@500"
|
||||
id: font_date
|
||||
size: 34
|
||||
glyphs: *sk_glyphs
|
||||
glyphsets: [GF_Latin_Kernel]
|
||||
|
||||
color:
|
||||
- id: ink
|
||||
red: 0%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
- id: paper
|
||||
red: 100%
|
||||
green: 100%
|
||||
blue: 100%
|
||||
|
||||
display:
|
||||
- platform: it8951
|
||||
id: m5paper_display
|
||||
model: m5stack-m5paper
|
||||
rotation: 270
|
||||
update_interval: never
|
||||
# 1bpp: updates use the fast DU waveform, full GC16 refresh every 30 updates
|
||||
grayscale: false
|
||||
dithering: false
|
||||
lambda: |-
|
||||
// 540x960 portrait
|
||||
it.fill(id(paper));
|
||||
const int w = it.get_width();
|
||||
const int cx = w / 2;
|
||||
const float batt = id(battery_level).state;
|
||||
|
||||
// status screen after power-on, until Home Assistant connects
|
||||
if (id(show_status) && !id(calendar_page)) {
|
||||
it.image(cx, 200, id(esphome_logo), ImageAlign::CENTER);
|
||||
it.print(cx, 320, id(font_title), id(ink), TextAlign::CENTER, "${name}");
|
||||
|
||||
const bool wifi_ok = wifi::global_wifi_component->is_connected();
|
||||
if (wifi_ok) {
|
||||
it.printf(40, 440, id(font_status), id(ink), "WiFi: connected (%d dBm)", wifi::global_wifi_component->wifi_rssi());
|
||||
// IPv6 first (skip link-local), then IPv4
|
||||
int y = 484;
|
||||
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
for (bool v6 : {true, false}) {
|
||||
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||
if (!ip.is_set() || ip.is_ip6() != v6 || y > 590)
|
||||
continue;
|
||||
ip.str_to(buf);
|
||||
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||
continue;
|
||||
it.print(60, y, id(font_small), id(ink), buf);
|
||||
y += 34;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
it.print(40, 440, id(font_status), id(ink), "WiFi: connecting...");
|
||||
}
|
||||
const bool api_ok = api::global_api_server->is_connected_with_state_subscription();
|
||||
it.print(40, 620, id(font_status), id(ink),
|
||||
api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...");
|
||||
if (!std::isnan(batt))
|
||||
it.printf(40, 670, id(font_status), id(ink), "Battery: %.0f%% (%.2f V)", batt, id(battery_voltage).state);
|
||||
it.printf(40, 720, id(font_status), id(ink), "Calendar: %u events stored", m5paper_calendar::events().count);
|
||||
it.printf(40, 900, id(font_small), id(ink), "ESPHome %s", ESPHOME_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
// calendar screen
|
||||
using namespace m5paper_calendar;
|
||||
image::Image *icons[] = {id(icon_birthday), id(icon_waste)};
|
||||
image::Image *marks[] = {id(mark_birthday), id(mark_waste)};
|
||||
static const char *const DAYS[] = {"Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota", "Nedeľa"};
|
||||
static const char *const DAYS_SHORT[] = {"Po", "Ut", "St", "Št", "Pi", "So", "Ne"};
|
||||
static const char *const MONTHS[] = {"Január", "Február", "Marec", "Apríl", "Máj", "Jún",
|
||||
"Júl", "August", "September", "Október", "November", "December"};
|
||||
|
||||
const auto &ev = m5paper_calendar::events();
|
||||
const auto now = id(rtc_time).now();
|
||||
if (!now.is_valid()) {
|
||||
it.print(cx, 400, id(font_status), id(ink), TextAlign::CENTER, "Čas nie je k dispozícii");
|
||||
return;
|
||||
}
|
||||
const int32_t today = local_day(now);
|
||||
|
||||
// header: date, battery
|
||||
it.printf(24, 16, id(font_date), id(ink), "%s %d. %d.", DAYS[weekday(today)], now.day_of_month, now.month);
|
||||
if (!std::isnan(batt))
|
||||
it.printf(w - 24, 22, id(font_small), id(ink), TextAlign::TOP_RIGHT, "%.0f %%", batt);
|
||||
|
||||
// month grid; days with events get the icons of their kinds
|
||||
const int32_t first = days_from_civil(now.year, now.month, 1);
|
||||
const int32_t next = now.month == 12 ? days_from_civil(now.year + 1, 1, 1) : days_from_civil(now.year, now.month + 1, 1);
|
||||
const int dim = next - first, lead = weekday(first);
|
||||
uint8_t kinds[32] = {};
|
||||
for (int i = 0; i < ev.count; i++) {
|
||||
if (ev.items[i].day >= first && ev.items[i].day < next)
|
||||
kinds[ev.items[i].day - first + 1] |= 1 << ev.items[i].kind;
|
||||
}
|
||||
const int col_w = 70, row_h = 54, gx = (w - 7 * col_w) / 2, gy = 150;
|
||||
it.printf(cx, 70, id(font_status), id(ink), TextAlign::TOP_CENTER, "%s %d", MONTHS[now.month - 1], now.year);
|
||||
for (int c = 0; c < 7; c++)
|
||||
it.print(gx + c * col_w + col_w / 2, 116, id(font_small), id(ink), TextAlign::TOP_CENTER, DAYS_SHORT[c]);
|
||||
for (int d = 1; d <= dim; d++) {
|
||||
const int cell = lead + d - 1;
|
||||
const int x = gx + (cell % 7) * col_w, y = gy + (cell / 7) * row_h;
|
||||
const bool is_today = first + d - 1 == today;
|
||||
if (is_today)
|
||||
it.filled_rectangle(x + 3, y + 1, col_w - 6, row_h - 2, id(ink));
|
||||
const Color fg = is_today ? id(paper) : id(ink);
|
||||
it.printf(x + col_w / 2, y + 4, id(font_small), fg, TextAlign::TOP_CENTER, "%d", d);
|
||||
const int n = __builtin_popcount(kinds[d]);
|
||||
int mx = x + (col_w - n * 20) / 2;
|
||||
for (int k = 0; k < 2; k++) {
|
||||
if (kinds[d] & (1 << k)) {
|
||||
it.image(mx, y + 32, marks[k], fg);
|
||||
mx += 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// next 14 days; waste collection today or tomorrow is inverted: put the bin out
|
||||
const int top = 490, list_h = 50, max_rows = 8;
|
||||
it.horizontal_line(24, top - 12, w - 48, id(ink));
|
||||
int rows = 0, more = 0;
|
||||
for (int i = 0; i < ev.count; i++) {
|
||||
const Event &e = ev.items[i];
|
||||
if (e.day < today || e.day >= today + 14)
|
||||
continue;
|
||||
if (rows == max_rows) {
|
||||
more++;
|
||||
continue;
|
||||
}
|
||||
const int y = top + rows++ * list_h;
|
||||
const bool urgent = e.kind == WASTE && e.day <= today + 1;
|
||||
if (urgent)
|
||||
it.filled_rectangle(16, y, w - 32, list_h - 4, id(ink));
|
||||
const Color fg = urgent ? id(paper) : id(ink);
|
||||
const int ty = y + (list_h - 4) / 2;
|
||||
if (e.day == today)
|
||||
it.print(28, ty, id(font_small), fg, TextAlign::CENTER_LEFT, "Dnes");
|
||||
else if (e.day == today + 1)
|
||||
it.print(28, ty, id(font_small), fg, TextAlign::CENTER_LEFT, "Zajtra");
|
||||
else {
|
||||
const auto t = ESPTime::from_epoch_local(uint32_t(e.day) * 86400 + 43200);
|
||||
it.printf(28, ty, id(font_small), fg, TextAlign::CENTER_LEFT, "%s %d. %d.", DAYS_SHORT[weekday(e.day)],
|
||||
t.day_of_month, t.month);
|
||||
}
|
||||
if (e.kind < 2)
|
||||
it.image(150, ty - 16, icons[e.kind], fg);
|
||||
it.start_clipping(194, y, w - 28, y + list_h);
|
||||
if (e.minute == ALL_DAY)
|
||||
it.print(194, ty, id(font_small), fg, TextAlign::CENTER_LEFT, e.summary);
|
||||
else
|
||||
it.printf(194, ty, id(font_small), fg, TextAlign::CENTER_LEFT, "%02d:%02d %s", e.minute / 60, e.minute % 60,
|
||||
e.summary);
|
||||
it.end_clipping();
|
||||
}
|
||||
if (!ev.valid())
|
||||
it.print(cx, top + 20, id(font_small), id(ink), TextAlign::TOP_CENTER, "Kalendár nie je k dispozícii");
|
||||
else if (rows == 0)
|
||||
it.print(cx, top + 20, id(font_small), id(ink), TextAlign::TOP_CENTER, "Žiadne udalosti v najbližších 14 dňoch");
|
||||
else if (more > 0)
|
||||
it.printf(w - 28, top + rows * list_h, id(font_small), id(ink), TextAlign::TOP_RIGHT, "+%d ďalšie", more);
|
||||
|
||||
// footer: IPv6 address (only while WiFi is up), last download
|
||||
it.horizontal_line(24, 910, w - 48, id(ink));
|
||||
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||
if (!ip.is_set() || !ip.is_ip6())
|
||||
continue;
|
||||
ip.str_to(buf);
|
||||
if (strncmp(buf, "fe80", 4) != 0) {
|
||||
it.print(24, 944, id(font_footer), id(ink), TextAlign::BOTTOM_LEFT, buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ev.valid()) {
|
||||
const auto f = ESPTime::from_epoch_local(ev.fetched_at);
|
||||
it.printf(w - 24, 944, id(font_footer), id(ink), TextAlign::BOTTOM_RIGHT, "%d. %d. %02d:%02d", f.day_of_month,
|
||||
f.month, f.hour, f.minute);
|
||||
}
|
||||
|
||||
touchscreen:
|
||||
- platform: gt911
|
||||
id: gt911_touchscreen
|
||||
display: m5paper_display
|
||||
# GPIO36 is input-only; INT is only driven as output when reset_pin is set
|
||||
interrupt_pin:
|
||||
number: GPIO36
|
||||
mode:
|
||||
input: true
|
||||
output: false
|
||||
+20
-18
@@ -1,4 +1,19 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite
|
||||
# Pinout:
|
||||
# GPIO1 UART0 TX
|
||||
# GPIO3 UART0 RX
|
||||
# GPIO12 IR
|
||||
# GPIO19 NC
|
||||
# GPIO21 I2C1 SCL
|
||||
# GPIO22 1-WIRE
|
||||
# GPIO23 UART1 RX
|
||||
# GPIO25 I2C1 SDA
|
||||
# GPIO26 I2C0 SDA -> uart_modbus (uart rx_pin)
|
||||
# GPIO27 NEO -> atom_led (status LED, common/status_led_atom.yaml)
|
||||
# GPIO32 I2C0 SCL -> uart_modbus (uart tx_pin)
|
||||
# GPIO33 UART1 TX
|
||||
# GPIO39 BUTTON
|
||||
substitutions:
|
||||
device: mains-power
|
||||
name: Mains Power
|
||||
@@ -22,25 +37,14 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
# PINOUTS: M5ATOM
|
||||
# IR G12
|
||||
# NEO G27
|
||||
# BUTTON G39
|
||||
# I2C0 SDA G26
|
||||
# I2C0 SCL G32
|
||||
# I2C1 SDA G25
|
||||
# I2C1 SCL G21
|
||||
# UART0 RX G3
|
||||
# UART0 TX G1
|
||||
# UART1 RX G23
|
||||
# UART1 TX G33
|
||||
# 1-WIRE G22
|
||||
# NC G19
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include common/status_led_atom.yaml
|
||||
|
||||
modbus:
|
||||
uart_id: uart_modbus
|
||||
@@ -228,9 +232,7 @@ button:
|
||||
- pzemac.reset_energy: pzemac_l3
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
- id: !extend ha_time
|
||||
on_time:
|
||||
- cron: '0 0 0 * * *'
|
||||
then:
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
---
|
||||
# Hardware: Nordic nRF52840 DK
|
||||
# Pinout:
|
||||
# P0.06 UART0 TX -> logger (J-Link VCOM)
|
||||
# P0.13 LED 1 (active low) -> ANT+ Connected (a configured device is received)
|
||||
# P0.14 LED 2 (active low) -> ANT+ Unknown Device (an unconfigured one is in range)
|
||||
# P1.01 UART1 RX <- mcu-nrf-dk UART1 TX (P1.02), packet_transport
|
||||
# P1.02 UART1 TX -> mcu-nrf-dk UART1 RX (P1.01), packet_transport; plus GND
|
||||
# Radio: ANT+ receiver (RadiANT), no network of its own; values go to mcu-nrf-dk
|
||||
# ANT+ devices:
|
||||
# 655730 Garmin HRM-Pro heart rate
|
||||
# 498521 Concept2 PM5 (rower) fitness equipment (FE-C)
|
||||
# 1044953 Garmin Tempe environment (temperature)
|
||||
# 604693 Garmin Varia RTL515 bike radar, battery only
|
||||
# 136052 Garmin HRM-Dual heart rate (pain cave)
|
||||
# 334424 Garmin HRM-Tri heart rate (bike)
|
||||
# 886796 Garmin Rally RS200 power + cadence
|
||||
# 343922 Garmin Speed Sensor 2 speed
|
||||
# 42898 SRAM Force AXS shifting (Cube Litening C:68X), battery only
|
||||
esphome:
|
||||
name: mcu-nrf-ant
|
||||
friendly_name: nRF ANT+
|
||||
|
||||
# needs the esphomeplatformzephyr fork (esphome-zephyr); RadiANT needs NCS
|
||||
zephyr:
|
||||
variant: NRF52
|
||||
board: nrf52840dk/nrf52840
|
||||
framework:
|
||||
type: ncs
|
||||
|
||||
logger:
|
||||
hardware_uart: UART0
|
||||
|
||||
external_components:
|
||||
- source: github://randybb/esphome-components
|
||||
components: [ant_plus]
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
id: led_known
|
||||
pin: {number: P0.13, inverted: true}
|
||||
- platform: gpio
|
||||
id: led_unknown
|
||||
pin: {number: P0.14, inverted: true}
|
||||
|
||||
ant_plus:
|
||||
network_key: !secret ant_plus_network_key
|
||||
connected:
|
||||
id: ant_connected
|
||||
name: ANT+ Connected
|
||||
on_press: {output.turn_on: led_known}
|
||||
on_release: {output.turn_off: led_known}
|
||||
unknown_device:
|
||||
id: ant_unknown_device
|
||||
name: ANT+ Unknown Device
|
||||
on_press: {output.turn_on: led_unknown}
|
||||
on_release: {output.turn_off: led_unknown}
|
||||
devices:
|
||||
- type: heart_rate # Garmin HRM-Pro
|
||||
device_number: 655730
|
||||
heart_rate:
|
||||
id: hrm_pro_heart_rate
|
||||
name: Garmin HRM-Pro Heart Rate
|
||||
battery_level:
|
||||
id: hrm_pro_battery
|
||||
name: Garmin HRM-Pro Battery
|
||||
- type: fitness_equipment # Concept2 PM5 (rower)
|
||||
device_number: 498521
|
||||
power:
|
||||
id: pm5_power
|
||||
name: Concept2 PM5 Power
|
||||
cadence:
|
||||
id: pm5_stroke_rate
|
||||
name: Concept2 PM5 Stroke Rate
|
||||
in_use:
|
||||
id: pm5_in_use
|
||||
name: Concept2 PM5 In Use
|
||||
- type: temperature # Garmin Tempe
|
||||
device_number: 1044953
|
||||
temperature:
|
||||
id: tempe_temperature
|
||||
name: Garmin Tempe Temperature
|
||||
temperature_min:
|
||||
id: tempe_temperature_min
|
||||
name: Garmin Tempe Temperature 24h Min
|
||||
temperature_max:
|
||||
id: tempe_temperature_max
|
||||
name: Garmin Tempe Temperature 24h Max
|
||||
battery_voltage:
|
||||
id: tempe_battery
|
||||
name: Garmin Tempe Battery
|
||||
battery_low:
|
||||
id: tempe_battery_low
|
||||
name: Garmin Tempe Battery Low
|
||||
- type: bike_radar # Garmin Varia RTL515
|
||||
device_number: 604693
|
||||
battery_voltage:
|
||||
id: varia_battery
|
||||
name: Garmin Varia RTL515 Battery
|
||||
battery_low:
|
||||
id: varia_battery_low
|
||||
name: Garmin Varia RTL515 Battery Low
|
||||
- type: heart_rate # Garmin HRM-Dual
|
||||
device_number: 136052
|
||||
heart_rate:
|
||||
id: hrm_dual_heart_rate
|
||||
name: Garmin HRM-Dual Heart Rate
|
||||
battery_level:
|
||||
id: hrm_dual_battery
|
||||
name: Garmin HRM-Dual Battery
|
||||
- type: heart_rate # Garmin HRM-Tri
|
||||
device_number: 334424
|
||||
heart_rate:
|
||||
id: hrm_tri_heart_rate
|
||||
name: Garmin HRM-Tri Heart Rate
|
||||
battery_level:
|
||||
id: hrm_tri_battery
|
||||
name: Garmin HRM-Tri Battery
|
||||
- type: power # Garmin Rally RS200 (a battery in each pedal)
|
||||
device_number: 886796
|
||||
power:
|
||||
id: rally_power
|
||||
name: Garmin Rally RS200 Power
|
||||
cadence:
|
||||
id: rally_cadence
|
||||
name: Garmin Rally RS200 Cadence
|
||||
battery_low:
|
||||
id: rally_battery_low
|
||||
name: Garmin Rally RS200 Battery Low
|
||||
- type: speed # Garmin Speed Sensor 2
|
||||
device_number: 343922
|
||||
speed:
|
||||
id: speed2_speed
|
||||
name: Garmin Speed Sensor 2 Speed
|
||||
battery_voltage:
|
||||
id: speed2_battery
|
||||
name: Garmin Speed Sensor 2 Battery
|
||||
battery_low:
|
||||
id: speed2_battery_low
|
||||
name: Garmin Speed Sensor 2 Battery Low
|
||||
- type: shifting # SRAM Force AXS (Cube Litening C:68X)
|
||||
device_number: 42898
|
||||
battery_low:
|
||||
id: sram_battery_low
|
||||
name: SRAM Force AXS Battery Low
|
||||
|
||||
uart:
|
||||
id: link
|
||||
port: UART1
|
||||
# the DK's default UART1 pins, same on both boards, wires crossed
|
||||
tx_pin: P1.02
|
||||
rx_pin: P1.01
|
||||
baud_rate: 115200
|
||||
|
||||
packet_transport:
|
||||
- platform: uart
|
||||
uart_id: link
|
||||
update_interval: 1s
|
||||
sensors:
|
||||
- hrm_pro_heart_rate
|
||||
- hrm_pro_battery
|
||||
- pm5_power
|
||||
- pm5_stroke_rate
|
||||
- tempe_temperature
|
||||
- tempe_temperature_min
|
||||
- tempe_temperature_max
|
||||
- tempe_battery
|
||||
- varia_battery
|
||||
- hrm_dual_heart_rate
|
||||
- hrm_dual_battery
|
||||
- hrm_tri_heart_rate
|
||||
- hrm_tri_battery
|
||||
- rally_power
|
||||
- rally_cadence
|
||||
- speed2_speed
|
||||
- speed2_battery
|
||||
binary_sensors:
|
||||
- ant_connected
|
||||
- ant_unknown_device
|
||||
- pm5_in_use
|
||||
- tempe_battery_low
|
||||
- varia_battery_low
|
||||
- rally_battery_low
|
||||
- speed2_battery_low
|
||||
- sram_battery_low
|
||||
+290
-5
@@ -1,11 +1,296 @@
|
||||
---
|
||||
# Hardware: Nordic nRF52840 DK
|
||||
# Pinout:
|
||||
# P0.11 Button 1 (active low) -> Button 1 (binary_sensor gpio)
|
||||
# P0.12 Button 2 (active low) -> Button 2 (binary_sensor gpio)
|
||||
# P0.13 LED 1 (active low) -> LED 1 (light binary)
|
||||
# P0.14 LED 2 (active low) -> LED 2 (light binary)
|
||||
# P0.15 LED 3 (active low) -> LED 3 (light binary)
|
||||
# P0.16 LED 4 (active low) -> LED 4 (light binary)
|
||||
# P0.24 Button 3 (active low) -> Button 3 (binary_sensor gpio)
|
||||
# P0.25 Button 4 (active low) -> Button 4 (binary_sensor gpio)
|
||||
# P1.01 UART1 RX <- mcu-nrf-ant UART1 TX (P1.02), packet_transport; plus GND
|
||||
# P1.02 UART1 TX -> mcu-nrf-ant UART1 RX (P1.01), packet_transport
|
||||
# ANT+ values (Garmin HRM-Pro, Concept2 PM5, Garmin Tempe, Garmin Varia RTL515) come from mcu-nrf-ant
|
||||
esphome:
|
||||
name: mcu-nrf-dk
|
||||
friendly_name: mcu-nrf-dk
|
||||
friendly_name: nRF DK
|
||||
|
||||
nrf52:
|
||||
board: nrf52840_dk
|
||||
bootloader: adafruit_nrf52_sd140_v7
|
||||
# needs the esphomeplatformzephyr fork (esphome-zephyr), upstream has no zephyr: platform yet
|
||||
zephyr:
|
||||
variant: NRF52
|
||||
board: nrf52840dk/nrf52840
|
||||
framework:
|
||||
type: zephyr
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
|
||||
network:
|
||||
enable_ipv6: true
|
||||
|
||||
# Active dataset of the Thread network (HA: Settings > Thread > ... > dataset TLV)
|
||||
openthread:
|
||||
tlv: !secret thread_tlv
|
||||
# end device, doesn't route for others; saves flash (FTD fills the MCUboot slot)
|
||||
device_type: MTD
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_key
|
||||
|
||||
# ANT+ values from mcu-nrf-ant (second nRF52840 DK) over UART
|
||||
uart:
|
||||
id: ant_link
|
||||
port: UART1
|
||||
# the DK's default UART1 pins, same on both boards, wires crossed
|
||||
rx_pin: P1.01
|
||||
tx_pin: P1.02
|
||||
baud_rate: 115200
|
||||
|
||||
packet_transport:
|
||||
- platform: uart
|
||||
uart_id: ant_link
|
||||
providers:
|
||||
- name: mcu-nrf-ant
|
||||
|
||||
sensor:
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: hrm_pro_heart_rate
|
||||
name: Garmin HRM-Pro Heart Rate
|
||||
internal: false
|
||||
unit_of_measurement: bpm
|
||||
icon: mdi:heart-pulse
|
||||
filters:
|
||||
- throttle: 1s
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: hrm_pro_battery
|
||||
name: Garmin HRM-Pro Battery
|
||||
internal: false
|
||||
unit_of_measurement: "%"
|
||||
device_class: battery
|
||||
entity_category: diagnostic
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: pm5_power
|
||||
name: Concept2 PM5 Power
|
||||
internal: false
|
||||
unit_of_measurement: W
|
||||
device_class: power
|
||||
filters:
|
||||
- throttle: 1s
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: pm5_stroke_rate
|
||||
name: Concept2 PM5 Stroke Rate
|
||||
internal: false
|
||||
unit_of_measurement: spm
|
||||
icon: mdi:rowing
|
||||
filters:
|
||||
- throttle: 1s
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: tempe_temperature
|
||||
name: Garmin Tempe Temperature
|
||||
internal: false
|
||||
unit_of_measurement: °C
|
||||
device_class: temperature
|
||||
accuracy_decimals: 1
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: tempe_temperature_min
|
||||
name: Garmin Tempe Temperature 24h Min
|
||||
internal: false
|
||||
unit_of_measurement: °C
|
||||
device_class: temperature
|
||||
accuracy_decimals: 1
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: tempe_temperature_max
|
||||
name: Garmin Tempe Temperature 24h Max
|
||||
internal: false
|
||||
unit_of_measurement: °C
|
||||
device_class: temperature
|
||||
accuracy_decimals: 1
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: tempe_battery
|
||||
name: Garmin Tempe Battery
|
||||
internal: false
|
||||
unit_of_measurement: V
|
||||
device_class: voltage
|
||||
accuracy_decimals: 2
|
||||
entity_category: diagnostic
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: varia_battery
|
||||
name: Garmin Varia RTL515 Battery
|
||||
internal: false
|
||||
unit_of_measurement: V
|
||||
device_class: voltage
|
||||
accuracy_decimals: 2
|
||||
entity_category: diagnostic
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: hrm_dual_heart_rate
|
||||
name: Garmin HRM-Dual Heart Rate
|
||||
internal: false
|
||||
unit_of_measurement: bpm
|
||||
icon: mdi:heart-pulse
|
||||
filters:
|
||||
- throttle: 1s
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: hrm_dual_battery
|
||||
name: Garmin HRM-Dual Battery
|
||||
internal: false
|
||||
unit_of_measurement: "%"
|
||||
device_class: battery
|
||||
entity_category: diagnostic
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: hrm_tri_heart_rate
|
||||
name: Garmin HRM-Tri Heart Rate
|
||||
internal: false
|
||||
unit_of_measurement: bpm
|
||||
icon: mdi:heart-pulse
|
||||
filters:
|
||||
- throttle: 1s
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: hrm_tri_battery
|
||||
name: Garmin HRM-Tri Battery
|
||||
internal: false
|
||||
unit_of_measurement: "%"
|
||||
device_class: battery
|
||||
entity_category: diagnostic
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: rally_power
|
||||
name: Garmin Rally RS200 Power
|
||||
internal: false
|
||||
unit_of_measurement: W
|
||||
device_class: power
|
||||
filters:
|
||||
- throttle: 1s
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: rally_cadence
|
||||
name: Garmin Rally RS200 Cadence
|
||||
internal: false
|
||||
unit_of_measurement: rpm
|
||||
icon: mdi:rotate-right
|
||||
filters:
|
||||
- throttle: 1s
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: speed2_speed
|
||||
name: Garmin Speed Sensor 2 Speed
|
||||
internal: false
|
||||
unit_of_measurement: m/s
|
||||
device_class: speed
|
||||
accuracy_decimals: 2
|
||||
filters:
|
||||
- throttle: 1s
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: speed2_battery
|
||||
name: Garmin Speed Sensor 2 Battery
|
||||
internal: false
|
||||
unit_of_measurement: V
|
||||
device_class: voltage
|
||||
accuracy_decimals: 2
|
||||
entity_category: diagnostic
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
id: led_1_out
|
||||
pin: {number: P0.13, inverted: true}
|
||||
- platform: gpio
|
||||
id: led_2_out
|
||||
pin: {number: P0.14, inverted: true}
|
||||
- platform: gpio
|
||||
id: led_3_out
|
||||
pin: {number: P0.15, inverted: true}
|
||||
- platform: gpio
|
||||
id: led_4_out
|
||||
pin: {number: P0.16, inverted: true}
|
||||
|
||||
light:
|
||||
- {platform: binary, name: LED 1, id: led_1, output: led_1_out}
|
||||
- {platform: binary, name: LED 2, id: led_2, output: led_2_out}
|
||||
- {platform: binary, name: LED 3, id: led_3, output: led_3_out}
|
||||
- {platform: binary, name: LED 4, id: led_4, output: led_4_out}
|
||||
|
||||
# each button toggles its LED locally and is visible in HA
|
||||
binary_sensor:
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: ant_connected
|
||||
name: ANT+ Connected
|
||||
internal: false
|
||||
device_class: connectivity
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: ant_unknown_device
|
||||
name: ANT+ Unknown Device
|
||||
internal: false
|
||||
icon: mdi:access-point
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: pm5_in_use
|
||||
name: Concept2 PM5 In Use
|
||||
internal: false
|
||||
device_class: running
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: tempe_battery_low
|
||||
name: Garmin Tempe Battery Low
|
||||
internal: false
|
||||
device_class: battery
|
||||
entity_category: diagnostic
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: varia_battery_low
|
||||
name: Garmin Varia RTL515 Battery Low
|
||||
internal: false
|
||||
device_class: battery
|
||||
entity_category: diagnostic
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: rally_battery_low
|
||||
name: Garmin Rally RS200 Battery Low
|
||||
internal: false
|
||||
device_class: battery
|
||||
entity_category: diagnostic
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: speed2_battery_low
|
||||
name: Garmin Speed Sensor 2 Battery Low
|
||||
internal: false
|
||||
device_class: battery
|
||||
entity_category: diagnostic
|
||||
- platform: packet_transport
|
||||
provider: mcu-nrf-ant
|
||||
remote_id: sram_battery_low
|
||||
name: SRAM Force AXS Battery Low
|
||||
internal: false
|
||||
device_class: battery
|
||||
entity_category: diagnostic
|
||||
- platform: gpio
|
||||
name: Button 1
|
||||
pin: {number: P0.11, inverted: true, mode: {input: true, pullup: true}}
|
||||
on_press: {light.toggle: led_1}
|
||||
- platform: gpio
|
||||
name: Button 2
|
||||
pin: {number: P0.12, inverted: true, mode: {input: true, pullup: true}}
|
||||
on_press: {light.toggle: led_2}
|
||||
- platform: gpio
|
||||
name: Button 3
|
||||
pin: {number: P0.24, inverted: true, mode: {input: true, pullup: true}}
|
||||
on_press: {light.toggle: led_3}
|
||||
- platform: gpio
|
||||
name: Button 4
|
||||
pin: {number: P0.25, inverted: true, mode: {input: true, pullup: true}}
|
||||
on_press: {light.toggle: led_4}
|
||||
+57
-220
@@ -1,191 +1,77 @@
|
||||
---
|
||||
# Hardware: Sonoff NSPanel (EU)
|
||||
# Pinout:
|
||||
# GPIO2 IO2
|
||||
# GPIO4 Screen power -> Screen Power (switch gpio) (inverted)
|
||||
# GPIO14 Left button -> Left Button (binary_sensor gpio) (inverted)
|
||||
# GPIO16 Display UART TX -> display_uart (uart tx_pin)
|
||||
# GPIO17 Display UART RX -> display_uart (uart rx_pin)
|
||||
# GPIO19 Relay 2
|
||||
# GPIO21 Buzzer -> buzzer_out (output ledc)
|
||||
# GPIO22 Relay 1 -> relay_1 (output gpio)
|
||||
# GPIO23 Relay 3 (no relay, connector)
|
||||
# GPIO27 Right button -> Right Button (binary_sensor gpio) (inverted)
|
||||
# GPIO37 ALS (light sensor)
|
||||
# GPIO38 NTC temperature (ADC) -> ntc_source (sensor adc)
|
||||
substitutions:
|
||||
device: nspanel-bedroom
|
||||
name: Bedroom NSPanel
|
||||
name_short: Bedroom
|
||||
area: Bedroom
|
||||
comment: NSPanel
|
||||
panel_recv_topic: "tele/${device}/RESULT"
|
||||
panel_send_topic: "cmnd/${device}/CustomSend"
|
||||
tft_url: !secret tft_url
|
||||
|
||||
|
||||
esphome:
|
||||
name: mcu-${device}
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
on_boot:
|
||||
then:
|
||||
- switch.turn_off: screen_power
|
||||
- delay: 1s
|
||||
- switch.turn_on: screen_power
|
||||
|
||||
esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
minimum_chip_revision: 3.0
|
||||
sram1_as_iram: true
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/sairon/esphome-nspanel-lovelace-ui
|
||||
ref: dev
|
||||
components: [nspanel_lovelace]
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
api:
|
||||
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
|
||||
actions:
|
||||
- action: play_rtttl
|
||||
variables:
|
||||
song: string
|
||||
then:
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
|
||||
- action: notify
|
||||
variables:
|
||||
heading: string
|
||||
message: string
|
||||
song: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
- delay: 10s
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~~");'
|
||||
|
||||
- action: update_tft
|
||||
then:
|
||||
- lambda: |-
|
||||
id(nspanel_id).upload_tft("${tft_url}");
|
||||
|
||||
- action: upload_tft
|
||||
variables:
|
||||
url: string
|
||||
then:
|
||||
- lambda: |-
|
||||
id(nspanel_id).upload_tft(url);
|
||||
|
||||
# Service to send a command directly to the display. Useful for testing
|
||||
- action: send_command
|
||||
variables:
|
||||
cmd: string
|
||||
then:
|
||||
- lambda: "id(nspanel_id).send_custom_command(cmd.c_str());"
|
||||
|
||||
# Service to pusblish to mqtt used for refreshin the panel
|
||||
- action: publish_to_recv_topic
|
||||
variables:
|
||||
cmd: string
|
||||
then:
|
||||
- mqtt.publish:
|
||||
topic: $panel_recv_topic
|
||||
payload: !lambda "return cmd;"
|
||||
|
||||
# Service to send a command wake the screen
|
||||
- action: wake
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("wake");'
|
||||
|
||||
# Service to send a command to show screensaver (some of the values are hardcoded)
|
||||
- action: show_screensaver
|
||||
then:
|
||||
- mqtt.publish:
|
||||
topic: $panel_recv_topic
|
||||
payload: '{"CustomRecv":"event,sleepReached,cardGrid"}'
|
||||
# Service to navigate to screen
|
||||
- action: navigate_to_page
|
||||
variables:
|
||||
page: string
|
||||
then:
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate." + page + ",button";
|
||||
|
||||
# Service to send a command disable screensaver
|
||||
- action: disable_screensaver
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("timeout~0");'
|
||||
|
||||
# Service to send a command enabled screensaver
|
||||
- action: enable_screensaver
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("timeout~20");'
|
||||
|
||||
# Service to send a command wake the screen
|
||||
- action: dim_0_to_100
|
||||
variables:
|
||||
intensity: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("dimmode~"+intensity+"~100");'
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
- action: show_entity
|
||||
variables:
|
||||
entity: string
|
||||
title: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupLight~" + title + "~" + entity);'
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
- action: notify_on_screensaver
|
||||
variables:
|
||||
line1: string
|
||||
line2: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~" + line1 + "~" + line2);'
|
||||
- rtttl.play: "short:d=4,o=5,b=100:16e6"
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
# interaction of the buttons are on implemented yet
|
||||
- action: notify_fullscreen
|
||||
variables:
|
||||
title: string
|
||||
description: string
|
||||
button1: string
|
||||
button2: string
|
||||
time_secs: string
|
||||
then:
|
||||
# show notification screen
|
||||
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupNotify");'
|
||||
# set values on notification screen
|
||||
# color defined as number created in binary and converted to decimal (rrrrggggbbbbaaaa)
|
||||
- lambda: 'id(nspanel_id).send_custom_command("entityUpdateDetail~id~"+ title + "~65535~" + button1 +"~3840~" + button2 + "~61440~" + description + "~65535~" + time_secs);'
|
||||
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
||||
|
||||
uart:
|
||||
id: display_uart
|
||||
tx_pin: 16
|
||||
rx_pin: 17
|
||||
baud_rate: 115200
|
||||
|
||||
mqtt:
|
||||
id: mqtt_client
|
||||
broker: !secret mqtt_ip
|
||||
username: !secret mqtt_username
|
||||
password: !secret mqtt_password
|
||||
discovery: false
|
||||
idf_send_async: true
|
||||
reboot_timeout: 0s
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
nspanel: !include
|
||||
file: common/nspanel.yaml
|
||||
vars:
|
||||
temperature_entity: sensor.zigbee_bedroom_temperature
|
||||
sleep_dim: !include
|
||||
file: common/nspanel_sleep_dim.yaml
|
||||
vars:
|
||||
sleep_override_entity: light.bedroom_lights
|
||||
|
||||
nspanel_lovelace:
|
||||
id: nspanel_id
|
||||
mqtt_recv_topic: $panel_recv_topic
|
||||
mqtt_send_topic: $panel_send_topic
|
||||
cards:
|
||||
- type: cardGrid
|
||||
title: Spálňa
|
||||
entities:
|
||||
- name: TV
|
||||
entity_id: switch.mcu_socket_01_relay
|
||||
icon: television-classic
|
||||
- name: Raid
|
||||
entity_id: switch.mcu_socket_02_relay
|
||||
icon: emoticon-dead
|
||||
- name: ""
|
||||
entity_id: switch.mcu_socket_03_relay
|
||||
icon: power-socket-fr
|
||||
- name: Svetlo
|
||||
entity_id: light.mcu_nspanel_bedroom_light
|
||||
icon: wall-sconce-flat
|
||||
- name: Nočná lampa J
|
||||
entity_id: light.mcu_bedroom_lamp_right_light
|
||||
icon: lamp
|
||||
- name: Nočná lampa R
|
||||
entity_id: light.mcu_bedroom_lamp_left_light
|
||||
icon: lamp
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
- type: cardThermo
|
||||
title: Kúrenie
|
||||
thermo_entity_id: climate.bt_trv_bedroom
|
||||
|
||||
- type: cardEntities
|
||||
title: Info
|
||||
entities:
|
||||
- name: Zariadenie
|
||||
entity_id: button.bedroom_nspanel_info
|
||||
icon: information-outline
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
@@ -195,10 +81,7 @@ binary_sensor:
|
||||
inverted: true
|
||||
on_click:
|
||||
- light.toggle: light_1
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
|
||||
- lambda: 'id(nspanel_id).render_default_page();'
|
||||
|
||||
- platform: gpio
|
||||
name: Right Button
|
||||
@@ -212,10 +95,7 @@ binary_sensor:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.bedroom_lamps
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
|
||||
- lambda: 'id(nspanel_id).render_default_page();'
|
||||
|
||||
light:
|
||||
- platform: binary
|
||||
@@ -224,10 +104,6 @@ light:
|
||||
output: relay_1
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
id: buzzer_out
|
||||
pin:
|
||||
number: 21
|
||||
- id: relay_1
|
||||
platform: gpio
|
||||
pin:
|
||||
@@ -236,42 +112,3 @@ output:
|
||||
# platform: gpio
|
||||
# pin:
|
||||
# number: 19
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
name: Screen Power
|
||||
id: screen_power
|
||||
entity_category: config
|
||||
pin:
|
||||
number: 4
|
||||
inverted: true
|
||||
restore_mode: ALWAYS_ON
|
||||
|
||||
rtttl:
|
||||
id: buzzer
|
||||
output: buzzer_out
|
||||
|
||||
sensor:
|
||||
- platform: adc
|
||||
id: ntc_source
|
||||
pin: 38
|
||||
update_interval: 5min
|
||||
attenuation: 12db
|
||||
|
||||
- platform: resistance
|
||||
id: resistance_sensor
|
||||
sensor: ntc_source
|
||||
configuration: DOWNSTREAM
|
||||
resistor: 11.2kOhm
|
||||
|
||||
- platform: ntc
|
||||
id: temperature
|
||||
sensor: resistance_sensor
|
||||
calibration:
|
||||
b_constant: 3950
|
||||
reference_temperature: 25°C
|
||||
reference_resistance: 10kOhm
|
||||
name: Temperature
|
||||
|
||||
- platform: template
|
||||
id: room_temperature_id
|
||||
+72
-30
@@ -1,4 +1,18 @@
|
||||
---
|
||||
# Hardware: Sonoff NSPanel (EU)
|
||||
# Pinout:
|
||||
# GPIO2 IO2
|
||||
# GPIO4 Screen power -> Screen Power (switch gpio) (inverted)
|
||||
# GPIO14 Left button -> Left Button (binary_sensor gpio) (inverted)
|
||||
# GPIO16 Display UART TX -> display_uart (uart tx_pin)
|
||||
# GPIO17 Display UART RX -> display_uart (uart rx_pin)
|
||||
# GPIO19 Relay 2 -> Relay 2 (switch gpio)
|
||||
# GPIO21 Buzzer -> buzzer_out (output ledc)
|
||||
# GPIO22 Relay 1 -> Relay 1 (switch gpio)
|
||||
# GPIO23 Relay 3 (no relay, connector)
|
||||
# GPIO27 Right button -> Right Button (binary_sensor gpio) (inverted)
|
||||
# GPIO37 ALS (light sensor)
|
||||
# GPIO38 NTC temperature (ADC) -> ntc_source (sensor adc)
|
||||
substitutions:
|
||||
device: nspanel-dev
|
||||
name: NSPanel DEV
|
||||
@@ -37,10 +51,9 @@ esp32:
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
minimum_chip_revision: 3.0
|
||||
sram1_as_iram: true
|
||||
include_builtin_idf_components:
|
||||
- esp_http_client
|
||||
|
||||
external_components:
|
||||
# - source:
|
||||
@@ -51,14 +64,18 @@ external_components:
|
||||
# components: [nspanel_lovelace]
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/olicooper/esphome-nspanel-lovelace-native
|
||||
ref: dev
|
||||
# url: https://github.com/olicooper/esphome-nspanel-lovelace-native
|
||||
# ref: dev
|
||||
url: https://github.com/randybb/esphome-nspanel-lovelace-native
|
||||
ref: devices
|
||||
refresh: 0s
|
||||
components: [nspanel_lovelace]
|
||||
# - source: components
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
nspanel_popup: !include common/nspanel_popup.yaml
|
||||
# common: !include common/common_dev.yaml
|
||||
|
||||
# web_server:
|
||||
@@ -84,7 +101,6 @@ api:
|
||||
song: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
|
||||
# - lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
# - delay: 10s
|
||||
@@ -203,15 +219,37 @@ uart:
|
||||
nspanel_lovelace:
|
||||
id: nspanel_id
|
||||
sleep_timeout: 10
|
||||
experimental_vertical_ui: true
|
||||
locale:
|
||||
language: nspanel/sk.json
|
||||
language: sk
|
||||
temperature_unit: celcius
|
||||
screensaver:
|
||||
time_id: ha_time
|
||||
time_format: '%H:%M~$name_day'
|
||||
time_format: '%H:%M'
|
||||
date_format: '%A, %d. %b. %Y'
|
||||
weather:
|
||||
entity_id: sensor.weather_forecast_daily
|
||||
# entity_id: weather.openweathermap
|
||||
# forecast_method: service
|
||||
icon_color: [255, 255, 255]
|
||||
entities:
|
||||
- entity_id: sensor.zigbee_office_r_temperature
|
||||
icon:
|
||||
value: home-thermometer-outline
|
||||
color: [255, 255, 255]
|
||||
precision: 1
|
||||
# type: screensaver2
|
||||
## Custom entities, filled into the layout slots in order after the weather items.
|
||||
## Use 'entity_id: delete' to leave a slot empty.
|
||||
## screensaver: weather main + 4 forecast items, 1 extra entity switches to the alternative layout (max 6 items)
|
||||
## screensaver2: weather main only, then 3 left + 6 bottom + 5 indicator icons (max 15 items)
|
||||
# entities:
|
||||
# - entity_id: sensor.outside_humidity
|
||||
# name: Humidity
|
||||
# icon: water-percent
|
||||
# - entity_id: delete
|
||||
# - entity_id: binary_sensor.front_door
|
||||
|
||||
# type: screensaver2
|
||||
# statusIcon1:
|
||||
# entity: switch.office_r_light
|
||||
@@ -281,7 +319,7 @@ nspanel_lovelace:
|
||||
entity_id: light.living_room
|
||||
icon: ceiling-light
|
||||
- name: TV
|
||||
entity_id: light.tv_light
|
||||
entity_id: light.mcu_tv_light_light
|
||||
icon: led-strip-variant
|
||||
- name: (Stôl)
|
||||
entity_id: light.dining_table_ambilight
|
||||
@@ -293,7 +331,7 @@ nspanel_lovelace:
|
||||
title: Romanko
|
||||
entities:
|
||||
- name: Svetlo
|
||||
entity_id: switch.office_r_light
|
||||
entity_id: light.mcu_nspanel_office_r_light
|
||||
icon: wall-sconce-flat
|
||||
- name: LED pásik
|
||||
entity_id: light.office_r_desk_strip
|
||||
@@ -305,40 +343,38 @@ nspanel_lovelace:
|
||||
- name: (Monitor)
|
||||
entity_id: light.office_r_display_lamp_ambilight
|
||||
icon: monitor-star
|
||||
- name: Whitebox
|
||||
entity_id: switch.whitebox
|
||||
- type: cardGrid
|
||||
title: Von
|
||||
entities:
|
||||
- name: Terasa
|
||||
entity_id: light.patio_light
|
||||
entity_id: light.patio_lights
|
||||
icon: lightbulb-group
|
||||
- name: Terasa
|
||||
entity_id: switch.patio_relay
|
||||
entity_id: switch.mcu_patio_relay
|
||||
icon: power-socket-fr
|
||||
- name: Vzadu
|
||||
entity_id: switch.outside_light_back
|
||||
entity_id: switch.mcu_nspanel_patio_outside_light_back
|
||||
icon: light-flood-down
|
||||
- name: Z. domček
|
||||
entity_id: switch.shed_light
|
||||
entity_id: switch.mcu_shed_light
|
||||
icon: wall-sconce-flat
|
||||
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
|
||||
- name: Z. domček
|
||||
entity_id: switch.shed_socket
|
||||
entity_id: switch.mcu_shed_socket
|
||||
icon: power-socket-fr
|
||||
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
|
||||
- name: Záhrada
|
||||
entity_id: switch.garden_light
|
||||
entity_id: switch.mcu_shed_garden_light
|
||||
icon: light-flood-down
|
||||
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
|
||||
- type: cardGrid
|
||||
title: Brána
|
||||
entities:
|
||||
- name: Otvoriť
|
||||
entity_id: button.gate_open
|
||||
entity_id: button.mcu_gate_open
|
||||
icon: gate-open
|
||||
- name: Zatvoriť
|
||||
entity_id: button.gate_close
|
||||
entity_id: button.mcu_gate_close
|
||||
icon: gate
|
||||
- name: Automatizácia
|
||||
entity_id: navigate.card_gate_automation
|
||||
@@ -346,19 +382,19 @@ nspanel_lovelace:
|
||||
title: Závlaha
|
||||
entities:
|
||||
- name: A1
|
||||
entity_id: switch.irrigation_valve_a1
|
||||
entity_id: switch.mcu_well_irrigation_valve_a1
|
||||
icon: valve
|
||||
- name: A2
|
||||
entity_id: switch.irrigation_valve_a2
|
||||
entity_id: switch.mcu_well_irrigation_valve_a2
|
||||
icon: valve
|
||||
- name: A3
|
||||
entity_id: switch.irrigation_valve_a3
|
||||
entity_id: switch.mcu_well_irrigation_valve_a3
|
||||
icon: valve
|
||||
- name: A4
|
||||
entity_id: switch.irrigation_valve_a4
|
||||
entity_id: switch.mcu_well_irrigation_valve_a4
|
||||
icon: valve
|
||||
- name: A5
|
||||
entity_id: switch.irrigation_valve_a5
|
||||
entity_id: switch.mcu_well_irrigation_valve_a5
|
||||
icon: valve
|
||||
- name: Automatizácia
|
||||
entity_id: navigate.card_irrigation_automation
|
||||
@@ -381,13 +417,13 @@ nspanel_lovelace:
|
||||
title: Zwift
|
||||
entities:
|
||||
- name: Svetlo
|
||||
entity_id: light.basement_light
|
||||
entity_id: light.mcu_basement_light
|
||||
icon: lightbulb-multiple
|
||||
- name: LED
|
||||
entity_id: light.exercise_room
|
||||
icon: led-strip-variant
|
||||
- name: Ventilátor
|
||||
entity_id: light.exercise_room_fan
|
||||
entity_id: light.mcu_dimmer_exercise_room_fan
|
||||
icon: fan
|
||||
- name: Automatizácia
|
||||
entity_id: navigate.card_exercise_automation
|
||||
@@ -421,10 +457,12 @@ nspanel_lovelace:
|
||||
- name: Rast trávy
|
||||
entity_id: automation.irrigation_grow_grass
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
- type: cardEntities
|
||||
title: Info
|
||||
entities:
|
||||
- name: Zariadenie
|
||||
entity_id: button.nspanel_dev_info
|
||||
icon: information-outline
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
@@ -530,6 +568,10 @@ text_sensor:
|
||||
- platform: homeassistant
|
||||
id: name_day
|
||||
entity_id: sensor.nameday_sk
|
||||
on_value:
|
||||
- lambda: |-
|
||||
id(nspanel_id).set_time_format("%H:%M~" + x);
|
||||
id(nspanel_id).update_time();
|
||||
|
||||
# esp32_ble_tracker:
|
||||
# scan_parameters:
|
||||
|
||||
+119
-227
@@ -1,189 +1,134 @@
|
||||
---
|
||||
# Hardware: Sonoff NSPanel (EU)
|
||||
# Pinout:
|
||||
# GPIO2 IO2
|
||||
# GPIO4 Screen power -> Screen Power (switch gpio) (inverted)
|
||||
# GPIO14 Left button -> Left Button (binary_sensor gpio) (inverted)
|
||||
# GPIO16 Display UART TX -> display_uart (uart tx_pin)
|
||||
# GPIO17 Display UART RX -> display_uart (uart rx_pin)
|
||||
# GPIO19 Relay 2 -> Dining Table Light MAIN (switch gpio)
|
||||
# GPIO21 Buzzer -> buzzer_out (output ledc)
|
||||
# GPIO22 Relay 1 -> Kitchen Worktop Light (switch gpio)
|
||||
# GPIO23 Relay 3 (no relay, connector)
|
||||
# GPIO27 Right button -> Right Button (binary_sensor gpio) (inverted)
|
||||
# GPIO37 ALS (light sensor)
|
||||
# GPIO38 NTC temperature (ADC) -> ntc_source (sensor adc)
|
||||
substitutions:
|
||||
device: nspanel-fireplace
|
||||
name: Fireplace NSPanel
|
||||
area: Living Room & Kitchen
|
||||
comment: NSPanel
|
||||
panel_recv_topic: "tele/${device}/RESULT"
|
||||
panel_send_topic: "cmnd/${device}/CustomSend"
|
||||
tft_url: !secret tft_url
|
||||
|
||||
esphome:
|
||||
name: mcu-${device}
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
on_boot:
|
||||
then:
|
||||
- switch.turn_off: screen_power
|
||||
- delay: 1s
|
||||
- switch.turn_on: screen_power
|
||||
|
||||
esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
minimum_chip_revision: 3.0
|
||||
sram1_as_iram: true
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/sairon/esphome-nspanel-lovelace-ui
|
||||
ref: dev
|
||||
components: [nspanel_lovelace]
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
api:
|
||||
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
|
||||
actions:
|
||||
- action: play_rtttl
|
||||
variables:
|
||||
song: string
|
||||
then:
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
|
||||
- action: notify
|
||||
variables:
|
||||
heading: string
|
||||
message: string
|
||||
song: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
- delay: 10s
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~~");'
|
||||
|
||||
- action: update_tft
|
||||
then:
|
||||
- lambda: |-
|
||||
id(nspanel_id).upload_tft("${tft_url}");
|
||||
|
||||
- action: upload_tft
|
||||
variables:
|
||||
url: string
|
||||
then:
|
||||
- lambda: |-
|
||||
id(nspanel_id).upload_tft(url);
|
||||
|
||||
# Service to send a command directly to the display. Useful for testing
|
||||
- action: send_command
|
||||
variables:
|
||||
cmd: string
|
||||
then:
|
||||
- lambda: "id(nspanel_id).send_custom_command(cmd.c_str());"
|
||||
|
||||
# Service to pusblish to mqtt used for refreshin the panel
|
||||
- action: publish_to_recv_topic
|
||||
variables:
|
||||
cmd: string
|
||||
then:
|
||||
- mqtt.publish:
|
||||
topic: $panel_recv_topic
|
||||
payload: !lambda "return cmd;"
|
||||
|
||||
# Service to send a command wake the screen
|
||||
- action: wake
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("wake");'
|
||||
|
||||
# Service to send a command to show screensaver (some of the values are hardcoded)
|
||||
- action: show_screensaver
|
||||
then:
|
||||
- mqtt.publish:
|
||||
topic: $panel_recv_topic
|
||||
payload: '{"CustomRecv":"event,sleepReached,cardGrid"}'
|
||||
# Service to navigate to screen
|
||||
- action: navigate_to_page
|
||||
variables:
|
||||
page: string
|
||||
then:
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate." + page + ",button";
|
||||
|
||||
# Service to send a command disable screensaver
|
||||
- action: disable_screensaver
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("timeout~0");'
|
||||
|
||||
# Service to send a command enabled screensaver
|
||||
- action: enable_screensaver
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("timeout~20");'
|
||||
|
||||
# Service to send a command wake the screen
|
||||
- action: dim_0_to_100
|
||||
variables:
|
||||
intensity: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("dimmode~"+intensity+"~100");'
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
- action: show_entity
|
||||
variables:
|
||||
entity: string
|
||||
title: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupLight~" + title + "~" + entity);'
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
- action: notify_on_screensaver
|
||||
variables:
|
||||
line1: string
|
||||
line2: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~" + line1 + "~" + line2);'
|
||||
- rtttl.play: "short:d=4,o=5,b=100:16e6"
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
# interaction of the buttons are on implemented yet
|
||||
- action: notify_fullscreen
|
||||
variables:
|
||||
title: string
|
||||
description: string
|
||||
button1: string
|
||||
button2: string
|
||||
time_secs: string
|
||||
then:
|
||||
# show notification screen
|
||||
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupNotify");'
|
||||
# set values on notification screen
|
||||
# color defined as number created in binary and converted to decimal (rrrrggggbbbbaaaa)
|
||||
- lambda: 'id(nspanel_id).send_custom_command("entityUpdateDetail~id~"+ title + "~65535~" + button1 +"~3840~" + button2 + "~61440~" + description + "~65535~" + time_secs);'
|
||||
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
||||
|
||||
uart:
|
||||
id: display_uart
|
||||
tx_pin: 16
|
||||
rx_pin: 17
|
||||
baud_rate: 115200
|
||||
|
||||
mqtt:
|
||||
id: mqtt_client
|
||||
broker: !secret mqtt_ip
|
||||
username: !secret mqtt_username
|
||||
password: !secret mqtt_password
|
||||
discovery: false
|
||||
idf_send_async: true
|
||||
reboot_timeout: 0s
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
nspanel: !include
|
||||
file: common/nspanel.yaml
|
||||
vars:
|
||||
temperature_entity: sensor.zigbee_living_room_kitchen_temperature
|
||||
temperature_name: Living Room Temperature
|
||||
|
||||
nspanel_lovelace:
|
||||
id: nspanel_id
|
||||
mqtt_recv_topic: $panel_recv_topic
|
||||
mqtt_send_topic: $panel_send_topic
|
||||
cards:
|
||||
- type: cardGrid
|
||||
title: Kuchyňa / Obývačka
|
||||
entities:
|
||||
- name: Kuchyňa / Obývačka
|
||||
entity_id: light.kitchen
|
||||
icon: ceiling-light
|
||||
- name: Stôl
|
||||
entity_id: light.dining_table
|
||||
icon: ceiling-light
|
||||
- name: Gauč
|
||||
entity_id: light.living_room
|
||||
icon: ceiling-light
|
||||
- name: TV
|
||||
entity_id: light.mcu_tv_light_strip
|
||||
icon: led-strip-variant
|
||||
- name: (Stôl)
|
||||
entity_id: light.dining_table_ambilight
|
||||
icon: ceiling-light-multiple-outline
|
||||
- name: (Gauč)
|
||||
entity_id: light.living_room_ambilight
|
||||
icon: ceiling-light-multiple-outline
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
- type: cardGrid
|
||||
title: Kuchyňa
|
||||
entities:
|
||||
- name: Linka
|
||||
entity_id: light.worktop_lights
|
||||
icon: countertop
|
||||
- name: Sporák
|
||||
entity_id: light.mcu_kitchen_stove
|
||||
icon: stove
|
||||
|
||||
- type: cardGrid
|
||||
title: Von
|
||||
entities:
|
||||
- name: Terasa
|
||||
entity_id: light.patio_lights
|
||||
icon: lightbulb-group
|
||||
- name: Terasa
|
||||
entity_id: switch.mcu_patio_relay
|
||||
icon: power-socket-fr
|
||||
- name: Vzadu
|
||||
entity_id: switch.mcu_nspanel_patio_outside_light_back
|
||||
icon: light-flood-down
|
||||
- name: Z. domček
|
||||
entity_id: switch.mcu_shed_light
|
||||
icon: wall-sconce-flat
|
||||
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
|
||||
- name: Z. domček
|
||||
entity_id: switch.mcu_shed_socket
|
||||
icon: power-socket-fr
|
||||
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
|
||||
- name: Záhrada
|
||||
entity_id: switch.mcu_shed_garden_light
|
||||
icon: light-flood-down
|
||||
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
|
||||
|
||||
- type: cardGrid
|
||||
title: Brána
|
||||
entities:
|
||||
- name: Otvoriť
|
||||
entity_id: button.mcu_gate_open
|
||||
icon: gate-open
|
||||
- name: Zatvoriť
|
||||
entity_id: button.mcu_gate_close
|
||||
icon: gate
|
||||
- name: Automatizácia
|
||||
entity_id: navigate.card_gate_automation
|
||||
|
||||
- type: cardThermo
|
||||
title: Kúrenie
|
||||
thermo_entity_id: climate.bt_trv_living_room_kitchen
|
||||
|
||||
- type: cardThermo
|
||||
title: Kúrenie - Izba
|
||||
thermo_entity_id: climate.bt_trv_room
|
||||
|
||||
- type: cardEntities
|
||||
title: Automatizácia
|
||||
id: card_gate_automation
|
||||
hidden: true
|
||||
entities:
|
||||
- name: Auto-otváranie
|
||||
entity_id: switch.gate_auto_open
|
||||
- name: Auto-zatváranie
|
||||
entity_id: switch.gate_auto_close
|
||||
|
||||
- type: cardEntities
|
||||
title: Info
|
||||
entities:
|
||||
- name: Zariadenie
|
||||
entity_id: button.fireplace_nspanel_info
|
||||
icon: information-outline
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
@@ -200,10 +145,7 @@ binary_sensor:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.living_room
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
|
||||
- lambda: 'id(nspanel_id).render_default_page();'
|
||||
- min_length: 500ms
|
||||
max_length: 2000ms
|
||||
then:
|
||||
@@ -211,10 +153,8 @@ binary_sensor:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.worktop_lights
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_kitchen,button";
|
||||
- lambda: 'id(nspanel_id).render_default_page();'
|
||||
- lambda: 'id(nspanel_id).render_next_page();'
|
||||
|
||||
- platform: gpio
|
||||
name: Right Button
|
||||
@@ -230,10 +170,7 @@ binary_sensor:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.dining_table
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
|
||||
- lambda: 'id(nspanel_id).render_default_page();'
|
||||
- min_length: 500ms
|
||||
max_length: 2000ms
|
||||
then:
|
||||
@@ -241,15 +178,8 @@ binary_sensor:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.mcu_kitchen_stove
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_kitchen,button";
|
||||
output:
|
||||
- platform: ledc
|
||||
id: buzzer_out
|
||||
pin:
|
||||
number: 21
|
||||
- lambda: 'id(nspanel_id).render_default_page();'
|
||||
- lambda: 'id(nspanel_id).render_next_page();'
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
@@ -264,41 +194,3 @@ switch:
|
||||
entity_category: config
|
||||
pin:
|
||||
number: 19
|
||||
|
||||
- platform: gpio
|
||||
name: Screen Power
|
||||
id: screen_power
|
||||
entity_category: config
|
||||
pin:
|
||||
number: 4
|
||||
inverted: true
|
||||
restore_mode: ALWAYS_ON
|
||||
|
||||
rtttl:
|
||||
id: buzzer
|
||||
output: buzzer_out
|
||||
|
||||
sensor:
|
||||
- platform: adc
|
||||
id: ntc_source
|
||||
pin: 38
|
||||
update_interval: 5min
|
||||
attenuation: 12db
|
||||
|
||||
- platform: resistance
|
||||
id: resistance_sensor
|
||||
sensor: ntc_source
|
||||
configuration: DOWNSTREAM
|
||||
resistor: 11.2kOhm
|
||||
|
||||
- platform: ntc
|
||||
id: temperature
|
||||
sensor: resistance_sensor
|
||||
calibration:
|
||||
b_constant: 3950
|
||||
reference_temperature: 25°C
|
||||
reference_resistance: 10kOhm
|
||||
name: "Living Room Temperature"
|
||||
|
||||
- platform: template
|
||||
id: room_temperature_id
|
||||
+68
-252
@@ -1,217 +1,89 @@
|
||||
---
|
||||
# Hardware: Sonoff NSPanel (EU)
|
||||
# Pinout:
|
||||
# GPIO2 IO2
|
||||
# GPIO4 Screen power -> Screen Power (switch gpio) (inverted)
|
||||
# GPIO14 Left button -> Left Button (binary_sensor gpio) (inverted)
|
||||
# GPIO16 Display UART TX -> display_uart (uart tx_pin)
|
||||
# GPIO17 Display UART RX -> display_uart (uart rx_pin)
|
||||
# GPIO19 Relay 2
|
||||
# GPIO21 Buzzer -> buzzer_out (output ledc)
|
||||
# GPIO22 Relay 1 -> relay_1 (output gpio)
|
||||
# GPIO23 Relay 3 (no relay, connector)
|
||||
# GPIO27 Right button -> Right Button (binary_sensor gpio) (inverted)
|
||||
# GPIO37 ALS (light sensor)
|
||||
# GPIO38 NTC temperature (ADC) -> ntc_source (sensor adc)
|
||||
substitutions:
|
||||
device: nspanel-office-j
|
||||
name: Office J NSPanel
|
||||
area: Office J
|
||||
comment: NSPanel
|
||||
panel_recv_topic: "tele/${device}/RESULT"
|
||||
panel_send_topic: "cmnd/${device}/CustomSend"
|
||||
tft_url: !secret tft_url
|
||||
|
||||
|
||||
esphome:
|
||||
name: mcu-${device}
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
on_boot:
|
||||
then:
|
||||
- switch.turn_off: screen_power
|
||||
- delay: 1s
|
||||
- switch.turn_on: screen_power
|
||||
|
||||
esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
minimum_chip_revision: 3.0
|
||||
sram1_as_iram: true
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/sairon/esphome-nspanel-lovelace-ui
|
||||
ref: dev
|
||||
components: [nspanel_lovelace]
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
api:
|
||||
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
|
||||
#
|
||||
# stupnovane:d=4,o=6,b=180:c,e,g
|
||||
# help:d=4,o=6,b=100:f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f
|
||||
# gate_opening:d=16,o=5,b=160:a, 8p, a
|
||||
# fireplace_attention:d=16,o=5,b=160:a, 8p, g, 16p, c
|
||||
actions:
|
||||
- action: play_rtttl
|
||||
variables:
|
||||
song: string
|
||||
then:
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
|
||||
- action: notify
|
||||
variables:
|
||||
heading: string
|
||||
message: string
|
||||
song: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
- delay: 10s
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~~");'
|
||||
|
||||
- action: update_tft
|
||||
then:
|
||||
- lambda: |-
|
||||
id(nspanel_id).upload_tft("${tft_url}");
|
||||
|
||||
- action: upload_tft
|
||||
variables:
|
||||
url: string
|
||||
then:
|
||||
- lambda: |-
|
||||
id(nspanel_id).upload_tft(url);
|
||||
|
||||
# Service to send a command directly to the display. Useful for testing
|
||||
- action: send_command
|
||||
variables:
|
||||
cmd: string
|
||||
then:
|
||||
- lambda: "id(nspanel_id).send_custom_command(cmd.c_str());"
|
||||
|
||||
# Service to pusblish to mqtt used for refreshin the panel
|
||||
- action: publish_to_recv_topic
|
||||
variables:
|
||||
cmd: string
|
||||
then:
|
||||
- mqtt.publish:
|
||||
topic: $panel_recv_topic
|
||||
payload: !lambda "return cmd;"
|
||||
|
||||
# Service to send a command wake the screen
|
||||
- action: wake
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("wake");'
|
||||
|
||||
# Service to send a command to show screensaver (some of the values are hardcoded)
|
||||
- action: show_screensaver
|
||||
then:
|
||||
- mqtt.publish:
|
||||
topic: $panel_recv_topic
|
||||
payload: '{"CustomRecv":"event,sleepReached,cardGrid"}'
|
||||
# Service to navigate to screen
|
||||
- action: navigate_to_page
|
||||
variables:
|
||||
page: string
|
||||
then:
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate." + page + ",button";
|
||||
|
||||
# Service to send a command disable screensaver
|
||||
- action: disable_screensaver
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("timeout~0");'
|
||||
|
||||
# Service to send a command enabled screensaver
|
||||
- action: enable_screensaver
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("timeout~20");'
|
||||
|
||||
# Service to send a command wake the screen
|
||||
- action: dim_0_to_100
|
||||
variables:
|
||||
intensity: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("dimmode~"+intensity+"~100");'
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
- action: show_entity
|
||||
variables:
|
||||
entity: string
|
||||
title: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupLight~" + title + "~" + entity);'
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
- action: notify_on_screensaver
|
||||
variables:
|
||||
line1: string
|
||||
line2: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~" + line1 + "~" + line2);'
|
||||
- rtttl.play: "short:d=4,o=5,b=100:16e6"
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
# interaction of the buttons are on implemented yet
|
||||
- action: notify_fullscreen
|
||||
variables:
|
||||
title: string
|
||||
description: string
|
||||
button1: string
|
||||
button2: string
|
||||
time_secs: string
|
||||
then:
|
||||
# show notification screen
|
||||
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupNotify");'
|
||||
# set values on notification screen
|
||||
# color defined as number created in binary and converted to decimal (rrrrggggbbbbaaaa)
|
||||
- lambda: 'id(nspanel_id).send_custom_command("entityUpdateDetail~id~"+ title + "~65535~" + button1 +"~3840~" + button2 + "~61440~" + description + "~65535~" + time_secs);'
|
||||
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
||||
|
||||
uart:
|
||||
id: display_uart
|
||||
tx_pin: 16
|
||||
rx_pin: 17
|
||||
baud_rate: 115200
|
||||
|
||||
mqtt:
|
||||
id: mqtt_client
|
||||
broker: !secret mqtt_ip
|
||||
username: !secret mqtt_username
|
||||
password: !secret mqtt_password
|
||||
discovery: false
|
||||
idf_send_async: true
|
||||
reboot_timeout: 0s
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
nspanel: !include
|
||||
file: common/nspanel.yaml
|
||||
vars:
|
||||
temperature_entity: sensor.zigbee_office_j_temperature
|
||||
ntc_reference_temperature: 22°C
|
||||
sleep_dim: !include
|
||||
file: common/nspanel_sleep_dim.yaml
|
||||
vars:
|
||||
sleep_override_entity: light.office_j_lights
|
||||
|
||||
nspanel_lovelace:
|
||||
id: nspanel_id
|
||||
mqtt_recv_topic: $panel_recv_topic
|
||||
mqtt_send_topic: $panel_send_topic
|
||||
cards:
|
||||
- type: cardGrid
|
||||
title: Janko
|
||||
entities:
|
||||
- name: Svetlo
|
||||
entity_id: light.mcu_nspanel_office_j_light
|
||||
icon: wall-sconce-flat
|
||||
- name: Nočná lampa
|
||||
entity_id: light.bedside_left
|
||||
icon: lamp
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
- type: cardGrid
|
||||
title: Brána
|
||||
entities:
|
||||
- name: Otvoriť
|
||||
entity_id: button.mcu_gate_open
|
||||
icon: gate-open
|
||||
- name: Zatvoriť
|
||||
entity_id: button.mcu_gate_close
|
||||
icon: gate
|
||||
- name: Automatizácia
|
||||
entity_id: navigate.card_gate_automation
|
||||
|
||||
- type: cardThermo
|
||||
title: Kúrenie
|
||||
thermo_entity_id: climate.bt_trv_office_j
|
||||
|
||||
- type: cardEntities
|
||||
title: Automatizácia
|
||||
id: card_gate_automation
|
||||
hidden: true
|
||||
entities:
|
||||
- name: Auto-otváranie
|
||||
entity_id: switch.gate_auto_open
|
||||
- name: Auto-zatváranie
|
||||
entity_id: switch.gate_auto_close
|
||||
|
||||
- type: cardEntities
|
||||
title: Info
|
||||
entities:
|
||||
- name: Zariadenie
|
||||
entity_id: button.office_j_nspanel_info
|
||||
icon: information-outline
|
||||
|
||||
binary_sensor:
|
||||
# - platform: gpio
|
||||
# name: Left Button
|
||||
# pin:
|
||||
# number: 14
|
||||
# inverted: true
|
||||
# on_click:
|
||||
# - switch.toggle: relay_1
|
||||
|
||||
# - platform: gpio
|
||||
# name: Right Button
|
||||
# pin:
|
||||
# number: 27
|
||||
# inverted: true
|
||||
# # on_click:
|
||||
# # - switch.toggle: relay_2
|
||||
# on_click:
|
||||
# - homeassistant.service:
|
||||
# service: light.toggle
|
||||
# data:
|
||||
# entity_id: light.office_r_desk_strip
|
||||
- platform: gpio
|
||||
name: Left Button
|
||||
pin:
|
||||
@@ -219,10 +91,7 @@ binary_sensor:
|
||||
inverted: true
|
||||
on_click:
|
||||
- light.toggle: light_1
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
|
||||
- lambda: 'id(nspanel_id).render_default_page();'
|
||||
|
||||
- platform: gpio
|
||||
name: Right Button
|
||||
@@ -234,16 +103,9 @@ binary_sensor:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.bedside_left
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
|
||||
- lambda: 'id(nspanel_id).render_default_page();'
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
id: buzzer_out
|
||||
pin:
|
||||
number: 21
|
||||
- id: relay_1
|
||||
platform: gpio
|
||||
pin:
|
||||
@@ -262,49 +124,3 @@ light:
|
||||
# # - platform: binary
|
||||
# # name: Relay 2
|
||||
# # output: relay_2
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
name: Screen Power
|
||||
id: screen_power
|
||||
entity_category: config
|
||||
pin:
|
||||
number: 4
|
||||
inverted: true
|
||||
restore_mode: ALWAYS_ON
|
||||
|
||||
rtttl:
|
||||
id: buzzer
|
||||
output: buzzer_out
|
||||
|
||||
sensor:
|
||||
- platform: adc
|
||||
id: ntc_source
|
||||
pin: 38
|
||||
update_interval: 5min
|
||||
attenuation: 12db
|
||||
- platform: resistance
|
||||
id: resistance_sensor
|
||||
sensor: ntc_source
|
||||
configuration: DOWNSTREAM
|
||||
resistor: 11.2kOhm
|
||||
- platform: ntc
|
||||
id: temperature
|
||||
sensor: resistance_sensor
|
||||
calibration:
|
||||
b_constant: 3950
|
||||
reference_temperature: 22°C
|
||||
reference_resistance: 10kOhm
|
||||
name: Temperature
|
||||
- platform: template
|
||||
id: room_temperature_id
|
||||
|
||||
# esp32_ble_tracker:
|
||||
|
||||
# text_sensor:
|
||||
# - platform: ble_scanner
|
||||
# name: BLE Scanner
|
||||
|
||||
# esp32_ble_beacon:
|
||||
# type: iBeacon
|
||||
# uuid: '6d68252f-2e6f-b5b9-965f-43c0a3713880'
|
||||
+71
-263
@@ -1,44 +1,38 @@
|
||||
---
|
||||
# Hardware: Sonoff NSPanel (EU)
|
||||
# Pinout:
|
||||
# GPIO2 IO2
|
||||
# GPIO4 Screen power -> Screen Power (switch gpio) (inverted)
|
||||
# GPIO14 Left button -> Left Button (binary_sensor gpio) (inverted)
|
||||
# GPIO16 Display UART TX -> display_uart (uart tx_pin)
|
||||
# GPIO17 Display UART RX -> display_uart (uart rx_pin)
|
||||
# GPIO19 Relay 2 -> relay_2 (switch gpio)
|
||||
# GPIO21 Buzzer -> buzzer_out (output ledc)
|
||||
# GPIO22 Relay 1 -> relay_1 (output gpio)
|
||||
# GPIO23 Relay 3 (no relay, connector) -> Presence Light (light esp32_rmt_led_strip)
|
||||
# GPIO27 Right button -> Right Button (binary_sensor gpio) (inverted)
|
||||
# GPIO37 ALS (light sensor)
|
||||
# GPIO38 NTC temperature (ADC) -> ntc_source (sensor adc)
|
||||
substitutions:
|
||||
device: nspanel-office-r
|
||||
name: Office R NSPanel
|
||||
area: Office R
|
||||
comment: NSPanel
|
||||
panel_recv_topic: "tele/${device}/RESULT"
|
||||
panel_send_topic: "cmnd/${device}/CustomSend"
|
||||
tft_url: !secret tft_url
|
||||
|
||||
|
||||
esphome:
|
||||
name: mcu-${device}
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
on_boot:
|
||||
then:
|
||||
- switch.turn_off: screen_power
|
||||
- delay: 1s
|
||||
- switch.turn_on: screen_power
|
||||
|
||||
esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
enable_lwip_mdns_queries: true
|
||||
minimum_chip_revision: 3.0
|
||||
sram1_as_iram: true
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/sairon/esphome-nspanel-lovelace-ui
|
||||
ref: dev
|
||||
components: [nspanel_lovelace]
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
nspanel: !include
|
||||
file: common/nspanel.yaml
|
||||
vars:
|
||||
temperature_entity: sensor.zigbee_office_r_temperature
|
||||
ntc_reference_temperature: 22°C
|
||||
|
||||
script:
|
||||
- id: geiger_click
|
||||
@@ -57,180 +51,59 @@ script:
|
||||
return esphome::random_uint32() % 450 + 50;
|
||||
- script.execute: geiger_click
|
||||
|
||||
api:
|
||||
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
|
||||
#
|
||||
# stupnovane:d=4,o=6,b=180:c,e,g
|
||||
# help:d=4,o=6,b=100:f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f
|
||||
# gate_opening:d=16,o=5,b=160:a, 8p, a
|
||||
# fireplace_attention:d=16,o=5,b=160:a, 8p, g, 16p, c
|
||||
actions:
|
||||
- action: play_rtttl
|
||||
variables:
|
||||
song: string
|
||||
then:
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
|
||||
- action: notify
|
||||
variables:
|
||||
heading: string
|
||||
message: string
|
||||
song: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
- delay: 10s
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~~");'
|
||||
|
||||
- action: update_tft
|
||||
then:
|
||||
- logger.log: "tft updating from: ${tft_url}"
|
||||
- lambda: |-
|
||||
id(nspanel_id).upload_tft("${tft_url}");
|
||||
|
||||
- action: upload_tft
|
||||
variables:
|
||||
url: string
|
||||
then:
|
||||
- lambda: |-
|
||||
id(nspanel_id).upload_tft(url);
|
||||
|
||||
# Service to send a command directly to the display. Useful for testing
|
||||
- action: send_command
|
||||
variables:
|
||||
cmd: string
|
||||
then:
|
||||
- lambda: "id(nspanel_id).send_custom_command(cmd.c_str());"
|
||||
|
||||
# Service to pusblish to mqtt used for refreshin the panel
|
||||
- action: publish_to_recv_topic
|
||||
variables:
|
||||
cmd: string
|
||||
then:
|
||||
- mqtt.publish:
|
||||
topic: $panel_recv_topic
|
||||
payload: !lambda "return cmd;"
|
||||
|
||||
# Service to send a command wake the screen
|
||||
- action: wake
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("wake");'
|
||||
|
||||
# Service to send a command to show screensaver (some of the values are hardcoded)
|
||||
- action: show_screensaver
|
||||
then:
|
||||
- mqtt.publish:
|
||||
topic: $panel_recv_topic
|
||||
payload: '{"CustomRecv":"event,sleepReached,cardGrid"}'
|
||||
# Service to navigate to screen
|
||||
- action: navigate_to_page
|
||||
variables:
|
||||
page: string
|
||||
then:
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate." + page + ",button";
|
||||
|
||||
# Service to send a command disable screensaver
|
||||
- action: disable_screensaver
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("timeout~0");'
|
||||
|
||||
# Service to send a command enabled screensaver
|
||||
- action: enable_screensaver
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("timeout~20");'
|
||||
|
||||
# Service to send a command wake the screen
|
||||
- action: dim_0_to_100
|
||||
variables:
|
||||
intensity: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("dimmode~"+intensity+"~100");'
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
- action: show_entity
|
||||
variables:
|
||||
entity: string
|
||||
title: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupLight~" + title + "~" + entity);'
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
- action: notify_on_screensaver
|
||||
variables:
|
||||
line1: string
|
||||
line2: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~" + line1 + "~" + line2);'
|
||||
- rtttl.play: "short:d=4,o=5,b=100:16e6"
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
# interaction of the buttons are on implemented yet
|
||||
- action: notify_fullscreen
|
||||
variables:
|
||||
title: string
|
||||
description: string
|
||||
button1: string
|
||||
button2: string
|
||||
time_secs: string
|
||||
then:
|
||||
# show notification screen
|
||||
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupNotify");'
|
||||
# set values on notification screen
|
||||
# color defined as number created in binary and converted to decimal (rrrrggggbbbbaaaa)
|
||||
- lambda: 'id(nspanel_id).send_custom_command("entityUpdateDetail~id~"+ title + "~65535~" + button1 +"~3840~" + button2 + "~61440~" + description + "~65535~" + time_secs);'
|
||||
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
||||
|
||||
uart:
|
||||
id: display_uart
|
||||
tx_pin: 16
|
||||
rx_pin: 17
|
||||
baud_rate: 115200
|
||||
|
||||
mqtt:
|
||||
id: mqtt_client
|
||||
broker: !secret mqtt_ip
|
||||
username: !secret mqtt_username
|
||||
password: !secret mqtt_password
|
||||
discovery: false
|
||||
idf_send_async: true
|
||||
reboot_timeout: 0s
|
||||
|
||||
nspanel_lovelace:
|
||||
id: nspanel_id
|
||||
mqtt_recv_topic: $panel_recv_topic
|
||||
mqtt_send_topic: $panel_send_topic
|
||||
cards:
|
||||
- type: cardGrid
|
||||
title: Romanko
|
||||
entities:
|
||||
- name: Svetlo
|
||||
entity_id: light.mcu_nspanel_office_r_light
|
||||
icon: wall-sconce-flat
|
||||
- name: LED pásik
|
||||
entity_id: light.office_r_desk_strip
|
||||
icon: led-strip-variant
|
||||
- entity_id: delete
|
||||
- name: Monitor
|
||||
entity_id: light.office_r_display_lamp
|
||||
icon: monitor-star
|
||||
- name: (Monitor)
|
||||
entity_id: light.office_r_display_lamp_ambilight
|
||||
icon: monitor-star
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
- type: cardGrid
|
||||
title: Brána
|
||||
entities:
|
||||
- name: Otvoriť
|
||||
entity_id: button.mcu_gate_open
|
||||
icon: gate-open
|
||||
- name: Zatvoriť
|
||||
entity_id: button.mcu_gate_close
|
||||
icon: gate
|
||||
- name: Automatizácia
|
||||
entity_id: navigate.card_gate_automation
|
||||
|
||||
- type: cardThermo
|
||||
title: Kúrenie
|
||||
thermo_entity_id: climate.bt_trv_office_r
|
||||
|
||||
- type: cardEntities
|
||||
title: Automatizácia
|
||||
id: card_gate_automation
|
||||
hidden: true
|
||||
entities:
|
||||
- name: Auto-otváranie
|
||||
entity_id: switch.gate_auto_open
|
||||
- name: Auto-zatváranie
|
||||
entity_id: switch.gate_auto_close
|
||||
|
||||
- type: cardEntities
|
||||
title: Info
|
||||
entities:
|
||||
- name: Zariadenie
|
||||
entity_id: button.office_r_nspanel_info
|
||||
icon: information-outline
|
||||
|
||||
binary_sensor:
|
||||
# - platform: gpio
|
||||
# name: Left Button
|
||||
# pin:
|
||||
# number: 14
|
||||
# inverted: true
|
||||
# on_click:
|
||||
# - switch.toggle: relay_1
|
||||
|
||||
# - platform: gpio
|
||||
# name: Right Button
|
||||
# pin:
|
||||
# number: 27
|
||||
# inverted: true
|
||||
# # on_click:
|
||||
# # - switch.toggle: relay_2
|
||||
# on_click:
|
||||
# - homeassistant.service:
|
||||
# service: light.toggle
|
||||
# data:
|
||||
# entity_id: light.office_r_desk_strip
|
||||
- platform: gpio
|
||||
name: Left Button
|
||||
pin:
|
||||
@@ -241,11 +114,7 @@ binary_sensor:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.office_r_desk_strip
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress1,navigate.cardGrid_main,button";
|
||||
# - lambda: 'id(nspanel_id).send_custom_command("event,buttonPress1,navigate.cardGrid_main,button");'
|
||||
- lambda: 'id(nspanel_id).render_default_page();'
|
||||
|
||||
- platform: gpio
|
||||
name: Right Button
|
||||
@@ -254,17 +123,9 @@ binary_sensor:
|
||||
inverted: true
|
||||
on_click:
|
||||
- light.toggle: light_1
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
|
||||
# - lambda: 'id(nspanel_id).send_custom_command("event,buttonPress2,navigate.cardGrid_main,button");'
|
||||
- lambda: 'id(nspanel_id).render_default_page();'
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
id: buzzer_out
|
||||
pin:
|
||||
number: 21
|
||||
- id: relay_1
|
||||
platform: gpio
|
||||
pin:
|
||||
@@ -281,7 +142,6 @@ light:
|
||||
channel_colors: GRB
|
||||
pin: 23
|
||||
num_leds: 3
|
||||
# rmt_channel: 0
|
||||
chipset: SK6812
|
||||
effects:
|
||||
- pulse:
|
||||
@@ -298,29 +158,13 @@ light:
|
||||
id: light_1
|
||||
output: relay_1
|
||||
restore_mode: RESTORE_DEFAULT_OFF
|
||||
# # - platform: binary
|
||||
# # name: Relay 2
|
||||
# # output: relay_2
|
||||
|
||||
switch:
|
||||
# - platform: gpio
|
||||
# name: Light
|
||||
# id: relay_1
|
||||
# pin:
|
||||
# number: 22
|
||||
- platform: gpio
|
||||
# name: Relay 2
|
||||
id: relay_2
|
||||
pin:
|
||||
number: 19
|
||||
- platform: gpio
|
||||
name: Screen Power
|
||||
id: screen_power
|
||||
entity_category: config
|
||||
pin:
|
||||
number: 4
|
||||
inverted: true
|
||||
restore_mode: ALWAYS_ON
|
||||
- platform: template
|
||||
name: "Geiger Counter"
|
||||
optimistic: True
|
||||
@@ -328,39 +172,3 @@ switch:
|
||||
- script.execute: geiger_click
|
||||
turn_off_action:
|
||||
- script.stop: geiger_click
|
||||
|
||||
rtttl:
|
||||
id: buzzer
|
||||
output: buzzer_out
|
||||
|
||||
sensor:
|
||||
- platform: adc
|
||||
id: ntc_source
|
||||
pin: 38
|
||||
update_interval: 5min
|
||||
attenuation: 12db
|
||||
- platform: resistance
|
||||
id: resistance_sensor
|
||||
sensor: ntc_source
|
||||
configuration: DOWNSTREAM
|
||||
resistor: 11.2kOhm
|
||||
- platform: ntc
|
||||
id: temperature
|
||||
sensor: resistance_sensor
|
||||
calibration:
|
||||
b_constant: 3950
|
||||
reference_temperature: 22°C
|
||||
reference_resistance: 10kOhm
|
||||
name: Temperature
|
||||
- platform: template
|
||||
id: room_temperature_id
|
||||
|
||||
# esp32_ble_tracker:
|
||||
|
||||
# text_sensor:
|
||||
# - platform: ble_scanner
|
||||
# name: BLE Scanner
|
||||
|
||||
# esp32_ble_beacon:
|
||||
# type: iBeacon
|
||||
# uuid: '6d68252f-2e6f-b5b9-965f-43c0a3713880'
|
||||
+162
-219
@@ -1,189 +1,165 @@
|
||||
---
|
||||
# Hardware: Sonoff NSPanel (EU)
|
||||
# Pinout:
|
||||
# GPIO2 IO2
|
||||
# GPIO4 Screen power -> Screen Power (switch gpio) (inverted)
|
||||
# GPIO14 Left button -> Left Button (binary_sensor gpio) (inverted)
|
||||
# GPIO16 Display UART TX -> display_uart (uart tx_pin)
|
||||
# GPIO17 Display UART RX -> display_uart (uart rx_pin)
|
||||
# GPIO19 Relay 2 -> Light MAIN (switch gpio)
|
||||
# GPIO21 Buzzer -> buzzer_out (output ledc)
|
||||
# GPIO22 Relay 1 -> Outside Light Back (switch gpio)
|
||||
# GPIO23 Relay 3 (no relay, connector)
|
||||
# GPIO27 Right button -> Right Button (binary_sensor gpio) (inverted)
|
||||
# GPIO37 ALS (light sensor)
|
||||
# GPIO38 NTC temperature (ADC) -> ntc_source (sensor adc)
|
||||
substitutions:
|
||||
device: nspanel-patio
|
||||
name: Patio NSPanel
|
||||
area: Living Room & Kitchen
|
||||
comment: ${area}, Patio | NSPanel
|
||||
panel_recv_topic: "tele/${device}/RESULT"
|
||||
panel_send_topic: "cmnd/${device}/CustomSend"
|
||||
tft_url: !secret tft_url
|
||||
|
||||
esphome:
|
||||
name: mcu-${device}
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
on_boot:
|
||||
then:
|
||||
- switch.turn_off: screen_power
|
||||
- delay: 1s
|
||||
- switch.turn_on: screen_power
|
||||
|
||||
esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
minimum_chip_revision: 3.0
|
||||
sram1_as_iram: true
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/sairon/esphome-nspanel-lovelace-ui
|
||||
ref: dev
|
||||
components: [nspanel_lovelace]
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
api:
|
||||
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
|
||||
actions:
|
||||
- action: play_rtttl
|
||||
variables:
|
||||
song: string
|
||||
then:
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
|
||||
- action: notify
|
||||
variables:
|
||||
heading: string
|
||||
message: string
|
||||
song: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
|
||||
- rtttl.play:
|
||||
rtttl: !lambda 'return song;'
|
||||
- delay: 10s
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~~");'
|
||||
|
||||
- action: update_tft
|
||||
then:
|
||||
- lambda: |-
|
||||
id(nspanel_id).upload_tft("${tft_url}");
|
||||
|
||||
- action: upload_tft
|
||||
variables:
|
||||
url: string
|
||||
then:
|
||||
- lambda: |-
|
||||
id(nspanel_id).upload_tft(url);
|
||||
|
||||
# Service to send a command directly to the display. Useful for testing
|
||||
- action: send_command
|
||||
variables:
|
||||
cmd: string
|
||||
then:
|
||||
- lambda: "id(nspanel_id).send_custom_command(cmd.c_str());"
|
||||
|
||||
# Service to pusblish to mqtt used for refreshin the panel
|
||||
- action: publish_to_recv_topic
|
||||
variables:
|
||||
cmd: string
|
||||
then:
|
||||
- mqtt.publish:
|
||||
topic: $panel_recv_topic
|
||||
payload: !lambda "return cmd;"
|
||||
|
||||
# Service to send a command wake the screen
|
||||
- action: wake
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("wake");'
|
||||
|
||||
# Service to send a command to show screensaver (some of the values are hardcoded)
|
||||
- action: show_screensaver
|
||||
then:
|
||||
- mqtt.publish:
|
||||
topic: $panel_recv_topic
|
||||
payload: '{"CustomRecv":"event,sleepReached,cardGrid"}'
|
||||
# Service to navigate to screen
|
||||
- action: navigate_to_page
|
||||
variables:
|
||||
page: string
|
||||
then:
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate." + page + ",button";
|
||||
|
||||
# Service to send a command disable screensaver
|
||||
- action: disable_screensaver
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("timeout~0");'
|
||||
|
||||
# Service to send a command enabled screensaver
|
||||
- action: enable_screensaver
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("timeout~20");'
|
||||
|
||||
# Service to send a command wake the screen
|
||||
- action: dim_0_to_100
|
||||
variables:
|
||||
intensity: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("dimmode~"+intensity+"~100");'
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
- action: show_entity
|
||||
variables:
|
||||
entity: string
|
||||
title: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupLight~" + title + "~" + entity);'
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
- action: notify_on_screensaver
|
||||
variables:
|
||||
line1: string
|
||||
line2: string
|
||||
then:
|
||||
- lambda: 'id(nspanel_id).send_custom_command("notify~" + line1 + "~" + line2);'
|
||||
- rtttl.play: "short:d=4,o=5,b=100:16e6"
|
||||
|
||||
# Service to send a command To show the screen of a specific like
|
||||
# interaction of the buttons are on implemented yet
|
||||
- action: notify_fullscreen
|
||||
variables:
|
||||
title: string
|
||||
description: string
|
||||
button1: string
|
||||
button2: string
|
||||
time_secs: string
|
||||
then:
|
||||
# show notification screen
|
||||
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupNotify");'
|
||||
# set values on notification screen
|
||||
# color defined as number created in binary and converted to decimal (rrrrggggbbbbaaaa)
|
||||
- lambda: 'id(nspanel_id).send_custom_command("entityUpdateDetail~id~"+ title + "~65535~" + button1 +"~3840~" + button2 + "~61440~" + description + "~65535~" + time_secs);'
|
||||
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
||||
|
||||
uart:
|
||||
id: display_uart
|
||||
tx_pin: 16
|
||||
rx_pin: 17
|
||||
baud_rate: 115200
|
||||
|
||||
mqtt:
|
||||
id: mqtt_client
|
||||
broker: !secret mqtt_ip
|
||||
username: !secret mqtt_username
|
||||
password: !secret mqtt_password
|
||||
discovery: false
|
||||
idf_send_async: true
|
||||
reboot_timeout: 0s
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
nspanel: !include
|
||||
file: common/nspanel.yaml
|
||||
vars:
|
||||
temperature_entity: sensor.zigbee_living_room_kitchen_temperature
|
||||
temperature_name: Living Room Temperature
|
||||
|
||||
nspanel_lovelace:
|
||||
id: nspanel_id
|
||||
mqtt_recv_topic: $panel_recv_topic
|
||||
mqtt_send_topic: $panel_send_topic
|
||||
cards:
|
||||
- type: cardGrid
|
||||
title: Kuchyňa / Obývačka
|
||||
id: card_home
|
||||
entities:
|
||||
- name: Kuchyňa / Obývačka
|
||||
entity_id: light.kitchen
|
||||
icon: ceiling-light
|
||||
- name: Stôl
|
||||
entity_id: light.dining_table
|
||||
icon: ceiling-light
|
||||
- name: Gauč
|
||||
entity_id: light.living_room
|
||||
icon: ceiling-light
|
||||
- name: TV
|
||||
entity_id: light.mcu_tv_light_strip
|
||||
icon: led-strip-variant
|
||||
- name: (Stôl)
|
||||
entity_id: light.dining_table_ambilight
|
||||
icon: ceiling-light-multiple-outline
|
||||
- name: (Gauč)
|
||||
entity_id: light.living_room_ambilight
|
||||
icon: ceiling-light-multiple-outline
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
- type: cardGrid
|
||||
title: Kuchyňa
|
||||
entities:
|
||||
- name: Linka
|
||||
entity_id: light.worktop_lights
|
||||
icon: countertop
|
||||
- name: Sporák
|
||||
entity_id: light.mcu_kitchen_stove
|
||||
icon: stove
|
||||
|
||||
- type: cardGrid
|
||||
title: Von
|
||||
id: card_outside
|
||||
entities:
|
||||
- name: Terasa
|
||||
entity_id: light.patio_lights
|
||||
icon: lightbulb-group
|
||||
- name: Terasa
|
||||
entity_id: switch.mcu_patio_relay
|
||||
icon: power-socket-fr
|
||||
- name: Vzadu
|
||||
entity_id: switch.mcu_nspanel_patio_outside_light_back
|
||||
icon: light-flood-down
|
||||
- name: Z. domček
|
||||
entity_id: switch.mcu_shed_light
|
||||
icon: wall-sconce-flat
|
||||
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
|
||||
- name: Z. domček
|
||||
entity_id: switch.mcu_shed_socket
|
||||
icon: power-socket-fr
|
||||
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
|
||||
- name: Záhrada
|
||||
entity_id: switch.mcu_shed_garden_light
|
||||
icon: light-flood-down
|
||||
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
|
||||
|
||||
- type: cardGrid
|
||||
title: Závlaha
|
||||
entities:
|
||||
- name: A1
|
||||
entity_id: switch.mcu_well_irrigation_valve_a1
|
||||
icon: valve
|
||||
- name: A2
|
||||
entity_id: switch.mcu_well_irrigation_valve_a2
|
||||
icon: valve
|
||||
- name: A3
|
||||
entity_id: switch.mcu_well_irrigation_valve_a3
|
||||
icon: valve
|
||||
- name: A4
|
||||
entity_id: switch.mcu_well_irrigation_valve_a4
|
||||
icon: valve
|
||||
- name: A5
|
||||
entity_id: switch.mcu_well_irrigation_valve_a5
|
||||
icon: valve
|
||||
- name: Automatizácia
|
||||
entity_id: navigate.card_irrigation_automation
|
||||
|
||||
- type: cardGrid
|
||||
title: Brána
|
||||
entities:
|
||||
- name: Otvoriť
|
||||
entity_id: button.mcu_gate_open
|
||||
icon: gate-open
|
||||
- name: Zatvoriť
|
||||
entity_id: button.mcu_gate_close
|
||||
icon: gate
|
||||
- name: Automatizácia
|
||||
entity_id: navigate.card_gate_automation
|
||||
|
||||
- type: cardThermo
|
||||
title: Kúrenie
|
||||
thermo_entity_id: climate.bt_trv_living_room_kitchen
|
||||
|
||||
- type: cardEntities
|
||||
title: Automatizácia
|
||||
id: card_gate_automation
|
||||
hidden: true
|
||||
entities:
|
||||
- name: Auto-otváranie
|
||||
entity_id: switch.gate_auto_open
|
||||
- name: Auto-zatváranie
|
||||
entity_id: switch.gate_auto_close
|
||||
|
||||
- type: cardEntities
|
||||
title: Automatizácia
|
||||
id: card_irrigation_automation
|
||||
hidden: true
|
||||
entities:
|
||||
- name: Ráno
|
||||
entity_id: automation.irrigation_morning
|
||||
- name: Terasa
|
||||
entity_id: automation.irrigation_patio
|
||||
- name: Rast trávy
|
||||
entity_id: automation.irrigation_grow_grass
|
||||
|
||||
- type: cardEntities
|
||||
title: Info
|
||||
entities:
|
||||
- name: Zariadenie
|
||||
entity_id: button.patio_nspanel_info
|
||||
icon: information-outline
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
@@ -193,10 +169,7 @@ binary_sensor:
|
||||
inverted: true
|
||||
on_click:
|
||||
- switch.toggle: relay_1
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
|
||||
- script.execute: show_outside_card
|
||||
|
||||
- platform: gpio
|
||||
name: Right Button
|
||||
@@ -205,25 +178,23 @@ binary_sensor:
|
||||
inverted: true
|
||||
on_click:
|
||||
if:
|
||||
# mcu-patio online -> toggle its lights; otherwise (no API or relay_2 cut its power) the relay
|
||||
condition:
|
||||
api.connected:
|
||||
and:
|
||||
- api.connected:
|
||||
- binary_sensor.is_on: patio_status
|
||||
then:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.patio_lights
|
||||
- mqtt.publish_json:
|
||||
topic: $panel_recv_topic
|
||||
payload: |-
|
||||
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
|
||||
- script.execute: show_outside_card
|
||||
else:
|
||||
- switch.toggle: relay_2
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
id: buzzer_out
|
||||
pin:
|
||||
number: 21
|
||||
- platform: homeassistant
|
||||
id: patio_status
|
||||
entity_id: binary_sensor.mcu_patio_status
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
@@ -240,40 +211,12 @@ switch:
|
||||
pin:
|
||||
number: 19
|
||||
|
||||
- platform: gpio
|
||||
name: Screen Power
|
||||
id: screen_power
|
||||
entity_category: config
|
||||
pin:
|
||||
number: 4
|
||||
inverted: true
|
||||
restore_mode: ALWAYS_ON
|
||||
|
||||
rtttl:
|
||||
id: buzzer
|
||||
output: buzzer_out
|
||||
|
||||
sensor:
|
||||
- platform: adc
|
||||
id: ntc_source
|
||||
pin: 38
|
||||
update_interval: 5min
|
||||
attenuation: 12db
|
||||
|
||||
- platform: resistance
|
||||
id: resistance_sensor
|
||||
sensor: ntc_source
|
||||
configuration: DOWNSTREAM
|
||||
resistor: 11.2kOhm
|
||||
|
||||
- platform: ntc
|
||||
id: temperature
|
||||
sensor: resistance_sensor
|
||||
calibration:
|
||||
b_constant: 3950
|
||||
reference_temperature: 25°C
|
||||
reference_resistance: 10kOhm
|
||||
name: Living Room Temperature
|
||||
|
||||
- platform: template
|
||||
id: room_temperature_id
|
||||
script:
|
||||
# jump straight to the "Von" card (one render instead of home + next + next):
|
||||
# make it the default page for a moment, render it, restore the home card
|
||||
- id: show_outside_card
|
||||
then:
|
||||
- lambda: |-
|
||||
id(nspanel_id).set_default_page("card_outside");
|
||||
id(nspanel_id).render_default_page();
|
||||
id(nspanel_id).set_default_page("card_home");
|
||||
+14
-7
@@ -1,4 +1,15 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Stamp C3
|
||||
# Pinout:
|
||||
# GPIO0 I2C SDA
|
||||
# GPIO1 I2C SCL
|
||||
# GPIO2 NEO
|
||||
# GPIO3 BUTTON
|
||||
# GPIO4 -> led_red (output ledc)
|
||||
# GPIO5 -> led_green (output ledc)
|
||||
# GPIO6 -> led_blue (output ledc)
|
||||
# GPIO20 UART RX
|
||||
# GPIO21 UART TX
|
||||
substitutions:
|
||||
device: office-r
|
||||
name: Office R
|
||||
@@ -18,17 +29,13 @@ esp32:
|
||||
variant: esp32c3
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
|
||||
# GPIO21 UART TX
|
||||
# GPIO20 UART RX
|
||||
# GPIO0 I2C SDA
|
||||
# GPIO1 I2C SCL
|
||||
# GPIO2 NEO
|
||||
# GPIO3 BUTTON
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
light:
|
||||
- platform: rgb
|
||||
|
||||
+21
-20
@@ -1,4 +1,21 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite + ATOM Hub Switch
|
||||
# Pinout:
|
||||
# GPIO1 UART0 TX
|
||||
# GPIO3 UART0 RX
|
||||
# GPIO12 IR
|
||||
# GPIO19 Relay2 -> Relay (switch gpio)
|
||||
# GPIO21 I2C1 SCL -> i2c (i2c scl)
|
||||
# GPIO22 Relay1 -> power_supply1 (power_supply)
|
||||
# GPIO23 RS485 Tx
|
||||
# GPIO25 I2C1 SDA - red -> i2c (i2c sda)
|
||||
# GPIO26 I2C0 SDA - white
|
||||
# GPIO27 NEO 1x -> atom_led (status LED, common/status_led_atom.yaml)
|
||||
# GPIO32 I2C0 SCL
|
||||
# GPIO33 RS485 Rx
|
||||
# GPIO39 BUTTON -> Status Button (binary_sensor gpio)
|
||||
# I2C:
|
||||
# 0x40 pca9685_hub1 (pca9685)
|
||||
substitutions:
|
||||
device: patio
|
||||
name: Patio
|
||||
@@ -33,32 +50,16 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include common/status_led_atom.yaml
|
||||
|
||||
|
||||
# PINOUTS: M5ATOM+Atom Hub Switch
|
||||
# IR G12
|
||||
# NEO G27 1x
|
||||
# BUTTON G39
|
||||
# I2C0 SDA G26 - white
|
||||
# I2C0 SCL G32
|
||||
# I2C1 SDA G25 - red
|
||||
# I2C1 SCL G21
|
||||
# UART0 RX G3
|
||||
# UART0 TX G1
|
||||
# RS485 Rx G33
|
||||
# RS485 Tx G23
|
||||
# Relay1 G22
|
||||
# Relay2 G19
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: ha_time
|
||||
timezone: Europe/Bratislava
|
||||
|
||||
i2c:
|
||||
# - id: i2c0
|
||||
# sda: 26
|
||||
|
||||
+14
-3
@@ -1,4 +1,16 @@
|
||||
---
|
||||
# Hardware: Sonoff POW Elite
|
||||
# Pinout:
|
||||
# GPIO0 -> reset (binary_sensor gpio)
|
||||
# GPIO2 -> relay_on (switch gpio)
|
||||
# GPIO4 -> relay_off (switch gpio)
|
||||
# GPIO5 -> wifi_status_led (light status_led) (inverted)
|
||||
# GPIO14 -> tm1621_display (display tm1621 data_pin)
|
||||
# GPIO16 -> cse7766_uart (uart rx_pin)
|
||||
# GPIO18 -> led (output ledc) (inverted)
|
||||
# GPIO25 -> tm1621_display (display tm1621 cs_pin)
|
||||
# GPIO26 -> tm1621_display (display tm1621 read_pin)
|
||||
# GPIO27 -> tm1621_display (display tm1621 write_pin)
|
||||
substitutions:
|
||||
device: pow-dishwasher
|
||||
name: Dishwasher POW
|
||||
@@ -18,11 +30,13 @@ esp32:
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
minimum_chip_revision: 3.1
|
||||
sram1_as_iram: true
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
uart:
|
||||
- id: cse7766_uart
|
||||
@@ -186,6 +200,3 @@ switch:
|
||||
- delay: 500ms
|
||||
- switch.turn_off: relay_off # bi-stable relay so no need to keep on
|
||||
interlock: [relay_on]
|
||||
|
||||
time:
|
||||
- !include common/time/homeassistant.yaml
|
||||
+14
-4
@@ -1,8 +1,20 @@
|
||||
---
|
||||
# Hardware: Sonoff POW Elite
|
||||
# Pinout:
|
||||
# GPIO0 -> reset (binary_sensor gpio)
|
||||
# GPIO2 -> relay_on (switch gpio)
|
||||
# GPIO4 -> relay_off (switch gpio)
|
||||
# GPIO5 -> wifi_status_led (light status_led) (inverted)
|
||||
# GPIO14 -> tm1621_display (display tm1621 data_pin)
|
||||
# GPIO16 -> cse7766_uart (uart rx_pin)
|
||||
# GPIO18 -> led (output ledc) (inverted)
|
||||
# GPIO25 -> tm1621_display (display tm1621 cs_pin)
|
||||
# GPIO26 -> tm1621_display (display tm1621 read_pin)
|
||||
# GPIO27 -> tm1621_display (display tm1621 write_pin)
|
||||
substitutions:
|
||||
device: pow-washer
|
||||
name: Washer POW
|
||||
comment:
|
||||
comment: "spare"
|
||||
area: Washroom
|
||||
update_interval: 10s
|
||||
|
||||
@@ -18,6 +30,7 @@ esp32:
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
flash_chip: gd # same device as pow-dishwasher (not read from the device)
|
||||
minimum_chip_revision: 3.1
|
||||
sram1_as_iram: true
|
||||
|
||||
@@ -186,6 +199,3 @@ switch:
|
||||
- delay: 500ms
|
||||
- switch.turn_off: relay_off # bi-stable relay so no need to keep on
|
||||
interlock: [relay_on]
|
||||
|
||||
time:
|
||||
- !include common/time/homeassistant.yaml
|
||||
+15
@@ -1,4 +1,15 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite
|
||||
# Pinout:
|
||||
# GPIO19 -> spi (spi mosi_pin)
|
||||
# GPIO21 -> rf_tx (remote_transmitter)
|
||||
# GPIO22 -> spi (spi miso_pin)
|
||||
# GPIO23 -> spi (spi clk_pin)
|
||||
# GPIO25 -> rf_rx (remote_receiver)
|
||||
# GPIO26 Grove SDA
|
||||
# GPIO27 NEO (ATOM RGB LED) -> atom_led (status LED, common/status_led_atom.yaml)
|
||||
# GPIO32 Grove SCL
|
||||
# GPIO33 -> cc1101 (cc1101 cs_pin)
|
||||
substitutions:
|
||||
device: rf
|
||||
name: RF Bridge
|
||||
@@ -15,10 +26,14 @@ esp32:
|
||||
variant: esp32
|
||||
flash_size: 4MB
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include common/status_led_atom.yaml
|
||||
|
||||
spi:
|
||||
clk_pin: 23
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
---
|
||||
# Hardware: Sonoff RF Bridge (obsolete)
|
||||
# Pinout:
|
||||
# GPIO4 -> remote_receiver (remote_receiver)
|
||||
# GPIO5 -> remote_transmitter (remote_transmitter)
|
||||
# GPIO13 -> status_led (status_led) (inverted)
|
||||
substitutions:
|
||||
device: rf433
|
||||
name: RF433 Bridge
|
||||
@@ -16,6 +21,7 @@ esp8266:
|
||||
|
||||
packages:
|
||||
common: !include common/common_esp8266.yaml
|
||||
mdns_ipv6: !include common/esp8266_mdns_ipv6.yaml
|
||||
|
||||
remote_receiver:
|
||||
pin: 4
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Stamp S3 + PN5180 module (5 V and 3.3 V both needed)
|
||||
# Pinout:
|
||||
# GPIO0 Button
|
||||
# GPIO3 (external RFID LED, not fitted yet; notify_led uses the onboard RGB for now)
|
||||
# GPIO4 Buzzer -> buzzer (output ledc)
|
||||
# GPIO5 -> spi (spi miso_pin) PN5180 MISO
|
||||
# GPIO6 -> spi (spi mosi_pin) PN5180 MOSI
|
||||
# GPIO7 -> spi (spi clk_pin) PN5180 SCK
|
||||
# GPIO8 -> pn5180 (cs_pin) PN5180 NSS
|
||||
# GPIO9 -> pn5180 (busy_pin) PN5180 BUSY
|
||||
# GPIO10 -> pn5180 (reset_pin) PN5180 RST
|
||||
# GPIO11 -> pn5180 (irq_pin) PN5180 IRQ
|
||||
# GPIO19 U-
|
||||
# GPIO20 U+
|
||||
# GPIO21 RGB -> notify_led (light esp32_rmt_led_strip), also status_led
|
||||
# PN5180 REQ, AUX, GPIO left open (REQ high at reset = firmware download mode)
|
||||
substitutions:
|
||||
device: rfid-openprinttag
|
||||
name: OpenPrintTag
|
||||
area: DEV
|
||||
comment: Filament spool reader (OpenPrintTag, Creality CFS)
|
||||
|
||||
esphome:
|
||||
name: mcu-${device}
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
|
||||
esp32:
|
||||
variant: esp32s3
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include
|
||||
file: common/status_led.yaml
|
||||
vars:
|
||||
light_id: notify_led
|
||||
show_errors: "true"
|
||||
|
||||
external_components:
|
||||
- source: github://randybb/esphome-components
|
||||
components: [pn5180, openprinttag, creality_cfs]
|
||||
refresh: 1h
|
||||
|
||||
# Called by the OpenPrintTag integration (openprinttag.update) to update the aux region
|
||||
api:
|
||||
actions:
|
||||
- action: openprinttag_write_aux
|
||||
variables:
|
||||
uid: string
|
||||
aux: string
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
switch.is_on: led_enabled
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: notify_led
|
||||
brightness: 100%
|
||||
red: 100%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
flash_length: 500ms
|
||||
- lambda: id(spool).write_aux(uid, aux);
|
||||
# Writes a Creality CFS record (48 characters, optionally 48 more for sector 2) to the
|
||||
# Creality tag on the reader
|
||||
- action: creality_cfs_write
|
||||
variables:
|
||||
uid: string
|
||||
data: string
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
switch.is_on: led_enabled
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: notify_led
|
||||
brightness: 100%
|
||||
red: 100%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
flash_length: 500ms
|
||||
- lambda: id(creality).write(uid, data);
|
||||
|
||||
spi:
|
||||
clk_pin: GPIO7
|
||||
mosi_pin: GPIO6
|
||||
miso_pin: GPIO5
|
||||
|
||||
pn5180:
|
||||
id: pn5180_reader
|
||||
cs_pin: GPIO8
|
||||
busy_pin: GPIO9
|
||||
reset_pin: GPIO10
|
||||
irq_pin: GPIO11
|
||||
on_tag:
|
||||
- if:
|
||||
condition:
|
||||
switch.is_on: led_enabled
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: notify_led
|
||||
brightness: 100%
|
||||
red: 0%
|
||||
green: 100%
|
||||
blue: 0%
|
||||
flash_length: 500ms
|
||||
- if:
|
||||
condition:
|
||||
switch.is_on: buzzer_enabled
|
||||
then:
|
||||
- rtttl.play: "success:d=32,o=5,b=100:c,g,b"
|
||||
- homeassistant.tag_scanned: !lambda return x;
|
||||
on_tag_removed:
|
||||
- if:
|
||||
condition:
|
||||
switch.is_on: buzzer_enabled
|
||||
then:
|
||||
- rtttl.play: "removed:d=16,o=5,b=100:b"
|
||||
|
||||
openprinttag:
|
||||
id: spool
|
||||
# Raw tag record for the OpenPrintTag integration (randybb/ha-openprinttag), empty when removed
|
||||
on_openprinttag:
|
||||
- homeassistant.event:
|
||||
event: esphome.openprinttag
|
||||
data:
|
||||
uid: !lambda return uid;
|
||||
payload: !lambda return payload;
|
||||
|
||||
creality_cfs:
|
||||
id: creality
|
||||
# The Creality record (ASCII) for Home Assistant, empty when removed
|
||||
on_creality:
|
||||
- homeassistant.event:
|
||||
event: esphome.creality_cfs
|
||||
data:
|
||||
uid: !lambda return uid;
|
||||
data: !lambda return data;
|
||||
|
||||
sensor:
|
||||
- platform: creality_cfs
|
||||
name: Creality length
|
||||
- platform: openprinttag
|
||||
type: remaining_weight
|
||||
name: Remaining weight
|
||||
- platform: openprinttag
|
||||
type: consumed_weight
|
||||
name: Consumed weight
|
||||
- platform: openprinttag
|
||||
type: actual_weight
|
||||
name: Full weight
|
||||
- platform: openprinttag
|
||||
type: min_print_temperature
|
||||
name: Min print temperature
|
||||
- platform: openprinttag
|
||||
type: max_print_temperature
|
||||
name: Max print temperature
|
||||
- platform: openprinttag
|
||||
type: max_bed_temperature
|
||||
name: Max bed temperature
|
||||
- platform: openprinttag
|
||||
type: drying_temperature
|
||||
name: Drying temperature
|
||||
- platform: openprinttag
|
||||
type: drying_time
|
||||
name: Drying time
|
||||
|
||||
text_sensor:
|
||||
- platform: pn5180
|
||||
name: Tag UID
|
||||
- platform: creality_cfs
|
||||
type: material_id
|
||||
name: Creality material ID
|
||||
- platform: creality_cfs
|
||||
type: color
|
||||
name: Creality color
|
||||
id: creality_color
|
||||
on_value:
|
||||
- script.execute: show_filament_color
|
||||
- platform: creality_cfs
|
||||
type: serial
|
||||
name: Creality serial
|
||||
- platform: openprinttag
|
||||
type: material_type
|
||||
name: Material
|
||||
- platform: openprinttag
|
||||
type: material_name
|
||||
name: Material name
|
||||
- platform: openprinttag
|
||||
type: brand_name
|
||||
name: Brand
|
||||
- platform: openprinttag
|
||||
type: primary_color
|
||||
name: Color
|
||||
id: openprinttag_color
|
||||
on_value:
|
||||
- script.execute: show_filament_color
|
||||
|
||||
script:
|
||||
# The filament color on the filament_color light: the OpenPrintTag's, else the Creality tag's
|
||||
- id: show_filament_color
|
||||
then:
|
||||
- lambda: |-
|
||||
std::string x = id(openprinttag_color).state;
|
||||
if (x.size() < 7)
|
||||
x = id(creality_color).state;
|
||||
if (x.size() < 7) {
|
||||
id(filament_color).turn_off().set_transition_length(0).perform();
|
||||
return;
|
||||
}
|
||||
uint32_t c = strtoul(x.c_str() + 1, nullptr, 16);
|
||||
if (x.size() > 7)
|
||||
c >>= 8; // #rrggbbaa
|
||||
// ESPHome scales the strongest channel to 100 %, the darkness goes to the brightness
|
||||
const float r = ((c >> 16) & 0xFF) / 255.0f, g = ((c >> 8) & 0xFF) / 255.0f, b = (c & 0xFF) / 255.0f;
|
||||
const float m = std::max({r, g, b});
|
||||
if (m == 0.0f) {
|
||||
id(filament_color).turn_off().set_transition_length(0).perform(); // black
|
||||
return;
|
||||
}
|
||||
id(filament_color).turn_on()
|
||||
.set_rgb(r, g, b)
|
||||
.set_brightness(m)
|
||||
.set_transition_length(0)
|
||||
.perform();
|
||||
|
||||
light:
|
||||
# Onboard RGB for now: tag feedback and status. With the external LED fitted, move
|
||||
# notify_led to pin 3 and give the onboard one back to status_led (as on mcu-rfid-reader)
|
||||
- platform: esp32_rmt_led_strip
|
||||
id: notify_led
|
||||
name: Notify LED
|
||||
channel_colors: GRB
|
||||
pin: 21
|
||||
num_leds: 1
|
||||
chipset: ws2812
|
||||
restore_mode: ALWAYS_OFF
|
||||
rmt_symbols: 96
|
||||
|
||||
# Shows the filament color in Home Assistant; drives nothing, the next tag read overwrites it
|
||||
- platform: rgb
|
||||
id: filament_color
|
||||
name: Filament color
|
||||
icon: mdi:printer-3d-nozzle
|
||||
red: color_none
|
||||
green: color_none
|
||||
blue: color_none
|
||||
restore_mode: ALWAYS_OFF
|
||||
default_transition_length: 0s
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
pin: 4
|
||||
id: buzzer
|
||||
- platform: template
|
||||
id: color_none
|
||||
type: float
|
||||
write_action: []
|
||||
|
||||
rtttl:
|
||||
output: buzzer
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
name: Buzzer Enabled
|
||||
id: buzzer_enabled
|
||||
icon: mdi:volume-high
|
||||
optimistic: true
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
entity_category: config
|
||||
- platform: template
|
||||
name: LED enabled
|
||||
id: led_enabled
|
||||
icon: mdi:alarm-light-outline
|
||||
optimistic: true
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
entity_category: config
|
||||
+27
-21
@@ -1,4 +1,16 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Stamp S3
|
||||
# Pinout:
|
||||
# GPIO0 Button
|
||||
# GPIO1 I2C SDA -> i2c_rfid (i2c sda)
|
||||
# GPIO2 I2C SDA, Notify LED -> i2c_rfid (i2c scl)
|
||||
# GPIO3 -> notify_led (light esp32_rmt_led_strip)
|
||||
# GPIO4 Buzzer -> buzzer (output ledc)
|
||||
# GPIO19 U-
|
||||
# GPIO20 U+
|
||||
# GPIO21 RGB -> status_led (light esp32_rmt_led_strip)
|
||||
# I2C:
|
||||
# 0x24 pn532_module (pn532_i2c)
|
||||
substitutions:
|
||||
device: rfid-reader
|
||||
name: RFID Reader
|
||||
@@ -16,7 +28,7 @@ esphome:
|
||||
# - wait_until:
|
||||
# api.connected:
|
||||
# - logger.log: API is connected!
|
||||
# - rtttl.play: "success:d=24,o=5,b=100:c,g,b"
|
||||
# - rtttl.play: "success:d=32,o=5,b=100:c,g,b"
|
||||
# - light.turn_on:
|
||||
# id: notify_led
|
||||
# brightness: 100%
|
||||
@@ -31,6 +43,8 @@ esp32:
|
||||
variant: esp32s3
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
|
||||
# external_components:
|
||||
@@ -40,19 +54,11 @@ esp32:
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
# logger:
|
||||
# hardware_uart: UART0
|
||||
|
||||
# GPIO20 U+
|
||||
# GPIO19 U-
|
||||
# GPIO0 Button
|
||||
# GPIO1 I2C SDA
|
||||
# GPIO2 I2C SDA
|
||||
# GPIO2 Notify LED
|
||||
# GPIO4 Buzzer
|
||||
# GPIO21 RGB
|
||||
|
||||
api:
|
||||
actions:
|
||||
- action: rfidreader_tag_ok
|
||||
@@ -85,13 +91,13 @@ api:
|
||||
uri += tag_id;
|
||||
message->add_uri_record(uri);
|
||||
id(pn532_module).write_mode(message);
|
||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
||||
- rtttl.play: "write:d=32,o=5,b=100:b"
|
||||
- wait_until:
|
||||
not:
|
||||
pn532.is_writing:
|
||||
- light.turn_off:
|
||||
id: notify_led
|
||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
||||
- rtttl.play: "write:d=32,o=5,b=100:b,b"
|
||||
|
||||
- action: write_music_tag
|
||||
variables:
|
||||
@@ -117,13 +123,13 @@ api:
|
||||
message->add_text_record(text);
|
||||
}
|
||||
id(pn532_module).write_mode(message);
|
||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
||||
- rtttl.play: "write:d=32,o=5,b=100:b"
|
||||
- wait_until:
|
||||
not:
|
||||
pn532.is_writing:
|
||||
- light.turn_off:
|
||||
id: notify_led
|
||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
||||
- rtttl.play: "write:d=32,o=5,b=100:b,b"
|
||||
|
||||
globals:
|
||||
- id: source
|
||||
@@ -165,7 +171,7 @@ pn532_i2c:
|
||||
id(info)="";
|
||||
if (tag.has_ndef_message()) {
|
||||
auto message = tag.get_ndef_message();
|
||||
auto records = message->get_records();
|
||||
const auto &records = message->get_records();
|
||||
for (auto &record : records) {
|
||||
std::string payload = record->get_payload();
|
||||
std::string type = record->get_type();
|
||||
@@ -265,7 +271,7 @@ pn532_i2c:
|
||||
condition:
|
||||
switch.is_on: buzzer_enabled
|
||||
then:
|
||||
- rtttl.play: "success:d=24,o=5,b=100:c,g,b"
|
||||
- rtttl.play: "success:d=32,o=5,b=100:c,g,b"
|
||||
on_tag_removed:
|
||||
then:
|
||||
- homeassistant.event:
|
||||
@@ -307,13 +313,13 @@ button:
|
||||
message->add_uri_record(uri);
|
||||
ESP_LOGD("tagreader", "Writing payload: %s", uri.c_str());
|
||||
id(pn532_module).write_mode(message);
|
||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
||||
- rtttl.play: "write:d=32,o=5,b=100:b"
|
||||
- wait_until:
|
||||
not:
|
||||
pn532.is_writing:
|
||||
- light.turn_off:
|
||||
id: notify_led
|
||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
||||
- rtttl.play: "write:d=32,o=5,b=100:b,b"
|
||||
- platform: template
|
||||
name: Clean Tag
|
||||
id: clean_tag
|
||||
@@ -327,13 +333,13 @@ button:
|
||||
green: 64.7%
|
||||
blue: 0%
|
||||
- lambda: 'id(pn532_module).clean_mode();'
|
||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
||||
- rtttl.play: "write:d=32,o=5,b=100:b"
|
||||
- wait_until:
|
||||
not:
|
||||
pn532.is_writing:
|
||||
- light.turn_off:
|
||||
id: notify_led
|
||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
||||
- rtttl.play: "write:d=32,o=5,b=100:b,b"
|
||||
- platform: template
|
||||
name: Cancel writing
|
||||
id: cancel_writing
|
||||
@@ -343,7 +349,7 @@ button:
|
||||
- lambda: 'id(pn532_module).read_mode();'
|
||||
- light.turn_off:
|
||||
id: notify_led
|
||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
||||
- rtttl.play: "write:d=32,o=5,b=100:b,b"
|
||||
|
||||
light:
|
||||
- platform: esp32_rmt_led_strip
|
||||
|
||||
+15
-3
@@ -1,4 +1,15 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite + ATOM Hub Switch
|
||||
# Pinout:
|
||||
# GPIO19 -> Light (switch gpio)
|
||||
# GPIO21 -> i2c (i2c scl)
|
||||
# GPIO22 -> Garden Light (switch gpio)
|
||||
# GPIO25 -> i2c (i2c sda)
|
||||
# GPIO26 Grove SDA
|
||||
# GPIO27 NEO (ATOM RGB LED) -> atom_led (status LED, common/status_led_atom.yaml)
|
||||
# GPIO32 Grove SCL
|
||||
# I2C:
|
||||
# 0x27 pca9554_module (pca9554)
|
||||
substitutions:
|
||||
device: shed
|
||||
name: Shed
|
||||
@@ -15,12 +26,16 @@ esp32:
|
||||
variant: esp32
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
type: esp-idf
|
||||
# version: 5.4.2
|
||||
# platform_version: 54.03.21
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include common/status_led_atom.yaml
|
||||
|
||||
i2c:
|
||||
sda: 25
|
||||
@@ -108,6 +123,3 @@ switch:
|
||||
pin:
|
||||
number: 19
|
||||
mode: OUTPUT
|
||||
|
||||
time:
|
||||
- !include common/time/homeassistant.yaml
|
||||
+13
-14
@@ -1,4 +1,15 @@
|
||||
---
|
||||
# Hardware: BlitzWolf BW-SHP2/SHP6
|
||||
# Pinout:
|
||||
# GPIO0 GPIO_LED1_INV Red LED (inverted), Power Status -> led_red (output gpio)
|
||||
# GPIO1 GPIO_USER RXD, Optional sensor
|
||||
# GPIO2 GPIO_LEDLNK_INV Blue LED (inverted), Link Status -> led_blue (output gpio)
|
||||
# GPIO3 GPIO_USER TXD, Optional sensor
|
||||
# GPIO5 GPIO_HJL_CF BL0937 or HJL-01 CF power -> sensor/hlw8012 (sensor hlw8012 cf_pin)
|
||||
# GPIO12 GPIO_NRG_SEL_INV BL0937 or HJL-01 Sel output (0 = Voltage) -> sensor/hlw8012 (sensor hlw8012 sel_pin) (inverted)
|
||||
# GPIO13 GPIO_KEY1 Button -> Button (binary_sensor gpio) (inverted)
|
||||
# GPIO14 GPIO_NRG_CF1 BL0937 or HJL-01 CF1 current / voltage -> sensor/hlw8012 (sensor hlw8012 cf1_pin)
|
||||
# GPIO15 GPIO_REL1 Relay -> Relay (switch gpio)
|
||||
substitutions:
|
||||
device: socket-01
|
||||
name: Socket 01
|
||||
@@ -18,18 +29,7 @@ esp8266:
|
||||
|
||||
packages:
|
||||
common: !include common/common_esp8266.yaml
|
||||
|
||||
# BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring)
|
||||
# Pin Name Function
|
||||
# GPIO15 GPIO_REL1 Relay
|
||||
# GPIO13 GPIO_KEY1 Button
|
||||
# GPIO00 GPIO_LED1_INV Red LED (inverted), Power Status
|
||||
# GPIO02 GPIO_LEDLNK_INV Blue LED (inverted), Link Status
|
||||
# GPIO05 GPIO_HJL_CF BL0937 or HJL-01 CF power
|
||||
# GPIO12 GPIO_NRG_SEL_INV BL0937 or HJL-01 Sel output (0 = Voltage)
|
||||
# GPIO14 GPIO_NRG_CF1 BL0937 or HJL-01 CF1 current / voltage
|
||||
# GPIO01 GPIO_USER RXD, Optional sensor
|
||||
# GPIO03 GPIO_USER TXD, Optional sensor
|
||||
mdns_ipv6: !include common/esp8266_mdns_ipv6.yaml
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
@@ -121,8 +121,7 @@ switch:
|
||||
# number: GPIO00
|
||||
# inverted: True
|
||||
|
||||
time:
|
||||
- !include common/time/homeassistant.yaml
|
||||
# time:
|
||||
# - platform: homeassistant
|
||||
# timezone: Europe/Bratislava
|
||||
# on_time:
|
||||
|
||||
+13
-14
@@ -1,4 +1,15 @@
|
||||
---
|
||||
# Hardware: BlitzWolf BW-SHP2/SHP6
|
||||
# Pinout:
|
||||
# GPIO0 GPIO_LED1_INV Red LED (inverted), Power Status -> led_red (output gpio)
|
||||
# GPIO1 GPIO_USER RXD, Optional sensor
|
||||
# GPIO2 GPIO_LEDLNK_INV Blue LED (inverted), Link Status -> led_blue (output gpio)
|
||||
# GPIO3 GPIO_USER TXD, Optional sensor
|
||||
# GPIO5 GPIO_HJL_CF BL0937 or HJL-01 CF power -> sensor/hlw8012 (sensor hlw8012 cf_pin)
|
||||
# GPIO12 GPIO_NRG_SEL_INV BL0937 or HJL-01 Sel output (0 = Voltage) -> sensor/hlw8012 (sensor hlw8012 sel_pin) (inverted)
|
||||
# GPIO13 GPIO_KEY1 Button -> Button (binary_sensor gpio) (inverted)
|
||||
# GPIO14 GPIO_NRG_CF1 BL0937 or HJL-01 CF1 current / voltage -> sensor/hlw8012 (sensor hlw8012 cf1_pin)
|
||||
# GPIO15 GPIO_REL1 Relay -> Relay (switch gpio)
|
||||
substitutions:
|
||||
device: socket-02
|
||||
name: Socket 02
|
||||
@@ -18,18 +29,7 @@ esp8266:
|
||||
|
||||
packages:
|
||||
common: !include common/common_esp8266.yaml
|
||||
|
||||
# BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring)
|
||||
# Pin Name Function
|
||||
# GPIO15 GPIO_REL1 Relay
|
||||
# GPIO13 GPIO_KEY1 Button
|
||||
# GPIO00 GPIO_LED1_INV Red LED (inverted), Power Status
|
||||
# GPIO02 GPIO_LEDLNK_INV Blue LED (inverted), Link Status
|
||||
# GPIO05 GPIO_HJL_CF BL0937 or HJL-01 CF power
|
||||
# GPIO12 GPIO_NRG_SEL_INV BL0937 or HJL-01 Sel output (0 = Voltage)
|
||||
# GPIO14 GPIO_NRG_CF1 BL0937 or HJL-01 CF1 current / voltage
|
||||
# GPIO01 GPIO_USER RXD, Optional sensor
|
||||
# GPIO03 GPIO_USER TXD, Optional sensor
|
||||
mdns_ipv6: !include common/esp8266_mdns_ipv6.yaml
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
@@ -122,8 +122,7 @@ switch:
|
||||
on_turn_off:
|
||||
- light.turn_off: light_red
|
||||
|
||||
time:
|
||||
- !include common/time/homeassistant.yaml
|
||||
# time:
|
||||
# - platform: homeassistant
|
||||
# timezone: Europe/Bratislava
|
||||
# on_time:
|
||||
|
||||
+14
-14
@@ -1,4 +1,15 @@
|
||||
---
|
||||
# Hardware: BlitzWolf BW-SHP2/SHP6
|
||||
# Pinout:
|
||||
# GPIO0 GPIO_LED1_INV Red LED (inverted), Power Status -> led_red (output gpio)
|
||||
# GPIO1 GPIO_USER RXD, Optional sensor
|
||||
# GPIO2 GPIO_LEDLNK_INV Blue LED (inverted), Link Status -> led_blue (output gpio)
|
||||
# GPIO3 GPIO_USER TXD, Optional sensor
|
||||
# GPIO5 GPIO_HJL_CF BL0937 or HJL-01 CF power -> sensor/hlw8012 (sensor hlw8012 cf_pin)
|
||||
# GPIO12 GPIO_NRG_SEL_INV BL0937 or HJL-01 Sel output (0 = Voltage) -> sensor/hlw8012 (sensor hlw8012 sel_pin) (inverted)
|
||||
# GPIO13 GPIO_KEY1 Button -> Button (binary_sensor gpio) (inverted)
|
||||
# GPIO14 GPIO_NRG_CF1 BL0937 or HJL-01 CF1 current / voltage -> sensor/hlw8012 (sensor hlw8012 cf1_pin)
|
||||
# GPIO15 GPIO_REL1 Relay -> Relay (switch gpio)
|
||||
substitutions:
|
||||
device: socket-03
|
||||
name: Socket 03
|
||||
@@ -19,23 +30,13 @@ esphome:
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
|
||||
esp8266:
|
||||
board: esp01_1m
|
||||
|
||||
packages:
|
||||
common: !include common/common_esp8266.yaml
|
||||
|
||||
# BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring)
|
||||
# Pin Name Function
|
||||
# GPIO15 GPIO_REL1 Relay
|
||||
# GPIO13 GPIO_KEY1 Button
|
||||
# GPIO00 GPIO_LED1_INV Red LED (inverted), Power Status
|
||||
# GPIO02 GPIO_LEDLNK_INV Blue LED (inverted), Link Status
|
||||
# GPIO05 GPIO_HJL_CF BL0937 or HJL-01 CF power
|
||||
# GPIO12 GPIO_NRG_SEL_INV BL0937 or HJL-01 Sel output (0 = Voltage)
|
||||
# GPIO14 GPIO_NRG_CF1 BL0937 or HJL-01 CF1 current / voltage
|
||||
# GPIO01 GPIO_USER RXD, Optional sensor
|
||||
# GPIO03 GPIO_USER TXD, Optional sensor
|
||||
mdns_ipv6: !include common/esp8266_mdns_ipv6.yaml
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
@@ -144,8 +145,7 @@ switch:
|
||||
# number: GPIO00
|
||||
# inverted: True
|
||||
|
||||
time:
|
||||
- !include common/time/homeassistant.yaml
|
||||
# time:
|
||||
# - platform: homeassistant
|
||||
# timezone: Europe/Bratislava
|
||||
# on_time:
|
||||
|
||||
@@ -0,0 +1,943 @@
|
||||
# Hardware: M5Stack CoreS3 + Stack-chan kit
|
||||
# Pinout:
|
||||
# GPIO0 -> i2s_bus (i2s_audio i2s_mclk_pin)
|
||||
# GPIO2 -> Camera (esp32_camera)
|
||||
# GPIO3 -> display_tft (display mipi_spi cs_pin)
|
||||
# GPIO5 -> ir_tx (remote_transmitter)
|
||||
# GPIO6 -> uart_servo (uart tx_pin)
|
||||
# GPIO7 -> uart_servo (uart rx_pin)
|
||||
# GPIO10 -> ir_rx (remote_receiver)
|
||||
# GPIO11 -> i2c_bsp (i2c scl)
|
||||
# GPIO12 -> i2c_bsp (i2c sda)
|
||||
# GPIO13 -> speaker_id (speaker i2s_audio i2s_dout_pin)
|
||||
# GPIO14 -> stackchan_microphone (microphone i2s_audio i2s_din_pin)
|
||||
# GPIO15 -> Camera (esp32_camera data_pins)
|
||||
# GPIO16 -> Camera (esp32_camera data_pins)
|
||||
# GPIO33 -> i2s_bus (i2s_audio i2s_lrclk_pin)
|
||||
# GPIO34 -> i2s_bus (i2s_audio i2s_bclk_pin)
|
||||
# GPIO35 -> display_tft (display mipi_spi dc_pin)
|
||||
# GPIO36 -> spi_bus (spi clk_pin)
|
||||
# GPIO37 -> spi_bus (spi mosi_pin)
|
||||
# GPIO38 -> Camera (esp32_camera href_pin)
|
||||
# GPIO39 -> Camera (esp32_camera data_pins)
|
||||
# GPIO40 -> Camera (esp32_camera data_pins)
|
||||
# GPIO41 -> Camera (esp32_camera data_pins)
|
||||
# GPIO42 -> Camera (esp32_camera data_pins)
|
||||
# GPIO45 -> Camera (esp32_camera pixel_clock_pin)
|
||||
# GPIO46 -> Camera (esp32_camera vsync_pin)
|
||||
# GPIO47 -> Camera (esp32_camera data_pins)
|
||||
# GPIO48 -> Camera (esp32_camera data_pins)
|
||||
# I2C:
|
||||
# 0x23 sensor ltr_als_ps (sensor ltr_als_ps)
|
||||
# 0x34 axp2101_pmu (axp2101)
|
||||
# 0x36 aw88298_dac (audio_dac aw88298)
|
||||
# 0x38 touch_lcd (touchscreen ft63x6)
|
||||
# 0x40 es7210_adc (audio_adc es7210)
|
||||
# 0x41 sensor ina226 (sensor ina226)
|
||||
# 0x51 rtc_time (time bm8563)
|
||||
# 0x58 aw9523b_hub (aw9523b)
|
||||
# 0x68 touch_hub (si12t)
|
||||
# 0x6F m5ioe1_hub (m5ioe1)
|
||||
substitutions:
|
||||
device: stackchan
|
||||
name: StackChan
|
||||
area: DEV
|
||||
comment: StackChan
|
||||
|
||||
esphome:
|
||||
name: mcu-${device}
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
on_boot:
|
||||
- priority: -100
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: light_bar
|
||||
brightness: 20%
|
||||
red: 0%
|
||||
green: 0%
|
||||
blue: 100%
|
||||
- component.update: display_tft
|
||||
|
||||
esp32:
|
||||
variant: esp32s3
|
||||
flash_size: 16MB
|
||||
flash_frequency: 40MHz # the IDF 5.5.5 bootloader reads garbage from this flash (mfr 0x46) at 80MHz
|
||||
toolchain: esp-idf
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
type: esp-idf
|
||||
|
||||
psram:
|
||||
mode: quad
|
||||
speed: 80MHz
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
external_components:
|
||||
- source: github://m5stack/esphome-yaml/components
|
||||
components: [axp2101, aw88298, aw9523b, si12t, m5ioe1, ftservo, scs9009]
|
||||
refresh: 0s
|
||||
- source: github://randybb/esphome-components
|
||||
components: [audio_spectrum]
|
||||
refresh: 1h
|
||||
|
||||
globals:
|
||||
- id: in_love
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: show_status
|
||||
type: bool
|
||||
initial_value: 'true'
|
||||
- id: mouth_open
|
||||
type: float
|
||||
initial_value: '0.0'
|
||||
- id: eyes_open
|
||||
type: bool
|
||||
initial_value: 'true'
|
||||
- id: speaking
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: listening
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: gaze_x
|
||||
type: int
|
||||
initial_value: '0'
|
||||
- id: gaze_y
|
||||
type: int
|
||||
initial_value: '0'
|
||||
|
||||
i2c:
|
||||
- id: i2c_bsp
|
||||
sda: 12
|
||||
scl: 11
|
||||
frequency: 100kHz
|
||||
|
||||
axp2101:
|
||||
id: axp2101_pmu
|
||||
i2c_id: i2c_bsp
|
||||
|
||||
aw9523b:
|
||||
id: aw9523b_hub
|
||||
i2c_id: i2c_bsp
|
||||
p0_drive_mode: PUSH_PULL
|
||||
|
||||
m5ioe1:
|
||||
id: m5ioe1_hub
|
||||
i2c_id: i2c_bsp
|
||||
reset: true
|
||||
|
||||
output:
|
||||
- platform: axp2101
|
||||
id: lcd_backlight_output
|
||||
type: range
|
||||
channel: DLDO1
|
||||
min_voltage: 2600
|
||||
max_voltage: 3300
|
||||
- platform: axp2101
|
||||
channel: ALDO1
|
||||
voltage: 1800
|
||||
- platform: axp2101
|
||||
channel: ALDO2
|
||||
voltage: 3300
|
||||
- platform: axp2101
|
||||
channel: ALDO3
|
||||
voltage: 3300
|
||||
- platform: axp2101
|
||||
channel: ALDO4
|
||||
voltage: 3300
|
||||
- platform: axp2101
|
||||
channel: BLDO1
|
||||
voltage: 2800
|
||||
- platform: axp2101
|
||||
channel: BLDO2
|
||||
voltage: 1500
|
||||
- platform: axp2101
|
||||
channel: DCDC1
|
||||
voltage: 3300
|
||||
- platform: axp2101
|
||||
channel: DCDC4
|
||||
voltage: 1800
|
||||
- platform: axp2101
|
||||
channel: DCDC3
|
||||
voltage: 3300
|
||||
- platform: axp2101
|
||||
channel: DCDC5
|
||||
voltage: 3300
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
id: boost_en
|
||||
name: Boost
|
||||
entity_category: config
|
||||
pin:
|
||||
aw9523b_id: aw9523b_hub
|
||||
number: 15
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
- platform: gpio
|
||||
id: usb_otg_en
|
||||
name: USB OTG
|
||||
entity_category: config
|
||||
pin:
|
||||
aw9523b_id: aw9523b_hub
|
||||
number: 5
|
||||
restore_mode: RESTORE_DEFAULT_OFF
|
||||
- platform: gpio
|
||||
id: bus_out_en
|
||||
name: BUS Out
|
||||
entity_category: config
|
||||
pin:
|
||||
aw9523b_id: aw9523b_hub
|
||||
number: 1
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
- platform: gpio
|
||||
id: light_bar_en
|
||||
internal: true
|
||||
pin:
|
||||
m5ioe1_id: m5ioe1_hub
|
||||
number: 13
|
||||
mode:
|
||||
output: true
|
||||
pullup: true
|
||||
restore_mode: ALWAYS_ON
|
||||
- platform: gpio
|
||||
id: aw_rst
|
||||
internal: true
|
||||
pin:
|
||||
aw9523b_id: aw9523b_hub
|
||||
number: 2
|
||||
restore_mode: ALWAYS_ON
|
||||
- platform: gpio
|
||||
id: cam_rst
|
||||
internal: true
|
||||
pin:
|
||||
aw9523b_id: aw9523b_hub
|
||||
number: 8
|
||||
restore_mode: ALWAYS_ON
|
||||
- platform: gpio
|
||||
id: servo_power
|
||||
internal: true
|
||||
pin:
|
||||
m5ioe1_id: m5ioe1_hub
|
||||
number: 0
|
||||
mode:
|
||||
output: true
|
||||
pullup: true
|
||||
restore_mode: ALWAYS_ON
|
||||
|
||||
i2s_audio:
|
||||
- id: i2s_bus
|
||||
i2s_lrclk_pin: 33
|
||||
i2s_bclk_pin: 34
|
||||
i2s_mclk_pin:
|
||||
number: 0
|
||||
ignore_strapping_warning: true
|
||||
|
||||
audio_adc:
|
||||
- platform: es7210
|
||||
id: es7210_adc
|
||||
i2c_id: i2c_bsp
|
||||
bits_per_sample: 16bit
|
||||
sample_rate: 16000
|
||||
mic_gain: 36
|
||||
|
||||
audio_dac:
|
||||
- platform: aw88298
|
||||
id: aw88298_dac
|
||||
i2c_id: i2c_bsp
|
||||
sample_rate: 48000
|
||||
|
||||
microphone:
|
||||
- platform: i2s_audio
|
||||
id: stackchan_microphone
|
||||
i2s_audio_id: i2s_bus
|
||||
i2s_din_pin: 14
|
||||
adc_type: external
|
||||
sample_rate: 16000
|
||||
bits_per_sample: 16bit
|
||||
|
||||
speaker:
|
||||
- platform: i2s_audio
|
||||
id: speaker_id
|
||||
i2s_audio_id: i2s_bus
|
||||
dac_type: external
|
||||
i2s_dout_pin: 13
|
||||
audio_dac: aw88298_dac
|
||||
sample_rate: 48000
|
||||
buffer_duration: 100ms
|
||||
# pass-through that computes the spectrum shown on the display while music plays
|
||||
- platform: audio_spectrum
|
||||
id: spectrum
|
||||
output_speaker: speaker_id
|
||||
bands: 20
|
||||
- platform: mixer
|
||||
id: mixer_speaker_id
|
||||
output_speaker: spectrum
|
||||
num_channels: 1
|
||||
source_speakers:
|
||||
- id: announcement_spk_mixer_input
|
||||
- id: media_spk_mixer_input
|
||||
- platform: resampler
|
||||
id: media_spk_resampling_input
|
||||
output_speaker: media_spk_mixer_input
|
||||
- platform: resampler
|
||||
id: announcement_spk_resampling_input
|
||||
output_speaker: announcement_spk_mixer_input
|
||||
|
||||
media_player:
|
||||
- platform: speaker
|
||||
id: media_out
|
||||
name: Player
|
||||
buffer_size: 14000
|
||||
# AW88298 maps volume linearly to -96..0 dB, the lower half is inaudible
|
||||
volume_min: 0.5
|
||||
volume_max: 1.0
|
||||
media_pipeline:
|
||||
speaker: media_spk_resampling_input
|
||||
format: NONE
|
||||
announcement_pipeline:
|
||||
speaker: announcement_spk_resampling_input
|
||||
format: NONE
|
||||
on_announcement:
|
||||
- script.execute: release_i2s_bus
|
||||
on_play:
|
||||
- script.execute: release_i2s_bus
|
||||
on_idle:
|
||||
- if:
|
||||
condition:
|
||||
not:
|
||||
voice_assistant.is_running:
|
||||
then:
|
||||
- micro_wake_word.start:
|
||||
|
||||
micro_wake_word:
|
||||
id: mww
|
||||
microphone: stackchan_microphone
|
||||
models:
|
||||
- model: okay_nabu
|
||||
on_wake_word_detected:
|
||||
- voice_assistant.start:
|
||||
wake_word: !lambda return wake_word;
|
||||
|
||||
voice_assistant:
|
||||
id: va
|
||||
microphone: stackchan_microphone
|
||||
media_player: media_out
|
||||
micro_wake_word: mww
|
||||
noise_suppression_level: 2
|
||||
auto_gain: 31dBFS
|
||||
on_client_connected:
|
||||
- micro_wake_word.start:
|
||||
- delay: 2s
|
||||
- lambda: id(show_status) = false;
|
||||
- component.update: display_tft
|
||||
on_client_disconnected:
|
||||
- micro_wake_word.stop:
|
||||
- lambda: id(show_status) = true;
|
||||
- component.update: display_tft
|
||||
on_listening:
|
||||
- lambda: |-
|
||||
id(listening) = true;
|
||||
id(gaze_x) = 0;
|
||||
id(gaze_y) = 0;
|
||||
- component.update: display_tft
|
||||
- light.turn_on: {id: light_bar, brightness: 60%, red: 0%, green: 60%, blue: 100%}
|
||||
on_stt_end:
|
||||
- lambda: id(listening) = false;
|
||||
- component.update: display_tft
|
||||
- light.turn_on: {id: light_bar, brightness: 40%, red: 80%, green: 50%, blue: 0%}
|
||||
on_tts_start:
|
||||
- lambda: id(speaking) = true;
|
||||
- light.turn_on: {id: light_bar, brightness: 60%, red: 0%, green: 100%, blue: 20%}
|
||||
on_end:
|
||||
- wait_until:
|
||||
not:
|
||||
media_player.is_announcing: media_out
|
||||
- lambda: |-
|
||||
id(speaking) = false;
|
||||
id(listening) = false;
|
||||
id(mouth_open) = 0.0f;
|
||||
- component.update: display_tft
|
||||
- light.turn_on: {id: light_bar, brightness: 20%, red: 0%, green: 0%, blue: 100%}
|
||||
- wait_until:
|
||||
not: voice_assistant.is_running
|
||||
- micro_wake_word.start:
|
||||
on_error:
|
||||
- light.turn_on: {id: light_bar, brightness: 60%, red: 100%, green: 0%, blue: 0%}
|
||||
|
||||
script:
|
||||
# short tap on the head starts the assistant
|
||||
- id: head_tap
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
not: voice_assistant.is_running
|
||||
then:
|
||||
- voice_assistant.start:
|
||||
# stroking the head shows the in-love face for a while
|
||||
- id: head_stroke
|
||||
mode: restart
|
||||
then:
|
||||
- lambda: id(in_love) = true;
|
||||
- component.update: display_tft
|
||||
- delay: 5s
|
||||
- lambda: id(in_love) = false;
|
||||
- component.update: display_tft
|
||||
# mic and speaker share one I2S bus - the mic must be released before playback
|
||||
- id: release_i2s_bus
|
||||
mode: single
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
microphone.is_capturing:
|
||||
then:
|
||||
- micro_wake_word.stop:
|
||||
# stop VA only while it is listening (e.g. continued conversation), not during its own response
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(listening);
|
||||
then:
|
||||
- voice_assistant.stop:
|
||||
- wait_until:
|
||||
condition:
|
||||
not:
|
||||
microphone.is_capturing:
|
||||
timeout: 2s
|
||||
|
||||
interval:
|
||||
- interval: 80ms # spectrum analyzer frame rate while music plays
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- media_player.is_playing: media_out
|
||||
- not: voice_assistant.is_running
|
||||
then:
|
||||
- component.update: display_tft
|
||||
# head touch gestures: a touch episode ends after 250 ms without contact;
|
||||
# tap = short episode, stroke = longer episode that reaches the pads one after another
|
||||
- interval: 50ms
|
||||
then:
|
||||
- lambda: |-
|
||||
static bool active = false;
|
||||
static uint32_t start = 0, last_on = 0;
|
||||
static uint32_t first_on[3];
|
||||
const bool on[3] = {id(head_touch_1).state != "No touch", id(head_touch_2).state != "No touch",
|
||||
id(head_touch_3).state != "No touch"};
|
||||
const uint32_t now = millis();
|
||||
|
||||
if (on[0] || on[1] || on[2]) {
|
||||
if (!active) {
|
||||
active = true;
|
||||
start = now;
|
||||
first_on[0] = first_on[1] = first_on[2] = 0;
|
||||
}
|
||||
for (int i = 0; i < 3; i++)
|
||||
if (on[i] && first_on[i] == 0)
|
||||
first_on[i] = now;
|
||||
last_on = now;
|
||||
return;
|
||||
}
|
||||
if (!active || now - last_on < 250)
|
||||
return;
|
||||
active = false;
|
||||
|
||||
const uint32_t duration = last_on - start;
|
||||
int pads = 0;
|
||||
uint32_t earliest = UINT32_MAX, latest = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (first_on[i] == 0)
|
||||
continue;
|
||||
pads++;
|
||||
earliest = std::min(earliest, first_on[i]);
|
||||
latest = std::max(latest, first_on[i]);
|
||||
}
|
||||
const uint32_t spread = latest - earliest;
|
||||
ESP_LOGD("head_touch", "duration %u ms, pads %d, spread %u ms, order 1:%d 2:%d 3:%d", (unsigned) duration,
|
||||
pads, (unsigned) spread, first_on[0] ? (int) (first_on[0] - earliest) : -1,
|
||||
first_on[1] ? (int) (first_on[1] - earliest) : -1, first_on[2] ? (int) (first_on[2] - earliest) : -1);
|
||||
|
||||
if (pads >= 2 && spread >= 150 && duration >= 400) {
|
||||
id(head_stroke).execute();
|
||||
} else if (duration <= 600) {
|
||||
id(head_tap).execute();
|
||||
}
|
||||
- interval: 1s # status screen refresh until Home Assistant connects
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(show_status);
|
||||
then:
|
||||
- component.update: display_tft
|
||||
- interval: 90ms
|
||||
then:
|
||||
- lambda: |-
|
||||
if (id(speaking)) {
|
||||
id(mouth_open) = 0.15f + (esp_random() % 85) / 100.0f;
|
||||
id(display_tft).update();
|
||||
} else if (id(mouth_open) > 0) {
|
||||
id(mouth_open) = 0;
|
||||
id(display_tft).update();
|
||||
}
|
||||
- interval: 3s # blinking
|
||||
then:
|
||||
- delay: !lambda return esp_random() % 3000;
|
||||
- lambda: id(eyes_open) = false;
|
||||
- component.update: display_tft
|
||||
- delay: 150ms
|
||||
- lambda: id(eyes_open) = true;
|
||||
- component.update: display_tft
|
||||
- interval: 7s # looking around while idle
|
||||
then:
|
||||
- lambda: |-
|
||||
if (!id(speaking) && !id(listening)) {
|
||||
id(gaze_x) = (int) (esp_random() % 21) - 10;
|
||||
id(gaze_y) = (int) (esp_random() % 11) - 5;
|
||||
id(display_tft).update();
|
||||
}
|
||||
|
||||
spi:
|
||||
- id: spi_bus
|
||||
clk_pin: 36
|
||||
mosi_pin: 37
|
||||
|
||||
image:
|
||||
- platform: file
|
||||
file: assets/images/made-for-esphome-white-on-black.svg
|
||||
id: esphome_logo
|
||||
resize: 230x68
|
||||
type: RGB565
|
||||
transparency: alpha_channel
|
||||
- platform: file
|
||||
file: mdi:ear-hearing
|
||||
id: icon_listening
|
||||
resize: 28x28
|
||||
type: BINARY
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_title
|
||||
size: 24
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_status
|
||||
size: 16
|
||||
|
||||
color:
|
||||
- id: status_ok
|
||||
red: 30%
|
||||
green: 85%
|
||||
blue: 40%
|
||||
- id: status_wait
|
||||
red: 100%
|
||||
green: 70%
|
||||
blue: 0%
|
||||
- id: love_red
|
||||
red: 100%
|
||||
green: 25%
|
||||
blue: 45%
|
||||
- id: status_low
|
||||
red: 100%
|
||||
green: 20%
|
||||
blue: 20%
|
||||
- id: status_dim
|
||||
red: 55%
|
||||
green: 55%
|
||||
blue: 55%
|
||||
- id: face_fg
|
||||
red: 100%
|
||||
green: 100%
|
||||
blue: 100%
|
||||
- id: face_bg
|
||||
red: 0%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
|
||||
display:
|
||||
- platform: mipi_spi
|
||||
id: display_tft
|
||||
model: M5CORE
|
||||
dc_pin: 35
|
||||
reset_pin:
|
||||
aw9523b_id: aw9523b_hub
|
||||
number: 9
|
||||
cs_pin:
|
||||
number: 3
|
||||
ignore_strapping_warning: true
|
||||
data_rate: 40MHz
|
||||
invert_colors: true
|
||||
update_interval: never
|
||||
auto_clear_enabled: false
|
||||
lambda: |-
|
||||
static bool first_draw = true;
|
||||
static bool dot_shown = false;
|
||||
static bool bar_drawn = false;
|
||||
static int last_bars = -1, last_batt = -2;
|
||||
static bool last_charging = false;
|
||||
|
||||
const bool wifi_ok = wifi::global_wifi_component->is_connected();
|
||||
const int8_t rssi = wifi_ok ? wifi::global_wifi_component->wifi_rssi() : -127;
|
||||
const int wifi_bars = !wifi_ok ? 0 : rssi > -55 ? 4 : rssi > -65 ? 3 : rssi > -75 ? 2 : 1;
|
||||
const int batt = std::isnan(id(battery_level).state) ? -1 : (int) roundf(id(battery_level).state);
|
||||
const bool charging = id(battery_charging).state;
|
||||
|
||||
// status screen until Home Assistant connects
|
||||
if (id(show_status)) {
|
||||
first_draw = true;
|
||||
it.fill(id(face_bg));
|
||||
it.image(160, 46, id(esphome_logo), ImageAlign::CENTER);
|
||||
it.print(160, 95, id(font_title), id(face_fg), TextAlign::CENTER, "${name}");
|
||||
|
||||
if (wifi_ok) {
|
||||
it.printf(20, 114, id(font_status), id(status_ok), "WiFi: connected (%d dBm)", rssi);
|
||||
// one global IPv6 (skip link-local) and one IPv4 address
|
||||
int y = 133;
|
||||
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
for (bool v6 : {true, false}) {
|
||||
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||
if (!ip.is_set() || ip.is_ip6() != v6)
|
||||
continue;
|
||||
ip.str_to(buf);
|
||||
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||
continue;
|
||||
it.print(30, y, id(font_status), id(status_dim), buf);
|
||||
y += 19;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
it.print(20, 114, id(font_status), id(status_wait), "WiFi: connecting...");
|
||||
}
|
||||
|
||||
const bool api_ok = api::global_api_server->is_connected();
|
||||
it.print(20, 172, id(font_status), api_ok ? id(status_ok) : id(status_wait),
|
||||
api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...");
|
||||
|
||||
if (batt >= 0) {
|
||||
it.printf(20, 191, id(font_status), batt <= 20 && !charging ? id(status_low) : id(status_dim),
|
||||
"Battery: %d%%%s", batt, charging ? " (charging)" : "");
|
||||
}
|
||||
it.printf(20, 212, id(font_status), id(status_dim), "ESPHome %s", ESPHOME_VERSION);
|
||||
it.printf(300, 212, id(font_status), id(status_dim), TextAlign::TOP_RIGHT, "up %us",
|
||||
(unsigned) (millis() / 1000));
|
||||
return;
|
||||
}
|
||||
|
||||
// stack-chan face: 2 eyes + mouth, 320x240
|
||||
// the whole screen is cleared only once, then just the face areas, so that only the
|
||||
// changed region is sent over SPI instead of the whole 150 kB buffer
|
||||
const bool love = id(in_love);
|
||||
const bool music = id(media_out).state == media_player::MEDIA_PLAYER_STATE_PLAYING && !id(va).is_running();
|
||||
// switching between face, in-love face and spectrum repaints everything
|
||||
static int last_mode = -1;
|
||||
const int mode = music ? 2 : love ? 1 : 0;
|
||||
if (mode != last_mode) {
|
||||
last_mode = mode;
|
||||
first_draw = true;
|
||||
}
|
||||
|
||||
// the in-love face is drawn outside the partially cleared areas, so it always repaints everything
|
||||
static bool spectrum_reset = true;
|
||||
if (first_draw || love) {
|
||||
it.fill(id(face_bg));
|
||||
first_draw = false;
|
||||
bar_drawn = false;
|
||||
spectrum_reset = true;
|
||||
} else if (!music) {
|
||||
it.filled_rectangle(70, 78, 181, 31, id(face_bg)); // eyes including gaze range
|
||||
it.filled_rectangle(105, 130, 111, 38, id(face_bg)); // mouth
|
||||
if (dot_shown && !id(listening)) {
|
||||
it.filled_rectangle(254, 2, 28, 28, id(face_bg));
|
||||
}
|
||||
}
|
||||
dot_shown = id(listening);
|
||||
|
||||
// status bar (battery top-left, wifi top-right), redrawn only when something changes
|
||||
if (!bar_drawn || wifi_bars != last_bars || batt != last_batt || charging != last_charging) {
|
||||
bar_drawn = true;
|
||||
last_bars = wifi_bars;
|
||||
last_batt = batt;
|
||||
last_charging = charging;
|
||||
it.filled_rectangle(4, 4, 90, 22, id(face_bg));
|
||||
it.filled_rectangle(288, 4, 28, 22, id(face_bg));
|
||||
|
||||
// battery: outline, terminal nub and fill proportional to the charge
|
||||
if (batt >= 0) {
|
||||
const Color c = charging ? id(status_ok) : batt <= 20 ? id(status_low) : id(status_dim);
|
||||
it.rectangle(8, 8, 26, 13, c);
|
||||
it.filled_rectangle(34, 12, 2, 5, c);
|
||||
it.filled_rectangle(10, 10, 22 * batt / 100, 9, c);
|
||||
it.printf(40, 6, id(font_status), c, "%d%%", batt);
|
||||
}
|
||||
|
||||
// wifi: 4 bars, unused ones dimmed; red when disconnected
|
||||
for (int i = 0; i < 4; i++) {
|
||||
const int h = 4 + 3 * i;
|
||||
const Color c = !wifi_ok ? id(status_low) : i < wifi_bars ? id(status_dim) : Color(40, 40, 40);
|
||||
it.filled_rectangle(290 + i * 6, 21 - h, 4, h, c);
|
||||
}
|
||||
}
|
||||
// Winamp style spectrum analyzer instead of the face while music plays.
|
||||
// Only the segments that changed since the last frame are drawn or cleared, which keeps both the
|
||||
// pixel work and the SPI transfer small.
|
||||
if (music) {
|
||||
static float peak[32];
|
||||
static uint32_t peak_time[32];
|
||||
static int prev_segs[32], prev_cap[32];
|
||||
const uint32_t now = millis();
|
||||
const int bands = std::min<int>(id(spectrum).get_bands(), 32);
|
||||
const int x0 = 12, x1 = 308, top = 40, bottom = 228;
|
||||
const int slot = (x1 - x0) / bands, bar_w = slot - 3;
|
||||
const int seg_h = 5; // 4 px LED segment + 1 px gap
|
||||
const int max_segs = (bottom - top) / seg_h;
|
||||
if (spectrum_reset) {
|
||||
spectrum_reset = false;
|
||||
for (int b = 0; b < 32; b++) {
|
||||
peak[b] = 0.0f;
|
||||
prev_segs[b] = 0;
|
||||
prev_cap[b] = -1;
|
||||
}
|
||||
}
|
||||
auto seg_y = [&](int s) { return bottom - (s + 1) * seg_h + 1; };
|
||||
for (int b = 0; b < bands; b++) {
|
||||
const float level = id(spectrum).get_level(b);
|
||||
const int x = x0 + b * slot + 1;
|
||||
const int segs = std::min(max_segs, (int) (level * max_segs));
|
||||
|
||||
// peak caps hold for a moment, then fall slowly
|
||||
if (level >= peak[b]) {
|
||||
peak[b] = level;
|
||||
peak_time[b] = now;
|
||||
} else if (now - peak_time[b] > 400) {
|
||||
peak[b] = std::max(0.0f, peak[b] - 0.03f);
|
||||
}
|
||||
const int cap = peak[b] > 0.0f ? std::min(max_segs - 1, std::max(segs, (int) (peak[b] * max_segs))) : -1;
|
||||
|
||||
// the old cap is cleared first; bar segments drawn below repaint anything it overlapped
|
||||
if (prev_cap[b] >= 0 && prev_cap[b] != cap)
|
||||
it.filled_rectangle(x, seg_y(prev_cap[b]), bar_w, seg_h - 1, id(face_bg));
|
||||
|
||||
if (segs > prev_segs[b]) {
|
||||
for (int s = prev_segs[b]; s < segs; s++) {
|
||||
// green at the bottom, through yellow to red at the top
|
||||
const float f = float(s) / float(max_segs - 1);
|
||||
const Color c = f < 0.5f ? Color((uint8_t) (510 * f), 230, 0) : Color(255, (uint8_t) (460 * (1.0f - f)), 0);
|
||||
it.filled_rectangle(x, seg_y(s), bar_w, seg_h - 1, c);
|
||||
}
|
||||
} else if (segs < prev_segs[b]) {
|
||||
it.filled_rectangle(x, seg_y(prev_segs[b] - 1), bar_w, (prev_segs[b] - segs) * seg_h, id(face_bg));
|
||||
}
|
||||
prev_segs[b] = segs;
|
||||
|
||||
if (cap >= segs && cap >= 0 && (cap != prev_cap[b] || cap == segs)) {
|
||||
const int cy = seg_y(cap) + seg_h - 3;
|
||||
it.filled_rectangle(x, cy, bar_w, 2, id(status_dim));
|
||||
}
|
||||
prev_cap[b] = cap;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const int ex = id(gaze_x), ey = id(gaze_y);
|
||||
const int eye_y = 93 + ey, eye_r = 8;
|
||||
const int lx = 90 + ex, rx = 230 + ex;
|
||||
|
||||
if (love) {
|
||||
// heart eyes, blush and a wide smile
|
||||
for (int cx : {lx, rx}) {
|
||||
it.filled_circle(cx - 6, eye_y - 3, 7, id(love_red));
|
||||
it.filled_circle(cx + 6, eye_y - 3, 7, id(love_red));
|
||||
it.filled_triangle(cx - 13, eye_y - 1, cx + 13, eye_y - 1, cx, eye_y + 14, id(love_red));
|
||||
}
|
||||
it.filled_circle(60 + ex, 130 + ey, 12, Color(90, 20, 40));
|
||||
it.filled_circle(260 + ex, 130 + ey, 12, Color(90, 20, 40));
|
||||
if (!id(speaking)) {
|
||||
it.filled_circle(160 + ex / 2, 132 + ey / 2, 30, id(face_fg));
|
||||
it.filled_circle(160 + ex / 2, 122 + ey / 2, 30, id(face_bg));
|
||||
}
|
||||
} else if (id(eyes_open)) {
|
||||
it.filled_circle(lx, eye_y, eye_r, id(face_fg));
|
||||
it.filled_circle(rx, eye_y, eye_r, id(face_fg));
|
||||
} else {
|
||||
it.filled_rectangle(lx - eye_r, eye_y - 2, eye_r * 2, 4, id(face_fg));
|
||||
it.filled_rectangle(rx - eye_r, eye_y - 2, eye_r * 2, 4, id(face_fg));
|
||||
}
|
||||
|
||||
const float m = id(mouth_open);
|
||||
if (!love || id(speaking)) {
|
||||
const int mw = 90 - (int) (40 * m);
|
||||
const int mh = 4 + (int) (26 * m);
|
||||
it.filled_rectangle(160 - mw / 2 + ex / 2, 148 - mh / 2 + ey / 2, mw, mh, id(face_fg));
|
||||
}
|
||||
|
||||
if (id(listening)) {
|
||||
it.image(254, 2, id(icon_listening), id(face_fg), id(face_bg)); // left of the wifi bars
|
||||
}
|
||||
|
||||
touchscreen:
|
||||
- platform: ft63x6
|
||||
id: touch_lcd
|
||||
i2c_id: i2c_bsp
|
||||
reset_pin:
|
||||
aw9523b_id: aw9523b_hub
|
||||
number: 0
|
||||
on_touch:
|
||||
- voice_assistant.start:
|
||||
|
||||
esp32_camera:
|
||||
id: camera_id
|
||||
name: Camera
|
||||
i2c_id: i2c_bsp
|
||||
setup_priority: -200 # after power rails and IO expanders
|
||||
pixel_format: RGB565
|
||||
max_framerate: 15 fps
|
||||
frame_buffer_count: 1
|
||||
# GC0308 has no JPEG, convert RGB565 -> JPEG for HA (as in BSP); runs in the main loop, so no periodic frames
|
||||
jpeg_quality: 12
|
||||
idle_framerate: 0 fps
|
||||
vsync_pin:
|
||||
number: 46
|
||||
ignore_strapping_warning: true
|
||||
href_pin: 38
|
||||
external_clock:
|
||||
pin: 2
|
||||
frequency: 20MHz
|
||||
pixel_clock_pin:
|
||||
number: 45
|
||||
ignore_strapping_warning: true
|
||||
data_pins: [39, 40, 41, 42, 15, 16, 48, 47]
|
||||
resolution: 320x240
|
||||
frame_buffer_location: PSRAM
|
||||
|
||||
sensor:
|
||||
- platform: axp2101
|
||||
id: axp2101_sensor
|
||||
battery_level:
|
||||
id: battery_level
|
||||
name: Battery Level
|
||||
filters:
|
||||
- delta: 1.0
|
||||
on_value:
|
||||
- component.update: display_tft
|
||||
battery_charging:
|
||||
id: battery_charging
|
||||
name: Battery Charging
|
||||
on_state:
|
||||
- component.update: display_tft
|
||||
- platform: ltr_als_ps
|
||||
i2c_id: i2c_bsp
|
||||
address: 0x23
|
||||
type: ALS_PS
|
||||
ambient_light:
|
||||
id: ambient_light
|
||||
name: Ambient Light
|
||||
ps_counts:
|
||||
id: proximity_counts
|
||||
name: Proximity Counts
|
||||
- platform: ina226
|
||||
i2c_id: i2c_bsp
|
||||
address: 0x41
|
||||
shunt_resistance: 0.01 ohm
|
||||
max_current: 3.2A
|
||||
current:
|
||||
id: ina226_current
|
||||
name: Current
|
||||
power:
|
||||
id: ina226_power
|
||||
name: Power
|
||||
bus_voltage:
|
||||
id: ina226_bus_voltage
|
||||
name: Bus Voltage
|
||||
|
||||
si12t:
|
||||
id: touch_hub
|
||||
i2c_id: i2c_bsp
|
||||
# report low-strength touches too, not only medium/high
|
||||
intr_level: OUTPUT_ALL
|
||||
|
||||
text_sensor:
|
||||
- platform: si12t
|
||||
id: head_touch_1
|
||||
name: Head Touch 1
|
||||
channel: CH_1
|
||||
# lower level = lower touch threshold: SENS_LEVEL_1 = 0.7 % capacitance change (default level 3 is 1.2 %),
|
||||
# SENS_LEVEL_0 made neighbouring pads trigger together
|
||||
sensitivity: SENS_LEVEL_1
|
||||
extend_threshold: false
|
||||
update_interval: 100ms
|
||||
internal: true
|
||||
- platform: si12t
|
||||
id: head_touch_2
|
||||
name: Head Touch 2
|
||||
channel: CH_2
|
||||
sensitivity: SENS_LEVEL_1
|
||||
extend_threshold: false
|
||||
update_interval: 100ms
|
||||
internal: true
|
||||
- platform: si12t
|
||||
id: head_touch_3
|
||||
name: Head Touch 3
|
||||
channel: CH_3
|
||||
sensitivity: SENS_LEVEL_1
|
||||
extend_threshold: false
|
||||
update_interval: 100ms
|
||||
internal: true
|
||||
|
||||
uart:
|
||||
- id: uart_servo
|
||||
tx_pin: 6
|
||||
rx_pin: 7
|
||||
baud_rate: 1000000
|
||||
|
||||
scs9009:
|
||||
|
||||
ftservo:
|
||||
- platform: scs9009
|
||||
id: x_servo
|
||||
address: 1
|
||||
- platform: scs9009
|
||||
id: y_servo
|
||||
address: 2
|
||||
|
||||
number:
|
||||
- platform: ftservo
|
||||
ftservo_id: x_servo
|
||||
angle:
|
||||
id: servo_x_angle
|
||||
name: Servo X Angle
|
||||
min_value: -164
|
||||
max_value: 164
|
||||
use_raw_angle: false
|
||||
angle_offset: 164
|
||||
step: 5
|
||||
|
||||
light:
|
||||
- platform: m5ioe1
|
||||
id: light_bar
|
||||
name: Light Bar
|
||||
num_led: 12
|
||||
- platform: monochromatic
|
||||
id: lcd_backlight
|
||||
name: LCD Backlight
|
||||
output: lcd_backlight_output
|
||||
restore_mode: ALWAYS_ON
|
||||
|
||||
remote_transmitter:
|
||||
id: ir_tx
|
||||
pin: 5
|
||||
carrier_duty_percent: 50%
|
||||
non_blocking: true
|
||||
|
||||
remote_receiver:
|
||||
id: ir_rx
|
||||
pin: 10
|
||||
|
||||
time:
|
||||
- platform: bm8563
|
||||
id: rtc_time
|
||||
i2c_id: i2c_bsp
|
||||
update_interval: never
|
||||
@@ -1,4 +1,8 @@
|
||||
---
|
||||
# Hardware: Sonoff Mini
|
||||
# Pinout:
|
||||
# GPIO12 -> relay (output gpio)
|
||||
# GPIO13 -> led_blue (output gpio)
|
||||
substitutions:
|
||||
device: staircase-light
|
||||
name: Staircase
|
||||
@@ -16,6 +20,7 @@ esp8266:
|
||||
|
||||
packages:
|
||||
common: !include common/common_esp8266.yaml
|
||||
mdns_ipv6: !include common/esp8266_mdns_ipv6.yaml
|
||||
|
||||
# wifi:
|
||||
# use_address: mcu-mini-04.local
|
||||
@@ -49,8 +54,18 @@ binary_sensor:
|
||||
entity_id: binary_sensor.zigbee_staircase_pir_occupancy
|
||||
on_state:
|
||||
if:
|
||||
condition:
|
||||
binary_sensor.is_off: motion
|
||||
condition: &no_motion
|
||||
and:
|
||||
- binary_sensor.is_off: motion
|
||||
- binary_sensor.is_off: motion_2
|
||||
then:
|
||||
- light.turn_off: light_relay
|
||||
- platform: homeassistant
|
||||
id: motion_2
|
||||
entity_id: binary_sensor.zigbee_staircase_2_pir_occupancy
|
||||
on_state:
|
||||
if:
|
||||
condition: *no_motion
|
||||
then:
|
||||
- light.turn_off: light_relay
|
||||
|
||||
|
||||
+243
-79
@@ -1,4 +1,14 @@
|
||||
---
|
||||
# Hardware: M5Stack M5Stamp C3
|
||||
# Pinout:
|
||||
# GPIO0 Grove SCL -> touchscreen/xpt2046 (touchscreen xpt2046 cs_pin)
|
||||
# GPIO1 Grove SDA -> touchscreen/xpt2046 (touchscreen xpt2046 interrupt_pin)
|
||||
# GPIO4 -> spi (spi clk_pin)
|
||||
# GPIO5 -> spi (spi mosi_pin)
|
||||
# GPIO6 -> spi (spi miso_pin)
|
||||
# GPIO7 -> main_display (display mipi_spi cs_pin)
|
||||
# GPIO8 -> main_display (display mipi_spi dc_pin)
|
||||
# GPIO10 -> main_display (display mipi_spi reset_pin)
|
||||
substitutions:
|
||||
device: tft
|
||||
name: TFT
|
||||
@@ -10,104 +20,258 @@ esphome:
|
||||
friendly_name: ${name}
|
||||
area: ${area}
|
||||
comment: ${comment}
|
||||
platformio_options:
|
||||
board_build.flash_mode: dio
|
||||
|
||||
on_boot:
|
||||
- priority: -100
|
||||
then:
|
||||
- lvgl.page.show: status_page
|
||||
|
||||
esp32:
|
||||
variant: esp32c3
|
||||
framework:
|
||||
advanced:
|
||||
flash_chip: generic
|
||||
type: esp-idf
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
|
||||
logger:
|
||||
logs:
|
||||
component: ERROR
|
||||
|
||||
# GPIO1 UART TX
|
||||
# GPIO3 UART RX
|
||||
# GPIO4 I2C SDA
|
||||
# GPIO5 I2C SCL
|
||||
# GPIO12 DISPLAY DIO
|
||||
# GPIO12 DISPLAY CLOCK
|
||||
# GPIO14 BUZZER
|
||||
# GPIO16 RGB
|
||||
api:
|
||||
# status page until Home Assistant connects (log viewers don't subscribe to states)
|
||||
on_client_connected:
|
||||
- delay: 2s
|
||||
- if:
|
||||
condition:
|
||||
api.connected:
|
||||
state_subscription_only: true
|
||||
then:
|
||||
- lvgl.page.show: main_page
|
||||
on_client_disconnected:
|
||||
- if:
|
||||
condition:
|
||||
not:
|
||||
api.connected:
|
||||
state_subscription_only: true
|
||||
then:
|
||||
- lvgl.page.show: status_page
|
||||
|
||||
spi:
|
||||
clk_pin: 4
|
||||
mosi_pin: 5
|
||||
miso_pin: 6
|
||||
|
||||
color:
|
||||
- id: my_green
|
||||
hex: 00ff00
|
||||
- id: my_magenta
|
||||
hex: ff00ff
|
||||
- id: my_red
|
||||
hex: ff0000
|
||||
- id: my_white
|
||||
hex: ffffff
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_default
|
||||
size: 20
|
||||
- file: "gfonts://B612"
|
||||
id: b612_24
|
||||
size: 24
|
||||
- file: "gfonts://B612"
|
||||
id: b612_16
|
||||
size: 16
|
||||
- file: "gfonts://B612"
|
||||
id: b612_12
|
||||
size: 12
|
||||
- file: "gfonts://B612 Mono"
|
||||
id: b612_24_mono
|
||||
size: 24
|
||||
- file: "gfonts://B612 Mono"
|
||||
id: b612_16_mono
|
||||
size: 16
|
||||
- file: "gfonts://B612 Mono"
|
||||
id: b612_12_mono
|
||||
size: 12
|
||||
- file: "gfonts://Roboto"
|
||||
id: roboto_32
|
||||
size: 32
|
||||
- file: "gfonts://Roboto"
|
||||
id: roboto_24
|
||||
size: 24
|
||||
- file: "gfonts://Roboto"
|
||||
id: roboto_16
|
||||
size: 16
|
||||
- file: "gfonts://Roboto"
|
||||
id: roboto_12
|
||||
size: 12
|
||||
- file: "gfonts://Roboto Mono"
|
||||
id: roboto_32_mono
|
||||
size: 32
|
||||
- file: "gfonts://Roboto Mono"
|
||||
id: roboto_24_mono
|
||||
size: 24
|
||||
- file: "gfonts://Roboto Mono"
|
||||
id: roboto_16_mono
|
||||
size: 16
|
||||
- file: "gfonts://Roboto Mono"
|
||||
id: roboto_12_mono
|
||||
size: 12
|
||||
|
||||
|
||||
display:
|
||||
- platform: mipi_spi
|
||||
id: main_display
|
||||
model: ILI9341
|
||||
cs_pin: 7
|
||||
dc_pin: 8
|
||||
reset_pin: 10
|
||||
invert_colors: false
|
||||
lambda: |-
|
||||
it.fill(Color::BLACK);
|
||||
it.print(0, 0, id(b612_12), id(my_white), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
||||
it.print(0, 14, id(b612_12), id(my_green), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
||||
it.print(0, 28, id(b612_12), id(my_magenta), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
||||
it.print(0, 42, id(b612_12), id(my_red), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
||||
data_rate: 40MHz
|
||||
update_interval: never
|
||||
auto_clear_enabled: false
|
||||
|
||||
touchscreen:
|
||||
- platform: xpt2046
|
||||
display: main_display
|
||||
cs_pin: 0
|
||||
interrupt_pin: 1
|
||||
transform:
|
||||
mirror_x: true
|
||||
calibration:
|
||||
x_min: 330
|
||||
x_max: 3620
|
||||
y_min: 230
|
||||
y_max: 3680
|
||||
|
||||
script:
|
||||
- id: update_status
|
||||
then:
|
||||
- lambda: |-
|
||||
// same colors as the other status screens: ok green, waiting orange, details gray
|
||||
static const uint32_t OK = 0x4DD966, WAIT = 0xFFB300;
|
||||
auto set = [](lv_obj_t *label, const std::string &text, uint32_t color) {
|
||||
lv_label_set_text(label, text.c_str());
|
||||
lv_obj_set_style_text_color(label, lv_color_hex(color), LV_PART_MAIN);
|
||||
};
|
||||
|
||||
const bool wifi_ok = wifi::global_wifi_component->is_connected();
|
||||
set(id(status_wifi),
|
||||
wifi_ok ? str_sprintf("WiFi: connected (%d dBm)", wifi::global_wifi_component->wifi_rssi())
|
||||
: std::string("WiFi: connecting..."),
|
||||
wifi_ok ? OK : WAIT);
|
||||
|
||||
// IPv6 first (skip link-local), then IPv4
|
||||
std::string ips;
|
||||
if (wifi_ok) {
|
||||
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
for (bool v6 : {true, false}) {
|
||||
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||
if (!ip.is_set() || ip.is_ip6() != v6)
|
||||
continue;
|
||||
ip.str_to(buf);
|
||||
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||
continue;
|
||||
if (!ips.empty())
|
||||
ips += "\n";
|
||||
ips += buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
lv_label_set_text(id(status_ips), ips.c_str());
|
||||
if (ips.empty())
|
||||
lv_obj_add_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||
else
|
||||
lv_obj_remove_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
const bool api_ok = api::global_api_server->is_connected();
|
||||
set(id(status_ha), api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...",
|
||||
api_ok ? OK : WAIT);
|
||||
|
||||
const uint32_t up = millis() / 1000;
|
||||
lv_label_set_text(id(status_footer),
|
||||
str_sprintf("ESPHome %s\nup %ud %02u:%02u:%02u", ESPHOME_VERSION, up / 86400,
|
||||
(up / 3600) % 24, (up / 60) % 60, up % 60).c_str());
|
||||
interval:
|
||||
- interval: 1s
|
||||
then:
|
||||
- lvgl.label.update:
|
||||
id: clock_label
|
||||
text: !lambda |-
|
||||
auto now = id(ha_time).now();
|
||||
if (!now.is_valid())
|
||||
return std::string("--:--");
|
||||
return now.strftime("%H:%M");
|
||||
- if:
|
||||
condition:
|
||||
lvgl.page.is_showing: status_page
|
||||
then:
|
||||
- script.execute: update_status
|
||||
|
||||
image:
|
||||
- platform: file
|
||||
file: assets/images/made-for-esphome-white-on-black.svg
|
||||
id: esphome_logo
|
||||
resize: 220x65
|
||||
type: RGB565
|
||||
transparency: alpha_channel
|
||||
|
||||
font:
|
||||
- file: "gfonts://Roboto@500"
|
||||
id: font_clock
|
||||
size: 80
|
||||
glyphs: "0123456789:-"
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_title
|
||||
size: 24
|
||||
- file: "gfonts://Roboto"
|
||||
id: font_status
|
||||
size: 14
|
||||
|
||||
lvgl:
|
||||
log_level: WARN
|
||||
color_depth: 16
|
||||
bg_color: 0x000000
|
||||
text_color: 0xFFFFFF
|
||||
theme:
|
||||
button:
|
||||
bg_color: 0x2B2F36
|
||||
radius: 12
|
||||
checked:
|
||||
bg_color: 0xE08A00
|
||||
buttonmatrix:
|
||||
bg_opa: TRANSP
|
||||
border_width: 0
|
||||
items:
|
||||
bg_color: 0x2B2F36
|
||||
text_color: 0xFFFFFF
|
||||
radius: 12
|
||||
checked:
|
||||
bg_color: 0xE08A00
|
||||
top_layer:
|
||||
widgets:
|
||||
# same navigation bar as on the other LVGL displays
|
||||
- buttonmatrix:
|
||||
id: nav_bar
|
||||
align: bottom_mid
|
||||
width: 100%
|
||||
height: 48
|
||||
pad_all: 4
|
||||
pad_column: 6
|
||||
items:
|
||||
text_font: montserrat_20
|
||||
rows:
|
||||
- buttons:
|
||||
- id: page_prev
|
||||
text: ""
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.previous:
|
||||
- id: page_home
|
||||
text: ""
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.show: main_page
|
||||
- id: page_next
|
||||
text: ""
|
||||
on_press:
|
||||
then:
|
||||
lvgl.page.next:
|
||||
pages:
|
||||
# 1: clock
|
||||
- id: main_page
|
||||
widgets:
|
||||
- label:
|
||||
id: clock_label
|
||||
align: CENTER
|
||||
y: -24
|
||||
text_font: font_clock
|
||||
text: "--:--"
|
||||
|
||||
# 2: status (shown until Home Assistant connects)
|
||||
- id: status_page
|
||||
on_load:
|
||||
- script.execute: update_status
|
||||
widgets:
|
||||
- image:
|
||||
src: esphome_logo
|
||||
align: TOP_MID
|
||||
y: 8
|
||||
- label:
|
||||
align: TOP_MID
|
||||
y: 78
|
||||
text_font: font_title
|
||||
text: "${name}"
|
||||
- obj:
|
||||
x: 8
|
||||
y: 116
|
||||
width: 224
|
||||
height: SIZE_CONTENT
|
||||
bg_opa: TRANSP
|
||||
border_width: 0
|
||||
pad_all: 0
|
||||
scrollbar_mode: "OFF"
|
||||
layout:
|
||||
type: flex
|
||||
flex_flow: column
|
||||
pad_row: 6
|
||||
widgets:
|
||||
- label:
|
||||
id: status_wifi
|
||||
text_font: font_status
|
||||
text: ""
|
||||
- label:
|
||||
id: status_ips
|
||||
text_font: font_status
|
||||
text_color: 0x8C8C8C
|
||||
text: ""
|
||||
- label:
|
||||
id: status_ha
|
||||
text_font: font_status
|
||||
text: ""
|
||||
- label:
|
||||
id: status_footer
|
||||
text_font: font_status
|
||||
text_color: 0x8C8C8C
|
||||
text: ""
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
# Hardware: LilyGO T-PCIE
|
||||
substitutions:
|
||||
device: tracker
|
||||
name: Tracker
|
||||
|
||||
+19
-10
@@ -1,4 +1,22 @@
|
||||
---
|
||||
# Hardware: Huacanxing H801
|
||||
# Pinout:
|
||||
# # H801 LED module
|
||||
# Function Pin Channel
|
||||
# R GPIO15 PWM1
|
||||
# G GPIO13 PWM2
|
||||
# B GPIO12 PWM3
|
||||
# W1 GPIO14 PWM4
|
||||
# W2 GPIO04 PWM5
|
||||
# LED D1 R GPIO05
|
||||
# LED D2 G GPIO01
|
||||
# Used:
|
||||
# GPIO4 pwm_warm_white (output esp8266_pwm)
|
||||
# GPIO5 status_led_id (light status_led)
|
||||
# GPIO12 pwm_blue (output esp8266_pwm)
|
||||
# GPIO13 pwm_green (output esp8266_pwm)
|
||||
# GPIO14 pwm_cold_white (output esp8266_pwm)
|
||||
# GPIO15 pwm_red (output esp8266_pwm)
|
||||
substitutions:
|
||||
device: tv-light
|
||||
name: TV Light
|
||||
@@ -16,16 +34,7 @@ esp8266:
|
||||
|
||||
packages:
|
||||
common: !include common/common_esp8266.yaml
|
||||
|
||||
## H801 LED module
|
||||
# Function Pin Channel
|
||||
# R GPIO15 PWM1
|
||||
# G GPIO13 PWM2
|
||||
# B GPIO12 PWM3
|
||||
# W1 GPIO14 PWM4
|
||||
# W2 GPIO04 PWM5
|
||||
# LED D1 R GPIO05
|
||||
# LED D2 G GPIO01
|
||||
mdns_ipv6: !include common/esp8266_mdns_ipv6.yaml
|
||||
|
||||
light:
|
||||
- platform: status_led
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite
|
||||
# Pinout: see common/vehicle_base.yaml
|
||||
substitutions:
|
||||
device: vehicle-honda-pcx125
|
||||
name: Vehicle Honda PCX125
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite
|
||||
# Pinout: see common/vehicle_base.yaml
|
||||
substitutions:
|
||||
device: vehicle-suzuki-dl650
|
||||
name: Vehicle Suzuki DL650
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite
|
||||
# Pinout: see common/vehicle_base.yaml
|
||||
substitutions:
|
||||
device: vehicle-toyota-auris-ts
|
||||
name: Vehicle Toyota Auris TS
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite
|
||||
# Pinout: see common/vehicle_base.yaml
|
||||
substitutions:
|
||||
device: vehicle-toyota-yaris-cross
|
||||
name: Vehicle Toyota Yaris Cross
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
---
|
||||
# Hardware: Sonoff Basic R2
|
||||
# Pinout:
|
||||
# GPIO0 BUTTON -> Button (binary_sensor gpio) (inverted)
|
||||
# GPIO1 TX PIN
|
||||
# GPIO3 RX PIN
|
||||
# GPIO12 RELAY -> Relay (switch gpio)
|
||||
# GPIO13 LED1 -> led (output gpio)
|
||||
substitutions:
|
||||
device: waterproof-socket
|
||||
name: Waterproof Socket
|
||||
@@ -16,13 +23,7 @@ esp8266:
|
||||
|
||||
packages:
|
||||
common: !include common/common_esp8266.yaml
|
||||
|
||||
# Sonoff Basic R2
|
||||
# GPIO00 - BUTTON
|
||||
# GPIO12 - RELAY
|
||||
# GPIO13 - LED1
|
||||
# GPIO03 - RX PIN
|
||||
# GPIO01 - TX PIN
|
||||
mdns_ipv6: !include common/esp8266_mdns_ipv6.yaml
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
|
||||
+14
-3
@@ -1,4 +1,15 @@
|
||||
---
|
||||
# Hardware: M5Stack ATOM Lite + ATOM Hub Switch
|
||||
# Pinout:
|
||||
# GPIO21 -> i2c (i2c scl)
|
||||
# GPIO23 -> modbus_uart (uart tx_pin)
|
||||
# GPIO25 -> i2c (i2c sda)
|
||||
# GPIO26 Grove SDA
|
||||
# GPIO27 NEO (ATOM RGB LED) -> atom_led (status LED, common/status_led_atom.yaml)
|
||||
# GPIO32 Grove SCL
|
||||
# GPIO33 -> modbus_uart (uart rx_pin)
|
||||
# I2C:
|
||||
# 0x27 relay_module (pca9554)
|
||||
substitutions:
|
||||
device: well
|
||||
name: Well
|
||||
@@ -30,10 +41,13 @@ esp32:
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
flash_chip: gd
|
||||
sram1_as_iram: true
|
||||
|
||||
packages:
|
||||
common: !include common/common.yaml
|
||||
ipv6_only: !include common/ipv6_only.yaml
|
||||
status_led: !include common/status_led_atom.yaml
|
||||
|
||||
api:
|
||||
actions:
|
||||
@@ -357,6 +371,3 @@ valve:
|
||||
- switch.turn_on: irrigation_relay_8
|
||||
close_action:
|
||||
- switch.turn_off: irrigation_relay_8
|
||||
|
||||
time:
|
||||
- !include common/time/homeassistant.yaml
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
{
|
||||
"none": "Žiady",
|
||||
"unknown": "Neznámy",
|
||||
"preset_mode": "Prednastavený režim",
|
||||
"swing_mode": "Režim oscilácie",
|
||||
"fan_mode": "Režim ventilátora",
|
||||
"activity": "Aktivita",
|
||||
"away": "Preč",
|
||||
"boost": "Boost",
|
||||
"comfort": "Komfort",
|
||||
"eco": "Eko",
|
||||
"home": "Domov",
|
||||
"sleep": "Spánok",
|
||||
"cool": "Chladenie",
|
||||
"cooling": "Chladí",
|
||||
"dry": "Sušenie",
|
||||
"drying": "Suší",
|
||||
"fan": "Ventilátor",
|
||||
"heat": "Kúrenie",
|
||||
"heating": "Kúri",
|
||||
"heat_cool": "Kúrenie/Chladenie",
|
||||
"idle": "V kľude",
|
||||
"auto": "Automatický",
|
||||
"fan_only": "Len ventilátor",
|
||||
"on": "Zapnuté",
|
||||
"off": "Vypnuté",
|
||||
"currently": "Aktuálne",
|
||||
"state": "Stav",
|
||||
"action": "Akcia",
|
||||
"lock": "Zamknúť",
|
||||
"unlock": "Odomknúť",
|
||||
"paused": "Pozastavené",
|
||||
"active": "Aktívne",
|
||||
"activate": "Aktivovať",
|
||||
"press": "Stlačiť",
|
||||
"run": "Spustiť",
|
||||
"speed": "Rýchlosť",
|
||||
"brightness": "Jas",
|
||||
"color": "Farba",
|
||||
"color_temp": "Teplota farby",
|
||||
"above_horizon": "Nad horizontom",
|
||||
"below_horizon": "Pod horizontom",
|
||||
"position": "Pozícia",
|
||||
"tilt_position": "Náklon",
|
||||
"start": "Štart",
|
||||
"pause": "Pauza",
|
||||
"cancel": "Zrušiť",
|
||||
"finish": "Dokončiť",
|
||||
"arm_home": "Arm (doma)",
|
||||
"arm_away": "Arm (mimo domov)",
|
||||
"arm_night": "Arm (noc)",
|
||||
"arm_vacation": "Arm (dovolenka)",
|
||||
"arm_custom_bypass": "Arm (bypass)",
|
||||
"disarm": "Vypnúť zabezpečenie",
|
||||
"disarming": "Vypínanie zabezpečenia",
|
||||
"disarmed": "Zabezpečenie vypnuté",
|
||||
"arming": "Zabezpečuje sa",
|
||||
"pending": "Čekanie",
|
||||
"triggered": "Poplach",
|
||||
"armed_away": "Zabezpečené (mimo domov)",
|
||||
"armed_home": "Zabezpečené (doma)",
|
||||
"armed_night": "Zabezpečené (noc)",
|
||||
"armed_vacation": "Zabezpečené (dovolenka)",
|
||||
"armed_custom_bypass": "Zabezpečené (bypass)",
|
||||
"not_home": "Mimo domov",
|
||||
"start_cleaning": "Začať upratovať",
|
||||
"return_to_base": "Späť na základňu",
|
||||
"docked": "Zaparkované",
|
||||
"turn_on": "Zapnúť",
|
||||
"turn_off": "Vypnúť",
|
||||
"month_january": "Január",
|
||||
"month_jan": "Jan",
|
||||
"month_february": "Február",
|
||||
"month_feb": "Feb",
|
||||
"month_march": "Marec",
|
||||
"month_mar": "Mar",
|
||||
"month_april": "Apríl",
|
||||
"month_apr": "Apr",
|
||||
"month_may": "Máj",
|
||||
"month_june": "Jún",
|
||||
"month_jun": "Jun",
|
||||
"month_july": "Júl",
|
||||
"month_jul": "Jul",
|
||||
"month_august": "August",
|
||||
"month_aug": "Aug",
|
||||
"month_september": "September",
|
||||
"month_sep": "Sep",
|
||||
"month_october": "Október",
|
||||
"month_oct": "Okt",
|
||||
"month_november": "November",
|
||||
"month_nov": "Nov",
|
||||
"month_december": "December",
|
||||
"month_dec": "Dec",
|
||||
"dow_sunday": "Nedela",
|
||||
"dow_sun": "Ne",
|
||||
"dow_monday": "Pondelok",
|
||||
"dow_mon": "Po",
|
||||
"dow_tuesday": "Utorok",
|
||||
"dow_tue": "Ut",
|
||||
"dow_wednesday": "Streda",
|
||||
"dow_wed": "St",
|
||||
"dow_thursday": "Štvrtok",
|
||||
"dow_thu": "Št",
|
||||
"dow_friday": "Piatok",
|
||||
"dow_fri": "Pi",
|
||||
"dow_saturday": "Sobota",
|
||||
"dow_sat": "So"
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
wifi_ssid: "ssid"
|
||||
wifi_password: "password"
|
||||
|
||||
wifi_ssid_ipv6: "ssid"
|
||||
wifi_password_ipv6: "password"
|
||||
|
||||
api_password: "password"
|
||||
api_key: "oSsCFxRUqIJLpT98cQwSwNxYoC7zbUh0YBHczAWRyBI="
|
||||
|
||||
@@ -26,3 +29,9 @@ ble_beacon_toyota_auris_minor: 61958
|
||||
ble_beacon_toyota_yaris_uuid: "00000000-0000-0000-0000-000000000000"
|
||||
ble_beacon_toyota_yaris_honda_major: 10167
|
||||
ble_beacon_toyota_yaris_honda_minor: 61958
|
||||
|
||||
# Thread active dataset (hex TLV) from Home Assistant
|
||||
thread_tlv: "0E080000000000010000"
|
||||
|
||||
# ANT+ network key (8 bytes hex), from thisisant.com
|
||||
ant_plus_network_key: "0000000000000000"
|
||||
Reference in new issue
Block a user