Merge branch 'platformio-pch-libretiny' into pch-strict-ci

This commit is contained in:
J. Nick Koston
2026-09-26 10:32:27 +01:00
361 changed files with 6451 additions and 5996 deletions
+3
View File
@@ -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
View File
@@ -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 \
+4
View File
@@ -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",
+47 -87
View File
@@ -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,
)
+9 -5
View File
@@ -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.
"""
+1 -7
View File
@@ -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.
+14
View File
@@ -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);
+14 -36
View File
@@ -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
+7 -41
View File
@@ -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
+2 -19
View File
@@ -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:
-21
View File
@@ -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
-30
View File
@@ -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
+8 -32
View File
@@ -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:
+9
View File
@@ -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();
+6
View File
@@ -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,
+6 -3
View File
@@ -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);
+3
View File
@@ -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 {
+4 -21
View File
@@ -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)
-21
View File
@@ -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
+2 -9
View File
@@ -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
+3 -10
View File
@@ -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
+2 -15
View File
@@ -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:
+3 -12
View File
@@ -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):
-20
View File
@@ -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
+13 -58
View File
@@ -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:
+9 -20
View File
@@ -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):
-20
View File
@@ -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; }
+1
View File
@@ -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();
-11
View File
@@ -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
+2 -18
View File
@@ -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)
-11
View File
@@ -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
+2 -14
View File
@@ -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)
+2 -9
View File
@@ -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
-5
View File
@@ -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
+2 -16
View File
@@ -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
+2 -11
View File
@@ -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)
-12
View File
@@ -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) {
-9
View File
@@ -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
+4 -29
View File
@@ -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
+15 -82
View File
@@ -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 -19
View File
@@ -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
+3 -3
View File
@@ -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 -10
View File
@@ -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
+18 -30
View File
@@ -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
+34 -59
View File
@@ -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(":?", "")
+2
View File
@@ -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")
+3 -17
View File
@@ -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
-13
View File
@@ -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
+12 -23
View File
@@ -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
)
+28 -183
View File
@@ -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
-134
View File
@@ -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
+62 -117
View File
@@ -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
+29 -106
View File
@@ -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
-128
View File
@@ -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
+34 -134
View File
@@ -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:
+2 -17
View File
@@ -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
-8
View File
@@ -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
+2 -17
View File
@@ -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
-18
View File
@@ -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
+6 -37
View File
@@ -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
-23
View File
@@ -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
+4 -33
View File
@@ -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
+3 -17
View File
@@ -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
+4 -31
View File
@@ -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
+5 -19
View File
@@ -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
+3 -3
View File
@@ -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);
}
-21
View File
@@ -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
+12 -39
View File
@@ -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
+2 -22
View File
@@ -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
-20
View File
@@ -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
-1
View File
@@ -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>
+98 -68
View File
@@ -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)
+74 -11
View File
@@ -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
}
)
+2 -1
View File
@@ -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="_"
+28 -41
View File
@@ -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();
+11 -35
View File
@@ -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
+345
View File
@@ -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)
+26 -42
View File
@@ -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
-18
View File
@@ -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
+2 -15
View File
@@ -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)
+9 -37
View File
@@ -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
-38
View File
@@ -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