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.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 (
CONF_CODE,
CONF_ID,
CONF_MQTT_ID,
CONF_ON_STATE,
CONF_TRIGGER_ID,
CONF_CODE,
CONF_WEB_SERVER_ID,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@grahambrown11", "@hwstar"]
@ -77,67 +78,72 @@ AlarmControlPanelCondition = alarm_control_panel_ns.class_(
"AlarmControlPanelCondition", automation.Condition
)
ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
web_server.WEBSERVER_SORTING_SCHEMA
).extend(
{
cv.GenerateID(): cv.declare_id(AlarmControlPanel),
cv.Optional(CONF_ON_STATE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
}
),
cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger),
}
),
cv.Optional(CONF_ON_ARMING): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmingTrigger),
}
),
cv.Optional(CONF_ON_PENDING): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PendingTrigger),
}
),
cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedHomeTrigger),
}
),
cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedNightTrigger),
}
),
cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedAwayTrigger),
}
),
cv.Optional(CONF_ON_DISARMED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DisarmedTrigger),
}
),
cv.Optional(CONF_ON_CLEARED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger),
}
),
cv.Optional(CONF_ON_CHIME): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChimeTrigger),
}
),
cv.Optional(CONF_ON_READY): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReadyTrigger),
}
),
}
ALARM_CONTROL_PANEL_SCHEMA = (
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
.extend(
{
cv.GenerateID(): cv.declare_id(AlarmControlPanel),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
mqtt.MQTTAlarmControlPanelComponent
),
cv.Optional(CONF_ON_STATE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
}
),
cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger),
}
),
cv.Optional(CONF_ON_ARMING): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmingTrigger),
}
),
cv.Optional(CONF_ON_PENDING): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PendingTrigger),
}
),
cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedHomeTrigger),
}
),
cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedNightTrigger),
}
),
cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedAwayTrigger),
}
),
cv.Optional(CONF_ON_DISARMED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DisarmedTrigger),
}
),
cv.Optional(CONF_ON_CLEARED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger),
}
),
cv.Optional(CONF_ON_CHIME): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChimeTrigger),
}
),
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(
@ -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:
web_server_ = await cg.get_variable(webserver_id)
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):

View file

@ -1,10 +1,11 @@
import re
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import Condition
import esphome.codegen as cg
from esphome.components import logger
from esphome.components.esp32 import add_idf_sdkconfig_option
import esphome.config_validation as cv
from esphome.const import (
CONF_AVAILABILITY,
CONF_BIRTH_MESSAGE,
@ -13,21 +14,21 @@ from esphome.const import (
CONF_CLIENT_CERTIFICATE,
CONF_CLIENT_CERTIFICATE_KEY,
CONF_CLIENT_ID,
CONF_COMMAND_TOPIC,
CONF_COMMAND_RETAIN,
CONF_COMMAND_TOPIC,
CONF_DISCOVERY,
CONF_DISCOVERY_OBJECT_ID_GENERATOR,
CONF_DISCOVERY_PREFIX,
CONF_DISCOVERY_RETAIN,
CONF_DISCOVERY_UNIQUE_ID_GENERATOR,
CONF_DISCOVERY_OBJECT_ID_GENERATOR,
CONF_ID,
CONF_KEEPALIVE,
CONF_LEVEL,
CONF_LOG_TOPIC,
CONF_ON_JSON_MESSAGE,
CONF_ON_MESSAGE,
CONF_ON_CONNECT,
CONF_ON_DISCONNECT,
CONF_ON_JSON_MESSAGE,
CONF_ON_MESSAGE,
CONF_PASSWORD,
CONF_PAYLOAD,
CONF_PAYLOAD_AVAILABLE,
@ -45,12 +46,11 @@ from esphome.const import (
CONF_USE_ABBREVIATIONS,
CONF_USERNAME,
CONF_WILL_MESSAGE,
PLATFORM_BK72XX,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_BK72XX,
)
from esphome.core import coroutine_with_priority, CORE
from esphome.components.esp32 import add_idf_sdkconfig_option
from esphome.core import CORE, coroutine_with_priority
DEPENDENCIES = ["network"]
@ -110,6 +110,9 @@ MQTTDisconnectTrigger = mqtt_ns.class_(
MQTTComponent = mqtt_ns.class_("MQTTComponent", cg.Component)
MQTTConnectedCondition = mqtt_ns.class_("MQTTConnectedCondition", Condition)
MQTTAlarmControlPanelComponent = mqtt_ns.class_(
"MQTTAlarmControlPanelComponent", MQTTComponent
)
MQTTBinarySensorComponent = mqtt_ns.class_("MQTTBinarySensorComponent", MQTTComponent)
MQTTClimateComponent = mqtt_ns.class_("MQTTClimateComponent", 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 {
return VALVE_CLOSED;
}
alarm_control_panel:
- platform: template
name: Alarm Control Panel
binary_sensors:
- input: some_binary_sensor