mirror of
https://github.com/Threnklyn/esphome-dev.git
synced 2026-06-13 08:03:32 +02: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:
@@ -1,108 +1,89 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphome.automation import ACTION_REGISTRY, maybe_simple_id
|
||||
from esphome.components.power_supply import PowerSupplyComponent
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.automation import ACTION_REGISTRY, maybe_simple_id
|
||||
from esphome.components import power_supply
|
||||
from esphome.const import CONF_ID, CONF_INVERTED, CONF_LEVEL, CONF_MAX_POWER, \
|
||||
CONF_MIN_POWER, CONF_POWER_SUPPLY
|
||||
from esphome.core import CORE
|
||||
from esphome.cpp_generator import Pvariable, add, get_variable, templatable
|
||||
from esphome.cpp_types import Action, esphome_ns, float_
|
||||
from esphome.core import CORE, coroutine
|
||||
|
||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||
|
||||
})
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
BINARY_OUTPUT_SCHEMA = cv.Schema({
|
||||
vol.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(PowerSupplyComponent),
|
||||
vol.Optional(CONF_INVERTED): cv.boolean,
|
||||
cv.Optional(CONF_POWER_SUPPLY): cv.use_variable_id(power_supply.PowerSupply),
|
||||
cv.Optional(CONF_INVERTED): cv.boolean,
|
||||
})
|
||||
|
||||
BINARY_OUTPUT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BINARY_OUTPUT_SCHEMA.schema)
|
||||
|
||||
FLOAT_OUTPUT_SCHEMA = BINARY_OUTPUT_SCHEMA.extend({
|
||||
vol.Optional(CONF_MAX_POWER): cv.percentage,
|
||||
vol.Optional(CONF_MIN_POWER): cv.percentage,
|
||||
cv.Optional(CONF_MAX_POWER): cv.percentage,
|
||||
cv.Optional(CONF_MIN_POWER): cv.percentage,
|
||||
})
|
||||
|
||||
FLOAT_OUTPUT_PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(FLOAT_OUTPUT_SCHEMA.schema)
|
||||
|
||||
output_ns = esphome_ns.namespace('output')
|
||||
output_ns = cg.esphome_ns.namespace('output')
|
||||
BinaryOutput = output_ns.class_('BinaryOutput')
|
||||
BinaryOutputPtr = BinaryOutput.operator('ptr')
|
||||
FloatOutput = output_ns.class_('FloatOutput', BinaryOutput)
|
||||
FloatOutputPtr = FloatOutput.operator('ptr')
|
||||
|
||||
# Actions
|
||||
TurnOffAction = output_ns.class_('TurnOffAction', Action)
|
||||
TurnOnAction = output_ns.class_('TurnOnAction', Action)
|
||||
SetLevelAction = output_ns.class_('SetLevelAction', Action)
|
||||
TurnOffAction = output_ns.class_('TurnOffAction', cg.Action)
|
||||
TurnOnAction = output_ns.class_('TurnOnAction', cg.Action)
|
||||
SetLevelAction = output_ns.class_('SetLevelAction', cg.Action)
|
||||
|
||||
|
||||
def setup_output_platform_(obj, config, skip_power_supply=False):
|
||||
@coroutine
|
||||
def setup_output_platform_(obj, config):
|
||||
if CONF_INVERTED in config:
|
||||
add(obj.set_inverted(config[CONF_INVERTED]))
|
||||
if not skip_power_supply and CONF_POWER_SUPPLY in config:
|
||||
power_supply = yield get_variable(config[CONF_POWER_SUPPLY])
|
||||
add(obj.set_power_supply(power_supply))
|
||||
cg.add(obj.set_inverted(config[CONF_INVERTED]))
|
||||
if CONF_POWER_SUPPLY in config:
|
||||
power_supply_ = yield cg.get_variable(config[CONF_POWER_SUPPLY])
|
||||
cg.add(obj.set_power_supply(power_supply_))
|
||||
if CONF_MAX_POWER in config:
|
||||
add(obj.set_max_power(config[CONF_MAX_POWER]))
|
||||
cg.add(obj.set_max_power(config[CONF_MAX_POWER]))
|
||||
if CONF_MIN_POWER in config:
|
||||
add(obj.set_min_power(config[CONF_MIN_POWER]))
|
||||
|
||||
|
||||
def setup_output_platform(obj, config, skip_power_supply=False):
|
||||
CORE.add_job(setup_output_platform_, obj, config, skip_power_supply)
|
||||
cg.add(obj.set_min_power(config[CONF_MIN_POWER]))
|
||||
|
||||
|
||||
@coroutine
|
||||
def register_output(var, config):
|
||||
output_var = Pvariable(config[CONF_ID], var, has_side_effects=True)
|
||||
CORE.add_job(setup_output_platform_, output_var, config)
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
yield setup_output_platform_(var, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_OUTPUT'
|
||||
|
||||
CONF_OUTPUT_TURN_ON = 'output.turn_on'
|
||||
OUTPUT_TURN_ON_ACTION = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(BinaryOutput),
|
||||
BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id({
|
||||
cv.Required(CONF_ID): cv.use_variable_id(BinaryOutput),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_ON, OUTPUT_TURN_ON_ACTION)
|
||||
@ACTION_REGISTRY.register('output.turn_on', BINARY_OUTPUT_ACTION_SCHEMA)
|
||||
def output_turn_on_to_code(config, action_id, template_arg, args):
|
||||
var = yield get_variable(config[CONF_ID])
|
||||
rhs = var.make_turn_on_action(template_arg)
|
||||
var = yield cg.get_variable(config[CONF_ID])
|
||||
type = TurnOnAction.template(template_arg)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
rhs = type.new(var)
|
||||
yield cg.Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_OUTPUT_TURN_OFF = 'output.turn_off'
|
||||
OUTPUT_TURN_OFF_ACTION = maybe_simple_id({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(BinaryOutput)
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_OUTPUT_TURN_OFF, OUTPUT_TURN_OFF_ACTION)
|
||||
@ACTION_REGISTRY.register('output.turn_off', BINARY_OUTPUT_ACTION_SCHEMA)
|
||||
def output_turn_off_to_code(config, action_id, template_arg, args):
|
||||
var = yield get_variable(config[CONF_ID])
|
||||
rhs = var.make_turn_off_action(template_arg)
|
||||
var = yield cg.get_variable(config[CONF_ID])
|
||||
type = TurnOffAction.template(template_arg)
|
||||
yield Pvariable(action_id, rhs, type=type)
|
||||
rhs = type.new(var)
|
||||
yield cg.Pvariable(action_id, rhs, type=type)
|
||||
|
||||
|
||||
CONF_OUTPUT_SET_LEVEL = 'output.set_level'
|
||||
OUTPUT_SET_LEVEL_ACTION = cv.Schema({
|
||||
vol.Required(CONF_ID): cv.use_variable_id(FloatOutput),
|
||||
vol.Required(CONF_LEVEL): cv.templatable(cv.percentage),
|
||||
})
|
||||
|
||||
|
||||
@ACTION_REGISTRY.register(CONF_OUTPUT_SET_LEVEL, OUTPUT_SET_LEVEL_ACTION)
|
||||
@ACTION_REGISTRY.register('output.set_level', cv.Schema({
|
||||
cv.Required(CONF_ID): cv.use_variable_id(FloatOutput),
|
||||
cv.Required(CONF_LEVEL): cv.templatable(cv.percentage),
|
||||
}))
|
||||
def output_set_level_to_code(config, action_id, template_arg, args):
|
||||
var = yield get_variable(config[CONF_ID])
|
||||
rhs = var.make_set_level_action(template_arg)
|
||||
var = yield cg.get_variable(config[CONF_ID])
|
||||
type = SetLevelAction.template(template_arg)
|
||||
action = Pvariable(action_id, rhs, type=type)
|
||||
template_ = yield templatable(config[CONF_LEVEL], args, float_)
|
||||
add(action.set_level(template_))
|
||||
rhs = type.new(var)
|
||||
action = cg.Pvariable(action_id, rhs, type=type)
|
||||
template_ = yield cg.templatable(config[CONF_LEVEL], args, float)
|
||||
cg.add(action.set_level(template_))
|
||||
yield action
|
||||
|
||||
|
||||
def to_code(config):
|
||||
cg.add_global(output_ns.using)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "automation.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace output {
|
||||
|
||||
static const char *TAG = "output.automation";
|
||||
|
||||
} // namespace output
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/output/binary_output.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace output {
|
||||
|
||||
template<typename... Ts> class TurnOffAction : public Action<Ts...> {
|
||||
public:
|
||||
TurnOffAction(BinaryOutput *output) : output_(output) {}
|
||||
|
||||
void play(Ts... x) override {
|
||||
this->output_->turn_off();
|
||||
this->play_next(x...);
|
||||
}
|
||||
|
||||
protected:
|
||||
BinaryOutput *output_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class TurnOnAction : public Action<Ts...> {
|
||||
public:
|
||||
TurnOnAction(BinaryOutput *output) : output_(output) {}
|
||||
|
||||
void play(Ts... x) override {
|
||||
this->output_->turn_on();
|
||||
this->play_next(x...);
|
||||
}
|
||||
|
||||
protected:
|
||||
BinaryOutput *output_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetLevelAction : public Action<Ts...> {
|
||||
public:
|
||||
SetLevelAction(FloatOutput *output) : output_(output) {}
|
||||
|
||||
TEMPLATABLE_VALUE(float, level)
|
||||
void play(Ts... x) override {
|
||||
this->output_->set_level(this->level_.value(x...));
|
||||
this->play_next(x...);
|
||||
}
|
||||
|
||||
protected:
|
||||
FloatOutput *output_;
|
||||
};
|
||||
|
||||
} // namespace output
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
#include "esphome/components/power_supply/power_supply.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace output {
|
||||
|
||||
#define LOG_BINARY_OUTPUT(this) \
|
||||
if (this->inverted_) { \
|
||||
ESP_LOGCONFIG(TAG, " Inverted: YES"); \
|
||||
}
|
||||
|
||||
class BinaryOutput {
|
||||
public:
|
||||
/// Set the inversion state of this binary output.
|
||||
void set_inverted(bool inverted) { this->inverted_ = inverted; }
|
||||
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
/** Use this to connect up a power supply to this output.
|
||||
*
|
||||
* Whenever this output is enabled, the power supply will automatically be turned on.
|
||||
*
|
||||
* @param power_supply The PowerSupplyComponent, set this to nullptr to disable the power supply.
|
||||
*/
|
||||
void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_supply_ = power_supply; }
|
||||
#endif
|
||||
|
||||
/// Enable this binary output.
|
||||
virtual void turn_on() {
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
if (this->power_supply_ != nullptr && !this->has_requested_high_power_) {
|
||||
this->power_supply_->request_high_power();
|
||||
this->has_requested_high_power_ = true;
|
||||
}
|
||||
#endif
|
||||
this->write_state(!this->inverted_);
|
||||
}
|
||||
|
||||
/// Disable this binary output.
|
||||
virtual void turn_off() {
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
if (this->power_supply_ != nullptr && this->has_requested_high_power_) {
|
||||
this->power_supply_->unrequest_high_power();
|
||||
this->has_requested_high_power_ = false;
|
||||
}
|
||||
#endif
|
||||
this->write_state(this->inverted_);
|
||||
}
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
/// Return whether this binary output is inverted.
|
||||
bool is_inverted() const { return this->inverted_; }
|
||||
|
||||
protected:
|
||||
virtual void write_state(bool state) = 0;
|
||||
|
||||
bool inverted_{false};
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
power_supply::PowerSupply *power_supply_{nullptr};
|
||||
bool has_requested_high_power_{false};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace output
|
||||
} // namespace esphome
|
||||
@@ -1,56 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_OUTPUTS, CONF_TYPE
|
||||
from esphome.cpp_generator import Pvariable, get_variable
|
||||
from esphome.cpp_helpers import setup_component
|
||||
|
||||
BinaryCopyOutput = output.output_ns.class_('BinaryCopyOutput', output.BinaryOutput)
|
||||
FloatCopyOutput = output.output_ns.class_('FloatCopyOutput', output.FloatOutput)
|
||||
|
||||
BINARY_SCHEMA = output.PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(BinaryCopyOutput),
|
||||
vol.Required(CONF_TYPE): 'binary',
|
||||
vol.Required(CONF_OUTPUTS): cv.ensure_list(cv.use_variable_id(output.BinaryOutput)),
|
||||
})
|
||||
|
||||
FLOAT_SCHEMA = output.PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(FloatCopyOutput),
|
||||
vol.Required(CONF_TYPE): 'float',
|
||||
vol.Required(CONF_OUTPUTS): cv.ensure_list(cv.use_variable_id(output.FloatOutput)),
|
||||
})
|
||||
|
||||
|
||||
def validate_copy_output(value):
|
||||
if not isinstance(value, dict):
|
||||
raise vol.Invalid("Value must be dict")
|
||||
type = cv.string_strict(value.get(CONF_TYPE, 'float')).lower()
|
||||
value[CONF_TYPE] = type
|
||||
if type == 'binary':
|
||||
return BINARY_SCHEMA(value)
|
||||
if type == 'float':
|
||||
return FLOAT_SCHEMA(value)
|
||||
raise vol.Invalid("type must either be binary or float, not {}!".format(type))
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = validate_copy_output
|
||||
|
||||
|
||||
def to_code(config):
|
||||
outputs = []
|
||||
for out in config[CONF_OUTPUTS]:
|
||||
outputs.append((yield get_variable(out)))
|
||||
|
||||
klass = {
|
||||
'binary': BinaryCopyOutput,
|
||||
'float': FloatCopyOutput,
|
||||
}[config[CONF_TYPE]]
|
||||
rhs = klass.new(outputs)
|
||||
gpio = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
output.setup_output_platform(gpio, config)
|
||||
setup_component(gpio, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_COPY_OUTPUT'
|
||||
@@ -1,67 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_OUTPUTS, CONF_TYPE
|
||||
from esphome.cpp_generator import process_lambda, variable
|
||||
from esphome.cpp_types import std_vector
|
||||
|
||||
CustomBinaryOutputConstructor = output.output_ns.class_('CustomBinaryOutputConstructor')
|
||||
CustomFloatOutputConstructor = output.output_ns.class_('CustomFloatOutputConstructor')
|
||||
|
||||
BINARY_SCHEMA = output.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(CustomBinaryOutputConstructor),
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Required(CONF_TYPE): 'binary',
|
||||
vol.Required(CONF_OUTPUTS):
|
||||
cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(output.BinaryOutput),
|
||||
})),
|
||||
})
|
||||
|
||||
FLOAT_SCHEMA = output.PLATFORM_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(CustomFloatOutputConstructor),
|
||||
vol.Required(CONF_LAMBDA): cv.lambda_,
|
||||
vol.Required(CONF_TYPE): 'float',
|
||||
vol.Required(CONF_OUTPUTS):
|
||||
cv.ensure_list(output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(output.FloatOutput),
|
||||
})),
|
||||
})
|
||||
|
||||
|
||||
def validate_custom_output(value):
|
||||
if not isinstance(value, dict):
|
||||
raise vol.Invalid("Value must be dict")
|
||||
if CONF_TYPE not in value:
|
||||
raise vol.Invalid("type not specified!")
|
||||
type = cv.string_strict(value[CONF_TYPE]).lower()
|
||||
value[CONF_TYPE] = type
|
||||
if type == 'binary':
|
||||
return BINARY_SCHEMA(value)
|
||||
if type == 'float':
|
||||
return FLOAT_SCHEMA(value)
|
||||
raise vol.Invalid("type must either be binary or float, not {}!".format(type))
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = validate_custom_output
|
||||
|
||||
|
||||
def to_code(config):
|
||||
type = config[CONF_TYPE]
|
||||
if type == 'binary':
|
||||
ret_type = output.BinaryOutputPtr
|
||||
klass = CustomBinaryOutputConstructor
|
||||
else:
|
||||
ret_type = output.FloatOutputPtr
|
||||
klass = CustomFloatOutputConstructor
|
||||
template_ = yield process_lambda(config[CONF_LAMBDA], [],
|
||||
return_type=std_vector.template(ret_type))
|
||||
|
||||
rhs = klass(template_)
|
||||
custom = variable(config[CONF_ID], rhs)
|
||||
for i, conf in enumerate(config[CONF_OUTPUTS]):
|
||||
output.register_output(custom.get_output(i), conf)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_CUSTOM_OUTPUT'
|
||||
@@ -1,41 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphome import pins
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266
|
||||
from esphome.cpp_generator import Pvariable, add
|
||||
from esphome.cpp_helpers import gpio_output_pin_expression, setup_component
|
||||
from esphome.cpp_types import App, Component
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP8266]
|
||||
|
||||
|
||||
def valid_pwm_pin(value):
|
||||
num = value[CONF_NUMBER]
|
||||
cv.one_of(0, 1, 2, 3, 4, 5, 9, 10, 12, 13, 14, 15, 16)(num)
|
||||
return value
|
||||
|
||||
|
||||
ESP8266PWMOutput = output.output_ns.class_('ESP8266PWMOutput', output.FloatOutput, Component)
|
||||
|
||||
PLATFORM_SCHEMA = output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(ESP8266PWMOutput),
|
||||
vol.Required(CONF_PIN): vol.All(pins.internal_gpio_output_pin_schema, valid_pwm_pin),
|
||||
vol.Optional(CONF_FREQUENCY): vol.All(cv.frequency, vol.Range(min=1.0e-6)),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin = yield gpio_output_pin_expression(config[CONF_PIN])
|
||||
rhs = App.make_esp8266_pwm_output(pin)
|
||||
gpio = Pvariable(config[CONF_ID], rhs)
|
||||
|
||||
if CONF_FREQUENCY in config:
|
||||
add(gpio.set_frequency(config[CONF_FREQUENCY]))
|
||||
|
||||
output.setup_output_platform(gpio, config)
|
||||
setup_component(gpio, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_ESP8266_PWM_OUTPUT'
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "float_output.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace output {
|
||||
|
||||
static const char *TAG = "output.float";
|
||||
|
||||
void FloatOutput::set_max_power(float max_power) {
|
||||
this->max_power_ = clamp(max_power, this->min_power_, 1.0f); // Clamp to MIN>=MAX>=1.0
|
||||
}
|
||||
|
||||
float FloatOutput::get_max_power() const { return this->max_power_; }
|
||||
|
||||
void FloatOutput::set_min_power(float min_power) {
|
||||
this->min_power_ = clamp(min_power, 0.0f, this->max_power_); // Clamp to 0.0>=MIN>=MAX
|
||||
}
|
||||
|
||||
float FloatOutput::get_min_power() const { return this->min_power_; }
|
||||
|
||||
void FloatOutput::set_level(float state) {
|
||||
state = clamp(state, 0.0f, 1.0f);
|
||||
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
if (state > 0.0f) { // ON
|
||||
if (this->power_supply_ != nullptr && !this->has_requested_high_power_) {
|
||||
this->power_supply_->request_high_power();
|
||||
this->has_requested_high_power_ = true;
|
||||
}
|
||||
} else { // OFF
|
||||
if (this->power_supply_ != nullptr && this->has_requested_high_power_) {
|
||||
this->power_supply_->unrequest_high_power();
|
||||
this->has_requested_high_power_ = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
float adjusted_value = (state * (this->max_power_ - this->min_power_)) + this->min_power_;
|
||||
if (this->is_inverted())
|
||||
adjusted_value = 1.0f - adjusted_value;
|
||||
this->write_state(adjusted_value);
|
||||
}
|
||||
|
||||
void FloatOutput::write_state(bool state) { this->set_level(state != this->inverted_ ? 1.0f : 0.0f); }
|
||||
|
||||
} // namespace output
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "binary_output.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace output {
|
||||
|
||||
#define LOG_FLOAT_OUTPUT(this) \
|
||||
LOG_BINARY_OUTPUT(this) \
|
||||
if (this->max_power_ != 1.0f) { \
|
||||
ESP_LOGCONFIG(TAG, " Max Power: %.1f%%", this->max_power_ * 100.0f); \
|
||||
} \
|
||||
if (this->min_power_ != 0.0f) { \
|
||||
ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->min_power_ * 100.0f); \
|
||||
}
|
||||
|
||||
/** Base class for all output components that can output a variable level, like PWM.
|
||||
*
|
||||
* Floating Point Outputs always use output values in the range from 0.0 to 1.0 (inclusive), where 0.0 means off
|
||||
* and 1.0 means fully on. While using floating point numbers might make computation slower, it
|
||||
* makes using maths much easier and (in theory) supports all possible bit depths.
|
||||
*
|
||||
* If you want to create a FloatOutput yourself, you essentially just have to override write_state(float).
|
||||
* That method will be called for you with inversion and max-min power and offset to min power already applied.
|
||||
*
|
||||
* This interface is compatible with BinaryOutput (and will automatically convert the binary states to floating
|
||||
* point states for you). Additionally, this class provides a way for users to set a minimum and/or maximum power
|
||||
* output
|
||||
*/
|
||||
class FloatOutput : public BinaryOutput {
|
||||
public:
|
||||
/** Set the maximum power output of this component.
|
||||
*
|
||||
* All values are multiplied by max_power - min_power and offset to min_power to get the adjusted value.
|
||||
*
|
||||
* @param max_power Automatically clamped from 0 or min_power to 1.
|
||||
*/
|
||||
void set_max_power(float max_power);
|
||||
|
||||
/** Set the minimum power output of this component.
|
||||
*
|
||||
* All values are multiplied by max_power - min_power and offset by min_power to get the adjusted value.
|
||||
*
|
||||
* @param min_power Automatically clamped from 0 to max_power or 1.
|
||||
*/
|
||||
void set_min_power(float min_power);
|
||||
|
||||
/// Set the level of this float output, this is called from the front-end.
|
||||
void set_level(float state);
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
|
||||
/// Get the maximum power output.
|
||||
float get_max_power() const;
|
||||
|
||||
/// Get the minimum power output.
|
||||
float get_min_power() const;
|
||||
|
||||
protected:
|
||||
/// Implement BinarySensor's write_enabled; this should never be called.
|
||||
void write_state(bool state) override;
|
||||
virtual void write_state(float state) = 0;
|
||||
|
||||
float max_power_{1.0f};
|
||||
float min_power_{0.0f};
|
||||
};
|
||||
|
||||
} // namespace output
|
||||
} // namespace esphome
|
||||
@@ -1,28 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphome import pins
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_PIN
|
||||
from esphome.cpp_generator import Pvariable
|
||||
from esphome.cpp_helpers import gpio_output_pin_expression, setup_component
|
||||
from esphome.cpp_types import App, Component
|
||||
|
||||
GPIOBinaryOutputComponent = output.output_ns.class_('GPIOBinaryOutputComponent',
|
||||
output.BinaryOutput, Component)
|
||||
|
||||
PLATFORM_SCHEMA = output.BINARY_OUTPUT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(GPIOBinaryOutputComponent),
|
||||
vol.Required(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
pin = yield gpio_output_pin_expression(config[CONF_PIN])
|
||||
rhs = App.make_gpio_output(pin)
|
||||
gpio = Pvariable(config[CONF_ID], rhs)
|
||||
output.setup_output_platform(gpio, config)
|
||||
setup_component(gpio, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_GPIO_OUTPUT'
|
||||
@@ -1,47 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphome import pins
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import APB_CLOCK_FREQ, CONF_BIT_DEPTH, CONF_CHANNEL, CONF_FREQUENCY, \
|
||||
CONF_ID, CONF_PIN, ESP_PLATFORM_ESP32
|
||||
from esphome.cpp_generator import Pvariable, add
|
||||
from esphome.cpp_helpers import setup_component
|
||||
from esphome.cpp_types import App, Component
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
||||
|
||||
def validate_frequency_bit_depth(obj):
|
||||
frequency = obj.get(CONF_FREQUENCY, 1000)
|
||||
bit_depth = obj.get(CONF_BIT_DEPTH, 12)
|
||||
max_freq = APB_CLOCK_FREQ / (2**bit_depth)
|
||||
if frequency > max_freq:
|
||||
raise vol.Invalid('Maximum frequency for bit depth {} is {}Hz'.format(bit_depth, max_freq))
|
||||
return obj
|
||||
|
||||
|
||||
LEDCOutputComponent = output.output_ns.class_('LEDCOutputComponent', output.FloatOutput, Component)
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(LEDCOutputComponent),
|
||||
vol.Required(CONF_PIN): pins.output_pin,
|
||||
vol.Optional(CONF_FREQUENCY): cv.frequency,
|
||||
vol.Optional(CONF_BIT_DEPTH): vol.All(vol.Coerce(int), vol.Range(min=1, max=15)),
|
||||
vol.Optional(CONF_CHANNEL): vol.All(vol.Coerce(int), vol.Range(min=0, max=15))
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema), validate_frequency_bit_depth)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
frequency = config.get(CONF_FREQUENCY)
|
||||
if frequency is None and CONF_BIT_DEPTH in config:
|
||||
frequency = 1000
|
||||
rhs = App.make_ledc_output(config[CONF_PIN], frequency, config.get(CONF_BIT_DEPTH))
|
||||
ledc = Pvariable(config[CONF_ID], rhs)
|
||||
if CONF_CHANNEL in config:
|
||||
add(ledc.set_channel(config[CONF_CHANNEL]))
|
||||
output.setup_output_platform(ledc, config)
|
||||
setup_component(ledc, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_LEDC_OUTPUT'
|
||||
@@ -1,33 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphome.components import output
|
||||
from esphome.components.my9231 import MY9231OutputComponent
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID, CONF_MY9231_ID, CONF_POWER_SUPPLY
|
||||
from esphome.cpp_generator import Pvariable, get_variable
|
||||
from esphome.cpp_helpers import setup_component
|
||||
|
||||
DEPENDENCIES = ['my9231']
|
||||
|
||||
Channel = MY9231OutputComponent.class_('Channel', output.FloatOutput)
|
||||
|
||||
PLATFORM_SCHEMA = output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(Channel),
|
||||
vol.Required(CONF_CHANNEL): vol.All(vol.Coerce(int),
|
||||
vol.Range(min=0, max=65535)),
|
||||
cv.GenerateID(CONF_MY9231_ID): cv.use_variable_id(MY9231OutputComponent),
|
||||
}).extend(cv.COMPONENT_SCHEMA.schema)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
power_supply = None
|
||||
if CONF_POWER_SUPPLY in config:
|
||||
power_supply = yield get_variable(config[CONF_POWER_SUPPLY])
|
||||
my9231 = yield get_variable(config[CONF_MY9231_ID])
|
||||
rhs = my9231.create_channel(config[CONF_CHANNEL], power_supply)
|
||||
out = Pvariable(config[CONF_ID], rhs)
|
||||
output.setup_output_platform(out, config, skip_power_supply=True)
|
||||
setup_component(out, config)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_MY9231_OUTPUT'
|
||||
@@ -1,31 +0,0 @@
|
||||
import voluptuous as vol
|
||||
|
||||
from esphome.components import output
|
||||
from esphome.components.pca9685 import PCA9685OutputComponent
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID, CONF_PCA9685_ID, CONF_POWER_SUPPLY
|
||||
from esphome.cpp_generator import Pvariable, get_variable
|
||||
|
||||
DEPENDENCIES = ['pca9685']
|
||||
|
||||
Channel = PCA9685OutputComponent.class_('Channel', output.FloatOutput)
|
||||
|
||||
PLATFORM_SCHEMA = output.FLOAT_OUTPUT_PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_ID): cv.declare_variable_id(Channel),
|
||||
vol.Required(CONF_CHANNEL): vol.All(vol.Coerce(int),
|
||||
vol.Range(min=0, max=15)),
|
||||
cv.GenerateID(CONF_PCA9685_ID): cv.use_variable_id(PCA9685OutputComponent),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
power_supply = None
|
||||
if CONF_POWER_SUPPLY in config:
|
||||
power_supply = yield get_variable(config[CONF_POWER_SUPPLY])
|
||||
pca9685 = yield get_variable(config[CONF_PCA9685_ID])
|
||||
rhs = pca9685.create_channel(config[CONF_CHANNEL], power_supply)
|
||||
out = Pvariable(config[CONF_ID], rhs)
|
||||
output.setup_output_platform(out, config, skip_power_supply=True)
|
||||
|
||||
|
||||
BUILD_FLAGS = '-DUSE_PCA9685_OUTPUT'
|
||||
@@ -0,0 +1,19 @@
|
||||
from esphome.components import output, switch
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_ID, CONF_NAME, CONF_OUTPUT
|
||||
from .. import output_ns
|
||||
|
||||
OutputSwitch = output_ns.class_('OutputSwitch', switch.Switch, cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = cv.nameable(switch.SWITCH_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(OutputSwitch),
|
||||
cv.Required(CONF_OUTPUT): cv.use_variable_id(output.BinaryOutput),
|
||||
}).extend(cv.COMPONENT_SCHEMA))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
output_ = yield cg.get_variable(config[CONF_OUTPUT])
|
||||
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], output_)
|
||||
yield cg.register_component(var, config)
|
||||
yield switch.register_switch(var, config)
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "output_switch.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace output {
|
||||
|
||||
static const char *TAG = "output.switch";
|
||||
|
||||
void OutputSwitch::dump_config() { LOG_SWITCH("", "Output Switch", this); }
|
||||
void OutputSwitch::setup() {
|
||||
auto restored = this->get_initial_state();
|
||||
if (!restored.has_value())
|
||||
return;
|
||||
|
||||
if (*restored) {
|
||||
this->turn_on();
|
||||
} else {
|
||||
this->turn_off();
|
||||
}
|
||||
}
|
||||
void OutputSwitch::write_state(bool state) {
|
||||
if (state) {
|
||||
this->output_->turn_on();
|
||||
} else {
|
||||
this->output_->turn_off();
|
||||
}
|
||||
this->publish_state(state);
|
||||
}
|
||||
|
||||
} // namespace output
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "esphome/components/output/binary_output.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace output {
|
||||
|
||||
class OutputSwitch : public switch_::Switch, public Component {
|
||||
public:
|
||||
OutputSwitch(const std::string &name, BinaryOutput *output) : Switch(name), output_(output) {}
|
||||
|
||||
void setup() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
|
||||
output::BinaryOutput *output_;
|
||||
};
|
||||
|
||||
} // namespace output
|
||||
} // namespace esphome
|
||||
Reference in New Issue
Block a user