🏗 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
+22
View File
@@ -0,0 +1,22 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c
from esphome.const import CONF_FREQUENCY, CONF_ID
DEPENDENCIES = ['i2c']
MULTI_CONF = True
pca9685_ns = cg.esphome_ns.namespace('pca9685')
PCA9685Output = pca9685_ns.class_('PCA9685Output', cg.Component, i2c.I2CDevice)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_variable_id(PCA9685Output),
cv.Required(CONF_FREQUENCY): cv.All(cv.frequency,
cv.Range(min=23.84, max=1525.88)),
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x40))
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], config[CONF_FREQUENCY])
yield cg.register_component(var, config)
yield i2c.register_i2c_device(var, config)
+24
View File
@@ -0,0 +1,24 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import output
from esphome.const import CONF_CHANNEL, CONF_ID
from . import PCA9685Output, pca9685_ns
DEPENDENCIES = ['pca9685']
PCA9685Channel = pca9685_ns.class_('PCA9685Channel', output.FloatOutput)
CONF_PCA9685_ID = 'pca9685_id'
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
cv.Required(CONF_ID): cv.declare_variable_id(PCA9685Channel),
cv.GenerateID(CONF_PCA9685_ID): cv.use_variable_id(PCA9685Output),
cv.Required(CONF_CHANNEL): cv.All(cv.Coerce(int), cv.Range(min=0, max=15)),
})
def to_code(config):
paren = yield cg.get_variable(config[CONF_PCA9685_ID])
rhs = paren.create_channel(config[CONF_CHANNEL])
var = cg.Pvariable(config[CONF_ID], rhs)
yield output.register_output(var, config)
@@ -0,0 +1,141 @@
#include "pca9685_output.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace pca9685 {
static const char *TAG = "pca9685";
const uint8_t PCA9685_MODE_INVERTED = 0x10;
const uint8_t PCA9685_MODE_OUTPUT_ONACK = 0x08;
const uint8_t PCA9685_MODE_OUTPUT_TOTEM_POLE = 0x04;
const uint8_t PCA9685_MODE_OUTNE_HIGHZ = 0x02;
const uint8_t PCA9685_MODE_OUTNE_LOW = 0x01;
static const uint8_t PCA9685_REGISTER_SOFTWARE_RESET = 0x06;
static const uint8_t PCA9685_REGISTER_MODE1 = 0x00;
static const uint8_t PCA9685_REGISTER_MODE2 = 0x01;
static const uint8_t PCA9685_REGISTER_LED0 = 0x06;
static const uint8_t PCA9685_REGISTER_PRE_SCALE = 0xFE;
static const uint8_t PCA9685_MODE1_RESTART = 0b10000000;
static const uint8_t PCA9685_MODE1_AUTOINC = 0b00100000;
static const uint8_t PCA9685_MODE1_SLEEP = 0b00010000;
void PCA9685Output::setup() {
ESP_LOGCONFIG(TAG, "Setting up PCA9685OutputComponent...");
ESP_LOGV(TAG, " Resetting devices...");
if (!this->write_bytes(PCA9685_REGISTER_SOFTWARE_RESET, nullptr, 0)) {
this->mark_failed();
return;
}
if (!this->write_byte(PCA9685_REGISTER_MODE1, PCA9685_MODE1_RESTART | PCA9685_MODE1_AUTOINC)) {
this->mark_failed();
return;
}
if (!this->write_byte(PCA9685_REGISTER_MODE2, this->mode_)) {
this->mark_failed();
return;
}
int pre_scaler = static_cast<int>((25000000 / (4096 * this->frequency_)) - 1);
if (pre_scaler > 255)
pre_scaler = 255;
if (pre_scaler < 3)
pre_scaler = 3;
ESP_LOGV(TAG, " -> Prescaler: %d", pre_scaler);
uint8_t mode1;
if (!this->read_byte(PCA9685_REGISTER_MODE1, &mode1)) {
this->mark_failed();
return;
}
mode1 = (mode1 & ~PCA9685_MODE1_RESTART) | PCA9685_MODE1_SLEEP;
if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
this->mark_failed();
return;
}
if (!this->write_byte(PCA9685_REGISTER_PRE_SCALE, pre_scaler)) {
this->mark_failed();
return;
}
mode1 = (mode1 & ~PCA9685_MODE1_SLEEP) | PCA9685_MODE1_RESTART;
if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
this->mark_failed();
return;
}
delayMicroseconds(500);
this->loop();
}
void PCA9685Output::dump_config() {
ESP_LOGCONFIG(TAG, "PCA9685:");
ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_);
ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_);
if (this->is_failed()) {
ESP_LOGE(TAG, "Setting up PCA9685 failed!");
}
}
void PCA9685Output::loop() {
if (this->min_channel_ == 0xFF || !this->update_)
return;
const uint16_t num_channels = this->max_channel_ - this->min_channel_ + 1;
for (uint8_t channel = this->min_channel_; channel <= this->max_channel_; channel++) {
uint16_t phase_begin = uint16_t(channel - this->min_channel_) / num_channels * 4096;
uint16_t phase_end;
uint16_t amount = this->pwm_amounts_[channel];
if (amount == 0) {
phase_end = 4096;
} else if (amount >= 4096) {
phase_begin = 4096;
phase_end = 0;
} else {
phase_end = phase_begin + amount;
if (phase_end >= 4096)
phase_end -= 4096;
}
ESP_LOGVV(TAG, "Channel %02u: amount=%04u phase_begin=%04u phase_end=%04u", channel, amount, phase_begin,
phase_end);
uint8_t data[4];
data[0] = phase_begin & 0xFF;
data[1] = (phase_begin >> 8) & 0xFF;
data[2] = phase_end & 0xFF;
data[3] = (phase_end >> 8) & 0xFF;
uint8_t reg = PCA9685_REGISTER_LED0 + 4 * channel;
if (!this->write_bytes(reg, data, 4)) {
this->status_set_warning();
return;
}
}
this->status_clear_warning();
this->update_ = false;
}
PCA9685Channel *PCA9685Output::create_channel(uint8_t channel) {
this->min_channel_ = std::min(this->min_channel_, channel);
this->max_channel_ = std::max(this->max_channel_, channel);
auto *c = new PCA9685Channel(this, channel);
return c;
}
void PCA9685Channel::write_state(float state) {
const uint16_t max_duty = 4096;
const float duty_rounded = roundf(state * max_duty);
auto duty = static_cast<uint16_t>(duty_rounded);
this->parent_->set_channel_value_(this->channel_, duty);
}
} // namespace pca9685
} // namespace esphome
@@ -0,0 +1,68 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/output/float_output.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace pca9685 {
/// Inverts polarity of channel output signal
extern const uint8_t PCA9685_MODE_INVERTED;
/// Channel update happens upon ACK (post-set) rather than on STOP (endTransmission)
extern const uint8_t PCA9685_MODE_OUTPUT_ONACK;
/// Use a totem-pole (push-pull) style output rather than an open-drain structure.
extern const uint8_t PCA9685_MODE_OUTPUT_TOTEM_POLE;
/// For active low output enable, sets channel output to high-impedance state
extern const uint8_t PCA9685_MODE_OUTNE_HIGHZ;
/// Similarly, sets channel output to high if in totem-pole mode, otherwise
extern const uint8_t PCA9685_MODE_OUTNE_LOW;
class PCA9685Output;
class PCA9685Channel : public output::FloatOutput {
public:
PCA9685Channel(PCA9685Output *parent, uint8_t channel) : parent_(parent), channel_(channel) {}
protected:
void write_state(float state) override;
PCA9685Output *parent_;
uint8_t channel_;
};
/// PCA9685 float output component.
class PCA9685Output : public Component, public i2c::I2CDevice {
public:
PCA9685Output(float frequency, uint8_t mode = PCA9685_MODE_OUTPUT_ONACK | PCA9685_MODE_OUTPUT_TOTEM_POLE)
: frequency_(frequency), mode_(mode) {}
PCA9685Channel *create_channel(uint8_t channel);
void setup() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::HARDWARE; }
void loop() override;
protected:
friend PCA9685Channel;
void set_channel_value_(uint8_t channel, uint16_t value) {
if (this->pwm_amounts_[channel] != value)
this->update_ = true;
this->pwm_amounts_[channel] = value;
}
float frequency_;
uint8_t mode_;
uint8_t min_channel_{0xFF};
uint8_t max_channel_{0x00};
uint16_t pwm_amounts_[16] = {
0,
};
bool update_{true};
};
} // namespace pca9685
} // namespace esphome