-
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathnspanel_esphome_page_alarm.yaml
More file actions
285 lines (250 loc) · 13.1 KB
/
nspanel_esphome_page_alarm.yaml
File metadata and controls
285 lines (250 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
#####################################################################################################
##### NSPanel Easy - https://github.com/edwardtfn/NSPanel-Easy #####
#####################################################################################################
##### ESPHOME - Page alarm #####
##### PLEASE only make changes if it is necessary and also the required knowledge is available. #####
##### For normal use with the Blueprint, no changes are necessary. #####
#####################################################################################################
---
substitutions:
require_disarm_before_rearm: false
PAGE_ALARM_DELAY_DEFAULT: ${DELAY_DEFAULT}
PAGE_ALARM_DELAY_LONG: ${DELAY_LONG}
PAGE_ALARM_DELAY_SHORT: ${DELAY_SHORT}
TAG_PAGE_ALARM: nspanel.page.alarm
api:
actions:
# Updates the alarm settings page with current state and configuration, integrating with the panel's interface.
- action: page_alarm
variables:
state: string # Current state of the alarm system (e.g., "armed_home", "disarmed").
supported_features: int # Bitmask representing the alarm system's supported features, determining available controls on the page.
code_format: string # Format required for the alarm code (numeric, alphanumeric).
code_arm_required: bool # Indicates if a code is needed to arm the system.
entity: string # Entity ID for the alarm system, enabling state updates and control.
then:
# To do: This page constructor should be moved to Blueprint
- lambda: |-
if (current_page_id != get_page_id("alarm")) return;
// Helper function to update alarm button state and visibility
// Handles feature checking, logging, and UI updates for all buttons including disarm
auto update_alarm_button = [&](
const char* button_name, // Button prefix (e.g., "bt_home", "bt_disarm")
const char* armed_state, // State string (e.g., "armed_home", "disarmed")
bool is_armed,
bool is_triggered,
AlarmFeature feature_bit // Supported feature bit (ALWAYS_SHOW = always show)
) {
// Check if button should be processed
if (feature_bit != AlarmFeature::ALWAYS_SHOW &&
!((supported_features & static_cast<uint8_t>(feature_bit)) || (state == armed_state))) {
return;
}
ESP_LOGV("${TAG_PAGE_ALARM}", "Button - %s (state: %s)", button_name, armed_state);
const bool is_this_state = (state == armed_state);
// Update button appearance based on state
disp1->set_component_pic(
(std::string(button_name) + "_pic").c_str(),
is_this_state ? 43 : 42
);
const uint32_t bg_color = is_this_state ? Colors::GREEN : Colors::GRAY_LIGHT;
const uint32_t font_color = is_this_state ? Colors::WHITE : Colors::BLACK;
disp1->set_component_background_color((std::string(button_name) + "_text").c_str(), bg_color);
disp1->set_component_background_color((std::string(button_name) + "_icon").c_str(), bg_color);
disp1->set_component_font_color((std::string(button_name) + "_text").c_str(), font_color);
disp1->set_component_font_color((std::string(button_name) + "_icon").c_str(), font_color);
// Handle visibility based on button type and configuration
bool should_hide;
if (feature_bit == AlarmFeature::ALWAYS_SHOW) {
// Disarm button: hide when already disarmed
should_hide = is_this_state;
} else {
// Armed mode buttons: hide based on configuration
#ifdef USE_REQUIRE_DISARM_BEFORE_REARM
should_hide = is_this_state || is_triggered || is_armed;
#else // USE_REQUIRE_DISARM_BEFORE_REARM
should_hide = is_this_state || is_triggered;
#endif // USE_REQUIRE_DISARM_BEFORE_REARM
}
#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) // ESPHome v2025.11.0+
disp1->set_component_visibility(button_name, !should_hide);
#else // ESPHome < v2025.11.0
disp1->send_command_printf("vis %s,%i", button_name, should_hide ? 0 : 1);
#endif // ESPHOME_VERSION_CODE
feed_wdt_delay(${PAGE_ALARM_DELAY_SHORT});
};
// Set detailed entity sensor
ESP_LOGV("${TAG_PAGE_ALARM}", "Set detailed entity sensor");
detailed_entity->publish_state(entity.c_str());
// Determine alarm states
const bool is_armed = (state.find("armed_") == 0) || (state == "arming") || (state == "pending");
const bool is_triggered = (state == "triggered");
#ifdef USE_REQUIRE_DISARM_BEFORE_REARM
ESP_LOGV("${TAG_PAGE_ALARM}", "Alarm is armed: %s", YESNO(is_armed));
#endif // USE_REQUIRE_DISARM_BEFORE_REARM
ESP_LOGV("${TAG_PAGE_ALARM}", "Alarm is triggered: %s", YESNO(is_triggered));
// Update all alarm mode buttons
update_alarm_button("bt_home", "armed_home", is_armed, is_triggered, AlarmFeature::ARM_HOME);
update_alarm_button("bt_away", "armed_away", is_armed, is_triggered, AlarmFeature::ARM_AWAY);
update_alarm_button("bt_night", "armed_night", is_armed, is_triggered, AlarmFeature::ARM_NIGHT);
update_alarm_button("bt_vacat", "armed_vacation", is_armed, is_triggered, AlarmFeature::ARM_VACATION);
update_alarm_button("bt_bypass", "armed_custom_bypass", is_armed, is_triggered, AlarmFeature::ARM_CUSTOM_BYPASS);
update_alarm_button("bt_disarm", "disarmed", is_armed, is_triggered, AlarmFeature::ALWAYS_SHOW);
// Page - Params
ESP_LOGV("${TAG_PAGE_ALARM}", "Page - Params");
disp1->set_component_text("code_format", code_format.c_str());
disp1->set_component_text("code_arm_req", code_arm_required ? "1" : "0");
feed_wdt_delay(${PAGE_ALARM_DELAY_SHORT});
// Buttons - Text
ESP_LOGV("${TAG_PAGE_ALARM}", "Buttons - Text");
disp1->set_component_text("bt_home_text", wrapText("${LANG_ALARM_HOME}",
10, ${LANG_BYTES_PER_CHAR}).c_str());
disp1->set_component_text("bt_away_text", wrapText("${LANG_ALARM_AWAY}",
10, ${LANG_BYTES_PER_CHAR}).c_str());
disp1->set_component_text("bt_night_text", wrapText("${LANG_ALARM_NIGHT}",
10, ${LANG_BYTES_PER_CHAR}).c_str());
disp1->set_component_text("bt_vacat_text", wrapText("${LANG_ALARM_VACATION}",
10, ${LANG_BYTES_PER_CHAR}).c_str());
disp1->set_component_text("bt_bypass_text", wrapText("${LANG_ALARM_BYPASS}",
10, ${LANG_BYTES_PER_CHAR}).c_str());
disp1->set_component_text("bt_disarm_text", wrapText("${LANG_ALARM_DISARM}",
10, ${LANG_BYTES_PER_CHAR}).c_str());
feed_wdt_delay(${PAGE_ALARM_DELAY_SHORT});
ESP_LOGV("${TAG_PAGE_ALARM}", "Done!");
esphome:
platformio_options:
build_flags:
- -D NSPANEL_EASY_PAGE_ALARM
nspanel_easy:
require_disarm_before_rearm: ${require_disarm_before_rearm}
script:
- id: !extend event_from_display # Defined by hw_display
then:
- lambda: |-
if (params[0] == "alarm") {
// CSV Format: alarm,key,code_format,code_arm_req
// params[0]=page, params[1]=key, params[2]=code_format, params[3]=code_arm_req, params[4]=mui
if (params_count != 5) {
ESP_LOGW("${TAG_PAGE_ALARM}", "Bad params");
return;
}
const std::string& key = params[1];
const std::string& code_format = params[2];
const std::string& code_arm_req = params[3];
const std::string& mui = params[4];
ESP_LOGV("${TAG_PAGE_ALARM}", "key=%s, format=%s, arm_req=%s, mui=%s",
key.c_str(), code_format.c_str(), code_arm_req.c_str(), mui.c_str());
if (code_format == "number" && (key == "disarm" || code_arm_req == "1")) {
ESP_LOGV("${TAG_PAGE_ALARM}", "Open keypad");
goto_page->execute(get_page_id("keyb_num"));
disp1->set_component_value("page_id", get_page_id("alarm"));
disp1->set_component_text("domain", "alarm");
disp1->set_component_text("key", key.c_str());
disp1->set_component_text("value", "click");
disp1->set_component_text("entity", detailed_entity->state.c_str());
disp1->set_component_text("title", mui.c_str());
} else {
ESP_LOGV("${TAG_PAGE_ALARM}", "Executing direct alarm action: key=%s, format=%s",
key.c_str(), code_format.c_str());
alarm_control_panel_action->execute(detailed_entity->state.c_str(), key.c_str(),
code_format.c_str(), "");
} // endif numeric keypad required
}
- id: alarm_control_panel_action
mode: single
parameters:
entity: string
key: string
code_format: string
pin: string
then:
- lambda: |-
ESP_LOGV("${TAG_PAGE_ALARM}", "Alarm control panel action call");
ESP_LOGV("${TAG_PAGE_ALARM}", " entity_id: %s", entity.c_str());
ESP_LOGV("${TAG_PAGE_ALARM}", " key: %s", key.c_str());
ESP_LOGV("${TAG_PAGE_ALARM}", " pin provided: %s", pin.empty() ? "NO" : "YES");
- if:
condition:
- lambda: return pin.empty();
then:
- homeassistant.action:
action: &alarm_control_panel_action !lambda |-
static std::string action_str;
action_str = "alarm_control_panel.alarm_";
if (key != "disarm") action_str += "arm_";
if (key == "bypass") action_str += "custom_";
action_str += key;
return action_str.c_str();
data:
entity_id: !lambda return entity;
else:
- homeassistant.action:
action: *alarm_control_panel_action
data:
entity_id: !lambda return entity;
code: !lambda return pin;
- id: !extend dump_config # Defined by nspanel_esphome_core_base.yaml
then:
- lambda: |-
// Check for requirements
#if !defined(NSPANEL_EASY_PAGE_KEYB_NUM)
#error "The package nspanel_esphome_standard_page_keyb_num.yaml is required."
#endif
- id: page_alarm
mode: single
then: # There's nothing here so far
- id: !extend page_change
then:
- lambda: |-
if (new_page_id == get_page_id("alarm"))
page_alarm->execute();
- id: !extend stop_all
then:
- lambda: |-
alarm_control_panel_action->stop();
update_alarm_icon->stop();
- id: !extend stop_page_constructors
then:
- lambda: page_alarm->stop();
- id: update_alarm_icon
mode: single
parameters:
component: string
state: string
then:
- lambda: |-
const char* icon = Icons::SHIELD_ALERT_OUTLINE;
uint16_t color = Colors::WHITE;
// Optimized state checking using first character for fast branching
if (state[0] == 'd') { // "disarmed" or "disarming"
icon = Icons::SHIELD_OFF_OUTLINE;
color = (state == "disarming") ? Colors::YELLOW : Colors::WHITE;
} else if (state[0] == 'a') { // "armed_*" or "arming"
if (state == "arming") {
icon = Icons::SHIELD_OUTLINE;
color = Colors::YELLOW;
} else {
color = Colors::GREEN;
if (state == "armed_home") {
icon = Icons::SHIELD_HOME_OUTLINE;
} else if (state == "armed_away") {
icon = Icons::SHIELD_LOCK_OUTLINE;
} else if (state == "armed_night") {
icon = Icons::SHIELD_MOON_OUTLINE;
} else if (state == "armed_vacation") {
icon = Icons::SHIELD_AIRPLANE_OUTLINE;
} else if (state == "armed_custom_bypass") {
icon = Icons::SHIELD_HALF_FULL;
}
}
} else if (state == "pending") {
icon = Icons::SHIELD_OUTLINE;
color = Colors::YELLOW;
} else if (state == "triggered") {
icon = Icons::SHIELD_ALERT_OUTLINE;
color = Colors::RED;
}
// Apply the icon and color to the display component
disp1->set_component_text(component.c_str(), icon);
disp1->set_component_font_color(component.c_str(), static_cast<uint16_t>(color));
...