mirror of
https://github.com/esphome/esphome.git
synced 2025-11-03 00:21:56 +00:00
Compare commits
352 Commits
jesserockz
...
2022.4.0b2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
343b9ab455 | ||
|
|
dcb226b202 | ||
|
|
2243021b58 | ||
|
|
d5134e88b1 | ||
|
|
c59adf612f | ||
|
|
a82d8ea0c3 | ||
|
|
ad57faa9a9 | ||
|
|
a9b5e8d036 | ||
|
|
8be704e591 | ||
|
|
b622a8fa58 | ||
|
|
a519e5c475 | ||
|
|
d620b6dd5e | ||
|
|
99335d986e | ||
|
|
7895cd92cd | ||
|
|
8b2c032da6 | ||
|
|
da336247eb | ||
|
|
dabd27d4be | ||
|
|
fdda47db6e | ||
|
|
efa6fd03e5 | ||
|
|
9e3e34acf5 | ||
|
|
a2d0c1bf18 | ||
|
|
7663716ae8 | ||
|
|
c2cacb3478 | ||
|
|
84666b54b9 | ||
|
|
2b91c23bf3 | ||
|
|
3297267a16 | ||
|
|
a9e653724c | ||
|
|
5e79a1f500 | ||
|
|
d4ff98680a | ||
|
|
ba8d255cb4 | ||
|
|
06f4ad922c | ||
|
|
bff06e448b | ||
|
|
d48ffa2913 | ||
|
|
d97c3a7e01 | ||
|
|
0b1161f7ef | ||
|
|
061e1a471d | ||
|
|
a39d874600 | ||
|
|
de96376565 | ||
|
|
c54c20ab3c | ||
|
|
70fafa473b | ||
|
|
2e436eae6b | ||
|
|
fd7e861ff5 | ||
|
|
792108686c | ||
|
|
fa1b5117fd | ||
|
|
b0bd9e0a34 | ||
|
|
05dc97099a | ||
|
|
9de61fcf58 | ||
|
|
7f7175b184 | ||
|
|
cf5c640ae4 | ||
|
|
6b9371d105 | ||
|
|
9a82057303 | ||
|
|
48584e94c4 | ||
|
|
d8024a5928 | ||
|
|
2034ab4f6c | ||
|
|
58b70b42dd | ||
|
|
1496bc1b07 | ||
|
|
bfbf88b2ea | ||
|
|
e621b938e3 | ||
|
|
59e6e798dd | ||
|
|
e5c2dbc7ec | ||
|
|
756f71c382 | ||
|
|
b7535693fa | ||
|
|
06a3505698 | ||
|
|
0372d17a11 | ||
|
|
4525588116 | ||
|
|
68e957c147 | ||
|
|
99f5ed1461 | ||
|
|
59f67796dc | ||
|
|
aafdfa933e | ||
|
|
3208c8ed1e | ||
|
|
6bf733e24e | ||
|
|
65d3e8fbfc | ||
|
|
a29d65d47c | ||
|
|
efa8f0730d | ||
|
|
0af1edefff | ||
|
|
023d26f521 | ||
|
|
5068619f1b | ||
|
|
5b2457af0b | ||
|
|
900b4f1af9 | ||
|
|
4c22a98b0b | ||
|
|
3b8ca80900 | ||
|
|
dc6eff83ea | ||
|
|
38ff66debd | ||
|
|
1d2e0f74ea | ||
|
|
bf60e40d0b | ||
|
|
c9094ca537 | ||
|
|
68b3fd6b8f | ||
|
|
9323b3a248 | ||
|
|
b55e9329d9 | ||
|
|
a5b4105971 | ||
|
|
d1feaa935d | ||
|
|
6919930aaa | ||
|
|
69633826bb | ||
|
|
771162bfb1 | ||
|
|
ba785e29e9 | ||
|
|
2c7b104f4a | ||
|
|
78951c197a | ||
|
|
07c1cf7137 | ||
|
|
d26141151a | ||
|
|
f59dbe4a88 | ||
|
|
8dae7f8225 | ||
|
|
5811389891 | ||
|
|
debcaf6fb7 | ||
|
|
b8d10a62c2 | ||
|
|
d2b209234f | ||
|
|
34c9d8be50 | ||
|
|
ae57ad0c81 | ||
|
|
0c1520dd9c | ||
|
|
d594f43ebd | ||
|
|
125c693e3f | ||
|
|
ad2f857e15 | ||
|
|
e445d6aada | ||
|
|
88fbb0ffbb | ||
|
|
231908fe9f | ||
|
|
f137cc10f4 | ||
|
|
b528f48417 | ||
|
|
ec7a79049a | ||
|
|
6ddad6b299 | ||
|
|
16dc7762f9 | ||
|
|
dc0ed8857f | ||
|
|
bb6b77bd98 | ||
|
|
dcc80f9032 | ||
|
|
dd554bcdf4 | ||
|
|
f376a39e55 | ||
|
|
8dcc9d6b66 | ||
|
|
a576c9f21f | ||
|
|
71a438e2cb | ||
|
|
272d6f2a8b | ||
|
|
5dc776e55f | ||
|
|
72d60f30f7 | ||
|
|
869743a742 | ||
|
|
7b03e07908 | ||
|
|
348f880e15 | ||
|
|
ead597d0fb | ||
|
|
afbf989715 | ||
|
|
01b62a16c3 | ||
|
|
c5eba04517 | ||
|
|
282313ab52 | ||
|
|
d274545e77 | ||
|
|
d3fda37615 | ||
|
|
cbe3092404 | ||
|
|
6dfe3039d0 | ||
|
|
d6009453df | ||
|
|
c81323ef91 | ||
|
|
961c27f1c2 | ||
|
|
fe4a14e6cc | ||
|
|
50848c2f4d | ||
|
|
d32633b3c7 | ||
|
|
b37739eec2 | ||
|
|
28f87dc804 | ||
|
|
41879e41e6 | ||
|
|
fc0a6546a2 | ||
|
|
ffd4280d6c | ||
|
|
db3b955b0f | ||
|
|
5516f65971 | ||
|
|
9471df0a1b | ||
|
|
6d39f64be7 | ||
|
|
b89d0a9a73 | ||
|
|
4bb779d9a5 | ||
|
|
386a5b6362 | ||
|
|
e32a999cd0 | ||
|
|
bfbc6a4bad | ||
|
|
8c9e0e552d | ||
|
|
8aaf9fd83f | ||
|
|
08057720b8 | ||
|
|
bfaa648837 | ||
|
|
d504daef91 | ||
|
|
b8d3ef2f49 | ||
|
|
3bf6320030 | ||
|
|
708b928c73 | ||
|
|
649366ff44 | ||
|
|
e5c9e87fad | ||
|
|
f3d9d707b6 | ||
|
|
090e10730c | ||
|
|
fbc84861c7 | ||
|
|
e763469af8 | ||
|
|
3c0c514e44 | ||
|
|
ed5e2dd332 | ||
|
|
09b7c6f550 | ||
|
|
df315a1f51 | ||
|
|
7ee4bb621c | ||
|
|
24874f4c3c | ||
|
|
c128880033 | ||
|
|
a66e94a0b0 | ||
|
|
56870ed4a8 | ||
|
|
3ac720df47 | ||
|
|
1bc757ad06 | ||
|
|
f72abc6f3d | ||
|
|
5ac88de985 | ||
|
|
0826b367d6 | ||
|
|
329bf861d6 | ||
|
|
9dcd3d18a0 | ||
|
|
db66cd88b6 | ||
|
|
86c205fe43 | ||
|
|
c6414138c7 | ||
|
|
36b355eb82 | ||
|
|
7be9291b13 | ||
|
|
ea9e75039b | ||
|
|
a5fb036011 | ||
|
|
e55506f9db | ||
|
|
50ec1d0445 | ||
|
|
3d5e1d8d91 | ||
|
|
db2128a344 | ||
|
|
21db43db06 | ||
|
|
5009b3029f | ||
|
|
57a029189c | ||
|
|
0cb715bb76 | ||
|
|
7d03823afd | ||
|
|
8e1c9f5042 | ||
|
|
980b7cda8f | ||
|
|
3a72dd5cb6 | ||
|
|
3178243811 | ||
|
|
d30e2f2a4f | ||
|
|
6226dae05c | ||
|
|
9c6a475a6e | ||
|
|
8294d10d5b | ||
|
|
67558bec47 | ||
|
|
84873d4074 | ||
|
|
58a0b28a39 | ||
|
|
b37d3a66cc | ||
|
|
7e495a5e27 | ||
|
|
c41547fd4a | ||
|
|
0d47d41c85 | ||
|
|
41a3a17456 | ||
|
|
cbbafbcca2 | ||
|
|
c75566b374 | ||
|
|
7279f1fcc1 | ||
|
|
d7432f7c10 | ||
|
|
b0a0a153f3 | ||
|
|
024632dbd0 | ||
|
|
0a545a28b9 | ||
|
|
0f2df59998 | ||
|
|
29a7d32f77 | ||
|
|
687a7e9b2f | ||
|
|
09e8782318 | ||
|
|
f2aea02210 | ||
|
|
194f922312 | ||
|
|
fea3c48098 | ||
|
|
c2f57baec2 | ||
|
|
f4a140e126 | ||
|
|
ab506b09fe | ||
|
|
87e1cdeedb | ||
|
|
81a36146ef | ||
|
|
7fa4a68a27 | ||
|
|
f1c5e2ef81 | ||
|
|
b526155cce | ||
|
|
62c3f301e7 | ||
|
|
38cb988809 | ||
|
|
b976ac54c8 | ||
|
|
78026e766f | ||
|
|
b4cd8d21a5 | ||
|
|
7552893311 | ||
|
|
21c896d8f8 | ||
|
|
4b7fe202ec | ||
|
|
9f4519210f | ||
|
|
b0506afa5b | ||
|
|
8cbb379898 | ||
|
|
b226215593 | ||
|
|
19970729a9 | ||
|
|
d2ebfd2833 | ||
|
|
bd782fc828 | ||
|
|
23560e608c | ||
|
|
f1377b560e | ||
|
|
72108684ea | ||
|
|
c6adaaea97 | ||
|
|
91999a38ca | ||
|
|
b34eed125d | ||
|
|
2abe09529a | ||
|
|
9aaaf4dd4b | ||
|
|
cbfbcf7f1b | ||
|
|
c7651dc40d | ||
|
|
eda1c471ad | ||
|
|
c7ef18fbc4 | ||
|
|
901ec918b1 | ||
|
|
6bdae55ee1 | ||
|
|
dfb96e4b7f | ||
|
|
ff2c316b18 | ||
|
|
5be52f71f9 | ||
|
|
42873dd37c | ||
|
|
f93e7d4e3a | ||
|
|
bbcd523967 | ||
|
|
68cbe58d00 | ||
|
|
115bca98f1 | ||
|
|
ed0b34b2fe | ||
|
|
ab34401421 | ||
|
|
eed0c18d65 | ||
|
|
e5a38ce748 | ||
|
|
7d9d9fcf36 | ||
|
|
f0aba6ceb2 | ||
|
|
ab07ee57c6 | ||
|
|
eae3d72a4d | ||
|
|
7b8d826704 | ||
|
|
e7baa42e63 | ||
|
|
2f32833a22 | ||
|
|
f6935a4b4b | ||
|
|
332c9e891b | ||
|
|
b91ee4847f | ||
|
|
625463d871 | ||
|
|
6433a01e07 | ||
|
|
56cc31e8e7 | ||
|
|
3af297aa76 | ||
|
|
996ec59d28 | ||
|
|
95593eeeab | ||
|
|
dad244fb7a | ||
|
|
adb5d27d95 | ||
|
|
8456a8cecb | ||
|
|
b9f66373c1 | ||
|
|
9ac365feef | ||
|
|
43bbd58a44 | ||
|
|
7feffa64f3 | ||
|
|
ea0977abb4 | ||
|
|
4c83dc7c28 | ||
|
|
e10ab1da78 | ||
|
|
1b0e60374b | ||
|
|
3a760fbb44 | ||
|
|
6ef57a2973 | ||
|
|
3e9c7f2e9f | ||
|
|
430598b7a1 | ||
|
|
91611b09b4 | ||
|
|
ecd115851f | ||
|
|
4a1e50fed1 | ||
|
|
d6d037047b | ||
|
|
b5734c2b20 | ||
|
|
723fb7eaac | ||
|
|
63a9acaa19 | ||
|
|
0524f8c677 | ||
|
|
70b62f272e | ||
|
|
f0089b7940 | ||
|
|
4b44280d53 | ||
|
|
f045382d20 | ||
|
|
db3fa1ade7 | ||
|
|
f83950fd75 | ||
|
|
4dd1bf920d | ||
|
|
98755f3621 | ||
|
|
c3a8a044b9 | ||
|
|
15b5ea43a7 | ||
|
|
ec683fc227 | ||
|
|
d4e65eb82a | ||
|
|
10c6601b0a | ||
|
|
73940bc1bd | ||
|
|
9b7fb829f9 | ||
|
|
c51d8c9021 | ||
|
|
d8a6dfe5ce | ||
|
|
5f7cef0b06 | ||
|
|
48ff2ffc68 | ||
|
|
b3b9ccd314 | ||
|
|
e63c7b483b | ||
|
|
f57980b069 | ||
|
|
7006aa0d2a | ||
|
|
8051c1ca99 | ||
|
|
a779592414 | ||
|
|
112215848d |
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -9,4 +9,3 @@ contact_links:
|
||||
- name: Frequently Asked Question
|
||||
url: https://esphome.io/guides/faq.html
|
||||
about: Please view the FAQ for common questions and what to include in a bug report.
|
||||
|
||||
|
||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,4 +1,4 @@
|
||||
# What does this implement/fix?
|
||||
# What does this implement/fix?
|
||||
|
||||
Quick description and explanation of changes
|
||||
|
||||
@@ -35,6 +35,6 @@ Quick description and explanation of changes
|
||||
## Checklist:
|
||||
- [ ] The code change is tested and works locally.
|
||||
- [ ] Tests have been added to verify that the new code works (under `tests/` folder).
|
||||
|
||||
|
||||
If user exposed functionality or configuration variables are added/changed:
|
||||
- [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/ambv/black
|
||||
rev: 22.1.0
|
||||
rev: 22.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
@@ -26,7 +26,7 @@ repos:
|
||||
- --branch=release
|
||||
- --branch=beta
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.31.0
|
||||
rev: v2.31.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py38-plus]
|
||||
|
||||
16
CODEOWNERS
16
CODEOWNERS
@@ -28,6 +28,7 @@ esphome/components/atc_mithermometer/* @ahpohl
|
||||
esphome/components/b_parasite/* @rbaron
|
||||
esphome/components/ballu/* @bazuchan
|
||||
esphome/components/bang_bang/* @OttoWinter
|
||||
esphome/components/bh1750/* @OttoWinter
|
||||
esphome/components/binary_sensor/* @esphome/core
|
||||
esphome/components/bl0940/* @tobias-
|
||||
esphome/components/ble_client/* @buxtronix
|
||||
@@ -43,6 +44,7 @@ esphome/components/climate/* @esphome/core
|
||||
esphome/components/climate_ir/* @glmnet
|
||||
esphome/components/color_temperature/* @jesserockz
|
||||
esphome/components/coolix/* @glmnet
|
||||
esphome/components/copy/* @OttoWinter
|
||||
esphome/components/cover/* @esphome/core
|
||||
esphome/components/cs5460a/* @balrog-kun
|
||||
esphome/components/cse7761/* @berfenger
|
||||
@@ -78,7 +80,9 @@ esphome/components/hbridge/light/* @DotNetDann
|
||||
esphome/components/heatpumpir/* @rob-deutsch
|
||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||
esphome/components/homeassistant/* @OttoWinter
|
||||
esphome/components/honeywellabp/* @RubyBailey
|
||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||
esphome/components/hydreon_rgxx/* @functionpointer
|
||||
esphome/components/i2c/* @esphome/core
|
||||
esphome/components/improv_serial/* @esphome/core
|
||||
esphome/components/ina260/* @MrEditor97
|
||||
@@ -94,6 +98,7 @@ esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
||||
esphome/components/lock/* @esphome/core
|
||||
esphome/components/logger/* @esphome/core
|
||||
esphome/components/ltr390/* @sjtrny
|
||||
esphome/components/max44009/* @berfenger
|
||||
esphome/components/max7219digit/* @rspaargaren
|
||||
esphome/components/max9611/* @mckaymatthew
|
||||
esphome/components/mcp23008/* @jesserockz
|
||||
@@ -105,6 +110,7 @@ esphome/components/mcp23x17_base/* @jesserockz
|
||||
esphome/components/mcp23xxx_base/* @jesserockz
|
||||
esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||
esphome/components/mcp3204/* @rsumner
|
||||
esphome/components/mcp4728/* @berfenger
|
||||
esphome/components/mcp47a1/* @jesserockz
|
||||
esphome/components/mcp9808/* @k7hpn
|
||||
esphome/components/md5/* @esphome/core
|
||||
@@ -121,6 +127,9 @@ esphome/components/modbus_controller/select/* @martgras @stegm
|
||||
esphome/components/modbus_controller/sensor/* @martgras
|
||||
esphome/components/modbus_controller/switch/* @martgras
|
||||
esphome/components/modbus_controller/text_sensor/* @martgras
|
||||
esphome/components/mopeka_ble/* @spbrogan
|
||||
esphome/components/mopeka_pro_check/* @spbrogan
|
||||
esphome/components/mpu6886/* @fabaff
|
||||
esphome/components/network/* @esphome/core
|
||||
esphome/components/nextion/* @senexcrenshaw
|
||||
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
||||
@@ -141,8 +150,9 @@ esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
||||
esphome/components/power_supply/* @esphome/core
|
||||
esphome/components/preferences/* @esphome/core
|
||||
esphome/components/psram/* @esphome/core
|
||||
esphome/components/pulse_meter/* @stevebaxter
|
||||
esphome/components/pulse_meter/* @cstaahl @stevebaxter
|
||||
esphome/components/pvvx_mithermometer/* @pasiz
|
||||
esphome/components/qmp6988/* @andrewpc
|
||||
esphome/components/qr_code/* @wjtje
|
||||
esphome/components/radon_eye_ble/* @jeffeb3
|
||||
esphome/components/radon_eye_rd200/* @jeffeb3
|
||||
@@ -160,13 +170,16 @@ esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||
esphome/components/sdp3x/* @Azimath
|
||||
esphome/components/selec_meter/* @sourabhjaiswal
|
||||
esphome/components/select/* @esphome/core
|
||||
esphome/components/sensirion_common/* @martgras
|
||||
esphome/components/sensor/* @esphome/core
|
||||
esphome/components/sgp40/* @SenexCrenshaw
|
||||
esphome/components/shelly_dimmer/* @edge90 @rnauber
|
||||
esphome/components/sht4x/* @sjtrny
|
||||
esphome/components/shutdown/* @esphome/core @jsuanet
|
||||
esphome/components/sim800l/* @glmnet
|
||||
esphome/components/sm2135/* @BoukeHaarsma23
|
||||
esphome/components/socket/* @esphome/core
|
||||
esphome/components/sonoff_d1/* @anatoly-savchenkov
|
||||
esphome/components/spi/* @esphome/core
|
||||
esphome/components/ssd1322_base/* @kbx81
|
||||
esphome/components/ssd1322_spi/* @kbx81
|
||||
@@ -214,4 +227,5 @@ esphome/components/whirlpool/* @glmnet
|
||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
||||
esphome/components/xiaomi_mhoc303/* @drug123
|
||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
||||
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
||||
esphome/components/xpt2046/* @numo68
|
||||
|
||||
@@ -5,7 +5,7 @@ For a detailed guide, please see https://esphome.io/guides/contributing.html#con
|
||||
Things to note when contributing:
|
||||
|
||||
- Please test your changes :)
|
||||
- If a new feature is added or an existing user-facing feature is changed, you should also
|
||||
- If a new feature is added or an existing user-facing feature is changed, you should also
|
||||
update the [docs](https://github.com/esphome/esphome-docs). See [contributing to esphome-docs](https://esphome.io/guides/contributing.html#contributing-to-esphomedocs)
|
||||
for more information.
|
||||
- Please also update the tests in the `tests/` folder. You can do so by just adding a line in one of the YAML files
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
ARG BASEIMGTYPE=docker
|
||||
|
||||
# https://github.com/hassio-addons/addon-debian-base/releases
|
||||
FROM ghcr.io/hassio-addons/debian-base/amd64:5.2.3 AS base-hassio-amd64
|
||||
FROM ghcr.io/hassio-addons/debian-base/aarch64:5.2.3 AS base-hassio-arm64
|
||||
FROM ghcr.io/hassio-addons/debian-base/armv7:5.2.3 AS base-hassio-armv7
|
||||
FROM ghcr.io/hassio-addons/debian-base/amd64:5.3.0 AS base-hassio-amd64
|
||||
FROM ghcr.io/hassio-addons/debian-base/aarch64:5.3.0 AS base-hassio-arm64
|
||||
FROM ghcr.io/hassio-addons/debian-base/armv7:5.3.0 AS base-hassio-armv7
|
||||
# https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye
|
||||
FROM debian:bullseye-20220125-slim AS base-docker-amd64
|
||||
FROM debian:bullseye-20220125-slim AS base-docker-arm64
|
||||
FROM debian:bullseye-20220125-slim AS base-docker-armv7
|
||||
FROM debian:bullseye-20220328-slim AS base-docker-amd64
|
||||
FROM debian:bullseye-20220328-slim AS base-docker-arm64
|
||||
FROM debian:bullseye-20220328-slim AS base-docker-armv7
|
||||
|
||||
# Use TARGETARCH/TARGETVARIANT defined by docker
|
||||
# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
|
||||
@@ -23,7 +23,7 @@ RUN \
|
||||
# Use pinned versions so that we get updates with build caching
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
python3=3.9.2-3 \
|
||||
python3-pip=20.3.4-4 \
|
||||
python3-pip=20.3.4-4+deb11u1 \
|
||||
python3-setuptools=52.0.0-4 \
|
||||
python3-pil=8.1.2+dfsg-0.3+deb11u1 \
|
||||
python3-cryptography=3.3.2-1 \
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
# Check SSL requirements, if enabled
|
||||
if bashio::config.true 'ssl'; then
|
||||
if ! bashio::config.has_value 'certfile'; then
|
||||
bashio::fatal 'SSL is enabled, but no certfile was specified.'
|
||||
bashio::log.fatal 'SSL is enabled, but no certfile was specified.'
|
||||
bashio::exit.nok
|
||||
fi
|
||||
|
||||
if ! bashio::config.has_value 'keyfile'; then
|
||||
bashio::fatal 'SSL is enabled, but no keyfile was specified'
|
||||
bashio::log.fatal 'SSL is enabled, but no keyfile was specified'
|
||||
bashio::exit.nok
|
||||
fi
|
||||
|
||||
|
||||
@@ -262,21 +262,16 @@ async def repeat_action_to_code(config, action_id, template_arg, args):
|
||||
return var
|
||||
|
||||
|
||||
def validate_wait_until(value):
|
||||
schema = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||
cv.Optional(CONF_TIMEOUT): cv.templatable(
|
||||
cv.positive_time_period_milliseconds
|
||||
),
|
||||
}
|
||||
)
|
||||
if isinstance(value, dict) and CONF_CONDITION in value:
|
||||
return schema(value)
|
||||
return validate_wait_until({CONF_CONDITION: value})
|
||||
_validate_wait_until = cv.maybe_simple_value(
|
||||
{
|
||||
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||
cv.Optional(CONF_TIMEOUT): cv.templatable(cv.positive_time_period_milliseconds),
|
||||
},
|
||||
key=CONF_CONDITION,
|
||||
)
|
||||
|
||||
|
||||
@register_action("wait_until", WaitUntilAction, validate_wait_until)
|
||||
@register_action("wait_until", WaitUntilAction, _validate_wait_until)
|
||||
async def wait_until_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
||||
|
||||
@@ -76,6 +76,8 @@ async def to_code(config):
|
||||
pos = 0
|
||||
for frameIndex in range(frames):
|
||||
image.seek(frameIndex)
|
||||
if CONF_RESIZE in config:
|
||||
image.thumbnail(config[CONF_RESIZE])
|
||||
frame = image.convert("RGB")
|
||||
if CONF_RESIZE in config:
|
||||
frame = frame.resize([width, height])
|
||||
|
||||
@@ -23,7 +23,7 @@ static const char *const TAG = "api.connection";
|
||||
static const int ESP32_CAMERA_STOP_STREAM = 5000;
|
||||
|
||||
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
|
||||
: parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
|
||||
: parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
|
||||
this->proto_write_buffer_.reserve(64);
|
||||
|
||||
#if defined(USE_API_PLAINTEXT)
|
||||
@@ -105,6 +105,7 @@ void APIConnection::loop() {
|
||||
ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str());
|
||||
}
|
||||
} else if (now - this->last_traffic_ > keepalive) {
|
||||
ESP_LOGVV(TAG, "Sending keepalive PING...");
|
||||
this->sent_ping_ = true;
|
||||
this->send_ping_request(PingRequest());
|
||||
}
|
||||
@@ -908,7 +909,7 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
this->last_traffic_ = millis();
|
||||
// Do not set last_traffic_ on send
|
||||
return true;
|
||||
}
|
||||
void APIConnection::on_unauthenticated_access() {
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "esphome/components/socket/socket.h"
|
||||
#include "api_pb2.h"
|
||||
#include "api_pb2_service.h"
|
||||
#include "util.h"
|
||||
#include "list_entities.h"
|
||||
#include "subscribe_state.h"
|
||||
#include "user_services.h"
|
||||
|
||||
@@ -40,8 +40,7 @@ bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { return this->client_->s
|
||||
#endif
|
||||
|
||||
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
|
||||
ListEntitiesIterator::ListEntitiesIterator(APIServer *server, APIConnection *client)
|
||||
: ComponentIterator(server), client_(client) {}
|
||||
ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {}
|
||||
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
|
||||
auto resp = service->encode_list_service_response();
|
||||
return this->client_->send_list_entities_services_response(resp);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/component_iterator.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
@@ -11,7 +11,7 @@ class APIConnection;
|
||||
|
||||
class ListEntitiesIterator : public ComponentIterator {
|
||||
public:
|
||||
ListEntitiesIterator(APIServer *server, APIConnection *client);
|
||||
ListEntitiesIterator(APIConnection *client);
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
|
||||
#endif
|
||||
@@ -60,5 +60,3 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
#include "api_server.h"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "proto.h"
|
||||
#include "util.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
@@ -195,6 +195,20 @@ class ProtoWriteBuffer {
|
||||
this->write((value >> 16) & 0xFF);
|
||||
this->write((value >> 24) & 0xFF);
|
||||
}
|
||||
void encode_fixed64(uint32_t field_id, uint64_t value, bool force = false) {
|
||||
if (value == 0 && !force)
|
||||
return;
|
||||
|
||||
this->encode_field_raw(field_id, 5);
|
||||
this->write((value >> 0) & 0xFF);
|
||||
this->write((value >> 8) & 0xFF);
|
||||
this->write((value >> 16) & 0xFF);
|
||||
this->write((value >> 24) & 0xFF);
|
||||
this->write((value >> 32) & 0xFF);
|
||||
this->write((value >> 40) & 0xFF);
|
||||
this->write((value >> 48) & 0xFF);
|
||||
this->write((value >> 56) & 0xFF);
|
||||
}
|
||||
template<typename T> void encode_enum(uint32_t field_id, T value, bool force = false) {
|
||||
this->encode_uint32(field_id, static_cast<uint32_t>(value), force);
|
||||
}
|
||||
@@ -229,6 +243,15 @@ class ProtoWriteBuffer {
|
||||
}
|
||||
this->encode_uint32(field_id, uvalue, force);
|
||||
}
|
||||
void encode_sint64(uint32_t field_id, int64_t value, bool force = false) {
|
||||
uint64_t uvalue;
|
||||
if (value < 0) {
|
||||
uvalue = ~(value << 1);
|
||||
} else {
|
||||
uvalue = value << 1;
|
||||
}
|
||||
this->encode_uint64(field_id, uvalue, force);
|
||||
}
|
||||
template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) {
|
||||
this->encode_field_raw(field_id, 2);
|
||||
size_t begin = this->buffer_->size();
|
||||
|
||||
@@ -50,8 +50,7 @@ bool InitialStateIterator::on_select(select::Select *select) {
|
||||
#ifdef USE_LOCK
|
||||
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); }
|
||||
#endif
|
||||
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
|
||||
: ComponentIterator(server), client_(client) {}
|
||||
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/component_iterator.h"
|
||||
#include "esphome/core/controller.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
@@ -12,7 +12,7 @@ class APIConnection;
|
||||
|
||||
class InitialStateIterator : public ComponentIterator {
|
||||
public:
|
||||
InitialStateIterator(APIServer *server, APIConnection *client);
|
||||
InitialStateIterator(APIConnection *client);
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
|
||||
#endif
|
||||
@@ -55,5 +55,3 @@ class InitialStateIterator : public ComponentIterator {
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
#include "api_server.h"
|
||||
|
||||
@@ -38,7 +38,7 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
const auto &data = service_data.data;
|
||||
|
||||
const uint8_t protocol_version = data[0] >> 4;
|
||||
if (protocol_version != 1) {
|
||||
if (protocol_version != 1 && protocol_version != 2) {
|
||||
ESP_LOGE(TAG, "Unsupported protocol version: %u", protocol_version);
|
||||
return false;
|
||||
}
|
||||
@@ -57,9 +57,15 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
uint16_t battery_millivolt = data[2] << 8 | data[3];
|
||||
float battery_voltage = battery_millivolt / 1000.0f;
|
||||
|
||||
// Temperature in 1000 * Celsius.
|
||||
uint16_t temp_millicelcius = data[4] << 8 | data[5];
|
||||
float temp_celcius = temp_millicelcius / 1000.0f;
|
||||
// Temperature in 1000 * Celsius (protocol v1) or 100 * Celsius (protocol v2).
|
||||
float temp_celsius;
|
||||
if (protocol_version == 1) {
|
||||
uint16_t temp_millicelsius = data[4] << 8 | data[5];
|
||||
temp_celsius = temp_millicelsius / 1000.0f;
|
||||
} else {
|
||||
int16_t temp_centicelsius = data[4] << 8 | data[5];
|
||||
temp_celsius = temp_centicelsius / 100.0f;
|
||||
}
|
||||
|
||||
// Relative air humidity in the range [0, 2^16).
|
||||
uint16_t humidity = data[6] << 8 | data[7];
|
||||
@@ -76,7 +82,7 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
battery_voltage_->publish_state(battery_voltage);
|
||||
}
|
||||
if (temperature_ != nullptr) {
|
||||
temperature_->publish_state(temp_celcius);
|
||||
temperature_->publish_state(temp_celsius);
|
||||
}
|
||||
if (humidity_ != nullptr) {
|
||||
humidity_->publish_state(humidity_percent);
|
||||
|
||||
@@ -9,18 +9,109 @@ static const char *const TAG = "bh1750.sensor";
|
||||
static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001;
|
||||
static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000; // last 3 bits
|
||||
static const uint8_t BH1750_COMMAND_MT_REG_LO = 0b01100000; // last 5 bits
|
||||
static const uint8_t BH1750_COMMAND_ONE_TIME_L = 0b00100011;
|
||||
static const uint8_t BH1750_COMMAND_ONE_TIME_H = 0b00100000;
|
||||
static const uint8_t BH1750_COMMAND_ONE_TIME_H2 = 0b00100001;
|
||||
|
||||
/*
|
||||
bh1750 properties:
|
||||
|
||||
L-resolution mode:
|
||||
- resolution 4lx (@ mtreg=69)
|
||||
- measurement time: typ=16ms, max=24ms, scaled by MTreg value divided by 69
|
||||
- formula: counts / 1.2 * (69 / MTreg) lx
|
||||
H-resolution mode:
|
||||
- resolution 1lx (@ mtreg=69)
|
||||
- measurement time: typ=120ms, max=180ms, scaled by MTreg value divided by 69
|
||||
- formula: counts / 1.2 * (69 / MTreg) lx
|
||||
H-resolution mode2:
|
||||
- resolution 0.5lx (@ mtreg=69)
|
||||
- measurement time: typ=120ms, max=180ms, scaled by MTreg value divided by 69
|
||||
- formula: counts / 1.2 * (69 / MTreg) / 2 lx
|
||||
|
||||
MTreg:
|
||||
- min=31, default=69, max=254
|
||||
|
||||
-> only reason to use l-resolution is faster, but offers no higher range
|
||||
-> below ~7000lx, makes sense to use H-resolution2 @ MTreg=254
|
||||
-> try to maximize MTreg to get lowest noise level
|
||||
*/
|
||||
|
||||
void BH1750Sensor::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up BH1750 '%s'...", this->name_.c_str());
|
||||
if (!this->write_bytes(BH1750_COMMAND_POWER_ON, nullptr, 0)) {
|
||||
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
|
||||
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t mtreg_hi = (this->measurement_duration_ >> 5) & 0b111;
|
||||
uint8_t mtreg_lo = (this->measurement_duration_ >> 0) & 0b11111;
|
||||
this->write_bytes(BH1750_COMMAND_MT_REG_HI | mtreg_hi, nullptr, 0);
|
||||
this->write_bytes(BH1750_COMMAND_MT_REG_LO | mtreg_lo, nullptr, 0);
|
||||
void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f) {
|
||||
// turn on (after one-shot sensor automatically powers down)
|
||||
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
|
||||
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Turning on BH1750 failed");
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (active_mtreg_ != mtreg) {
|
||||
// set mtreg
|
||||
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111);
|
||||
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111);
|
||||
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Setting measurement time for BH1750 failed");
|
||||
active_mtreg_ = 0;
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
active_mtreg_ = mtreg;
|
||||
}
|
||||
|
||||
uint8_t cmd;
|
||||
uint16_t meas_time;
|
||||
switch (mode) {
|
||||
case BH1750_MODE_L:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_L;
|
||||
meas_time = 24 * mtreg / 69;
|
||||
break;
|
||||
case BH1750_MODE_H:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_H;
|
||||
meas_time = 180 * mtreg / 69;
|
||||
break;
|
||||
case BH1750_MODE_H2:
|
||||
cmd = BH1750_COMMAND_ONE_TIME_H2;
|
||||
meas_time = 180 * mtreg / 69;
|
||||
break;
|
||||
default:
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
if (this->write(&cmd, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Starting measurement for BH1750 failed");
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
|
||||
// probably not needed, but adjust for rounding
|
||||
meas_time++;
|
||||
|
||||
this->set_timeout("read", meas_time, [this, mode, mtreg, f]() {
|
||||
uint16_t raw_value;
|
||||
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Reading BH1750 data failed");
|
||||
f(NAN);
|
||||
return;
|
||||
}
|
||||
raw_value = i2c::i2ctohs(raw_value);
|
||||
|
||||
float lx = float(raw_value) / 1.2f;
|
||||
lx *= 69.0f / mtreg;
|
||||
if (mode == BH1750_MODE_H2)
|
||||
lx /= 2.0f;
|
||||
|
||||
f(lx);
|
||||
});
|
||||
}
|
||||
|
||||
void BH1750Sensor::dump_config() {
|
||||
@@ -30,64 +121,49 @@ void BH1750Sensor::dump_config() {
|
||||
ESP_LOGE(TAG, "Communication with BH1750 failed!");
|
||||
}
|
||||
|
||||
const char *resolution_s;
|
||||
switch (this->resolution_) {
|
||||
case BH1750_RESOLUTION_0P5_LX:
|
||||
resolution_s = "0.5";
|
||||
break;
|
||||
case BH1750_RESOLUTION_1P0_LX:
|
||||
resolution_s = "1";
|
||||
break;
|
||||
case BH1750_RESOLUTION_4P0_LX:
|
||||
resolution_s = "4";
|
||||
break;
|
||||
default:
|
||||
resolution_s = "Unknown";
|
||||
break;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Resolution: %s", resolution_s);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void BH1750Sensor::update() {
|
||||
if (!this->write_bytes(this->resolution_, nullptr, 0))
|
||||
return;
|
||||
// first do a quick measurement in L-mode with full range
|
||||
// to find right range
|
||||
this->read_lx_(BH1750_MODE_L, 31, [this](float val) {
|
||||
if (std::isnan(val)) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t wait = 0;
|
||||
// use max conversion times
|
||||
switch (this->resolution_) {
|
||||
case BH1750_RESOLUTION_0P5_LX:
|
||||
case BH1750_RESOLUTION_1P0_LX:
|
||||
wait = 180;
|
||||
break;
|
||||
case BH1750_RESOLUTION_4P0_LX:
|
||||
wait = 24;
|
||||
break;
|
||||
}
|
||||
BH1750Mode use_mode;
|
||||
uint8_t use_mtreg;
|
||||
if (val <= 7000) {
|
||||
use_mode = BH1750_MODE_H2;
|
||||
use_mtreg = 254;
|
||||
} else {
|
||||
use_mode = BH1750_MODE_H;
|
||||
// lx = counts / 1.2 * (69 / mtreg)
|
||||
// -> mtreg = counts / 1.2 * (69 / lx)
|
||||
// calculate for counts=50000 (allow some range to not saturate, but maximize mtreg)
|
||||
// -> mtreg = 50000*(10/12)*(69/lx)
|
||||
int ideal_mtreg = 50000 * 10 * 69 / (12 * (int) val);
|
||||
use_mtreg = std::min(254, std::max(31, ideal_mtreg));
|
||||
}
|
||||
ESP_LOGV(TAG, "L result: %f -> Calculated mode=%d, mtreg=%d", val, (int) use_mode, use_mtreg);
|
||||
|
||||
this->set_timeout("illuminance", wait, [this]() { this->read_data_(); });
|
||||
this->read_lx_(use_mode, use_mtreg, [this](float val) {
|
||||
if (std::isnan(val)) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), val);
|
||||
this->status_clear_warning();
|
||||
this->publish_state(val);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||
void BH1750Sensor::read_data_() {
|
||||
uint16_t raw_value;
|
||||
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
raw_value = i2c::i2ctohs(raw_value);
|
||||
|
||||
float lx = float(raw_value) / 1.2f;
|
||||
lx *= 69.0f / this->measurement_duration_;
|
||||
if (this->resolution_ == BH1750_RESOLUTION_0P5_LX) {
|
||||
lx /= 2.0f;
|
||||
}
|
||||
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), lx);
|
||||
this->publish_state(lx);
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
void BH1750Sensor::set_resolution(BH1750Resolution resolution) { this->resolution_ = resolution; }
|
||||
|
||||
} // namespace bh1750
|
||||
} // namespace esphome
|
||||
|
||||
@@ -7,29 +7,15 @@
|
||||
namespace esphome {
|
||||
namespace bh1750 {
|
||||
|
||||
/// Enum listing all resolutions that can be used with the BH1750
|
||||
enum BH1750Resolution {
|
||||
BH1750_RESOLUTION_4P0_LX = 0b00100011, // one-time low resolution mode
|
||||
BH1750_RESOLUTION_1P0_LX = 0b00100000, // one-time high resolution mode 1
|
||||
BH1750_RESOLUTION_0P5_LX = 0b00100001, // one-time high resolution mode 2
|
||||
enum BH1750Mode {
|
||||
BH1750_MODE_L,
|
||||
BH1750_MODE_H,
|
||||
BH1750_MODE_H2,
|
||||
};
|
||||
|
||||
/// This class implements support for the i2c-based BH1750 ambient light sensor.
|
||||
class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
/** Set the resolution of this sensor.
|
||||
*
|
||||
* Possible values are:
|
||||
*
|
||||
* - `BH1750_RESOLUTION_4P0_LX`
|
||||
* - `BH1750_RESOLUTION_1P0_LX`
|
||||
* - `BH1750_RESOLUTION_0P5_LX` (default)
|
||||
*
|
||||
* @param resolution The new resolution of the sensor.
|
||||
*/
|
||||
void set_resolution(BH1750Resolution resolution);
|
||||
void set_measurement_duration(uint8_t measurement_duration) { measurement_duration_ = measurement_duration; }
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
void setup() override;
|
||||
@@ -38,10 +24,9 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
void read_data_();
|
||||
void read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f);
|
||||
|
||||
BH1750Resolution resolution_{BH1750_RESOLUTION_0P5_LX};
|
||||
uint8_t measurement_duration_;
|
||||
uint8_t active_mtreg_{0};
|
||||
};
|
||||
|
||||
} // namespace bh1750
|
||||
|
||||
@@ -2,28 +2,20 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import (
|
||||
CONF_RESOLUTION,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_LUX,
|
||||
CONF_MEASUREMENT_DURATION,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
CODEOWNERS = ["@OttoWinter"]
|
||||
|
||||
bh1750_ns = cg.esphome_ns.namespace("bh1750")
|
||||
BH1750Resolution = bh1750_ns.enum("BH1750Resolution")
|
||||
BH1750_RESOLUTIONS = {
|
||||
4.0: BH1750Resolution.BH1750_RESOLUTION_4P0_LX,
|
||||
1.0: BH1750Resolution.BH1750_RESOLUTION_1P0_LX,
|
||||
0.5: BH1750Resolution.BH1750_RESOLUTION_0P5_LX,
|
||||
}
|
||||
|
||||
BH1750Sensor = bh1750_ns.class_(
|
||||
"BH1750Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONF_MEASUREMENT_TIME = "measurement_time"
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
BH1750Sensor,
|
||||
@@ -34,14 +26,11 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(
|
||||
BH1750_RESOLUTIONS, float=True
|
||||
cv.Optional("resolution"): cv.invalid(
|
||||
"The 'resolution' option has been removed. The optimal value is now dynamically calculated."
|
||||
),
|
||||
cv.Optional(CONF_MEASUREMENT_DURATION, default=69): cv.int_range(
|
||||
min=31, max=254
|
||||
),
|
||||
cv.Optional(CONF_MEASUREMENT_TIME): cv.invalid(
|
||||
"The 'measurement_time' option has been replaced with 'measurement_duration' in 1.18.0"
|
||||
cv.Optional("measurement_duration"): cv.invalid(
|
||||
"The 'measurement_duration' option has been removed. The optimal value is now dynamically calculated."
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -54,6 +43,3 @@ async def to_code(config):
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
|
||||
cg.add(var.set_measurement_duration(config[CONF_MEASUREMENT_DURATION]))
|
||||
|
||||
@@ -118,16 +118,21 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||
this->set_states_(espbt::ClientState::IDLE);
|
||||
break;
|
||||
}
|
||||
this->conn_id = param->open.conn_id;
|
||||
auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if, param->open.conn_id);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_CONNECT_EVT: {
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_CONNECT_EVT", this->address_str().c_str());
|
||||
this->conn_id = param->connect.conn_id;
|
||||
auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if, param->connect.conn_id);
|
||||
if (ret) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%d", ret);
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%x", ret);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_CFG_MTU_EVT: {
|
||||
if (param->cfg_mtu.status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "cfg_mtu to %s failed, status %d", this->address_str().c_str(), param->cfg_mtu.status);
|
||||
ESP_LOGW(TAG, "cfg_mtu to %s failed, mtu %d, status %d", this->address_str().c_str(), param->cfg_mtu.mtu,
|
||||
param->cfg_mtu.status);
|
||||
this->set_states_(espbt::ClientState::IDLE);
|
||||
break;
|
||||
}
|
||||
@@ -139,7 +144,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||
if (memcmp(param->disconnect.remote_bda, this->remote_bda, 6) != 0) {
|
||||
return;
|
||||
}
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT", this->address_str().c_str());
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->address_str().c_str(), param->disconnect.reason);
|
||||
for (auto &svc : this->services_)
|
||||
delete svc; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
this->services_.clear();
|
||||
@@ -201,6 +206,32 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||
}
|
||||
}
|
||||
|
||||
void BLEClient::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
||||
switch (event) {
|
||||
// This event is sent by the server when it requests security
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||
ESP_LOGV(TAG, "ESP_GAP_BLE_SEC_REQ_EVT %x", event);
|
||||
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
|
||||
break;
|
||||
// This event is sent once authentication has completed
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT:
|
||||
esp_bd_addr_t bd_addr;
|
||||
memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
|
||||
ESP_LOGI(TAG, "auth complete. remote BD_ADDR: %s", format_hex(bd_addr, 6).c_str());
|
||||
if (!param->ble_security.auth_cmpl.success) {
|
||||
ESP_LOGE(TAG, "auth fail reason = 0x%x", param->ble_security.auth_cmpl.fail_reason);
|
||||
} else {
|
||||
ESP_LOGV(TAG, "auth success. address type = %d auth mode = %d", param->ble_security.auth_cmpl.addr_type,
|
||||
param->ble_security.auth_cmpl.auth_mode);
|
||||
}
|
||||
break;
|
||||
// There are other events we'll want to implement at some point to support things like pass key
|
||||
// https://github.com/espressif/esp-idf/blob/cba69dd088344ed9d26739f04736ae7a37541b3a/examples/bluetooth/bluedroid/ble/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse GATT values into a float for a sensor.
|
||||
// Ref: https://www.bluetooth.com/specifications/assigned-numbers/format-types/
|
||||
float BLEClient::parse_char_value(uint8_t *value, uint16_t length) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include <esp_gattc_api.h>
|
||||
#include <esp_bt_defs.h>
|
||||
#include <esp_gatt_common_api.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
@@ -86,6 +87,7 @@ class BLEClient : public espbt::ESPBTClient, public Component {
|
||||
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
|
||||
bool parse_device(const espbt::ESPBTDevice &device) override;
|
||||
void on_scan_end() override {}
|
||||
void connect() override;
|
||||
|
||||
121
esphome/components/ble_client/text_sensor/__init__.py
Normal file
121
esphome/components/ble_client/text_sensor/__init__.py
Normal file
@@ -0,0 +1,121 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor, ble_client, esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_SERVICE_UUID,
|
||||
)
|
||||
from esphome import automation
|
||||
from .. import ble_client_ns
|
||||
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
|
||||
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
|
||||
CONF_DESCRIPTOR_UUID = "descriptor_uuid"
|
||||
|
||||
CONF_NOTIFY = "notify"
|
||||
CONF_ON_NOTIFY = "on_notify"
|
||||
|
||||
adv_data_t = cg.std_vector.template(cg.uint8)
|
||||
adv_data_t_const_ref = adv_data_t.operator("ref").operator("const")
|
||||
|
||||
BLETextSensor = ble_client_ns.class_(
|
||||
"BLETextSensor",
|
||||
text_sensor.TextSensor,
|
||||
cg.PollingComponent,
|
||||
ble_client.BLEClientNode,
|
||||
)
|
||||
BLETextSensorNotifyTrigger = ble_client_ns.class_(
|
||||
"BLETextSensorNotifyTrigger", automation.Trigger.template(cg.std_string)
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
text_sensor.TEXT_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLETextSensor),
|
||||
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Optional(CONF_NOTIFY, default=False): cv.boolean,
|
||||
cv.Optional(CONF_ON_NOTIFY): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
BLETextSensorNotifyTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
|
||||
)
|
||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
|
||||
cg.add(
|
||||
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
|
||||
)
|
||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
|
||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
|
||||
cg.add(var.set_service_uuid128(uuid128))
|
||||
|
||||
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
var.set_char_uuid16(
|
||||
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid32_format
|
||||
):
|
||||
cg.add(
|
||||
var.set_char_uuid32(
|
||||
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid128_format
|
||||
):
|
||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
|
||||
config[CONF_CHARACTERISTIC_UUID]
|
||||
)
|
||||
cg.add(var.set_char_uuid128(uuid128))
|
||||
|
||||
if CONF_DESCRIPTOR_UUID in config:
|
||||
if len(config[CONF_DESCRIPTOR_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
var.set_descr_uuid16(
|
||||
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid32_format
|
||||
):
|
||||
cg.add(
|
||||
var.set_descr_uuid32(
|
||||
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid128_format
|
||||
):
|
||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
|
||||
config[CONF_DESCRIPTOR_UUID]
|
||||
)
|
||||
cg.add(var.set_descr_uuid128(uuid128))
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
cg.add(var.set_enable_notify(config[CONF_NOTIFY]))
|
||||
await text_sensor.register_text_sensor(var, config)
|
||||
for conf in config.get(CONF_ON_NOTIFY, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await ble_client.register_ble_node(trigger, config)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
38
esphome/components/ble_client/text_sensor/automation.h
Normal file
38
esphome/components/ble_client/text_sensor/automation.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/ble_client/text_sensor/ble_text_sensor.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
class BLETextSensorNotifyTrigger : public Trigger<std::string>, public BLETextSensor {
|
||||
public:
|
||||
explicit BLETextSensorNotifyTrigger(BLETextSensor *sensor) { sensor_ = sensor; }
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override {
|
||||
switch (event) {
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
this->sensor_->node_state = espbt::ClientState::ESTABLISHED;
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (param->notify.conn_id != this->sensor_->parent()->conn_id || param->notify.handle != this->sensor_->handle)
|
||||
break;
|
||||
this->trigger(this->sensor_->parse_data(param->notify.value, param->notify.value_len));
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
BLETextSensor *sensor_;
|
||||
};
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
137
esphome/components/ble_client/text_sensor/ble_text_sensor.cpp
Normal file
137
esphome/components/ble_client/text_sensor/ble_text_sensor.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "ble_text_sensor.h"
|
||||
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
static const char *const TAG = "ble_text_sensor";
|
||||
|
||||
static const std::string EMPTY = "";
|
||||
|
||||
uint32_t BLETextSensor::hash_base() { return 193967603UL; }
|
||||
|
||||
void BLETextSensor::loop() {}
|
||||
|
||||
void BLETextSensor::dump_config() {
|
||||
LOG_TEXT_SENSOR("", "BLE Text Sensor", this);
|
||||
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_OPEN_EVT: {
|
||||
if (param->open.status == ESP_GATT_OK) {
|
||||
ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str());
|
||||
this->status_set_warning();
|
||||
this->publish_state(EMPTY);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
this->handle = 0;
|
||||
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
|
||||
if (chr == nullptr) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(EMPTY);
|
||||
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", this->service_uuid_.to_string().c_str(),
|
||||
this->char_uuid_.to_string().c_str());
|
||||
break;
|
||||
}
|
||||
this->handle = chr->handle;
|
||||
if (this->descr_uuid_.get_uuid().len > 0) {
|
||||
auto *descr = chr->get_descriptor(this->descr_uuid_);
|
||||
if (descr == nullptr) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(EMPTY);
|
||||
ESP_LOGW(TAG, "No sensor descriptor found at service %s char %s descr %s",
|
||||
this->service_uuid_.to_string().c_str(), this->char_uuid_.to_string().c_str(),
|
||||
this->descr_uuid_.to_string().c_str());
|
||||
break;
|
||||
}
|
||||
this->handle = descr->handle;
|
||||
}
|
||||
if (this->notify_) {
|
||||
auto status =
|
||||
esp_ble_gattc_register_for_notify(this->parent()->gattc_if, this->parent()->remote_bda, chr->handle);
|
||||
if (status) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
|
||||
}
|
||||
} else {
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_READ_CHAR_EVT: {
|
||||
if (param->read.conn_id != this->parent()->conn_id)
|
||||
break;
|
||||
if (param->read.status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
||||
break;
|
||||
}
|
||||
if (param->read.handle == this->handle) {
|
||||
this->status_clear_warning();
|
||||
this->publish_state(this->parse_data(param->read.value, param->read.value_len));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (param->notify.conn_id != this->parent()->conn_id || param->notify.handle != this->handle)
|
||||
break;
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
|
||||
param->notify.handle, param->notify.value[0]);
|
||||
this->publish_state(this->parse_data(param->notify.value, param->notify.value_len));
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string BLETextSensor::parse_data(uint8_t *value, uint16_t value_len) {
|
||||
std::string text(value, value + value_len);
|
||||
return text;
|
||||
}
|
||||
|
||||
void BLETextSensor::update() {
|
||||
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
|
||||
return;
|
||||
}
|
||||
if (this->handle == 0) {
|
||||
ESP_LOGW(TAG, "[%s] Cannot poll, no service or characteristic found", this->get_name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto status =
|
||||
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(EMPTY);
|
||||
ESP_LOGW(TAG, "[%s] Error sending read request for sensor, status=%d", this->get_name().c_str(), status);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
#endif
|
||||
47
esphome/components/ble_client/text_sensor/ble_text_sensor.h
Normal file
47
esphome/components/ble_client/text_sensor/ble_text_sensor.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
class BLETextSensor : public text_sensor::TextSensor, public PollingComponent, public BLEClientNode {
|
||||
public:
|
||||
void loop() override;
|
||||
void update() override;
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void set_char_uuid16(uint16_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void set_enable_notify(bool notify) { this->notify_ = notify; }
|
||||
std::string parse_data(uint8_t *value, uint16_t value_len);
|
||||
uint16_t handle;
|
||||
|
||||
protected:
|
||||
uint32_t hash_base() override;
|
||||
bool notify_;
|
||||
espbt::ESPBTUUID service_uuid_;
|
||||
espbt::ESPBTUUID char_uuid_;
|
||||
espbt::ESPBTUUID descr_uuid_;
|
||||
};
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
#endif
|
||||
@@ -18,11 +18,7 @@ void Button::add_on_press_callback(std::function<void()> &&callback) { this->pre
|
||||
uint32_t Button::hash_base() { return 1495763804UL; }
|
||||
|
||||
void Button::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||
std::string Button::get_device_class() {
|
||||
if (this->device_class_.has_value())
|
||||
return *this->device_class_;
|
||||
return "";
|
||||
}
|
||||
std::string Button::get_device_class() { return this->device_class_; }
|
||||
|
||||
} // namespace button
|
||||
} // namespace esphome
|
||||
|
||||
@@ -45,12 +45,12 @@ class Button : public EntityBase {
|
||||
protected:
|
||||
/** You should implement this virtual method if you want to create your own button.
|
||||
*/
|
||||
virtual void press_action(){};
|
||||
virtual void press_action() = 0;
|
||||
|
||||
uint32_t hash_base() override;
|
||||
|
||||
CallbackManager<void()> press_callback_{};
|
||||
optional<std::string> device_class_{};
|
||||
std::string device_class_{};
|
||||
};
|
||||
|
||||
} // namespace button
|
||||
|
||||
@@ -10,6 +10,7 @@ IS_PLATFORM_COMPONENT = True
|
||||
CONF_CAN_ID = "can_id"
|
||||
CONF_CAN_ID_MASK = "can_id_mask"
|
||||
CONF_USE_EXTENDED_ID = "use_extended_id"
|
||||
CONF_REMOTE_TRANSMISSION_REQUEST = "remote_transmission_request"
|
||||
CONF_CANBUS_ID = "canbus_id"
|
||||
CONF_BIT_RATE = "bit_rate"
|
||||
CONF_ON_FRAME = "on_frame"
|
||||
@@ -122,6 +123,7 @@ async def register_canbus(var, config):
|
||||
cv.GenerateID(CONF_CANBUS_ID): cv.use_id(CanbusComponent),
|
||||
cv.Optional(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
|
||||
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
|
||||
cv.Optional(CONF_REMOTE_TRANSMISSION_REQUEST, default=False): cv.boolean,
|
||||
cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
|
||||
},
|
||||
validate_id,
|
||||
@@ -140,6 +142,11 @@ async def canbus_action_to_code(config, action_id, template_arg, args):
|
||||
)
|
||||
cg.add(var.set_use_extended_id(use_extended_id))
|
||||
|
||||
remote_transmission_request = await cg.templatable(
|
||||
config[CONF_REMOTE_TRANSMISSION_REQUEST], args, bool
|
||||
)
|
||||
cg.add(var.set_remote_transmission_request(remote_transmission_request))
|
||||
|
||||
data = config[CONF_DATA]
|
||||
if isinstance(data, bytes):
|
||||
data = [int(x) for x in data]
|
||||
|
||||
@@ -22,20 +22,22 @@ void Canbus::dump_config() {
|
||||
}
|
||||
}
|
||||
|
||||
void Canbus::send_data(uint32_t can_id, bool use_extended_id, const std::vector<uint8_t> &data) {
|
||||
void Canbus::send_data(uint32_t can_id, bool use_extended_id, bool remote_transmission_request,
|
||||
const std::vector<uint8_t> &data) {
|
||||
struct CanFrame can_message;
|
||||
|
||||
uint8_t size = static_cast<uint8_t>(data.size());
|
||||
if (use_extended_id) {
|
||||
ESP_LOGD(TAG, "send extended id=0x%08x size=%d", can_id, size);
|
||||
ESP_LOGD(TAG, "send extended id=0x%08x rtr=%s size=%d", can_id, TRUEFALSE(remote_transmission_request), size);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "send extended id=0x%03x size=%d", can_id, size);
|
||||
ESP_LOGD(TAG, "send extended id=0x%03x rtr=%s size=%d", can_id, TRUEFALSE(remote_transmission_request), size);
|
||||
}
|
||||
if (size > CAN_MAX_DATA_LENGTH)
|
||||
size = CAN_MAX_DATA_LENGTH;
|
||||
can_message.can_data_length_code = size;
|
||||
can_message.can_id = can_id;
|
||||
can_message.use_extended_id = use_extended_id;
|
||||
can_message.remote_transmission_request = remote_transmission_request;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
can_message.data[i] = data[i];
|
||||
|
||||
@@ -62,7 +62,12 @@ class Canbus : public Component {
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
void loop() override;
|
||||
|
||||
void send_data(uint32_t can_id, bool use_extended_id, const std::vector<uint8_t> &data);
|
||||
void send_data(uint32_t can_id, bool use_extended_id, bool remote_transmission_request,
|
||||
const std::vector<uint8_t> &data);
|
||||
void send_data(uint32_t can_id, bool use_extended_id, const std::vector<uint8_t> &data) {
|
||||
// for backwards compatibility only
|
||||
this->send_data(can_id, use_extended_id, false, data);
|
||||
}
|
||||
void set_can_id(uint32_t can_id) { this->can_id_ = can_id; }
|
||||
void set_use_extended_id(bool use_extended_id) { this->use_extended_id_ = use_extended_id; }
|
||||
void set_bitrate(CanSpeed bit_rate) { this->bit_rate_ = bit_rate; }
|
||||
@@ -96,21 +101,26 @@ template<typename... Ts> class CanbusSendAction : public Action<Ts...>, public P
|
||||
|
||||
void set_use_extended_id(bool use_extended_id) { this->use_extended_id_ = use_extended_id; }
|
||||
|
||||
void set_remote_transmission_request(bool remote_transmission_request) {
|
||||
this->remote_transmission_request_ = remote_transmission_request;
|
||||
}
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto can_id = this->can_id_.has_value() ? *this->can_id_ : this->parent_->can_id_;
|
||||
auto use_extended_id =
|
||||
this->use_extended_id_.has_value() ? *this->use_extended_id_ : this->parent_->use_extended_id_;
|
||||
if (this->static_) {
|
||||
this->parent_->send_data(can_id, use_extended_id, this->data_static_);
|
||||
this->parent_->send_data(can_id, use_extended_id, this->remote_transmission_request_, this->data_static_);
|
||||
} else {
|
||||
auto val = this->data_func_(x...);
|
||||
this->parent_->send_data(can_id, use_extended_id, val);
|
||||
this->parent_->send_data(can_id, use_extended_id, this->remote_transmission_request_, val);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
optional<uint32_t> can_id_{};
|
||||
optional<bool> use_extended_id_{};
|
||||
bool remote_transmission_request_{false};
|
||||
bool static_{false};
|
||||
std::function<std::vector<uint8_t>(Ts...)> data_func_{};
|
||||
std::vector<uint8_t> data_static_{};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "climate.h"
|
||||
#include "esphome/core/macros.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace climate {
|
||||
@@ -326,14 +327,17 @@ optional<ClimateDeviceRestoreState> Climate::restore_state_() {
|
||||
return recovered;
|
||||
}
|
||||
void Climate::save_state_() {
|
||||
#if defined(USE_ESP_IDF) && !defined(CLANG_TIDY)
|
||||
#if (defined(USE_ESP_IDF) || (defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(3, 0, 0))) && \
|
||||
!defined(CLANG_TIDY)
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
#define TEMP_IGNORE_MEMACCESS
|
||||
#endif
|
||||
ClimateDeviceRestoreState state{};
|
||||
// initialize as zero to prevent random data on stack triggering erase
|
||||
memset(&state, 0, sizeof(ClimateDeviceRestoreState));
|
||||
#if USE_ESP_IDF && !defined(CLANG_TIDY)
|
||||
#ifdef TEMP_IGNORE_MEMACCESS
|
||||
#pragma GCC diagnostic pop
|
||||
#undef TEMP_IGNORE_MEMACCESS
|
||||
#endif
|
||||
|
||||
state.mode = this->mode;
|
||||
|
||||
@@ -76,7 +76,7 @@ enum ClimateSwingMode : uint8_t {
|
||||
CLIMATE_SWING_HORIZONTAL = 3,
|
||||
};
|
||||
|
||||
/// Enum for all modes a climate swing can be in
|
||||
/// Enum for all preset modes
|
||||
enum ClimatePreset : uint8_t {
|
||||
/// No preset is active
|
||||
CLIMATE_PRESET_NONE = 0,
|
||||
@@ -108,7 +108,7 @@ const LogString *climate_fan_mode_to_string(ClimateFanMode mode);
|
||||
/// Convert the given ClimateSwingMode to a human-readable string.
|
||||
const LogString *climate_swing_mode_to_string(ClimateSwingMode mode);
|
||||
|
||||
/// Convert the given ClimateSwingMode to a human-readable string.
|
||||
/// Convert the given PresetMode to a human-readable string.
|
||||
const LogString *climate_preset_to_string(ClimatePreset preset);
|
||||
|
||||
} // namespace climate
|
||||
|
||||
@@ -141,7 +141,7 @@ class ClimateTraits {
|
||||
}
|
||||
bool supports_swing_mode(ClimateSwingMode swing_mode) const { return supported_swing_modes_.count(swing_mode); }
|
||||
bool get_supports_swing_modes() const { return !supported_swing_modes_.empty(); }
|
||||
std::set<ClimateSwingMode> get_supported_swing_modes() { return supported_swing_modes_; }
|
||||
std::set<ClimateSwingMode> get_supported_swing_modes() const { return supported_swing_modes_; }
|
||||
|
||||
float get_visual_min_temperature() const { return visual_min_temperature_; }
|
||||
void set_visual_min_temperature(float visual_min_temperature) { visual_min_temperature_ = visual_min_temperature; }
|
||||
|
||||
5
esphome/components/copy/__init__.py
Normal file
5
esphome/components/copy/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
CODEOWNERS = ["@OttoWinter"]
|
||||
|
||||
copy_ns = cg.esphome_ns.namespace("copy")
|
||||
41
esphome/components/copy/binary_sensor/__init__.py
Normal file
41
esphome/components/copy/binary_sensor/__init__.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyBinarySensor = copy_ns.class_(
|
||||
"CopyBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
binary_sensor.binary_sensor_schema(CopyBinarySensor)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(binary_sensor.BinarySensor),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_DEVICE_CLASS, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
18
esphome/components/copy/binary_sensor/copy_binary_sensor.cpp
Normal file
18
esphome/components/copy/binary_sensor/copy_binary_sensor.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "copy_binary_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.binary_sensor";
|
||||
|
||||
void CopyBinarySensor::setup() {
|
||||
source_->add_on_state_callback([this](bool value) { this->publish_state(value); });
|
||||
if (source_->has_state())
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopyBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Copy Binary Sensor", this); }
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
21
esphome/components/copy/binary_sensor/copy_binary_sensor.h
Normal file
21
esphome/components/copy/binary_sensor/copy_binary_sensor.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyBinarySensor : public binary_sensor::BinarySensor, public Component {
|
||||
public:
|
||||
void set_source(binary_sensor::BinarySensor *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
binary_sensor::BinarySensor *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
42
esphome/components/copy/button/__init__.py
Normal file
42
esphome/components/copy/button/__init__.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import button
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyButton = copy_ns.class_("CopyButton", button.Button, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
button.button_schema()
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopyButton),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(button.Button),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_DEVICE_CLASS, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await button.register_button(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
14
esphome/components/copy/button/copy_button.cpp
Normal file
14
esphome/components/copy/button/copy_button.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "copy_button.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.button";
|
||||
|
||||
void CopyButton::dump_config() { LOG_BUTTON("", "Copy Button", this); }
|
||||
|
||||
void CopyButton::press_action() { source_->press(); }
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
22
esphome/components/copy/button/copy_button.h
Normal file
22
esphome/components/copy/button/copy_button.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/button/button.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyButton : public button::Button, public Component {
|
||||
public:
|
||||
void set_source(button::Button *source) { source_ = source; }
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
|
||||
button::Button *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
38
esphome/components/copy/cover/__init__.py
Normal file
38
esphome/components/copy/cover/__init__.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import cover
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyCover = copy_ns.class_("CopyCover", cover.Cover, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopyCover),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(cover.Cover),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_DEVICE_CLASS, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cover.register_cover(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
50
esphome/components/copy/cover/copy_cover.cpp
Normal file
50
esphome/components/copy/cover/copy_cover.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "copy_cover.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.cover";
|
||||
|
||||
void CopyCover::setup() {
|
||||
source_->add_on_state_callback([this]() {
|
||||
this->current_operation = this->source_->current_operation;
|
||||
this->position = this->source_->position;
|
||||
this->tilt = this->source_->tilt;
|
||||
this->publish_state();
|
||||
});
|
||||
|
||||
this->current_operation = this->source_->current_operation;
|
||||
this->position = this->source_->position;
|
||||
this->tilt = this->source_->tilt;
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void CopyCover::dump_config() { LOG_COVER("", "Copy Cover", this); }
|
||||
|
||||
cover::CoverTraits CopyCover::get_traits() {
|
||||
auto base = source_->get_traits();
|
||||
cover::CoverTraits traits{};
|
||||
// copy traits manually so it doesn't break when new options are added
|
||||
// but the control() method hasn't implemented them yet.
|
||||
traits.set_is_assumed_state(base.get_is_assumed_state());
|
||||
traits.set_supports_position(base.get_supports_position());
|
||||
traits.set_supports_tilt(base.get_supports_tilt());
|
||||
traits.set_supports_toggle(base.get_supports_toggle());
|
||||
return traits;
|
||||
}
|
||||
|
||||
void CopyCover::control(const cover::CoverCall &call) {
|
||||
auto call2 = source_->make_call();
|
||||
call2.set_stop(call.get_stop());
|
||||
if (call.get_tilt().has_value())
|
||||
call2.set_tilt(*call.get_tilt());
|
||||
if (call.get_position().has_value())
|
||||
call2.set_position(*call.get_position());
|
||||
if (call.get_tilt().has_value())
|
||||
call2.set_tilt(*call.get_tilt());
|
||||
call2.perform();
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
25
esphome/components/copy/cover/copy_cover.h
Normal file
25
esphome/components/copy/cover/copy_cover.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/cover/cover.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyCover : public cover::Cover, public Component {
|
||||
public:
|
||||
void set_source(cover::Cover *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
cover::CoverTraits get_traits() override;
|
||||
|
||||
protected:
|
||||
void control(const cover::CoverCall &call) override;
|
||||
|
||||
cover::Cover *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
36
esphome/components/copy/fan/__init__.py
Normal file
36
esphome/components/copy/fan/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import fan
|
||||
from esphome.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyFan = copy_ns.class_("CopyFan", fan.Fan, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopyFan),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(fan.Fan),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await fan.register_fan(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
53
esphome/components/copy/fan/copy_fan.cpp
Normal file
53
esphome/components/copy/fan/copy_fan.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "copy_fan.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.fan";
|
||||
|
||||
void CopyFan::setup() {
|
||||
source_->add_on_state_callback([this]() {
|
||||
this->state = source_->state;
|
||||
this->oscillating = source_->oscillating;
|
||||
this->speed = source_->speed;
|
||||
this->direction = source_->direction;
|
||||
this->publish_state();
|
||||
});
|
||||
|
||||
this->state = source_->state;
|
||||
this->oscillating = source_->oscillating;
|
||||
this->speed = source_->speed;
|
||||
this->direction = source_->direction;
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void CopyFan::dump_config() { LOG_FAN("", "Copy Fan", this); }
|
||||
|
||||
fan::FanTraits CopyFan::get_traits() {
|
||||
fan::FanTraits traits;
|
||||
auto base = source_->get_traits();
|
||||
// copy traits manually so it doesn't break when new options are added
|
||||
// but the control() method hasn't implemented them yet.
|
||||
traits.set_oscillation(base.supports_oscillation());
|
||||
traits.set_speed(base.supports_speed());
|
||||
traits.set_supported_speed_count(base.supported_speed_count());
|
||||
traits.set_direction(base.supports_direction());
|
||||
return traits;
|
||||
}
|
||||
|
||||
void CopyFan::control(const fan::FanCall &call) {
|
||||
auto call2 = source_->make_call();
|
||||
if (call.get_state().has_value())
|
||||
call2.set_state(*call.get_state());
|
||||
if (call.get_oscillating().has_value())
|
||||
call2.set_oscillating(*call.get_oscillating());
|
||||
if (call.get_speed().has_value())
|
||||
call2.set_speed(*call.get_speed());
|
||||
if (call.get_direction().has_value())
|
||||
call2.set_direction(*call.get_direction());
|
||||
call2.perform();
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
26
esphome/components/copy/fan/copy_fan.h
Normal file
26
esphome/components/copy/fan/copy_fan.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/fan/fan.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyFan : public fan::Fan, public Component {
|
||||
public:
|
||||
void set_source(fan::Fan *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
fan::FanTraits get_traits() override;
|
||||
|
||||
protected:
|
||||
void control(const fan::FanCall &call) override;
|
||||
;
|
||||
|
||||
fan::Fan *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
36
esphome/components/copy/lock/__init__.py
Normal file
36
esphome/components/copy/lock/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import lock
|
||||
from esphome.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyLock = copy_ns.class_("CopyLock", lock.Lock, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = lock.LOCK_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopyLock),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(lock.Lock),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await lock.register_lock(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
29
esphome/components/copy/lock/copy_lock.cpp
Normal file
29
esphome/components/copy/lock/copy_lock.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "copy_lock.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.lock";
|
||||
|
||||
void CopyLock::setup() {
|
||||
source_->add_on_state_callback([this]() { this->publish_state(source_->state); });
|
||||
|
||||
traits.set_assumed_state(source_->traits.get_assumed_state());
|
||||
traits.set_requires_code(source_->traits.get_requires_code());
|
||||
traits.set_supported_states(source_->traits.get_supported_states());
|
||||
traits.set_supports_open(source_->traits.get_supports_open());
|
||||
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopyLock::dump_config() { LOG_LOCK("", "Copy Lock", this); }
|
||||
|
||||
void CopyLock::control(const lock::LockCall &call) {
|
||||
auto call2 = source_->make_call();
|
||||
call2.set_state(call.get_state());
|
||||
call2.perform();
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
23
esphome/components/copy/lock/copy_lock.h
Normal file
23
esphome/components/copy/lock/copy_lock.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/lock/lock.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyLock : public lock::Lock, public Component {
|
||||
public:
|
||||
void set_source(lock::Lock *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
void control(const lock::LockCall &call) override;
|
||||
|
||||
lock::Lock *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
38
esphome/components/copy/number/__init__.py
Normal file
38
esphome/components/copy/number/__init__.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import number
|
||||
from esphome.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_MODE,
|
||||
CONF_SOURCE_ID,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyNumber = copy_ns.class_("CopyNumber", number.Number, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = number.NUMBER_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopyNumber),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(number.Number),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_UNIT_OF_MEASUREMENT, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_MODE, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await number.new_number(config, min_value=0, max_value=0, step=0)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
29
esphome/components/copy/number/copy_number.cpp
Normal file
29
esphome/components/copy/number/copy_number.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "copy_number.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.number";
|
||||
|
||||
void CopyNumber::setup() {
|
||||
source_->add_on_state_callback([this](float value) { this->publish_state(value); });
|
||||
|
||||
traits.set_min_value(source_->traits.get_min_value());
|
||||
traits.set_max_value(source_->traits.get_max_value());
|
||||
traits.set_step(source_->traits.get_step());
|
||||
|
||||
if (source_->has_state())
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopyNumber::dump_config() { LOG_NUMBER("", "Copy Number", this); }
|
||||
|
||||
void CopyNumber::control(float value) {
|
||||
auto call2 = source_->make_call();
|
||||
call2.set_value(value);
|
||||
call2.perform();
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
23
esphome/components/copy/number/copy_number.h
Normal file
23
esphome/components/copy/number/copy_number.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/number/number.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyNumber : public number::Number, public Component {
|
||||
public:
|
||||
void set_source(number::Number *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
void control(float value) override;
|
||||
|
||||
number::Number *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
36
esphome/components/copy/select/__init__.py
Normal file
36
esphome/components/copy/select/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import select
|
||||
from esphome.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopySelect = copy_ns.class_("CopySelect", select.Select, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = select.SELECT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopySelect),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(select.Select),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await select.register_select(var, config, options=[])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
27
esphome/components/copy/select/copy_select.cpp
Normal file
27
esphome/components/copy/select/copy_select.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "copy_select.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.select";
|
||||
|
||||
void CopySelect::setup() {
|
||||
source_->add_on_state_callback([this](const std::string &value) { this->publish_state(value); });
|
||||
|
||||
traits.set_options(source_->traits.get_options());
|
||||
|
||||
if (source_->has_state())
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopySelect::dump_config() { LOG_SELECT("", "Copy Select", this); }
|
||||
|
||||
void CopySelect::control(const std::string &value) {
|
||||
auto call = source_->make_call();
|
||||
call.set_option(value);
|
||||
call.perform();
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
23
esphome/components/copy/select/copy_select.h
Normal file
23
esphome/components/copy/select/copy_select.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/select/select.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopySelect : public select::Select, public Component {
|
||||
public:
|
||||
void set_source(select::Select *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
|
||||
select::Select *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
45
esphome/components/copy/sensor/__init__.py
Normal file
45
esphome/components/copy/sensor/__init__.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_SOURCE_ID,
|
||||
CONF_STATE_CLASS,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
CONF_ACCURACY_DECIMALS,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopySensor = copy_ns.class_("CopySensor", sensor.Sensor, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(CopySensor)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(sensor.Sensor),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_UNIT_OF_MEASUREMENT, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ACCURACY_DECIMALS, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_DEVICE_CLASS, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_STATE_CLASS, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
18
esphome/components/copy/sensor/copy_sensor.cpp
Normal file
18
esphome/components/copy/sensor/copy_sensor.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "copy_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.sensor";
|
||||
|
||||
void CopySensor::setup() {
|
||||
source_->add_on_state_callback([this](float value) { this->publish_state(value); });
|
||||
if (source_->has_state())
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopySensor::dump_config() { LOG_SENSOR("", "Copy Sensor", this); }
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
21
esphome/components/copy/sensor/copy_sensor.h
Normal file
21
esphome/components/copy/sensor/copy_sensor.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopySensor : public sensor::Sensor, public Component {
|
||||
public:
|
||||
void set_source(sensor::Sensor *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
sensor::Sensor *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
38
esphome/components/copy/switch/__init__.py
Normal file
38
esphome/components/copy/switch/__init__.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import switch
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopySwitch = copy_ns.class_("CopySwitch", switch.Switch, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopySwitch),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(switch.Switch),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_DEVICE_CLASS, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await switch.register_switch(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
26
esphome/components/copy/switch/copy_switch.cpp
Normal file
26
esphome/components/copy/switch/copy_switch.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "copy_switch.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.switch";
|
||||
|
||||
void CopySwitch::setup() {
|
||||
source_->add_on_state_callback([this](float value) { this->publish_state(value); });
|
||||
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopySwitch::dump_config() { LOG_SWITCH("", "Copy Switch", this); }
|
||||
|
||||
void CopySwitch::write_state(bool state) {
|
||||
if (state) {
|
||||
source_->turn_on();
|
||||
} else {
|
||||
source_->turn_off();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
23
esphome/components/copy/switch/copy_switch.h
Normal file
23
esphome/components/copy/switch/copy_switch.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopySwitch : public switch_::Switch, public Component {
|
||||
public:
|
||||
void set_source(switch_::Switch *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
|
||||
switch_::Switch *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
37
esphome/components/copy/text_sensor/__init__.py
Normal file
37
esphome/components/copy/text_sensor/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor
|
||||
from esphome.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_SOURCE_ID,
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
from .. import copy_ns
|
||||
|
||||
CopyTextSensor = copy_ns.class_("CopyTextSensor", text_sensor.TextSensor, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
text_sensor.text_sensor_schema(CopyTextSensor)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(text_sensor.TextSensor),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
inherit_property_from(CONF_ENTITY_CATEGORY, CONF_SOURCE_ID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await text_sensor.new_text_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||
cg.add(var.set_source(source))
|
||||
18
esphome/components/copy/text_sensor/copy_text_sensor.cpp
Normal file
18
esphome/components/copy/text_sensor/copy_text_sensor.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "copy_text_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
static const char *const TAG = "copy.text_sensor";
|
||||
|
||||
void CopyTextSensor::setup() {
|
||||
source_->add_on_state_callback([this](const std::string &value) { this->publish_state(value); });
|
||||
if (source_->has_state())
|
||||
this->publish_state(source_->state);
|
||||
}
|
||||
|
||||
void CopyTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Copy Sensor", this); }
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
21
esphome/components/copy/text_sensor/copy_text_sensor.h
Normal file
21
esphome/components/copy/text_sensor/copy_text_sensor.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace copy {
|
||||
|
||||
class CopyTextSensor : public text_sensor::TextSensor, public Component {
|
||||
public:
|
||||
void set_source(text_sensor::TextSensor *source) { source_ = source; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
text_sensor::TextSensor *source_;
|
||||
};
|
||||
|
||||
} // namespace copy
|
||||
} // namespace esphome
|
||||
@@ -111,6 +111,9 @@ void DallasComponent::update() {
|
||||
if (!result) {
|
||||
ESP_LOGE(TAG, "Requesting conversion failed");
|
||||
this->status_set_warning();
|
||||
for (auto *sensor : this->sensors_) {
|
||||
sensor->publish_state(NAN);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,6 @@ void IRAM_ATTR ESPOneWire::select(uint64_t address) {
|
||||
void IRAM_ATTR ESPOneWire::reset_search() {
|
||||
this->last_discrepancy_ = 0;
|
||||
this->last_device_flag_ = false;
|
||||
this->last_family_discrepancy_ = 0;
|
||||
this->rom_number_ = 0;
|
||||
}
|
||||
uint64_t IRAM_ATTR ESPOneWire::search() {
|
||||
@@ -195,9 +194,6 @@ uint64_t IRAM_ATTR ESPOneWire::search() {
|
||||
|
||||
if (!branch) {
|
||||
last_zero = id_bit_number;
|
||||
if (last_zero < 9) {
|
||||
this->last_discrepancy_ = last_zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ class ESPOneWire {
|
||||
|
||||
ISRInternalGPIOPin pin_;
|
||||
uint8_t last_discrepancy_{0};
|
||||
uint8_t last_family_discrepancy_{0};
|
||||
bool last_device_flag_{false};
|
||||
uint64_t rom_number_{0};
|
||||
};
|
||||
|
||||
@@ -98,6 +98,8 @@ CELL_VOLTAGE_SCHEMA = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
icon=ICON_FLASH,
|
||||
accuracy_decimals=3,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
|
||||
@@ -1,19 +1,79 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import time
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins, automation
|
||||
from esphome.const import (
|
||||
CONF_HOUR,
|
||||
CONF_ID,
|
||||
CONF_MINUTE,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
CONF_PINS,
|
||||
CONF_RUN_DURATION,
|
||||
CONF_SECOND,
|
||||
CONF_SLEEP_DURATION,
|
||||
CONF_TIME_ID,
|
||||
CONF_WAKEUP_PIN,
|
||||
)
|
||||
|
||||
from esphome.components.esp32 import get_esp32_variant
|
||||
from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32,
|
||||
VARIANT_ESP32C3,
|
||||
VARIANT_ESP32S2,
|
||||
)
|
||||
|
||||
WAKEUP_PINS = {
|
||||
VARIANT_ESP32: [
|
||||
0,
|
||||
2,
|
||||
4,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
32,
|
||||
33,
|
||||
34,
|
||||
35,
|
||||
36,
|
||||
37,
|
||||
38,
|
||||
39,
|
||||
],
|
||||
VARIANT_ESP32C3: [0, 1, 2, 3, 4, 5],
|
||||
VARIANT_ESP32S2: [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def validate_pin_number(value):
|
||||
valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35, 36, 37, 38, 39]
|
||||
valid_pins = WAKEUP_PINS.get(get_esp32_variant(), WAKEUP_PINS[VARIANT_ESP32])
|
||||
if value[CONF_NUMBER] not in valid_pins:
|
||||
raise cv.Invalid(
|
||||
f"Only pins {', '.join(str(x) for x in valid_pins)} support wakeup"
|
||||
@@ -21,6 +81,14 @@ def validate_pin_number(value):
|
||||
return value
|
||||
|
||||
|
||||
def validate_config(config):
|
||||
if get_esp32_variant() == VARIANT_ESP32C3 and CONF_ESP32_EXT1_WAKEUP in config:
|
||||
raise cv.Invalid("ESP32-C3 does not support wakeup from touch.")
|
||||
if get_esp32_variant() == VARIANT_ESP32C3 and CONF_TOUCH_WAKEUP in config:
|
||||
raise cv.Invalid("ESP32-C3 does not support wakeup from ext1")
|
||||
return config
|
||||
|
||||
|
||||
deep_sleep_ns = cg.esphome_ns.namespace("deep_sleep")
|
||||
DeepSleepComponent = deep_sleep_ns.class_("DeepSleepComponent", cg.Component)
|
||||
EnterDeepSleepAction = deep_sleep_ns.class_("EnterDeepSleepAction", automation.Action)
|
||||
@@ -49,6 +117,7 @@ CONF_TOUCH_WAKEUP = "touch_wakeup"
|
||||
CONF_DEFAULT = "default"
|
||||
CONF_GPIO_WAKEUP_REASON = "gpio_wakeup_reason"
|
||||
CONF_TOUCH_WAKEUP_REASON = "touch_wakeup_reason"
|
||||
CONF_UNTIL = "until"
|
||||
|
||||
WAKEUP_CAUSES_SCHEMA = cv.Schema(
|
||||
{
|
||||
@@ -139,13 +208,19 @@ async def to_code(config):
|
||||
cg.add_define("USE_DEEP_SLEEP")
|
||||
|
||||
|
||||
DEEP_SLEEP_ENTER_SCHEMA = automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(DeepSleepComponent),
|
||||
cv.Optional(CONF_SLEEP_DURATION): cv.templatable(
|
||||
cv.positive_time_period_milliseconds
|
||||
),
|
||||
}
|
||||
DEEP_SLEEP_ENTER_SCHEMA = cv.All(
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(DeepSleepComponent),
|
||||
cv.Exclusive(CONF_SLEEP_DURATION, "time"): cv.templatable(
|
||||
cv.positive_time_period_milliseconds
|
||||
),
|
||||
# Only on ESP32 due to how long the RTC on ESP8266 can stay asleep
|
||||
cv.Exclusive(CONF_UNTIL, "time"): cv.All(cv.only_on_esp32, cv.time_of_day),
|
||||
cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
||||
}
|
||||
),
|
||||
cv.has_none_or_all_keys(CONF_UNTIL, CONF_TIME_ID),
|
||||
)
|
||||
|
||||
|
||||
@@ -165,6 +240,14 @@ async def deep_sleep_enter_to_code(config, action_id, template_arg, args):
|
||||
if CONF_SLEEP_DURATION in config:
|
||||
template_ = await cg.templatable(config[CONF_SLEEP_DURATION], args, cg.int32)
|
||||
cg.add(var.set_sleep_duration(template_))
|
||||
|
||||
if CONF_UNTIL in config:
|
||||
until = config[CONF_UNTIL]
|
||||
cg.add(var.set_until(until[CONF_HOUR], until[CONF_MINUTE], until[CONF_SECOND]))
|
||||
|
||||
time_ = await cg.get_variable(config[CONF_TIME_ID])
|
||||
cg.add(var.set_time(time_))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "deep_sleep_component.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cinttypes>
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
#include <Esp.h>
|
||||
@@ -101,10 +102,12 @@ void DeepSleepComponent::begin_sleep(bool manual) {
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "Beginning Deep Sleep");
|
||||
if (this->sleep_duration_.has_value())
|
||||
ESP_LOGI(TAG, "Sleeping for %" PRId64 "us", *this->sleep_duration_);
|
||||
|
||||
App.run_safe_shutdown_hooks();
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3)
|
||||
if (this->sleep_duration_.has_value())
|
||||
esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
|
||||
if (this->wakeup_pin_ != nullptr) {
|
||||
@@ -126,6 +129,18 @@ void DeepSleepComponent::begin_sleep(bool manual) {
|
||||
esp_deep_sleep_start();
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32_VARIANT_ESP32C3
|
||||
if (this->sleep_duration_.has_value())
|
||||
esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
|
||||
if (this->wakeup_pin_ != nullptr) {
|
||||
bool level = !this->wakeup_pin_->is_inverted();
|
||||
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
|
||||
level = !level;
|
||||
}
|
||||
esp_deep_sleep_enable_gpio_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
ESP.deepSleep(*this->sleep_duration_); // NOLINT(readability-static-accessed-through-instance)
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <esp_sleep.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_TIME
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace deep_sleep {
|
||||
|
||||
@@ -57,13 +61,16 @@ class DeepSleepComponent : public Component {
|
||||
public:
|
||||
/// Set the duration in ms the component should sleep once it's in deep sleep mode.
|
||||
void set_sleep_duration(uint32_t time_ms);
|
||||
#ifdef USE_ESP32
|
||||
#if defined(USE_ESP32)
|
||||
/** Set the pin to wake up to on the ESP32 once it's in deep sleep mode.
|
||||
* Use the inverted property to set the wakeup level.
|
||||
*/
|
||||
void set_wakeup_pin(InternalGPIOPin *pin) { this->wakeup_pin_ = pin; }
|
||||
|
||||
void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode);
|
||||
#endif
|
||||
|
||||
#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3)
|
||||
|
||||
void set_ext1_wakeup(Ext1Wakeup ext1_wakeup);
|
||||
|
||||
@@ -113,15 +120,71 @@ template<typename... Ts> class EnterDeepSleepAction : public Action<Ts...> {
|
||||
EnterDeepSleepAction(DeepSleepComponent *deep_sleep) : deep_sleep_(deep_sleep) {}
|
||||
TEMPLATABLE_VALUE(uint32_t, sleep_duration);
|
||||
|
||||
#ifdef USE_TIME
|
||||
void set_until(uint8_t hour, uint8_t minute, uint8_t second) {
|
||||
this->hour_ = hour;
|
||||
this->minute_ = minute;
|
||||
this->second_ = second;
|
||||
}
|
||||
|
||||
void set_time(time::RealTimeClock *time) { this->time_ = time; }
|
||||
#endif
|
||||
|
||||
void play(Ts... x) override {
|
||||
if (this->sleep_duration_.has_value()) {
|
||||
this->deep_sleep_->set_sleep_duration(this->sleep_duration_.value(x...));
|
||||
}
|
||||
#ifdef USE_TIME
|
||||
|
||||
if (this->hour_.has_value()) {
|
||||
auto time = this->time_->now();
|
||||
const uint32_t timestamp_now = time.timestamp;
|
||||
|
||||
bool after_time = false;
|
||||
if (time.hour > this->hour_) {
|
||||
after_time = true;
|
||||
} else {
|
||||
if (time.hour == this->hour_) {
|
||||
if (time.minute > this->minute_) {
|
||||
after_time = true;
|
||||
} else {
|
||||
if (time.minute == this->minute_) {
|
||||
if (time.second > this->second_) {
|
||||
after_time = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time.hour = *this->hour_;
|
||||
time.minute = *this->minute_;
|
||||
time.second = *this->second_;
|
||||
time.recalc_timestamp_utc();
|
||||
|
||||
time_t timestamp = time.timestamp; // timestamp in local time zone
|
||||
|
||||
if (after_time)
|
||||
timestamp += 60 * 60 * 24;
|
||||
|
||||
int32_t offset = time::ESPTime::timezone_offset();
|
||||
timestamp -= offset; // Change timestamp to utc
|
||||
const uint32_t ms_left = (timestamp - timestamp_now) * 1000;
|
||||
this->deep_sleep_->set_sleep_duration(ms_left);
|
||||
}
|
||||
#endif
|
||||
this->deep_sleep_->begin_sleep(true);
|
||||
}
|
||||
|
||||
protected:
|
||||
DeepSleepComponent *deep_sleep_;
|
||||
#ifdef USE_TIME
|
||||
optional<uint8_t> hour_;
|
||||
optional<uint8_t> minute_;
|
||||
optional<uint8_t> second_;
|
||||
|
||||
time::RealTimeClock *time_;
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename... Ts> class PreventDeepSleepAction : public Action<Ts...> {
|
||||
|
||||
@@ -12,6 +12,7 @@ using namespace esphome::cover;
|
||||
CoverTraits EndstopCover::get_traits() {
|
||||
auto traits = CoverTraits();
|
||||
traits.set_supports_position(true);
|
||||
traits.set_supports_toggle(true);
|
||||
traits.set_is_assumed_state(false);
|
||||
return traits;
|
||||
}
|
||||
@@ -20,6 +21,20 @@ void EndstopCover::control(const CoverCall &call) {
|
||||
this->start_direction_(COVER_OPERATION_IDLE);
|
||||
this->publish_state();
|
||||
}
|
||||
if (call.get_toggle().has_value()) {
|
||||
if (this->current_operation != COVER_OPERATION_IDLE) {
|
||||
this->start_direction_(COVER_OPERATION_IDLE);
|
||||
this->publish_state();
|
||||
} else {
|
||||
if (this->position == COVER_CLOSED || this->last_operation_ == COVER_OPERATION_CLOSING) {
|
||||
this->target_position_ = COVER_OPEN;
|
||||
this->start_direction_(COVER_OPERATION_OPENING);
|
||||
} else {
|
||||
this->target_position_ = COVER_CLOSED;
|
||||
this->start_direction_(COVER_OPERATION_CLOSING);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (call.get_position().has_value()) {
|
||||
auto pos = *call.get_position();
|
||||
if (pos == this->position) {
|
||||
@@ -125,9 +140,11 @@ void EndstopCover::start_direction_(CoverOperation dir) {
|
||||
trig = this->stop_trigger_;
|
||||
break;
|
||||
case COVER_OPERATION_OPENING:
|
||||
this->last_operation_ = dir;
|
||||
trig = this->open_trigger_;
|
||||
break;
|
||||
case COVER_OPERATION_CLOSING:
|
||||
this->last_operation_ = dir;
|
||||
trig = this->close_trigger_;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -51,6 +51,7 @@ class EndstopCover : public cover::Cover, public Component {
|
||||
uint32_t start_dir_time_{0};
|
||||
uint32_t last_publish_time_{0};
|
||||
float target_position_{0};
|
||||
cover::CoverOperation last_operation_{cover::COVER_OPERATION_OPENING};
|
||||
};
|
||||
|
||||
} // namespace endstop
|
||||
|
||||
@@ -61,12 +61,30 @@ def set_core_data(config):
|
||||
return config
|
||||
|
||||
|
||||
def get_esp32_variant():
|
||||
return CORE.data[KEY_ESP32][KEY_VARIANT]
|
||||
def get_esp32_variant(core_obj=None):
|
||||
return (core_obj or CORE).data[KEY_ESP32][KEY_VARIANT]
|
||||
|
||||
|
||||
def is_esp32c3():
|
||||
return get_esp32_variant() == VARIANT_ESP32C3
|
||||
def only_on_variant(*, supported=None, unsupported=None):
|
||||
"""Config validator for features only available on some ESP32 variants."""
|
||||
if supported is not None and not isinstance(supported, list):
|
||||
supported = [supported]
|
||||
if unsupported is not None and not isinstance(unsupported, list):
|
||||
unsupported = [unsupported]
|
||||
|
||||
def validator_(obj):
|
||||
variant = get_esp32_variant()
|
||||
if supported is not None and variant not in supported:
|
||||
raise cv.Invalid(
|
||||
f"This feature is only available on {', '.join(supported)}"
|
||||
)
|
||||
if unsupported is not None and variant in unsupported:
|
||||
raise cv.Invalid(
|
||||
f"This feature is not available on {', '.join(unsupported)}"
|
||||
)
|
||||
return obj
|
||||
|
||||
return validator_
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# Source https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1005864664
|
||||
|
||||
import esptool
|
||||
import os
|
||||
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||
import esptool
|
||||
else:
|
||||
import subprocess
|
||||
from SCons.Script import ARGUMENTS
|
||||
|
||||
# pylint: disable=E0602
|
||||
@@ -42,8 +46,11 @@ def esp32_create_combined_bin(source, target, env):
|
||||
print()
|
||||
print(f"Using esptool.py arguments: {' '.join(cmd)}")
|
||||
print()
|
||||
esptool.main(cmd)
|
||||
|
||||
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||
esptool.main(cmd)
|
||||
else:
|
||||
subprocess.run(["esptool.py", *cmd])
|
||||
|
||||
# pylint: disable=E0602
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa
|
||||
|
||||
@@ -262,6 +262,9 @@ void ESP32BLETracker::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (auto *client : global_esp32_ble_tracker->clients_) {
|
||||
client->gap_event_handler(event, param);
|
||||
}
|
||||
}
|
||||
|
||||
void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m) {
|
||||
|
||||
@@ -155,6 +155,7 @@ class ESPBTClient : public ESPBTDeviceListener {
|
||||
public:
|
||||
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) = 0;
|
||||
virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
|
||||
virtual void connect() = 0;
|
||||
void set_state(ClientState st) { this->state_ = st; }
|
||||
ClientState state() const { return state_; }
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
import functools
|
||||
from pathlib import Path
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
import requests
|
||||
|
||||
from esphome import core
|
||||
from esphome.components import display
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_FILE, CONF_GLYPHS, CONF_ID, CONF_RAW_DATA_ID, CONF_SIZE
|
||||
from esphome.const import (
|
||||
CONF_FAMILY,
|
||||
CONF_FILE,
|
||||
CONF_GLYPHS,
|
||||
CONF_ID,
|
||||
CONF_RAW_DATA_ID,
|
||||
CONF_TYPE,
|
||||
CONF_SIZE,
|
||||
CONF_PATH,
|
||||
CONF_WEIGHT,
|
||||
)
|
||||
from esphome.core import CORE, HexInt
|
||||
|
||||
|
||||
DOMAIN = "font"
|
||||
DEPENDENCIES = ["display"]
|
||||
MULTI_CONF = True
|
||||
|
||||
@@ -71,6 +88,128 @@ def validate_truetype_file(value):
|
||||
return cv.file_(value)
|
||||
|
||||
|
||||
def _compute_gfonts_local_path(value) -> Path:
|
||||
name = f"{value[CONF_FAMILY]}@{value[CONF_WEIGHT]}@{value[CONF_ITALIC]}@v1"
|
||||
base_dir = Path(CORE.config_dir) / ".esphome" / DOMAIN
|
||||
h = hashlib.new("sha256")
|
||||
h.update(name.encode())
|
||||
return base_dir / h.hexdigest()[:8] / "font.ttf"
|
||||
|
||||
|
||||
TYPE_LOCAL = "local"
|
||||
TYPE_GFONTS = "gfonts"
|
||||
LOCAL_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_PATH): validate_truetype_file,
|
||||
}
|
||||
)
|
||||
CONF_ITALIC = "italic"
|
||||
FONT_WEIGHTS = {
|
||||
"thin": 100,
|
||||
"extra-light": 200,
|
||||
"light": 300,
|
||||
"regular": 400,
|
||||
"medium": 500,
|
||||
"semi-bold": 600,
|
||||
"bold": 700,
|
||||
"extra-bold": 800,
|
||||
"black": 900,
|
||||
}
|
||||
|
||||
|
||||
def validate_weight_name(value):
|
||||
return FONT_WEIGHTS[cv.one_of(*FONT_WEIGHTS, lower=True, space="-")(value)]
|
||||
|
||||
|
||||
def download_gfonts(value):
|
||||
wght = value[CONF_WEIGHT]
|
||||
if value[CONF_ITALIC]:
|
||||
wght = f"1,{wght}"
|
||||
name = f"{value[CONF_FAMILY]}@{value[CONF_WEIGHT]}"
|
||||
url = f"https://fonts.googleapis.com/css2?family={value[CONF_FAMILY]}:wght@{wght}"
|
||||
|
||||
path = _compute_gfonts_local_path(value)
|
||||
if path.is_file():
|
||||
return value
|
||||
try:
|
||||
req = requests.get(url)
|
||||
req.raise_for_status()
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise cv.Invalid(
|
||||
f"Could not download font for {name}, please check the fonts exists "
|
||||
f"at google fonts ({e})"
|
||||
)
|
||||
match = re.search(r"src:\s+url\((.+)\)\s+format\('truetype'\);", req.text)
|
||||
if match is None:
|
||||
raise cv.Invalid(
|
||||
f"Could not extract ttf file from gfonts response for {name}, "
|
||||
f"please report this."
|
||||
)
|
||||
|
||||
ttf_url = match.group(1)
|
||||
try:
|
||||
req = requests.get(ttf_url)
|
||||
req.raise_for_status()
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise cv.Invalid(f"Could not download ttf file for {name} ({ttf_url}): {e}")
|
||||
|
||||
path.parent.mkdir(exist_ok=True, parents=True)
|
||||
path.write_bytes(req.content)
|
||||
return value
|
||||
|
||||
|
||||
GFONTS_SCHEMA = cv.All(
|
||||
{
|
||||
cv.Required(CONF_FAMILY): cv.string_strict,
|
||||
cv.Optional(CONF_WEIGHT, default="regular"): cv.Any(
|
||||
cv.int_, validate_weight_name
|
||||
),
|
||||
cv.Optional(CONF_ITALIC, default=False): cv.boolean,
|
||||
},
|
||||
download_gfonts,
|
||||
)
|
||||
|
||||
|
||||
def validate_file_shorthand(value):
|
||||
value = cv.string_strict(value)
|
||||
if value.startswith("gfonts://"):
|
||||
match = re.match(r"^gfonts://([^@]+)(@.+)?$", value)
|
||||
if match is None:
|
||||
raise cv.Invalid("Could not parse gfonts shorthand syntax, please check it")
|
||||
family = match.group(1)
|
||||
weight = match.group(2)
|
||||
data = {
|
||||
CONF_TYPE: TYPE_GFONTS,
|
||||
CONF_FAMILY: family,
|
||||
}
|
||||
if weight is not None:
|
||||
data[CONF_WEIGHT] = weight[1:]
|
||||
return FILE_SCHEMA(data)
|
||||
return FILE_SCHEMA(
|
||||
{
|
||||
CONF_TYPE: TYPE_LOCAL,
|
||||
CONF_PATH: value,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
TYPED_FILE_SCHEMA = cv.typed_schema(
|
||||
{
|
||||
TYPE_LOCAL: LOCAL_SCHEMA,
|
||||
TYPE_GFONTS: GFONTS_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _file_schema(value):
|
||||
if isinstance(value, str):
|
||||
return validate_file_shorthand(value)
|
||||
return TYPED_FILE_SCHEMA(value)
|
||||
|
||||
|
||||
FILE_SCHEMA = cv.Schema(_file_schema)
|
||||
|
||||
|
||||
DEFAULT_GLYPHS = (
|
||||
' !"%()+=,-.:/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°'
|
||||
)
|
||||
@@ -79,7 +218,7 @@ CONF_RAW_GLYPH_ID = "raw_glyph_id"
|
||||
FONT_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(Font),
|
||||
cv.Required(CONF_FILE): validate_truetype_file,
|
||||
cv.Required(CONF_FILE): FILE_SCHEMA,
|
||||
cv.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs,
|
||||
cv.Optional(CONF_SIZE, default=20): cv.int_range(min=1),
|
||||
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
|
||||
@@ -93,9 +232,13 @@ CONFIG_SCHEMA = cv.All(validate_pillow_installed, FONT_SCHEMA)
|
||||
async def to_code(config):
|
||||
from PIL import ImageFont
|
||||
|
||||
path = CORE.relative_config_path(config[CONF_FILE])
|
||||
conf = config[CONF_FILE]
|
||||
if conf[CONF_TYPE] == TYPE_LOCAL:
|
||||
path = CORE.relative_config_path(conf[CONF_PATH])
|
||||
elif conf[CONF_TYPE] == TYPE_GFONTS:
|
||||
path = _compute_gfonts_local_path(conf)
|
||||
try:
|
||||
font = ImageFont.truetype(path, config[CONF_SIZE])
|
||||
font = ImageFont.truetype(str(path), config[CONF_SIZE])
|
||||
except Exception as e:
|
||||
raise core.EsphomeError(f"Could not load truetype file {path}: {e}")
|
||||
|
||||
|
||||
@@ -17,29 +17,29 @@ const uint8_t FUJITSU_GENERAL_TEMP_MAX = 30; // Celsius
|
||||
* turn
|
||||
* on temp mode fan swing
|
||||
* * | | | | | | *
|
||||
*
|
||||
*
|
||||
* temperatures 1 1248 124 124 1
|
||||
* auto auto 18 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000100 00000000 00000000 00000000 00000000 00000000 00000100 11110001
|
||||
* auto auto 19 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10001100 00000000 00000000 00000000 00000000 00000000 00000100 11111110
|
||||
* auto auto 30 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 00000000 00000000 00000000 00000000 00000000 00000100 11110011
|
||||
*
|
||||
*
|
||||
* on flag:
|
||||
* on at 16 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000000 00100000 00000000 00000000 00000000 00000000 00000100 11010101
|
||||
* down to 16 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000000 00100000 00000000 00000000 00000000 00000000 00000100 00110101
|
||||
*
|
||||
*
|
||||
* mode options:
|
||||
* auto auto 30 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 00000000 00000000 00000000 00000000 00000000 00000100 11110011
|
||||
* cool auto 30 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 10000000 00000000 00000000 00000000 00000000 00000100 01110011
|
||||
* dry auto 30 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 01000000 00000000 00000000 00000000 00000000 00000100 10110011
|
||||
* fan (auto) (30) 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 11000000 00000000 00000000 00000000 00000000 00000100 00110011
|
||||
* heat auto 30 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 00100000 00000000 00000000 00000000 00000000 00000100 11010011
|
||||
*
|
||||
*
|
||||
* fan options:
|
||||
* heat 30 high 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 10000111 00100000 10000000 00000000 00000000 00000000 00000100 01010011
|
||||
* heat 30 med 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000111 00100000 01000000 00000000 00000000 00000000 00000100 01010011
|
||||
* heat 30 low 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000111 00100000 11000000 00000000 00000000 00000000 00000100 10010011
|
||||
* heat 30 quiet 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000111 00100000 00100000 00000000 00000000 00000000 00000100 00010011
|
||||
*
|
||||
*
|
||||
* swing options:
|
||||
* heat 30 swing vert 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000111 00100000 00101000 00000000 00000000 00000000 00000100 00011101
|
||||
* heat 30 noswing 00101000 11000110 00000000 00001000 00001000 01111111 10010000 00001100 00000111 00100000 00100000 00000000 00000000 00000000 00000100 00010011
|
||||
|
||||
@@ -7,9 +7,11 @@ namespace growatt_solar {
|
||||
static const char *const TAG = "growatt_solar";
|
||||
|
||||
static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04;
|
||||
static const uint8_t MODBUS_REGISTER_COUNT = 33;
|
||||
static const uint8_t MODBUS_REGISTER_COUNT[] = {33, 95}; // indexed with enum GrowattProtocolVersion
|
||||
|
||||
void GrowattSolar::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); }
|
||||
void GrowattSolar::update() {
|
||||
this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT[this->protocol_version_]);
|
||||
}
|
||||
|
||||
void GrowattSolar::on_modbus_data(const std::vector<uint8_t> &data) {
|
||||
auto publish_1_reg_sensor_state = [&](sensor::Sensor *sensor, size_t i, float unit) -> void {
|
||||
@@ -27,37 +29,76 @@ void GrowattSolar::on_modbus_data(const std::vector<uint8_t> &data) {
|
||||
sensor->publish_state(value);
|
||||
};
|
||||
|
||||
publish_1_reg_sensor_state(this->inverter_status_, 0, 1);
|
||||
switch (this->protocol_version_) {
|
||||
case RTU: {
|
||||
publish_1_reg_sensor_state(this->inverter_status_, 0, 1);
|
||||
|
||||
publish_2_reg_sensor_state(this->pv_active_power_sensor_, 1, 2, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->pv_active_power_sensor_, 1, 2, ONE_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, 3, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, 4, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, 5, 6, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, 3, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, 4, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, 5, 6, ONE_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, 7, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, 8, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, 9, 10, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, 7, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, 8, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, 9, 10, ONE_DEC_UNIT);
|
||||
|
||||
publish_2_reg_sensor_state(this->grid_active_power_sensor_, 11, 12, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->grid_frequency_sensor_, 13, TWO_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->grid_active_power_sensor_, 11, 12, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->grid_frequency_sensor_, 13, TWO_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, 14, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[0].current_sensor_, 15, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, 16, 17, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, 14, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[0].current_sensor_, 15, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, 16, 17, ONE_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, 18, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[1].current_sensor_, 19, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, 20, 21, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, 18, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[1].current_sensor_, 19, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, 20, 21, ONE_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, 22, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[2].current_sensor_, 23, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, 24, 25, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, 22, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[2].current_sensor_, 23, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, 24, 25, ONE_DEC_UNIT);
|
||||
|
||||
publish_2_reg_sensor_state(this->today_production_, 26, 27, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->total_energy_production_, 28, 29, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->today_production_, 26, 27, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->total_energy_production_, 28, 29, ONE_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->inverter_module_temp_, 32, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->inverter_module_temp_, 32, ONE_DEC_UNIT);
|
||||
break;
|
||||
}
|
||||
case RTU2: {
|
||||
publish_1_reg_sensor_state(this->inverter_status_, 0, 1);
|
||||
|
||||
publish_2_reg_sensor_state(this->pv_active_power_sensor_, 1, 2, ONE_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, 3, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, 4, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, 5, 6, ONE_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, 7, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, 8, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, 9, 10, ONE_DEC_UNIT);
|
||||
|
||||
publish_2_reg_sensor_state(this->grid_active_power_sensor_, 35, 36, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->grid_frequency_sensor_, 37, TWO_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, 38, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[0].current_sensor_, 39, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, 40, 41, ONE_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, 42, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[1].current_sensor_, 43, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, 44, 45, ONE_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, 46, ONE_DEC_UNIT);
|
||||
publish_1_reg_sensor_state(this->phases_[2].current_sensor_, 47, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, 48, 49, ONE_DEC_UNIT);
|
||||
|
||||
publish_2_reg_sensor_state(this->today_production_, 53, 54, ONE_DEC_UNIT);
|
||||
publish_2_reg_sensor_state(this->total_energy_production_, 55, 56, ONE_DEC_UNIT);
|
||||
|
||||
publish_1_reg_sensor_state(this->inverter_module_temp_, 93, ONE_DEC_UNIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GrowattSolar::dump_config() {
|
||||
|
||||
@@ -10,12 +10,19 @@ namespace growatt_solar {
|
||||
static const float TWO_DEC_UNIT = 0.01;
|
||||
static const float ONE_DEC_UNIT = 0.1;
|
||||
|
||||
enum GrowattProtocolVersion {
|
||||
RTU = 0,
|
||||
RTU2,
|
||||
};
|
||||
|
||||
class GrowattSolar : public PollingComponent, public modbus::ModbusDevice {
|
||||
public:
|
||||
void update() override;
|
||||
void on_modbus_data(const std::vector<uint8_t> &data) override;
|
||||
void dump_config() override;
|
||||
|
||||
void set_protocol_version(GrowattProtocolVersion protocol_version) { this->protocol_version_ = protocol_version; }
|
||||
|
||||
void set_inverter_status_sensor(sensor::Sensor *sensor) { this->inverter_status_ = sensor; }
|
||||
|
||||
void set_grid_frequency_sensor(sensor::Sensor *sensor) { this->grid_frequency_sensor_ = sensor; }
|
||||
@@ -67,6 +74,7 @@ class GrowattSolar : public PollingComponent, public modbus::ModbusDevice {
|
||||
sensor::Sensor *today_production_{nullptr};
|
||||
sensor::Sensor *total_energy_production_{nullptr};
|
||||
sensor::Sensor *inverter_module_temp_{nullptr};
|
||||
GrowattProtocolVersion protocol_version_;
|
||||
};
|
||||
|
||||
} // namespace growatt_solar
|
||||
|
||||
@@ -39,7 +39,7 @@ UNIT_MILLIAMPERE = "mA"
|
||||
CONF_INVERTER_STATUS = "inverter_status"
|
||||
CONF_PV_ACTIVE_POWER = "pv_active_power"
|
||||
CONF_INVERTER_MODULE_TEMP = "inverter_module_temp"
|
||||
|
||||
CONF_PROTOCOL_VERSION = "protocol_version"
|
||||
|
||||
AUTO_LOAD = ["modbus"]
|
||||
CODEOWNERS = ["@leeuwte"]
|
||||
@@ -95,10 +95,20 @@ PV_SCHEMA = cv.Schema(
|
||||
{cv.Optional(sensor): schema for sensor, schema in PV_SENSORS.items()}
|
||||
)
|
||||
|
||||
GrowattProtocolVersion = growatt_solar_ns.enum("GrowattProtocolVersion")
|
||||
PROTOCOL_VERSIONS = {
|
||||
"RTU": GrowattProtocolVersion.RTU,
|
||||
"RTU2": GrowattProtocolVersion.RTU2,
|
||||
}
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(GrowattSolar),
|
||||
cv.Optional(CONF_PROTOCOL_VERSION, default="RTU"): cv.enum(
|
||||
PROTOCOL_VERSIONS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_PHASE_A): PHASE_SCHEMA,
|
||||
cv.Optional(CONF_PHASE_B): PHASE_SCHEMA,
|
||||
cv.Optional(CONF_PHASE_C): PHASE_SCHEMA,
|
||||
@@ -152,6 +162,8 @@ async def to_code(config):
|
||||
await cg.register_component(var, config)
|
||||
await modbus.register_modbus_device(var, config)
|
||||
|
||||
cg.add(var.set_protocol_version(config[CONF_PROTOCOL_VERSION]))
|
||||
|
||||
if CONF_INVERTER_STATUS in config:
|
||||
sens = await sensor.new_sensor(config[CONF_INVERTER_STATUS])
|
||||
cg.add(var.set_inverter_status_sensor(sens))
|
||||
|
||||
@@ -25,6 +25,7 @@ PROTOCOLS = {
|
||||
"daikin_arc417": Protocol.PROTOCOL_DAIKIN_ARC417,
|
||||
"daikin_arc480": Protocol.PROTOCOL_DAIKIN_ARC480,
|
||||
"daikin": Protocol.PROTOCOL_DAIKIN,
|
||||
"electroluxyal": Protocol.PROTOCOL_ELECTROLUXYAL,
|
||||
"fuego": Protocol.PROTOCOL_FUEGO,
|
||||
"fujitsu_awyz": Protocol.PROTOCOL_FUJITSU_AWYZ,
|
||||
"gree": Protocol.PROTOCOL_GREE,
|
||||
@@ -112,6 +113,4 @@ def to_code(config):
|
||||
cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE]))
|
||||
cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE]))
|
||||
|
||||
# PIO isn't updating releases, so referencing the release tag directly. See:
|
||||
# https://github.com/ToniA/arduino-heatpumpir/commit/0948c619d86407a4e50e8db2f3c193e0576c86fd
|
||||
cg.add_library("", "", "https://github.com/ToniA/arduino-heatpumpir.git#1.0.18")
|
||||
cg.add_library("tonia/HeatpumpIR", "1.0.20")
|
||||
|
||||
@@ -20,6 +20,7 @@ const std::map<Protocol, std::function<HeatpumpIR *()>> PROTOCOL_CONSTRUCTOR_MAP
|
||||
{PROTOCOL_DAIKIN_ARC417, []() { return new DaikinHeatpumpARC417IR(); }}, // NOLINT
|
||||
{PROTOCOL_DAIKIN_ARC480, []() { return new DaikinHeatpumpARC480A14IR(); }}, // NOLINT
|
||||
{PROTOCOL_DAIKIN, []() { return new DaikinHeatpumpIR(); }}, // NOLINT
|
||||
{PROTOCOL_ELECTROLUXYAL, []() { return new ElectroluxYALHeatpumpIR(); }}, // NOLINT
|
||||
{PROTOCOL_FUEGO, []() { return new FuegoHeatpumpIR(); }}, // NOLINT
|
||||
{PROTOCOL_FUJITSU_AWYZ, []() { return new FujitsuHeatpumpIR(); }}, // NOLINT
|
||||
{PROTOCOL_GREE, []() { return new GreeGenericHeatpumpIR(); }}, // NOLINT
|
||||
|
||||
@@ -20,6 +20,7 @@ enum Protocol {
|
||||
PROTOCOL_DAIKIN_ARC417,
|
||||
PROTOCOL_DAIKIN_ARC480,
|
||||
PROTOCOL_DAIKIN,
|
||||
PROTOCOL_ELECTROLUXYAL,
|
||||
PROTOCOL_FUEGO,
|
||||
PROTOCOL_FUJITSU_AWYZ,
|
||||
PROTOCOL_GREE,
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace hm3301 {
|
||||
|
||||
class AbstractAQICalculator {
|
||||
public:
|
||||
virtual uint8_t get_aqi(uint16_t pm2_5_value, uint16_t pm10_0_value) = 0;
|
||||
virtual uint16_t get_aqi(uint16_t pm2_5_value, uint16_t pm10_0_value) = 0;
|
||||
};
|
||||
|
||||
} // namespace hm3301
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace hm3301 {
|
||||
|
||||
class AQICalculator : public AbstractAQICalculator {
|
||||
public:
|
||||
uint8_t get_aqi(uint16_t pm2_5_value, uint16_t pm10_0_value) override {
|
||||
uint16_t get_aqi(uint16_t pm2_5_value, uint16_t pm10_0_value) override {
|
||||
int pm2_5_index = calculate_index_(pm2_5_value, pm2_5_calculation_grid_);
|
||||
int pm10_0_index = calculate_index_(pm10_0_value, pm10_0_calculation_grid_);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace hm3301 {
|
||||
|
||||
class CAQICalculator : public AbstractAQICalculator {
|
||||
public:
|
||||
uint8_t get_aqi(uint16_t pm2_5_value, uint16_t pm10_0_value) override {
|
||||
uint16_t get_aqi(uint16_t pm2_5_value, uint16_t pm10_0_value) override {
|
||||
int pm2_5_index = calculate_index_(pm2_5_value, pm2_5_calculation_grid_);
|
||||
int pm10_0_index = calculate_index_(pm10_0_value, pm10_0_calculation_grid_);
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ void HM3301Component::update() {
|
||||
pm_10_0_value = get_sensor_value_(data_buffer_, PM_10_0_VALUE_INDEX);
|
||||
}
|
||||
|
||||
int8_t aqi_value = -1;
|
||||
int16_t aqi_value = -1;
|
||||
if (this->aqi_sensor_ != nullptr && pm_2_5_value != -1 && pm_10_0_value != -1) {
|
||||
AbstractAQICalculator *calculator = this->aqi_calculator_factory_.get_calculator(this->aqi_calc_type_);
|
||||
aqi_value = calculator->get_aqi(pm_2_5_value, pm_10_0_value);
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ATTRIBUTE, CONF_ENTITY_ID, CONF_INTERNAL
|
||||
|
||||
CODEOWNERS = ["@OttoWinter"]
|
||||
homeassistant_ns = cg.esphome_ns.namespace("homeassistant")
|
||||
|
||||
HOME_ASSISTANT_IMPORT_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ENTITY_ID): cv.entity_id,
|
||||
cv.Optional(CONF_ATTRIBUTE): cv.string,
|
||||
cv.Optional(CONF_INTERNAL, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def setup_home_assistant_entity(var, config):
|
||||
cg.add(var.set_entity_id(config[CONF_ENTITY_ID]))
|
||||
if CONF_ATTRIBUTE in config:
|
||||
cg.add(var.set_attribute(config[CONF_ATTRIBUTE]))
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import CONF_ATTRIBUTE, CONF_ENTITY_ID
|
||||
from .. import homeassistant_ns
|
||||
|
||||
from .. import (
|
||||
HOME_ASSISTANT_IMPORT_SCHEMA,
|
||||
homeassistant_ns,
|
||||
setup_home_assistant_entity,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["api"]
|
||||
|
||||
HomeassistantBinarySensor = homeassistant_ns.class_(
|
||||
"HomeassistantBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
binary_sensor.binary_sensor_schema(HomeassistantBinarySensor)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_ENTITY_ID): cv.entity_id,
|
||||
cv.Optional(CONF_ATTRIBUTE): cv.string,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(HomeassistantBinarySensor).extend(
|
||||
HOME_ASSISTANT_IMPORT_SCHEMA
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_entity_id(config[CONF_ENTITY_ID]))
|
||||
if CONF_ATTRIBUTE in config:
|
||||
cg.add(var.set_attribute(config[CONF_ATTRIBUTE]))
|
||||
setup_home_assistant_entity(var, config)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_ATTRIBUTE,
|
||||
CONF_ENTITY_ID,
|
||||
CONF_ID,
|
||||
|
||||
from .. import (
|
||||
HOME_ASSISTANT_IMPORT_SCHEMA,
|
||||
homeassistant_ns,
|
||||
setup_home_assistant_entity,
|
||||
)
|
||||
from .. import homeassistant_ns
|
||||
|
||||
DEPENDENCIES = ["api"]
|
||||
|
||||
@@ -14,19 +13,12 @@ HomeassistantSensor = homeassistant_ns.class_(
|
||||
"HomeassistantSensor", sensor.Sensor, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = sensor.sensor_schema(HomeassistantSensor, accuracy_decimals=1,).extend(
|
||||
{
|
||||
cv.Required(CONF_ENTITY_ID): cv.entity_id,
|
||||
cv.Optional(CONF_ATTRIBUTE): cv.string,
|
||||
}
|
||||
CONFIG_SCHEMA = sensor.sensor_schema(HomeassistantSensor, accuracy_decimals=1).extend(
|
||||
HOME_ASSISTANT_IMPORT_SCHEMA
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
cg.add(var.set_entity_id(config[CONF_ENTITY_ID]))
|
||||
if CONF_ATTRIBUTE in config:
|
||||
cg.add(var.set_attribute(config[CONF_ATTRIBUTE]))
|
||||
setup_home_assistant_entity(var, config)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor
|
||||
from esphome.const import CONF_ATTRIBUTE, CONF_ENTITY_ID
|
||||
|
||||
from .. import homeassistant_ns
|
||||
from .. import (
|
||||
HOME_ASSISTANT_IMPORT_SCHEMA,
|
||||
homeassistant_ns,
|
||||
setup_home_assistant_entity,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["api"]
|
||||
|
||||
@@ -11,19 +13,12 @@ HomeassistantTextSensor = homeassistant_ns.class_(
|
||||
"HomeassistantTextSensor", text_sensor.TextSensor, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = text_sensor.text_sensor_schema().extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HomeassistantTextSensor),
|
||||
cv.Required(CONF_ENTITY_ID): cv.entity_id,
|
||||
cv.Optional(CONF_ATTRIBUTE): cv.string,
|
||||
}
|
||||
CONFIG_SCHEMA = text_sensor.text_sensor_schema(HomeassistantTextSensor).extend(
|
||||
HOME_ASSISTANT_IMPORT_SCHEMA
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await text_sensor.new_text_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_entity_id(config[CONF_ENTITY_ID]))
|
||||
if CONF_ATTRIBUTE in config:
|
||||
cg.add(var.set_attribute(config[CONF_ATTRIBUTE]))
|
||||
setup_home_assistant_entity(var, config)
|
||||
|
||||
1
esphome/components/honeywellabp/__init__.py
Normal file
1
esphome/components/honeywellabp/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Support for Honeywell ABP"""
|
||||
102
esphome/components/honeywellabp/honeywellabp.cpp
Normal file
102
esphome/components/honeywellabp/honeywellabp.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "honeywellabp.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace honeywellabp {
|
||||
|
||||
static const char *const TAG = "honeywellabp";
|
||||
|
||||
const float MIN_COUNT = 1638.4; // 1638 counts (10% of 2^14 counts or 0x0666)
|
||||
const float MAX_COUNT = 14745.6; // 14745 counts (90% of 2^14 counts or 0x3999)
|
||||
|
||||
void HONEYWELLABPSensor::setup() {
|
||||
ESP_LOGD(TAG, "Setting up Honeywell ABP Sensor ");
|
||||
this->spi_setup();
|
||||
}
|
||||
|
||||
uint8_t HONEYWELLABPSensor::readsensor_() {
|
||||
// Polls the sensor for new data.
|
||||
// transfer 4 bytes (the last two are temperature only used by some sensors)
|
||||
this->enable();
|
||||
buf_[0] = this->read_byte();
|
||||
buf_[1] = this->read_byte();
|
||||
buf_[2] = this->read_byte();
|
||||
buf_[3] = this->read_byte();
|
||||
this->disable();
|
||||
|
||||
// Check the status codes:
|
||||
// status = 0 : normal operation
|
||||
// status = 1 : device in command mode
|
||||
// status = 2 : stale data
|
||||
// status = 3 : diagnostic condition
|
||||
status_ = buf_[0] >> 6 & 0x3;
|
||||
ESP_LOGV(TAG, "Sensor status %d", status_);
|
||||
|
||||
// if device is normal and there is new data, bitmask and save the raw data
|
||||
if (status_ == 0) {
|
||||
// 14 - bit pressure is the last 6 bits of byte 0 (high bits) & all of byte 1 (lowest 8 bits)
|
||||
pressure_count_ = ((uint16_t)(buf_[0]) << 8 & 0x3F00) | ((uint16_t)(buf_[1]) & 0xFF);
|
||||
// 11 - bit temperature is all of byte 2 (lowest 8 bits) and the first three bits of byte 3
|
||||
temperature_count_ = (((uint16_t)(buf_[2]) << 3) & 0x7F8) | (((uint16_t)(buf_[3]) >> 5) & 0x7);
|
||||
ESP_LOGV(TAG, "Sensor pressure_count_ %d", pressure_count_);
|
||||
ESP_LOGV(TAG, "Sensor temperature_count_ %d", temperature_count_);
|
||||
}
|
||||
return status_;
|
||||
}
|
||||
|
||||
// returns status
|
||||
uint8_t HONEYWELLABPSensor::readstatus_() { return status_; }
|
||||
|
||||
// The pressure value from the most recent reading in raw counts
|
||||
int HONEYWELLABPSensor::rawpressure_() { return pressure_count_; }
|
||||
|
||||
// The temperature value from the most recent reading in raw counts
|
||||
int HONEYWELLABPSensor::rawtemperature_() { return temperature_count_; }
|
||||
|
||||
// Converts a digital pressure measurement in counts to pressure measured
|
||||
float HONEYWELLABPSensor::countstopressure_(const int counts, const float min_pressure, const float max_pressure) {
|
||||
return ((((float) counts - MIN_COUNT) * (max_pressure - min_pressure)) / (MAX_COUNT - MIN_COUNT)) + min_pressure;
|
||||
}
|
||||
|
||||
// Converts a digital temperature measurement in counts to temperature in C
|
||||
// This will be invalid if sensore daoes not have temperature measurement capability
|
||||
float HONEYWELLABPSensor::countstotemperatures_(const int counts) { return (((float) counts / 2047.0) * 200.0) - 50.0; }
|
||||
|
||||
// Pressure value from the most recent reading in units
|
||||
float HONEYWELLABPSensor::read_pressure_() {
|
||||
return countstopressure_(pressure_count_, honeywellabp_min_pressure_, honeywellabp_max_pressure_);
|
||||
}
|
||||
|
||||
// Temperature value from the most recent reading in degrees C
|
||||
float HONEYWELLABPSensor::read_temperature_() { return countstotemperatures_(temperature_count_); }
|
||||
|
||||
void HONEYWELLABPSensor::update() {
|
||||
ESP_LOGV(TAG, "Update Honeywell ABP Sensor");
|
||||
if (readsensor_() == 0) {
|
||||
if (this->pressure_sensor_ != nullptr)
|
||||
this->pressure_sensor_->publish_state(read_pressure_() * 1.0);
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(read_temperature_() * 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
float HONEYWELLABPSensor::get_setup_priority() const { return setup_priority::LATE; }
|
||||
|
||||
void HONEYWELLABPSensor::dump_config() {
|
||||
// LOG_SENSOR("", "HONEYWELLABP", this);
|
||||
LOG_PIN(" CS Pin: ", this->cs_);
|
||||
ESP_LOGCONFIG(TAG, " Min Pressure Range: %0.1f", honeywellabp_min_pressure_);
|
||||
ESP_LOGCONFIG(TAG, " Max Pressure Range: %0.1f", honeywellabp_max_pressure_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void HONEYWELLABPSensor::set_honeywellabp_min_pressure(float min_pressure) {
|
||||
this->honeywellabp_min_pressure_ = min_pressure;
|
||||
}
|
||||
|
||||
void HONEYWELLABPSensor::set_honeywellabp_max_pressure(float max_pressure) {
|
||||
this->honeywellabp_max_pressure_ = max_pressure;
|
||||
}
|
||||
|
||||
} // namespace honeywellabp
|
||||
} // namespace esphome
|
||||
45
esphome/components/honeywellabp/honeywellabp.h
Normal file
45
esphome/components/honeywellabp/honeywellabp.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// for Honeywell ABP sensor
|
||||
// adapting code from https://github.com/vwls/Honeywell_pressure_sensors
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace honeywellabp {
|
||||
|
||||
class HONEYWELLABPSensor : public PollingComponent,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_200KHZ> {
|
||||
public:
|
||||
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
|
||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||
void setup() override;
|
||||
void update() override;
|
||||
float get_setup_priority() const override;
|
||||
void dump_config() override;
|
||||
void set_honeywellabp_min_pressure(float min_pressure);
|
||||
void set_honeywellabp_max_pressure(float max_pressure);
|
||||
|
||||
protected:
|
||||
float honeywellabp_min_pressure_ = 0.0;
|
||||
float honeywellabp_max_pressure_ = 0.0;
|
||||
uint8_t buf_[4]; // buffer to hold sensor data
|
||||
uint8_t status_ = 0; // byte to hold status information.
|
||||
int pressure_count_ = 0; // hold raw pressure data (14 - bit, 0 - 16384)
|
||||
int temperature_count_ = 0; // hold raw temperature data (11 - bit, 0 - 2048)
|
||||
sensor::Sensor *pressure_sensor_;
|
||||
sensor::Sensor *temperature_sensor_;
|
||||
uint8_t readsensor_();
|
||||
uint8_t readstatus_();
|
||||
int rawpressure_();
|
||||
int rawtemperature_();
|
||||
float countstopressure_(int counts, float min_pressure, float max_pressure);
|
||||
float countstotemperatures_(int counts);
|
||||
float read_pressure_();
|
||||
float read_temperature_();
|
||||
};
|
||||
|
||||
} // namespace honeywellabp
|
||||
} // namespace esphome
|
||||
70
esphome/components/honeywellabp/sensor.py
Normal file
70
esphome/components/honeywellabp/sensor.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.components import spi
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_PRESSURE,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["spi"]
|
||||
CODEOWNERS = ["@RubyBailey"]
|
||||
|
||||
CONF_MIN_PRESSURE = "min_pressure"
|
||||
CONF_MAX_PRESSURE = "max_pressure"
|
||||
|
||||
honeywellabp_ns = cg.esphome_ns.namespace("honeywellabp")
|
||||
HONEYWELLABPSensor = honeywellabp_ns.class_(
|
||||
"HONEYWELLABPSensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HONEYWELLABPSensor),
|
||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||
unit_of_measurement="psi",
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Required(CONF_MIN_PRESSURE): cv.float_,
|
||||
cv.Required(CONF_MAX_PRESSURE): cv.float_,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(spi.spi_device_schema(cs_pin_required=True))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
|
||||
if CONF_PRESSURE in config:
|
||||
conf = config[CONF_PRESSURE]
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_pressure_sensor(sens))
|
||||
cg.add(var.set_honeywellabp_min_pressure(conf[CONF_MIN_PRESSURE]))
|
||||
cg.add(var.set_honeywellabp_max_pressure(conf[CONF_MAX_PRESSURE]))
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
conf = config[CONF_TEMPERATURE]
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
11
esphome/components/hydreon_rgxx/__init__.py
Normal file
11
esphome/components/hydreon_rgxx/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
|
||||
CODEOWNERS = ["@functionpointer"]
|
||||
DEPENDENCIES = ["uart"]
|
||||
|
||||
hydreon_rgxx_ns = cg.esphome_ns.namespace("hydreon_rgxx")
|
||||
RGModel = hydreon_rgxx_ns.enum("RGModel")
|
||||
HydreonRGxxComponent = hydreon_rgxx_ns.class_(
|
||||
"HydreonRGxxComponent", cg.PollingComponent, uart.UARTDevice
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user