1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-13 08:42:18 +01:00

🏗 Merge C++ into python codebase (#504)

## Description:

Move esphome-core codebase into esphome (and a bunch of other refactors). See https://github.com/esphome/feature-requests/issues/97

Yes this is a shit ton of work and no there's no way to automate it :( But it will be worth it 👍

Progress:
- Core support (file copy etc): 80%
- Base Abstractions (light, switch): ~50%
- Integrations: ~10%
- Working? Yes, (but only with ported components).

Other refactors:
- Moves all codegen related stuff into a single class: `esphome.codegen` (imported as `cg`)
- Rework coroutine syntax
- Move from `component/platform.py` to `domain/component.py` structure as with HA
- Move all defaults out of C++ and into config validation.
- Remove `make_...` helpers from Application class. Reason: Merge conflicts with every single new integration.
- Pointer Variables are stored globally instead of locally in setup(). Reason: stack size limit.

Future work:
- Rework const.py - Move all `CONF_...` into a conf class (usage `conf.UPDATE_INTERVAL` vs `CONF_UPDATE_INTERVAL`). Reason: Less convoluted import block
- Enable loading from `custom_components` folder.

**Related issue (if applicable):** https://github.com/esphome/feature-requests/issues/97

**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>

## Checklist:
  - [ ] The code change is tested and works locally.
  - [ ] Tests have been added to verify that the new code works (under `tests/` folder).

If user exposed functionality or configuration variables are added/changed:
  - [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs).
This commit is contained in:
Otto Winter
2019-04-17 12:06:00 +02:00
committed by GitHub
parent 049807e3ab
commit 6682c43dfa
817 changed files with 54156 additions and 10830 deletions

View File

@@ -0,0 +1,3 @@
import esphome.codegen as cg
template_ns = cg.esphome_ns.namespace('template_')

View File

@@ -0,0 +1,39 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.automation import ACTION_REGISTRY
from esphome.components import binary_sensor
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_STATE
from .. import template_ns
TemplateBinarySensor = template_ns.class_('TemplateBinarySensor', binary_sensor.BinarySensor,
cg.Component)
CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(TemplateBinarySensor),
cv.Optional(CONF_LAMBDA): cv.lambda_,
}).extend(cv.COMPONENT_SCHEMA))
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME])
yield cg.register_component(var, config)
yield binary_sensor.register_binary_sensor(var, config)
if CONF_LAMBDA in config:
template_ = yield cg.process_lambda(config[CONF_LAMBDA], [],
return_type=cg.optional.template(bool))
cg.add(var.set_template(template_))
@ACTION_REGISTRY.register('binary_sensor.template.publish', cv.Schema({
cv.Required(CONF_ID): cv.use_variable_id(binary_sensor.BinarySensor),
cv.Required(CONF_STATE): cv.templatable(cv.boolean),
}))
def binary_sensor_template_publish_to_code(config, action_id, template_arg, args):
var = yield cg.get_variable(config[CONF_ID])
type = binary_sensor.BinarySensorPublishAction.template(template_arg)
rhs = type.new(var)
action = cg.Pvariable(action_id, rhs, type=type)
template_ = yield cg.templatable(config[CONF_STATE], args, bool)
cg.add(action.set_state(template_))
yield action

View File

@@ -0,0 +1,21 @@
#include "template_binary_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace template_ {
static const char *TAG = "template.binary_sensor";
void TemplateBinarySensor::loop() {
if (!this->f_.has_value())
return;
auto s = (*this->f_)();
if (s.has_value()) {
this->publish_state(*s);
}
}
void TemplateBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Template Binary Sensor", this); }
} // namespace template_
} // namespace esphome

View File

@@ -0,0 +1,25 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome {
namespace template_ {
class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor {
public:
explicit TemplateBinarySensor(const std::string &name) : BinarySensor(name) {}
void set_template(std::function<optional<bool>()> &&f) { this->f_ = f; }
void loop() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::HARDWARE; }
protected:
optional<std::function<optional<bool>()>> f_{};
};
} // namespace template_
} // namespace esphome

View File

@@ -0,0 +1,75 @@
from esphome import automation
from esphome.automation import ACTION_REGISTRY
from esphome.components import cover
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_ASSUMED_STATE, CONF_CLOSE_ACTION, CONF_CURRENT_OPERATION, CONF_ID, \
CONF_LAMBDA, CONF_NAME, CONF_OPEN_ACTION, CONF_OPTIMISTIC, CONF_POSITION, CONF_RESTORE_MODE, \
CONF_STATE, CONF_STOP_ACTION
from .. import template_ns
TemplateCover = template_ns.class_('TemplateCover', cover.Cover)
TemplateCoverRestoreMode = template_ns.enum('TemplateCoverRestoreMode')
RESTORE_MODES = {
'NO_RESTORE': TemplateCoverRestoreMode.COVER_NO_RESTORE,
'RESTORE': TemplateCoverRestoreMode.COVER_RESTORE,
'RESTORE_AND_CALL': TemplateCoverRestoreMode.COVER_RESTORE_AND_CALL,
}
CONFIG_SCHEMA = cv.nameable(cover.COVER_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(TemplateCover),
cv.Optional(CONF_LAMBDA): cv.lambda_,
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean,
cv.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_RESTORE_MODE, default='RESTORE'): cv.one_of(*RESTORE_MODES, upper=True),
}).extend(cv.COMPONENT_SCHEMA))
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME])
yield cg.register_component(var, config)
yield cover.register_cover(var, config)
if CONF_LAMBDA in config:
template_ = yield cg.process_lambda(config[CONF_LAMBDA], [],
return_type=cg.optional.template(float))
cg.add(var.set_state_lambda(template_))
if CONF_OPEN_ACTION in config:
yield automation.build_automation(var.get_open_trigger(), [], config[CONF_OPEN_ACTION])
if CONF_CLOSE_ACTION in config:
yield automation.build_automation(var.get_close_trigger(), [], config[CONF_CLOSE_ACTION])
if CONF_STOP_ACTION in config:
yield automation.build_automation(var.get_stop_trigger(), [], config[CONF_STOP_ACTION])
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE]))
cg.add(var.set_restore_mode(RESTORE_MODES[config[CONF_RESTORE_MODE]]))
@ACTION_REGISTRY.register('cover.template.publish', cv.Schema({
cv.Required(CONF_ID): cv.use_variable_id(cover.Cover),
cv.Exclusive(CONF_STATE, 'pos'): cv.templatable(cover.validate_cover_state),
cv.Exclusive(CONF_POSITION, 'pos'): cv.templatable(cv.zero_to_one_float),
cv.Optional(CONF_CURRENT_OPERATION): cv.templatable(cover.validate_cover_operation),
}))
def cover_template_publish_to_code(config, action_id, template_arg, args):
var = yield cg.get_variable(config[CONF_ID])
type = cover.CoverPublishAction.template(template_arg)
rhs = type.new(var)
action = cg.Pvariable(action_id, rhs, type=type)
if CONF_STATE in config:
template_ = yield cg.templatable(config[CONF_STATE], args, float, to_exp=cover.COVER_STATES)
cg.add(action.set_position(template_))
if CONF_POSITION in config:
template_ = yield cg.templatable(config[CONF_POSITION], args, float,
to_exp=cover.COVER_STATES)
cg.add(action.set_position(template_))
if CONF_CURRENT_OPERATION in config:
template_ = yield cg.templatable(config[CONF_CURRENT_OPERATION], args,
cover.CoverOperation, to_exp=cover.COVER_OPERATIONS)
cg.add(action.set_current_operation(template_))
yield action

View File

@@ -0,0 +1,137 @@
#include "template_cover.h"
#include "esphome/core/log.h"
namespace esphome {
namespace template_ {
using namespace esphome::cover;
static const char *TAG = "template.cover";
TemplateCover::TemplateCover(const std::string &name)
: Cover(name),
open_trigger_(new Trigger<>()),
close_trigger_(new Trigger<>),
stop_trigger_(new Trigger<>()),
position_trigger_(new Trigger<float>()),
tilt_trigger_(new Trigger<float>()) {}
void TemplateCover::setup() {
ESP_LOGCONFIG(TAG, "Setting up template cover '%s'...", this->name_.c_str());
switch (this->restore_mode_) {
case COVER_NO_RESTORE:
break;
case COVER_RESTORE: {
auto restore = this->restore_state_();
if (restore.has_value())
restore->apply(this);
break;
}
case COVER_RESTORE_AND_CALL: {
auto restore = this->restore_state_();
if (restore.has_value()) {
restore->to_call(this).perform();
}
break;
}
}
}
void TemplateCover::loop() {
bool changed = false;
if (this->state_f_.has_value()) {
auto s = (*this->state_f_)();
if (s.has_value()) {
auto pos = clamp(*s, 0.0f, 1.0f);
if (pos != this->position) {
this->position = pos;
changed = true;
}
}
}
if (this->tilt_f_.has_value()) {
auto s = (*this->tilt_f_)();
if (s.has_value()) {
auto tilt = clamp(*s, 0.0f, 1.0f);
if (tilt != this->tilt) {
this->tilt = tilt;
changed = true;
}
}
}
if (changed)
this->publish_state();
}
void TemplateCover::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
void TemplateCover::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
void TemplateCover::set_state_lambda(std::function<optional<float>()> &&f) { this->state_f_ = f; }
float TemplateCover::get_setup_priority() const { return setup_priority::HARDWARE; }
Trigger<> *TemplateCover::get_open_trigger() const { return this->open_trigger_; }
Trigger<> *TemplateCover::get_close_trigger() const { return this->close_trigger_; }
Trigger<> *TemplateCover::get_stop_trigger() const { return this->stop_trigger_; }
void TemplateCover::dump_config() { LOG_COVER("", "Template Cover", this); }
void TemplateCover::control(const CoverCall &call) {
if (call.get_stop()) {
this->stop_prev_trigger_();
this->stop_trigger_->trigger();
this->prev_command_trigger_ = this->stop_trigger_;
this->current_operation = COVER_OPERATION_IDLE;
this->publish_state();
}
if (call.get_position().has_value()) {
auto pos = *call.get_position();
this->stop_prev_trigger_();
if (pos < this->position) {
this->current_operation = COVER_OPERATION_CLOSING;
} else if (pos > this->position) {
this->current_operation = COVER_OPERATION_OPENING;
}
if (pos == COVER_OPEN) {
this->open_trigger_->trigger();
this->prev_command_trigger_ = this->open_trigger_;
} else if (pos == COVER_CLOSED) {
this->close_trigger_->trigger();
this->prev_command_trigger_ = this->close_trigger_;
}
this->position_trigger_->trigger(pos);
if (this->optimistic_) {
this->position = pos;
}
}
if (call.get_tilt().has_value()) {
auto tilt = *call.get_tilt();
this->tilt_trigger_->trigger(tilt);
if (this->optimistic_) {
this->tilt = tilt;
}
}
this->publish_state();
}
CoverTraits TemplateCover::get_traits() {
auto traits = CoverTraits();
traits.set_is_assumed_state(this->assumed_state_);
traits.set_supports_position(this->has_position_);
traits.set_supports_tilt(this->has_tilt_);
return traits;
}
Trigger<float> *TemplateCover::get_position_trigger() const { return this->position_trigger_; }
Trigger<float> *TemplateCover::get_tilt_trigger() const { return this->tilt_trigger_; }
void TemplateCover::set_tilt_lambda(std::function<optional<float>()> &&tilt_f) { this->tilt_f_ = tilt_f; }
void TemplateCover::set_has_position(bool has_position) { this->has_position_ = has_position; }
void TemplateCover::set_has_tilt(bool has_tilt) { this->has_tilt_ = has_tilt; }
void TemplateCover::stop_prev_trigger_() {
if (this->prev_command_trigger_ != nullptr) {
this->prev_command_trigger_->stop();
this->prev_command_trigger_ = nullptr;
}
}
} // namespace template_
} // namespace esphome

View File

@@ -0,0 +1,60 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/cover/cover.h"
namespace esphome {
namespace template_ {
enum TemplateCoverRestoreMode {
COVER_NO_RESTORE,
COVER_RESTORE,
COVER_RESTORE_AND_CALL,
};
class TemplateCover : public cover::Cover, public Component {
public:
explicit TemplateCover(const std::string &name);
void set_state_lambda(std::function<optional<float>()> &&f);
Trigger<> *get_open_trigger() const;
Trigger<> *get_close_trigger() const;
Trigger<> *get_stop_trigger() const;
Trigger<float> *get_position_trigger() const;
Trigger<float> *get_tilt_trigger() const;
void set_optimistic(bool optimistic);
void set_assumed_state(bool assumed_state);
void set_tilt_lambda(std::function<optional<float>()> &&tilt_f);
void set_has_position(bool has_position);
void set_has_tilt(bool has_tilt);
void set_restore_mode(TemplateCoverRestoreMode restore_mode) { restore_mode_ = restore_mode; }
void setup() override;
void loop() override;
void dump_config() override;
float get_setup_priority() const override;
protected:
void control(const cover::CoverCall &call) override;
cover::CoverTraits get_traits() override;
void stop_prev_trigger_();
TemplateCoverRestoreMode restore_mode_{COVER_RESTORE};
optional<std::function<optional<float>()>> state_f_;
optional<std::function<optional<float>()>> tilt_f_;
bool assumed_state_{false};
bool optimistic_{false};
Trigger<> *open_trigger_;
Trigger<> *close_trigger_;
Trigger<> *stop_trigger_;
Trigger<> *prev_command_trigger_{nullptr};
Trigger<float> *position_trigger_;
bool has_position_{false};
Trigger<float> *tilt_trigger_;
bool has_tilt_{false};
};
} // namespace template_
} // namespace esphome

View File

@@ -0,0 +1,45 @@
from esphome.automation import ACTION_REGISTRY
from esphome.components import sensor
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_STATE, CONF_UPDATE_INTERVAL
from .. import template_ns
TemplateSensor = template_ns.class_('TemplateSensor', sensor.PollingSensorComponent)
CONFIG_SCHEMA = cv.nameable(sensor.SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(TemplateSensor),
cv.Optional(CONF_LAMBDA): cv.lambda_,
cv.Optional(CONF_UPDATE_INTERVAL, default="60s"): cv.update_interval,
}).extend(cv.COMPONENT_SCHEMA))
def to_code(config):
rhs = TemplateSensor.new(config[CONF_NAME], config[CONF_UPDATE_INTERVAL])
template = cg.Pvariable(config[CONF_ID], rhs)
yield cg.register_component(template, config)
yield sensor.register_sensor(template, config)
if CONF_LAMBDA in config:
template_ = yield cg.process_lambda(config[CONF_LAMBDA], [],
return_type=cg.optional.template(float))
cg.add(template.set_template(template_))
CONF_SENSOR_TEMPLATE_PUBLISH = 'sensor.template.publish'
SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.use_variable_id(sensor.Sensor),
cv.Required(CONF_STATE): cv.templatable(cv.float_),
})
@ACTION_REGISTRY.register(CONF_SENSOR_TEMPLATE_PUBLISH, SENSOR_TEMPLATE_PUBLISH_ACTION_SCHEMA)
def sensor_template_publish_to_code(config, action_id, template_arg, args):
var = yield cg.get_variable(config[CONF_ID])
type = sensor.SensorPublishAction.template(template_arg)
rhs = type.new(var)
action = cg.Pvariable(action_id, rhs, type=type)
template_ = yield cg.templatable(config[CONF_STATE], args, float)
cg.add(action.set_state(template_))
yield action

View File

@@ -0,0 +1,28 @@
#include "template_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace template_ {
static const char *TAG = "template.sensor";
TemplateSensor::TemplateSensor(const std::string &name, uint32_t update_interval)
: PollingSensorComponent(name, update_interval) {}
void TemplateSensor::update() {
if (!this->f_.has_value())
return;
auto val = (*this->f_)();
if (val.has_value()) {
this->publish_state(*val);
}
}
float TemplateSensor::get_setup_priority() const { return setup_priority::HARDWARE; }
void TemplateSensor::set_template(std::function<optional<float>()> &&f) { this->f_ = f; }
void TemplateSensor::dump_config() {
LOG_SENSOR("", "Template Sensor", this);
LOG_UPDATE_INTERVAL(this);
}
} // namespace template_
} // namespace esphome

View File

@@ -0,0 +1,26 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace template_ {
class TemplateSensor : public sensor::PollingSensorComponent {
public:
TemplateSensor(const std::string &name, uint32_t update_interval);
void set_template(std::function<optional<float>()> &&f);
void update() override;
void dump_config() override;
float get_setup_priority() const override;
protected:
optional<std::function<optional<float>()>> f_;
};
} // namespace template_
} // namespace esphome

View File

@@ -0,0 +1,54 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import ACTION_REGISTRY
from esphome.components import switch
from esphome.const import CONF_ASSUMED_STATE, CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_OPTIMISTIC, \
CONF_RESTORE_STATE, CONF_STATE, CONF_TURN_OFF_ACTION, CONF_TURN_ON_ACTION
from .. import template_ns
TemplateSwitch = template_ns.class_('TemplateSwitch', switch.Switch, cg.Component)
CONFIG_SCHEMA = cv.nameable(switch.SWITCH_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(TemplateSwitch),
cv.Optional(CONF_LAMBDA): cv.lambda_,
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean,
cv.Optional(CONF_TURN_OFF_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_TURN_ON_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_RESTORE_STATE, default=False): cv.boolean,
}).extend(cv.COMPONENT_SCHEMA))
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME])
yield cg.register_component(var, config)
yield switch.register_switch(var, config)
if CONF_LAMBDA in config:
template_ = yield cg.process_lambda(config[CONF_LAMBDA], [],
return_type=cg.optional.template(bool))
cg.add(var.set_state_lambda(template_))
if CONF_TURN_OFF_ACTION in config:
yield automation.build_automation(var.get_turn_off_trigger(), [],
config[CONF_TURN_OFF_ACTION])
if CONF_TURN_ON_ACTION in config:
yield automation.build_automation(var.get_turn_on_trigger(), [],
config[CONF_TURN_ON_ACTION])
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE]))
cg.add(var.set_restore_state(config[CONF_RESTORE_STATE]))
@ACTION_REGISTRY.register('switch.template.publish', cv.Schema({
cv.Required(CONF_ID): cv.use_variable_id(switch.Switch),
cv.Required(CONF_STATE): cv.templatable(cv.boolean),
}))
def switch_template_publish_to_code(config, action_id, template_arg, args):
var = yield cg.get_variable(config[CONF_ID])
type = switch.SwitchPublishAction.template(template_arg)
rhs = type.new(var)
action = cg.Pvariable(action_id, rhs, type=type)
template_ = yield cg.templatable(config[CONF_STATE], args, bool)
cg.add(action.set_state(template_))
yield action

View File

@@ -0,0 +1,66 @@
#include "template_switch.h"
#include "esphome/core/log.h"
namespace esphome {
namespace template_ {
static const char *TAG = "template.switch";
TemplateSwitch::TemplateSwitch(const std::string &name)
: switch_::Switch(name), Component(), turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {}
void TemplateSwitch::loop() {
if (!this->f_.has_value())
return;
auto s = (*this->f_)();
if (!s.has_value())
return;
this->publish_state(*s);
}
void TemplateSwitch::write_state(bool state) {
if (this->prev_trigger_ != nullptr) {
this->prev_trigger_->stop();
}
if (state) {
this->prev_trigger_ = this->turn_on_trigger_;
this->turn_on_trigger_->trigger();
} else {
this->prev_trigger_ = this->turn_off_trigger_;
this->turn_off_trigger_->trigger();
}
if (this->optimistic_)
this->publish_state(state);
}
void TemplateSwitch::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
bool TemplateSwitch::assumed_state() { return this->assumed_state_; }
void TemplateSwitch::set_state_lambda(std::function<optional<bool>()> &&f) { this->f_ = f; }
float TemplateSwitch::get_setup_priority() const { return setup_priority::HARDWARE; }
Trigger<> *TemplateSwitch::get_turn_on_trigger() const { return this->turn_on_trigger_; }
Trigger<> *TemplateSwitch::get_turn_off_trigger() const { return this->turn_off_trigger_; }
void TemplateSwitch::setup() {
if (!this->restore_state_)
return;
auto restored = this->get_initial_state();
if (!restored.has_value())
return;
ESP_LOGD(TAG, " Restored state %s", ONOFF(*restored));
if (*restored) {
this->turn_on();
} else {
this->turn_off();
}
}
void TemplateSwitch::dump_config() {
LOG_SWITCH("", "Template Switch", this);
ESP_LOGCONFIG(TAG, " Restore State: %s", YESNO(this->restore_state_));
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
}
void TemplateSwitch::set_restore_state(bool restore_state) { this->restore_state_ = restore_state; }
void TemplateSwitch::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; }
} // namespace template_
} // namespace esphome

View File

@@ -0,0 +1,42 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/switch/switch.h"
namespace esphome {
namespace template_ {
class TemplateSwitch : public switch_::Switch, public Component {
public:
explicit TemplateSwitch(const std::string &name);
void setup() override;
void dump_config() override;
void set_state_lambda(std::function<optional<bool>()> &&f);
void set_restore_state(bool restore_state);
Trigger<> *get_turn_on_trigger() const;
Trigger<> *get_turn_off_trigger() const;
void set_optimistic(bool optimistic);
void set_assumed_state(bool assumed_state);
void loop() override;
float get_setup_priority() const override;
protected:
bool assumed_state() override;
void write_state(bool state) override;
optional<std::function<optional<bool>()>> f_;
bool optimistic_{false};
bool assumed_state_{false};
Trigger<> *turn_on_trigger_;
Trigger<> *turn_off_trigger_;
Trigger<> *prev_trigger_{nullptr};
bool restore_state_{false};
};
} // namespace template_
} // namespace esphome

View File

@@ -0,0 +1,42 @@
from esphome.automation import ACTION_REGISTRY
from esphome.components import text_sensor
from esphome.components.text_sensor import TextSensorPublishAction
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_STATE, CONF_UPDATE_INTERVAL
from .. import template_ns
TemplateTextSensor = template_ns.class_('TemplateTextSensor', text_sensor.TextSensor,
cg.PollingComponent)
CONFIG_SCHEMA = cv.nameable(text_sensor.TEXT_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(TemplateTextSensor),
cv.Optional(CONF_LAMBDA): cv.lambda_,
cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval,
}).extend(cv.COMPONENT_SCHEMA))
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UPDATE_INTERVAL])
yield cg.register_component(var, config)
yield text_sensor.register_text_sensor(var, config)
if CONF_LAMBDA in config:
template_ = yield cg.process_lambda(config[CONF_LAMBDA], [],
return_type=cg.optional.template(cg.std_string))
cg.add(var.set_template(template_))
@ACTION_REGISTRY.register('text_sensor.template.publish', cv.Schema({
cv.Required(CONF_ID): cv.use_variable_id(text_sensor.TextSensor),
cv.Required(CONF_STATE): cv.templatable(cv.string_strict),
}))
def text_sensor_template_publish_to_code(config, action_id, template_arg, args):
var = yield cg.get_variable(config[CONF_ID])
type = TextSensorPublishAction.template(template_arg)
rhs = type.new(var)
action = cg.Pvariable(action_id, rhs, type=type)
template_ = yield cg.templatable(config[CONF_STATE], args, cg.std_string)
cg.add(action.set_state(template_))
yield action

View File

@@ -0,0 +1,25 @@
#include "template_text_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace template_ {
static const char *TAG = "template.text_sensor";
TemplateTextSensor::TemplateTextSensor(const std::string &name, uint32_t update_interval)
: TextSensor(name), PollingComponent(update_interval) {}
void TemplateTextSensor::update() {
if (!this->f_.has_value())
return;
auto val = (*this->f_)();
if (val.has_value()) {
this->publish_state(*val);
}
}
float TemplateTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; }
void TemplateTextSensor::set_template(std::function<optional<std::string>()> &&f) { this->f_ = f; }
void TemplateTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Template Sensor", this); }
} // namespace template_
} // namespace esphome

View File

@@ -0,0 +1,27 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/text_sensor/text_sensor.h"
namespace esphome {
namespace template_ {
class TemplateTextSensor : public text_sensor::TextSensor, public PollingComponent {
public:
TemplateTextSensor(const std::string &name, uint32_t update_interval);
void set_template(std::function<optional<std::string>()> &&f);
void update() override;
float get_setup_priority() const override;
void dump_config() override;
protected:
optional<std::function<optional<std::string>()>> f_{};
};
} // namespace template_
} // namespace esphome