ESPHome 2026.5.0-dev
Loading...
Searching...
No Matches
mqtt_alarm_control_panel.cpp
Go to the documentation of this file.
2#include "esphome/core/log.h"
4
5#include "mqtt_const.h"
6
7#ifdef USE_MQTT
8#ifdef USE_ALARM_CONTROL_PANEL
9
10namespace esphome::mqtt {
11
12static const char *const TAG = "mqtt.alarm_control_panel";
13
14using namespace esphome::alarm_control_panel;
15
16// Alarm state MQTT strings indexed by AlarmControlPanelState enum (0-9)
17PROGMEM_STRING_TABLE(AlarmMqttStateStrings, "disarmed", "armed_home", "armed_away", "armed_night", "armed_vacation",
18 "armed_custom_bypass", "pending", "arming", "disarming", "triggered", "unknown");
19
20static ProgmemStr alarm_state_to_mqtt_str(AlarmControlPanelState state) {
21 return AlarmMqttStateStrings::get_progmem_str(static_cast<uint8_t>(state), AlarmMqttStateStrings::LAST_INDEX);
22}
23
25 : alarm_control_panel_(alarm_control_panel) {}
26
27static bool apply_command(AlarmControlPanelCall &call, const char *state) {
28 if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("ARM_AWAY")) == 0) {
29 call.arm_away();
30 } else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("ARM_HOME")) == 0) {
31 call.arm_home();
32 } else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("ARM_NIGHT")) == 0) {
33 call.arm_night();
34 } else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("ARM_VACATION")) == 0) {
35 call.arm_vacation();
36 } else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("ARM_CUSTOM_BYPASS")) == 0) {
37 call.arm_custom_bypass();
38 } else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("DISARM")) == 0) {
39 call.disarm();
40 } else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("PENDING")) == 0) {
41 call.pending();
42 } else if (ESPHOME_strcasecmp_P(state, ESPHOME_PSTR("TRIGGERED")) == 0) {
43 call.triggered();
44 } else {
45 return false;
46 }
47 return true;
48}
49
52 [this](AlarmControlPanelState /*state*/) { this->publish_state(); });
53 this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) {
54 auto call = this->alarm_control_panel_->make_call();
55 if (!payload.empty() && payload[0] == '{') {
56 // JSON payload: {"state": "DISARM", "code": "1234"}
57 JsonDocument doc = json::parse_json(payload);
58 JsonObject root = doc.as<JsonObject>();
59 if (!root.isNull()) {
60 const char *state = root[ESPHOME_F("state")];
61 if (state == nullptr) {
62 ESP_LOGW(TAG, "'%s': JSON payload missing 'state' key", this->friendly_name_().c_str());
63 } else if (!apply_command(call, state)) {
64 ESP_LOGW(TAG, "'%s': Received unknown state in JSON payload: %s", this->friendly_name_().c_str(), state);
65 } else {
66 const char *code = root[ESPHOME_F("code")];
67 if (code != nullptr) {
68 call.set_code(code);
69 }
70 }
71 }
72 } else if (!apply_command(call, payload.c_str())) {
73 ESP_LOGW(TAG, "'%s': Received unknown command payload %s", this->friendly_name_().c_str(), payload.c_str());
74 }
75 call.perform();
76 });
77}
78
80 ESP_LOGCONFIG(TAG, "MQTT alarm_control_panel '%s':", this->alarm_control_panel_->get_name().c_str());
81 LOG_MQTT_COMPONENT(true, true);
82 ESP_LOGCONFIG(TAG,
83 " Supported Features: %" PRIu32 "\n"
84 " Requires Code to Disarm: %s\n"
85 " Requires Code To Arm: %s",
89}
90
92 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
93 JsonArray supported_features = root[MQTT_SUPPORTED_FEATURES].to<JsonArray>();
94 const uint32_t acp_supported_features = this->alarm_control_panel_->get_supported_features();
95 if (acp_supported_features & ACP_FEAT_ARM_AWAY) {
96 supported_features.add(ESPHOME_F("arm_away"));
97 }
98 if (acp_supported_features & ACP_FEAT_ARM_HOME) {
99 supported_features.add(ESPHOME_F("arm_home"));
100 }
101 if (acp_supported_features & ACP_FEAT_ARM_NIGHT) {
102 supported_features.add(ESPHOME_F("arm_night"));
103 }
104 if (acp_supported_features & ACP_FEAT_ARM_VACATION) {
105 supported_features.add(ESPHOME_F("arm_vacation"));
106 }
107 if (acp_supported_features & ACP_FEAT_ARM_CUSTOM_BYPASS) {
108 supported_features.add(ESPHOME_F("arm_custom_bypass"));
109 }
110 if (acp_supported_features & ACP_FEAT_TRIGGER) {
111 supported_features.add(ESPHOME_F("trigger"));
112 }
113 root[MQTT_CODE_DISARM_REQUIRED] = this->alarm_control_panel_->get_requires_code();
114 root[MQTT_CODE_ARM_REQUIRED] = this->alarm_control_panel_->get_requires_code_to_arm();
115}
116
118const EntityBase *MQTTAlarmControlPanelComponent::get_entity() const { return this->alarm_control_panel_; }
119
122 char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
123 return this->publish(this->get_state_topic_to_(topic_buf),
124 alarm_state_to_mqtt_str(this->alarm_control_panel_->get_state()));
125}
126
127} // namespace esphome::mqtt
128
129#endif
130#endif // USE_MQTT
const StringRef & get_name() const
Definition entity_base.h:71
constexpr const char * c_str() const
Definition string_ref.h:73
void add_on_state_callback(F &&callback)
Add a callback for when the state of the alarm_control_panel changes.
virtual uint32_t get_supported_features() const =0
A numeric representation of the supported features as per HomeAssistant.
AlarmControlPanelState get_state() const
Get the state.
virtual bool get_requires_code_to_arm() const =0
Returns if the alarm_control_panel requires a code to arm.
virtual bool get_requires_code() const =0
Returns if the alarm_control_panel has a code.
AlarmControlPanelCall make_call()
Make a AlarmControlPanelCall.
MQTTAlarmControlPanelComponent(alarm_control_panel::AlarmControlPanel *alarm_control_panel)
void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override
alarm_control_panel::AlarmControlPanel * alarm_control_panel_
bool publish(const std::string &topic, const std::string &payload)
Send a MQTT message.
StringRef get_state_topic_to_(std::span< char, MQTT_DEFAULT_TOPIC_MAX_LEN > buf) const
Get the MQTT state topic into a buffer (no heap allocation for non-lambda custom topics).
const StringRef & friendly_name_() const
Get the friendly name of this MQTT component.
std::string get_command_topic_() const
Get the MQTT topic for listening to commands (allocates std::string).
void subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos=0)
Subscribe to a MQTT topic.
bool state
Definition fan.h:2
PROGMEM_STRING_TABLE(AlarmControlPanelStateStrings, "DISARMED", "ARMED_HOME", "ARMED_AWAY", "ARMED_NIGHT", "ARMED_VACATION", "ARMED_CUSTOM_BYPASS", "PENDING", "ARMING", "DISARMING", "TRIGGERED", "UNKNOWN")
bool parse_json(const std::string &data, const json_parse_t &f)
Parse a JSON string and run the provided json parse function if it's valid.
Definition json_util.cpp:27
MQTT_COMPONENT_TYPE(MQTTAlarmControlPanelComponent, "alarm_control_panel") const EntityBase *MQTTAlarmControlPanelComponent
const __FlashStringHelper * ProgmemStr
Definition progmem.h:27
Simple Helper struct used for Home Assistant MQTT send_discovery().