mirror of
https://github.com/esphome/esphome.git
synced 2024-12-21 16:27:44 +01:00
Event entity support (#6451)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
b03d0f37a4
commit
c531a528f0
@ -119,6 +119,7 @@ esphome/components/esp32_rmt/* @jesserockz
|
||||
esphome/components/esp32_rmt_led_strip/* @jesserockz
|
||||
esphome/components/esp8266/* @esphome/core
|
||||
esphome/components/ethernet_info/* @gtjadsonsantos
|
||||
esphome/components/event/* @nohat
|
||||
esphome/components/exposure_notifications/* @OttoWinter
|
||||
esphome/components/ezo/* @ssieb
|
||||
esphome/components/ezo_pmp/* @carlos-sarmiento
|
||||
@ -359,6 +360,7 @@ esphome/components/tee501/* @Stock-M
|
||||
esphome/components/teleinfo/* @0hax
|
||||
esphome/components/template/alarm_control_panel/* @grahambrown11 @hwstar
|
||||
esphome/components/template/datetime/* @rfdarter
|
||||
esphome/components/template/event/* @nohat
|
||||
esphome/components/template/fan/* @ssieb
|
||||
esphome/components/text/* @mauritskorse
|
||||
esphome/components/thermostat/* @kbx81
|
||||
|
@ -1702,6 +1702,32 @@ message TimeCommandRequest {
|
||||
uint32 second = 4;
|
||||
}
|
||||
|
||||
// ==================== EVENT ====================
|
||||
message ListEntitiesEventResponse {
|
||||
option (id) = 107;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_EVENT";
|
||||
|
||||
string object_id = 1;
|
||||
fixed32 key = 2;
|
||||
string name = 3;
|
||||
string unique_id = 4;
|
||||
|
||||
string icon = 5;
|
||||
bool disabled_by_default = 6;
|
||||
EntityCategory entity_category = 7;
|
||||
string device_class = 8;
|
||||
|
||||
repeated string event_types = 9;
|
||||
}
|
||||
message EventResponse {
|
||||
option (id) = 108;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_EVENT";
|
||||
|
||||
fixed32 key = 1;
|
||||
string event_type = 2;
|
||||
}
|
||||
|
||||
// ==================== VALVE ====================
|
||||
message ListEntitiesValveResponse {
|
||||
|
@ -1209,6 +1209,30 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
bool APIConnection::send_event(event::Event *event, std::string event_type) {
|
||||
EventResponse resp{};
|
||||
resp.key = event->get_object_id_hash();
|
||||
resp.event_type = std::move(event_type);
|
||||
return this->send_event_response(resp);
|
||||
}
|
||||
bool APIConnection::send_event_info(event::Event *event) {
|
||||
ListEntitiesEventResponse msg;
|
||||
msg.key = event->get_object_id_hash();
|
||||
msg.object_id = event->get_object_id();
|
||||
if (event->has_own_name())
|
||||
msg.name = event->get_name();
|
||||
msg.unique_id = get_default_unique_id("event", event);
|
||||
msg.icon = event->get_icon();
|
||||
msg.disabled_by_default = event->is_disabled_by_default();
|
||||
msg.entity_category = static_cast<enums::EntityCategory>(event->get_entity_category());
|
||||
msg.device_class = event->get_device_class();
|
||||
for (const auto &event_type : event->get_event_types())
|
||||
msg.event_types.push_back(event_type);
|
||||
return this->send_list_entities_event_response(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
|
||||
if (this->log_subscription_ < level)
|
||||
return false;
|
||||
|
@ -153,6 +153,11 @@ class APIConnection : public APIServerConnection {
|
||||
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
bool send_event(event::Event *event, std::string event_type);
|
||||
bool send_event_info(event::Event *event);
|
||||
#endif
|
||||
|
||||
void on_disconnect_response(const DisconnectResponse &value) override;
|
||||
void on_ping_response(const PingResponse &value) override {
|
||||
// we initiated ping
|
||||
|
@ -7709,6 +7709,157 @@ void TimeCommandRequest::dump_to(std::string &out) const {
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 6: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->entity_category = value.as_enum<enums::EntityCategory>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->object_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->name = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 4: {
|
||||
this->unique_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 5: {
|
||||
this->icon = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 8: {
|
||||
this->device_class = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 9: {
|
||||
this->event_types.push_back(value.as_string());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesEventResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
buffer.encode_string(4, this->unique_id);
|
||||
buffer.encode_string(5, this->icon);
|
||||
buffer.encode_bool(6, this->disabled_by_default);
|
||||
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
|
||||
buffer.encode_string(8, this->device_class);
|
||||
for (auto &it : this->event_types) {
|
||||
buffer.encode_string(9, it, true);
|
||||
}
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesEventResponse::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("ListEntitiesEventResponse {\n");
|
||||
out.append(" object_id: ");
|
||||
out.append("'").append(this->object_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%" PRIu32, this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" name: ");
|
||||
out.append("'").append(this->name).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" unique_id: ");
|
||||
out.append("'").append(this->unique_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" icon: ");
|
||||
out.append("'").append(this->icon).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" entity_category: ");
|
||||
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" device_class: ");
|
||||
out.append("'").append(this->device_class).append("'");
|
||||
out.append("\n");
|
||||
|
||||
for (const auto &it : this->event_types) {
|
||||
out.append(" event_types: ");
|
||||
out.append("'").append(it).append("'");
|
||||
out.append("\n");
|
||||
}
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool EventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->event_type = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool EventResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void EventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_string(2, this->event_type);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void EventResponse::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("EventResponse {\n");
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%" PRIu32, this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" event_type: ");
|
||||
out.append("'").append(this->event_type).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesValveResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 6: {
|
||||
|
@ -1974,6 +1974,40 @@ class TimeCommandRequest : public ProtoMessage {
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class ListEntitiesEventResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
std::string icon{};
|
||||
bool disabled_by_default{false};
|
||||
enums::EntityCategory entity_category{};
|
||||
std::string device_class{};
|
||||
std::vector<std::string> event_types{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class EventResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0};
|
||||
std::string event_type{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
};
|
||||
class ListEntitiesValveResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{};
|
||||
|
@ -557,6 +557,22 @@ bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse &
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool APIServerConnectionBase::send_list_entities_event_response(const ListEntitiesEventResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_event_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesEventResponse>(msg, 107);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool APIServerConnectionBase::send_event_response(const EventResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_event_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<EventResponse>(msg, 108);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
bool APIServerConnectionBase::send_list_entities_valve_response(const ListEntitiesValveResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
|
@ -280,6 +280,12 @@ class APIServerConnectionBase : public ProtoService {
|
||||
#ifdef USE_DATETIME_TIME
|
||||
virtual void on_time_command_request(const TimeCommandRequest &value){};
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool send_list_entities_event_response(const ListEntitiesEventResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool send_event_response(const EventResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
bool send_list_entities_valve_response(const ListEntitiesValveResponse &msg);
|
||||
#endif
|
||||
|
@ -318,6 +318,13 @@ void APIServer::on_media_player_update(media_player::MediaPlayer *obj) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void APIServer::on_event(event::Event *obj, const std::string &event_type) {
|
||||
for (auto &c : this->clients_)
|
||||
c->send_event(obj, event_type);
|
||||
}
|
||||
#endif
|
||||
|
||||
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
|
||||
void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
||||
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
@ -96,6 +96,9 @@ class APIServer : public Component, public Controller {
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
void on_event(event::Event *obj, const std::string &event_type) override;
|
||||
#endif
|
||||
|
||||
bool is_connected() const;
|
||||
|
||||
|
@ -89,6 +89,9 @@ bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmCont
|
||||
return this->client_->send_alarm_control_panel_info(a_alarm_control_panel);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool ListEntitiesIterator::on_event(event::Event *event) { return this->client_->send_event_info(event); }
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
@ -69,6 +69,9 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool on_event(event::Event *event) override;
|
||||
#endif
|
||||
bool on_end() override;
|
||||
|
||||
|
@ -66,6 +66,9 @@ class InitialStateIterator : public ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool on_event(event::Event *event) override { return true; };
|
||||
#endif
|
||||
protected:
|
||||
APIConnection *client_;
|
||||
|
134
esphome/components/event/__init__.py
Normal file
134
esphome/components/event/__init__.py
Normal file
@ -0,0 +1,134 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import mqtt
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_ON_EVENT,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_MQTT_ID,
|
||||
CONF_EVENT_TYPE,
|
||||
DEVICE_CLASS_BUTTON,
|
||||
DEVICE_CLASS_DOORBELL,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_MOTION,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.cpp_helpers import setup_entity
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
|
||||
CODEOWNERS = ["@nohat"]
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_BUTTON,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_DOORBELL,
|
||||
DEVICE_CLASS_MOTION,
|
||||
]
|
||||
|
||||
event_ns = cg.esphome_ns.namespace("event")
|
||||
Event = event_ns.class_("Event", cg.EntityBase)
|
||||
EventPtr = Event.operator("ptr")
|
||||
|
||||
TriggerEventAction = event_ns.class_("TriggerEventAction", automation.Action)
|
||||
|
||||
EventTrigger = event_ns.class_("EventTrigger", automation.Trigger.template())
|
||||
|
||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||
|
||||
EVENT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent),
|
||||
cv.GenerateID(): cv.declare_id(Event),
|
||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||
cv.Optional(CONF_ON_EVENT): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
_UNDEF = object()
|
||||
|
||||
|
||||
def event_schema(
|
||||
class_: MockObjClass = _UNDEF,
|
||||
*,
|
||||
icon: str = _UNDEF,
|
||||
entity_category: str = _UNDEF,
|
||||
device_class: str = _UNDEF,
|
||||
) -> cv.Schema:
|
||||
schema = {}
|
||||
|
||||
if class_ is not _UNDEF:
|
||||
schema[cv.GenerateID()] = cv.declare_id(class_)
|
||||
|
||||
for key, default, validator in [
|
||||
(CONF_ICON, icon, cv.icon),
|
||||
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
||||
]:
|
||||
if default is not _UNDEF:
|
||||
schema[cv.Optional(key, default=default)] = validator
|
||||
|
||||
return EVENT_SCHEMA.extend(schema)
|
||||
|
||||
|
||||
async def setup_event_core_(var, config, *, event_types: list[str]):
|
||||
await setup_entity(var, config)
|
||||
|
||||
for conf in config.get(CONF_ON_EVENT, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.std_string, "event_type")], conf
|
||||
)
|
||||
|
||||
cg.add(var.set_event_types(event_types))
|
||||
|
||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
||||
cg.add(var.set_device_class(device_class))
|
||||
|
||||
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_event(var, config, *, event_types: list[str]):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_event(var))
|
||||
await setup_event_core_(var, config, event_types=event_types)
|
||||
|
||||
|
||||
async def new_event(config, *, event_types: list[str]):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await register_event(var, config, event_types=event_types)
|
||||
return var
|
||||
|
||||
|
||||
TRIGGER_EVENT_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(Event),
|
||||
cv.Required(CONF_EVENT_TYPE): cv.templatable(cv.string_strict),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action("event.trigger", TriggerEventAction, TRIGGER_EVENT_SCHEMA)
|
||||
async def event_fire_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
templ = await cg.templatable(config[CONF_EVENT_TYPE], args, cg.std_string)
|
||||
cg.add(var.set_event_type(templ))
|
||||
return var
|
||||
|
||||
|
||||
@coroutine_with_priority(100.0)
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_EVENT")
|
||||
cg.add_global(event_ns.using)
|
25
esphome/components/event/automation.h
Normal file
25
esphome/components/event/automation.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/event/event.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace event {
|
||||
|
||||
template<typename... Ts> class TriggerEventAction : public Action<Ts...>, public Parented<Event> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(std::string, event_type)
|
||||
|
||||
void play(Ts... x) override { this->parent_->trigger(this->event_type_.value(x...)); }
|
||||
};
|
||||
|
||||
class EventTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
EventTrigger(Event *event) {
|
||||
event->add_on_event_callback([this](const std::string &event_type) { this->trigger(event_type); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace event
|
||||
} // namespace esphome
|
24
esphome/components/event/event.cpp
Normal file
24
esphome/components/event/event.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "event.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace event {
|
||||
|
||||
static const char *const TAG = "event";
|
||||
|
||||
void Event::trigger(const std::string &event_type) {
|
||||
if (types_.find(event_type) == types_.end()) {
|
||||
ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str());
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), event_type.c_str());
|
||||
this->event_callback_.call(event_type);
|
||||
}
|
||||
|
||||
void Event::add_on_event_callback(std::function<void(const std::string &event_type)> &&callback) {
|
||||
this->event_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
} // namespace event
|
||||
} // namespace esphome
|
37
esphome/components/event/event.h
Normal file
37
esphome/components/event/event.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace event {
|
||||
|
||||
#define LOG_EVENT(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_icon().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
|
||||
} \
|
||||
if (!(obj)->get_device_class().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
class Event : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
void trigger(const std::string &event_type);
|
||||
void set_event_types(const std::set<std::string> &event_types) { this->types_ = event_types; }
|
||||
std::set<std::string> get_event_types() const { return this->types_; }
|
||||
void add_on_event_callback(std::function<void(const std::string &event_type)> &&callback);
|
||||
|
||||
protected:
|
||||
CallbackManager<void(const std::string &event_type)> event_callback_;
|
||||
std::set<std::string> types_;
|
||||
};
|
||||
|
||||
} // namespace event
|
||||
} // namespace esphome
|
@ -119,6 +119,7 @@ MQTTTextComponent = mqtt_ns.class_("MQTTTextComponent", MQTTComponent)
|
||||
MQTTSelectComponent = mqtt_ns.class_("MQTTSelectComponent", MQTTComponent)
|
||||
MQTTButtonComponent = mqtt_ns.class_("MQTTButtonComponent", MQTTComponent)
|
||||
MQTTLockComponent = mqtt_ns.class_("MQTTLockComponent", MQTTComponent)
|
||||
MQTTEventComponent = mqtt_ns.class_("MQTTEventComponent", MQTTComponent)
|
||||
MQTTValveComponent = mqtt_ns.class_("MQTTValveComponent", MQTTComponent)
|
||||
|
||||
MQTTDiscoveryUniqueIdGenerator = mqtt_ns.enum("MQTTDiscoveryUniqueIdGenerator")
|
||||
|
@ -73,6 +73,8 @@ constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "en";
|
||||
constexpr const char *const MQTT_ENTITY_CATEGORY = "ent_cat";
|
||||
constexpr const char *const MQTT_ERROR_TEMPLATE = "err_tpl";
|
||||
constexpr const char *const MQTT_ERROR_TOPIC = "err_t";
|
||||
constexpr const char *const MQTT_EVENT_TYPE = "event_type";
|
||||
constexpr const char *const MQTT_EVENT_TYPES = "evt_typ";
|
||||
constexpr const char *const MQTT_EXPIRE_AFTER = "exp_aft";
|
||||
constexpr const char *const MQTT_FAN_MODE_COMMAND_TEMPLATE = "fan_mode_cmd_tpl";
|
||||
constexpr const char *const MQTT_FAN_MODE_COMMAND_TOPIC = "fan_mode_cmd_t";
|
||||
@ -330,6 +332,8 @@ constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "enabled_by_default";
|
||||
constexpr const char *const MQTT_ENTITY_CATEGORY = "entity_category";
|
||||
constexpr const char *const MQTT_ERROR_TEMPLATE = "error_template";
|
||||
constexpr const char *const MQTT_ERROR_TOPIC = "error_topic";
|
||||
constexpr const char *const MQTT_EVENT_TYPE = "event_type";
|
||||
constexpr const char *const MQTT_EVENT_TYPES = "event_types";
|
||||
constexpr const char *const MQTT_EXPIRE_AFTER = "expire_after";
|
||||
constexpr const char *const MQTT_FAN_MODE_COMMAND_TEMPLATE = "fan_mode_command_template";
|
||||
constexpr const char *const MQTT_FAN_MODE_COMMAND_TOPIC = "fan_mode_command_topic";
|
||||
|
54
esphome/components/mqtt/mqtt_event.cpp
Normal file
54
esphome/components/mqtt/mqtt_event.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include "mqtt_event.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
#ifdef USE_MQTT
|
||||
#ifdef USE_EVENT
|
||||
|
||||
namespace esphome {
|
||||
namespace mqtt {
|
||||
|
||||
static const char *const TAG = "mqtt.event";
|
||||
|
||||
using namespace esphome::event;
|
||||
|
||||
MQTTEventComponent::MQTTEventComponent(event::Event *event) : event_(event) {}
|
||||
|
||||
void MQTTEventComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
JsonArray event_types = root.createNestedArray(MQTT_EVENT_TYPES);
|
||||
for (const auto &event_type : this->event_->get_event_types())
|
||||
event_types.add(event_type);
|
||||
|
||||
if (!this->event_->get_device_class().empty())
|
||||
root[MQTT_DEVICE_CLASS] = this->event_->get_device_class();
|
||||
|
||||
config.command_topic = false;
|
||||
}
|
||||
|
||||
void MQTTEventComponent::setup() {
|
||||
this->event_->add_on_event_callback([this](const std::string &event_type) { this->publish_event_(event_type); });
|
||||
}
|
||||
|
||||
void MQTTEventComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT Event '%s': ", this->event_->get_name().c_str());
|
||||
ESP_LOGCONFIG(TAG, "Event Types: ");
|
||||
for (const auto &event_type : this->event_->get_event_types()) {
|
||||
ESP_LOGCONFIG(TAG, "- %s", event_type.c_str());
|
||||
}
|
||||
LOG_MQTT_COMPONENT(true, true);
|
||||
}
|
||||
|
||||
bool MQTTEventComponent::publish_event_(const std::string &event_type) {
|
||||
return this->publish_json(this->get_state_topic_(),
|
||||
[event_type](JsonObject root) { root[MQTT_EVENT_TYPE] = event_type; });
|
||||
}
|
||||
|
||||
std::string MQTTEventComponent::component_type() const { return "event"; }
|
||||
const EntityBase *MQTTEventComponent::get_entity() const { return this->event_; }
|
||||
|
||||
} // namespace mqtt
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
#endif // USE_MQTT
|
39
esphome/components/mqtt/mqtt_event.h
Normal file
39
esphome/components/mqtt/mqtt_event.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_MQTT
|
||||
#ifdef USE_EVENT
|
||||
|
||||
#include "esphome/components/event/event.h"
|
||||
#include "mqtt_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mqtt {
|
||||
|
||||
class MQTTEventComponent : public mqtt::MQTTComponent {
|
||||
public:
|
||||
explicit MQTTEventComponent(event::Event *event);
|
||||
|
||||
void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override;
|
||||
|
||||
void setup() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
/// Events do not send a state so just return true.
|
||||
bool send_initial_state() override { return true; }
|
||||
|
||||
protected:
|
||||
bool publish_event_(const std::string &event_type);
|
||||
std::string component_type() const override;
|
||||
const EntityBase *get_entity() const override;
|
||||
|
||||
event::Event *event_;
|
||||
};
|
||||
|
||||
} // namespace mqtt
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
#endif // USE_MQTT
|
24
esphome/components/template/event/__init__.py
Normal file
24
esphome/components/template/event/__init__.py
Normal file
@ -0,0 +1,24 @@
|
||||
import esphome.config_validation as cv
|
||||
|
||||
from esphome.components import event
|
||||
|
||||
import esphome.codegen as cg
|
||||
|
||||
from esphome.const import CONF_EVENT_TYPES
|
||||
|
||||
from .. import template_ns
|
||||
|
||||
CODEOWNERS = ["@nohat"]
|
||||
|
||||
TemplateEvent = template_ns.class_("TemplateEvent", event.Event, cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = event.event_schema(TemplateEvent).extend(
|
||||
{
|
||||
cv.Required(CONF_EVENT_TYPES): cv.ensure_list(cv.string_strict),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await event.new_event(config, event_types=config[CONF_EVENT_TYPES])
|
||||
await cg.register_component(var, config)
|
12
esphome/components/template/event/template_event.h
Normal file
12
esphome/components/template/event/template_event.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/event/event.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace template_ {
|
||||
|
||||
class TemplateEvent : public Component, public event::Event {};
|
||||
|
||||
} // namespace template_
|
||||
} // namespace esphome
|
@ -159,5 +159,14 @@ bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmCont
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
bool ListEntitiesIterator::on_event(event::Event *event) {
|
||||
// Null event type, since we are just iterating over entities
|
||||
const std::string null_event_type = "";
|
||||
this->web_server_->events_.send(this->web_server_->event_json(event, null_event_type, DETAIL_ALL).c_str(), "state");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace web_server
|
||||
} // namespace esphome
|
||||
|
@ -62,6 +62,9 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool on_event(event::Event *event) override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
WebServer *web_server_;
|
||||
|
@ -1352,6 +1352,28 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void WebServer::on_event(event::Event *obj, const std::string &event_type) {
|
||||
this->events_.send(this->event_json(obj, event_type, DETAIL_STATE).c_str(), "state");
|
||||
}
|
||||
|
||||
std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) {
|
||||
return json::build_json([obj, event_type, start_config](JsonObject root) {
|
||||
set_json_id(root, obj, "event-" + obj->get_object_id(), start_config);
|
||||
if (!event_type.empty()) {
|
||||
root["event_type"] = event_type;
|
||||
}
|
||||
if (start_config == DETAIL_ALL) {
|
||||
JsonArray event_types = root.createNestedArray("event_types");
|
||||
for (auto const &event_type : obj->get_event_types()) {
|
||||
event_types.add(event_type);
|
||||
}
|
||||
root["device_class"] = obj->get_device_class();
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
bool WebServer::canHandle(AsyncWebServerRequest *request) {
|
||||
if (request->url() == "/")
|
||||
return true;
|
||||
|
@ -297,6 +297,13 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
alarm_control_panel::AlarmControlPanelState value, JsonDetail start_config);
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void on_event(event::Event *obj, const std::string &event_type) override;
|
||||
|
||||
/// Dump the event details with its value as a JSON string.
|
||||
std::string event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config);
|
||||
#endif
|
||||
|
||||
/// Override the web handler's canHandle method.
|
||||
bool canHandle(AsyncWebServerRequest *request) override;
|
||||
/// Override the web handler's handleRequest method.
|
||||
|
@ -251,6 +251,8 @@ CONF_ESP8266_DISABLE_SSL_SUPPORT = "esp8266_disable_ssl_support"
|
||||
CONF_ESPHOME = "esphome"
|
||||
CONF_ETHERNET = "ethernet"
|
||||
CONF_EVENT = "event"
|
||||
CONF_EVENT_TYPE = "event_type"
|
||||
CONF_EVENT_TYPES = "event_types"
|
||||
CONF_EXPIRE_AFTER = "expire_after"
|
||||
CONF_EXPORT_ACTIVE_ENERGY = "export_active_energy"
|
||||
CONF_EXPORT_REACTIVE_ENERGY = "export_reactive_energy"
|
||||
@ -517,6 +519,7 @@ CONF_ON_DOUBLE_CLICK = "on_double_click"
|
||||
CONF_ON_ENROLLMENT_DONE = "on_enrollment_done"
|
||||
CONF_ON_ENROLLMENT_FAILED = "on_enrollment_failed"
|
||||
CONF_ON_ENROLLMENT_SCAN = "on_enrollment_scan"
|
||||
CONF_ON_EVENT = "on_event"
|
||||
CONF_ON_FINGER_SCAN_INVALID = "on_finger_scan_invalid"
|
||||
CONF_ON_FINGER_SCAN_MATCHED = "on_finger_scan_matched"
|
||||
CONF_ON_FINGER_SCAN_MISPLACED = "on_finger_scan_misplaced"
|
||||
@ -1024,6 +1027,7 @@ DEVICE_CLASS_AWNING = "awning"
|
||||
DEVICE_CLASS_BATTERY = "battery"
|
||||
DEVICE_CLASS_BATTERY_CHARGING = "battery_charging"
|
||||
DEVICE_CLASS_BLIND = "blind"
|
||||
DEVICE_CLASS_BUTTON = "button"
|
||||
DEVICE_CLASS_CARBON_DIOXIDE = "carbon_dioxide"
|
||||
DEVICE_CLASS_CARBON_MONOXIDE = "carbon_monoxide"
|
||||
DEVICE_CLASS_COLD = "cold"
|
||||
@ -1036,6 +1040,7 @@ DEVICE_CLASS_DATA_SIZE = "data_size"
|
||||
DEVICE_CLASS_DATE = "date"
|
||||
DEVICE_CLASS_DISTANCE = "distance"
|
||||
DEVICE_CLASS_DOOR = "door"
|
||||
DEVICE_CLASS_DOORBELL = "doorbell"
|
||||
DEVICE_CLASS_DURATION = "duration"
|
||||
DEVICE_CLASS_EMPTY = ""
|
||||
DEVICE_CLASS_ENERGY = "energy"
|
||||
|
@ -63,6 +63,9 @@
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
#include "esphome/components/alarm_control_panel/alarm_control_panel.h"
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
#include "esphome/components/event/event.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
@ -164,6 +167,10 @@ class Application {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void register_event(event::Event *event) { this->events_.push_back(event); }
|
||||
#endif
|
||||
|
||||
/// Register the component in this Application instance.
|
||||
template<class C> C *register_component(C *c) {
|
||||
static_assert(std::is_base_of<Component, C>::value, "Only Component subclasses can be registered");
|
||||
@ -386,6 +393,16 @@ class Application {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
const std::vector<event::Event *> &get_events() { return this->events_; }
|
||||
event::Event *get_event_by_key(uint32_t key, bool include_internal = false) {
|
||||
for (auto *obj : this->events_)
|
||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||
return obj;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
protected:
|
||||
@ -409,6 +426,9 @@ class Application {
|
||||
#ifdef USE_BUTTON
|
||||
std::vector<button::Button *> buttons_{};
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
std::vector<event::Event *> events_{};
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
std::vector<sensor::Sensor *> sensors_{};
|
||||
#endif
|
||||
|
@ -321,6 +321,21 @@ void ComponentIterator::advance() {
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
case IteratorState::EVENT:
|
||||
if (this->at_ >= App.get_events().size()) {
|
||||
advance_platform = true;
|
||||
} else {
|
||||
auto *event = App.get_events()[this->at_];
|
||||
if (event->is_internal() && !this->include_internal_) {
|
||||
success = true;
|
||||
break;
|
||||
} else {
|
||||
success = this->on_event(event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case IteratorState::MAX:
|
||||
if (this->on_end()) {
|
||||
|
@ -80,6 +80,9 @@ class ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
virtual bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) = 0;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
virtual bool on_event(event::Event *event) = 0;
|
||||
#endif
|
||||
virtual bool on_end();
|
||||
|
||||
@ -146,6 +149,9 @@ class ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
ALARM_CONTROL_PANEL,
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
EVENT,
|
||||
#endif
|
||||
MAX,
|
||||
} state_{IteratorState::NONE};
|
||||
|
@ -109,6 +109,12 @@ void Controller::setup_controller(bool include_internal) {
|
||||
obj->add_on_state_callback([this, obj]() { this->on_alarm_control_panel_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
for (auto *obj : App.get_events()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_event_callback([this, obj](const std::string &event_type) { this->on_event(obj, event_type); });
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
|
@ -55,6 +55,9 @@
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
#include "esphome/components/alarm_control_panel/alarm_control_panel.h"
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
#include "esphome/components/event/event.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
@ -112,6 +115,9 @@ class Controller {
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
virtual void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj){};
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
virtual void on_event(event::Event *obj, const std::string &event_type){};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace esphome
|
||||
|
@ -24,6 +24,7 @@
|
||||
#define USE_CLIMATE
|
||||
#define USE_COVER
|
||||
#define USE_DEEP_SLEEP
|
||||
#define USE_EVENT
|
||||
#define USE_FAN
|
||||
#define USE_GRAPH
|
||||
#define USE_HOMEASSISTANT_TIME
|
||||
|
@ -624,6 +624,7 @@ def lint_trailing_whitespace(fname, match):
|
||||
"esphome/components/datetime/date_entity.h",
|
||||
"esphome/components/datetime/time_entity.h",
|
||||
"esphome/components/display/display.h",
|
||||
"esphome/components/event/event.h",
|
||||
"esphome/components/fan/fan.h",
|
||||
"esphome/components/i2c/i2c.h",
|
||||
"esphome/components/lock/lock.h",
|
||||
|
9
tests/components/event/test.esp32-c3-idf.yaml
Normal file
9
tests/components/event/test.esp32-c3-idf.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
event:
|
||||
- platform: template
|
||||
name: Event
|
||||
id: some_event
|
||||
event_types:
|
||||
- template_event_type1
|
||||
- template_event_type2
|
||||
on_event:
|
||||
- logger.log: Event fired
|
9
tests/components/event/test.esp32-c3.yaml
Normal file
9
tests/components/event/test.esp32-c3.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
event:
|
||||
- platform: template
|
||||
name: Event
|
||||
id: some_event
|
||||
event_types:
|
||||
- template_event_type1
|
||||
- template_event_type2
|
||||
on_event:
|
||||
- logger.log: Event fired
|
9
tests/components/event/test.esp32-idf.yaml
Normal file
9
tests/components/event/test.esp32-idf.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
event:
|
||||
- platform: template
|
||||
name: Event
|
||||
id: some_event
|
||||
event_types:
|
||||
- template_event_type1
|
||||
- template_event_type2
|
||||
on_event:
|
||||
- logger.log: Event fired
|
9
tests/components/event/test.esp32.yaml
Normal file
9
tests/components/event/test.esp32.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
event:
|
||||
- platform: template
|
||||
name: Event
|
||||
id: some_event
|
||||
event_types:
|
||||
- template_event_type1
|
||||
- template_event_type2
|
||||
on_event:
|
||||
- logger.log: Event fired
|
9
tests/components/event/test.esp8266.yaml
Normal file
9
tests/components/event/test.esp8266.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
event:
|
||||
- platform: template
|
||||
name: Event
|
||||
id: some_event
|
||||
event_types:
|
||||
- template_event_type1
|
||||
- template_event_type2
|
||||
on_event:
|
||||
- logger.log: Event fired
|
9
tests/components/event/test.rp2040.yaml
Normal file
9
tests/components/event/test.rp2040.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
event:
|
||||
- platform: template
|
||||
name: Event
|
||||
id: some_event
|
||||
event_types:
|
||||
- template_event_type1
|
||||
- template_event_type2
|
||||
on_event:
|
||||
- logger.log: Event fired
|
Loading…
Reference in New Issue
Block a user