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:
3
esphome/components/template/__init__.py
Normal file
3
esphome/components/template/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
template_ns = cg.esphome_ns.namespace('template_')
|
39
esphome/components/template/binary_sensor/__init__.py
Normal file
39
esphome/components/template/binary_sensor/__init__.py
Normal 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
|
@@ -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
|
@@ -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
|
75
esphome/components/template/cover/__init__.py
Normal file
75
esphome/components/template/cover/__init__.py
Normal 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
|
137
esphome/components/template/cover/template_cover.cpp
Normal file
137
esphome/components/template/cover/template_cover.cpp
Normal 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
|
60
esphome/components/template/cover/template_cover.h
Normal file
60
esphome/components/template/cover/template_cover.h
Normal 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
|
45
esphome/components/template/sensor/__init__.py
Normal file
45
esphome/components/template/sensor/__init__.py
Normal 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
|
28
esphome/components/template/sensor/template_sensor.cpp
Normal file
28
esphome/components/template/sensor/template_sensor.cpp
Normal 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
|
26
esphome/components/template/sensor/template_sensor.h
Normal file
26
esphome/components/template/sensor/template_sensor.h
Normal 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
|
54
esphome/components/template/switch/__init__.py
Normal file
54
esphome/components/template/switch/__init__.py
Normal 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
|
66
esphome/components/template/switch/template_switch.cpp
Normal file
66
esphome/components/template/switch/template_switch.cpp
Normal 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
|
42
esphome/components/template/switch/template_switch.h
Normal file
42
esphome/components/template/switch/template_switch.h
Normal 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
|
42
esphome/components/template/text_sensor/__init__.py
Normal file
42
esphome/components/template/text_sensor/__init__.py
Normal 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
|
@@ -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
|
@@ -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
|
Reference in New Issue
Block a user