mirror of
https://github.com/esphome/esphome.git
synced 2026-07-10 08:55:36 +00:00
Merge branch 'dev' into binary_sensor_multiclick_static_array
This commit is contained in:
@@ -154,7 +154,7 @@ jobs:
|
||||
. venv/bin/activate
|
||||
pytest -vv --cov-report=xml --tb=native -n auto tests --ignore=tests/integration/
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5.5.3
|
||||
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Save Python virtual environment cache
|
||||
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
@@ -86,6 +86,6 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -11,7 +11,7 @@ ci:
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.15.6
|
||||
rev: v0.15.8
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -92,6 +92,7 @@ esphome/components/bmp3xx_i2c/* @latonita
|
||||
esphome/components/bmp3xx_spi/* @latonita
|
||||
esphome/components/bmp581_base/* @danielkent-net @kahrendt
|
||||
esphome/components/bmp581_i2c/* @danielkent-net @kahrendt
|
||||
esphome/components/bmp581_spi/* @danielkent-net @kahrendt
|
||||
esphome/components/bp1658cj/* @Cossid
|
||||
esphome/components/bp5758d/* @Cossid
|
||||
esphome/components/bthome_mithermometer/* @nagyrobi
|
||||
|
||||
@@ -137,6 +137,9 @@ UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
|
||||
SuspendComponentAction = cg.esphome_ns.class_("SuspendComponentAction", Action)
|
||||
ResumeComponentAction = cg.esphome_ns.class_("ResumeComponentAction", Action)
|
||||
Automation = cg.esphome_ns.class_("Automation")
|
||||
TriggerForwarder = cg.esphome_ns.class_("TriggerForwarder")
|
||||
TriggerOnTrueForwarder = cg.esphome_ns.class_("TriggerOnTrueForwarder")
|
||||
TriggerOnFalseForwarder = cg.esphome_ns.class_("TriggerOnFalseForwarder")
|
||||
|
||||
LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
|
||||
StatelessLambdaCondition = cg.esphome_ns.class_("StatelessLambdaCondition", Condition)
|
||||
@@ -661,3 +664,44 @@ async def build_automation(
|
||||
actions = await build_action_list(config[CONF_THEN], templ, args)
|
||||
cg.add(obj.add_actions(actions))
|
||||
return obj
|
||||
|
||||
|
||||
async def build_callback_automation(
|
||||
parent: MockObj,
|
||||
callback_method: str,
|
||||
args: TemplateArgsType,
|
||||
config: ConfigType,
|
||||
forwarder: MockObj | MockObjClass | None = None,
|
||||
) -> None:
|
||||
"""Build an Automation and register it as a callback on the parent.
|
||||
|
||||
Eliminates the need for a Trigger wrapper object by registering the
|
||||
automation's trigger() directly as a callback on the parent component.
|
||||
|
||||
Uses template forwarder structs so the compiler deduplicates the operator()
|
||||
body across all call sites with the same signature. The forwarder must be
|
||||
pointer-sized (single Automation* field) to fit inline in Callback::ctx_
|
||||
and avoid heap allocation.
|
||||
|
||||
:param parent: The component object (e.g., button, sensor).
|
||||
:param callback_method: Name of the callback method (e.g., "add_on_press_callback").
|
||||
:param args: Automation template args as list of (type, name) tuples.
|
||||
:param config: The automation config dict.
|
||||
:param forwarder: Optional forwarder type to use instead of the default
|
||||
TriggerForwarder<Ts...>. Pass any struct type whose aggregate init takes
|
||||
a single Automation pointer (e.g., TriggerOnTrueForwarder).
|
||||
"""
|
||||
arg_types = [arg[0] for arg in args]
|
||||
templ = cg.TemplateArguments(*arg_types)
|
||||
obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ)
|
||||
actions = await build_action_list(config[CONF_THEN], templ, args)
|
||||
cg.add(obj.add_actions(actions))
|
||||
# Use template forwarder structs for deduplication. The compiler generates
|
||||
# one operator() per forwarder type; different automation pointers are just
|
||||
# data in the struct.
|
||||
if forwarder is None:
|
||||
forwarder = TriggerForwarder.template(templ)
|
||||
# RawExpression for aggregate init — both forwarder and obj are codegen
|
||||
# MockObjs (not user input), and there's no Expression type for positional
|
||||
# aggregate initialization (StructInitializer uses named fields).
|
||||
cg.add(getattr(parent, callback_method)(cg.RawExpression(f"{forwarder}{{{obj}}}")))
|
||||
|
||||
@@ -10,7 +10,6 @@ from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MQTT_ID,
|
||||
CONF_ON_STATE,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_WEB_SERVER,
|
||||
)
|
||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||
@@ -34,39 +33,9 @@ CONF_ON_READY = "on_ready"
|
||||
alarm_control_panel_ns = cg.esphome_ns.namespace("alarm_control_panel")
|
||||
AlarmControlPanel = alarm_control_panel_ns.class_("AlarmControlPanel", cg.EntityBase)
|
||||
|
||||
StateTrigger = alarm_control_panel_ns.class_(
|
||||
"StateTrigger", automation.Trigger.template()
|
||||
)
|
||||
TriggeredTrigger = alarm_control_panel_ns.class_(
|
||||
"TriggeredTrigger", automation.Trigger.template()
|
||||
)
|
||||
ClearedTrigger = alarm_control_panel_ns.class_(
|
||||
"ClearedTrigger", automation.Trigger.template()
|
||||
)
|
||||
ArmingTrigger = alarm_control_panel_ns.class_(
|
||||
"ArmingTrigger", automation.Trigger.template()
|
||||
)
|
||||
PendingTrigger = alarm_control_panel_ns.class_(
|
||||
"PendingTrigger", automation.Trigger.template()
|
||||
)
|
||||
ArmedHomeTrigger = alarm_control_panel_ns.class_(
|
||||
"ArmedHomeTrigger", automation.Trigger.template()
|
||||
)
|
||||
ArmedNightTrigger = alarm_control_panel_ns.class_(
|
||||
"ArmedNightTrigger", automation.Trigger.template()
|
||||
)
|
||||
ArmedAwayTrigger = alarm_control_panel_ns.class_(
|
||||
"ArmedAwayTrigger", automation.Trigger.template()
|
||||
)
|
||||
DisarmedTrigger = alarm_control_panel_ns.class_(
|
||||
"DisarmedTrigger", automation.Trigger.template()
|
||||
)
|
||||
ChimeTrigger = alarm_control_panel_ns.class_(
|
||||
"ChimeTrigger", automation.Trigger.template()
|
||||
)
|
||||
ReadyTrigger = alarm_control_panel_ns.class_(
|
||||
"ReadyTrigger", automation.Trigger.template()
|
||||
)
|
||||
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)
|
||||
@@ -89,61 +58,17 @@ _ALARM_CONTROL_PANEL_SCHEMA = (
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
||||
mqtt.MQTTAlarmControlPanelComponent
|
||||
),
|
||||
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ARMING): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmingTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_PENDING): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PendingTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedHomeTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedNightTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedAwayTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_DISARMED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DisarmedTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_CLEARED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_CHIME): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChimeTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_READY): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReadyTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_STATE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ARMING): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_PENDING): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_DISARMED): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_CLEARED): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_CHIME): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_READY): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -189,38 +114,39 @@ ALARM_CONTROL_PANEL_CONDITION_SCHEMA = maybe_simple_id(
|
||||
@setup_entity("alarm_control_panel")
|
||||
async def setup_alarm_control_panel_core_(var, config):
|
||||
for conf in config.get(CONF_ON_STATE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf in config.get(CONF_ON_TRIGGERED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf in config.get(CONF_ON_ARMING, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf in config.get(CONF_ON_PENDING, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf in config.get(CONF_ON_ARMED_HOME, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf in config.get(CONF_ON_ARMED_NIGHT, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf in config.get(CONF_ON_ARMED_AWAY, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf in config.get(CONF_ON_DISARMED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_state_callback", [], conf, forwarder=StateAnyForwarder
|
||||
)
|
||||
_STATE_ENTER_MAP = {
|
||||
CONF_ON_TRIGGERED: AlarmControlPanelState.ACP_STATE_TRIGGERED,
|
||||
CONF_ON_ARMING: AlarmControlPanelState.ACP_STATE_ARMING,
|
||||
CONF_ON_PENDING: AlarmControlPanelState.ACP_STATE_PENDING,
|
||||
CONF_ON_ARMED_HOME: AlarmControlPanelState.ACP_STATE_ARMED_HOME,
|
||||
CONF_ON_ARMED_NIGHT: AlarmControlPanelState.ACP_STATE_ARMED_NIGHT,
|
||||
CONF_ON_ARMED_AWAY: AlarmControlPanelState.ACP_STATE_ARMED_AWAY,
|
||||
CONF_ON_DISARMED: AlarmControlPanelState.ACP_STATE_DISARMED,
|
||||
}
|
||||
for conf_key, state_enum in _STATE_ENTER_MAP.items():
|
||||
for conf in config.get(conf_key, []):
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_on_state_callback",
|
||||
[],
|
||||
conf,
|
||||
forwarder=StateEnterForwarder.template(state_enum),
|
||||
)
|
||||
for conf in config.get(CONF_ON_CLEARED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_cleared_callback", [], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_CHIME, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_chime_callback", [], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_READY, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_ready_callback", [], conf
|
||||
)
|
||||
if web_server_config := config.get(CONF_WEB_SERVER):
|
||||
await web_server.add_entity_config(var, web_server_config)
|
||||
if mqtt_id := config.get(CONF_MQTT_ID):
|
||||
|
||||
@@ -35,8 +35,8 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
|
||||
LOG_STR_ARG(alarm_control_panel_state_to_string(state)),
|
||||
LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state)));
|
||||
this->current_state_ = state;
|
||||
// Single state callback - triggers check get_state() for specific states
|
||||
this->state_callback_.call();
|
||||
// Single state callback - listeners receive the new state as an argument
|
||||
this->state_callback_.call(state);
|
||||
#if defined(USE_ALARM_CONTROL_PANEL) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_alarm_control_panel_update(this);
|
||||
#endif
|
||||
|
||||
@@ -145,8 +145,8 @@ class AlarmControlPanel : public EntityBase {
|
||||
uint32_t last_update_;
|
||||
// the call control function
|
||||
virtual void control(const AlarmControlPanelCall &call) = 0;
|
||||
// state callback - triggers check get_state() for specific state
|
||||
LazyCallbackManager<void()> state_callback_{};
|
||||
// state callback - passes the new state to listeners
|
||||
LazyCallbackManager<void(AlarmControlPanelState)> state_callback_{};
|
||||
// clear callback - fires when leaving TRIGGERED state
|
||||
LazyCallbackManager<void()> cleared_callback_{};
|
||||
// chime callback
|
||||
|
||||
@@ -5,60 +5,27 @@
|
||||
|
||||
namespace esphome::alarm_control_panel {
|
||||
|
||||
/// Trigger on any state change
|
||||
class StateTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit StateTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
alarm_control_panel->add_on_state_callback([this]() { this->trigger(); });
|
||||
/// Callback forwarder that triggers an Automation<> on any state change.
|
||||
/// Pointer-sized (single Automation* field) to fit inline in Callback::ctx_.
|
||||
struct StateAnyForwarder {
|
||||
Automation<> *automation;
|
||||
void operator()(AlarmControlPanelState /*state*/) const { this->automation->trigger(); }
|
||||
};
|
||||
|
||||
/// Callback forwarder that triggers an Automation<> only when the alarm enters a specific state.
|
||||
/// Pointer-sized (single Automation* field) to fit inline in Callback::ctx_.
|
||||
template<AlarmControlPanelState State> struct StateEnterForwarder {
|
||||
Automation<> *automation;
|
||||
void operator()(AlarmControlPanelState state) const {
|
||||
if (state == State)
|
||||
this->automation->trigger();
|
||||
}
|
||||
};
|
||||
|
||||
/// Template trigger that fires when entering a specific state
|
||||
template<AlarmControlPanelState State> class StateEnterTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit StateEnterTrigger(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {
|
||||
alarm_control_panel->add_on_state_callback([this]() {
|
||||
if (this->alarm_control_panel_->get_state() == State)
|
||||
this->trigger();
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
AlarmControlPanel *alarm_control_panel_;
|
||||
};
|
||||
|
||||
// Type aliases for state-specific triggers
|
||||
using TriggeredTrigger = StateEnterTrigger<ACP_STATE_TRIGGERED>;
|
||||
using ArmingTrigger = StateEnterTrigger<ACP_STATE_ARMING>;
|
||||
using PendingTrigger = StateEnterTrigger<ACP_STATE_PENDING>;
|
||||
using ArmedHomeTrigger = StateEnterTrigger<ACP_STATE_ARMED_HOME>;
|
||||
using ArmedNightTrigger = StateEnterTrigger<ACP_STATE_ARMED_NIGHT>;
|
||||
using ArmedAwayTrigger = StateEnterTrigger<ACP_STATE_ARMED_AWAY>;
|
||||
using DisarmedTrigger = StateEnterTrigger<ACP_STATE_DISARMED>;
|
||||
|
||||
/// Trigger when leaving TRIGGERED state (alarm cleared)
|
||||
class ClearedTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit ClearedTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
alarm_control_panel->add_on_cleared_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
/// Trigger on chime event (zone opened while disarmed)
|
||||
class ChimeTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit ChimeTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
alarm_control_panel->add_on_chime_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
/// Trigger on ready state change
|
||||
class ReadyTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit ReadyTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||
alarm_control_panel->add_on_ready_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(StateAnyForwarder) <= sizeof(void *));
|
||||
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 : public Action<Ts...> {
|
||||
public:
|
||||
|
||||
@@ -2512,6 +2512,7 @@ message ListEntitiesInfraredResponse {
|
||||
EntityCategory entity_category = 6;
|
||||
uint32 device_id = 7 [(field_ifdef) = "USE_DEVICES"];
|
||||
uint32 capabilities = 8; // Bitfield of InfraredCapabilityFlags
|
||||
uint32 receiver_frequency = 9; // Demodulation frequency of the IR receiver in Hz (0 = unspecified)
|
||||
}
|
||||
|
||||
// Command to transmit infrared/RF data using raw timings
|
||||
|
||||
@@ -1549,6 +1549,7 @@ uint16_t APIConnection::try_send_infrared_info(EntityBase *entity, APIConnection
|
||||
auto *infrared = static_cast<infrared::Infrared *>(entity);
|
||||
ListEntitiesInfraredResponse msg;
|
||||
msg.capabilities = infrared->get_capability_flags();
|
||||
msg.receiver_frequency = infrared->get_traits().get_receiver_frequency_hz();
|
||||
return fill_and_encode_entity_info(infrared, msg, conn, remaining_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3657,6 +3657,7 @@ void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint32(7, this->device_id);
|
||||
#endif
|
||||
buffer.encode_uint32(8, this->capabilities);
|
||||
buffer.encode_uint32(9, this->receiver_frequency);
|
||||
}
|
||||
uint32_t ListEntitiesInfraredResponse::calculate_size() const {
|
||||
uint32_t size = 0;
|
||||
@@ -3672,6 +3673,7 @@ uint32_t ListEntitiesInfraredResponse::calculate_size() const {
|
||||
size += ProtoSize::calc_uint32(1, this->device_id);
|
||||
#endif
|
||||
size += ProtoSize::calc_uint32(1, this->capabilities);
|
||||
size += ProtoSize::calc_uint32(1, this->receiver_frequency);
|
||||
return size;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3041,11 +3041,12 @@ class ZWaveProxyRequest final : public ProtoDecodableMessage {
|
||||
class ListEntitiesInfraredResponse final : public InfoResponseProtoMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 135;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 44;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 48;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const LogString *message_name() const override { return LOG_STR("list_entities_infrared_response"); }
|
||||
#endif
|
||||
uint32_t capabilities{0};
|
||||
uint32_t receiver_frequency{0};
|
||||
void encode(ProtoWriteBuffer &buffer) const;
|
||||
uint32_t calculate_size() const;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
|
||||
@@ -2572,6 +2572,7 @@ const char *ListEntitiesInfraredResponse::dump_to(DumpBuffer &out) const {
|
||||
dump_field(out, ESPHOME_PSTR("device_id"), this->device_id);
|
||||
#endif
|
||||
dump_field(out, ESPHOME_PSTR("capabilities"), this->capabilities);
|
||||
dump_field(out, ESPHOME_PSTR("receiver_frequency"), this->receiver_frequency);
|
||||
return out.c_str();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -550,8 +550,8 @@ float ATM90E32Component::get_phase_harmonic_active_power_(uint8_t phase) {
|
||||
}
|
||||
|
||||
float ATM90E32Component::get_phase_angle_(uint8_t phase) {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_PANGLE + phase) / 10.0;
|
||||
return (val > 180) ? (float) (val - 360.0f) : (float) val;
|
||||
float val = this->read16_(ATM90E32_REGISTER_PANGLE + phase) / 10.0f;
|
||||
return (val > 180.0f) ? val - 360.0f : val;
|
||||
}
|
||||
|
||||
float ATM90E32Component::get_phase_peak_current_(uint8_t phase) {
|
||||
|
||||
@@ -134,7 +134,6 @@ class ATM90E32Component : public PollingComponent,
|
||||
void set_freq_status_text_sensor(text_sensor::TextSensor *sensor) { this->freq_status_text_sensor_ = sensor; }
|
||||
#endif
|
||||
uint16_t calculate_voltage_threshold(int line_freq, uint16_t ugain, float multiplier);
|
||||
int32_t last_periodic_millis = millis();
|
||||
|
||||
protected:
|
||||
#ifdef USE_NUMBER
|
||||
|
||||
@@ -120,10 +120,6 @@ BinarySensorInitiallyOff = binary_sensor_ns.class_(
|
||||
BinarySensorPtr = BinarySensor.operator("ptr")
|
||||
|
||||
# Triggers
|
||||
PressTrigger = binary_sensor_ns.class_("PressTrigger", automation.Trigger.template())
|
||||
ReleaseTrigger = binary_sensor_ns.class_(
|
||||
"ReleaseTrigger", automation.Trigger.template()
|
||||
)
|
||||
ClickTrigger = binary_sensor_ns.class_("ClickTrigger", automation.Trigger.template())
|
||||
DoubleClickTrigger = binary_sensor_ns.class_(
|
||||
"DoubleClickTrigger", automation.Trigger.template()
|
||||
@@ -133,13 +129,6 @@ MultiClickTriggerBase = binary_sensor_ns.class_(
|
||||
)
|
||||
MultiClickTrigger = binary_sensor_ns.class_("MultiClickTrigger", MultiClickTriggerBase)
|
||||
MultiClickTriggerEvent = binary_sensor_ns.struct("MultiClickTriggerEvent")
|
||||
StateTrigger = binary_sensor_ns.class_(
|
||||
"StateTrigger", automation.Trigger.template(bool)
|
||||
)
|
||||
StateChangeTrigger = binary_sensor_ns.class_(
|
||||
"StateChangeTrigger",
|
||||
automation.Trigger.template(cg.optional.template(bool), cg.optional.template(bool)),
|
||||
)
|
||||
|
||||
BinarySensorPublishAction = binary_sensor_ns.class_(
|
||||
"BinarySensorPublishAction", automation.Action
|
||||
@@ -459,16 +448,8 @@ _BINARY_SENSOR_SCHEMA = (
|
||||
): cv.boolean,
|
||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||
cv.Optional(CONF_FILTERS): validate_filters,
|
||||
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_RELEASE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_PRESS): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_RELEASE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_CLICK): cv.All(
|
||||
automation.validate_automation(
|
||||
{
|
||||
@@ -512,16 +493,8 @@ _BINARY_SENSOR_SCHEMA = (
|
||||
): cv.positive_time_period_milliseconds,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateChangeTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_STATE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -559,13 +532,14 @@ def binary_sensor_schema(
|
||||
|
||||
@coroutine_with_priority(CoroPriority.AUTOMATION)
|
||||
async def _build_binary_sensor_automations(var, config):
|
||||
for conf in config.get(CONF_ON_PRESS, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_RELEASE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf_key, forwarder in (
|
||||
(CONF_ON_PRESS, automation.TriggerOnTrueForwarder),
|
||||
(CONF_ON_RELEASE, automation.TriggerOnFalseForwarder),
|
||||
):
|
||||
for conf in config.get(conf_key, []):
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_state_callback", [], conf, forwarder=forwarder
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_CLICK, []):
|
||||
trigger = cg.new_Pvariable(
|
||||
@@ -598,13 +572,14 @@ async def _build_binary_sensor_automations(var, config):
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_STATE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(bool, "x")], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_state_callback", [(bool, "x")], conf
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_STATE_CHANGE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger,
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_full_state_callback",
|
||||
[
|
||||
(cg.optional.template(bool), "x_previous"),
|
||||
(cg.optional.template(bool), "x"),
|
||||
|
||||
@@ -124,7 +124,7 @@ def set_reference_values(config):
|
||||
config.setdefault(CONF_VOLTAGE_REFERENCE, DEFAULT_BL0940_LEGACY_UREF)
|
||||
config.setdefault(CONF_CURRENT_REFERENCE, DEFAULT_BL0940_LEGACY_IREF)
|
||||
config.setdefault(CONF_POWER_REFERENCE, DEFAULT_BL0940_LEGACY_PREF)
|
||||
config.setdefault(CONF_ENERGY_REFERENCE, DEFAULT_BL0940_LEGACY_PREF)
|
||||
config.setdefault(CONF_ENERGY_REFERENCE, DEFAULT_BL0940_LEGACY_EREF)
|
||||
else:
|
||||
vref = config.get(CONF_VOLTAGE_REFERENCE, DEFAULT_BL0940_VREF)
|
||||
r_one = config.get(CONF_RESISTOR_ONE, DEFAULT_BL0940_R1)
|
||||
|
||||
@@ -469,14 +469,18 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float &
|
||||
}
|
||||
|
||||
bool BMP581Component::reset_() {
|
||||
// - activates interface (only relevant for SPI mode)
|
||||
// - writes reset command to the command register
|
||||
// - waits for sensor to complete reset
|
||||
// - activates interface (only relevant for SPI mode)
|
||||
// - returns the Power-On-Reboot interrupt status, which is asserted if successful
|
||||
|
||||
// activates communication interface (SPI only)
|
||||
this->activate_interface();
|
||||
|
||||
// writes reset command to BMP's command register
|
||||
if (!this->bmp_write_byte(BMP581_COMMAND, RESET_COMMAND)) {
|
||||
ESP_LOGE(TAG, "Failed to write reset command");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -484,6 +488,9 @@ bool BMP581Component::reset_() {
|
||||
// - round up to 3 ms
|
||||
delay(3);
|
||||
|
||||
// reactivates communication interface after reset (SPI only)
|
||||
this->activate_interface();
|
||||
|
||||
// read interrupt status register
|
||||
if (!this->bmp_read_byte(BMP581_INT_STATUS, &this->int_status_.reg)) {
|
||||
ESP_LOGE(TAG, "Failed to read interrupt status register");
|
||||
@@ -491,7 +498,7 @@ bool BMP581Component::reset_() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Power-On-Reboot bit is asserted if sensor successfully reset
|
||||
// power-On-Reboot bit is asserted if sensor successfully reset
|
||||
return this->int_status_.bit.por;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,9 @@ class BMP581Component : public PollingComponent {
|
||||
virtual bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
|
||||
virtual bool bmp_write_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
|
||||
|
||||
// Interface activation function. Only used for SPI interface; no-op for I2C.
|
||||
virtual void activate_interface() {}
|
||||
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
sensor::Sensor *pressure_sensor_{nullptr};
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include "bmp581_spi.h"
|
||||
#include "esphome/components/bmp581_base/bmp581_base.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
|
||||
namespace esphome::bmp581_spi {
|
||||
|
||||
static const char *const TAG = "bmp581_spi";
|
||||
|
||||
// OR (|) register with BMP_SPI_READ for read
|
||||
inline constexpr uint8_t BMP_SPI_READ = 0x80;
|
||||
|
||||
// AND (&) register with BMP_SPI_WRITE for write
|
||||
inline constexpr uint8_t BMP_SPI_WRITE = 0x7F;
|
||||
|
||||
void BMP581SPIComponent::dump_config() {
|
||||
BMP581Component::dump_config();
|
||||
LOG_SPI_DEVICE(this);
|
||||
}
|
||||
|
||||
void BMP581SPIComponent::setup() {
|
||||
this->spi_setup();
|
||||
BMP581Component::setup();
|
||||
}
|
||||
|
||||
void BMP581SPIComponent::activate_interface() {
|
||||
// - forces the device into SPI mode using a dummy read
|
||||
uint8_t dummy_read = 0;
|
||||
this->bmp_read_byte(bmp581_base::BMP581_CHIP_ID, &dummy_read);
|
||||
}
|
||||
|
||||
// In SPI mode, only 7 bits of the register addresses are used; the MSB of register address is not used
|
||||
// and replaced by a read/write bit (RW = ‘0’ for write and RW = ‘1’ for read).
|
||||
// Example: address 0xF7 is accessed by using SPI register address 0x77. For write access, the byte
|
||||
// 0x77 is transferred, for read access, the byte 0xF7 is transferred.
|
||||
// The expressions BMP_SPI_READ (| with register) and BMP_SPI_WRITE (& with register)
|
||||
// are defined for readability.
|
||||
// https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp581-ds004.pdf
|
||||
|
||||
bool BMP581SPIComponent::bmp_read_byte(uint8_t a_register, uint8_t *data) {
|
||||
this->enable();
|
||||
this->transfer_byte(a_register | BMP_SPI_READ);
|
||||
*data = this->transfer_byte(0);
|
||||
this->disable();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BMP581SPIComponent::bmp_write_byte(uint8_t a_register, uint8_t data) {
|
||||
this->enable();
|
||||
this->transfer_byte(a_register & BMP_SPI_WRITE);
|
||||
this->transfer_byte(data);
|
||||
this->disable();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BMP581SPIComponent::bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
|
||||
this->enable();
|
||||
this->transfer_byte(a_register | BMP_SPI_READ);
|
||||
this->read_array(data, len);
|
||||
this->disable();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BMP581SPIComponent::bmp_write_bytes(uint8_t a_register, uint8_t *data, size_t len) {
|
||||
this->enable();
|
||||
this->transfer_byte(a_register & BMP_SPI_WRITE);
|
||||
this->write_array(data, len);
|
||||
this->disable();
|
||||
return true;
|
||||
}
|
||||
} // namespace esphome::bmp581_spi
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/bmp581_base/bmp581_base.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
|
||||
namespace esphome::bmp581_spi {
|
||||
|
||||
// BMP581 is technically compatible with SPI Mode0 and Mode3. Default to Mode3.
|
||||
class BMP581SPIComponent : public esphome::bmp581_base::BMP581Component,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH,
|
||||
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_200KHZ> {
|
||||
public:
|
||||
void setup() override;
|
||||
bool bmp_read_byte(uint8_t a_register, uint8_t *data) override;
|
||||
bool bmp_write_byte(uint8_t a_register, uint8_t data) override;
|
||||
bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
|
||||
bool bmp_write_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
void activate_interface() override;
|
||||
};
|
||||
|
||||
} // namespace esphome::bmp581_spi
|
||||
@@ -0,0 +1,48 @@
|
||||
import logging
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import spi
|
||||
from esphome.components.spi import CONF_SPI_MODE
|
||||
import esphome.config_validation as cv
|
||||
|
||||
from ..bmp581_base import CONFIG_SCHEMA_BASE, to_code_base
|
||||
|
||||
AUTO_LOAD = ["bmp581_base"]
|
||||
CODEOWNERS = ["@kahrendt", "@danielkent-net"]
|
||||
DEPENDENCIES = ["spi"]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
VALID_SPI_MODES = {
|
||||
0: "MODE0",
|
||||
"0": "MODE0",
|
||||
"MODE0": "MODE0",
|
||||
3: "MODE3",
|
||||
"3": "MODE3",
|
||||
"MODE3": "MODE3",
|
||||
}
|
||||
|
||||
bmp581_ns = cg.esphome_ns.namespace("bmp581_spi")
|
||||
BMP581SPIComponent = bmp581_ns.class_(
|
||||
"BMP581SPIComponent", cg.PollingComponent, spi.SPIDevice
|
||||
)
|
||||
|
||||
|
||||
def check_spi_mode(config):
|
||||
spi_mode = config.get(CONF_SPI_MODE)
|
||||
if spi_mode not in VALID_SPI_MODES:
|
||||
raise cv.Invalid("BMP581 only supports SPI mode 3")
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
CONFIG_SCHEMA_BASE.extend(spi.spi_device_schema(default_mode="mode3")).extend(
|
||||
{cv.GenerateID(): cv.declare_id(BMP581SPIComponent)}
|
||||
),
|
||||
check_spi_mode,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await to_code_base(config)
|
||||
await spi.register_spi_device(var, config)
|
||||
@@ -10,7 +10,6 @@ from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MQTT_ID,
|
||||
CONF_ON_PRESS,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_WEB_SERVER,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_IDENTIFY,
|
||||
@@ -41,10 +40,6 @@ ButtonPtr = Button.operator("ptr")
|
||||
|
||||
PressAction = button_ns.class_("PressAction", automation.Action)
|
||||
|
||||
ButtonPressTrigger = button_ns.class_(
|
||||
"ButtonPressTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||
|
||||
|
||||
@@ -55,11 +50,7 @@ _BUTTON_SCHEMA = (
|
||||
{
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTButtonComponent),
|
||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ButtonPressTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_PRESS): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -91,8 +82,9 @@ def button_schema(
|
||||
@setup_entity("button")
|
||||
async def setup_button_core_(var, config):
|
||||
for conf in config.get(CONF_ON_PRESS, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_press_callback", [], conf
|
||||
)
|
||||
|
||||
setup_device_class(config)
|
||||
|
||||
|
||||
@@ -367,7 +367,7 @@ optional<ClimateDeviceRestoreState> Climate::restore_state_() {
|
||||
return recovered;
|
||||
}
|
||||
|
||||
void Climate::save_state_() {
|
||||
void Climate::save_state_(const ClimateTraits &traits) {
|
||||
#if (defined(USE_ESP32) || (defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(3, 0, 0))) && \
|
||||
!defined(CLANG_TIDY)
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
@@ -382,7 +382,6 @@ void Climate::save_state_() {
|
||||
#endif
|
||||
|
||||
state.mode = this->mode;
|
||||
auto traits = this->get_traits();
|
||||
if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE |
|
||||
CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) {
|
||||
state.target_temperature_low = this->target_temperature_low;
|
||||
@@ -480,7 +479,7 @@ void Climate::publish_state() {
|
||||
ControllerRegistry::notify_climate_update(this);
|
||||
#endif
|
||||
// Save state
|
||||
this->save_state_();
|
||||
this->save_state_(traits);
|
||||
}
|
||||
|
||||
ClimateTraits Climate::get_traits() {
|
||||
|
||||
@@ -335,7 +335,8 @@ class Climate : public EntityBase {
|
||||
/** Internal method to save the state of the climate device to recover memory. This is automatically
|
||||
* called from publish_state()
|
||||
*/
|
||||
void save_state_();
|
||||
void save_state_(const ClimateTraits &traits);
|
||||
void save_state_() { this->save_state_(this->traits()); }
|
||||
|
||||
void dump_traits_(const char *tag);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ CONF_ON_PACKET = "on_packet"
|
||||
CONF_ON_RECEIVE = "on_receive"
|
||||
CONF_ON_STATE_CHANGE = "on_state_change"
|
||||
CONF_PARITY = "parity"
|
||||
CONF_RECEIVER_FREQUENCY = "receiver_frequency"
|
||||
CONF_REQUEST_HEADERS = "request_headers"
|
||||
CONF_ROWS = "rows"
|
||||
CONF_STOP_BITS = "stop_bits"
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace copy {
|
||||
static const char *const TAG = "copy.lock";
|
||||
|
||||
void CopyLock::setup() {
|
||||
source_->add_on_state_callback([this]() { this->publish_state(source_->state); });
|
||||
source_->add_on_state_callback([this](lock::LockState state) { this->publish_state(state); });
|
||||
|
||||
traits.set_assumed_state(source_->traits.get_assumed_state());
|
||||
traits.set_requires_code(source_->traits.get_requires_code());
|
||||
|
||||
@@ -2,16 +2,13 @@ from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_DEVICE, CONF_FILE, CONF_ID, CONF_TRIGGER_ID, CONF_VOLUME
|
||||
from esphome.const import CONF_DEVICE, CONF_FILE, CONF_ID, CONF_VOLUME
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
CODEOWNERS = ["@glmnet"]
|
||||
|
||||
dfplayer_ns = cg.esphome_ns.namespace("dfplayer")
|
||||
DFPlayer = dfplayer_ns.class_("DFPlayer", cg.Component)
|
||||
DFPlayerFinishedPlaybackTrigger = dfplayer_ns.class_(
|
||||
"DFPlayerFinishedPlaybackTrigger", automation.Trigger.template()
|
||||
)
|
||||
DFPlayerIsPlayingCondition = dfplayer_ns.class_(
|
||||
"DFPlayerIsPlayingCondition", automation.Condition
|
||||
)
|
||||
@@ -58,13 +55,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DFPlayer),
|
||||
cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
DFPlayerFinishedPlaybackTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation({}),
|
||||
}
|
||||
).extend(uart.UART_DEVICE_SCHEMA)
|
||||
)
|
||||
@@ -79,8 +70,9 @@ async def to_code(config):
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
for conf in config.get(CONF_ON_FINISHED_PLAYBACK, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_finished_playback_callback", [], conf
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
|
||||
@@ -171,12 +171,5 @@ template<typename... Ts> class DFPlayerIsPlayingCondition : public Condition<Ts.
|
||||
bool check(const Ts &...x) override { return this->parent_->is_playing(); }
|
||||
};
|
||||
|
||||
class DFPlayerFinishedPlaybackTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit DFPlayerFinishedPlaybackTrigger(DFPlayer *parent) {
|
||||
parent->add_on_finished_playback_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dfplayer
|
||||
} // namespace esphome
|
||||
|
||||
@@ -690,9 +690,15 @@ ARDUINO_IDF_VERSION_LOOKUP = {
|
||||
ESP_IDF_FRAMEWORK_VERSION_LOOKUP = {
|
||||
"recommended": cv.Version(5, 5, 3, "1"),
|
||||
"latest": cv.Version(5, 5, 3, "1"),
|
||||
"dev": cv.Version(5, 5, 3, "1"),
|
||||
"dev": cv.Version(5, 5, 4),
|
||||
}
|
||||
ESP_IDF_PLATFORM_VERSION_LOOKUP = {
|
||||
cv.Version(
|
||||
6, 0, 0
|
||||
): "https://github.com/pioarduino/platform-espressif32.git#prep_IDF6",
|
||||
cv.Version(
|
||||
5, 5, 4
|
||||
): "https://github.com/pioarduino/platform-espressif32.git#develop",
|
||||
cv.Version(5, 5, 3, "1"): cv.Version(55, 3, 37),
|
||||
cv.Version(5, 5, 3): cv.Version(55, 3, 37),
|
||||
cv.Version(5, 5, 2): cv.Version(55, 3, 37),
|
||||
@@ -1587,7 +1593,7 @@ async def to_code(config):
|
||||
if conf[CONF_ADVANCED][CONF_ENABLE_FULL_PRINTF]:
|
||||
cg.add_define("USE_FULL_PRINTF")
|
||||
else:
|
||||
for symbol in ("vprintf", "printf", "fprintf"):
|
||||
for symbol in ("vprintf", "printf", "fprintf", "vfprintf"):
|
||||
cg.add_build_flag(f"-Wl,--wrap={symbol}")
|
||||
else:
|
||||
cg.add_build_flag("-DUSE_ARDUINO")
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
* Linker wrap stubs for FILE*-based printf functions.
|
||||
*
|
||||
* ESP-IDF SDK components (gpio driver, ringbuf, log_write) reference
|
||||
* fprintf(), printf(), and vprintf() which pull in newlib's _vfprintf_r
|
||||
* (~11 KB). This is a separate implementation from _svfprintf_r (used by
|
||||
* snprintf/vsnprintf) that handles FILE* stream I/O with buffering and
|
||||
* locking.
|
||||
* fprintf(), printf(), vprintf(), and vfprintf() which pull in the full
|
||||
* printf implementation (~11 KB on newlib's _vfprintf_r, ~2.8 KB on
|
||||
* picolibc's vfprintf). This is a separate implementation from the one
|
||||
* used by snprintf/vsnprintf that handles FILE* stream I/O with buffering
|
||||
* and locking.
|
||||
*
|
||||
* ESPHome replaces the ESP-IDF log handler via esp_log_set_vprintf_(),
|
||||
* so the SDK's vprintf() path is dead code at runtime. The fprintf()
|
||||
@@ -70,11 +71,15 @@ int __wrap_printf(const char *fmt, ...) {
|
||||
return len;
|
||||
}
|
||||
|
||||
int __wrap_vfprintf(FILE *stream, const char *fmt, va_list ap) {
|
||||
char buf[PRINTF_BUFFER_SIZE];
|
||||
return write_printf_buffer(stream, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
|
||||
}
|
||||
|
||||
int __wrap_fprintf(FILE *stream, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char buf[PRINTF_BUFFER_SIZE];
|
||||
int len = write_printf_buffer(stream, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
|
||||
int len = __wrap_vfprintf(stream, fmt, ap);
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
// For V3_WITHOUT_CACHE, we already set fast params before connecting
|
||||
// No need to update them again here
|
||||
this->log_event_("Searching for services");
|
||||
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
|
||||
esp_ble_gattc_search_service(esp_gattc_if, param->open.conn_id, nullptr);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_CONNECT_EVT: {
|
||||
|
||||
@@ -159,7 +159,7 @@ void BLEService::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t g
|
||||
break;
|
||||
}
|
||||
case ESP_GATTS_STOP_EVT: {
|
||||
if (param->start.service_handle == this->handle_) {
|
||||
if (param->stop.service_handle == this->handle_) {
|
||||
this->state_ = STOPPED;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -10,7 +10,6 @@ from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MQTT_ID,
|
||||
CONF_ON_EVENT,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_WEB_SERVER,
|
||||
DEVICE_CLASS_BUTTON,
|
||||
DEVICE_CLASS_DOORBELL,
|
||||
@@ -41,8 +40,6 @@ EventPtr = Event.operator("ptr")
|
||||
|
||||
TriggerEventAction = event_ns.class_("TriggerEventAction", automation.Action)
|
||||
|
||||
EventTrigger = event_ns.class_("EventTrigger", automation.Trigger.template())
|
||||
|
||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||
|
||||
_EVENT_SCHEMA = (
|
||||
@@ -53,11 +50,7 @@ _EVENT_SCHEMA = (
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent),
|
||||
cv.GenerateID(): cv.declare_id(Event),
|
||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||
cv.Optional(CONF_ON_EVENT): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_EVENT): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -92,8 +85,9 @@ def event_schema(
|
||||
@setup_entity("event")
|
||||
async def setup_event_core_(var, config, *, event_types: list[str]):
|
||||
for conf in config.get(CONF_ON_EVENT, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.StringRef, "event_type")], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_event_callback", [(cg.StringRef, "event_type")], conf
|
||||
)
|
||||
|
||||
cg.add(var.set_event_types(event_types))
|
||||
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#pragma once
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "ezo.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ezo {
|
||||
|
||||
class LedTrigger : public Trigger<bool> {
|
||||
public:
|
||||
explicit LedTrigger(EZOSensor *ezo) {
|
||||
ezo->add_led_state_callback([this](bool value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
class CustomTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
explicit CustomTrigger(EZOSensor *ezo) {
|
||||
ezo->add_custom_callback([this](const std::string &value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
class TTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
explicit TTrigger(EZOSensor *ezo) {
|
||||
ezo->add_t_callback([this](const std::string &value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
class CalibrationTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
explicit CalibrationTrigger(EZOSensor *ezo) {
|
||||
ezo->add_calibration_callback([this](const std::string &value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
class SlopeTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
explicit SlopeTrigger(EZOSensor *ezo) {
|
||||
ezo->add_slope_callback([this](const std::string &value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
class DeviceInformationTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
explicit DeviceInformationTrigger(EZOSensor *ezo) {
|
||||
ezo->add_device_infomation_callback([this](const std::string &value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ezo
|
||||
} // namespace esphome
|
||||
@@ -2,7 +2,7 @@ from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_TRIGGER_ID
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@ssieb"]
|
||||
|
||||
@@ -21,61 +21,16 @@ EZOSensor = ezo_ns.class_(
|
||||
"EZOSensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CustomTrigger = ezo_ns.class_(
|
||||
"CustomTrigger", automation.Trigger.template(cg.std_string)
|
||||
)
|
||||
|
||||
|
||||
TTrigger = ezo_ns.class_("TTrigger", automation.Trigger.template(cg.std_string))
|
||||
|
||||
SlopeTrigger = ezo_ns.class_("SlopeTrigger", automation.Trigger.template(cg.std_string))
|
||||
|
||||
CalibrationTrigger = ezo_ns.class_(
|
||||
"CalibrationTrigger", automation.Trigger.template(cg.std_string)
|
||||
)
|
||||
|
||||
DeviceInformationTrigger = ezo_ns.class_(
|
||||
"DeviceInformationTrigger", automation.Trigger.template(cg.std_string)
|
||||
)
|
||||
|
||||
LedTrigger = ezo_ns.class_("LedTrigger", automation.Trigger.template(cg.bool_))
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(EZOSensor)
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(CONF_ON_CUSTOM): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CustomTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_CALIBRATION): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CalibrationTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_SLOPE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SlopeTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_T): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_DEVICE_INFORMATION): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
DeviceInformationTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_LED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LedTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_CUSTOM): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_CALIBRATION): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_SLOPE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_T): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_DEVICE_INFORMATION): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_LED): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
@@ -90,25 +45,26 @@ async def to_code(config):
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
for conf in config.get(CONF_ON_CUSTOM, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_custom_callback", [(cg.std_string, "x")], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_LED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(bool, "x")], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_led_state_callback", [(bool, "x")], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_DEVICE_INFORMATION, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_device_infomation_callback", [(cg.std_string, "x")], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_SLOPE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_slope_callback", [(cg.std_string, "x")], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_CALIBRATION, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_calibration_callback", [(cg.std_string, "x")], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_T, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_t_callback", [(cg.std_string, "x")], conf
|
||||
)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
from esphome.automation import Trigger, build_automation, validate_automation
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.esp8266 import CONF_RESTORE_FROM_FLASH, KEY_ESP8266
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_TRIGGER_ID,
|
||||
PLATFORM_BK72XX,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
@@ -18,7 +17,6 @@ CODEOWNERS = ["@anatoly-savchenkov"]
|
||||
|
||||
factory_reset_ns = cg.esphome_ns.namespace("factory_reset")
|
||||
FactoryResetComponent = factory_reset_ns.class_("FactoryResetComponent", cg.Component)
|
||||
FastBootTrigger = factory_reset_ns.class_("FastBootTrigger", Trigger, cg.Component)
|
||||
|
||||
CONF_MAX_DELAY = "max_delay"
|
||||
CONF_RESETS_REQUIRED = "resets_required"
|
||||
@@ -55,11 +53,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
),
|
||||
),
|
||||
cv.Optional(CONF_RESETS_REQUIRED): cv.positive_not_null_int,
|
||||
cv.Optional(CONF_ON_INCREMENT): validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FastBootTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_INCREMENT): automation.validate_automation({}),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
_validate,
|
||||
@@ -88,12 +82,9 @@ async def to_code(config):
|
||||
)
|
||||
await cg.register_component(var, config)
|
||||
for conf in config.get(CONF_ON_INCREMENT, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await build_automation(
|
||||
trigger,
|
||||
[
|
||||
(cg.uint8, "x"),
|
||||
(cg.uint8, "target"),
|
||||
],
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_increment_callback",
|
||||
[(cg.uint8, "x"), (cg.uint8, "target")],
|
||||
conf,
|
||||
)
|
||||
|
||||
@@ -30,12 +30,6 @@ class FactoryResetComponent : public Component {
|
||||
uint8_t required_count_; // The number of boot attempts before fast boot is enabled
|
||||
};
|
||||
|
||||
class FastBootTrigger : public Trigger<uint8_t, uint8_t> {
|
||||
public:
|
||||
explicit FastBootTrigger(FactoryResetComponent *parent) {
|
||||
parent->add_increment_callback([this](uint8_t current, uint8_t target) { this->trigger(current, target); });
|
||||
}
|
||||
};
|
||||
} // namespace esphome::factory_reset
|
||||
|
||||
#endif // !defined(USE_RP2040) && !defined(USE_HOST)
|
||||
|
||||
@@ -21,7 +21,6 @@ from esphome.const import (
|
||||
CONF_SENSING_PIN,
|
||||
CONF_SPEED,
|
||||
CONF_STATE,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@OnFreund", "@loongyh", "@alexborro"]
|
||||
@@ -38,38 +37,6 @@ FingerprintGrowComponent = fingerprint_grow_ns.class_(
|
||||
"FingerprintGrowComponent", cg.PollingComponent, uart.UARTDevice
|
||||
)
|
||||
|
||||
FingerScanStartTrigger = fingerprint_grow_ns.class_(
|
||||
"FingerScanStartTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
FingerScanMatchedTrigger = fingerprint_grow_ns.class_(
|
||||
"FingerScanMatchedTrigger", automation.Trigger.template(cg.uint16, cg.uint16)
|
||||
)
|
||||
|
||||
FingerScanUnmatchedTrigger = fingerprint_grow_ns.class_(
|
||||
"FingerScanUnmatchedTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
FingerScanMisplacedTrigger = fingerprint_grow_ns.class_(
|
||||
"FingerScanMisplacedTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
FingerScanInvalidTrigger = fingerprint_grow_ns.class_(
|
||||
"FingerScanInvalidTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
EnrollmentScanTrigger = fingerprint_grow_ns.class_(
|
||||
"EnrollmentScanTrigger", automation.Trigger.template(cg.uint8, cg.uint16)
|
||||
)
|
||||
|
||||
EnrollmentDoneTrigger = fingerprint_grow_ns.class_(
|
||||
"EnrollmentDoneTrigger", automation.Trigger.template(cg.uint16)
|
||||
)
|
||||
|
||||
EnrollmentFailedTrigger = fingerprint_grow_ns.class_(
|
||||
"EnrollmentFailedTrigger", automation.Trigger.template(cg.uint16)
|
||||
)
|
||||
|
||||
EnrollmentAction = fingerprint_grow_ns.class_("EnrollmentAction", automation.Action)
|
||||
CancelEnrollmentAction = fingerprint_grow_ns.class_(
|
||||
"CancelEnrollmentAction", automation.Action
|
||||
@@ -125,62 +92,22 @@ CONFIG_SCHEMA = cv.All(
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_PASSWORD): cv.uint32_t,
|
||||
cv.Optional(CONF_NEW_PASSWORD): cv.uint32_t,
|
||||
cv.Optional(CONF_ON_FINGER_SCAN_START): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FingerScanStartTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINGER_SCAN_START): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_FINGER_SCAN_MATCHED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FingerScanMatchedTrigger
|
||||
),
|
||||
}
|
||||
{}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINGER_SCAN_UNMATCHED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FingerScanUnmatchedTrigger
|
||||
),
|
||||
}
|
||||
{}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINGER_SCAN_MISPLACED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FingerScanMisplacedTrigger
|
||||
),
|
||||
}
|
||||
{}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINGER_SCAN_INVALID): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FingerScanInvalidTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_SCAN): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
EnrollmentScanTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_DONE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
EnrollmentDoneTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_FAILED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
EnrollmentFailedTrigger
|
||||
),
|
||||
}
|
||||
{}
|
||||
),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_SCAN): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_DONE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_FAILED): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("500ms"))
|
||||
@@ -214,40 +141,43 @@ async def to_code(config):
|
||||
cg.add(var.set_idle_period_to_sleep_ms(idle_period_to_sleep_ms))
|
||||
|
||||
for conf in config.get(CONF_ON_FINGER_SCAN_START, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_finger_scan_start_callback", [], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_FINGER_SCAN_MATCHED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.uint16, "finger_id"), (cg.uint16, "confidence")], conf
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_on_finger_scan_matched_callback",
|
||||
[(cg.uint16, "finger_id"), (cg.uint16, "confidence")],
|
||||
conf,
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_FINGER_SCAN_UNMATCHED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_finger_scan_unmatched_callback", [], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_FINGER_SCAN_MISPLACED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_finger_scan_misplaced_callback", [], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_FINGER_SCAN_INVALID, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_finger_scan_invalid_callback", [], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_ENROLLMENT_SCAN, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.uint8, "scan_num"), (cg.uint16, "finger_id")], conf
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_on_enrollment_scan_callback",
|
||||
[(cg.uint8, "scan_num"), (cg.uint16, "finger_id")],
|
||||
conf,
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_ENROLLMENT_DONE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.uint16, "finger_id")], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_enrollment_done_callback", [(cg.uint16, "finger_id")], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_ENROLLMENT_FAILED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.uint16, "finger_id")], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_enrollment_failed_callback", [(cg.uint16, "finger_id")], conf
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
|
||||
@@ -210,64 +210,6 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
|
||||
CallbackManager<void(uint16_t)> enrollment_failed_callback_;
|
||||
};
|
||||
|
||||
class FingerScanStartTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit FingerScanStartTrigger(FingerprintGrowComponent *parent) {
|
||||
parent->add_on_finger_scan_start_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class FingerScanMatchedTrigger : public Trigger<uint16_t, uint16_t> {
|
||||
public:
|
||||
explicit FingerScanMatchedTrigger(FingerprintGrowComponent *parent) {
|
||||
parent->add_on_finger_scan_matched_callback(
|
||||
[this](uint16_t finger_id, uint16_t confidence) { this->trigger(finger_id, confidence); });
|
||||
}
|
||||
};
|
||||
|
||||
class FingerScanUnmatchedTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit FingerScanUnmatchedTrigger(FingerprintGrowComponent *parent) {
|
||||
parent->add_on_finger_scan_unmatched_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class FingerScanMisplacedTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit FingerScanMisplacedTrigger(FingerprintGrowComponent *parent) {
|
||||
parent->add_on_finger_scan_misplaced_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class FingerScanInvalidTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit FingerScanInvalidTrigger(FingerprintGrowComponent *parent) {
|
||||
parent->add_on_finger_scan_invalid_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class EnrollmentScanTrigger : public Trigger<uint8_t, uint16_t> {
|
||||
public:
|
||||
explicit EnrollmentScanTrigger(FingerprintGrowComponent *parent) {
|
||||
parent->add_on_enrollment_scan_callback(
|
||||
[this](uint8_t scan_num, uint16_t finger_id) { this->trigger(scan_num, finger_id); });
|
||||
}
|
||||
};
|
||||
|
||||
class EnrollmentDoneTrigger : public Trigger<uint16_t> {
|
||||
public:
|
||||
explicit EnrollmentDoneTrigger(FingerprintGrowComponent *parent) {
|
||||
parent->add_on_enrollment_done_callback([this](uint16_t finger_id) { this->trigger(finger_id); });
|
||||
}
|
||||
};
|
||||
|
||||
class EnrollmentFailedTrigger : public Trigger<uint16_t> {
|
||||
public:
|
||||
explicit EnrollmentFailedTrigger(FingerprintGrowComponent *parent) {
|
||||
parent->add_on_enrollment_failed_callback([this](uint16_t finger_id) { this->trigger(finger_id); });
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class EnrollmentAction : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, finger_id)
|
||||
|
||||
@@ -22,7 +22,6 @@ from esphome.const import (
|
||||
CONF_SUPPORTED_SWING_MODES,
|
||||
CONF_TARGET_TEMPERATURE,
|
||||
CONF_TEMPERATURE_STEP,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_VISUAL,
|
||||
CONF_WIFI,
|
||||
)
|
||||
@@ -122,21 +121,6 @@ SUPPORTED_HON_CONTROL_METHODS = {
|
||||
"SET_SINGLE_PARAMETER": HonControlMethod.SET_SINGLE_PARAMETER,
|
||||
}
|
||||
|
||||
HaierAlarmStartTrigger = haier_ns.class_(
|
||||
"HaierAlarmStartTrigger",
|
||||
automation.Trigger.template(cg.uint8, cg.const_char_ptr),
|
||||
)
|
||||
|
||||
HaierAlarmEndTrigger = haier_ns.class_(
|
||||
"HaierAlarmEndTrigger",
|
||||
automation.Trigger.template(cg.uint8, cg.const_char_ptr),
|
||||
)
|
||||
|
||||
StatusMessageTrigger = haier_ns.class_(
|
||||
"StatusMessageTrigger",
|
||||
automation.Trigger.template(cg.const_char_ptr, cg.size_t),
|
||||
)
|
||||
|
||||
|
||||
def validate_visual(config):
|
||||
if CONF_VISUAL in config:
|
||||
@@ -203,13 +187,7 @@ def _base_config_schema(class_: MockObjClass) -> cv.Schema:
|
||||
cv.Optional(
|
||||
CONF_ANSWER_TIMEOUT,
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_ON_STATUS_MESSAGE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
StatusMessageTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_STATUS_MESSAGE): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
@@ -264,19 +242,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
f"The {CONF_OUTDOOR_TEMPERATURE} option is deprecated, use a sensor for a haier platform instead"
|
||||
),
|
||||
cv.Optional(CONF_ON_ALARM_START): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
HaierAlarmStartTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ALARM_END): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
HaierAlarmEndTrigger
|
||||
),
|
||||
}
|
||||
{}
|
||||
),
|
||||
cv.Optional(CONF_ON_ALARM_END): automation.validate_automation({}),
|
||||
}
|
||||
),
|
||||
},
|
||||
@@ -530,19 +498,25 @@ async def to_code(config):
|
||||
var.set_status_message_header_size(config[CONF_STATUS_MESSAGE_HEADER_SIZE])
|
||||
)
|
||||
for conf in config.get(CONF_ON_ALARM_START, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.uint8, "code"), (cg.const_char_ptr, "message")], conf
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_alarm_start_callback",
|
||||
[(cg.uint8, "code"), (cg.const_char_ptr, "message")],
|
||||
conf,
|
||||
)
|
||||
for conf in config.get(CONF_ON_ALARM_END, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.uint8, "code"), (cg.const_char_ptr, "message")], conf
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_alarm_end_callback",
|
||||
[(cg.uint8, "code"), (cg.const_char_ptr, "message")],
|
||||
conf,
|
||||
)
|
||||
for conf in config.get(CONF_ON_STATUS_MESSAGE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.const_char_ptr, "data"), (cg.size_t, "data_size")], conf
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_status_message_callback",
|
||||
[(cg.const_char_ptr, "data"), (cg.size_t, "data_size")],
|
||||
conf,
|
||||
)
|
||||
# https://github.com/paveldn/HaierProtocol
|
||||
cg.add_library("pavlodn/HaierProtocol", "0.9.31")
|
||||
|
||||
@@ -177,12 +177,5 @@ class HaierClimateBase : public esphome::Component,
|
||||
ESPPreferenceObject base_rtc_;
|
||||
};
|
||||
|
||||
class StatusMessageTrigger : public Trigger<const char *, size_t> {
|
||||
public:
|
||||
explicit StatusMessageTrigger(HaierClimateBase *parent) {
|
||||
parent->add_status_message_callback([this](const char *data, size_t data_size) { this->trigger(data, data_size); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace haier
|
||||
} // namespace esphome
|
||||
|
||||
@@ -200,21 +200,5 @@ class HonClimate : public HaierClimateBase {
|
||||
SwitchState quiet_mode_state_{SwitchState::OFF};
|
||||
};
|
||||
|
||||
class HaierAlarmStartTrigger : public Trigger<uint8_t, const char *> {
|
||||
public:
|
||||
explicit HaierAlarmStartTrigger(HonClimate *parent) {
|
||||
parent->add_alarm_start_callback(
|
||||
[this](uint8_t alarm_code, const char *alarm_message) { this->trigger(alarm_code, alarm_message); });
|
||||
}
|
||||
};
|
||||
|
||||
class HaierAlarmEndTrigger : public Trigger<uint8_t, const char *> {
|
||||
public:
|
||||
explicit HaierAlarmEndTrigger(HonClimate *parent) {
|
||||
parent->add_alarm_end_callback(
|
||||
[this](uint8_t alarm_code, const char *alarm_message) { this->trigger(alarm_code, alarm_message); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace haier
|
||||
} // namespace esphome
|
||||
|
||||
@@ -8,7 +8,6 @@ from esphome.const import (
|
||||
CONF_NAME,
|
||||
CONF_ON_ENROLLMENT_DONE,
|
||||
CONF_ON_ENROLLMENT_FAILED,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@OnFreund"]
|
||||
@@ -28,33 +27,6 @@ HlkFm22xComponent = hlk_fm22x_ns.class_(
|
||||
"HlkFm22xComponent", cg.PollingComponent, uart.UARTDevice
|
||||
)
|
||||
|
||||
FaceScanMatchedTrigger = hlk_fm22x_ns.class_(
|
||||
"FaceScanMatchedTrigger", automation.Trigger.template(cg.int16, cg.std_string)
|
||||
)
|
||||
|
||||
FaceScanUnmatchedTrigger = hlk_fm22x_ns.class_(
|
||||
"FaceScanUnmatchedTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
FaceScanInvalidTrigger = hlk_fm22x_ns.class_(
|
||||
"FaceScanInvalidTrigger", automation.Trigger.template(cg.uint8)
|
||||
)
|
||||
|
||||
FaceInfoTrigger = hlk_fm22x_ns.class_(
|
||||
"FaceInfoTrigger",
|
||||
automation.Trigger.template(
|
||||
cg.int16, cg.int16, cg.int16, cg.int16, cg.int16, cg.int16, cg.int16, cg.int16
|
||||
),
|
||||
)
|
||||
|
||||
EnrollmentDoneTrigger = hlk_fm22x_ns.class_(
|
||||
"EnrollmentDoneTrigger", automation.Trigger.template(cg.int16, cg.uint8)
|
||||
)
|
||||
|
||||
EnrollmentFailedTrigger = hlk_fm22x_ns.class_(
|
||||
"EnrollmentFailedTrigger", automation.Trigger.template(cg.uint8)
|
||||
)
|
||||
|
||||
EnrollmentAction = hlk_fm22x_ns.class_("EnrollmentAction", automation.Action)
|
||||
DeleteAction = hlk_fm22x_ns.class_("DeleteAction", automation.Action)
|
||||
DeleteAllAction = hlk_fm22x_ns.class_("DeleteAllAction", automation.Action)
|
||||
@@ -65,46 +37,14 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HlkFm22xComponent),
|
||||
cv.Optional(CONF_ON_FACE_SCAN_MATCHED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FaceScanMatchedTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FACE_SCAN_MATCHED): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_FACE_SCAN_UNMATCHED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FaceScanUnmatchedTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FACE_SCAN_INVALID): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FaceScanInvalidTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FACE_INFO): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FaceInfoTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_DONE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
EnrollmentDoneTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_FAILED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
EnrollmentFailedTrigger
|
||||
),
|
||||
}
|
||||
{}
|
||||
),
|
||||
cv.Optional(CONF_ON_FACE_SCAN_INVALID): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_FACE_INFO): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_DONE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_FAILED): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("50ms"))
|
||||
@@ -118,23 +58,27 @@ async def to_code(config):
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
for conf in config.get(CONF_ON_FACE_SCAN_MATCHED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.int16, "face_id"), (cg.std_string, "name")], conf
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_on_face_scan_matched_callback",
|
||||
[(cg.int16, "face_id"), (cg.std_string, "name")],
|
||||
conf,
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_FACE_SCAN_UNMATCHED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_face_scan_unmatched_callback", [], conf
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_FACE_SCAN_INVALID, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.uint8, "error")], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_face_scan_invalid_callback", [(cg.uint8, "error")], conf
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_FACE_INFO, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger,
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_on_face_info_callback",
|
||||
[
|
||||
(cg.int16, "status"),
|
||||
(cg.int16, "left"),
|
||||
@@ -149,14 +93,17 @@ async def to_code(config):
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_ENROLLMENT_DONE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.int16, "face_id"), (cg.uint8, "direction")], conf
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_on_enrollment_done_callback",
|
||||
[(cg.int16, "face_id"), (cg.uint8, "direction")],
|
||||
conf,
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_ENROLLMENT_FAILED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.uint8, "error")], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_enrollment_failed_callback", [(cg.uint8, "error")], conf
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
|
||||
@@ -141,52 +141,6 @@ class HlkFm22xComponent : public PollingComponent, public uart::UARTDevice {
|
||||
CallbackManager<void(uint8_t)> enrollment_failed_callback_;
|
||||
};
|
||||
|
||||
class FaceScanMatchedTrigger : public Trigger<int16_t, std::string> {
|
||||
public:
|
||||
explicit FaceScanMatchedTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_face_scan_matched_callback(
|
||||
[this](int16_t face_id, const std::string &name) { this->trigger(face_id, name); });
|
||||
}
|
||||
};
|
||||
|
||||
class FaceScanUnmatchedTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit FaceScanUnmatchedTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_face_scan_unmatched_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class FaceScanInvalidTrigger : public Trigger<uint8_t> {
|
||||
public:
|
||||
explicit FaceScanInvalidTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_face_scan_invalid_callback([this](uint8_t error) { this->trigger(error); });
|
||||
}
|
||||
};
|
||||
|
||||
class FaceInfoTrigger : public Trigger<int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t> {
|
||||
public:
|
||||
explicit FaceInfoTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_face_info_callback(
|
||||
[this](int16_t status, int16_t left, int16_t top, int16_t right, int16_t bottom, int16_t yaw, int16_t pitch,
|
||||
int16_t roll) { this->trigger(status, left, top, right, bottom, yaw, pitch, roll); });
|
||||
}
|
||||
};
|
||||
|
||||
class EnrollmentDoneTrigger : public Trigger<int16_t, uint8_t> {
|
||||
public:
|
||||
explicit EnrollmentDoneTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_enrollment_done_callback(
|
||||
[this](int16_t face_id, uint8_t direction) { this->trigger(face_id, direction); });
|
||||
}
|
||||
};
|
||||
|
||||
class EnrollmentFailedTrigger : public Trigger<uint8_t> {
|
||||
public:
|
||||
explicit EnrollmentFailedTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_enrollment_failed_callback([this](uint8_t error) { this->trigger(error); });
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class EnrollmentAction : public Action<Ts...>, public Parented<HlkFm22xComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(std::string, name)
|
||||
|
||||
@@ -101,9 +101,13 @@ class InfraredTraits {
|
||||
bool get_supports_receiver() const { return this->supports_receiver_; }
|
||||
void set_supports_receiver(bool supports) { this->supports_receiver_ = supports; }
|
||||
|
||||
uint32_t get_receiver_frequency_hz() const { return this->receiver_frequency_hz_; }
|
||||
void set_receiver_frequency_hz(uint32_t freq) { this->receiver_frequency_hz_ = freq; }
|
||||
|
||||
protected:
|
||||
bool supports_transmitter_{false};
|
||||
bool supports_receiver_{false};
|
||||
uint32_t receiver_frequency_hz_{0}; // Demodulation frequency of the IR receiver in Hz (0 = unspecified)
|
||||
};
|
||||
|
||||
/// Infrared - Base class for infrared remote control implementations
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Any
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import infrared, remote_receiver, remote_transmitter
|
||||
from esphome.components.const import CONF_RECEIVER_FREQUENCY
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_FREQUENCY
|
||||
import esphome.final_validate as fv
|
||||
@@ -19,6 +20,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
infrared.infrared_schema(IrRfProxy).extend(
|
||||
{
|
||||
cv.Optional(CONF_FREQUENCY, default=0): cv.frequency,
|
||||
cv.Optional(CONF_RECEIVER_FREQUENCY): cv.frequency,
|
||||
cv.Optional(CONF_REMOTE_RECEIVER_ID): cv.use_id(
|
||||
remote_receiver.RemoteReceiverComponent
|
||||
),
|
||||
@@ -33,7 +35,14 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
def _final_validate(config: dict[str, Any]) -> None:
|
||||
"""Validate that transmitters have a proper carrier duty cycle."""
|
||||
# Only validate if this is an infrared (not RF) configuration with a transmitter
|
||||
# receiver_frequency is only meaningful for receiver configurations
|
||||
if CONF_RECEIVER_FREQUENCY in config and CONF_REMOTE_RECEIVER_ID not in config:
|
||||
raise cv.Invalid(
|
||||
f"'{CONF_RECEIVER_FREQUENCY}' can only be used with '{CONF_REMOTE_RECEIVER_ID}', "
|
||||
"not with a transmitter"
|
||||
)
|
||||
|
||||
# Only validate duty cycle if this is an infrared (not RF) configuration with a transmitter
|
||||
if config.get(CONF_FREQUENCY, 0) != 0 or CONF_REMOTE_TRANSMITTER_ID not in config:
|
||||
return
|
||||
|
||||
@@ -75,3 +84,7 @@ async def to_code(config: dict[str, Any]) -> None:
|
||||
if CONF_REMOTE_RECEIVER_ID in config:
|
||||
receiver = await cg.get_variable(config[CONF_REMOTE_RECEIVER_ID])
|
||||
cg.add(var.set_receiver(receiver))
|
||||
|
||||
# Set receiver demodulation frequency if specified (metadata only, no hardware effect)
|
||||
if CONF_RECEIVER_FREQUENCY in config:
|
||||
cg.add(var.set_receiver_frequency(config[CONF_RECEIVER_FREQUENCY]))
|
||||
|
||||
@@ -22,6 +22,9 @@ class IrRfProxy : public infrared::Infrared {
|
||||
/// Check if this is RF mode (non-zero frequency)
|
||||
bool is_rf() const { return this->frequency_khz_ > 0; }
|
||||
|
||||
/// Set the receiver's hardware demodulation frequency in Hz (metadata only, does not affect hardware)
|
||||
void set_receiver_frequency(uint32_t frequency_hz) { this->get_traits().set_receiver_frequency_hz(frequency_hz); }
|
||||
|
||||
protected:
|
||||
// RF frequency in kHz (Hz / 1000); 0 = infrared, non-zero = RF
|
||||
uint32_t frequency_khz_{0};
|
||||
|
||||
@@ -2,7 +2,7 @@ from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_ON_DATA, CONF_THROTTLE, CONF_TRIGGER_ID
|
||||
from esphome.const import CONF_ID, CONF_ON_DATA, CONF_THROTTLE
|
||||
|
||||
AUTO_LOAD = ["ld24xx"]
|
||||
DEPENDENCIES = ["uart"]
|
||||
@@ -12,7 +12,6 @@ MULTI_CONF = True
|
||||
ld2450_ns = cg.esphome_ns.namespace("ld2450")
|
||||
LD2450Component = ld2450_ns.class_("LD2450Component", cg.Component, uart.UARTDevice)
|
||||
|
||||
LD2450DataTrigger = ld2450_ns.class_("LD2450DataTrigger", automation.Trigger.template())
|
||||
|
||||
CONF_LD2450_ID = "ld2450_id"
|
||||
|
||||
@@ -23,11 +22,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_THROTTLE): cv.invalid(
|
||||
f"{CONF_THROTTLE} has been removed; use per-sensor filters, instead"
|
||||
),
|
||||
cv.Optional(CONF_ON_DATA): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LD2450DataTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_DATA): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
@@ -54,5 +49,6 @@ async def to_code(config):
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
for conf in config.get(CONF_ON_DATA, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_data_callback", [], conf
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/component.h"
|
||||
#ifdef USE_SENSOR
|
||||
@@ -201,11 +200,4 @@ class LD2450Component : public Component, public uart::UARTDevice {
|
||||
LazyCallbackManager<void()> data_callback_;
|
||||
};
|
||||
|
||||
class LD2450DataTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit LD2450DataTrigger(LD2450Component *parent) {
|
||||
parent->add_on_data_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::ld2450
|
||||
|
||||
@@ -24,6 +24,7 @@ from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INITIAL_STATE,
|
||||
CONF_MQTT_ID,
|
||||
CONF_NAME,
|
||||
CONF_ON_STATE,
|
||||
CONF_ON_TURN_OFF,
|
||||
CONF_ON_TURN_ON,
|
||||
@@ -41,6 +42,8 @@ from esphome.const import (
|
||||
from esphome.core import CORE, ID, CoroPriority, HexInt, Lambda, coroutine_with_priority
|
||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .automation import LIGHT_STATE_SCHEMA
|
||||
from .effects import (
|
||||
@@ -70,9 +73,19 @@ IS_PLATFORM_COMPONENT = True
|
||||
DOMAIN = "light"
|
||||
|
||||
|
||||
@dataclass
|
||||
class EffectRef:
|
||||
"""A pending effect name reference from a light action to validate."""
|
||||
|
||||
light_id: ID
|
||||
effect_name: str
|
||||
component_path: list[str | int] # path_context when the action was validated
|
||||
|
||||
|
||||
@dataclass
|
||||
class LightData:
|
||||
gamma_tables: dict = field(default_factory=dict) # gamma_value -> fwd_arr
|
||||
effect_refs: list[EffectRef] = field(default_factory=list)
|
||||
|
||||
|
||||
def _get_data() -> LightData:
|
||||
@@ -115,6 +128,68 @@ def _get_or_create_gamma_table(gamma_correct):
|
||||
return fwd_arr
|
||||
|
||||
|
||||
def find_effect_index(effects: list, effect_name: str) -> int | None:
|
||||
"""Find the 1-based index of an effect by name (case-insensitive).
|
||||
|
||||
Returns the 1-based index if found, or None if not found.
|
||||
"""
|
||||
effect_name_lower = effect_name.lower()
|
||||
for i, effect_conf in enumerate(effects):
|
||||
key = next(iter(effect_conf))
|
||||
if effect_conf[key][CONF_NAME].lower() == effect_name_lower:
|
||||
return i + 1
|
||||
return None
|
||||
|
||||
|
||||
def available_effects_str(effects: list) -> str:
|
||||
"""Return a comma-separated string of available effect names."""
|
||||
available = [
|
||||
effect_conf[next(iter(effect_conf))][CONF_NAME] for effect_conf in effects
|
||||
]
|
||||
return ", ".join(f"'{name}'" for name in available) if available else "none"
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> ConfigType:
|
||||
"""Validate all recorded effect name references against their target lights.
|
||||
|
||||
This runs once per light platform instance. If no light platform is configured,
|
||||
this never runs — but the ID validator will catch the missing light ID separately.
|
||||
"""
|
||||
data = _get_data()
|
||||
if not data.effect_refs:
|
||||
return config
|
||||
|
||||
# Drain the list so we only validate once even though
|
||||
# FINAL_VALIDATE_SCHEMA runs for each light platform instance.
|
||||
refs = data.effect_refs
|
||||
data.effect_refs = []
|
||||
|
||||
fconf = fv.full_config.get()
|
||||
|
||||
for ref in refs:
|
||||
try:
|
||||
light_path = fconf.get_path_for_id(ref.light_id)[:-1]
|
||||
light_config = fconf.get_config_for_path(light_path)
|
||||
except KeyError:
|
||||
# Light ID not found — ID validation will have already reported this
|
||||
continue
|
||||
|
||||
effects = light_config.get(CONF_EFFECTS, [])
|
||||
|
||||
if find_effect_index(effects, ref.effect_name) is None:
|
||||
raise cv.FinalExternalInvalid(
|
||||
f"Effect '{ref.effect_name}' not found for light "
|
||||
f"'{ref.light_id}'. "
|
||||
f"Available effects: {available_effects_str(effects)}",
|
||||
path=[cv.ROOT_CONFIG_PATH] + ref.component_path,
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
LightRestoreMode = light_ns.enum("LightRestoreMode")
|
||||
RESTORE_MODES = {
|
||||
"RESTORE_DEFAULT_OFF": LightRestoreMode.LIGHT_RESTORE_DEFAULT_OFF,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.config import path_context
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BLUE,
|
||||
@@ -17,7 +18,6 @@ from esphome.const import (
|
||||
CONF_LIMIT_MODE,
|
||||
CONF_MAX_BRIGHTNESS,
|
||||
CONF_MIN_BRIGHTNESS,
|
||||
CONF_NAME,
|
||||
CONF_RANGE_FROM,
|
||||
CONF_RANGE_TO,
|
||||
CONF_RED,
|
||||
@@ -26,7 +26,7 @@ from esphome.const import (
|
||||
CONF_WARM_WHITE,
|
||||
CONF_WHITE,
|
||||
)
|
||||
from esphome.core import CORE, Lambda
|
||||
from esphome.core import CORE, EsphomeError, Lambda
|
||||
from esphome.cpp_generator import LambdaExpression
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -98,6 +98,31 @@ LIGHT_CONTROL_ACTION_SCHEMA = LIGHT_STATE_SCHEMA.extend(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _record_effect_ref(config: ConfigType) -> ConfigType:
|
||||
"""Record a static effect name reference for later cross-component validation."""
|
||||
if CONF_EFFECT not in config:
|
||||
return config
|
||||
effect = config[CONF_EFFECT]
|
||||
if isinstance(effect, Lambda):
|
||||
return config # Lambda effects resolved at runtime
|
||||
if effect.lower() == "none":
|
||||
return config # "None" is always valid
|
||||
|
||||
from . import EffectRef, _get_data
|
||||
|
||||
_get_data().effect_refs.append(
|
||||
EffectRef(
|
||||
light_id=config[CONF_ID],
|
||||
effect_name=effect,
|
||||
component_path=path_context.get(),
|
||||
)
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
LIGHT_CONTROL_ACTION_SCHEMA.add_extra(_record_effect_ref)
|
||||
|
||||
LIGHT_TURN_OFF_ACTION_SCHEMA = automation.maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(LightState),
|
||||
@@ -122,18 +147,24 @@ def _resolve_effect_index(config: ConfigType) -> int:
|
||||
Effect index 0 means "None" (no effect). Effects are 1-indexed matching
|
||||
the C++ convention in LightState.
|
||||
"""
|
||||
from . import available_effects_str, find_effect_index
|
||||
|
||||
original_name = config[CONF_EFFECT]
|
||||
effect_name = original_name.lower()
|
||||
if effect_name == "none":
|
||||
if original_name.lower() == "none":
|
||||
return 0
|
||||
light_id = config[CONF_ID]
|
||||
light_path = CORE.config.get_path_for_id(light_id)[:-1]
|
||||
light_config = CORE.config.get_config_for_path(light_path)
|
||||
for i, effect_conf in enumerate(light_config.get(CONF_EFFECTS, [])):
|
||||
key = next(iter(effect_conf))
|
||||
if effect_conf[key][CONF_NAME].lower() == effect_name:
|
||||
return i + 1
|
||||
raise ValueError(f"Effect '{original_name}' not found in light '{light_id}'")
|
||||
effects = light_config.get(CONF_EFFECTS, [])
|
||||
index = find_effect_index(effects, original_name)
|
||||
if index is not None:
|
||||
return index
|
||||
# Should never reach here — effect names are validated during config
|
||||
# validation in FINAL_VALIDATE_SCHEMA. This is a safety net.
|
||||
raise EsphomeError(
|
||||
f"Effect '{original_name}' not found for light '{light_id}'. "
|
||||
f"Available effects: {available_effects_str(effects)}"
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
|
||||
@@ -385,7 +385,7 @@ void LightCall::transform_parameters_() {
|
||||
!(this->color_mode_ & ColorCapability::WHITE) && //
|
||||
!(this->color_mode_ & ColorCapability::COLOR_TEMPERATURE) && //
|
||||
min_mireds > 0.0f && max_mireds > 0.0f) {
|
||||
ESP_LOGD(TAG, "'%s': setting cold/warm white channels using white/color temperature values",
|
||||
ESP_LOGV(TAG, "'%s': setting cold/warm white channels using white/color temperature values",
|
||||
this->parent_->get_name().c_str());
|
||||
// Only compute cold_white/warm_white from color_temperature if they're not already explicitly set.
|
||||
// This is important for state restoration, where both color_temperature and cold_white/warm_white
|
||||
@@ -432,7 +432,7 @@ ColorMode LightCall::compute_color_mode_() {
|
||||
|
||||
// Don't change if the current mode is in the intersection (suitable AND supported)
|
||||
if (ColorModeMask::mask_contains(intersection, current_mode)) {
|
||||
ESP_LOGI(TAG, "'%s': color mode not specified; retaining %s", this->parent_->get_name().c_str(),
|
||||
ESP_LOGV(TAG, "'%s': color mode not specified; retaining %s", this->parent_->get_name().c_str(),
|
||||
LOG_STR_ARG(color_mode_to_human(current_mode)));
|
||||
return current_mode;
|
||||
}
|
||||
@@ -440,7 +440,7 @@ ColorMode LightCall::compute_color_mode_() {
|
||||
// Use the preferred suitable mode.
|
||||
if (intersection != 0) {
|
||||
ColorMode mode = ColorModeMask::first_value_from_mask(intersection);
|
||||
ESP_LOGI(TAG, "'%s': color mode not specified; using %s", this->parent_->get_name().c_str(),
|
||||
ESP_LOGV(TAG, "'%s': color mode not specified; using %s", this->parent_->get_name().c_str(),
|
||||
LOG_STR_ARG(color_mode_to_human(mode)));
|
||||
return mode;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ from esphome.const import (
|
||||
CONF_MQTT_ID,
|
||||
CONF_ON_LOCK,
|
||||
CONF_ON_UNLOCK,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_WEB_SERVER,
|
||||
)
|
||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||
@@ -31,8 +30,7 @@ OpenAction = lock_ns.class_("OpenAction", automation.Action)
|
||||
LockPublishAction = lock_ns.class_("LockPublishAction", automation.Action)
|
||||
|
||||
LockCondition = lock_ns.class_("LockCondition", Condition)
|
||||
LockLockTrigger = lock_ns.class_("LockLockTrigger", automation.Trigger.template())
|
||||
LockUnlockTrigger = lock_ns.class_("LockUnlockTrigger", automation.Trigger.template())
|
||||
LockStateForwarder = lock_ns.class_("LockStateForwarder")
|
||||
|
||||
LockState = lock_ns.enum("LockState")
|
||||
|
||||
@@ -52,16 +50,8 @@ _LOCK_SCHEMA = (
|
||||
.extend(
|
||||
{
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTLockComponent),
|
||||
cv.Optional(CONF_ON_LOCK): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LockLockTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_UNLOCK): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LockUnlockTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_LOCK): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_UNLOCK): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -93,12 +83,18 @@ def lock_schema(
|
||||
|
||||
@setup_entity("lock")
|
||||
async def _setup_lock_core(var, config):
|
||||
for conf in config.get(CONF_ON_LOCK, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf in config.get(CONF_ON_UNLOCK, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf_key, state_enum in (
|
||||
(CONF_ON_LOCK, LockState.LOCK_STATE_LOCKED),
|
||||
(CONF_ON_UNLOCK, LockState.LOCK_STATE_UNLOCKED),
|
||||
):
|
||||
for conf in config.get(conf_key, []):
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_on_state_callback",
|
||||
[],
|
||||
conf,
|
||||
forwarder=LockStateForwarder.template(state_enum),
|
||||
)
|
||||
|
||||
if mqtt_id := config.get(CONF_MQTT_ID):
|
||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||
|
||||
@@ -49,21 +49,17 @@ template<typename... Ts> class LockCondition : public Condition<Ts...> {
|
||||
bool state_;
|
||||
};
|
||||
|
||||
template<LockState State> class LockStateTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit LockStateTrigger(Lock *a_lock) : lock_(a_lock) {
|
||||
a_lock->add_on_state_callback([this]() {
|
||||
if (this->lock_->state == State) {
|
||||
this->trigger();
|
||||
}
|
||||
});
|
||||
/// Callback forwarder that triggers an Automation<> only when a specific lock state is entered.
|
||||
/// Pointer-sized (single Automation* field) to fit inline in Callback::ctx_.
|
||||
template<LockState State> struct LockStateForwarder {
|
||||
Automation<> *automation;
|
||||
void operator()(LockState state) const {
|
||||
if (state == State)
|
||||
this->automation->trigger();
|
||||
}
|
||||
|
||||
protected:
|
||||
Lock *lock_;
|
||||
};
|
||||
|
||||
using LockLockTrigger = LockStateTrigger<LockState::LOCK_STATE_LOCKED>;
|
||||
using LockUnlockTrigger = LockStateTrigger<LockState::LOCK_STATE_UNLOCKED>;
|
||||
static_assert(sizeof(LockStateForwarder<LockState::LOCK_STATE_LOCKED>) <= sizeof(void *));
|
||||
static_assert(std::is_trivially_copyable_v<LockStateForwarder<LockState::LOCK_STATE_LOCKED>>);
|
||||
|
||||
} // namespace esphome::lock
|
||||
|
||||
@@ -42,7 +42,7 @@ void Lock::publish_state(LockState state) {
|
||||
this->state = state;
|
||||
this->rtc_.save(&this->state);
|
||||
ESP_LOGV(TAG, "'%s' >> %s", this->name_.c_str(), LOG_STR_ARG(lock_state_to_string(state)));
|
||||
this->state_callback_.call();
|
||||
this->state_callback_.call(state);
|
||||
#if defined(USE_LOCK) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_lock_update(this);
|
||||
#endif
|
||||
|
||||
@@ -148,7 +148,7 @@ class Lock : public EntityBase {
|
||||
|
||||
/** Set callback for state changes.
|
||||
*
|
||||
* @param callback The void(bool) callback.
|
||||
* @param callback The void(LockState) callback.
|
||||
*/
|
||||
template<typename F> void add_on_state_callback(F &&callback) {
|
||||
this->state_callback_.add(std::forward<F>(callback));
|
||||
@@ -178,7 +178,7 @@ class Lock : public EntityBase {
|
||||
*/
|
||||
virtual void control(const LockCall &call) = 0;
|
||||
|
||||
LazyCallbackManager<void()> state_callback_{};
|
||||
LazyCallbackManager<void(LockState)> state_callback_{};
|
||||
Deduplicator<LockState> publish_dedup_;
|
||||
ESPPreferenceObject rtc_;
|
||||
};
|
||||
|
||||
@@ -243,6 +243,16 @@ void Logger::dump_config() {
|
||||
#endif
|
||||
#ifdef USE_ZEPHYR
|
||||
dump_crash_();
|
||||
#endif
|
||||
// Warn users that VERBOSE/VERY_VERBOSE logging impacts performance.
|
||||
// Only the compiled log level matters — all log calls up to this level
|
||||
// are in the binary and will be formatted (vsnprintf) and block UART.
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
ESP_LOGW(TAG, "VERY_VERBOSE logging is active — significant performance impact, short-term debugging only\n"
|
||||
" May cause connection instability. Set log level to DEBUG or lower for long-term use.");
|
||||
#elif ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
ESP_LOGI(TAG, "VERBOSE logging is active — performance impact, short-term debugging only\n"
|
||||
" Set log level to DEBUG or lower for long-term use.");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,14 @@ class LTRAlsPs501Component : public PollingComponent, public i2c::I2CDevice {
|
||||
void set_actual_integration_time_sensor(sensor::Sensor *sensor) { this->actual_integration_time_sensor_ = sensor; }
|
||||
void set_proximity_counts_sensor(sensor::Sensor *sensor) { this->proximity_counts_sensor_ = sensor; }
|
||||
|
||||
template<typename F> void add_on_ps_high_trigger_callback(F &&callback) {
|
||||
this->on_ps_high_trigger_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
template<typename F> void add_on_ps_low_trigger_callback(F &&callback) {
|
||||
this->on_ps_low_trigger_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
protected:
|
||||
//
|
||||
// Internal state machine, used to split all the actions into
|
||||
@@ -151,36 +159,8 @@ class LTRAlsPs501Component : public PollingComponent, public i2c::I2CDevice {
|
||||
}
|
||||
bool is_any_ps_sensor_enabled_() const { return this->proximity_counts_sensor_ != nullptr; }
|
||||
|
||||
//
|
||||
// Trigger section for the automations
|
||||
//
|
||||
friend class LTRPsHighTrigger;
|
||||
friend class LTRPsLowTrigger;
|
||||
|
||||
CallbackManager<void()> on_ps_high_trigger_callback_;
|
||||
CallbackManager<void()> on_ps_low_trigger_callback_;
|
||||
|
||||
template<typename F> void add_on_ps_high_trigger_callback_(F &&callback) {
|
||||
this->on_ps_high_trigger_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
template<typename F> void add_on_ps_low_trigger_callback_(F &&callback) {
|
||||
this->on_ps_low_trigger_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
};
|
||||
|
||||
class LTRPsHighTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit LTRPsHighTrigger(LTRAlsPs501Component *parent) {
|
||||
parent->add_on_ps_high_trigger_callback_([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class LTRPsLowTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit LTRPsLowTrigger(LTRAlsPs501Component *parent) {
|
||||
parent->add_on_ps_low_trigger_callback_([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
} // namespace ltr501
|
||||
} // namespace esphome
|
||||
|
||||
@@ -14,7 +14,6 @@ from esphome.const import (
|
||||
CONF_INTEGRATION_TIME,
|
||||
CONF_NAME,
|
||||
CONF_REPEAT,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TYPE,
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
@@ -87,9 +86,6 @@ PS_GAINS = {
|
||||
"16X": PsGain.PS_GAIN_16,
|
||||
}
|
||||
|
||||
LTRPsHighTrigger = ltr501_ns.class_("LTRPsHighTrigger", automation.Trigger.template())
|
||||
LTRPsLowTrigger = ltr501_ns.class_("LTRPsLowTrigger", automation.Trigger.template())
|
||||
|
||||
|
||||
def validate_integration_time(value):
|
||||
value = cv.positive_time_period_milliseconds(value).total_milliseconds
|
||||
@@ -146,16 +142,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_PS_LOW_THRESHOLD, default=0): cv.int_range(
|
||||
min=0, max=65535
|
||||
),
|
||||
cv.Optional(CONF_ON_PS_HIGH_THRESHOLD): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LTRPsHighTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_PS_LOW_THRESHOLD): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LTRPsLowTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_PS_HIGH_THRESHOLD): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_PS_LOW_THRESHOLD): automation.validate_automation({}),
|
||||
cv.Optional(CONF_AMBIENT_LIGHT): cv.maybe_simple_value(
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_LUX,
|
||||
@@ -252,13 +240,14 @@ async def to_code(config):
|
||||
sens = await sensor.new_sensor(prox_cnt_config)
|
||||
cg.add(var.set_proximity_counts_sensor(sens))
|
||||
|
||||
for prox_high_tr in config.get(CONF_ON_PS_HIGH_THRESHOLD, []):
|
||||
trigger = cg.new_Pvariable(prox_high_tr[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], prox_high_tr)
|
||||
|
||||
for prox_low_tr in config.get(CONF_ON_PS_LOW_THRESHOLD, []):
|
||||
trigger = cg.new_Pvariable(prox_low_tr[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], prox_low_tr)
|
||||
for conf in config.get(CONF_ON_PS_HIGH_THRESHOLD, []):
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_ps_high_trigger_callback", [], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_PS_LOW_THRESHOLD, []):
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_ps_low_trigger_callback", [], conf
|
||||
)
|
||||
|
||||
cg.add(var.set_ltr_type(config[CONF_TYPE]))
|
||||
|
||||
|
||||
@@ -58,6 +58,14 @@ class LTRAlsPsComponent : public PollingComponent, public i2c::I2CDevice {
|
||||
void set_actual_integration_time_sensor(sensor::Sensor *sensor) { this->actual_integration_time_sensor_ = sensor; }
|
||||
void set_proximity_counts_sensor(sensor::Sensor *sensor) { this->proximity_counts_sensor_ = sensor; }
|
||||
|
||||
template<typename F> void add_on_ps_high_trigger_callback(F &&callback) {
|
||||
this->on_ps_high_trigger_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
template<typename F> void add_on_ps_low_trigger_callback(F &&callback) {
|
||||
this->on_ps_low_trigger_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
protected:
|
||||
//
|
||||
// Internal state machine, used to split all the actions into
|
||||
@@ -151,36 +159,8 @@ class LTRAlsPsComponent : public PollingComponent, public i2c::I2CDevice {
|
||||
}
|
||||
bool is_any_ps_sensor_enabled_() const { return this->proximity_counts_sensor_ != nullptr; }
|
||||
|
||||
//
|
||||
// Trigger section for the automations
|
||||
//
|
||||
friend class LTRPsHighTrigger;
|
||||
friend class LTRPsLowTrigger;
|
||||
|
||||
CallbackManager<void()> on_ps_high_trigger_callback_;
|
||||
CallbackManager<void()> on_ps_low_trigger_callback_;
|
||||
|
||||
template<typename F> void add_on_ps_high_trigger_callback_(F &&callback) {
|
||||
this->on_ps_high_trigger_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
template<typename F> void add_on_ps_low_trigger_callback_(F &&callback) {
|
||||
this->on_ps_low_trigger_callback_.add(std::forward<F>(callback));
|
||||
}
|
||||
};
|
||||
|
||||
class LTRPsHighTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit LTRPsHighTrigger(LTRAlsPsComponent *parent) {
|
||||
parent->add_on_ps_high_trigger_callback_([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class LTRPsLowTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit LTRPsLowTrigger(LTRAlsPsComponent *parent) {
|
||||
parent->add_on_ps_low_trigger_callback_([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
} // namespace ltr_als_ps
|
||||
} // namespace esphome
|
||||
|
||||
@@ -14,7 +14,6 @@ from esphome.const import (
|
||||
CONF_INTEGRATION_TIME,
|
||||
CONF_NAME,
|
||||
CONF_REPEAT,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TYPE,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
ICON_BRIGHTNESS_5,
|
||||
@@ -93,11 +92,6 @@ PS_GAINS = {
|
||||
"64X": PsGain.PS_GAIN_64,
|
||||
}
|
||||
|
||||
LTRPsHighTrigger = ltr_als_ps_ns.class_(
|
||||
"LTRPsHighTrigger", automation.Trigger.template()
|
||||
)
|
||||
LTRPsLowTrigger = ltr_als_ps_ns.class_("LTRPsLowTrigger", automation.Trigger.template())
|
||||
|
||||
|
||||
def validate_integration_time(value):
|
||||
value = cv.positive_time_period_milliseconds(value).total_milliseconds
|
||||
@@ -143,16 +137,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_PS_LOW_THRESHOLD, default=0): cv.int_range(
|
||||
min=0, max=65535
|
||||
),
|
||||
cv.Optional(CONF_ON_PS_HIGH_THRESHOLD): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LTRPsHighTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_PS_LOW_THRESHOLD): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LTRPsLowTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_PS_HIGH_THRESHOLD): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_PS_LOW_THRESHOLD): automation.validate_automation({}),
|
||||
cv.Optional(CONF_AMBIENT_LIGHT): cv.maybe_simple_value(
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_LUX,
|
||||
@@ -244,13 +230,14 @@ async def to_code(config):
|
||||
sens = await sensor.new_sensor(prox_cnt_config)
|
||||
cg.add(var.set_proximity_counts_sensor(sens))
|
||||
|
||||
for prox_high_tr in config.get(CONF_ON_PS_HIGH_THRESHOLD, []):
|
||||
trigger = cg.new_Pvariable(prox_high_tr[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], prox_high_tr)
|
||||
|
||||
for prox_low_tr in config.get(CONF_ON_PS_LOW_THRESHOLD, []):
|
||||
trigger = cg.new_Pvariable(prox_low_tr[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], prox_low_tr)
|
||||
for conf in config.get(CONF_ON_PS_HIGH_THRESHOLD, []):
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_ps_high_trigger_callback", [], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_PS_LOW_THRESHOLD, []):
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_ps_low_trigger_callback", [], conf
|
||||
)
|
||||
|
||||
cg.add(var.set_ltr_type(config[CONF_TYPE]))
|
||||
|
||||
|
||||
@@ -8,17 +8,17 @@ namespace max44009 {
|
||||
static const char *const TAG = "max44009.sensor";
|
||||
|
||||
// REGISTERS
|
||||
static const uint8_t MAX44009_REGISTER_CONFIGURATION = 0x02;
|
||||
static const uint8_t MAX44009_LUX_READING_HIGH = 0x03;
|
||||
static const uint8_t MAX44009_LUX_READING_LOW = 0x04;
|
||||
static constexpr uint8_t MAX44009_REGISTER_CONFIGURATION = 0x02;
|
||||
static constexpr uint8_t MAX44009_LUX_READING_HIGH = 0x03;
|
||||
static constexpr uint8_t MAX44009_LUX_READING_LOW = 0x04;
|
||||
// CONFIGURATION MASKS
|
||||
static const uint8_t MAX44009_CFG_CONTINUOUS = 0x80;
|
||||
static constexpr uint8_t MAX44009_CFG_CONTINUOUS = 0x80;
|
||||
// ERROR CODES
|
||||
static const uint8_t MAX44009_OK = 0;
|
||||
static const uint8_t MAX44009_ERROR_WIRE_REQUEST = -10;
|
||||
static const uint8_t MAX44009_ERROR_OVERFLOW = -20;
|
||||
static const uint8_t MAX44009_ERROR_HIGH_BYTE = -30;
|
||||
static const uint8_t MAX44009_ERROR_LOW_BYTE = -31;
|
||||
static constexpr int8_t MAX44009_OK = 0;
|
||||
static constexpr int8_t MAX44009_ERROR_WIRE_REQUEST = -10;
|
||||
static constexpr int8_t MAX44009_ERROR_OVERFLOW = -20;
|
||||
static constexpr int8_t MAX44009_ERROR_HIGH_BYTE = -30;
|
||||
static constexpr int8_t MAX44009_ERROR_LOW_BYTE = -31;
|
||||
|
||||
void MAX44009Sensor::setup() {
|
||||
bool state_ok = false;
|
||||
|
||||
@@ -28,8 +28,8 @@ class MAX44009Sensor : public sensor::Sensor, public PollingComponent, public i2
|
||||
uint8_t read_(uint8_t reg);
|
||||
void write_(uint8_t reg, uint8_t value);
|
||||
|
||||
int error_;
|
||||
MAX44009Mode mode_;
|
||||
int8_t error_{0};
|
||||
MAX44009Mode mode_{MAX44009_MODE_AUTO};
|
||||
};
|
||||
|
||||
} // namespace max44009
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace mcp23008 {
|
||||
|
||||
static const char *const TAG = "mcp23008";
|
||||
|
||||
static constexpr uint8_t IOCON_ODR = 0x04; // Open-drain output for INT pin
|
||||
|
||||
void MCP23008::setup() {
|
||||
uint8_t iocon;
|
||||
if (!this->read_reg(mcp23x08_base::MCP23X08_IOCON, &iocon)) {
|
||||
@@ -18,7 +20,7 @@ void MCP23008::setup() {
|
||||
|
||||
if (this->open_drain_ints_) {
|
||||
// enable open-drain interrupt pins, 3.3V-safe
|
||||
this->write_reg(mcp23x08_base::MCP23X08_IOCON, 0x04);
|
||||
this->write_reg(mcp23x08_base::MCP23X08_IOCON, iocon | IOCON_ODR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace mcp23017 {
|
||||
|
||||
static const char *const TAG = "mcp23017";
|
||||
|
||||
static constexpr uint8_t IOCON_ODR = 0x04; // Open-drain output for INT pin
|
||||
|
||||
void MCP23017::setup() {
|
||||
uint8_t iocon;
|
||||
if (!this->read_reg(mcp23x17_base::MCP23X17_IOCONA, &iocon)) {
|
||||
@@ -19,8 +21,8 @@ void MCP23017::setup() {
|
||||
|
||||
if (this->open_drain_ints_) {
|
||||
// enable open-drain interrupt pins, 3.3V-safe
|
||||
this->write_reg(mcp23x17_base::MCP23X17_IOCONA, 0x04);
|
||||
this->write_reg(mcp23x17_base::MCP23X17_IOCONB, 0x04);
|
||||
this->write_reg(mcp23x17_base::MCP23X17_IOCONA, iocon | IOCON_ODR);
|
||||
this->write_reg(mcp23x17_base::MCP23X17_IOCONB, iocon | IOCON_ODR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,11 @@ namespace mcp23s08 {
|
||||
|
||||
static const char *const TAG = "mcp23s08";
|
||||
|
||||
// IOCON register bits
|
||||
static constexpr uint8_t IOCON_SEQOP = 0x20; // Sequential operation mode
|
||||
static constexpr uint8_t IOCON_HAEN = 0x08; // Hardware address enable
|
||||
static constexpr uint8_t IOCON_ODR = 0x04; // Open-drain output for INT pin
|
||||
|
||||
void MCP23S08::set_device_address(uint8_t device_addr) {
|
||||
if (device_addr != 0) {
|
||||
this->device_opcode_ |= ((device_addr & 0x03) << 1);
|
||||
@@ -15,19 +20,19 @@ void MCP23S08::set_device_address(uint8_t device_addr) {
|
||||
void MCP23S08::setup() {
|
||||
this->spi_setup();
|
||||
|
||||
// Enable HAEN (broadcast to all chips since HAEN isn't active yet)
|
||||
this->enable();
|
||||
uint8_t cmd = 0b01000000;
|
||||
this->transfer_byte(cmd);
|
||||
this->transfer_byte(0b01000000);
|
||||
this->transfer_byte(mcp23x08_base::MCP23X08_IOCON);
|
||||
this->transfer_byte(0b00011000); // Enable HAEN pins for addressing
|
||||
this->transfer_byte(IOCON_SEQOP | IOCON_HAEN);
|
||||
this->disable();
|
||||
|
||||
// Read current output register state
|
||||
this->read_reg(mcp23x08_base::MCP23X08_OLAT, &this->olat_);
|
||||
|
||||
if (this->open_drain_ints_) {
|
||||
// enable open-drain interrupt pins, 3.3V-safe
|
||||
this->write_reg(mcp23x08_base::MCP23X08_IOCON, 0x04);
|
||||
// enable open-drain interrupt pins, 3.3V-safe (addressed, only this chip)
|
||||
this->write_reg(mcp23x08_base::MCP23X08_IOCON, IOCON_SEQOP | IOCON_HAEN | IOCON_ODR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,11 @@ namespace mcp23s17 {
|
||||
|
||||
static const char *const TAG = "mcp23s17";
|
||||
|
||||
// IOCON register bits
|
||||
static constexpr uint8_t IOCON_SEQOP = 0x20; // Sequential operation mode
|
||||
static constexpr uint8_t IOCON_HAEN = 0x08; // Hardware address enable
|
||||
static constexpr uint8_t IOCON_ODR = 0x04; // Open-drain output for INT pin
|
||||
|
||||
void MCP23S17::set_device_address(uint8_t device_addr) {
|
||||
if (device_addr != 0) {
|
||||
this->device_opcode_ |= ((device_addr & 0b111) << 1);
|
||||
@@ -15,18 +20,17 @@ void MCP23S17::set_device_address(uint8_t device_addr) {
|
||||
void MCP23S17::setup() {
|
||||
this->spi_setup();
|
||||
|
||||
// Enable HAEN (broadcast to addresses 0 and 4 since HAEN isn't active yet)
|
||||
this->enable();
|
||||
uint8_t cmd = 0b01000000;
|
||||
this->transfer_byte(cmd);
|
||||
this->transfer_byte(0b01000000);
|
||||
this->transfer_byte(mcp23x17_base::MCP23X17_IOCONA);
|
||||
this->transfer_byte(0b00011000); // Enable HAEN pins for addressing
|
||||
this->transfer_byte(IOCON_SEQOP | IOCON_HAEN);
|
||||
this->disable();
|
||||
|
||||
this->enable();
|
||||
cmd = 0b01001000;
|
||||
this->transfer_byte(cmd);
|
||||
this->transfer_byte(0b01001000);
|
||||
this->transfer_byte(mcp23x17_base::MCP23X17_IOCONA);
|
||||
this->transfer_byte(0b00011000); // Enable HAEN pins for addressing
|
||||
this->transfer_byte(IOCON_SEQOP | IOCON_HAEN);
|
||||
this->disable();
|
||||
|
||||
// Read current output register state
|
||||
@@ -34,9 +38,9 @@ void MCP23S17::setup() {
|
||||
this->read_reg(mcp23x17_base::MCP23X17_OLATB, &this->olat_b_);
|
||||
|
||||
if (this->open_drain_ints_) {
|
||||
// enable open-drain interrupt pins, 3.3V-safe
|
||||
this->write_reg(mcp23x17_base::MCP23X17_IOCONA, 0x04);
|
||||
this->write_reg(mcp23x17_base::MCP23X17_IOCONB, 0x04);
|
||||
// enable open-drain interrupt pins, 3.3V-safe (addressed, only this chip)
|
||||
this->write_reg(mcp23x17_base::MCP23X17_IOCONA, IOCON_SEQOP | IOCON_HAEN | IOCON_ODR);
|
||||
this->write_reg(mcp23x17_base::MCP23X17_IOCONB, IOCON_SEQOP | IOCON_HAEN | IOCON_ODR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ from esphome.const import (
|
||||
CONF_ON_STATE,
|
||||
CONF_ON_TURN_OFF,
|
||||
CONF_ON_TURN_ON,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_VOLUME,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
@@ -65,15 +64,19 @@ _COMMAND_ACTIONS = [
|
||||
"clear_playlist",
|
||||
]
|
||||
|
||||
# State triggers: (config_key, C++ class name)
|
||||
StateAnyForwarder = media_player_ns.class_("StateAnyForwarder")
|
||||
StateEnterForwarder = media_player_ns.class_("StateEnterForwarder")
|
||||
MediaPlayerState = media_player_ns.enum("MediaPlayerState")
|
||||
|
||||
# State triggers: (config_key, state enum or None for any-state)
|
||||
_STATE_TRIGGERS = [
|
||||
(CONF_ON_STATE, "StateTrigger"),
|
||||
(CONF_ON_IDLE, "IdleTrigger"),
|
||||
(CONF_ON_PLAY, "PlayTrigger"),
|
||||
(CONF_ON_PAUSE, "PauseTrigger"),
|
||||
(CONF_ON_ANNOUNCEMENT, "AnnouncementTrigger"),
|
||||
(CONF_ON_TURN_ON, "OnTrigger"),
|
||||
(CONF_ON_TURN_OFF, "OffTrigger"),
|
||||
(CONF_ON_STATE, None),
|
||||
(CONF_ON_IDLE, MediaPlayerState.MEDIA_PLAYER_STATE_IDLE),
|
||||
(CONF_ON_PLAY, MediaPlayerState.MEDIA_PLAYER_STATE_PLAYING),
|
||||
(CONF_ON_PAUSE, MediaPlayerState.MEDIA_PLAYER_STATE_PAUSED),
|
||||
(CONF_ON_ANNOUNCEMENT, MediaPlayerState.MEDIA_PLAYER_STATE_ANNOUNCING),
|
||||
(CONF_ON_TURN_ON, MediaPlayerState.MEDIA_PLAYER_STATE_ON),
|
||||
(CONF_ON_TURN_OFF, MediaPlayerState.MEDIA_PLAYER_STATE_OFF),
|
||||
]
|
||||
|
||||
# State conditions that all share the same schema and codegen handler
|
||||
@@ -98,10 +101,15 @@ VolumeSetAction = media_player_ns.class_(
|
||||
|
||||
@setup_entity("media_player")
|
||||
async def setup_media_player_core_(var, config):
|
||||
for conf_key, _ in _STATE_TRIGGERS:
|
||||
for conf_key, state_enum in _STATE_TRIGGERS:
|
||||
for conf in config.get(conf_key, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
if state_enum is None:
|
||||
forwarder = StateAnyForwarder
|
||||
else:
|
||||
forwarder = StateEnterForwarder.template(state_enum)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_state_callback", [], conf, forwarder=forwarder
|
||||
)
|
||||
|
||||
|
||||
async def register_media_player(var, config):
|
||||
@@ -120,14 +128,8 @@ async def new_media_player(config, *args):
|
||||
|
||||
_MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(conf_key): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
media_player_ns.class_(class_name, automation.Trigger.template())
|
||||
),
|
||||
}
|
||||
)
|
||||
for conf_key, class_name in _STATE_TRIGGERS
|
||||
cv.Optional(conf_key): automation.validate_automation({})
|
||||
for conf_key, _ in _STATE_TRIGGERS
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -71,32 +71,27 @@ template<typename... Ts> class VolumeSetAction : public Action<Ts...>, public Pa
|
||||
void play(const Ts &...x) override { this->parent_->make_call().set_volume(this->volume_.value(x...)).perform(); }
|
||||
};
|
||||
|
||||
class StateTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit StateTrigger(MediaPlayer *player) {
|
||||
player->add_on_state_callback([this]() { this->trigger(); });
|
||||
/// Callback forwarder that triggers an Automation<> on any state change.
|
||||
/// Pointer-sized (single Automation* field) to fit inline in Callback::ctx_.
|
||||
struct StateAnyForwarder {
|
||||
Automation<> *automation;
|
||||
void operator()(MediaPlayerState /*state*/) const { this->automation->trigger(); }
|
||||
};
|
||||
|
||||
/// Callback forwarder that triggers an Automation<> only when a specific media player state is entered.
|
||||
/// Pointer-sized (single Automation* field) to fit inline in Callback::ctx_.
|
||||
template<MediaPlayerState State> struct StateEnterForwarder {
|
||||
Automation<> *automation;
|
||||
void operator()(MediaPlayerState state) const {
|
||||
if (state == State)
|
||||
this->automation->trigger();
|
||||
}
|
||||
};
|
||||
|
||||
template<MediaPlayerState State> class MediaPlayerStateTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit MediaPlayerStateTrigger(MediaPlayer *player) : player_(player) {
|
||||
player->add_on_state_callback([this]() {
|
||||
if (this->player_->state == State)
|
||||
this->trigger();
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
MediaPlayer *player_;
|
||||
};
|
||||
|
||||
using IdleTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_IDLE>;
|
||||
using PlayTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING>;
|
||||
using PauseTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_PAUSED>;
|
||||
using AnnouncementTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING>;
|
||||
using OnTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_ON>;
|
||||
using OffTrigger = MediaPlayerStateTrigger<MediaPlayerState::MEDIA_PLAYER_STATE_OFF>;
|
||||
static_assert(sizeof(StateAnyForwarder) <= sizeof(void *));
|
||||
static_assert(std::is_trivially_copyable_v<StateAnyForwarder>);
|
||||
static_assert(sizeof(StateEnterForwarder<MediaPlayerState::MEDIA_PLAYER_STATE_IDLE>) <= sizeof(void *));
|
||||
static_assert(std::is_trivially_copyable_v<StateEnterForwarder<MediaPlayerState::MEDIA_PLAYER_STATE_IDLE>>);
|
||||
|
||||
template<typename... Ts> class IsIdleCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
|
||||
public:
|
||||
|
||||
@@ -199,7 +199,7 @@ MediaPlayerCall &MediaPlayerCall::set_announcement(bool announce) {
|
||||
}
|
||||
|
||||
void MediaPlayer::publish_state() {
|
||||
this->state_callback_.call();
|
||||
this->state_callback_.call(this->state);
|
||||
#if defined(USE_MEDIA_PLAYER) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_media_player_update(this);
|
||||
#endif
|
||||
|
||||
@@ -168,7 +168,7 @@ class MediaPlayer : public EntityBase {
|
||||
|
||||
virtual void control(const MediaPlayerCall &call) = 0;
|
||||
|
||||
LazyCallbackManager<void()> state_callback_{};
|
||||
LazyCallbackManager<void(MediaPlayerState)> state_callback_{};
|
||||
};
|
||||
|
||||
} // namespace media_player
|
||||
|
||||
@@ -126,21 +126,21 @@ void MMC5603Component::update() {
|
||||
int32_t raw_x = 0;
|
||||
raw_x |= buffer[0] << 12;
|
||||
raw_x |= buffer[1] << 4;
|
||||
raw_x |= buffer[2] << 0;
|
||||
raw_x |= buffer[2] & 0x0F;
|
||||
|
||||
const float x = 0.00625 * (raw_x - 524288);
|
||||
|
||||
int32_t raw_y = 0;
|
||||
raw_y |= buffer[3] << 12;
|
||||
raw_y |= buffer[4] << 4;
|
||||
raw_y |= buffer[5] << 0;
|
||||
raw_y |= buffer[5] & 0x0F;
|
||||
|
||||
const float y = 0.00625 * (raw_y - 524288);
|
||||
|
||||
int32_t raw_z = 0;
|
||||
raw_z |= buffer[6] << 12;
|
||||
raw_z |= buffer[7] << 4;
|
||||
raw_z |= buffer[8] << 0;
|
||||
raw_z |= buffer[8] & 0x0F;
|
||||
|
||||
const float z = 0.00625 * (raw_z - 524288);
|
||||
|
||||
|
||||
@@ -5,14 +5,7 @@ import esphome.codegen as cg
|
||||
from esphome.components import modbus
|
||||
from esphome.components.const import CONF_ENABLED
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_NAME,
|
||||
CONF_OFFSET,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_OFFSET
|
||||
from esphome.cpp_helpers import logging
|
||||
|
||||
from .const import (
|
||||
@@ -135,17 +128,6 @@ CPP_TYPE_REGISTER_MAP = {
|
||||
"FP32_R": cg.float_,
|
||||
}
|
||||
|
||||
ModbusCommandSentTrigger = modbus_controller_ns.class_(
|
||||
"ModbusCommandSentTrigger", automation.Trigger.template(cg.int_, cg.int_)
|
||||
)
|
||||
|
||||
ModbusOnlineTrigger = modbus_controller_ns.class_(
|
||||
"ModbusOnlineTrigger", automation.Trigger.template(cg.int_, cg.int_)
|
||||
)
|
||||
|
||||
ModbusOfflineTrigger = modbus_controller_ns.class_(
|
||||
"ModbusOfflineTrigger", automation.Trigger.template(cg.int_, cg.int_)
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -182,23 +164,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(
|
||||
CONF_SERVER_REGISTERS,
|
||||
): cv.ensure_list(ModbusServerRegisterSchema),
|
||||
cv.Optional(CONF_ON_COMMAND_SENT): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
ModbusCommandSentTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ONLINE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ModbusOnlineTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_OFFLINE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ModbusOfflineTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_COMMAND_SENT): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ONLINE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_OFFLINE): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
@@ -363,19 +331,25 @@ async def to_code(config):
|
||||
cg.add(var.add_server_register(server_register_var))
|
||||
await register_modbus_device(var, config)
|
||||
for conf in config.get(CONF_ON_COMMAND_SENT, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.int_, "function_code"), (cg.int_, "address")], conf
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_on_command_sent_callback",
|
||||
[(cg.int_, "function_code"), (cg.int_, "address")],
|
||||
conf,
|
||||
)
|
||||
for conf in config.get(CONF_ON_ONLINE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.int_, "function_code"), (cg.int_, "address")], conf
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_on_online_callback",
|
||||
[(cg.int_, "function_code"), (cg.int_, "address")],
|
||||
conf,
|
||||
)
|
||||
for conf in config.get(CONF_ON_OFFLINE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.int_, "function_code"), (cg.int_, "address")], conf
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_on_offline_callback",
|
||||
[(cg.int_, "function_code"), (cg.int_, "address")],
|
||||
conf,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/modbus_controller/modbus_controller.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace modbus_controller {
|
||||
|
||||
class ModbusCommandSentTrigger : public Trigger<int, int> {
|
||||
public:
|
||||
ModbusCommandSentTrigger(ModbusController *a_modbuscontroller) {
|
||||
a_modbuscontroller->add_on_command_sent_callback(
|
||||
[this](int function_code, int address) { this->trigger(function_code, address); });
|
||||
}
|
||||
};
|
||||
|
||||
class ModbusOnlineTrigger : public Trigger<int, int> {
|
||||
public:
|
||||
ModbusOnlineTrigger(ModbusController *a_modbuscontroller) {
|
||||
a_modbuscontroller->add_on_online_callback(
|
||||
[this](int function_code, int address) { this->trigger(function_code, address); });
|
||||
}
|
||||
};
|
||||
|
||||
class ModbusOfflineTrigger : public Trigger<int, int> {
|
||||
public:
|
||||
ModbusOfflineTrigger(ModbusController *a_modbuscontroller) {
|
||||
a_modbuscontroller->add_on_offline_callback(
|
||||
[this](int function_code, int address) { this->trigger(function_code, address); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace modbus_controller
|
||||
} // namespace esphome
|
||||
@@ -15,7 +15,7 @@ class ModbusFloatOutput : public output::FloatOutput, public Component, public S
|
||||
this->register_type = ModbusRegisterType::HOLDING;
|
||||
this->start_address = start_address;
|
||||
this->offset = offset;
|
||||
this->bitmask = bitmask;
|
||||
this->bitmask = 0xFFFFFFFF;
|
||||
this->register_count = register_count;
|
||||
this->sensor_value_type = value_type;
|
||||
this->skip_updates = 0;
|
||||
@@ -47,7 +47,7 @@ class ModbusBinaryOutput : public output::BinaryOutput, public Component, public
|
||||
ModbusBinaryOutput(uint16_t start_address, uint8_t offset) {
|
||||
this->register_type = ModbusRegisterType::COIL;
|
||||
this->start_address = start_address;
|
||||
this->bitmask = bitmask;
|
||||
this->bitmask = 0xFFFFFFFF;
|
||||
this->sensor_value_type = SensorValueType::BIT;
|
||||
this->skip_updates = 0;
|
||||
this->register_count = 1;
|
||||
|
||||
@@ -48,7 +48,8 @@ static bool apply_command(AlarmControlPanelCall &call, const char *state) {
|
||||
}
|
||||
|
||||
void MQTTAlarmControlPanelComponent::setup() {
|
||||
this->alarm_control_panel_->add_on_state_callback([this]() { this->publish_state(); });
|
||||
this->alarm_control_panel_->add_on_state_callback(
|
||||
[this](AlarmControlPanelState /*state*/) { this->publish_state(); });
|
||||
this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) {
|
||||
auto call = this->alarm_control_panel_->make_call();
|
||||
if (!payload.empty() && payload[0] == '{') {
|
||||
|
||||
@@ -28,7 +28,8 @@ void MQTTLockComponent::setup() {
|
||||
this->status_momentary_warning("state", 5000);
|
||||
}
|
||||
});
|
||||
this->lock_->add_on_state_callback([this]() { this->defer("send", [this]() { this->publish_state(); }); });
|
||||
this->lock_->add_on_state_callback(
|
||||
[this](LockState /*state*/) { this->defer("send", [this]() { this->publish_state(); }); });
|
||||
}
|
||||
void MQTTLockComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT Lock '%s': ", this->lock_->get_name().c_str());
|
||||
|
||||
@@ -5,50 +5,6 @@
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
|
||||
class BufferOverflowTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit BufferOverflowTrigger(Nextion *nextion) {
|
||||
nextion->add_buffer_overflow_event_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class SetupTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit SetupTrigger(Nextion *nextion) {
|
||||
nextion->add_setup_state_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class SleepTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit SleepTrigger(Nextion *nextion) {
|
||||
nextion->add_sleep_state_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class WakeTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit WakeTrigger(Nextion *nextion) {
|
||||
nextion->add_wake_state_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class PageTrigger : public Trigger<uint8_t> {
|
||||
public:
|
||||
explicit PageTrigger(Nextion *nextion) {
|
||||
nextion->add_new_page_callback([this](const uint8_t page_id) { this->trigger(page_id); });
|
||||
}
|
||||
};
|
||||
|
||||
class TouchTrigger : public Trigger<uint8_t, uint8_t, bool> {
|
||||
public:
|
||||
explicit TouchTrigger(Nextion *nextion) {
|
||||
nextion->add_touch_event_callback([this](uint8_t page_id, uint8_t component_id, bool touch_event) {
|
||||
this->trigger(page_id, component_id, touch_event);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class NextionSetBrightnessAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit NextionSetBrightnessAction(Nextion *component) : component_(component) {}
|
||||
|
||||
@@ -2,13 +2,7 @@ from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import display, esp32, uart
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BRIGHTNESS,
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_ON_TOUCH,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
from esphome.const import CONF_BRIGHTNESS, CONF_ID, CONF_LAMBDA, CONF_ON_TOUCH
|
||||
from esphome.core import CORE, TimePeriod
|
||||
|
||||
from . import ( # noqa: F401 pylint: disable=unused-import
|
||||
@@ -55,14 +49,6 @@ def AUTO_LOAD() -> list[str]:
|
||||
NextionSetBrightnessAction = nextion_ns.class_(
|
||||
"NextionSetBrightnessAction", automation.Action
|
||||
)
|
||||
SetupTrigger = nextion_ns.class_("SetupTrigger", automation.Trigger.template())
|
||||
SleepTrigger = nextion_ns.class_("SleepTrigger", automation.Trigger.template())
|
||||
WakeTrigger = nextion_ns.class_("WakeTrigger", automation.Trigger.template())
|
||||
PageTrigger = nextion_ns.class_("PageTrigger", automation.Trigger.template())
|
||||
TouchTrigger = nextion_ns.class_("TouchTrigger", automation.Trigger.template())
|
||||
BufferOverflowTrigger = nextion_ns.class_(
|
||||
"BufferOverflowTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
|
||||
def _validate_tft_upload(config):
|
||||
@@ -101,38 +87,12 @@ CONFIG_SCHEMA = cv.All(
|
||||
),
|
||||
cv.Optional(CONF_MAX_COMMANDS_PER_LOOP): cv.uint16_t,
|
||||
cv.Optional(CONF_MAX_QUEUE_SIZE): cv.positive_int,
|
||||
cv.Optional(CONF_ON_BUFFER_OVERFLOW): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
BufferOverflowTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_PAGE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PageTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_SETUP): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SetupTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_SLEEP): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SleepTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_TOUCH): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TouchTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_WAKE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WakeTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_BUFFER_OVERFLOW): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_PAGE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_SETUP): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_SLEEP): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_TOUCH): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_WAKE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_SKIP_CONNECTION_HANDSHAKE, default=False): cv.boolean,
|
||||
cv.Optional(CONF_STARTUP_OVERRIDE_MS, default="8000ms"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
@@ -273,25 +233,25 @@ async def to_code(config):
|
||||
await display.register_display(var, config)
|
||||
|
||||
for conf in config.get(CONF_ON_SETUP, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_setup_state_callback", [], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_SLEEP, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_sleep_state_callback", [], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_WAKE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_wake_state_callback", [], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_PAGE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.uint8, "x")], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_new_page_callback", [(cg.uint8, "x")], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_TOUCH, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger,
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_touch_event_callback",
|
||||
[
|
||||
(cg.uint8, "page_id"),
|
||||
(cg.uint8, "component_id"),
|
||||
@@ -299,7 +259,7 @@ async def to_code(config):
|
||||
],
|
||||
conf,
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_BUFFER_OVERFLOW, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_buffer_overflow_event_callback", [], conf
|
||||
)
|
||||
|
||||
@@ -10,6 +10,10 @@ namespace nextion {
|
||||
|
||||
static const char *const TAG = "nextion";
|
||||
|
||||
// Nextion command terminator: three consecutive 0xFF bytes (per Nextion Instruction Set v1.1).
|
||||
static constexpr uint8_t COMMAND_DELIMITER[3] = {0xFF, 0xFF, 0xFF};
|
||||
static constexpr size_t DELIMITER_SIZE = sizeof(COMMAND_DELIMITER);
|
||||
|
||||
void Nextion::setup() {
|
||||
this->is_setup_ = false;
|
||||
this->connection_state_.ignore_is_setup_ = true;
|
||||
@@ -90,7 +94,7 @@ bool Nextion::check_connect_() {
|
||||
#endif // NEXTION_PROTOCOL_LOG
|
||||
|
||||
ESP_LOGW(TAG, "Not connected");
|
||||
comok_sent_ = 0;
|
||||
this->comok_sent_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -415,7 +419,8 @@ void Nextion::process_nextion_commands_() {
|
||||
#ifdef NEXTION_PROTOCOL_LOG
|
||||
this->print_queue_members_();
|
||||
#endif
|
||||
while ((to_process_length = this->command_data_.find(COMMAND_DELIMITER)) != std::string::npos) {
|
||||
while ((to_process_length = this->command_data_.find(reinterpret_cast<const char *>(COMMAND_DELIMITER), 0,
|
||||
DELIMITER_SIZE)) != std::string::npos) {
|
||||
#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP
|
||||
if (++commands_processed > this->max_commands_per_loop_) {
|
||||
ESP_LOGW(TAG, "Command processing limit exceeded");
|
||||
@@ -423,8 +428,8 @@ void Nextion::process_nextion_commands_() {
|
||||
}
|
||||
#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP
|
||||
ESP_LOGN(TAG, "queue size: %zu", this->nextion_queue_.size());
|
||||
while (to_process_length + COMMAND_DELIMITER.length() < this->command_data_.length() &&
|
||||
static_cast<uint8_t>(this->command_data_[to_process_length + COMMAND_DELIMITER.length()]) == 0xFF) {
|
||||
while (to_process_length + DELIMITER_SIZE < this->command_data_.length() &&
|
||||
static_cast<uint8_t>(this->command_data_[to_process_length + DELIMITER_SIZE]) == 0xFF) {
|
||||
++to_process_length;
|
||||
ESP_LOGN(TAG, "Add 0xFF");
|
||||
}
|
||||
@@ -829,17 +834,17 @@ void Nextion::process_nextion_commands_() {
|
||||
break;
|
||||
}
|
||||
|
||||
this->command_data_.erase(0, to_process_length + COMMAND_DELIMITER.length() + 1);
|
||||
this->command_data_.erase(0, to_process_length + DELIMITER_SIZE + 1);
|
||||
}
|
||||
|
||||
const uint32_t ms = App.get_loop_component_start_time();
|
||||
|
||||
if (this->max_q_age_ms_ > 0 && !this->nextion_queue_.empty() &&
|
||||
ms - this->nextion_queue_.front()->queue_time > this->max_q_age_ms_) {
|
||||
for (size_t i = 0; i < this->nextion_queue_.size(); i++) {
|
||||
NextionComponentBase *component = this->nextion_queue_[i]->component;
|
||||
if (ms - this->nextion_queue_[i]->queue_time > this->max_q_age_ms_) {
|
||||
if (this->nextion_queue_[i]->queue_time == 0) {
|
||||
for (auto it = this->nextion_queue_.begin(); it != this->nextion_queue_.end();) {
|
||||
NextionComponentBase *component = (*it)->component;
|
||||
if (ms - (*it)->queue_time > this->max_q_age_ms_) {
|
||||
if ((*it)->queue_time == 0) {
|
||||
ESP_LOGD(TAG, "Remove old queue '%s':'%s' (t=0)", component->get_queue_type_string().c_str(),
|
||||
component->get_variable_name().c_str());
|
||||
}
|
||||
@@ -858,10 +863,8 @@ void Nextion::process_nextion_commands_() {
|
||||
delete component; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
|
||||
delete this->nextion_queue_[i]; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
|
||||
this->nextion_queue_.erase(this->nextion_queue_.begin() + i);
|
||||
i--;
|
||||
delete *it; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
it = this->nextion_queue_.erase(it);
|
||||
|
||||
} else {
|
||||
break;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/components/display/display.h"
|
||||
#include "esphome/components/display/display_color_utils.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/time.h"
|
||||
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "nextion_base.h"
|
||||
#include "nextion_component.h"
|
||||
#include "esphome/components/display/display.h"
|
||||
#include "esphome/components/display/display_color_utils.h"
|
||||
|
||||
#ifdef USE_NEXTION_TFT_UPLOAD
|
||||
#ifdef USE_ESP32
|
||||
@@ -29,8 +29,6 @@ class NextionComponentBase;
|
||||
|
||||
using nextion_writer_t = display::DisplayWriter<Nextion>;
|
||||
|
||||
static const std::string COMMAND_DELIMITER{static_cast<char>(255), static_cast<char>(255), static_cast<char>(255)};
|
||||
|
||||
#ifdef USE_NEXTION_COMMAND_SPACING
|
||||
class NextionCommandPacer {
|
||||
public:
|
||||
@@ -1393,8 +1391,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||
void process_pending_in_queue_();
|
||||
#endif // USE_NEXTION_COMMAND_SPACING
|
||||
|
||||
std::deque<NextionQueue *> nextion_queue_;
|
||||
std::deque<NextionQueue *> waveform_queue_;
|
||||
std::list<NextionQueue *> nextion_queue_;
|
||||
std::list<NextionQueue *> waveform_queue_;
|
||||
uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag);
|
||||
void all_components_send_state_(bool force_update = false);
|
||||
uint32_t comok_sent_ = 0;
|
||||
|
||||
@@ -106,7 +106,7 @@ void Nextion::set_component_pressed_foreground_color(const char *component, uint
|
||||
}
|
||||
|
||||
void Nextion::set_component_pressed_foreground_color(const char *component, const char *color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pressed_foreground_color", " %s.pco2=%s", component, color);
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pressed_foreground_color", "%s.pco2=%s", component, color);
|
||||
}
|
||||
|
||||
void Nextion::set_component_pressed_foreground_color(const char *component, Color color) {
|
||||
@@ -134,7 +134,7 @@ void Nextion::set_component_pressed_font_color(const char *component, uint16_t c
|
||||
}
|
||||
|
||||
void Nextion::set_component_pressed_font_color(const char *component, const char *color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", " %s.pco2=%s", component, color);
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", "%s.pco2=%s", component, color);
|
||||
}
|
||||
|
||||
void Nextion::set_component_pressed_font_color(const char *component, Color color) {
|
||||
|
||||
@@ -22,9 +22,9 @@ static constexpr size_t NEXTION_MAX_RESPONSE_LOG_BYTES = 16;
|
||||
int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
|
||||
uint32_t range_size = this->tft_size_ - range_start;
|
||||
ESP_LOGV(TAG, "Heap: %" PRIu32, EspClass::getFreeHeap());
|
||||
uint32_t range_end = ((upload_first_chunk_sent_ or this->tft_size_ < 4096) ? this->tft_size_ : 4096) - 1;
|
||||
uint32_t range_end = ((this->upload_first_chunk_sent_ || this->tft_size_ < 4096) ? this->tft_size_ : 4096) - 1;
|
||||
ESP_LOGD(TAG, "Range start: %" PRIu32, range_start);
|
||||
if (range_size <= 0 or range_end <= range_start) {
|
||||
if (range_size <= 0 || range_end <= range_start) {
|
||||
ESP_LOGE(TAG, "Invalid range end: %" PRIu32 ", size: %" PRIu32, range_end, range_size);
|
||||
return -1;
|
||||
}
|
||||
@@ -34,7 +34,7 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
|
||||
ESP_LOGV(TAG, "Range: %s", range_header);
|
||||
http_client.addHeader("Range", range_header);
|
||||
int code = http_client.GET();
|
||||
if (code != HTTP_CODE_OK and code != HTTP_CODE_PARTIAL_CONTENT) {
|
||||
if (code != HTTP_CODE_OK && code != HTTP_CODE_PARTIAL_CONTENT) {
|
||||
ESP_LOGW(TAG, "HTTP failed: %s", HTTPClient::errorToString(code).c_str());
|
||||
return -1;
|
||||
}
|
||||
@@ -80,12 +80,12 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
|
||||
recv_string.clear();
|
||||
this->write_array(buffer, buffer_size);
|
||||
App.feed_wdt();
|
||||
this->recv_ret_string_(recv_string, upload_first_chunk_sent_ ? 500 : 5000, true);
|
||||
this->recv_ret_string_(recv_string, this->upload_first_chunk_sent_ ? 500 : 5000, true);
|
||||
this->content_length_ -= read_len;
|
||||
const float upload_percentage = 100.0f * (this->tft_size_ - this->content_length_) / this->tft_size_;
|
||||
ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 ")", upload_percentage, this->content_length_,
|
||||
EspClass::getFreeHeap());
|
||||
upload_first_chunk_sent_ = true;
|
||||
this->upload_first_chunk_sent_ = true;
|
||||
if (recv_string.empty()) {
|
||||
ESP_LOGW(TAG, "No response from display during upload");
|
||||
allocator.deallocate(buffer, 4096);
|
||||
@@ -112,7 +112,7 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
|
||||
allocator.deallocate(buffer, 4096);
|
||||
buffer = nullptr;
|
||||
return range_end + 1;
|
||||
} else if (recv_string[0] != 0x05 and recv_string[0] != 0x08) { // 0x05 == "ok"
|
||||
} else if (recv_string[0] != 0x05 && recv_string[0] != 0x08) { // 0x05 == "ok"
|
||||
char hex_buf[format_hex_pretty_size(NEXTION_MAX_RESPONSE_LOG_BYTES)];
|
||||
ESP_LOGE(
|
||||
TAG, "Invalid response: [%s]",
|
||||
@@ -214,7 +214,7 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
|
||||
++tries;
|
||||
}
|
||||
|
||||
if (code != 200 and code != 206) {
|
||||
if (code != 200 && code != 206) {
|
||||
ESP_LOGE(TAG, "HTTP request failed with status %d", code);
|
||||
return this->upload_end_(false);
|
||||
}
|
||||
|
||||
@@ -155,9 +155,6 @@ Number = number_ns.class_("Number", cg.EntityBase)
|
||||
NumberPtr = Number.operator("ptr")
|
||||
|
||||
# Triggers
|
||||
NumberStateTrigger = number_ns.class_(
|
||||
"NumberStateTrigger", automation.Trigger.template(cg.float_)
|
||||
)
|
||||
ValueRangeTrigger = number_ns.class_(
|
||||
"ValueRangeTrigger", automation.Trigger.template(cg.float_), cg.Component
|
||||
)
|
||||
@@ -198,11 +195,7 @@ _NUMBER_SCHEMA = (
|
||||
.extend(
|
||||
{
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTNumberComponent),
|
||||
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(NumberStateTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_VALUE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValueRangeTrigger),
|
||||
@@ -248,8 +241,9 @@ def number_schema(
|
||||
@coroutine_with_priority(CoroPriority.AUTOMATION)
|
||||
async def _build_number_automations(var, config):
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_state_callback", [(float, "x")], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_VALUE_RANGE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await cg.register_component(trigger, conf)
|
||||
|
||||
@@ -7,14 +7,7 @@ from esphome.components.const import CONF_REQUEST_HEADERS
|
||||
from esphome.components.http_request import CONF_HTTP_REQUEST_ID, HttpRequestComponent
|
||||
from esphome.components.image import CONF_TRANSPARENCY, add_metadata
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BUFFER_SIZE,
|
||||
CONF_ID,
|
||||
CONF_ON_ERROR,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TYPE,
|
||||
CONF_URL,
|
||||
)
|
||||
from esphome.const import CONF_BUFFER_SIZE, CONF_ID, CONF_ON_ERROR, CONF_TYPE, CONF_URL
|
||||
from esphome.core import Lambda
|
||||
|
||||
AUTO_LOAD = ["image", "runtime_image"]
|
||||
@@ -41,14 +34,6 @@ ReleaseImageAction = online_image_ns.class_(
|
||||
"OnlineImageReleaseAction", automation.Action, cg.Parented.template(OnlineImage)
|
||||
)
|
||||
|
||||
# Triggers
|
||||
DownloadFinishedTrigger = online_image_ns.class_(
|
||||
"DownloadFinishedTrigger", automation.Trigger.template()
|
||||
)
|
||||
DownloadErrorTrigger = online_image_ns.class_(
|
||||
"DownloadErrorTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
|
||||
ONLINE_IMAGE_SCHEMA = (
|
||||
runtime_image.runtime_image_schema(OnlineImage)
|
||||
@@ -61,18 +46,8 @@ ONLINE_IMAGE_SCHEMA = (
|
||||
cv.Optional(CONF_REQUEST_HEADERS): cv.All(
|
||||
cv.Schema({cv.string: cv.templatable(cv.string)})
|
||||
),
|
||||
cv.Optional(CONF_ON_DOWNLOAD_FINISHED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
DownloadFinishedTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ERROR): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DownloadErrorTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_DOWNLOAD_FINISHED): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ERROR): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("never"))
|
||||
@@ -165,9 +140,11 @@ async def to_code(config):
|
||||
cg.add(var.add_request_header(key, value))
|
||||
|
||||
for conf in config.get(CONF_ON_DOWNLOAD_FINISHED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(bool, "cached")], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_finished_callback", [(bool, "cached")], conf
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_ERROR, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_error_callback", [], conf
|
||||
)
|
||||
|
||||
@@ -129,18 +129,4 @@ template<typename... Ts> class OnlineImageReleaseAction : public Action<Ts...> {
|
||||
OnlineImage *parent_;
|
||||
};
|
||||
|
||||
class DownloadFinishedTrigger : public Trigger<bool> {
|
||||
public:
|
||||
explicit DownloadFinishedTrigger(OnlineImage *parent) {
|
||||
parent->add_on_finished_callback([this](bool cached) { this->trigger(cached); });
|
||||
}
|
||||
};
|
||||
|
||||
class DownloadErrorTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit DownloadErrorTrigger(OnlineImage *parent) {
|
||||
parent->add_on_error_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::online_image
|
||||
|
||||
@@ -101,10 +101,10 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
|
||||
if (!zc_symmetrical || !amplitude_convergent) {
|
||||
// The frequency/amplitude is not fully accurate yet, try to wait
|
||||
// until the fault clears, or terminate after a while anyway
|
||||
if (zc_symmetrical) {
|
||||
if (!zc_symmetrical) {
|
||||
ESP_LOGVV(TAG, "%s: ZC is not symmetrical", this->id_.c_str());
|
||||
}
|
||||
if (amplitude_convergent) {
|
||||
if (!amplitude_convergent) {
|
||||
ESP_LOGVV(TAG, "%s: Amplitude is not convergent", this->id_.c_str());
|
||||
}
|
||||
uint32_t phase = this->relay_function_.phase_count;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/optional.h"
|
||||
#include "pid_controller.h"
|
||||
#include "pid_simulator.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace pid {
|
||||
|
||||
class PIDSimulator : public PollingComponent, public output::FloatOutput {
|
||||
public:
|
||||
PIDSimulator() : PollingComponent(1000) {}
|
||||
|
||||
float surface = 1; /// surface area in m²
|
||||
float mass = 3; /// mass of simulated object in kg
|
||||
float temperature = 21; /// current temperature of object in °C
|
||||
float efficiency = 0.98; /// heating efficiency, 1 is 100% efficient
|
||||
float thermal_conductivity = 15; /// thermal conductivity of surface are in W/(m*K), here: steel
|
||||
float specific_heat_capacity = 4.182; /// specific heat capacity of mass in kJ/(kg*K), here: water
|
||||
float heat_power = 500; /// Heating power in W
|
||||
float ambient_temperature = 20; /// Ambient temperature in °C
|
||||
float update_interval = 1; /// The simulated updated interval in seconds
|
||||
std::vector<float> delayed_temps; /// storage of past temperatures for delaying temperature reading
|
||||
size_t delay_cycles = 15; /// how many update cycles to delay the output
|
||||
float output_value = 0.0; /// Current output value of heating element
|
||||
sensor::Sensor *sensor = new sensor::Sensor();
|
||||
|
||||
float delta_t(float power) {
|
||||
// P = Q / t
|
||||
// Q = c * m * 𝚫t
|
||||
// 𝚫t = (P*t) / (c*m)
|
||||
float c = this->specific_heat_capacity;
|
||||
float t = this->update_interval;
|
||||
float p = power / 1000; // in kW
|
||||
float m = this->mass;
|
||||
return (p * t) / (c * m);
|
||||
}
|
||||
|
||||
float update_temp() {
|
||||
float value = clamp(output_value, 0.0f, 1.0f);
|
||||
|
||||
// Heat
|
||||
float power = value * heat_power * efficiency;
|
||||
temperature += this->delta_t(power);
|
||||
|
||||
// Cool
|
||||
// Q = k_w * A * (T_mass - T_ambient)
|
||||
// P = Q / t
|
||||
float dt = temperature - ambient_temperature;
|
||||
float cool_power = (thermal_conductivity * surface * dt) / update_interval;
|
||||
temperature -= this->delta_t(cool_power);
|
||||
|
||||
// Delay temperature readings
|
||||
delayed_temps.push_back(temperature);
|
||||
if (delayed_temps.size() > delay_cycles)
|
||||
delayed_temps.erase(delayed_temps.begin());
|
||||
float prev_temp = this->delayed_temps[0];
|
||||
float alpha = 0.1f;
|
||||
float ret = (1 - alpha) * prev_temp + alpha * prev_temp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void setup() override { sensor->publish_state(this->temperature); }
|
||||
void update() override {
|
||||
float new_temp = this->update_temp();
|
||||
sensor->publish_state(new_temp);
|
||||
}
|
||||
|
||||
protected:
|
||||
void write_state(float state) override { this->output_value = state; }
|
||||
};
|
||||
|
||||
} // namespace pid
|
||||
} // namespace esphome
|
||||
@@ -19,10 +19,6 @@ CONF_PN532_ID = "pn532_id"
|
||||
pn532_ns = cg.esphome_ns.namespace("pn532")
|
||||
PN532 = pn532_ns.class_("PN532", cg.PollingComponent)
|
||||
|
||||
PN532OnFinishedWriteTrigger = pn532_ns.class_(
|
||||
"PN532OnFinishedWriteTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
PN532IsWritingCondition = pn532_ns.class_(
|
||||
"PN532IsWritingCondition", automation.Condition
|
||||
)
|
||||
@@ -35,13 +31,7 @@ PN532_SCHEMA = cv.Schema(
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(nfc.NfcOnTagTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINISHED_WRITE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
PN532OnFinishedWriteTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINISHED_WRITE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_TAG_REMOVED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(nfc.NfcOnTagTrigger),
|
||||
@@ -77,8 +67,9 @@ async def setup_pn532(var, config):
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_FINISHED_WRITE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_finished_write_callback", [], conf
|
||||
)
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
|
||||
@@ -133,13 +133,6 @@ class PN532BinarySensor : public binary_sensor::BinarySensor {
|
||||
bool found_{false};
|
||||
};
|
||||
|
||||
class PN532OnFinishedWriteTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit PN532OnFinishedWriteTrigger(PN532 *parent) {
|
||||
parent->add_on_finished_write_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class PN532IsWritingCondition : public Condition<Ts...>, public Parented<PN532> {
|
||||
public:
|
||||
bool check(const Ts &...x) override { return this->parent_->is_writing(); }
|
||||
|
||||
@@ -50,14 +50,6 @@ SetWriteMessageAction = pn7150_ns.class_("SetWriteMessageAction", automation.Act
|
||||
SetWriteModeAction = pn7150_ns.class_("SetWriteModeAction", automation.Action)
|
||||
|
||||
|
||||
PN7150OnEmulatedTagScanTrigger = pn7150_ns.class_(
|
||||
"PN7150OnEmulatedTagScanTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
PN7150OnFinishedWriteTrigger = pn7150_ns.class_(
|
||||
"PN7150OnFinishedWriteTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
PN7150IsWritingCondition = pn7150_ns.class_(
|
||||
"PN7150IsWritingCondition", automation.Condition
|
||||
)
|
||||
@@ -83,20 +75,8 @@ SET_MESSAGE_ACTION_SCHEMA = cv.Schema(
|
||||
PN7150_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(PN7150),
|
||||
cv.Optional(CONF_ON_EMULATED_TAG_SCAN): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
PN7150OnEmulatedTagScanTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINISHED_WRITE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
PN7150OnFinishedWriteTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_EMULATED_TAG_SCAN): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_FINISHED_WRITE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_TAG): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(nfc.NfcOnTagTrigger),
|
||||
@@ -215,12 +195,14 @@ async def setup_pn7150(var, config):
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_EMULATED_TAG_SCAN, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_emulated_tag_scan_callback", [], conf
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_FINISHED_WRITE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_finished_write_callback", [], conf
|
||||
)
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
|
||||
@@ -7,20 +7,6 @@
|
||||
namespace esphome {
|
||||
namespace pn7150 {
|
||||
|
||||
class PN7150OnEmulatedTagScanTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit PN7150OnEmulatedTagScanTrigger(PN7150 *parent) {
|
||||
parent->add_on_emulated_tag_scan_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class PN7150OnFinishedWriteTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit PN7150OnFinishedWriteTrigger(PN7150 *parent) {
|
||||
parent->add_on_finished_write_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class PN7150IsWritingCondition : public Condition<Ts...>, public Parented<PN7150> {
|
||||
public:
|
||||
bool check(const Ts &...x) override { return this->parent_->is_writing(); }
|
||||
|
||||
@@ -52,14 +52,6 @@ SetWriteMessageAction = pn7160_ns.class_("SetWriteMessageAction", automation.Act
|
||||
SetWriteModeAction = pn7160_ns.class_("SetWriteModeAction", automation.Action)
|
||||
|
||||
|
||||
PN7160OnEmulatedTagScanTrigger = pn7160_ns.class_(
|
||||
"PN7160OnEmulatedTagScanTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
PN7160OnFinishedWriteTrigger = pn7160_ns.class_(
|
||||
"PN7160OnFinishedWriteTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
PN7160IsWritingCondition = pn7160_ns.class_(
|
||||
"PN7160IsWritingCondition", automation.Condition
|
||||
)
|
||||
@@ -85,20 +77,8 @@ SET_MESSAGE_ACTION_SCHEMA = cv.Schema(
|
||||
PN7160_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(PN7160),
|
||||
cv.Optional(CONF_ON_EMULATED_TAG_SCAN): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
PN7160OnEmulatedTagScanTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINISHED_WRITE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
PN7160OnFinishedWriteTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_EMULATED_TAG_SCAN): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_FINISHED_WRITE): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_TAG): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(nfc.NfcOnTagTrigger),
|
||||
@@ -227,12 +207,14 @@ async def setup_pn7160(var, config):
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_EMULATED_TAG_SCAN, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_emulated_tag_scan_callback", [], conf
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_FINISHED_WRITE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_finished_write_callback", [], conf
|
||||
)
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
|
||||
@@ -7,20 +7,6 @@
|
||||
namespace esphome {
|
||||
namespace pn7160 {
|
||||
|
||||
class PN7160OnEmulatedTagScanTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit PN7160OnEmulatedTagScanTrigger(PN7160 *parent) {
|
||||
parent->add_on_emulated_tag_scan_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class PN7160OnFinishedWriteTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit PN7160OnFinishedWriteTrigger(PN7160 *parent) {
|
||||
parent->add_on_finished_write_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class PN7160IsWritingCondition : public Condition<Ts...>, public Parented<PN7160> {
|
||||
public:
|
||||
bool check(const Ts &...x) override { return this->parent_->is_writing(); }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "gobox_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
@@ -25,7 +26,7 @@ void GoboxProtocol::encode(RemoteTransmitData *dst, const GoboxData &data) {
|
||||
dst->set_carrier_frequency(38000);
|
||||
dst->reserve((HEADER_SIZE + CODE_SIZE + 1) * 2);
|
||||
uint64_t code = (HEADER << CODE_SIZE) | (data.code & ((1UL << CODE_SIZE) - 1));
|
||||
ESP_LOGI(TAG, "Send Gobox: code=0x%Lx", code);
|
||||
ESP_LOGI(TAG, "Send Gobox: code=0x%016" PRIx64, code);
|
||||
for (int16_t i = (HEADER_SIZE + CODE_SIZE - 1); i >= 0; i--) {
|
||||
if (code & ((uint64_t) 1 << i)) {
|
||||
dst->item(BIT_MARK_US, BIT_ONE_SPACE_US);
|
||||
|
||||
@@ -12,7 +12,6 @@ from esphome.const import (
|
||||
CONF_PROTOCOL,
|
||||
CONF_RAW,
|
||||
CONF_SYNC,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
@@ -26,14 +25,6 @@ RFBridgeComponent = rf_bridge_ns.class_(
|
||||
RFBridgeData = rf_bridge_ns.struct("RFBridgeData")
|
||||
RFBridgeAdvancedData = rf_bridge_ns.struct("RFBridgeAdvancedData")
|
||||
|
||||
RFBridgeReceivedCodeTrigger = rf_bridge_ns.class_(
|
||||
"RFBridgeReceivedCodeTrigger", automation.Trigger.template(RFBridgeData)
|
||||
)
|
||||
RFBridgeReceivedAdvancedCodeTrigger = rf_bridge_ns.class_(
|
||||
"RFBridgeReceivedAdvancedCodeTrigger",
|
||||
automation.Trigger.template(RFBridgeAdvancedData),
|
||||
)
|
||||
|
||||
RFBridgeSendCodeAction = rf_bridge_ns.class_(
|
||||
"RFBridgeSendCodeAction", automation.Action
|
||||
)
|
||||
@@ -65,19 +56,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(RFBridgeComponent),
|
||||
cv.Optional(CONF_ON_CODE_RECEIVED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
RFBridgeReceivedCodeTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_CODE_RECEIVED): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_ADVANCED_CODE_RECEIVED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
RFBridgeReceivedAdvancedCodeTrigger
|
||||
),
|
||||
}
|
||||
{}
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -92,13 +73,15 @@ async def to_code(config):
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
for conf in config.get(CONF_ON_CODE_RECEIVED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(RFBridgeData, "data")], conf)
|
||||
|
||||
await automation.build_callback_automation(
|
||||
var, "add_on_code_received_callback", [(RFBridgeData, "data")], conf
|
||||
)
|
||||
for conf in config.get(CONF_ON_ADVANCED_CODE_RECEIVED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(RFBridgeAdvancedData, "data")], conf
|
||||
await automation.build_callback_automation(
|
||||
var,
|
||||
"add_on_advanced_code_received_callback",
|
||||
[(RFBridgeAdvancedData, "data")],
|
||||
conf,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user