Compare commits
187 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
993044c870 | ||
|
|
a8c1b63edb | ||
|
|
db7d946e1b | ||
|
|
41d9059a2f | ||
|
|
e26e0d7c01 | ||
|
|
ad41c07a1f | ||
|
|
5732f3b044 | ||
|
|
712115b6ce | ||
|
|
9283559c6b | ||
|
|
6b393438e9 | ||
|
|
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 | ||
|
|
fc7348d46d | ||
|
|
8be2456c7e | ||
|
|
bb5f7249a6 | ||
|
|
7f7175b184 | ||
|
|
cf5c640ae4 | ||
|
|
6b9371d105 | ||
|
|
9a82057303 | ||
|
|
48584e94c4 | ||
|
|
fc94a5d0ee | ||
|
|
d8024a5928 | ||
|
|
2034ab4f6c | ||
|
|
24029cc918 | ||
|
|
9a9d5964ee | ||
|
|
4e4a512107 | ||
|
|
0729ed538e | ||
|
|
24b75b7ed6 | ||
|
|
58b70b42dd | ||
|
|
1496bc1b07 | ||
|
|
bfbf88b2ea | ||
|
|
e621b938e3 | ||
|
|
ec3618ecb8 | ||
|
|
792a24f38d | ||
|
|
652e8a015b | ||
|
|
59e6e798dd | ||
|
|
e5c2dbc7ec | ||
|
|
756f71c382 | ||
|
|
b7535693fa | ||
|
|
06a3505698 | ||
|
|
0372d17a11 | ||
|
|
4525588116 | ||
|
|
68e957c147 | ||
|
|
99f5ed1461 | ||
|
|
59f67796dc | ||
|
|
aafdfa933e | ||
|
|
3208c8ed1e | ||
|
|
6bf733e24e | ||
|
|
65d3e8fbfc | ||
|
|
a29d65d47c | ||
|
|
efa8f0730d | ||
|
|
0af1edefff | ||
|
|
023d26f521 | ||
|
|
5068619f1b | ||
|
|
5b2457af0b | ||
|
|
900b4f1af9 | ||
|
|
4c22a98b0b | ||
|
|
3b8ca80900 | ||
|
|
1ef6fd8fb0 | ||
|
|
942b0de7fd | ||
|
|
859cca49d1 | ||
|
|
dc6eff83ea | ||
|
|
38ff66debd | ||
|
|
1d2e0f74ea | ||
|
|
8f7ff25624 | ||
|
|
97aca8e54c | ||
|
|
95acf19067 | ||
|
|
3d0899aa58 | ||
|
|
bf60e40d0b | ||
|
|
c9094ca537 | ||
|
|
68b3fd6b8f | ||
|
|
9323b3a248 | ||
|
|
b55e9329d9 | ||
|
|
a5b4105971 | ||
|
|
d1feaa935d | ||
|
|
6919930aaa | ||
|
|
69633826bb | ||
|
|
771162bfb1 | ||
|
|
ba785e29e9 | ||
|
|
138d6e505b | ||
|
|
2748e6ba29 | ||
|
|
dbd4e927d8 | ||
|
|
e73d47918f | ||
|
|
b881bc071e | ||
|
|
1d0395d1c7 | ||
|
|
616c787e37 | ||
|
|
0c4de2bc97 | ||
|
|
2c7b104f4a | ||
|
|
78951c197a | ||
|
|
07c1cf7137 | ||
|
|
d26141151a | ||
|
|
f59dbe4a88 | ||
|
|
8dae7f8225 | ||
|
|
5811389891 | ||
|
|
debcaf6fb7 | ||
|
|
b8d10a62c2 | ||
|
|
d2b209234f | ||
|
|
34c9d8be50 | ||
|
|
ae57ad0c81 | ||
|
|
0c1520dd9c | ||
|
|
d594f43ebd | ||
|
|
125c693e3f | ||
|
|
ad2f857e15 | ||
|
|
e445d6aada | ||
|
|
88fbb0ffbb | ||
|
|
231908fe9f | ||
|
|
f137cc10f4 | ||
|
|
c2f5ac9eba | ||
|
|
5764c988af | ||
|
|
ccc2fbfd67 | ||
|
|
1a8f8adc2a | ||
|
|
7a242bb4ed | ||
|
|
3b8bb09ae3 | ||
|
|
140db85d21 | ||
|
|
ccce4b19e8 | ||
|
|
8cb9be7560 | ||
|
|
953f0569fb | ||
|
|
34c229fd33 | ||
|
|
958ad0d750 | ||
|
|
36ddd9dd69 | ||
|
|
38259c96c9 | ||
|
|
c054fb8a2c | ||
|
|
5a0b8328d8 | ||
|
|
ffa19426d7 | ||
|
|
c123804294 | ||
|
|
4e24551b90 | ||
|
|
51cb5da7f0 | ||
|
|
41f84447cc | ||
|
|
ce073a704b | ||
|
|
113232ebb6 | ||
|
|
a13a1225b7 | ||
|
|
0ec84be5da | ||
|
|
b1cefb7e3e | ||
|
|
cc0c1c08b9 | ||
|
|
3a67884451 | ||
|
|
72e716cdf1 | ||
|
|
40e06c9819 | ||
|
|
ad6c5ff11d | ||
|
|
335512e232 | ||
|
|
2622e59b0b | ||
|
|
35e6a13cd1 | ||
|
|
b48490badc |
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
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -80,7 +80,7 @@ jobs:
|
||||
uses: actions/setup-python@v2
|
||||
id: python
|
||||
with:
|
||||
python-version: '3.7'
|
||||
python-version: '3.8'
|
||||
|
||||
- name: Cache virtualenv
|
||||
uses: actions/cache@v2
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -77,6 +77,7 @@ venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
venv-*/
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
@@ -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:
|
||||
@@ -25,3 +25,8 @@ repos:
|
||||
- --branch=dev
|
||||
- --branch=release
|
||||
- --branch=beta
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.31.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py38-plus]
|
||||
|
||||
17
CODEOWNERS
17
CODEOWNERS
@@ -19,6 +19,7 @@ esphome/components/airthings_wave_mini/* @ncareau
|
||||
esphome/components/airthings_wave_plus/* @jeromelaban
|
||||
esphome/components/am43/* @buxtronix
|
||||
esphome/components/am43/cover/* @buxtronix
|
||||
esphome/components/analog_threshold/* @ianchi
|
||||
esphome/components/animation/* @syndlex
|
||||
esphome/components/anova/* @buxtronix
|
||||
esphome/components/api/* @OttoWinter
|
||||
@@ -27,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
|
||||
@@ -42,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
|
||||
@@ -77,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
|
||||
@@ -93,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
|
||||
@@ -104,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
|
||||
@@ -120,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
|
||||
@@ -140,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
|
||||
@@ -159,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
|
||||
@@ -213,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
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
# One of "docker", "hassio"
|
||||
ARG BASEIMGTYPE=docker
|
||||
|
||||
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 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
|
||||
# https://github.com/hassio-addons/addon-debian-base/releases
|
||||
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-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
|
||||
@@ -21,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 \
|
||||
@@ -52,16 +54,16 @@ RUN \
|
||||
&& mkdir -p /piolibs
|
||||
|
||||
|
||||
|
||||
# ======================= docker-type image =======================
|
||||
FROM base AS docker
|
||||
|
||||
# First install requirements to leverage caching when requirements don't change
|
||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
||||
RUN \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
||||
&& /platformio_install_deps.py /platformio.ini
|
||||
|
||||
|
||||
# ======================= docker-type image =======================
|
||||
FROM base AS docker
|
||||
|
||||
# Copy esphome and install
|
||||
COPY . /esphome
|
||||
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
|
||||
@@ -93,7 +95,7 @@ RUN \
|
||||
apt-get update \
|
||||
# Use pinned versions so that we get updates with build caching
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
nginx=1.18.0-6.1 \
|
||||
nginx-light=1.18.0-6.1 \
|
||||
&& rm -rf \
|
||||
/tmp/* \
|
||||
/var/{cache,log}/* \
|
||||
@@ -104,12 +106,6 @@ ARG BUILD_VERSION=dev
|
||||
# Copy root filesystem
|
||||
COPY docker/ha-addon-rootfs/ /
|
||||
|
||||
# First install requirements to leverage caching when requirements don't change
|
||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
||||
RUN \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
||||
&& /platformio_install_deps.py /platformio.ini
|
||||
|
||||
# Copy esphome and install
|
||||
COPY . /esphome
|
||||
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
|
||||
@@ -147,10 +143,8 @@ RUN \
|
||||
/var/{cache,log}/* \
|
||||
/var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
|
||||
RUN \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
|
||||
&& /platformio_install_deps.py /platformio.ini
|
||||
COPY requirements_test.txt /
|
||||
RUN pip3 install --no-cache-dir -r /requirements_test.txt
|
||||
|
||||
VOLUME ["/esphome"]
|
||||
WORKDIR /esphome
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -778,10 +778,10 @@ def run_esphome(argv):
|
||||
_LOGGER.warning("Please instead use:")
|
||||
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion))
|
||||
|
||||
if sys.version_info < (3, 7, 0):
|
||||
if sys.version_info < (3, 8, 0):
|
||||
_LOGGER.error(
|
||||
"You're running ESPHome with Python <3.7. ESPHome is no longer compatible "
|
||||
"with this Python version. Please reinstall ESPHome with Python 3.7+"
|
||||
"You're running ESPHome with Python <3.8. ESPHome is no longer compatible "
|
||||
"with this Python version. Please reinstall ESPHome with Python 3.8+"
|
||||
)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -133,6 +133,7 @@ ADCSensor = adc_ns.class_(
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
sensor.sensor_schema(
|
||||
ADCSensor,
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
@@ -140,7 +141,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ADCSensor),
|
||||
cv.Required(CONF_PIN): validate_adc_pin,
|
||||
cv.Optional(CONF_RAW, default=False): cv.boolean,
|
||||
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
|
||||
|
||||
@@ -52,6 +52,7 @@ ADS1115Sensor = ads1115_ns.class_(
|
||||
CONF_ADS1115_ID = "ads1115_id"
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
ADS1115Sensor,
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
@@ -59,7 +60,6 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ADS1115Sensor),
|
||||
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
|
||||
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
|
||||
cv.Required(CONF_GAIN): validate_gain,
|
||||
|
||||
1
esphome/components/analog_threshold/__init__.py
Normal file
1
esphome/components/analog_threshold/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@ianchi"]
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "analog_threshold_binary_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace analog_threshold {
|
||||
|
||||
static const char *const TAG = "analog_threshold.binary_sensor";
|
||||
|
||||
void AnalogThresholdBinarySensor::setup() {
|
||||
float sensor_value = this->sensor_->get_state();
|
||||
|
||||
// TRUE state is defined to be when sensor is >= threshold
|
||||
// so when undefined sensor value initialize to FALSE
|
||||
if (std::isnan(sensor_value)) {
|
||||
this->publish_initial_state(false);
|
||||
} else {
|
||||
this->publish_initial_state(sensor_value >= (this->lower_threshold_ + this->upper_threshold_) / 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
|
||||
this->sensor_ = analog_sensor;
|
||||
|
||||
this->sensor_->add_on_state_callback([this](float sensor_value) {
|
||||
// if there is an invalid sensor reading, ignore the change and keep the current state
|
||||
if (!std::isnan(sensor_value)) {
|
||||
this->publish_state(sensor_value >= (this->state ? this->lower_threshold_ : this->upper_threshold_));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AnalogThresholdBinarySensor::dump_config() {
|
||||
LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this);
|
||||
LOG_SENSOR(" ", "Sensor", this->sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_);
|
||||
ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_);
|
||||
}
|
||||
|
||||
} // namespace analog_threshold
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace analog_threshold {
|
||||
|
||||
class AnalogThresholdBinarySensor : public Component, public binary_sensor::BinarySensor {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void setup() override;
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
void set_sensor(sensor::Sensor *analog_sensor);
|
||||
void set_upper_threshold(float threshold) { this->upper_threshold_ = threshold; }
|
||||
void set_lower_threshold(float threshold) { this->lower_threshold_ = threshold; }
|
||||
|
||||
protected:
|
||||
sensor::Sensor *sensor_{nullptr};
|
||||
|
||||
float upper_threshold_;
|
||||
float lower_threshold_;
|
||||
};
|
||||
|
||||
} // namespace analog_threshold
|
||||
} // namespace esphome
|
||||
44
esphome/components/analog_threshold/binary_sensor.py
Normal file
44
esphome/components/analog_threshold/binary_sensor.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor, sensor
|
||||
from esphome.const import (
|
||||
CONF_SENSOR_ID,
|
||||
CONF_THRESHOLD,
|
||||
)
|
||||
|
||||
analog_threshold_ns = cg.esphome_ns.namespace("analog_threshold")
|
||||
|
||||
AnalogThresholdBinarySensor = analog_threshold_ns.class_(
|
||||
"AnalogThresholdBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
|
||||
CONF_UPPER = "upper"
|
||||
CONF_LOWER = "lower"
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(AnalogThresholdBinarySensor),
|
||||
cv.Required(CONF_SENSOR_ID): cv.use_id(sensor.Sensor),
|
||||
cv.Required(CONF_THRESHOLD): cv.Any(
|
||||
cv.float_,
|
||||
cv.Schema(
|
||||
{cv.Required(CONF_UPPER): cv.float_, cv.Required(CONF_LOWER): cv.float_}
|
||||
),
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
sens = await cg.get_variable(config[CONF_SENSOR_ID])
|
||||
cg.add(var.set_sensor(sens))
|
||||
|
||||
if isinstance(config[CONF_THRESHOLD], float):
|
||||
cg.add(var.set_upper_threshold(config[CONF_THRESHOLD]))
|
||||
cg.add(var.set_lower_threshold(config[CONF_THRESHOLD]))
|
||||
else:
|
||||
cg.add(var.set_upper_threshold(config[CONF_THRESHOLD][CONF_UPPER]))
|
||||
cg.add(var.set_lower_threshold(config[CONF_THRESHOLD][CONF_LOWER]))
|
||||
@@ -1,7 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING
|
||||
from esphome.const import CONF_DIRECTION, DEVICE_CLASS_MOVING
|
||||
from . import APDS9960, CONF_APDS9960_ID
|
||||
|
||||
DEPENDENCIES = ["apds9960"]
|
||||
@@ -13,13 +13,12 @@ DIRECTIONS = {
|
||||
"RIGHT": "set_right_direction",
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_MOVING
|
||||
).extend(
|
||||
{
|
||||
cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
|
||||
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
|
||||
cv.Optional(
|
||||
CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING
|
||||
): binary_sensor.device_class,
|
||||
cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -21,7 +21,6 @@ async def async_run_logs(config, address):
|
||||
if CONF_ENCRYPTION in conf:
|
||||
noise_psk = conf[CONF_ENCRYPTION][CONF_KEY]
|
||||
_LOGGER.info("Starting log output from %s using esphome API", address)
|
||||
zc = zeroconf.Zeroconf()
|
||||
cli = APIClient(
|
||||
address,
|
||||
port,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -5,7 +5,7 @@ from . import AS3935, CONF_AS3935_ID
|
||||
|
||||
DEPENDENCIES = ["as3935"]
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend(
|
||||
{
|
||||
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_DISTANCE,
|
||||
CONF_LIGHTNING_ENERGY,
|
||||
STATE_CLASS_NONE,
|
||||
UNIT_KILOMETER,
|
||||
ICON_SIGNAL_DISTANCE_VARIANT,
|
||||
ICON_FLASH,
|
||||
@@ -20,12 +19,10 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
unit_of_measurement=UNIT_KILOMETER,
|
||||
icon=ICON_SIGNAL_DISTANCE_VARIANT,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
),
|
||||
cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
|
||||
icon=ICON_FLASH,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
@@ -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,31 +2,23 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
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,
|
||||
unit_of_measurement=UNIT_LUX,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
@@ -34,15 +26,11 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BH1750Sensor),
|
||||
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."
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -52,10 +40,6 @@ CONFIG_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)
|
||||
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]))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
from esphome.cpp_helpers import setup_entity
|
||||
from esphome import automation, core
|
||||
from esphome.automation import Condition, maybe_simple_id
|
||||
@@ -7,7 +8,9 @@ from esphome.components import mqtt
|
||||
from esphome.const import (
|
||||
CONF_DELAY,
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_FILTERS,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_INVALID_COOLDOWN,
|
||||
CONF_INVERTED,
|
||||
@@ -22,7 +25,6 @@ from esphome.const import (
|
||||
CONF_STATE,
|
||||
CONF_TIMING,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_NAME,
|
||||
CONF_MQTT_ID,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
@@ -315,7 +317,7 @@ def validate_multi_click_timing(value):
|
||||
return timings
|
||||
|
||||
|
||||
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||
|
||||
|
||||
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
||||
@@ -324,7 +326,7 @@ BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).ex
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
||||
mqtt.MQTTBinarySensorComponent
|
||||
),
|
||||
cv.Optional(CONF_DEVICE_CLASS): device_class,
|
||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||
cv.Optional(CONF_FILTERS): validate_filters,
|
||||
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
|
||||
{
|
||||
@@ -377,6 +379,39 @@ BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).ex
|
||||
}
|
||||
)
|
||||
|
||||
_UNDEF = object()
|
||||
|
||||
|
||||
def binary_sensor_schema(
|
||||
class_: MockObjClass = _UNDEF,
|
||||
*,
|
||||
icon: str = _UNDEF,
|
||||
entity_category: str = _UNDEF,
|
||||
device_class: str = _UNDEF,
|
||||
) -> cv.Schema:
|
||||
schema = BINARY_SENSOR_SCHEMA
|
||||
if class_ is not _UNDEF:
|
||||
schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)})
|
||||
if icon is not _UNDEF:
|
||||
schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
|
||||
if entity_category is not _UNDEF:
|
||||
schema = schema.extend(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_ENTITY_CATEGORY, default=entity_category
|
||||
): cv.entity_category
|
||||
}
|
||||
)
|
||||
if device_class is not _UNDEF:
|
||||
schema = schema.extend(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_DEVICE_CLASS, default=device_class
|
||||
): validate_device_class
|
||||
}
|
||||
)
|
||||
return schema
|
||||
|
||||
|
||||
async def setup_binary_sensor_core_(var, config):
|
||||
await setup_entity(var, config)
|
||||
@@ -443,7 +478,7 @@ async def register_binary_sensor(var, config):
|
||||
|
||||
|
||||
async def new_binary_sensor(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME])
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await register_binary_sensor(var, config)
|
||||
return var
|
||||
|
||||
|
||||
@@ -42,8 +42,7 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
|
||||
}
|
||||
}
|
||||
std::string BinarySensor::device_class() { return ""; }
|
||||
BinarySensor::BinarySensor(const std::string &name) : EntityBase(name), state(false) {}
|
||||
BinarySensor::BinarySensor() : BinarySensor("") {}
|
||||
BinarySensor::BinarySensor() : state(false) {}
|
||||
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||
std::string BinarySensor::get_device_class() {
|
||||
if (this->device_class_.has_value())
|
||||
|
||||
@@ -26,11 +26,6 @@ namespace binary_sensor {
|
||||
class BinarySensor : public EntityBase {
|
||||
public:
|
||||
explicit BinarySensor();
|
||||
/** Construct a binary sensor with the specified name
|
||||
*
|
||||
* @param name Name of this binary sensor.
|
||||
*/
|
||||
explicit BinarySensor(const std::string &name);
|
||||
|
||||
/** Add a callback to be notified of state changes.
|
||||
*
|
||||
|
||||
@@ -3,14 +3,12 @@ import esphome.config_validation as cv
|
||||
|
||||
from esphome.components import sensor, binary_sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_CHANNELS,
|
||||
CONF_VALUE,
|
||||
CONF_TYPE,
|
||||
ICON_CHECK_CIRCLE_OUTLINE,
|
||||
CONF_BINARY_SENSOR,
|
||||
CONF_GROUP,
|
||||
STATE_CLASS_NONE,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["binary_sensor"]
|
||||
@@ -33,12 +31,11 @@ entry = {
|
||||
CONFIG_SCHEMA = cv.typed_schema(
|
||||
{
|
||||
CONF_GROUP: sensor.sensor_schema(
|
||||
BinarySensorMap,
|
||||
icon=ICON_CHECK_CIRCLE_OUTLINE,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BinarySensorMap),
|
||||
cv.Required(CONF_CHANNELS): cv.All(
|
||||
cv.ensure_list(entry), cv.Length(min=1)
|
||||
),
|
||||
@@ -50,9 +47,8 @@ CONFIG_SCHEMA = cv.typed_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)
|
||||
|
||||
constant = SENSOR_MAP_TYPES[config[CONF_TYPE]]
|
||||
cg.add(var.set_sensor_type(constant))
|
||||
|
||||
@@ -12,9 +12,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_NONE,
|
||||
UNIT_AMPERE,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_KILOWATT_HOURS,
|
||||
@@ -35,38 +33,39 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BL0940),
|
||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
|
||||
UNIT_AMPERE,
|
||||
ICON_EMPTY,
|
||||
2,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_POWER): sensor.sensor_schema(
|
||||
UNIT_WATT, ICON_EMPTY, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ENERGY): sensor.sensor_schema(
|
||||
UNIT_KILOWATT_HOURS,
|
||||
ICON_EMPTY,
|
||||
0,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
STATE_CLASS_NONE,
|
||||
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
),
|
||||
cv.Optional(CONF_INTERNAL_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS,
|
||||
ICON_EMPTY,
|
||||
0,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_NONE,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_EXTERNAL_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS,
|
||||
ICON_EMPTY,
|
||||
0,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_NONE,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2,9 +2,7 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, ble_client, esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
STATE_CLASS_NONE,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_SERVICE_UUID,
|
||||
)
|
||||
@@ -31,12 +29,11 @@ BLESensorNotifyTrigger = ble_client_ns.class_(
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
sensor.sensor_schema(
|
||||
BLESensor,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLESensor),
|
||||
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,
|
||||
@@ -57,7 +54,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
var = await sensor.new_sensor(config)
|
||||
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]))
|
||||
@@ -124,7 +121,6 @@ async def to_code(config):
|
||||
await cg.register_component(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
cg.add(var.set_enable_notify(config[CONF_NOTIFY]))
|
||||
await sensor.register_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)
|
||||
|
||||
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
|
||||
@@ -7,7 +7,6 @@ from esphome.const import (
|
||||
CONF_IBEACON_MAJOR,
|
||||
CONF_IBEACON_MINOR,
|
||||
CONF_IBEACON_UUID,
|
||||
CONF_ID,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
@@ -30,9 +29,9 @@ def _validate(config):
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
binary_sensor.binary_sensor_schema(BLEPresenceDevice)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLEPresenceDevice),
|
||||
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t,
|
||||
@@ -48,10 +47,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
await binary_sensor.register_binary_sensor(var, config)
|
||||
|
||||
if CONF_MAC_ADDRESS in config:
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
@@ -4,7 +4,6 @@ from esphome.components import sensor, esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_SERVICE_UUID,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_DECIBEL,
|
||||
@@ -19,6 +18,7 @@ BLERSSISensor = ble_rssi_ns.class_(
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
sensor.sensor_schema(
|
||||
BLERSSISensor,
|
||||
unit_of_measurement=UNIT_DECIBEL,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
@@ -26,7 +26,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLERSSISensor),
|
||||
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
||||
}
|
||||
@@ -38,10 +37,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
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 esp32_ble_tracker.register_ble_device(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
if CONF_MAC_ADDRESS in config:
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
@@ -13,7 +13,7 @@ BLEScanner = ble_scanner_ns.class_(
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
text_sensor.text_sensor_schema(klass=BLEScanner)
|
||||
text_sensor.text_sensor_schema(BLEScanner)
|
||||
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,7 +8,9 @@ CODEOWNERS = ["@mvturnho", "@danielschramm"]
|
||||
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"
|
||||
@@ -38,7 +40,7 @@ canbus_ns = cg.esphome_ns.namespace("canbus")
|
||||
CanbusComponent = canbus_ns.class_("CanbusComponent", cg.Component)
|
||||
CanbusTrigger = canbus_ns.class_(
|
||||
"CanbusTrigger",
|
||||
automation.Trigger.template(cg.std_vector.template(cg.uint8)),
|
||||
automation.Trigger.template(cg.std_vector.template(cg.uint8), cg.uint32),
|
||||
cg.Component,
|
||||
)
|
||||
CanSpeed = canbus_ns.enum("CAN_SPEED")
|
||||
@@ -72,6 +74,9 @@ CANBUS_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger),
|
||||
cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
|
||||
cv.Optional(CONF_CAN_ID_MASK, default=0x1FFFFFFF): cv.int_range(
|
||||
min=0, max=0x1FFFFFFF
|
||||
),
|
||||
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
|
||||
},
|
||||
validate_id,
|
||||
@@ -90,11 +95,16 @@ async def setup_canbus_core_(var, config):
|
||||
|
||||
for conf in config.get(CONF_ON_FRAME, []):
|
||||
can_id = conf[CONF_CAN_ID]
|
||||
can_id_mask = conf[CONF_CAN_ID_MASK]
|
||||
ext_id = conf[CONF_USE_EXTENDED_ID]
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id)
|
||||
trigger = cg.new_Pvariable(
|
||||
conf[CONF_TRIGGER_ID], var, can_id, can_id_mask, ext_id
|
||||
)
|
||||
await cg.register_component(trigger, conf)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.std_vector.template(cg.uint8), "x")], conf
|
||||
trigger,
|
||||
[(cg.std_vector.template(cg.uint8), "x"), (cg.uint32, "can_id")],
|
||||
conf,
|
||||
)
|
||||
|
||||
|
||||
@@ -113,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,
|
||||
@@ -126,12 +137,16 @@ async def canbus_action_to_code(config, action_id, template_arg, args):
|
||||
if CONF_CAN_ID in config:
|
||||
can_id = await cg.templatable(config[CONF_CAN_ID], args, cg.uint32)
|
||||
cg.add(var.set_can_id(can_id))
|
||||
|
||||
use_extended_id = await cg.templatable(
|
||||
config[CONF_USE_EXTENDED_ID], args, cg.uint32
|
||||
)
|
||||
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];
|
||||
@@ -56,13 +58,15 @@ void Canbus::add_trigger(CanbusTrigger *trigger) {
|
||||
|
||||
void Canbus::loop() {
|
||||
struct CanFrame can_message;
|
||||
// readmessage
|
||||
if (this->read_message(&can_message) == canbus::ERROR_OK) {
|
||||
// read all messages until queue is empty
|
||||
int message_counter = 0;
|
||||
while (this->read_message(&can_message) == canbus::ERROR_OK) {
|
||||
message_counter++;
|
||||
if (can_message.use_extended_id) {
|
||||
ESP_LOGD(TAG, "received can message extended can_id=0x%x size=%d", can_message.can_id,
|
||||
ESP_LOGD(TAG, "received can message (#%d) extended can_id=0x%x size=%d", message_counter, can_message.can_id,
|
||||
can_message.can_data_length_code);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "received can message std can_id=0x%x size=%d", can_message.can_id,
|
||||
ESP_LOGD(TAG, "received can message (#%d) std can_id=0x%x size=%d", message_counter, can_message.can_id,
|
||||
can_message.can_data_length_code);
|
||||
}
|
||||
|
||||
@@ -76,8 +80,9 @@ void Canbus::loop() {
|
||||
|
||||
// fire all triggers
|
||||
for (auto *trigger : this->triggers_) {
|
||||
if ((trigger->can_id_ == can_message.can_id) && (trigger->use_extended_id_ == can_message.use_extended_id)) {
|
||||
trigger->trigger(data);
|
||||
if ((trigger->can_id_ == (can_message.can_id & trigger->can_id_mask_)) &&
|
||||
(trigger->use_extended_id_ == can_message.use_extended_id)) {
|
||||
trigger->trigger(data, can_message.can_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,37 +101,44 @@ 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_{};
|
||||
};
|
||||
|
||||
class CanbusTrigger : public Trigger<std::vector<uint8_t>>, public Component {
|
||||
class CanbusTrigger : public Trigger<std::vector<uint8_t>, uint32_t>, public Component {
|
||||
friend class Canbus;
|
||||
|
||||
public:
|
||||
explicit CanbusTrigger(Canbus *parent, const std::uint32_t can_id, const bool use_extended_id)
|
||||
: parent_(parent), can_id_(can_id), use_extended_id_(use_extended_id){};
|
||||
explicit CanbusTrigger(Canbus *parent, const std::uint32_t can_id, const std::uint32_t can_id_mask,
|
||||
const bool use_extended_id)
|
||||
: parent_(parent), can_id_(can_id), can_id_mask_(can_id_mask), use_extended_id_(use_extended_id){};
|
||||
void setup() override { this->parent_->add_trigger(this); }
|
||||
|
||||
protected:
|
||||
Canbus *parent_;
|
||||
uint32_t can_id_;
|
||||
uint32_t can_id_mask_;
|
||||
bool use_extended_id_;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
from esphome.const import CONF_CHANNEL
|
||||
from . import cap1188_ns, CAP1188Component, CONF_CAP1188_ID
|
||||
|
||||
DEPENDENCIES = ["cap1188"]
|
||||
CAP1188Channel = cap1188_ns.class_("CAP1188Channel", binary_sensor.BinarySensor)
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(CAP1188Channel).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CAP1188Channel),
|
||||
cv.GenerateID(CONF_CAP1188_ID): cv.use_id(CAP1188Component),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7),
|
||||
}
|
||||
@@ -17,8 +16,7 @@ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await binary_sensor.register_binary_sensor(var, config)
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
hub = await cg.get_variable(config[CONF_CAP1188_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
|
||||
|
||||
107
esphome/components/captive_portal/captive_index.h
Normal file
107
esphome/components/captive_portal/captive_index.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
// Generated from https://github.com/esphome/esphome-webserver
|
||||
#include "esphome/core/hal.h"
|
||||
namespace esphome {
|
||||
|
||||
namespace captive_portal {
|
||||
|
||||
const uint8_t INDEX_GZ[] PROGMEM = {
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xdd, 0x58, 0x09, 0x6f, 0xdc, 0x36, 0x16, 0xfe, 0x2b,
|
||||
0xac, 0x92, 0x74, 0x34, 0x8d, 0xc5, 0xd1, 0x31, 0x97, 0x35, 0xd2, 0x14, 0x89, 0x37, 0x45, 0x0b, 0x24, 0x69, 0x00,
|
||||
0xbb, 0x5d, 0x14, 0x69, 0x00, 0x73, 0x24, 0x6a, 0xc4, 0x58, 0xa2, 0x54, 0x91, 0x9a, 0x23, 0x83, 0xd9, 0xdf, 0xde,
|
||||
0x47, 0x52, 0x73, 0x38, 0x6b, 0x2f, 0x90, 0x62, 0x8b, 0xa2, 0x4d, 0x6c, 0x9a, 0xc7, 0x3b, 0x3f, 0xf2, 0xf1, 0x3d,
|
||||
0x2a, 0xfa, 0x2a, 0xad, 0x12, 0xb9, 0xad, 0x29, 0xca, 0x65, 0x59, 0xcc, 0x23, 0xd5, 0xa2, 0x82, 0xf0, 0x65, 0x4c,
|
||||
0x39, 0x8c, 0x28, 0x49, 0xe7, 0x51, 0x49, 0x25, 0x41, 0x49, 0x4e, 0x1a, 0x41, 0x65, 0xfc, 0xd3, 0xcd, 0x77, 0xce,
|
||||
0x14, 0x0d, 0xe6, 0x51, 0xc1, 0xf8, 0x1d, 0x6a, 0x68, 0x11, 0xb3, 0xa4, 0xe2, 0x28, 0x6f, 0x68, 0x16, 0xa7, 0x44,
|
||||
0x92, 0x90, 0x95, 0x64, 0x49, 0x15, 0x81, 0x66, 0xe3, 0xa4, 0xa4, 0xf1, 0x8a, 0xd1, 0x75, 0x5d, 0x35, 0x12, 0x01,
|
||||
0xa5, 0xa4, 0x5c, 0xc6, 0xd6, 0x9a, 0xa5, 0x32, 0x8f, 0x53, 0xba, 0x62, 0x09, 0x75, 0xf4, 0xe0, 0x82, 0x71, 0x26,
|
||||
0x19, 0x29, 0x1c, 0x91, 0x90, 0x82, 0xc6, 0xde, 0x45, 0x2b, 0x68, 0xa3, 0x07, 0x64, 0x01, 0x63, 0x5e, 0x59, 0x20,
|
||||
0x52, 0x24, 0x0d, 0xab, 0x25, 0x52, 0xf6, 0xc6, 0x65, 0x95, 0xb6, 0x05, 0x9d, 0x67, 0x2d, 0x4f, 0x24, 0x03, 0x0b,
|
||||
0x84, 0xcd, 0xfb, 0xbb, 0x82, 0x4a, 0x44, 0xe3, 0x37, 0x44, 0xe6, 0xb8, 0x24, 0x1b, 0xdb, 0x74, 0x18, 0xb7, 0xfd,
|
||||
0x6f, 0x6c, 0xfe, 0xdc, 0x73, 0xdd, 0xfe, 0x85, 0x6e, 0xdc, 0xfe, 0x00, 0xfe, 0xce, 0x1a, 0x2a, 0xdb, 0x86, 0x23,
|
||||
0x62, 0xdf, 0x46, 0x35, 0x50, 0xa2, 0x34, 0xb6, 0x4a, 0xcf, 0xc7, 0xae, 0x3b, 0x45, 0xde, 0x25, 0xf6, 0x47, 0x8e,
|
||||
0xe7, 0xe1, 0xc0, 0xf1, 0x46, 0xc9, 0xc4, 0x19, 0x21, 0x6f, 0x08, 0x8d, 0xef, 0xe3, 0x11, 0x72, 0x3f, 0x59, 0x28,
|
||||
0x63, 0x45, 0x11, 0x5b, 0xbc, 0xe2, 0xd4, 0x42, 0x42, 0x36, 0xd5, 0x1d, 0x8d, 0xad, 0xa4, 0x6d, 0x1a, 0xf0, 0xee,
|
||||
0xaa, 0x2a, 0xaa, 0x06, 0xac, 0xfd, 0x95, 0xa3, 0x7b, 0xff, 0xbe, 0x58, 0x87, 0x6c, 0x08, 0x17, 0x59, 0xd5, 0x94,
|
||||
0xb1, 0xa5, 0x41, 0xb1, 0x9f, 0xee, 0xe8, 0x1e, 0xa9, 0xa6, 0x7f, 0xb6, 0xe8, 0x54, 0x0d, 0x5b, 0x32, 0x1e, 0x5b,
|
||||
0x9e, 0x8f, 0xbc, 0x29, 0xe8, 0xbd, 0xed, 0xef, 0x8f, 0xa0, 0x10, 0x05, 0x4a, 0xe7, 0x66, 0x65, 0xbf, 0xbf, 0x8d,
|
||||
0xc4, 0x6a, 0x89, 0x36, 0x65, 0xc1, 0x45, 0x6c, 0xe5, 0x52, 0xd6, 0xe1, 0x60, 0xb0, 0x5e, 0xaf, 0xf1, 0x3a, 0xc0,
|
||||
0x55, 0xb3, 0x1c, 0xf8, 0xae, 0xeb, 0x0e, 0x80, 0xc2, 0x42, 0x66, 0x7f, 0x2c, 0x7f, 0x68, 0xa1, 0x9c, 0xb2, 0x65,
|
||||
0x2e, 0x75, 0x7f, 0xfe, 0x74, 0xc7, 0xf7, 0x91, 0xa2, 0x98, 0xdf, 0x7e, 0x38, 0xd3, 0xd2, 0x9c, 0x69, 0xe1, 0xdf,
|
||||
0x12, 0xdb, 0x3a, 0xb8, 0xda, 0x7b, 0xa3, 0x8c, 0x9a, 0x10, 0x1f, 0xf9, 0xc8, 0xd5, 0xff, 0x7d, 0x47, 0xf5, 0xbb,
|
||||
0x91, 0xf3, 0xd9, 0x08, 0x9d, 0x8d, 0xe0, 0xaf, 0x02, 0xd0, 0x2f, 0xc7, 0xce, 0xe5, 0x91, 0xdf, 0x53, 0xeb, 0x2b,
|
||||
0xcf, 0x3d, 0x4d, 0x28, 0xa6, 0xef, 0xc7, 0xe7, 0x63, 0xc7, 0xff, 0x59, 0x11, 0x68, 0xf4, 0x8f, 0x5c, 0x8e, 0x9f,
|
||||
0x7b, 0x3f, 0x8f, 0xc9, 0x08, 0x8d, 0xba, 0x99, 0x91, 0xa3, 0xfa, 0xc7, 0x91, 0xd6, 0x85, 0x46, 0x2b, 0x20, 0x2b,
|
||||
0x9d, 0xb1, 0x33, 0x22, 0x01, 0x0a, 0x3a, 0xab, 0xa0, 0x07, 0xd3, 0x63, 0xe0, 0x3e, 0x9b, 0x73, 0x82, 0x4f, 0xbd,
|
||||
0xc1, 0xdc, 0xea, 0x87, 0x96, 0x75, 0x82, 0xa1, 0x3a, 0x87, 0x01, 0x7f, 0xac, 0xe0, 0xdc, 0x59, 0x56, 0x7f, 0x6f,
|
||||
0x7d, 0x2b, 0xc8, 0x8a, 0x5a, 0x71, 0x1c, 0x43, 0xa8, 0xb5, 0x25, 0x9c, 0x10, 0x5c, 0x54, 0x09, 0x51, 0x2c, 0x58,
|
||||
0x50, 0xd2, 0x24, 0xf9, 0xd7, 0x5f, 0xdb, 0xc7, 0xa5, 0x25, 0x95, 0xaf, 0x0a, 0xaa, 0xba, 0xe2, 0xe5, 0xf6, 0x86,
|
||||
0x2c, 0xdf, 0x42, 0x00, 0xd9, 0x16, 0x11, 0x2c, 0xa5, 0x56, 0xff, 0xbd, 0xfb, 0x01, 0x0b, 0xb9, 0x2d, 0x28, 0x4e,
|
||||
0x99, 0xa8, 0x0b, 0xb2, 0x8d, 0xad, 0x05, 0xc8, 0xba, 0xb3, 0xfa, 0x17, 0x19, 0x95, 0x49, 0x6e, 0x5b, 0x03, 0x08,
|
||||
0xb1, 0x8c, 0x2d, 0xf1, 0x47, 0x51, 0x71, 0xab, 0x8f, 0x65, 0x4e, 0xb9, 0x6d, 0x1f, 0x2c, 0x54, 0xf6, 0x71, 0xbd,
|
||||
0x64, 0x3f, 0xb4, 0x74, 0xb4, 0x41, 0x32, 0xa9, 0x42, 0x0e, 0xab, 0xe0, 0xbd, 0x38, 0xce, 0x2e, 0xaa, 0x74, 0xfb,
|
||||
0x88, 0x79, 0xb9, 0x67, 0x6c, 0x63, 0x9c, 0xd3, 0xe6, 0x86, 0x6e, 0xe0, 0xb8, 0xfc, 0x9b, 0x7d, 0xc7, 0xd0, 0x5b,
|
||||
0x2a, 0xd7, 0x55, 0x73, 0x27, 0x42, 0x64, 0x3d, 0x37, 0xe2, 0x66, 0x26, 0x42, 0x39, 0x26, 0xb5, 0xc0, 0xa2, 0x80,
|
||||
0xf0, 0xb7, 0xbd, 0x3e, 0xc4, 0x6a, 0x7d, 0xdf, 0x14, 0x83, 0xe2, 0x6d, 0x94, 0xb2, 0x15, 0x4a, 0x0a, 0x22, 0xe0,
|
||||
0xb8, 0x72, 0x23, 0xcb, 0x42, 0x87, 0xb8, 0xaa, 0x78, 0x02, 0xfc, 0x77, 0xb1, 0xf5, 0x00, 0x76, 0x2f, 0xb7, 0x3f,
|
||||
0xa4, 0x76, 0x4f, 0x00, 0x6a, 0xbd, 0x3e, 0x5e, 0x91, 0xa2, 0xa5, 0x28, 0x46, 0x32, 0x67, 0xe2, 0x64, 0xe2, 0xec,
|
||||
0x51, 0xb6, 0x5a, 0xdc, 0x01, 0x57, 0x06, 0xcb, 0xc2, 0xee, 0x5b, 0xc7, 0x38, 0x8e, 0x88, 0xb9, 0xe5, 0xac, 0x27,
|
||||
0xd6, 0x67, 0x36, 0x39, 0x05, 0xcd, 0xa4, 0x75, 0x16, 0xf0, 0x4f, 0x77, 0x70, 0x1b, 0xe1, 0x06, 0xf4, 0xf7, 0xf7,
|
||||
0xa7, 0xd9, 0x48, 0xd4, 0x84, 0x7f, 0xce, 0xaa, 0x6c, 0xd4, 0x81, 0x85, 0x55, 0x4f, 0x45, 0x17, 0x10, 0x9d, 0x74,
|
||||
0x0e, 0xc8, 0xb1, 0xff, 0x74, 0x07, 0x71, 0xa6, 0x8e, 0xce, 0xdd, 0x49, 0x68, 0x34, 0x00, 0x84, 0xe6, 0xb7, 0xfb,
|
||||
0x7e, 0xff, 0xe4, 0xce, 0x6f, 0x2d, 0x6d, 0xb6, 0xd7, 0xb4, 0xa0, 0x89, 0xac, 0x1a, 0xdb, 0x7a, 0x02, 0x9a, 0xe0,
|
||||
0x24, 0x68, 0xbf, 0xbf, 0xbf, 0x79, 0xf3, 0x3a, 0xae, 0x6c, 0xda, 0xbf, 0x78, 0x8c, 0x5a, 0xdd, 0xea, 0xef, 0xe1,
|
||||
0x56, 0xff, 0x4f, 0xdc, 0x53, 0xf7, 0x7a, 0xef, 0x03, 0xb0, 0x1a, 0xaf, 0x4f, 0x97, 0xbb, 0xba, 0x00, 0x9e, 0xc3,
|
||||
0x25, 0x72, 0x61, 0x3d, 0x17, 0xb6, 0x33, 0x1e, 0xf5, 0x41, 0x3d, 0xfc, 0x80, 0xe9, 0xfa, 0x7a, 0x86, 0x6b, 0x5a,
|
||||
0x1d, 0xd1, 0xf9, 0x37, 0xbb, 0x45, 0xb5, 0x71, 0x04, 0xfb, 0xc4, 0xf8, 0x32, 0x64, 0x3c, 0xa7, 0x0d, 0x93, 0x7b,
|
||||
0x30, 0x17, 0x6e, 0xfa, 0xba, 0x95, 0xbb, 0x9a, 0xa4, 0xa9, 0x5a, 0x19, 0xd5, 0x9b, 0x59, 0x06, 0x79, 0x41, 0x51,
|
||||
0xd2, 0xd0, 0xa3, 0xe5, 0xde, 0xac, 0xeb, 0x2b, 0x28, 0xbc, 0x1c, 0x3d, 0xdb, 0xab, 0x83, 0xb7, 0x93, 0xb0, 0x65,
|
||||
0x0e, 0x29, 0xd8, 0x92, 0x87, 0x09, 0xd8, 0x4d, 0x1b, 0xc3, 0x94, 0x91, 0x92, 0x15, 0xdb, 0x50, 0xc0, 0x65, 0xe8,
|
||||
0x40, 0xc2, 0x60, 0xd9, 0x7e, 0xd1, 0x4a, 0x59, 0x71, 0xd0, 0xdd, 0xa4, 0xb4, 0x09, 0xdd, 0x99, 0xe9, 0x38, 0x0d,
|
||||
0x49, 0x59, 0x2b, 0x42, 0x1c, 0x34, 0xb4, 0x9c, 0x2d, 0x48, 0x72, 0xb7, 0x6c, 0xaa, 0x96, 0xa7, 0x4e, 0xa2, 0x6e,
|
||||
0xeb, 0xf0, 0x89, 0x97, 0x91, 0x80, 0x26, 0xb3, 0x6e, 0x94, 0x65, 0xd9, 0x0c, 0x90, 0xa0, 0x8e, 0xb9, 0xfc, 0x42,
|
||||
0x1f, 0x0f, 0x15, 0xdb, 0x99, 0x99, 0xd8, 0x57, 0x13, 0xc6, 0x46, 0x48, 0x25, 0xcf, 0x66, 0x07, 0x77, 0xdc, 0x19,
|
||||
0xa4, 0x01, 0x01, 0x42, 0x6a, 0x88, 0x7f, 0x30, 0x73, 0x5f, 0x12, 0xc6, 0xcf, 0xad, 0x57, 0x67, 0x65, 0xd6, 0x85,
|
||||
0x2f, 0xc0, 0xa2, 0xd5, 0xe8, 0x20, 0x9e, 0x41, 0xa2, 0x32, 0xb9, 0x30, 0xf4, 0xc7, 0x6e, 0xbd, 0xd9, 0xe3, 0xee,
|
||||
0x8c, 0xec, 0x0e, 0xd4, 0x59, 0x41, 0x37, 0xb3, 0x8f, 0xad, 0x90, 0x2c, 0xdb, 0x3a, 0x5d, 0x2e, 0x0d, 0xe1, 0xbc,
|
||||
0x40, 0x0e, 0x5d, 0x00, 0x29, 0xa5, 0x7c, 0xa6, 0x75, 0x38, 0x4c, 0xd2, 0x52, 0x74, 0x38, 0x1d, 0xc5, 0xe8, 0x53,
|
||||
0x7a, 0x5f, 0xd6, 0xff, 0xa2, 0x56, 0xc7, 0x71, 0x57, 0x92, 0x06, 0x72, 0x8b, 0xb3, 0xa8, 0x00, 0xd3, 0x32, 0x74,
|
||||
0x26, 0xb0, 0x57, 0xdd, 0x94, 0x12, 0x06, 0x9e, 0x83, 0x99, 0xfa, 0x6e, 0x3a, 0xe0, 0xed, 0xd5, 0x1b, 0x24, 0xaa,
|
||||
0x82, 0xa5, 0x1d, 0x9d, 0x26, 0x41, 0xee, 0x11, 0x1e, 0x0f, 0xb6, 0x1b, 0xa9, 0xb9, 0x03, 0xd4, 0xc3, 0x6c, 0x4a,
|
||||
0x3c, 0xf7, 0x81, 0x1d, 0x49, 0xb3, 0xcc, 0x5f, 0x64, 0x47, 0xa4, 0x54, 0xaa, 0xdd, 0xb3, 0xee, 0x54, 0xf8, 0x43,
|
||||
0x10, 0x70, 0xd8, 0x1b, 0xe8, 0xef, 0x99, 0x8e, 0x8b, 0xdd, 0x99, 0x14, 0x7d, 0x52, 0xc3, 0xb6, 0x29, 0xec, 0x87,
|
||||
0x4e, 0xee, 0xb3, 0xe0, 0xea, 0x94, 0x09, 0x7b, 0x8f, 0x67, 0xc2, 0x1e, 0x52, 0xb5, 0xcb, 0xcb, 0x6a, 0x13, 0xf7,
|
||||
0x74, 0x4e, 0x1a, 0xc2, 0x4f, 0xef, 0x59, 0xf0, 0x0a, 0xf8, 0xff, 0x2f, 0x29, 0xee, 0x0f, 0xa7, 0xb7, 0x2f, 0x48,
|
||||
0x6d, 0x5f, 0x98, 0xd5, 0x8c, 0x77, 0xca, 0x79, 0xe8, 0x41, 0xfa, 0x62, 0x58, 0xb0, 0xa5, 0xf7, 0x67, 0x40, 0xfb,
|
||||
0xdf, 0x38, 0x06, 0x2f, 0xbc, 0x29, 0xbe, 0x44, 0xba, 0x31, 0x10, 0xe1, 0x60, 0x8a, 0x26, 0x57, 0x43, 0x3c, 0xf4,
|
||||
0x90, 0xaa, 0x9a, 0xc6, 0x68, 0x82, 0xa7, 0x40, 0x30, 0xc6, 0xc1, 0x04, 0x26, 0x90, 0xef, 0xe1, 0xd1, 0x6b, 0x3f,
|
||||
0xc0, 0xe3, 0x11, 0x50, 0xf9, 0x2e, 0x0e, 0x7c, 0x64, 0x68, 0xc7, 0xd8, 0x07, 0x71, 0x8a, 0x24, 0x28, 0x01, 0xe8,
|
||||
0x24, 0xc0, 0xee, 0x04, 0xc4, 0x8d, 0xb1, 0x7b, 0x89, 0xa7, 0x63, 0x34, 0xc5, 0x13, 0x80, 0x0e, 0x0f, 0x47, 0x85,
|
||||
0x33, 0xc2, 0x1e, 0x4c, 0x07, 0x63, 0x32, 0xc5, 0xc3, 0x00, 0xe9, 0xc6, 0xc0, 0x31, 0x01, 0x11, 0x0e, 0x76, 0xbd,
|
||||
0xd7, 0x01, 0xf6, 0x27, 0xa0, 0x77, 0x38, 0x7c, 0x01, 0x62, 0x2f, 0x87, 0xc8, 0xb4, 0x06, 0x5e, 0x50, 0x30, 0x7a,
|
||||
0x0c, 0x34, 0xff, 0x9f, 0x0b, 0x1a, 0x40, 0xe2, 0xa1, 0x00, 0x5f, 0x42, 0xec, 0x7a, 0x8a, 0xdf, 0xb4, 0x06, 0x37,
|
||||
0xcf, 0x43, 0xee, 0x1f, 0xc6, 0x2c, 0xf8, 0xe7, 0x62, 0xe6, 0x29, 0x04, 0xa0, 0x0b, 0xba, 0x41, 0x0e, 0xd2, 0x8d,
|
||||
0xd1, 0x0d, 0xcc, 0xd3, 0xab, 0x4b, 0x34, 0x05, 0xae, 0xf1, 0x14, 0x5d, 0xa2, 0x91, 0x42, 0x17, 0xd8, 0x87, 0x86,
|
||||
0xc9, 0x01, 0xa6, 0x2f, 0x84, 0x71, 0xf8, 0x37, 0x86, 0xf1, 0x31, 0x9f, 0xfe, 0xc6, 0x2e, 0xfd, 0x15, 0x57, 0x10,
|
||||
0x94, 0x63, 0xba, 0x0c, 0x8b, 0x06, 0xe6, 0x15, 0xaf, 0xaa, 0x28, 0x78, 0x94, 0x43, 0x35, 0x02, 0xef, 0x7a, 0x0f,
|
||||
0xb1, 0x34, 0xce, 0xbd, 0xf9, 0xbd, 0x2a, 0x1d, 0x28, 0xbd, 0x79, 0xa4, 0xd3, 0xf9, 0xfc, 0x26, 0xa7, 0xe8, 0xd5,
|
||||
0xf5, 0x3b, 0x78, 0x08, 0x16, 0x05, 0xe2, 0xd5, 0x1a, 0xde, 0x9b, 0x5b, 0x24, 0x2b, 0xf5, 0x82, 0xe7, 0x50, 0x2a,
|
||||
0xaa, 0x2e, 0x3c, 0x20, 0x50, 0x57, 0x2c, 0x60, 0x8c, 0xa3, 0x45, 0x33, 0x7f, 0x57, 0x50, 0x22, 0x28, 0x5a, 0xb2,
|
||||
0x15, 0x45, 0x4c, 0x42, 0x1d, 0x50, 0x52, 0x24, 0x99, 0x6a, 0x8e, 0x8c, 0x9a, 0xee, 0x6d, 0x25, 0x69, 0x88, 0xae,
|
||||
0xaa, 0x7a, 0xab, 0x85, 0x24, 0x39, 0xe1, 0x4b, 0x9a, 0x1e, 0x84, 0x29, 0xea, 0x6d, 0xd5, 0x36, 0xe8, 0x97, 0x17,
|
||||
0x6f, 0x5e, 0xab, 0x87, 0x36, 0x45, 0x4e, 0xa7, 0x6c, 0x23, 0xd1, 0x8f, 0x37, 0x2f, 0x50, 0x5b, 0xc3, 0xa6, 0x53,
|
||||
0x63, 0x5b, 0xb5, 0xa2, 0xcd, 0x1a, 0x2a, 0x4b, 0xaa, 0x48, 0x40, 0xb9, 0xa0, 0x52, 0x42, 0xa1, 0x21, 0x30, 0x94,
|
||||
0xce, 0xda, 0x13, 0x53, 0x75, 0x83, 0xbb, 0x20, 0x7e, 0xde, 0x95, 0xd7, 0x51, 0x1e, 0x18, 0xd7, 0xaf, 0x3b, 0x6a,
|
||||
0x70, 0x3d, 0x98, 0x47, 0xea, 0x39, 0x8d, 0x88, 0x7e, 0x84, 0xc4, 0x83, 0x35, 0xcb, 0x98, 0x7a, 0xb8, 0xcd, 0x23,
|
||||
0x5d, 0x8f, 0x2a, 0x09, 0xaa, 0x24, 0x32, 0x5f, 0x34, 0x74, 0xaf, 0xa0, 0x7c, 0x09, 0xaf, 0x64, 0xd8, 0x70, 0xa8,
|
||||
0x50, 0x12, 0x9a, 0x57, 0x05, 0x54, 0x40, 0xf1, 0xf5, 0xf5, 0x0f, 0xff, 0x52, 0x9f, 0x3f, 0xc0, 0xcf, 0x13, 0x27,
|
||||
0x3c, 0x29, 0x0c, 0xa3, 0xea, 0x74, 0x7c, 0xe3, 0xa1, 0xf9, 0x90, 0x51, 0xc3, 0x7b, 0x00, 0xfc, 0x4e, 0xef, 0x49,
|
||||
0x79, 0x77, 0x98, 0xec, 0x24, 0xe9, 0x5f, 0x5d, 0xd9, 0x1a, 0x26, 0xd1, 0x2e, 0x4a, 0x26, 0xe7, 0xd7, 0x60, 0x60,
|
||||
0x34, 0x30, 0x0b, 0xe0, 0x9c, 0x72, 0xc0, 0xd0, 0xe6, 0x1d, 0x0f, 0xec, 0xa8, 0x42, 0xec, 0x27, 0x8d, 0x98, 0xd9,
|
||||
0x60, 0xed, 0x65, 0x49, 0x65, 0x5e, 0xa5, 0xf1, 0xbb, 0x1f, 0xaf, 0x6f, 0x8e, 0x1e, 0x77, 0xb0, 0x52, 0x9e, 0x98,
|
||||
0x0f, 0x2c, 0x6d, 0x21, 0x59, 0x4d, 0x1a, 0xa9, 0xc5, 0x3a, 0x2a, 0xce, 0x0e, 0x1e, 0xe9, 0x75, 0xbd, 0x33, 0xda,
|
||||
0xa9, 0x8e, 0x71, 0x30, 0x47, 0x0f, 0xd9, 0x78, 0xd0, 0xfd, 0x99, 0x95, 0x03, 0x73, 0x14, 0x07, 0xe6, 0x5c, 0x0e,
|
||||
0xf4, 0xe7, 0xa7, 0xdf, 0x01, 0xf1, 0x69, 0xfc, 0xac, 0x8e, 0x12, 0x00, 0x00};
|
||||
|
||||
} // namespace captive_portal
|
||||
} // namespace esphome
|
||||
@@ -4,60 +4,27 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/components/wifi/wifi_component.h"
|
||||
#include "captive_index.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace captive_portal {
|
||||
|
||||
static const char *const TAG = "captive_portal";
|
||||
|
||||
void CaptivePortal::handle_index(AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *stream = request->beginResponseStream("text/html");
|
||||
stream->print(F("<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" "
|
||||
"content=\"width=device-width,initial-scale=1,user-scalable=no\"/><title>"));
|
||||
stream->print(App.get_name().c_str());
|
||||
stream->print(F("</title><link rel=\"stylesheet\" href=\"/stylesheet.css\">"));
|
||||
stream->print(F("<script>function c(l){document.getElementById('ssid').value=l.innerText||l.textContent; "
|
||||
"document.getElementById('psk').focus();}</script>"));
|
||||
stream->print(F("</head>"));
|
||||
stream->print(F("<body><div class=\"main\"><h1>WiFi Networks</h1>"));
|
||||
|
||||
if (request->hasArg("save")) {
|
||||
stream->print(F("<div class=\"info\">The ESP will now try to connect to the network...<br/>Please give it some "
|
||||
"time to connect.<br/>Note: Copy the changed network to your YAML file - the next OTA update will "
|
||||
"overwrite these settings.</div>"));
|
||||
}
|
||||
void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *stream = request->beginResponseStream("application/json");
|
||||
stream->addHeader("cache-control", "public, max-age=0, must-revalidate");
|
||||
stream->printf(R"({"name":"%s","aps":[{})", App.get_name().c_str());
|
||||
|
||||
for (auto &scan : wifi::global_wifi_component->get_scan_result()) {
|
||||
if (scan.get_is_hidden())
|
||||
continue;
|
||||
|
||||
stream->print(F("<div class=\"network\" onclick=\"c(this)\"><a href=\"#\" class=\"network-left\">"));
|
||||
|
||||
if (scan.get_rssi() >= -50) {
|
||||
stream->print(F("<img src=\"/wifi-strength-4.svg\">"));
|
||||
} else if (scan.get_rssi() >= -65) {
|
||||
stream->print(F("<img src=\"/wifi-strength-3.svg\">"));
|
||||
} else if (scan.get_rssi() >= -85) {
|
||||
stream->print(F("<img src=\"/wifi-strength-2.svg\">"));
|
||||
} else {
|
||||
stream->print(F("<img src=\"/wifi-strength-1.svg\">"));
|
||||
}
|
||||
|
||||
stream->print(F("<span class=\"network-ssid\">"));
|
||||
stream->print(scan.get_ssid().c_str());
|
||||
stream->print(F("</span></a>"));
|
||||
if (scan.get_with_auth()) {
|
||||
stream->print(F("<img src=\"/lock.svg\">"));
|
||||
}
|
||||
stream->print(F("</div>"));
|
||||
// Assumes no " in ssid, possible unicode isses?
|
||||
stream->printf(R"(,{"ssid":"%s","rssi":%d,"lock":%d})", scan.get_ssid().c_str(), scan.get_rssi(),
|
||||
scan.get_with_auth());
|
||||
}
|
||||
|
||||
stream->print(F("<h3>WiFi Settings</h3><form method=\"GET\" action=\"/wifisave\"><input id=\"ssid\" name=\"ssid\" "
|
||||
"length=32 placeholder=\"SSID\"><br/><input id=\"psk\" name=\"psk\" length=64 type=\"password\" "
|
||||
"placeholder=\"Password\"><br/><br/><button type=\"submit\">Save</button></form><br><hr><br>"));
|
||||
stream->print(F("<h1>OTA Update</h1><form method=\"POST\" action=\"/update\" enctype=\"multipart/form-data\"><input "
|
||||
"type=\"file\" name=\"update\"><button type=\"submit\">Update</button></form>"));
|
||||
stream->print(F("</div></body></html>"));
|
||||
stream->print(F("]}"));
|
||||
request->send(stream);
|
||||
}
|
||||
void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
|
||||
@@ -68,7 +35,7 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
|
||||
ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str());
|
||||
wifi::global_wifi_component->save_wifi_sta(ssid, psk);
|
||||
wifi::global_wifi_component->start_scanning();
|
||||
request->redirect("/?save=true");
|
||||
request->redirect("/?save");
|
||||
}
|
||||
|
||||
void CaptivePortal::setup() {}
|
||||
@@ -98,44 +65,21 @@ void CaptivePortal::start() {
|
||||
this->active_ = true;
|
||||
}
|
||||
|
||||
const char STYLESHEET_CSS[] PROGMEM =
|
||||
R"(*{box-sizing:inherit}div,input{padding:5px;font-size:1em}input{width:95%}body{text-align:center;font-family:sans-serif}button{border:0;border-radius:.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;padding:0}.main{text-align:left;display:inline-block;min-width:260px}.network{display:flex;justify-content:space-between;align-items:center}.network-left{display:flex;align-items:center}.network-ssid{margin-bottom:-7px;margin-left:10px}.info{border:1px solid;margin:10px 0;padding:15px 10px;color:#4f8a10;background-color:#dff2bf})";
|
||||
const char LOCK_SVG[] PROGMEM =
|
||||
R"(<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M12 17a2 2 0 0 0 2-2 2 2 0 0 0-2-2 2 2 0 0 0-2 2 2 2 0 0 0 2 2m6-9a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2h1V6a5 5 0 0 1 5-5 5 5 0 0 1 5 5v2h1m-6-5a3 3 0 0 0-3 3v2h6V6a3 3 0 0 0-3-3z"/></svg>)";
|
||||
|
||||
void CaptivePortal::handleRequest(AsyncWebServerRequest *req) {
|
||||
if (req->url() == "/") {
|
||||
this->handle_index(req);
|
||||
AsyncWebServerResponse *response = req->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
|
||||
response->addHeader("Content-Encoding", "gzip");
|
||||
req->send(response);
|
||||
return;
|
||||
} else if (req->url() == "/config.json") {
|
||||
this->handle_config(req);
|
||||
return;
|
||||
} else if (req->url() == "/wifisave") {
|
||||
this->handle_wifisave(req);
|
||||
return;
|
||||
} else if (req->url() == "/stylesheet.css") {
|
||||
req->send_P(200, "text/css", STYLESHEET_CSS);
|
||||
return;
|
||||
} else if (req->url() == "/lock.svg") {
|
||||
req->send_P(200, "image/svg+xml", LOCK_SVG);
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncResponseStream *stream = req->beginResponseStream("image/svg+xml");
|
||||
stream->print(F("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\"><path d=\"M12 3A18.9 18.9 0 0 "
|
||||
"0 .38 7C4.41 12.06 7.89 16.37 12 21.5L23.65 7C20.32 4.41 16.22 3 12 "));
|
||||
if (req->url() == "/wifi-strength-4.svg") {
|
||||
stream->print(F("3z"));
|
||||
} else {
|
||||
if (req->url() == "/wifi-strength-1.svg") {
|
||||
stream->print(F("3m0 2c3.07 0 6.09.86 8.71 2.45l-5.1 6.36a8.43 8.43 0 0 0-7.22-.01L3.27 7.4"));
|
||||
} else if (req->url() == "/wifi-strength-2.svg") {
|
||||
stream->print(F("3m0 2c3.07 0 6.09.86 8.71 2.45l-3.21 3.98a11.32 11.32 0 0 0-11 0L3.27 7.4"));
|
||||
} else if (req->url() == "/wifi-strength-3.svg") {
|
||||
stream->print(F("3m0 2c3.07 0 6.09.86 8.71 2.45l-1.94 2.43A13.6 13.6 0 0 0 12 8C9 8 6.68 9 5.21 9.84l-1.94-2."));
|
||||
}
|
||||
stream->print(F("4A16.94 16.94 0 0 1 12 5z"));
|
||||
}
|
||||
stream->print(F("\"/></svg>"));
|
||||
req->send(stream);
|
||||
}
|
||||
|
||||
CaptivePortal::CaptivePortal(web_server_base::WebServerBase *base) : base_(base) { global_captive_portal = this; }
|
||||
float CaptivePortal::get_setup_priority() const {
|
||||
// Before WiFi
|
||||
|
||||
@@ -58,7 +58,7 @@ class CaptivePortal : public AsyncWebHandler, public Component {
|
||||
return false;
|
||||
}
|
||||
|
||||
void handle_index(AsyncWebServerRequest *request);
|
||||
void handle_config(AsyncWebServerRequest *request);
|
||||
|
||||
void handle_wifisave(AsyncWebServerRequest *request);
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
<!-- HTTP_HEAD -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
|
||||
<title>{{ App.get_name() }}</title>
|
||||
<link rel="stylesheet" href="./stylesheet.css">
|
||||
<script>
|
||||
function c(l) {
|
||||
document.getElementById('ssid').value = l.innerText || l.textContent;
|
||||
document.getElementById('psk').focus();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<h1>WiFi Networks</h1>
|
||||
<div class="info">
|
||||
The ESP will now try to connect to the network...<br/>
|
||||
Please give it some time to connect.<br/>
|
||||
Note: Copy the changed network to your YAML file - the next OTA update will overwrite these settings.
|
||||
</div>
|
||||
<div class="network" onclick="c(this)">
|
||||
<a href="#" class="network-left">
|
||||
<img src="./wifi-strength-4.svg">
|
||||
<span class="network-ssid">AP1</span>
|
||||
</a>
|
||||
<img src="./lock.svg">
|
||||
</div>
|
||||
<div class="network" onclick="c(this)">
|
||||
<a href="#" class="network-left">
|
||||
<img src="./wifi-strength-2.svg">
|
||||
<span class="network-ssid">AP2</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h3>WiFi Settings</h3>
|
||||
<form method="GET" action="/wifisave">
|
||||
<input id="ssid" name="ssid" length=32 placeholder="SSID"><br/>
|
||||
<input id="psk" name="psk" length=64 type="password" placeholder="Password"><br/>
|
||||
<br/>
|
||||
<button type="submit">Save</button>
|
||||
</form>
|
||||
<br><hr>
|
||||
<br>
|
||||
|
||||
<h1>OTA Update</h1>
|
||||
<form method="POST" action="/update" enctype="multipart/form-data">
|
||||
<input type="file" name="update">
|
||||
<button type="submit">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M12 17a2 2 0 0 0 2-2 2 2 0 0 0-2-2 2 2 0 0 0-2 2 2 2 0 0 0 2 2m6-9a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2h1V6a5 5 0 0 1 5-5 5 5 0 0 1 5 5v2h1m-6-5a3 3 0 0 0-3 3v2h6V6a3 3 0 0 0-3-3z"/></svg>
|
||||
|
Before Width: | Height: | Size: 307 B |
@@ -1,58 +0,0 @@
|
||||
* {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
div, input {
|
||||
padding: 5px;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
body {
|
||||
text-align: center;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 0;
|
||||
border-radius: 0.3rem;
|
||||
background-color: #1fa3ec;
|
||||
color: #fff;
|
||||
line-height: 2.4rem;
|
||||
font-size: 1.2rem;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.main {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
min-width: 260px;
|
||||
}
|
||||
|
||||
.network {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.network-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.network-ssid {
|
||||
margin-bottom: -7px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.info {
|
||||
border: 1px solid;
|
||||
margin: 10px 0px;
|
||||
padding: 15px 10px;
|
||||
color: #4f8a10;
|
||||
background-color: #dff2bf;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M12 3A18.9 18.9 0 0 0 .38 7C4.41 12.06 7.89 16.37 12 21.5L23.65 7C20.32 4.41 16.22 3 12 3m0 2c3.07 0 6.09.86 8.71 2.45l-5.1 6.36a8.43 8.43 0 0 0-7.22-.01L3.27 7.44A16.94 16.94 0 0 1 12 5z"/></svg>
|
||||
|
Before Width: | Height: | Size: 268 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M12 3A18.9 18.9 0 0 0 .38 7C4.41 12.06 7.89 16.37 12 21.5L23.65 7C20.32 4.41 16.22 3 12 3m0 2c3.07 0 6.09.86 8.71 2.45l-3.21 3.98a11.32 11.32 0 0 0-11 0L3.27 7.44A16.94 16.94 0 0 1 12 5z"/></svg>
|
||||
|
Before Width: | Height: | Size: 267 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M12 3A18.9 18.9 0 0 0 .38 7C4.41 12.06 7.89 16.37 12 21.5L23.65 7C20.32 4.41 16.22 3 12 3m0 2c3.07 0 6.09.86 8.71 2.45l-1.94 2.43A13.6 13.6 0 0 0 12 8C9 8 6.68 9 5.21 9.84l-1.94-2.4A16.94 16.94 0 0 1 12 5z"/></svg>
|
||||
|
Before Width: | Height: | Size: 286 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M12 3A18.9 18.9 0 0 0 .38 7C4.41 12.06 7.89 16.37 12 21.5L23.65 7C20.32 4.41 16.22 3 12 3z"/></svg>
|
||||
|
Before Width: | Height: | Size: 171 B |
@@ -25,6 +25,7 @@ CONF_CD74HC4067_ID = "cd74hc4067_id"
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
CD74HC4067Sensor,
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
@@ -33,7 +34,6 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CD74HC4067Sensor),
|
||||
cv.GenerateID(CONF_CD74HC4067_ID): cv.use_id(CD74HC4067Component),
|
||||
cv.Required(CONF_NUMBER): cv.int_range(0, 15),
|
||||
cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler),
|
||||
@@ -47,8 +47,8 @@ async def to_code(config):
|
||||
parent = await cg.get_variable(config[CONF_CD74HC4067_ID])
|
||||
|
||||
var = cg.new_Pvariable(config[CONF_ID], parent)
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
cg.add(var.set_pin(config[CONF_NUMBER]))
|
||||
|
||||
sens = await cg.get_variable(config[CONF_SENSOR])
|
||||
|
||||
@@ -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
|
||||
@@ -3,7 +3,6 @@ import esphome.config_validation as cv
|
||||
from esphome.components import sensor, voltage_sampler
|
||||
from esphome.const import (
|
||||
CONF_SENSOR,
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_AMPERE,
|
||||
@@ -19,6 +18,7 @@ CTClampSensor = ct_clamp_ns.class_("CTClampSensor", sensor.Sensor, cg.PollingCom
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
CTClampSensor,
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
@@ -26,7 +26,6 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CTClampSensor),
|
||||
cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler),
|
||||
cv.Optional(
|
||||
CONF_SAMPLE_DURATION, default="200ms"
|
||||
@@ -38,9 +37,8 @@ CONFIG_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)
|
||||
|
||||
sens = await cg.get_variable(config[CONF_SENSOR])
|
||||
cg.add(var.set_source(sens))
|
||||
|
||||
@@ -11,7 +11,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor),
|
||||
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||
cv.Required(CONF_BINARY_SENSORS): cv.ensure_list(
|
||||
binary_sensor.BINARY_SENSOR_SCHEMA
|
||||
binary_sensor.binary_sensor_schema()
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -32,6 +32,11 @@ void DallasComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up DallasComponent...");
|
||||
|
||||
pin_->setup();
|
||||
|
||||
// clear bus with 480µs high, otherwise initial reset in search_vec() fails
|
||||
pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
|
||||
delayMicroseconds(480);
|
||||
|
||||
one_wire_ = new ESPOneWire(pin_); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
|
||||
std::vector<uint64_t> raw_sensors;
|
||||
@@ -99,20 +104,25 @@ void DallasComponent::update() {
|
||||
this->status_clear_warning();
|
||||
|
||||
bool result;
|
||||
if (!this->one_wire_->reset()) {
|
||||
result = false;
|
||||
} else {
|
||||
result = true;
|
||||
this->one_wire_->skip();
|
||||
this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION);
|
||||
{
|
||||
InterruptLock lock;
|
||||
result = this->one_wire_->reset();
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
ESP_LOGE(TAG, "Requesting conversion failed");
|
||||
this->status_set_warning();
|
||||
for (auto *sensor : this->sensors_) {
|
||||
sensor->publish_state(NAN);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
InterruptLock lock;
|
||||
this->one_wire_->skip();
|
||||
this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION);
|
||||
}
|
||||
|
||||
for (auto *sensor : this->sensors_) {
|
||||
this->set_timeout(sensor->get_address_name(), sensor->millis_to_wait_for_conversion(), [this, sensor] {
|
||||
bool res = sensor->read_scratch_pad();
|
||||
@@ -152,16 +162,26 @@ const std::string &DallasTemperatureSensor::get_address_name() {
|
||||
}
|
||||
bool IRAM_ATTR DallasTemperatureSensor::read_scratch_pad() {
|
||||
auto *wire = this->parent_->one_wire_;
|
||||
if (!wire->reset()) {
|
||||
return false;
|
||||
|
||||
{
|
||||
InterruptLock lock;
|
||||
|
||||
if (!wire->reset()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
wire->select(this->address_);
|
||||
wire->write8(DALLAS_COMMAND_READ_SCRATCH_PAD);
|
||||
{
|
||||
InterruptLock lock;
|
||||
|
||||
for (unsigned char &i : this->scratch_pad_) {
|
||||
i = wire->read8();
|
||||
wire->select(this->address_);
|
||||
wire->write8(DALLAS_COMMAND_READ_SCRATCH_PAD);
|
||||
|
||||
for (unsigned char &i : this->scratch_pad_) {
|
||||
i = wire->read8();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool DallasTemperatureSensor::setup_sensor() {
|
||||
@@ -200,17 +220,20 @@ bool DallasTemperatureSensor::setup_sensor() {
|
||||
}
|
||||
|
||||
auto *wire = this->parent_->one_wire_;
|
||||
if (wire->reset()) {
|
||||
wire->select(this->address_);
|
||||
wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD);
|
||||
wire->write8(this->scratch_pad_[2]); // high alarm temp
|
||||
wire->write8(this->scratch_pad_[3]); // low alarm temp
|
||||
wire->write8(this->scratch_pad_[4]); // resolution
|
||||
wire->reset();
|
||||
{
|
||||
InterruptLock lock;
|
||||
if (wire->reset()) {
|
||||
wire->select(this->address_);
|
||||
wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD);
|
||||
wire->write8(this->scratch_pad_[2]); // high alarm temp
|
||||
wire->write8(this->scratch_pad_[3]); // low alarm temp
|
||||
wire->write8(this->scratch_pad_[4]); // resolution
|
||||
wire->reset();
|
||||
|
||||
// write value to EEPROM
|
||||
wire->select(this->address_);
|
||||
wire->write8(0x48);
|
||||
// write value to EEPROM
|
||||
wire->select(this->address_);
|
||||
wire->write8(0x48);
|
||||
}
|
||||
}
|
||||
|
||||
delay(20); // allow it to finish operation
|
||||
|
||||
@@ -15,8 +15,6 @@ ESPOneWire::ESPOneWire(InternalGPIOPin *pin) { pin_ = pin->to_isr(); }
|
||||
bool HOT IRAM_ATTR ESPOneWire::reset() {
|
||||
// See reset here:
|
||||
// https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
|
||||
InterruptLock lock;
|
||||
|
||||
// Wait for communication to clear (delay G)
|
||||
pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
|
||||
uint8_t retries = 125;
|
||||
@@ -43,16 +41,18 @@ bool HOT IRAM_ATTR ESPOneWire::reset() {
|
||||
}
|
||||
|
||||
void HOT IRAM_ATTR ESPOneWire::write_bit(bool bit) {
|
||||
// See write 1/0 bit here:
|
||||
// https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
|
||||
InterruptLock lock;
|
||||
|
||||
// drive bus low
|
||||
pin_.pin_mode(gpio::FLAG_OUTPUT);
|
||||
pin_.digital_write(false);
|
||||
|
||||
uint32_t delay0 = bit ? 10 : 65;
|
||||
uint32_t delay1 = bit ? 55 : 5;
|
||||
// from datasheet:
|
||||
// write 0 low time: t_low0: min=60µs, max=120µs
|
||||
// write 1 low time: t_low1: min=1µs, max=15µs
|
||||
// time slot: t_slot: min=60µs, max=120µs
|
||||
// recovery time: t_rec: min=1µs
|
||||
// ds18b20 appears to read the bus after roughly 14µs
|
||||
uint32_t delay0 = bit ? 6 : 60;
|
||||
uint32_t delay1 = bit ? 54 : 5;
|
||||
|
||||
// delay A/C
|
||||
delayMicroseconds(delay0);
|
||||
@@ -63,72 +63,99 @@ void HOT IRAM_ATTR ESPOneWire::write_bit(bool bit) {
|
||||
}
|
||||
|
||||
bool HOT IRAM_ATTR ESPOneWire::read_bit() {
|
||||
// See read bit here:
|
||||
// https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
|
||||
InterruptLock lock;
|
||||
|
||||
// drive bus low, delay A
|
||||
// drive bus low
|
||||
pin_.pin_mode(gpio::FLAG_OUTPUT);
|
||||
pin_.digital_write(false);
|
||||
|
||||
// note: for reading we'll need very accurate timing, as the
|
||||
// timing for the digital_read() is tight; according to the datasheet,
|
||||
// we should read at the end of 16µs starting from the bus low
|
||||
// typically, the ds18b20 pulls the line high after 11µs for a logical 1
|
||||
// and 29µs for a logical 0
|
||||
|
||||
uint32_t start = micros();
|
||||
// datasheet says >1µs
|
||||
delayMicroseconds(3);
|
||||
|
||||
// release bus, delay E
|
||||
pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
|
||||
delayMicroseconds(10);
|
||||
|
||||
// Unfortunately some frameworks have different characteristics than others
|
||||
// esp32 arduino appears to pull the bus low only after the digital_write(false),
|
||||
// whereas on esp-idf it already happens during the pin_mode(OUTPUT)
|
||||
// manually correct for this with these constants.
|
||||
|
||||
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
|
||||
uint32_t timing_constant = 14;
|
||||
#elif defined(USE_ESP32_FRAMEWORK_ESP_IDF)
|
||||
uint32_t timing_constant = 12;
|
||||
#else
|
||||
uint32_t timing_constant = 14;
|
||||
#endif
|
||||
|
||||
// measure from start value directly, to get best accurate timing no matter
|
||||
// how long pin_mode/delayMicroseconds took
|
||||
while (micros() - start < timing_constant)
|
||||
;
|
||||
|
||||
// sample bus to read bit from peer
|
||||
bool r = pin_.digital_read();
|
||||
|
||||
// delay F
|
||||
delayMicroseconds(53);
|
||||
// read slot is at least 60µs; get as close to 60µs to spend less time with interrupts locked
|
||||
uint32_t now = micros();
|
||||
if (now - start < 60)
|
||||
delayMicroseconds(60 - (now - start));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void ESPOneWire::write8(uint8_t val) {
|
||||
void IRAM_ATTR ESPOneWire::write8(uint8_t val) {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
this->write_bit(bool((1u << i) & val));
|
||||
}
|
||||
}
|
||||
|
||||
void ESPOneWire::write64(uint64_t val) {
|
||||
void IRAM_ATTR ESPOneWire::write64(uint64_t val) {
|
||||
for (uint8_t i = 0; i < 64; i++) {
|
||||
this->write_bit(bool((1ULL << i) & val));
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ESPOneWire::read8() {
|
||||
uint8_t IRAM_ATTR ESPOneWire::read8() {
|
||||
uint8_t ret = 0;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
ret |= (uint8_t(this->read_bit()) << i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
uint64_t ESPOneWire::read64() {
|
||||
uint64_t IRAM_ATTR ESPOneWire::read64() {
|
||||
uint64_t ret = 0;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
ret |= (uint64_t(this->read_bit()) << i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void ESPOneWire::select(uint64_t address) {
|
||||
void IRAM_ATTR ESPOneWire::select(uint64_t address) {
|
||||
this->write8(ONE_WIRE_ROM_SELECT);
|
||||
this->write64(address);
|
||||
}
|
||||
void ESPOneWire::reset_search() {
|
||||
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 ESPOneWire::search() {
|
||||
uint64_t IRAM_ATTR ESPOneWire::search() {
|
||||
if (this->last_device_flag_) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
if (!this->reset()) {
|
||||
// Reset failed or no devices present
|
||||
this->reset_search();
|
||||
return 0u;
|
||||
{
|
||||
InterruptLock lock;
|
||||
if (!this->reset()) {
|
||||
// Reset failed or no devices present
|
||||
this->reset_search();
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t id_bit_number = 1;
|
||||
@@ -137,58 +164,58 @@ uint64_t ESPOneWire::search() {
|
||||
bool search_result = false;
|
||||
uint8_t rom_byte_mask = 1;
|
||||
|
||||
// Initiate search
|
||||
this->write8(ONE_WIRE_ROM_SEARCH);
|
||||
do {
|
||||
// read bit
|
||||
bool id_bit = this->read_bit();
|
||||
// read its complement
|
||||
bool cmp_id_bit = this->read_bit();
|
||||
{
|
||||
InterruptLock lock;
|
||||
// Initiate search
|
||||
this->write8(ONE_WIRE_ROM_SEARCH);
|
||||
do {
|
||||
// read bit
|
||||
bool id_bit = this->read_bit();
|
||||
// read its complement
|
||||
bool cmp_id_bit = this->read_bit();
|
||||
|
||||
if (id_bit && cmp_id_bit) {
|
||||
// No devices participating in search
|
||||
break;
|
||||
}
|
||||
|
||||
bool branch;
|
||||
|
||||
if (id_bit != cmp_id_bit) {
|
||||
// only chose one branch, the other one doesn't have any devices.
|
||||
branch = id_bit;
|
||||
} else {
|
||||
// there are devices with both 0s and 1s at this bit
|
||||
if (id_bit_number < this->last_discrepancy_) {
|
||||
branch = (this->rom_number8_()[rom_byte_number] & rom_byte_mask) > 0;
|
||||
} else {
|
||||
branch = id_bit_number == this->last_discrepancy_;
|
||||
if (id_bit && cmp_id_bit) {
|
||||
// No devices participating in search
|
||||
break;
|
||||
}
|
||||
|
||||
if (!branch) {
|
||||
last_zero = id_bit_number;
|
||||
if (last_zero < 9) {
|
||||
this->last_discrepancy_ = last_zero;
|
||||
bool branch;
|
||||
|
||||
if (id_bit != cmp_id_bit) {
|
||||
// only chose one branch, the other one doesn't have any devices.
|
||||
branch = id_bit;
|
||||
} else {
|
||||
// there are devices with both 0s and 1s at this bit
|
||||
if (id_bit_number < this->last_discrepancy_) {
|
||||
branch = (this->rom_number8_()[rom_byte_number] & rom_byte_mask) > 0;
|
||||
} else {
|
||||
branch = id_bit_number == this->last_discrepancy_;
|
||||
}
|
||||
|
||||
if (!branch) {
|
||||
last_zero = id_bit_number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (branch) {
|
||||
// set bit
|
||||
this->rom_number8_()[rom_byte_number] |= rom_byte_mask;
|
||||
} else {
|
||||
// clear bit
|
||||
this->rom_number8_()[rom_byte_number] &= ~rom_byte_mask;
|
||||
}
|
||||
if (branch) {
|
||||
// set bit
|
||||
this->rom_number8_()[rom_byte_number] |= rom_byte_mask;
|
||||
} else {
|
||||
// clear bit
|
||||
this->rom_number8_()[rom_byte_number] &= ~rom_byte_mask;
|
||||
}
|
||||
|
||||
// choose/announce branch
|
||||
this->write_bit(branch);
|
||||
id_bit_number++;
|
||||
rom_byte_mask <<= 1;
|
||||
if (rom_byte_mask == 0u) {
|
||||
// go to next byte
|
||||
rom_byte_number++;
|
||||
rom_byte_mask = 1;
|
||||
}
|
||||
} while (rom_byte_number < 8); // loop through all bytes
|
||||
// choose/announce branch
|
||||
this->write_bit(branch);
|
||||
id_bit_number++;
|
||||
rom_byte_mask <<= 1;
|
||||
if (rom_byte_mask == 0u) {
|
||||
// go to next byte
|
||||
rom_byte_number++;
|
||||
rom_byte_mask = 1;
|
||||
}
|
||||
} while (rom_byte_number < 8); // loop through all bytes
|
||||
}
|
||||
|
||||
if (id_bit_number >= 65) {
|
||||
this->last_discrepancy_ = last_zero;
|
||||
@@ -217,7 +244,7 @@ std::vector<uint64_t> ESPOneWire::search_vec() {
|
||||
|
||||
return res;
|
||||
}
|
||||
void ESPOneWire::skip() {
|
||||
void IRAM_ATTR ESPOneWire::skip() {
|
||||
this->write8(0xCC); // skip ROM
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@ from esphome.const import (
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
CONF_ID,
|
||||
)
|
||||
from . import DallasComponent, dallas_ns
|
||||
|
||||
@@ -17,13 +16,13 @@ DallasTemperatureSensor = dallas_ns.class_("DallasTemperatureSensor", sensor.Sen
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
sensor.sensor_schema(
|
||||
DallasTemperatureSensor,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DallasTemperatureSensor),
|
||||
cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent),
|
||||
cv.Optional(CONF_ADDRESS): cv.hex_int,
|
||||
cv.Optional(CONF_INDEX): cv.positive_int,
|
||||
@@ -36,7 +35,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_DALLAS_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
var = await sensor.new_sensor(config)
|
||||
|
||||
if CONF_ADDRESS in config:
|
||||
cg.add(var.set_address(config[CONF_ADDRESS]))
|
||||
@@ -49,4 +48,3 @@ async def to_code(config):
|
||||
cg.add(var.set_parent(hub))
|
||||
|
||||
cg.add(hub.register_sensor(var))
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import CONF_ID
|
||||
from . import DalyBmsComponent, CONF_BMS_DALY_ID
|
||||
|
||||
CONF_CHARGING_MOS_ENABLED = "charging_mos_enabled"
|
||||
@@ -18,18 +17,10 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent),
|
||||
cv.Optional(
|
||||
CONF_CHARGING_MOS_ENABLED
|
||||
): binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(binary_sensor.BinarySensor),
|
||||
}
|
||||
),
|
||||
): binary_sensor.binary_sensor_schema(),
|
||||
cv.Optional(
|
||||
CONF_DISCHARGING_MOS_ENABLED
|
||||
): binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(binary_sensor.BinarySensor),
|
||||
}
|
||||
),
|
||||
): binary_sensor.binary_sensor_schema(),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
@@ -38,9 +29,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
async def setup_conf(config, key, hub):
|
||||
if key in config:
|
||||
conf = config[key]
|
||||
sens = cg.new_Pvariable(conf[CONF_ID])
|
||||
await binary_sensor.register_binary_sensor(sens, conf)
|
||||
cg.add(getattr(hub, f"set_{key}_binary_sensor")(sens))
|
||||
var = await binary_sensor.new_binary_sensor(conf)
|
||||
cg.add(getattr(hub, f"set_{key}_binary_sensor")(var))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
||||
@@ -16,6 +16,7 @@ static const uint8_t DALY_REQUEST_MIN_MAX_VOLTAGE = 0x91;
|
||||
static const uint8_t DALY_REQUEST_MIN_MAX_TEMPERATURE = 0x92;
|
||||
static const uint8_t DALY_REQUEST_MOS = 0x93;
|
||||
static const uint8_t DALY_REQUEST_STATUS = 0x94;
|
||||
static const uint8_t DALY_REQUEST_CELL_VOLTAGE = 0x95;
|
||||
static const uint8_t DALY_REQUEST_TEMPERATURE = 0x96;
|
||||
|
||||
void DalyBmsComponent::setup() {}
|
||||
@@ -31,6 +32,7 @@ void DalyBmsComponent::update() {
|
||||
this->request_data_(DALY_REQUEST_MIN_MAX_TEMPERATURE);
|
||||
this->request_data_(DALY_REQUEST_MOS);
|
||||
this->request_data_(DALY_REQUEST_STATUS);
|
||||
this->request_data_(DALY_REQUEST_CELL_VOLTAGE);
|
||||
this->request_data_(DALY_REQUEST_TEMPERATURE);
|
||||
|
||||
std::vector<uint8_t> get_battery_level_data;
|
||||
@@ -166,6 +168,71 @@ void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
|
||||
}
|
||||
break;
|
||||
|
||||
case DALY_REQUEST_CELL_VOLTAGE:
|
||||
switch (it[4]) {
|
||||
case 1:
|
||||
if (this->cell_1_voltage_) {
|
||||
this->cell_1_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
|
||||
}
|
||||
if (this->cell_2_voltage_) {
|
||||
this->cell_2_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
|
||||
}
|
||||
if (this->cell_3_voltage_) {
|
||||
this->cell_3_voltage_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (this->cell_4_voltage_) {
|
||||
this->cell_4_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
|
||||
}
|
||||
if (this->cell_5_voltage_) {
|
||||
this->cell_5_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
|
||||
}
|
||||
if (this->cell_6_voltage_) {
|
||||
this->cell_6_voltage_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (this->cell_7_voltage_) {
|
||||
this->cell_7_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
|
||||
}
|
||||
if (this->cell_8_voltage_) {
|
||||
this->cell_8_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
|
||||
}
|
||||
if (this->cell_9_voltage_) {
|
||||
this->cell_9_voltage_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (this->cell_10_voltage_) {
|
||||
this->cell_10_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
|
||||
}
|
||||
if (this->cell_11_voltage_) {
|
||||
this->cell_11_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
|
||||
}
|
||||
if (this->cell_12_voltage_) {
|
||||
this->cell_12_voltage_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (this->cell_13_voltage_) {
|
||||
this->cell_13_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
|
||||
}
|
||||
if (this->cell_14_voltage_) {
|
||||
this->cell_14_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
|
||||
}
|
||||
if (this->cell_15_voltage_) {
|
||||
this->cell_15_voltage_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if (this->cell_16_voltage_) {
|
||||
this->cell_16_voltage_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,23 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice {
|
||||
void set_cells_number_sensor(sensor::Sensor *cells_number) { cells_number_ = cells_number; }
|
||||
void set_temperature_1_sensor(sensor::Sensor *temperature_1_sensor) { temperature_1_sensor_ = temperature_1_sensor; }
|
||||
void set_temperature_2_sensor(sensor::Sensor *temperature_2_sensor) { temperature_2_sensor_ = temperature_2_sensor; }
|
||||
void set_cell_1_voltage_sensor(sensor::Sensor *cell_1_voltage) { cell_1_voltage_ = cell_1_voltage; }
|
||||
void set_cell_2_voltage_sensor(sensor::Sensor *cell_2_voltage) { cell_2_voltage_ = cell_2_voltage; }
|
||||
void set_cell_3_voltage_sensor(sensor::Sensor *cell_3_voltage) { cell_3_voltage_ = cell_3_voltage; }
|
||||
void set_cell_4_voltage_sensor(sensor::Sensor *cell_4_voltage) { cell_4_voltage_ = cell_4_voltage; }
|
||||
void set_cell_5_voltage_sensor(sensor::Sensor *cell_5_voltage) { cell_5_voltage_ = cell_5_voltage; }
|
||||
void set_cell_6_voltage_sensor(sensor::Sensor *cell_6_voltage) { cell_6_voltage_ = cell_6_voltage; }
|
||||
void set_cell_7_voltage_sensor(sensor::Sensor *cell_7_voltage) { cell_7_voltage_ = cell_7_voltage; }
|
||||
void set_cell_8_voltage_sensor(sensor::Sensor *cell_8_voltage) { cell_8_voltage_ = cell_8_voltage; }
|
||||
void set_cell_9_voltage_sensor(sensor::Sensor *cell_9_voltage) { cell_9_voltage_ = cell_9_voltage; }
|
||||
void set_cell_10_voltage_sensor(sensor::Sensor *cell_10_voltage) { cell_10_voltage_ = cell_10_voltage; }
|
||||
void set_cell_11_voltage_sensor(sensor::Sensor *cell_11_voltage) { cell_11_voltage_ = cell_11_voltage; }
|
||||
void set_cell_12_voltage_sensor(sensor::Sensor *cell_12_voltage) { cell_12_voltage_ = cell_12_voltage; }
|
||||
void set_cell_13_voltage_sensor(sensor::Sensor *cell_13_voltage) { cell_13_voltage_ = cell_13_voltage; }
|
||||
void set_cell_14_voltage_sensor(sensor::Sensor *cell_14_voltage) { cell_14_voltage_ = cell_14_voltage; }
|
||||
void set_cell_15_voltage_sensor(sensor::Sensor *cell_15_voltage) { cell_15_voltage_ = cell_15_voltage; }
|
||||
void set_cell_16_voltage_sensor(sensor::Sensor *cell_16_voltage) { cell_16_voltage_ = cell_16_voltage; }
|
||||
|
||||
// TEXT_SENSORS
|
||||
void set_status_text_sensor(text_sensor::TextSensor *status_text_sensor) { status_text_sensor_ = status_text_sensor; }
|
||||
// BINARY_SENSORS
|
||||
@@ -72,6 +89,22 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice {
|
||||
sensor::Sensor *cells_number_{nullptr};
|
||||
sensor::Sensor *temperature_1_sensor_{nullptr};
|
||||
sensor::Sensor *temperature_2_sensor_{nullptr};
|
||||
sensor::Sensor *cell_1_voltage_{nullptr};
|
||||
sensor::Sensor *cell_2_voltage_{nullptr};
|
||||
sensor::Sensor *cell_3_voltage_{nullptr};
|
||||
sensor::Sensor *cell_4_voltage_{nullptr};
|
||||
sensor::Sensor *cell_5_voltage_{nullptr};
|
||||
sensor::Sensor *cell_6_voltage_{nullptr};
|
||||
sensor::Sensor *cell_7_voltage_{nullptr};
|
||||
sensor::Sensor *cell_8_voltage_{nullptr};
|
||||
sensor::Sensor *cell_9_voltage_{nullptr};
|
||||
sensor::Sensor *cell_10_voltage_{nullptr};
|
||||
sensor::Sensor *cell_11_voltage_{nullptr};
|
||||
sensor::Sensor *cell_12_voltage_{nullptr};
|
||||
sensor::Sensor *cell_13_voltage_{nullptr};
|
||||
sensor::Sensor *cell_14_voltage_{nullptr};
|
||||
sensor::Sensor *cell_15_voltage_{nullptr};
|
||||
sensor::Sensor *cell_16_voltage_{nullptr};
|
||||
|
||||
text_sensor::TextSensor *status_text_sensor_{nullptr};
|
||||
|
||||
|
||||
@@ -11,14 +11,11 @@ from esphome.const import (
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_NONE,
|
||||
UNIT_VOLT,
|
||||
UNIT_AMPERE,
|
||||
UNIT_PERCENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_EMPTY,
|
||||
ICON_FLASH,
|
||||
ICON_PERCENT,
|
||||
ICON_COUNTER,
|
||||
@@ -39,6 +36,22 @@ CONF_REMAINING_CAPACITY = "remaining_capacity"
|
||||
CONF_TEMPERATURE_1 = "temperature_1"
|
||||
CONF_TEMPERATURE_2 = "temperature_2"
|
||||
|
||||
CONF_CELL_1_VOLTAGE = "cell_1_voltage"
|
||||
CONF_CELL_2_VOLTAGE = "cell_2_voltage"
|
||||
CONF_CELL_3_VOLTAGE = "cell_3_voltage"
|
||||
CONF_CELL_4_VOLTAGE = "cell_4_voltage"
|
||||
CONF_CELL_5_VOLTAGE = "cell_5_voltage"
|
||||
CONF_CELL_6_VOLTAGE = "cell_6_voltage"
|
||||
CONF_CELL_7_VOLTAGE = "cell_7_voltage"
|
||||
CONF_CELL_8_VOLTAGE = "cell_8_voltage"
|
||||
CONF_CELL_9_VOLTAGE = "cell_9_voltage"
|
||||
CONF_CELL_10_VOLTAGE = "cell_10_voltage"
|
||||
CONF_CELL_11_VOLTAGE = "cell_11_voltage"
|
||||
CONF_CELL_12_VOLTAGE = "cell_12_voltage"
|
||||
CONF_CELL_13_VOLTAGE = "cell_13_voltage"
|
||||
CONF_CELL_14_VOLTAGE = "cell_14_voltage"
|
||||
CONF_CELL_15_VOLTAGE = "cell_15_voltage"
|
||||
CONF_CELL_16_VOLTAGE = "cell_16_voltage"
|
||||
ICON_CURRENT_DC = "mdi:current-dc"
|
||||
ICON_BATTERY_OUTLINE = "mdi:battery-outline"
|
||||
ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up"
|
||||
@@ -63,117 +76,142 @@ TYPES = [
|
||||
CONF_REMAINING_CAPACITY,
|
||||
CONF_TEMPERATURE_1,
|
||||
CONF_TEMPERATURE_2,
|
||||
CONF_CELL_1_VOLTAGE,
|
||||
CONF_CELL_2_VOLTAGE,
|
||||
CONF_CELL_3_VOLTAGE,
|
||||
CONF_CELL_4_VOLTAGE,
|
||||
CONF_CELL_5_VOLTAGE,
|
||||
CONF_CELL_6_VOLTAGE,
|
||||
CONF_CELL_7_VOLTAGE,
|
||||
CONF_CELL_8_VOLTAGE,
|
||||
CONF_CELL_9_VOLTAGE,
|
||||
CONF_CELL_10_VOLTAGE,
|
||||
CONF_CELL_11_VOLTAGE,
|
||||
CONF_CELL_12_VOLTAGE,
|
||||
CONF_CELL_13_VOLTAGE,
|
||||
CONF_CELL_14_VOLTAGE,
|
||||
CONF_CELL_15_VOLTAGE,
|
||||
CONF_CELL_16_VOLTAGE,
|
||||
]
|
||||
|
||||
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(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent),
|
||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||
UNIT_VOLT,
|
||||
ICON_FLASH,
|
||||
1,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
icon=ICON_FLASH,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
|
||||
UNIT_AMPERE,
|
||||
ICON_CURRENT_DC,
|
||||
1,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
icon=ICON_CURRENT_DC,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
|
||||
UNIT_PERCENT,
|
||||
ICON_PERCENT,
|
||||
1,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
icon=ICON_PERCENT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_MAX_CELL_VOLTAGE): sensor.sensor_schema(
|
||||
UNIT_VOLT,
|
||||
ICON_FLASH,
|
||||
2,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
icon=ICON_FLASH,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_MAX_CELL_VOLTAGE_NUMBER): sensor.sensor_schema(
|
||||
UNIT_EMPTY,
|
||||
ICON_COUNTER,
|
||||
0,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_NONE,
|
||||
icon=ICON_COUNTER,
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional(CONF_MIN_CELL_VOLTAGE): sensor.sensor_schema(
|
||||
UNIT_VOLT,
|
||||
ICON_FLASH,
|
||||
2,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
icon=ICON_FLASH,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_MIN_CELL_VOLTAGE_NUMBER): sensor.sensor_schema(
|
||||
UNIT_EMPTY,
|
||||
ICON_COUNTER,
|
||||
0,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_NONE,
|
||||
icon=ICON_COUNTER,
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional(CONF_MAX_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS,
|
||||
ICON_THERMOMETER_CHEVRON_UP,
|
||||
0,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER_CHEVRON_UP,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_MAX_TEMPERATURE_PROBE_NUMBER): sensor.sensor_schema(
|
||||
UNIT_EMPTY,
|
||||
ICON_COUNTER,
|
||||
0,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_NONE,
|
||||
icon=ICON_COUNTER,
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional(CONF_MIN_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS,
|
||||
ICON_THERMOMETER_CHEVRON_DOWN,
|
||||
0,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER_CHEVRON_DOWN,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_MIN_TEMPERATURE_PROBE_NUMBER): sensor.sensor_schema(
|
||||
UNIT_EMPTY,
|
||||
ICON_COUNTER,
|
||||
0,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_NONE,
|
||||
icon=ICON_COUNTER,
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional(CONF_REMAINING_CAPACITY): sensor.sensor_schema(
|
||||
UNIT_AMPERE_HOUR,
|
||||
ICON_GAUGE,
|
||||
2,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_AMPERE_HOUR,
|
||||
icon=ICON_GAUGE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CELLS_NUMBER): sensor.sensor_schema(
|
||||
UNIT_EMPTY,
|
||||
ICON_COUNTER,
|
||||
0,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_NONE,
|
||||
icon=ICON_COUNTER,
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema(
|
||||
UNIT_CELSIUS,
|
||||
ICON_THERMOMETER,
|
||||
0,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema(
|
||||
UNIT_CELSIUS,
|
||||
ICON_THERMOMETER,
|
||||
0,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CELL_1_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_2_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_3_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_4_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_5_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_6_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_7_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_8_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_9_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_10_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_11_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_12_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_13_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_14_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_15_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
cv.Optional(CONF_CELL_16_VOLTAGE): CELL_VOLTAGE_SCHEMA,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
@@ -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...> {
|
||||
|
||||
@@ -132,12 +132,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
},
|
||||
],
|
||||
): [
|
||||
binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
binary_sensor.binary_sensor_schema(DemoBinarySensor).extend(
|
||||
cv.polling_component_schema("60s")
|
||||
).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DemoBinarySensor),
|
||||
}
|
||||
)
|
||||
],
|
||||
cv.Optional(
|
||||
@@ -337,12 +333,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
},
|
||||
],
|
||||
): [
|
||||
sensor.sensor_schema(accuracy_decimals=0)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DemoSensor),
|
||||
}
|
||||
sensor.sensor_schema(DemoSensor, accuracy_decimals=0).extend(
|
||||
cv.polling_component_schema("60s")
|
||||
)
|
||||
],
|
||||
cv.Optional(
|
||||
@@ -376,7 +368,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
},
|
||||
],
|
||||
): [
|
||||
text_sensor.text_sensor_schema(klass=DemoTextSensor).extend(
|
||||
text_sensor.text_sensor_schema(DemoTextSensor).extend(
|
||||
cv.polling_component_schema("60s")
|
||||
)
|
||||
],
|
||||
@@ -386,9 +378,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
async def to_code(config):
|
||||
for conf in config[CONF_BINARY_SENSORS]:
|
||||
var = cg.new_Pvariable(conf[CONF_ID])
|
||||
var = await binary_sensor.new_binary_sensor(conf)
|
||||
await cg.register_component(var, conf)
|
||||
await binary_sensor.register_binary_sensor(var, conf)
|
||||
|
||||
for conf in config[CONF_CLIMATES]:
|
||||
var = cg.new_Pvariable(conf[CONF_ID])
|
||||
@@ -427,9 +418,8 @@ async def to_code(config):
|
||||
cg.add(var.set_type(conf[CONF_TYPE]))
|
||||
|
||||
for conf in config[CONF_SENSORS]:
|
||||
var = cg.new_Pvariable(conf[CONF_ID])
|
||||
var = await sensor.new_sensor(conf)
|
||||
await cg.register_component(var, conf)
|
||||
await sensor.register_sensor(var, conf)
|
||||
|
||||
for conf in config[CONF_SWITCHES]:
|
||||
var = cg.new_Pvariable(conf[CONF_ID])
|
||||
|
||||
@@ -2,19 +2,16 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_GAS,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_NONE,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
UNIT_AMPERE,
|
||||
UNIT_CUBIC_METER,
|
||||
UNIT_EMPTY,
|
||||
UNIT_KILOWATT,
|
||||
UNIT_KILOWATT_HOURS,
|
||||
UNIT_KILOVOLT_AMPS_REACTIVE_HOURS,
|
||||
@@ -30,202 +27,214 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_DSMR_ID): cv.use_id(Dsmr),
|
||||
cv.Optional("energy_delivered_lux"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT_HOURS,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional("energy_delivered_tariff1"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT_HOURS,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional("energy_delivered_tariff2"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT_HOURS,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional("energy_returned_lux"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT_HOURS,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional("energy_returned_tariff1"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT_HOURS,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional("energy_returned_tariff2"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT_HOURS,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional("total_imported_energy"): sensor.sensor_schema(
|
||||
UNIT_KILOVOLT_AMPS_REACTIVE_HOURS,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_NONE,
|
||||
unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE_HOURS,
|
||||
accuracy_decimals=3,
|
||||
),
|
||||
cv.Optional("total_exported_energy"): sensor.sensor_schema(
|
||||
UNIT_KILOVOLT_AMPS_REACTIVE_HOURS,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_NONE,
|
||||
unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE_HOURS,
|
||||
accuracy_decimals=3,
|
||||
),
|
||||
cv.Optional("power_delivered"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_KILOWATT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("power_returned"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_KILOWATT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("reactive_power_delivered"): sensor.sensor_schema(
|
||||
UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_POWER,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
accuracy_decimals=3,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("reactive_power_returned"): sensor.sensor_schema(
|
||||
UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_POWER,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
accuracy_decimals=3,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("electricity_threshold"): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_EMPTY, 3, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
|
||||
accuracy_decimals=3,
|
||||
),
|
||||
cv.Optional("electricity_switch_position"): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_EMPTY, 3, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
|
||||
accuracy_decimals=3,
|
||||
),
|
||||
cv.Optional("electricity_failures"): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional("electricity_long_failures"): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional("electricity_sags_l1"): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional("electricity_sags_l2"): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional("electricity_sags_l3"): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional("electricity_swells_l1"): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional("electricity_swells_l2"): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional("electricity_swells_l3"): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
|
||||
accuracy_decimals=0,
|
||||
),
|
||||
cv.Optional("current_l1"): sensor.sensor_schema(
|
||||
UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("current_l2"): sensor.sensor_schema(
|
||||
UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("current_l3"): sensor.sensor_schema(
|
||||
UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("power_delivered_l1"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_KILOWATT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("power_delivered_l2"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_KILOWATT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("power_delivered_l3"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_KILOWATT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("power_returned_l1"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_KILOWATT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("power_returned_l2"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_KILOWATT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("power_returned_l3"): sensor.sensor_schema(
|
||||
UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
||||
unit_of_measurement=UNIT_KILOWATT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("reactive_power_delivered_l1"): sensor.sensor_schema(
|
||||
UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
accuracy_decimals=3,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("reactive_power_delivered_l2"): sensor.sensor_schema(
|
||||
UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
accuracy_decimals=3,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("reactive_power_delivered_l3"): sensor.sensor_schema(
|
||||
UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
accuracy_decimals=3,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("reactive_power_returned_l1"): sensor.sensor_schema(
|
||||
UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
accuracy_decimals=3,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("reactive_power_returned_l2"): sensor.sensor_schema(
|
||||
UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
accuracy_decimals=3,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("reactive_power_returned_l3"): sensor.sensor_schema(
|
||||
UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE,
|
||||
accuracy_decimals=3,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("voltage_l1"): sensor.sensor_schema(
|
||||
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_NONE
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("voltage_l2"): sensor.sensor_schema(
|
||||
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_NONE
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("voltage_l3"): sensor.sensor_schema(
|
||||
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_NONE
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional("gas_delivered"): sensor.sensor_schema(
|
||||
UNIT_CUBIC_METER,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_GAS,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
unit_of_measurement=UNIT_CUBIC_METER,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_GAS,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional("gas_delivered_be"): sensor.sensor_schema(
|
||||
UNIT_CUBIC_METER,
|
||||
ICON_EMPTY,
|
||||
3,
|
||||
DEVICE_CLASS_GAS,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
unit_of_measurement=UNIT_CUBIC_METER,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_GAS,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
@@ -238,10 +247,10 @@ async def to_code(config):
|
||||
for key, conf in config.items():
|
||||
if not isinstance(conf, dict):
|
||||
continue
|
||||
id = conf.get("id")
|
||||
id = conf[CONF_ID]
|
||||
if id and id.type == sensor.Sensor:
|
||||
s = await sensor.new_sensor(conf)
|
||||
cg.add(getattr(hub, f"set_{key}")(s))
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(getattr(hub, f"set_{key}")(sens))
|
||||
sensors.append(f"F({key})")
|
||||
|
||||
if sensors:
|
||||
|
||||
@@ -3,7 +3,6 @@ import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_PIN,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_PERCENT,
|
||||
@@ -17,25 +16,20 @@ DutyCycleSensor = duty_cycle_ns.class_(
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
DutyCycleSensor,
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
icon=ICON_PERCENT,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DutyCycleSensor),
|
||||
cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema),
|
||||
}
|
||||
)
|
||||
.extend({cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema)})
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||
cg.add(var.set_pin(pin))
|
||||
|
||||
@@ -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_; }
|
||||
|
||||
@@ -2,7 +2,6 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_MICROTESLA,
|
||||
ICON_MAGNET,
|
||||
@@ -15,23 +14,15 @@ ESP32HallSensor = esp32_hall_ns.class_(
|
||||
"ESP32HallSensor", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MICROTESLA,
|
||||
icon=ICON_MAGNET,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ESP32HallSensor),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
)
|
||||
CONFIG_SCHEMA = sensor.sensor_schema(
|
||||
ESP32HallSensor,
|
||||
unit_of_measurement=UNIT_MICROTESLA,
|
||||
icon=ICON_MAGNET,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(cv.polling_component_schema("60s"))
|
||||
|
||||
|
||||
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)
|
||||
|
||||
@@ -39,9 +39,8 @@ ESP32TouchBinarySensor = esp32_touch_ns.class_(
|
||||
"ESP32TouchBinarySensor", binary_sensor.BinarySensor
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(ESP32TouchBinarySensor).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ESP32TouchBinarySensor),
|
||||
cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_id(ESP32TouchComponent),
|
||||
cv.Required(CONF_PIN): validate_touch_pad,
|
||||
cv.Required(CONF_THRESHOLD): cv.uint16_t,
|
||||
|
||||
@@ -17,11 +17,16 @@ import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.helpers import copy_file_if_changed
|
||||
|
||||
from .const import CONF_RESTORE_FROM_FLASH, KEY_BOARD, KEY_ESP8266, esp8266_ns
|
||||
from .const import (
|
||||
CONF_RESTORE_FROM_FLASH,
|
||||
KEY_BOARD,
|
||||
KEY_ESP8266,
|
||||
KEY_PIN_INITIAL_STATES,
|
||||
esp8266_ns,
|
||||
)
|
||||
from .boards import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS
|
||||
|
||||
# force import gpio to register pin schema
|
||||
from .gpio import esp8266_pin_to_code # noqa
|
||||
from .gpio import PinInitialState, add_pin_initial_states_array
|
||||
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
@@ -37,6 +42,9 @@ def set_core_data(config):
|
||||
config[CONF_FRAMEWORK][CONF_VERSION]
|
||||
)
|
||||
CORE.data[KEY_ESP8266][KEY_BOARD] = config[CONF_BOARD]
|
||||
CORE.data[KEY_ESP8266][KEY_PIN_INITIAL_STATES] = [
|
||||
PinInitialState() for _ in range(16)
|
||||
]
|
||||
return config
|
||||
|
||||
|
||||
@@ -221,6 +229,8 @@ async def to_code(config):
|
||||
if ld_script is not None:
|
||||
cg.add_platformio_option("board_build.ldscript", ld_script)
|
||||
|
||||
CORE.add_job(add_pin_initial_states_array)
|
||||
|
||||
|
||||
# Called by writer.py
|
||||
def copy_files():
|
||||
|
||||
@@ -2,6 +2,7 @@ import esphome.codegen as cg
|
||||
|
||||
KEY_ESP8266 = "esp8266"
|
||||
KEY_BOARD = "board"
|
||||
KEY_PIN_INITIAL_STATES = "pin_initial_states"
|
||||
CONF_RESTORE_FROM_FLASH = "restore_from_flash"
|
||||
|
||||
# esp8266 namespace is already defined by arduino, manually prefix esphome
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#ifdef USE_ESP8266
|
||||
|
||||
#include "core.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "preferences.h"
|
||||
@@ -53,6 +54,15 @@ extern "C" void resetPins() { // NOLINT
|
||||
// however, not strictly needed as we set up the pins properly
|
||||
// ourselves and this causes pins to toggle during reboot.
|
||||
force_link_symbols();
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
uint8_t mode = ESPHOME_ESP8266_GPIO_INITIAL_MODE[i];
|
||||
uint8_t level = ESPHOME_ESP8266_GPIO_INITIAL_LEVEL[i];
|
||||
if (mode != 255)
|
||||
pinMode(i, mode); // NOLINT
|
||||
if (level != 255)
|
||||
digitalWrite(i, level); // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
14
esphome/components/esp8266/core.h
Normal file
14
esphome/components/esp8266/core.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
extern const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_MODE[16];
|
||||
extern const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_LEVEL[16];
|
||||
|
||||
namespace esphome {
|
||||
namespace esp8266 {} // namespace esp8266
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP8266
|
||||
@@ -1,4 +1,6 @@
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
@@ -12,12 +14,12 @@ from esphome.const import (
|
||||
CONF_PULLUP,
|
||||
)
|
||||
from esphome import pins
|
||||
from esphome.core import CORE
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
||||
from . import boards
|
||||
from .const import KEY_BOARD, KEY_ESP8266, esp8266_ns
|
||||
from .const import KEY_BOARD, KEY_ESP8266, KEY_PIN_INITIAL_STATES, esp8266_ns
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -160,11 +162,57 @@ ESP8266_PIN_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PinInitialState:
|
||||
mode = 255
|
||||
level: int = 255
|
||||
|
||||
|
||||
@pins.PIN_SCHEMA_REGISTRY.register("esp8266", ESP8266_PIN_SCHEMA)
|
||||
async def esp8266_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
num = config[CONF_NUMBER]
|
||||
mode = config[CONF_MODE]
|
||||
cg.add(var.set_pin(num))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(mode)))
|
||||
if num < 16:
|
||||
initial_state: PinInitialState = CORE.data[KEY_ESP8266][KEY_PIN_INITIAL_STATES][
|
||||
num
|
||||
]
|
||||
if mode[CONF_INPUT]:
|
||||
if mode[CONF_PULLDOWN]:
|
||||
initial_state.mode = cg.global_ns.INPUT_PULLDOWN_16
|
||||
elif mode[CONF_PULLUP]:
|
||||
initial_state.mode = cg.global_ns.INPUT_PULLUP
|
||||
else:
|
||||
initial_state.mode = cg.global_ns.INPUT
|
||||
elif mode[CONF_OUTPUT]:
|
||||
if mode[CONF_OPEN_DRAIN]:
|
||||
initial_state.mode = cg.global_ns.OUTPUT_OPEN_DRAIN
|
||||
else:
|
||||
initial_state.mode = cg.global_ns.OUTPUT
|
||||
initial_state.level = int(config[CONF_INVERTED])
|
||||
|
||||
return var
|
||||
|
||||
|
||||
@coroutine_with_priority(-999.0)
|
||||
async def add_pin_initial_states_array():
|
||||
# Add includes at the very end, so that they override everything
|
||||
initial_states: List[PinInitialState] = CORE.data[KEY_ESP8266][
|
||||
KEY_PIN_INITIAL_STATES
|
||||
]
|
||||
initial_modes_s = ", ".join(str(x.mode) for x in initial_states)
|
||||
initial_levels_s = ", ".join(str(x.level) for x in initial_states)
|
||||
|
||||
cg.add_global(
|
||||
cg.RawExpression(
|
||||
f"const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_MODE[16] = {{{initial_modes_s}}}"
|
||||
)
|
||||
)
|
||||
cg.add_global(
|
||||
cg.RawExpression(
|
||||
f"const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_LEVEL[16] = {{{initial_levels_s}}}"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ from . import CONF_FINGERPRINT_GROW_ID, FingerprintGrowComponent
|
||||
|
||||
DEPENDENCIES = ["fingerprint_grow"]
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend(
|
||||
{
|
||||
cv.GenerateID(CONF_FINGERPRINT_GROW_ID): cv.use_id(FingerprintGrowComponent),
|
||||
cv.Optional(CONF_ICON, default=ICON_KEY_PLUS): cv.icon,
|
||||
|
||||
@@ -14,7 +14,6 @@ from esphome.const import (
|
||||
ICON_DATABASE,
|
||||
ICON_FINGERPRINT,
|
||||
ICON_SECURITY,
|
||||
STATE_CLASS_NONE,
|
||||
)
|
||||
from . import CONF_FINGERPRINT_GROW_ID, FingerprintGrowComponent
|
||||
|
||||
@@ -26,36 +25,30 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
cv.Optional(CONF_FINGERPRINT_COUNT): sensor.sensor_schema(
|
||||
icon=ICON_FINGERPRINT,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_STATUS): sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_CAPACITY): sensor.sensor_schema(
|
||||
icon=ICON_DATABASE,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_SECURITY_LEVEL): sensor.sensor_schema(
|
||||
icon=ICON_SECURITY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_LAST_FINGER_ID): sensor.sensor_schema(
|
||||
icon=ICON_ACCOUNT,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_LAST_CONFIDENCE): sensor.sensor_schema(
|
||||
icon=ICON_ACCOUNT_CHECK,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,25 +2,27 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import CONF_ID, CONF_PIN
|
||||
from esphome.const import CONF_PIN
|
||||
from .. import gpio_ns
|
||||
|
||||
GPIOBinarySensor = gpio_ns.class_(
|
||||
"GPIOBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(GPIOBinarySensor),
|
||||
cv.Required(CONF_PIN): pins.gpio_input_pin_schema,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
CONFIG_SCHEMA = (
|
||||
binary_sensor.binary_sensor_schema(GPIOBinarySensor)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_PIN): pins.gpio_input_pin_schema,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await binary_sensor.register_binary_sensor(var, config)
|
||||
|
||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||
cg.add(var.set_pin(pin))
|
||||
|
||||
@@ -11,7 +11,6 @@ from esphome.const import (
|
||||
CONF_ALTITUDE,
|
||||
CONF_SATELLITES,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_NONE,
|
||||
UNIT_DEGREES,
|
||||
UNIT_KILOMETER_PER_HOUR,
|
||||
UNIT_METER,
|
||||
@@ -35,27 +34,22 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_LATITUDE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREES,
|
||||
accuracy_decimals=6,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
),
|
||||
cv.Optional(CONF_LONGITUDE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREES,
|
||||
accuracy_decimals=6,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
),
|
||||
cv.Optional(CONF_SPEED): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_KILOMETER_PER_HOUR,
|
||||
accuracy_decimals=6,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
),
|
||||
cv.Optional(CONF_COURSE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREES,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
),
|
||||
cv.Optional(CONF_ALTITUDE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_METER,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
),
|
||||
cv.Optional(CONF_SATELLITES): sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -8,7 +8,6 @@ from esphome.const import (
|
||||
CONF_RANGE,
|
||||
ICON_MAGNET,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_NONE,
|
||||
UNIT_MICROTESLA,
|
||||
UNIT_DEGREES,
|
||||
ICON_SCREEN_ROTATION,
|
||||
@@ -88,7 +87,6 @@ heading_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREES,
|
||||
icon=ICON_SCREEN_ROTATION,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
|
||||
@@ -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,28 +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, CONF_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.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HomeassistantBinarySensor),
|
||||
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 = cg.new_Pvariable(config[CONF_ID])
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await binary_sensor.register_binary_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,13 +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,
|
||||
STATE_CLASS_NONE,
|
||||
|
||||
from .. import (
|
||||
HOME_ASSISTANT_IMPORT_SCHEMA,
|
||||
homeassistant_ns,
|
||||
setup_home_assistant_entity,
|
||||
)
|
||||
from .. import homeassistant_ns
|
||||
|
||||
DEPENDENCIES = ["api"]
|
||||
|
||||
@@ -15,23 +13,12 @@ HomeassistantSensor = homeassistant_ns.class_(
|
||||
"HomeassistantSensor", sensor.Sensor, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = sensor.sensor_schema(
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HomeassistantSensor),
|
||||
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))
|
||||
@@ -1,8 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, uart
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_METER,
|
||||
ICON_ARROW_EXPAND_VERTICAL,
|
||||
@@ -16,24 +14,16 @@ HrxlMaxsonarWrComponent = hrxlmaxsonarwr_ns.class_(
|
||||
"HrxlMaxsonarWrComponent", sensor.Sensor, cg.Component, uart.UARTDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_METER,
|
||||
icon=ICON_ARROW_EXPAND_VERTICAL,
|
||||
accuracy_decimals=3,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HrxlMaxsonarWrComponent),
|
||||
}
|
||||
)
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
)
|
||||
CONFIG_SCHEMA = sensor.sensor_schema(
|
||||
HrxlMaxsonarWrComponent,
|
||||
unit_of_measurement=UNIT_METER,
|
||||
icon=ICON_ARROW_EXPAND_VERTICAL,
|
||||
accuracy_decimals=3,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(uart.UART_DEVICE_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)
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
@@ -120,10 +120,16 @@ void HttpRequestComponent::close() {
|
||||
}
|
||||
|
||||
const char *HttpRequestComponent::get_string() {
|
||||
// The static variable is here because HTTPClient::getString() returns a String on ESP32, and we need something to
|
||||
// to keep a buffer alive.
|
||||
static std::string str;
|
||||
str = this->client_.getString().c_str();
|
||||
#if defined(ESP32)
|
||||
// The static variable is here because HTTPClient::getString() returns a String on ESP32,
|
||||
// and we need something to keep a buffer alive.
|
||||
static String str;
|
||||
#else
|
||||
// However on ESP8266, HTTPClient::getString() returns a String& to a member variable.
|
||||
// Leaving this the default so that any new platform either doesn't copy, or encounters a compilation error.
|
||||
auto &
|
||||
#endif
|
||||
str = this->client_.getString();
|
||||
return str.c_str();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_CLK_PIN,
|
||||
CONF_GAIN,
|
||||
CONF_ID,
|
||||
ICON_SCALE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
@@ -24,13 +23,13 @@ GAINS = {
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
HX711Sensor,
|
||||
icon=ICON_SCALE,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HX711Sensor),
|
||||
cv.Required(CONF_DOUT_PIN): pins.gpio_input_pin_schema,
|
||||
cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_GAIN, default=128): cv.enum(GAINS, int=True),
|
||||
@@ -41,9 +40,8 @@ CONFIG_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)
|
||||
|
||||
dout_pin = await cg.gpio_pin_expression(config[CONF_DOUT_PIN])
|
||||
cg.add(var.set_dout_pin(dout_pin))
|
||||
|
||||
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
|
||||
)
|
||||
36
esphome/components/hydreon_rgxx/binary_sensor.py
Normal file
36
esphome/components/hydreon_rgxx/binary_sensor.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_COLD,
|
||||
)
|
||||
|
||||
from . import hydreon_rgxx_ns, HydreonRGxxComponent
|
||||
|
||||
CONF_HYDREON_RGXX_ID = "hydreon_rgxx_id"
|
||||
CONF_TOO_COLD = "too_cold"
|
||||
|
||||
HydreonRGxxBinarySensor = hydreon_rgxx_ns.class_(
|
||||
"HydreonRGxxBinaryComponent", cg.Component
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HydreonRGxxBinarySensor),
|
||||
cv.GenerateID(CONF_HYDREON_RGXX_ID): cv.use_id(HydreonRGxxComponent),
|
||||
cv.Optional(CONF_TOO_COLD): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_COLD
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
main_sensor = await cg.get_variable(config[CONF_HYDREON_RGXX_ID])
|
||||
bin_component = cg.new_Pvariable(config[CONF_ID], main_sensor)
|
||||
await cg.register_component(bin_component, config)
|
||||
if CONF_TOO_COLD in config:
|
||||
tc = await binary_sensor.new_binary_sensor(config[CONF_TOO_COLD])
|
||||
cg.add(main_sensor.set_too_cold_sensor(tc))
|
||||
211
esphome/components/hydreon_rgxx/hydreon_rgxx.cpp
Normal file
211
esphome/components/hydreon_rgxx/hydreon_rgxx.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
#include "hydreon_rgxx.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace hydreon_rgxx {
|
||||
|
||||
static const char *const TAG = "hydreon_rgxx.sensor";
|
||||
static const int MAX_DATA_LENGTH_BYTES = 80;
|
||||
static const uint8_t ASCII_LF = 0x0A;
|
||||
#define HYDREON_RGXX_COMMA ,
|
||||
static const char *const PROTOCOL_NAMES[] = {HYDREON_RGXX_PROTOCOL_LIST(, HYDREON_RGXX_COMMA)};
|
||||
|
||||
void HydreonRGxxComponent::dump_config() {
|
||||
this->check_uart_settings(9600, 1, esphome::uart::UART_CONFIG_PARITY_NONE, 8);
|
||||
ESP_LOGCONFIG(TAG, "hydreon_rgxx:");
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Connection with hydreon_rgxx failed!");
|
||||
}
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
|
||||
int i = 0;
|
||||
#define HYDREON_RGXX_LOG_SENSOR(s) \
|
||||
if (this->sensors_[i++] != nullptr) { \
|
||||
LOG_SENSOR(" ", #s, this->sensors_[i - 1]); \
|
||||
}
|
||||
HYDREON_RGXX_PROTOCOL_LIST(HYDREON_RGXX_LOG_SENSOR, );
|
||||
}
|
||||
|
||||
void HydreonRGxxComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up hydreon_rgxx...");
|
||||
while (this->available() != 0) {
|
||||
this->read();
|
||||
}
|
||||
this->schedule_reboot_();
|
||||
}
|
||||
|
||||
bool HydreonRGxxComponent::sensor_missing_() {
|
||||
if (this->sensors_received_ == -1) {
|
||||
// no request sent yet, don't check
|
||||
return false;
|
||||
} else {
|
||||
if (this->sensors_received_ == 0) {
|
||||
ESP_LOGW(TAG, "No data at all");
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < NUM_SENSORS; i++) {
|
||||
if (this->sensors_[i] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if ((this->sensors_received_ >> i & 1) == 0) {
|
||||
ESP_LOGW(TAG, "Missing %s", PROTOCOL_NAMES[i]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void HydreonRGxxComponent::update() {
|
||||
if (this->boot_count_ > 0) {
|
||||
if (this->sensor_missing_()) {
|
||||
this->no_response_count_++;
|
||||
ESP_LOGE(TAG, "data missing %d times", this->no_response_count_);
|
||||
if (this->no_response_count_ > 15) {
|
||||
ESP_LOGE(TAG, "asking sensor to reboot");
|
||||
for (auto &sensor : this->sensors_) {
|
||||
if (sensor != nullptr) {
|
||||
sensor->publish_state(NAN);
|
||||
}
|
||||
}
|
||||
this->schedule_reboot_();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this->no_response_count_ = 0;
|
||||
}
|
||||
this->write_str("R\n");
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
if (this->too_cold_sensor_ != nullptr) {
|
||||
this->too_cold_sensor_->publish_state(this->too_cold_);
|
||||
}
|
||||
#endif
|
||||
this->too_cold_ = false;
|
||||
this->sensors_received_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void HydreonRGxxComponent::loop() {
|
||||
uint8_t data;
|
||||
while (this->available() > 0) {
|
||||
if (this->read_byte(&data)) {
|
||||
buffer_ += (char) data;
|
||||
if (this->buffer_.back() == static_cast<char>(ASCII_LF) || this->buffer_.length() >= MAX_DATA_LENGTH_BYTES) {
|
||||
// complete line received
|
||||
this->process_line_();
|
||||
this->buffer_.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Communication with the sensor is asynchronous.
|
||||
* We send requests and let esphome continue doing its thing.
|
||||
* Once we have received a complete line, we process it.
|
||||
*
|
||||
* Catching communication failures is done in two layers:
|
||||
*
|
||||
* 1. We check if all requested data has been received
|
||||
* before we send out the next request. If data keeps
|
||||
* missing, we escalate.
|
||||
* 2. Request the sensor to reboot. We retry based on
|
||||
* a timeout. If the sensor does not respond after
|
||||
* several boot attempts, we give up.
|
||||
*/
|
||||
void HydreonRGxxComponent::schedule_reboot_() {
|
||||
this->boot_count_ = 0;
|
||||
this->set_interval("reboot", 5000, [this]() {
|
||||
if (this->boot_count_ < 0) {
|
||||
ESP_LOGW(TAG, "hydreon_rgxx failed to boot %d times", -this->boot_count_);
|
||||
}
|
||||
this->boot_count_--;
|
||||
this->write_str("K\n");
|
||||
if (this->boot_count_ < -5) {
|
||||
ESP_LOGE(TAG, "hydreon_rgxx can't boot, giving up");
|
||||
for (auto &sensor : this->sensors_) {
|
||||
if (sensor != nullptr) {
|
||||
sensor->publish_state(NAN);
|
||||
}
|
||||
}
|
||||
this->mark_failed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool HydreonRGxxComponent::buffer_starts_with_(const std::string &prefix) {
|
||||
return this->buffer_starts_with_(prefix.c_str());
|
||||
}
|
||||
|
||||
bool HydreonRGxxComponent::buffer_starts_with_(const char *prefix) { return buffer_.rfind(prefix, 0) == 0; }
|
||||
|
||||
void HydreonRGxxComponent::process_line_() {
|
||||
ESP_LOGV(TAG, "Read from serial: %s", this->buffer_.substr(0, this->buffer_.size() - 2).c_str());
|
||||
|
||||
if (buffer_[0] == ';') {
|
||||
ESP_LOGI(TAG, "Comment: %s", this->buffer_.substr(0, this->buffer_.size() - 2).c_str());
|
||||
return;
|
||||
}
|
||||
if (this->buffer_starts_with_("PwrDays")) {
|
||||
if (this->boot_count_ <= 0) {
|
||||
this->boot_count_ = 1;
|
||||
} else {
|
||||
this->boot_count_++;
|
||||
}
|
||||
this->cancel_interval("reboot");
|
||||
this->no_response_count_ = 0;
|
||||
ESP_LOGI(TAG, "Boot detected: %s", this->buffer_.substr(0, this->buffer_.size() - 2).c_str());
|
||||
this->write_str("P\nH\nM\n"); // set sensor to polling mode, high res mode, metric mode
|
||||
return;
|
||||
}
|
||||
if (this->buffer_starts_with_("SW")) {
|
||||
std::string::size_type majend = this->buffer_.find('.');
|
||||
std::string::size_type endversion = this->buffer_.find(' ', 3);
|
||||
if (majend == std::string::npos || endversion == std::string::npos || majend > endversion) {
|
||||
ESP_LOGW(TAG, "invalid version string: %s", this->buffer_.substr(0, this->buffer_.size() - 2).c_str());
|
||||
}
|
||||
int major = strtol(this->buffer_.substr(3, majend - 3).c_str(), nullptr, 10);
|
||||
int minor = strtol(this->buffer_.substr(majend + 1, endversion - (majend + 1)).c_str(), nullptr, 10);
|
||||
|
||||
if (major > 10 || minor >= 1000 || minor < 0 || major < 0) {
|
||||
ESP_LOGW(TAG, "invalid version: %s", this->buffer_.substr(0, this->buffer_.size() - 2).c_str());
|
||||
}
|
||||
this->sw_version_ = major * 1000 + minor;
|
||||
ESP_LOGI(TAG, "detected sw version %i", this->sw_version_);
|
||||
return;
|
||||
}
|
||||
bool is_data_line = false;
|
||||
for (int i = 0; i < NUM_SENSORS; i++) {
|
||||
if (this->sensors_[i] != nullptr && this->buffer_starts_with_(PROTOCOL_NAMES[i])) {
|
||||
is_data_line = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_data_line) {
|
||||
std::string::size_type tc = this->buffer_.find("TooCold");
|
||||
this->too_cold_ |= tc != std::string::npos;
|
||||
if (this->too_cold_) {
|
||||
ESP_LOGD(TAG, "Received TooCold");
|
||||
}
|
||||
for (int i = 0; i < NUM_SENSORS; i++) {
|
||||
if (this->sensors_[i] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
std::string::size_type n = this->buffer_.find(PROTOCOL_NAMES[i]);
|
||||
if (n == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
int data = strtol(this->buffer_.substr(n + strlen(PROTOCOL_NAMES[i])).c_str(), nullptr, 10);
|
||||
this->sensors_[i]->publish_state(data);
|
||||
ESP_LOGD(TAG, "Received %s: %f", PROTOCOL_NAMES[i], this->sensors_[i]->get_raw_state());
|
||||
this->sensors_received_ |= (1 << i);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Got unknown line: %s", this->buffer_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
float HydreonRGxxComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
} // namespace hydreon_rgxx
|
||||
} // namespace esphome
|
||||
76
esphome/components/hydreon_rgxx/hydreon_rgxx.h
Normal file
76
esphome/components/hydreon_rgxx/hydreon_rgxx.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace hydreon_rgxx {
|
||||
|
||||
enum RGModel {
|
||||
RG9 = 1,
|
||||
RG15 = 2,
|
||||
};
|
||||
|
||||
#ifdef HYDREON_RGXX_NUM_SENSORS
|
||||
static const uint8_t NUM_SENSORS = HYDREON_RGXX_NUM_SENSORS;
|
||||
#else
|
||||
static const uint8_t NUM_SENSORS = 1;
|
||||
#endif
|
||||
|
||||
#ifndef HYDREON_RGXX_PROTOCOL_LIST
|
||||
#define HYDREON_RGXX_PROTOCOL_LIST(F, SEP) F("")
|
||||
#endif
|
||||
|
||||
class HydreonRGxxComponent : public PollingComponent, public uart::UARTDevice {
|
||||
public:
|
||||
void set_sensor(sensor::Sensor *sensor, int index) { this->sensors_[index] = sensor; }
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
void set_too_cold_sensor(binary_sensor::BinarySensor *sensor) { this->too_cold_sensor_ = sensor; }
|
||||
#endif
|
||||
void set_model(RGModel model) { model_ = model; }
|
||||
|
||||
/// Schedule data readings.
|
||||
void update() override;
|
||||
/// Read data once available
|
||||
void loop() override;
|
||||
/// Setup the sensor and test for a connection.
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
void process_line_();
|
||||
void schedule_reboot_();
|
||||
bool buffer_starts_with_(const std::string &prefix);
|
||||
bool buffer_starts_with_(const char *prefix);
|
||||
bool sensor_missing_();
|
||||
|
||||
sensor::Sensor *sensors_[NUM_SENSORS] = {nullptr};
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
binary_sensor::BinarySensor *too_cold_sensor_ = nullptr;
|
||||
#endif
|
||||
|
||||
int16_t boot_count_ = 0;
|
||||
int16_t no_response_count_ = 0;
|
||||
std::string buffer_;
|
||||
RGModel model_ = RG9;
|
||||
int sw_version_ = 0;
|
||||
bool too_cold_ = false;
|
||||
|
||||
// bit field showing which sensors we have received data for
|
||||
int sensors_received_ = -1;
|
||||
};
|
||||
|
||||
class HydreonRGxxBinaryComponent : public Component {
|
||||
public:
|
||||
HydreonRGxxBinaryComponent(HydreonRGxxComponent *parent) {}
|
||||
};
|
||||
|
||||
} // namespace hydreon_rgxx
|
||||
} // namespace esphome
|
||||
119
esphome/components/hydreon_rgxx/sensor.py
Normal file
119
esphome/components/hydreon_rgxx/sensor.py
Normal file
@@ -0,0 +1,119 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import uart, sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MODEL,
|
||||
CONF_MOISTURE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
|
||||
from . import RGModel, HydreonRGxxComponent
|
||||
|
||||
UNIT_INTENSITY = "intensity"
|
||||
UNIT_MILLIMETERS = "mm"
|
||||
UNIT_MILLIMETERS_PER_HOUR = "mm/h"
|
||||
|
||||
CONF_ACC = "acc"
|
||||
CONF_EVENT_ACC = "event_acc"
|
||||
CONF_TOTAL_ACC = "total_acc"
|
||||
CONF_R_INT = "r_int"
|
||||
|
||||
RG_MODELS = {
|
||||
"RG_9": RGModel.RG9,
|
||||
"RG_15": RGModel.RG15,
|
||||
# https://rainsensors.com/wp-content/uploads/sites/3/2020/07/rg-15_instructions_sw_1.000.pdf
|
||||
# https://rainsensors.com/wp-content/uploads/sites/3/2021/03/2020.08.25-rg-9_instructions.pdf
|
||||
# https://rainsensors.com/wp-content/uploads/sites/3/2021/03/2021.03.11-rg-9_instructions.pdf
|
||||
}
|
||||
SUPPORTED_SENSORS = {
|
||||
CONF_ACC: ["RG_15"],
|
||||
CONF_EVENT_ACC: ["RG_15"],
|
||||
CONF_TOTAL_ACC: ["RG_15"],
|
||||
CONF_R_INT: ["RG_15"],
|
||||
CONF_MOISTURE: ["RG_9"],
|
||||
}
|
||||
PROTOCOL_NAMES = {
|
||||
CONF_MOISTURE: "R",
|
||||
CONF_ACC: "Acc",
|
||||
CONF_R_INT: "Rint",
|
||||
CONF_EVENT_ACC: "EventAcc",
|
||||
CONF_TOTAL_ACC: "TotalAcc",
|
||||
}
|
||||
|
||||
|
||||
def _validate(config):
|
||||
for conf, models in SUPPORTED_SENSORS.items():
|
||||
if conf in config:
|
||||
if config[CONF_MODEL] not in models:
|
||||
raise cv.Invalid(
|
||||
f"{conf} is only available on {' and '.join(models)}, not {config[CONF_MODEL]}"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HydreonRGxxComponent),
|
||||
cv.Required(CONF_MODEL): cv.enum(
|
||||
RG_MODELS,
|
||||
upper=True,
|
||||
space="_",
|
||||
),
|
||||
cv.Optional(CONF_ACC): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MILLIMETERS,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_EVENT_ACC): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MILLIMETERS,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_TOTAL_ACC): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MILLIMETERS,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_R_INT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MILLIMETERS_PER_HOUR,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_MOISTURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_INTENSITY,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(uart.UART_DEVICE_SCHEMA),
|
||||
_validate,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
cg.add_define(
|
||||
"HYDREON_RGXX_PROTOCOL_LIST(F, sep)",
|
||||
cg.RawExpression(
|
||||
" sep ".join([f'F("{name}")' for name in PROTOCOL_NAMES.values()])
|
||||
),
|
||||
)
|
||||
cg.add_define("HYDREON_RGXX_NUM_SENSORS", len(PROTOCOL_NAMES))
|
||||
|
||||
for i, conf in enumerate(PROTOCOL_NAMES):
|
||||
if conf in config:
|
||||
sens = await sensor.new_sensor(config[conf])
|
||||
cg.add(var.set_sensor(sens, i))
|
||||
@@ -46,21 +46,21 @@ class I2CDevice {
|
||||
I2CRegister reg(uint8_t a_register) { return {this, a_register}; }
|
||||
|
||||
ErrorCode read(uint8_t *data, size_t len) { return bus_->read(address_, data, len); }
|
||||
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len) {
|
||||
ErrorCode err = this->write(&a_register, 1);
|
||||
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop = true) {
|
||||
ErrorCode err = this->write(&a_register, 1, stop);
|
||||
if (err != ERROR_OK)
|
||||
return err;
|
||||
return this->read(data, len);
|
||||
}
|
||||
|
||||
ErrorCode write(const uint8_t *data, uint8_t len) { return bus_->write(address_, data, len); }
|
||||
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len) {
|
||||
ErrorCode write(const uint8_t *data, uint8_t len, bool stop = true) { return bus_->write(address_, data, len, stop); }
|
||||
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop = true) {
|
||||
WriteBuffer buffers[2];
|
||||
buffers[0].data = &a_register;
|
||||
buffers[0].len = 1;
|
||||
buffers[1].data = data;
|
||||
buffers[1].len = len;
|
||||
return bus_->writev(address_, buffers, 2);
|
||||
return bus_->writev(address_, buffers, 2, stop);
|
||||
}
|
||||
|
||||
// Compat APIs
|
||||
@@ -93,7 +93,9 @@ class I2CDevice {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read_byte(uint8_t a_register, uint8_t *data) { return read_register(a_register, data, 1) == ERROR_OK; }
|
||||
bool read_byte(uint8_t a_register, uint8_t *data, bool stop = true) {
|
||||
return read_register(a_register, data, 1, stop) == ERROR_OK;
|
||||
}
|
||||
|
||||
optional<uint8_t> read_byte(uint8_t a_register) {
|
||||
uint8_t data;
|
||||
@@ -104,8 +106,8 @@ class I2CDevice {
|
||||
|
||||
bool read_byte_16(uint8_t a_register, uint16_t *data) { return read_bytes_16(a_register, data, 1); }
|
||||
|
||||
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) {
|
||||
return write_register(a_register, data, len) == ERROR_OK;
|
||||
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len, bool stop = true) {
|
||||
return write_register(a_register, data, len, stop) == ERROR_OK;
|
||||
}
|
||||
|
||||
bool write_bytes(uint8_t a_register, const std::vector<uint8_t> &data) {
|
||||
@@ -118,7 +120,9 @@ class I2CDevice {
|
||||
|
||||
bool write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len);
|
||||
|
||||
bool write_byte(uint8_t a_register, uint8_t data) { return write_bytes(a_register, &data, 1); }
|
||||
bool write_byte(uint8_t a_register, uint8_t data, bool stop = true) {
|
||||
return write_bytes(a_register, &data, 1, stop);
|
||||
}
|
||||
|
||||
bool write_byte_16(uint8_t a_register, uint16_t data) { return write_bytes_16(a_register, &data, 1); }
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ enum ErrorCode {
|
||||
ERROR_NOT_INITIALIZED = 4,
|
||||
ERROR_TOO_LARGE = 5,
|
||||
ERROR_UNKNOWN = 6,
|
||||
ERROR_CRC = 7,
|
||||
};
|
||||
|
||||
struct ReadBuffer {
|
||||
@@ -36,12 +37,18 @@ class I2CBus {
|
||||
}
|
||||
virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) = 0;
|
||||
virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len) {
|
||||
return write(address, buffer, len, true);
|
||||
}
|
||||
virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len, bool stop) {
|
||||
WriteBuffer buf;
|
||||
buf.data = buffer;
|
||||
buf.len = len;
|
||||
return writev(address, &buf, 1);
|
||||
return writev(address, &buf, 1, stop);
|
||||
}
|
||||
virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) = 0;
|
||||
virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) {
|
||||
return writev(address, buffers, cnt, true);
|
||||
}
|
||||
virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) = 0;
|
||||
|
||||
protected:
|
||||
void i2c_scan_() {
|
||||
|
||||
@@ -104,7 +104,7 @@ ErrorCode ArduinoI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt)
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) {
|
||||
ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) {
|
||||
// logging is only enabled with vv level, if warnings are shown the caller
|
||||
// should log them
|
||||
if (!initialized_) {
|
||||
@@ -139,7 +139,7 @@ ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cn
|
||||
return ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
uint8_t status = wire_->endTransmission(true);
|
||||
uint8_t status = wire_->endTransmission(stop);
|
||||
if (status == 0) {
|
||||
return ERROR_OK;
|
||||
} else if (status == 1) {
|
||||
|
||||
@@ -20,7 +20,7 @@ class ArduinoI2CBus : public I2CBus, public Component {
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override;
|
||||
ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) override;
|
||||
ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) override;
|
||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
||||
|
||||
void set_scan(bool scan) { scan_ = scan; }
|
||||
|
||||
@@ -142,7 +142,7 @@ ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) {
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) {
|
||||
ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) {
|
||||
// logging is only enabled with vv level, if warnings are shown the caller
|
||||
// should log them
|
||||
if (!initialized_) {
|
||||
|
||||
@@ -20,7 +20,7 @@ class IDFI2CBus : public I2CBus, public Component {
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override;
|
||||
ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) override;
|
||||
ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) override;
|
||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
||||
|
||||
void set_scan(bool scan) { scan_ = scan; }
|
||||
|
||||
@@ -25,10 +25,10 @@ static const uint8_t PROGMEM INITCMD_M5STACK[] = {
|
||||
0xF2, 1, 0x00, // 3Gamma Function Disable
|
||||
ILI9341_GAMMASET , 1, 0x01, // Gamma curve selected
|
||||
ILI9341_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||
0x0E, 0x09, 0x00,
|
||||
ILI9341_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||
0x31, 0x36, 0x0F,
|
||||
ILI9341_SLPOUT , 0x80, // Exit Sleep
|
||||
ILI9341_DISPON , 0x80, // Display on
|
||||
@@ -55,10 +55,10 @@ static const uint8_t PROGMEM INITCMD_TFT[] = {
|
||||
0xF2, 1, 0x00, // 3Gamma Function Disable
|
||||
ILI9341_GAMMASET , 1, 0x01, // Gamma curve selected
|
||||
ILI9341_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||
0x0E, 0x09, 0x00,
|
||||
ILI9341_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||
0x31, 0x36, 0x0F,
|
||||
ILI9341_SLPOUT , 0x80, // Exit Sleep
|
||||
ILI9341_DISPON , 0x80, // Display on
|
||||
|
||||
@@ -16,47 +16,73 @@ static const char *const TAG = "json";
|
||||
static std::vector<char> global_json_build_buffer; // NOLINT
|
||||
|
||||
std::string build_json(const json_build_t &f) {
|
||||
// Here we are allocating as much heap memory as available minus 2kb to be safe
|
||||
// Here we are allocating up to 5kb of memory,
|
||||
// with the heap size minus 2kb to be safe if less than 5kb
|
||||
// as we can not have a true dynamic sized document.
|
||||
// The excess memory is freed below with `shrinkToFit()`
|
||||
#ifdef USE_ESP8266
|
||||
const size_t free_heap = ESP.getMaxFreeBlockSize() - 2048; // NOLINT(readability-static-accessed-through-instance)
|
||||
const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance)
|
||||
#elif defined(USE_ESP32)
|
||||
const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT) - 2048;
|
||||
const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument json_document(free_heap);
|
||||
const size_t request_size = std::min(free_heap, (size_t) 512);
|
||||
|
||||
DynamicJsonDocument json_document(request_size);
|
||||
if (json_document.capacity() == 0) {
|
||||
ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, largest free heap block: %u bytes",
|
||||
request_size, free_heap);
|
||||
return "{}";
|
||||
}
|
||||
JsonObject root = json_document.to<JsonObject>();
|
||||
f(root);
|
||||
json_document.shrinkToFit();
|
||||
|
||||
ESP_LOGV(TAG, "Size after shrink %u bytes", json_document.capacity());
|
||||
std::string output;
|
||||
serializeJson(json_document, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
void parse_json(const std::string &data, const json_parse_t &f) {
|
||||
// Here we are allocating as much heap memory as available minus 2kb to be safe
|
||||
// Here we are allocating 1.5 times the data size,
|
||||
// with the heap size minus 2kb to be safe if less than that
|
||||
// as we can not have a true dynamic sized document.
|
||||
// The excess memory is freed below with `shrinkToFit()`
|
||||
#ifdef USE_ESP8266
|
||||
const size_t free_heap = ESP.getMaxFreeBlockSize() - 2048; // NOLINT(readability-static-accessed-through-instance)
|
||||
const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance)
|
||||
#elif defined(USE_ESP32)
|
||||
const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT) - 2048;
|
||||
const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
|
||||
#endif
|
||||
bool pass = false;
|
||||
size_t request_size = std::min(free_heap, (size_t)(data.size() * 1.5));
|
||||
do {
|
||||
DynamicJsonDocument json_document(request_size);
|
||||
if (json_document.capacity() == 0) {
|
||||
ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, free heap: %u", request_size,
|
||||
free_heap);
|
||||
return;
|
||||
}
|
||||
DeserializationError err = deserializeJson(json_document, data);
|
||||
json_document.shrinkToFit();
|
||||
|
||||
DynamicJsonDocument json_document(free_heap);
|
||||
DeserializationError err = deserializeJson(json_document, data);
|
||||
json_document.shrinkToFit();
|
||||
JsonObject root = json_document.as<JsonObject>();
|
||||
|
||||
JsonObject root = json_document.as<JsonObject>();
|
||||
|
||||
if (err) {
|
||||
ESP_LOGW(TAG, "Parsing JSON failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
f(root);
|
||||
if (err == DeserializationError::Ok) {
|
||||
pass = true;
|
||||
f(root);
|
||||
} else if (err == DeserializationError::NoMemory) {
|
||||
if (request_size * 2 >= free_heap) {
|
||||
ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller");
|
||||
return;
|
||||
}
|
||||
ESP_LOGV(TAG, "Increasing memory allocation.");
|
||||
request_size *= 2;
|
||||
continue;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "JSON parse error: %s", err.c_str());
|
||||
return;
|
||||
}
|
||||
} while (!pass);
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import display
|
||||
from esphome.const import CONF_DIMENSIONS
|
||||
from esphome.const import CONF_DIMENSIONS, CONF_POSITION, CONF_DATA
|
||||
|
||||
CONF_USER_CHARACTERS = "user_characters"
|
||||
|
||||
lcd_base_ns = cg.esphome_ns.namespace("lcd_base")
|
||||
LCDDisplay = lcd_base_ns.class_("LCDDisplay", cg.PollingComponent)
|
||||
@@ -16,9 +18,35 @@ def validate_lcd_dimensions(value):
|
||||
return value
|
||||
|
||||
|
||||
def validate_user_characters(value):
|
||||
positions = set()
|
||||
for conf in value:
|
||||
if conf[CONF_POSITION] in positions:
|
||||
raise cv.Invalid(
|
||||
f"Duplicate user defined character at position {conf[CONF_POSITION]}"
|
||||
)
|
||||
positions.add(conf[CONF_POSITION])
|
||||
return value
|
||||
|
||||
|
||||
LCD_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_DIMENSIONS): validate_lcd_dimensions,
|
||||
cv.Optional(CONF_USER_CHARACTERS): cv.All(
|
||||
cv.ensure_list(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_POSITION): cv.int_range(min=0, max=7),
|
||||
cv.Required(CONF_DATA): cv.All(
|
||||
cv.ensure_list(cv.int_range(min=0, max=31)),
|
||||
cv.Length(min=8, max=8),
|
||||
),
|
||||
}
|
||||
),
|
||||
),
|
||||
cv.Length(max=8),
|
||||
validate_user_characters,
|
||||
),
|
||||
}
|
||||
).extend(cv.polling_component_schema("1s"))
|
||||
|
||||
@@ -27,3 +55,6 @@ async def setup_lcd_display(var, config):
|
||||
await cg.register_component(var, config)
|
||||
await display.register_display(var, config)
|
||||
cg.add(var.set_dimensions(config[CONF_DIMENSIONS][0], config[CONF_DIMENSIONS][1]))
|
||||
if CONF_USER_CHARACTERS in config:
|
||||
for usr in config[CONF_USER_CHARACTERS]:
|
||||
cg.add(var.set_user_defined_char(usr[CONF_POSITION], usr[CONF_DATA]))
|
||||
|
||||
@@ -65,6 +65,13 @@ void LCDDisplay::setup() {
|
||||
this->command_(LCD_DISPLAY_COMMAND_FUNCTION_SET | display_function);
|
||||
}
|
||||
|
||||
// store user defined characters
|
||||
for (auto &user_defined_char : this->user_defined_chars_) {
|
||||
this->command_(LCD_DISPLAY_COMMAND_SET_CGRAM_ADDR | (user_defined_char.first << 3));
|
||||
for (auto data : user_defined_char.second)
|
||||
this->send(data, true);
|
||||
}
|
||||
|
||||
this->command_(LCD_DISPLAY_COMMAND_FUNCTION_SET | display_function);
|
||||
uint8_t display_control = LCD_DISPLAY_DISPLAY_ON;
|
||||
this->command_(LCD_DISPLAY_COMMAND_DISPLAY_CONTROL | display_control);
|
||||
@@ -160,6 +167,13 @@ void LCDDisplay::strftime(uint8_t column, uint8_t row, const char *format, time:
|
||||
}
|
||||
void LCDDisplay::strftime(const char *format, time::ESPTime time) { this->strftime(0, 0, format, time); }
|
||||
#endif
|
||||
void LCDDisplay::loadchar(uint8_t location, uint8_t charmap[]) {
|
||||
location &= 0x7; // we only have 8 locations 0-7
|
||||
this->command_(LCD_DISPLAY_COMMAND_SET_CGRAM_ADDR | (location << 3));
|
||||
for (int i = 0; i < 8; i++) {
|
||||
this->send(charmap[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lcd_base
|
||||
} // namespace esphome
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace esphome {
|
||||
namespace lcd_base {
|
||||
|
||||
@@ -19,6 +21,8 @@ class LCDDisplay : public PollingComponent {
|
||||
this->rows_ = rows;
|
||||
}
|
||||
|
||||
void set_user_defined_char(uint8_t pos, const std::vector<uint8_t> &data) { this->user_defined_chars_[pos] = data; }
|
||||
|
||||
void setup() override;
|
||||
float get_setup_priority() const override;
|
||||
void update() override;
|
||||
@@ -47,6 +51,9 @@ class LCDDisplay : public PollingComponent {
|
||||
void strftime(const char *format, time::ESPTime time) __attribute__((format(strftime, 2, 0)));
|
||||
#endif
|
||||
|
||||
/// Load custom char to given location
|
||||
void loadchar(uint8_t location, uint8_t charmap[]);
|
||||
|
||||
protected:
|
||||
virtual bool is_four_bit_mode() = 0;
|
||||
virtual void write_n_bits(uint8_t value, uint8_t n) = 0;
|
||||
@@ -58,6 +65,7 @@ class LCDDisplay : public PollingComponent {
|
||||
uint8_t columns_;
|
||||
uint8_t rows_;
|
||||
uint8_t *buffer_{nullptr};
|
||||
std::map<uint8_t, std::vector<uint8_t> > user_defined_chars_;
|
||||
};
|
||||
|
||||
} // namespace lcd_base
|
||||
|
||||
@@ -43,7 +43,7 @@ async def to_code(config):
|
||||
await lcd_base.setup_lcd_display(var, config)
|
||||
pins_ = []
|
||||
for conf in config[CONF_DATA_PINS]:
|
||||
pins_.append((await cg.gpio_pin_expression(conf)))
|
||||
pins_.append(await cg.gpio_pin_expression(conf))
|
||||
cg.add(var.set_data_pins(*pins_))
|
||||
enable = await cg.gpio_pin_expression(config[CONF_ENABLE_PIN])
|
||||
cg.add(var.set_enable_pin(enable))
|
||||
|
||||
@@ -52,6 +52,8 @@ RESTORE_MODES = {
|
||||
"ALWAYS_ON": LightRestoreMode.LIGHT_ALWAYS_ON,
|
||||
"RESTORE_INVERTED_DEFAULT_OFF": LightRestoreMode.LIGHT_RESTORE_INVERTED_DEFAULT_OFF,
|
||||
"RESTORE_INVERTED_DEFAULT_ON": LightRestoreMode.LIGHT_RESTORE_INVERTED_DEFAULT_ON,
|
||||
"RESTORE_AND_OFF": LightRestoreMode.LIGHT_RESTORE_AND_OFF,
|
||||
"RESTORE_AND_ON": LightRestoreMode.LIGHT_RESTORE_AND_ON,
|
||||
}
|
||||
|
||||
LIGHT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
|
||||
@@ -68,6 +68,12 @@ void LightState::setup() {
|
||||
recovered.state = !recovered.state;
|
||||
}
|
||||
break;
|
||||
case LIGHT_RESTORE_AND_OFF:
|
||||
case LIGHT_RESTORE_AND_ON:
|
||||
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_object_id_hash());
|
||||
this->rtc_.load(&recovered);
|
||||
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
|
||||
break;
|
||||
case LIGHT_ALWAYS_OFF:
|
||||
recovered.state = false;
|
||||
break;
|
||||
|
||||
@@ -22,6 +22,8 @@ enum LightRestoreMode {
|
||||
LIGHT_ALWAYS_ON,
|
||||
LIGHT_RESTORE_INVERTED_DEFAULT_OFF,
|
||||
LIGHT_RESTORE_INVERTED_DEFAULT_ON,
|
||||
LIGHT_RESTORE_AND_OFF,
|
||||
LIGHT_RESTORE_AND_ON,
|
||||
};
|
||||
|
||||
/** This class represents the communication layer between the front-end MQTT layer and the
|
||||
|
||||
@@ -113,8 +113,27 @@ void LilygoT547Touchscreen::loop() {
|
||||
if (tp.state == 0x06)
|
||||
tp.state = 0x07;
|
||||
|
||||
tp.y = (uint16_t)((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F));
|
||||
tp.x = (uint16_t)((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F));
|
||||
uint16_t y = (uint16_t)((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F));
|
||||
uint16_t x = (uint16_t)((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F));
|
||||
|
||||
switch (this->rotation_) {
|
||||
case ROTATE_0_DEGREES:
|
||||
tp.y = this->display_height_ - y;
|
||||
tp.x = x;
|
||||
break;
|
||||
case ROTATE_90_DEGREES:
|
||||
tp.x = this->display_height_ - y;
|
||||
tp.y = this->display_width_ - x;
|
||||
break;
|
||||
case ROTATE_180_DEGREES:
|
||||
tp.y = y;
|
||||
tp.x = this->display_width_ - x;
|
||||
break;
|
||||
case ROTATE_270_DEGREES:
|
||||
tp.x = y;
|
||||
tp.y = x;
|
||||
break;
|
||||
}
|
||||
|
||||
this->defer([this, tp]() { this->send_touch_(tp); });
|
||||
}
|
||||
@@ -122,8 +141,28 @@ void LilygoT547Touchscreen::loop() {
|
||||
TouchPoint tp;
|
||||
tp.id = (buffer[0] >> 4) & 0x0F;
|
||||
tp.state = 0x06;
|
||||
tp.y = (uint16_t)((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F));
|
||||
tp.x = (uint16_t)((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F));
|
||||
|
||||
uint16_t y = (uint16_t)((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F));
|
||||
uint16_t x = (uint16_t)((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F));
|
||||
|
||||
switch (this->rotation_) {
|
||||
case ROTATE_0_DEGREES:
|
||||
tp.y = this->display_height_ - y;
|
||||
tp.x = x;
|
||||
break;
|
||||
case ROTATE_90_DEGREES:
|
||||
tp.x = this->display_height_ - y;
|
||||
tp.y = this->display_width_ - x;
|
||||
break;
|
||||
case ROTATE_180_DEGREES:
|
||||
tp.y = y;
|
||||
tp.x = this->display_width_ - x;
|
||||
break;
|
||||
case ROTATE_270_DEGREES:
|
||||
tp.x = y;
|
||||
tp.y = x;
|
||||
break;
|
||||
}
|
||||
|
||||
this->defer([this, tp]() { this->send_touch_(tp); });
|
||||
}
|
||||
|
||||
@@ -203,15 +203,6 @@ async def to_code(config):
|
||||
)
|
||||
|
||||
|
||||
def maybe_simple_message(schema):
|
||||
def validator(value):
|
||||
if isinstance(value, dict):
|
||||
return cv.Schema(schema)(value)
|
||||
return cv.Schema(schema)({CONF_FORMAT: value})
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
def validate_printf(value):
|
||||
# https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python
|
||||
cfmt = r"""
|
||||
@@ -234,7 +225,7 @@ def validate_printf(value):
|
||||
|
||||
CONF_LOGGER_LOG = "logger.log"
|
||||
LOGGER_LOG_ACTION_SCHEMA = cv.All(
|
||||
maybe_simple_message(
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.Required(CONF_FORMAT): cv.string,
|
||||
cv.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_),
|
||||
@@ -242,9 +233,10 @@ LOGGER_LOG_ACTION_SCHEMA = cv.All(
|
||||
*LOG_LEVEL_TO_ESP_LOG, upper=True
|
||||
),
|
||||
cv.Optional(CONF_TAG, default="main"): cv.string,
|
||||
}
|
||||
),
|
||||
validate_printf,
|
||||
},
|
||||
validate_printf,
|
||||
key=CONF_FORMAT,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -52,16 +52,28 @@ CONFIG_SCHEMA = cv.All(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LTR390Component),
|
||||
cv.Optional(CONF_LIGHT): sensor.sensor_schema(
|
||||
UNIT_LUX, ICON_BRIGHTNESS_5, 1, DEVICE_CLASS_ILLUMINANCE
|
||||
unit_of_measurement=UNIT_LUX,
|
||||
icon=ICON_BRIGHTNESS_5,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
),
|
||||
cv.Optional(CONF_AMBIENT_LIGHT): sensor.sensor_schema(
|
||||
UNIT_COUNTS, ICON_BRIGHTNESS_5, 1, DEVICE_CLASS_ILLUMINANCE
|
||||
unit_of_measurement=UNIT_COUNTS,
|
||||
icon=ICON_BRIGHTNESS_5,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
),
|
||||
cv.Optional(CONF_UV_INDEX): sensor.sensor_schema(
|
||||
UNIT_UVI, ICON_BRIGHTNESS_5, 5, DEVICE_CLASS_ILLUMINANCE
|
||||
unit_of_measurement=UNIT_UVI,
|
||||
icon=ICON_BRIGHTNESS_5,
|
||||
accuracy_decimals=5,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
),
|
||||
cv.Optional(CONF_UV): sensor.sensor_schema(
|
||||
UNIT_COUNTS, ICON_BRIGHTNESS_5, 1, DEVICE_CLASS_ILLUMINANCE
|
||||
unit_of_measurement=UNIT_COUNTS,
|
||||
icon=ICON_BRIGHTNESS_5,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
),
|
||||
cv.Optional(CONF_GAIN, default="X3"): cv.enum(GAIN_OPTIONS),
|
||||
cv.Optional(CONF_RESOLUTION, default=18): cv.enum(RES_OPTIONS),
|
||||
|
||||
@@ -2,7 +2,6 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, spi
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_REFERENCE_TEMPERATURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
@@ -16,13 +15,13 @@ MAX31855Sensor = max31855_ns.class_(
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
MAX31855Sensor,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MAX31855Sensor),
|
||||
cv.Optional(CONF_REFERENCE_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=2,
|
||||
@@ -37,10 +36,9 @@ CONFIG_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 spi.register_spi_device(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
if CONF_REFERENCE_TEMPERATURE in config:
|
||||
tc_ref = await sensor.new_sensor(config[CONF_REFERENCE_TEMPERATURE])
|
||||
cg.add(var.set_reference_sensor(tc_ref))
|
||||
|
||||
@@ -2,7 +2,6 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, spi
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MAINS_FILTER,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
@@ -22,6 +21,7 @@ FILTER = {
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
MAX31856Sensor,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
@@ -29,7 +29,6 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MAX31856Sensor),
|
||||
cv.Optional(CONF_MAINS_FILTER, default="60HZ"): cv.enum(
|
||||
FILTER, upper=True, space=""
|
||||
),
|
||||
@@ -41,8 +40,7 @@ CONFIG_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 spi.register_spi_device(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
cg.add(var.set_filter(config[CONF_MAINS_FILTER]))
|
||||
|
||||
@@ -2,7 +2,6 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, spi
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MAINS_FILTER,
|
||||
CONF_REFERENCE_RESISTANCE,
|
||||
CONF_RTD_NOMINAL_RESISTANCE,
|
||||
@@ -25,6 +24,7 @@ FILTER = {
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
MAX31865Sensor,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
@@ -32,7 +32,6 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MAX31865Sensor),
|
||||
cv.Required(CONF_REFERENCE_RESISTANCE): cv.All(
|
||||
cv.resistance, cv.Range(min=100, max=10000)
|
||||
),
|
||||
@@ -51,10 +50,9 @@ CONFIG_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 spi.register_spi_device(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
cg.add(var.set_reference_resistance(config[CONF_REFERENCE_RESISTANCE]))
|
||||
cg.add(var.set_nominal_resistance(config[CONF_RTD_NOMINAL_RESISTANCE]))
|
||||
cg.add(var.set_filter(config[CONF_MAINS_FILTER]))
|
||||
|
||||
0
esphome/components/max44009/__init__.py
Normal file
0
esphome/components/max44009/__init__.py
Normal file
144
esphome/components/max44009/max44009.cpp
Normal file
144
esphome/components/max44009/max44009.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
#include "max44009.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace max44009 {
|
||||
|
||||
static const char *const TAG = "max44009.sensor";
|
||||
|
||||
// REGISTERS
|
||||
static const uint8_t MAX44009_REGISTER_CONFIGURATION = 0x02;
|
||||
static const uint8_t MAX44009_LUX_READING_HIGH = 0x03;
|
||||
static const uint8_t MAX44009_LUX_READING_LOW = 0x04;
|
||||
// CONFIGURATION MASKS
|
||||
static const uint8_t MAX44009_CFG_CONTINUOUS = 0x80;
|
||||
// ERROR CODES
|
||||
static const uint8_t MAX44009_OK = 0;
|
||||
static const uint8_t MAX44009_ERROR_WIRE_REQUEST = -10;
|
||||
static const uint8_t MAX44009_ERROR_OVERFLOW = -20;
|
||||
static const uint8_t MAX44009_ERROR_HIGH_BYTE = -30;
|
||||
static const uint8_t MAX44009_ERROR_LOW_BYTE = -31;
|
||||
|
||||
void MAX44009Sensor::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up MAX44009...");
|
||||
bool state_ok = false;
|
||||
if (this->mode_ == MAX44009Mode::MAX44009_MODE_LOW_POWER) {
|
||||
state_ok = this->set_low_power_mode();
|
||||
} else if (this->mode_ == MAX44009Mode::MAX44009_MODE_CONTINUOUS) {
|
||||
state_ok = this->set_continuous_mode();
|
||||
} else {
|
||||
/*
|
||||
* Mode AUTO: Set mode depending on update interval
|
||||
* - On low power mode, the IC measures lux intensity only once every 800ms
|
||||
* regardless of integration time
|
||||
* - On continuous mode, the IC continuously measures lux intensity
|
||||
*/
|
||||
if (this->get_update_interval() < 800) {
|
||||
state_ok = this->set_continuous_mode();
|
||||
} else {
|
||||
state_ok = this->set_low_power_mode();
|
||||
}
|
||||
}
|
||||
if (!state_ok)
|
||||
this->mark_failed();
|
||||
}
|
||||
|
||||
void MAX44009Sensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MAX44009:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication with MAX44009 failed!");
|
||||
}
|
||||
}
|
||||
|
||||
float MAX44009Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void MAX44009Sensor::update() {
|
||||
// update sensor illuminance value
|
||||
float lux = this->read_illuminance_();
|
||||
if (this->error_ != MAX44009_OK) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
} else {
|
||||
this->status_clear_warning();
|
||||
this->publish_state(lux);
|
||||
}
|
||||
}
|
||||
|
||||
float MAX44009Sensor::read_illuminance_() {
|
||||
uint8_t datahigh = this->read_(MAX44009_LUX_READING_HIGH);
|
||||
if (error_ != MAX44009_OK) {
|
||||
this->error_ = MAX44009_ERROR_HIGH_BYTE;
|
||||
return this->error_;
|
||||
}
|
||||
uint8_t datalow = this->read_(MAX44009_LUX_READING_LOW);
|
||||
if (error_ != MAX44009_OK) {
|
||||
this->error_ = MAX44009_ERROR_LOW_BYTE;
|
||||
return this->error_;
|
||||
}
|
||||
uint8_t exponent = datahigh >> 4;
|
||||
if (exponent == 0x0F) {
|
||||
this->error_ = MAX44009_ERROR_OVERFLOW;
|
||||
return this->error_;
|
||||
}
|
||||
|
||||
return this->convert_to_lux_(datahigh, datalow);
|
||||
}
|
||||
|
||||
float MAX44009Sensor::convert_to_lux_(uint8_t data_high, uint8_t data_low) {
|
||||
uint8_t exponent = data_high >> 4;
|
||||
uint32_t mantissa = ((data_high & 0x0F) << 4) + (data_low & 0x0F);
|
||||
return ((0x0001 << exponent) * 0.045) * mantissa;
|
||||
}
|
||||
|
||||
bool MAX44009Sensor::set_continuous_mode() {
|
||||
uint8_t config = this->read_(MAX44009_REGISTER_CONFIGURATION);
|
||||
if (this->error_ == MAX44009_OK) {
|
||||
config |= MAX44009_CFG_CONTINUOUS;
|
||||
this->write_(MAX44009_REGISTER_CONFIGURATION, config);
|
||||
this->status_clear_warning();
|
||||
ESP_LOGV(TAG, "set to continuous mode");
|
||||
return true;
|
||||
} else {
|
||||
this->status_set_warning();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MAX44009Sensor::set_low_power_mode() {
|
||||
uint8_t config = this->read_(MAX44009_REGISTER_CONFIGURATION);
|
||||
if (this->error_ == MAX44009_OK) {
|
||||
config &= ~MAX44009_CFG_CONTINUOUS;
|
||||
this->write_(MAX44009_REGISTER_CONFIGURATION, config);
|
||||
this->status_clear_warning();
|
||||
ESP_LOGV(TAG, "set to low power mode");
|
||||
return true;
|
||||
} else {
|
||||
this->status_set_warning();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t MAX44009Sensor::read_(uint8_t reg) {
|
||||
uint8_t data = 0;
|
||||
if (!this->read_byte(reg, &data)) {
|
||||
this->error_ = MAX44009_ERROR_WIRE_REQUEST;
|
||||
} else {
|
||||
this->error_ = MAX44009_OK;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void MAX44009Sensor::write_(uint8_t reg, uint8_t value) {
|
||||
if (!this->write_byte(reg, value)) {
|
||||
this->error_ = MAX44009_ERROR_WIRE_REQUEST;
|
||||
} else {
|
||||
this->error_ = MAX44009_OK;
|
||||
}
|
||||
}
|
||||
|
||||
void MAX44009Sensor::set_mode(MAX44009Mode mode) { this->mode_ = mode; }
|
||||
|
||||
} // namespace max44009
|
||||
} // namespace esphome
|
||||
37
esphome/components/max44009/max44009.h
Normal file
37
esphome/components/max44009/max44009.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace max44009 {
|
||||
|
||||
enum MAX44009Mode { MAX44009_MODE_AUTO, MAX44009_MODE_LOW_POWER, MAX44009_MODE_CONTINUOUS };
|
||||
|
||||
/// This class implements support for the MAX44009 Illuminance i2c sensor.
|
||||
class MAX44009Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
MAX44009Sensor() {}
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
void update() override;
|
||||
void set_mode(MAX44009Mode mode);
|
||||
bool set_continuous_mode();
|
||||
bool set_low_power_mode();
|
||||
|
||||
protected:
|
||||
/// Read the illuminance value
|
||||
float read_illuminance_();
|
||||
float convert_to_lux_(uint8_t data_high, uint8_t data_low);
|
||||
uint8_t read_(uint8_t reg);
|
||||
void write_(uint8_t reg, uint8_t value);
|
||||
|
||||
int error_;
|
||||
MAX44009Mode mode_;
|
||||
};
|
||||
|
||||
} // namespace max44009
|
||||
} // namespace esphome
|
||||
53
esphome/components/max44009/sensor.py
Normal file
53
esphome/components/max44009/sensor.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, i2c
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MODE,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_LUX,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@berfenger"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
max44009_ns = cg.esphome_ns.namespace("max44009")
|
||||
MAX44009Sensor = max44009_ns.class_(
|
||||
"MAX44009Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
MAX44009Mode = max44009_ns.enum("MAX44009Mode")
|
||||
MODE_OPTIONS = {
|
||||
"auto": MAX44009Mode.MAX44009_MODE_AUTO,
|
||||
"low_power": MAX44009Mode.MAX44009_MODE_LOW_POWER,
|
||||
"continuous": MAX44009Mode.MAX44009_MODE_CONTINUOUS,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_LUX,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MAX44009Sensor),
|
||||
cv.Optional(CONF_MODE, default="low_power"): cv.enum(
|
||||
MODE_OPTIONS, lower=True
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x4A))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
cg.add(var.set_mode(config[CONF_MODE]))
|
||||
@@ -2,7 +2,6 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, spi
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
@@ -15,23 +14,18 @@ MAX6675Sensor = max6675_ns.class_(
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
MAX6675Sensor,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MAX6675Sensor),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(spi.spi_device_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 spi.register_spi_device(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
@@ -20,7 +20,7 @@ void MCP3204::dump_config() {
|
||||
}
|
||||
|
||||
float MCP3204::read_data(uint8_t pin) {
|
||||
uint8_t adc_primary_config = 0b00000110 & 0b00000111;
|
||||
uint8_t adc_primary_config = 0b00000110 | (pin >> 2);
|
||||
uint8_t adc_secondary_config = pin << 6;
|
||||
this->enable();
|
||||
this->transfer_byte(adc_primary_config);
|
||||
|
||||
@@ -17,7 +17,7 @@ CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MCP3204Sensor),
|
||||
cv.GenerateID(CONF_MCP3204_ID): cv.use_id(MCP3204),
|
||||
cv.Required(CONF_NUMBER): cv.int_range(min=0, max=3),
|
||||
cv.Required(CONF_NUMBER): cv.int_range(min=0, max=7),
|
||||
}
|
||||
).extend(cv.polling_component_schema("60s"))
|
||||
|
||||
|
||||
29
esphome/components/mcp4728/__init__.py
Normal file
29
esphome/components/mcp4728/__init__.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@berfenger"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
CONF_STORE_IN_EEPROM = "store_in_eeprom"
|
||||
|
||||
mcp4728_ns = cg.esphome_ns.namespace("mcp4728")
|
||||
MCP4728Component = mcp4728_ns.class_("MCP4728Component", cg.Component, i2c.I2CDevice)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MCP4728Component),
|
||||
cv.Optional(CONF_STORE_IN_EEPROM, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(i2c.i2c_device_schema(0x60))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID], config[CONF_STORE_IN_EEPROM])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
121
esphome/components/mcp4728/mcp4728_output.cpp
Normal file
121
esphome/components/mcp4728/mcp4728_output.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "mcp4728_output.h"
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mcp4728 {
|
||||
|
||||
static const char *const TAG = "mcp4728";
|
||||
|
||||
void MCP4728Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up MCP4728 (0x%02X)...", this->address_);
|
||||
auto err = this->write(nullptr, 0);
|
||||
if (err != i2c::ERROR_OK) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MCP4728Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MCP4728:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication with MCP4728 failed!");
|
||||
}
|
||||
}
|
||||
|
||||
void MCP4728Component::loop() {
|
||||
if (this->update_) {
|
||||
this->update_ = false;
|
||||
if (this->store_in_eeprom_) {
|
||||
if (!this->seq_write_()) {
|
||||
this->status_set_error();
|
||||
} else {
|
||||
this->status_clear_error();
|
||||
}
|
||||
} else {
|
||||
if (!this->multi_write_()) {
|
||||
this->status_set_error();
|
||||
} else {
|
||||
this->status_clear_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MCP4728Component::set_channel_value_(MCP4728ChannelIdx channel, uint16_t value) {
|
||||
uint8_t cn = 0;
|
||||
if (channel == MCP4728_CHANNEL_A) {
|
||||
cn = 'A';
|
||||
} else if (channel == MCP4728_CHANNEL_B) {
|
||||
cn = 'B';
|
||||
} else if (channel == MCP4728_CHANNEL_C) {
|
||||
cn = 'C';
|
||||
} else {
|
||||
cn = 'D';
|
||||
}
|
||||
ESP_LOGV(TAG, "Setting MCP4728 channel %c to %d!", cn, value);
|
||||
reg_[channel].data = value;
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
bool MCP4728Component::multi_write_() {
|
||||
i2c::ErrorCode err[4];
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
uint8_t wd[3];
|
||||
wd[0] = ((uint8_t) CMD::MULTI_WRITE | (i << 1)) & 0xFE;
|
||||
wd[1] = ((uint8_t) reg_[i].vref << 7) | ((uint8_t) reg_[i].pd << 5) | ((uint8_t) reg_[i].gain << 4) |
|
||||
(reg_[i].data >> 8);
|
||||
wd[2] = reg_[i].data & 0xFF;
|
||||
err[i] = this->write(wd, sizeof(wd));
|
||||
}
|
||||
bool ok = true;
|
||||
for (auto &e : err) {
|
||||
if (e != i2c::ERROR_OK) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool MCP4728Component::seq_write_() {
|
||||
uint8_t wd[9];
|
||||
wd[0] = (uint8_t) CMD::SEQ_WRITE;
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
wd[i * 2 + 1] = ((uint8_t) reg_[i].vref << 7) | ((uint8_t) reg_[i].pd << 5) | ((uint8_t) reg_[i].gain << 4) |
|
||||
(reg_[i].data >> 8);
|
||||
wd[i * 2 + 2] = reg_[i].data & 0xFF;
|
||||
}
|
||||
auto err = this->write(wd, sizeof(wd));
|
||||
return err == i2c::ERROR_OK;
|
||||
}
|
||||
|
||||
void MCP4728Component::select_vref_(MCP4728ChannelIdx channel, MCP4728Vref vref) {
|
||||
reg_[channel].vref = vref;
|
||||
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
void MCP4728Component::select_power_down_(MCP4728ChannelIdx channel, MCP4728PwrDown pd) {
|
||||
reg_[channel].pd = pd;
|
||||
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
void MCP4728Component::select_gain_(MCP4728ChannelIdx channel, MCP4728Gain gain) {
|
||||
reg_[channel].gain = gain;
|
||||
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
void MCP4728Channel::write_state(float state) {
|
||||
const uint16_t max_duty = 4095;
|
||||
const float duty_rounded = roundf(state * max_duty);
|
||||
auto duty = static_cast<uint16_t>(duty_rounded);
|
||||
this->parent_->set_channel_value_(this->channel_, duty);
|
||||
}
|
||||
|
||||
} // namespace mcp4728
|
||||
} // namespace esphome
|
||||
91
esphome/components/mcp4728/mcp4728_output.h
Normal file
91
esphome/components/mcp4728/mcp4728_output.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mcp4728 {
|
||||
|
||||
enum class CMD {
|
||||
FAST_WRITE = 0x00,
|
||||
MULTI_WRITE = 0x40,
|
||||
SINGLE_WRITE = 0x58,
|
||||
SEQ_WRITE = 0x50,
|
||||
SELECT_VREF = 0x80,
|
||||
SELECT_GAIN = 0xC0,
|
||||
SELECT_POWER_DOWN = 0xA0
|
||||
};
|
||||
|
||||
enum MCP4728Vref { MCP4728_VREF_VDD = 0, MCP4728_VREF_INTERNAL_2_8V = 1 };
|
||||
|
||||
enum MCP4728PwrDown {
|
||||
MCP4728_PD_NORMAL = 0,
|
||||
MCP4728_PD_GND_1KOHM = 1,
|
||||
MCP4728_PD_GND_100KOHM = 2,
|
||||
MCP4728_PD_GND_500KOHM = 3
|
||||
};
|
||||
|
||||
enum MCP4728Gain { MCP4728_GAIN_X1 = 0, MCP4728_GAIN_X2 = 1 };
|
||||
|
||||
enum MCP4728ChannelIdx { MCP4728_CHANNEL_A = 0, MCP4728_CHANNEL_B = 1, MCP4728_CHANNEL_C = 2, MCP4728_CHANNEL_D = 3 };
|
||||
|
||||
struct DACInputData {
|
||||
MCP4728Vref vref;
|
||||
MCP4728PwrDown pd;
|
||||
MCP4728Gain gain;
|
||||
uint16_t data;
|
||||
};
|
||||
|
||||
class MCP4728Channel;
|
||||
|
||||
/// MCP4728 float output component.
|
||||
class MCP4728Component : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
MCP4728Component(bool store_in_eeprom) : store_in_eeprom_(store_in_eeprom) {}
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
void loop() override;
|
||||
|
||||
protected:
|
||||
friend MCP4728Channel;
|
||||
void set_channel_value_(MCP4728ChannelIdx channel, uint16_t value);
|
||||
bool multi_write_();
|
||||
bool seq_write_();
|
||||
void select_vref_(MCP4728ChannelIdx channel, MCP4728Vref vref);
|
||||
void select_power_down_(MCP4728ChannelIdx channel, MCP4728PwrDown pd);
|
||||
void select_gain_(MCP4728ChannelIdx channel, MCP4728Gain gain);
|
||||
|
||||
private:
|
||||
DACInputData reg_[4];
|
||||
bool store_in_eeprom_ = false;
|
||||
bool update_ = false;
|
||||
};
|
||||
|
||||
class MCP4728Channel : public output::FloatOutput {
|
||||
public:
|
||||
MCP4728Channel(MCP4728Component *parent, MCP4728ChannelIdx channel, MCP4728Vref vref, MCP4728Gain gain,
|
||||
MCP4728PwrDown pwrdown)
|
||||
: parent_(parent), channel_(channel), vref_(vref), gain_(gain), pwrdown_(pwrdown) {
|
||||
// update VREF
|
||||
parent->select_vref_(channel, vref_);
|
||||
// update PD
|
||||
parent->select_power_down_(channel, pwrdown_);
|
||||
// update GAIN
|
||||
parent->select_gain_(channel, gain_);
|
||||
}
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
|
||||
MCP4728Component *parent_;
|
||||
MCP4728ChannelIdx channel_;
|
||||
MCP4728Vref vref_;
|
||||
MCP4728Gain gain_;
|
||||
MCP4728PwrDown pwrdown_;
|
||||
};
|
||||
|
||||
} // namespace mcp4728
|
||||
} // namespace esphome
|
||||
63
esphome/components/mcp4728/output.py
Normal file
63
esphome/components/mcp4728/output.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import output
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID, CONF_GAIN
|
||||
from . import MCP4728Component, mcp4728_ns
|
||||
|
||||
DEPENDENCIES = ["mcp4728"]
|
||||
|
||||
MCP4728Channel = mcp4728_ns.class_("MCP4728Channel", output.FloatOutput)
|
||||
CONF_MCP4728_ID = "mcp4728_id"
|
||||
CONF_VREF = "vref"
|
||||
CONF_POWER_DOWN = "power_down"
|
||||
|
||||
MCP4728Vref = mcp4728_ns.enum("MCP4728Vref")
|
||||
VREF_OPTIONS = {
|
||||
"vdd": MCP4728Vref.MCP4728_VREF_VDD,
|
||||
"internal": MCP4728Vref.MCP4728_VREF_INTERNAL_2_8V,
|
||||
}
|
||||
|
||||
MCP4728Gain = mcp4728_ns.enum("MCP4728Gain")
|
||||
GAIN_OPTIONS = {"X1": MCP4728Gain.MCP4728_GAIN_X1, "X2": MCP4728Gain.MCP4728_GAIN_X2}
|
||||
|
||||
MCP4728PwrDown = mcp4728_ns.enum("MCP4728PwrDown")
|
||||
PWRDOWN_OPTIONS = {
|
||||
"normal": MCP4728PwrDown.MCP4728_PD_NORMAL,
|
||||
"gnd_1k": MCP4728PwrDown.MCP4728_PD_GND_1KOHM,
|
||||
"gnd_100k": MCP4728PwrDown.MCP4728_PD_GND_100KOHM,
|
||||
"gnd_500k": MCP4728PwrDown.MCP4728_PD_GND_500KOHM,
|
||||
}
|
||||
|
||||
MCP4728ChannelIdx = mcp4728_ns.enum("MCP4728ChannelIdx")
|
||||
CHANNEL_OPTIONS = {
|
||||
"A": MCP4728ChannelIdx.MCP4728_CHANNEL_A,
|
||||
"B": MCP4728ChannelIdx.MCP4728_CHANNEL_B,
|
||||
"C": MCP4728ChannelIdx.MCP4728_CHANNEL_C,
|
||||
"D": MCP4728ChannelIdx.MCP4728_CHANNEL_D,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(MCP4728Channel),
|
||||
cv.GenerateID(CONF_MCP4728_ID): cv.use_id(MCP4728Component),
|
||||
cv.Required(CONF_CHANNEL): cv.enum(CHANNEL_OPTIONS, upper=True),
|
||||
cv.Optional(CONF_VREF, default="vdd"): cv.enum(VREF_OPTIONS, upper=False),
|
||||
cv.Optional(CONF_POWER_DOWN, default="normal"): cv.enum(
|
||||
PWRDOWN_OPTIONS, upper=False
|
||||
),
|
||||
cv.Optional(CONF_GAIN, default="X1"): cv.enum(GAIN_OPTIONS, upper=True),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
paren = await cg.get_variable(config[CONF_MCP4728_ID])
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
paren,
|
||||
config[CONF_CHANNEL],
|
||||
config[CONF_VREF],
|
||||
config[CONF_GAIN],
|
||||
config[CONF_POWER_DOWN],
|
||||
)
|
||||
await output.register_output(var, config)
|
||||
@@ -2,7 +2,6 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
@@ -18,23 +17,18 @@ MCP9808Sensor = mcp9808_ns.class_(
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
MCP9808Sensor,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MCP9808Sensor),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x18))
|
||||
)
|
||||
|
||||
|
||||
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 i2c.register_i2c_device(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
@@ -71,9 +71,9 @@ SENSOR_VALUE_TYPE = {
|
||||
"S_DWORD": SensorValueType.S_DWORD,
|
||||
"S_DWORD_R": SensorValueType.S_DWORD_R,
|
||||
"U_QWORD": SensorValueType.U_QWORD,
|
||||
"U_QWORDU_R": SensorValueType.U_QWORD_R,
|
||||
"U_QWORD_R": SensorValueType.U_QWORD_R,
|
||||
"S_QWORD": SensorValueType.S_QWORD,
|
||||
"U_QWORD_R": SensorValueType.S_QWORD_R,
|
||||
"S_QWORD_R": SensorValueType.S_QWORD_R,
|
||||
"FP32": SensorValueType.FP32,
|
||||
"FP32_R": SensorValueType.FP32_R,
|
||||
}
|
||||
@@ -87,9 +87,9 @@ TYPE_REGISTER_MAP = {
|
||||
"S_DWORD": 2,
|
||||
"S_DWORD_R": 2,
|
||||
"U_QWORD": 4,
|
||||
"U_QWORDU_R": 4,
|
||||
"S_QWORD": 4,
|
||||
"U_QWORD_R": 4,
|
||||
"S_QWORD": 4,
|
||||
"S_QWORD_R": 4,
|
||||
"FP32": 2,
|
||||
"FP32_R": 2,
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ ModbusBinarySensor = modbus_controller_ns.class_(
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
binary_sensor.BINARY_SENSOR_SCHEMA.extend(cv.COMPONENT_SCHEMA)
|
||||
binary_sensor.binary_sensor_schema(ModbusBinarySensor)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(ModbusItemBaseSchema)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ModbusBinarySensor),
|
||||
cv.Optional(CONF_REGISTER_TYPE): cv.enum(MODBUS_REGISTER_TYPE),
|
||||
}
|
||||
),
|
||||
|
||||
@@ -455,6 +455,28 @@ ModbusCommandItem ModbusCommandItem::create_custom_command(
|
||||
return cmd;
|
||||
}
|
||||
|
||||
ModbusCommandItem ModbusCommandItem::create_custom_command(
|
||||
ModbusController *modbusdevice, const std::vector<uint16_t> &values,
|
||||
std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
|
||||
&&handler) {
|
||||
ModbusCommandItem cmd = {};
|
||||
cmd.modbusdevice = modbusdevice;
|
||||
cmd.function_code = ModbusFunctionCode::CUSTOM;
|
||||
if (handler == nullptr) {
|
||||
cmd.on_data_func = [](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
|
||||
ESP_LOGI(TAG, "Custom Command sent");
|
||||
};
|
||||
} else {
|
||||
cmd.on_data_func = handler;
|
||||
}
|
||||
for (auto v : values) {
|
||||
cmd.payload.push_back((v >> 8) & 0xFF);
|
||||
cmd.payload.push_back(v & 0xFF);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
bool ModbusCommandItem::send() {
|
||||
if (this->function_code != ModbusFunctionCode::CUSTOM) {
|
||||
modbusdevice->send(uint8_t(this->function_code), this->register_address, this->register_count, this->payload.size(),
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/modbus/modbus.h"
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
@@ -374,8 +374,8 @@ class ModbusCommandItem {
|
||||
const std::vector<bool> &values);
|
||||
/** Create custom modbus command
|
||||
* @param modbusdevice pointer to the device to execute the command
|
||||
* @param values byte vector of data to be sent to the device. The compplete payload must be provided with the
|
||||
* exception of the crc codess
|
||||
* @param values byte vector of data to be sent to the device. The complete payload must be provided with the
|
||||
* exception of the crc codes
|
||||
* @param handler function called when the response is received. Default is just logging a response
|
||||
* @return ModbusCommandItem with the prepared command
|
||||
*/
|
||||
@@ -383,6 +383,18 @@ class ModbusCommandItem {
|
||||
ModbusController *modbusdevice, const std::vector<uint8_t> &values,
|
||||
std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
|
||||
&&handler = nullptr);
|
||||
|
||||
/** Create custom modbus command
|
||||
* @param modbusdevice pointer to the device to execute the command
|
||||
* @param values word vector of data to be sent to the device. The complete payload must be provided with the
|
||||
* exception of the crc codes
|
||||
* @param handler function called when the response is received. Default is just logging a response
|
||||
* @return ModbusCommandItem with the prepared command
|
||||
*/
|
||||
static ModbusCommandItem create_custom_command(
|
||||
ModbusController *modbusdevice, const std::vector<uint16_t> &values,
|
||||
std::function<void(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data)>
|
||||
&&handler = nullptr);
|
||||
};
|
||||
|
||||
/** Modbus controller class.
|
||||
|
||||
@@ -11,6 +11,7 @@ from esphome.const import (
|
||||
)
|
||||
|
||||
from .. import (
|
||||
MODBUS_WRITE_REGISTER_TYPE,
|
||||
add_modbus_base_properties,
|
||||
modbus_controller_ns,
|
||||
modbus_calc_properties,
|
||||
@@ -24,6 +25,7 @@ from ..const import (
|
||||
CONF_CUSTOM_COMMAND,
|
||||
CONF_FORCE_NEW_RANGE,
|
||||
CONF_MODBUS_CONTROLLER_ID,
|
||||
CONF_REGISTER_TYPE,
|
||||
CONF_SKIP_UPDATES,
|
||||
CONF_USE_WRITE_MULTIPLE,
|
||||
CONF_VALUE_TYPE,
|
||||
@@ -61,6 +63,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
number.NUMBER_SCHEMA.extend(ModbusItemBaseSchema).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ModbusNumber),
|
||||
cv.Optional(CONF_REGISTER_TYPE, default="holding"): cv.enum(
|
||||
MODBUS_WRITE_REGISTER_TYPE
|
||||
),
|
||||
cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum(SENSOR_VALUE_TYPE),
|
||||
cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda,
|
||||
# 24 bits are the maximum value for fp32 before precison is lost
|
||||
@@ -81,6 +86,7 @@ async def to_code(config):
|
||||
byte_offset, reg_count = modbus_calc_properties(config)
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
config[CONF_REGISTER_TYPE],
|
||||
config[CONF_ADDRESS],
|
||||
byte_offset,
|
||||
config[CONF_BITMASK],
|
||||
|
||||
@@ -26,6 +26,7 @@ void ModbusNumber::parse_and_publish(const std::vector<uint8_t> &data) {
|
||||
}
|
||||
|
||||
void ModbusNumber::control(float value) {
|
||||
ModbusCommandItem write_cmd;
|
||||
std::vector<uint16_t> data;
|
||||
float write_value = value;
|
||||
// Is there are lambda configured?
|
||||
@@ -45,33 +46,39 @@ void ModbusNumber::control(float value) {
|
||||
write_value = multiply_by_ * write_value;
|
||||
}
|
||||
|
||||
// lambda didn't set payload
|
||||
if (data.empty()) {
|
||||
data = float_to_payload(write_value, this->sensor_value_type);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
"Updating register: connected Sensor=%s start address=0x%X register count=%d new value=%.02f (val=%.02f)",
|
||||
this->get_name().c_str(), this->start_address, this->register_count, value, write_value);
|
||||
|
||||
// Create and send the write command
|
||||
ModbusCommandItem write_cmd;
|
||||
if (this->register_count == 1 && !this->use_write_multiple_) {
|
||||
// since offset is in bytes and a register is 16 bits we get the start by adding offset/2
|
||||
write_cmd =
|
||||
ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset / 2, data[0]);
|
||||
if (!data.empty()) {
|
||||
ESP_LOGV(TAG, "Modbus Number write raw: %s", format_hex_pretty(data).c_str());
|
||||
write_cmd = ModbusCommandItem::create_custom_command(
|
||||
this->parent_, data,
|
||||
[this, write_cmd](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
|
||||
this->parent_->on_write_register_response(write_cmd.register_type, this->start_address, data);
|
||||
});
|
||||
} else {
|
||||
write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset / 2,
|
||||
this->register_count, data);
|
||||
data = float_to_payload(write_value, this->sensor_value_type);
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
"Updating register: connected Sensor=%s start address=0x%X register count=%d new value=%.02f (val=%.02f)",
|
||||
this->get_name().c_str(), this->start_address, this->register_count, value, write_value);
|
||||
|
||||
// Create and send the write command
|
||||
if (this->register_count == 1 && !this->use_write_multiple_) {
|
||||
// since offset is in bytes and a register is 16 bits we get the start by adding offset/2
|
||||
write_cmd =
|
||||
ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset / 2, data[0]);
|
||||
} else {
|
||||
write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset / 2,
|
||||
this->register_count, data);
|
||||
}
|
||||
// publish new value
|
||||
write_cmd.on_data_func = [this, write_cmd, value](ModbusRegisterType register_type, uint16_t start_address,
|
||||
const std::vector<uint8_t> &data) {
|
||||
// gets called when the write command is ack'd from the device
|
||||
parent_->on_write_register_response(write_cmd.register_type, start_address, data);
|
||||
this->publish_state(value);
|
||||
};
|
||||
}
|
||||
// publish new value
|
||||
write_cmd.on_data_func = [this, write_cmd, value](ModbusRegisterType register_type, uint16_t start_address,
|
||||
const std::vector<uint8_t> &data) {
|
||||
// gets called when the write command is ack'd from the device
|
||||
parent_->on_write_register_response(write_cmd.register_type, start_address, data);
|
||||
this->publish_state(value);
|
||||
};
|
||||
parent_->queue_command(write_cmd);
|
||||
this->publish_state(value);
|
||||
}
|
||||
void ModbusNumber::dump_config() { LOG_NUMBER(TAG, "Modbus Number", this); }
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user