ESPHome 2025.5.0
Loading...
Searching...
No Matches
template_alarm_control_panel.cpp
Go to the documentation of this file.
1
3#include <utility>
7#include "esphome/core/log.h"
8
9namespace esphome {
10namespace template_ {
11
12using namespace esphome::alarm_control_panel;
13
14static const char *const TAG = "template.alarm_control_panel";
15
17
18#ifdef USE_BINARY_SENSOR
20 // Save the flags and type. Assign a store index for the per sensor data type.
22 sd.last_chime_state = false;
23 this->sensor_map_[sensor].flags = flags;
24 this->sensor_map_[sensor].type = type;
25 this->sensor_data_.push_back(sd);
26 this->sensor_map_[sensor].store_index = this->next_store_index_++;
27};
28#endif
29
31 ESP_LOGCONFIG(TAG, "TemplateAlarmControlPanel:");
32 ESP_LOGCONFIG(TAG, " Current State: %s", LOG_STR_ARG(alarm_control_panel_state_to_string(this->current_state_)));
33 ESP_LOGCONFIG(TAG, " Number of Codes: %u", this->codes_.size());
34 if (!this->codes_.empty())
35 ESP_LOGCONFIG(TAG, " Requires Code To Arm: %s", YESNO(this->requires_code_to_arm_));
36 ESP_LOGCONFIG(TAG, " Arming Away Time: %" PRIu32 "s", (this->arming_away_time_ / 1000));
37 if (this->arming_home_time_ != 0)
38 ESP_LOGCONFIG(TAG, " Arming Home Time: %" PRIu32 "s", (this->arming_home_time_ / 1000));
39 if (this->arming_night_time_ != 0)
40 ESP_LOGCONFIG(TAG, " Arming Night Time: %" PRIu32 "s", (this->arming_night_time_ / 1000));
41 ESP_LOGCONFIG(TAG, " Pending Time: %" PRIu32 "s", (this->pending_time_ / 1000));
42 ESP_LOGCONFIG(TAG, " Trigger Time: %" PRIu32 "s", (this->trigger_time_ / 1000));
43 ESP_LOGCONFIG(TAG, " Supported Features: %" PRIu32, this->get_supported_features());
44#ifdef USE_BINARY_SENSOR
45 for (auto sensor_info : this->sensor_map_) {
46 ESP_LOGCONFIG(TAG, " Binary Sensor:");
47 ESP_LOGCONFIG(TAG, " Name: %s", sensor_info.first->get_name().c_str());
48 ESP_LOGCONFIG(TAG, " Armed home bypass: %s",
49 TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME));
50 ESP_LOGCONFIG(TAG, " Armed night bypass: %s",
51 TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT));
52 ESP_LOGCONFIG(TAG, " Chime mode: %s", TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME));
53 const char *sensor_type;
54 switch (sensor_info.second.type) {
56 sensor_type = "instant";
57 break;
59 sensor_type = "delayed_follower";
60 break;
62 sensor_type = "instant_always";
63 break;
65 default:
66 sensor_type = "delayed";
67 }
68 ESP_LOGCONFIG(TAG, " Sensor type: %s", sensor_type);
69 }
70#endif
71}
72
74 ESP_LOGCONFIG(TAG, "Setting up Template AlarmControlPanel '%s'...", this->name_.c_str());
75 switch (this->restore_mode_) {
78 break;
80 uint8_t value;
82 if (this->pref_.load(&value)) {
84 } else {
86 }
87 break;
88 }
89 }
90 this->desired_state_ = this->current_state_;
91}
92
94 // change from ARMING to ARMED_x after the arming_time_ has passed
95 if (this->current_state_ == ACP_STATE_ARMING) {
96 auto delay = this->arming_away_time_;
99 }
102 }
103 if ((millis() - this->last_update_) > delay) {
104 this->publish_state(this->desired_state_);
105 }
106 return;
107 }
108 // change from PENDING to TRIGGERED after the delay_time_ has passed
109 if (this->current_state_ == ACP_STATE_PENDING && (millis() - this->last_update_) > this->pending_time_) {
111 return;
112 }
113 auto future_state = this->current_state_;
114 // reset triggered if all clear
115 if (this->current_state_ == ACP_STATE_TRIGGERED && this->trigger_time_ > 0 &&
116 (millis() - this->last_update_) > this->trigger_time_) {
117 future_state = this->desired_state_;
118 }
119
120 bool delayed_sensor_not_ready = false;
121 bool instant_sensor_not_ready = false;
122
123#ifdef USE_BINARY_SENSOR
124 // Test all of the sensors in the list regardless of the alarm panel state
125 for (auto sensor_info : this->sensor_map_) {
126 // Check for chime zones
127 if ((sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME)) {
128 // Look for the transition from closed to open
129 if ((!this->sensor_data_[sensor_info.second.store_index].last_chime_state) && (sensor_info.first->state)) {
130 // Must be disarmed to chime
131 if (this->current_state_ == ACP_STATE_DISARMED) {
132 this->chime_callback_.call();
133 }
134 }
135 // Record the sensor state change
136 this->sensor_data_[sensor_info.second.store_index].last_chime_state = sensor_info.first->state;
137 }
138 // Check for triggered sensors
139 if (sensor_info.first->state) { // Sensor triggered?
140 // Skip if bypass armed home
142 (sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)) {
143 continue;
144 }
145 // Skip if bypass armed night
147 (sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT)) {
148 continue;
149 }
150
151 switch (sensor_info.second.type) {
153 instant_sensor_not_ready = true;
154 break;
156 instant_sensor_not_ready = true;
157 future_state = ACP_STATE_TRIGGERED;
158 break;
160 // Look to see if we are in the pending state
161 if (this->current_state_ == ACP_STATE_PENDING) {
162 delayed_sensor_not_ready = true;
163 } else {
164 instant_sensor_not_ready = true;
165 }
166 break;
168 default:
169 delayed_sensor_not_ready = true;
170 }
171 }
172 }
173 // Update all sensors not ready flag
174 this->sensors_ready_ = ((!instant_sensor_not_ready) && (!delayed_sensor_not_ready));
175
176 // Call the ready state change callback if there was a change
177 if (this->sensors_ready_ != this->sensors_ready_last_) {
178 this->ready_callback_.call();
180 }
181
182#endif
183 if (this->is_state_armed(future_state) && (!this->sensors_ready_)) {
184 // Instant sensors
185 if (instant_sensor_not_ready) {
187 } else if (delayed_sensor_not_ready) {
188 // Delayed sensors
189 if ((this->pending_time_ > 0) && (this->current_state_ != ACP_STATE_TRIGGERED)) {
191 } else {
193 }
194 }
195 } else if (future_state != this->current_state_) {
196 this->publish_state(future_state);
197 }
198}
199
201 if (!this->codes_.empty()) {
202 if (code.has_value()) {
203 ESP_LOGVV(TAG, "Checking code: %s", code.value().c_str());
204 return (std::count(this->codes_.begin(), this->codes_.end(), code.value()) == 1);
205 }
206 ESP_LOGD(TAG, "No code provided");
207 return false;
208 }
209 return true;
210}
211
213 uint32_t features = ACP_FEAT_ARM_AWAY | ACP_FEAT_TRIGGER;
214 if (this->supports_arm_home_) {
215 features |= ACP_FEAT_ARM_HOME;
216 }
217 if (this->supports_arm_night_) {
218 features |= ACP_FEAT_ARM_NIGHT;
219 }
220 return features;
221}
222
223bool TemplateAlarmControlPanel::get_requires_code() const { return !this->codes_.empty(); }
224
226 uint32_t delay) {
227 if (this->current_state_ != ACP_STATE_DISARMED) {
228 ESP_LOGW(TAG, "Cannot arm when not disarmed");
229 return;
230 }
231 if (this->requires_code_to_arm_ && !this->is_code_valid_(std::move(code))) {
232 ESP_LOGW(TAG, "Not arming code doesn't match");
233 return;
234 }
235 this->desired_state_ = state;
236 if (delay > 0) {
238 } else {
239 this->publish_state(state);
240 }
241}
242
244 if (call.get_state()) {
245 if (call.get_state() == ACP_STATE_ARMED_AWAY) {
246 this->arm_(call.get_code(), ACP_STATE_ARMED_AWAY, this->arming_away_time_);
247 } else if (call.get_state() == ACP_STATE_ARMED_HOME) {
248 this->arm_(call.get_code(), ACP_STATE_ARMED_HOME, this->arming_home_time_);
249 } else if (call.get_state() == ACP_STATE_ARMED_NIGHT) {
250 this->arm_(call.get_code(), ACP_STATE_ARMED_NIGHT, this->arming_night_time_);
251 } else if (call.get_state() == ACP_STATE_DISARMED) {
252 if (!this->is_code_valid_(call.get_code())) {
253 ESP_LOGW(TAG, "Not disarming code doesn't match");
254 return;
255 }
258 } else if (call.get_state() == ACP_STATE_TRIGGERED) {
260 } else if (call.get_state() == ACP_STATE_PENDING) {
262 } else {
263 ESP_LOGE(TAG, "State not yet implemented: %s",
264 LOG_STR_ARG(alarm_control_panel_state_to_string(*call.get_state())));
265 }
266 }
267}
268
269} // namespace template_
270} // namespace esphome
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
uint32_t get_object_id_hash()
constexpr const char * c_str() const
Definition string_ref.h:68
const optional< AlarmControlPanelState > & get_state() const
bool is_state_armed(AlarmControlPanelState state)
void publish_state(AlarmControlPanelState state)
Set the state of the alarm_control_panel.
Base class for all binary_sensor-type classes.
bool has_value() const
Definition optional.h:87
value_type const & value() const
Definition optional.h:89
void arm_(optional< std::string > code, alarm_control_panel::AlarmControlPanelState state, uint32_t delay)
void add_sensor(binary_sensor::BinarySensor *sensor, uint16_t flags=0, AlarmSensorType type=ALARM_SENSOR_TYPE_DELAYED)
Add a binary_sensor to the alarm_panel.
std::map< binary_sensor::BinarySensor *, SensorInfo > sensor_map_
void control(const alarm_control_panel::AlarmControlPanelCall &call) override
uint8_t type
bool state
Definition fan.h:0
const LogString * alarm_control_panel_state_to_string(AlarmControlPanelState state)
Returns a string representation of the state.
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
ESPPreferences * global_preferences
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:28
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:27