ESPHome 2026.11.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
12ESPHOME_LOG_TAG(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->command_topic_source_(), [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", LOG_STR_ARG(this->log_name_()));
63 } else if (!apply_command(call, state)) {
64 ESP_LOGW(TAG, "'%s': Received unknown state in JSON payload: %s", LOG_STR_ARG(this->log_name_()), 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", LOG_STR_ARG(this->log_name_()), payload.c_str());
74 }
75 call.perform();
76 });
77}
78
80 LOG_MQTT_COMPONENT(true, true);
81 ESP_LOGCONFIG(TAG,
82 " Supported Features: %" PRIu32 "\n"
83 " Requires Code to Disarm: %s\n"
84 " Requires Code To Arm: %s",
88}
89
91 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
92 JsonArray supported_features = root[MQTT_SUPPORTED_FEATURES].to<JsonArray>();
93 const uint32_t acp_supported_features = this->alarm_control_panel_->get_supported_features();
94 if (acp_supported_features & ACP_FEAT_ARM_AWAY) {
95 supported_features.add(ESPHOME_F("arm_away"));
96 }
97 if (acp_supported_features & ACP_FEAT_ARM_HOME) {
98 supported_features.add(ESPHOME_F("arm_home"));
99 }
100 if (acp_supported_features & ACP_FEAT_ARM_NIGHT) {
101 supported_features.add(ESPHOME_F("arm_night"));
102 }
103 if (acp_supported_features & ACP_FEAT_ARM_VACATION) {
104 supported_features.add(ESPHOME_F("arm_vacation"));
105 }
106 if (acp_supported_features & ACP_FEAT_ARM_CUSTOM_BYPASS) {
107 supported_features.add(ESPHOME_F("arm_custom_bypass"));
108 }
109 if (acp_supported_features & ACP_FEAT_TRIGGER) {
110 supported_features.add(ESPHOME_F("trigger"));
111 }
112 root[MQTT_CODE_DISARM_REQUIRED] = this->alarm_control_panel_->get_requires_code();
113 root[MQTT_CODE_ARM_REQUIRED] = this->alarm_control_panel_->get_requires_code_to_arm();
114}
115
117const EntityBase *MQTTAlarmControlPanelComponent::get_entity() const { return this->alarm_control_panel_; }
118
121 char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
122 return this->publish(this->get_state_topic_to_(topic_buf),
123 alarm_state_to_mqtt_str(this->alarm_control_panel_->get_state()));
124}
125
126} // namespace esphome::mqtt
127
128#endif
129#endif // USE_MQTT
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_
const LogString * log_name_() const
Entity name for a "%s" log argument.
void subscribe(T &&topic, F &&callback, uint8_t qos=0)
Subscribe to a MQTT topic.
MQTTTopicSource command_topic_source_() const
The command topic as a subscription source. A lambda topic is read once and copied,...
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).
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")
ESPHOME_LOG_TAG(TAG, "alarm_control_panel")
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:28
MQTT_COMPONENT_TYPE(MQTTAlarmControlPanelComponent, "alarm_control_panel") const EntityBase *MQTTAlarmControlPanelComponent
const __FlashStringHelper * ProgmemStr
Definition progmem.h:31
Simple Helper struct used for Home Assistant MQTT send_discovery().