diff --git a/esphome/components/alarm_control_panel/alarm_control_panel.cpp b/esphome/components/alarm_control_panel/alarm_control_panel.cpp index c29e02c8ef..585401028a 100644 --- a/esphome/components/alarm_control_panel/alarm_control_panel.cpp +++ b/esphome/components/alarm_control_panel/alarm_control_panel.cpp @@ -35,29 +35,15 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) { ESP_LOGD(TAG, "Set state to: %s, previous: %s", 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; - this->state_callback_.call(); + + for (auto *listener : this->listeners_) { + listener->on_state(state, prev_state); + } + #if defined(USE_ALARM_CONTROL_PANEL) && defined(USE_CONTROLLER_REGISTRY) ControllerRegistry::notify_alarm_control_panel_update(this); #endif - if (state == ACP_STATE_TRIGGERED) { - this->triggered_callback_.call(); - } else if (state == ACP_STATE_ARMING) { - this->arming_callback_.call(); - } else if (state == ACP_STATE_PENDING) { - this->pending_callback_.call(); - } else if (state == ACP_STATE_ARMED_HOME) { - this->armed_home_callback_.call(); - } else if (state == ACP_STATE_ARMED_NIGHT) { - this->armed_night_callback_.call(); - } else if (state == ACP_STATE_ARMED_AWAY) { - this->armed_away_callback_.call(); - } else if (state == ACP_STATE_DISARMED) { - this->disarmed_callback_.call(); - } - if (prev_state == ACP_STATE_TRIGGERED) { - this->cleared_callback_.call(); - } if (state == this->desired_state_) { // only store when in the desired state this->pref_.save(&state); @@ -65,48 +51,14 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) { } } -void AlarmControlPanel::add_on_state_callback(std::function &&callback) { - this->state_callback_.add(std::move(callback)); +void AlarmControlPanel::notify_chime() { + for (auto *listener : this->listeners_) + listener->on_chime(); } -void AlarmControlPanel::add_on_triggered_callback(std::function &&callback) { - this->triggered_callback_.add(std::move(callback)); -} - -void AlarmControlPanel::add_on_arming_callback(std::function &&callback) { - this->arming_callback_.add(std::move(callback)); -} - -void AlarmControlPanel::add_on_armed_home_callback(std::function &&callback) { - this->armed_home_callback_.add(std::move(callback)); -} - -void AlarmControlPanel::add_on_armed_night_callback(std::function &&callback) { - this->armed_night_callback_.add(std::move(callback)); -} - -void AlarmControlPanel::add_on_armed_away_callback(std::function &&callback) { - this->armed_away_callback_.add(std::move(callback)); -} - -void AlarmControlPanel::add_on_pending_callback(std::function &&callback) { - this->pending_callback_.add(std::move(callback)); -} - -void AlarmControlPanel::add_on_disarmed_callback(std::function &&callback) { - this->disarmed_callback_.add(std::move(callback)); -} - -void AlarmControlPanel::add_on_cleared_callback(std::function &&callback) { - this->cleared_callback_.add(std::move(callback)); -} - -void AlarmControlPanel::add_on_chime_callback(std::function &&callback) { - this->chime_callback_.add(std::move(callback)); -} - -void AlarmControlPanel::add_on_ready_callback(std::function &&callback) { - this->ready_callback_.add(std::move(callback)); +void AlarmControlPanel::notify_ready() { + for (auto *listener : this->listeners_) + listener->on_ready(); } void AlarmControlPanel::arm_away(optional code) { diff --git a/esphome/components/alarm_control_panel/alarm_control_panel.h b/esphome/components/alarm_control_panel/alarm_control_panel.h index 85c2b2148e..d9090e3c19 100644 --- a/esphome/components/alarm_control_panel/alarm_control_panel.h +++ b/esphome/components/alarm_control_panel/alarm_control_panel.h @@ -1,17 +1,32 @@ #pragma once -#include +#include #include "alarm_control_panel_call.h" #include "alarm_control_panel_state.h" -#include "esphome/core/automation.h" +#include "esphome/core/component.h" #include "esphome/core/entity_base.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/preferences.h" namespace esphome { namespace alarm_control_panel { +/// Listener interface for alarm control panel events. +/// Implement this interface and register with add_listener() to receive notifications. +class AlarmControlPanelListener { + public: + virtual ~AlarmControlPanelListener() = default; + /// Called when state changes. Check new_state to filter specific states. + virtual void on_state(AlarmControlPanelState new_state, AlarmControlPanelState prev_state) {} + /// Called when a chime zone opens while disarmed. + virtual void on_chime() {} + /// Called when ready state changes. + virtual void on_ready() {} +}; + enum AlarmControlPanelFeature : uint8_t { // Matches Home Assistant values ACP_FEAT_ARM_HOME = 1 << 0, @@ -35,71 +50,19 @@ class AlarmControlPanel : public EntityBase { */ void publish_state(AlarmControlPanelState state); - /** Add a callback for when the state of the alarm_control_panel changes + /** Register a listener for alarm control panel events. * - * @param callback The callback function + * @param listener The listener to add (must remain valid for lifetime of panel) */ - void add_on_state_callback(std::function &&callback); + void add_listener(AlarmControlPanelListener *listener) { this->listeners_.push_back(listener); } - /** Add a callback for when the state of the alarm_control_panel chanes to triggered - * - * @param callback The callback function + /** Notify listeners of a chime event (zone opened while disarmed). */ - void add_on_triggered_callback(std::function &&callback); + void notify_chime(); - /** Add a callback for when the state of the alarm_control_panel chanes to arming - * - * @param callback The callback function + /** Notify listeners of a ready state change. */ - void add_on_arming_callback(std::function &&callback); - - /** Add a callback for when the state of the alarm_control_panel changes to pending - * - * @param callback The callback function - */ - void add_on_pending_callback(std::function &&callback); - - /** Add a callback for when the state of the alarm_control_panel changes to armed_home - * - * @param callback The callback function - */ - void add_on_armed_home_callback(std::function &&callback); - - /** Add a callback for when the state of the alarm_control_panel changes to armed_night - * - * @param callback The callback function - */ - void add_on_armed_night_callback(std::function &&callback); - - /** Add a callback for when the state of the alarm_control_panel changes to armed_away - * - * @param callback The callback function - */ - void add_on_armed_away_callback(std::function &&callback); - - /** Add a callback for when the state of the alarm_control_panel changes to disarmed - * - * @param callback The callback function - */ - void add_on_disarmed_callback(std::function &&callback); - - /** Add a callback for when the state of the alarm_control_panel clears from triggered - * - * @param callback The callback function - */ - void add_on_cleared_callback(std::function &&callback); - - /** Add a callback for when a chime zone goes from closed to open - * - * @param callback The callback function - */ - void add_on_chime_callback(std::function &&callback); - - /** Add a callback for when a ready state changes - * - * @param callback The callback function - */ - void add_on_ready_callback(std::function &&callback); + void notify_ready(); /** A numeric representation of the supported features as per HomeAssistant * @@ -172,28 +135,8 @@ class AlarmControlPanel : public EntityBase { uint32_t last_update_; // the call control function virtual void control(const AlarmControlPanelCall &call) = 0; - // state callback - CallbackManager state_callback_{}; - // trigger callback - CallbackManager triggered_callback_{}; - // arming callback - CallbackManager arming_callback_{}; - // pending callback - CallbackManager pending_callback_{}; - // armed_home callback - CallbackManager armed_home_callback_{}; - // armed_night callback - CallbackManager armed_night_callback_{}; - // armed_away callback - CallbackManager armed_away_callback_{}; - // disarmed callback - CallbackManager disarmed_callback_{}; - // clear callback - CallbackManager cleared_callback_{}; - // chime callback - CallbackManager chime_callback_{}; - // ready callback - CallbackManager ready_callback_{}; + // registered listeners + std::vector listeners_; }; } // namespace alarm_control_panel diff --git a/esphome/components/alarm_control_panel/automation.h b/esphome/components/alarm_control_panel/automation.h index db2ef78158..533f4892d1 100644 --- a/esphome/components/alarm_control_panel/automation.h +++ b/esphome/components/alarm_control_panel/automation.h @@ -6,81 +6,94 @@ namespace esphome { namespace alarm_control_panel { -class StateTrigger : public Trigger<> { +class StateTrigger final : public Trigger<>, public AlarmControlPanelListener { public: - explicit StateTrigger(AlarmControlPanel *alarm_control_panel) { - alarm_control_panel->add_on_state_callback([this]() { this->trigger(); }); + explicit StateTrigger(AlarmControlPanel *alarm_control_panel) { alarm_control_panel->add_listener(this); } + void on_state(AlarmControlPanelState new_state, AlarmControlPanelState prev_state) override { this->trigger(); } +}; + +class TriggeredTrigger final : public Trigger<>, public AlarmControlPanelListener { + public: + explicit TriggeredTrigger(AlarmControlPanel *alarm_control_panel) { alarm_control_panel->add_listener(this); } + void on_state(AlarmControlPanelState new_state, AlarmControlPanelState prev_state) override { + if (new_state == ACP_STATE_TRIGGERED) + this->trigger(); } }; -class TriggeredTrigger : public Trigger<> { +class ArmingTrigger final : public Trigger<>, public AlarmControlPanelListener { public: - explicit TriggeredTrigger(AlarmControlPanel *alarm_control_panel) { - alarm_control_panel->add_on_triggered_callback([this]() { this->trigger(); }); + explicit ArmingTrigger(AlarmControlPanel *alarm_control_panel) { alarm_control_panel->add_listener(this); } + void on_state(AlarmControlPanelState new_state, AlarmControlPanelState prev_state) override { + if (new_state == ACP_STATE_ARMING) + this->trigger(); } }; -class ArmingTrigger : public Trigger<> { +class PendingTrigger final : public Trigger<>, public AlarmControlPanelListener { public: - explicit ArmingTrigger(AlarmControlPanel *alarm_control_panel) { - alarm_control_panel->add_on_arming_callback([this]() { this->trigger(); }); + explicit PendingTrigger(AlarmControlPanel *alarm_control_panel) { alarm_control_panel->add_listener(this); } + void on_state(AlarmControlPanelState new_state, AlarmControlPanelState prev_state) override { + if (new_state == ACP_STATE_PENDING) + this->trigger(); } }; -class PendingTrigger : public Trigger<> { +class ArmedHomeTrigger final : public Trigger<>, public AlarmControlPanelListener { public: - explicit PendingTrigger(AlarmControlPanel *alarm_control_panel) { - alarm_control_panel->add_on_pending_callback([this]() { this->trigger(); }); + explicit ArmedHomeTrigger(AlarmControlPanel *alarm_control_panel) { alarm_control_panel->add_listener(this); } + void on_state(AlarmControlPanelState new_state, AlarmControlPanelState prev_state) override { + if (new_state == ACP_STATE_ARMED_HOME) + this->trigger(); } }; -class ArmedHomeTrigger : public Trigger<> { +class ArmedNightTrigger final : public Trigger<>, public AlarmControlPanelListener { public: - explicit ArmedHomeTrigger(AlarmControlPanel *alarm_control_panel) { - alarm_control_panel->add_on_armed_home_callback([this]() { this->trigger(); }); + explicit ArmedNightTrigger(AlarmControlPanel *alarm_control_panel) { alarm_control_panel->add_listener(this); } + void on_state(AlarmControlPanelState new_state, AlarmControlPanelState prev_state) override { + if (new_state == ACP_STATE_ARMED_NIGHT) + this->trigger(); } }; -class ArmedNightTrigger : public Trigger<> { +class ArmedAwayTrigger final : public Trigger<>, public AlarmControlPanelListener { public: - explicit ArmedNightTrigger(AlarmControlPanel *alarm_control_panel) { - alarm_control_panel->add_on_armed_night_callback([this]() { this->trigger(); }); + explicit ArmedAwayTrigger(AlarmControlPanel *alarm_control_panel) { alarm_control_panel->add_listener(this); } + void on_state(AlarmControlPanelState new_state, AlarmControlPanelState prev_state) override { + if (new_state == ACP_STATE_ARMED_AWAY) + this->trigger(); } }; -class ArmedAwayTrigger : public Trigger<> { +class DisarmedTrigger final : public Trigger<>, public AlarmControlPanelListener { public: - explicit ArmedAwayTrigger(AlarmControlPanel *alarm_control_panel) { - alarm_control_panel->add_on_armed_away_callback([this]() { this->trigger(); }); + explicit DisarmedTrigger(AlarmControlPanel *alarm_control_panel) { alarm_control_panel->add_listener(this); } + void on_state(AlarmControlPanelState new_state, AlarmControlPanelState prev_state) override { + if (new_state == ACP_STATE_DISARMED) + this->trigger(); } }; -class DisarmedTrigger : public Trigger<> { +class ClearedTrigger final : public Trigger<>, public AlarmControlPanelListener { public: - explicit DisarmedTrigger(AlarmControlPanel *alarm_control_panel) { - alarm_control_panel->add_on_disarmed_callback([this]() { this->trigger(); }); + explicit ClearedTrigger(AlarmControlPanel *alarm_control_panel) { alarm_control_panel->add_listener(this); } + void on_state(AlarmControlPanelState new_state, AlarmControlPanelState prev_state) override { + if (prev_state == ACP_STATE_TRIGGERED) + this->trigger(); } }; -class ClearedTrigger : public Trigger<> { +class ChimeTrigger final : public Trigger<>, public AlarmControlPanelListener { public: - explicit ClearedTrigger(AlarmControlPanel *alarm_control_panel) { - alarm_control_panel->add_on_cleared_callback([this]() { this->trigger(); }); - } + explicit ChimeTrigger(AlarmControlPanel *alarm_control_panel) { alarm_control_panel->add_listener(this); } + void on_chime() override { this->trigger(); } }; -class ChimeTrigger : public Trigger<> { +class ReadyTrigger final : public Trigger<>, public AlarmControlPanelListener { public: - explicit ChimeTrigger(AlarmControlPanel *alarm_control_panel) { - alarm_control_panel->add_on_chime_callback([this]() { this->trigger(); }); - } -}; - -class ReadyTrigger : public Trigger<> { - public: - explicit ReadyTrigger(AlarmControlPanel *alarm_control_panel) { - alarm_control_panel->add_on_ready_callback([this]() { this->trigger(); }); - } + explicit ReadyTrigger(AlarmControlPanel *alarm_control_panel) { alarm_control_panel->add_listener(this); } + void on_ready() override { this->trigger(); } }; template class ArmAwayAction : public Action { diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp index dd3df5f8aa..c96d696862 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp @@ -16,7 +16,7 @@ using namespace esphome::alarm_control_panel; MQTTAlarmControlPanelComponent::MQTTAlarmControlPanelComponent(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {} void MQTTAlarmControlPanelComponent::setup() { - this->alarm_control_panel_->add_on_state_callback([this]() { this->publish_state(); }); + this->alarm_control_panel_->add_listener(this); this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) { auto call = this->alarm_control_panel_->make_call(); if (strcasecmp(payload.c_str(), "ARM_AWAY") == 0) { diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.h b/esphome/components/mqtt/mqtt_alarm_control_panel.h index 4ad37b7314..47d8610d0b 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.h +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.h @@ -11,7 +11,8 @@ namespace esphome { namespace mqtt { -class MQTTAlarmControlPanelComponent : public mqtt::MQTTComponent { +class MQTTAlarmControlPanelComponent final : public mqtt::MQTTComponent, + public alarm_control_panel::AlarmControlPanelListener { public: explicit MQTTAlarmControlPanelComponent(alarm_control_panel::AlarmControlPanel *alarm_control_panel); @@ -25,6 +26,12 @@ class MQTTAlarmControlPanelComponent : public mqtt::MQTTComponent { void dump_config() override; + // AlarmControlPanelListener interface + void on_state(alarm_control_panel::AlarmControlPanelState new_state, + alarm_control_panel::AlarmControlPanelState prev_state) override { + this->publish_state(); + } + protected: std::string component_type() const override; const EntityBase *get_entity() const override; diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp index f025435261..003acb06ff 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp @@ -133,7 +133,7 @@ void TemplateAlarmControlPanel::loop() { if ((!this->sensor_data_[info.store_index].last_chime_state) && (sensor->state)) { // Must be disarmed to chime if (this->current_state_ == ACP_STATE_DISARMED) { - this->chime_callback_.call(); + this->notify_chime(); } } // Record the sensor state change @@ -182,7 +182,7 @@ void TemplateAlarmControlPanel::loop() { // Call the ready state change callback if there was a change if (this->sensors_ready_ != sensors_ready) { this->sensors_ready_ = sensors_ready; - this->ready_callback_.call(); + this->notify_ready(); } #endif