diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index 06a0ed94df..c374568149 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -1,15 +1,27 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import pins -from esphome.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN, CONF_UART_ID +from esphome import pins, automation +from esphome.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN, CONF_UART_ID, CONF_DATA from esphome.core import CORE, coroutine +from esphome.py_compat import text_type, binary_type, char_to_byte uart_ns = cg.esphome_ns.namespace('uart') UARTComponent = uart_ns.class_('UARTComponent', cg.Component) UARTDevice = uart_ns.class_('UARTDevice') +UARTWriteAction = uart_ns.class_('UARTWriteAction', automation.Action) MULTI_CONF = True +def validate_raw_data(value): + if isinstance(value, text_type): + return value.encode('utf-8') + if isinstance(value, str): + return value + if isinstance(value, list): + return cv.Schema([cv.hex_uint8_t])(value) + raise cv.Invalid("data must either be a string wrapped in quotes or a list of bytes") + + def validate_rx_pin(value): value = pins.input_pin(value) if CORE.is_esp8266 and value >= 16: @@ -51,3 +63,22 @@ def register_uart_device(var, config): """ parent = yield cg.get_variable(config[CONF_UART_ID]) cg.add(var.set_uart_parent(parent)) + + +@automation.register_action('uart.write', UARTWriteAction, cv.maybe_simple_value({ + cv.GenerateID(): cv.use_id(UARTComponent), + cv.Required(CONF_DATA): cv.templatable(validate_raw_data), +}, key=CONF_DATA)) +def uart_write_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + yield cg.register_parented(var, config[CONF_ID]) + data = config[CONF_DATA] + if isinstance(data, binary_type): + data = [char_to_byte(x) for x in data] + + if cg.is_template(data): + templ = yield cg.templatable(data, args, cg.std_vector.template(cg.uint8)) + cg.add(var.set_data_template(templ)) + else: + cg.add(var.set_data_static(data)) + yield var diff --git a/esphome/components/uart/automation.h b/esphome/components/uart/automation.h new file mode 100644 index 0000000000..9686f94413 --- /dev/null +++ b/esphome/components/uart/automation.h @@ -0,0 +1,36 @@ +#pragma once + +#include "uart.h" +#include "esphome/core/automation.h" + +namespace esphome { +namespace uart { + +template class UARTWriteAction : public Action, public Parented { + public: + void set_data_template(std::function(Ts...)> func) { + this->data_func_ = func; + this->static_ = false; + } + void set_data_static(const std::vector &data) { + this->data_static_ = data; + this->static_ = true; + } + + void play(Ts... x) override { + if (this->static_) { + this->parent_->write_array(this->data_static_); + } else { + auto val = this->data_func_(x...); + this->parent_->write_array(val); + } + } + + protected: + bool static_{false}; + std::function(Ts...)> data_func_{}; + std::vector data_static_{}; +}; + +} // namespace uart +} // namespace esphome diff --git a/esphome/components/uart/switch/__init__.py b/esphome/components/uart/switch/__init__.py index dae63a2add..b6f622604f 100644 --- a/esphome/components/uart/switch/__init__.py +++ b/esphome/components/uart/switch/__init__.py @@ -3,27 +3,17 @@ import esphome.config_validation as cv from esphome.components import switch, uart from esphome.const import CONF_DATA, CONF_ID, CONF_INVERTED from esphome.core import HexInt -from esphome.py_compat import text_type, binary_type, char_to_byte -from .. import uart_ns +from esphome.py_compat import binary_type, char_to_byte +from .. import uart_ns, validate_raw_data DEPENDENCIES = ['uart'] UARTSwitch = uart_ns.class_('UARTSwitch', switch.Switch, uart.UARTDevice, cg.Component) -def validate_data(value): - if isinstance(value, text_type): - return value.encode('utf-8') - if isinstance(value, str): - return value - if isinstance(value, list): - return cv.Schema([cv.hex_uint8_t])(value) - raise cv.Invalid("data must either be a string wrapped in quotes or a list of bytes") - - CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(UARTSwitch), - cv.Required(CONF_DATA): validate_data, + cv.Required(CONF_DATA): validate_raw_data, cv.Optional(CONF_INVERTED): cv.invalid("UART switches do not support inverted mode!"), }).extend(uart.UART_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index 005536de4a..666b8e2fb2 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -49,6 +49,7 @@ class UARTComponent : public Component, public Stream { void write_byte(uint8_t data); void write_array(const uint8_t *data, size_t len); + void write_array(const std::vector &data) { this->write_array(&data[0], data.size()); } void write_str(const char *str); @@ -97,6 +98,7 @@ class UARTDevice : public Stream { void write_byte(uint8_t data) { this->parent_->write_byte(data); } void write_array(const uint8_t *data, size_t len) { this->parent_->write_array(data, len); } + void write_array(const std::vector &data) { this->parent_->write_array(data); } void write_str(const char *str) { this->parent_->write_str(str); } diff --git a/esphome/config_validation.py b/esphome/config_validation.py index cc49144d86..50d30c3ea6 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1210,13 +1210,14 @@ def validate_registry(name, registry): return ensure_list(validate_registry_entry(name, registry)) -def maybe_simple_value(*validators): +def maybe_simple_value(*validators, **kwargs): + key = kwargs.pop('key', CONF_VALUE) validator = All(*validators) def validate(value): - if isinstance(value, dict) and CONF_VALUE in value: + if isinstance(value, dict) and key in value: return validator(value) - return validator({CONF_VALUE: value}) + return validator({key: value}) return validate diff --git a/tests/test1.yaml b/tests/test1.yaml index 8ff29d8c5b..a9f49e789f 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -103,6 +103,12 @@ mqtt: - light.control: id: living_room_lights brightness: !lambda 'return id(living_room_lights).current_values.get_brightness() + 0.5;' + - uart.write: + id: uart0 + data: Hello World + - uart.write: [0x00, 0x20, 0x30] + - uart.write: !lambda |- + return {}; i2c: sda: 21 @@ -120,6 +126,7 @@ uart: tx_pin: GPIO22 rx_pin: GPIO23 baud_rate: 115200 + id: uart0 ota: safe_mode: True