Adds MQTT component to Alarm Control panel component (#7188)

This commit is contained in:
Nate Clark 2024-08-05 23:53:52 -04:00 committed by GitHub
parent 3ba9caa118
commit 7074fa06ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 260 additions and 75 deletions

View file

@ -1,16 +1,17 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import web_server
from esphome import automation from esphome import automation
from esphome.automation import maybe_simple_id from esphome.automation import maybe_simple_id
from esphome.core import CORE, coroutine_with_priority import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_CODE,
CONF_ID, CONF_ID,
CONF_MQTT_ID,
CONF_ON_STATE, CONF_ON_STATE,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_CODE,
CONF_WEB_SERVER_ID, CONF_WEB_SERVER_ID,
) )
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@grahambrown11", "@hwstar"] CODEOWNERS = ["@grahambrown11", "@hwstar"]
@ -77,67 +78,72 @@ AlarmControlPanelCondition = alarm_control_panel_ns.class_(
"AlarmControlPanelCondition", automation.Condition "AlarmControlPanelCondition", automation.Condition
) )
ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( ALARM_CONTROL_PANEL_SCHEMA = (
web_server.WEBSERVER_SORTING_SCHEMA cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
).extend( .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
{ .extend(
cv.GenerateID(): cv.declare_id(AlarmControlPanel), {
cv.Optional(CONF_ON_STATE): automation.validate_automation( cv.GenerateID(): cv.declare_id(AlarmControlPanel),
{ cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), mqtt.MQTTAlarmControlPanelComponent
} ),
), cv.Optional(CONF_ON_STATE): automation.validate_automation(
cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation( {
{ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger), }
} ),
), cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation(
cv.Optional(CONF_ON_ARMING): automation.validate_automation( {
{ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmingTrigger), }
} ),
), cv.Optional(CONF_ON_ARMING): automation.validate_automation(
cv.Optional(CONF_ON_PENDING): automation.validate_automation( {
{ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmingTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PendingTrigger), }
} ),
), cv.Optional(CONF_ON_PENDING): automation.validate_automation(
cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation( {
{ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PendingTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedHomeTrigger), }
} ),
), cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation(
cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation( {
{ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedHomeTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedNightTrigger), }
} ),
), cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation(
cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation( {
{ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedNightTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedAwayTrigger), }
} ),
), cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation(
cv.Optional(CONF_ON_DISARMED): automation.validate_automation( {
{ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedAwayTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DisarmedTrigger), }
} ),
), cv.Optional(CONF_ON_DISARMED): automation.validate_automation(
cv.Optional(CONF_ON_CLEARED): automation.validate_automation( {
{ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DisarmedTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger), }
} ),
), cv.Optional(CONF_ON_CLEARED): automation.validate_automation(
cv.Optional(CONF_ON_CHIME): automation.validate_automation( {
{ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChimeTrigger), }
} ),
), cv.Optional(CONF_ON_CHIME): automation.validate_automation(
cv.Optional(CONF_ON_READY): automation.validate_automation( {
{ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChimeTrigger),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReadyTrigger), }
} ),
), cv.Optional(CONF_ON_READY): automation.validate_automation(
} {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReadyTrigger),
}
),
}
)
) )
ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id( ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id(
@ -192,6 +198,9 @@ async def setup_alarm_control_panel_core_(var, config):
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
web_server_ = await cg.get_variable(webserver_id) web_server_ = await cg.get_variable(webserver_id)
web_server.add_entity_to_sorting_list(web_server_, var, config) web_server.add_entity_to_sorting_list(web_server_, var, config)
if mqtt_id := config.get(CONF_MQTT_ID):
mqtt_ = cg.new_Pvariable(mqtt_id, var)
await mqtt.register_mqtt_component(mqtt_, config)
async def register_alarm_control_panel(var, config): async def register_alarm_control_panel(var, config):

View file

@ -1,10 +1,11 @@
import re import re
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.automation import Condition from esphome.automation import Condition
import esphome.codegen as cg
from esphome.components import logger from esphome.components import logger
from esphome.components.esp32 import add_idf_sdkconfig_option
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_AVAILABILITY, CONF_AVAILABILITY,
CONF_BIRTH_MESSAGE, CONF_BIRTH_MESSAGE,
@ -13,21 +14,21 @@ from esphome.const import (
CONF_CLIENT_CERTIFICATE, CONF_CLIENT_CERTIFICATE,
CONF_CLIENT_CERTIFICATE_KEY, CONF_CLIENT_CERTIFICATE_KEY,
CONF_CLIENT_ID, CONF_CLIENT_ID,
CONF_COMMAND_TOPIC,
CONF_COMMAND_RETAIN, CONF_COMMAND_RETAIN,
CONF_COMMAND_TOPIC,
CONF_DISCOVERY, CONF_DISCOVERY,
CONF_DISCOVERY_OBJECT_ID_GENERATOR,
CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_PREFIX,
CONF_DISCOVERY_RETAIN, CONF_DISCOVERY_RETAIN,
CONF_DISCOVERY_UNIQUE_ID_GENERATOR, CONF_DISCOVERY_UNIQUE_ID_GENERATOR,
CONF_DISCOVERY_OBJECT_ID_GENERATOR,
CONF_ID, CONF_ID,
CONF_KEEPALIVE, CONF_KEEPALIVE,
CONF_LEVEL, CONF_LEVEL,
CONF_LOG_TOPIC, CONF_LOG_TOPIC,
CONF_ON_JSON_MESSAGE,
CONF_ON_MESSAGE,
CONF_ON_CONNECT, CONF_ON_CONNECT,
CONF_ON_DISCONNECT, CONF_ON_DISCONNECT,
CONF_ON_JSON_MESSAGE,
CONF_ON_MESSAGE,
CONF_PASSWORD, CONF_PASSWORD,
CONF_PAYLOAD, CONF_PAYLOAD,
CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_AVAILABLE,
@ -45,12 +46,11 @@ from esphome.const import (
CONF_USE_ABBREVIATIONS, CONF_USE_ABBREVIATIONS,
CONF_USERNAME, CONF_USERNAME,
CONF_WILL_MESSAGE, CONF_WILL_MESSAGE,
PLATFORM_BK72XX,
PLATFORM_ESP32, PLATFORM_ESP32,
PLATFORM_ESP8266, PLATFORM_ESP8266,
PLATFORM_BK72XX,
) )
from esphome.core import coroutine_with_priority, CORE from esphome.core import CORE, coroutine_with_priority
from esphome.components.esp32 import add_idf_sdkconfig_option
DEPENDENCIES = ["network"] DEPENDENCIES = ["network"]
@ -110,6 +110,9 @@ MQTTDisconnectTrigger = mqtt_ns.class_(
MQTTComponent = mqtt_ns.class_("MQTTComponent", cg.Component) MQTTComponent = mqtt_ns.class_("MQTTComponent", cg.Component)
MQTTConnectedCondition = mqtt_ns.class_("MQTTConnectedCondition", Condition) MQTTConnectedCondition = mqtt_ns.class_("MQTTConnectedCondition", Condition)
MQTTAlarmControlPanelComponent = mqtt_ns.class_(
"MQTTAlarmControlPanelComponent", MQTTComponent
)
MQTTBinarySensorComponent = mqtt_ns.class_("MQTTBinarySensorComponent", MQTTComponent) MQTTBinarySensorComponent = mqtt_ns.class_("MQTTBinarySensorComponent", MQTTComponent)
MQTTClimateComponent = mqtt_ns.class_("MQTTClimateComponent", MQTTComponent) MQTTClimateComponent = mqtt_ns.class_("MQTTClimateComponent", MQTTComponent)
MQTTCoverComponent = mqtt_ns.class_("MQTTCoverComponent", MQTTComponent) MQTTCoverComponent = mqtt_ns.class_("MQTTCoverComponent", MQTTComponent)

View file

@ -0,0 +1,128 @@
#include "mqtt_alarm_control_panel.h"
#include "esphome/core/log.h"
#include "mqtt_const.h"
#ifdef USE_MQTT
#ifdef USE_ALARM_CONTROL_PANEL
namespace esphome {
namespace mqtt {
static const char *const TAG = "mqtt.alarm_control_panel";
using namespace esphome::alarm_control_panel;
MQTTAlarmControlPanelComponent::MQTTAlarmControlPanelComponent(AlarmControlPanel *alarm_control_panel)
: alarm_control_panel_(alarm_control_panel) {}
void MQTTAlarmControlPanelComponent::setup() {
this->alarm_control_panel_->add_on_state_callback([this]() { this->publish_state(); });
this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) {
auto call = this->alarm_control_panel_->make_call();
if (strcasecmp(payload.c_str(), "ARM_AWAY") == 0) {
call.arm_away();
} else if (strcasecmp(payload.c_str(), "ARM_HOME") == 0) {
call.arm_home();
} else if (strcasecmp(payload.c_str(), "ARM_NIGHT") == 0) {
call.arm_night();
} else if (strcasecmp(payload.c_str(), "ARM_VACATION") == 0) {
call.arm_vacation();
} else if (strcasecmp(payload.c_str(), "ARM_CUSTOM_BYPASS") == 0) {
call.arm_custom_bypass();
} else if (strcasecmp(payload.c_str(), "DISARM") == 0) {
call.disarm();
} else if (strcasecmp(payload.c_str(), "PENDING") == 0) {
call.pending();
} else if (strcasecmp(payload.c_str(), "TRIGGERED") == 0) {
call.triggered();
} else {
ESP_LOGW(TAG, "'%s': Received unknown command payload %s", this->friendly_name().c_str(), payload.c_str());
}
call.perform();
});
}
void MQTTAlarmControlPanelComponent::dump_config() {
ESP_LOGCONFIG(TAG, "MQTT alarm_control_panel '%s':", this->alarm_control_panel_->get_name().c_str());
LOG_MQTT_COMPONENT(true, true)
ESP_LOGCONFIG(TAG, " Supported Features: %" PRIu32, this->alarm_control_panel_->get_supported_features());
ESP_LOGCONFIG(TAG, " Requires Code to Disarm: %s", YESNO(this->alarm_control_panel_->get_requires_code()));
ESP_LOGCONFIG(TAG, " Requires Code To Arm: %s", YESNO(this->alarm_control_panel_->get_requires_code_to_arm()));
}
void MQTTAlarmControlPanelComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
JsonArray supported_features = root.createNestedArray(MQTT_SUPPORTED_FEATURES);
const uint32_t acp_supported_features = this->alarm_control_panel_->get_supported_features();
if (acp_supported_features & ACP_FEAT_ARM_AWAY) {
supported_features.add("arm_away");
}
if (acp_supported_features & ACP_FEAT_ARM_HOME) {
supported_features.add("arm_home");
}
if (acp_supported_features & ACP_FEAT_ARM_NIGHT) {
supported_features.add("arm_night");
}
if (acp_supported_features & ACP_FEAT_ARM_VACATION) {
supported_features.add("arm_vacation");
}
if (acp_supported_features & ACP_FEAT_ARM_CUSTOM_BYPASS) {
supported_features.add("arm_custom_bypass");
}
if (acp_supported_features & ACP_FEAT_TRIGGER) {
supported_features.add("trigger");
}
root[MQTT_CODE_DISARM_REQUIRED] = this->alarm_control_panel_->get_requires_code();
root[MQTT_CODE_ARM_REQUIRED] = this->alarm_control_panel_->get_requires_code_to_arm();
}
std::string MQTTAlarmControlPanelComponent::component_type() const { return "alarm_control_panel"; }
const EntityBase *MQTTAlarmControlPanelComponent::get_entity() const { return this->alarm_control_panel_; }
bool MQTTAlarmControlPanelComponent::send_initial_state() { return this->publish_state(); }
bool MQTTAlarmControlPanelComponent::publish_state() {
bool success = true;
const char *state_s = "";
switch (this->alarm_control_panel_->get_state()) {
case ACP_STATE_DISARMED:
state_s = "disarmed";
break;
case ACP_STATE_ARMED_HOME:
state_s = "armed_home";
break;
case ACP_STATE_ARMED_AWAY:
state_s = "armed_away";
break;
case ACP_STATE_ARMED_NIGHT:
state_s = "armed_night";
break;
case ACP_STATE_ARMED_VACATION:
state_s = "armed_vacation";
break;
case ACP_STATE_ARMED_CUSTOM_BYPASS:
state_s = "armed_custom_bypass";
break;
case ACP_STATE_PENDING:
state_s = "pending";
break;
case ACP_STATE_ARMING:
state_s = "arming";
break;
case ACP_STATE_DISARMING:
state_s = "disarming";
break;
case ACP_STATE_TRIGGERED:
state_s = "triggered";
break;
default:
state_s = "unknown";
}
if (!this->publish(this->get_state_topic_(), state_s))
success = false;
return success;
}
} // namespace mqtt
} // namespace esphome
#endif
#endif // USE_MQTT

View file

@ -0,0 +1,39 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_MQTT
#ifdef USE_ALARM_CONTROL_PANEL
#include "mqtt_component.h"
#include "esphome/components/alarm_control_panel/alarm_control_panel.h"
namespace esphome {
namespace mqtt {
class MQTTAlarmControlPanelComponent : public mqtt::MQTTComponent {
public:
explicit MQTTAlarmControlPanelComponent(alarm_control_panel::AlarmControlPanel *alarm_control_panel);
void setup() override;
void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override;
bool send_initial_state() override;
bool publish_state();
void dump_config() override;
protected:
std::string component_type() const override;
const EntityBase *get_entity() const override;
alarm_control_panel::AlarmControlPanel *alarm_control_panel_;
};
} // namespace mqtt
} // namespace esphome
#endif
#endif // USE_MQTT

View file

@ -426,3 +426,9 @@ valve:
} else { } else {
return VALVE_CLOSED; return VALVE_CLOSED;
} }
alarm_control_panel:
- platform: template
name: Alarm Control Panel
binary_sensors:
- input: some_binary_sensor