🏗 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
+3
View File
@@ -0,0 +1,3 @@
import esphome.codegen as cg
nextion_ns = cg.esphome_ns.namespace('nextion')
@@ -0,0 +1,28 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import CONF_COMPONENT_ID, CONF_NAME, CONF_PAGE_ID, CONF_ID
from . import nextion_ns
from .display import Nextion
DEPENDENCIES = ['display']
CONF_NEXTION_ID = 'nextion_id'
NextionTouchComponent = nextion_ns.class_('NextionTouchComponent', binary_sensor.BinarySensor)
CONFIG_SCHEMA = cv.nameable(binary_sensor.BINARY_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(NextionTouchComponent),
cv.GenerateID(CONF_NEXTION_ID): cv.use_variable_id(Nextion),
cv.Required(CONF_PAGE_ID): cv.uint8_t,
cv.Required(CONF_COMPONENT_ID): cv.uint8_t,
}))
def to_code(config):
hub = yield cg.get_variable(config[CONF_NEXTION_ID])
rhs = hub.make_touch_component(config[CONF_NAME], config[CONF_PAGE_ID],
config[CONF_COMPONENT_ID])
var = cg.Pvariable(config[CONF_ID], rhs)
yield binary_sensor.register_binary_sensor(var, config)
+29
View File
@@ -0,0 +1,29 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import display, uart
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_UPDATE_INTERVAL
from . import nextion_ns
DEPENDENCIES = ['uart']
AUTO_LOAD = ['binary_sensor']
Nextion = nextion_ns.class_('Nextion', cg.PollingComponent, uart.UARTDevice)
NextionRef = Nextion.operator('ref')
CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({
cv.GenerateID(): cv.declare_variable_id(Nextion),
cv.Optional(CONF_UPDATE_INTERVAL, default='5s'): cv.update_interval,
}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield uart.register_uart_device(var, config)
if CONF_LAMBDA in config:
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(NextionRef, 'it')],
return_type=cg.void)
cg.add(var.set_writer(lambda_))
yield display.register_display(var, config)
+292
View File
@@ -0,0 +1,292 @@
#include "nextion.h"
#include "esphome/core/log.h"
namespace esphome {
namespace nextion {
static const char *TAG = "nextion";
void Nextion::setup() {
this->send_command_no_ack("");
this->send_command_printf("bkcmd=3");
this->goto_page("0");
}
float Nextion::get_setup_priority() const { return setup_priority::PROCESSOR; }
void Nextion::update() {
if (this->writer_.has_value()) {
(*this->writer_)(*this);
}
}
void Nextion::send_command_no_ack(const char *command) {
// Flush RX...
this->loop();
this->write_str(command);
const uint8_t data[3] = {0xFF, 0xFF, 0xFF};
this->write_array(data, sizeof(data));
}
bool Nextion::ack_() {
if (!this->wait_for_ack_)
return true;
uint32_t start = millis();
while (!this->read_until_ack_()) {
if (millis() - start > 100) {
ESP_LOGW(TAG, "Waiting for ACK timed out!");
return false;
}
}
return true;
}
void Nextion::set_component_text(const char *component, const char *text) {
this->send_command_printf("%s.txt=\"%s\"", component, text);
}
void Nextion::set_component_value(const char *component, int value) {
this->send_command_printf("%s.val=%d", component, value);
}
void Nextion::display_picture(int picture_id, int x_start, int y_start) {
this->send_command_printf("pic %d %d %d", picture_id, x_start, y_start);
}
void Nextion::set_component_background_color(const char *component, const char *color) {
this->send_command_printf("%s.bco=\"%s\"", component, color);
}
void Nextion::set_component_pressed_background_color(const char *component, const char *color) {
this->send_command_printf("%s.bco2=\"%s\"", component, color);
}
void Nextion::set_component_font_color(const char *component, const char *color) {
this->send_command_printf("%s.pco=\"%s\"", component, color);
}
void Nextion::set_component_pressed_font_color(const char *component, const char *color) {
this->send_command_printf("%s.pco2=\"%s\"", component, color);
}
void Nextion::set_component_coordinates(const char *component, int x, int y) {
this->send_command_printf("%s.xcen=%d", component, x);
this->send_command_printf("%s.ycen=%d", component, y);
}
void Nextion::set_component_font(const char *component, uint8_t font_id) {
this->send_command_printf("%s.font=%d", component, font_id);
}
void Nextion::goto_page(const char *page) { this->send_command_printf("page %s", page); }
bool Nextion::send_command_printf(const char *format, ...) {
char buffer[256];
va_list arg;
va_start(arg, format);
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
va_end(arg);
if (ret <= 0) {
ESP_LOGW(TAG, "Building command for format '%s' failed!", format);
return false;
}
this->send_command_no_ack(buffer);
if (!this->ack_()) {
ESP_LOGW(TAG, "Sending command '%s' failed because no ACK was received", buffer);
return false;
}
return true;
}
void Nextion::hide_component(const char *component) { this->send_command_printf("vis %s,0", component); }
void Nextion::show_component(const char *component) { this->send_command_printf("vis %s,1", component); }
void Nextion::enable_component_touch(const char *component) { this->send_command_printf("tsw %s,1", component); }
void Nextion::disable_component_touch(const char *component) { this->send_command_printf("tsw %s,0", component); }
void Nextion::add_waveform_data(int component_id, uint8_t channel_number, uint8_t value) {
this->send_command_printf("add %d,%u,%u", component_id, channel_number, value);
}
void Nextion::fill_area(int x1, int y1, int width, int height, const char *color) {
this->send_command_printf("fill %d,%d,%d,%d,%s", x1, y1, width, height, color);
}
void Nextion::line(int x1, int y1, int x2, int y2, const char *color) {
this->send_command_printf("line %d,%d,%d,%d,%s", x1, y1, x2, y2, color);
}
void Nextion::rectangle(int x1, int y1, int width, int height, const char *color) {
this->send_command_printf("draw %d,%d,%d,%d,%s", x1, y1, x1 + width, y1 + height, color);
}
void Nextion::circle(int center_x, int center_y, int radius, const char *color) {
this->send_command_printf("cir %d,%d,%d,%s", center_x, center_y, radius, color);
}
void Nextion::filled_circle(int center_x, int center_y, int radius, const char *color) {
this->send_command_printf("cirs %d,%d,%d,%s", center_x, center_y, radius, color);
}
bool Nextion::read_until_ack_() {
while (this->available() >= 4) {
// flush preceding filler bytes
uint8_t temp;
while (this->available() && this->peek_byte(&temp) && temp == 0xFF)
this->read_byte(&temp);
if (!this->available())
break;
uint8_t event;
// event type
this->read_byte(&event);
uint8_t data[255];
// total length of data (including end bytes)
uint8_t data_length = 0;
// message is terminated by three consecutive 0xFF
// this variable keeps track of ohow many of those have
// been received
uint8_t end_length = 0;
while (this->available() && end_length < 3 && data_length < sizeof(data)) {
uint8_t byte;
this->read_byte(&byte);
if (byte == 0xFF) {
end_length++;
} else {
end_length = 0;
}
data[data_length++] = byte;
}
if (end_length != 3) {
ESP_LOGW(TAG, "Received unknown filler end bytes from Nextion!");
continue;
}
data_length -= 3; // remove filler bytes
bool invalid_data_length = false;
switch (event) {
case 0x01: // successful execution of instruction (ACK)
return true;
case 0x00: // invalid instruction
ESP_LOGW(TAG, "Nextion reported invalid instruction!");
break;
case 0x02: // component ID invalid
ESP_LOGW(TAG, "Nextion reported component ID invalid!");
break;
case 0x03: // page ID invalid
ESP_LOGW(TAG, "Nextion reported page ID invalid!");
break;
case 0x04: // picture ID invalid
ESP_LOGW(TAG, "Nextion reported picture ID invalid!");
break;
case 0x05: // font ID invalid
ESP_LOGW(TAG, "Nextion reported font ID invalid!");
break;
case 0x11: // baud rate setting invalid
ESP_LOGW(TAG, "Nextion reported baud rate invalid!");
break;
case 0x12: // curve control ID number or channel number is invalid
ESP_LOGW(TAG, "Nextion reported control/channel ID invalid!");
break;
case 0x1A: // variable name invalid
ESP_LOGW(TAG, "Nextion reported variable name invalid!");
break;
case 0x1B: // variable operation invalid
ESP_LOGW(TAG, "Nextion reported variable operation invalid!");
break;
case 0x1C: // failed to assign
ESP_LOGW(TAG, "Nextion reported failed to assign variable!");
break;
case 0x1D: // operate EEPROM failed
ESP_LOGW(TAG, "Nextion reported operating EEPROM failed!");
break;
case 0x1E: // parameter quantity invalid
ESP_LOGW(TAG, "Nextion reported parameter quantity invalid!");
break;
case 0x1F: // IO operation failed
ESP_LOGW(TAG, "Nextion reported component I/O operation invalid!");
break;
case 0x20: // undefined escape characters
ESP_LOGW(TAG, "Nextion reported undefined escape characters!");
break;
case 0x23: // too long variable name
ESP_LOGW(TAG, "Nextion reported too long variable name!");
break;
case 0x65: { // touch event return data
if (data_length != 3) {
invalid_data_length = true;
break;
}
uint8_t page_id = data[0];
uint8_t component_id = data[1];
uint8_t touch_event = data[2]; // 0 -> release, 1 -> press
ESP_LOGD(TAG, "Got touch page=%u component=%u type=%s", page_id, component_id,
touch_event ? "PRESS" : "RELEASE");
for (auto *touch : this->touch_) {
touch->process(page_id, component_id, touch_event);
}
break;
}
case 0x67:
case 0x68: { // touch coordinate data
if (data_length != 5) {
invalid_data_length = true;
break;
}
uint16_t x = (uint16_t(data[0]) << 8) | data[1];
uint16_t y = (uint16_t(data[2]) << 8) | data[3];
uint8_t touch_event = data[4]; // 0 -> release, 1 -> press
ESP_LOGD(TAG, "Got touch at x=%u y=%u type=%s", x, y, touch_event ? "PRESS" : "RELEASE");
break;
}
case 0x66: // sendme page id
case 0x70: // string variable data return
case 0x71: // numeric variable data return
case 0x86: // device automatically enters into sleep mode
case 0x87: // device automatically wakes up
case 0x88: // system successful start up
case 0x89: // start SD card upgrade
case 0xFD: // data transparent transmit finished
case 0xFE: // data transparent transmit ready
break;
default:
ESP_LOGW(TAG, "Received unknown event from nextion: 0x%02X", event);
break;
}
if (invalid_data_length) {
ESP_LOGW(TAG, "Invalid data length from nextion!");
}
}
return false;
}
void Nextion::loop() {
while (this->available() >= 4) {
this->read_until_ack_();
}
}
#ifdef USE_TIME
void Nextion::set_nextion_rtc_time(time::ESPTime time) {
this->send_command_printf("rtc0=%u", time.year);
this->send_command_printf("rtc1=%u", time.month);
this->send_command_printf("rtc2=%u", time.day_of_month);
this->send_command_printf("rtc3=%u", time.hour);
this->send_command_printf("rtc4=%u", time.minute);
this->send_command_printf("rtc5=%u", time.second);
}
#endif
void Nextion::set_backlight_brightness(uint8_t brightness) { this->send_command_printf("dim=%u", brightness); }
void Nextion::set_touch_sleep_timeout(uint16_t timeout) { this->send_command_printf("thsp=%u", timeout); }
NextionTouchComponent *Nextion::make_touch_component(const std::string &name, uint8_t page_id, uint8_t component_id) {
auto *ret = new NextionTouchComponent(name, page_id, component_id);
this->touch_.push_back(ret);
return ret;
}
void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; }
void Nextion::set_component_text_printf(const char *component, const char *format, ...) {
va_list arg;
va_start(arg, format);
char buffer[256];
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
va_end(arg);
if (ret > 0)
this->set_component_text(component, buffer);
}
void Nextion::set_wait_for_ack(bool wait_for_ack) { this->wait_for_ack_ = wait_for_ack; }
void NextionTouchComponent::process(uint8_t page_id, uint8_t component_id, bool on) {
if (this->page_id_ == page_id && this->component_id_ == component_id) {
this->publish_state(on);
}
}
NextionTouchComponent::NextionTouchComponent(const std::string &name, uint8_t page_id, uint8_t component_id)
: BinarySensor(name), page_id_(page_id), component_id_(component_id) {}
} // namespace nextion
} // namespace esphome
+234
View File
@@ -0,0 +1,234 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/components/uart/uart.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#ifdef USE_TIME
#include "esphome/components/time/real_time_clock.h"
#endif
namespace esphome {
namespace nextion {
class NextionTouchComponent;
class Nextion;
using nextion_writer_t = std::function<void(Nextion &)>;
class Nextion : public PollingComponent, public uart::UARTDevice {
public:
Nextion() : PollingComponent(0) {}
/**
* Set the text of a component to a static string.
* @param component The component name.
* @param text The static text to set.
*/
void set_component_text(const char *component, const char *text);
/**
* Set the text of a component to a formatted string
* @param component The component name.
* @param format The printf-style format string.
* @param ... The arguments to the format.
*/
void set_component_text_printf(const char *component, const char *format, ...) __attribute__((format(printf, 3, 4)));
/**
* Set the integer value of a component
* @param component The component name.
* @param value The value to set.
*/
void set_component_value(const char *component, int value);
/**
* Set the picture of an image component.
* @param component The component name.
* @param value The picture name.
*/
void set_component_picture(const char *component, const char *picture) {
this->send_command_printf("%s.val=%s", component, picture);
}
/**
* Set the background color of a component.
* @param component The component name.
* @param color The color (as a string).
*/
void set_component_background_color(const char *component, const char *color);
/**
* Set the pressed background color of a component.
* @param component The component name.
* @param color The color (as a string).
*/
void set_component_pressed_background_color(const char *component, const char *color);
/**
* Set the font color of a component.
* @param component The component name.
* @param color The color (as a string).
*/
void set_component_font_color(const char *component, const char *color);
/**
* Set the pressed font color of a component.
* @param component The component name.
* @param color The color (as a string).
*/
void set_component_pressed_font_color(const char *component, const char *color);
/**
* Set the coordinates of a component on screen.
* @param component The component name.
* @param x The x coordinate.
* @param y The y coordinate.
*/
void set_component_coordinates(const char *component, int x, int y);
/**
* Set the font id for a component.
* @param component The component name.
* @param font_id The ID of the font (number).
*/
void set_component_font(const char *component, uint8_t font_id);
#ifdef USE_TIME
/**
* Send the current time to the nextion display.
* @param time The time instance to send (get this with id(my_time).now() ).
*/
void set_nextion_rtc_time(time::ESPTime time);
#endif
/**
* Show the page with a given name.
* @param page The name of the page.
*/
void goto_page(const char *page);
/**
* Hide a component.
* @param component The component name.
*/
void hide_component(const char *component);
/**
* Show a component.
* @param component The component name.
*/
void show_component(const char *component);
/**
* Enable touch for a component.
* @param component The component name.
*/
void enable_component_touch(const char *component);
/**
* Disable touch for a component.
* @param component The component name.
*/
void disable_component_touch(const char *component);
/**
* Add waveform data to a waveform component
* @param component_id The integer component id.
* @param channel_number The channel number to write to.
* @param value The value to write.
*/
void add_waveform_data(int component_id, uint8_t channel_number, uint8_t value);
/**
* Display a picture at coordinates.
* @param picture_id The picture id.
* @param x1 The x coordinate.
* @param y1 The y coordniate.
*/
void display_picture(int picture_id, int x_start, int y_start);
/**
* Fill a rectangle with a color.
* @param x1 The starting x coordinate.
* @param y1 The starting y coordinate.
* @param width The width to draw.
* @param height The height to draw.
* @param color The color to draw with (as a string).
*/
void fill_area(int x1, int y1, int width, int height, const char *color);
/**
* Draw a line on the screen.
* @param x1 The starting x coordinate.
* @param y1 The starting y coordinate.
* @param x2 The ending x coordinate.
* @param y2 The ending y coordinate.
* @param color The color to draw with (as a string).
*/
void line(int x1, int y1, int x2, int y2, const char *color);
/**
* Draw a rectangle outline.
* @param x1 The starting x coordinate.
* @param y1 The starting y coordinate.
* @param width The width of the rectangle.
* @param height The height of the rectangle.
* @param color The color to draw with (as a string).
*/
void rectangle(int x1, int y1, int width, int height, const char *color);
/**
* Draw a circle outline
* @param center_x The center x coordinate.
* @param center_y The center y coordinate.
* @param radius The circle radius.
* @param color The color to draw with (as a string).
*/
void circle(int center_x, int center_y, int radius, const char *color);
/**
* Draw a filled circled.
* @param center_x The center x coordinate.
* @param center_y The center y coordinate.
* @param radius The circle radius.
* @param color The color to draw with (as a string).
*/
void filled_circle(int center_x, int center_y, int radius, const char *color);
/** Set the brightness of the backlight.
*
* @param brightness The brightness, from 0 to 100.
*/
void set_backlight_brightness(uint8_t brightness);
/**
* Set the touch sleep timeout of the display.
* @param timeout Timeout in seconds.
*/
void set_touch_sleep_timeout(uint16_t timeout);
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
NextionTouchComponent *make_touch_component(const std::string &name, uint8_t page_id, uint8_t component_id);
void setup() override;
float get_setup_priority() const override;
void update() override;
void loop() override;
void set_writer(const nextion_writer_t &writer);
/**
* Manually send a raw command to the display and don't wait for an acknowledgement packet.
* @param command The command to write, for example "vis b0,0".
*/
void send_command_no_ack(const char *command);
/**
* Manually send a raw formatted command to the display.
* @param format The printf-style command format, like "vis %s,0"
* @param ... The format arguments
* @return Whether the send was successful.
*/
bool send_command_printf(const char *format, ...) __attribute__((format(printf, 2, 3)));
void set_wait_for_ack(bool wait_for_ack);
protected:
bool ack_();
bool read_until_ack_();
std::vector<NextionTouchComponent *> touch_;
optional<nextion_writer_t> writer_;
bool wait_for_ack_{true};
};
class NextionTouchComponent : public binary_sensor::BinarySensor {
public:
NextionTouchComponent(const std::string &name, uint8_t page_id, uint8_t component_id);
void process(uint8_t page_id, uint8_t component_id, bool on);
protected:
uint8_t page_id_;
uint8_t component_id_;
};
} // namespace nextion
} // namespace esphome