mirror of
https://github.com/esphome/esphome.git
synced 2024-11-28 01:34:18 +01:00
first commit of the status_indicator comp.
This commit is contained in:
parent
a9e3a8ae8c
commit
d8b42afdf2
3 changed files with 363 additions and 0 deletions
121
esphome/components/status_indicator/__init__.py
Normal file
121
esphome/components/status_indicator/__init__.py
Normal file
|
@ -0,0 +1,121 @@
|
|||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
import esphome.automation as auto
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_PRIORITY,
|
||||
CONF_GROUP,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ["@nielsnl68"]
|
||||
|
||||
status_led_ns = cg.esphome_ns.namespace("status_indicator")
|
||||
StatusLED = status_led_ns.class_("StatusIndicator", cg.Component)
|
||||
StatusTrigger = status_led_ns.class_("StatusTrigger", auto.Trigger.template())
|
||||
StatusAction = status_led_ns.class_("StatusAction", auto.Trigger.template())
|
||||
CONF_TRIGGER_LIST = {
|
||||
"on_app_error": True,
|
||||
"on_clear_app_error": True,
|
||||
"on_app_warning": True,
|
||||
"on_clear_app_warning": True,
|
||||
"on_network_connected": True,
|
||||
"on_network_disconnected": True,
|
||||
"on_wifi_ap_enabled": True,
|
||||
"on_wifi_ap_disabled": True,
|
||||
"on_api_connected": True,
|
||||
"on_api_disconnected": True,
|
||||
"on_mqtt_connected": True,
|
||||
"on_mgtt_disconnected": True,
|
||||
"on_custom_status": False,
|
||||
}
|
||||
|
||||
|
||||
def trigger_setup(Single):
|
||||
return auto.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StatusTrigger),
|
||||
cv.Optional(CONF_GROUP, default=""): cv.string,
|
||||
cv.Optional(CONF_PRIORITY, default=0): cv.int_range(0, 10),
|
||||
},
|
||||
single=Single,
|
||||
)
|
||||
|
||||
|
||||
def add_default_triggers():
|
||||
result = {}
|
||||
|
||||
for trigger, single in CONF_TRIGGER_LIST.items():
|
||||
result[cv.Optional(trigger)] = trigger_setup(single)
|
||||
return cv.Schema(result)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(StatusLED),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(add_default_triggers())
|
||||
)
|
||||
|
||||
|
||||
async def add_trigger(var, conf, key):
|
||||
trigger = cg.new_Pvariable(
|
||||
conf[CONF_TRIGGER_ID],
|
||||
var,
|
||||
conf[CONF_GROUP],
|
||||
conf[CONF_PRIORITY],
|
||||
)
|
||||
await auto.build_automation(trigger, [], conf)
|
||||
cg.add(var.set_trigger(key, trigger))
|
||||
|
||||
|
||||
@coroutine_with_priority(80.0)
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
for trigger_name, single in CONF_TRIGGER_LIST.items():
|
||||
conf = config.get(trigger_name, None)
|
||||
if conf is None:
|
||||
continue
|
||||
if single:
|
||||
await add_trigger(var, conf, trigger_name)
|
||||
else:
|
||||
for conf in config.get(trigger_name, []):
|
||||
await add_trigger(var, conf, conf[CONF_TRIGGER_ID].id)
|
||||
|
||||
|
||||
@auto.register_action(
|
||||
"status.push",
|
||||
StatusAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_TRIGGER_ID): cv.use_id(StatusTrigger),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def status_action_push_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_TRIGGER_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
cg.add(var.set_state(True))
|
||||
return var
|
||||
|
||||
|
||||
@auto.register_action(
|
||||
"status.pop",
|
||||
StatusAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_TRIGGER_ID): cv.use_id(StatusTrigger),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def status_action_pop_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_TRIGGER_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
cg.add(var.set_state(False))
|
||||
return var
|
153
esphome/components/status_indicator/status_indicator.cpp
Normal file
153
esphome/components/status_indicator/status_indicator.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
#include "status_indicator.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
#include "esphome/components/network/util.h"
|
||||
|
||||
#ifdef USE_WIFI
|
||||
#include "esphome/components/wifi/wifi_component.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_MQTT
|
||||
#include "esphome/components/mqtt/mqtt_client.h"
|
||||
#endif
|
||||
#ifdef USE_API
|
||||
#include "esphome/components/api/api_server.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace status_indicator {
|
||||
|
||||
static const char *const TAG = "status_indicator";
|
||||
|
||||
void StatusIndicator::dump_config() { ESP_LOGCONFIG(TAG, "Status Indicator:"); }
|
||||
void StatusIndicator::loop() {
|
||||
std::string status{""};
|
||||
if ((App.get_app_state() & STATUS_LED_ERROR) != 0u) {
|
||||
status = "on_app_error";
|
||||
this->status_.on_error = 1;
|
||||
} else if (this->status_.on_error == 1) {
|
||||
status = "on_clear_app_error";
|
||||
this->status_.on_error = 0;
|
||||
} else if ((App.get_app_state() & STATUS_LED_WARNING) != 0u) {
|
||||
status = "on_app_warning";
|
||||
this->status_.on_warning = 1;
|
||||
} else if (this->status_.on_warning == 1) {
|
||||
status = "on_clear_app_warning";
|
||||
this->status_.on_warning = 0;
|
||||
}
|
||||
if (this->current_trigger_ != nullptr) {
|
||||
if (this->current_trigger_->is_action_running()) {
|
||||
if (status == "") {
|
||||
return;
|
||||
}
|
||||
this->current_trigger_->stop_action();
|
||||
}
|
||||
}
|
||||
if (network::has_network()) {
|
||||
#ifdef USE_WIFI
|
||||
if (status == "" && wifi::global_wifi_component->is_ap_enabled()) {
|
||||
status = "on_wifi_ap_enabled";
|
||||
this->status_.on_wifi_ap = 1;
|
||||
} else if (this->status_.on_wifi_ap == 1) {
|
||||
status = "on_wifi_ap_disabled";
|
||||
this->status_.on_wifi_ap = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (status == "" && not network::is_connected()) {
|
||||
status = "on_network_disconnected";
|
||||
this->status_.on_network = 1;
|
||||
} else if (this->status_.on_network == 1) {
|
||||
status = "on_network_connected";
|
||||
this->status_.on_network = 0;
|
||||
}
|
||||
|
||||
#ifdef USE_API
|
||||
if (status == "" && api::global_api_server != nullptr && not api::global_api_server->is_connected()) {
|
||||
status = "on_api_disconnected";
|
||||
this->status_.on_api = 1;
|
||||
} else if (this->status_.on_error == 1) {
|
||||
status = "on_api_connected";
|
||||
this->status_.on_api = 0;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_MQTT
|
||||
if (status == "" && mqtt::global_mqtt_client != nullptr && not mqtt::global_mqtt_client->is_connected()) {
|
||||
status = "on_mqtt_disconnected";
|
||||
this->status_.on_mqtt = 1;
|
||||
} else if (this->status_.on_mqtt == 1) {
|
||||
status = "on_mqtt_connected";
|
||||
this->status_.on_mqtt = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (this->current_status_ != status) {
|
||||
if (status != "") {
|
||||
this->current_trigger_ = get_trigger(status);
|
||||
if (this->current_trigger_ != nullptr) {
|
||||
this->current_trigger_->trigger();
|
||||
}
|
||||
} else {
|
||||
this->current_trigger_ = nullptr;
|
||||
if (!this->custom_triggers_.empty()) {
|
||||
this->custom_triggers_[0]->trigger();
|
||||
}
|
||||
}
|
||||
this->current_status_ = status;
|
||||
}
|
||||
}
|
||||
|
||||
float StatusIndicator::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
float StatusIndicator::get_loop_priority() const { return 50.0f; }
|
||||
|
||||
StatusTrigger *StatusIndicator::get_trigger(std::string key) {
|
||||
auto search = this->triggers_.find(key);
|
||||
if (search != this->triggers_.end())
|
||||
return search->second;
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void StatusIndicator::set_trigger(std::string key, StatusTrigger *trigger) { this->triggers_[key] = trigger; }
|
||||
|
||||
void StatusIndicator::push_trigger(StatusTrigger *trigger) {
|
||||
this->pop_trigger(trigger, true);
|
||||
uint32_t x = 0;
|
||||
while (this->custom_triggers_.size() > x) {
|
||||
if (trigger->get_priority() <= this->custom_triggers_[x]->get_priority()) {
|
||||
this->custom_triggers_.insert(this->custom_triggers_.begin() + x, trigger);
|
||||
break;
|
||||
} else {
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatusIndicator::pop_trigger(StatusTrigger *trigger, bool incl_group) {
|
||||
uint32_t x = 0;
|
||||
while (this->custom_triggers_.size() > x) {
|
||||
if (trigger == this->custom_triggers_[x]) {
|
||||
this->custom_triggers_.erase(this->custom_triggers_.begin() + x);
|
||||
} else if (incl_group && trigger->get_group() != "" && trigger->get_group() == this->custom_triggers_[x]->get_group()) {
|
||||
this->custom_triggers_.erase(this->custom_triggers_.begin() + x);
|
||||
} else {
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatusIndicator::pop_trigger(std::string group) {
|
||||
uint32_t x = 0;
|
||||
while (this->custom_triggers_.size() > x) {
|
||||
if ( group == this->custom_triggers_[x]->get_group()) {
|
||||
this->custom_triggers_.erase(this->custom_triggers_.begin() + x);
|
||||
} else {
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace status_indicator
|
||||
} // namespace esphome
|
89
esphome/components/status_indicator/status_indicator.h
Normal file
89
esphome/components/status_indicator/status_indicator.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/light/automation.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace status_indicator {
|
||||
class StatusTrigger;
|
||||
union StatusFlags {
|
||||
struct {
|
||||
int on_error : 1;
|
||||
int on_warning : 1;
|
||||
int on_network : 1;
|
||||
int on_api : 1;
|
||||
int on_mqtt : 1;
|
||||
int on_wifi_ap : 1;
|
||||
};
|
||||
int setter = 0;
|
||||
};
|
||||
|
||||
class StatusIndicator : public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
float get_loop_priority() const override;
|
||||
|
||||
StatusTrigger *get_trigger(std::string key);
|
||||
void set_trigger(std::string key, StatusTrigger *trigger);
|
||||
void push_trigger(StatusTrigger *trigger);
|
||||
void pop_trigger(StatusTrigger *trigger, bool incl_group = false);
|
||||
void pop_trigger(std::string group);
|
||||
|
||||
protected:
|
||||
std::string current_status_{""};
|
||||
StatusTrigger *current_trigger_{nullptr};
|
||||
StatusFlags status_;
|
||||
std::map<std::string, StatusTrigger *> triggers_{};
|
||||
std::vector<StatusTrigger *> custom_triggers_{};
|
||||
};
|
||||
|
||||
class StatusTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit StatusTrigger(StatusIndicator *parent, std::string group, uint32_t priority)
|
||||
: parent_(parent), group_(group), priority_(priority) {}
|
||||
std::string get_group() { return this->group_; }
|
||||
uint32 get_priority() { return this->priority_; }
|
||||
void push_me() { parent_->push_trigger(this); }
|
||||
void pop_me() { parent_->pop_trigger(this, false); }
|
||||
|
||||
protected:
|
||||
StatusIndicator *parent_;
|
||||
std::string group_; /// Minimum length of click. 0 means no minimum.
|
||||
uint32_t priority_; /// Maximum length of click. 0 means no maximum.
|
||||
};
|
||||
|
||||
template<typename... Ts> class StatusCondition : public Condition<Ts...> {
|
||||
public:
|
||||
StatusCondition(StatusIndicator *parent, bool state) : parent_(parent), state_(state) {}
|
||||
bool check(Ts... x) override { return (this->parent_->status_.setter == 0) == this->state_; }
|
||||
|
||||
protected:
|
||||
StatusIndicator *parent_;
|
||||
bool state_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class StatusAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit StatusAction(StatusTrigger *trigger) : trigger_(trigger) {}
|
||||
TEMPLATABLE_VALUE(bool, state)
|
||||
|
||||
void play(Ts... x) override {
|
||||
if (this->state_) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
StatusTrigger *trigger_;
|
||||
};
|
||||
|
||||
} // namespace status_indicator
|
||||
} // namespace esphome
|
Loading…
Reference in a new issue