ESPHome 2025.12.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
29static const LogString *sensor_type_to_string(AlarmSensorType type) {
30 switch (type) {
32 return LOG_STR("instant");
34 return LOG_STR("delayed_follower");
36 return LOG_STR("instant_always");
38 default:
39 return LOG_STR("delayed");
40 }
41}
42#endif
43
45 ESP_LOGCONFIG(TAG,
46 "TemplateAlarmControlPanel:\n"
47 " Current State: %s\n"
48 " Number of Codes: %u\n"
49 " Requires Code To Arm: %s\n"
50 " Arming Away Time: %" PRIu32 "s\n"
51 " Arming Home Time: %" PRIu32 "s\n"
52 " Arming Night Time: %" PRIu32 "s\n"
53 " Pending Time: %" PRIu32 "s\n"
54 " Trigger Time: %" PRIu32 "s\n"
55 " Supported Features: %" PRIu32,
56 LOG_STR_ARG(alarm_control_panel_state_to_string(this->current_state_)), this->codes_.size(),
57 YESNO(!this->codes_.empty() && this->requires_code_to_arm_), (this->arming_away_time_ / 1000),
58 (this->arming_home_time_ / 1000), (this->arming_night_time_ / 1000), (this->pending_time_ / 1000),
59 (this->trigger_time_ / 1000), this->get_supported_features());
60#ifdef USE_BINARY_SENSOR
61 for (auto const &[sensor, info] : this->sensor_map_) {
62 ESP_LOGCONFIG(TAG,
63 " Binary Sensor:\n"
64 " Name: %s\n"
65 " Type: %s\n"
66 " Armed home bypass: %s\n"
67 " Armed night bypass: %s\n"
68 " Auto bypass: %s\n"
69 " Chime mode: %s",
70 sensor->get_name().c_str(), LOG_STR_ARG(sensor_type_to_string(info.type)),
71 TRUEFALSE(info.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME),
72 TRUEFALSE(info.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT),
73 TRUEFALSE(info.flags & BINARY_SENSOR_MODE_BYPASS_AUTO),
74 TRUEFALSE(info.flags & BINARY_SENSOR_MODE_CHIME));
75 }
76#endif
77}
78
82 uint8_t value;
84 if (this->pref_.load(&value)) {
86 }
87 }
88 this->desired_state_ = this->current_state_;
89}
90
92 // change from ARMING to ARMED_x after the arming_time_ has passed
93 if (this->current_state_ == ACP_STATE_ARMING) {
94 auto delay = this->arming_away_time_;
97 }
100 }
101 if ((millis() - this->last_update_) > delay) {
102 this->bypass_before_arming();
103 this->publish_state(this->desired_state_);
104 }
105 return;
106 }
107 // change from PENDING to TRIGGERED after the delay_time_ has passed
108 if (this->current_state_ == ACP_STATE_PENDING && (millis() - this->last_update_) > this->pending_time_) {
110 return;
111 }
112 auto next_state = this->current_state_;
113 // reset triggered if all clear
114 if (this->current_state_ == ACP_STATE_TRIGGERED && this->trigger_time_ > 0 &&
115 (millis() - this->last_update_) > this->trigger_time_) {
116 next_state = this->desired_state_;
117 }
118
119 bool delayed_sensor_faulted = false;
120 bool instant_sensor_faulted = false;
121
122#ifdef USE_BINARY_SENSOR
123 // Test all of the sensors regardless of the alarm panel state
124 for (auto const &[sensor, info] : this->sensor_map_) {
125 // Check for chime zones
126 if (info.flags & BINARY_SENSOR_MODE_CHIME) {
127 // Look for the transition from closed to open
128 if ((!this->sensor_data_[info.store_index].last_chime_state) && (sensor->state)) {
129 // Must be disarmed to chime
130 if (this->current_state_ == ACP_STATE_DISARMED) {
131 this->chime_callback_.call();
132 }
133 }
134 // Record the sensor state change
135 this->sensor_data_[info.store_index].last_chime_state = sensor->state;
136 }
137 // Check for faulted sensors
138 if (sensor->state) {
139 // Skip if auto bypassed
140 if (std::count(this->bypassed_sensor_indicies_.begin(), this->bypassed_sensor_indicies_.end(),
141 info.store_index) == 1) {
142 continue;
143 }
144 // Skip if bypass armed home
146 continue;
147 }
148 // Skip if bypass armed night
150 continue;
151 }
152
153 switch (info.type) {
155 next_state = ACP_STATE_TRIGGERED;
156 [[fallthrough]];
158 instant_sensor_faulted = true;
159 break;
161 // Look to see if we are in the pending state
162 if (this->current_state_ == ACP_STATE_PENDING) {
163 delayed_sensor_faulted = true;
164 } else {
165 instant_sensor_faulted = true;
166 }
167 break;
169 default:
170 delayed_sensor_faulted = true;
171 }
172 }
173 }
174 // Update all sensors ready flag
175 bool sensors_ready = !(instant_sensor_faulted || delayed_sensor_faulted);
176
177 // Call the ready state change callback if there was a change
178 if (this->sensors_ready_ != sensors_ready) {
179 this->sensors_ready_ = sensors_ready;
180 this->ready_callback_.call();
181 }
182
183#endif
184 if (this->is_state_armed(next_state) && (!this->sensors_ready_)) {
185 // Instant sensors
186 if (instant_sensor_faulted) {
188 } else if (delayed_sensor_faulted) {
189 // Delayed sensors
190 if ((this->pending_time_ > 0) && (this->current_state_ != ACP_STATE_TRIGGERED)) {
192 } else {
194 }
195 }
196 } else if (next_state != this->current_state_) {
197 this->publish_state(next_state);
198 }
199}
200
202 if (!this->codes_.empty()) {
203 if (code.has_value()) {
204 ESP_LOGVV(TAG, "Checking code: %s", code.value().c_str());
205 return (std::count(this->codes_.begin(), this->codes_.end(), code.value()) == 1);
206 }
207 ESP_LOGD(TAG, "No code provided");
208 return false;
209 }
210 return true;
211}
212
214 uint32_t features = ACP_FEAT_ARM_AWAY | ACP_FEAT_TRIGGER;
215 if (this->supports_arm_home_) {
216 features |= ACP_FEAT_ARM_HOME;
217 }
218 if (this->supports_arm_night_) {
219 features |= ACP_FEAT_ARM_NIGHT;
220 }
221 return features;
222}
223
225 uint32_t delay) {
226 if (this->current_state_ != ACP_STATE_DISARMED) {
227 ESP_LOGW(TAG, "Cannot arm when not disarmed");
228 return;
229 }
230 if (this->requires_code_to_arm_ && !this->is_code_valid_(std::move(code))) {
231 ESP_LOGW(TAG, "Not arming code doesn't match");
232 return;
233 }
234 this->desired_state_ = state;
235 if (delay > 0) {
237 } else {
238 this->bypass_before_arming();
239 this->publish_state(state);
240 }
241}
242
244#ifdef USE_BINARY_SENSOR
245 for (auto const &[sensor, info] : this->sensor_map_) {
246 // Check for faulted bypass_auto sensors and remove them from monitoring
247 if ((info.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor->state)) {
248 ESP_LOGW(TAG, "'%s' is faulted and will be automatically bypassed", sensor->get_name().c_str());
249 this->bypassed_sensor_indicies_.push_back(info.store_index);
250 }
251 }
252#endif
253}
254
256 if (call.get_state()) {
257 if (call.get_state() == ACP_STATE_ARMED_AWAY) {
258 this->arm_(call.get_code(), ACP_STATE_ARMED_AWAY, this->arming_away_time_);
259 } else if (call.get_state() == ACP_STATE_ARMED_HOME) {
260 this->arm_(call.get_code(), ACP_STATE_ARMED_HOME, this->arming_home_time_);
261 } else if (call.get_state() == ACP_STATE_ARMED_NIGHT) {
262 this->arm_(call.get_code(), ACP_STATE_ARMED_NIGHT, this->arming_night_time_);
263 } else if (call.get_state() == ACP_STATE_DISARMED) {
264 if (!this->is_code_valid_(call.get_code())) {
265 ESP_LOGW(TAG, "Not disarming code doesn't match");
266 return;
267 }
270#ifdef USE_BINARY_SENSOR
271 this->bypassed_sensor_indicies_.clear();
272#endif
273 } else if (call.get_state() == ACP_STATE_TRIGGERED) {
275 } else if (call.get_state() == ACP_STATE_PENDING) {
277 } else {
278 ESP_LOGE(TAG, "State not yet implemented: %s",
279 LOG_STR_ARG(alarm_control_panel_state_to_string(*call.get_state())));
280 }
281 }
282}
283
284} // namespace template_
285} // namespace esphome
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
uint32_t get_preference_hash()
Get a unique hash for storing preferences/settings for this entity.
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
uint16_t type
uint16_t flags
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:31
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:30