ESPHome  2024.11.0
template_alarm_control_panel.cpp
Go to the documentation of this file.
1 
3 #include <utility>
6 #include "esphome/core/helpers.h"
7 #include "esphome/core/log.h"
8 
9 namespace esphome {
10 namespace template_ {
11 
12 using namespace esphome::alarm_control_panel;
13 
14 static 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.
21  SensorDataStore sd;
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  default:
63  sensor_type = "delayed";
64  }
65  ESP_LOGCONFIG(TAG, " Sensor type: %s", sensor_type);
66  }
67 #endif
68 }
69 
71  ESP_LOGCONFIG(TAG, "Setting up Template AlarmControlPanel '%s'...", this->name_.c_str());
72  switch (this->restore_mode_) {
74  this->current_state_ = ACP_STATE_DISARMED;
75  break;
77  uint8_t value;
78  this->pref_ = global_preferences->make_preference<uint8_t>(this->get_object_id_hash());
79  if (this->pref_.load(&value)) {
80  this->current_state_ = static_cast<alarm_control_panel::AlarmControlPanelState>(value);
81  } else {
82  this->current_state_ = ACP_STATE_DISARMED;
83  }
84  break;
85  }
86  }
87  this->desired_state_ = this->current_state_;
88 }
89 
91  // change from ARMING to ARMED_x after the arming_time_ has passed
92  if (this->current_state_ == ACP_STATE_ARMING) {
93  auto delay = this->arming_away_time_;
94  if (this->desired_state_ == ACP_STATE_ARMED_HOME) {
95  delay = this->arming_home_time_;
96  }
97  if (this->desired_state_ == ACP_STATE_ARMED_NIGHT) {
98  delay = this->arming_night_time_;
99  }
100  if ((millis() - this->last_update_) > delay) {
101  this->publish_state(this->desired_state_);
102  }
103  return;
104  }
105  // change from PENDING to TRIGGERED after the delay_time_ has passed
106  if (this->current_state_ == ACP_STATE_PENDING && (millis() - this->last_update_) > this->pending_time_) {
107  this->publish_state(ACP_STATE_TRIGGERED);
108  return;
109  }
110  auto future_state = this->current_state_;
111  // reset triggered if all clear
112  if (this->current_state_ == ACP_STATE_TRIGGERED && this->trigger_time_ > 0 &&
113  (millis() - this->last_update_) > this->trigger_time_) {
114  future_state = this->desired_state_;
115  }
116 
117  bool delayed_sensor_not_ready = false;
118  bool instant_sensor_not_ready = false;
119 
120 #ifdef USE_BINARY_SENSOR
121  // Test all of the sensors in the list regardless of the alarm panel state
122  for (auto sensor_info : this->sensor_map_) {
123  // Check for chime zones
124  if ((sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME)) {
125  // Look for the transition from closed to open
126  if ((!this->sensor_data_[sensor_info.second.store_index].last_chime_state) && (sensor_info.first->state)) {
127  // Must be disarmed to chime
128  if (this->current_state_ == ACP_STATE_DISARMED) {
129  this->chime_callback_.call();
130  }
131  }
132  // Record the sensor state change
133  this->sensor_data_[sensor_info.second.store_index].last_chime_state = sensor_info.first->state;
134  }
135  // Check for triggered sensors
136  if (sensor_info.first->state) { // Sensor triggered?
137  // Skip if bypass armed home
138  if (this->current_state_ == ACP_STATE_ARMED_HOME &&
139  (sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)) {
140  continue;
141  }
142  // Skip if bypass armed night
143  if (this->current_state_ == ACP_STATE_ARMED_NIGHT &&
144  (sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT)) {
145  continue;
146  }
147 
148  // If sensor type is of type instant
149  if (sensor_info.second.type == ALARM_SENSOR_TYPE_INSTANT) {
150  instant_sensor_not_ready = true;
151  break;
152  }
153  // If sensor type is of type interior follower
154  if (sensor_info.second.type == ALARM_SENSOR_TYPE_DELAYED_FOLLOWER) {
155  // Look to see if we are in the pending state
156  if (this->current_state_ == ACP_STATE_PENDING) {
157  delayed_sensor_not_ready = true;
158  } else {
159  instant_sensor_not_ready = true;
160  }
161  }
162  // If sensor type is of type delayed
163  if (sensor_info.second.type == ALARM_SENSOR_TYPE_DELAYED) {
164  delayed_sensor_not_ready = true;
165  break;
166  }
167  }
168  }
169  // Update all sensors not ready flag
170  this->sensors_ready_ = ((!instant_sensor_not_ready) && (!delayed_sensor_not_ready));
171 
172  // Call the ready state change callback if there was a change
173  if (this->sensors_ready_ != this->sensors_ready_last_) {
174  this->ready_callback_.call();
175  this->sensors_ready_last_ = this->sensors_ready_;
176  }
177 
178 #endif
179  if (this->is_state_armed(future_state) && (!this->sensors_ready_)) {
180  // Instant sensors
181  if (instant_sensor_not_ready) {
182  this->publish_state(ACP_STATE_TRIGGERED);
183  } else if (delayed_sensor_not_ready) {
184  // Delayed sensors
185  if ((this->pending_time_ > 0) && (this->current_state_ != ACP_STATE_TRIGGERED)) {
186  this->publish_state(ACP_STATE_PENDING);
187  } else {
188  this->publish_state(ACP_STATE_TRIGGERED);
189  }
190  }
191  } else if (future_state != this->current_state_) {
192  this->publish_state(future_state);
193  }
194 }
195 
197  if (!this->codes_.empty()) {
198  if (code.has_value()) {
199  ESP_LOGVV(TAG, "Checking code: %s", code.value().c_str());
200  return (std::count(this->codes_.begin(), this->codes_.end(), code.value()) == 1);
201  }
202  ESP_LOGD(TAG, "No code provided");
203  return false;
204  }
205  return true;
206 }
207 
209  uint32_t features = ACP_FEAT_ARM_AWAY | ACP_FEAT_TRIGGER;
210  if (this->supports_arm_home_) {
211  features |= ACP_FEAT_ARM_HOME;
212  }
213  if (this->supports_arm_night_) {
214  features |= ACP_FEAT_ARM_NIGHT;
215  }
216  return features;
217 }
218 
219 bool TemplateAlarmControlPanel::get_requires_code() const { return !this->codes_.empty(); }
220 
222  uint32_t delay) {
223  if (this->current_state_ != ACP_STATE_DISARMED) {
224  ESP_LOGW(TAG, "Cannot arm when not disarmed");
225  return;
226  }
227  if (this->requires_code_to_arm_ && !this->is_code_valid_(std::move(code))) {
228  ESP_LOGW(TAG, "Not arming code doesn't match");
229  return;
230  }
231  this->desired_state_ = state;
232  if (delay > 0) {
233  this->publish_state(ACP_STATE_ARMING);
234  } else {
235  this->publish_state(state);
236  }
237 }
238 
240  if (call.get_state()) {
241  if (call.get_state() == ACP_STATE_ARMED_AWAY) {
242  this->arm_(call.get_code(), ACP_STATE_ARMED_AWAY, this->arming_away_time_);
243  } else if (call.get_state() == ACP_STATE_ARMED_HOME) {
244  this->arm_(call.get_code(), ACP_STATE_ARMED_HOME, this->arming_home_time_);
245  } else if (call.get_state() == ACP_STATE_ARMED_NIGHT) {
246  this->arm_(call.get_code(), ACP_STATE_ARMED_NIGHT, this->arming_night_time_);
247  } else if (call.get_state() == ACP_STATE_DISARMED) {
248  if (!this->is_code_valid_(call.get_code())) {
249  ESP_LOGW(TAG, "Not disarming code doesn't match");
250  return;
251  }
252  this->desired_state_ = ACP_STATE_DISARMED;
253  this->publish_state(ACP_STATE_DISARMED);
254  } else if (call.get_state() == ACP_STATE_TRIGGERED) {
255  this->publish_state(ACP_STATE_TRIGGERED);
256  } else if (call.get_state() == ACP_STATE_PENDING) {
257  this->publish_state(ACP_STATE_PENDING);
258  } else {
259  ESP_LOGE(TAG, "State not yet implemented: %s",
260  LOG_STR_ARG(alarm_control_panel_state_to_string(*call.get_state())));
261  }
262  }
263 }
264 
265 } // namespace template_
266 } // namespace esphome
value_type const & value() const
Definition: optional.h:89
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.
void control(const alarm_control_panel::AlarmControlPanelCall &call) override
bool has_value() const
Definition: optional.h:87
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
const LogString * alarm_control_panel_state_to_string(AlarmControlPanelState state)
Returns a string representation of the state.
ESPPreferences * global_preferences
uint8_t type
void arm_(optional< std::string > code, alarm_control_panel::AlarmControlPanelState state, uint32_t delay)
const optional< AlarmControlPanelState > & get_state() const
const uint32_t flags
Definition: stm32flash.h:85
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
Base class for all binary_sensor-type classes.
Definition: binary_sensor.h:37
esphome::sensor::Sensor * sensor
Definition: statsd.h:38
bool state
Definition: fan.h:34
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26