ESPHome 2025.9.0-dev
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,
33 " Current State: %s\n"
34 " Number of Codes: %u",
35 LOG_STR_ARG(alarm_control_panel_state_to_string(this->current_state_)), this->codes_.size());
36 if (!this->codes_.empty())
37 ESP_LOGCONFIG(TAG, " Requires Code To Arm: %s", YESNO(this->requires_code_to_arm_));
38 ESP_LOGCONFIG(TAG, " Arming Away Time: %" PRIu32 "s", (this->arming_away_time_ / 1000));
39 if (this->arming_home_time_ != 0)
40 ESP_LOGCONFIG(TAG, " Arming Home Time: %" PRIu32 "s", (this->arming_home_time_ / 1000));
41 if (this->arming_night_time_ != 0)
42 ESP_LOGCONFIG(TAG, " Arming Night Time: %" PRIu32 "s", (this->arming_night_time_ / 1000));
43 ESP_LOGCONFIG(TAG,
44 " Pending Time: %" PRIu32 "s\n"
45 " Trigger Time: %" PRIu32 "s\n"
46 " Supported Features: %" PRIu32,
47 (this->pending_time_ / 1000), (this->trigger_time_ / 1000), this->get_supported_features());
48#ifdef USE_BINARY_SENSOR
49 for (auto sensor_info : this->sensor_map_) {
50 ESP_LOGCONFIG(TAG, " Binary Sensor:");
51 ESP_LOGCONFIG(TAG,
52 " Name: %s\n"
53 " Armed home bypass: %s\n"
54 " Armed night bypass: %s\n"
55 " Auto bypass: %s\n"
56 " Chime mode: %s",
57 sensor_info.first->get_name().c_str(),
58 TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME),
59 TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT),
60 TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO),
61 TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME));
62 const char *sensor_type;
63 switch (sensor_info.second.type) {
65 sensor_type = "instant";
66 break;
68 sensor_type = "delayed_follower";
69 break;
71 sensor_type = "instant_always";
72 break;
74 default:
75 sensor_type = "delayed";
76 }
77 ESP_LOGCONFIG(TAG, " Sensor type: %s", sensor_type);
78 }
79#endif
80}
81
83 switch (this->restore_mode_) {
86 break;
88 uint8_t value;
90 if (this->pref_.load(&value)) {
92 } else {
94 }
95 break;
96 }
97 }
98 this->desired_state_ = this->current_state_;
99}
100
102 // change from ARMING to ARMED_x after the arming_time_ has passed
103 if (this->current_state_ == ACP_STATE_ARMING) {
104 auto delay = this->arming_away_time_;
106 delay = this->arming_home_time_;
107 }
110 }
111 if ((millis() - this->last_update_) > delay) {
112 this->bypass_before_arming();
113 this->publish_state(this->desired_state_);
114 }
115 return;
116 }
117 // change from PENDING to TRIGGERED after the delay_time_ has passed
118 if (this->current_state_ == ACP_STATE_PENDING && (millis() - this->last_update_) > this->pending_time_) {
120 return;
121 }
122 auto future_state = this->current_state_;
123 // reset triggered if all clear
124 if (this->current_state_ == ACP_STATE_TRIGGERED && this->trigger_time_ > 0 &&
125 (millis() - this->last_update_) > this->trigger_time_) {
126 future_state = this->desired_state_;
127 }
128
129 bool delayed_sensor_not_ready = false;
130 bool instant_sensor_not_ready = false;
131
132#ifdef USE_BINARY_SENSOR
133 // Test all of the sensors in the list regardless of the alarm panel state
134 for (auto sensor_info : this->sensor_map_) {
135 // Check for chime zones
136 if ((sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME)) {
137 // Look for the transition from closed to open
138 if ((!this->sensor_data_[sensor_info.second.store_index].last_chime_state) && (sensor_info.first->state)) {
139 // Must be disarmed to chime
140 if (this->current_state_ == ACP_STATE_DISARMED) {
141 this->chime_callback_.call();
142 }
143 }
144 // Record the sensor state change
145 this->sensor_data_[sensor_info.second.store_index].last_chime_state = sensor_info.first->state;
146 }
147 // Check for triggered sensors
148 if (sensor_info.first->state) { // Sensor triggered?
149 // Skip if auto bypassed
150 if (std::count(this->bypassed_sensor_indicies_.begin(), this->bypassed_sensor_indicies_.end(),
151 sensor_info.second.store_index) == 1) {
152 continue;
153 }
154 // Skip if bypass armed home
156 (sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)) {
157 continue;
158 }
159 // Skip if bypass armed night
161 (sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT)) {
162 continue;
163 }
164
165 switch (sensor_info.second.type) {
167 instant_sensor_not_ready = true;
168 break;
170 instant_sensor_not_ready = true;
171 future_state = ACP_STATE_TRIGGERED;
172 break;
174 // Look to see if we are in the pending state
175 if (this->current_state_ == ACP_STATE_PENDING) {
176 delayed_sensor_not_ready = true;
177 } else {
178 instant_sensor_not_ready = true;
179 }
180 break;
182 default:
183 delayed_sensor_not_ready = true;
184 }
185 }
186 }
187 // Update all sensors not ready flag
188 this->sensors_ready_ = ((!instant_sensor_not_ready) && (!delayed_sensor_not_ready));
189
190 // Call the ready state change callback if there was a change
191 if (this->sensors_ready_ != this->sensors_ready_last_) {
192 this->ready_callback_.call();
194 }
195
196#endif
197 if (this->is_state_armed(future_state) && (!this->sensors_ready_)) {
198 // Instant sensors
199 if (instant_sensor_not_ready) {
201 } else if (delayed_sensor_not_ready) {
202 // Delayed sensors
203 if ((this->pending_time_ > 0) && (this->current_state_ != ACP_STATE_TRIGGERED)) {
205 } else {
207 }
208 }
209 } else if (future_state != this->current_state_) {
210 this->publish_state(future_state);
211 }
212}
213
215 if (!this->codes_.empty()) {
216 if (code.has_value()) {
217 ESP_LOGVV(TAG, "Checking code: %s", code.value().c_str());
218 return (std::count(this->codes_.begin(), this->codes_.end(), code.value()) == 1);
219 }
220 ESP_LOGD(TAG, "No code provided");
221 return false;
222 }
223 return true;
224}
225
227 uint32_t features = ACP_FEAT_ARM_AWAY | ACP_FEAT_TRIGGER;
228 if (this->supports_arm_home_) {
229 features |= ACP_FEAT_ARM_HOME;
230 }
231 if (this->supports_arm_night_) {
232 features |= ACP_FEAT_ARM_NIGHT;
233 }
234 return features;
235}
236
237bool TemplateAlarmControlPanel::get_requires_code() const { return !this->codes_.empty(); }
238
240 uint32_t delay) {
241 if (this->current_state_ != ACP_STATE_DISARMED) {
242 ESP_LOGW(TAG, "Cannot arm when not disarmed");
243 return;
244 }
245 if (this->requires_code_to_arm_ && !this->is_code_valid_(std::move(code))) {
246 ESP_LOGW(TAG, "Not arming code doesn't match");
247 return;
248 }
249 this->desired_state_ = state;
250 if (delay > 0) {
252 } else {
253 this->bypass_before_arming();
254 this->publish_state(state);
255 }
256}
257
259#ifdef USE_BINARY_SENSOR
260 for (auto sensor_info : this->sensor_map_) {
261 // Check for sensors left on and set to bypass automatically and remove them from monitoring
262 if ((sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor_info.first->state)) {
263 ESP_LOGW(TAG, "'%s' is left on and will be automatically bypassed", sensor_info.first->get_name().c_str());
264 this->bypassed_sensor_indicies_.push_back(sensor_info.second.store_index);
265 }
266 }
267#endif
268}
269
271 if (call.get_state()) {
272 if (call.get_state() == ACP_STATE_ARMED_AWAY) {
273 this->arm_(call.get_code(), ACP_STATE_ARMED_AWAY, this->arming_away_time_);
274 } else if (call.get_state() == ACP_STATE_ARMED_HOME) {
275 this->arm_(call.get_code(), ACP_STATE_ARMED_HOME, this->arming_home_time_);
276 } else if (call.get_state() == ACP_STATE_ARMED_NIGHT) {
277 this->arm_(call.get_code(), ACP_STATE_ARMED_NIGHT, this->arming_night_time_);
278 } else if (call.get_state() == ACP_STATE_DISARMED) {
279 if (!this->is_code_valid_(call.get_code())) {
280 ESP_LOGW(TAG, "Not disarming code doesn't match");
281 return;
282 }
285#ifdef USE_BINARY_SENSOR
286 this->bypassed_sensor_indicies_.clear();
287#endif
288 } else if (call.get_state() == ACP_STATE_TRIGGERED) {
290 } else if (call.get_state() == ACP_STATE_PENDING) {
292 } else {
293 ESP_LOGE(TAG, "State not yet implemented: %s",
294 LOG_STR_ARG(alarm_control_panel_state_to_string(*call.get_state())));
295 }
296 }
297}
298
299} // namespace template_
300} // namespace esphome
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
uint32_t get_object_id_hash()
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:92
value_type const & value() const
Definition optional.h:94
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:29
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28