mirror of
https://github.com/esphome/esphome.git
synced 2026-10-23 22:48:48 +00:00
Compare commits
166
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3500ecc1c1 | ||
|
|
c793ab30a7 | ||
|
|
1753c45e53 | ||
|
|
1767e4b0be | ||
|
|
5575dd8928 | ||
|
|
8e4592fa87 | ||
|
|
76addfec20 | ||
|
|
704dd4b445 | ||
|
|
c7073b9a0b | ||
|
|
480577e2f4 | ||
|
|
3808c767fd | ||
|
|
d6a9292c41 | ||
|
|
e6ad995c8e | ||
|
|
84ce39e493 | ||
|
|
d9242520e3 | ||
|
|
03bf2de7df | ||
|
|
a70d75e469 | ||
|
|
2dcb97774d | ||
|
|
be9741c6de | ||
|
|
b1c138c612 | ||
|
|
9526233821 | ||
|
|
512e43793b | ||
|
|
603f16623f | ||
|
|
218e696654 | ||
|
|
3e68e0f5e1 | ||
|
|
dd456ea0b2 | ||
|
|
e54d66e620 | ||
|
|
a3bb17d2c6 | ||
|
|
71c0f46e35 | ||
|
|
a153fcc8ee | ||
|
|
85724892cf | ||
|
|
deea006b39 | ||
|
|
619728b686 | ||
|
|
c35d98b622 | ||
|
|
b2d6c78499 | ||
|
|
a801e414a0 | ||
|
|
22a36de4ae | ||
|
|
eca95348ac | ||
|
|
ed9616d512 | ||
|
|
0cd1ae01f1 | ||
|
|
2a416fbce6 | ||
|
|
270d989481 | ||
|
|
3665bcc4e3 | ||
|
|
0922df9bb3 | ||
|
|
54a28b3e3b | ||
|
|
032a2b7200 | ||
|
|
0bb78186f2 | ||
|
|
2fe1333cba | ||
|
|
265754c467 | ||
|
|
6580f4f314 | ||
|
|
5ad63e3bb6 | ||
|
|
a8ab035b59 | ||
|
|
968d57c56b | ||
|
|
1307faef06 | ||
|
|
ee117886b9 | ||
|
|
1dbad7b951 | ||
|
|
5df6c65cc2 | ||
|
|
af5efabdc6 | ||
|
|
a2754f655c | ||
|
|
34e6d3ec9c | ||
|
|
21362f8c92 | ||
|
|
626d84bc64 | ||
|
|
68fa7b9847 | ||
|
|
32dc3b8535 | ||
|
|
74bcec34ad | ||
|
|
c1687aba4c | ||
|
|
ec642f928f | ||
|
|
ec9a207f42 | ||
|
|
3b7847d951 | ||
|
|
1197f22f00 | ||
|
|
e034662dc6 | ||
|
|
42e849967a | ||
|
|
e6f1ed8d51 | ||
|
|
bb9067acd7 | ||
|
|
928cf7d696 | ||
|
|
89afb111ad | ||
|
|
d1ec52e95a | ||
|
|
818d43f742 | ||
|
|
1a28a9220b | ||
|
|
022733c46b | ||
|
|
0f6fc503bd | ||
|
|
ae62960193 | ||
|
|
325c747dcd | ||
|
|
5759c6b8e6 | ||
|
|
c9ba6c355c | ||
|
|
d02da3c8de | ||
|
|
a576092f25 | ||
|
|
792d650fb2 | ||
|
|
f72857151a | ||
|
|
5f8f075e76 | ||
|
|
c2c8cb5eb3 | ||
|
|
5c2b05d0eb | ||
|
|
e110613ef1 | ||
|
|
4324b7d084 | ||
|
|
37d6140193 | ||
|
|
4f192130f8 | ||
|
|
4ef17cebdb | ||
|
|
35276a38bc | ||
|
|
635df9c6ad | ||
|
|
d16d8c111e | ||
|
|
1ead61f11f | ||
|
|
bd06d37d1a | ||
|
|
7c6fac91ff | ||
|
|
052a6096e6 | ||
|
|
25b684f762 | ||
|
|
62f8cc3436 | ||
|
|
34f9152d61 | ||
|
|
73af6b0500 | ||
|
|
a384aae5b1 | ||
|
|
905267a9c3 | ||
|
|
d08c845c65 | ||
|
|
91dfb92c60 | ||
|
|
cef9418a0f | ||
|
|
c0a693dfce | ||
|
|
1b4721d445 | ||
|
|
f8d31c3e54 | ||
|
|
eafcca8274 | ||
|
|
06b585d373 | ||
|
|
885f2fe5d8 | ||
|
|
4e27b2fc12 | ||
|
|
375900bfcd | ||
|
|
6d664d3fef | ||
|
|
4dc2b5911b | ||
|
|
687466b6cd | ||
|
|
dcdc042a4b | ||
|
|
d2a1f7ee31 | ||
|
|
a5a883fd19 | ||
|
|
7671d4410c | ||
|
|
298bd5ec72 | ||
|
|
b8afcdfb3c | ||
|
|
9031df757b | ||
|
|
107bebebad | ||
|
|
1147b024d6 | ||
|
|
36ba2f6106 | ||
|
|
bb0eabf241 | ||
|
|
d9969dd533 | ||
|
|
51ae3bb642 | ||
|
|
b3ef5cf2f3 | ||
|
|
b3f5789dc6 | ||
|
|
1ddacb4931 | ||
|
|
e38ee35cd8 | ||
|
|
81fecf2106 | ||
|
|
76e5b07416 | ||
|
|
10d3ced897 | ||
|
|
6fa7b87e6a | ||
|
|
c0b7c5e590 | ||
|
|
57dd09018e | ||
|
|
04aa59563d | ||
|
|
9440012413 | ||
|
|
38db8c1554 | ||
|
|
ab0c32c2aa | ||
|
|
cd6a34401b | ||
|
|
5ab92ed363 | ||
|
|
1368ce4342 | ||
|
|
dc0d79635b | ||
|
|
1ff6b29b0d | ||
|
|
49caeae780 | ||
|
|
e1a6c615b1 | ||
|
|
e95e56f100 | ||
|
|
126dc8b0b2 | ||
|
|
3298988f5d | ||
|
|
802c2633ed | ||
|
|
51b1fdcc8b | ||
|
|
6555da2906 | ||
|
|
6dbd15b903 | ||
|
|
0399379c0d |
No files matched your search
@@ -395,6 +395,8 @@ jobs:
|
||||
- common
|
||||
- determine-jobs
|
||||
if: needs.determine-jobs.outputs.integration-tests == 'true'
|
||||
# A hung test is failed by pytest's --timeout below; this bounds the rest
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -471,7 +473,8 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
echo "Bucket ${{ matrix.bucket.name }}: running ${#test_files[@]} integration tests"
|
||||
pytest -vv --no-cov --tb=native --durations=30 -n auto --dist worksteal \
|
||||
# A hung test fails after 30 min: the 15 min shared build lock wait plus a cold compile
|
||||
pytest -vv --no-cov --tb=native --durations=30 -n auto --dist worksteal --timeout=1800 \
|
||||
--junitxml=junit-integration.xml "${test_files[@]}"
|
||||
- name: Upload test artifacts
|
||||
# Tests that compare rendered output write the image they actually got here, so a
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
days-before-close: 7
|
||||
stale-label: stale
|
||||
exempt-label: not-stale
|
||||
conflict-label: needs-rebase
|
||||
conflict-label: has-conflicts
|
||||
ignored-users: esphbot,codecov-commenter
|
||||
stale-pr-message: >
|
||||
There hasn't been any activity on this pull request recently. This
|
||||
|
||||
@@ -225,6 +225,15 @@ file does, and it is the authority when they disagree. The most useful starting
|
||||
stays inside the `#ifdef`.
|
||||
* **Failures after `setup()`:** `mark_failed()` is for `setup()` only. A runtime read or write failure
|
||||
sets `status_set_warning()` and the next success clears it; writes skip work when `is_failed()`.
|
||||
* **`dump_config()` already prints the failure reason:** for a failed component, core prints
|
||||
`<component> is marked FAILED: <reason>` right after the component's `dump_config()`, using the reason
|
||||
passed to `mark_failed()`. So `dump_config()` never checks `is_failed()` to log a failure, and no
|
||||
`error_code_` member exists only to pick that message. Pass the reason where setup fails instead:
|
||||
`this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL))` for a failed bus transfer, a short specific reason
|
||||
otherwise (`LOG_STR("Wrong chip ID")`). Core also logs it at that moment, so no `ESP_LOGE` repeats it
|
||||
before `mark_failed()`; keep a separate line only when it prints a value read at run time, such as the
|
||||
chip ID that did not match. Only the first reason is kept, so do not call `status_set_error()` before
|
||||
`mark_failed()` and do not mark the component failed again after a helper already did.
|
||||
* **Fixed platform facts compile to nothing:** gate on `#ifdef USE_ESP8266` and friends, not on a
|
||||
runtime check of something the build already knows.
|
||||
* **Logging string literals:** wrap literals passed as `%s` arguments in `LOG_STR_LITERAL()` so they
|
||||
@@ -278,7 +287,8 @@ file does, and it is the authority when they disagree. The most useful starting
|
||||
`from esphome.components.my_component import DOMAIN`. Keep it in alphabetical order with the other
|
||||
metadata constants. CI (`lint_component_domain`) fails without it.
|
||||
- `DEPENDENCIES`: List of required components
|
||||
- `AUTO_LOAD`: Components to automatically load
|
||||
- `AUTO_LOAD`: Components to automatically load. Not a way to put entities under the component's
|
||||
own key; see "Entities of a hub" below.
|
||||
- `CONFLICTS_WITH`: Incompatible components
|
||||
- `CODEOWNERS`: GitHub usernames responsible for maintenance
|
||||
- `MULTI_CONF`: Whether multiple instances are allowed
|
||||
@@ -370,6 +380,26 @@ file does, and it is the authority when they disagree. The most useful starting
|
||||
var = await switch.new_switch(config)
|
||||
```
|
||||
|
||||
- **Entities of a hub:** a new component's entities go in platform files (`sensor.py`,
|
||||
`binary_sensor.py`, ...) that take the hub's id, never under the hub's own key with their platforms
|
||||
added to `AUTO_LOAD` to make that work. Auto loading a platform because the C++ uses its type is
|
||||
fine. See https://developers.esphome.io/architecture/components/#exposing-multiple-entities.
|
||||
```yaml
|
||||
# Bad - entities under the hub's key
|
||||
my_hub:
|
||||
connected:
|
||||
name: Connected
|
||||
|
||||
# Good - the hub, then one platform entry that holds its entities
|
||||
my_hub:
|
||||
id: hub1
|
||||
binary_sensor:
|
||||
- platform: my_hub
|
||||
my_hub_id: hub1 # optional with a single hub
|
||||
connected:
|
||||
name: Connected
|
||||
```
|
||||
|
||||
- **Optional child entities of a hub:** bind the config once with `sensor.sub_sensors(config)` (or
|
||||
`sub_binary_sensors`, `sub_text_sensors`, `sub_buttons`, `sub_switches`, `sub_numbers`,
|
||||
`sub_selects` in their domains), adding `parent=hub` for entities that derive from `Parented<T>`,
|
||||
@@ -380,15 +410,25 @@ file does, and it is the authority when they disagree. The most useful starting
|
||||
itself, `hub` for one fetched with `cg.get_variable`) so the calls fit on one line. Loops whose
|
||||
setter also takes an index, such as `set_gate_threshold(x, n)`, stay as they are.
|
||||
```python
|
||||
# my_hub/sensor.py
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
hub = await cg.get_variable(config[CONF_MY_HUB_ID])
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_TEMPERATURE, var.set_temperature_sensor)
|
||||
await sensors(CONF_HUMIDITY, var.set_humidity_sensor)
|
||||
buttons = button.sub_buttons(config, parent=var)
|
||||
await buttons(CONF_RESTART, var.set_restart_button)
|
||||
await sensors(CONF_TEMPERATURE, hub.set_temperature_sensor)
|
||||
await sensors(CONF_HUMIDITY, hub.set_humidity_sensor)
|
||||
|
||||
|
||||
# my_hub/button.py
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_MY_HUB_ID])
|
||||
buttons = button.sub_buttons(config, parent=hub)
|
||||
await buttons(CONF_RESTART, hub.set_restart_button)
|
||||
```
|
||||
|
||||
- **Pins and referenced components:** set them with `await cg.add_pin(var.set_cs_pin, config[CONF_CS_PIN])`
|
||||
and `await cg.add_variable(var.set_bus, config[CONF_BUS_ID])`; both skip a `None` value, so pass
|
||||
`config.get(KEY)` for an optional key, and return what they created. Use `cg.register_parented` for `set_parent`.
|
||||
|
||||
* **Automations (Triggers, Actions, Conditions):**
|
||||
|
||||
Automations have three building blocks: **Triggers** (fire when something happens), **Actions** (do something), and **Conditions** (check if something is true).
|
||||
|
||||
+16
-1
@@ -30,6 +30,9 @@ esphome/components/airthings_ble/* @jeromelaban
|
||||
esphome/components/airthings_wave_base/* @jeromelaban @ncareau
|
||||
esphome/components/airthings_wave_mini/* @ncareau
|
||||
esphome/components/airthings_wave_plus/* @jeromelaban @precurse
|
||||
esphome/components/airton/* @Procsiab
|
||||
esphome/components/airton/select/* @Procsiab
|
||||
esphome/components/airton/switch/* @Procsiab
|
||||
esphome/components/alarm_control_panel/* @grahambrown11 @hwstar
|
||||
esphome/components/alpha3/* @jan-hofmeier
|
||||
esphome/components/am2315c/* @swoboda1337
|
||||
@@ -157,6 +160,9 @@ esphome/components/delonghi/* @grob6000
|
||||
esphome/components/dew_point/* @CFlix
|
||||
esphome/components/dfplayer/* @glmnet
|
||||
esphome/components/dfrobot_sen0395/* @niklasweber
|
||||
esphome/components/dfrobot_sen0465_base/* @tzabbi
|
||||
esphome/components/dfrobot_sen0465_i2c/* @tzabbi
|
||||
esphome/components/dfrobot_sen0465_uart/* @tzabbi
|
||||
esphome/components/dfrobot_sen0609/* @remcom
|
||||
esphome/components/dht/* @OttoWinter
|
||||
esphome/components/display_menu_base/* @numo68
|
||||
@@ -252,6 +258,7 @@ esphome/components/haier/text_sensor/* @paveldn
|
||||
esphome/components/haier_tundra/* @fauxpark
|
||||
esphome/components/haier_tundra/button/* @fauxpark
|
||||
esphome/components/haier_tundra/switch/* @fauxpark
|
||||
esphome/components/haier_water_heater/* @TheLexus
|
||||
esphome/components/havells_solar/* @sourabhjaiswal
|
||||
esphome/components/hbridge/fan/* @WeekendWarrior
|
||||
esphome/components/hbridge/light/* @DotNetDann
|
||||
@@ -272,6 +279,7 @@ esphome/components/hmac_sha256/* @dwmw2
|
||||
esphome/components/hoermann_hcp/* @zweckj
|
||||
esphome/components/homeassistant/* @esphome/core @OttoWinter
|
||||
esphome/components/homeassistant/button/* @jesserockz
|
||||
esphome/components/homeassistant/light/* @egormanga
|
||||
esphome/components/homeassistant/number/* @landonr
|
||||
esphome/components/homeassistant/select/* @jesserockz
|
||||
esphome/components/homeassistant/switch/* @Links2004
|
||||
@@ -390,6 +398,7 @@ esphome/components/micro_wake_word/* @jesserockz @kahrendt
|
||||
esphome/components/micronova/* @edenhaus @jorre05
|
||||
esphome/components/microphone/* @jesserockz @kahrendt
|
||||
esphome/components/mics_4514/* @jesserockz
|
||||
esphome/components/mics_5524/* @nliaudat
|
||||
esphome/components/midea/* @dudanov
|
||||
esphome/components/midea_ir/* @dudanov
|
||||
esphome/components/midea_portasplit/* @Fexiven
|
||||
@@ -423,6 +432,9 @@ esphome/components/mpl3115a2/* @kbickar
|
||||
esphome/components/mpu6886/* @fabaff
|
||||
esphome/components/ms8607/* @e28eta
|
||||
esphome/components/msa3xx/* @latonita
|
||||
esphome/components/mt6701/* @slimcdk
|
||||
esphome/components/mt6701_i2c/* @slimcdk
|
||||
esphome/components/mt6701_spi/* @slimcdk
|
||||
esphome/components/nau7802/* @cujomalainey
|
||||
esphome/components/network/* @esphome/core
|
||||
esphome/components/nextion/* @edwardtfn @senexcrenshaw
|
||||
@@ -507,6 +519,7 @@ esphome/components/rp2040_pwm/* @jesserockz
|
||||
esphome/components/rp2_ble_tracker/* @bdraco
|
||||
esphome/components/rpi_dpi_rgb/* @clydebarrow
|
||||
esphome/components/rtl87xx/* @kuba2k2
|
||||
esphome/components/rtsp_server/* @max246
|
||||
esphome/components/rtttl/* @glmnet @ximex
|
||||
esphome/components/runtime_image/* @clydebarrow @guillempages @kahrendt
|
||||
esphome/components/runtime_stats/* @bdraco
|
||||
@@ -679,10 +692,11 @@ esphome/components/vbus/* @ssieb
|
||||
esphome/components/veml3235/* @kbx81
|
||||
esphome/components/veml7700/* @latonita
|
||||
esphome/components/version/* @esphome/core
|
||||
esphome/components/vevor_dtx/* @Bofr5555
|
||||
esphome/components/voice_assistant/* @jesserockz @kahrendt
|
||||
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
|
||||
esphome/components/watchdog/* @oarcher
|
||||
esphome/components/water_heater/* @dhoeben
|
||||
esphome/components/water_heater/* @esphome/core
|
||||
esphome/components/waveshare_epaper/* @clydebarrow
|
||||
esphome/components/waveshare_io_ch32v003/* @latonita
|
||||
esphome/components/web_server/ota/* @esphome/core
|
||||
@@ -721,6 +735,7 @@ esphome/components/xl9535/* @mreditor97
|
||||
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
|
||||
esphome/components/xxtea/* @clydebarrow
|
||||
esphome/components/zephyr/* @tomaszduda23
|
||||
esphome/components/zephyr_coredump/* @tomaszduda23
|
||||
esphome/components/zephyr_mcumgr/ota/* @tomaszduda23
|
||||
esphome/components/zephyr_pwm/* @wiomoc
|
||||
esphome/components/zhlt01/* @cfeenstra1024
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ RUN \
|
||||
-r /requirements.txt
|
||||
|
||||
# Install the ESPHome Device Builder dashboard.
|
||||
RUN uv pip install --no-cache-dir esphome-device-builder==1.23.1
|
||||
RUN uv pip install --no-cache-dir esphome-device-builder==1.24.0
|
||||
|
||||
RUN \
|
||||
platformio settings set enable_telemetry No \
|
||||
|
||||
+126
-3
@@ -14,6 +14,7 @@ from esphome.const import (
|
||||
CONF_COUNT,
|
||||
CONF_ELSE,
|
||||
CONF_ID,
|
||||
CONF_IF,
|
||||
CONF_THEN,
|
||||
CONF_TIME,
|
||||
CONF_TIMEOUT,
|
||||
@@ -34,6 +35,9 @@ from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
from esphome.types import ConfigType, SafeExpType
|
||||
from esphome.util import Registry
|
||||
|
||||
CONF_CHOOSE = "choose"
|
||||
CONF_CHOICE_ID = "choice_id"
|
||||
|
||||
|
||||
def progmem_bytes(name: str, data: bytes | list[int]) -> MockObj:
|
||||
"""Shared PROGMEM table for constant bytes; equal payloads share one, empty is nullptr."""
|
||||
@@ -840,10 +844,39 @@ async def delay_action_to_code(
|
||||
return var
|
||||
|
||||
|
||||
def _wrap_bare_if_condition(value: Any) -> Any:
|
||||
"""Allow a single condition directly under `if:` in place of `condition: {...}`."""
|
||||
if not isinstance(value, dict):
|
||||
return value
|
||||
# `any` and `all` are also registered conditions, but here they are the if action's own keys
|
||||
bare = [
|
||||
key
|
||||
for key in value
|
||||
if key in CONDITION_REGISTRY and key not in (CONF_ANY, CONF_ALL)
|
||||
]
|
||||
if not bare:
|
||||
return value
|
||||
if len(bare) > 1:
|
||||
raise cv.Invalid(
|
||||
f"Only one condition may be given directly under 'if', found {', '.join(bare)}. "
|
||||
f"Use '{CONF_CONDITION}', '{CONF_ALL}' or '{CONF_ANY}' to combine conditions"
|
||||
)
|
||||
if other := [k for k in (CONF_CONDITION, CONF_ANY, CONF_ALL) if k in value]:
|
||||
raise cv.Invalid(
|
||||
f"'{bare[0]}' given directly under 'if' cannot be used with '{other[0]}'. "
|
||||
f"Move it into '{other[0]}'"
|
||||
)
|
||||
key = bare[0]
|
||||
result = {k: v for k, v in value.items() if k != key}
|
||||
result[CONF_CONDITION] = {key: value[key]}
|
||||
return result
|
||||
|
||||
|
||||
@register_action(
|
||||
"if",
|
||||
IfAction,
|
||||
cv.All(
|
||||
_wrap_bare_if_condition,
|
||||
{
|
||||
cv.Exclusive(
|
||||
CONF_CONDITION, CONF_CONDITION
|
||||
@@ -870,14 +903,104 @@ async def if_action_to_code(
|
||||
cond_conf = next(el for el in config if el in (CONF_ANY, CONF_ALL, CONF_CONDITION))
|
||||
condition = await build_condition(config[cond_conf], template_arg, args)
|
||||
var = cg.new_Pvariable(action_id, if_template_arg, condition)
|
||||
then_actions = else_actions = None
|
||||
if CONF_THEN in config:
|
||||
for action in await build_action_list(config[CONF_THEN], template_arg, args):
|
||||
then_actions = await build_action_list(config[CONF_THEN], template_arg, args)
|
||||
if has_else:
|
||||
else_actions = await build_action_list(config[CONF_ELSE], template_arg, args)
|
||||
_add_if_branches(var, then_actions, else_actions)
|
||||
return var
|
||||
|
||||
|
||||
def _add_if_branches(
|
||||
var: MockObj,
|
||||
then_actions: list[MockObj] | None,
|
||||
else_actions: list[MockObj] | None,
|
||||
) -> None:
|
||||
"""Add the branches of an IfAction one action at a time; None leaves a branch out."""
|
||||
if then_actions is not None:
|
||||
for action in then_actions:
|
||||
cg.add(var.add_then_action(action))
|
||||
cg.add(var.finish_then())
|
||||
if has_else:
|
||||
for action in await build_action_list(config[CONF_ELSE], template_arg, args):
|
||||
if else_actions is not None:
|
||||
for action in else_actions:
|
||||
cg.add(var.add_else_action(action))
|
||||
cg.add(var.finish_else())
|
||||
|
||||
|
||||
def _validate_choice(choice: ConfigType) -> ConfigType:
|
||||
"""A choice is either `if` with `then`, or `else` on its own."""
|
||||
if CONF_ELSE in choice:
|
||||
for key in (CONF_IF, CONF_THEN):
|
||||
if key in choice:
|
||||
raise cv.Invalid(f"'{key}' cannot be used with 'else'", path=[key])
|
||||
return choice
|
||||
for key in (CONF_IF, CONF_THEN):
|
||||
if key not in choice:
|
||||
raise cv.Invalid(f"'{key}' is required", path=[key])
|
||||
return choice
|
||||
|
||||
|
||||
def _validate_choose_else(choices: list[ConfigType]) -> list[ConfigType]:
|
||||
"""`else` may only be the last choice, and not the only one."""
|
||||
for index, choice in enumerate(choices[:-1]):
|
||||
if CONF_ELSE in choice:
|
||||
raise cv.Invalid(
|
||||
"Only the last choice may be 'else'", path=[index, CONF_ELSE]
|
||||
)
|
||||
if len(choices) == 1 and CONF_ELSE in choices[0]:
|
||||
raise cv.Invalid(
|
||||
"At least one choice must have 'if' and 'then'", path=[0, CONF_ELSE]
|
||||
)
|
||||
return choices
|
||||
|
||||
|
||||
@register_action(
|
||||
CONF_CHOOSE,
|
||||
IfAction,
|
||||
cv.All(
|
||||
cv.ensure_list(
|
||||
cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_CHOICE_ID): cv.declare_id(IfAction),
|
||||
cv.Optional(CONF_IF): validate_potentially_and_condition,
|
||||
cv.Optional(CONF_THEN): validate_action_list,
|
||||
cv.Optional(CONF_ELSE): validate_action_list,
|
||||
}
|
||||
),
|
||||
_validate_choice,
|
||||
)
|
||||
),
|
||||
cv.Length(min=1),
|
||||
_validate_choose_else,
|
||||
),
|
||||
synchronous=True,
|
||||
)
|
||||
async def choose_action_to_code(
|
||||
config: list[ConfigType],
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
# Built as a chain of if/else actions: each choice's else branch holds the next choice,
|
||||
# and the last choice's else branch holds the `else` actions, if any.
|
||||
else_actions: list[MockObj] | None = None
|
||||
if CONF_ELSE in config[-1]:
|
||||
else_actions = await build_action_list(
|
||||
config[-1][CONF_ELSE], template_arg, args
|
||||
)
|
||||
config = config[:-1]
|
||||
var: MockObj | None = None
|
||||
for index in reversed(range(len(config))):
|
||||
choice = config[index]
|
||||
if_template_arg = cg.TemplateArguments(else_actions is not None, *template_arg)
|
||||
condition = await build_condition(choice[CONF_IF], template_arg, args)
|
||||
choice_id = action_id if index == 0 else choice[CONF_CHOICE_ID]
|
||||
var = cg.new_Pvariable(choice_id, if_template_arg, condition)
|
||||
then_actions = await build_action_list(choice[CONF_THEN], template_arg, args)
|
||||
_add_if_branches(var, then_actions, else_actions)
|
||||
else_actions = [var]
|
||||
return var
|
||||
|
||||
|
||||
|
||||
@@ -53,11 +53,14 @@ from esphome.cpp_generator import ( # noqa: F401
|
||||
with_local_variable,
|
||||
)
|
||||
from esphome.cpp_helpers import ( # noqa: F401
|
||||
add_pin,
|
||||
add_variable,
|
||||
build_registry_entry,
|
||||
build_registry_list,
|
||||
extract_registry_entry_config,
|
||||
get_slot_count,
|
||||
gpio_pin_expression,
|
||||
new_bus_component,
|
||||
past_safe_mode,
|
||||
register_component,
|
||||
register_parented,
|
||||
|
||||
@@ -9,15 +9,13 @@ namespace esphome::a01nyub {
|
||||
ESPHOME_LOG_TAG(TAG, "a01nyub.sensor");
|
||||
|
||||
void A01nyubComponent::loop() {
|
||||
uint8_t data;
|
||||
while (this->available() > 0) {
|
||||
this->read_byte(&data);
|
||||
this->read_available([this](uint8_t data) {
|
||||
if (this->buffer_.empty() && (data != 0xff))
|
||||
continue;
|
||||
return;
|
||||
buffer_.push_back(data);
|
||||
if (this->buffer_.size() == 4)
|
||||
this->check_buffer_();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void A01nyubComponent::check_buffer_() {
|
||||
|
||||
@@ -9,15 +9,13 @@ namespace esphome::a02yyuw {
|
||||
ESPHOME_LOG_TAG(TAG, "a02yyuw.sensor");
|
||||
|
||||
void A02yyuwComponent::loop() {
|
||||
uint8_t data;
|
||||
while (this->available() > 0) {
|
||||
this->read_byte(&data);
|
||||
this->read_available([this](uint8_t data) {
|
||||
if (this->buffer_.empty() && (data != 0xff))
|
||||
continue;
|
||||
return;
|
||||
buffer_.push_back(data);
|
||||
if (this->buffer_.size() == 4)
|
||||
this->check_buffer_();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void A02yyuwComponent::check_buffer_() {
|
||||
|
||||
@@ -23,11 +23,6 @@ async def to_code(config: ConfigType) -> None:
|
||||
await cg.register_component(var, config)
|
||||
await stepper.register_stepper(var, config)
|
||||
|
||||
step_pin = await cg.gpio_pin_expression(config[CONF_STEP_PIN])
|
||||
cg.add(var.set_step_pin(step_pin))
|
||||
dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
|
||||
cg.add(var.set_dir_pin(dir_pin))
|
||||
|
||||
if sleep_pin_config := config.get(CONF_SLEEP_PIN):
|
||||
sleep_pin = await cg.gpio_pin_expression(sleep_pin_config)
|
||||
cg.add(var.set_sleep_pin(sleep_pin))
|
||||
await cg.add_pin(var.set_step_pin, config[CONF_STEP_PIN])
|
||||
await cg.add_pin(var.set_dir_pin, config[CONF_DIR_PIN])
|
||||
await cg.add_pin(var.set_sleep_pin, config.get(CONF_SLEEP_PIN))
|
||||
@@ -19,11 +19,7 @@ AbsoluteHumidityComponent = absolute_humidity_ns.class_(
|
||||
SaturationVaporPressureEquation = absolute_humidity_ns.enum(
|
||||
"SaturationVaporPressureEquation"
|
||||
)
|
||||
EQUATION = {
|
||||
"BUCK": SaturationVaporPressureEquation.BUCK,
|
||||
"TETENS": SaturationVaporPressureEquation.TETENS,
|
||||
"WOBUS": SaturationVaporPressureEquation.WOBUS,
|
||||
}
|
||||
EQUATION = SaturationVaporPressureEquation.options("BUCK", "TETENS", "WOBUS")
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
@@ -48,10 +44,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
temperature_sensor = await cg.get_variable(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature_sensor(temperature_sensor))
|
||||
|
||||
humidity_sensor = await cg.get_variable(config[CONF_HUMIDITY])
|
||||
cg.add(var.set_humidity_sensor(humidity_sensor))
|
||||
await cg.add_variable(var.set_temperature_sensor, config[CONF_TEMPERATURE])
|
||||
await cg.add_variable(var.set_humidity_sensor, config[CONF_HUMIDITY])
|
||||
|
||||
cg.add(var.set_equation(config[CONF_EQUATION]))
|
||||
@@ -14,11 +14,9 @@ ac_dimmer_ns = cg.esphome_ns.namespace("ac_dimmer")
|
||||
AcDimmer = ac_dimmer_ns.class_("AcDimmer", output.FloatOutput, cg.Component)
|
||||
|
||||
DimMethod = ac_dimmer_ns.enum("DimMethod")
|
||||
DIM_METHODS = {
|
||||
"LEADING_PULSE": DimMethod.DIM_METHOD_LEADING_PULSE,
|
||||
"LEADING": DimMethod.DIM_METHOD_LEADING,
|
||||
"TRAILING": DimMethod.DIM_METHOD_TRAILING,
|
||||
}
|
||||
DIM_METHODS = DimMethod.options(
|
||||
"LEADING_PULSE", "LEADING", "TRAILING", prefix="DIM_METHOD_"
|
||||
)
|
||||
|
||||
ZC_INTERRUPT_TYPES = {
|
||||
"RISING": gpio_ns.INTERRUPT_RISING_EDGE,
|
||||
@@ -70,10 +68,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
config[CONF_MIN_POWER] = 0.1
|
||||
await output.register_output(var, config)
|
||||
|
||||
pin = await cg.gpio_pin_expression(config[CONF_GATE_PIN])
|
||||
cg.add(var.set_gate_pin(pin))
|
||||
pin = await cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
|
||||
cg.add(var.set_zero_cross_pin(pin))
|
||||
await cg.add_pin(var.set_gate_pin, config[CONF_GATE_PIN])
|
||||
await cg.add_pin(var.set_zero_cross_pin, config[CONF_ZERO_CROSS_PIN])
|
||||
cg.add(var.set_zero_cross_interrupt_type(config[CONF_ZERO_CROSS_INTERRUPT_TYPE]))
|
||||
cg.add(var.set_init_with_half_cycle(config[CONF_INIT_WITH_HALF_CYCLE]))
|
||||
cg.add(var.set_method(config[CONF_METHOD]))
|
||||
@@ -68,32 +68,32 @@ void AdalightLightEffect::apply(light::AddressableLight &it, const Color ¤
|
||||
blank_all_leds_(it);
|
||||
}
|
||||
|
||||
if (this->available() > 0) {
|
||||
ESP_LOGV(TAG, "Frame: Available (size=%d).", this->available());
|
||||
const size_t avail = this->available();
|
||||
if (avail > 0) {
|
||||
ESP_LOGV(TAG, "Frame: Available (size=%zu).", avail);
|
||||
}
|
||||
|
||||
while (this->available() != 0) {
|
||||
uint8_t data;
|
||||
if (!this->read_byte(&data))
|
||||
break;
|
||||
this->frame_.push_back(data);
|
||||
this->last_byte_ = now;
|
||||
this->read_available(
|
||||
[this, &it, now](uint8_t data) {
|
||||
this->frame_.push_back(data);
|
||||
this->last_byte_ = now;
|
||||
|
||||
switch (this->parse_frame_(it)) {
|
||||
case INVALID:
|
||||
ESP_LOGD(TAG, "Frame: Invalid (size=%zu, first=%d).", this->frame_.size(), this->frame_[0]);
|
||||
reset_frame_(it);
|
||||
break;
|
||||
switch (this->parse_frame_(it)) {
|
||||
case INVALID:
|
||||
ESP_LOGD(TAG, "Frame: Invalid (size=%zu, first=%d).", this->frame_.size(), this->frame_[0]);
|
||||
reset_frame_(it);
|
||||
break;
|
||||
|
||||
case PARTIAL:
|
||||
break;
|
||||
case PARTIAL:
|
||||
break;
|
||||
|
||||
case CONSUMED:
|
||||
ESP_LOGV(TAG, "Frame: Consumed (size=%zu).", this->frame_.size());
|
||||
reset_frame_(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case CONSUMED:
|
||||
ESP_LOGV(TAG, "Frame: Consumed (size=%zu).", this->frame_.size());
|
||||
reset_frame_(it);
|
||||
break;
|
||||
}
|
||||
},
|
||||
avail);
|
||||
}
|
||||
|
||||
AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableLight &it) {
|
||||
|
||||
@@ -46,11 +46,7 @@ ATTENUATION_MODES = {
|
||||
|
||||
sampling_mode = adc_ns.enum("SamplingMode", is_class=True)
|
||||
|
||||
SAMPLING_MODES = {
|
||||
"avg": sampling_mode.AVG,
|
||||
"min": sampling_mode.MIN,
|
||||
"max": sampling_mode.MAX,
|
||||
}
|
||||
SAMPLING_MODES = sampling_mode.options("avg", "min", "max")
|
||||
|
||||
adc_unit_t = cg.global_ns.enum("adc_unit_t", is_class=True)
|
||||
|
||||
|
||||
@@ -164,8 +164,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
# Remove before 2027.2.0
|
||||
cg.add(var.set_is_temperature())
|
||||
elif not CORE.is_nrf52 or config[CONF_PIN][CONF_NUMBER] not in EXTRA_ADC:
|
||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||
cg.add(var.set_pin(pin))
|
||||
await cg.add_pin(var.set_pin, config[CONF_PIN])
|
||||
|
||||
# Skip the setters when the config matches the C++ initializers.
|
||||
if config[CONF_RAW]:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import spi
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DEPENDENCIES = ["spi"]
|
||||
@@ -20,6 +19,4 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
await spi.new_spi_component(config)
|
||||
@@ -56,8 +56,4 @@ async def to_code(config: ConfigType) -> None:
|
||||
)
|
||||
cg.add(var.set_pixel_mapper(pixel_mapper_template_))
|
||||
|
||||
if lambda_config := config.get(CONF_LAMBDA):
|
||||
lambda_ = await cg.process_lambda(
|
||||
lambda_config, [(display.DisplayRef, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
||||
await display.add_writer(var, config)
|
||||
@@ -11,12 +11,6 @@
|
||||
|
||||
namespace esphome::ade7880 {
|
||||
|
||||
// adapted from https://stackoverflow.com/a/55912127/1886371
|
||||
template<size_t Bits, typename T> inline T sign_extend(const T &v) noexcept {
|
||||
using S = struct { signed Val : Bits; };
|
||||
return reinterpret_cast<const S *>(&v)->Val;
|
||||
}
|
||||
|
||||
// Register types
|
||||
// unsigned 8-bit (uint8_t)
|
||||
// signed 10-bit - 16-bit ZP on wire (int16_t, needs sign extension)
|
||||
@@ -49,7 +43,7 @@ uint16_t ADE7880::read_u16_register16_(uint16_t a_register) {
|
||||
|
||||
int32_t ADE7880::read_s24zp_register16_(uint16_t a_register) {
|
||||
// s24zp means 24 bit signed value in the lower 24 bits of a 32-bit register
|
||||
int32_t in;
|
||||
uint32_t in;
|
||||
this->read_register16(a_register, reinterpret_cast<uint8_t *>(&in), sizeof(in));
|
||||
return sign_extend<24>(convert_big_endian(in));
|
||||
}
|
||||
|
||||
@@ -282,20 +282,11 @@ async def power_channel(config: ConfigType) -> MockObj:
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config)
|
||||
|
||||
if irq0_pin := config.get(CONF_IRQ0_PIN):
|
||||
pin = await cg.gpio_pin_expression(irq0_pin)
|
||||
cg.add(var.set_irq0_pin(pin))
|
||||
|
||||
pin = await cg.gpio_pin_expression(config[CONF_IRQ1_PIN])
|
||||
cg.add(var.set_irq1_pin(pin))
|
||||
|
||||
if reset_pin := config.get(CONF_RESET_PIN):
|
||||
pin = await cg.gpio_pin_expression(reset_pin)
|
||||
cg.add(var.set_reset_pin(pin))
|
||||
await cg.add_pin(var.set_irq0_pin, config.get(CONF_IRQ0_PIN))
|
||||
await cg.add_pin(var.set_irq1_pin, config[CONF_IRQ1_PIN])
|
||||
await cg.add_pin(var.set_reset_pin, config.get(CONF_RESET_PIN))
|
||||
|
||||
if frequency := config.get(CONF_FREQUENCY):
|
||||
cg.add(var.set_frequency(frequency))
|
||||
|
||||
@@ -169,9 +169,7 @@ ADE7953_CONFIG_SCHEMA = cv.Schema(
|
||||
async def register_ade7953(var: MockObj, config: ConfigType) -> None:
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if irq_pin_config := config.get(CONF_IRQ_PIN):
|
||||
irq_pin = await cg.gpio_pin_expression(irq_pin_config)
|
||||
cg.add(var.set_irq_pin(irq_pin))
|
||||
await cg.add_pin(var.set_irq_pin, config.get(CONF_IRQ_PIN))
|
||||
|
||||
cg.add(var.set_pga_v(PGA_GAINS[config.get(CONF_VOLTAGE_PGA_GAIN)]))
|
||||
cg.add(var.set_pga_ia(PGA_GAINS[config.get(CONF_CURRENT_PGA_GAIN_A)]))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
@@ -27,8 +26,6 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config)
|
||||
|
||||
cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
|
||||
@@ -11,7 +11,7 @@ static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
|
||||
void ADS1115Component::setup() {
|
||||
uint16_t value;
|
||||
if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &value)) {
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ void ADS1115Component::setup() {
|
||||
config |= 0b0000000000000011;
|
||||
|
||||
if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
this->prev_config_ = config;
|
||||
@@ -66,9 +66,6 @@ void ADS1115Component::setup() {
|
||||
void ADS1115Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "ADS1115:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
}
|
||||
float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain,
|
||||
ADS1115Resolution resolution, ADS1115Samplerate samplerate) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import spi
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@solomondg1"]
|
||||
@@ -26,6 +25,4 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
await spi.new_spi_component(config)
|
||||
@@ -4,7 +4,6 @@ from esphome.components import i2c, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_ID,
|
||||
CONF_MODE,
|
||||
CONF_TVOC,
|
||||
CONF_VALUE,
|
||||
@@ -58,9 +57,7 @@ FINAL_VALIDATE_SCHEMA = i2c.final_validate_device_schema("ags10", max_frequency=
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config)
|
||||
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_TVOC, var.set_tvoc)
|
||||
@@ -84,11 +81,9 @@ automation.register_apply_action(
|
||||
)
|
||||
|
||||
AGS10SetZeroPointActionMode = ags10_ns.enum("AGS10SetZeroPointActionMode")
|
||||
AGS10_SET_ZERO_POINT_ACTION_MODE = {
|
||||
"FACTORY_DEFAULT": AGS10SetZeroPointActionMode.FACTORY_DEFAULT,
|
||||
"CURRENT_VALUE": AGS10SetZeroPointActionMode.CURRENT_VALUE,
|
||||
"CUSTOM_VALUE": AGS10SetZeroPointActionMode.CUSTOM_VALUE,
|
||||
}
|
||||
AGS10_SET_ZERO_POINT_ACTION_MODE = AGS10SetZeroPointActionMode.options(
|
||||
"FACTORY_DEFAULT", "CURRENT_VALUE", "CUSTOM_VALUE"
|
||||
)
|
||||
|
||||
AGS10_SET_ZERO_POINT_SCHEMA = cv.Schema(
|
||||
{
|
||||
|
||||
@@ -52,8 +52,7 @@ void AHT10Component::setup() {
|
||||
break;
|
||||
}
|
||||
if (error_code != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
uint8_t cal_attempts = 0;
|
||||
@@ -61,20 +60,17 @@ void AHT10Component::setup() {
|
||||
while (data & AHT10_STATUS_BUSY) {
|
||||
delay(AHT10_DEFAULT_DELAY);
|
||||
if (this->read(&data, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
++cal_attempts;
|
||||
if (cal_attempts > AHT10_INIT_ATTEMPTS) {
|
||||
ESP_LOGE(TAG, "Initialization timed out");
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR("Initialization timed out"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ((data & 0x68) != 0x08) { // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED
|
||||
ESP_LOGE(TAG, "Initialization failed");
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR("Initialization failed"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -152,9 +148,6 @@ void AHT10Component::update() {
|
||||
void AHT10Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "AHT10:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ from esphome.components import i2c, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_HUMIDITY,
|
||||
CONF_ID,
|
||||
CONF_TEMPERATURE,
|
||||
CONF_VARIANT,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
@@ -20,10 +19,7 @@ aht10_ns = cg.esphome_ns.namespace("aht10")
|
||||
AHT10Component = aht10_ns.class_("AHT10Component", cg.PollingComponent, i2c.I2CDevice)
|
||||
|
||||
AHT10Variant = aht10_ns.enum("AHT10Variant")
|
||||
AHT10_VARIANTS = {
|
||||
"AHT10": AHT10Variant.AHT10,
|
||||
"AHT20": AHT10Variant.AHT20,
|
||||
}
|
||||
AHT10_VARIANTS = AHT10Variant.options("AHT10", "AHT20")
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
@@ -52,9 +48,7 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config)
|
||||
cg.add(var.set_variant(config[CONF_VARIANT]))
|
||||
|
||||
sensors = sensor.sub_sensors(config)
|
||||
|
||||
@@ -10,8 +10,7 @@ ESPHOME_LOG_TAG(TAG, "aic3204");
|
||||
|
||||
#define ERROR_CHECK(err, msg) \
|
||||
if (!(err)) { \
|
||||
ESP_LOGE(TAG, msg); \
|
||||
this->mark_failed(); \
|
||||
this->mark_failed(LOG_STR(msg)); \
|
||||
return; \
|
||||
}
|
||||
|
||||
@@ -108,10 +107,6 @@ void AIC3204::setup() {
|
||||
void AIC3204::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "AIC3204:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
bool AIC3204::set_mute_off() {
|
||||
|
||||
@@ -3,7 +3,7 @@ import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
from esphome.components.audio_dac import AudioDac
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_MODE
|
||||
from esphome.const import CONF_MODE
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
@@ -41,6 +41,4 @@ automation.register_apply_action(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
await i2c.new_i2c_component(config)
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "airthings_listener.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cinttypes>
|
||||
|
||||
@@ -12,10 +13,7 @@ bool AirthingsListener::parse_device(const ble_device_base::ESPBTDevice &device)
|
||||
if (it.data.size() < 4)
|
||||
continue;
|
||||
|
||||
uint32_t sn = it.data[0];
|
||||
sn |= ((uint32_t) it.data[1] << 8);
|
||||
sn |= ((uint32_t) it.data[2] << 16);
|
||||
sn |= ((uint32_t) it.data[3] << 24);
|
||||
uint32_t sn = encode_value_le<uint32_t>(it.data.data());
|
||||
|
||||
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||
ESP_LOGD(TAG, "Found AirThings device Serial:%" PRIu32 " (MAC: %s)", sn, device.address_str_to(addr_buf));
|
||||
|
||||
@@ -29,10 +29,7 @@ AirthingsWavePlus = airthings_wave_plus_ns.class_(
|
||||
|
||||
CONF_DEVICE_TYPE = "device_type"
|
||||
WaveDeviceType = airthings_wave_plus_ns.enum("WaveDeviceType")
|
||||
DEVICE_TYPES = {
|
||||
"WAVE_PLUS": WaveDeviceType.WAVE_PLUS,
|
||||
"WAVE_GEN2": WaveDeviceType.WAVE_GEN2,
|
||||
}
|
||||
DEVICE_TYPES = WaveDeviceType.options("WAVE_PLUS", "WAVE_GEN2")
|
||||
|
||||
|
||||
def validate_wave_gen2_config(config: ConfigType) -> ConfigType:
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
CODEOWNERS = ["@Procsiab"]
|
||||
DOMAIN = "airton"
|
||||
@@ -0,0 +1,319 @@
|
||||
#include "airton.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::airton {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "airton.climate");
|
||||
|
||||
// Distinct from the climate's own RESTORE_STATE_VERSION, which shares the entity's preference key
|
||||
static constexpr uint32_t RESTORE_SETTINGS_VERSION = 0x2E86A1F3UL;
|
||||
|
||||
// From IRutils.h of IRremoteESP8266 library
|
||||
static uint8_t checksum(const uint8_t *frame) {
|
||||
return static_cast<uint8_t>(0x7F - sum_bytes(frame, AIRTON_STATE_FRAME_SIZE - 1)) ^ 0x2C;
|
||||
}
|
||||
|
||||
void AirtonClimate::setup() {
|
||||
climate_ir::ClimateIR::setup();
|
||||
// The flap position is the one source of truth for the swing mode the climate state restored
|
||||
this->settings_pref_ = this->make_entity_preference<AirtonSettings>(RESTORE_SETTINGS_VERSION);
|
||||
this->settings_pref_.load(&this->settings_);
|
||||
this->sync_swing_mode_();
|
||||
this->publish_settings_();
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void AirtonClimate::publish_settings_() {
|
||||
#ifdef USE_SWITCH
|
||||
if (this->sleep_mode_switch_ != nullptr)
|
||||
this->sleep_mode_switch_->publish_state(this->settings_.sleep_state);
|
||||
if (this->display_switch_ != nullptr)
|
||||
this->display_switch_->publish_state(this->settings_.display_state);
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
if (this->vertical_direction_select_ != nullptr)
|
||||
this->vertical_direction_select_->publish_state(static_cast<size_t>(this->settings_.vertical_direction));
|
||||
#endif
|
||||
}
|
||||
|
||||
void AirtonClimate::save_settings_() { this->settings_pref_.save(&this->settings_); }
|
||||
|
||||
void AirtonClimate::sync_swing_mode_() {
|
||||
this->swing_mode = this->settings_.vertical_direction == VerticalDirection::VERTICAL_DIRECTION_SWING
|
||||
? climate::CLIMATE_SWING_VERTICAL
|
||||
: climate::CLIMATE_SWING_OFF;
|
||||
}
|
||||
|
||||
// The unit has no sleep mode while it only fans or picks the mode itself
|
||||
bool AirtonClimate::sleep_mode_allowed_() const {
|
||||
return this->mode != climate::CLIMATE_MODE_FAN_ONLY && this->mode != climate::CLIMATE_MODE_HEAT_COOL;
|
||||
}
|
||||
|
||||
// Each setter stores and saves before it publishes: an automation on the entity runs inside
|
||||
// publish_state() and may set the value again, which must not be undone afterwards
|
||||
bool AirtonClimate::set_sleep_mode_state(bool state) {
|
||||
state = state && this->sleep_mode_allowed_();
|
||||
const bool changed = state != this->settings_.sleep_state;
|
||||
if (changed) {
|
||||
this->settings_.sleep_state = state;
|
||||
this->save_settings_();
|
||||
}
|
||||
#ifdef USE_SWITCH
|
||||
if (this->sleep_mode_switch_ != nullptr)
|
||||
this->sleep_mode_switch_->publish_state(state);
|
||||
#endif
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool AirtonClimate::set_display_state(bool state) {
|
||||
const bool changed = state != this->settings_.display_state;
|
||||
if (changed) {
|
||||
this->settings_.display_state = state;
|
||||
this->save_settings_();
|
||||
}
|
||||
#ifdef USE_SWITCH
|
||||
if (this->display_switch_ != nullptr)
|
||||
this->display_switch_->publish_state(state);
|
||||
#endif
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool AirtonClimate::set_vertical_direction_state(VerticalDirection direction) {
|
||||
const bool changed = direction != this->settings_.vertical_direction;
|
||||
if (changed) {
|
||||
this->settings_.vertical_direction = direction;
|
||||
this->sync_swing_mode_();
|
||||
this->save_settings_();
|
||||
}
|
||||
#ifdef USE_SELECT
|
||||
if (this->vertical_direction_select_ != nullptr)
|
||||
this->vertical_direction_select_->publish_state(static_cast<size_t>(direction));
|
||||
#endif
|
||||
return changed;
|
||||
}
|
||||
|
||||
void AirtonClimate::send_settings() {
|
||||
this->transmit_state();
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void AirtonClimate::control(const climate::ClimateCall &call) {
|
||||
// ClimateIR::control() sends and publishes the whole state afterwards
|
||||
// Only a change of swing mode moves the flap, so a repeated swing-off keeps a fixed position
|
||||
if (auto swing_mode = call.get_swing_mode(); swing_mode.has_value() && *swing_mode != this->swing_mode) {
|
||||
this->set_vertical_direction_state(*swing_mode == climate::CLIMATE_SWING_VERTICAL
|
||||
? VerticalDirection::VERTICAL_DIRECTION_SWING
|
||||
: VerticalDirection::VERTICAL_DIRECTION_OFF);
|
||||
}
|
||||
ClimateIR::control(call);
|
||||
}
|
||||
|
||||
void AirtonClimate::transmit_state() {
|
||||
// Entering fan-only or automatic mode drops sleep mode, as the remote does
|
||||
if (!this->sleep_mode_allowed_())
|
||||
this->set_sleep_mode_state(false);
|
||||
// Sampled valid state
|
||||
// Power: On, Mode: 2 (Dry), Fan: 1 (Quiet), Temp: 20C, Swing(V): On, Econo: Off, Turbo: Off, Light: On, Health: On,
|
||||
// Sleep: Off. 0x74C461041A11D3
|
||||
// The turbo bit above the fan speed stays clear, as the remote sends it
|
||||
uint8_t remote_state[AIRTON_STATE_FRAME_SIZE] = {
|
||||
AIRTON_HEADER_BYTE0,
|
||||
AIRTON_HEADER_BYTE1,
|
||||
static_cast<uint8_t>(this->operation_mode_() | (this->fan_speed_() << 4)),
|
||||
this->temperature_(),
|
||||
static_cast<uint8_t>(this->settings_.vertical_direction),
|
||||
this->operation_settings_(),
|
||||
0,
|
||||
};
|
||||
remote_state[6] = checksum(remote_state);
|
||||
|
||||
ESP_LOGV(TAG, "Sending: %02X %02X %02X %02X %02X %02X %02X", remote_state[6], remote_state[5], remote_state[4],
|
||||
remote_state[3], remote_state[2], remote_state[1], remote_state[0]);
|
||||
|
||||
auto transmit = this->transmitter_->transmit();
|
||||
auto *data = transmit.get_data();
|
||||
data->set_carrier_frequency(AIRTON_IR_FREQUENCY);
|
||||
data->mark(AIRTON_HEADER_MARK);
|
||||
data->space(AIRTON_HEADER_SPACE);
|
||||
for (uint8_t payload_byte : remote_state) {
|
||||
for (uint8_t payload_bit_cursor = 0; payload_bit_cursor < 8; payload_bit_cursor++) {
|
||||
data->mark(AIRTON_BIT_MARK);
|
||||
bool bit = payload_byte & (1 << payload_bit_cursor);
|
||||
data->space(bit ? AIRTON_ONE_SPACE : AIRTON_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
|
||||
data->mark(AIRTON_BIT_MARK);
|
||||
data->space(AIRTON_MESSAGE_SPACE);
|
||||
|
||||
transmit.perform();
|
||||
}
|
||||
|
||||
uint8_t AirtonClimate::operation_mode_() {
|
||||
uint8_t operating_mode = 0b1000; // First bit is for power state
|
||||
switch (this->mode) {
|
||||
case climate::CLIMATE_MODE_COOL:
|
||||
operating_mode |= AIRTON_MODE_COOL;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_DRY:
|
||||
operating_mode |= AIRTON_MODE_DRY;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
operating_mode |= AIRTON_MODE_HEAT;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT_COOL:
|
||||
operating_mode |= AIRTON_MODE_AUTO;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||
operating_mode |= AIRTON_MODE_FAN;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_OFF:
|
||||
default:
|
||||
operating_mode = 0b0111 & this->previous_mode_; // Set previous mode with power state bit off
|
||||
}
|
||||
this->previous_mode_ = operating_mode;
|
||||
return operating_mode;
|
||||
}
|
||||
|
||||
uint8_t AirtonClimate::fan_speed_() {
|
||||
uint8_t fan_speed;
|
||||
switch (this->fan_mode.value_or(climate::CLIMATE_FAN_AUTO)) {
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
fan_speed = AIRTON_FAN_1;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
fan_speed = AIRTON_FAN_3;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
fan_speed = AIRTON_FAN_5;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
default:
|
||||
fan_speed = AIRTON_FAN_AUTO;
|
||||
}
|
||||
return fan_speed;
|
||||
}
|
||||
|
||||
uint8_t AirtonClimate::temperature_() {
|
||||
switch (this->mode) {
|
||||
case climate::CLIMATE_MODE_HEAT_COOL:
|
||||
return AIRTON_AUTO_MODE_TEMP - AIRTON_TEMP_MIN;
|
||||
default:
|
||||
return static_cast<uint8_t>(roundf(clamp<float>(this->target_temperature, AIRTON_TEMP_MIN, AIRTON_TEMP_MAX))) -
|
||||
AIRTON_TEMP_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
// The bits of this packet's byte have the following meanings (from MSB to LSB)
|
||||
// Light, Health, Unknown, HeatOn, Unknown, NotAutoOn, Sleep, Econo
|
||||
uint8_t AirtonClimate::operation_settings_() {
|
||||
uint8_t settings = 0;
|
||||
if (this->mode == climate::CLIMATE_MODE_HEAT) { // Set heating bit if on the corresponding mode
|
||||
settings |= (1 << 4);
|
||||
}
|
||||
if (this->settings_.display_state) {
|
||||
settings |= (1 << 7);
|
||||
}
|
||||
if (this->settings_.sleep_state) {
|
||||
settings |= (1 << 1);
|
||||
}
|
||||
settings |= 0b01000000; // Health, as the remote always sends it
|
||||
if (this->mode != climate::CLIMATE_MODE_HEAT_COOL) {
|
||||
settings |= 0b00000100; // NotAutoOn: clear only while the unit picks the mode itself
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
void AirtonClimate::parse_state_frame_(const uint8_t *frame) {
|
||||
uint8_t mode = frame[2];
|
||||
// A later "off" repeats the mode the remote last sent, as the remote itself does
|
||||
this->previous_mode_ = mode & 0b00001111;
|
||||
if (mode & 0b00001000) { // The power bit
|
||||
switch (mode & 0b00000111) {
|
||||
case AIRTON_MODE_COOL:
|
||||
this->mode = climate::CLIMATE_MODE_COOL;
|
||||
break;
|
||||
case AIRTON_MODE_DRY:
|
||||
this->mode = climate::CLIMATE_MODE_DRY;
|
||||
break;
|
||||
case AIRTON_MODE_HEAT:
|
||||
this->mode = climate::CLIMATE_MODE_HEAT;
|
||||
break;
|
||||
case AIRTON_MODE_AUTO:
|
||||
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
|
||||
break;
|
||||
case AIRTON_MODE_FAN:
|
||||
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this->mode = climate::CLIMATE_MODE_OFF;
|
||||
}
|
||||
uint8_t fan_mode = (frame[2] & 0b01110000) >> 4;
|
||||
|
||||
switch (fan_mode) {
|
||||
case AIRTON_FAN_1:
|
||||
case AIRTON_FAN_2:
|
||||
this->fan_mode = climate::CLIMATE_FAN_LOW;
|
||||
break;
|
||||
case AIRTON_FAN_3:
|
||||
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
|
||||
break;
|
||||
case AIRTON_FAN_4:
|
||||
case AIRTON_FAN_5:
|
||||
this->fan_mode = climate::CLIMATE_FAN_HIGH;
|
||||
break;
|
||||
case AIRTON_FAN_AUTO:
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
break;
|
||||
}
|
||||
|
||||
this->target_temperature = (frame[3] & 0b00001111) + AIRTON_TEMP_MIN;
|
||||
|
||||
const uint8_t direction = frame[4] & 0b00001111;
|
||||
if (direction <= static_cast<uint8_t>(VerticalDirection::VERTICAL_DIRECTION_DOWN)) {
|
||||
this->set_vertical_direction_state(static_cast<VerticalDirection>(direction));
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Unknown vertical direction %u", direction);
|
||||
}
|
||||
this->set_display_state((frame[5] & 0b10000000) != 0);
|
||||
this->set_sleep_mode_state((frame[5] & 0b00000010) != 0);
|
||||
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
bool AirtonClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
uint8_t remote_state[AIRTON_STATE_FRAME_SIZE] = {};
|
||||
if (!data.expect_item(AIRTON_HEADER_MARK, AIRTON_HEADER_SPACE)) {
|
||||
ESP_LOGV(TAG, "Wrong header encoding detected!");
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < AIRTON_STATE_FRAME_SIZE; i++) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (data.expect_item(AIRTON_BIT_MARK, AIRTON_ONE_SPACE)) {
|
||||
remote_state[i] |= 1 << j;
|
||||
} else if (!data.expect_item(AIRTON_BIT_MARK, AIRTON_ZERO_SPACE)) {
|
||||
ESP_LOGV(TAG, "Wrong modulation encoding for: Byte %d, bit %d", i, j);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remote_state[0] != AIRTON_HEADER_BYTE0 || remote_state[1] != AIRTON_HEADER_BYTE1) {
|
||||
ESP_LOGV(TAG, "Wrong header contents: %02X %02X", remote_state[1], remote_state[0]);
|
||||
return false;
|
||||
}
|
||||
const uint8_t expected = checksum(remote_state);
|
||||
if (remote_state[AIRTON_STATE_FRAME_SIZE - 1] != expected) {
|
||||
ESP_LOGV(TAG, "Checksum error: calculated %02X, received %02X", expected,
|
||||
remote_state[AIRTON_STATE_FRAME_SIZE - 1]);
|
||||
return false;
|
||||
}
|
||||
ESP_LOGV(TAG, "Received: %02X %02X %02X %02X %02X %02X %02X", remote_state[6], remote_state[5], remote_state[4],
|
||||
remote_state[3], remote_state[2], remote_state[1], remote_state[0]);
|
||||
this->parse_state_frame_(remote_state);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace esphome::airton
|
||||
@@ -0,0 +1,137 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/climate_ir/climate_ir.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
#include "esphome/components/select/select.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::airton {
|
||||
|
||||
// Values for Airton SMVH09B-2A2A3NH IR Controllers
|
||||
static constexpr uint8_t AIRTON_TEMP_MIN = 16; // Celsius
|
||||
static constexpr uint8_t AIRTON_TEMP_MAX = 31; // Celsius
|
||||
static constexpr uint8_t AIRTON_AUTO_MODE_TEMP = 25; // The fixed setpoint the unit uses in auto mode
|
||||
|
||||
static constexpr uint8_t AIRTON_MODE_AUTO = 0b000;
|
||||
static constexpr uint8_t AIRTON_MODE_COOL = 0b001;
|
||||
static constexpr uint8_t AIRTON_MODE_HEAT = 0b100;
|
||||
static constexpr uint8_t AIRTON_MODE_DRY = 0b010;
|
||||
static constexpr uint8_t AIRTON_MODE_FAN = 0b011;
|
||||
|
||||
static constexpr uint8_t AIRTON_FAN_AUTO = 0b000;
|
||||
static constexpr uint8_t AIRTON_FAN_1 = 0b001;
|
||||
static constexpr uint8_t AIRTON_FAN_2 = 0b010;
|
||||
static constexpr uint8_t AIRTON_FAN_3 = 0b011;
|
||||
static constexpr uint8_t AIRTON_FAN_4 = 0b100;
|
||||
static constexpr uint8_t AIRTON_FAN_5 = 0b101;
|
||||
|
||||
static constexpr uint32_t AIRTON_IR_FREQUENCY = 38000;
|
||||
static constexpr uint32_t AIRTON_HEADER_MARK = 6630;
|
||||
static constexpr uint32_t AIRTON_HEADER_SPACE = 3350;
|
||||
static constexpr uint32_t AIRTON_BIT_MARK = 400;
|
||||
static constexpr uint32_t AIRTON_ONE_SPACE = 1260;
|
||||
static constexpr uint32_t AIRTON_ZERO_SPACE = 430;
|
||||
static constexpr uint32_t AIRTON_MESSAGE_SPACE = 100000;
|
||||
|
||||
static constexpr uint8_t AIRTON_STATE_FRAME_SIZE = 7;
|
||||
// Every frame starts with these two bytes
|
||||
static constexpr uint8_t AIRTON_HEADER_BYTE0 = 0xD3;
|
||||
static constexpr uint8_t AIRTON_HEADER_BYTE1 = 0x11;
|
||||
|
||||
// Position of the air outlet flap; the values are the nibble the frame carries and the index
|
||||
// of the option in the vertical direction select
|
||||
enum class VerticalDirection : uint8_t {
|
||||
VERTICAL_DIRECTION_OFF = 0,
|
||||
VERTICAL_DIRECTION_SWING = 1,
|
||||
VERTICAL_DIRECTION_UP = 2,
|
||||
VERTICAL_DIRECTION_MIDDLE_UP = 3,
|
||||
VERTICAL_DIRECTION_MIDDLE = 4,
|
||||
VERTICAL_DIRECTION_MIDDLE_DOWN = 5,
|
||||
VERTICAL_DIRECTION_DOWN = 6,
|
||||
};
|
||||
|
||||
// The on/off settings the switch platform exposes
|
||||
enum class AirtonSetting : uint8_t {
|
||||
AIRTON_SETTING_SLEEP_MODE,
|
||||
AIRTON_SETTING_DISPLAY,
|
||||
};
|
||||
|
||||
// The settings the unit keeps that the climate state does not carry; saved to flash when they change
|
||||
struct AirtonSettings {
|
||||
bool sleep_state;
|
||||
bool display_state;
|
||||
VerticalDirection vertical_direction;
|
||||
};
|
||||
|
||||
class AirtonClimate : public climate_ir::ClimateIR {
|
||||
public:
|
||||
AirtonClimate()
|
||||
: climate_ir::ClimateIR(AIRTON_TEMP_MIN, AIRTON_TEMP_MAX, 1.0f, true, true,
|
||||
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
|
||||
climate::CLIMATE_FAN_HIGH},
|
||||
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {}
|
||||
void setup() override;
|
||||
void control(const climate::ClimateCall &call) override;
|
||||
|
||||
// The direction the settings start from until a saved one is restored in setup()
|
||||
void set_default_vertical_direction(VerticalDirection direction) { this->settings_.vertical_direction = direction; }
|
||||
|
||||
// The setters update the state, publish their entity and save; they return whether anything changed.
|
||||
// A change made here (switch, select, action) is then sent with send_settings(); a frame from the
|
||||
// remote only reports what the unit already did.
|
||||
bool set_sleep_mode_state(bool state);
|
||||
bool set_display_state(bool state);
|
||||
bool set_vertical_direction_state(VerticalDirection direction);
|
||||
void send_settings();
|
||||
void set_setting(AirtonSetting setting, bool state) {
|
||||
const bool changed = setting == AirtonSetting::AIRTON_SETTING_DISPLAY ? this->set_display_state(state)
|
||||
: this->set_sleep_mode_state(state);
|
||||
if (changed)
|
||||
this->send_settings();
|
||||
}
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
void set_sleep_mode_switch(switch_::Switch *sw) { this->sleep_mode_switch_ = sw; }
|
||||
void set_display_switch(switch_::Switch *sw) { this->display_switch_ = sw; }
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void set_vertical_direction_select(select::Select *sel) { this->vertical_direction_select_ = sel; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void publish_settings_();
|
||||
void save_settings_();
|
||||
void sync_swing_mode_();
|
||||
bool sleep_mode_allowed_() const;
|
||||
|
||||
void transmit_state() override;
|
||||
uint8_t operation_mode_();
|
||||
uint8_t fan_speed_();
|
||||
uint8_t temperature_();
|
||||
uint8_t operation_settings_();
|
||||
|
||||
bool on_receive(remote_base::RemoteReceiveData data) override;
|
||||
void parse_state_frame_(const uint8_t *frame);
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
switch_::Switch *sleep_mode_switch_{nullptr};
|
||||
switch_::Switch *display_switch_{nullptr};
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
select::Select *vertical_direction_select_{nullptr};
|
||||
#endif
|
||||
ESPPreferenceObject settings_pref_;
|
||||
// The unit's own defaults: display lit, no sleep mode, flap still
|
||||
AirtonSettings settings_{false, true, VerticalDirection::VERTICAL_DIRECTION_OFF};
|
||||
// The last mode sent, so turning off repeats it with the power bit cleared
|
||||
uint8_t previous_mode_{0};
|
||||
};
|
||||
|
||||
} // namespace esphome::airton
|
||||
@@ -0,0 +1,71 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import climate_ir
|
||||
import esphome.config_validation as cv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
AUTO_LOAD = ["climate_ir"]
|
||||
CODEOWNERS = ["@Procsiab"]
|
||||
|
||||
airton_ns = cg.esphome_ns.namespace("airton")
|
||||
AirtonClimate = airton_ns.class_("AirtonClimate", climate_ir.ClimateIR)
|
||||
|
||||
CONF_AIRTON_ID = "airton_id"
|
||||
CONF_SLEEP_MODE = "sleep_mode"
|
||||
CONF_VERTICAL_DIRECTION = "vertical_direction"
|
||||
|
||||
VerticalDirection = airton_ns.enum("VerticalDirection", is_class=True)
|
||||
# In the order of the enum, so the option index is the direction value
|
||||
VERTICAL_DIRECTIONS = VerticalDirection.options(
|
||||
"off",
|
||||
"swing",
|
||||
"up",
|
||||
"middle-up",
|
||||
"middle",
|
||||
"middle-down",
|
||||
"down",
|
||||
prefix="VERTICAL_DIRECTION_",
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(AirtonClimate).extend(
|
||||
{
|
||||
cv.Optional(CONF_VERTICAL_DIRECTION, default="off"): cv.enum(
|
||||
VERTICAL_DIRECTIONS
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
AIRTON_ACTION_SCHEMA = automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(AirtonClimate),
|
||||
}
|
||||
)
|
||||
|
||||
for _name, _call in (
|
||||
(
|
||||
"climate.airton.display_on",
|
||||
"set_setting(esphome::airton::AirtonSetting::AIRTON_SETTING_DISPLAY, true)",
|
||||
),
|
||||
(
|
||||
"climate.airton.display_off",
|
||||
"set_setting(esphome::airton::AirtonSetting::AIRTON_SETTING_DISPLAY, false)",
|
||||
),
|
||||
(
|
||||
"climate.airton.sleep_on",
|
||||
"set_setting(esphome::airton::AirtonSetting::AIRTON_SETTING_SLEEP_MODE, true)",
|
||||
),
|
||||
(
|
||||
"climate.airton.sleep_off",
|
||||
"set_setting(esphome::airton::AirtonSetting::AIRTON_SETTING_SLEEP_MODE, false)",
|
||||
),
|
||||
):
|
||||
automation.register_apply_action(
|
||||
_name, AIRTON_ACTION_SCHEMA, automation.ApplyCall(_call)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = await climate_ir.new_climate_ir(config)
|
||||
# The default until a saved setting is restored in setup(); off is the member's initial value
|
||||
if (direction := config[CONF_VERTICAL_DIRECTION]) != "off":
|
||||
cg.add(var.set_default_vertical_direction(direction))
|
||||
@@ -0,0 +1,37 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import select
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import ENTITY_CATEGORY_CONFIG
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from ..climate import (
|
||||
CONF_AIRTON_ID,
|
||||
CONF_VERTICAL_DIRECTION,
|
||||
VERTICAL_DIRECTIONS,
|
||||
AirtonClimate,
|
||||
airton_ns,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@Procsiab"]
|
||||
|
||||
VerticalDirectionSelect = airton_ns.class_("VerticalDirectionSelect", select.Select)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_AIRTON_ID): cv.use_id(AirtonClimate),
|
||||
cv.Optional(CONF_VERTICAL_DIRECTION): select.select_schema(
|
||||
VerticalDirectionSelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_AIRTON_ID])
|
||||
selects = select.sub_selects(config, parent=hub)
|
||||
await selects(
|
||||
CONF_VERTICAL_DIRECTION,
|
||||
hub.set_vertical_direction_select,
|
||||
options=list(VERTICAL_DIRECTIONS),
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../airton.h"
|
||||
|
||||
namespace esphome::airton {
|
||||
|
||||
class VerticalDirectionSelect : public select::Select, public Parented<AirtonClimate> {
|
||||
protected:
|
||||
// The options are in the order of the enum, so the index is the direction; the parent publishes the state
|
||||
void control(size_t index) override {
|
||||
if (this->parent_->set_vertical_direction_state(static_cast<VerticalDirection>(index)))
|
||||
this->parent_->send_settings();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::airton
|
||||
@@ -0,0 +1,41 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import switch
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_DISPLAY, ENTITY_CATEGORY_CONFIG
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from ..climate import CONF_AIRTON_ID, CONF_SLEEP_MODE, AirtonClimate, airton_ns
|
||||
|
||||
CODEOWNERS = ["@Procsiab"]
|
||||
|
||||
AirtonSwitch = airton_ns.class_("AirtonSwitch", switch.Switch)
|
||||
AirtonSetting = airton_ns.enum("AirtonSetting", is_class=True)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_AIRTON_ID): cv.use_id(AirtonClimate),
|
||||
cv.Optional(CONF_DISPLAY): switch.switch_schema(
|
||||
AirtonSwitch,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
default_restore_mode="DISABLED",
|
||||
),
|
||||
cv.Optional(CONF_SLEEP_MODE): switch.switch_schema(
|
||||
AirtonSwitch,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
default_restore_mode="DISABLED",
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_AIRTON_ID])
|
||||
switches = switch.sub_switches(config, parent=hub)
|
||||
await switches(
|
||||
CONF_DISPLAY, hub.set_display_switch, AirtonSetting.AIRTON_SETTING_DISPLAY
|
||||
)
|
||||
await switches(
|
||||
CONF_SLEEP_MODE,
|
||||
hub.set_sleep_mode_switch,
|
||||
AirtonSetting.AIRTON_SETTING_SLEEP_MODE,
|
||||
)
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "../airton.h"
|
||||
|
||||
namespace esphome::airton {
|
||||
|
||||
// One of the unit's on/off settings; the parent sends the state and publishes it back
|
||||
class AirtonSwitch : public switch_::Switch, public Parented<AirtonClimate> {
|
||||
public:
|
||||
explicit AirtonSwitch(AirtonSetting setting) : setting_(setting) {}
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override { this->parent_->set_setting(this->setting_, state); }
|
||||
|
||||
AirtonSetting setting_;
|
||||
};
|
||||
|
||||
} // namespace esphome::airton
|
||||
@@ -5,7 +5,6 @@ from esphome.const import (
|
||||
CONF_CURRENT,
|
||||
CONF_FLOW,
|
||||
CONF_HEAD,
|
||||
CONF_ID,
|
||||
CONF_POWER,
|
||||
CONF_SPEED,
|
||||
CONF_VOLTAGE,
|
||||
@@ -70,9 +69,7 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
var = await ble_client.new_ble_node_component(config)
|
||||
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_FLOW, var.set_flow_sensor)
|
||||
|
||||
@@ -42,21 +42,18 @@ bool AM2315C::reset_register_(uint8_t reg) {
|
||||
data[2] = 0;
|
||||
ESP_LOGD(TAG, "Reset register: 0x%02x", reg);
|
||||
if (this->write(data, 3) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Write failed!");
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return false;
|
||||
}
|
||||
delay(5);
|
||||
if (this->read(data, 3) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Read failed!");
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return false;
|
||||
}
|
||||
delay(10);
|
||||
data[0] = 0xB0 | reg;
|
||||
if (this->write(data, 3) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Write failed!");
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return false;
|
||||
}
|
||||
delay(5);
|
||||
@@ -67,7 +64,7 @@ bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) {
|
||||
uint32_t raw;
|
||||
raw = (data[1] << 12) | (data[2] << 4) | (data[3] >> 4);
|
||||
humidity = raw * 9.5367431640625e-5;
|
||||
raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
|
||||
raw = encode_uint24(data[3] & 0x0F, data[4], data[5]);
|
||||
temperature = raw * 1.9073486328125e-4 - 50;
|
||||
return crc8(data, 6, 0xFF, 0x31, true) == data[6];
|
||||
}
|
||||
@@ -76,8 +73,7 @@ void AM2315C::setup() {
|
||||
// get status
|
||||
uint8_t status = 0;
|
||||
if (this->read(&status, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Read failed!");
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -86,18 +82,9 @@ void AM2315C::setup() {
|
||||
// never required during testing
|
||||
if ((status & 0x18) != 0x18) {
|
||||
ESP_LOGD(TAG, "Resetting AM2315C registers");
|
||||
if (!this->reset_register_(0x1B)) {
|
||||
this->mark_failed();
|
||||
// reset_register_() marks the component failed with its own reason
|
||||
if (!this->reset_register_(0x1B) || !this->reset_register_(0x1C) || !this->reset_register_(0x1E))
|
||||
return;
|
||||
}
|
||||
if (!this->reset_register_(0x1C)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (!this->reset_register_(0x1E)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,9 +155,6 @@ void AM2315C::update() {
|
||||
void AM2315C::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "AM2315C:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ from esphome.components import i2c, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_HUMIDITY,
|
||||
CONF_ID,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
@@ -42,9 +41,7 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config)
|
||||
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_TEMPERATURE, var.set_temperature_sensor)
|
||||
|
||||
@@ -37,16 +37,13 @@ void AM2320Component::setup() {
|
||||
data[0] = 0;
|
||||
data[1] = 4;
|
||||
if (!this->read_data_(data)) {
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
}
|
||||
void AM2320Component::dump_config() {
|
||||
ESP_LOGD(TAG, "AM2320:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||
}
|
||||
@@ -72,12 +69,7 @@ bool AM2320Component::read_data_(uint8_t *data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t checksum;
|
||||
|
||||
checksum = data[7] << 8;
|
||||
checksum += data[6];
|
||||
|
||||
if (crc16(data, 6) != checksum) {
|
||||
if (crc16(data, 6) != encode_value_le<uint16_t>(&data[6])) {
|
||||
ESP_LOGW(TAG, "AM2320 Checksum invalid!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ from esphome.components import i2c, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_HUMIDITY,
|
||||
CONF_ID,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
@@ -44,9 +43,7 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config)
|
||||
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_TEMPERATURE, var.set_temperature_sensor)
|
||||
|
||||
@@ -19,7 +19,7 @@ CONFIG_SCHEMA = (
|
||||
cover.cover_schema(Am43Component)
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(CONF_PIN, default=8888): cv.int_range(min=0, max=0xFFFF),
|
||||
cv.Optional(CONF_PIN, default=8888): cv.uint16_t,
|
||||
cv.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -3,7 +3,6 @@ from esphome.components import ble_client, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BATTERY_LEVEL,
|
||||
CONF_ID,
|
||||
CONF_ILLUMINANCE,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
@@ -44,9 +43,7 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
var = await ble_client.new_ble_node_component(config)
|
||||
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_BATTERY_LEVEL, var.set_battery)
|
||||
|
||||
@@ -37,8 +37,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
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))
|
||||
await cg.add_variable(var.set_sensor, config[CONF_SENSOR_ID])
|
||||
|
||||
if isinstance(config[CONF_THRESHOLD], dict):
|
||||
lower = await cg.templatable(config[CONF_THRESHOLD][CONF_LOWER], [], cg.float_)
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
namespace esphome::animation {
|
||||
|
||||
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count,
|
||||
image::ImageType type, image::Transparency transparent)
|
||||
: Image(data_start, width, height, type, transparent),
|
||||
image::ImageType type, image::Transparency transparent, bool big_endian)
|
||||
: Image(data_start, width, height, type, transparent, big_endian),
|
||||
animation_data_start_(data_start),
|
||||
current_frame_(0),
|
||||
animation_frame_count_(animation_frame_count),
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace esphome::animation {
|
||||
class Animation final : public image::Image {
|
||||
public:
|
||||
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type,
|
||||
image::Transparency transparent);
|
||||
image::Transparency transparent, bool big_endian = false);
|
||||
|
||||
uint32_t get_animation_frame_count() const;
|
||||
int get_current_frame() const;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.const import CONF_LOOP
|
||||
from esphome.components.const import CONF_BYTE_ORDER, CONF_LOOP
|
||||
from esphome.components.file import image as file_image
|
||||
from esphome.components.file.image import image_schema, write_image
|
||||
from esphome.components.image import Image_, validate_settings
|
||||
@@ -90,6 +90,7 @@ async def setup_animation(config: ConfigType) -> None:
|
||||
frame_count,
|
||||
image_type,
|
||||
trans_value,
|
||||
config.get(CONF_BYTE_ORDER) == "BIG_ENDIAN",
|
||||
)
|
||||
if loop_config := config.get(CONF_LOOP):
|
||||
start = loop_config[CONF_START_FRAME]
|
||||
|
||||
@@ -28,15 +28,6 @@ static constexpr uint32_t APC1_NO_DATA_TIMEOUT_MS = 15000;
|
||||
// Drop a partial frame when the bytes stop mid-way
|
||||
static constexpr uint32_t APC1_RX_IDLE_TIMEOUT_MS = 200;
|
||||
|
||||
// Frames and commands end with the 16-bit sum of every byte before it
|
||||
static uint16_t apc1_checksum(const uint8_t *data, size_t len) {
|
||||
uint16_t sum = 0;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
sum += data[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
void APC1Component::setup() {
|
||||
if (this->set_pin_ != nullptr) {
|
||||
this->set_pin_->setup();
|
||||
@@ -113,16 +104,14 @@ void APC1Component::loop() {
|
||||
this->rx_index_ = 0;
|
||||
}
|
||||
|
||||
while (this->available()) {
|
||||
this->read_available([this, now](uint8_t byte) {
|
||||
this->last_transmission_ = now;
|
||||
uint8_t byte;
|
||||
this->read_byte(&byte);
|
||||
|
||||
if (this->rx_index_ == 0) {
|
||||
if (byte == APC1_HEADER_1) {
|
||||
this->rx_buffer_[this->rx_index_++] = byte;
|
||||
}
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->rx_index_ == 1) {
|
||||
@@ -131,12 +120,12 @@ void APC1Component::loop() {
|
||||
} else if (byte != APC1_HEADER_1) {
|
||||
this->rx_index_ = 0;
|
||||
}
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
this->rx_buffer_[this->rx_index_++] = byte;
|
||||
if (this->rx_index_ < APC1_FRAME_HEADER_SIZE) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
// The payload carries at least the checksum and must fit the buffer; checking it before the sum keeps a
|
||||
@@ -145,19 +134,19 @@ void APC1Component::loop() {
|
||||
if (payload_len < 2 || payload_len > this->rx_buffer_.size() - APC1_FRAME_HEADER_SIZE) {
|
||||
ESP_LOGW(TAG, "Invalid frame length: %u", payload_len);
|
||||
this->rx_index_ = 0;
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
const uint8_t total_len = payload_len + APC1_FRAME_HEADER_SIZE;
|
||||
if (this->rx_index_ == total_len) {
|
||||
this->parse_frame_(total_len);
|
||||
this->rx_index_ = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void APC1Component::parse_frame_(uint16_t total_len) {
|
||||
uint16_t expected_checksum = encode_uint16(this->rx_buffer_[total_len - 2], this->rx_buffer_[total_len - 1]);
|
||||
uint16_t calculated_checksum = apc1_checksum(this->rx_buffer_.data(), total_len - 2);
|
||||
uint16_t calculated_checksum = sum16_bytes(this->rx_buffer_.data(), total_len - 2);
|
||||
if (expected_checksum != calculated_checksum) {
|
||||
ESP_LOGW(TAG, "APC1 checksum mismatch: calculated 0x%04X != expected 0x%04X", calculated_checksum,
|
||||
expected_checksum);
|
||||
@@ -343,7 +332,7 @@ void APC1Component::send_command_(APC1Command cmd, uint16_t data) {
|
||||
0,
|
||||
0,
|
||||
};
|
||||
uint16_t checksum = apc1_checksum(buf.data(), 5);
|
||||
uint16_t checksum = sum16_bytes(buf.data(), 5);
|
||||
buf[5] = static_cast<uint8_t>((checksum >> 8) & 0xFF);
|
||||
buf[6] = static_cast<uint8_t>(checksum & 0xFF);
|
||||
this->write_array(buf);
|
||||
|
||||
@@ -7,7 +7,6 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ECO2,
|
||||
CONF_HUMIDITY,
|
||||
CONF_ID,
|
||||
CONF_PM_0_3UM,
|
||||
CONF_PM_0_5UM,
|
||||
CONF_PM_1_0,
|
||||
@@ -165,9 +164,7 @@ FINAL_VALIDATE_SCHEMA = final_validate
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
var = await uart.new_uart_component(config)
|
||||
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_PM_1_0, var.set_pm_1_0_sensor)
|
||||
@@ -194,13 +191,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
await sensors(CONF_RS3, var.set_rs3_sensor)
|
||||
await sensors(CONF_ERROR_CODE, var.set_error_code_sensor)
|
||||
|
||||
if (set_pin_config := config.get(CONF_SET_PIN)) is not None:
|
||||
set_pin = await cg.gpio_pin_expression(set_pin_config)
|
||||
cg.add(var.set_set_pin(set_pin))
|
||||
|
||||
if (reset_pin_config := config.get(CONF_RESET_PIN)) is not None:
|
||||
reset_pin = await cg.gpio_pin_expression(reset_pin_config)
|
||||
cg.add(var.set_reset_pin(reset_pin))
|
||||
await cg.add_pin(var.set_set_pin, config.get(CONF_SET_PIN))
|
||||
await cg.add_pin(var.set_reset_pin, config.get(CONF_RESET_PIN))
|
||||
|
||||
|
||||
APC1_ACTION_SCHEMA = maybe_simple_id(
|
||||
|
||||
@@ -27,45 +27,36 @@ void APCProteousCover::setup() {
|
||||
}
|
||||
|
||||
void APCProteousCover::loop() {
|
||||
uint8_t data;
|
||||
while (this->available() > 0) {
|
||||
if (!this->read_byte(&data)) {
|
||||
continue;
|
||||
this->read_available([this](uint8_t data) {
|
||||
const char *line = this->line_reader_.feed(data);
|
||||
if (this->line_reader_.overflowed()) {
|
||||
// Start over from this byte, so a frame right after unterminated noise is still read
|
||||
ESP_LOGVV(TAG, "rx overflow, discarding");
|
||||
this->line_reader_.restart_with(data);
|
||||
} else if (line != nullptr && line[0] != '\0') {
|
||||
ESP_LOGVV(TAG, "rx: '%s'", line);
|
||||
this->parse_response_(line);
|
||||
}
|
||||
if (data == '\r') {
|
||||
if (this->rx_len_ != 0) {
|
||||
this->rx_buffer_[this->rx_len_] = '\0';
|
||||
ESP_LOGVV(TAG, "rx: '%s'", this->rx_buffer_);
|
||||
this->parse_response_();
|
||||
this->rx_len_ = 0;
|
||||
}
|
||||
} else if (data != '\n') {
|
||||
if (this->rx_len_ >= MAX_RESPONSE_LEN) {
|
||||
ESP_LOGVV(TAG, "rx overflow, discarding");
|
||||
this->rx_len_ = 0;
|
||||
}
|
||||
this->rx_buffer_[this->rx_len_++] = static_cast<char>(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void APCProteousCover::parse_response_() {
|
||||
void APCProteousCover::parse_response_(const char *line) {
|
||||
// Expected format: "?s=XX" or "?x=XX" where XX is a hex value
|
||||
if (this->rx_len_ < 4 || this->rx_buffer_[0] != '?' || this->rx_buffer_[2] != '=') {
|
||||
ESP_LOGV(TAG, "Invalid response: %s", this->rx_buffer_);
|
||||
if (line[0] != '?' || line[1] == '\0' || line[2] != '=' || line[3] == '\0') {
|
||||
ESP_LOGV(TAG, "Invalid response: %s", line);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *hex_str = this->rx_buffer_ + 3;
|
||||
const char *hex_str = line + 3;
|
||||
char *end_ptr;
|
||||
uint8_t value = static_cast<uint8_t>(strtol(hex_str, &end_ptr, 16));
|
||||
if (end_ptr == hex_str) {
|
||||
ESP_LOGW(TAG, "Failed to parse hex value: %s", this->rx_buffer_);
|
||||
ESP_LOGW(TAG, "Failed to parse hex value: %s", line);
|
||||
return;
|
||||
}
|
||||
|
||||
bool state_changed = false;
|
||||
char type = this->rx_buffer_[1];
|
||||
char type = line[1];
|
||||
if (type == 's') {
|
||||
// bit 0 = operating, bit 1 = direction (0 = opening, 1 = closing)
|
||||
CoverOperation new_operation = COVER_OPERATION_IDLE;
|
||||
@@ -154,11 +145,10 @@ void APCProteousCover::update() {
|
||||
return;
|
||||
}
|
||||
// The line is idle, so anything left in the buffer is an unterminated echo
|
||||
if (this->rx_len_ != 0) {
|
||||
this->rx_buffer_[this->rx_len_] = '\0';
|
||||
ESP_LOGVV(TAG, "discarding stale rx '%s' before query", this->rx_buffer_);
|
||||
this->rx_len_ = 0;
|
||||
if (this->line_reader_.length() != 0) {
|
||||
ESP_LOGVV(TAG, "discarding stale rx '%s' before query", this->line_reader_.partial());
|
||||
}
|
||||
this->line_reader_.reset();
|
||||
|
||||
const char *query = this->query_s_next_ ? QUERY_S : QUERY_X;
|
||||
ESP_LOGVV(TAG, "tx: %s", query);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/line_reader.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/components/cover/cover.h"
|
||||
|
||||
@@ -17,7 +18,7 @@ class APCProteousCover : public cover::Cover, public PollingComponent, public ua
|
||||
|
||||
protected:
|
||||
void control(const cover::CoverCall &call) override;
|
||||
void parse_response_();
|
||||
void parse_response_(const char *line);
|
||||
void send_command_(const char *cmd);
|
||||
void write_command_(const char *cmd, uint32_t now);
|
||||
void stop_cmd_(uint32_t now);
|
||||
@@ -43,8 +44,7 @@ class APCProteousCover : public cover::Cover, public PollingComponent, public ua
|
||||
const char *pending_command_{nullptr};
|
||||
uint32_t pending_command_time_{0};
|
||||
uint32_t last_command_tx_{0};
|
||||
char rx_buffer_[MAX_RESPONSE_LEN + 1];
|
||||
uint8_t rx_len_{0};
|
||||
LineReader<MAX_RESPONSE_LEN + 1, '\r', '\n'> line_reader_;
|
||||
uint8_t command_retries_{0};
|
||||
bool query_s_next_{true};
|
||||
};
|
||||
|
||||
@@ -34,8 +34,7 @@ enum { // APDS9306 registers
|
||||
|
||||
#define APDS9306_ERROR_CHECK(func, error) \
|
||||
if (!(func)) { \
|
||||
ESP_LOGE(TAG, error); \
|
||||
this->mark_failed(); \
|
||||
this->mark_failed(LOG_STR(error)); \
|
||||
return; \
|
||||
}
|
||||
#define APDS9306_WARNING_CHECK(func, warning) \
|
||||
@@ -48,23 +47,14 @@ enum { // APDS9306 registers
|
||||
ESP_LOGV(TAG, "Writing 0x%02x to 0x%02x", value, reg); \
|
||||
if (!this->write_byte(reg, value)) { \
|
||||
ESP_LOGE(TAG, "Failed writing 0x%02x to 0x%02x", value, reg); \
|
||||
this->mark_failed(); \
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); \
|
||||
return; \
|
||||
}
|
||||
|
||||
void APDS9306::setup() {
|
||||
uint8_t id;
|
||||
if (!this->read_byte(APDS9306_PART_ID, &id)) { // Part ID register
|
||||
this->error_code_ = COMMUNICATION_FAILED;
|
||||
this->mark_failed();
|
||||
// 0xB1 for APDS9306, 0xB3 for APDS9306-065; they differ only in bit 1
|
||||
if (!this->check_chip_id(this, APDS9306_PART_ID, 0xB1, 0xFD))
|
||||
return;
|
||||
}
|
||||
|
||||
if (id != 0xB1 && id != 0xB3) { // 0xB1 for APDS9306 0xB3 for APDS9306-065
|
||||
this->error_code_ = WRONG_ID;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// ALS resolution and measurement, see datasheet or init.py for options
|
||||
uint8_t als_meas_rate = ((this->bit_width_ & 0x07) << 4) | (this->measurement_rate_ & 0x07);
|
||||
@@ -89,20 +79,6 @@ void APDS9306::dump_config() {
|
||||
LOG_SENSOR("", "APDS9306", this);
|
||||
LOG_I2C_DEVICE(this);
|
||||
|
||||
if (this->is_failed()) {
|
||||
switch (this->error_code_) {
|
||||
case COMMUNICATION_FAILED:
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
break;
|
||||
case WRONG_ID:
|
||||
ESP_LOGE(TAG, "APDS9306 has invalid id!");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Setting up APDS9306 registers failed!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Gain: %u\n"
|
||||
" Measurement rate: %u\n"
|
||||
|
||||
@@ -50,12 +50,6 @@ class APDS9306 final : public sensor::Sensor, public PollingComponent, public i2
|
||||
void set_ambient_light_gain(AmbientLightGain gain) { this->gain_ = gain; }
|
||||
|
||||
protected:
|
||||
enum ErrorCode {
|
||||
NONE = 0,
|
||||
COMMUNICATION_FAILED,
|
||||
WRONG_ID,
|
||||
} error_code_{NONE};
|
||||
|
||||
MeasurementBitWidth bit_width_;
|
||||
MeasurementRate measurement_rate_;
|
||||
AmbientLightGain gain_;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "apds9930.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -64,16 +65,8 @@ static constexpr uint16_t ALS_SATURATION = 1024 * (256 - APDS9930_ATIME_VALUE);
|
||||
bool APDS9930Component::write_reg_(uint8_t reg, uint8_t value) { return this->write_byte(reg | APDS9930_CMD, value); }
|
||||
|
||||
void APDS9930Component::setup() {
|
||||
uint8_t id;
|
||||
if (!this->read_byte(APDS9930_ID | APDS9930_CMD, &id)) {
|
||||
this->mark_failed(LOG_STR("Communication failed"));
|
||||
if (!this->check_chip_id(this, APDS9930_ID | APDS9930_CMD, APDS9930_CHIP_ID))
|
||||
return;
|
||||
}
|
||||
if (id != APDS9930_CHIP_ID) {
|
||||
ESP_LOGE(TAG, "Wrong ID 0x%02X (expected 0x%02X)", id, APDS9930_CHIP_ID);
|
||||
this->mark_failed(LOG_STR("Wrong chip ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t control =
|
||||
(this->led_drive_ & 0b11) << CONTROL_PDRIVE_SHIFT | CONTROL_PDIODE_CH1 << CONTROL_PDIODE_SHIFT |
|
||||
@@ -119,8 +112,8 @@ void APDS9930Component::update() {
|
||||
const uint8_t status = raw[0];
|
||||
|
||||
if (this->illuminance_sensor_ != nullptr && (status & APDS9930_AVALID) != 0) {
|
||||
const uint16_t ch0 = encode_uint16(raw[2], raw[1]);
|
||||
const uint16_t ch1 = encode_uint16(raw[4], raw[3]);
|
||||
const uint16_t ch0 = encode_value_le<uint16_t>(&raw[1]);
|
||||
const uint16_t ch1 = encode_value_le<uint16_t>(&raw[3]);
|
||||
if (ch0 >= ALS_SATURATION || ch1 >= ALS_SATURATION) {
|
||||
// A saturated channel would give a lux value that is too low, not too high
|
||||
ESP_LOGW(TAG, "Light channel saturated, lower ambient_light_gain");
|
||||
@@ -133,7 +126,7 @@ void APDS9930Component::update() {
|
||||
}
|
||||
|
||||
if (this->proximity_sensor_ != nullptr && (status & APDS9930_PVALID) != 0) {
|
||||
this->proximity_sensor_->publish_state(encode_uint16(raw[6], raw[5]));
|
||||
this->proximity_sensor_->publish_state(encode_value_le<uint16_t>(&raw[5]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ from esphome.components import i2c, sensor
|
||||
from esphome.components.const import UNIT_COUNTS
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_ILLUMINANCE,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
@@ -59,9 +58,7 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config)
|
||||
cg.add(var.set_led_drive(config[CONF_LED_DRIVE]))
|
||||
cg.add(var.set_proximity_gain(config[CONF_PROXIMITY_GAIN]))
|
||||
cg.add(var.set_ambient_gain(config[CONF_AMBIENT_LIGHT_GAIN]))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
@@ -60,9 +59,7 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config)
|
||||
cg.add(var.set_led_drive(config[CONF_LED_DRIVE]))
|
||||
cg.add(var.set_proximity_gain(config[CONF_PROXIMITY_GAIN]))
|
||||
cg.add(var.set_ambient_gain(config[CONF_AMBIENT_LIGHT_GAIN]))
|
||||
|
||||
@@ -8,7 +8,7 @@ ESPHOME_LOG_TAG(TAG, "apds9960");
|
||||
|
||||
#define APDS9960_ERROR_CHECK(func) \
|
||||
if (!(func)) { \
|
||||
this->mark_failed(); \
|
||||
this->mark_failed(LOG_STR("Setting up APDS9960 registers failed")); \
|
||||
return; \
|
||||
}
|
||||
#define APDS9960_WRITE_BYTE(reg, value) APDS9960_ERROR_CHECK(this->write_byte(reg, value));
|
||||
@@ -16,14 +16,12 @@ ESPHOME_LOG_TAG(TAG, "apds9960");
|
||||
void APDS9960::setup() {
|
||||
uint8_t id;
|
||||
if (!this->read_byte(0x92, &id)) { // ID register
|
||||
this->error_code_ = COMMUNICATION_FAILED;
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
|
||||
if (id != 0xAB && id != 0x9C && id != 0xA8 && id != 0x9E) { // APDS9960 all should have one of these IDs
|
||||
this->error_code_ = WRONG_ID;
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR("APDS9960 has invalid id"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -135,20 +133,6 @@ void APDS9960::dump_config() {
|
||||
LOG_SENSOR(" ", "Clear channel", this->clear_sensor_);
|
||||
LOG_SENSOR(" ", "Proximity", this->proximity_sensor_);
|
||||
#endif
|
||||
|
||||
if (this->is_failed()) {
|
||||
switch (this->error_code_) {
|
||||
case COMMUNICATION_FAILED:
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
break;
|
||||
case WRONG_ID:
|
||||
ESP_LOGE(TAG, "APDS9960 has invalid id!");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Setting up APDS9960 registers failed!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define APDS9960_WARNING_CHECK(func, warning) \
|
||||
@@ -178,13 +162,13 @@ void APDS9960::read_color_data_(uint8_t status) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t raw[8];
|
||||
APDS9960_WARNING_CHECK(this->read_bytes(0x94, raw, 8), "Reading color values failed.");
|
||||
uint16_t raw[4];
|
||||
APDS9960_WARNING_CHECK(this->read_bytes_16_le(0x94, raw, 4), "Reading color values failed.");
|
||||
|
||||
uint16_t uint_clear = (uint16_t(raw[1]) << 8) | raw[0];
|
||||
uint16_t uint_red = (uint16_t(raw[3]) << 8) | raw[2];
|
||||
uint16_t uint_green = (uint16_t(raw[5]) << 8) | raw[4];
|
||||
uint16_t uint_blue = (uint16_t(raw[7]) << 8) | raw[6];
|
||||
uint16_t uint_clear = raw[0];
|
||||
uint16_t uint_red = raw[1];
|
||||
uint16_t uint_green = raw[2];
|
||||
uint16_t uint_blue = raw[3];
|
||||
|
||||
float clear_perc = (uint_clear / float(UINT16_MAX)) * 100.0f;
|
||||
float red_perc = (uint_red / float(UINT16_MAX)) * 100.0f;
|
||||
|
||||
@@ -58,11 +58,6 @@ class APDS9960 final : public PollingComponent, public i2c::I2CDevice {
|
||||
uint8_t gesture_gain_;
|
||||
uint8_t gesture_wait_time_;
|
||||
|
||||
enum ErrorCode {
|
||||
NONE = 0,
|
||||
COMMUNICATION_FAILED,
|
||||
WRONG_ID,
|
||||
} error_code_{NONE};
|
||||
bool gesture_up_started_{false};
|
||||
bool gesture_down_started_{false};
|
||||
bool gesture_left_started_{false};
|
||||
|
||||
@@ -152,10 +152,7 @@ void OutgoingConnectionManager::loop(APIServer *server) {
|
||||
|
||||
void OutgoingConnectionManager::try_dial_(APIServer *server, uint32_t now) {
|
||||
if (!network::is_connected()) {
|
||||
// Flips within seconds of boot; recheck fast so a deep sleep wake
|
||||
// window is not spent waiting
|
||||
this->schedule_wait_(now, NETWORK_RETRY_MS);
|
||||
return;
|
||||
return; // still WAITING with the wait elapsed, so the next loop rechecks
|
||||
}
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addr_len = this->target_sockaddr_(&addr);
|
||||
@@ -197,7 +194,6 @@ void OutgoingConnectionManager::try_dial_(APIServer *server, uint32_t now) {
|
||||
}
|
||||
this->state_ = DialState::DIAL_STATE_CONNECTING;
|
||||
this->state_ts_ = now;
|
||||
this->last_poll_ = now;
|
||||
}
|
||||
|
||||
void OutgoingConnectionManager::poll_connect_(APIServer *server, uint32_t now) {
|
||||
@@ -206,10 +202,6 @@ void OutgoingConnectionManager::poll_connect_(APIServer *server, uint32_t now) {
|
||||
this->schedule_retry_(now);
|
||||
return;
|
||||
}
|
||||
if (now - this->last_poll_ < CONNECT_POLL_INTERVAL_MS) {
|
||||
return;
|
||||
}
|
||||
this->last_poll_ = now;
|
||||
int err = 0;
|
||||
switch (socket::poll_connect(*this->dial_socket_, err)) {
|
||||
case socket::ConnectPollResult::CONNECT_POLL_RESULT_PENDING:
|
||||
|
||||
@@ -59,8 +59,6 @@ class OutgoingConnectionManager {
|
||||
static constexpr uint32_t BACKOFF_MIN_MS = 5000;
|
||||
static constexpr uint32_t BACKOFF_MAX_MS = 300000;
|
||||
static constexpr uint32_t CONNECT_TIMEOUT_MS = 10000;
|
||||
static constexpr uint32_t CONNECT_POLL_INTERVAL_MS = 250;
|
||||
static constexpr uint32_t NETWORK_RETRY_MS = 500;
|
||||
static constexpr uint32_t PRECONDITION_RETRY_MS = 5000;
|
||||
// A deep sleep wake window is too short to spend on the delay, so those
|
||||
// builds dial out as soon as the target is gone
|
||||
@@ -104,7 +102,6 @@ class OutgoingConnectionManager {
|
||||
uint32_t backoff_{BACKOFF_MIN_MS};
|
||||
uint32_t wait_{BOOT_WAIT_MS};
|
||||
uint32_t state_ts_{0};
|
||||
uint32_t last_poll_{0};
|
||||
|
||||
// Byte-aligned types last
|
||||
#ifndef API_OUTGOING_CONNECTION_HOST
|
||||
|
||||
@@ -65,6 +65,9 @@ void APIServer::socket_failed_(const LogString *msg) {
|
||||
}
|
||||
|
||||
void APIServer::setup() {
|
||||
#if defined(USE_API_HOMEASSISTANT_STATES) && defined(HOMEASSISTANT_STATE_SUBSCRIPTION_COUNT)
|
||||
this->state_subs_.reserve(HOMEASSISTANT_STATE_SUBSCRIPTION_COUNT);
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
// Always reserve the slot: flash preferences are positional on esp8266, so
|
||||
// a yaml key build must keep the layout of a runtime key build
|
||||
|
||||
@@ -208,6 +208,7 @@ WIZARD_SCHEMA = cv.All(
|
||||
WIZARD_INPUT_DOMAINS = (
|
||||
"binary_sensor",
|
||||
"button",
|
||||
"light",
|
||||
"number",
|
||||
"select",
|
||||
"sensor",
|
||||
@@ -216,7 +217,14 @@ WIZARD_INPUT_DOMAINS = (
|
||||
"text_sensor",
|
||||
)
|
||||
# Platforms that act on one family of Home Assistant domains, which the input's filters must stay within
|
||||
WIZARD_DOMAIN_LIMITED_PLATFORMS = ("button", "number", "select", "switch", "text")
|
||||
WIZARD_DOMAIN_LIMITED_PLATFORMS = (
|
||||
"button",
|
||||
"light",
|
||||
"number",
|
||||
"select",
|
||||
"switch",
|
||||
"text",
|
||||
)
|
||||
|
||||
|
||||
def wizard_input_ids(api_config: ConfigType) -> set[str]:
|
||||
|
||||
@@ -53,11 +53,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
pm_2_5_sensor = await cg.get_variable(config[CONF_PM_2_5])
|
||||
cg.add(var.set_pm_2_5_sensor(pm_2_5_sensor))
|
||||
|
||||
pm_10_0_sensor = await cg.get_variable(config[CONF_PM_10_0])
|
||||
cg.add(var.set_pm_10_0_sensor(pm_10_0_sensor))
|
||||
await cg.add_variable(var.set_pm_2_5_sensor, config[CONF_PM_2_5])
|
||||
await cg.add_variable(var.set_pm_10_0_sensor, config[CONF_PM_10_0])
|
||||
|
||||
cg.add(var.set_aqi_calculation_type(config[CONF_CALCULATION_TYPE]))
|
||||
cg.add(var.set_extended_range(config.get(CONF_EXTENDED_RANGE, False)))
|
||||
@@ -5,7 +5,6 @@ from esphome.const import (
|
||||
CONF_BATTERY_LEVEL,
|
||||
CONF_CO2,
|
||||
CONF_HUMIDITY,
|
||||
CONF_ID,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_PRESSURE,
|
||||
CONF_RADON,
|
||||
@@ -131,9 +130,9 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID], config[CONF_MAC_ADDRESS].as_hex)
|
||||
await cg.register_component(var, config)
|
||||
await ble_device_base.register_ble_device(var, config)
|
||||
var = await ble_device_base.new_ble_device_component(
|
||||
config, config[CONF_MAC_ADDRESS].as_hex
|
||||
)
|
||||
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_CO2, var.set_co2_sensor)
|
||||
|
||||
@@ -48,8 +48,7 @@ AS3935_SCHEMA = cv.Schema(
|
||||
async def setup_as3935(var: MockObj, config: ConfigType) -> None:
|
||||
await cg.register_component(var, config)
|
||||
|
||||
irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
|
||||
cg.add(var.set_irq_pin(irq_pin))
|
||||
await cg.add_pin(var.set_irq_pin, config[CONF_IRQ_PIN])
|
||||
cg.add(var.set_indoor(config[CONF_INDOOR]))
|
||||
cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))
|
||||
cg.add(var.set_watchdog_threshold(config[CONF_WATCHDOG_THRESHOLD]))
|
||||
|
||||
@@ -9,7 +9,6 @@ from esphome.const import (
|
||||
CONF_DIR_PIN,
|
||||
CONF_DIRECTION,
|
||||
CONF_HYSTERESIS,
|
||||
CONF_ID,
|
||||
CONF_POWER_MODE,
|
||||
CONF_RANGE,
|
||||
CONF_WATCHDOG,
|
||||
@@ -209,9 +208,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config)
|
||||
|
||||
cg.add(var.set_direction(config[CONF_DIRECTION]))
|
||||
cg.add(var.set_watchdog(config[CONF_WATCHDOG]))
|
||||
@@ -221,9 +218,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_fast_filter(config[CONF_FAST_FILTER]))
|
||||
cg.add(var.set_start_position(config[CONF_START_POSITION]))
|
||||
|
||||
if dir_pin_config := config.get(CONF_DIR_PIN):
|
||||
pin = await cg.gpio_pin_expression(dir_pin_config)
|
||||
cg.add(var.set_dir_pin(pin))
|
||||
await cg.add_pin(var.set_dir_pin, config.get(CONF_DIR_PIN))
|
||||
|
||||
if (end_position_config := config.get(CONF_END_POSITION, None)) is not None:
|
||||
cg.add(var.set_end_position(end_position_config))
|
||||
|
||||
@@ -23,7 +23,7 @@ static const uint8_t REGISTER_MAGNITUDE = 0x1B; // 16 bytes / R
|
||||
|
||||
void AS5600Component::setup() {
|
||||
if (!this->read_byte(REGISTER_STATUS).has_value()) {
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ void AS5600Component::setup() {
|
||||
|
||||
// write config to config register
|
||||
if (!this->write_byte_16(REGISTER_CONF, config)) {
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -87,10 +87,8 @@ void AS5600Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "AS5600:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
if (this->is_failed())
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Watchdog: %d\n"
|
||||
|
||||
@@ -30,10 +30,7 @@ CONF_END_POSITION = "end_position"
|
||||
CONF_OUT_OF_RANGE_MODE = "out_of_range_mode"
|
||||
|
||||
OutOfRangeMode = as5600_ns.enum("OutRangeMode")
|
||||
OUT_OF_RANGE_MODES = {
|
||||
"MIN_MAX": OutOfRangeMode.OUT_RANGE_MODE_MIN_MAX,
|
||||
"NAN": OutOfRangeMode.OUT_RANGE_MODE_NAN,
|
||||
}
|
||||
OUT_OF_RANGE_MODES = OutOfRangeMode.options("MIN_MAX", "NAN", prefix="OUT_RANGE_MODE_")
|
||||
|
||||
|
||||
CONF_AS5600_ID = "as5600_id"
|
||||
|
||||
@@ -9,19 +9,12 @@ ESPHOME_LOG_TAG(TAG, "as7341");
|
||||
void AS7341Component::setup() {
|
||||
LOG_I2C_DEVICE(this);
|
||||
|
||||
// Verify device ID
|
||||
uint8_t id;
|
||||
this->read_byte(AS7341_ID, &id);
|
||||
ESP_LOGCONFIG(TAG, " Read ID: 0x%X", id);
|
||||
if ((id & 0xFC) != (AS7341_CHIP_ID << 2)) {
|
||||
this->mark_failed();
|
||||
if (!this->check_chip_id(this, AS7341_ID, AS7341_CHIP_ID << 2, 0xFC))
|
||||
return;
|
||||
}
|
||||
|
||||
// Power on (enter IDLE state)
|
||||
if (!this->enable_power(true)) {
|
||||
ESP_LOGE(TAG, " Power on failed!");
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR("Power on failed"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -35,9 +28,6 @@ void AS7341Component::setup() {
|
||||
void AS7341Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "AS7341:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Gain: %u\n"
|
||||
@@ -105,16 +95,16 @@ uint8_t AS7341Component::get_atime() {
|
||||
}
|
||||
|
||||
uint16_t AS7341Component::get_astep() {
|
||||
uint16_t data;
|
||||
this->read_byte_16(AS7341_ASTEP, &data);
|
||||
return this->swap_bytes(data);
|
||||
uint16_t data = 0;
|
||||
this->read_byte_16_le(AS7341_ASTEP, &data);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool AS7341Component::setup_gain(AS7341Gain gain) { return this->write_byte(AS7341_CFG1, gain); }
|
||||
|
||||
bool AS7341Component::setup_atime(uint8_t atime) { return this->write_byte(AS7341_ATIME, atime); }
|
||||
|
||||
bool AS7341Component::setup_astep(uint16_t astep) { return this->write_byte_16(AS7341_ASTEP, swap_bytes(astep)); }
|
||||
bool AS7341Component::setup_astep(uint16_t astep) { return this->write_byte_16_le(AS7341_ASTEP, astep); }
|
||||
|
||||
bool AS7341Component::read_channels(uint16_t *data) {
|
||||
this->set_smux_low_channels(true);
|
||||
@@ -263,6 +253,4 @@ bool AS7341Component::clear_register_bit(uint8_t address, uint8_t bit_position)
|
||||
return this->write_byte(address, data);
|
||||
}
|
||||
|
||||
uint16_t AS7341Component::swap_bytes(uint16_t data) { return (data >> 8) | (data << 8); }
|
||||
|
||||
} // namespace esphome::as7341
|
||||
@@ -118,7 +118,6 @@ class AS7341Component final : public PollingComponent, public i2c::I2CDevice {
|
||||
bool write_register_bit(uint8_t address, bool value, uint8_t bit_position);
|
||||
bool set_register_bit(uint8_t address, uint8_t bit_position);
|
||||
bool clear_register_bit(uint8_t address, uint8_t bit_position);
|
||||
uint16_t swap_bytes(uint16_t data);
|
||||
|
||||
protected:
|
||||
sensor::Sensor *f1_{nullptr};
|
||||
|
||||
@@ -5,7 +5,6 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_CLEAR,
|
||||
CONF_GAIN,
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
ICON_BRIGHTNESS_5,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
@@ -74,7 +73,7 @@ CONFIG_SCHEMA = (
|
||||
cv.Optional(CONF_CLEAR): SENSOR_SCHEMA,
|
||||
cv.Optional(CONF_NIR): SENSOR_SCHEMA,
|
||||
cv.Optional(CONF_GAIN, default="X8"): cv.enum(GAIN_OPTIONS),
|
||||
cv.Optional(CONF_ATIME, default=29): cv.int_range(min=0, max=255),
|
||||
cv.Optional(CONF_ATIME, default=29): cv.uint8_t,
|
||||
cv.Optional(CONF_ASTEP, default=599): cv.int_range(min=0, max=65534),
|
||||
}
|
||||
)
|
||||
@@ -97,9 +96,7 @@ SENSORS = {
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config)
|
||||
|
||||
cg.add(var.set_gain(config[CONF_GAIN]))
|
||||
cg.add(var.set_atime(config[CONF_ATIME]))
|
||||
|
||||
@@ -5,7 +5,6 @@ from esphome.const import (
|
||||
CONF_CALIBRATION,
|
||||
CONF_GAIN,
|
||||
CONF_GLASS_ATTENUATION_FACTOR,
|
||||
CONF_ID,
|
||||
CONF_TYPE,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
@@ -55,12 +54,9 @@ GAINS_41 = {
|
||||
GAINS_43 = {**GAINS_41, "X1024": Gain.GAIN_1024X, "X2048": Gain.GAIN_2048X}
|
||||
|
||||
Normalization = as734x_ns.enum("Normalization", True)
|
||||
NORMALIZATIONS = {
|
||||
"NONE": Normalization.NORMALIZATION_NONE,
|
||||
"ALL": Normalization.NORMALIZATION_ALL,
|
||||
"BANDS": Normalization.NORMALIZATION_BANDS,
|
||||
"CLEAR": Normalization.NORMALIZATION_CLEAR,
|
||||
}
|
||||
NORMALIZATIONS = Normalization.options(
|
||||
"NONE", "ALL", "BANDS", "CLEAR", prefix="NORMALIZATION_"
|
||||
)
|
||||
|
||||
# Per-channel correction for the golden device, measured by the manufacturer.
|
||||
DEFAULT_CHANNEL_CORRECTION_43 = [
|
||||
@@ -131,7 +127,7 @@ def _schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(AS734XComponent),
|
||||
cv.Optional(CONF_GAIN, default="X8"): cv.enum(gains),
|
||||
cv.Optional(CONF_ATIME, default=29): cv.int_range(min=0, max=255),
|
||||
cv.Optional(CONF_ATIME, default=29): cv.uint8_t,
|
||||
cv.Optional(CONF_ASTEP, default=599): cv.int_range(min=0, max=65534),
|
||||
cv.Optional(CONF_CALIBRATION, default={}): _calibration_schema(bands),
|
||||
cv.Optional(CONF_NORMALIZE_BASIC_COUNTS, default="NONE"): cv.enum(
|
||||
@@ -169,9 +165,7 @@ def _per_band(
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
chip = config[CONF_TYPE]
|
||||
cg.add_define("USE_AS7341" if chip == "AS7341" else "USE_AS7343")
|
||||
var = cg.new_Pvariable(config[CONF_ID], cg.RawExpression(f"&as734x::{chip}_CHIP"))
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config, cg.RawExpression(f"&as734x::{chip}_CHIP"))
|
||||
cg.add(var.set_gain(config[CONF_GAIN]))
|
||||
cg.add(var.set_atime(config[CONF_ATIME]))
|
||||
cg.add(var.set_astep(config[CONF_ASTEP]))
|
||||
|
||||
@@ -277,7 +277,7 @@ bool AS734XComponent::update_bits_(uint8_t reg, uint8_t mask, bool value) {
|
||||
bool AS734XComponent::write_exposure_() {
|
||||
const ChipInfo *chip = this->chip_;
|
||||
if (!this->select_bank_(REG_ATIME) || !this->write_byte(REG_ATIME, this->atime_) ||
|
||||
!this->write_byte_16(pgm(chip->astep_reg), byteswap(this->astep_)) ||
|
||||
!this->write_byte_16_le(pgm(chip->astep_reg), this->astep_) ||
|
||||
!this->write_byte(pgm(chip->cfg1_reg), this->gain_)) {
|
||||
return false;
|
||||
}
|
||||
@@ -355,9 +355,6 @@ void AS734XComponent::dump_config() {
|
||||
#ifdef USE_AS734X_RGB
|
||||
LOG_TEXT_SENSOR(" ", "RGB hex", this->rgb_hex_sensor_);
|
||||
#endif
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
void AS734XComponent::enable_led(bool enable) {
|
||||
@@ -462,7 +459,7 @@ void AS734XComponent::read_step_() {
|
||||
if (frame[0] & ASTATUS_ASAT) {
|
||||
ESP_LOGV(TAG, "Step %u saturated", this->step_);
|
||||
}
|
||||
auto slot_value = [&frame](uint8_t slot) { return encode_uint16(frame[2 + 2 * slot], frame[1 + 2 * slot]); };
|
||||
auto slot_value = [&frame](uint8_t slot) { return encode_value_le<uint16_t>(&frame[1 + 2 * slot]); };
|
||||
const uint8_t *readout = pgm(this->chip_->readout) + this->step_ * NUM_BANDS;
|
||||
const uint8_t extra_clear = pgm(this->chip_->extra_clear_slot);
|
||||
for (uint8_t band = 0; band < NUM_BANDS; band++) {
|
||||
|
||||
@@ -108,8 +108,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> ConfigType:
|
||||
full = fv.full_config.get()
|
||||
hub = full.get_config_for_path(full.get_path_for_id(config[CONF_AS734X_ID])[:-1])
|
||||
hub = fv.get_declaring_config(config[CONF_AS734X_ID])
|
||||
if hub[CONF_TYPE] != "AS7341":
|
||||
return config
|
||||
for key in _BAND_KEYS:
|
||||
|
||||
@@ -73,9 +73,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
await i2c.new_i2c_component(config)
|
||||
|
||||
|
||||
# Actions
|
||||
@@ -93,7 +91,7 @@ def _megahertz(value: float) -> int:
|
||||
|
||||
|
||||
def _microamps(value: float) -> int:
|
||||
return int(value * 1000000)
|
||||
return round(value * 1000000)
|
||||
|
||||
|
||||
RADAR_SETTINGS_SCHEMA = cv.Schema(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "atc_mithermometer.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::atc_mithermometer {
|
||||
@@ -89,7 +90,7 @@ bool ATCMiThermometer::parse_message_(const std::vector<uint8_t> &message, Parse
|
||||
}
|
||||
|
||||
// temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C
|
||||
const int16_t temperature = uint16_t(data[7]) | (uint16_t(data[6]) << 8);
|
||||
const int16_t temperature = encode_value<int16_t>(&data[6]);
|
||||
result.temperature = temperature / 10.0f;
|
||||
|
||||
// humidity, 1 byte, 8-bit unsigned integer, 1.0 %
|
||||
@@ -99,7 +100,7 @@ bool ATCMiThermometer::parse_message_(const std::vector<uint8_t> &message, Parse
|
||||
result.battery_level = data[9];
|
||||
|
||||
// battery, 2 bytes, 16-bit unsigned integer, 0.001 V
|
||||
const int16_t battery_voltage = uint16_t(data[11]) | (uint16_t(data[10]) << 8);
|
||||
const int16_t battery_voltage = encode_value<int16_t>(&data[10]);
|
||||
result.battery_voltage = battery_voltage / 1.0e3f;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -5,7 +5,6 @@ from esphome.const import (
|
||||
CONF_BATTERY_LEVEL,
|
||||
CONF_BATTERY_VOLTAGE,
|
||||
CONF_HUMIDITY,
|
||||
CONF_ID,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_SIGNAL_STRENGTH,
|
||||
CONF_TEMPERATURE,
|
||||
@@ -79,9 +78,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await ble_device_base.register_ble_device(var, config)
|
||||
var = await ble_device_base.new_ble_device_component(config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ void ATM90E26Component::setup() {
|
||||
uint16_t read = this->read16_(ATM90E26_REGISTER_LASTDATA);
|
||||
if (read != 0x0030) {
|
||||
ESP_LOGW(TAG, "Could not initialize ATM90E26 IC, check SPI settings: %d", read);
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
// TODO: 100 * <nominal voltage, e.g. 230> * sqrt(2) * <fraction of nominal, e.g. 0.9> / (4 * gain_voltage/32768)
|
||||
@@ -120,21 +120,18 @@ void ATM90E26Component::setup() {
|
||||
|
||||
ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS1 was incorrect, expected: 0x%04X",
|
||||
this->read16_(ATM90E26_REGISTER_CS1));
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR("Checksum mismatch"));
|
||||
}
|
||||
if (sys_status & 0x3000) { // Checksum 2 Error
|
||||
ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS2 was incorrect, expected: 0x%04X",
|
||||
this->read16_(ATM90E26_REGISTER_CS2));
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR("Checksum mismatch"));
|
||||
}
|
||||
}
|
||||
|
||||
void ATM90E26Component::dump_config() {
|
||||
ESP_LOGCONFIG("", "ATM90E26:");
|
||||
LOG_PIN(" CS Pin: ", this->cs_);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_SENSOR(" ", "Voltage A", this->voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current A", this->current_sensor_);
|
||||
|
||||
@@ -5,7 +5,6 @@ from esphome.const import (
|
||||
CONF_CURRENT,
|
||||
CONF_FORWARD_ACTIVE_ENERGY,
|
||||
CONF_FREQUENCY,
|
||||
CONF_ID,
|
||||
CONF_LINE_FREQUENCY,
|
||||
CONF_POWER,
|
||||
CONF_POWER_FACTOR,
|
||||
@@ -125,9 +124,7 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
var = await spi.new_spi_component(config)
|
||||
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_VOLTAGE, var.set_voltage_sensor)
|
||||
|
||||
@@ -117,12 +117,9 @@ void ATM90E32Component::loop() {
|
||||
}
|
||||
|
||||
void ATM90E32Component::update() {
|
||||
if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) {
|
||||
this->status_set_warning();
|
||||
if (!this->status_comm_result(this->read16_(ATM90E32_REGISTER_METEREN) == 1, nullptr))
|
||||
return;
|
||||
}
|
||||
this->set_publish_interval_flag_(true);
|
||||
this->status_clear_warning();
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
this->check_phase_status();
|
||||
@@ -168,7 +165,7 @@ void ATM90E32Component::setup() {
|
||||
this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA); // enable register config access
|
||||
if (!this->validate_spi_read_(0x55AA, "setup()")) {
|
||||
ESP_LOGW(TAG, "Could not initialize ATM90E32 IC, check SPI settings");
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -411,9 +408,6 @@ void ATM90E32Component::log_calibration_status_() {
|
||||
void ATM90E32Component::dump_config() {
|
||||
ESP_LOGCONFIG("", "ATM90E32:");
|
||||
LOG_PIN(" CS Pin: ", this->cs_);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_SENSOR(" ", "Voltage A", this->phase_[PHASEA].voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current A", this->phase_[PHASEA].current_sensor_);
|
||||
|
||||
@@ -27,13 +27,7 @@ audio_ns = cg.esphome_ns.namespace("audio")
|
||||
|
||||
AudioFile = audio_ns.struct("AudioFile")
|
||||
AudioFileType = audio_ns.enum("AudioFileType", is_class=True)
|
||||
AUDIO_FILE_TYPE_ENUM = {
|
||||
"NONE": AudioFileType.NONE,
|
||||
"WAV": AudioFileType.WAV,
|
||||
"MP3": AudioFileType.MP3,
|
||||
"FLAC": AudioFileType.FLAC,
|
||||
"OPUS": AudioFileType.OPUS,
|
||||
}
|
||||
AUDIO_FILE_TYPE_ENUM = AudioFileType.options("NONE", "WAV", "MP3", "FLAC", "OPUS")
|
||||
|
||||
MEMORY_PSRAM = "psram"
|
||||
MEMORY_INTERNAL = "internal"
|
||||
|
||||
@@ -23,14 +23,15 @@ AudioStreamInfo::AudioStreamInfo(uint8_t bits_per_sample, uint8_t channels, uint
|
||||
}
|
||||
|
||||
uint32_t AudioStreamInfo::frames_to_microseconds(uint32_t frames) const {
|
||||
return (frames * 1000000 + (this->sample_rate_ >> 1)) / this->sample_rate_;
|
||||
// In 64 bits: frames * 1000000 overflows 32 bits above 4294 frames, about 90 ms at 48 kHz.
|
||||
return (static_cast<uint64_t>(frames) * 1000000 + (this->sample_rate_ >> 1)) / this->sample_rate_;
|
||||
}
|
||||
|
||||
uint32_t AudioStreamInfo::frames_to_milliseconds_with_remainder(uint32_t *total_frames) const {
|
||||
uint32_t unprocessable_frames = *total_frames % (this->sample_rate_ / this->ms_sample_rate_gcd_);
|
||||
uint32_t frames_for_ms_calculation = *total_frames - unprocessable_frames;
|
||||
|
||||
uint32_t playback_ms = (frames_for_ms_calculation * 1000) / this->sample_rate_;
|
||||
uint32_t playback_ms = (static_cast<uint64_t>(frames_for_ms_calculation) * 1000) / this->sample_rate_;
|
||||
*total_frames = unprocessable_frames;
|
||||
return playback_ms;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class AudioStreamInfo {
|
||||
/// @return Duration in milliseconds that will store `bytes` bytes of audio. May round down for certain sample rates
|
||||
/// or values of `bytes`.
|
||||
uint32_t bytes_to_ms(size_t bytes) const {
|
||||
return bytes * 1000 / (this->sample_rate_ * this->bytes_per_sample_ * this->channels_);
|
||||
return static_cast<uint64_t>(bytes) * 1000 / (this->sample_rate_ * this->bytes_per_sample_ * this->channels_);
|
||||
}
|
||||
|
||||
/// @brief Convert bytes to frames.
|
||||
@@ -60,18 +60,20 @@ class AudioStreamInfo {
|
||||
/// @brief Converts duration to frames.
|
||||
/// @param ms Duration in milliseconds
|
||||
/// @return Audio frames that will store `ms` milliseconds of audio. May round down for certain sample rates.
|
||||
uint32_t ms_to_frames(uint32_t ms) const { return (ms * this->sample_rate_) / 1000; }
|
||||
uint32_t ms_to_frames(uint32_t ms) const { return (static_cast<uint64_t>(ms) * this->sample_rate_) / 1000; }
|
||||
|
||||
/// @brief Converts duration to samples.
|
||||
/// @param ms Duration in milliseconds
|
||||
/// @return Audio samples that will store `ms` milliseconds of audio. May round down for certain sample rates.
|
||||
uint32_t ms_to_samples(uint32_t ms) const { return (ms * this->channels_ * this->sample_rate_) / 1000; }
|
||||
uint32_t ms_to_samples(uint32_t ms) const {
|
||||
return (static_cast<uint64_t>(ms) * this->channels_ * this->sample_rate_) / 1000;
|
||||
}
|
||||
|
||||
/// @brief Converts duration to bytes. May round down for certain sample rates.
|
||||
/// @param ms Duration in milliseconds
|
||||
/// @return Bytes that will store `ms` milliseconds of audio. May round down for certain sample rates.
|
||||
size_t ms_to_bytes(uint32_t ms) const {
|
||||
return (ms * this->bytes_per_sample_ * this->channels_ * this->sample_rate_) / 1000;
|
||||
return (static_cast<uint64_t>(ms) * this->bytes_per_sample_ * this->channels_ * this->sample_rate_) / 1000;
|
||||
}
|
||||
|
||||
/// @brief Computes the duration, in microseconds, the given amount of frames represents.
|
||||
|
||||
@@ -31,7 +31,5 @@ async def to_code(config: ConfigType) -> None:
|
||||
await touchscreen.register_touchscreen(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if interrupt_pin := config.get(CONF_INTERRUPT_PIN):
|
||||
cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin)))
|
||||
if reset_pin := config.get(CONF_RESET_PIN):
|
||||
cg.add(var.set_reset_pin(await cg.gpio_pin_expression(reset_pin)))
|
||||
await cg.add_pin(var.set_interrupt_pin, config.get(CONF_INTERRUPT_PIN))
|
||||
await cg.add_pin(var.set_reset_pin, config.get(CONF_RESET_PIN))
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "b_parasite.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::b_parasite {
|
||||
@@ -62,29 +63,29 @@ bool BParasite::parse_device(const ble_device_base::ESPBTDevice &device) {
|
||||
}
|
||||
|
||||
// Battery voltage in millivolts.
|
||||
uint16_t battery_millivolt = data[2] << 8 | data[3];
|
||||
uint16_t battery_millivolt = encode_value<uint16_t>(&data[2]);
|
||||
float battery_voltage = battery_millivolt / 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];
|
||||
uint16_t temp_millicelsius = encode_value<uint16_t>(&data[4]);
|
||||
temp_celsius = temp_millicelsius / 1000.0f;
|
||||
} else {
|
||||
int16_t temp_centicelsius = data[4] << 8 | data[5];
|
||||
int16_t temp_centicelsius = encode_value<int16_t>(&data[4]);
|
||||
temp_celsius = temp_centicelsius / 100.0f;
|
||||
}
|
||||
|
||||
// Relative air humidity in the range [0, 2^16).
|
||||
uint16_t humidity = data[6] << 8 | data[7];
|
||||
uint16_t humidity = encode_value<uint16_t>(&data[6]);
|
||||
float humidity_percent = (100.0f * humidity) / (1 << 16);
|
||||
|
||||
// Relative soil moisture in [0 - 2^16).
|
||||
uint16_t soil_moisture = data[8] << 8 | data[9];
|
||||
uint16_t soil_moisture = encode_value<uint16_t>(&data[8]);
|
||||
float moisture_percent = (100.0f * soil_moisture) / (1 << 16);
|
||||
|
||||
// Ambient light in lux.
|
||||
float illuminance = has_illuminance ? data[16] << 8 | data[17] : 0.0f;
|
||||
float illuminance = has_illuminance ? encode_value<uint16_t>(&data[16]) : 0.0f;
|
||||
|
||||
if (battery_voltage_ != nullptr) {
|
||||
battery_voltage_->publish_state(battery_voltage);
|
||||
|
||||
@@ -4,7 +4,6 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BATTERY_VOLTAGE,
|
||||
CONF_HUMIDITY,
|
||||
CONF_ID,
|
||||
CONF_ILLUMINANCE,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_MOISTURE,
|
||||
@@ -76,9 +75,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await ble_device_base.register_ble_device(var, config)
|
||||
var = await ble_device_base.new_ble_device_component(config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
|
||||
@@ -46,12 +46,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
var = await climate.new_climate(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
sens = await cg.get_variable(config[CONF_SENSOR])
|
||||
cg.add(var.set_sensor(sens))
|
||||
|
||||
if CONF_HUMIDITY_SENSOR in config:
|
||||
sens = await cg.get_variable(config[CONF_HUMIDITY_SENSOR])
|
||||
cg.add(var.set_humidity_sensor(sens))
|
||||
await cg.add_variable(var.set_sensor, config[CONF_SENSOR])
|
||||
await cg.add_variable(var.set_humidity_sensor, config.get(CONF_HUMIDITY_SENSOR))
|
||||
|
||||
normal_config = BangBangClimateTargetTempConfig(
|
||||
config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import ble_client, time
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_RECEIVE_TIMEOUT, CONF_TIME_ID
|
||||
from esphome.const import CONF_RECEIVE_TIMEOUT, CONF_TIME_ID
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -41,11 +41,7 @@ async def register_bedjet_child(var: MockObj, config: ConfigType) -> None:
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
if time_id := config.get(CONF_TIME_ID):
|
||||
time_ = await cg.get_variable(time_id)
|
||||
cg.add(var.set_time_id(time_))
|
||||
var = await ble_client.new_ble_node_component(config)
|
||||
await cg.add_variable(var.set_time_id, config.get(CONF_TIME_ID))
|
||||
if (receive_timeout := config.get(CONF_RECEIVE_TIMEOUT)) is not None:
|
||||
cg.add(var.set_status_timeout(receive_timeout))
|
||||
@@ -12,14 +12,10 @@ DEPENDENCIES = ["bedjet"]
|
||||
BedJetClimate = bedjet_ns.class_("BedJetClimate", climate.Climate, cg.PollingComponent)
|
||||
BedjetHeatMode = bedjet_ns.enum("BedjetHeatMode")
|
||||
BedjetTemperatureSource = bedjet_ns.enum("BedjetTemperatureSource")
|
||||
BEDJET_HEAT_MODES = {
|
||||
"heat": BedjetHeatMode.HEAT_MODE_HEAT,
|
||||
"extended": BedjetHeatMode.HEAT_MODE_EXTENDED,
|
||||
}
|
||||
BEDJET_TEMPERATURE_SOURCES = {
|
||||
"outlet": BedjetTemperatureSource.TEMPERATURE_SOURCE_OUTLET,
|
||||
"ambient": BedjetTemperatureSource.TEMPERATURE_SOURCE_AMBIENT,
|
||||
}
|
||||
BEDJET_HEAT_MODES = BedjetHeatMode.options("heat", "extended", prefix="HEAT_MODE_")
|
||||
BEDJET_TEMPERATURE_SOURCES = BedjetTemperatureSource.options(
|
||||
"outlet", "ambient", prefix="TEMPERATURE_SOURCE_"
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
climate.climate_schema(BedJetClimate)
|
||||
|
||||
@@ -6,7 +6,6 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_GAIN,
|
||||
CONF_GLASS_ATTENUATION_FACTOR,
|
||||
CONF_ID,
|
||||
CONF_INTEGRATION_TIME,
|
||||
)
|
||||
from esphome.types import ConfigType
|
||||
@@ -25,11 +24,7 @@ BH1745Component = bh1745_ns.class_(
|
||||
)
|
||||
|
||||
AdcGain = bh1745_ns.enum("AdcGain", is_class=True)
|
||||
ADC_GAINS = {
|
||||
"1X": AdcGain.ADC_GAIN_1X,
|
||||
"2X": AdcGain.ADC_GAIN_2X,
|
||||
"16X": AdcGain.ADC_GAIN_16X,
|
||||
}
|
||||
ADC_GAINS = AdcGain.options("1X", "2X", "16X", prefix="ADC_GAIN_")
|
||||
|
||||
MeasurementTime = bh1745_ns.enum("MeasurementTime", is_class=True)
|
||||
MEASUREMENT_TIMES = {
|
||||
@@ -66,9 +61,7 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
var = await i2c.new_i2c_component(config)
|
||||
|
||||
cg.add(var.set_adc_gain(config[CONF_GAIN]))
|
||||
cg.add(var.set_measurement_time(config[CONF_INTEGRATION_TIME]))
|
||||
|
||||
@@ -63,15 +63,8 @@ static float calculate_cct(uint16_t red, uint16_t green, uint16_t blue, bool low
|
||||
}
|
||||
|
||||
void BH1745Component::setup() {
|
||||
uint8_t manufacturer_id;
|
||||
if (!this->read_byte(REG_MANUFACTURER_ID, &manufacturer_id)) {
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
if (!this->check_chip_id(this, REG_MANUFACTURER_ID, MANUFACTURER_ID))
|
||||
return;
|
||||
}
|
||||
if (manufacturer_id != MANUFACTURER_ID) {
|
||||
this->mark_failed(LOG_STR("Wrong manufacturer ID"));
|
||||
return;
|
||||
}
|
||||
uint8_t system_control;
|
||||
if (!this->read_byte(REG_SYSTEM_CONTROL, &system_control) ||
|
||||
(system_control & SYSTEM_CONTROL_PART_ID_MASK) != PART_ID) {
|
||||
@@ -140,18 +133,17 @@ void BH1745Component::update() {
|
||||
if ((mode_control2 & MODE_CONTROL2_VALID) == 0)
|
||||
return;
|
||||
|
||||
uint8_t buffer[BH1745_CHANNELS * 2];
|
||||
if (!this->read_bytes(REG_RED_DATA_LSB, buffer, sizeof(buffer))) {
|
||||
uint16_t buffer[BH1745_CHANNELS];
|
||||
if (!this->read_bytes_16_le(REG_RED_DATA_LSB, buffer, BH1745_CHANNELS)) {
|
||||
this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
this->status_clear_warning();
|
||||
|
||||
// Each channel is sent low byte first.
|
||||
const uint16_t red = encode_uint16(buffer[1], buffer[0]);
|
||||
const uint16_t green = encode_uint16(buffer[3], buffer[2]);
|
||||
const uint16_t blue = encode_uint16(buffer[5], buffer[4]);
|
||||
const uint16_t clear = encode_uint16(buffer[7], buffer[6]);
|
||||
const uint16_t red = buffer[0];
|
||||
const uint16_t green = buffer[1];
|
||||
const uint16_t blue = buffer[2];
|
||||
const uint16_t clear = buffer[3];
|
||||
ESP_LOGV(TAG, "Red: %u, Green: %u, Blue: %u, Clear: %u", red, green, blue, clear);
|
||||
|
||||
if (this->red_counts_sensor_ != nullptr)
|
||||
|
||||
@@ -65,7 +65,7 @@ MTreg:
|
||||
void BH1750Sensor::setup() {
|
||||
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
|
||||
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
|
||||
this->mark_failed();
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
this->state_ = IDLE;
|
||||
@@ -74,9 +74,6 @@ void BH1750Sensor::setup() {
|
||||
void BH1750Sensor::dump_config() {
|
||||
LOG_SENSOR("", "BH1750", this);
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, this->get_name().c_str());
|
||||
}
|
||||
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "bh1900nux.h"
|
||||
|
||||
@@ -36,7 +37,7 @@ void BH1900NUXSensor::update() {
|
||||
|
||||
// Combined raw value, unsigned and unaligned 16 bit
|
||||
// Temperature is represented in just 12 bits, shift needed
|
||||
int16_t raw_temperature_register_value = encode_uint16(temperature_raw[0], temperature_raw[1]);
|
||||
int16_t raw_temperature_register_value = encode_value<int16_t>(temperature_raw);
|
||||
raw_temperature_register_value >>= 4;
|
||||
float temperature_value = raw_temperature_register_value * SENSOR_RESOLUTION; // Apply sensor resolution
|
||||
|
||||
|
||||
@@ -25,13 +25,6 @@ async def to_code(config: ConfigType) -> None:
|
||||
var = await fan.new_fan(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||
cg.add(var.set_output(output_))
|
||||
|
||||
if oscillation_output_id := config.get(CONF_OSCILLATION_OUTPUT):
|
||||
oscillation_output = await cg.get_variable(oscillation_output_id)
|
||||
cg.add(var.set_oscillating(oscillation_output))
|
||||
|
||||
if direction_output_id := config.get(CONF_DIRECTION_OUTPUT):
|
||||
direction_output = await cg.get_variable(direction_output_id)
|
||||
cg.add(var.set_direction(direction_output))
|
||||
await cg.add_variable(var.set_output, config[CONF_OUTPUT])
|
||||
await cg.add_variable(var.set_oscillating, config.get(CONF_OSCILLATION_OUTPUT))
|
||||
await cg.add_variable(var.set_direction, config.get(CONF_DIRECTION_OUTPUT))
|
||||
@@ -20,5 +20,4 @@ async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
||||
await light.register_light(var, config)
|
||||
|
||||
out = await cg.get_variable(config[CONF_OUTPUT])
|
||||
cg.add(var.set_output(out))
|
||||
await cg.add_variable(var.set_output, config[CONF_OUTPUT])
|
||||
@@ -144,8 +144,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
parent = await cg.get_variable(config[CONF_BK72XX_BLE_ID])
|
||||
cg.add(var.set_parent(parent))
|
||||
await cg.register_parented(var, config[CONF_BK72XX_BLE_ID])
|
||||
# The tracker registers itself as a controller scan listener in setup();
|
||||
# request the codegen-sized StaticVector slot for it.
|
||||
bk72xx_ble.request_scan_listener_slot()
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#include "bl0906.h"
|
||||
#include "constants.h"
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::bl0906 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "bl0906");
|
||||
|
||||
constexpr uint32_t to_uint32_t(ube24_t input) { return input.h << 16 | input.m << 8 | input.l; }
|
||||
constexpr uint32_t to_uint32_t(ube24_t input) { return encode_uint24(input.h, input.m, input.l); }
|
||||
|
||||
constexpr int32_t to_int32_t(sbe24_t input) {
|
||||
return static_cast<int32_t>(encode_uint32((uint8_t) input.h, input.m, input.l, 0)) >> 8;
|
||||
@@ -19,8 +20,7 @@ constexpr uint8_t bl0906_checksum(const uint8_t address, const DataPacket *data)
|
||||
}
|
||||
|
||||
void BL0906::loop() {
|
||||
while (this->available())
|
||||
this->flush();
|
||||
this->discard_available();
|
||||
|
||||
if (this->current_stage_ == STAGE_IDLE) {
|
||||
// Woken up between cycles to drain the action queue. Go back to sleep.
|
||||
@@ -91,8 +91,7 @@ void BL0906::advance_stage_() {
|
||||
}
|
||||
|
||||
void BL0906::setup() {
|
||||
while (this->available())
|
||||
this->flush();
|
||||
this->discard_available();
|
||||
this->write_array(USR_WRPROT_WITABLE, sizeof(USR_WRPROT_WITABLE));
|
||||
// Calibration (1: register address; 2: value before calibration; 3: value after calibration)
|
||||
this->bias_correction_(BL0906_RMSOS_1, 0.01600, 0); // Calibration current_1
|
||||
@@ -133,9 +132,7 @@ void BL0906::handle_actions_() {
|
||||
}
|
||||
}
|
||||
|
||||
while (this->available()) {
|
||||
this->read();
|
||||
}
|
||||
this->discard_available();
|
||||
|
||||
this->action_queue_.clear();
|
||||
}
|
||||
|
||||
@@ -147,9 +147,7 @@ automation.register_apply_action(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
var = await uart.new_uart_component(config)
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_FREQUENCY, var.set_frequency_sensor)
|
||||
await sensors(CONF_TEMPERATURE, var.set_temperature_sensor)
|
||||
|
||||
Loaded 100 of 1506 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user