mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 10:12:52 +00:00
Merge branch 'esp8266-native-pch' into esp32-idf-pch
This commit is contained in:
@@ -429,6 +429,7 @@ esphome/components/psram/* @esphome/core
|
||||
esphome/components/pulse_meter/* @cstaahl @stevebaxter @TrentHouliston
|
||||
esphome/components/pvvx_mithermometer/* @pasiz
|
||||
esphome/components/pylontech/* @functionpointer
|
||||
esphome/components/pzem6l24/* @nuttytree
|
||||
esphome/components/qmi8658/* @clydebarrow
|
||||
esphome/components/qmp6988/* @andrewpc
|
||||
esphome/components/qr_code/* @wjtje
|
||||
@@ -548,6 +549,7 @@ esphome/components/sx126x/* @swoboda1337
|
||||
esphome/components/sx127x/* @swoboda1337
|
||||
esphome/components/sy6970/* @linkedupbits
|
||||
esphome/components/syslog/* @clydebarrow
|
||||
esphome/components/systa_bus/* @Mat931
|
||||
esphome/components/t6615/* @tylermenezes
|
||||
esphome/components/tas2780/* @remcom
|
||||
esphome/components/tc74/* @sethgirvan
|
||||
@@ -562,6 +564,7 @@ esphome/components/template/datetime/* @rfdarter
|
||||
esphome/components/template/event/* @nohat
|
||||
esphome/components/template/fan/* @ssieb
|
||||
esphome/components/text/* @mauritskorse
|
||||
esphome/components/tfluna/* @candrews
|
||||
esphome/components/thermopro_ble/* @sittner
|
||||
esphome/components/thermostat/* @kbx81
|
||||
esphome/components/time/* @esphome/core
|
||||
|
||||
+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.15.0
|
||||
RUN uv pip install --no-cache-dir esphome-device-builder==1.16.0
|
||||
|
||||
RUN \
|
||||
platformio settings set enable_telemetry No \
|
||||
|
||||
@@ -68,12 +68,16 @@ def idedata_candidates(build_path: Path) -> list[Path]:
|
||||
The candidate idedata JSON paths, most specific first
|
||||
"""
|
||||
name = build_path.name
|
||||
data_dir = build_path.parent.parent / "idedata"
|
||||
# Native backends suffix the cache by toolchain (<name>.arduino.json)
|
||||
suffixed = sorted(data_dir.glob(f"{name}.*.json")) if data_dir.is_dir() else []
|
||||
return [
|
||||
# In .pioenvs for test builds
|
||||
build_path / ".pioenvs" / name / "idedata.json",
|
||||
# Both toolchains cache it in the data dir, which holds this build dir:
|
||||
# <data_dir>/idedata/<name>.json next to <data_dir>/build/<name>
|
||||
build_path.parent.parent / "idedata" / f"{name}.json",
|
||||
*suffixed,
|
||||
# Regular builds, invoked from the config dir or from anywhere
|
||||
Path.cwd() / ".esphome" / "idedata" / f"{name}.json",
|
||||
Path.home() / ".esphome" / "idedata" / f"{name}.json",
|
||||
|
||||
@@ -6,9 +6,8 @@ import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
from typing import Any
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from esphome.arduino8266 import framework
|
||||
from esphome.build_helpers.ccache import resolve_ccache_path
|
||||
from esphome.const import (
|
||||
CONF_COMPILE_PROCESS_LIMIT,
|
||||
@@ -17,9 +16,12 @@ from esphome.const import (
|
||||
KEY_FRAMEWORK_VERSION,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError
|
||||
from esphome.helpers import write_file_if_changed
|
||||
from esphome.helpers import write_file
|
||||
from esphome.types import ConfigType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from esphome.arduino8266.framework import InstalledPaths
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# ESP8266 user RAM (matches upload.maximum_ram_size in every board manifest)
|
||||
@@ -53,6 +55,11 @@ def get_elf_path() -> Path:
|
||||
|
||||
|
||||
def _toolchain_tool(name: str) -> Path:
|
||||
# Imported here, not at module scope: the serial upload/logs fast path
|
||||
# resolves this module for its artifact paths alone, and framework
|
||||
# pulls in the whole package-download stack
|
||||
from esphome.arduino8266 import framework
|
||||
|
||||
return framework.toolchain_tool(framework.get_toolchain_path(), name)
|
||||
|
||||
|
||||
@@ -75,32 +82,23 @@ def get_readelf_path() -> Path:
|
||||
|
||||
|
||||
def run_compile(config: ConfigType, verbose: bool) -> int:
|
||||
from esphome.arduino8266 import framework
|
||||
from esphome.build_gen import arduino8266 as build_gen
|
||||
|
||||
_warn_ignored_platformio_options()
|
||||
paths = framework.check_and_install(CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION])
|
||||
# Resolved once: the probe is not free and three consumers need it
|
||||
ccache = resolve_ccache_path()
|
||||
ninja_changed = build_gen.write_project(paths, ccache)
|
||||
build_gen.write_project(paths, ccache)
|
||||
|
||||
build_dir = get_build_dir()
|
||||
env = framework.get_build_env(paths.toolchain, ccache)
|
||||
|
||||
# Regenerate the compile DB when it is older than build.ninja.
|
||||
# Freshness rides a stamp: the DB is written via write_file_if_changed
|
||||
# (mtime feeds get_idedata's cache), so identical content would
|
||||
# otherwise stay "stale" forever
|
||||
# The DB is a pure function of build.ninja; regenerate when it is older
|
||||
compdb = build_dir / "compile_commands.json"
|
||||
compdb_stamp = build_dir / ".compile_commands.stamp"
|
||||
ninja_file = build_dir / "build.ninja"
|
||||
if (
|
||||
ninja_changed
|
||||
or not compdb.is_file()
|
||||
or not compdb_stamp.is_file()
|
||||
or compdb_stamp.stat().st_mtime < ninja_file.stat().st_mtime
|
||||
):
|
||||
if not compdb.is_file() or compdb.stat().st_mtime < ninja_file.stat().st_mtime:
|
||||
_write_compile_commands(paths.ninja, build_dir, env)
|
||||
compdb_stamp.touch()
|
||||
|
||||
cmd = [str(paths.ninja)]
|
||||
if verbose:
|
||||
@@ -112,38 +110,13 @@ def run_compile(config: ConfigType, verbose: bool) -> int:
|
||||
targets = ["firmware.factory.bin", "firmware.ota.bin"]
|
||||
cmd += targets
|
||||
|
||||
# The dry-run probe keeps a no-op rebuild quiet; a rewritten manifest
|
||||
# all but guarantees work, so skip it then. cwd (not -C) drops the
|
||||
# "Entering directory" banner.
|
||||
skip_build = False
|
||||
if not ninja_changed:
|
||||
probe = subprocess.run(
|
||||
[str(paths.ninja), "-n", *targets],
|
||||
cwd=build_dir,
|
||||
env=env,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
close_fds=False,
|
||||
)
|
||||
if probe.stderr.strip():
|
||||
# A load-time diagnostic flags a generator bug; the skip branch
|
||||
# would otherwise swallow it forever
|
||||
_LOGGER.warning("ninja: %s", probe.stderr.strip())
|
||||
if probe.returncode != 0:
|
||||
# An unknown target here is the defective-manifest case; fall
|
||||
# through to the real build so the error prints attributably
|
||||
_LOGGER.debug("ninja probe failed; running the full build")
|
||||
skip_build = probe.returncode == 0 and "no work to do" in probe.stdout
|
||||
if skip_build:
|
||||
_LOGGER.debug("ninja: nothing to rebuild")
|
||||
else:
|
||||
_LOGGER.debug("Running: %s", " ".join(cmd))
|
||||
rc = subprocess.run(
|
||||
cmd, cwd=build_dir, env=env, check=False, close_fds=False
|
||||
).returncode
|
||||
if rc != 0:
|
||||
return rc
|
||||
# cwd, not -C: drops ninja's "Entering directory" banner
|
||||
_LOGGER.debug("Running: %s", " ".join(cmd))
|
||||
rc = subprocess.run(
|
||||
cmd, cwd=build_dir, env=env, check=False, close_fds=False
|
||||
).returncode
|
||||
if rc != 0:
|
||||
return rc
|
||||
|
||||
# ninja already refused missing targets; existence covers a rule that
|
||||
# ran but wrote elsewhere
|
||||
@@ -200,12 +173,10 @@ def _write_compile_commands(
|
||||
"ninja produced an empty compile database; the generator's rule "
|
||||
"names no longer match"
|
||||
)
|
||||
# write_file_if_changed keeps the mtime stable on no-op builds so the
|
||||
# idedata cache in get_idedata() stays valid.
|
||||
write_file_if_changed(compdb, result.stdout)
|
||||
write_file(compdb, result.stdout)
|
||||
|
||||
|
||||
def _parse_app_size(build_dir: Path, paths: framework.InstalledPaths) -> int | None:
|
||||
def _parse_app_size(build_dir: Path, paths: InstalledPaths) -> int | None:
|
||||
"""Read the app flash budget (irom0_0_seg length) from the linker script."""
|
||||
from esphome.build_gen.arduino8266 import get_flash_ld_path
|
||||
from esphome.components.esp8266.build_surgery import segment_length
|
||||
@@ -219,50 +190,43 @@ def _parse_app_size(build_dir: Path, paths: framework.InstalledPaths) -> int | N
|
||||
# A corrupt script degrades the same way, never aborts the build
|
||||
_LOGGER.warning("Cannot read linker script for the Flash summary: %s", err)
|
||||
return None
|
||||
app_size = segment_length(ld_text, "irom0_0_seg")
|
||||
if app_size is None:
|
||||
_LOGGER.warning("irom0_0_seg not found in %s; skipping Flash summary", ld_path)
|
||||
return None
|
||||
if app_size == 0:
|
||||
_LOGGER.warning(
|
||||
"irom0_0_seg has zero length in %s; skipping Flash summary", ld_path
|
||||
)
|
||||
if not (app_size := segment_length(ld_text, "irom0_0_seg")):
|
||||
_LOGGER.warning("No usable irom0_0_seg in %s; skipping Flash summary", ld_path)
|
||||
return None
|
||||
return app_size
|
||||
|
||||
|
||||
def _print_size_summary(build_dir: Path, paths: framework.InstalledPaths) -> bool:
|
||||
def _print_size_summary(build_dir: Path, paths: InstalledPaths) -> bool:
|
||||
"""Print the RAM/Flash lines ``ci_memory_impact_extract.py`` parses;
|
||||
False when skipped."""
|
||||
from esphome.arduino8266.framework import toolchain_tool
|
||||
from esphome.build_helpers.size_summary import print_size_line
|
||||
|
||||
size_tool = _toolchain_tool("size")
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[str(size_tool), "-A", "-d", str(get_elf_path())],
|
||||
[
|
||||
str(toolchain_tool(paths.toolchain, "size")),
|
||||
"-A",
|
||||
"-d",
|
||||
str(build_dir / "firmware.elf"),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
check=True,
|
||||
close_fds=False,
|
||||
)
|
||||
except OSError as err:
|
||||
except (OSError, subprocess.CalledProcessError) as err:
|
||||
# The summary is a bonus artifact like idedata; a truncated
|
||||
# toolchain extraction must not discard an already-linked build
|
||||
_LOGGER.warning("Could not summarize firmware size: %s", err)
|
||||
return False
|
||||
if result.returncode != 0:
|
||||
_LOGGER.warning("Could not summarize firmware size: %s", result.stderr)
|
||||
return False
|
||||
sections: dict[str, int] = {}
|
||||
for line in result.stdout.splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) >= 2 and parts[0].startswith("."):
|
||||
try:
|
||||
sections[parts[0]] = int(parts[1])
|
||||
except ValueError:
|
||||
# An unparsed RAM/Flash section trips the missing-sections
|
||||
# guard below, so no total is built on a dropped value
|
||||
_LOGGER.warning("Unparsable size output for section %s", parts[0])
|
||||
# -d prints decimal sizes; anything else trips the missing-sections guard
|
||||
sections = {
|
||||
parts[0]: int(parts[1])
|
||||
for line in result.stdout.splitlines()
|
||||
if (parts := line.split())[:1] and parts[0].startswith(".") and len(parts) >= 2
|
||||
if parts[1].isdigit()
|
||||
}
|
||||
if missing := set(_RAM_SECTIONS + _FLASH_SECTIONS) - set(sections):
|
||||
# A defaulted 0 would print a confidently wrong total for CI's metric
|
||||
_LOGGER.warning(
|
||||
@@ -282,18 +246,14 @@ def _print_size_summary(build_dir: Path, paths: framework.InstalledPaths) -> boo
|
||||
return True
|
||||
|
||||
|
||||
# Sentinel: "resolve for me"; None is a real value meaning disabled.
|
||||
_CCACHE_UNRESOLVED: Any = object()
|
||||
|
||||
|
||||
def get_idedata(ccache: str | None = _CCACHE_UNRESOLVED) -> dict | None:
|
||||
def get_idedata(ccache: str | None = None) -> dict | None:
|
||||
"""Derive idedata from the build's compile_commands.json (same
|
||||
contract as ``espidf.toolchain.get_idedata``)."""
|
||||
from esphome.build_helpers.idedata import load_or_build_idedata
|
||||
|
||||
if ccache is _CCACHE_UNRESOLVED:
|
||||
# Uncached: env/PATH can change between builds in a host process
|
||||
ccache = resolve_ccache_path()
|
||||
# A disabled ccache resolves to None without spawning anything, so
|
||||
# re-resolving here costs nothing when the caller has no answer
|
||||
launcher = ccache or resolve_ccache_path()
|
||||
return load_or_build_idedata(
|
||||
get_build_dir() / "compile_commands.json",
|
||||
get_elf_path(),
|
||||
@@ -302,5 +262,5 @@ def get_idedata(ccache: str | None = _CCACHE_UNRESOLVED) -> dict | None:
|
||||
CORE.relative_internal_path("idedata", f"{CORE.name}.arduino.json"),
|
||||
# The compile DB's commands carry the same ccache prefix the ninja
|
||||
# rules were generated with
|
||||
launcher=str(ccache) if ccache else None,
|
||||
launcher=str(launcher) if launcher else None,
|
||||
)
|
||||
|
||||
@@ -239,9 +239,9 @@ class ApplyCall:
|
||||
|
||||
Each arg is ``(conf_key, type_)`` or ``(conf_key, type_, const_fn)``. A ``conf_key`` may be a
|
||||
path into nested sections. A plain ``str`` ``type_`` is raw C++ type text and may use
|
||||
``{parent}``. ``const_fn(config, value)`` renders a constant's argument text; a lambda bypasses
|
||||
it. The statement is skipped when none of its keys is set, always emitted when it has no
|
||||
keys, and a partial set is a config error.
|
||||
``{parent}``. ``const_fn(config, value)`` renders a constant's argument text; a lambda or an
|
||||
id bypasses it. The statement is skipped when none of its keys is set, always emitted when it
|
||||
has no keys, and a partial set is a config error.
|
||||
"""
|
||||
|
||||
target: str
|
||||
@@ -280,8 +280,8 @@ class ApplyField:
|
||||
Double a literal brace in a template. ``conf_key`` may be a path into nested sections.
|
||||
``type_`` may be a C++ type string using ``{parent}`` when the type is only known per
|
||||
instance. ``const_fn(config, value)`` renders a constant's argument text when ``cg.safe_exp``
|
||||
is not the right spelling (unit conversion belongs in the validator); a lambda bypasses it,
|
||||
so the target must also take a plain ``type_``. An absent key emits nothing.
|
||||
is not the right spelling (unit conversion belongs in the validator); a lambda or an id
|
||||
bypasses it, so the target must also take a plain ``type_``. An absent key emits nothing.
|
||||
"""
|
||||
|
||||
conf_key: str | tuple[str, ...]
|
||||
@@ -380,6 +380,9 @@ async def _render_values(
|
||||
expr = call_lambda(inner)
|
||||
bare = compare and isinstance(expr, cg.RawExpression)
|
||||
exprs.append(f"({expr})" if bare else str(expr))
|
||||
elif isinstance(value, ID):
|
||||
# Qualified like the parent, so a trigger arg named like the id cannot shadow it.
|
||||
exprs.append(f"::{await cg.get_variable(value)}")
|
||||
elif const_fn is not None:
|
||||
exprs.append(const_fn(config, value))
|
||||
else:
|
||||
@@ -402,6 +405,7 @@ def register_apply_action(
|
||||
|
||||
Generates one stateless function for ``ApplyAction<Ts...>``: the parent (read from
|
||||
``id_key``) and constants are baked in, lambdas are called inline with the trigger args.
|
||||
A constant that is an id (``cv.use_id`` under ``cv.templatable``) is the object it names.
|
||||
With ``call`` every statement targets the call object ``auto apply_call = parent->call()``,
|
||||
and ``apply_call.perform()`` is appended.
|
||||
"""
|
||||
|
||||
@@ -47,13 +47,7 @@ def warn_if_idedata_missing(get_idedata: Callable[[], dict | None]) -> None:
|
||||
"memory-analysis data will be unavailable for this build)",
|
||||
err,
|
||||
)
|
||||
if isinstance(err, (EsphomeError, OSError)):
|
||||
# Routine environmental failures keep the detail at debug
|
||||
_LOGGER.debug("Idedata failure detail", exc_info=True)
|
||||
else:
|
||||
# LookupError/ValueError/RuntimeError smell like a parsing bug;
|
||||
# a permanently masked traceback would hide it on every build
|
||||
_LOGGER.warning("Idedata failure detail", exc_info=True)
|
||||
_LOGGER.debug("Idedata failure detail", exc_info=True)
|
||||
|
||||
|
||||
# C++ translation-unit suffixes.
|
||||
|
||||
@@ -106,6 +106,20 @@ bool AGS10Component::set_zero_point_with_factory_defaults() { return this->set_z
|
||||
|
||||
bool AGS10Component::set_zero_point_with_current_resistance() { return this->set_zero_point_with(ZP_CURRENT); }
|
||||
|
||||
void AGS10Component::set_zero_point(AGS10SetZeroPointActionMode mode, uint16_t value) {
|
||||
switch (mode) {
|
||||
case FACTORY_DEFAULT:
|
||||
this->set_zero_point_with_factory_defaults();
|
||||
break;
|
||||
case CURRENT_VALUE:
|
||||
this->set_zero_point_with_current_resistance();
|
||||
break;
|
||||
case CUSTOM_VALUE:
|
||||
this->set_zero_point_with(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool AGS10Component::set_zero_point_with(uint16_t value) {
|
||||
std::array<uint8_t, 5> data{0x00, 0x0C, (uint8_t) ((value >> 8) & 0xFF), (uint8_t) (value & 0xFF), 0};
|
||||
data[4] = crc8(data.data(), 4, 0xFF, 0x31, true);
|
||||
|
||||
@@ -2,11 +2,19 @@
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome::ags10 {
|
||||
|
||||
enum AGS10SetZeroPointActionMode {
|
||||
// Zero-point reset.
|
||||
FACTORY_DEFAULT,
|
||||
// Zero-point calibration with current resistance.
|
||||
CURRENT_VALUE,
|
||||
// Zero-point calibration with custom resistance.
|
||||
CUSTOM_VALUE,
|
||||
};
|
||||
|
||||
class AGS10Component final : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
/**
|
||||
@@ -47,6 +55,11 @@ class AGS10Component final : public PollingComponent, public i2c::I2CDevice {
|
||||
*/
|
||||
bool set_zero_point_with_current_resistance();
|
||||
|
||||
/**
|
||||
* Sets zero-point by mode; the value is only used for CUSTOM_VALUE.
|
||||
*/
|
||||
void set_zero_point(AGS10SetZeroPointActionMode mode, uint16_t value);
|
||||
|
||||
/**
|
||||
* Sets zero-point with the value.
|
||||
*/
|
||||
@@ -100,39 +113,4 @@ class AGS10Component final : public PollingComponent, public i2c::I2CDevice {
|
||||
template<size_t N> optional<std::array<uint8_t, N>> read_and_check_(uint8_t a_register);
|
||||
};
|
||||
|
||||
template<typename... Ts> class AGS10NewI2cAddressAction final : public Action<Ts...>, public Parented<AGS10Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, new_address)
|
||||
|
||||
void play(const Ts &...x) override { this->parent_->new_i2c_address(this->new_address_.value(x...)); }
|
||||
};
|
||||
|
||||
enum AGS10SetZeroPointActionMode {
|
||||
// Zero-point reset.
|
||||
FACTORY_DEFAULT,
|
||||
// Zero-point calibration with current resistance.
|
||||
CURRENT_VALUE,
|
||||
// Zero-point calibration with custom resistance.
|
||||
CUSTOM_VALUE,
|
||||
};
|
||||
|
||||
template<typename... Ts> class AGS10SetZeroPointAction final : public Action<Ts...>, public Parented<AGS10Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, value)
|
||||
TEMPLATABLE_VALUE(AGS10SetZeroPointActionMode, mode)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
switch (this->mode_.value(x...)) {
|
||||
case FACTORY_DEFAULT:
|
||||
this->parent_->set_zero_point_with_factory_defaults();
|
||||
break;
|
||||
case CURRENT_VALUE:
|
||||
this->parent_->set_zero_point_with_current_resistance();
|
||||
break;
|
||||
case CUSTOM_VALUE:
|
||||
this->parent_->set_zero_point_with(this->value_.value(x...));
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace esphome::ags10
|
||||
|
||||
@@ -17,8 +17,6 @@ from esphome.const import (
|
||||
UNIT_OHM,
|
||||
UNIT_PARTS_PER_BILLION,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CONF_RESISTANCE = "resistance"
|
||||
@@ -28,12 +26,6 @@ DEPENDENCIES = ["i2c"]
|
||||
ags10_ns = cg.esphome_ns.namespace("ags10")
|
||||
AGS10Component = ags10_ns.class_("AGS10Component", cg.PollingComponent, i2c.I2CDevice)
|
||||
|
||||
# Actions
|
||||
AGS10NewI2cAddressAction = ags10_ns.class_(
|
||||
"AGS10NewI2cAddressAction", automation.Action
|
||||
)
|
||||
AGS10SetZeroPointAction = ags10_ns.class_("AGS10SetZeroPointAction", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
@@ -91,24 +83,11 @@ AGS10_NEW_I2C_ADDRESS_SCHEMA = cv.maybe_simple_value(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"ags10.new_i2c_address",
|
||||
AGS10NewI2cAddressAction,
|
||||
AGS10_NEW_I2C_ADDRESS_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_ADDRESS, "new_i2c_address", cg.uint8),
|
||||
)
|
||||
async def ags10newi2caddress_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
address = await cg.templatable(config[CONF_ADDRESS], args, cg.uint8)
|
||||
cg.add(var.set_new_address(address))
|
||||
return var
|
||||
|
||||
|
||||
AGS10SetZeroPointActionMode = ags10_ns.enum("AGS10SetZeroPointActionMode")
|
||||
AGS10_SET_ZERO_POINT_ACTION_MODE = {
|
||||
@@ -128,24 +107,11 @@ AGS10_SET_ZERO_POINT_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"ags10.set_zero_point",
|
||||
AGS10SetZeroPointAction,
|
||||
AGS10_SET_ZERO_POINT_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyCall(
|
||||
"set_zero_point({}, {})",
|
||||
((CONF_MODE, AGS10SetZeroPointActionMode), (CONF_VALUE, cg.uint16)),
|
||||
),
|
||||
)
|
||||
async def ags10setzeropoint_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
mode = await cg.templatable(
|
||||
config.get(CONF_MODE), args, AGS10SetZeroPointActionMode
|
||||
)
|
||||
cg.add(var.set_mode(mode))
|
||||
value = await cg.templatable(config[CONF_VALUE], args, cg.uint16)
|
||||
cg.add(var.set_value(value))
|
||||
return var
|
||||
|
||||
@@ -4,8 +4,6 @@ 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.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
@@ -14,7 +12,6 @@ DEPENDENCIES = ["i2c"]
|
||||
aic3204_ns = cg.esphome_ns.namespace("aic3204")
|
||||
AIC3204 = aic3204_ns.class_("AIC3204", AudioDac, cg.Component, i2c.I2CDevice)
|
||||
|
||||
SetAutoMuteAction = aic3204_ns.class_("SetAutoMuteAction", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
@@ -36,25 +33,11 @@ SET_AUTO_MUTE_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"aic3204.set_auto_mute_mode",
|
||||
SetAutoMuteAction,
|
||||
SET_AUTO_MUTE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_MODE, "set_auto_mute_mode", cg.uint8),
|
||||
)
|
||||
async def aic3204_set_volume_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
template_ = await cg.templatable(config.get(CONF_MODE), args, cg.uint8)
|
||||
cg.add(var.set_auto_mute_mode(template_))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "aic3204.h"
|
||||
|
||||
namespace esphome::aic3204 {
|
||||
|
||||
template<typename... Ts> class SetAutoMuteAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit SetAutoMuteAction(AIC3204 *aic3204) : aic3204_(aic3204) {}
|
||||
|
||||
TEMPLATABLE_VALUE(uint8_t, auto_mute_mode)
|
||||
|
||||
void play(const Ts &...x) override { this->aic3204_->set_auto_mute_mode(this->auto_mute_mode_.value(x...)); }
|
||||
|
||||
protected:
|
||||
AIC3204 *aic3204_;
|
||||
};
|
||||
|
||||
} // namespace esphome::aic3204
|
||||
@@ -41,14 +41,6 @@ StateAnyForwarder = alarm_control_panel_ns.class_("StateAnyForwarder")
|
||||
StateEnterForwarder = alarm_control_panel_ns.class_("StateEnterForwarder")
|
||||
AlarmControlPanelState = alarm_control_panel_ns.enum("AlarmControlPanelState")
|
||||
|
||||
PendingAction = alarm_control_panel_ns.class_("PendingAction", automation.Action)
|
||||
TriggeredAction = alarm_control_panel_ns.class_("TriggeredAction", automation.Action)
|
||||
ChimeAction = alarm_control_panel_ns.class_("ChimeAction", automation.Action)
|
||||
ReadyAction = alarm_control_panel_ns.class_("ReadyAction", automation.Action)
|
||||
|
||||
AlarmControlPanelCondition = alarm_control_panel_ns.class_(
|
||||
"AlarmControlPanelCondition", automation.Condition
|
||||
)
|
||||
|
||||
_ALARM_CONTROL_PANEL_SCHEMA = (
|
||||
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||
@@ -208,65 +200,22 @@ for _name, _arm in (
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"alarm_control_panel.pending",
|
||||
PendingAction,
|
||||
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def alarm_action_pending_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"alarm_control_panel.triggered",
|
||||
TriggeredAction,
|
||||
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def alarm_action_trigger_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"alarm_control_panel.chime",
|
||||
ChimeAction,
|
||||
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def alarm_action_chime_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"alarm_control_panel.ready",
|
||||
ReadyAction,
|
||||
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
@automation.register_condition(
|
||||
"alarm_control_panel.ready",
|
||||
AlarmControlPanelCondition,
|
||||
ALARM_CONTROL_PANEL_CONDITION_SCHEMA,
|
||||
)
|
||||
async def alarm_action_ready_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
"alarm_control_panel.is_armed",
|
||||
AlarmControlPanelCondition,
|
||||
ALARM_CONTROL_PANEL_CONDITION_SCHEMA,
|
||||
)
|
||||
async def alarm_control_panel_is_armed_to_code(
|
||||
config, condition_id, template_arg, args
|
||||
for _name, _call in (
|
||||
("alarm_control_panel.pending", "pending()"),
|
||||
("alarm_control_panel.triggered", "triggered()"),
|
||||
):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(condition_id, template_arg, paren)
|
||||
automation.register_apply_action(
|
||||
_name,
|
||||
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
|
||||
automation.ApplyCall(_call),
|
||||
call="make_call",
|
||||
)
|
||||
|
||||
|
||||
for _name in ("alarm_control_panel.ready", "alarm_control_panel.is_armed"):
|
||||
automation.register_apply_condition(
|
||||
_name, ALARM_CONTROL_PANEL_CONDITION_SCHEMA, "is_armed_pending_or_triggered()"
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.CORE)
|
||||
|
||||
@@ -130,6 +130,12 @@ class AlarmControlPanel : public EntityBase {
|
||||
|
||||
// is the state one of the armed states
|
||||
bool is_state_armed(AlarmControlPanelState state);
|
||||
/// Armed, pending (entry delay) or triggered; not ARMING (exit delay). Backs the is_armed and ready
|
||||
/// conditions.
|
||||
bool is_armed_pending_or_triggered() {
|
||||
auto state = this->get_state();
|
||||
return this->is_state_armed(state) || state == ACP_STATE_PENDING || state == ACP_STATE_TRIGGERED;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend AlarmControlPanelCall;
|
||||
|
||||
@@ -27,36 +27,4 @@ static_assert(std::is_trivially_copyable_v<StateAnyForwarder>);
|
||||
static_assert(sizeof(StateEnterForwarder<ACP_STATE_TRIGGERED>) <= sizeof(void *));
|
||||
static_assert(std::is_trivially_copyable_v<StateEnterForwarder<ACP_STATE_TRIGGERED>>);
|
||||
|
||||
template<typename... Ts> class PendingAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit PendingAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
||||
|
||||
void play(const Ts &...x) override { this->alarm_control_panel_->make_call().pending().perform(); }
|
||||
|
||||
protected:
|
||||
AlarmControlPanel *alarm_control_panel_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class TriggeredAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit TriggeredAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
||||
|
||||
void play(const Ts &...x) override { this->alarm_control_panel_->make_call().triggered().perform(); }
|
||||
|
||||
protected:
|
||||
AlarmControlPanel *alarm_control_panel_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class AlarmControlPanelCondition final : public Condition<Ts...> {
|
||||
public:
|
||||
AlarmControlPanelCondition(AlarmControlPanel *parent) : parent_(parent) {}
|
||||
bool check(const Ts &...x) override {
|
||||
return this->parent_->is_state_armed(this->parent_->get_state()) ||
|
||||
this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_TRIGGERED;
|
||||
}
|
||||
|
||||
protected:
|
||||
AlarmControlPanel *parent_;
|
||||
};
|
||||
|
||||
} // namespace esphome::alarm_control_panel
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
#include "esphome/components/image/image.h"
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
namespace esphome::animation {
|
||||
|
||||
class Animation final : public image::Image {
|
||||
@@ -35,32 +33,4 @@ class Animation final : public image::Image {
|
||||
int loop_current_iteration_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class AnimationNextFrameAction final : public Action<Ts...> {
|
||||
public:
|
||||
AnimationNextFrameAction(Animation *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) override { this->parent_->next_frame(); }
|
||||
|
||||
protected:
|
||||
Animation *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class AnimationPrevFrameAction final : public Action<Ts...> {
|
||||
public:
|
||||
AnimationPrevFrameAction(Animation *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) override { this->parent_->prev_frame(); }
|
||||
|
||||
protected:
|
||||
Animation *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class AnimationSetFrameAction final : public Action<Ts...> {
|
||||
public:
|
||||
AnimationSetFrameAction(Animation *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(uint16_t, frame)
|
||||
void play(const Ts &...x) override { this->parent_->set_frame(this->frame_.value(x...)); }
|
||||
|
||||
protected:
|
||||
Animation *parent_;
|
||||
};
|
||||
|
||||
} // namespace esphome::animation
|
||||
|
||||
@@ -6,8 +6,6 @@ from esphome.components.file.image import image_schema, write_image
|
||||
from esphome.components.image import Image_, validate_settings
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_REPEAT
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@syndlex"]
|
||||
@@ -26,17 +24,6 @@ animation_ns = cg.esphome_ns.namespace("animation")
|
||||
|
||||
Animation_ = animation_ns.class_("Animation", Image_)
|
||||
|
||||
# Actions
|
||||
NextFrameAction = animation_ns.class_(
|
||||
"AnimationNextFrameAction", automation.Action, cg.Parented.template(Animation_)
|
||||
)
|
||||
PrevFrameAction = animation_ns.class_(
|
||||
"AnimationPrevFrameAction", automation.Action, cg.Parented.template(Animation_)
|
||||
)
|
||||
SetFrameAction = animation_ns.class_(
|
||||
"AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
|
||||
)
|
||||
|
||||
ANIMATION_SCHEMA = image_schema(Animation_).extend(
|
||||
{
|
||||
cv.Optional(CONF_LOOP): cv.All(
|
||||
@@ -72,28 +59,17 @@ SET_FRAME_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"animation.next_frame", NextFrameAction, NEXT_FRAME_SCHEMA, synchronous=True
|
||||
automation.register_apply_action(
|
||||
"animation.next_frame", NEXT_FRAME_SCHEMA, automation.ApplyCall("next_frame()")
|
||||
)
|
||||
@automation.register_action(
|
||||
"animation.prev_frame", PrevFrameAction, PREV_FRAME_SCHEMA, synchronous=True
|
||||
automation.register_apply_action(
|
||||
"animation.prev_frame", PREV_FRAME_SCHEMA, automation.ApplyCall("prev_frame()")
|
||||
)
|
||||
@automation.register_action(
|
||||
"animation.set_frame", SetFrameAction, SET_FRAME_SCHEMA, synchronous=True
|
||||
automation.register_apply_action(
|
||||
"animation.set_frame",
|
||||
SET_FRAME_SCHEMA,
|
||||
automation.ApplyField(CONF_FRAME, "set_frame", cg.uint16),
|
||||
)
|
||||
async def animation_action_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
if (frame := config.get(CONF_FRAME)) is not None:
|
||||
template_ = await cg.templatable(frame, args, cg.uint16)
|
||||
cg.add(var.set_frame(template_))
|
||||
return var
|
||||
|
||||
|
||||
async def setup_animation(config: ConfigType) -> None:
|
||||
|
||||
@@ -803,6 +803,9 @@ message SwitchStateResponse {
|
||||
fixed32 key = 1 [(force) = true];
|
||||
bool state = 2;
|
||||
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
|
||||
// If the switch does not have a valid state yet.
|
||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||
bool missing_state = 4;
|
||||
}
|
||||
message SwitchCommandRequest {
|
||||
option (id) = 33;
|
||||
@@ -1244,6 +1247,9 @@ message ClimateStateResponse {
|
||||
float current_humidity = 14;
|
||||
float target_humidity = 15;
|
||||
uint32 device_id = 16 [(field_ifdef) = "USE_DEVICES"];
|
||||
// If the climate device does not have a valid state yet.
|
||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||
bool missing_state = 17;
|
||||
}
|
||||
message ClimateCommandRequest {
|
||||
option (id) = 48;
|
||||
@@ -1330,6 +1336,9 @@ message WaterHeaterStateResponse {
|
||||
uint32 state = 6;
|
||||
float target_temperature_low = 7;
|
||||
float target_temperature_high = 8;
|
||||
// If the water heater does not have a valid state yet.
|
||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||
bool missing_state = 9;
|
||||
}
|
||||
|
||||
// Bitmask for WaterHeaterCommandRequest.has_fields
|
||||
|
||||
@@ -709,6 +709,7 @@ uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection
|
||||
auto *a_switch = static_cast<switch_::Switch *>(entity);
|
||||
SwitchStateResponse resp;
|
||||
resp.state = a_switch->state;
|
||||
resp.missing_state = !a_switch->has_state();
|
||||
return fill_and_encode_entity_state(a_switch, resp, conn, remaining_size);
|
||||
}
|
||||
|
||||
@@ -754,6 +755,7 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
|
||||
auto traits = climate->get_traits();
|
||||
resp.mode = static_cast<enums::ClimateMode>(climate->mode);
|
||||
resp.action = static_cast<enums::ClimateAction>(climate->action);
|
||||
resp.missing_state = !climate->has_state();
|
||||
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE))
|
||||
resp.current_temperature = climate->current_temperature;
|
||||
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE |
|
||||
@@ -1447,6 +1449,7 @@ uint16_t APIConnection::try_send_water_heater_state(EntityBase *entity, APIConne
|
||||
auto *wh = static_cast<water_heater::WaterHeater *>(entity);
|
||||
WaterHeaterStateResponse resp;
|
||||
resp.mode = static_cast<enums::WaterHeaterMode>(wh->get_mode());
|
||||
resp.missing_state = !wh->has_state();
|
||||
resp.current_temperature = wh->get_current_temperature();
|
||||
resp.target_temperature = wh->get_target_temperature();
|
||||
resp.target_temperature_low = wh->get_target_temperature_low();
|
||||
|
||||
@@ -938,6 +938,7 @@ uint8_t *SwitchStateResponse::encode_msg(const void *self, ProtoWriteBuffer &buf
|
||||
#ifdef USE_DEVICES
|
||||
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 3, msg.device_id);
|
||||
#endif
|
||||
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.missing_state);
|
||||
return pos;
|
||||
}
|
||||
uint32_t SwitchStateResponse::calc_size_msg(const void *self) {
|
||||
@@ -948,6 +949,7 @@ uint32_t SwitchStateResponse::calc_size_msg(const void *self) {
|
||||
#ifdef USE_DEVICES
|
||||
size += ProtoSize::calc_uint32(1, msg.device_id);
|
||||
#endif
|
||||
size += ProtoSize::calc_bool(1, msg.missing_state);
|
||||
return size;
|
||||
}
|
||||
void SwitchCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) {
|
||||
@@ -1630,6 +1632,7 @@ uint8_t *ClimateStateResponse::encode_msg(const void *self, ProtoWriteBuffer &bu
|
||||
#ifdef USE_DEVICES
|
||||
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 16, msg.device_id);
|
||||
#endif
|
||||
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 17, msg.missing_state);
|
||||
return pos;
|
||||
}
|
||||
uint32_t ClimateStateResponse::calc_size_msg(const void *self) {
|
||||
@@ -1652,6 +1655,7 @@ uint32_t ClimateStateResponse::calc_size_msg(const void *self) {
|
||||
#ifdef USE_DEVICES
|
||||
size += ProtoSize::calc_uint32(2, msg.device_id);
|
||||
#endif
|
||||
size += ProtoSize::calc_bool(2, msg.missing_state);
|
||||
return size;
|
||||
}
|
||||
void ClimateCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) {
|
||||
@@ -1806,6 +1810,7 @@ uint8_t *WaterHeaterStateResponse::encode_msg(const void *self, ProtoWriteBuffer
|
||||
if (uint32_t raw = float_to_raw(msg.target_temperature_high); raw != 0) [[likely]] {
|
||||
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 69, raw);
|
||||
}
|
||||
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 9, msg.missing_state);
|
||||
return pos;
|
||||
}
|
||||
uint32_t WaterHeaterStateResponse::calc_size_msg(const void *self) {
|
||||
@@ -1821,6 +1826,7 @@ uint32_t WaterHeaterStateResponse::calc_size_msg(const void *self) {
|
||||
size += ProtoSize::calc_uint32(1, msg.state);
|
||||
size += ProtoSize::calc_float(1, msg.target_temperature_low);
|
||||
size += ProtoSize::calc_float(1, msg.target_temperature_high);
|
||||
size += ProtoSize::calc_bool(1, msg.missing_state);
|
||||
return size;
|
||||
}
|
||||
void WaterHeaterCommandRequest::decode_field(void *self, uint32_t tag, const uint8_t *data,
|
||||
|
||||
@@ -1109,11 +1109,12 @@ class ListEntitiesSwitchResponse final : public InfoResponseProtoMessage {
|
||||
class SwitchStateResponse final : public StateResponseProtoMessage {
|
||||
public:
|
||||
static constexpr uint16_t MESSAGE_TYPE = 26;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 11;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 13;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const LogString *message_name() const override { return LOG_STR("switch_state_response"); }
|
||||
#endif
|
||||
bool state{false};
|
||||
bool missing_state{false};
|
||||
static uint8_t *encode_msg(const void *self, ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM);
|
||||
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
|
||||
return encode_msg(this, buffer PROTO_ENCODE_DEBUG_ARG);
|
||||
@@ -1690,7 +1691,7 @@ class ListEntitiesClimateResponse final : public InfoResponseProtoMessage {
|
||||
class ClimateStateResponse final : public StateResponseProtoMessage {
|
||||
public:
|
||||
static constexpr uint16_t MESSAGE_TYPE = 47;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 68;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 71;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const LogString *message_name() const override { return LOG_STR("climate_state_response"); }
|
||||
#endif
|
||||
@@ -1707,6 +1708,7 @@ class ClimateStateResponse final : public StateResponseProtoMessage {
|
||||
StringRef custom_preset{nullptr, 0}; // null until set, encode only
|
||||
float current_humidity{0.0f};
|
||||
float target_humidity{0.0f};
|
||||
bool missing_state{false};
|
||||
static uint8_t *encode_msg(const void *self, ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM);
|
||||
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
|
||||
return encode_msg(this, buffer PROTO_ENCODE_DEBUG_ARG);
|
||||
@@ -1786,7 +1788,7 @@ class ListEntitiesWaterHeaterResponse final : public InfoResponseProtoMessage {
|
||||
class WaterHeaterStateResponse final : public StateResponseProtoMessage {
|
||||
public:
|
||||
static constexpr uint16_t MESSAGE_TYPE = 133;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 35;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 37;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const LogString *message_name() const override { return LOG_STR("water_heater_state_response"); }
|
||||
#endif
|
||||
@@ -1796,6 +1798,7 @@ class WaterHeaterStateResponse final : public StateResponseProtoMessage {
|
||||
uint32_t state{0};
|
||||
float target_temperature_low{0.0f};
|
||||
float target_temperature_high{0.0f};
|
||||
bool missing_state{false};
|
||||
static uint8_t *encode_msg(const void *self, ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM);
|
||||
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
|
||||
return encode_msg(this, buffer PROTO_ENCODE_DEBUG_ARG);
|
||||
|
||||
@@ -1342,6 +1342,7 @@ const char *SwitchStateResponse::dump_to(DumpBuffer &out) const {
|
||||
#ifdef USE_DEVICES
|
||||
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
|
||||
#endif
|
||||
dump_field(out, ESPHOME_PSTR("missing_state"), this->missing_state);
|
||||
return out.c_str();
|
||||
}
|
||||
const char *SwitchCommandRequest::dump_to(DumpBuffer &out) const {
|
||||
@@ -1684,6 +1685,7 @@ const char *ClimateStateResponse::dump_to(DumpBuffer &out) const {
|
||||
#ifdef USE_DEVICES
|
||||
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
|
||||
#endif
|
||||
dump_field(out, ESPHOME_PSTR("missing_state"), this->missing_state);
|
||||
return out.c_str();
|
||||
}
|
||||
const char *ClimateCommandRequest::dump_to(DumpBuffer &out) const {
|
||||
@@ -1751,6 +1753,7 @@ const char *WaterHeaterStateResponse::dump_to(DumpBuffer &out) const {
|
||||
dump_field(out, ESPHOME_PSTR("state"), this->state);
|
||||
dump_field(out, ESPHOME_PSTR("target_temperature_low"), this->target_temperature_low);
|
||||
dump_field(out, ESPHOME_PSTR("target_temperature_high"), this->target_temperature_high);
|
||||
dump_field(out, ESPHOME_PSTR("missing_state"), this->missing_state);
|
||||
return out.c_str();
|
||||
}
|
||||
const char *WaterHeaterCommandRequest::dump_to(DumpBuffer &out) const {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_MIC_GAIN
|
||||
from esphome.core import ID, CoroPriority, coroutine_with_priority
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.const import CONF_MIC_GAIN
|
||||
from esphome.core import CoroPriority, coroutine_with_priority
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
@@ -12,8 +11,6 @@ IS_PLATFORM_COMPONENT = True
|
||||
audio_adc_ns = cg.esphome_ns.namespace("audio_adc")
|
||||
AudioAdc = audio_adc_ns.class_("AudioAdc")
|
||||
|
||||
SetMicGainAction = audio_adc_ns.class_("SetMicGainAction", automation.Action)
|
||||
|
||||
|
||||
SET_MIC_GAIN_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
{
|
||||
@@ -24,25 +21,11 @@ SET_MIC_GAIN_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"audio_adc.set_mic_gain",
|
||||
SetMicGainAction,
|
||||
SET_MIC_GAIN_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_MIC_GAIN, "set_mic_gain", cg.float_),
|
||||
)
|
||||
async def audio_adc_set_mic_gain_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
template_ = await cg.templatable(config.get(CONF_MIC_GAIN), args, cg.float_)
|
||||
cg.add(var.set_mic_gain(template_))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.CORE)
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "audio_adc.h"
|
||||
|
||||
namespace esphome::audio_adc {
|
||||
|
||||
template<typename... Ts> class SetMicGainAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit SetMicGainAction(AudioAdc *audio_adc) : audio_adc_(audio_adc) {}
|
||||
|
||||
TEMPLATABLE_VALUE(float, mic_gain)
|
||||
|
||||
void play(const Ts &...x) override { this->audio_adc_->set_mic_gain(this->mic_gain_.value(x...)); }
|
||||
|
||||
protected:
|
||||
AudioAdc *audio_adc_;
|
||||
};
|
||||
|
||||
} // namespace esphome::audio_adc
|
||||
@@ -134,9 +134,6 @@ MultiClickTriggerBase = binary_sensor_ns.class_(
|
||||
MultiClickTrigger = binary_sensor_ns.class_("MultiClickTrigger", MultiClickTriggerBase)
|
||||
MultiClickTriggerEvent = binary_sensor_ns.struct("MultiClickTriggerEvent")
|
||||
|
||||
BinarySensorInvalidateAction = binary_sensor_ns.class_(
|
||||
"BinarySensorInvalidateAction", automation.Action
|
||||
)
|
||||
|
||||
# Filters
|
||||
Filter = binary_sensor_ns.class_("Filter")
|
||||
@@ -655,20 +652,16 @@ async def to_code(config):
|
||||
cg.add_global(binary_sensor_ns.using)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"binary_sensor.invalidate_state",
|
||||
BinarySensorInvalidateAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(BinarySensor),
|
||||
},
|
||||
key=CONF_ID,
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyCall("invalidate_state()"),
|
||||
)
|
||||
async def binary_sensor_invalidate_state_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
# automation.cpp only implements the click/double_click/multi_click triggers
|
||||
|
||||
@@ -120,14 +120,4 @@ template<size_t N> class MultiClickTrigger final : public MultiClickTriggerBase
|
||||
std::array<MultiClickTriggerEvent, N> timing_storage_{};
|
||||
};
|
||||
|
||||
template<typename... Ts> class BinarySensorInvalidateAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit BinarySensorInvalidateAction(BinarySensor *sensor) : sensor_(sensor) {}
|
||||
|
||||
void play(const Ts &...x) override { this->sensor_->invalidate_state(); }
|
||||
|
||||
protected:
|
||||
BinarySensor *sensor_;
|
||||
};
|
||||
|
||||
} // namespace esphome::binary_sensor
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/datatypes.h"
|
||||
|
||||
@@ -47,8 +46,6 @@ struct sbe24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-ali
|
||||
int8_t h{0};
|
||||
} __attribute__((packed));
|
||||
|
||||
template<typename... Ts> class ResetEnergyAction;
|
||||
|
||||
class BL0906;
|
||||
|
||||
using ActionCallbackFuncPtr = void (BL0906::*)();
|
||||
@@ -85,9 +82,10 @@ class BL0906 final : public PollingComponent, public uart::UARTDevice {
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
template<typename... Ts> friend class ResetEnergyAction;
|
||||
/// Queue an energy counter reset for the next poll
|
||||
void reset_energy() { this->enqueue_action_(&BL0906::reset_energy_); }
|
||||
|
||||
protected:
|
||||
void reset_energy_();
|
||||
|
||||
void read_data_(uint8_t address, float reference, sensor::Sensor *sensor);
|
||||
@@ -103,9 +101,4 @@ class BL0906 final : public PollingComponent, public uart::UARTDevice {
|
||||
std::vector<ActionCallbackFuncPtr> action_queue_{};
|
||||
};
|
||||
|
||||
template<typename... Ts> class ResetEnergyAction final : public Action<Ts...>, public Parented<BL0906> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->enqueue_action_(&BL0906::reset_energy_); }
|
||||
};
|
||||
|
||||
} // namespace esphome::bl0906
|
||||
|
||||
@@ -32,8 +32,6 @@ from esphome.const import (
|
||||
UNIT_VOLT,
|
||||
UNIT_WATT,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
# Import ICONS not included in esphome's const.py, from the local components const.py
|
||||
@@ -45,7 +43,6 @@ CONF_TOTAL_ENERGY = "total_energy"
|
||||
|
||||
bl0906_ns = cg.esphome_ns.namespace("bl0906")
|
||||
BL0906 = bl0906_ns.class_("BL0906", cg.PollingComponent, uart.UARTDevice)
|
||||
ResetEnergyAction = bl0906_ns.class_("ResetEnergyAction", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
@@ -138,25 +135,15 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"bl0906.reset_energy",
|
||||
ResetEnergyAction,
|
||||
maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(BL0906),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyCall("reset_energy()"),
|
||||
)
|
||||
async def reset_energy_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
|
||||
@@ -207,26 +207,20 @@ BLE_REMOVE_BOND_ACTION_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_simple_action(
|
||||
"ble_client.disconnect",
|
||||
BLEDisconnectAction,
|
||||
BLE_CONNECT_ACTION_SCHEMA,
|
||||
synchronous=False,
|
||||
)
|
||||
async def ble_disconnect_to_code(config, action_id, template_arg, args):
|
||||
parent = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, parent)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_simple_action(
|
||||
"ble_client.connect",
|
||||
BLEConnectAction,
|
||||
BLE_CONNECT_ACTION_SCHEMA,
|
||||
synchronous=False,
|
||||
)
|
||||
async def ble_connect_to_code(config, action_id, template_arg, args):
|
||||
parent = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, parent)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
@@ -328,15 +322,12 @@ async def passkey_reply_to_code(config, action_id, template_arg, args):
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_simple_action(
|
||||
"ble_client.remove_bond",
|
||||
BLERemoveBondAction,
|
||||
BLE_REMOVE_BOND_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def remove_bond_to_code(config, action_id, template_arg, args):
|
||||
parent = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, parent)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
||||
@@ -34,24 +34,4 @@ class BM8563 final : public time::RealTimeClock, public i2c::I2CDevice {
|
||||
uint8_t byte_to_bcd2_(uint8_t value);
|
||||
};
|
||||
|
||||
template<typename... Ts> class WriteAction final : public Action<Ts...>, public Parented<BM8563> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->write_time(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class ReadAction final : public Action<Ts...>, public Parented<BM8563> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->read_time(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class TimerAction final : public Action<Ts...>, public Parented<BM8563> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint32_t, duration)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
auto duration = this->duration_.value(x...);
|
||||
this->parent_->start_timer(duration);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::bm8563
|
||||
|
||||
@@ -3,8 +3,6 @@ import esphome.codegen as cg
|
||||
from esphome.components import i2c, time
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_DURATION, CONF_ID
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
@@ -13,9 +11,6 @@ I2C_ADDR = 0x51
|
||||
|
||||
bm8563_ns = cg.esphome_ns.namespace("bm8563")
|
||||
BM8563 = bm8563_ns.class_("BM8563", time.RealTimeClock, i2c.I2CDevice)
|
||||
WriteAction = bm8563_ns.class_("WriteAction", automation.Action)
|
||||
ReadAction = bm8563_ns.class_("ReadAction", automation.Action)
|
||||
TimerAction = bm8563_ns.class_("TimerAction", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
time.TIME_SCHEMA.extend(
|
||||
@@ -28,70 +23,30 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"bm8563.write_time",
|
||||
WriteAction,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(BM8563),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
BM8563_ACTION_SCHEMA = automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(BM8563),
|
||||
}
|
||||
)
|
||||
async def bm8563_write_time_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
automation.register_apply_action(
|
||||
"bm8563.write_time", BM8563_ACTION_SCHEMA, automation.ApplyCall("write_time()")
|
||||
)
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"bm8563.read_time", BM8563_ACTION_SCHEMA, automation.ApplyCall("read_time()")
|
||||
)
|
||||
|
||||
automation.register_apply_action(
|
||||
"bm8563.start_timer",
|
||||
TimerAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(BM8563),
|
||||
cv.Required(CONF_DURATION): cv.templatable(cv.positive_time_period_seconds),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_DURATION, "start_timer", cg.uint32),
|
||||
)
|
||||
async def bm8563_start_timer_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
template_ = await cg.templatable(config[CONF_DURATION], args, cg.uint32)
|
||||
cg.add(var.set_duration(template_))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"bm8563.read_time",
|
||||
ReadAction,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(BM8563),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
)
|
||||
async def bm8563_read_time_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
|
||||
@@ -351,10 +351,6 @@ async def to_code(config):
|
||||
|
||||
|
||||
# Actions
|
||||
BeginTxAction = ns.class_("BeginTxAction", automation.Action)
|
||||
BeginRxAction = ns.class_("BeginRxAction", automation.Action)
|
||||
ResetAction = ns.class_("ResetAction", automation.Action)
|
||||
SetIdleAction = ns.class_("SetIdleAction", automation.Action)
|
||||
SendPacketAction = ns.class_(
|
||||
"SendPacketAction", automation.Action, cg.Parented.template(CC1101Component)
|
||||
)
|
||||
@@ -364,22 +360,15 @@ CC1101_ACTION_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"cc1101.begin_tx", BeginTxAction, CC1101_ACTION_SCHEMA, synchronous=True
|
||||
)
|
||||
@automation.register_action(
|
||||
"cc1101.begin_rx", BeginRxAction, CC1101_ACTION_SCHEMA, synchronous=True
|
||||
)
|
||||
@automation.register_action(
|
||||
"cc1101.reset", ResetAction, CC1101_ACTION_SCHEMA, synchronous=True
|
||||
)
|
||||
@automation.register_action(
|
||||
"cc1101.set_idle", SetIdleAction, CC1101_ACTION_SCHEMA, synchronous=True
|
||||
)
|
||||
async def cc1101_action_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
for _name, _call in (
|
||||
("cc1101.begin_tx", "begin_tx()"),
|
||||
("cc1101.begin_rx", "begin_rx()"),
|
||||
("cc1101.reset", "reset()"),
|
||||
("cc1101.set_idle", "set_idle()"),
|
||||
):
|
||||
automation.register_apply_action(
|
||||
_name, CC1101_ACTION_SCHEMA, automation.ApplyCall(_call)
|
||||
)
|
||||
|
||||
|
||||
def validate_raw_data(value):
|
||||
|
||||
@@ -130,26 +130,6 @@ class CC1101Component final : public Component,
|
||||
};
|
||||
|
||||
// Action Wrappers
|
||||
template<typename... Ts> class BeginTxAction final : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->begin_tx(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class BeginRxAction final : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->begin_rx(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class ResetAction final : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->reset(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetIdleAction final : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->set_idle(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SendPacketAction final : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
void set_data_template(std::function<std::vector<uint8_t>(Ts...)> func) { this->data_func_ = func; }
|
||||
|
||||
@@ -435,6 +435,7 @@ void Climate::save_state_(const ClimateTraits &traits) {
|
||||
}
|
||||
|
||||
void Climate::publish_state() {
|
||||
this->set_has_state(true);
|
||||
ESP_LOGV(TAG, "'%s' >>", this->name_.c_str());
|
||||
auto traits = this->get_traits();
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
@@ -23,14 +22,4 @@ class CM1106Component final : public PollingComponent, public uart::UARTDevice {
|
||||
bool cm1106_write_command_(const uint8_t *command, size_t command_len, uint8_t *response, size_t response_len);
|
||||
};
|
||||
|
||||
template<typename... Ts> class CM1106CalibrateZeroAction final : public Action<Ts...> {
|
||||
public:
|
||||
CM1106CalibrateZeroAction(CM1106Component *cm1106) : cm1106_(cm1106) {}
|
||||
|
||||
void play(const Ts &...x) override { this->cm1106_->calibrate_zero(400); }
|
||||
|
||||
protected:
|
||||
CM1106Component *cm1106_;
|
||||
};
|
||||
|
||||
} // namespace esphome::cm1106
|
||||
|
||||
@@ -13,8 +13,6 @@ from esphome.const import (
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_PARTS_PER_MILLION,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
@@ -24,10 +22,6 @@ cm1106_ns = cg.esphome_ns.namespace("cm1106")
|
||||
CM1106Component = cm1106_ns.class_(
|
||||
"CM1106Component", cg.PollingComponent, uart.UARTDevice
|
||||
)
|
||||
CM1106CalibrateZeroAction = cm1106_ns.class_(
|
||||
"CM1106CalibrateZeroAction",
|
||||
automation.Action,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
@@ -72,18 +66,8 @@ CALIBRATION_ACTION_SCHEMA = maybe_simple_id(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"cm1106.calibrate_zero",
|
||||
CM1106CalibrateZeroAction,
|
||||
CALIBRATION_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyCall("calibrate_zero(400)"),
|
||||
)
|
||||
async def cm1106_calibration_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
"""Service code generation entry point."""
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
|
||||
@@ -108,14 +107,4 @@ class CS5460AComponent final : public Component,
|
||||
uint32_t prev_raw_energy_{0};
|
||||
};
|
||||
|
||||
template<typename... Ts> class CS5460ARestartAction final : public Action<Ts...> {
|
||||
public:
|
||||
CS5460ARestartAction(CS5460AComponent *cs5460a) : cs5460a_(cs5460a) {}
|
||||
|
||||
void play(const Ts &...x) override { cs5460a_->restart(); }
|
||||
|
||||
protected:
|
||||
CS5460AComponent *cs5460a_;
|
||||
};
|
||||
|
||||
} // namespace esphome::cs5460a
|
||||
|
||||
@@ -17,8 +17,6 @@ from esphome.const import (
|
||||
UNIT_VOLT,
|
||||
UNIT_WATT,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@balrog-kun"]
|
||||
@@ -32,7 +30,6 @@ PGA_GAIN_OPTIONS = {
|
||||
}
|
||||
|
||||
CS5460AComponent = cs5460a_ns.class_("CS5460AComponent", spi.SPIDevice, cg.Component)
|
||||
CS5460ARestartAction = cs5460a_ns.class_("CS5460ARestartAction", automation.Action)
|
||||
|
||||
CONF_SAMPLES = "samples"
|
||||
CONF_PHASE_OFFSET = "phase_offset"
|
||||
@@ -131,21 +128,12 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_power_sensor(sens))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"cs5460a.restart",
|
||||
CS5460ARestartAction,
|
||||
maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(CS5460AComponent),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyCall("restart()"),
|
||||
)
|
||||
async def restart_action_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
@@ -12,9 +12,6 @@ CODEOWNERS = ["@glmnet"]
|
||||
|
||||
dfplayer_ns = cg.esphome_ns.namespace("dfplayer")
|
||||
DFPlayer = dfplayer_ns.class_("DFPlayer", cg.Component)
|
||||
DFPlayerIsPlayingCondition = dfplayer_ns.class_(
|
||||
"DFPlayerIsPlayingCondition", automation.Condition
|
||||
)
|
||||
|
||||
MULTI_CONF = True
|
||||
CONF_FOLDER = "folder"
|
||||
@@ -199,16 +196,12 @@ automation.register_apply_action(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
automation.register_apply_condition(
|
||||
"dfplayer.is_playing",
|
||||
DFPlayerIsPlayingCondition,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(DFPlayer),
|
||||
}
|
||||
),
|
||||
"is_playing()",
|
||||
)
|
||||
async def dfplayer_is_playing_to_code(config, condition_id, template_arg, args):
|
||||
var = cg.new_Pvariable(condition_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
@@ -77,9 +77,4 @@ class DFPlayer final : public uart::UARTDevice, public Component {
|
||||
CallbackManager<void()> on_finished_playback_callback_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class DFPlayerIsPlayingCondition final : public Condition<Ts...>, public Parented<DFPlayer> {
|
||||
public:
|
||||
bool check(const Ts &...x) override { return this->parent_->is_playing(); }
|
||||
};
|
||||
|
||||
} // namespace esphome::dfplayer
|
||||
|
||||
@@ -20,9 +20,6 @@ DfrobotSen0395Component = dfrobot_sen0395_ns.class_(
|
||||
)
|
||||
|
||||
# Actions
|
||||
DfrobotSen0395ResetAction = dfrobot_sen0395_ns.class_(
|
||||
"DfrobotSen0395ResetAction", automation.Action
|
||||
)
|
||||
DfrobotSen0395SettingsAction = dfrobot_sen0395_ns.class_(
|
||||
"DfrobotSen0395SettingsAction", automation.Action
|
||||
)
|
||||
@@ -49,26 +46,15 @@ async def to_code(config: ConfigType) -> None:
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"dfrobot_sen0395.reset",
|
||||
DfrobotSen0395ResetAction,
|
||||
maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(DfrobotSen0395Component),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyCall("reset_system()"),
|
||||
)
|
||||
async def dfrobot_sen0395_reset_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
return var
|
||||
|
||||
|
||||
def range_segment_list(input: Any) -> list:
|
||||
|
||||
@@ -7,12 +7,6 @@
|
||||
|
||||
namespace esphome::dfrobot_sen0395 {
|
||||
|
||||
template<typename... Ts>
|
||||
class DfrobotSen0395ResetAction final : public Action<Ts...>, public Parented<DfrobotSen0395Component> {
|
||||
public:
|
||||
void play(const Ts &...x) { this->parent_->enqueue(make_unique<ResetSystemCommand>()); }
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class DfrobotSen0395SettingsAction final : public Action<Ts...>, public Parented<DfrobotSen0395Component> {
|
||||
public:
|
||||
|
||||
@@ -94,6 +94,8 @@ class DfrobotSen0395Component final : public uart::UARTDevice, public Component
|
||||
#endif
|
||||
|
||||
int8_t enqueue(std::unique_ptr<Command> cmd);
|
||||
/// Queue a sensor reset
|
||||
void reset_system() { this->enqueue(make_unique<ResetSystemCommand>()); }
|
||||
|
||||
protected:
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
|
||||
@@ -39,9 +39,6 @@ DisplayPage = display_ns.class_("DisplayPage")
|
||||
DisplayPagePtr = DisplayPage.operator("ptr")
|
||||
DisplayRef = Display.operator("ref")
|
||||
DisplayPageShowAction = display_ns.class_("DisplayPageShowAction", automation.Action)
|
||||
DisplayIsDisplayingPageCondition = display_ns.class_(
|
||||
"DisplayIsDisplayingPageCondition", automation.Condition
|
||||
)
|
||||
DisplayOnPageChangeTrigger = display_ns.class_(
|
||||
"DisplayOnPageChangeTrigger", automation.Trigger
|
||||
)
|
||||
@@ -301,9 +298,8 @@ automation.register_apply_action(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
automation.register_apply_condition(
|
||||
"display.is_displaying_page",
|
||||
DisplayIsDisplayingPageCondition,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(CONF_ID): cv.use_id(Display),
|
||||
@@ -311,13 +307,8 @@ automation.register_apply_action(
|
||||
},
|
||||
key=CONF_PAGE_ID,
|
||||
),
|
||||
automation.ApplyCall("get_active_page() == {}", ((CONF_PAGE_ID, DisplayPagePtr),)),
|
||||
)
|
||||
async def display_is_displaying_page_to_code(config, condition_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
page = await cg.get_variable(config[CONF_PAGE_ID])
|
||||
var = cg.new_Pvariable(condition_id, template_arg, paren)
|
||||
cg.add(var.set_page(page))
|
||||
return var
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.CORE)
|
||||
|
||||
@@ -848,18 +848,6 @@ template<typename... Ts> class DisplayPageShowAction final : public Action<Ts...
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayIsDisplayingPageCondition final : public Condition<Ts...> {
|
||||
public:
|
||||
DisplayIsDisplayingPageCondition(Display *parent) : parent_(parent) {}
|
||||
|
||||
void set_page(DisplayPage *page) { this->page_ = page; }
|
||||
bool check(const Ts &...x) override { return this->parent_->get_active_page() == this->page_; }
|
||||
|
||||
protected:
|
||||
Display *parent_;
|
||||
DisplayPage *page_;
|
||||
};
|
||||
|
||||
class DisplayOnPageChangeTrigger final : public Trigger<DisplayPage *, DisplayPage *> {
|
||||
public:
|
||||
explicit DisplayOnPageChangeTrigger(Display *parent) { parent->add_on_page_change_trigger(this); }
|
||||
|
||||
@@ -55,18 +55,6 @@ MenuItemSwitch = display_menu_base_ns.class_("MenuItemSwitch")
|
||||
MenuItemCommand = display_menu_base_ns.class_("MenuItemCommand")
|
||||
MenuItemCustom = display_menu_base_ns.class_("MenuItemCustom")
|
||||
|
||||
UpAction = display_menu_base_ns.class_("UpAction", automation.Action)
|
||||
DownAction = display_menu_base_ns.class_("DownAction", automation.Action)
|
||||
LeftAction = display_menu_base_ns.class_("LeftAction", automation.Action)
|
||||
RightAction = display_menu_base_ns.class_("RightAction", automation.Action)
|
||||
EnterAction = display_menu_base_ns.class_("EnterAction", automation.Action)
|
||||
ShowAction = display_menu_base_ns.class_("ShowAction", automation.Action)
|
||||
HideAction = display_menu_base_ns.class_("HideAction", automation.Action)
|
||||
ShowMainAction = display_menu_base_ns.class_("ShowMainAction", automation.Action)
|
||||
|
||||
IsActiveCondition = display_menu_base_ns.class_(
|
||||
"IsActiveCondition", automation.Condition
|
||||
)
|
||||
|
||||
MenuItemType = display_menu_base_ns.enum("MenuItemType")
|
||||
|
||||
@@ -294,85 +282,23 @@ MENU_ACTION_SCHEMA = maybe_simple_id(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"display_menu.up", UpAction, MENU_ACTION_SCHEMA, synchronous=True
|
||||
for _name, _call in (
|
||||
("display_menu.up", "up()"),
|
||||
("display_menu.down", "down()"),
|
||||
("display_menu.left", "left()"),
|
||||
("display_menu.right", "right()"),
|
||||
("display_menu.enter", "enter()"),
|
||||
("display_menu.show", "show()"),
|
||||
("display_menu.hide", "hide()"),
|
||||
("display_menu.show_main", "show_main()"),
|
||||
):
|
||||
automation.register_apply_action(
|
||||
_name, MENU_ACTION_SCHEMA, automation.ApplyCall(_call)
|
||||
)
|
||||
|
||||
automation.register_apply_condition(
|
||||
"display_menu.is_active", MENU_ACTION_SCHEMA, "is_active()"
|
||||
)
|
||||
async def menu_up_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"display_menu.down", DownAction, MENU_ACTION_SCHEMA, synchronous=True
|
||||
)
|
||||
async def menu_down_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"display_menu.left", LeftAction, MENU_ACTION_SCHEMA, synchronous=True
|
||||
)
|
||||
async def menu_left_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"display_menu.right", RightAction, MENU_ACTION_SCHEMA, synchronous=True
|
||||
)
|
||||
async def menu_right_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"display_menu.enter", EnterAction, MENU_ACTION_SCHEMA, synchronous=True
|
||||
)
|
||||
async def menu_enter_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"display_menu.show", ShowAction, MENU_ACTION_SCHEMA, synchronous=True
|
||||
)
|
||||
async def menu_show_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"display_menu.hide", HideAction, MENU_ACTION_SCHEMA, synchronous=True
|
||||
)
|
||||
async def menu_hide_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"display_menu.show_main",
|
||||
ShowMainAction,
|
||||
MENU_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def menu_show_main_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
"display_menu.is_active",
|
||||
IsActiveCondition,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(CONF_ID): cv.use_id(DisplayMenuComponent),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def display_menu_is_active_to_code(config, condition_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(condition_id, template_arg, paren)
|
||||
|
||||
|
||||
async def menu_item_to_code(menu, config, parent):
|
||||
|
||||
@@ -5,94 +5,6 @@
|
||||
|
||||
namespace esphome::display_menu_base {
|
||||
|
||||
template<typename... Ts> class UpAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit UpAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(const Ts &...x) override { this->menu_->up(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class DownAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit DownAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(const Ts &...x) override { this->menu_->down(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class LeftAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit LeftAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(const Ts &...x) override { this->menu_->left(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class RightAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit RightAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(const Ts &...x) override { this->menu_->right(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EnterAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit EnterAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(const Ts &...x) override { this->menu_->enter(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class ShowAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit ShowAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(const Ts &...x) override { this->menu_->show(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class HideAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit HideAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(const Ts &...x) override { this->menu_->hide(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class ShowMainAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit ShowMainAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(const Ts &...x) override { this->menu_->show_main(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
};
|
||||
template<typename... Ts> class IsActiveCondition final : public Condition<Ts...> {
|
||||
public:
|
||||
explicit IsActiveCondition(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
bool check(const Ts &...x) override { return this->menu_->is_active(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
};
|
||||
|
||||
class DisplayMenuOnEnterTrigger final : public Trigger<const MenuItem *> {
|
||||
public:
|
||||
explicit DisplayMenuOnEnterTrigger(MenuItem *parent) : parent_(parent) {
|
||||
|
||||
@@ -55,13 +55,4 @@ class DS1307Component final : public time::RealTimeClock, public i2c::I2CDevice
|
||||
} ds1307_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class WriteAction final : public Action<Ts...>, public Parented<DS1307Component> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->write_time(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class ReadAction final : public Action<Ts...>, public Parented<DS1307Component> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->read_time(); }
|
||||
};
|
||||
} // namespace esphome::ds1307
|
||||
|
||||
@@ -3,16 +3,12 @@ import esphome.codegen as cg
|
||||
from esphome.components import i2c, time
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@badbadc0ffee"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
ds1307_ns = cg.esphome_ns.namespace("ds1307")
|
||||
DS1307Component = ds1307_ns.class_("DS1307Component", time.RealTimeClock, i2c.I2CDevice)
|
||||
WriteAction = ds1307_ns.class_("WriteAction", automation.Action)
|
||||
ReadAction = ds1307_ns.class_("ReadAction", automation.Action)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = time.TIME_SCHEMA.extend(
|
||||
@@ -22,46 +18,25 @@ CONFIG_SCHEMA = time.TIME_SCHEMA.extend(
|
||||
).extend(i2c.i2c_device_schema(0x68))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"ds1307.write_time",
|
||||
WriteAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(DS1307Component),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyCall("write_time()"),
|
||||
)
|
||||
async def ds1307_write_time_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"ds1307.read_time",
|
||||
ReadAction,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(DS1307Component),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyCall("read_time()"),
|
||||
)
|
||||
async def ds1307_read_time_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
@@ -47,27 +46,4 @@ class DutyTimeSensor final : public sensor::Sensor, public PollingComponent {
|
||||
bool restore_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class BaseAction : public Action<Ts...>, public Parented<DutyTimeSensor> {};
|
||||
|
||||
template<typename... Ts> class StartAction : public BaseAction<Ts...> {
|
||||
void play(const Ts &...x) override { this->parent_->start(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class StopAction : public BaseAction<Ts...> {
|
||||
void play(const Ts &...x) override { this->parent_->stop(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class ResetAction : public BaseAction<Ts...> {
|
||||
void play(const Ts &...x) override { this->parent_->reset(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class RunningCondition final : public Condition<Ts...>, public Parented<DutyTimeSensor> {
|
||||
public:
|
||||
explicit RunningCondition(DutyTimeSensor *parent, bool state) : Parented(parent), state_(state) {}
|
||||
|
||||
protected:
|
||||
bool check(const Ts &...x) override { return this->parent_->is_running() == this->state_; }
|
||||
bool state_;
|
||||
};
|
||||
|
||||
} // namespace esphome::duty_time_sensor
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
from esphome.automation import (
|
||||
Action,
|
||||
Condition,
|
||||
maybe_simple_id,
|
||||
register_action,
|
||||
register_condition,
|
||||
)
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor, sensor
|
||||
import esphome.config_validation as cv
|
||||
@@ -19,8 +13,6 @@ from esphome.const import (
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
UNIT_SECOND,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CONF_LAST_TIME = "last_time"
|
||||
@@ -29,14 +21,6 @@ duty_time_sensor_ns = cg.esphome_ns.namespace("duty_time_sensor")
|
||||
DutyTimeSensor = duty_time_sensor_ns.class_(
|
||||
"DutyTimeSensor", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
BaseAction = duty_time_sensor_ns.class_("BaseAction", Action, cg.Parented)
|
||||
StartAction = duty_time_sensor_ns.class_("StartAction", BaseAction)
|
||||
StopAction = duty_time_sensor_ns.class_("StopAction", BaseAction)
|
||||
ResetAction = duty_time_sensor_ns.class_("ResetAction", BaseAction)
|
||||
SetAction = duty_time_sensor_ns.class_("SetAction", BaseAction)
|
||||
RunningCondition = duty_time_sensor_ns.class_(
|
||||
"RunningCondition", Condition, cg.Parented
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
@@ -86,76 +70,25 @@ async def to_code(config: ConfigType) -> None:
|
||||
|
||||
# AUTOMATIONS
|
||||
|
||||
DUTY_TIME_ID_SCHEMA = maybe_simple_id(
|
||||
DUTY_TIME_ID_SCHEMA = automation.maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(DutyTimeSensor),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@register_action(
|
||||
"sensor.duty_time.start", StartAction, DUTY_TIME_ID_SCHEMA, synchronous=True
|
||||
for _name, _call in (
|
||||
("sensor.duty_time.start", "start()"),
|
||||
("sensor.duty_time.stop", "stop()"),
|
||||
("sensor.duty_time.reset", "reset()"),
|
||||
):
|
||||
automation.register_apply_action(
|
||||
_name, DUTY_TIME_ID_SCHEMA, automation.ApplyCall(_call)
|
||||
)
|
||||
|
||||
automation.register_apply_condition(
|
||||
"sensor.duty_time.is_running", DUTY_TIME_ID_SCHEMA, "is_running()"
|
||||
)
|
||||
async def sensor_runtime_start_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
@register_action(
|
||||
"sensor.duty_time.stop", StopAction, DUTY_TIME_ID_SCHEMA, synchronous=True
|
||||
automation.register_apply_condition(
|
||||
"sensor.duty_time.is_not_running", DUTY_TIME_ID_SCHEMA, "is_running() == false"
|
||||
)
|
||||
async def sensor_runtime_stop_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
@register_action(
|
||||
"sensor.duty_time.reset", ResetAction, DUTY_TIME_ID_SCHEMA, synchronous=True
|
||||
)
|
||||
async def sensor_runtime_reset_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
@register_condition(
|
||||
"sensor.duty_time.is_running", RunningCondition, DUTY_TIME_ID_SCHEMA
|
||||
)
|
||||
async def duty_time_is_running_to_code(
|
||||
config: ConfigType,
|
||||
condition_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(condition_id, template_arg, paren, True)
|
||||
|
||||
|
||||
@register_condition(
|
||||
"sensor.duty_time.is_not_running", RunningCondition, DUTY_TIME_ID_SCHEMA
|
||||
)
|
||||
async def duty_time_is_not_running_to_code(
|
||||
config: ConfigType,
|
||||
condition_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(condition_id, template_arg, paren, False)
|
||||
|
||||
@@ -11,8 +11,7 @@ from esphome.const import (
|
||||
CONF_RX_BUFFER_SIZE,
|
||||
CONF_UART_ID,
|
||||
)
|
||||
from esphome.core import CORE, ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.core import CORE
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -23,9 +22,6 @@ DEPENDENCIES = ["uart"]
|
||||
emontx_ns = cg.esphome_ns.namespace("emontx")
|
||||
EmonTx = emontx_ns.class_("EmonTx", cg.Component, uart.UARTDevice)
|
||||
|
||||
# Action to send command to emonTx
|
||||
EmonTxSendCommandAction = emontx_ns.class_("EmonTxSendCommandAction", automation.Action)
|
||||
|
||||
CONF_EMONTX_ID = "emontx_id"
|
||||
CONF_TAG_NAME = "tag_name"
|
||||
CONF_ON_JSON = "on_json"
|
||||
@@ -139,20 +135,16 @@ EMONTX_SEND_COMMAND_ACTION_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
def _plain_literal(config: ConfigType, value: str) -> str:
|
||||
return str(cg.safe_exp(value))
|
||||
|
||||
|
||||
automation.register_apply_action(
|
||||
"emontx.send_command",
|
||||
EmonTxSendCommandAction,
|
||||
EMONTX_SEND_COMMAND_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
# A constant is a plain literal for the const char * overload; a lambda returns a
|
||||
# std::string and takes the inline overload.
|
||||
automation.ApplyField(
|
||||
CONF_COMMAND, "send_command", cg.std_string, const_fn=_plain_literal
|
||||
),
|
||||
)
|
||||
async def emontx_send_command_action_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
template_ = await cg.templatable(config[CONF_COMMAND], args, cg.std_string)
|
||||
cg.add(var.set_command(template_))
|
||||
return var
|
||||
|
||||
@@ -94,9 +94,9 @@ void EmonTx::dump_config() {
|
||||
*
|
||||
* @param command The command string to send (LF will be appended automatically).
|
||||
*/
|
||||
void EmonTx::send_command(const std::string &command) {
|
||||
ESP_LOGD(TAG, "Sending command to emonTx: %s", command.c_str());
|
||||
this->write_str(command.c_str());
|
||||
void EmonTx::send_command(const char *command) {
|
||||
ESP_LOGD(TAG, "Sending command to emonTx: %s", command);
|
||||
this->write_str(command);
|
||||
this->write_byte('\n');
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
@@ -39,7 +38,8 @@ class EmonTx final : public Component, public uart::UARTDevice {
|
||||
template<typename F> void add_on_data_callback(F &&callback) { this->data_callbacks_.add(std::forward<F>(callback)); }
|
||||
|
||||
// Send command to emonTx via UART
|
||||
void send_command(const std::string &command);
|
||||
void send_command(const char *command);
|
||||
void send_command(const std::string &command) { this->send_command(command.c_str()); }
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void init_sensors(size_t count) { this->sensors_.init(count); }
|
||||
@@ -58,12 +58,4 @@ class EmonTx final : public Component, public uart::UARTDevice {
|
||||
std::array<char, MAX_LINE_LENGTH + 1> buffer_{};
|
||||
};
|
||||
|
||||
// Action to send command to emonTx
|
||||
template<typename... Ts> class EmonTxSendCommandAction final : public Action<Ts...>, public Parented<EmonTx> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(std::string, command)
|
||||
|
||||
void play(const Ts &...x) override { this->parent_->send_command(this->command_.value(x...)); }
|
||||
};
|
||||
|
||||
} // namespace esphome::emontx
|
||||
|
||||
@@ -32,8 +32,7 @@ from esphome.const import (
|
||||
CONF_NAME,
|
||||
CONF_NAME_ADD_MAC_SUFFIX,
|
||||
)
|
||||
from esphome.core import CORE, ID, TimePeriod
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.core import CORE, TimePeriod
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -606,38 +605,27 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add_define("USE_ESP32_BLE_UUID")
|
||||
|
||||
|
||||
@automation.register_condition("ble.enabled", BLEEnabledCondition, cv.Schema({}))
|
||||
async def ble_enabled_to_code(
|
||||
config: ConfigType,
|
||||
condition_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
return cg.new_Pvariable(condition_id, template_arg)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ble.enable", BLEEnableAction, cv.Schema({}), synchronous=True
|
||||
automation.register_bare_condition(
|
||||
"ble.enabled",
|
||||
BLEEnabledCondition,
|
||||
cv.Schema({}),
|
||||
)
|
||||
async def ble_enable_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
return cg.new_Pvariable(action_id, template_arg)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ble.disable", BLEDisableAction, cv.Schema({}), synchronous=True
|
||||
automation.register_bare_action(
|
||||
"ble.enable",
|
||||
BLEEnableAction,
|
||||
cv.Schema({}),
|
||||
synchronous=True,
|
||||
)
|
||||
|
||||
|
||||
automation.register_bare_action(
|
||||
"ble.disable",
|
||||
BLEDisableAction,
|
||||
cv.Schema({}),
|
||||
synchronous=True,
|
||||
)
|
||||
async def ble_disable_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
return cg.new_Pvariable(action_id, template_arg)
|
||||
|
||||
|
||||
# ble_advertising.cpp is fully #ifdef'd on USE_ESP32_BLE_ADVERTISING, set
|
||||
|
||||
@@ -3,7 +3,6 @@ from pathlib import Path
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from esphome.build_helpers.pch import pch_extra_scripts
|
||||
@@ -39,7 +38,7 @@ from esphome.platformio.toolchain import copy_ccache_script, copy_pch_script
|
||||
from esphome.storage_json import StorageJSON
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .boards import BOARDS, ESP8266_BOARD_BUILD, board_ld_script
|
||||
from .boards import BOARDS, board_ld_script
|
||||
from .const import (
|
||||
BUILD_FLASH_MODES,
|
||||
CONF_EARLY_PIN_INIT,
|
||||
@@ -47,6 +46,7 @@ from .const import (
|
||||
CONF_ENABLE_SERIAL1,
|
||||
CONF_RESTORE_FROM_FLASH,
|
||||
KEY_BOARD,
|
||||
KEY_DECODE_TOOLS,
|
||||
KEY_ESP8266,
|
||||
KEY_PIN_INITIAL_STATES,
|
||||
KEY_SCANF_FLOAT,
|
||||
@@ -142,12 +142,7 @@ def _validate_native_toolchain(config: ConfigType) -> ConfigType:
|
||||
"'toolchain: arduino' does not support a custom framework source; "
|
||||
"use 'toolchain: platformio'"
|
||||
)
|
||||
# BOARDS is a subset of ESP8266_BOARD_BUILD today; the second clause is
|
||||
# a drift guard for the independently regenerated tables
|
||||
if (
|
||||
config[CONF_BOARD] not in BOARDS
|
||||
or config[CONF_BOARD] not in ESP8266_BOARD_BUILD
|
||||
):
|
||||
if config[CONF_BOARD] not in BOARDS:
|
||||
raise cv.Invalid(
|
||||
f"Board '{config[CONF_BOARD]}' is not supported by "
|
||||
"'toolchain: arduino'; use 'toolchain: platformio'"
|
||||
@@ -594,73 +589,53 @@ ESP8266_EXCEPTION_CODES = {
|
||||
}
|
||||
|
||||
|
||||
_DECODE_WARNED_AT: dict[str, float] = {}
|
||||
def _resolve_decode_tools(config: ConfigType) -> tuple[str, str] | None:
|
||||
"""``(addr2line, elf)`` for this build, or None after warning why."""
|
||||
if (native_toolchain := native_toolchain_module()) is not None:
|
||||
addr2line = native_toolchain.get_addr2line_path()
|
||||
elf = native_toolchain.get_elf_path()
|
||||
for path in (addr2line, elf):
|
||||
if not path.is_file():
|
||||
_LOGGER.warning("Cannot decode crash addresses: %s missing", path)
|
||||
return None
|
||||
return str(addr2line), str(elf)
|
||||
from esphome.platformio import toolchain
|
||||
|
||||
idedata = toolchain.get_idedata(config)
|
||||
if not idedata.addr2line_path or not idedata.firmware_elf_path:
|
||||
_LOGGER.warning("Cannot decode crash addresses: no addr2line or ELF in idedata")
|
||||
return None
|
||||
return idedata.addr2line_path, idedata.firmware_elf_path
|
||||
|
||||
|
||||
def _warn_decode_problem(key: str, message: str, *args) -> bool:
|
||||
"""Warn, deduplicated briefly so a burst of stack-dump addresses warns
|
||||
once but a later dump warns again; returns whether it warned so the
|
||||
caller can mark suppressed addresses individually."""
|
||||
now = time.monotonic()
|
||||
last = _DECODE_WARNED_AT.get(key)
|
||||
if last is not None and now - last < 30:
|
||||
return False
|
||||
_DECODE_WARNED_AT[key] = now
|
||||
_LOGGER.warning(message, *args)
|
||||
return True
|
||||
def _decode_tools(config: ConfigType) -> tuple[str, str] | None:
|
||||
"""Resolved once per run: the tools are a property of the build, not of
|
||||
the address, so a stack dump cannot repeat the failure warning."""
|
||||
data = CORE.data.setdefault(KEY_ESP8266, {})
|
||||
if KEY_DECODE_TOOLS not in data:
|
||||
data[KEY_DECODE_TOOLS] = _resolve_decode_tools(config)
|
||||
return data[KEY_DECODE_TOOLS]
|
||||
|
||||
|
||||
def _decode_pc(config: ConfigType, addr: str, *, bulk: bool = False) -> None:
|
||||
"""Decode one crash address. ``bulk``: the caller is scanning every
|
||||
8-hex stack word, most of which are not code addresses -- unmappable
|
||||
ones log at debug so real frames are not buried."""
|
||||
if (native_toolchain := native_toolchain_module()) is not None:
|
||||
addr2line = native_toolchain.get_addr2line_path()
|
||||
elf = native_toolchain.get_elf_path()
|
||||
for path in (addr2line, elf):
|
||||
if not path.is_file():
|
||||
_warn_decode_problem(
|
||||
str(path), "Cannot decode crash addresses: %s missing", path
|
||||
)
|
||||
# The detailed warning names no address; mark named
|
||||
# registers, but bulk stack words at debug (~150 per dump)
|
||||
log = _LOGGER.debug if bulk else _LOGGER.warning
|
||||
log("Not decoded %s (toolchain file missing)", addr)
|
||||
return
|
||||
addr2line, elf = str(addr2line), str(elf)
|
||||
else:
|
||||
from esphome.platformio import toolchain
|
||||
|
||||
idedata = toolchain.get_idedata(config)
|
||||
if not idedata.addr2line_path or not idedata.firmware_elf_path:
|
||||
_warn_decode_problem(
|
||||
"no-addr2line",
|
||||
"Cannot decode crash addresses: no addr2line or ELF in idedata",
|
||||
)
|
||||
log = _LOGGER.debug if bulk else _LOGGER.warning
|
||||
log("Not decoded %s (no addr2line or ELF)", addr)
|
||||
return
|
||||
addr2line, elf = idedata.addr2line_path, idedata.firmware_elf_path
|
||||
# Bulk stack words are ~150 per dump; only named registers warn
|
||||
log = _LOGGER.debug if bulk else _LOGGER.warning
|
||||
if (tools := _decode_tools(config)) is None:
|
||||
log("Not decoded %s (no addr2line or ELF)", addr)
|
||||
return
|
||||
addr2line, elf = tools
|
||||
command = [addr2line, "-pfiaC", "-e", elf, addr]
|
||||
try:
|
||||
translation = subprocess.check_output(command, close_fds=False).decode().strip()
|
||||
except Exception as err: # noqa: BLE001 # pylint: disable=broad-except
|
||||
# Warn (rate-limited across a dump); mark undecoded addresses
|
||||
# inline or the rest read as merely unmappable
|
||||
if not _warn_decode_problem(
|
||||
"addr2line-failed", "Could not decode crash address %s (%s)", addr, err
|
||||
):
|
||||
# The detailed warning already named this address; mark only
|
||||
# the rate-limited ones, and bulk stack words at debug
|
||||
log = _LOGGER.debug if bulk else _LOGGER.warning
|
||||
log("Not decoded %s (addr2line failed)", addr)
|
||||
log("Could not decode crash address %s (%s)", addr, err)
|
||||
_LOGGER.debug("Caught exception for command %s", command, exc_info=1)
|
||||
return
|
||||
|
||||
if "?? ??:0" in translation:
|
||||
# A named register that fails to decode is confusing silence; a
|
||||
# bulk stack word failing is the expected common case
|
||||
log = _LOGGER.debug if bulk else _LOGGER.warning
|
||||
log("Not decoded %s (address not in %s)", addr, elf)
|
||||
return
|
||||
translation = translation.replace(" at ??:?", "").replace(":?", "")
|
||||
|
||||
@@ -19,6 +19,8 @@ KEY_SERIAL1_REQUIRED = "serial1_required"
|
||||
KEY_SCANF_FLOAT = "scanf_float"
|
||||
# Per-board flash-layout override consumed by board_ld_script()
|
||||
KEY_LDSCRIPT = "ldscript"
|
||||
# Crash-decode tools, resolved once per run by __init__
|
||||
KEY_DECODE_TOOLS = "decode_tools"
|
||||
|
||||
# esp8266 namespace is already defined by arduino, manually prefix esphome
|
||||
esp8266_ns = cg.global_ns.namespace("esphome").namespace("esp8266")
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from typing import Any
|
||||
|
||||
from esphome.automation import Action, register_action
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.esp32 import VARIANT_ESP32P4, only_on_variant
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID, CONF_VOLTAGE
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.final_validate import full_config
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -16,7 +15,6 @@ DOMAIN = "esp_ldo"
|
||||
|
||||
esp_ldo_ns = cg.esphome_ns.namespace("esp_ldo")
|
||||
EspLdo = esp_ldo_ns.class_("EspLdo", cg.Component)
|
||||
AdjustAction = esp_ldo_ns.class_("AdjustAction", Action)
|
||||
|
||||
CHANNELS = (1, 2, 3, 4)
|
||||
CHANNELS_INTERNAL = (1, 2)
|
||||
@@ -123,9 +121,8 @@ def adjusted_ldo_id(value: Any) -> ID:
|
||||
return value
|
||||
|
||||
|
||||
@register_action(
|
||||
automation.register_apply_action(
|
||||
"esp_ldo.voltage.adjust",
|
||||
AdjustAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_ID): adjusted_ldo_id,
|
||||
@@ -134,16 +131,5 @@ def adjusted_ldo_id(value: Any) -> ID:
|
||||
),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_VOLTAGE, "adjust_voltage", cg.float_),
|
||||
)
|
||||
async def ldo_voltage_adjust_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
parent = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, parent)
|
||||
template_ = await cg.templatable(config[CONF_VOLTAGE], args, cg.float_)
|
||||
cg.add(var.set_voltage(template_))
|
||||
return var
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#ifdef USE_ESP32_VARIANT_ESP32P4
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esp_ldo_regulator.h"
|
||||
|
||||
namespace esphome::esp_ldo {
|
||||
@@ -27,18 +26,6 @@ class EspLdo final : public Component {
|
||||
esp_ldo_channel_handle_t handle_{};
|
||||
};
|
||||
|
||||
template<typename... Ts> class AdjustAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit AdjustAction(EspLdo *ldo) : ldo_(ldo) {}
|
||||
|
||||
TEMPLATABLE_VALUE(float, voltage)
|
||||
|
||||
void play(const Ts &...x) override { this->ldo_->adjust_voltage(this->voltage_.value(x...)); }
|
||||
|
||||
protected:
|
||||
EspLdo *ldo_;
|
||||
};
|
||||
|
||||
} // namespace esphome::esp_ldo
|
||||
|
||||
#endif // USE_ESP32_VARIANT_ESP32P4
|
||||
|
||||
@@ -53,12 +53,10 @@ from esphome.const import (
|
||||
)
|
||||
from esphome.core import (
|
||||
CORE,
|
||||
ID,
|
||||
CoroPriority,
|
||||
TimePeriodMilliseconds,
|
||||
coroutine_with_priority,
|
||||
)
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -924,24 +922,15 @@ def _filter_source_files() -> list[str]:
|
||||
FILTER_SOURCE_FILES = _filter_source_files
|
||||
|
||||
|
||||
async def _new_pvariable_to_code(
|
||||
config: ConfigType,
|
||||
id_: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
return cg.new_Pvariable(id_, template_arg)
|
||||
|
||||
|
||||
for _name, _cls in (
|
||||
("ethernet.connected", EthernetConnectedCondition),
|
||||
("ethernet.enabled", EthernetEnabledCondition),
|
||||
):
|
||||
automation.register_condition(_name, _cls, cv.Schema({}))(_new_pvariable_to_code)
|
||||
for _name, _cls in (
|
||||
("ethernet.enable", EthernetEnableAction),
|
||||
("ethernet.disable", EthernetDisableAction),
|
||||
):
|
||||
automation.register_action(_name, _cls, cv.Schema({}), synchronous=True)(
|
||||
_new_pvariable_to_code
|
||||
)
|
||||
automation.register_bare_condition(
|
||||
"ethernet.connected", EthernetConnectedCondition, cv.Schema({})
|
||||
)
|
||||
automation.register_bare_condition(
|
||||
"ethernet.enabled", EthernetEnabledCondition, cv.Schema({})
|
||||
)
|
||||
automation.register_bare_action(
|
||||
"ethernet.enable", EthernetEnableAction, cv.Schema({}), synchronous=True
|
||||
)
|
||||
automation.register_bare_action(
|
||||
"ethernet.disable", EthernetDisableAction, cv.Schema({}), synchronous=True
|
||||
)
|
||||
|
||||
@@ -44,112 +44,19 @@ EZO_PMP_NO_ARGS_ACTION_SCHEMA = maybe_simple_id(
|
||||
}
|
||||
)
|
||||
|
||||
# Actions that do not require more arguments
|
||||
|
||||
EzoPMPFindAction = ezo_pmp_ns.class_("EzoPMPFindAction", automation.Action)
|
||||
EzoPMPClearTotalVolumeDispensedAction = ezo_pmp_ns.class_(
|
||||
"EzoPMPClearTotalVolumeDispensedAction", automation.Action
|
||||
)
|
||||
EzoPMPClearCalibrationAction = ezo_pmp_ns.class_(
|
||||
"EzoPMPClearCalibrationAction", automation.Action
|
||||
)
|
||||
EzoPMPPauseDosingAction = ezo_pmp_ns.class_(
|
||||
"EzoPMPPauseDosingAction", automation.Action
|
||||
)
|
||||
EzoPMPStopDosingAction = ezo_pmp_ns.class_("EzoPMPStopDosingAction", automation.Action)
|
||||
EzoPMPDoseContinuouslyAction = ezo_pmp_ns.class_(
|
||||
"EzoPMPDoseContinuouslyAction", automation.Action
|
||||
)
|
||||
|
||||
# Actions that require more arguments
|
||||
EzoPMPDoseVolumeAction = ezo_pmp_ns.class_("EzoPMPDoseVolumeAction", automation.Action)
|
||||
EzoPMPDoseVolumeOverTimeAction = ezo_pmp_ns.class_(
|
||||
"EzoPMPDoseVolumeOverTimeAction", automation.Action
|
||||
)
|
||||
EzoPMPDoseWithConstantFlowRateAction = ezo_pmp_ns.class_(
|
||||
"EzoPMPDoseWithConstantFlowRateAction", automation.Action
|
||||
)
|
||||
EzoPMPSetCalibrationVolumeAction = ezo_pmp_ns.class_(
|
||||
"EzoPMPSetCalibrationVolumeAction", automation.Action
|
||||
)
|
||||
EzoPMPChangeI2CAddressAction = ezo_pmp_ns.class_(
|
||||
"EzoPMPChangeI2CAddressAction", automation.Action
|
||||
)
|
||||
EzoPMPArbitraryCommandAction = ezo_pmp_ns.class_(
|
||||
"EzoPMPArbitraryCommandAction", automation.Action
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ezo_pmp.find",
|
||||
EzoPMPFindAction,
|
||||
EZO_PMP_NO_ARGS_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def ezo_pmp_find_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ezo_pmp.dose_continuously",
|
||||
EzoPMPDoseContinuouslyAction,
|
||||
EZO_PMP_NO_ARGS_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def ezo_pmp_dose_continuously_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ezo_pmp.clear_total_volume_dosed",
|
||||
EzoPMPClearTotalVolumeDispensedAction,
|
||||
EZO_PMP_NO_ARGS_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def ezo_pmp_clear_total_volume_dosed_to_code(
|
||||
config, action_id, template_arg, args
|
||||
for _name, _method in (
|
||||
("ezo_pmp.find", "find()"),
|
||||
("ezo_pmp.dose_continuously", "dose_continuously()"),
|
||||
("ezo_pmp.clear_total_volume_dosed", "clear_total_volume_dosed()"),
|
||||
("ezo_pmp.clear_calibration", "clear_calibration()"),
|
||||
("ezo_pmp.pause_dosing", "pause_dosing()"),
|
||||
("ezo_pmp.stop_dosing", "stop_dosing()"),
|
||||
):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
automation.register_apply_action(
|
||||
_name, EZO_PMP_NO_ARGS_ACTION_SCHEMA, automation.ApplyCall(_method)
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ezo_pmp.clear_calibration",
|
||||
EzoPMPClearCalibrationAction,
|
||||
EZO_PMP_NO_ARGS_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def ezo_pmp_clear_calibration_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ezo_pmp.pause_dosing",
|
||||
EzoPMPPauseDosingAction,
|
||||
EZO_PMP_NO_ARGS_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def ezo_pmp_pause_dosing_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ezo_pmp.stop_dosing",
|
||||
EzoPMPStopDosingAction,
|
||||
EZO_PMP_NO_ARGS_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def ezo_pmp_stop_dosing_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
# Actions that require Multiple Args
|
||||
|
||||
EZO_PMP_DOSE_VOLUME_ACTION_SCHEMA = cv.All(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(EzoPMP),
|
||||
@@ -159,21 +66,11 @@ EZO_PMP_DOSE_VOLUME_ACTION_SCHEMA = cv.All(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"ezo_pmp.dose_volume",
|
||||
EzoPMPDoseVolumeAction,
|
||||
EZO_PMP_DOSE_VOLUME_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_VOLUME, "dose_volume", cg.double),
|
||||
)
|
||||
async def ezo_pmp_dose_volume_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
template_ = await cg.templatable(config[CONF_VOLUME], args, cg.double)
|
||||
cg.add(var.set_volume(template_))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
EZO_PMP_DOSE_VOLUME_OVER_TIME_ACTION_SCHEMA = cv.All(
|
||||
@@ -188,24 +85,14 @@ EZO_PMP_DOSE_VOLUME_OVER_TIME_ACTION_SCHEMA = cv.All(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"ezo_pmp.dose_volume_over_time",
|
||||
EzoPMPDoseVolumeOverTimeAction,
|
||||
EZO_PMP_DOSE_VOLUME_OVER_TIME_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyCall(
|
||||
"dose_volume_over_time({}, {})",
|
||||
((CONF_VOLUME, cg.double), (CONF_DURATION, cg.int_)),
|
||||
),
|
||||
)
|
||||
async def ezo_pmp_dose_volume_over_time_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
template_ = await cg.templatable(config[CONF_VOLUME], args, cg.double)
|
||||
cg.add(var.set_volume(template_))
|
||||
|
||||
template_ = await cg.templatable(config[CONF_DURATION], args, cg.int_)
|
||||
cg.add(var.set_duration(template_))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
EZO_PMP_DOSE_WITH_CONSTANT_FLOW_RATE_ACTION_SCHEMA = cv.All(
|
||||
@@ -220,26 +107,14 @@ EZO_PMP_DOSE_WITH_CONSTANT_FLOW_RATE_ACTION_SCHEMA = cv.All(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"ezo_pmp.dose_with_constant_flow_rate",
|
||||
EzoPMPDoseWithConstantFlowRateAction,
|
||||
EZO_PMP_DOSE_WITH_CONSTANT_FLOW_RATE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyCall(
|
||||
"dose_with_constant_flow_rate({}, {})",
|
||||
((CONF_VOLUME_PER_MINUTE, cg.double), (CONF_DURATION, cg.int_)),
|
||||
),
|
||||
)
|
||||
async def ezo_pmp_dose_with_constant_flow_rate_to_code(
|
||||
config, action_id, template_arg, args
|
||||
):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
template_ = await cg.templatable(config[CONF_VOLUME_PER_MINUTE], args, cg.double)
|
||||
cg.add(var.set_volume(template_))
|
||||
|
||||
template_ = await cg.templatable(config[CONF_DURATION], args, cg.int_)
|
||||
cg.add(var.set_duration(template_))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
EZO_PMP_SET_CALIBRATION_VOLUME_ACTION_SCHEMA = cv.All(
|
||||
@@ -251,21 +126,11 @@ EZO_PMP_SET_CALIBRATION_VOLUME_ACTION_SCHEMA = cv.All(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"ezo_pmp.set_calibration_volume",
|
||||
EzoPMPSetCalibrationVolumeAction,
|
||||
EZO_PMP_SET_CALIBRATION_VOLUME_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_VOLUME, "set_calibration_volume", cg.double),
|
||||
)
|
||||
async def ezo_pmp_set_calibration_volume_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
template_ = await cg.templatable(config[CONF_VOLUME], args, cg.double)
|
||||
cg.add(var.set_volume(template_))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
EZO_PMP_CHANGE_I2C_ADDRESS_ACTION_SCHEMA = cv.All(
|
||||
@@ -275,21 +140,11 @@ EZO_PMP_CHANGE_I2C_ADDRESS_ACTION_SCHEMA = cv.All(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"ezo_pmp.change_i2c_address",
|
||||
EzoPMPChangeI2CAddressAction,
|
||||
EZO_PMP_CHANGE_I2C_ADDRESS_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_ADDRESS, "change_i2c_address", cg.int_),
|
||||
)
|
||||
async def ezo_pmp_change_i2c_address_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.int_)
|
||||
cg.add(var.set_address(template_))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
EZO_PMP_ARBITRARY_COMMAND_ACTION_SCHEMA = cv.All(
|
||||
@@ -299,18 +154,8 @@ EZO_PMP_ARBITRARY_COMMAND_ACTION_SCHEMA = cv.All(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"ezo_pmp.arbitrary_command",
|
||||
EzoPMPArbitraryCommandAction,
|
||||
EZO_PMP_ARBITRARY_COMMAND_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_COMMAND, "exec_arbitrary_command", cg.std_string),
|
||||
)
|
||||
async def ezo_pmp_arbitrary_command_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
template_ = await cg.templatable(config[CONF_COMMAND], args, cg.std_string)
|
||||
cg.add(var.set_command(template_))
|
||||
|
||||
return var
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
@@ -113,137 +112,4 @@ class EzoPMP final : public PollingComponent, public i2c::I2CDevice {
|
||||
#endif
|
||||
};
|
||||
|
||||
// Action Templates
|
||||
template<typename... Ts> class EzoPMPFindAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPFindAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override { this->ezopmp_->find(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EzoPMPDoseContinuouslyAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPDoseContinuouslyAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override { this->ezopmp_->dose_continuously(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EzoPMPDoseVolumeAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPDoseVolumeAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override { this->ezopmp_->dose_volume(this->volume_.value(x...)); }
|
||||
TEMPLATABLE_VALUE(double, volume)
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EzoPMPDoseVolumeOverTimeAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPDoseVolumeOverTimeAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
this->ezopmp_->dose_volume_over_time(this->volume_.value(x...), this->duration_.value(x...));
|
||||
}
|
||||
TEMPLATABLE_VALUE(double, volume)
|
||||
TEMPLATABLE_VALUE(int, duration)
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EzoPMPDoseWithConstantFlowRateAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPDoseWithConstantFlowRateAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
this->ezopmp_->dose_with_constant_flow_rate(this->volume_.value(x...), this->duration_.value(x...));
|
||||
}
|
||||
TEMPLATABLE_VALUE(double, volume)
|
||||
TEMPLATABLE_VALUE(int, duration)
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EzoPMPSetCalibrationVolumeAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPSetCalibrationVolumeAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override { this->ezopmp_->set_calibration_volume(this->volume_.value(x...)); }
|
||||
TEMPLATABLE_VALUE(double, volume)
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EzoPMPClearTotalVolumeDispensedAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPClearTotalVolumeDispensedAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override { this->ezopmp_->clear_total_volume_dosed(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EzoPMPClearCalibrationAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPClearCalibrationAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override { this->ezopmp_->clear_calibration(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EzoPMPPauseDosingAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPPauseDosingAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override { this->ezopmp_->pause_dosing(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EzoPMPStopDosingAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPStopDosingAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override { this->ezopmp_->stop_dosing(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EzoPMPChangeI2CAddressAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPChangeI2CAddressAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override { this->ezopmp_->change_i2c_address(this->address_.value(x...)); }
|
||||
TEMPLATABLE_VALUE(int, address)
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class EzoPMPArbitraryCommandAction final : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPArbitraryCommandAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(const Ts &...x) override { this->ezopmp_->exec_arbitrary_command(this->command_.value(x...)); }
|
||||
TEMPLATABLE_VALUE(std::string, command)
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
};
|
||||
|
||||
} // namespace esphome::ezo_pmp
|
||||
|
||||
@@ -37,16 +37,6 @@ FingerprintGrowComponent = fingerprint_grow_ns.class_(
|
||||
"FingerprintGrowComponent", cg.PollingComponent, uart.UARTDevice
|
||||
)
|
||||
|
||||
EnrollmentAction = fingerprint_grow_ns.class_("EnrollmentAction", automation.Action)
|
||||
CancelEnrollmentAction = fingerprint_grow_ns.class_(
|
||||
"CancelEnrollmentAction", automation.Action
|
||||
)
|
||||
DeleteAction = fingerprint_grow_ns.class_("DeleteAction", automation.Action)
|
||||
DeleteAllAction = fingerprint_grow_ns.class_("DeleteAllAction", automation.Action)
|
||||
LEDControlAction = fingerprint_grow_ns.class_("LEDControlAction", automation.Action)
|
||||
AuraLEDControlAction = fingerprint_grow_ns.class_(
|
||||
"AuraLEDControlAction", automation.Action
|
||||
)
|
||||
|
||||
AuraLEDState = fingerprint_grow_ns.enum("GrowAuraLEDState", True)
|
||||
AURA_LED_STATES = {
|
||||
@@ -181,111 +171,68 @@ async def to_code(config):
|
||||
await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"fingerprint_grow.enroll",
|
||||
EnrollmentAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
|
||||
cv.Required(CONF_FINGER_ID): cv.templatable(cv.uint16_t),
|
||||
cv.Optional(CONF_NUM_SCANS): cv.templatable(cv.uint8_t),
|
||||
},
|
||||
key=CONF_FINGER_ID,
|
||||
),
|
||||
synchronous=True,
|
||||
)
|
||||
async def fingerprint_grow_enroll_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
template_ = await cg.templatable(config[CONF_FINGER_ID], args, cg.uint16)
|
||||
cg.add(var.set_finger_id(template_))
|
||||
if CONF_NUM_SCANS in config:
|
||||
template_ = await cg.templatable(config[CONF_NUM_SCANS], args, cg.uint8)
|
||||
cg.add(var.set_num_scans(template_))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"fingerprint_grow.cancel_enroll",
|
||||
CancelEnrollmentAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
)
|
||||
async def fingerprint_grow_cancel_enroll_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"fingerprint_grow.delete",
|
||||
DeleteAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
|
||||
cv.Required(CONF_FINGER_ID): cv.templatable(cv.uint16_t),
|
||||
},
|
||||
key=CONF_FINGER_ID,
|
||||
),
|
||||
synchronous=True,
|
||||
)
|
||||
async def fingerprint_grow_delete_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
template_ = await cg.templatable(config[CONF_FINGER_ID], args, cg.uint16)
|
||||
cg.add(var.set_finger_id(template_))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"fingerprint_grow.delete_all",
|
||||
DeleteAllAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
)
|
||||
async def fingerprint_grow_delete_all_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
FINGERPRINT_GROW_LED_CONTROL_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
FINGERPRINT_GROW_ID_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
|
||||
cv.Required(CONF_STATE): cv.templatable(cv.boolean),
|
||||
},
|
||||
key=CONF_STATE,
|
||||
}
|
||||
)
|
||||
|
||||
automation.register_apply_action(
|
||||
"fingerprint_grow.enroll",
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
|
||||
cv.Required(CONF_FINGER_ID): cv.templatable(cv.uint16_t),
|
||||
cv.Optional(CONF_NUM_SCANS, default=2): cv.templatable(
|
||||
cv.int_range(min=1, max=255)
|
||||
),
|
||||
},
|
||||
key=CONF_FINGER_ID,
|
||||
),
|
||||
automation.ApplyCall(
|
||||
"enroll_fingerprint({}, {})",
|
||||
((CONF_FINGER_ID, cg.uint16), (CONF_NUM_SCANS, cg.uint8)),
|
||||
),
|
||||
)
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"fingerprint_grow.cancel_enroll",
|
||||
FINGERPRINT_GROW_ID_SCHEMA,
|
||||
automation.ApplyCall("finish_enrollment(1)"),
|
||||
)
|
||||
|
||||
automation.register_apply_action(
|
||||
"fingerprint_grow.delete",
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
|
||||
cv.Required(CONF_FINGER_ID): cv.templatable(cv.uint16_t),
|
||||
},
|
||||
key=CONF_FINGER_ID,
|
||||
),
|
||||
automation.ApplyField(CONF_FINGER_ID, "delete_fingerprint", cg.uint16),
|
||||
)
|
||||
|
||||
automation.register_apply_action(
|
||||
"fingerprint_grow.delete_all",
|
||||
FINGERPRINT_GROW_ID_SCHEMA,
|
||||
automation.ApplyCall("delete_all_fingerprints()"),
|
||||
)
|
||||
|
||||
automation.register_apply_action(
|
||||
"fingerprint_grow.led_control",
|
||||
LEDControlAction,
|
||||
FINGERPRINT_GROW_LED_CONTROL_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
|
||||
cv.Required(CONF_STATE): cv.templatable(cv.boolean),
|
||||
},
|
||||
key=CONF_STATE,
|
||||
),
|
||||
automation.ApplyField(CONF_STATE, "led_control", cg.bool_),
|
||||
)
|
||||
async def fingerprint_grow_led_control_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
template_ = await cg.templatable(config[CONF_STATE], args, cg.bool_)
|
||||
cg.add(var.set_state(template_))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"fingerprint_grow.aura_led_control",
|
||||
AuraLEDControlAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(FingerprintGrowComponent),
|
||||
@@ -295,15 +242,13 @@ async def fingerprint_grow_led_control_to_code(config, action_id, template_arg,
|
||||
cv.Required(CONF_COUNT): cv.templatable(cv.uint8_t),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyCall(
|
||||
"aura_led_control({}, {}, {}, {})",
|
||||
(
|
||||
(CONF_STATE, cg.uint8),
|
||||
(CONF_SPEED, cg.uint8),
|
||||
(CONF_COLOR, cg.uint8),
|
||||
(CONF_COUNT, cg.uint8),
|
||||
),
|
||||
),
|
||||
)
|
||||
async def fingerprint_grow_aura_led_control_to_code(
|
||||
config, action_id, template_arg, args
|
||||
):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
for key in [CONF_STATE, CONF_SPEED, CONF_COLOR, CONF_COUNT]:
|
||||
template_ = await cg.templatable(config[key], args, cg.uint8)
|
||||
cg.add(getattr(var, f"set_{key}")(template_))
|
||||
return var
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
@@ -209,71 +208,4 @@ class FingerprintGrowComponent final : public PollingComponent, public uart::UAR
|
||||
CallbackManager<void(uint16_t)> enrollment_failed_callback_;
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class EnrollmentAction final : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, finger_id)
|
||||
TEMPLATABLE_VALUE(uint8_t, num_scans)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
auto finger_id = this->finger_id_.value(x...);
|
||||
auto num_scans = this->num_scans_.value(x...);
|
||||
if (num_scans) {
|
||||
this->parent_->enroll_fingerprint(finger_id, num_scans);
|
||||
} else {
|
||||
this->parent_->enroll_fingerprint(finger_id, 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class CancelEnrollmentAction final : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->finish_enrollment(1); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class DeleteAction final : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, finger_id)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
auto finger_id = this->finger_id_.value(x...);
|
||||
this->parent_->delete_fingerprint(finger_id);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class DeleteAllAction final : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->delete_all_fingerprints(); }
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class LEDControlAction final : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(bool, state)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
auto state = this->state_.value(x...);
|
||||
this->parent_->led_control(state);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class AuraLEDControlAction final : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, state)
|
||||
TEMPLATABLE_VALUE(uint8_t, speed)
|
||||
TEMPLATABLE_VALUE(uint8_t, color)
|
||||
TEMPLATABLE_VALUE(uint8_t, count)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
auto state = this->state_.value(x...);
|
||||
auto speed = this->speed_.value(x...);
|
||||
auto color = this->color_.value(x...);
|
||||
auto count = this->count_.value(x...);
|
||||
|
||||
this->parent_->aura_led_control(state, speed, color, count);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::fingerprint_grow
|
||||
|
||||
@@ -20,28 +20,9 @@ grove_tb6612fng_ns = cg.esphome_ns.namespace("grove_tb6612fng")
|
||||
GROVE_TB6612FNG = grove_tb6612fng_ns.class_(
|
||||
"GroveMotorDriveTB6612FNG", cg.Component, i2c.I2CDevice
|
||||
)
|
||||
GROVETB6612FNGMotorRunAction = grove_tb6612fng_ns.class_(
|
||||
"GROVETB6612FNGMotorRunAction", automation.Action
|
||||
)
|
||||
GROVETB6612FNGMotorBrakeAction = grove_tb6612fng_ns.class_(
|
||||
"GROVETB6612FNGMotorBrakeAction", automation.Action
|
||||
)
|
||||
GROVETB6612FNGMotorStopAction = grove_tb6612fng_ns.class_(
|
||||
"GROVETB6612FNGMotorStopAction", automation.Action
|
||||
)
|
||||
GROVETB6612FNGMotorStandbyAction = grove_tb6612fng_ns.class_(
|
||||
"GROVETB6612FNGMotorStandbyAction", automation.Action
|
||||
)
|
||||
GROVETB6612FNGMotorNoStandbyAction = grove_tb6612fng_ns.class_(
|
||||
"GROVETB6612FNGMotorNoStandbyAction", automation.Action
|
||||
)
|
||||
GROVETB6612FNGMotorChangeAddressAction = grove_tb6612fng_ns.class_(
|
||||
"GROVETB6612FNGMotorChangeAddressAction", automation.Action
|
||||
)
|
||||
|
||||
DIRECTION_TYPE = {
|
||||
"FORWARD": 1,
|
||||
"BACKWARD": 2,
|
||||
"BACKWARD": -1,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
@@ -61,9 +42,8 @@ async def to_code(config):
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"grove_tb6612fng.run",
|
||||
GROVETB6612FNGMotorRunAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
|
||||
@@ -72,109 +52,52 @@ async def to_code(config):
|
||||
cv.Required(CONF_DIRECTION): cv.enum(DIRECTION_TYPE, upper=True),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyCall(
|
||||
"dc_motor_run({}, {} * {})",
|
||||
((CONF_CHANNEL, cg.uint8), (CONF_DIRECTION, cg.int16), (CONF_SPEED, cg.uint16)),
|
||||
),
|
||||
)
|
||||
async def grove_tb6612fng_run_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
template_channel = await cg.templatable(config[CONF_CHANNEL], args, cg.uint8)
|
||||
template_speed = await cg.templatable(config[CONF_SPEED], args, cg.uint16)
|
||||
cg.add(var.set_channel(template_channel))
|
||||
cg.add(var.set_speed(template_speed))
|
||||
cg.add(var.set_direction(config[CONF_DIRECTION] == "FORWARD"))
|
||||
return var
|
||||
CHANNEL_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
|
||||
cv.Required(CONF_CHANNEL): cv.templatable(cv.int_range(min=0, max=1)),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"grove_tb6612fng.break",
|
||||
GROVETB6612FNGMotorBrakeAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
|
||||
cv.Required(CONF_CHANNEL): cv.templatable(cv.int_range(min=0, max=1)),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
CHANNEL_SCHEMA,
|
||||
automation.ApplyField(CONF_CHANNEL, "dc_motor_brake", cg.uint8),
|
||||
)
|
||||
async def grove_tb6612fng_break_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
template_channel = await cg.templatable(config[CONF_CHANNEL], args, cg.uint8)
|
||||
cg.add(var.set_channel(template_channel))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"grove_tb6612fng.stop",
|
||||
GROVETB6612FNGMotorStopAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
|
||||
cv.Required(CONF_CHANNEL): cv.templatable(cv.int_range(min=0, max=1)),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
CHANNEL_SCHEMA,
|
||||
automation.ApplyField(CONF_CHANNEL, "dc_motor_stop", cg.uint8),
|
||||
)
|
||||
async def grove_tb6612fng_stop_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
template_channel = await cg.templatable(config[CONF_CHANNEL], args, cg.uint8)
|
||||
cg.add(var.set_channel(template_channel))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"grove_tb6612fng.standby",
|
||||
GROVETB6612FNGMotorStandbyAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
PARENT_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
|
||||
}
|
||||
)
|
||||
async def grove_tb6612fng_standby_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"grove_tb6612fng.no_standby",
|
||||
GROVETB6612FNGMotorNoStandbyAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.register_apply_action(
|
||||
"grove_tb6612fng.standby", PARENT_SCHEMA, automation.ApplyCall("standby()")
|
||||
)
|
||||
async def grove_tb6612fng_no_standby_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
return var
|
||||
automation.register_apply_action(
|
||||
"grove_tb6612fng.no_standby", PARENT_SCHEMA, automation.ApplyCall("not_standby()")
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"grove_tb6612fng.change_address",
|
||||
GROVETB6612FNGMotorChangeAddressAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
|
||||
cv.Required(CONF_ADDRESS): cv.i2c_address,
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_ADDRESS, "set_i2c_addr", cg.uint8),
|
||||
)
|
||||
async def grove_tb6612fng_change_address_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
template_channel = await cg.templatable(config[CONF_ADDRESS], args, cg.uint8)
|
||||
cg.add(var.set_address(template_channel))
|
||||
return var
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
// #include "esphome/core/helpers.h"
|
||||
@@ -161,61 +160,4 @@ class GroveMotorDriveTB6612FNG final : public Component, public i2c::I2CDevice {
|
||||
uint8_t buffer_[16];
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class GROVETB6612FNGMotorRunAction final : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, channel)
|
||||
TEMPLATABLE_VALUE(uint16_t, speed)
|
||||
|
||||
void set_direction(bool forward) { this->forward_ = forward; }
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
auto channel = this->channel_.value(x...);
|
||||
int16_t speed = this->speed_.value(x...);
|
||||
if (!this->forward_) {
|
||||
speed = -speed;
|
||||
}
|
||||
this->parent_->dc_motor_run(channel, speed);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool forward_{true};
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class GROVETB6612FNGMotorBrakeAction final : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, channel)
|
||||
|
||||
void play(const Ts &...x) override { this->parent_->dc_motor_brake(this->channel_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class GROVETB6612FNGMotorStopAction final : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, channel)
|
||||
|
||||
void play(const Ts &...x) override { this->parent_->dc_motor_stop(this->channel_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class GROVETB6612FNGMotorStandbyAction final : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->standby(); }
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class GROVETB6612FNGMotorNoStandbyAction final : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->not_standby(); }
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class GROVETB6612FNGMotorChangeAddressAction final : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, address)
|
||||
|
||||
void play(const Ts &...x) override { this->parent_->set_i2c_addr(this->address_.value(x...)); }
|
||||
};
|
||||
|
||||
} // namespace esphome::grove_tb6612fng
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "haier_base.h"
|
||||
#include "hon_climate.h"
|
||||
|
||||
namespace esphome::haier {
|
||||
|
||||
template<typename... Ts> class DisplayOnAction final : public Action<Ts...> {
|
||||
public:
|
||||
DisplayOnAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) { this->parent_->set_display_state(true); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayOffAction final : public Action<Ts...> {
|
||||
public:
|
||||
DisplayOffAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) { this->parent_->set_display_state(false); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class BeeperOnAction final : public Action<Ts...> {
|
||||
public:
|
||||
BeeperOnAction(HonClimate *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) { this->parent_->set_beeper_state(true); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class BeeperOffAction final : public Action<Ts...> {
|
||||
public:
|
||||
BeeperOffAction(HonClimate *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) { this->parent_->set_beeper_state(false); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class VerticalAirflowAction final : public Action<Ts...> {
|
||||
public:
|
||||
VerticalAirflowAction(HonClimate *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(hon_protocol::VerticalSwingMode, direction)
|
||||
void play(const Ts &...x) { this->parent_->set_vertical_airflow(this->direction_.value(x...)); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class HorizontalAirflowAction final : public Action<Ts...> {
|
||||
public:
|
||||
HorizontalAirflowAction(HonClimate *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(hon_protocol::HorizontalSwingMode, direction)
|
||||
void play(const Ts &...x) { this->parent_->set_horizontal_airflow(this->direction_.value(x...)); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class HealthOnAction final : public Action<Ts...> {
|
||||
public:
|
||||
HealthOnAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) { this->parent_->set_health_mode(true); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class HealthOffAction final : public Action<Ts...> {
|
||||
public:
|
||||
HealthOffAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) { this->parent_->set_health_mode(false); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class StartSelfCleaningAction final : public Action<Ts...> {
|
||||
public:
|
||||
StartSelfCleaningAction(HonClimate *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) { this->parent_->start_self_cleaning(); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class StartSteriCleaningAction final : public Action<Ts...> {
|
||||
public:
|
||||
StartSteriCleaningAction(HonClimate *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) { this->parent_->start_steri_cleaning(); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class PowerOnAction final : public Action<Ts...> {
|
||||
public:
|
||||
PowerOnAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) { this->parent_->send_power_on_command(); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class PowerOffAction final : public Action<Ts...> {
|
||||
public:
|
||||
PowerOffAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) { this->parent_->send_power_off_command(); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class PowerToggleAction final : public Action<Ts...> {
|
||||
public:
|
||||
PowerToggleAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(const Ts &...x) { this->parent_->toggle_power(); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
};
|
||||
|
||||
} // namespace esphome::haier
|
||||
@@ -9,7 +9,6 @@ from esphome.const import (
|
||||
CONF_BEEPER,
|
||||
CONF_CURRENT_TEMPERATURE,
|
||||
CONF_DISPLAY,
|
||||
CONF_ID,
|
||||
CONF_LEVEL,
|
||||
CONF_LOGGER,
|
||||
CONF_LOGS,
|
||||
@@ -254,23 +253,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
# Actions
|
||||
DisplayOnAction = haier_ns.class_("DisplayOnAction", automation.Action)
|
||||
DisplayOffAction = haier_ns.class_("DisplayOffAction", automation.Action)
|
||||
BeeperOnAction = haier_ns.class_("BeeperOnAction", automation.Action)
|
||||
BeeperOffAction = haier_ns.class_("BeeperOffAction", automation.Action)
|
||||
StartSelfCleaningAction = haier_ns.class_("StartSelfCleaningAction", automation.Action)
|
||||
StartSteriCleaningAction = haier_ns.class_(
|
||||
"StartSteriCleaningAction", automation.Action
|
||||
)
|
||||
VerticalAirflowAction = haier_ns.class_("VerticalAirflowAction", automation.Action)
|
||||
HorizontalAirflowAction = haier_ns.class_("HorizontalAirflowAction", automation.Action)
|
||||
HealthOnAction = haier_ns.class_("HealthOnAction", automation.Action)
|
||||
HealthOffAction = haier_ns.class_("HealthOffAction", automation.Action)
|
||||
PowerOnAction = haier_ns.class_("PowerOnAction", automation.Action)
|
||||
PowerOffAction = haier_ns.class_("PowerOffAction", automation.Action)
|
||||
PowerToggleAction = haier_ns.class_("PowerToggleAction", automation.Action)
|
||||
|
||||
HAIER_BASE_ACTION_SCHEMA = automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HaierClimateBase),
|
||||
@@ -284,62 +266,35 @@ HAIER_HON_BASE_ACTION_SCHEMA = automation.maybe_simple_id(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"climate.haier.display_on",
|
||||
DisplayOnAction,
|
||||
HAIER_BASE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
@automation.register_action(
|
||||
"climate.haier.display_off",
|
||||
DisplayOffAction,
|
||||
HAIER_BASE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def display_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
for _name, _schema, _call in (
|
||||
("climate.haier.display_on", HAIER_BASE_ACTION_SCHEMA, "set_display_state(true)"),
|
||||
("climate.haier.display_off", HAIER_BASE_ACTION_SCHEMA, "set_display_state(false)"),
|
||||
("climate.haier.beeper_on", HAIER_HON_BASE_ACTION_SCHEMA, "set_beeper_state(true)"),
|
||||
(
|
||||
"climate.haier.beeper_off",
|
||||
HAIER_HON_BASE_ACTION_SCHEMA,
|
||||
"set_beeper_state(false)",
|
||||
),
|
||||
(
|
||||
"climate.haier.start_self_cleaning",
|
||||
HAIER_HON_BASE_ACTION_SCHEMA,
|
||||
"start_self_cleaning()",
|
||||
),
|
||||
(
|
||||
"climate.haier.start_steri_cleaning",
|
||||
HAIER_HON_BASE_ACTION_SCHEMA,
|
||||
"start_steri_cleaning()",
|
||||
),
|
||||
("climate.haier.health_on", HAIER_BASE_ACTION_SCHEMA, "set_health_mode(true)"),
|
||||
("climate.haier.health_off", HAIER_BASE_ACTION_SCHEMA, "set_health_mode(false)"),
|
||||
("climate.haier.power_on", HAIER_BASE_ACTION_SCHEMA, "send_power_on_command()"),
|
||||
("climate.haier.power_off", HAIER_BASE_ACTION_SCHEMA, "send_power_off_command()"),
|
||||
("climate.haier.power_toggle", HAIER_BASE_ACTION_SCHEMA, "toggle_power()"),
|
||||
):
|
||||
automation.register_apply_action(_name, _schema, automation.ApplyCall(_call))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"climate.haier.beeper_on",
|
||||
BeeperOnAction,
|
||||
HAIER_HON_BASE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
@automation.register_action(
|
||||
"climate.haier.beeper_off",
|
||||
BeeperOffAction,
|
||||
HAIER_HON_BASE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def beeper_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
# Start self cleaning or steri-cleaning action action
|
||||
@automation.register_action(
|
||||
"climate.haier.start_self_cleaning",
|
||||
StartSelfCleaningAction,
|
||||
HAIER_HON_BASE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
@automation.register_action(
|
||||
"climate.haier.start_steri_cleaning",
|
||||
StartSteriCleaningAction,
|
||||
HAIER_HON_BASE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def start_cleaning_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
# Set vertical airflow direction action
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"climate.haier.set_vertical_airflow",
|
||||
VerticalAirflowAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HonClimate),
|
||||
@@ -348,22 +303,13 @@ async def start_cleaning_to_code(config, action_id, template_arg, args):
|
||||
),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(
|
||||
CONF_VERTICAL_AIRFLOW, "set_vertical_airflow", AirflowVerticalDirection
|
||||
),
|
||||
)
|
||||
async def haier_set_vertical_airflow_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(
|
||||
config[CONF_VERTICAL_AIRFLOW], args, AirflowVerticalDirection
|
||||
)
|
||||
cg.add(var.set_direction(template_))
|
||||
return var
|
||||
|
||||
|
||||
# Set horizontal airflow direction action
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"climate.haier.set_horizontal_airflow",
|
||||
HorizontalAirflowAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HonClimate),
|
||||
@@ -372,56 +318,10 @@ async def haier_set_vertical_airflow_to_code(config, action_id, template_arg, ar
|
||||
),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(
|
||||
CONF_HORIZONTAL_AIRFLOW, "set_horizontal_airflow", AirflowHorizontalDirection
|
||||
),
|
||||
)
|
||||
async def haier_set_horizontal_airflow_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(
|
||||
config[CONF_HORIZONTAL_AIRFLOW], args, AirflowHorizontalDirection
|
||||
)
|
||||
cg.add(var.set_direction(template_))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"climate.haier.health_on",
|
||||
HealthOnAction,
|
||||
HAIER_BASE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
@automation.register_action(
|
||||
"climate.haier.health_off",
|
||||
HealthOffAction,
|
||||
HAIER_BASE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def health_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"climate.haier.power_on",
|
||||
PowerOnAction,
|
||||
HAIER_BASE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
@automation.register_action(
|
||||
"climate.haier.power_off",
|
||||
PowerOffAction,
|
||||
HAIER_BASE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
@automation.register_action(
|
||||
"climate.haier.power_toggle",
|
||||
PowerToggleAction,
|
||||
HAIER_BASE_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def power_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
def _final_validate(config) -> None:
|
||||
|
||||
@@ -7,14 +7,11 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_DECAY_MODE,
|
||||
CONF_ENABLE_PIN,
|
||||
CONF_ID,
|
||||
CONF_PIN_A,
|
||||
CONF_PIN_B,
|
||||
CONF_PRESET_MODES,
|
||||
CONF_SPEED_COUNT,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import hbridge_ns
|
||||
@@ -30,9 +27,6 @@ DECAY_MODE_OPTIONS = {
|
||||
"FAST": DecayMode.DECAY_MODE_FAST,
|
||||
}
|
||||
|
||||
# Actions
|
||||
BrakeAction = hbridge_ns.class_("BrakeAction", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
fan.fan_schema(HBridgeFan)
|
||||
.extend(
|
||||
@@ -51,20 +45,11 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"fan.hbridge.brake",
|
||||
BrakeAction,
|
||||
maybe_simple_id({cv.GenerateID(): cv.use_id(HBridgeFan)}),
|
||||
synchronous=True,
|
||||
automation.ApplyCall("brake()"),
|
||||
)
|
||||
async def fan_hbridge_brake_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/output/binary_output.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/components/fan/fan.h"
|
||||
@@ -46,13 +45,4 @@ class HBridgeFan final : public Component, public fan::Fan {
|
||||
void set_hbridge_levels_(float a_level, float b_level, float enable);
|
||||
};
|
||||
|
||||
template<typename... Ts> class BrakeAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit BrakeAction(HBridgeFan *parent) : parent_(parent) {}
|
||||
|
||||
void play(const Ts &...x) override { this->parent_->brake(); }
|
||||
|
||||
HBridgeFan *parent_;
|
||||
};
|
||||
|
||||
} // namespace esphome::hbridge
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
@@ -26,11 +25,4 @@ class HC8Component final : public PollingComponent, public uart::UARTDevice {
|
||||
bool warmup_complete_{false};
|
||||
};
|
||||
|
||||
template<typename... Ts> class HC8CalibrateAction final : public Action<Ts...>, public Parented<HC8Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, baseline)
|
||||
|
||||
void play(const Ts &...x) override { this->parent_->calibrate(this->baseline_.value(x...)); }
|
||||
};
|
||||
|
||||
} // namespace esphome::hc8
|
||||
|
||||
@@ -12,15 +12,12 @@ from esphome.const import (
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_PARTS_PER_MILLION,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
|
||||
hc8_ns = cg.esphome_ns.namespace("hc8")
|
||||
HC8Component = hc8_ns.class_("HC8Component", cg.PollingComponent, uart.UARTDevice)
|
||||
HC8CalibrateAction = hc8_ns.class_("HC8CalibrateAction", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
@@ -73,20 +70,8 @@ CALIBRATION_ACTION_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"hc8.calibrate",
|
||||
HC8CalibrateAction,
|
||||
CALIBRATION_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_BASELINE, "calibrate", cg.uint16),
|
||||
)
|
||||
async def hc8_calibration_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
template_ = await cg.templatable(config[CONF_BASELINE], args, cg.uint16)
|
||||
cg.add(var.set_baseline(template_))
|
||||
return var
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
@@ -48,21 +47,4 @@ class HDC302XComponent final : public PollingComponent, public i2c::I2CDevice {
|
||||
uint32_t conversion_delay_ms_();
|
||||
};
|
||||
|
||||
template<typename... Ts> class HeaterOnAction final : public Action<Ts...>, public Parented<HDC302XComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, power)
|
||||
TEMPLATABLE_VALUE(uint32_t, duration)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
auto power_val = this->power_.value(x...);
|
||||
auto duration_val = this->duration_.value(x...);
|
||||
this->parent_->start_heater(power_val, duration_val);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class HeaterOffAction final : public Action<Ts...>, public Parented<HDC302XComponent> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->stop_heater(); }
|
||||
};
|
||||
|
||||
} // namespace esphome::hdc302x
|
||||
|
||||
@@ -18,8 +18,6 @@ from esphome.const import (
|
||||
UNIT_CELSIUS,
|
||||
UNIT_PERCENT,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
@@ -37,10 +35,6 @@ POWER_MODE_OPTIONS = {
|
||||
"ULTRA_LOW_POWER": HDC302XPowerMode.ULTRA_LOW_POWER,
|
||||
}
|
||||
|
||||
# Actions
|
||||
HeaterOnAction = hdc302x_ns.class_("HeaterOnAction", automation.Action)
|
||||
HeaterOffAction = hdc302x_ns.class_("HeaterOffAction", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
@@ -118,39 +112,14 @@ HDC302X_HEATER_ON_ACTION_SCHEMA = maybe_simple_id(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"hdc302x.heater_on",
|
||||
HeaterOnAction,
|
||||
HDC302X_HEATER_ON_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyCall(
|
||||
"start_heater({}, {})", ((CONF_POWER, cg.uint16), (CONF_DURATION, cg.uint32))
|
||||
),
|
||||
)
|
||||
async def hdc302x_heater_on_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
template_ = await cg.templatable(config[CONF_POWER], args, cg.uint16)
|
||||
cg.add(var.set_power(template_))
|
||||
template_ = await cg.templatable(config[CONF_DURATION], args, cg.uint32)
|
||||
cg.add(var.set_duration(template_))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"hdc302x.heater_off",
|
||||
HeaterOffAction,
|
||||
HDC302X_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.register_apply_action(
|
||||
"hdc302x.heater_off", HDC302X_ACTION_SCHEMA, automation.ApplyCall("stop_heater()")
|
||||
)
|
||||
async def hdc302x_heater_off_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
namespace esphome::htu21d {
|
||||
|
||||
@@ -34,26 +33,4 @@ class HTU21DComponent final : public PollingComponent, public i2c::I2CDevice {
|
||||
HTU21DSensorModels sensor_model_{HTU21D_SENSOR_MODEL_HTU21D};
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetHeaterLevelAction final : public Action<Ts...>, public Parented<HTU21DComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, level)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
auto level = this->level_.value(x...);
|
||||
|
||||
this->parent_->set_heater_level(level);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetHeaterAction final : public Action<Ts...>, public Parented<HTU21DComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(bool, status)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
auto status = this->status_.value(x...);
|
||||
|
||||
this->parent_->set_heater(status);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::htu21d
|
||||
|
||||
@@ -17,8 +17,6 @@ from esphome.const import (
|
||||
UNIT_EMPTY,
|
||||
UNIT_PERCENT,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
@@ -27,8 +25,6 @@ htu21d_ns = cg.esphome_ns.namespace("htu21d")
|
||||
HTU21DComponent = htu21d_ns.class_(
|
||||
"HTU21DComponent", cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
SetHeaterLevelAction = htu21d_ns.class_("SetHeaterLevelAction", automation.Action)
|
||||
SetHeaterAction = htu21d_ns.class_("SetHeaterAction", automation.Action)
|
||||
HTU21DSensorModels = htu21d_ns.enum("HTU21DSensorModels")
|
||||
|
||||
MODELS = {
|
||||
@@ -86,9 +82,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_sensor_model(config[CONF_MODEL]))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"htu21d.set_heater_level",
|
||||
SetHeaterLevelAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HTU21DComponent),
|
||||
@@ -96,24 +91,11 @@ async def to_code(config: ConfigType) -> None:
|
||||
},
|
||||
key=CONF_LEVEL,
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_LEVEL, "set_heater_level", cg.uint8),
|
||||
)
|
||||
async def set_heater_level_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
level_ = await cg.templatable(config[CONF_LEVEL], args, cg.uint8)
|
||||
cg.add(var.set_level(level_))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"htu21d.set_heater",
|
||||
SetHeaterAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HTU21DComponent),
|
||||
@@ -121,16 +103,5 @@ async def set_heater_level_to_code(
|
||||
},
|
||||
key=CONF_STATUS,
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_STATUS, "set_heater", cg.bool_),
|
||||
)
|
||||
async def set_heater_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
status_ = await cg.templatable(config[CONF_STATUS], args, cg.bool_)
|
||||
cg.add(var.set_status(status_))
|
||||
return var
|
||||
|
||||
@@ -18,8 +18,7 @@ from esphome.const import (
|
||||
CONF_ROTATION,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from esphome.core import ID, EnumValue
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.core import EnumValue
|
||||
import esphome.final_validate as fv
|
||||
from esphome.helpers import add_class_to_obj
|
||||
from esphome.types import ConfigType
|
||||
@@ -167,7 +166,6 @@ ROTATIONS = {
|
||||
HUB75Display = hub75_ns.class_("HUB75Display", cg.PollingComponent, display.Display)
|
||||
Hub75Config = cg.global_ns.struct("Hub75Config")
|
||||
Hub75Pins = cg.global_ns.struct("Hub75Pins")
|
||||
SetBrightnessAction = hub75_ns.class_("SetBrightnessAction", automation.Action)
|
||||
|
||||
|
||||
def _merge_board_pins(config: ConfigType) -> ConfigType:
|
||||
@@ -619,9 +617,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_writer(lambda_))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"hub75.set_brightness",
|
||||
SetBrightnessAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HUB75Display),
|
||||
@@ -629,16 +626,5 @@ async def to_code(config: ConfigType) -> None:
|
||||
},
|
||||
key=CONF_BRIGHTNESS,
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_BRIGHTNESS, "set_brightness", cg.uint8),
|
||||
)
|
||||
async def hub75_set_brightness_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
template_ = await cg.templatable(config[CONF_BRIGHTNESS], args, cg.uint8)
|
||||
cg.add(var.set_brightness(template_))
|
||||
return var
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -51,13 +50,6 @@ class HUB75Display final : public display::Display {
|
||||
bool enabled_{false};
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetBrightnessAction final : public Action<Ts...>, public Parented<HUB75Display> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, brightness)
|
||||
|
||||
void play(const Ts &...x) override { this->parent_->set_brightness(this->brightness_.value(x...)); }
|
||||
};
|
||||
|
||||
} // namespace esphome::hub75
|
||||
|
||||
#endif
|
||||
|
||||
@@ -71,16 +71,4 @@ class IntegrationSensor final : public sensor::Sensor, public Component {
|
||||
float last_value_{0.0f};
|
||||
};
|
||||
|
||||
template<typename... Ts> class ResetAction final : public Action<Ts...>, public Parented<IntegrationSensor> {
|
||||
public:
|
||||
void play(const Ts &...x) override { this->parent_->reset(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetValueAction final : public Action<Ts...>, public Parented<IntegrationSensor> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(float, value)
|
||||
|
||||
void play(const Ts &...x) override { this->parent_->set_value(this->value_.value(x...)); }
|
||||
};
|
||||
|
||||
} // namespace esphome::integration
|
||||
|
||||
@@ -11,17 +11,13 @@ from esphome.const import (
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
CONF_VALUE,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
integration_ns = cg.esphome_ns.namespace("integration")
|
||||
IntegrationSensor = integration_ns.class_(
|
||||
"IntegrationSensor", sensor.Sensor, cg.Component
|
||||
)
|
||||
ResetAction = integration_ns.class_("ResetAction", automation.Action)
|
||||
SetValueAction = integration_ns.class_("SetValueAction", automation.Action)
|
||||
|
||||
IntegrationSensorTime = integration_ns.enum("IntegrationSensorTime")
|
||||
INTEGRATION_TIMES = {
|
||||
@@ -106,46 +102,23 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_restore(config[CONF_RESTORE]))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"sensor.integration.reset",
|
||||
ResetAction,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(IntegrationSensor),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyCall("reset()"),
|
||||
)
|
||||
async def sensor_integration_reset_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"sensor.integration.set_value",
|
||||
SetValueAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(IntegrationSensor),
|
||||
cv.Required(CONF_VALUE): cv.templatable(cv.float_),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_VALUE, "set_value", cg.float_),
|
||||
)
|
||||
async def sensor_integration_set_value_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
template_ = await cg.templatable(config[CONF_VALUE], args, cg.float_)
|
||||
cg.add(var.set_value(template_))
|
||||
return var
|
||||
|
||||
@@ -36,8 +36,7 @@ from esphome.const import (
|
||||
CONF_UPDATE_INTERVAL,
|
||||
CONF_WIDTH,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, RawExpression, TemplateArgsType
|
||||
from esphome.cpp_generator import RawExpression
|
||||
from esphome.final_validate import full_config
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -61,7 +60,6 @@ VCOM_REGISTER_OPTIONS = (VCOM_REGISTER_DEFAULT, VCOM_REGISTER_ALT)
|
||||
|
||||
it8951_ns = cg.esphome_ns.namespace("it8951")
|
||||
IT8951Display = it8951_ns.class_("IT8951Display", display.Display, spi.SPIDevice)
|
||||
IT8951UpdateAction = it8951_ns.class_("IT8951UpdateAction", automation.Action)
|
||||
|
||||
# Hardware waveform modes exposed to YAML. Strings are mapped to the C++
|
||||
# UpdateMode enum so the runtime can store the mode as a uint16_t rather
|
||||
@@ -80,6 +78,7 @@ UPDATE_MODE_OPTIONS = {
|
||||
"A2": UpdateMode.UPDATE_MODE_A2,
|
||||
"FAST": UpdateMode.UPDATE_MODE_DU,
|
||||
"FULL": UpdateMode.UPDATE_MODE_GC16,
|
||||
"DEFAULT": UpdateMode.UPDATE_MODE_NONE,
|
||||
}
|
||||
# Maps the YAML mode string directly to the C++ UpdateMode enum value, so the
|
||||
# config option and the it8951.update action share one validator.
|
||||
@@ -419,26 +418,13 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_transform(RawExpression(str(transform_value))))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"it8951.update",
|
||||
IT8951UpdateAction,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(IT8951Display),
|
||||
cv.Optional(CONF_MODE): cv.templatable(update_mode),
|
||||
cv.Optional(CONF_MODE, default="DEFAULT"): cv.templatable(update_mode),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_MODE, "update_mode", UpdateMode),
|
||||
)
|
||||
async def it8951_update_action_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
display_var = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, display_var)
|
||||
if mode := config.get(CONF_MODE):
|
||||
mode = await cg.templatable(mode, args, UpdateMode)
|
||||
cg.add(var.set_mode(mode))
|
||||
return var
|
||||
|
||||
@@ -777,12 +777,12 @@ void IT8951Display::update() {
|
||||
}
|
||||
|
||||
void IT8951Display::update_mode(UpdateMode mode) {
|
||||
if (!this->is_ready())
|
||||
return;
|
||||
if (mode == UPDATE_MODE_NONE) {
|
||||
ESP_LOGW(TAG, "Unknown update mode");
|
||||
this->update();
|
||||
return;
|
||||
}
|
||||
if (!this->is_ready())
|
||||
return;
|
||||
this->start_update_(mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "esphome/components/display/display.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
@@ -350,24 +349,4 @@ class IT8951Display : public Display,
|
||||
uint8_t dev_info_attempts_{0};
|
||||
};
|
||||
|
||||
// --- Automation action ---
|
||||
template<typename... Ts> class IT8951UpdateAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit IT8951UpdateAction(IT8951Display *display) : display_(display) {}
|
||||
TEMPLATABLE_VALUE(UpdateMode, mode)
|
||||
|
||||
protected:
|
||||
void play(const Ts &...x) override {
|
||||
if (!this->display_->is_ready())
|
||||
return;
|
||||
if (this->mode_.has_value()) {
|
||||
this->display_->update_mode(this->mode_.value(x...));
|
||||
} else {
|
||||
this->display_->update();
|
||||
}
|
||||
}
|
||||
|
||||
IT8951Display *display_;
|
||||
};
|
||||
|
||||
} // namespace esphome::it8951
|
||||
|
||||
@@ -15,7 +15,6 @@ from esphome.const import (
|
||||
CONF_TIMEOUT,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, literal
|
||||
from esphome.types import ConfigType, TemplateArgsType
|
||||
|
||||
@@ -36,8 +35,6 @@ CONF_ON_RESULT = "on_result"
|
||||
|
||||
key_collector_ns = cg.esphome_ns.namespace("key_collector")
|
||||
KeyCollector = key_collector_ns.class_("KeyCollector", cg.Component)
|
||||
EnableAction = key_collector_ns.class_("EnableAction", automation.Action)
|
||||
DisableAction = key_collector_ns.class_("DisableAction", automation.Action)
|
||||
|
||||
X_TYPE = cg.std_string_ref.operator("const")
|
||||
|
||||
@@ -135,43 +132,19 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_enabled(config[CONF_ENABLE_ON_BOOT]))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
KEY_COLLECTOR_ACTION_SCHEMA = automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(KeyCollector),
|
||||
}
|
||||
)
|
||||
|
||||
automation.register_apply_action(
|
||||
"key_collector.enable",
|
||||
EnableAction,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(KeyCollector),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
KEY_COLLECTOR_ACTION_SCHEMA,
|
||||
automation.ApplyCall("set_enabled(true)"),
|
||||
)
|
||||
async def enable_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"key_collector.disable",
|
||||
DisableAction,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(KeyCollector),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
KEY_COLLECTOR_ACTION_SCHEMA,
|
||||
automation.ApplyCall("set_enabled(false)"),
|
||||
)
|
||||
async def disable_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
@@ -54,12 +54,4 @@ class KeyCollector final : public Component {
|
||||
bool enabled_{};
|
||||
};
|
||||
|
||||
template<typename... Ts> class EnableAction final : public Action<Ts...>, public Parented<KeyCollector> {
|
||||
void play(const Ts &...x) override { this->parent_->set_enabled(true); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisableAction final : public Action<Ts...>, public Parented<KeyCollector> {
|
||||
void play(const Ts &...x) override { this->parent_->set_enabled(false); }
|
||||
};
|
||||
|
||||
} // namespace esphome::key_collector
|
||||
|
||||
@@ -4,8 +4,6 @@ import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_PASSWORD, CONF_THROTTLE, CONF_TIMEOUT
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
AUTO_LOAD = ["ld24xx"]
|
||||
@@ -86,11 +84,6 @@ CALIBRATION_ACTION_SCHEMA = maybe_simple_id(
|
||||
|
||||
|
||||
# Actions
|
||||
BluetoothPasswordSetAction = ld2410_ns.class_(
|
||||
"BluetoothPasswordSetAction", automation.Action
|
||||
)
|
||||
|
||||
|
||||
BLUETOOTH_PASSWORD_SET_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(LD2410Component),
|
||||
@@ -98,21 +91,8 @@ BLUETOOTH_PASSWORD_SET_SCHEMA = cv.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"bluetooth_password.set",
|
||||
BluetoothPasswordSetAction,
|
||||
BLUETOOTH_PASSWORD_SET_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_PASSWORD, "set_bluetooth_password", cg.std_string),
|
||||
)
|
||||
async def bluetooth_password_set_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(config[CONF_PASSWORD], args, cg.std_string)
|
||||
cg.add(var.set_password(template_))
|
||||
return var
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "ld2410.h"
|
||||
|
||||
namespace esphome::ld2410 {
|
||||
|
||||
template<typename... Ts> class BluetoothPasswordSetAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit BluetoothPasswordSetAction(LD2410Component *ld2410_comp) : ld2410_comp_(ld2410_comp) {}
|
||||
TEMPLATABLE_VALUE(std::string, password)
|
||||
|
||||
void play(const Ts &...x) override { this->ld2410_comp_->set_bluetooth_password(this->password_.value(x...)); }
|
||||
|
||||
protected:
|
||||
LD2410Component *ld2410_comp_;
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2410
|
||||
@@ -24,7 +24,6 @@
|
||||
#endif
|
||||
#include "esphome/components/ld24xx/ld24xx.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
@@ -10,20 +10,13 @@ from esphome.components.const import CONF_CHANNEL_COLORS, CONF_IS_WRGB
|
||||
from esphome.config_helpers import filter_source_files_from_defines
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BLUE,
|
||||
CONF_BRIGHTNESS,
|
||||
CONF_COLD_WHITE,
|
||||
CONF_COLD_WHITE_COLOR_TEMPERATURE,
|
||||
CONF_COLOR_BRIGHTNESS,
|
||||
CONF_COLOR_CORRECT,
|
||||
CONF_COLOR_MODE,
|
||||
CONF_COLOR_TEMPERATURE,
|
||||
CONF_DEFAULT_TRANSITION_LENGTH,
|
||||
CONF_EFFECTS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_FLASH_TRANSITION_LENGTH,
|
||||
CONF_GAMMA_CORRECT,
|
||||
CONF_GREEN,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_INITIAL_STATE,
|
||||
@@ -35,17 +28,14 @@ from esphome.const import (
|
||||
CONF_ON_TURN_ON,
|
||||
CONF_OUTPUT_ID,
|
||||
CONF_POWER_SUPPLY,
|
||||
CONF_RED,
|
||||
CONF_RESTORE_MODE,
|
||||
CONF_RESTORE_STATE,
|
||||
CONF_RGB_ORDER,
|
||||
CONF_STATE,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_WARM_WHITE,
|
||||
CONF_WARM_WHITE_COLOR_TEMPERATURE,
|
||||
CONF_WEB_SERVER,
|
||||
CONF_WHITE,
|
||||
)
|
||||
from esphome.core import CORE, ID, CoroPriority, HexInt, Lambda, coroutine_with_priority
|
||||
from esphome.core import CORE, ID, CoroPriority, HexInt, coroutine_with_priority
|
||||
from esphome.core.entity_helpers import (
|
||||
entity_duplicate_validator,
|
||||
queue_entity_register,
|
||||
@@ -64,6 +54,17 @@ from .effects import (
|
||||
RGB_EFFECTS,
|
||||
validate_effects,
|
||||
)
|
||||
from .restore_state import (
|
||||
LEGACY_RESTORE_MODES,
|
||||
RESTORE_STATE_NONE,
|
||||
RESTORE_STATE_SCHEMA,
|
||||
_build_state_lambda,
|
||||
_initial_state_overridden_by_legacy_mode,
|
||||
_initial_state_statements,
|
||||
_legacy_cold_boot_statements,
|
||||
_legacy_restore_statements,
|
||||
_restore_state_statements,
|
||||
)
|
||||
from .types import ( # noqa: F401
|
||||
AddressableLight,
|
||||
AddressableLightState,
|
||||
@@ -273,17 +274,34 @@ def migrate_channel_colors(
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> None:
|
||||
"""Validate all recorded effect name references against their target lights.
|
||||
"""Validate every configured light's own resolved config, and all recorded
|
||||
effect name references against their target lights.
|
||||
|
||||
This runs once per light platform instance. If no light platform is configured,
|
||||
this never runs — but the ID validator will catch the missing light ID separately.
|
||||
FINAL_VALIDATE_SCHEMA for a platform-based domain like `light:` runs once for
|
||||
the whole domain, not once per entry -- `config` is the full list of light
|
||||
platform entries across the file, not a single light's own config.
|
||||
"""
|
||||
for light_config in config:
|
||||
restore_mode = light_config.get(CONF_RESTORE_MODE)
|
||||
if restore_mode is not None:
|
||||
legacy = LEGACY_RESTORE_MODES[restore_mode]
|
||||
if _initial_state_overridden_by_legacy_mode(
|
||||
legacy, light_config.get(CONF_INITIAL_STATE)
|
||||
):
|
||||
_LOGGER.warning(
|
||||
"[%s] 'initial_state: state' is ignored because 'restore_mode: %s' "
|
||||
"always sets the light %s at boot; use 'restore_state:' instead for "
|
||||
"per-field control",
|
||||
light_config.get(CONF_NAME) or light_config[CONF_ID],
|
||||
restore_mode,
|
||||
"ON" if legacy.cold_boot_state else "OFF",
|
||||
)
|
||||
|
||||
data = _get_data()
|
||||
if not data.effect_refs and not data.effect_cycle_refs:
|
||||
return
|
||||
|
||||
# Drain the lists so we only validate once even though
|
||||
# FINAL_VALIDATE_SCHEMA runs for each light platform instance.
|
||||
# Drain the lists so each recorded reference is only validated once.
|
||||
refs = data.effect_refs
|
||||
data.effect_refs = []
|
||||
cycle_refs = data.effect_cycle_refs
|
||||
@@ -328,18 +346,6 @@ def _final_validate(config: ConfigType) -> None:
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
LightRestoreMode = light_ns.enum("LightRestoreMode")
|
||||
RESTORE_MODES = {
|
||||
"RESTORE_DEFAULT_OFF": LightRestoreMode.LIGHT_RESTORE_DEFAULT_OFF,
|
||||
"RESTORE_DEFAULT_ON": LightRestoreMode.LIGHT_RESTORE_DEFAULT_ON,
|
||||
"ALWAYS_OFF": LightRestoreMode.LIGHT_ALWAYS_OFF,
|
||||
"ALWAYS_ON": LightRestoreMode.LIGHT_ALWAYS_ON,
|
||||
"RESTORE_INVERTED_DEFAULT_OFF": LightRestoreMode.LIGHT_RESTORE_INVERTED_DEFAULT_OFF,
|
||||
"RESTORE_INVERTED_DEFAULT_ON": LightRestoreMode.LIGHT_RESTORE_INVERTED_DEFAULT_ON,
|
||||
"RESTORE_AND_OFF": LightRestoreMode.LIGHT_RESTORE_AND_OFF,
|
||||
"RESTORE_AND_ON": LightRestoreMode.LIGHT_RESTORE_AND_ON,
|
||||
}
|
||||
|
||||
# Schema default that also matches the C++ initializer in light_state.h; codegen
|
||||
# skips the setter when the config equals it.
|
||||
DEFAULT_FLASH_TRANSITION_LENGTH = "0s"
|
||||
@@ -354,9 +360,10 @@ LIGHT_SCHEMA = (
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
||||
mqtt.MQTTJSONLightComponent
|
||||
),
|
||||
cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
|
||||
RESTORE_MODES, upper=True, space="_"
|
||||
cv.Exclusive(CONF_RESTORE_MODE, "restore"): cv.one_of(
|
||||
*LEGACY_RESTORE_MODES, upper=True, space="_"
|
||||
),
|
||||
cv.Exclusive(CONF_RESTORE_STATE, "restore"): RESTORE_STATE_SCHEMA,
|
||||
cv.Optional(CONF_ON_TURN_ON): auto.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOnTrigger),
|
||||
@@ -435,6 +442,28 @@ class LightType(enum.IntEnum):
|
||||
ADDRESSABLE = 3
|
||||
|
||||
|
||||
def _apply_default_restore_mode(
|
||||
default_restore_mode: str,
|
||||
) -> Callable[[ConfigType], ConfigType]:
|
||||
# cv.Exclusive has no default, so apply the default here if neither key is configured.
|
||||
def validator(config: ConfigType) -> ConfigType:
|
||||
if CONF_RESTORE_MODE not in config and CONF_RESTORE_STATE not in config:
|
||||
config[CONF_RESTORE_MODE] = cv.one_of(
|
||||
*LEGACY_RESTORE_MODES, upper=True, space="_"
|
||||
)(default_restore_mode)
|
||||
return config
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
_BASE_SCHEMAS: dict[LightType, cv.Schema] = {
|
||||
LightType.BINARY: BINARY_LIGHT_SCHEMA,
|
||||
LightType.BRIGHTNESS_ONLY: BRIGHTNESS_ONLY_LIGHT_SCHEMA,
|
||||
LightType.RGB: RGB_LIGHT_SCHEMA,
|
||||
LightType.ADDRESSABLE: ADDRESSABLE_LIGHT_SCHEMA,
|
||||
}
|
||||
|
||||
|
||||
def light_schema(
|
||||
class_: MockObjClass,
|
||||
type_: LightType,
|
||||
@@ -450,25 +479,14 @@ def light_schema(
|
||||
for key, default, validator in [
|
||||
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||
(CONF_ICON, icon, cv.icon),
|
||||
(
|
||||
CONF_RESTORE_MODE,
|
||||
default_restore_mode,
|
||||
cv.enum(RESTORE_MODES, upper=True, space="_"),
|
||||
),
|
||||
]:
|
||||
if default is not cv.UNDEFINED:
|
||||
schema[cv.Optional(key, default=default)] = validator
|
||||
|
||||
if type_ == LightType.BINARY:
|
||||
return BINARY_LIGHT_SCHEMA.extend(schema)
|
||||
if type_ == LightType.BRIGHTNESS_ONLY:
|
||||
return BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend(schema)
|
||||
if type_ == LightType.RGB:
|
||||
return RGB_LIGHT_SCHEMA.extend(schema)
|
||||
if type_ == LightType.ADDRESSABLE:
|
||||
return ADDRESSABLE_LIGHT_SCHEMA.extend(schema)
|
||||
|
||||
raise ValueError(f"Invalid light type: {type_}")
|
||||
result = _BASE_SCHEMAS[type_].extend(schema)
|
||||
if default_restore_mode is not cv.UNDEFINED:
|
||||
result.add_extra(_apply_default_restore_mode(default_restore_mode))
|
||||
return result
|
||||
|
||||
|
||||
def validate_color_temperature_channels(value):
|
||||
@@ -487,31 +505,43 @@ def validate_color_temperature_channels(value):
|
||||
|
||||
@setup_entity("light")
|
||||
async def setup_light_core_(light_var, config, output_var):
|
||||
cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE]))
|
||||
# All 8 legacy restore_mode values, and the restore_state key, are just different
|
||||
# ways to build the same state callback and save_enabled flag that LightState's
|
||||
# runtime actually understands.
|
||||
initial_state_config = config.get(CONF_INITIAL_STATE)
|
||||
initial_statements = await _initial_state_statements(initial_state_config)
|
||||
|
||||
if (initial_state_config := config.get(CONF_INITIAL_STATE)) is not None:
|
||||
# Emit a stateless lambda that constructs the initial state — values live
|
||||
# in flash as code, not stored in the LightState object (~40 bytes saved).
|
||||
initial_state = LightStateRTCState(
|
||||
initial_state_config.get(CONF_COLOR_MODE, ColorMode.UNKNOWN),
|
||||
initial_state_config.get(CONF_STATE, False),
|
||||
initial_state_config.get(CONF_BRIGHTNESS, 1.0),
|
||||
initial_state_config.get(CONF_COLOR_BRIGHTNESS, 1.0),
|
||||
initial_state_config.get(CONF_RED, 1.0),
|
||||
initial_state_config.get(CONF_GREEN, 1.0),
|
||||
initial_state_config.get(CONF_BLUE, 1.0),
|
||||
initial_state_config.get(CONF_WHITE, 1.0),
|
||||
initial_state_config.get(CONF_COLOR_TEMPERATURE, 1.0),
|
||||
initial_state_config.get(CONF_COLD_WHITE, 1.0),
|
||||
initial_state_config.get(CONF_WARM_WHITE, 1.0),
|
||||
restore_mode = config.get(CONF_RESTORE_MODE)
|
||||
restore_state_config = config.get(CONF_RESTORE_STATE)
|
||||
if restore_state_config == RESTORE_STATE_NONE:
|
||||
# restore_state: none is explicit shorthand for "no restoring at all" --
|
||||
restore_state_config = None
|
||||
|
||||
if restore_mode is not None:
|
||||
legacy = LEGACY_RESTORE_MODES[restore_mode]
|
||||
initial_statements.extend(
|
||||
_legacy_cold_boot_statements(legacy, initial_state_config)
|
||||
)
|
||||
args = [(LightStateRTCState.operator("ref"), "s")]
|
||||
lamb = await cg.process_lambda(
|
||||
Lambda(f"s = {initial_state};"),
|
||||
args,
|
||||
return_type=cg.void,
|
||||
restore_statements = _legacy_restore_statements(legacy)
|
||||
save_enabled = legacy.save_enabled
|
||||
elif restore_state_config is not None:
|
||||
restore_statements = await _restore_state_statements(
|
||||
restore_state_config, initial_state_config
|
||||
)
|
||||
cg.add(light_var.set_initial_state(lamb))
|
||||
save_enabled = True
|
||||
else:
|
||||
# Neither key configured: no persistence, and no cold-boot forcing either.
|
||||
restore_statements = []
|
||||
save_enabled = False
|
||||
|
||||
if (
|
||||
lamb := await _build_state_lambda(
|
||||
initial_statements, restore_statements, save_enabled
|
||||
)
|
||||
) is not None:
|
||||
cg.add(light_var.set_state_callback(lamb))
|
||||
if save_enabled: # matches LightState::save_enabled_'s own default of false
|
||||
cg.add(light_var.set_save_enabled(save_enabled))
|
||||
|
||||
if (
|
||||
default_transition_length := config.get(CONF_DEFAULT_TRANSITION_LENGTH)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
from collections.abc import Callable
|
||||
from typing import Any, NamedTuple
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.config import path_context
|
||||
@@ -28,6 +31,7 @@ from esphome.const import (
|
||||
)
|
||||
from esphome.core import CORE, ID, EsphomeError, Lambda
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .types import (
|
||||
@@ -44,6 +48,28 @@ from .types import (
|
||||
|
||||
CONF_INCLUDE_NONE = "include_none"
|
||||
|
||||
_STATE_ON_OFF = cv.one_of("ON", "OFF", upper=True)
|
||||
|
||||
|
||||
@schema_extractor("one_of")
|
||||
def validate_light_state(value: Any) -> Any:
|
||||
"""Validate a light on/off state.
|
||||
|
||||
Documented as 'ON'/'OFF', but accepts all boolean forms for backward compatibility.
|
||||
"""
|
||||
if value == SCHEMA_EXTRACT:
|
||||
return ("ON", "OFF")
|
||||
try:
|
||||
return _STATE_ON_OFF(value) == "ON"
|
||||
except cv.Invalid:
|
||||
pass
|
||||
try:
|
||||
return cv.boolean(value)
|
||||
except cv.Invalid as err:
|
||||
raise cv.Invalid(
|
||||
f"Expected 'ON', 'OFF', or a boolean value, got {value!r}"
|
||||
) from err
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"light.toggle",
|
||||
@@ -71,19 +97,56 @@ async def light_toggle_to_code(config, action_id, template_arg, args):
|
||||
return var
|
||||
|
||||
|
||||
class LightStateField(NamedTuple):
|
||||
"""One field of a light's state: the single source for the schemas and boot-time
|
||||
codegen that deal with it."""
|
||||
|
||||
conf_key: str
|
||||
# Member of LightStateRTCState holding this field.
|
||||
member: str
|
||||
validator: Callable[[Any], Any]
|
||||
# The color mode providing this field, if it implies one.
|
||||
color_mode: MockObj | None = None
|
||||
templatable: bool = True
|
||||
|
||||
|
||||
# In LightStateRTCState member order.
|
||||
LIGHT_STATE_FIELDS: tuple[LightStateField, ...] = (
|
||||
LightStateField(CONF_STATE, "state", validate_light_state),
|
||||
LightStateField(
|
||||
CONF_COLOR_MODE,
|
||||
"color_mode",
|
||||
cv.enum(COLOR_MODES, upper=True, space="_"),
|
||||
templatable=False,
|
||||
),
|
||||
LightStateField(CONF_BRIGHTNESS, "brightness", cv.percentage, ColorMode.BRIGHTNESS),
|
||||
LightStateField(
|
||||
CONF_COLOR_BRIGHTNESS, "color_brightness", cv.percentage, ColorMode.RGB
|
||||
),
|
||||
LightStateField(CONF_RED, "red", cv.percentage, ColorMode.RGB),
|
||||
LightStateField(CONF_GREEN, "green", cv.percentage, ColorMode.RGB),
|
||||
LightStateField(CONF_BLUE, "blue", cv.percentage, ColorMode.RGB),
|
||||
LightStateField(CONF_WHITE, "white", cv.percentage, ColorMode.WHITE),
|
||||
LightStateField(
|
||||
CONF_COLOR_TEMPERATURE,
|
||||
"color_temp",
|
||||
cv.color_temperature,
|
||||
ColorMode.COLOR_TEMPERATURE,
|
||||
),
|
||||
LightStateField(
|
||||
CONF_COLD_WHITE, "cold_white", cv.percentage, ColorMode.COLD_WARM_WHITE
|
||||
),
|
||||
LightStateField(
|
||||
CONF_WARM_WHITE, "warm_white", cv.percentage, ColorMode.COLD_WARM_WHITE
|
||||
),
|
||||
)
|
||||
|
||||
LIGHT_STATE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_COLOR_MODE): cv.enum(COLOR_MODES, upper=True, space="_"),
|
||||
cv.Optional(CONF_STATE): cv.templatable(cv.boolean),
|
||||
cv.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage),
|
||||
cv.Optional(CONF_COLOR_BRIGHTNESS): cv.templatable(cv.percentage),
|
||||
cv.Optional(CONF_RED): cv.templatable(cv.percentage),
|
||||
cv.Optional(CONF_GREEN): cv.templatable(cv.percentage),
|
||||
cv.Optional(CONF_BLUE): cv.templatable(cv.percentage),
|
||||
cv.Optional(CONF_WHITE): cv.templatable(cv.percentage),
|
||||
cv.Optional(CONF_COLOR_TEMPERATURE): cv.templatable(cv.color_temperature),
|
||||
cv.Optional(CONF_COLD_WHITE): cv.templatable(cv.percentage),
|
||||
cv.Optional(CONF_WARM_WHITE): cv.templatable(cv.percentage),
|
||||
cv.Optional(field.conf_key): (
|
||||
cv.templatable(field.validator) if field.templatable else field.validator
|
||||
)
|
||||
for field in LIGHT_STATE_FIELDS
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ from esphome.cpp_generator import MockObjClass
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
from esphome.util import Registry
|
||||
|
||||
from .automation import validate_light_state
|
||||
from .types import (
|
||||
COLOR_MODES,
|
||||
AddressableColorWipeEffect,
|
||||
@@ -281,7 +282,7 @@ async def random_effect_to_code(config, effect_id):
|
||||
cv.ensure_list(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_STATE, default=True): cv.boolean,
|
||||
cv.Optional(CONF_STATE, default=True): validate_light_state,
|
||||
cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage,
|
||||
cv.Optional(CONF_COLOR_MODE): cv.enum(
|
||||
COLOR_MODES, upper=True, space="_"
|
||||
|
||||
@@ -13,6 +13,20 @@ namespace esphome::light {
|
||||
|
||||
static const char *const TAG = "light";
|
||||
|
||||
// Colour modes are bitmasks of capabilities. A mode the light doesn't support may be a bare set of
|
||||
// required capabilities (see restore_state.py's colour mode inference): use the first supported
|
||||
// mode that provides all of them, or leave it unchanged if there is none.
|
||||
static ColorMode resolve_color_mode(const LightTraits &traits, ColorMode requested) {
|
||||
if (requested == ColorMode::UNKNOWN || traits.supports_color_mode(requested))
|
||||
return requested;
|
||||
auto wanted = static_cast<uint8_t>(requested);
|
||||
for (ColorMode mode : traits.get_supported_color_modes()) {
|
||||
if ((static_cast<uint8_t>(mode) & wanted) == wanted)
|
||||
return mode;
|
||||
}
|
||||
return requested;
|
||||
}
|
||||
|
||||
LightState::LightState(LightOutput *output) : output_(output) {}
|
||||
|
||||
LightTraits LightState::get_traits() { return this->output_->get_traits(); }
|
||||
@@ -40,38 +54,14 @@ void LightState::setup() {
|
||||
|
||||
auto call = this->make_call();
|
||||
LightStateRTCState recovered{};
|
||||
if (this->initial_state_callback_) {
|
||||
this->initial_state_callback_(recovered);
|
||||
this->initial_state_callback_ = nullptr; // One-shot — no longer needed
|
||||
bool restored = false;
|
||||
if (this->save_enabled_) {
|
||||
this->rtc_ = this->make_entity_preference<LightStateRTCState>();
|
||||
restored = this->rtc_.load(&recovered);
|
||||
}
|
||||
switch (this->restore_mode_) {
|
||||
case LIGHT_RESTORE_DEFAULT_OFF:
|
||||
case LIGHT_RESTORE_DEFAULT_ON:
|
||||
case LIGHT_RESTORE_INVERTED_DEFAULT_OFF:
|
||||
case LIGHT_RESTORE_INVERTED_DEFAULT_ON:
|
||||
this->rtc_ = this->make_entity_preference<LightStateRTCState>();
|
||||
// Attempt to load from preferences, else fall back to default values
|
||||
if (!this->rtc_.load(&recovered)) {
|
||||
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_DEFAULT_ON ||
|
||||
this->restore_mode_ == LIGHT_RESTORE_INVERTED_DEFAULT_ON);
|
||||
} else if (this->restore_mode_ == LIGHT_RESTORE_INVERTED_DEFAULT_OFF ||
|
||||
this->restore_mode_ == LIGHT_RESTORE_INVERTED_DEFAULT_ON) {
|
||||
// Inverted restore state
|
||||
recovered.state = !recovered.state;
|
||||
}
|
||||
break;
|
||||
case LIGHT_RESTORE_AND_OFF:
|
||||
case LIGHT_RESTORE_AND_ON:
|
||||
this->rtc_ = this->make_entity_preference<LightStateRTCState>();
|
||||
this->rtc_.load(&recovered);
|
||||
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
|
||||
break;
|
||||
case LIGHT_ALWAYS_OFF:
|
||||
recovered.state = false;
|
||||
break;
|
||||
case LIGHT_ALWAYS_ON:
|
||||
recovered.state = true;
|
||||
break;
|
||||
if (this->state_callback_) {
|
||||
this->state_callback_(recovered, restored);
|
||||
this->state_callback_ = nullptr; // One-shot — no longer needed
|
||||
}
|
||||
|
||||
// A light coming up on boot must never end up on-but-invisible: if the resolved restore
|
||||
@@ -82,7 +72,7 @@ void LightState::setup() {
|
||||
recovered.brightness = 1.0f;
|
||||
}
|
||||
|
||||
call.set_color_mode_if_supported(recovered.color_mode);
|
||||
call.set_color_mode_if_supported(resolve_color_mode(traits, recovered.color_mode));
|
||||
call.set_state(recovered.state);
|
||||
call.set_brightness_if_supported(recovered.brightness);
|
||||
call.set_color_brightness_if_supported(recovered.color_brightness);
|
||||
@@ -428,17 +418,14 @@ void LightState::set_transformer_remote_values_(const LightColorValues &target,
|
||||
#endif
|
||||
|
||||
void LightState::save_remote_values_() {
|
||||
if (!this->save_enabled_)
|
||||
return;
|
||||
LightStateRTCState saved;
|
||||
saved.color_mode = this->remote_values.get_color_mode();
|
||||
switch (this->restore_mode_) {
|
||||
case LIGHT_RESTORE_AND_OFF:
|
||||
case LIGHT_RESTORE_AND_ON:
|
||||
saved.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
|
||||
break;
|
||||
default:
|
||||
saved.state = this->remote_values.is_on();
|
||||
break;
|
||||
}
|
||||
// Always the real on/off status (RESTORE_AND_ON/OFF used to persist a hardcoded
|
||||
// true/false here instead; harmless, since those modes force `state` again on
|
||||
// every load regardless of what was saved -- see _legacy_restore_statements).
|
||||
saved.state = this->remote_values.is_on();
|
||||
saved.brightness = this->remote_values.get_brightness();
|
||||
saved.color_brightness = this->remote_values.get_color_brightness();
|
||||
saved.red = this->remote_values.get_red();
|
||||
|
||||
@@ -44,33 +44,7 @@ class LightTargetStateReachedListener {
|
||||
virtual void on_light_target_state_reached() = 0;
|
||||
};
|
||||
|
||||
enum LightRestoreMode : uint8_t {
|
||||
LIGHT_RESTORE_DEFAULT_OFF,
|
||||
LIGHT_RESTORE_DEFAULT_ON,
|
||||
LIGHT_ALWAYS_OFF,
|
||||
LIGHT_ALWAYS_ON,
|
||||
LIGHT_RESTORE_INVERTED_DEFAULT_OFF,
|
||||
LIGHT_RESTORE_INVERTED_DEFAULT_ON,
|
||||
LIGHT_RESTORE_AND_OFF,
|
||||
LIGHT_RESTORE_AND_ON,
|
||||
};
|
||||
|
||||
struct LightStateRTCState {
|
||||
LightStateRTCState(ColorMode color_mode, bool state, float brightness, float color_brightness, float red, float green,
|
||||
float blue, float white, float color_temp, float cold_white, float warm_white)
|
||||
: brightness(brightness),
|
||||
color_brightness(color_brightness),
|
||||
red(red),
|
||||
green(green),
|
||||
blue(blue),
|
||||
white(white),
|
||||
color_temp(color_temp),
|
||||
cold_white(cold_white),
|
||||
warm_white(warm_white),
|
||||
effect(0),
|
||||
color_mode(color_mode),
|
||||
state(state) {}
|
||||
LightStateRTCState() = default;
|
||||
// Group 4-byte aligned members first
|
||||
float brightness{1.0f};
|
||||
float color_brightness{1.0f};
|
||||
@@ -211,12 +185,14 @@ class LightState : public EntityBase, public Component {
|
||||
float gamma_uncorrect_lut(float value) const { return value; }
|
||||
#endif // USE_LIGHT_GAMMA_LUT
|
||||
|
||||
/// Set the restore mode of this light
|
||||
void set_restore_mode(LightRestoreMode restore_mode) { this->restore_mode_ = restore_mode; }
|
||||
/// Set the callback that resolves the boot-time state, called once during setup then
|
||||
/// cleared. `restored` is true only when a persisted state actually loaded, in which
|
||||
/// case the state argument already holds the loaded values; otherwise it is freshly
|
||||
/// default-constructed. Values live in flash as code.
|
||||
void set_state_callback(void (*callback)(LightStateRTCState &, bool restored)) { this->state_callback_ = callback; }
|
||||
|
||||
/// Set a callback to populate the initial state defaults during setup.
|
||||
/// The callback is called once, then cleared. Values live in flash as code.
|
||||
void set_initial_state(void (*callback)(LightStateRTCState &)) { this->initial_state_callback_ = callback; }
|
||||
/// Set whether this light persists its state to preferences at all.
|
||||
void set_save_enabled(bool save_enabled) { this->save_enabled_ = save_enabled; }
|
||||
|
||||
/// Return whether the light has any effects that meet the trait requirements.
|
||||
bool supports_effects() const { return !this->effects_.empty(); }
|
||||
@@ -382,9 +358,9 @@ class LightState : public EntityBase, public Component {
|
||||
*/
|
||||
std::unique_ptr<std::vector<LightTargetStateReachedListener *>> target_state_reached_listeners_;
|
||||
|
||||
/// Callback to populate initial state defaults — called once during setup, then cleared.
|
||||
/// Callback that resolves the boot-time state — called once during setup, then cleared.
|
||||
/// Values live in flash as function body; no per-instance data storage beyond this pointer.
|
||||
void (*initial_state_callback_)(LightStateRTCState &){nullptr};
|
||||
void (*state_callback_)(LightStateRTCState &, bool restored){nullptr};
|
||||
|
||||
/// Value for storing the index of the currently active effect. 0 if no effect is active
|
||||
uint32_t active_effect_index_{};
|
||||
@@ -406,12 +382,12 @@ class LightState : public EntityBase, public Component {
|
||||
bool next_write_{true};
|
||||
// for effects, true if a transformer (transition) is active.
|
||||
bool is_transformer_active_{false};
|
||||
/// Whether this light persists its state to preferences at all.
|
||||
bool save_enabled_{false};
|
||||
#ifdef USE_LIGHT_TRANSITION_PUBLISH_INTERVAL
|
||||
/// True while the active transformer publishes current_values on an interval from loop().
|
||||
bool transition_publish_enabled_{false};
|
||||
#endif
|
||||
/// Restore mode of the light.
|
||||
LightRestoreMode restore_mode_;
|
||||
};
|
||||
|
||||
} // namespace esphome::light
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
"""
|
||||
Functions and classes to translate the `restore_mode:` and `restore_state:` config keys into
|
||||
the single runtime state callback that `LightState` actually understands.
|
||||
"""
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_COLOR_MODE, CONF_STATE
|
||||
from esphome.core import Lambda
|
||||
from esphome.cpp_generator import call_lambda
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .automation import LIGHT_STATE_FIELDS, validate_light_state
|
||||
from .types import ColorMode, LightStateRTCState
|
||||
|
||||
RESTORE_STATE_KEEP = "KEEP"
|
||||
RESTORE_STATE_INVERT = "INVERT"
|
||||
RESTORE_STATE_INITIAL = "INITIAL"
|
||||
RESTORE_STATE_ALL = "ALL"
|
||||
RESTORE_STATE_NONE = "NONE"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LegacyRestoreMode:
|
||||
cold_boot_state: bool
|
||||
restore_action: bool | str | None # None = no override, "INVERT", or force-to-bool
|
||||
save_enabled: bool
|
||||
|
||||
|
||||
LEGACY_RESTORE_MODES: dict[str, LegacyRestoreMode] = {
|
||||
"RESTORE_DEFAULT_OFF": LegacyRestoreMode(False, None, True),
|
||||
"RESTORE_DEFAULT_ON": LegacyRestoreMode(True, None, True),
|
||||
"ALWAYS_OFF": LegacyRestoreMode(False, None, False),
|
||||
"ALWAYS_ON": LegacyRestoreMode(True, None, False),
|
||||
"RESTORE_INVERTED_DEFAULT_OFF": LegacyRestoreMode(
|
||||
False, RESTORE_STATE_INVERT, True
|
||||
),
|
||||
"RESTORE_INVERTED_DEFAULT_ON": LegacyRestoreMode(True, RESTORE_STATE_INVERT, True),
|
||||
"RESTORE_AND_OFF": LegacyRestoreMode(False, False, True),
|
||||
"RESTORE_AND_ON": LegacyRestoreMode(True, True, True),
|
||||
}
|
||||
|
||||
# (config key, LightStateRTCState member) for every field, and for every field but `state`.
|
||||
_ALL_STATE_FIELDS: tuple[tuple[str, str], ...] = tuple(
|
||||
(field.conf_key, field.member) for field in LIGHT_STATE_FIELDS
|
||||
)
|
||||
_STATE_STRUCT_FIELDS: tuple[tuple[str, str], ...] = tuple(
|
||||
(conf_key, member)
|
||||
for conf_key, member in _ALL_STATE_FIELDS
|
||||
if conf_key != CONF_STATE
|
||||
)
|
||||
# Canonical struct-member order, used only to make generated code deterministic --
|
||||
# these are independent field assignments, so the actual order never affects behavior.
|
||||
_MEMBER_ORDER: tuple[str, ...] = tuple(member for _, member in _ALL_STATE_FIELDS)
|
||||
|
||||
# A pending `s.<member> = <value>;` statement, tagged with the member it writes.
|
||||
StateStatement = tuple[str, str]
|
||||
|
||||
|
||||
def _inferred_color_mode(initial_state_config: ConfigType | None) -> str | None:
|
||||
"""A C++ expression for the capabilities needed by the colour fields `initial_state:`
|
||||
sets, or None if `color_mode:` is given or no colour field is set.
|
||||
|
||||
Colour modes are bitmasks of capabilities, so the result is a bare capability set,
|
||||
not necessarily a real mode; LightState::setup() resolves it to a mode the light
|
||||
actually supports.
|
||||
"""
|
||||
if not initial_state_config or CONF_COLOR_MODE in initial_state_config:
|
||||
return None
|
||||
modes = sorted(
|
||||
{
|
||||
str(field.color_mode)
|
||||
for field in LIGHT_STATE_FIELDS
|
||||
if field.color_mode is not None
|
||||
and initial_state_config.get(field.conf_key) is not None
|
||||
}
|
||||
)
|
||||
if not modes:
|
||||
return None
|
||||
mask = " | ".join(f"static_cast<uint8_t>({mode})" for mode in modes)
|
||||
return f"static_cast<{ColorMode}>({mask})"
|
||||
|
||||
|
||||
def _partition_state_statements(
|
||||
initial_statements: list[StateStatement],
|
||||
restore_statements: list[StateStatement],
|
||||
save_enabled: bool,
|
||||
) -> list[str]:
|
||||
"""Split initial/restore statements into what must run unconditionally versus what
|
||||
depends on `restored`, and render the resulting lambda body lines.
|
||||
|
||||
Fields whose statement is identical in both branches (e.g. RESTORE_AND_ON's
|
||||
cold-boot and restore-time statements are both "s.state = true;") are hoisted out
|
||||
of the `restored` branch entirely, so only the fields that actually depend on
|
||||
`restored` end up inside it -- down to no branch at all when every field overlaps.
|
||||
A member appearing more than once in the same list keeps only its last statement
|
||||
(a plain, side-effect-free assignment): matches sequential-execution semantics,
|
||||
since an earlier write to the same member is always fully overwritten by a later
|
||||
one in the original code this replaces.
|
||||
|
||||
`save_enabled` is false exactly when `restore_statements` is empty and `restored`
|
||||
is unconditionally false at the call site (nothing is ever loaded), so the
|
||||
initial-only branch can skip its `if (!restored)` guard entirely.
|
||||
"""
|
||||
# dict() over (member, statement) pairs keeps the *last* entry per member.
|
||||
initial_map = dict(initial_statements)
|
||||
restore_map = dict(restore_statements)
|
||||
# A member outside _MEMBER_ORDER would be silently skipped below instead of
|
||||
# raising -- catch that here so a typo doesn't turn into wrong state on a device.
|
||||
assert set(initial_map) <= set(_MEMBER_ORDER)
|
||||
assert set(restore_map) <= set(_MEMBER_ORDER)
|
||||
|
||||
common: list[str] = []
|
||||
only_initial: list[str] = []
|
||||
only_restore: list[str] = []
|
||||
for member in _MEMBER_ORDER:
|
||||
initial_stmt = initial_map.get(member)
|
||||
restore_stmt = restore_map.get(member)
|
||||
if initial_stmt is not None and initial_stmt == restore_stmt:
|
||||
common.append(initial_stmt)
|
||||
continue
|
||||
if initial_stmt is not None:
|
||||
only_initial.append(initial_stmt)
|
||||
if restore_stmt is not None:
|
||||
only_restore.append(restore_stmt)
|
||||
|
||||
body = common
|
||||
if only_restore and only_initial:
|
||||
body += ["if (restored) {", *only_restore, "} else {", *only_initial, "}"]
|
||||
elif only_restore:
|
||||
body += ["if (restored) {", *only_restore, "}"]
|
||||
elif only_initial:
|
||||
if save_enabled:
|
||||
body += ["if (!restored) {", *only_initial, "}"]
|
||||
else:
|
||||
body += only_initial
|
||||
return body
|
||||
|
||||
|
||||
async def _build_state_lambda(
|
||||
initial_statements: list[StateStatement],
|
||||
restore_statements: list[StateStatement],
|
||||
save_enabled: bool,
|
||||
) -> Lambda | None:
|
||||
"""
|
||||
Combine the initial and restore statements into a single lambda that applies the
|
||||
correct values to a `LightStateRTCState &s` depending on whether a persisted state
|
||||
actually loaded.
|
||||
"""
|
||||
if not initial_statements and not restore_statements:
|
||||
return None
|
||||
body = _partition_state_statements(
|
||||
initial_statements, restore_statements, save_enabled
|
||||
)
|
||||
args = [(LightStateRTCState.operator("ref"), "s"), (cg.bool_, "restored")]
|
||||
return await cg.process_lambda(Lambda("\n".join(body)), args, return_type=cg.void)
|
||||
|
||||
|
||||
async def _process_value(value: Any, member: str) -> str:
|
||||
if isinstance(value, Lambda):
|
||||
return_type = cg.bool_ if member == CONF_STATE else cg.float_
|
||||
lamb = await cg.process_lambda(value, [], return_type=return_type)
|
||||
return call_lambda(lamb)
|
||||
return cg.safe_exp(value)
|
||||
|
||||
|
||||
async def _initial_state_statements(
|
||||
initial_state_config: ConfigType | None,
|
||||
) -> list[StateStatement]:
|
||||
"""
|
||||
Create assignments for every field the user set in `initial_state:`, in canonical
|
||||
struct-member order. A field given as `!lambda` is resolved and called immediately,
|
||||
the same way `light.control`'s own field lambdas are.
|
||||
"""
|
||||
if not initial_state_config:
|
||||
return []
|
||||
statements: list[StateStatement] = []
|
||||
for conf_key, member in _ALL_STATE_FIELDS:
|
||||
if (value := initial_state_config.get(conf_key)) is None:
|
||||
continue
|
||||
statements.append(
|
||||
(member, f"s.{member} = {await _process_value(value, member)};")
|
||||
)
|
||||
if (inferred := _inferred_color_mode(initial_state_config)) is not None:
|
||||
statements.append(("color_mode", f"s.color_mode = {inferred};"))
|
||||
return statements
|
||||
|
||||
|
||||
async def _resolve_initial_value(
|
||||
conf_key: str, member: str, initial_state_config: ConfigType | None
|
||||
) -> str:
|
||||
"""
|
||||
Return the C++ expression to use for a `restore_state:` field whose value is INITIAL
|
||||
"""
|
||||
if (
|
||||
initial_state_config is not None
|
||||
and (value := initial_state_config.get(conf_key)) is not None
|
||||
):
|
||||
return await _process_value(value, member)
|
||||
if conf_key == CONF_COLOR_MODE and (
|
||||
inferred := _inferred_color_mode(initial_state_config)
|
||||
):
|
||||
return inferred
|
||||
return f"LightStateRTCState{{}}.{member}"
|
||||
|
||||
|
||||
async def _restore_state_statements(
|
||||
restore_state_config: ConfigType, initial_state_config: ConfigType | None
|
||||
) -> list[StateStatement]:
|
||||
"""
|
||||
Create a list of statements to apply the user's `restore_state:` config
|
||||
"""
|
||||
statements: list[StateStatement] = []
|
||||
state = restore_state_config[CONF_STATE]
|
||||
if state == RESTORE_STATE_INVERT:
|
||||
statements.append(("state", "s.state = !s.state;"))
|
||||
elif state == RESTORE_STATE_INITIAL:
|
||||
expr = await _resolve_initial_value(CONF_STATE, "state", initial_state_config)
|
||||
statements.append(("state", f"s.state = {expr};"))
|
||||
elif state != RESTORE_STATE_KEEP:
|
||||
statements.append(("state", f"s.state = {cg.safe_exp(state)};"))
|
||||
for conf_key, member in _STATE_STRUCT_FIELDS:
|
||||
value = restore_state_config[conf_key]
|
||||
if value == RESTORE_STATE_INITIAL:
|
||||
expr = await _resolve_initial_value(conf_key, member, initial_state_config)
|
||||
elif value == RESTORE_STATE_KEEP:
|
||||
continue
|
||||
else:
|
||||
expr = cg.safe_exp(value)
|
||||
statements.append((member, f"s.{member} = {expr};"))
|
||||
return statements
|
||||
|
||||
|
||||
def _legacy_restore_statements(mode: LegacyRestoreMode) -> list[StateStatement]:
|
||||
"""
|
||||
Create a list of statements to apply the legacy restore_mode: behavior.
|
||||
"""
|
||||
if mode.restore_action is None:
|
||||
return []
|
||||
if mode.restore_action == RESTORE_STATE_INVERT:
|
||||
return [("state", "s.state = !s.state;")]
|
||||
return [("state", f"s.state = {str(mode.restore_action).lower()};")]
|
||||
|
||||
|
||||
def _legacy_cold_boot_statements(
|
||||
mode: LegacyRestoreMode, initial_state_config: ConfigType | None
|
||||
) -> list[StateStatement]:
|
||||
"""
|
||||
Create a list of statements to apply the legacy restore_mode: cold-boot behavior.
|
||||
"""
|
||||
existing_state = (
|
||||
initial_state_config.get(CONF_STATE) if initial_state_config else None
|
||||
)
|
||||
if existing_state is None:
|
||||
if not mode.cold_boot_state:
|
||||
return [] # already matches LightStateRTCState's own default
|
||||
elif existing_state == mode.cold_boot_state:
|
||||
return [] # initial_state: already set exactly this value
|
||||
return [("state", f"s.state = {str(mode.cold_boot_state).lower()};")]
|
||||
|
||||
|
||||
def _initial_state_overridden_by_legacy_mode(
|
||||
mode: LegacyRestoreMode, initial_state_config: ConfigType | None
|
||||
) -> bool:
|
||||
"""
|
||||
Is the user-config `initial_state:` value for `state` overridden by the legacy mode?
|
||||
"""
|
||||
if initial_state_config is None or CONF_STATE not in initial_state_config:
|
||||
return False
|
||||
return initial_state_config[CONF_STATE] != mode.cold_boot_state
|
||||
|
||||
|
||||
def _keep_or(validator: Callable[[Any], Any]) -> Callable[[Any], Any]:
|
||||
"""
|
||||
Extend a validator to also accept the `KEEP`/`INITIAL` options
|
||||
"""
|
||||
|
||||
@schema_extractor("one_of")
|
||||
def validate(value: Any) -> Any:
|
||||
if value == SCHEMA_EXTRACT:
|
||||
# Editor completion: the sentinels plus the wrapped validator's own values,
|
||||
# if it is an enum. Numeric validators have none to offer.
|
||||
try:
|
||||
inner = tuple(validator(SCHEMA_EXTRACT))
|
||||
except cv.Invalid:
|
||||
inner = ()
|
||||
return (RESTORE_STATE_KEEP, RESTORE_STATE_INITIAL, *inner)
|
||||
if isinstance(value, str):
|
||||
upper = value.strip().upper()
|
||||
if upper in (RESTORE_STATE_KEEP, RESTORE_STATE_INITIAL):
|
||||
return upper
|
||||
return validator(value)
|
||||
|
||||
return validate
|
||||
|
||||
|
||||
@schema_extractor("one_of")
|
||||
def _validate_restore_state_state(value: Any) -> str | bool:
|
||||
if value == SCHEMA_EXTRACT:
|
||||
return (
|
||||
RESTORE_STATE_KEEP,
|
||||
RESTORE_STATE_INVERT,
|
||||
RESTORE_STATE_INITIAL,
|
||||
*validate_light_state(SCHEMA_EXTRACT),
|
||||
)
|
||||
if isinstance(value, str):
|
||||
upper = value.strip().upper()
|
||||
if upper in (RESTORE_STATE_KEEP, RESTORE_STATE_INVERT, RESTORE_STATE_INITIAL):
|
||||
return upper
|
||||
return validate_light_state(value)
|
||||
|
||||
|
||||
_RESTORE_STATE_FIELDS_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(field.conf_key, default=RESTORE_STATE_KEEP): (
|
||||
_validate_restore_state_state
|
||||
if field.conf_key == CONF_STATE
|
||||
else _keep_or(field.validator)
|
||||
)
|
||||
for field in LIGHT_STATE_FIELDS
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@schema_extractor("schema")
|
||||
def RESTORE_STATE_SCHEMA(value: Any) -> ConfigType | str:
|
||||
"""
|
||||
The restore_state: config key can be a mapping of per-field overrides, `all`, or `none`.
|
||||
"""
|
||||
if value == SCHEMA_EXTRACT:
|
||||
# The `all`/`none` string shorthands have no representation in the extracted
|
||||
# docs schema; only the per-field mapping form is walked here.
|
||||
return _RESTORE_STATE_FIELDS_SCHEMA
|
||||
if isinstance(value, str):
|
||||
upper = value.strip().upper()
|
||||
if upper == RESTORE_STATE_ALL:
|
||||
value = {}
|
||||
elif upper == RESTORE_STATE_NONE:
|
||||
return RESTORE_STATE_NONE
|
||||
return _RESTORE_STATE_FIELDS_SCHEMA(value)
|
||||
@@ -11,8 +11,6 @@ from esphome.const import (
|
||||
CONF_REPEAT,
|
||||
CONF_WRITE_PIN,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.cpp_helpers import gpio_pin_expression
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -24,7 +22,6 @@ lightwaverf_ns = cg.esphome_ns.namespace("lightwaverf")
|
||||
LIGHTWAVERFComponent = lightwaverf_ns.class_(
|
||||
"LightWaveRF", cg.Component, cg.PollingComponent
|
||||
)
|
||||
LightwaveRawAction = lightwaverf_ns.class_("SendRawAction", automation.Action)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
@@ -36,49 +33,36 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
).extend(cv.polling_component_schema("1s"))
|
||||
|
||||
|
||||
LIGHTWAVE_SEND_SCHEMA = cv.Any(
|
||||
cv.int_range(min=1),
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(LIGHTWAVERFComponent),
|
||||
cv.Required(CONF_NAME): cv.string,
|
||||
cv.Required(CONF_CODE): cv.All(
|
||||
[cv.Any(cv.hex_uint8_t)],
|
||||
cv.Length(min=10),
|
||||
),
|
||||
cv.Optional(CONF_REPEAT, default=10): cv.int_,
|
||||
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
cv.Optional(CONF_PULSE_LENGTH, default=330): cv.int_,
|
||||
}
|
||||
LIGHTWAVE_SEND_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(LIGHTWAVERFComponent),
|
||||
cv.Required(CONF_NAME): cv.string,
|
||||
cv.Required(CONF_CODE): cv.All(
|
||||
[cv.Any(cv.hex_uint8_t)],
|
||||
cv.Length(min=10),
|
||||
),
|
||||
cv.Optional(CONF_REPEAT, default=10): cv.int_,
|
||||
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
cv.Optional(CONF_PULSE_LENGTH, default=330): cv.int_,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
automation.register_apply_action(
|
||||
"lightwaverf.send_raw",
|
||||
LIGHTWAVE_SEND_SCHEMA,
|
||||
automation.ApplyCall(
|
||||
"send_rx({}, {}, {}, {})",
|
||||
(
|
||||
(CONF_CODE, cg.std_vector.template(cg.uint8)),
|
||||
(CONF_REPEAT, cg.uint8),
|
||||
(CONF_INVERTED, cg.bool_),
|
||||
(CONF_PULSE_LENGTH, cg.int_),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"lightwaverf.send_raw",
|
||||
LightwaveRawAction,
|
||||
LIGHTWAVE_SEND_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def send_raw_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
template_ = await cg.templatable(config[CONF_REPEAT], args, cg.int_)
|
||||
cg.add(var.set_repeat(template_))
|
||||
template_ = await cg.templatable(config[CONF_INVERTED], args, cg.int_)
|
||||
cg.add(var.set_inverted(template_))
|
||||
template_ = await cg.templatable(config[CONF_PULSE_LENGTH], args, cg.int_)
|
||||
cg.add(var.set_pulse_length(template_))
|
||||
cg.add(var.set_code(config[CONF_CODE]))
|
||||
return var
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -37,28 +36,6 @@ class LightWaveRF final : public PollingComponent {
|
||||
LwTx lwtx_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class SendRawAction final : public Action<Ts...> {
|
||||
public:
|
||||
SendRawAction(LightWaveRF *parent) : parent_(parent){};
|
||||
TEMPLATABLE_VALUE(int, repeat);
|
||||
TEMPLATABLE_VALUE(int, inverted);
|
||||
TEMPLATABLE_VALUE(int, pulse_length);
|
||||
TEMPLATABLE_VALUE(std::vector<uint8_t>, code);
|
||||
void set_code(std::initializer_list<uint8_t> data) { this->code_ = std::vector<uint8_t>(data); }
|
||||
|
||||
void play(const Ts &...x) {
|
||||
int repeats = this->repeat_.value(x...);
|
||||
int inverted = this->inverted_.value(x...);
|
||||
int pulse_length = this->pulse_length_.value(x...);
|
||||
std::vector<uint8_t> msg = this->code_.value(x...);
|
||||
|
||||
this->parent_->send_rx(msg, repeats, inverted, pulse_length);
|
||||
}
|
||||
|
||||
protected:
|
||||
LightWaveRF *parent_;
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace esphome::lightwaverf
|
||||
#endif
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
|
||||
#pragma once
|
||||
#include "esphome/core/automation.h"
|
||||
#include "max17043.h"
|
||||
|
||||
namespace esphome::max17043 {
|
||||
|
||||
template<typename... Ts> class SleepAction final : public Action<Ts...> {
|
||||
public:
|
||||
explicit SleepAction(MAX17043Component *max17043) : max17043_(max17043) {}
|
||||
|
||||
void play(const Ts &...x) override { this->max17043_->sleep_mode(); }
|
||||
|
||||
protected:
|
||||
MAX17043Component *max17043_;
|
||||
};
|
||||
|
||||
} // namespace esphome::max17043
|
||||
@@ -14,8 +14,6 @@ from esphome.const import (
|
||||
UNIT_PERCENT,
|
||||
UNIT_VOLT,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
@@ -25,9 +23,6 @@ MAX17043Component = max17043_ns.class_(
|
||||
"MAX17043Component", cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
# Actions
|
||||
SleepAction = max17043_ns.class_("SleepAction", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
@@ -74,14 +69,6 @@ MAX17043_ACTION_SCHEMA = maybe_simple_id(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"max17043.sleep_mode", SleepAction, MAX17043_ACTION_SCHEMA, synchronous=True
|
||||
automation.register_apply_action(
|
||||
"max17043.sleep_mode", MAX17043_ACTION_SCHEMA, automation.ApplyCall("sleep_mode()")
|
||||
)
|
||||
async def max17043_sleep_mode_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
@@ -11,8 +11,7 @@ from esphome.const import (
|
||||
CONF_OUTPUT,
|
||||
CONF_PULLUP,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@looping40"]
|
||||
@@ -29,10 +28,6 @@ max6956_ns = cg.esphome_ns.namespace("max6956")
|
||||
MAX6956 = max6956_ns.class_("MAX6956", cg.Component, i2c.I2CDevice)
|
||||
MAX6956GPIOPin = max6956_ns.class_("MAX6956GPIOPin", cg.GPIOPin)
|
||||
|
||||
# Actions
|
||||
SetCurrentGlobalAction = max6956_ns.class_("SetCurrentGlobalAction", automation.Action)
|
||||
SetCurrentModeAction = max6956_ns.class_("SetCurrentModeAction", automation.Action)
|
||||
|
||||
MAX6956_CURRENTMODE = max6956_ns.enum("MAX6956CURRENTMODE")
|
||||
CURRENT_MODES = {
|
||||
"global": MAX6956_CURRENTMODE.GLOBAL,
|
||||
@@ -103,9 +98,8 @@ async def max6956_pin_to_code(config: ConfigType) -> MockObj:
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"max6956.set_brightness_global",
|
||||
SetCurrentGlobalAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(CONF_ID): cv.use_id(MAX6956),
|
||||
@@ -115,24 +109,12 @@ async def max6956_pin_to_code(config: ConfigType) -> MockObj:
|
||||
},
|
||||
key=CONF_BRIGHTNESS_GLOBAL,
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_BRIGHTNESS_GLOBAL, "set_brightness_global", cg.uint8),
|
||||
automation.ApplyCall("write_brightness_global()"),
|
||||
)
|
||||
async def max6956_set_brightness_global_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(config[CONF_BRIGHTNESS_GLOBAL], args, cg.uint8)
|
||||
cg.add(var.set_brightness_global(template_))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"max6956.set_brightness_mode",
|
||||
SetCurrentModeAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(MAX6956),
|
||||
@@ -142,18 +124,8 @@ async def max6956_set_brightness_global_to_code(
|
||||
},
|
||||
key=CONF_BRIGHTNESS_MODE,
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(
|
||||
CONF_BRIGHTNESS_MODE, "set_brightness_mode", MAX6956_CURRENTMODE
|
||||
),
|
||||
automation.ApplyCall("write_brightness_mode()"),
|
||||
)
|
||||
async def max6956_set_brightness_mode_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(
|
||||
config[CONF_BRIGHTNESS_MODE], args, MAX6956_CURRENTMODE
|
||||
)
|
||||
cg.add(var.set_brightness_mode(template_))
|
||||
return var
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/max6956/max6956.h"
|
||||
|
||||
namespace esphome::max6956 {
|
||||
|
||||
template<typename... Ts> class SetCurrentGlobalAction final : public Action<Ts...> {
|
||||
public:
|
||||
SetCurrentGlobalAction(MAX6956 *max6956) : max6956_(max6956) {}
|
||||
|
||||
TEMPLATABLE_VALUE(uint8_t, brightness_global)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
this->max6956_->set_brightness_global(this->brightness_global_.value(x...));
|
||||
this->max6956_->write_brightness_global();
|
||||
}
|
||||
|
||||
protected:
|
||||
MAX6956 *max6956_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetCurrentModeAction final : public Action<Ts...> {
|
||||
public:
|
||||
SetCurrentModeAction(MAX6956 *max6956) : max6956_(max6956) {}
|
||||
|
||||
TEMPLATABLE_VALUE(max6956::MAX6956CURRENTMODE, brightness_mode)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
this->max6956_->set_brightness_mode(this->brightness_mode_.value(x...));
|
||||
this->max6956_->write_brightness_mode();
|
||||
}
|
||||
|
||||
protected:
|
||||
MAX6956 *max6956_;
|
||||
};
|
||||
} // namespace esphome::max6956
|
||||
@@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include "max7219digit.h"
|
||||
|
||||
namespace esphome::max7219digit {
|
||||
|
||||
template<typename... Ts> class DisplayInvertAction final : public Action<Ts...>, public Parented<MAX7219Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(bool, state)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
bool state = this->state_.value(x...);
|
||||
this->parent_->invert_on_off(state);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayVisibilityAction final : public Action<Ts...>, public Parented<MAX7219Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(bool, state)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
bool state = this->state_.value(x...);
|
||||
this->parent_->turn_on_off(state);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayReverseAction final : public Action<Ts...>, public Parented<MAX7219Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(bool, state)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
bool state = this->state_.value(x...);
|
||||
this->parent_->set_reverse(state);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class DisplayIntensityAction final : public Action<Ts...>, public Parented<MAX7219Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, state)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
uint8_t state = this->state_.value(x...);
|
||||
this->parent_->set_intensity(state);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::max7219digit
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user