Merge branch 'esp8266-arduino-toolchain' into esp8266-native-pch

This commit is contained in:
J. Nick Koston
2026-09-23 23:36:21 +01:00
116 changed files with 887 additions and 2422 deletions
@@ -41,10 +41,6 @@ StateAnyForwarder = alarm_control_panel_ns.class_("StateAnyForwarder")
StateEnterForwarder = alarm_control_panel_ns.class_("StateEnterForwarder")
AlarmControlPanelState = alarm_control_panel_ns.enum("AlarmControlPanelState")
ArmAwayAction = alarm_control_panel_ns.class_("ArmAwayAction", automation.Action)
ArmHomeAction = alarm_control_panel_ns.class_("ArmHomeAction", automation.Action)
ArmNightAction = alarm_control_panel_ns.class_("ArmNightAction", automation.Action)
DisarmAction = alarm_control_panel_ns.class_("DisarmAction", automation.Action)
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)
@@ -196,64 +192,20 @@ async def new_alarm_control_panel(config, *args):
return var
@automation.register_action(
"alarm_control_panel.arm_away",
ArmAwayAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
async def alarm_action_arm_away_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)
if code_config := config.get(CONF_CODE):
templatable_ = await cg.templatable(code_config, args, cg.std_string)
cg.add(var.set_code(templatable_))
return var
@automation.register_action(
"alarm_control_panel.arm_home",
ArmHomeAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
async def alarm_action_arm_home_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)
if code_config := config.get(CONF_CODE):
templatable_ = await cg.templatable(code_config, args, cg.std_string)
cg.add(var.set_code(templatable_))
return var
@automation.register_action(
"alarm_control_panel.arm_night",
ArmNightAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
async def alarm_action_arm_night_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)
if CONF_CODE in config:
templatable_ = await cg.templatable(config[CONF_CODE], args, cg.std_string)
cg.add(var.set_code(templatable_))
return var
@automation.register_action(
"alarm_control_panel.disarm",
DisarmAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
async def alarm_action_disarm_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)
if code_config := config.get(CONF_CODE):
templatable_ = await cg.templatable(code_config, args, cg.std_string)
cg.add(var.set_code(templatable_))
return var
# Mirrors AlarmControlPanel::arm_with_code_: arm first, set the code only when given.
for _name, _arm in (
("alarm_control_panel.arm_away", "arm_away()"),
("alarm_control_panel.arm_home", "arm_home()"),
("alarm_control_panel.arm_night", "arm_night()"),
("alarm_control_panel.disarm", "disarm()"),
):
automation.register_apply_action(
_name,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
automation.ApplyCall(_arm),
automation.ApplyField(CONF_CODE, "set_code", cg.std_string),
call="make_call",
)
@automation.register_action(
@@ -27,54 +27,6 @@ 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 ArmAwayAction final : public Action<Ts...> {
public:
explicit ArmAwayAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
TEMPLATABLE_VALUE(std::string, code)
void play(const Ts &...x) override { this->alarm_control_panel_->arm_away(this->code_.optional_value(x...)); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class ArmHomeAction final : public Action<Ts...> {
public:
explicit ArmHomeAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
TEMPLATABLE_VALUE(std::string, code)
void play(const Ts &...x) override { this->alarm_control_panel_->arm_home(this->code_.optional_value(x...)); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class ArmNightAction final : public Action<Ts...> {
public:
explicit ArmNightAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
TEMPLATABLE_VALUE(std::string, code)
void play(const Ts &...x) override { this->alarm_control_panel_->arm_night(this->code_.optional_value(x...)); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class DisarmAction final : public Action<Ts...> {
public:
explicit DisarmAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
TEMPLATABLE_VALUE(std::string, code)
void play(const Ts &...x) override { this->alarm_control_panel_->disarm(this->code_.optional_value(x...)); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class PendingAction final : public Action<Ts...> {
public:
explicit PendingAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -134,9 +134,6 @@ MultiClickTriggerBase = binary_sensor_ns.class_(
MultiClickTrigger = binary_sensor_ns.class_("MultiClickTrigger", MultiClickTriggerBase)
MultiClickTriggerEvent = binary_sensor_ns.struct("MultiClickTriggerEvent")
BinarySensorPublishAction = binary_sensor_ns.class_(
"BinarySensorPublishAction", automation.Action
)
BinarySensorInvalidateAction = binary_sensor_ns.class_(
"BinarySensorInvalidateAction", automation.Action
)
@@ -165,20 +165,6 @@ template<typename... Ts> class BinarySensorCondition final : public Condition<Ts
bool state_;
};
template<typename... Ts> class BinarySensorPublishAction final : public Action<Ts...> {
public:
explicit BinarySensorPublishAction(BinarySensor *sensor) : sensor_(sensor) {}
TEMPLATABLE_VALUE(bool, state)
void play(const Ts &...x) override {
auto val = this->state_.value(x...);
this->sensor_->publish_state(val);
}
protected:
BinarySensor *sensor_;
};
template<typename... Ts> class BinarySensorInvalidateAction final : public Action<Ts...> {
public:
explicit BinarySensorInvalidateAction(BinarySensor *sensor) : sensor_(sensor) {}
@@ -53,7 +53,6 @@ BK72xxBLETracker = bk72xx_ble_tracker_ns.class_(
)
StartScanAction = bk72xx_ble_tracker_ns.class_("StartScanAction", automation.Action)
StopScanAction = bk72xx_ble_tracker_ns.class_("StopScanAction", automation.Action)
ESPBTAdvertiseTrigger = ble_automation.ESPBTAdvertiseTrigger
BLEServiceDataAdvertiseTrigger = ble_automation.BLEServiceDataAdvertiseTrigger
@@ -121,9 +120,8 @@ async def start_scan_action_to_code(
return var
@automation.register_action(
automation.register_apply_action(
"bk72xx_ble_tracker.stop_scan",
StopScanAction,
automation.maybe_simple_id(
cv.Schema(
{
@@ -131,17 +129,8 @@ async def start_scan_action_to_code(
}
)
),
synchronous=True,
automation.ApplyCall("stop_scan()"),
)
async def stop_scan_action_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: list,
) -> cg.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:
@@ -38,11 +38,6 @@ template<typename... Ts> class StartScanAction final : public Action<Ts...>, pub
}
};
template<typename... Ts> class StopScanAction final : public Action<Ts...>, public Parented<BK72xxBLETracker> {
public:
void play(const Ts &...x) override { this->parent_->stop_scan(); }
};
} // namespace esphome::bk72xx_ble_tracker
#endif // USE_LIBRETINY
+32 -82
View File
@@ -424,91 +424,41 @@ async def send_packet_action_to_code(config, action_id, template_arg, args):
return var
# Setter action definitions: (setter_name, validator, template_type, enum_map)
_SETTER_ACTIONS = [
(
"set_frequency",
cv.All(cv.frequency, cv.float_range(min=300.0e6, max=928.0e6)),
float,
None,
),
("set_output_power", cv.float_range(min=-30.0, max=11.0), float, None),
("set_modulation_type", cv.enum(MODULATION, upper=False), Modulation, MODULATION),
("set_symbol_rate", cv.float_range(min=600, max=500000), float, None),
(
"set_rx_attenuation",
cv.enum(RX_ATTENUATION, upper=False),
RxAttenuation,
RX_ATTENUATION,
),
("set_dc_blocking_filter", cv.boolean, bool, None),
("set_manchester", cv.boolean, bool, None),
(
"set_filter_bandwidth",
cv.All(cv.frequency, cv.float_range(min=58000, max=812000)),
float,
None,
),
(
"set_fsk_deviation",
cv.All(cv.frequency, cv.float_range(min=1500, max=381000)),
float,
None,
),
("set_msk_deviation", cv.int_range(min=1, max=8), cg.uint8, None),
("set_channel", cv.uint8_t, cg.uint8, None),
(
"set_channel_spacing",
cv.All(cv.frequency, cv.float_range(min=25000, max=405000)),
float,
None,
),
(
"set_if_frequency",
cv.All(cv.frequency, cv.float_range(min=25000, max=788000)),
float,
None,
),
]
_CONFIG_VALIDATORS = {opt.schema: validator for opt, validator in CONFIG_MAP.items()}
# Each key has a set_<key> action validated exactly like the config option.
_SETTER_ACTIONS = (
(CONF_FREQUENCY, cg.float_),
(CONF_OUTPUT_POWER, cg.float_),
(CONF_MODULATION_TYPE, Modulation),
(CONF_SYMBOL_RATE, cg.float_),
(CONF_RX_ATTENUATION, RxAttenuation),
(CONF_DC_BLOCKING_FILTER, cg.bool_),
(CONF_MANCHESTER, cg.bool_),
(CONF_FILTER_BANDWIDTH, cg.float_),
(CONF_FSK_DEVIATION, cg.float_),
(CONF_MSK_DEVIATION, cg.uint8),
(CONF_CHANNEL, cg.uint8),
(CONF_CHANNEL_SPACING, cg.float_),
(CONF_IF_FREQUENCY, cg.float_),
)
def _register_setter_actions():
for setter_name, validator, templ_type, enum_map in _SETTER_ACTIONS:
class_name = (
"".join(word.capitalize() for word in setter_name.split("_")) + "Action"
def _register_setter_actions() -> None:
for conf_key, value_type in _SETTER_ACTIONS:
automation.register_apply_action(
f"cc1101.set_{conf_key}",
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(CC1101Component),
cv.Required(CONF_VALUE): cv.templatable(
_CONFIG_VALIDATORS[conf_key]
),
},
key=CONF_VALUE,
),
automation.ApplyField(CONF_VALUE, f"set_{conf_key}", value_type),
)
action_cls = ns.class_(
class_name, automation.Action, cg.Parented.template(CC1101Component)
)
schema = cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(CC1101Component),
cv.Required(CONF_VALUE): cv.templatable(validator),
},
key=CONF_VALUE,
)
async def _setter_action_to_code(
config,
action_id,
template_arg,
args,
_setter=setter_name,
_type=templ_type,
_map=enum_map,
):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
data = config[CONF_VALUE]
if _map and not cg.is_template(data):
data = _map[data]
templ_ = await cg.templatable(data, args, _type)
cg.add(getattr(var, _setter)(templ_))
return var
automation.register_action(
f"cc1101.{setter_name}", action_cls, schema, synchronous=True
)(_setter_action_to_code)
_register_setter_actions()
-79
View File
@@ -174,83 +174,4 @@ template<typename... Ts> class SendPacketAction final : public Action<Ts...>, pu
size_t data_static_len_{0};
};
template<typename... Ts> class SetSymbolRateAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, symbol_rate)
void play(const Ts &...x) override { this->parent_->set_symbol_rate(this->symbol_rate_.value(x...)); }
};
template<typename... Ts> class SetFrequencyAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, frequency)
void play(const Ts &...x) override { this->parent_->set_frequency(this->frequency_.value(x...)); }
};
template<typename... Ts> class SetOutputPowerAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, output_power)
void play(const Ts &...x) override { this->parent_->set_output_power(this->output_power_.value(x...)); }
};
template<typename... Ts> class SetModulationTypeAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(Modulation, modulation_type)
void play(const Ts &...x) override { this->parent_->set_modulation_type(this->modulation_type_.value(x...)); }
};
template<typename... Ts> class SetRxAttenuationAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(RxAttenuation, rx_attenuation)
void play(const Ts &...x) override { this->parent_->set_rx_attenuation(this->rx_attenuation_.value(x...)); }
};
template<typename... Ts>
class SetDcBlockingFilterAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(bool, dc_blocking_filter)
void play(const Ts &...x) override { this->parent_->set_dc_blocking_filter(this->dc_blocking_filter_.value(x...)); }
};
template<typename... Ts> class SetManchesterAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(bool, manchester)
void play(const Ts &...x) override { this->parent_->set_manchester(this->manchester_.value(x...)); }
};
template<typename... Ts> class SetFilterBandwidthAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, filter_bandwidth)
void play(const Ts &...x) override { this->parent_->set_filter_bandwidth(this->filter_bandwidth_.value(x...)); }
};
template<typename... Ts> class SetFskDeviationAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, fsk_deviation)
void play(const Ts &...x) override { this->parent_->set_fsk_deviation(this->fsk_deviation_.value(x...)); }
};
template<typename... Ts> class SetMskDeviationAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(uint8_t, msk_deviation)
void play(const Ts &...x) override { this->parent_->set_msk_deviation(this->msk_deviation_.value(x...)); }
};
template<typename... Ts> class SetChannelAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(uint8_t, channel)
void play(const Ts &...x) override { this->parent_->set_channel(this->channel_.value(x...)); }
};
template<typename... Ts> class SetChannelSpacingAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, channel_spacing)
void play(const Ts &...x) override { this->parent_->set_channel_spacing(this->channel_spacing_.value(x...)); }
};
template<typename... Ts> class SetIfFrequencyAction final : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, if_frequency)
void play(const Ts &...x) override { this->parent_->set_if_frequency(this->if_frequency_.value(x...)); }
};
} // namespace esphome::cc1101
+34 -77
View File
@@ -50,18 +50,13 @@ from esphome.const import (
CONF_VISUAL,
CONF_WEB_SERVER,
)
from esphome.core import CORE, ID, CoroPriority, Lambda, coroutine_with_priority
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.core.entity_helpers import (
entity_duplicate_validator,
queue_entity_register,
setup_entity,
)
from esphome.cpp_generator import (
LambdaExpression,
MockObj,
MockObjClass,
TemplateArgsType,
)
from esphome.cpp_generator import MockObj, MockObjClass
from esphome.types import ConfigType, SafeExpType
IS_PLATFORM_COMPONENT = True
@@ -169,7 +164,6 @@ def visual_temperature_step(value: Any) -> ConfigType:
# Actions
ControlAction = climate_ns.class_("ControlAction", automation.Action)
StateTrigger = climate_ns.class_(
"StateTrigger", automation.Trigger.template(Climate.operator("ref"))
)
@@ -500,78 +494,41 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
)
@automation.register_action(
def _literal_with_length(config: ConfigType, value: str) -> str:
# The (const char *, size_t) overload compares bytes in place, so it needs a plain
# literal rather than the default PROGMEM rendering on ESP8266, and skips a strlen.
return f"{cg.safe_exp(value)}, {len(value.encode('utf-8'))}"
automation.register_apply_action(
"climate.control",
ControlAction,
CLIMATE_CONTROL_ACTION_SCHEMA,
synchronous=True,
automation.ApplyField(CONF_MODE, "set_mode", ClimateMode),
automation.ApplyField(CONF_TARGET_TEMPERATURE, "set_target_temperature", cg.float_),
automation.ApplyField(
CONF_TARGET_TEMPERATURE_LOW, "set_target_temperature_low", cg.float_
),
automation.ApplyField(
CONF_TARGET_TEMPERATURE_HIGH, "set_target_temperature_high", cg.float_
),
automation.ApplyField(CONF_TARGET_HUMIDITY, "set_target_humidity", cg.float_),
automation.ApplyField(CONF_FAN_MODE, "set_fan_mode", ClimateFanMode),
automation.ApplyField(
CONF_CUSTOM_FAN_MODE,
"set_fan_mode",
cg.std_string,
const_fn=_literal_with_length,
),
automation.ApplyField(CONF_PRESET, "set_preset", ClimatePreset),
automation.ApplyField(
CONF_CUSTOM_PRESET,
"set_preset",
cg.std_string,
const_fn=_literal_with_length,
),
automation.ApplyField(CONF_SWING_MODE, "set_swing_mode", ClimateSwingMode),
call="make_call",
)
async def climate_control_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID])
# All configured fields are folded into a single stateless lambda whose
# constants live in flash; the action stores only a function pointer.
# For custom_fan_mode/custom_preset the static-string path emits the
# (const char *, size_t) overload of set_fan_mode/set_preset to avoid
# constructing a std::string and calling runtime strlen.
FIELDS = (
(CONF_MODE, "set_mode", ClimateMode),
(CONF_TARGET_TEMPERATURE, "set_target_temperature", cg.float_),
(CONF_TARGET_TEMPERATURE_LOW, "set_target_temperature_low", cg.float_),
(CONF_TARGET_TEMPERATURE_HIGH, "set_target_temperature_high", cg.float_),
(CONF_TARGET_HUMIDITY, "set_target_humidity", cg.float_),
(CONF_FAN_MODE, "set_fan_mode", ClimateFanMode),
(CONF_CUSTOM_FAN_MODE, "set_fan_mode", cg.std_string),
(CONF_PRESET, "set_preset", ClimatePreset),
(CONF_CUSTOM_PRESET, "set_preset", cg.std_string),
(CONF_SWING_MODE, "set_swing_mode", ClimateSwingMode),
)
# Normalize trigger args to `const std::remove_cvref_t<T> &` so the
# apply lambda and any inner field lambdas (generated below via
# `process_lambda`) share one parameter spelling that's well-formed for
# any T (value, ref, or const-ref). Matches ControlAction::ApplyFn.
normalized_args = [
(cg.RawExpression(f"const std::remove_cvref_t<{cg.safe_exp(t)}> &"), n)
for t, n in args
]
fwd_args = ", ".join(name for _, name in args)
body_lines: list[str] = []
for conf_key, setter, type_ in FIELDS:
if (value := config.get(conf_key)) is None:
continue
if isinstance(value, Lambda):
inner = await cg.process_lambda(value, normalized_args, return_type=type_)
body_lines.append(f"call.{setter}(({inner})({fwd_args}));")
elif type_ is cg.std_string:
# Static custom strings: emit a flash literal and pass the
# UTF-8 byte length to skip the runtime strlen inside
# set_fan_mode/set_preset.
literal = cg.safe_exp(value)
body_lines.append(
f"call.{setter}({literal}, {len(value.encode('utf-8'))});"
)
else:
body_lines.append(f"call.{setter}({cg.safe_exp(value)});")
apply_args = [
(ClimateCall.operator("ref"), "call"),
*normalized_args,
]
apply_lambda = LambdaExpression(
["\n".join(body_lines)],
apply_args,
capture="",
return_type=cg.void,
)
return cg.new_Pvariable(action_id, template_arg, paren, apply_lambda)
@coroutine_with_priority(CoroPriority.CORE)
-28
View File
@@ -5,34 +5,6 @@
namespace esphome::climate {
// All configured fields are baked into a single stateless lambda whose
// constants live in flash. The action only stores one function pointer
// plus one parent pointer, regardless of how many fields the user set.
// Trigger args are forwarded to the apply function so user lambdas
// (e.g. `target_temperature: !lambda "return x;"`) keep working.
//
// Trigger args are normalized to `const std::remove_cvref_t<Ts> &...` so
// the codegen can emit a matching parameter list for both the apply lambda
// and any inner field lambdas without producing invalid C++ source text
// (e.g. `const T & &` if Ts already carries a reference, or `const const
// T &` if Ts already carries a const). This keeps trigger args no-copy
// regardless of whether the trigger supplies `T`, `T &`, or `const T &`.
template<typename... Ts> class ControlAction final : public Action<Ts...> {
public:
using ApplyFn = void (*)(ClimateCall &, const std::remove_cvref_t<Ts> &...);
ControlAction(Climate *climate, ApplyFn apply) : climate_(climate), apply_(apply) {}
void play(const Ts &...x) override {
auto call = this->climate_->make_call();
this->apply_(call, x...);
call.perform();
}
protected:
Climate *climate_;
ApplyFn apply_;
};
class ControlTrigger final : public Trigger<ClimateCall &> {
public:
ControlTrigger(Climate *climate) {
+60 -117
View File
@@ -1,3 +1,5 @@
from collections.abc import Callable
from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt, time, web_server
@@ -21,13 +23,13 @@ from esphome.const import (
CONF_WEB_SERVER,
CONF_YEAR,
)
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.core.entity_helpers import (
entity_duplicate_validator,
queue_entity_register,
setup_entity,
)
from esphome.cpp_generator import MockObj, MockObjClass, TemplateArgsType
from esphome.cpp_generator import MockObj, MockObjClass
from esphome.types import ConfigType, SafeExpType
CODEOWNERS = ["@rfdarter", "@jesserockz"]
@@ -40,11 +42,6 @@ DateEntity = datetime_ns.class_("DateEntity", DateTimeBase)
TimeEntity = datetime_ns.class_("TimeEntity", DateTimeBase)
DateTimeEntity = datetime_ns.class_("DateTimeEntity", DateTimeBase)
# Actions
DateSetAction = datetime_ns.class_("DateSetAction", automation.Action)
TimeSetAction = datetime_ns.class_("TimeSetAction", automation.Action)
DateTimeSetAction = datetime_ns.class_("DateTimeSetAction", automation.Action)
DateTimeStateTrigger = datetime_ns.class_(
"DateTimeStateTrigger", automation.Trigger.template(cg.ESPTime)
)
@@ -181,118 +178,64 @@ async def to_code(config: ConfigType) -> None:
cg.add_global(datetime_ns.using)
@automation.register_action(
"datetime.date.set",
DateSetAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(DateEntity),
cv.Required(CONF_DATE): cv.Any(
cv.returning_lambda, cv.date_time(date=True, time=False)
),
}
),
synchronous=True,
)
async def datetime_date_set_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
action_var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(action_var, config[CONF_ID])
date_config = config[CONF_DATE]
if cg.is_template(date_config):
template_ = await cg.templatable(date_config, args, cg.ESPTime)
cg.add(action_var.set_date(template_))
else:
date_struct = cg.StructInitializer(
cg.ESPTime,
("day_of_month", date_config[CONF_DAY]),
("month", date_config[CONF_MONTH]),
("year", date_config[CONF_YEAR]),
def _esptime_initializer(
members: tuple[tuple[str, str], ...],
) -> Callable[[ConfigType, ConfigType], str]:
def const_fn(config: ConfigType, value: ConfigType) -> str:
return str(
cg.StructInitializer(
cg.ESPTime, *((member, value[key]) for member, key in members)
)
)
template_ = await cg.templatable(date_struct, args, cg.ESPTime)
cg.add(action_var.set_date(template_))
return action_var
return const_fn
@automation.register_action(
"datetime.time.set",
TimeSetAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(TimeEntity),
cv.Required(CONF_TIME): cv.Any(
cv.returning_lambda, cv.date_time(date=False, time=True)
),
}
# ESPTime member order, so the designated initializer compiles.
_TIME_MEMBERS = (("second", CONF_SECOND), ("minute", CONF_MINUTE), ("hour", CONF_HOUR))
_DATE_MEMBERS = (("day_of_month", CONF_DAY), ("month", CONF_MONTH), ("year", CONF_YEAR))
for _name, _entity, _key, _target, _date, _time, _members in (
(
"datetime.date.set",
DateEntity,
CONF_DATE,
"set_date",
True,
False,
_DATE_MEMBERS,
),
synchronous=True,
)
async def datetime_time_set_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
action_var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(action_var, config[CONF_ID])
time_config = config[CONF_TIME]
if cg.is_template(time_config):
template_ = await cg.templatable(time_config, args, cg.ESPTime)
cg.add(action_var.set_time(template_))
else:
time_struct = cg.StructInitializer(
cg.ESPTime,
("second", time_config[CONF_SECOND]),
("minute", time_config[CONF_MINUTE]),
("hour", time_config[CONF_HOUR]),
)
template_ = await cg.templatable(time_struct, args, cg.ESPTime)
cg.add(action_var.set_time(template_))
return action_var
@automation.register_action(
"datetime.datetime.set",
DateTimeSetAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(DateTimeEntity),
cv.Required(CONF_DATETIME): cv.Any(
cv.returning_lambda, cv.date_time(date=True, time=True)
),
},
(
"datetime.time.set",
TimeEntity,
CONF_TIME,
"set_time",
False,
True,
_TIME_MEMBERS,
),
synchronous=True,
)
async def datetime_datetime_set_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
action_var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(action_var, config[CONF_ID])
datetime_config = config[CONF_DATETIME]
if cg.is_template(datetime_config):
template_ = await cg.templatable(datetime_config, args, cg.ESPTime)
cg.add(action_var.set_datetime(template_))
else:
datetime_struct = cg.StructInitializer(
cg.ESPTime,
("second", datetime_config[CONF_SECOND]),
("minute", datetime_config[CONF_MINUTE]),
("hour", datetime_config[CONF_HOUR]),
("day_of_month", datetime_config[CONF_DAY]),
("month", datetime_config[CONF_MONTH]),
("year", datetime_config[CONF_YEAR]),
)
template_ = await cg.templatable(datetime_struct, args, cg.ESPTime)
cg.add(action_var.set_datetime(template_))
return action_var
(
"datetime.datetime.set",
DateTimeEntity,
CONF_DATETIME,
"set_datetime",
True,
True,
_TIME_MEMBERS + _DATE_MEMBERS,
),
):
automation.register_apply_action(
_name,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(_entity),
cv.Required(_key): cv.Any(
cv.returning_lambda, cv.date_time(date=_date, time=_time)
),
}
),
automation.ApplyField(
_key, _target, cg.ESPTime, const_fn=_esptime_initializer(_members)
),
call="make_call",
)
-14
View File
@@ -98,20 +98,6 @@ class DateCall {
inline DateCall DateEntity::make_call() { return DateCall(this); }
template<typename... Ts> class DateSetAction final : public Action<Ts...>, public Parented<DateEntity> {
public:
TEMPLATABLE_VALUE(ESPTime, date)
void play(const Ts &...x) override {
auto call = this->parent_->make_call();
if (this->date_.has_value()) {
call.set_date(this->date_.value(x...));
}
call.perform();
}
};
} // namespace esphome::datetime
#endif // USE_DATETIME_DATE
@@ -123,20 +123,6 @@ class DateTimeCall {
inline DateTimeCall DateTimeEntity::make_call() { return DateTimeCall(this); }
template<typename... Ts> class DateTimeSetAction final : public Action<Ts...>, public Parented<DateTimeEntity> {
public:
TEMPLATABLE_VALUE(ESPTime, datetime)
void play(const Ts &...x) override {
auto call = this->parent_->make_call();
if (this->datetime_.has_value()) {
call.set_datetime(this->datetime_.value(x...));
}
call.perform();
}
};
#ifdef USE_TIME
class OnDateTimeTrigger final : public Trigger<>, public Component, public Parented<DateTimeEntity> {
public:
-14
View File
@@ -100,20 +100,6 @@ class TimeCall {
inline TimeCall TimeEntity::make_call() { return TimeCall(this); }
template<typename... Ts> class TimeSetAction final : public Action<Ts...>, public Parented<TimeEntity> {
public:
TEMPLATABLE_VALUE(ESPTime, time)
void play(const Ts &...x) override {
auto call = this->parent_->make_call();
if (this->time_.has_value()) {
call.set_time(this->time_.value(x...));
}
call.perform();
}
};
#ifdef USE_TIME
class OnTimeTrigger final : public Trigger<>, public Component, public Parented<TimeEntity> {
public:
+13 -30
View File
@@ -39,12 +39,6 @@ DisplayPage = display_ns.class_("DisplayPage")
DisplayPagePtr = DisplayPage.operator("ptr")
DisplayRef = Display.operator("ref")
DisplayPageShowAction = display_ns.class_("DisplayPageShowAction", automation.Action)
DisplayPageShowNextAction = display_ns.class_(
"DisplayPageShowNextAction", automation.Action
)
DisplayPageShowPrevAction = display_ns.class_(
"DisplayPageShowPrevAction", automation.Action
)
DisplayIsDisplayingPageCondition = display_ns.class_(
"DisplayIsDisplayingPageCondition", automation.Condition
)
@@ -288,34 +282,23 @@ async def display_page_show_to_code(config, action_id, template_arg, args):
return var
@automation.register_action(
# The id is the display itself, so it cannot be a lambda; a plain use_id rejects one clearly.
DISPLAY_PAGE_CYCLE_ACTION_SCHEMA = maybe_simple_id(
{
cv.GenerateID(CONF_ID): cv.use_id(Display),
}
)
automation.register_apply_action(
"display.page.show_next",
DisplayPageShowNextAction,
maybe_simple_id(
{
cv.GenerateID(CONF_ID): cv.templatable(cv.use_id(Display)),
}
),
synchronous=True,
DISPLAY_PAGE_CYCLE_ACTION_SCHEMA,
automation.ApplyCall("show_next_page()"),
)
async def display_page_show_next_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(
automation.register_apply_action(
"display.page.show_previous",
DisplayPageShowPrevAction,
maybe_simple_id(
{
cv.GenerateID(CONF_ID): cv.templatable(cv.use_id(Display)),
}
),
synchronous=True,
DISPLAY_PAGE_CYCLE_ACTION_SCHEMA,
automation.ApplyCall("show_prev_page()"),
)
async def display_page_show_previous_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(
-18
View File
@@ -848,24 +848,6 @@ template<typename... Ts> class DisplayPageShowAction final : public Action<Ts...
}
};
template<typename... Ts> class DisplayPageShowNextAction final : public Action<Ts...> {
public:
DisplayPageShowNextAction(Display *buffer) : buffer_(buffer) {}
void play(const Ts &...x) override { this->buffer_->show_next_page(); }
Display *buffer_;
};
template<typename... Ts> class DisplayPageShowPrevAction final : public Action<Ts...> {
public:
DisplayPageShowPrevAction(Display *buffer) : buffer_(buffer) {}
void play(const Ts &...x) override { this->buffer_->show_prev_page(); }
Display *buffer_;
};
template<typename... Ts> class DisplayIsDisplayingPageCondition final : public Condition<Ts...> {
public:
DisplayIsDisplayingPageCondition(Display *parent) : parent_(parent) {}
@@ -39,8 +39,7 @@ from esphome.const import (
CONF_SERVICE_UUID,
CONF_TRIGGER_ID,
)
from esphome.core import CORE, ID, CoroPriority, TimePeriod, coroutine_with_priority
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.core import CORE, CoroPriority, TimePeriod, coroutine_with_priority
from esphome.enum import StrEnum
from esphome.types import ConfigType
@@ -116,12 +115,6 @@ BLEEndOfScanTrigger = esp32_ble_tracker_ns.class_(
"BLEEndOfScanTrigger", automation.Trigger.template()
)
# Actions
ESP32BLEStartScanAction = esp32_ble_tracker_ns.class_(
"ESP32BLEStartScanAction", automation.Action
)
ESP32BLEStopScanAction = esp32_ble_tracker_ns.class_(
"ESP32BLEStopScanAction", automation.Action
)
def validate_max_connections_deprecated(config: ConfigType) -> ConfigType:
@@ -469,23 +462,12 @@ ESP32_BLE_START_SCAN_ACTION_SCHEMA = cv.Schema(
)
@automation.register_action(
automation.register_apply_action(
"esp32_ble_tracker.start_scan",
ESP32BLEStartScanAction,
ESP32_BLE_START_SCAN_ACTION_SCHEMA,
synchronous=True,
automation.ApplyField(CONF_CONTINUOUS, "set_scan_continuous", cg.bool_),
automation.ApplyCall("start_scan_if_idle()"),
)
async def esp32_ble_tracker_start_scan_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)
template_ = await cg.templatable(config[CONF_CONTINUOUS], args, cg.bool_)
cg.add(var.set_continuous(template_))
return var
ESP32_BLE_STOP_SCAN_ACTION_SCHEMA = automation.maybe_simple_id(
@@ -497,21 +479,11 @@ ESP32_BLE_STOP_SCAN_ACTION_SCHEMA = automation.maybe_simple_id(
)
@automation.register_action(
automation.register_apply_action(
"esp32_ble_tracker.stop_scan",
ESP32BLEStopScanAction,
ESP32_BLE_STOP_SCAN_ACTION_SCHEMA,
synchronous=True,
automation.ApplyCall("stop_scan()"),
)
async def esp32_ble_tracker_stop_scan_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])
return var
async def register_ble_device(
@@ -94,30 +94,6 @@ class BLEEndOfScanTrigger final : public Trigger<>, public ESPBTDeviceListener {
void on_scan_end() override { this->trigger(); }
};
template<typename... Ts> class ESP32BLEStartScanAction final : public Action<Ts...> {
public:
ESP32BLEStartScanAction(ESP32BLETracker *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(bool, continuous)
void play(const Ts &...x) override {
this->parent_->set_scan_continuous(this->continuous_.value(x...));
// Only call start_scan() if scanner is IDLE
// For other states (STARTING, RUNNING, STOPPING, FAILED), the normal state
// machine flow will eventually transition back to IDLE, at which point
// loop() will see scan_continuous_ and restart scanning if it is true.
if (this->parent_->get_scanner_state() == ScannerState::IDLE) {
this->parent_->start_scan();
}
}
protected:
ESP32BLETracker *parent_;
};
template<typename... Ts> class ESP32BLEStopScanAction final : public Action<Ts...>, public Parented<ESP32BLETracker> {
public:
void play(const Ts &...x) override { this->parent_->stop_scan(); }
};
} // namespace esphome::esp32_ble_tracker
#endif
@@ -218,6 +218,12 @@ class ESP32BLETracker final : public Component,
#endif
void start_scan();
// For the start_scan action: in any other state the state machine returns to IDLE on its own
// and loop() restarts scanning when scan_continuous_ is set, so only an idle scanner starts here.
void start_scan_if_idle() {
if (this->scanner_state_ == ScannerState::IDLE)
this->start_scan();
}
void stop_scan();
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
@@ -4,7 +4,6 @@
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/automation.h"
#include "esphome/components/output/float_output.h"
namespace esphome::esp8266_pwm {
@@ -37,19 +36,6 @@ class ESP8266PWM final : public output::FloatOutput, public Component {
float last_output_{0.0};
};
template<typename... Ts> class SetFrequencyAction final : public Action<Ts...> {
public:
SetFrequencyAction(ESP8266PWM *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(float, frequency);
void play(const Ts &...x) {
float freq = this->frequency_.value(x...);
this->parent_->update_frequency(freq);
}
ESP8266PWM *parent_;
};
} // namespace esphome::esp8266_pwm
#endif
+2 -17
View File
@@ -4,8 +4,6 @@ from esphome.components import output
from esphome.components.esp8266.const import require_waveform
import esphome.config_validation as cv
from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_NUMBER, CONF_PIN
from esphome.core import ID
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.types import ConfigType
DEPENDENCIES = ["esp8266"]
@@ -19,7 +17,6 @@ def valid_pwm_pin(value: ConfigType) -> ConfigType:
esp8266_pwm_ns = cg.esphome_ns.namespace("esp8266_pwm")
ESP8266PWM = esp8266_pwm_ns.class_("ESP8266PWM", output.FloatOutput, cg.Component)
SetFrequencyAction = esp8266_pwm_ns.class_("SetFrequencyAction", automation.Action)
validate_frequency = cv.All(cv.frequency, cv.float_range(min=1.0e-6))
# Schema default that also matches the C++ initializer in esp8266_pwm.h; codegen
@@ -57,25 +54,13 @@ async def to_code(config: ConfigType) -> None:
cg.add(var.set_frequency(frequency))
@automation.register_action(
automation.register_apply_action(
"output.esp8266_pwm.set_frequency",
SetFrequencyAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(ESP8266PWM),
cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency),
}
),
synchronous=True,
automation.ApplyField(CONF_FREQUENCY, "update_frequency", cg.float_),
)
async def esp8266_set_frequency_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_FREQUENCY], args, cg.float_)
cg.add(var.set_frequency(template_))
return var
+25 -47
View File
@@ -39,9 +39,6 @@ ESPNowRecvInfo = espnow_ns.class_("ESPNowRecvInfo")
ESPNowRecvInfoConstRef = ESPNowRecvInfo.operator("const").operator("ref")
SendAction = espnow_ns.class_("SendAction", automation.Action)
SetChannelAction = espnow_ns.class_("SetChannelAction", automation.Action)
AddPeerAction = espnow_ns.class_("AddPeerAction", automation.Action)
DeletePeerAction = espnow_ns.class_("DeletePeerAction", automation.Action)
ESPNowHandlerTrigger = automation.Trigger.template(
ESPNowRecvInfoConstRef,
@@ -232,12 +229,16 @@ def _validate_raw_data(value: Any) -> str | list:
)
def _mac_bytes(address: core.MACAddress) -> list[HexInt]:
return [HexInt(p) for p in address.parts]
async def register_peer(
var: MockObj, config: ConfigType, args: TemplateArgsType
) -> None:
peer = config[CONF_ADDRESS]
if isinstance(peer, core.MACAddress):
peer = [HexInt(p) for p in peer.parts]
peer = _mac_bytes(peer)
template_ = await cg.templatable(peer, args, peer_address_t, peer_address_t)
cg.add(var.set_address(template_))
@@ -323,40 +324,28 @@ async def send_action(
return var
@automation.register_action(
"espnow.peer.add",
AddPeerAction,
cv.maybe_simple_value(
PEER_SCHEMA,
key=CONF_ADDRESS,
),
synchronous=True,
)
@automation.register_action(
"espnow.peer.delete",
DeletePeerAction,
cv.maybe_simple_value(
PEER_SCHEMA,
key=CONF_ADDRESS,
),
synchronous=True,
)
async def peer_action(
config: ConfigType,
action_id: core.ID,
template_arg: cg.TemplateArguments,
args: list[tuple],
) -> MockObj:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
await register_peer(var, config, args)
return var
def _peer_address(config: ConfigType, value: core.MACAddress) -> str:
return str(cg.safe_exp(_mac_bytes(value)))
@automation.register_action(
for _name, _method in (
("espnow.peer.add", "add_peer_from_action"),
("espnow.peer.delete", "del_peer_from_action"),
):
automation.register_apply_action(
_name,
cv.maybe_simple_value(
PEER_SCHEMA,
key=CONF_ADDRESS,
),
automation.ApplyField(
CONF_ADDRESS, _method, peer_address_t, const_fn=_peer_address
),
)
automation.register_apply_action(
"espnow.set_channel",
SetChannelAction,
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(ESPNowComponent),
@@ -364,16 +353,5 @@ async def peer_action(
},
key=CONF_CHANNEL,
),
synchronous=True,
automation.ApplyField(CONF_CHANNEL, "set_channel_from_action", cg.uint8),
)
async def channel_action(
config: ConfigType,
action_id: core.ID,
template_arg: cg.TemplateArguments,
args: list[tuple],
) -> MockObj:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
template_ = await cg.templatable(config[CONF_CHANNEL], args, cg.uint8)
cg.add(var.set_channel(template_))
return var
-33
View File
@@ -86,39 +86,6 @@ template<typename... Ts> class SendAction final : public Action<Ts...>, public P
} flags_{0};
};
template<typename... Ts> class AddPeerAction final : public Action<Ts...>, public Parented<ESPNowComponent> {
TEMPLATABLE_VALUE(peer_address_t, address);
protected:
void play(const Ts &...x) override {
peer_address_t address = this->address_.value(x...);
this->parent_->add_peer(address.data());
}
};
template<typename... Ts> class DeletePeerAction final : public Action<Ts...>, public Parented<ESPNowComponent> {
TEMPLATABLE_VALUE(peer_address_t, address);
protected:
void play(const Ts &...x) override {
peer_address_t address = this->address_.value(x...);
this->parent_->del_peer(address.data());
}
};
template<typename... Ts> class SetChannelAction final : public Action<Ts...>, public Parented<ESPNowComponent> {
TEMPLATABLE_VALUE(uint8_t, channel)
protected:
void play(const Ts &...x) override {
if (this->parent_->is_wifi_enabled()) {
return;
}
this->parent_->set_wifi_channel(this->channel_.value(x...));
this->parent_->apply_wifi_channel();
}
};
class OnReceiveTrigger final : public Trigger<const ESPNowRecvInfo &, const uint8_t *, uint16_t>,
public ESPNowReceivedPacketHandler {
public:
@@ -116,9 +116,18 @@ class ESPNowComponent final : public Component {
esp_err_t add_peer(const uint8_t *peer);
// Remove a peer with the esp_now api and remove from the internal list if exists
esp_err_t del_peer(const uint8_t *peer);
// Action entry points; distinct names because add_peer(peer_address_t) only fills the boot-time list
esp_err_t add_peer_from_action(const peer_address_t &address) { return this->add_peer(address.data()); }
esp_err_t del_peer_from_action(const peer_address_t &address) { return this->del_peer(address.data()); }
void set_wifi_channel(uint8_t channel) { this->wifi_channel_ = channel; }
void apply_wifi_channel();
void set_channel_from_action(uint8_t channel) {
if (this->is_wifi_enabled())
return;
this->set_wifi_channel(channel);
this->apply_wifi_channel();
}
uint8_t get_wifi_channel();
void set_auto_add_peer(bool value) { this->auto_add_peer_ = value; }
+6 -17
View File
@@ -16,14 +16,14 @@ from esphome.const import (
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_MOTION,
)
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.core.entity_helpers import (
entity_duplicate_validator,
queue_entity_register,
setup_device_class,
setup_entity,
)
from esphome.cpp_generator import MockObj, MockObjClass, TemplateArgsType
from esphome.cpp_generator import MockObj, MockObjClass
from esphome.types import ConfigType
CODEOWNERS = ["@nohat"]
@@ -40,8 +40,6 @@ event_ns = cg.esphome_ns.namespace("event")
Event = event_ns.class_("Event", cg.EntityBase)
EventPtr = Event.operator("ptr")
TriggerEventAction = event_ns.class_("TriggerEventAction", automation.Action)
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
_EVENT_SCHEMA = (
@@ -135,20 +133,11 @@ TRIGGER_EVENT_SCHEMA = cv.Schema(
)
@automation.register_action(
"event.trigger", TriggerEventAction, TRIGGER_EVENT_SCHEMA, synchronous=True
automation.register_apply_action(
"event.trigger",
TRIGGER_EVENT_SCHEMA,
automation.ApplyField(CONF_EVENT_TYPE, "trigger", cg.std_string),
)
async def event_fire_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])
templ = await cg.templatable(config[CONF_EVENT_TYPE], args, cg.std_string)
cg.add(var.set_event_type(templ))
return var
@coroutine_with_priority(CoroPriority.CORE)
-7
View File
@@ -6,13 +6,6 @@
namespace esphome::event {
template<typename... Ts> class TriggerEventAction final : public Action<Ts...>, public Parented<Event> {
public:
TEMPLATABLE_VALUE(std::string, event_type)
void play(const Ts &...x) override { this->parent_->trigger(this->event_type_.value(x...)); }
};
class EventTrigger final : public Trigger<StringRef> {
public:
EventTrigger(Event *event) {
+6 -49
View File
@@ -31,19 +31,17 @@ from esphome.const import (
CONF_TRIGGER_ID,
CONF_WEB_SERVER,
)
from esphome.core import CORE, CoroPriority, Lambda, coroutine_with_priority
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.core.entity_helpers import (
entity_duplicate_validator,
queue_entity_register,
setup_entity,
)
from esphome.cpp_generator import LambdaExpression
IS_PLATFORM_COMPONENT = True
fan_ns = cg.esphome_ns.namespace("fan")
Fan = fan_ns.class_("Fan", cg.EntityBase)
FanCall = fan_ns.class_("FanCall")
FanDirection = fan_ns.enum("FanDirection", is_class=True)
FAN_DIRECTION_ENUM = {
@@ -63,7 +61,6 @@ RESTORE_MODES = {
}
# Actions
TurnOnAction = fan_ns.class_("TurnOnAction", automation.Action)
TurnOffAction = fan_ns.class_("TurnOffAction", automation.Action)
ToggleAction = fan_ns.class_("ToggleAction", automation.Action)
CycleSpeedAction = fan_ns.class_("CycleSpeedAction", automation.Action)
@@ -332,9 +329,8 @@ async def fan_turn_off_to_code(config, action_id, template_arg, args):
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
automation.register_apply_action(
"fan.turn_on",
TurnOnAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(Fan),
@@ -345,50 +341,11 @@ async def fan_turn_off_to_code(config, action_id, template_arg, args):
),
}
),
synchronous=True,
automation.ApplyField(CONF_OSCILLATING, "set_oscillating", cg.bool_),
automation.ApplyField(CONF_SPEED, "set_speed", cg.int_),
automation.ApplyField(CONF_DIRECTION, "set_direction", FanDirection),
call="turn_on",
)
async def fan_turn_on_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
# All configured fields are folded into a single stateless lambda whose
# constants live in flash; the action stores only a function pointer.
FIELDS = (
(CONF_OSCILLATING, "set_oscillating", cg.bool_),
(CONF_SPEED, "set_speed", cg.int_),
(CONF_DIRECTION, "set_direction", FanDirection),
)
# Normalize trigger args to `const std::remove_cvref_t<T> &` so the
# apply lambda and any inner field lambdas (generated below via
# `process_lambda`) share one parameter spelling that's well-formed for
# any T (value, ref, or const-ref). Matches TurnOnAction::ApplyFn.
normalized_args = [
(cg.RawExpression(f"const std::remove_cvref_t<{cg.safe_exp(t)}> &"), n)
for t, n in args
]
fwd_args = ", ".join(name for _, name in args)
body_lines: list[str] = []
for conf_key, setter, type_ in FIELDS:
if (value := config.get(conf_key)) is None:
continue
if isinstance(value, Lambda):
inner = await cg.process_lambda(value, normalized_args, return_type=type_)
body_lines.append(f"call.{setter}(({inner})({fwd_args}));")
else:
body_lines.append(f"call.{setter}({cg.safe_exp(value)});")
apply_args = [
(FanCall.operator("ref"), "call"),
*normalized_args,
]
apply_lambda = LambdaExpression(
["\n".join(body_lines)],
apply_args,
capture="",
return_type=cg.void,
)
return cg.new_Pvariable(action_id, template_arg, paren, apply_lambda)
@automation.register_action(
-27
View File
@@ -6,33 +6,6 @@
namespace esphome::fan {
// All configured fields are baked into a single stateless lambda whose
// constants live in flash. The action only stores one function pointer
// plus one parent pointer, regardless of how many fields the user set.
// Trigger args are forwarded to the apply function so user lambdas
// (e.g. `speed: !lambda "return x;"`) keep working.
//
// Trigger args are normalized to `const std::remove_cvref_t<Ts> &...` so
// the codegen can emit a matching parameter list for both the apply lambda
// and any inner field lambdas without producing invalid C++ source text
// (e.g. `const T & &` if Ts already carries a reference, or `const const
// T &` if Ts already carries a const). This keeps trigger args no-copy
// regardless of whether the trigger supplies `T`, `T &`, or `const T &`.
template<typename... Ts> class TurnOnAction final : public Action<Ts...> {
public:
using ApplyFn = void (*)(FanCall &, const std::remove_cvref_t<Ts> &...);
TurnOnAction(Fan *state, ApplyFn apply) : state_(state), apply_(apply) {}
void play(const Ts &...x) override {
auto call = this->state_->turn_on();
this->apply_(call, x...);
call.perform();
}
Fan *state_;
ApplyFn apply_;
};
template<typename... Ts> class TurnOffAction final : public Action<Ts...> {
public:
explicit TurnOffAction(Fan *state) : state_(state) {}
+9 -25
View File
@@ -8,8 +8,7 @@ from esphome.const import (
CONF_TYPE,
CONF_VALUE,
)
from esphome.core import ID, CoroPriority, coroutine_with_priority
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.core import CoroPriority, coroutine_with_priority
from esphome.types import ConfigType
CODEOWNERS = ["@esphome/core"]
@@ -21,7 +20,6 @@ RestoringGlobalsComponent = globals_ns.class_(
RestoringGlobalStringComponent = globals_ns.class_(
"RestoringGlobalStringComponent", cg.PollingComponent
)
GlobalVarSetAction = globals_ns.class_("GlobalVarSetAction", automation.Action)
CONF_MAX_RESTORE_DATA_LENGTH = "max_restore_data_length"
@@ -94,33 +92,19 @@ async def to_code(config: ConfigType) -> None:
cg.add(glob.set_name_hash(hash_))
@automation.register_action(
# A lambda returns the global's own type so `return 42;` reduces to a cast; the value
# itself is raw C++ text.
_VALUE_TYPE = "std::remove_reference_t<decltype({parent}->value())>"
automation.register_apply_action(
"globals.set",
GlobalVarSetAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(GlobalsComponent),
cv.Required(CONF_VALUE): cv.templatable(cv.string_strict),
}
),
synchronous=True,
automation.ApplyField(
CONF_VALUE, "value() = {}", _VALUE_TYPE, const_fn=lambda _, value: value
),
)
async def globals_set_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
full_id, paren = await cg.get_variable_with_full_id(config[CONF_ID])
template_arg = cg.TemplateArguments(full_id.type, *template_arg)
var = cg.new_Pvariable(action_id, template_arg, paren)
# Use the global's value_type alias as the lambda return type so
# TemplatableFn stores a direct function pointer instead of going through
# the deprecated converting trampoline when the value expression deduces
# to a different type (e.g. int literal assigned to a float global).
value_type = cg.RawExpression(f"{full_id.type}::value_type")
templ = await cg.templatable(
config[CONF_VALUE], args, value_type, to_exp=cg.RawExpression
)
cg.add(var.set_value(templ))
return var
+3 -15
View File
@@ -1,8 +1,10 @@
#pragma once
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include <array>
#include <cstring>
namespace esphome::globals {
@@ -127,20 +129,6 @@ template<typename T, uint8_t SZ> class RestoringGlobalStringComponent : public P
ESPPreferenceObject rtc_;
};
template<class C, typename... Ts> class GlobalVarSetAction final : public Action<Ts...> {
public:
explicit GlobalVarSetAction(C *parent) : parent_(parent) {}
using T = typename C::value_type;
TEMPLATABLE_VALUE(T, value);
void play(const Ts &...x) override { this->parent_->value() = this->value_.value(x...); }
protected:
C *parent_;
};
template<typename T> T &id(GlobalsComponent<T> *value) { return value->value(); }
template<typename T> T &id(RestoringGlobalsComponent<T> *value) { return value->value(); }
template<typename T, uint8_t SZ> T &id(RestoringGlobalStringComponent<T, SZ> *value) { return value->value(); }
+13 -50
View File
@@ -27,11 +27,6 @@ HlkFm22xComponent = hlk_fm22x_ns.class_(
"HlkFm22xComponent", cg.PollingComponent, uart.UARTDevice
)
EnrollmentAction = hlk_fm22x_ns.class_("EnrollmentAction", automation.Action)
DeleteAction = hlk_fm22x_ns.class_("DeleteAction", automation.Action)
DeleteAllAction = hlk_fm22x_ns.class_("DeleteAllAction", automation.Action)
ScanAction = hlk_fm22x_ns.class_("ScanAction", automation.Action)
ResetAction = hlk_fm22x_ns.class_("ResetAction", automation.Action)
CONFIG_SCHEMA = cv.All(
cv.Schema(
@@ -101,9 +96,8 @@ async def to_code(config):
await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS)
@automation.register_action(
automation.register_apply_action(
"hlk_fm22x.enroll",
EnrollmentAction,
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(HlkFm22xComponent),
@@ -112,22 +106,14 @@ async def to_code(config):
},
key=CONF_NAME,
),
synchronous=True,
automation.ApplyCall(
"enroll_face({}, static_cast<hlk_fm22x::HlkFm22xFaceDirection>({}))",
((CONF_NAME, cg.std_string), (CONF_DIRECTION, cg.uint8)),
),
)
async def hlk_fm22x_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_NAME], args, cg.std_string)
cg.add(var.set_name(template_))
template_ = await cg.templatable(config[CONF_DIRECTION], args, cg.uint8)
cg.add(var.set_direction(template_))
return var
@automation.register_action(
automation.register_apply_action(
"hlk_fm22x.delete",
DeleteAction,
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(HlkFm22xComponent),
@@ -135,60 +121,37 @@ async def hlk_fm22x_enroll_to_code(config, action_id, template_arg, args):
},
key=CONF_FACE_ID,
),
synchronous=True,
automation.ApplyField(CONF_FACE_ID, "delete_face", cg.int16),
)
async def hlk_fm22x_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_FACE_ID], args, cg.int16)
cg.add(var.set_face_id(template_))
return var
@automation.register_action(
automation.register_apply_action(
"hlk_fm22x.delete_all",
DeleteAllAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(HlkFm22xComponent),
}
),
synchronous=True,
automation.ApplyCall("delete_all_faces()"),
)
async def hlk_fm22x_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
@automation.register_action(
automation.register_apply_action(
"hlk_fm22x.scan",
ScanAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(HlkFm22xComponent),
}
),
synchronous=True,
automation.ApplyCall("scan_face()"),
)
async def hlk_fm22x_scan_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(
automation.register_apply_action(
"hlk_fm22x.reset",
ResetAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(HlkFm22xComponent),
}
),
synchronous=True,
automation.ApplyCall("reset()"),
)
async def hlk_fm22x_reset_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
-37
View File
@@ -141,41 +141,4 @@ class HlkFm22xComponent final : public PollingComponent, public uart::UARTDevice
CallbackManager<void(uint8_t)> enrollment_failed_callback_;
};
template<typename... Ts> class EnrollmentAction final : public Action<Ts...>, public Parented<HlkFm22xComponent> {
public:
TEMPLATABLE_VALUE(std::string, name)
TEMPLATABLE_VALUE(uint8_t, direction)
void play(const Ts &...x) override {
auto name = this->name_.value(x...);
auto direction = (HlkFm22xFaceDirection) this->direction_.value(x...);
this->parent_->enroll_face(name, direction);
}
};
template<typename... Ts> class DeleteAction final : public Action<Ts...>, public Parented<HlkFm22xComponent> {
public:
TEMPLATABLE_VALUE(int16_t, face_id)
void play(const Ts &...x) override {
auto face_id = this->face_id_.value(x...);
this->parent_->delete_face(face_id);
}
};
template<typename... Ts> class DeleteAllAction final : public Action<Ts...>, public Parented<HlkFm22xComponent> {
public:
void play(const Ts &...x) override { this->parent_->delete_all_faces(); }
};
template<typename... Ts> class ScanAction final : public Action<Ts...>, public Parented<HlkFm22xComponent> {
public:
void play(const Ts &...x) override { this->parent_->scan_face(); }
};
template<typename... Ts> class ResetAction final : public Action<Ts...>, public Parented<HlkFm22xComponent> {
public:
void play(const Ts &...x) override { this->parent_->reset(); }
};
} // namespace esphome::hlk_fm22x
-15
View File
@@ -2,7 +2,6 @@
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/automation.h"
#include "esphome/components/output/float_output.h"
#include <cstdint>
@@ -43,20 +42,6 @@ class LEDCOutput final : public output::FloatOutput, public Component {
bool initialized_ = false;
};
template<typename... Ts> class SetFrequencyAction final : public Action<Ts...> {
public:
SetFrequencyAction(LEDCOutput *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(float, frequency);
void play(const Ts &...x) {
float freq = this->frequency_.value(x...);
this->parent_->update_frequency(freq);
}
protected:
LEDCOutput *parent_;
};
} // namespace esphome::ledc
#endif
+2 -17
View File
@@ -12,8 +12,6 @@ from esphome.const import (
CONF_PHASE_ANGLE,
CONF_PIN,
)
from esphome.core import ID
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.types import ConfigType
DEPENDENCIES = ["esp32"]
@@ -45,7 +43,6 @@ def validate_frequency(value: Any) -> float:
ledc_ns = cg.esphome_ns.namespace("ledc")
LEDCOutput = ledc_ns.class_("LEDCOutput", output.FloatOutput, cg.Component)
SetFrequencyAction = ledc_ns.class_("SetFrequencyAction", automation.Action)
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
{
@@ -77,25 +74,13 @@ async def to_code(config: ConfigType) -> None:
cg.add(var.set_phase_angle(config[CONF_PHASE_ANGLE]))
@automation.register_action(
automation.register_apply_action(
"output.ledc.set_frequency",
SetFrequencyAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(LEDCOutput),
cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency),
}
),
synchronous=True,
automation.ApplyField(CONF_FREQUENCY, "update_frequency", cg.float_),
)
async def ledc_set_frequency_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_FREQUENCY], args, cg.float_)
cg.add(var.set_frequency(template_))
return var
@@ -2,7 +2,6 @@
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/automation.h"
#include "esphome/components/output/float_output.h"
#ifdef USE_LIBRETINY
@@ -34,20 +33,6 @@ class LibreTinyPWM final : public output::FloatOutput, public Component {
bool initialized_ = false;
};
template<typename... Ts> class SetFrequencyAction final : public Action<Ts...> {
public:
SetFrequencyAction(LibreTinyPWM *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(float, frequency);
void play(const Ts &...x) {
float freq = this->frequency_.value(x...);
this->parent_->update_frequency(freq);
}
protected:
LibreTinyPWM *parent_;
};
} // namespace esphome::libretiny_pwm
#endif
+2 -17
View File
@@ -3,15 +3,12 @@ import esphome.codegen as cg
from esphome.components import output
import esphome.config_validation as cv
from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_PIN
from esphome.core import ID
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.types import ConfigType
DEPENDENCIES = ["libretiny"]
libretinypwm_ns = cg.esphome_ns.namespace("libretiny_pwm")
LibreTinyPWM = libretinypwm_ns.class_("LibreTinyPWM", output.FloatOutput, cg.Component)
SetFrequencyAction = libretinypwm_ns.class_("SetFrequencyAction", automation.Action)
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
{
@@ -32,25 +29,13 @@ async def to_code(config: ConfigType) -> None:
cg.add(var.set_frequency(config[CONF_FREQUENCY]))
@automation.register_action(
automation.register_apply_action(
"output.libretiny_pwm.set_frequency",
SetFrequencyAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(LibreTinyPWM),
cv.Required(CONF_FREQUENCY): cv.templatable(cv.int_),
}
),
synchronous=True,
automation.ApplyField(CONF_FREQUENCY, "update_frequency", cg.float_),
)
async def libretiny_pwm_set_frequency_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_FREQUENCY], args, cg.float_)
cg.add(var.set_frequency(template_))
return var
-28
View File
@@ -31,34 +31,6 @@ template<bool HasTransitionLength, typename... Ts> class ToggleAction final : pu
transition_length_{};
};
// All configured fields are baked into a single stateless lambda whose
// constants live in flash. The action only stores one function pointer
// plus one parent pointer, regardless of how many fields the user set.
// Trigger args are forwarded to the apply function so user lambdas
// (e.g. `brightness: !lambda "return x;"`) keep working.
//
// Trigger args are normalized to `const std::remove_cvref_t<Ts> &...` so
// the codegen can emit a matching parameter list for both the apply lambda
// and any inner field lambdas without producing invalid C++ source text
// (e.g. `const T & &` if Ts already carries a reference, or `const const
// T &` if Ts already carries a const). This keeps trigger args no-copy
// regardless of whether the trigger supplies `T`, `T &`, or `const T &`.
template<typename... Ts> class LightControlAction final : public Action<Ts...> {
public:
using ApplyFn = void (*)(LightState *, LightCall &, const std::remove_cvref_t<Ts> &...);
LightControlAction(LightState *parent, ApplyFn apply) : parent_(parent), apply_(apply) {}
void play(const Ts &...x) override {
auto call = this->parent_->make_call();
this->apply_(this->parent_, call, x...);
call.perform();
}
protected:
LightState *parent_;
ApplyFn apply_;
};
template<bool HasTransitionLength, typename... Ts> class DimRelativeAction final : public Action<Ts...> {
public:
explicit DimRelativeAction(LightState *parent) : parent_(parent) {}
+42 -83
View File
@@ -27,7 +27,7 @@ from esphome.const import (
CONF_WHITE,
)
from esphome.core import CORE, ID, EsphomeError, Lambda
from esphome.cpp_generator import LambdaExpression, MockObj, TemplateArgsType
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.types import ConfigType
from .types import (
@@ -37,8 +37,6 @@ from .types import (
AddressableSet,
ColorMode,
DimRelativeAction,
LightCall,
LightControlAction,
LightEffectCycleAction,
LightIsOffCondition,
LightIsOnCondition,
@@ -147,7 +145,7 @@ LIGHT_TURN_ON_ACTION_SCHEMA = automation.maybe_simple_id(
)
def _resolve_effect_index(config: ConfigType) -> int:
def _resolve_effect_index(config: ConfigType, original_name: str) -> int:
"""Resolve a static effect name to its 1-based index at codegen time.
Effect index 0 means "None" (no effect). Effects are 1-indexed matching
@@ -155,7 +153,6 @@ def _resolve_effect_index(config: ConfigType) -> int:
"""
from . import available_effects_str, find_effect_index
original_name = config[CONF_EFFECT]
if original_name.lower() == "none":
return 0
light_id = config[CONF_ID]
@@ -173,87 +170,49 @@ def _resolve_effect_index(config: ConfigType) -> int:
)
@automation.register_action(
"light.turn_off", LightControlAction, LIGHT_TURN_OFF_ACTION_SCHEMA, synchronous=True
def _effect_index(config: ConfigType, value: str) -> str:
# Resolved at codegen time; the cast picks set_effect(uint32_t) over the optional overload.
return f"static_cast<uint32_t>({_resolve_effect_index(config, value)})"
_LIGHT_CONTROL_FIELDS = (
automation.ApplyField(CONF_COLOR_MODE, "set_color_mode", ColorMode),
automation.ApplyField(CONF_STATE, "set_state", cg.bool_),
automation.ApplyField(CONF_TRANSITION_LENGTH, "set_transition_length", cg.uint32),
automation.ApplyField(CONF_FLASH_LENGTH, "set_flash_length", cg.uint32),
automation.ApplyField(CONF_BRIGHTNESS, "set_brightness", cg.float_),
automation.ApplyField(CONF_COLOR_BRIGHTNESS, "set_color_brightness", cg.float_),
automation.ApplyField(CONF_RED, "set_red", cg.float_),
automation.ApplyField(CONF_GREEN, "set_green", cg.float_),
automation.ApplyField(CONF_BLUE, "set_blue", cg.float_),
automation.ApplyField(CONF_WHITE, "set_white", cg.float_),
automation.ApplyField(CONF_COLOR_TEMPERATURE, "set_color_temperature", cg.float_),
automation.ApplyField(CONF_COLD_WHITE, "set_cold_white", cg.float_),
automation.ApplyField(CONF_WARM_WHITE, "set_warm_white", cg.float_),
automation.ApplyField(
CONF_EFFECT, "set_effect", cg.std_string, const_fn=_effect_index
),
)
@automation.register_action(
"light.turn_on", LightControlAction, LIGHT_TURN_ON_ACTION_SCHEMA, synchronous=True
automation.register_apply_action(
"light.turn_off",
LIGHT_TURN_OFF_ACTION_SCHEMA,
automation.ApplyField(CONF_STATE, "set_state", cg.bool_),
automation.ApplyField(CONF_TRANSITION_LENGTH, "set_transition_length", cg.uint32),
call="make_call",
)
@automation.register_action(
"light.control", LightControlAction, LIGHT_CONTROL_ACTION_SCHEMA, synchronous=True
automation.register_apply_action(
"light.turn_on",
LIGHT_TURN_ON_ACTION_SCHEMA,
*_LIGHT_CONTROL_FIELDS,
call="make_call",
)
automation.register_apply_action(
"light.control",
LIGHT_CONTROL_ACTION_SCHEMA,
*_LIGHT_CONTROL_FIELDS,
call="make_call",
)
async def light_control_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
# All configured fields are folded into a single stateless lambda whose
# constants live in flash; the action stores only a function pointer.
FIELDS = (
(CONF_COLOR_MODE, "set_color_mode", ColorMode),
(CONF_STATE, "set_state", cg.bool_),
(CONF_TRANSITION_LENGTH, "set_transition_length", cg.uint32),
(CONF_FLASH_LENGTH, "set_flash_length", cg.uint32),
(CONF_BRIGHTNESS, "set_brightness", cg.float_),
(CONF_COLOR_BRIGHTNESS, "set_color_brightness", cg.float_),
(CONF_RED, "set_red", cg.float_),
(CONF_GREEN, "set_green", cg.float_),
(CONF_BLUE, "set_blue", cg.float_),
(CONF_WHITE, "set_white", cg.float_),
(CONF_COLOR_TEMPERATURE, "set_color_temperature", cg.float_),
(CONF_COLD_WHITE, "set_cold_white", cg.float_),
(CONF_WARM_WHITE, "set_warm_white", cg.float_),
)
# Normalize trigger args to `const std::remove_cvref_t<T> &` so the
# apply lambda and any inner field lambdas (generated below via
# `process_lambda`) share one parameter spelling that's well-formed for
# any T (value, ref, or const-ref). Matches LightControlAction::ApplyFn.
normalized_args = [
(cg.RawExpression(f"const std::remove_cvref_t<{cg.safe_exp(t)}> &"), n)
for t, n in args
]
fwd_args = ", ".join(name for _, name in args)
body_lines: list[str] = []
for conf_key, setter, type_ in FIELDS:
if conf_key not in config:
continue
value = config[conf_key]
if isinstance(value, Lambda):
inner = await cg.process_lambda(value, normalized_args, return_type=type_)
body_lines.append(f"call.{setter}(({inner})({fwd_args}));")
else:
body_lines.append(f"call.{setter}({cg.safe_exp(value)});")
if CONF_EFFECT in config:
if isinstance(config[CONF_EFFECT], Lambda):
inner_lambda = await cg.process_lambda(
config[CONF_EFFECT], normalized_args, return_type=cg.std_string
)
body_lines.append(
f"{{ auto __effect_s = ({inner_lambda})({fwd_args});\n"
f"call.set_effect(parent->get_effect_index("
f"__effect_s.c_str(), __effect_s.size())); }}"
)
else:
# Cast disambiguates between set_effect(uint32_t) and
# set_effect(optional<uint32_t>) when the literal is an int.
body_lines.append(
f"call.set_effect(static_cast<uint32_t>({_resolve_effect_index(config)}));"
)
apply_args = [
(LightState.operator("ptr"), "parent"),
(LightCall.operator("ref"), "call"),
*normalized_args,
]
apply_lambda = LambdaExpression(
["\n".join(body_lines)],
apply_args,
capture="",
return_type=cg.void,
)
return cg.new_Pvariable(action_id, template_arg, paren, apply_lambda)
def _record_effect_cycle_ref(config: ConfigType) -> ConfigType:
-2
View File
@@ -14,7 +14,6 @@ Color = cg.esphome_ns.class_("Color")
LightColorValues = light_ns.class_("LightColorValues")
LightStateRTCState = light_ns.struct("LightStateRTCState")
LightCall = light_ns.class_("LightCall")
# Addressable strips
ChannelColors = light_ns.struct("ChannelColors")
@@ -42,7 +41,6 @@ LIMIT_MODES = {
# Actions
ToggleAction = light_ns.class_("ToggleAction", automation.Action)
LightControlAction = light_ns.class_("LightControlAction", automation.Action)
LightEffectCycleAction = light_ns.class_("LightEffectCycleAction", automation.Action)
DimRelativeAction = light_ns.class_("DimRelativeAction", automation.Action)
AddressableSet = light_ns.class_("AddressableSet", automation.Action)
@@ -37,7 +37,6 @@ LN882HBLETracker = ln882h_ble_tracker_ns.class_(
)
StartScanAction = ln882h_ble_tracker_ns.class_("StartScanAction", automation.Action)
StopScanAction = ln882h_ble_tracker_ns.class_("StopScanAction", automation.Action)
ESPBTAdvertiseTrigger = ble_automation.ESPBTAdvertiseTrigger
BLEServiceDataAdvertiseTrigger = ble_automation.BLEServiceDataAdvertiseTrigger
@@ -103,9 +102,8 @@ async def start_scan_action_to_code(
return var
@automation.register_action(
automation.register_apply_action(
"ln882h_ble_tracker.stop_scan",
StopScanAction,
automation.maybe_simple_id(
cv.Schema(
{
@@ -113,17 +111,8 @@ async def start_scan_action_to_code(
}
)
),
synchronous=True,
automation.ApplyCall("stop_scan()"),
)
async def stop_scan_action_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: list,
) -> cg.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:
@@ -37,11 +37,6 @@ template<typename... Ts> class StartScanAction final : public Action<Ts...>, pub
}
};
template<typename... Ts> class StopScanAction final : public Action<Ts...>, public Parented<LN882HBLETracker> {
public:
void play(const Ts &...x) override { this->parent_->stop_scan(); }
};
} // namespace esphome::ln882h_ble_tracker
#endif // USE_LIBRETINY
+36 -60
View File
@@ -152,7 +152,7 @@ CONF_ON_PAUSE = "on_pause"
CONF_ON_ANNOUNCEMENT = "on_announcement"
CONF_MEDIA_URL = "media_url"
# Command actions that all share the same schema and codegen handler
# Command actions that all share the same schema and only differ in the command sent
_COMMAND_ACTIONS = [
"play",
"pause",
@@ -201,16 +201,7 @@ _STATE_CONDITIONS = [
"muted",
]
# Special action classes with custom schemas/handlers
PlayMediaAction = media_player_ns.class_(
"PlayMediaAction", automation.Action, cg.Parented.template(MediaPlayer)
)
EnqueueMediaAction = media_player_ns.class_(
"EnqueueMediaAction", automation.Action, cg.Parented.template(MediaPlayer)
)
VolumeSetAction = media_player_ns.class_(
"VolumeSetAction", automation.Action, cg.Parented.template(MediaPlayer)
)
MediaPlayerCommand = media_player_ns.enum("MediaPlayerCommand", is_class=True)
_CALLBACK_AUTOMATIONS = (
@@ -329,59 +320,50 @@ _MEDIA_URL_ACTION_SCHEMA = cv.maybe_simple_value(
)
async def _media_action_handler(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
media_url = await cg.templatable(config[CONF_MEDIA_URL], args, cg.std_string)
announcement = await cg.templatable(config[CONF_ANNOUNCEMENT], args, cg.bool_)
cg.add(var.set_media_url(media_url))
cg.add(var.set_announcement(announcement))
return var
_ANNOUNCEMENT_FIELD = automation.ApplyField(
CONF_ANNOUNCEMENT, "set_announcement", cg.bool_
)
_MEDIA_URL_FIELD = automation.ApplyField(CONF_MEDIA_URL, "set_media_url", cg.std_string)
automation.register_action(
def _set_command(command_name: str) -> automation.ApplyCall:
command = getattr(
MediaPlayerCommand, f"MEDIA_PLAYER_COMMAND_{command_name.upper()}"
)
return automation.ApplyCall(f"set_command({command})")
automation.register_apply_action(
"media_player.play_media",
PlayMediaAction,
_MEDIA_URL_ACTION_SCHEMA,
synchronous=True,
)(_media_action_handler)
_MEDIA_URL_FIELD,
_ANNOUNCEMENT_FIELD,
call="make_call",
)
automation.register_action(
automation.register_apply_action(
"media_player.enqueue",
EnqueueMediaAction,
_MEDIA_URL_ACTION_SCHEMA,
synchronous=True,
)(_media_action_handler)
_set_command("enqueue"),
_MEDIA_URL_FIELD,
_ANNOUNCEMENT_FIELD,
call="make_call",
)
for _action_name in _COMMAND_ACTIONS:
automation.register_apply_action(
f"media_player.{_action_name}",
MEDIA_PLAYER_ACTION_SCHEMA,
_set_command(_action_name),
_ANNOUNCEMENT_FIELD,
call="make_call",
)
def _snake_to_camel(name):
return "".join(word.capitalize() for word in name.split("_"))
def _register_command_actions():
async def handler(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
announcement = await cg.templatable(config[CONF_ANNOUNCEMENT], args, cg.bool_)
cg.add(var.set_announcement(announcement))
return var
for action_name in _COMMAND_ACTIONS:
class_name = f"{_snake_to_camel(action_name)}Action"
action_class = media_player_ns.class_(
class_name, automation.Action, cg.Parented.template(MediaPlayer)
)
automation.register_action(
f"media_player.{action_name}",
action_class,
MEDIA_PLAYER_ACTION_SCHEMA,
synchronous=True,
)(handler)
_register_command_actions()
def _register_state_conditions():
async def handler(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
@@ -401,9 +383,8 @@ def _register_state_conditions():
_register_state_conditions()
@automation.register_action(
automation.register_apply_action(
"media_player.volume_set",
VolumeSetAction,
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(MediaPlayer),
@@ -411,14 +392,9 @@ _register_state_conditions()
},
key=CONF_VOLUME,
),
synchronous=True,
automation.ApplyField(CONF_VOLUME, "set_volume", cg.float_),
call="make_call",
)
async def media_player_volume_set_action(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
volume = await cg.templatable(config[CONF_VOLUME], args, cg.float_)
cg.add(var.set_volume(volume))
return var
@coroutine_with_priority(CoroPriority.CORE)
@@ -5,76 +5,6 @@
namespace esphome::media_player {
template<MediaPlayerCommand Command, typename... Ts>
class MediaPlayerCommandAction final : public Action<Ts...>, public Parented<MediaPlayer> {
public:
TEMPLATABLE_VALUE(bool, announcement);
void play(const Ts &...x) override {
this->parent_->make_call().set_command(Command).set_announcement(this->announcement_.value(x...)).perform();
}
};
template<typename... Ts>
using PlayAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_PLAY, Ts...>;
template<typename... Ts>
using PauseAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_PAUSE, Ts...>;
template<typename... Ts>
using StopAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_STOP, Ts...>;
template<typename... Ts>
using ToggleAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_TOGGLE, Ts...>;
template<typename... Ts>
using VolumeUpAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_VOLUME_UP, Ts...>;
template<typename... Ts>
using VolumeDownAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_VOLUME_DOWN, Ts...>;
template<typename... Ts>
using TurnOnAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_TURN_ON, Ts...>;
template<typename... Ts>
using TurnOffAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_TURN_OFF, Ts...>;
template<typename... Ts>
using NextAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_NEXT, Ts...>;
template<typename... Ts>
using PreviousAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_PREVIOUS, Ts...>;
template<typename... Ts>
using MuteAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_MUTE, Ts...>;
template<typename... Ts>
using UnmuteAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_UNMUTE, Ts...>;
template<typename... Ts>
using RepeatOffAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_OFF, Ts...>;
template<typename... Ts>
using RepeatOneAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ONE, Ts...>;
template<typename... Ts>
using RepeatAllAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_REPEAT_ALL, Ts...>;
template<typename... Ts>
using ShuffleAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_SHUFFLE, Ts...>;
template<typename... Ts>
using UnshuffleAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_UNSHUFFLE, Ts...>;
template<typename... Ts>
using GroupJoinAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_GROUP_JOIN, Ts...>;
template<typename... Ts>
using ClearPlaylistAction = MediaPlayerCommandAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST, Ts...>;
template<MediaPlayerCommand Command, typename... Ts>
class MediaPlayerMediaAction final : public Action<Ts...>, public Parented<MediaPlayer> {
TEMPLATABLE_VALUE(std::string, media_url)
TEMPLATABLE_VALUE(bool, announcement)
void play(const Ts &...x) override {
auto call = this->parent_->make_call();
if constexpr (Command != MediaPlayerCommand::MEDIA_PLAYER_COMMAND_PLAY)
call.set_command(Command);
call.set_media_url(this->media_url_.value(x...)).set_announcement(this->announcement_.value(x...)).perform();
}
};
template<typename... Ts>
using PlayMediaAction = MediaPlayerMediaAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_PLAY, Ts...>;
template<typename... Ts>
using EnqueueMediaAction = MediaPlayerMediaAction<MediaPlayerCommand::MEDIA_PLAYER_COMMAND_ENQUEUE, Ts...>;
template<typename... Ts> class VolumeSetAction final : public Action<Ts...>, public Parented<MediaPlayer> {
TEMPLATABLE_VALUE(float, volume)
void play(const Ts &...x) override { this->parent_->make_call().set_volume(this->volume_.value(x...)).perform(); }
};
/// Callback forwarder that triggers an Automation<> on any state change.
/// Pointer-sized (single Automation* field) to fit inline in Callback::ctx_.
struct StateAnyForwarder {
@@ -10,8 +10,8 @@ from esphome.const import (
CONF_UPDATE_INTERVAL,
CONF_USE_FAHRENHEIT,
)
from esphome.core import ID, Lambda
from esphome.cpp_generator import LambdaExpression, MockObj
from esphome.core import ID
from esphome.cpp_generator import MockObj
from esphome.types import ConfigType, TemplateArgsType
CODEOWNERS = ["@crnjan"]
@@ -32,7 +32,6 @@ MitsubishiCN105Component = mitsubishi_ns.class_(
)
VaneState = mitsubishi_ns.struct("VaneState")
VaneCall = mitsubishi_ns.class_("VaneCall")
VerticalVaneMode = mitsubishi_ns.enum("VerticalVaneMode")
# The insertion order must match VALUES in
@@ -59,10 +58,6 @@ ClearRemoteTemperatureAction = mitsubishi_ns.class_(
cg.Parented.template(MitsubishiCN105Component),
)
VaneControlAction = mitsubishi_ns.class_(
"VaneControlAction",
automation.Action,
)
CONFIG_SCHEMA = (
cv.Schema(
@@ -117,8 +112,9 @@ async def to_code(config: ConfigType) -> None:
)
)
cg.add(var.set_use_fahrenheit(config[CONF_USE_FAHRENHEIT]))
# User lambdas may use unqualified enum names.
cg.add_global(mitsubishi_ns.using)
if on_state := config.get(CONF_VANE, {}).get(CONF_ON_STATE):
cg.add_global(mitsubishi_ns.using)
for conf in on_state:
await automation.build_callback_automation(
var,
@@ -183,14 +179,6 @@ async def clear_temperature_action_to_code(
return var
VANE_CONTROL_FIELDS = (
(
(CONF_VERTICAL, CONF_DIRECTION),
"vertical.set_direction",
VerticalVaneMode,
),
)
VANE_CONTROL_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(MitsubishiCN105Component),
@@ -205,46 +193,11 @@ VANE_CONTROL_ACTION_SCHEMA = cv.Schema(
)
@automation.register_action(
automation.register_apply_action(
f"{DOMAIN}.vane.control",
VaneControlAction,
VANE_CONTROL_ACTION_SCHEMA,
synchronous=True,
automation.ApplyField(
(CONF_VERTICAL, CONF_DIRECTION), "vertical.set_direction", VerticalVaneMode
),
call="make_vane_call",
)
async def vane_control_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
cg.add_global(mitsubishi_ns.using)
parent = await cg.get_variable(config[CONF_ID])
normalized_args = [
(cg.RawExpression(f"const std::remove_cvref_t<{cg.safe_exp(t)}> &"), name)
for t, name in args
]
forwarded_args = ", ".join(name for _, name in args)
body_lines: list[str] = []
for path, setter, type_ in VANE_CONTROL_FIELDS:
if (section := config.get(path[0])) is None:
continue
if (value := section.get(path[1])) is None:
continue
if isinstance(value, Lambda):
inner = await cg.process_lambda(
value,
normalized_args,
return_type=type_,
)
body_lines.append(f"call.{setter}(({inner})({forwarded_args}));")
else:
body_lines.append(f"call.{setter}({cg.safe_exp(value)});")
apply_lambda = LambdaExpression(
["\n".join(body_lines)],
[(VaneCall.operator("ref"), "call"), *normalized_args],
capture="",
return_type=cg.void,
)
return cg.new_Pvariable(action_id, template_arg, parent, apply_lambda)
@@ -4,8 +4,6 @@
#include "esphome/core/automation.h"
#include <type_traits>
namespace esphome::mitsubishi_cn105 {
template<typename... Ts>
@@ -22,21 +20,4 @@ class ClearRemoteTemperatureAction final : public Action<Ts...>, public Parented
void play(const Ts &...x) override { this->parent_->clear_remote_temperature(); }
};
template<typename... Ts> class VaneControlAction final : public Action<Ts...> {
public:
using ApplyFn = void (*)(VaneCall &, const std::remove_cvref_t<Ts> &...);
VaneControlAction(MitsubishiCN105Component *parent, ApplyFn apply) : parent_(parent), apply_(apply) {}
void play(const Ts &...x) override {
auto call = this->parent_->make_vane_call();
this->apply_(call, x...);
call.perform();
}
protected:
MitsubishiCN105Component *parent_;
ApplyFn apply_;
};
} // namespace esphome::mitsubishi_cn105
+27 -92
View File
@@ -163,10 +163,6 @@ ValueRangeTrigger = number_ns.class_(
"ValueRangeTrigger", automation.Trigger.template(cg.float_), cg.Component
)
# Actions
NumberSetAction = number_ns.class_("NumberSetAction", automation.Action)
NumberOperationAction = number_ns.class_("NumberOperationAction", automation.Action)
# Conditions
NumberInRangeCondition = number_ns.class_(
"NumberInRangeCondition", automation.Condition
@@ -366,85 +362,40 @@ OPERATION_BASE_SCHEMA = cv.Schema(
)
@automation.register_action(
automation.register_apply_action(
"number.set",
NumberSetAction,
OPERATION_BASE_SCHEMA.extend(
{
cv.Required(CONF_VALUE): cv.templatable(cv.float_),
}
),
synchronous=True,
automation.ApplyField(CONF_VALUE, "set_value", cg.float_),
call="make_call",
)
async def number_set_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_VALUE], args, cg.float_)
cg.add(var.set_value(template_))
return var
# The operation is fixed by the action name; CONF_MODE only stays accepted in the config.
for _name, _mode, _cycle in (
("number.increment", "INCREMENT", True),
("number.decrement", "DECREMENT", True),
("number.to_min", "TO_MIN", False),
("number.to_max", "TO_MAX", False),
):
_schema = {cv.Optional(CONF_MODE, default=_mode): cv.one_of(_mode, upper=True)}
_fields = [
automation.ApplyCall(f"with_operation({NUMBER_OPERATION_OPTIONS[_mode]})")
]
if _cycle:
_schema[cv.Optional(CONF_CYCLE, default=True)] = cv.boolean
_fields.append(automation.ApplyField(CONF_CYCLE, "with_cycle", cg.bool_))
automation.register_apply_action(
_name,
automation.maybe_simple_id(OPERATION_BASE_SCHEMA.extend(_schema)),
*_fields,
call="make_call",
)
@automation.register_action(
"number.increment",
NumberOperationAction,
automation.maybe_simple_id(
OPERATION_BASE_SCHEMA.extend(
{
cv.Optional(CONF_MODE, default="INCREMENT"): cv.one_of(
"INCREMENT", upper=True
),
cv.Optional(CONF_CYCLE, default=True): cv.boolean,
}
)
),
synchronous=True,
)
@automation.register_action(
"number.decrement",
NumberOperationAction,
automation.maybe_simple_id(
OPERATION_BASE_SCHEMA.extend(
{
cv.Optional(CONF_MODE, default="DECREMENT"): cv.one_of(
"DECREMENT", upper=True
),
cv.Optional(CONF_CYCLE, default=True): cv.boolean,
}
)
),
synchronous=True,
)
@automation.register_action(
"number.to_min",
NumberOperationAction,
automation.maybe_simple_id(
OPERATION_BASE_SCHEMA.extend(
{
cv.Optional(CONF_MODE, default="TO_MIN"): cv.one_of(
"TO_MIN", upper=True
),
}
)
),
synchronous=True,
)
@automation.register_action(
"number.to_max",
NumberOperationAction,
automation.maybe_simple_id(
OPERATION_BASE_SCHEMA.extend(
{
cv.Optional(CONF_MODE, default="TO_MAX"): cv.one_of(
"TO_MAX", upper=True
),
}
)
),
synchronous=True,
)
@automation.register_action(
automation.register_apply_action(
"number.operation",
NumberOperationAction,
OPERATION_BASE_SCHEMA.extend(
{
cv.Required(CONF_OPERATION): cv.templatable(
@@ -453,23 +404,7 @@ async def number_set_to_code(config, action_id, template_arg, args):
cv.Optional(CONF_CYCLE, default=True): cv.templatable(cv.boolean),
}
),
synchronous=True,
automation.ApplyField(CONF_OPERATION, "with_operation", NumberOperation),
automation.ApplyField(CONF_CYCLE, "with_cycle", cg.bool_),
call="make_call",
)
async def number_to_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)
if (operation := config.get(CONF_OPERATION)) is not None:
to_ = await cg.templatable(operation, args, NumberOperation)
cg.add(var.set_operation(to_))
if (cycle := config.get(CONF_CYCLE)) is not None:
template_ = await cg.templatable(cycle, args, cg.bool_)
cg.add(var.set_cycle(template_))
if (mode := config.get(CONF_MODE)) is not None:
template_ = await cg.templatable(
NUMBER_OPERATION_OPTIONS[mode], args, NumberOperation
)
cg.add(var.set_operation(template_))
if (cycle := config.get(CONF_CYCLE)) is not None:
template_ = await cg.templatable(cycle, args, cg.bool_)
cg.add(var.set_cycle(template_))
return var
-34
View File
@@ -13,40 +13,6 @@ class NumberStateTrigger final : public Trigger<float> {
}
};
template<typename... Ts> class NumberSetAction final : public Action<Ts...> {
public:
NumberSetAction(Number *number) : number_(number) {}
TEMPLATABLE_VALUE(float, value)
void play(const Ts &...x) override {
auto call = this->number_->make_call();
call.set_value(this->value_.value(x...));
call.perform();
}
protected:
Number *number_;
};
template<typename... Ts> class NumberOperationAction final : public Action<Ts...> {
public:
explicit NumberOperationAction(Number *number) : number_(number) {}
TEMPLATABLE_VALUE(NumberOperation, operation)
TEMPLATABLE_VALUE(bool, cycle)
void play(const Ts &...x) override {
auto call = this->number_->make_call();
call.with_operation(this->operation_.value(x...));
if (this->cycle_.has_value()) {
call.with_cycle(this->cycle_.value(x...));
}
call.perform();
}
protected:
Number *number_;
};
class ValueRangeTrigger final : public Trigger<float>, public Component {
public:
explicit ValueRangeTrigger(Number *parent) : parent_(parent) {}
+45 -68
View File
@@ -1,3 +1,5 @@
from dataclasses import dataclass
from esphome import automation
from esphome.automation import maybe_simple_id
import esphome.codegen as cg
@@ -12,12 +14,26 @@ from esphome.const import (
CONF_POWER_SUPPLY,
)
from esphome.core import CORE
from esphome.types import ConfigType
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
DOMAIN = "output"
CONF_ZERO_MEANS_ZERO = "zero_means_zero"
@dataclass
class OutputData:
power_scaling: bool = False
def _get_data() -> OutputData:
if DOMAIN not in CORE.data:
CORE.data[DOMAIN] = OutputData()
return CORE.data[DOMAIN]
BINARY_OUTPUT_SCHEMA = cv.Schema(
{
cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply),
@@ -39,13 +55,6 @@ BinaryOutputPtr = BinaryOutput.operator("ptr")
FloatOutput = output_ns.class_("FloatOutput", BinaryOutput)
FloatOutputPtr = FloatOutput.operator("ptr")
# Actions
TurnOffAction = output_ns.class_("TurnOffAction", automation.Action)
TurnOnAction = output_ns.class_("TurnOnAction", automation.Action)
SetLevelAction = output_ns.class_("SetLevelAction", automation.Action)
SetMinPowerAction = output_ns.class_("SetMinPowerAction", automation.Action)
SetMaxPowerAction = output_ns.class_("SetMaxPowerAction", automation.Action)
async def setup_output_platform_(obj, config):
if CONF_INVERTED in config:
@@ -85,81 +94,49 @@ BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id(
)
@automation.register_action(
"output.turn_on", TurnOnAction, BINARY_OUTPUT_ACTION_SCHEMA, synchronous=True
)
async def output_turn_on_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 _enable_power_scaling(config: ConfigType) -> ConfigType:
# set_min_power/set_max_power only exist with the define; to_code emits it from this fact.
_get_data().power_scaling = True
return config
@automation.register_action(
"output.turn_off", TurnOffAction, BINARY_OUTPUT_ACTION_SCHEMA, synchronous=True
)
async def output_turn_off_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, _call in (
("output.turn_on", "turn_on()"),
("output.turn_off", "turn_off()"),
):
automation.register_apply_action(
_name, BINARY_OUTPUT_ACTION_SCHEMA, automation.ApplyCall(_call)
)
@automation.register_action(
automation.register_apply_action(
"output.set_level",
SetLevelAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(FloatOutput),
cv.Required(CONF_LEVEL): cv.templatable(cv.percentage),
}
),
synchronous=True,
automation.ApplyField(CONF_LEVEL, "set_level", cg.float_),
)
async def output_set_level_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_LEVEL], args, cg.float_)
cg.add(var.set_level(template_))
return var
@automation.register_action(
"output.set_min_power",
SetMinPowerAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(FloatOutput),
cv.Required(CONF_MIN_POWER): cv.templatable(cv.percentage),
}
),
synchronous=True,
)
async def output_set_min_power_to_code(config, action_id, template_arg, args):
cg.add_define("USE_OUTPUT_FLOAT_POWER_SCALING")
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_MIN_POWER], args, cg.float_)
cg.add(var.set_min_power(template_))
return var
@automation.register_action(
"output.set_max_power",
SetMaxPowerAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(FloatOutput),
cv.Required(CONF_MAX_POWER): cv.templatable(cv.percentage),
}
),
synchronous=True,
)
async def output_set_max_power_to_code(config, action_id, template_arg, args):
cg.add_define("USE_OUTPUT_FLOAT_POWER_SCALING")
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_MAX_POWER], args, cg.float_)
cg.add(var.set_max_power(template_))
return var
for _name, _key, _target in (
("output.set_min_power", CONF_MIN_POWER, "set_min_power"),
("output.set_max_power", CONF_MAX_POWER, "set_max_power"),
):
automation.register_apply_action(
_name,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(FloatOutput),
cv.Required(_key): cv.templatable(cv.percentage),
}
).add_extra(_enable_power_scaling),
automation.ApplyField(_key, _target, cg.float_),
)
async def to_code(config):
cg.add_define("USE_OUTPUT")
if _get_data().power_scaling:
cg.add_define("USE_OUTPUT_FLOAT_POWER_SCALING")
cg.add_global(output_ns.using)
-69
View File
@@ -1,69 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/core/defines.h"
#include "esphome/components/output/binary_output.h"
#include "esphome/components/output/float_output.h"
namespace esphome::output {
template<typename... Ts> class TurnOffAction final : public Action<Ts...> {
public:
TurnOffAction(BinaryOutput *output) : output_(output) {}
void play(const Ts &...x) override { this->output_->turn_off(); }
protected:
BinaryOutput *output_;
};
template<typename... Ts> class TurnOnAction final : public Action<Ts...> {
public:
TurnOnAction(BinaryOutput *output) : output_(output) {}
void play(const Ts &...x) override { this->output_->turn_on(); }
protected:
BinaryOutput *output_;
};
template<typename... Ts> class SetLevelAction final : public Action<Ts...> {
public:
SetLevelAction(FloatOutput *output) : output_(output) {}
TEMPLATABLE_VALUE(float, level)
void play(const Ts &...x) override { this->output_->set_level(this->level_.value(x...)); }
protected:
FloatOutput *output_;
};
#ifdef USE_OUTPUT_FLOAT_POWER_SCALING
template<typename... Ts> class SetMinPowerAction final : public Action<Ts...> {
public:
SetMinPowerAction(FloatOutput *output) : output_(output) {}
TEMPLATABLE_VALUE(float, min_power)
void play(const Ts &...x) override { this->output_->set_min_power(this->min_power_.value(x...)); }
protected:
FloatOutput *output_;
};
template<typename... Ts> class SetMaxPowerAction final : public Action<Ts...> {
public:
SetMaxPowerAction(FloatOutput *output) : output_(output) {}
TEMPLATABLE_VALUE(float, max_power)
void play(const Ts &...x) override { this->output_->set_max_power(this->max_power_.value(x...)); }
protected:
FloatOutput *output_;
};
#endif // USE_OUTPUT_FLOAT_POWER_SCALING
} // namespace esphome::output
@@ -1,21 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/pulse_counter/pulse_counter_sensor.h"
namespace esphome::pulse_counter {
template<typename... Ts> class SetTotalPulsesAction final : public Action<Ts...> {
public:
SetTotalPulsesAction(PulseCounterSensor *pulse_counter) : pulse_counter_(pulse_counter) {}
TEMPLATABLE_VALUE(uint32_t, total_pulses)
void play(const Ts &...x) override { this->pulse_counter_->set_total_pulses(this->total_pulses_.value(x...)); }
protected:
PulseCounterSensor *pulse_counter_;
};
} // namespace esphome::pulse_counter
+3 -20
View File
@@ -21,8 +21,7 @@ from esphome.const import (
UNIT_PULSES,
UNIT_PULSES_PER_MINUTE,
)
from esphome.core import CORE, ID
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.core import CORE
from esphome.types import ConfigType
CONF_USE_PCNT = "use_pcnt"
@@ -41,10 +40,6 @@ PulseCounterSensor = pulse_counter_ns.class_(
"PulseCounterSensor", sensor.Sensor, cg.PollingComponent
)
SetTotalPulsesAction = pulse_counter_ns.class_(
"SetTotalPulsesAction", automation.Action
)
def validate_internal_filter(value: ConfigType) -> ConfigType:
use_pcnt = value.get(CONF_USE_PCNT)
@@ -150,25 +145,13 @@ async def to_code(config: ConfigType) -> None:
cg.add(var.set_total_sensor(sens))
@automation.register_action(
automation.register_apply_action(
"pulse_counter.set_total_pulses",
SetTotalPulsesAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(PulseCounterSensor),
cv.Required(CONF_VALUE): cv.templatable(cv.uint32_t),
}
),
synchronous=True,
automation.ApplyField(CONF_VALUE, "set_total_pulses", cg.uint32),
)
async def set_total_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)
template_ = await cg.templatable(config[CONF_VALUE], args, cg.uint32)
cg.add(var.set_total_pulses(template_))
return var
+2 -17
View File
@@ -3,8 +3,6 @@ import esphome.codegen as cg
from esphome.components import output
import esphome.config_validation as cv
from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_PIN
from esphome.core import ID
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.types import ConfigType
CODEOWNERS = ["@jesserockz"]
@@ -13,7 +11,6 @@ DEPENDENCIES = ["rp2"]
rp2040_pwm_ns = cg.esphome_ns.namespace("rp2040_pwm")
RP2040PWM = rp2040_pwm_ns.class_("RP2040PWM", output.FloatOutput, cg.Component)
SetFrequencyAction = rp2040_pwm_ns.class_("SetFrequencyAction", automation.Action)
validate_frequency = cv.All(cv.frequency, cv.float_range(min=1.0e-6))
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
@@ -36,25 +33,13 @@ async def to_code(config: ConfigType) -> None:
cg.add(var.set_frequency(config[CONF_FREQUENCY]))
@automation.register_action(
automation.register_apply_action(
"output.rp2040_pwm.set_frequency",
SetFrequencyAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(RP2040PWM),
cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency),
}
),
synchronous=True,
automation.ApplyField(CONF_FREQUENCY, "update_frequency", cg.float_),
)
async def rp2040_set_frequency_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_FREQUENCY], args, cg.float_)
cg.add(var.set_frequency(template_))
return var
@@ -3,7 +3,6 @@
#ifdef USE_RP2
#include "esphome/components/output/float_output.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
@@ -39,19 +38,6 @@ class RP2040PWM final : public output::FloatOutput, public Component {
bool frequency_changed_{false};
};
template<typename... Ts> class SetFrequencyAction final : public Action<Ts...> {
public:
SetFrequencyAction(RP2040PWM *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(float, frequency);
void play(const Ts &...x) {
float freq = this->frequency_.value(x...);
this->parent_->update_frequency(freq);
}
RP2040PWM *parent_;
};
} // namespace esphome::rp2040_pwm
#endif // USE_RP2
+2 -13
View File
@@ -42,7 +42,6 @@ RP2BLETracker = rp2_ble_tracker_ns.class_(
)
StartScanAction = rp2_ble_tracker_ns.class_("StartScanAction", automation.Action)
StopScanAction = rp2_ble_tracker_ns.class_("StopScanAction", automation.Action)
ESPBTAdvertiseTrigger = ble_automation.ESPBTAdvertiseTrigger
BLEServiceDataAdvertiseTrigger = ble_automation.BLEServiceDataAdvertiseTrigger
@@ -156,9 +155,8 @@ async def start_scan_action_to_code(
return var
@automation.register_action(
automation.register_apply_action(
"rp2_ble_tracker.stop_scan",
StopScanAction,
automation.maybe_simple_id(
cv.Schema(
{
@@ -166,14 +164,5 @@ async def start_scan_action_to_code(
}
)
),
synchronous=True,
automation.ApplyCall("stop_scan()"),
)
async def stop_scan_action_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: list,
) -> cg.MockObj:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var
@@ -37,11 +37,6 @@ template<typename... Ts> class StartScanAction final : public Action<Ts...>, pub
}
};
template<typename... Ts> class StopScanAction final : public Action<Ts...>, public Parented<RP2BLETracker> {
public:
void play(const Ts &...x) override { this->parent_->stop_scan(); }
};
} // namespace esphome::rp2_ble_tracker
#endif // USE_RP2
+31 -97
View File
@@ -40,11 +40,6 @@ SelectStateTrigger = select_ns.class_(
automation.Trigger.template(cg.StringRef, cg.size_t),
)
# Actions
SelectSetAction = select_ns.class_("SelectSetAction", automation.Action)
SelectSetIndexAction = select_ns.class_("SelectSetIndexAction", automation.Action)
SelectOperationAction = select_ns.class_("SelectOperationAction", automation.Action)
# Conditions
SelectIsCondition = select_ns.class_("SelectIsCondition", automation.Condition)
@@ -140,40 +135,27 @@ OPERATION_BASE_SCHEMA = cv.Schema(
)
@automation.register_action(
automation.register_apply_action(
"select.set",
SelectSetAction,
OPERATION_BASE_SCHEMA.extend(
{
cv.Required(CONF_OPTION): cv.templatable(cv.string_strict),
}
),
synchronous=True,
automation.ApplyField(CONF_OPTION, "set_option", cg.std_string),
call="make_call",
)
async def select_set_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_OPTION], args, cg.std_string)
cg.add(var.set_option(template_))
return var
@automation.register_action(
automation.register_apply_action(
"select.set_index",
SelectSetIndexAction,
OPERATION_BASE_SCHEMA.extend(
{
cv.Required(CONF_INDEX): cv.templatable(cv.positive_int),
}
),
synchronous=True,
automation.ApplyField(CONF_INDEX, "set_index", cg.size_t),
call="make_call",
)
async def select_set_index_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_INDEX], args, cg.size_t)
cg.add(var.set_index(template_))
return var
@automation.register_condition(
@@ -211,9 +193,8 @@ async def select_is_to_code(config, condition_id, template_arg, args):
return cg.new_Pvariable(condition_id, template_arg, paren, arg)
@automation.register_action(
automation.register_apply_action(
"select.operation",
SelectOperationAction,
OPERATION_BASE_SCHEMA.extend(
{
cv.Required(CONF_OPERATION): cv.templatable(
@@ -222,75 +203,28 @@ async def select_is_to_code(config, condition_id, template_arg, args):
cv.Optional(CONF_CYCLE, default=True): cv.templatable(cv.boolean),
}
),
synchronous=True,
automation.ApplyField(CONF_OPERATION, "with_operation", SelectOperation),
automation.ApplyField(CONF_CYCLE, "with_cycle", cg.bool_),
call="make_call",
)
@automation.register_action(
"select.next",
SelectOperationAction,
automation.maybe_simple_id(
OPERATION_BASE_SCHEMA.extend(
{
cv.Optional(CONF_MODE, default="NEXT"): cv.one_of("NEXT", upper=True),
cv.Optional(CONF_CYCLE, default=True): cv.boolean,
}
)
),
synchronous=True,
)
@automation.register_action(
"select.previous",
SelectOperationAction,
automation.maybe_simple_id(
OPERATION_BASE_SCHEMA.extend(
{
cv.Optional(CONF_MODE, default="PREVIOUS"): cv.one_of(
"PREVIOUS", upper=True
),
cv.Optional(CONF_CYCLE, default=True): cv.boolean,
}
)
),
synchronous=True,
)
@automation.register_action(
"select.first",
SelectOperationAction,
automation.maybe_simple_id(
OPERATION_BASE_SCHEMA.extend(
{
cv.Optional(CONF_MODE, default="FIRST"): cv.one_of("FIRST", upper=True),
}
)
),
synchronous=True,
)
@automation.register_action(
"select.last",
SelectOperationAction,
automation.maybe_simple_id(
OPERATION_BASE_SCHEMA.extend(
{
cv.Optional(CONF_MODE, default="LAST"): cv.one_of("LAST", upper=True),
}
)
),
synchronous=True,
)
async def select_operation_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)
if (operation := config.get(CONF_OPERATION)) is not None:
op_ = await cg.templatable(operation, args, SelectOperation)
cg.add(var.set_operation(op_))
if (cycle := config.get(CONF_CYCLE)) is not None:
template_ = await cg.templatable(cycle, args, cg.bool_)
cg.add(var.set_cycle(template_))
if (mode := config.get(CONF_MODE)) is not None:
template_ = await cg.templatable(
SELECT_OPERATION_OPTIONS[mode], args, SelectOperation
)
cg.add(var.set_operation(template_))
if (cycle := config.get(CONF_CYCLE)) is not None:
template_ = await cg.templatable(cycle, args, cg.bool_)
cg.add(var.set_cycle(template_))
return var
# The operation is fixed by the action name; CONF_MODE only stays accepted in the config.
for _name, _mode, _cycle in (
("select.next", "NEXT", True),
("select.previous", "PREVIOUS", True),
("select.first", "FIRST", False),
("select.last", "LAST", False),
):
_schema = {cv.Optional(CONF_MODE, default=_mode): cv.one_of(_mode, upper=True)}
_fields = [
automation.ApplyCall(f"with_operation({SELECT_OPERATION_OPTIONS[_mode]})")
]
if _cycle:
_schema[cv.Optional(CONF_CYCLE, default=True)] = cv.boolean
_fields.append(automation.ApplyField(CONF_CYCLE, "with_cycle", cg.bool_))
automation.register_apply_action(
_name,
automation.maybe_simple_id(OPERATION_BASE_SCHEMA.extend(_schema)),
*_fields,
call="make_call",
)
-49
View File
@@ -17,55 +17,6 @@ class SelectStateTrigger final : public Trigger<StringRef, size_t> {
Select *parent_;
};
template<typename... Ts> class SelectSetAction final : public Action<Ts...> {
public:
explicit SelectSetAction(Select *select) : select_(select) {}
TEMPLATABLE_VALUE(std::string, option)
void play(const Ts &...x) override {
auto call = this->select_->make_call();
call.set_option(this->option_.value(x...));
call.perform();
}
protected:
Select *select_;
};
template<typename... Ts> class SelectSetIndexAction final : public Action<Ts...> {
public:
explicit SelectSetIndexAction(Select *select) : select_(select) {}
TEMPLATABLE_VALUE(size_t, index)
void play(const Ts &...x) override {
auto call = this->select_->make_call();
call.set_index(this->index_.value(x...));
call.perform();
}
protected:
Select *select_;
};
template<typename... Ts> class SelectOperationAction final : public Action<Ts...> {
public:
explicit SelectOperationAction(Select *select) : select_(select) {}
TEMPLATABLE_VALUE(bool, cycle)
TEMPLATABLE_VALUE(SelectOperation, operation)
void play(const Ts &...x) override {
auto call = this->select_->make_call();
call.with_operation(this->operation_.value(x...));
if (this->cycle_.has_value()) {
call.with_cycle(this->cycle_.value(x...));
}
call.perform();
}
protected:
Select *select_;
};
template<size_t N, typename... Ts> class SelectIsCondition final : public Condition<Ts...> {
public:
SelectIsCondition(Select *parent, const char *const *option_list) : parent_(parent), option_list_(option_list) {}
-1
View File
@@ -256,7 +256,6 @@ SensorPtr = Sensor.operator("ptr")
ValueRangeTrigger = sensor_ns.class_(
"ValueRangeTrigger", automation.Trigger.template(cg.float_), cg.Component
)
SensorPublishAction = sensor_ns.class_("SensorPublishAction", automation.Action)
# Filters
Filter = sensor_ns.class_("Filter")
-11
View File
@@ -20,17 +20,6 @@ class SensorRawStateTrigger final : public Trigger<float> {
}
};
template<typename... Ts> class SensorPublishAction final : public Action<Ts...> {
public:
SensorPublishAction(Sensor *sensor) : sensor_(sensor) {}
TEMPLATABLE_VALUE(float, state)
void play(const Ts &...x) override { this->sensor_->publish_state(this->state_.value(x...)); }
protected:
Sensor *sensor_;
};
class ValueRangeTrigger final : public Trigger<float>, public Component {
public:
explicit ValueRangeTrigger(Sensor *parent) : parent_(parent) {}
+4 -30
View File
@@ -13,14 +13,10 @@ from esphome.const import (
CONF_RESTORE,
CONF_TRANSITION_LENGTH,
)
from esphome.core import ID
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.types import ConfigType
servo_ns = cg.esphome_ns.namespace("servo")
Servo = servo_ns.class_("Servo", cg.Component)
ServoWriteAction = servo_ns.class_("ServoWriteAction", automation.Action)
ServoDetachAction = servo_ns.class_("ServoDetachAction", automation.Action)
CONF_AUTO_DETACH_TIME = "auto_detach_time"
MULTI_CONF = True
@@ -56,45 +52,23 @@ async def to_code(config: ConfigType) -> None:
cg.add(var.set_transition_length(config[CONF_TRANSITION_LENGTH]))
@automation.register_action(
automation.register_apply_action(
"servo.write",
ServoWriteAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Servo),
cv.Required(CONF_LEVEL): cv.templatable(cv.possibly_negative_percentage),
}
),
synchronous=True,
automation.ApplyField(CONF_LEVEL, "write", cg.float_),
)
async def servo_write_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_LEVEL], args, cg.float_)
cg.add(var.set_value(template_))
return var
@automation.register_action(
automation.register_apply_action(
"servo.detach",
ServoDetachAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(Servo),
}
),
synchronous=True,
automation.ApplyCall("detach()"),
)
async def servo_detach_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)
-22
View File
@@ -1,6 +1,5 @@
#pragma once
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
@@ -51,25 +50,4 @@ class Servo final : public Component {
};
};
template<typename... Ts> class ServoWriteAction final : public Action<Ts...> {
public:
ServoWriteAction(Servo *servo) : servo_(servo) {}
TEMPLATABLE_VALUE(float, value)
void play(const Ts &...x) override { this->servo_->write(this->value_.value(x...)); }
protected:
Servo *servo_;
};
template<typename... Ts> class ServoDetachAction final : public Action<Ts...> {
public:
ServoDetachAction(Servo *servo) : servo_(servo) {}
void play(const Ts &...x) override { this->servo_->detach(); }
protected:
Servo *servo_;
};
} // namespace esphome::servo
+47 -95
View File
@@ -1,3 +1,6 @@
from collections.abc import Callable
from typing import Any
from esphome import automation
import esphome.codegen as cg
import esphome.config_validation as cv
@@ -11,18 +14,13 @@ from esphome.const import (
CONF_TARGET,
)
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.types import SafeExpType
IS_PLATFORM_COMPONENT = True
stepper_ns = cg.esphome_ns.namespace("stepper")
Stepper = stepper_ns.class_("Stepper")
SetTargetAction = stepper_ns.class_("SetTargetAction", automation.Action)
ReportPositionAction = stepper_ns.class_("ReportPositionAction", automation.Action)
SetSpeedAction = stepper_ns.class_("SetSpeedAction", automation.Action)
SetAccelerationAction = stepper_ns.class_("SetAccelerationAction", automation.Action)
SetDecelerationAction = stepper_ns.class_("SetDecelerationAction", automation.Action)
def validate_acceleration(value):
value = cv.string(value)
@@ -90,99 +88,53 @@ async def register_stepper(var, config):
await setup_stepper_core_(var, config)
@automation.register_action(
"stepper.set_target",
SetTargetAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Stepper),
cv.Required(CONF_TARGET): cv.templatable(cv.int_),
}
),
synchronous=True,
def _register_stepper_action(
name: str,
key: str,
validator: Callable[[Any], Any],
target: str,
type_: SafeExpType,
*extra: automation.ApplyCall,
) -> None:
automation.register_apply_action(
f"stepper.{name}",
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Stepper),
cv.Required(key): cv.templatable(validator),
}
),
automation.ApplyField(key, target, type_),
*extra,
)
_register_stepper_action("set_target", CONF_TARGET, cv.int_, "set_target", cg.int32)
_register_stepper_action(
"report_position", CONF_POSITION, cv.int_, "report_position", cg.int32
)
async def stepper_set_target_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_TARGET], args, cg.int32)
cg.add(var.set_target(template_))
return var
@automation.register_action(
"stepper.report_position",
ReportPositionAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Stepper),
cv.Required(CONF_POSITION): cv.templatable(cv.int_),
}
),
synchronous=True,
_register_stepper_action(
"set_speed",
CONF_SPEED,
validate_speed,
"set_max_speed",
cg.float_,
automation.ApplyCall("on_update_speed()"),
)
async def stepper_report_position_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_POSITION], args, cg.int32)
cg.add(var.set_position(template_))
return var
@automation.register_action(
"stepper.set_speed",
SetSpeedAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Stepper),
cv.Required(CONF_SPEED): cv.templatable(validate_speed),
}
),
synchronous=True,
_register_stepper_action(
"set_acceleration",
CONF_ACCELERATION,
validate_acceleration,
"set_acceleration",
cg.float_,
)
async def stepper_set_speed_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_SPEED], args, cg.float_)
cg.add(var.set_speed(template_))
return var
@automation.register_action(
"stepper.set_acceleration",
SetAccelerationAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Stepper),
cv.Required(CONF_ACCELERATION): cv.templatable(validate_acceleration),
}
),
synchronous=True,
_register_stepper_action(
"set_deceleration",
CONF_DECELERATION,
validate_acceleration,
"set_deceleration",
cg.float_,
)
async def stepper_set_acceleration_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_ACCELERATION], args, cg.float_)
cg.add(var.set_acceleration(template_))
return var
@automation.register_action(
"stepper.set_deceleration",
SetDecelerationAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Stepper),
cv.Required(CONF_DECELERATION): cv.templatable(validate_acceleration),
}
),
synchronous=True,
)
async def stepper_set_deceleration_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_DECELERATION], args, cg.float_)
cg.add(var.set_deceleration(template_))
return var
@coroutine_with_priority(CoroPriority.CORE)
-71
View File
@@ -1,7 +1,6 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
namespace esphome::stepper {
@@ -37,74 +36,4 @@ class Stepper {
uint32_t last_step_{0};
};
template<typename... Ts> class SetTargetAction final : public Action<Ts...> {
public:
explicit SetTargetAction(Stepper *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(int32_t, target)
void play(const Ts &...x) override { this->parent_->set_target(this->target_.value(x...)); }
protected:
Stepper *parent_;
};
template<typename... Ts> class ReportPositionAction final : public Action<Ts...> {
public:
explicit ReportPositionAction(Stepper *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(int32_t, position)
void play(const Ts &...x) override { this->parent_->report_position(this->position_.value(x...)); }
protected:
Stepper *parent_;
};
template<typename... Ts> class SetSpeedAction final : public Action<Ts...> {
public:
explicit SetSpeedAction(Stepper *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(float, speed);
void play(const Ts &...x) override {
float speed = this->speed_.value(x...);
this->parent_->set_max_speed(speed);
this->parent_->on_update_speed();
}
protected:
Stepper *parent_;
};
template<typename... Ts> class SetAccelerationAction final : public Action<Ts...> {
public:
explicit SetAccelerationAction(Stepper *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(float, acceleration);
void play(const Ts &...x) override {
float acceleration = this->acceleration_.value(x...);
this->parent_->set_acceleration(acceleration);
}
protected:
Stepper *parent_;
};
template<typename... Ts> class SetDecelerationAction final : public Action<Ts...> {
public:
explicit SetDecelerationAction(Stepper *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(float, deceleration);
void play(const Ts &...x) override {
float deceleration = this->deceleration_.value(x...);
this->parent_->set_deceleration(deceleration);
}
protected:
Stepper *parent_;
};
} // namespace esphome::stepper
@@ -60,20 +60,13 @@ async def to_code(config):
cg.add(var.set_template(template_))
@automation.register_action(
automation.register_apply_action(
"binary_sensor.template.publish",
binary_sensor.BinarySensorPublishAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(binary_sensor.BinarySensor),
cv.Required(CONF_STATE): cv.templatable(cv.boolean),
}
),
synchronous=True,
automation.ApplyField(CONF_STATE, "publish_state", cg.bool_),
)
async def binary_sensor_template_publish_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_STATE], args, cg.bool_)
cg.add(var.set_state(template_))
return var
+2 -16
View File
@@ -4,7 +4,6 @@ from esphome.components import lock
import esphome.config_validation as cv
from esphome.const import (
CONF_ASSUMED_STATE,
CONF_ID,
CONF_LAMBDA,
CONF_LOCK_ACTION,
CONF_OPEN_ACTION,
@@ -17,12 +16,6 @@ from .. import template_ns
TemplateLock = template_ns.class_("TemplateLock", lock.Lock, cg.Component)
TemplateLockPublishAction = template_ns.class_(
"TemplateLockPublishAction",
automation.Action,
cg.Parented.template(TemplateLock),
)
def validate(config):
if not config[CONF_OPTIMISTIC] and (
@@ -80,9 +73,8 @@ async def to_code(config):
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
@automation.register_action(
automation.register_apply_action(
"lock.template.publish",
TemplateLockPublishAction,
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(TemplateLock),
@@ -90,11 +82,5 @@ async def to_code(config):
},
key=CONF_STATE,
),
synchronous=True,
automation.ApplyField(CONF_STATE, "publish_state", lock.LockState),
)
async def lock_template_publish_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, lock.LockState)
cg.add(var.set_state(template_))
return var
@@ -1,16 +0,0 @@
#pragma once
#include "template_lock.h"
#include "esphome/core/automation.h"
namespace esphome::template_ {
template<typename... Ts> class TemplateLockPublishAction final : public Action<Ts...>, public Parented<TemplateLock> {
public:
TEMPLATABLE_VALUE(lock::LockState, state)
void play(const Ts &...x) override { this->parent_->publish_state(this->state_.value(x...)); }
};
} // namespace esphome::template_
@@ -49,20 +49,13 @@ async def to_code(config):
cg.add(var.set_template(template_))
@automation.register_action(
automation.register_apply_action(
"sensor.template.publish",
sensor.SensorPublishAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(sensor.Sensor),
cv.Required(CONF_STATE): cv.templatable(cv.float_),
}
),
synchronous=True,
automation.ApplyField(CONF_STATE, "publish_state", cg.float_),
)
async def sensor_template_publish_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_STATE], args, cg.float_)
cg.add(var.set_state(template_))
return var
@@ -1,7 +1,6 @@
from esphome import automation
import esphome.codegen as cg
from esphome.components import text_sensor
from esphome.components.text_sensor import TextSensorPublishAction
import esphome.config_validation as cv
from esphome.const import CONF_DEVICE_CLASS, CONF_ID, CONF_LAMBDA, CONF_STATE
@@ -38,20 +37,13 @@ async def to_code(config):
cg.add(var.set_template(template_))
@automation.register_action(
automation.register_apply_action(
"text_sensor.template.publish",
TextSensorPublishAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(text_sensor.TextSensor),
cv.Required(CONF_STATE): cv.templatable(cv.string_strict),
}
),
synchronous=True,
automation.ApplyField(CONF_STATE, "publish_state", cg.std_string),
)
async def text_sensor_template_publish_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_STATE], args, cg.std_string)
cg.add(var.set_state(template_))
return var
+9 -23
View File
@@ -7,7 +7,6 @@ from esphome.const import (
CONF_CLOSE_ACTION,
CONF_CURRENT_OPERATION,
CONF_DEVICE_CLASS,
CONF_ID,
CONF_LAMBDA,
CONF_OPEN_ACTION,
CONF_OPTIMISTIC,
@@ -22,10 +21,6 @@ from .. import template_ns
TemplateValve = template_ns.class_("TemplateValve", valve.Valve, cg.Component)
TemplateValvePublishAction = template_ns.class_(
"TemplateValvePublishAction", automation.Action, cg.Parented.template(TemplateValve)
)
TemplateValveRestoreMode = template_ns.enum("TemplateValveRestoreMode")
RESTORE_MODES = {
"NO_RESTORE": TemplateValveRestoreMode.VALVE_NO_RESTORE,
@@ -104,9 +99,10 @@ async def to_code(config):
cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE]))
@automation.register_action(
# CONF_STATE and CONF_POSITION are cv.Exclusive in the schema, so at most
# one is present and both write the position.
automation.register_apply_action(
"valve.template.publish",
TemplateValvePublishAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(TemplateValve),
@@ -117,20 +113,10 @@ async def to_code(config):
),
}
),
synchronous=True,
automation.ApplyField(CONF_STATE, "position = {}", cg.float_),
automation.ApplyField(CONF_POSITION, "position = {}", cg.float_),
automation.ApplyField(
CONF_CURRENT_OPERATION, "current_operation = {}", valve.ValveOperation
),
automation.ApplyCall("publish_state()"),
)
async def valve_template_publish_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
if state_config := config.get(CONF_STATE):
template_ = await cg.templatable(state_config, args, cg.float_)
cg.add(var.set_position(template_))
if (position_config := config.get(CONF_POSITION)) is not None:
template_ = await cg.templatable(position_config, args, cg.float_)
cg.add(var.set_position(template_))
if current_operation_config := config.get(CONF_CURRENT_OPERATION):
template_ = await cg.templatable(
current_operation_config, args, valve.ValveOperation
)
cg.add(var.set_current_operation(template_))
return var
@@ -1,22 +0,0 @@
#pragma once
#include "template_valve.h"
#include "esphome/core/automation.h"
namespace esphome::template_ {
template<typename... Ts> class TemplateValvePublishAction final : public Action<Ts...>, public Parented<TemplateValve> {
TEMPLATABLE_VALUE(float, position)
TEMPLATABLE_VALUE(valve::ValveOperation, current_operation)
void play(const Ts &...x) override {
if (this->position_.has_value())
this->parent_->position = this->position_.value(x...);
if (this->current_operation_.has_value())
this->parent_->current_operation = this->current_operation_.value(x...);
this->parent_->publish_state();
}
};
} // namespace esphome::template_
+5 -19
View File
@@ -13,13 +13,13 @@ from esphome.const import (
CONF_VALUE,
CONF_WEB_SERVER,
)
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.core.entity_helpers import (
entity_duplicate_validator,
queue_entity_register,
setup_entity,
)
from esphome.cpp_generator import MockObj, MockObjClass, TemplateArgsType
from esphome.cpp_generator import MockObj, MockObjClass
from esphome.types import ConfigType
CODEOWNERS = ["@mauritskorse"]
@@ -34,9 +34,6 @@ TextStateTrigger = text_ns.class_(
"TextStateTrigger", automation.Trigger.template(cg.std_string)
)
# Actions
TextSetAction = text_ns.class_("TextSetAction", automation.Action)
# Conditions
TextMode = text_ns.enum("TextMode")
@@ -160,24 +157,13 @@ OPERATION_BASE_SCHEMA = cv.Schema(
)
@automation.register_action(
automation.register_apply_action(
"text.set",
TextSetAction,
OPERATION_BASE_SCHEMA.extend(
{
cv.Required(CONF_VALUE): cv.templatable(cv.string_strict),
}
),
synchronous=True,
automation.ApplyField(CONF_VALUE, "set_value", cg.std_string),
call="make_call",
)
async def text_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_VALUE], args, cg.std_string)
cg.add(var.set_value(template_))
return var
-15
View File
@@ -13,19 +13,4 @@ class TextStateTrigger final : public Trigger<std::string> {
}
};
template<typename... Ts> class TextSetAction final : public Action<Ts...> {
public:
explicit TextSetAction(Text *text) : text_(text) {}
TEMPLATABLE_VALUE(std::string, value)
void play(const Ts &...x) override {
auto call = this->text_->make_call();
call.set_value(this->value_.value(x...));
call.perform();
}
protected:
Text *text_;
};
} // namespace esphome::text
@@ -43,9 +43,6 @@ text_sensor_ns = cg.esphome_ns.namespace("text_sensor")
TextSensor = text_sensor_ns.class_("TextSensor", cg.EntityBase)
TextSensorPtr = TextSensor.operator("ptr")
TextSensorPublishAction = text_sensor_ns.class_(
"TextSensorPublishAction", automation.Action
)
TextSensorStateCondition = text_sensor_ns.class_(
"TextSensorStateCondition", automation.Condition
)
@@ -34,15 +34,4 @@ template<typename... Ts> class TextSensorStateCondition final : public Condition
TextSensor *parent_;
};
template<typename... Ts> class TextSensorPublishAction final : public Action<Ts...> {
public:
TextSensorPublishAction(TextSensor *sensor) : sensor_(sensor) {}
TEMPLATABLE_VALUE(std::string, state)
void play(const Ts &...x) override { this->sensor_->publish_state(this->state_.value(x...)); }
protected:
TextSensor *sensor_;
};
} // namespace esphome::text_sensor
+2 -18
View File
@@ -32,9 +32,6 @@ UpdateEntity = update_ns.class_("UpdateEntity", cg.EntityBase)
UpdateInfo = update_ns.struct("UpdateInfo")
PerformAction = update_ns.class_(
"PerformAction", automation.Action, cg.Parented.template(UpdateEntity)
)
CheckAction = update_ns.class_(
"CheckAction", automation.Action, cg.Parented.template(UpdateEntity)
)
@@ -133,9 +130,8 @@ async def to_code(config: ConfigType) -> None:
cg.add_global(update_ns.using)
@automation.register_action(
automation.register_apply_action(
"update.perform",
PerformAction,
automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(UpdateEntity),
@@ -144,20 +140,8 @@ async def to_code(config: ConfigType) -> None:
): cv.templatable(cv.boolean),
}
),
synchronous=True,
automation.ApplyField(CONF_FORCE_UPDATE, "perform", cg.bool_),
)
async def update_perform_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])
force = await cg.templatable(config[CONF_FORCE_UPDATE], args, cg.bool_)
cg.add(var.set_force(force))
return var
@automation.register_action(
-7
View File
@@ -6,13 +6,6 @@
namespace esphome::update {
template<typename... Ts> class PerformAction final : public Action<Ts...>, public Parented<UpdateEntity> {
TEMPLATABLE_VALUE(bool, force)
public:
void play(const Ts &...x) override { this->parent_->perform(this->force_.value(x...)); }
};
template<typename... Ts> class CheckAction final : public Action<Ts...>, public Parented<UpdateEntity> {
public:
void play(const Ts &...x) override { this->parent_->check(); }
+15 -44
View File
@@ -1,5 +1,5 @@
from esphome import automation
from esphome.automation import register_action, register_condition
from esphome.automation import register_condition
import esphome.codegen as cg
from esphome.components import media_player, micro_wake_word, microphone, speaker
import esphome.config_validation as cv
@@ -62,15 +62,6 @@ MAX_MICROPHONE_SOURCES = 2
voice_assistant_ns = cg.esphome_ns.namespace("voice_assistant")
VoiceAssistant = voice_assistant_ns.class_("VoiceAssistant", cg.Component)
StartAction = voice_assistant_ns.class_(
"StartAction", automation.Action, cg.Parented.template(VoiceAssistant)
)
StartContinuousAction = voice_assistant_ns.class_(
"StartContinuousAction", automation.Action, cg.Parented.template(VoiceAssistant)
)
StopAction = voice_assistant_ns.class_(
"StopAction", automation.Action, cg.Parented.template(VoiceAssistant)
)
IsRunningCondition = voice_assistant_ns.class_(
"IsRunningCondition", automation.Condition, cg.Parented.template(VoiceAssistant)
)
@@ -406,51 +397,31 @@ async def to_code(config: ConfigType) -> None:
VOICE_ASSISTANT_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(VoiceAssistant)})
@register_action(
automation.register_apply_action(
"voice_assistant.start_continuous",
StartContinuousAction,
VOICE_ASSISTANT_ACTION_SCHEMA,
synchronous=True,
automation.ApplyCall("request_start(true, true)"),
)
@register_action(
# wake_word defaults to "" so a start without one clears the previous wake word,
# as the old action did.
automation.register_apply_action(
"voice_assistant.start",
StartAction,
VOICE_ASSISTANT_ACTION_SCHEMA.extend(
{
cv.Optional(CONF_SILENCE_DETECTION, default=True): cv.boolean,
cv.Optional(CONF_WAKE_WORD): cv.templatable(cv.string),
cv.Optional(CONF_WAKE_WORD, default=""): cv.templatable(cv.string),
}
),
synchronous=True,
automation.ApplyField(CONF_WAKE_WORD, "set_wake_word", cg.std_string),
automation.ApplyCall(
"request_start(false, {})", ((CONF_SILENCE_DETECTION, cg.bool_),)
),
)
async def voice_assistant_listen_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])
if CONF_SILENCE_DETECTION in config:
cg.add(var.set_silence_detection(config[CONF_SILENCE_DETECTION]))
if wake_word := config.get(CONF_WAKE_WORD):
templ = await cg.templatable(wake_word, args, cg.std_string)
cg.add(var.set_wake_word(templ))
return var
@register_action(
"voice_assistant.stop", StopAction, VOICE_ASSISTANT_ACTION_SCHEMA, synchronous=True
automation.register_apply_action(
"voice_assistant.stop",
VOICE_ASSISTANT_ACTION_SCHEMA,
automation.ApplyCall("request_stop()"),
)
async def voice_assistant_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_condition(
@@ -347,31 +347,6 @@ class VoiceAssistant final : public Component {
#endif
};
template<typename... Ts> class StartAction final : public Action<Ts...>, public Parented<VoiceAssistant> {
TEMPLATABLE_VALUE(std::string, wake_word);
public:
void play(const Ts &...x) override {
this->parent_->set_wake_word(this->wake_word_.value(x...));
this->parent_->request_start(false, this->silence_detection_);
}
void set_silence_detection(bool silence_detection) { this->silence_detection_ = silence_detection; }
protected:
bool silence_detection_;
};
template<typename... Ts> class StartContinuousAction final : public Action<Ts...>, public Parented<VoiceAssistant> {
public:
void play(const Ts &...x) override { this->parent_->request_start(true, true); }
};
template<typename... Ts> class StopAction final : public Action<Ts...>, public Parented<VoiceAssistant> {
public:
void play(const Ts &...x) override { this->parent_->request_stop(); }
};
template<typename... Ts> class IsRunningCondition final : public Condition<Ts...>, public Parented<VoiceAssistant> {
public:
bool check(const Ts &...x) override { return this->parent_->is_running() || this->parent_->is_continuous(); }
@@ -39,12 +39,13 @@ def test_trigger_codegen(
"set_manufacturer_uuid128((uint8_t*)(const uint8_t[16]){0xCD,0xAB,0xCD,0xAB,"
"0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB})" in main_cpp
)
# scan-control actions: templatable continuous lambda + parented actions.
# scan-control actions: templatable continuous lambda + stop_scan forwarded
# straight to the tracker (register_apply_action, no action class).
# Exactly one set_continuous: the bare start_scan emits none, pinning the
# restore-configured-mode divergence from esp32 against a future default=.
assert main_cpp.count("->set_continuous(") == 1
assert "startscanaction_id->set_continuous(" in main_cpp
assert "stopscanaction_id->set_parent(" in main_cpp
assert main_cpp.count("->stop_scan();") == 1
# scan_parameters continuous: false reaches the YAML-mode setter, not the
# runtime override.
assert "->set_configured_continuous(false)" in main_cpp
@@ -34,12 +34,13 @@ def test_trigger_codegen(
"set_manufacturer_uuid128((uint8_t*)(const uint8_t[16]){0xCD,0xAB,0xCD,0xAB,"
"0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB})" in main_cpp
)
# scan-control actions: templatable continuous lambda + parented actions.
# scan-control actions: templatable continuous lambda + stop_scan forwarded
# straight to the tracker (register_apply_action, no action class).
# Exactly one set_continuous: the bare start_scan emits none, pinning the
# restore-configured-mode divergence from esp32 against a future default=.
assert main_cpp.count("->set_continuous(") == 1
assert "startscanaction_id->set_continuous(" in main_cpp
assert "stopscanaction_id->set_parent(" in main_cpp
assert main_cpp.count("->stop_scan();") == 1
# scan_parameters continuous: false reaches the YAML-mode setter, not the
# runtime override.
assert "->set_configured_continuous(false)" in main_cpp
@@ -37,12 +37,13 @@ def test_trigger_codegen(
"set_manufacturer_uuid128((uint8_t*)(const uint8_t[16]){0xCD,0xAB,0xCD,0xAB,"
"0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB})" in main_cpp
)
# scan-control actions: templatable continuous lambda + parented actions.
# scan-control actions: templatable continuous lambda + stop_scan forwarded
# straight to the tracker (register_apply_action, no action class).
# Exactly one set_continuous: the bare start_scan emits none, pinning the
# restore-configured-mode divergence from esp32 against a future default=.
assert main_cpp.count("->set_continuous(") == 1
assert "startscanaction_id->set_continuous(" in main_cpp
assert "stopscanaction_id->set_parent(" in main_cpp
assert main_cpp.count("->stop_scan();") == 1
# scan_parameters continuous: false reaches the YAML-mode setter, not the
# runtime override.
assert "->set_configured_continuous(false)" in main_cpp
@@ -67,3 +67,28 @@ alarm_control_panel:
on_cleared:
then:
- logger.log: "### CLEARED ###"
button:
# Exercise the arm/disarm actions with a constant code, a lambda code and no code.
- platform: template
name: Alarm Panel Actions
on_press:
- alarm_control_panel.arm_away:
id: alarmcontrolpanel1
code: "1234"
- alarm_control_panel.arm_home:
id: alarmcontrolpanel1
code: !lambda |-
std::string code = "56";
code += "78";
return code;
- alarm_control_panel.arm_night:
id: alarmcontrolpanel1
code: "0000"
- alarm_control_panel.disarm:
id: alarmcontrolpanel1
code: "1234"
- alarm_control_panel.arm_away: alarmcontrolpanel2
- alarm_control_panel.arm_home: alarmcontrolpanel2
- alarm_control_panel.arm_night: alarmcontrolpanel2
- alarm_control_panel.disarm: alarmcontrolpanel2
@@ -37,6 +37,16 @@ binary_sensor:
format: "New state is %s"
args: ['x.has_value() ? ONOFF(x) : "Unknown"']
- binary_sensor.invalidate_state: binary_sensor_some_binary_sensor
- binary_sensor.template.publish:
id: publish_target
state: true
- binary_sensor.template.publish:
id: publish_target
state: !lambda "return x.value_or(false);"
- platform: template
id: publish_target
name: "Publish Target"
# Test autorepeat with default configuration (no timings)
- platform: template
+18 -2
View File
@@ -56,8 +56,8 @@ climate:
default_target_temperature_high: 22°C
button:
# Exercise the climate.control: action so ControlAction templates get
# build coverage. Various field combinations are tested.
# Exercise the climate.control: action so the register_apply_action codegen
# gets build coverage. Various field combinations are tested.
- platform: template
name: "Climate Control Mode"
on_press:
@@ -84,6 +84,22 @@ button:
- climate.control:
id: climate_test_thermostat
mode: "OFF"
# Custom strings take the (const char *, size_t) overload; the byte length of
# the non-ASCII value is computed at codegen time.
- platform: template
name: "Climate Control Custom Strings"
on_press:
- climate.control:
id: climate_test_thermostat
custom_fan_mode: "Über"
custom_preset: Default
- platform: template
name: "Climate Control Custom Lambdas"
on_press:
- climate.control:
id: climate_test_thermostat
custom_fan_mode: !lambda return "quiet";
custom_preset: !lambda return "Default";
# Exercise climate.control inside a trigger with non-empty Ts (number on_value
# passes float).
+40
View File
@@ -1,3 +1,43 @@
esphome:
on_boot:
then:
- datetime.date.set:
id: datetime_test_date
date:
year: 2021
month: 1
day: 1
- datetime.date.set:
id: datetime_test_date
date: !lambda "return {.day_of_month = 1, .month = 1, .year = 2021};"
- datetime.time.set:
id: datetime_test_time
time: "12:34:56"
- datetime.time.set:
id: datetime_test_time
time: !lambda "return {.second = 56, .minute = 34, .hour = 12};"
- datetime.datetime.set:
id: datetime_test_datetime
datetime: "2021-01-01 12:34:56"
- datetime.datetime.set:
id: datetime_test_datetime
datetime: !lambda "return {.second = 56, .minute = 34, .hour = 12, .day_of_month = 1, .month = 1, .year = 2021};"
datetime:
- platform: template
name: Datetime Test Date
id: datetime_test_date
type: date
optimistic: true
- platform: template
name: Datetime Test Time
id: datetime_test_time
type: time
optimistic: true
- platform: template
name: Datetime Test DateTime
id: datetime_test_datetime
type: datetime
optimistic: true
time:
+10
View File
@@ -1,3 +1,13 @@
esphome:
on_boot:
then:
- output.esp8266_pwm.set_frequency:
id: out
frequency: 100Hz
- output.esp8266_pwm.set_frequency:
id: out2
frequency: !lambda return 200.0f;
output:
- platform: esp8266_pwm
id: out
+7
View File
@@ -38,8 +38,15 @@ espnow:
data: !lambda 'return {0x01, 0x02, 0x03, 0x04, 0x05};'
- espnow.peer.add:
address: 11:22:33:44:55:66
- espnow.peer.add:
address: !lambda 'return {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};'
- espnow.peer.delete:
address: 11:22:33:44:55:66
- espnow.peer.delete:
address: !lambda 'return {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};'
- espnow.set_channel: 6
- espnow.set_channel:
channel: !lambda 'return 6;'
on_broadcast:
- logger.log:
format: "Broadcast from: %s = '%s' RSSI: %d"
+12
View File
@@ -18,3 +18,15 @@ event:
// Log using %.*s format for StringRef
ESP_LOGD("test", "Event type: %.*s", (int) event_type.size(), event_type.c_str());
}
button:
- platform: template
name: Trigger Event
on_press:
- event.trigger:
id: some_event
event_type: template_event_type1
- event.trigger:
id: some_event
event_type: !lambda |-
return id(some_event).has_event() ? "template_event_type2" : "template_event_type1";
+1 -1
View File
@@ -67,7 +67,7 @@ binary_sensor:
return false;
# Exercise fan.turn_on with various field combinations so the
# TurnOnAction codegen paths get build coverage.
# register_apply_action codegen paths get build coverage.
button:
- platform: template
name: "Fan Speed Only"
+2 -2
View File
@@ -4,8 +4,8 @@ esphome:
- globals.set:
id: glob_int
value: "10"
# Set a float global with an integer literal - must emit the correct
# return type so TemplatableFn stores a direct function pointer.
# Set a float global with an integer literal; the lambda must return the
# global's own type so the assignment needs no conversion.
- globals.set:
id: glob_float
value: "102"
+7
View File
@@ -4,11 +4,18 @@ esphome:
- hlk_fm22x.enroll:
name: "Test"
direction: 1
- hlk_fm22x.delete: 5
- hlk_fm22x.delete_all:
- hlk_fm22x.reset:
- hlk_fm22x.scan:
hlk_fm22x:
on_face_scan_matched:
- logger.log: test_hlk_22x_face_scan_matched
- hlk_fm22x.enroll:
name: !lambda 'return name + "-again";'
direction: !lambda 'return face_id % 3;'
- hlk_fm22x.delete: !lambda 'return face_id;'
on_face_scan_unmatched:
- logger.log: test_hlk_22x_face_scan_unmatched
on_face_scan_invalid:
@@ -50,7 +50,7 @@ esphome:
format: "After delay, body still: %s"
args:
- body.c_str()
# Regression test for esphome/esphome#16224: a LightControlAction
# Regression test for esphome/esphome#16224: a light.turn_on action
# nested inside on_response with capture_response: true puts
# `std::string &` into the trigger's Ts..., which exposed a codegen
# bug where the apply lambda's parameter list did not match the
+3
View File
@@ -4,6 +4,9 @@ esphome:
- output.ledc.set_frequency:
id: test_ledc
frequency: 100Hz
- output.ledc.set_frequency:
id: test_ledc
frequency: !lambda return 200.0f;
output:
- platform: ledc
+10
View File
@@ -77,6 +77,16 @@ esphome:
- light.control:
id: test_monochromatic_light
state: on
# flash_length and a turn_off with its own transition
- light.turn_on:
id: test_rgb_light
flash_length: 750ms
- light.turn_on:
id: test_rgb_light
flash_length: !lambda return 250;
- light.turn_off:
id: test_rgb_light
transition_length: 300ms
# Test static effect name resolution at codegen time
- light.turn_on:
id: test_monochromatic_light
+13 -9
View File
@@ -1,28 +1,30 @@
i2s_audio:
i2s_lrclk_pin: 13
i2s_bclk_pin: 14
i2s_mclk_pin: 15
speaker:
- platform: i2s_audio
id: test_speaker
i2s_dout_pin: 18
i2s_audio_id: i2s_audio_bus
i2s_dout_pin: ${i2s_dout_pin}
dac_type: external
media_player:
- platform: speaker
id: test_media_player
name: None
speaker: test_speaker
announcement_pipeline:
speaker: test_speaker
format: NONE
on_state:
- media_player.play:
- media_player.play_media: http://localhost/media.mp3
- media_player.play_media: !lambda 'return "http://localhost/media.mp3";'
- media_player.play_media: !lambda |-
return id(test_media_player).is_muted() ? "http://localhost/muted.mp3" : "http://localhost/media.mp3";
on_idle:
- media_player.pause:
on_play:
- media_player.stop:
- media_player.stop:
announcement: true
- media_player.stop:
announcement: !lambda 'return id(test_media_player).is_muted();'
on_announcement:
- media_player.play:
on_turn_on:
@@ -61,8 +63,10 @@ media_player:
- media_player.volume_up:
- media_player.volume_down:
- media_player.volume_set: 50%
- media_player.volume_set: !lambda 'return id(test_media_player).volume / 2;'
- media_player.enqueue: http://localhost/media.mp3
- media_player.enqueue: !lambda 'return "http://localhost/media.mp3";'
- media_player.enqueue: !lambda |-
return id(test_media_player).is_muted() ? "http://localhost/muted.mp3" : "http://localhost/media.mp3";
- media_player.enqueue:
media_url: http://localhost/media.mp3
announcement: true
@@ -0,0 +1,9 @@
substitutions:
i2s_dout_pin: GPIO18
packages:
i2s_audio: !include ../../test_build_components/common/i2s_audio/esp32-idf.yaml
media_player: !include common.yaml
wifi:
ap:
+11
View File
@@ -1,3 +1,10 @@
esphome:
on_boot:
then:
- display.page.show_next: page_display
- display.page.show_previous:
id: page_display
display:
- platform: mipi_spi
spi_16: true
@@ -32,7 +39,11 @@ display:
bus_mode: single
- platform: mipi_spi
id: page_display
model: WAVESHARE-1.83-V2
pages:
- lambda: it.rectangle(0, 0, 10, 10);
- lambda: it.rectangle(10, 10, 10, 10);
dc_pin:
allow_other_uses: true
number: ${dc_pin}
@@ -37,4 +37,4 @@ esphome:
- mitsubishi_cn105.vane.control:
id: ac
vertical:
direction: !lambda return esphome::mitsubishi_cn105::VERTICAL_VANE_MODE_SWING;
direction: !lambda return VERTICAL_VANE_MODE_SWING;
@@ -63,12 +63,4 @@ TEST(MitsubishiCN105ComponentTests, VaneCallAppliesVerticalDirection) {
EXPECT_EQ(hub.status().vane_mode, MitsubishiCN105::VaneMode::POSITION_5);
}
TEST(MitsubishiCN105ComponentTests, VaneControlActionAppliesConfiguredFields) {
MitsubishiCN105Component hub;
VaneControlAction<> action(&hub, [](VaneCall &call) { call.vertical.set_direction(VERTICAL_VANE_MODE_SWING); });
action.play();
EXPECT_EQ(hub.status().vane_mode, MitsubishiCN105::VaneMode::SWING);
}
} // namespace esphome::mitsubishi_cn105::testing
+27
View File
@@ -11,3 +11,30 @@ sensor:
- platform: number
name: "Test Number Value"
source_id: number_test_number
button:
- platform: template
name: "Test Number Actions"
on_press:
- number.set:
id: number_test_number
value: 42
- number.set:
id: number_test_number
value: !lambda |-
float next = id(number_test_number).state + 1;
return next;
- number.increment: number_test_number
- number.decrement:
id: number_test_number
cycle: false
- number.to_min: number_test_number
- number.to_max: number_test_number
- number.operation:
id: number_test_number
operation: increment
cycle: false
- number.operation:
id: number_test_number
operation: !lambda return NUMBER_OP_DECREMENT;
cycle: !lambda return true;

Some files were not shown because too many files have changed in this diff Show More