1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-29 22:24:26 +00: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

@@ -1,28 +1,21 @@
import voluptuous as vol
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.automation import ACTION_REGISTRY, maybe_simple_id, Condition
from esphome.components import mqtt
from esphome.components.mqtt import setup_mqtt_component
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_INTERNAL, CONF_MQTT_ID, CONF_DEVICE_CLASS, CONF_STATE, \
CONF_POSITION, CONF_TILT, CONF_STOP
from esphome.core import CORE
from esphome.cpp_generator import Pvariable, add, get_variable, templatable
from esphome.cpp_types import Action, Nameable, esphome_ns, App
from esphome.const import CONF_ID, CONF_INTERNAL, CONF_DEVICE_CLASS, CONF_STATE, \
CONF_POSITION, CONF_TILT, CONF_STOP, CONF_MQTT_ID
from esphome.core import CORE, coroutine
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
})
IS_PLATFORM_COMPONENT = True
DEVICE_CLASSES = [
'', 'awning', 'blind', 'curtain', 'damper', 'door', 'garage',
'shade', 'shutter', 'window'
]
cover_ns = esphome_ns.namespace('cover')
cover_ns = cg.esphome_ns.namespace('cover')
Cover = cover_ns.class_('Cover', Nameable)
MQTTCoverComponent = cover_ns.class_('MQTTCoverComponent', mqtt.MQTTComponent)
Cover = cover_ns.class_('Cover', cg.Nameable)
COVER_OPEN = cover_ns.COVER_OPEN
COVER_CLOSED = cover_ns.COVER_CLOSED
@@ -42,112 +35,102 @@ COVER_OPERATIONS = {
validate_cover_operation = cv.one_of(*COVER_OPERATIONS, upper=True)
# Actions
OpenAction = cover_ns.class_('OpenAction', Action)
CloseAction = cover_ns.class_('CloseAction', Action)
StopAction = cover_ns.class_('StopAction', Action)
ControlAction = cover_ns.class_('ControlAction', Action)
OpenAction = cover_ns.class_('OpenAction', cg.Action)
CloseAction = cover_ns.class_('CloseAction', cg.Action)
StopAction = cover_ns.class_('StopAction', cg.Action)
ControlAction = cover_ns.class_('ControlAction', cg.Action)
CoverPublishAction = cover_ns.class_('CoverPublishAction', cg.Action)
CoverIsOpenCondition = cover_ns.class_('CoverIsOpenCondition', Condition)
CoverIsClosedCondition = cover_ns.class_('CoverIsClosedCondition', Condition)
COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(Cover),
cv.GenerateID(CONF_MQTT_ID): cv.declare_variable_id(MQTTCoverComponent),
vol.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_variable_id(mqtt.MQTTCoverComponent),
cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
# TODO: MQTT topic options
})
COVER_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(COVER_SCHEMA.schema)
def setup_cover_core_(cover_var, config):
@coroutine
def setup_cover_core_(var, config):
if CONF_INTERNAL in config:
add(cover_var.set_internal(config[CONF_INTERNAL]))
cg.add(var.set_internal(config[CONF_INTERNAL]))
if CONF_DEVICE_CLASS in config:
add(cover_var.set_device_class(config[CONF_DEVICE_CLASS]))
setup_mqtt_component(cover_var.Pget_mqtt(), config)
def setup_cover(cover_obj, config):
CORE.add_job(setup_cover_core_, cover_obj, config)
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
if CONF_MQTT_ID in config:
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
yield mqtt.register_mqtt_component(mqtt_, config)
@coroutine
def register_cover(var, config):
if not CORE.has_id(config[CONF_ID]):
var = Pvariable(config[CONF_ID], var, has_side_effects=True)
add(App.register_cover(var))
CORE.add_job(setup_cover_core_, var, config)
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_cover(var))
yield setup_cover_core_(var, config)
BUILD_FLAGS = '-DUSE_COVER'
CONF_COVER_OPEN = 'cover.open'
COVER_OPEN_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(Cover),
COVER_ACTION_SCHEMA = maybe_simple_id({
cv.Required(CONF_ID): cv.use_variable_id(Cover),
})
@ACTION_REGISTRY.register(CONF_COVER_OPEN, COVER_OPEN_ACTION_SCHEMA)
@ACTION_REGISTRY.register('cover.open', COVER_ACTION_SCHEMA)
def cover_open_to_code(config, action_id, template_arg, args):
var = yield get_variable(config[CONF_ID])
var = yield cg.get_variable(config[CONF_ID])
type = OpenAction.template(template_arg)
rhs = type.new(var)
yield Pvariable(action_id, rhs, type=type)
yield cg.Pvariable(action_id, rhs, type=type)
CONF_COVER_CLOSE = 'cover.close'
COVER_CLOSE_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(Cover),
})
@ACTION_REGISTRY.register(CONF_COVER_CLOSE, COVER_CLOSE_ACTION_SCHEMA)
@ACTION_REGISTRY.register('cover.close', COVER_ACTION_SCHEMA)
def cover_close_to_code(config, action_id, template_arg, args):
var = yield get_variable(config[CONF_ID])
var = yield cg.get_variable(config[CONF_ID])
type = CloseAction.template(template_arg)
rhs = type.new(var)
yield Pvariable(action_id, rhs, type=type)
yield cg.Pvariable(action_id, rhs, type=type)
CONF_COVER_STOP = 'cover.stop'
COVER_STOP_ACTION_SCHEMA = maybe_simple_id({
vol.Required(CONF_ID): cv.use_variable_id(Cover),
})
@ACTION_REGISTRY.register(CONF_COVER_STOP, COVER_STOP_ACTION_SCHEMA)
@ACTION_REGISTRY.register('cover.stop', COVER_ACTION_SCHEMA)
def cover_stop_to_code(config, action_id, template_arg, args):
var = yield get_variable(config[CONF_ID])
var = yield cg.get_variable(config[CONF_ID])
type = StopAction.template(template_arg)
rhs = type.new(var)
yield Pvariable(action_id, rhs, type=type)
yield cg.Pvariable(action_id, rhs, type=type)
CONF_COVER_CONTROL = 'cover.control'
COVER_CONTROL_ACTION_SCHEMA = cv.Schema({
vol.Required(CONF_ID): cv.use_variable_id(Cover),
vol.Optional(CONF_STOP): cv.templatable(cv.boolean),
vol.Exclusive(CONF_STATE, 'pos'): cv.templatable(cv.one_of(*COVER_STATES)),
vol.Exclusive(CONF_POSITION, 'pos'): cv.templatable(cv.percentage),
vol.Optional(CONF_TILT): cv.templatable(cv.percentage),
cv.Required(CONF_ID): cv.use_variable_id(Cover),
cv.Optional(CONF_STOP): cv.templatable(cv.boolean),
cv.Exclusive(CONF_STATE, 'pos'): cv.templatable(cv.one_of(*COVER_STATES)),
cv.Exclusive(CONF_POSITION, 'pos'): cv.templatable(cv.percentage),
cv.Optional(CONF_TILT): cv.templatable(cv.percentage),
})
@ACTION_REGISTRY.register(CONF_COVER_CONTROL, COVER_CONTROL_ACTION_SCHEMA)
@ACTION_REGISTRY.register('cover.control', COVER_CONTROL_ACTION_SCHEMA)
def cover_control_to_code(config, action_id, template_arg, args):
var = yield get_variable(config[CONF_ID])
var = yield cg.get_variable(config[CONF_ID])
type = StopAction.template(template_arg)
rhs = type.new(var)
action = Pvariable(action_id, rhs, type=type)
action = cg.Pvariable(action_id, rhs, type=type)
if CONF_STOP in config:
template_ = yield templatable(config[CONF_STOP], args, bool)
add(action.set_stop(template_))
template_ = yield cg.templatable(config[CONF_STOP], args, bool)
cg.add(action.set_stop(template_))
if CONF_STATE in config:
template_ = yield templatable(config[CONF_STATE], args, float,
to_exp=COVER_STATES)
add(action.set_position(template_))
template_ = yield cg.templatable(config[CONF_STATE], args, float,
to_exp=COVER_STATES)
cg.add(action.set_position(template_))
if CONF_POSITION in config:
template_ = yield templatable(config[CONF_POSITION], args, float)
add(action.set_position(template_))
template_ = yield cg.templatable(config[CONF_POSITION], args, float)
cg.add(action.set_position(template_))
if CONF_TILT in config:
template_ = yield templatable(config[CONF_TILT], args, float)
add(action.set_tilt(template_))
template_ = yield cg.templatable(config[CONF_TILT], args, float)
cg.add(action.set_tilt(template_))
yield action
def to_code(config):
cg.add_define('USE_COVER')
cg.add_global(cover_ns.using)

View File

@@ -0,0 +1,113 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "cover.h"
namespace esphome {
namespace cover {
template<typename... Ts> class OpenAction : public Action<Ts...> {
public:
explicit OpenAction(Cover *cover) : cover_(cover) {}
void play(Ts... x) override {
this->cover_->open();
this->play_next(x...);
}
protected:
Cover *cover_;
};
template<typename... Ts> class CloseAction : public Action<Ts...> {
public:
explicit CloseAction(Cover *cover) : cover_(cover) {}
void play(Ts... x) override {
this->cover_->close();
this->play_next(x...);
}
protected:
Cover *cover_;
};
template<typename... Ts> class StopAction : public Action<Ts...> {
public:
explicit StopAction(Cover *cover) : cover_(cover) {}
void play(Ts... x) override {
this->cover_->stop();
this->play_next(x...);
}
protected:
Cover *cover_;
};
template<typename... Ts> class ControlAction : public Action<Ts...> {
public:
explicit ControlAction(Cover *cover) : cover_(cover) {}
void play(Ts... x) override {
auto call = this->cover_->make_call();
if (this->stop_.has_value())
call.set_stop(this->stop_.value(x...));
if (this->position_.has_value())
call.set_position(this->position_.value(x...));
if (this->tilt_.has_value())
call.set_tilt(this->tilt_.value(x...));
call.perform();
this->play_next(x...);
}
TEMPLATABLE_VALUE(bool, stop)
TEMPLATABLE_VALUE(float, position)
TEMPLATABLE_VALUE(float, tilt)
protected:
Cover *cover_;
};
template<typename... Ts> class CoverPublishAction : public Action<Ts...> {
public:
CoverPublishAction(Cover *cover) : cover_(cover) {}
void play(Ts... x) override {
if (this->position_.has_value())
this->cover_->position = this->position_.value(x...);
if (this->tilt_.has_value())
this->cover_->tilt = this->tilt_.value(x...);
if (this->current_operation_.has_value())
this->cover_->current_operation = this->current_operation_.value(x...);
this->cover_->publish_state();
this->play_next(x...);
}
TEMPLATABLE_VALUE(float, position)
TEMPLATABLE_VALUE(float, tilt)
TEMPLATABLE_VALUE(CoverOperation, current_operation)
protected:
Cover *cover_;
};
template<typename... Ts> class CoverIsOpenCondition : public Condition<Ts...> {
public:
CoverIsOpenCondition(Cover *cover) : cover_(cover) {}
bool check(Ts... x) override { return this->cover_->is_fully_open(); }
protected:
Cover *cover_;
};
template<typename... Ts> class CoverIsClosedCondition : public Condition<Ts...> {
public:
CoverIsClosedCondition(Cover *cover) : cover_(cover) {}
bool check(Ts... x) override { return this->cover_->is_fully_closed(); }
protected:
Cover *cover_;
};
} // namespace cover
} // namespace esphome

View File

@@ -0,0 +1,214 @@
#include "cover.h"
#include "esphome/core/log.h"
namespace esphome {
namespace cover {
static const char *TAG = "cover";
const float COVER_OPEN = 1.0f;
const float COVER_CLOSED = 0.0f;
const char *cover_command_to_str(float pos) {
if (pos == COVER_OPEN) {
return "OPEN";
} else if (pos == COVER_CLOSED) {
return "CLOSE";
} else {
return "UNKNOWN";
}
}
const char *cover_operation_to_str(CoverOperation op) {
switch (op) {
case COVER_OPERATION_IDLE:
return "IDLE";
case COVER_OPERATION_OPENING:
return "OPENING";
case COVER_OPERATION_CLOSING:
return "CLOSING";
default:
return "UNKNOWN";
}
}
Cover::Cover(const std::string &name) : Nameable(name), position{COVER_OPEN} {}
uint32_t Cover::hash_base() { return 1727367479UL; }
CoverCall::CoverCall(Cover *parent) : parent_(parent) {}
CoverCall &CoverCall::set_command(const char *command) {
if (strcasecmp(command, "OPEN") == 0) {
this->set_command_open();
} else if (strcasecmp(command, "CLOSE") == 0) {
this->set_command_close();
} else if (strcasecmp(command, "STOP") == 0) {
this->set_command_stop();
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
}
return *this;
}
CoverCall &CoverCall::set_command_open() {
this->position_ = COVER_OPEN;
return *this;
}
CoverCall &CoverCall::set_command_close() {
this->position_ = COVER_CLOSED;
return *this;
}
CoverCall &CoverCall::set_command_stop() {
this->stop_ = true;
return *this;
}
CoverCall &CoverCall::set_position(float position) {
this->position_ = position;
return *this;
}
CoverCall &CoverCall::set_tilt(float tilt) {
this->tilt_ = tilt;
return *this;
}
void CoverCall::perform() {
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
auto traits = this->parent_->get_traits();
this->validate_();
if (this->stop_) {
ESP_LOGD(TAG, " Command: STOP");
}
if (this->position_.has_value()) {
if (traits.get_supports_position()) {
ESP_LOGD(TAG, " Position: %.0f%%", *this->position_ * 100.0f);
} else {
ESP_LOGD(TAG, " Command: %s", cover_command_to_str(*this->position_));
}
}
if (this->tilt_.has_value()) {
ESP_LOGD(TAG, " Tilt: %.0f%%", *this->tilt_ * 100.0f);
}
this->parent_->control(*this);
}
const optional<float> &CoverCall::get_position() const { return this->position_; }
const optional<float> &CoverCall::get_tilt() const { return this->tilt_; }
void CoverCall::validate_() {
auto traits = this->parent_->get_traits();
if (this->position_.has_value()) {
auto pos = *this->position_;
if (!traits.get_supports_position() && pos != COVER_OPEN && pos != COVER_CLOSED) {
ESP_LOGW(TAG, "'%s' - This cover device does not support setting position!", this->parent_->get_name().c_str());
this->position_.reset();
} else if (pos < 0.0f || pos > 1.0f) {
ESP_LOGW(TAG, "'%s' - Position %.2f is out of range [0.0 - 1.0]", this->parent_->get_name().c_str(), pos);
this->position_ = clamp(pos, 0.0f, 1.0f);
}
}
if (this->tilt_.has_value()) {
auto tilt = *this->tilt_;
if (!traits.get_supports_tilt()) {
ESP_LOGW(TAG, "'%s' - This cover device does not support tilt!", this->parent_->get_name().c_str());
this->tilt_.reset();
} else if (tilt < 0.0f || tilt > 1.0f) {
ESP_LOGW(TAG, "'%s' - Tilt %.2f is out of range [0.0 - 1.0]", this->parent_->get_name().c_str(), tilt);
this->tilt_ = clamp(tilt, 0.0f, 1.0f);
}
}
if (this->stop_) {
if (this->position_.has_value()) {
ESP_LOGW(TAG, "Cannot set position when stopping a cover!");
this->position_.reset();
}
if (this->tilt_.has_value()) {
ESP_LOGW(TAG, "Cannot set tilt when stopping a cover!");
this->tilt_.reset();
}
}
}
CoverCall &CoverCall::set_stop(bool stop) {
this->stop_ = stop;
return *this;
}
bool CoverCall::get_stop() const { return this->stop_; }
void Cover::set_device_class(const std::string &device_class) { this->device_class_override_ = device_class; }
CoverCall Cover::make_call() { return {this}; }
void Cover::open() {
auto call = this->make_call();
call.set_command_open();
call.perform();
}
void Cover::close() {
auto call = this->make_call();
call.set_command_close();
call.perform();
}
void Cover::stop() {
auto call = this->make_call();
call.set_command_stop();
call.perform();
}
void Cover::add_on_state_callback(std::function<void()> &&f) { this->state_callback_.add(std::move(f)); }
void Cover::publish_state(bool save) {
this->position = clamp(this->position, 0.0f, 1.0f);
this->tilt = clamp(this->tilt, 0.0f, 1.0f);
ESP_LOGD(TAG, "'%s' - Publishing:", this->name_.c_str());
auto traits = this->get_traits();
if (traits.get_supports_position()) {
ESP_LOGD(TAG, " Position: %.0f%%", this->position * 100.0f);
} else {
if (this->position == COVER_OPEN) {
ESP_LOGD(TAG, " State: OPEN");
} else if (this->position == COVER_CLOSED) {
ESP_LOGD(TAG, " State: CLOSED");
} else {
ESP_LOGD(TAG, " State: UNKNOWN");
}
}
if (traits.get_supports_tilt()) {
ESP_LOGD(TAG, " Tilt: %.0f%%", this->tilt * 100.0f);
}
ESP_LOGD(TAG, " Current Operation: %s", cover_operation_to_str(this->current_operation));
this->state_callback_.call();
if (save) {
CoverRestoreState restore{};
memset(&restore, 0, sizeof(restore));
restore.position = this->position;
if (traits.get_supports_tilt()) {
restore.tilt = this->tilt;
}
this->rtc_.save(&restore);
}
}
optional<CoverRestoreState> Cover::restore_state_() {
this->rtc_ = global_preferences.make_preference<CoverRestoreState>(this->get_object_id_hash());
CoverRestoreState recovered{};
if (!this->rtc_.load(&recovered))
return {};
return recovered;
}
Cover::Cover() : Cover("") {}
std::string Cover::get_device_class() {
if (this->device_class_override_.has_value())
return *this->device_class_override_;
return this->device_class();
}
bool Cover::is_fully_open() const { return this->position == COVER_OPEN; }
bool Cover::is_fully_closed() const { return this->position == COVER_CLOSED; }
std::string Cover::device_class() { return ""; }
CoverCall CoverRestoreState::to_call(Cover *cover) {
auto call = cover->make_call();
auto traits = cover->get_traits();
call.set_position(this->position);
if (traits.get_supports_tilt())
call.set_tilt(this->tilt);
return call;
}
void CoverRestoreState::apply(Cover *cover) {
cover->position = this->position;
cover->tilt = this->tilt;
cover->publish_state();
}
} // namespace cover
} // namespace esphome

View File

@@ -0,0 +1,179 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "cover_traits.h"
namespace esphome {
namespace cover {
const extern float COVER_OPEN;
const extern float COVER_CLOSED;
#define LOG_COVER(prefix, type, obj) \
if (obj != nullptr) { \
ESP_LOGCONFIG(TAG, prefix type " '%s'", obj->get_name().c_str()); \
auto traits_ = obj->get_traits(); \
if (traits_.get_is_assumed_state()) { \
ESP_LOGCONFIG(TAG, prefix " Assumed State: YES"); \
} \
if (!obj->get_device_class().empty()) { \
ESP_LOGCONFIG(TAG, prefix " Device Class: '%s'", obj->get_device_class().c_str()); \
} \
}
class Cover;
class CoverCall {
public:
CoverCall(Cover *parent);
/// Set the command as a string, "STOP", "OPEN", "CLOSE".
CoverCall &set_command(const char *command);
/// Set the command to open the cover.
CoverCall &set_command_open();
/// Set the command to close the cover.
CoverCall &set_command_close();
/// Set the command to stop the cover.
CoverCall &set_command_stop();
/// Set the call to a certain target position.
CoverCall &set_position(float position);
/// Set the call to a certain target tilt.
CoverCall &set_tilt(float tilt);
/// Set whether this cover call should stop the cover.
CoverCall &set_stop(bool stop);
/// Perform the cover call.
void perform();
const optional<float> &get_position() const;
bool get_stop() const;
const optional<float> &get_tilt() const;
protected:
void validate_();
Cover *parent_;
bool stop_{false};
optional<float> position_{};
optional<float> tilt_{};
};
/// Struct used to store the restored state of a cover
struct CoverRestoreState {
float position;
float tilt;
/// Convert this struct to a cover call that can be performed.
CoverCall to_call(Cover *cover);
/// Apply these settings to the cover
void apply(Cover *cover);
} __attribute__((packed));
/// Enum encoding the current operation of a cover.
enum CoverOperation : uint8_t {
/// The cover is currently idle (not moving)
COVER_OPERATION_IDLE = 0,
/// The cover is currently opening.
COVER_OPERATION_OPENING,
/// The cover is currently closing.
COVER_OPERATION_CLOSING,
};
const char *cover_operation_to_str(CoverOperation op);
/** Base class for all cover devices.
*
* Covers currently have three properties:
* - position - The current position of the cover from 0.0 (fully closed) to 1.0 (fully open).
* For covers with only binary OPEN/CLOSED position this will always be either 0.0 or 1.0
* - tilt - The tilt value of the cover from 0.0 (closed) to 1.0 (closed)
* - current_operation - The operation the cover is currently performing, this can
* be one of IDLE, OPENING and CLOSING.
*
* For users: All cover operations must be performed over the .make_call() interface.
* To command a cover, use .make_call() to create a call object, set all properties
* you wish to set, and activate the command with .perform().
* For reading out the current values of the cover, use the public .position, .tilt etc
* properties (these are read-only for users)
*
* For integrations: Integrations must implement two methods: control() and get_traits().
* Control will be called with the arguments supplied by the user and should be used
* to control all values of the cover. Also implement get_traits() to return what operations
* the cover supports.
*/
class Cover : public Nameable {
public:
explicit Cover(const std::string &name);
explicit Cover();
/// The current operation of the cover (idle, opening, closing).
CoverOperation current_operation{COVER_OPERATION_IDLE};
union {
/** The position of the cover from 0.0 (fully closed) to 1.0 (fully open).
*
* For binary covers this is always equals to 0.0 or 1.0 (see also COVER_OPEN and
* COVER_CLOSED constants).
*/
float position;
ESPDEPRECATED("<cover>.state is deprecated, please use .position instead") float state;
};
/// The current tilt value of the cover from 0.0 to 1.0.
float tilt{COVER_OPEN};
/// Construct a new cover call used to control the cover.
CoverCall make_call();
/** Open the cover.
*
* This is a legacy method and may be removed later, please use `.make_call()` instead.
*/
void open();
/** Close the cover.
*
* This is a legacy method and may be removed later, please use `.make_call()` instead.
*/
void close();
/** Stop the cover.
*
* This is a legacy method and may be removed later, please use `.make_call()` instead.
*/
void stop();
void add_on_state_callback(std::function<void()> &&f);
/** Publish the current state of the cover.
*
* First set the .position, .tilt, etc values and then call this method
* to publish the state of the cover.
*
* @param save Whether to save the updated values in RTC area.
*/
void publish_state(bool save = true);
virtual CoverTraits get_traits() = 0;
void set_device_class(const std::string &device_class);
std::string get_device_class();
/// Helper method to check if the cover is fully open. Equivalent to comparing .position against 1.0
bool is_fully_open() const;
/// Helper method to check if the cover is fully closed. Equivalent to comparing .position against 0.0
bool is_fully_closed() const;
protected:
friend CoverCall;
virtual void control(const CoverCall &call) = 0;
virtual std::string device_class();
optional<CoverRestoreState> restore_state_();
uint32_t hash_base() override;
CallbackManager<void()> state_callback_{};
optional<std::string> device_class_override_{};
ESPPreferenceObject rtc_;
};
} // namespace cover
} // namespace esphome

View File

@@ -0,0 +1,24 @@
#pragma once
namespace esphome {
namespace cover {
class CoverTraits {
public:
CoverTraits() = default;
bool get_is_assumed_state() const { return this->is_assumed_state_; }
void set_is_assumed_state(bool is_assumed_state) { this->is_assumed_state_ = is_assumed_state; }
bool get_supports_position() const { return this->supports_position_; }
void set_supports_position(bool supports_position) { this->supports_position_ = supports_position; }
bool get_supports_tilt() const { return this->supports_tilt_; }
void set_supports_tilt(bool supports_tilt) { this->supports_tilt_ = supports_tilt; }
protected:
bool is_assumed_state_{false};
bool supports_position_{false};
bool supports_tilt_{false};
};
} // namespace cover
} // namespace esphome

View File

@@ -1,55 +0,0 @@
import voluptuous as vol
from esphome import automation
from esphome.components import binary_sensor, cover
import esphome.config_validation as cv
from esphome.const import CONF_CLOSE_ACTION, CONF_CLOSE_DURATION, \
CONF_CLOSE_ENDSTOP, CONF_ID, CONF_NAME, CONF_OPEN_ACTION, CONF_OPEN_DURATION, \
CONF_OPEN_ENDSTOP, CONF_STOP_ACTION, CONF_MAX_DURATION
from esphome.cpp_generator import Pvariable, add, get_variable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App, Component
EndstopCover = cover.cover_ns.class_('EndstopCover', cover.Cover, Component)
PLATFORM_SCHEMA = cv.nameable(cover.COVER_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(EndstopCover),
vol.Required(CONF_STOP_ACTION): automation.validate_automation(single=True),
vol.Required(CONF_OPEN_ENDSTOP): cv.use_variable_id(binary_sensor.BinarySensor),
vol.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True),
vol.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds,
vol.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
vol.Required(CONF_CLOSE_ENDSTOP): cv.use_variable_id(binary_sensor.BinarySensor),
vol.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
vol.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds,
}).extend(cv.COMPONENT_SCHEMA.schema))
def to_code(config):
rhs = App.register_component(EndstopCover.new(config[CONF_NAME]))
var = Pvariable(config[CONF_ID], rhs)
cover.register_cover(var, config)
setup_component(var, config)
automation.build_automations(var.get_stop_trigger(), [],
config[CONF_STOP_ACTION])
bin = yield get_variable(config[CONF_OPEN_ENDSTOP])
add(var.set_open_endstop(bin))
add(var.set_open_duration(config[CONF_OPEN_DURATION]))
automation.build_automations(var.get_open_trigger(), [],
config[CONF_OPEN_ACTION])
bin = yield get_variable(config[CONF_CLOSE_ENDSTOP])
add(var.set_close_endstop(bin))
add(var.set_close_duration(config[CONF_CLOSE_DURATION]))
automation.build_automations(var.get_close_trigger(), [],
config[CONF_CLOSE_ACTION])
if CONF_MAX_DURATION in config:
add(var.set_max_duration(config[CONF_MAX_DURATION]))
BUILD_FLAGS = '-DUSE_ENDSTOP_COVER'

View File

@@ -1,93 +0,0 @@
import voluptuous as vol
from esphome import automation
from esphome.automation import ACTION_REGISTRY
from esphome.components import cover
import esphome.config_validation as cv
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 esphome.cpp_generator import Pvariable, add, get_variable, process_lambda, templatable
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import Action, App, optional
TemplateCover = cover.cover_ns.class_('TemplateCover', cover.Cover)
CoverPublishAction = cover.cover_ns.class_('CoverPublishAction', Action)
TemplateCoverRestoreMode = cover.cover_ns.enum('TemplateCoverRestoreMode')
RESTORE_MODES = {
'NO_RESTORE': TemplateCoverRestoreMode.NO_RESTORE,
'RESTORE': TemplateCoverRestoreMode.RESTORE,
'RESTORE_AND_CALL': TemplateCoverRestoreMode.RESTORE_AND_CALL,
}
PLATFORM_SCHEMA = cv.nameable(cover.COVER_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(TemplateCover),
vol.Optional(CONF_LAMBDA): cv.lambda_,
vol.Optional(CONF_OPTIMISTIC): cv.boolean,
vol.Optional(CONF_ASSUMED_STATE): cv.boolean,
vol.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True),
vol.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
vol.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True),
vol.Optional(CONF_RESTORE_MODE): cv.one_of(*RESTORE_MODES, upper=True),
}).extend(cv.COMPONENT_SCHEMA.schema))
def to_code(config):
rhs = App.register_component(TemplateCover.new(config[CONF_NAME]))
var = Pvariable(config[CONF_ID], rhs)
cover.register_cover(var, config)
setup_component(var, config)
if CONF_LAMBDA in config:
template_ = yield process_lambda(config[CONF_LAMBDA], [],
return_type=optional.template(float))
add(var.set_state_lambda(template_))
if CONF_OPEN_ACTION in config:
automation.build_automations(var.get_open_trigger(), [],
config[CONF_OPEN_ACTION])
if CONF_CLOSE_ACTION in config:
automation.build_automations(var.get_close_trigger(), [],
config[CONF_CLOSE_ACTION])
if CONF_STOP_ACTION in config:
automation.build_automations(var.get_stop_trigger(), [],
config[CONF_STOP_ACTION])
if CONF_OPTIMISTIC in config:
add(var.set_optimistic(config[CONF_OPTIMISTIC]))
if CONF_ASSUMED_STATE in config:
add(var.set_assumed_state(config[CONF_ASSUMED_STATE]))
if CONF_RESTORE_MODE in config:
add(var.set_restore_mode(RESTORE_MODES[config[CONF_RESTORE_MODE]]))
BUILD_FLAGS = '-DUSE_TEMPLATE_COVER'
CONF_COVER_TEMPLATE_PUBLISH = 'cover.template.publish'
COVER_TEMPLATE_PUBLISH_ACTION_SCHEMA = cv.Schema({
vol.Required(CONF_ID): cv.use_variable_id(cover.Cover),
vol.Exclusive(CONF_STATE, 'pos'): cv.templatable(cover.validate_cover_state),
vol.Exclusive(CONF_POSITION, 'pos'): cv.templatable(cv.zero_to_one_float),
vol.Optional(CONF_CURRENT_OPERATION): cv.templatable(cover.validate_cover_operation),
})
@ACTION_REGISTRY.register(CONF_COVER_TEMPLATE_PUBLISH,
COVER_TEMPLATE_PUBLISH_ACTION_SCHEMA)
def cover_template_publish_to_code(config, action_id, template_arg, args):
var = yield get_variable(config[CONF_ID])
type = CoverPublishAction.template(template_arg)
rhs = type.new(var)
action = Pvariable(action_id, rhs, type=type)
if CONF_STATE in config:
template_ = yield templatable(config[CONF_STATE], args, float,
to_exp=cover.COVER_STATES)
add(action.set_position(template_))
if CONF_POSITION in config:
template_ = yield templatable(config[CONF_POSITION], args, float,
to_exp=cover.COVER_STATES)
add(action.set_position(template_))
if CONF_CURRENT_OPERATION in config:
template_ = yield templatable(config[CONF_CURRENT_OPERATION], args, cover.CoverOperation,
to_exp=cover.COVER_OPERATIONS)
add(action.set_current_operation(template_))
yield action

View File

@@ -1,44 +0,0 @@
import voluptuous as vol
from esphome import automation
from esphome.components import cover
import esphome.config_validation as cv
from esphome.const import CONF_CLOSE_ACTION, CONF_CLOSE_DURATION, CONF_ID, CONF_NAME, \
CONF_OPEN_ACTION, CONF_OPEN_DURATION, CONF_STOP_ACTION
from esphome.cpp_generator import Pvariable, add
from esphome.cpp_helpers import setup_component
from esphome.cpp_types import App, Component
TimeBasedCover = cover.cover_ns.class_('TimeBasedCover', cover.Cover, Component)
PLATFORM_SCHEMA = cv.nameable(cover.COVER_PLATFORM_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(TimeBasedCover),
vol.Required(CONF_STOP_ACTION): automation.validate_automation(single=True),
vol.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True),
vol.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds,
vol.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
vol.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
}).extend(cv.COMPONENT_SCHEMA.schema))
def to_code(config):
rhs = App.register_component(TimeBasedCover.new(config[CONF_NAME]))
var = Pvariable(config[CONF_ID], rhs)
cover.register_cover(var, config)
setup_component(var, config)
automation.build_automations(var.get_stop_trigger(), [],
config[CONF_STOP_ACTION])
add(var.set_open_duration(config[CONF_OPEN_DURATION]))
automation.build_automations(var.get_open_trigger(), [],
config[CONF_OPEN_ACTION])
add(var.set_close_duration(config[CONF_CLOSE_DURATION]))
automation.build_automations(var.get_close_trigger(), [],
config[CONF_CLOSE_ACTION])
BUILD_FLAGS = '-DUSE_TIME_BASED_COVER'