diff --git a/esphome/components/rf_bridge/__init__.py b/esphome/components/rf_bridge/__init__.py new file mode 100644 index 0000000000..1fd4fbc7bd --- /dev/null +++ b/esphome/components/rf_bridge/__init__.py @@ -0,0 +1,75 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_CODE, CONF_LOW, CONF_SYNC, CONF_HIGH +from esphome.components import uart + +DEPENDENCIES = ['uart'] + +rf_bridge_ns = cg.esphome_ns.namespace('rf_bridge') +RFBridgeComponent = rf_bridge_ns.class_('RFBridgeComponent', cg.Component, uart.UARTDevice) + +RFBridgeData = rf_bridge_ns.struct('RFBridgeData') + +RFBridgeReceivedCodeTrigger = rf_bridge_ns.class_('RFBridgeReceivedCodeTrigger', + automation.Trigger.template(RFBridgeData)) + +RFBridgeSendCodeAction = rf_bridge_ns.class_('RFBridgeSendCodeAction', automation.Action) +RFBridgeLearnAction = rf_bridge_ns.class_('RFBridgeLearnAction', automation.Action) + + +CONF_ON_CODE_RECEIVED = 'on_code_received' + +CONFIG_SCHEMA = cv.All(cv.Schema({ + cv.GenerateID(): cv.declare_id(RFBridgeComponent), + cv.Optional(CONF_ON_CODE_RECEIVED): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RFBridgeReceivedCodeTrigger), + }), +}).extend(uart.UART_DEVICE_SCHEMA).extend(cv.COMPONENT_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) + + for conf in config.get(CONF_ON_CODE_RECEIVED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + yield automation.build_automation(trigger, [(RFBridgeData, 'data')], conf) + + +RFBRIDGE_SEND_CODE_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.use_id(RFBridgeComponent), + cv.Required(CONF_SYNC): cv.templatable(cv.hex_uint16_t), + cv.Required(CONF_LOW): cv.templatable(cv.hex_uint16_t), + cv.Required(CONF_HIGH): cv.templatable(cv.hex_uint16_t), + cv.Required(CONF_CODE): cv.templatable(cv.hex_uint32_t) +}) + + +@automation.register_action('rf_bridge.send_code', RFBridgeSendCodeAction, + RFBRIDGE_SEND_CODE_SCHEMA) +def rf_bridge_send_code_to_code(config, action_id, template_args, args): + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_args, paren) + template_ = yield cg.templatable(config[CONF_SYNC], args, cg.uint16) + cg.add(var.set_sync(template_)) + template_ = yield cg.templatable(config[CONF_LOW], args, cg.uint16) + cg.add(var.set_low(template_)) + template_ = yield cg.templatable(config[CONF_HIGH], args, cg.uint16) + cg.add(var.set_high(template_)) + template_ = yield cg.templatable(config[CONF_CODE], args, cg.uint32) + cg.add(var.set_code(template_)) + yield var + + +RFBRIDGE_LEARN_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.use_id(RFBridgeComponent) +}) + + +@automation.register_action('rf_bridge.learn', RFBridgeLearnAction, RFBRIDGE_LEARN_SCHEMA) +def rf_bridge_learnx_to_code(config, action_id, template_args, args): + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_args, paren) + yield var diff --git a/esphome/components/rf_bridge/rf_bridge.cpp b/esphome/components/rf_bridge/rf_bridge.cpp new file mode 100644 index 0000000000..f1537cdc87 --- /dev/null +++ b/esphome/components/rf_bridge/rf_bridge.cpp @@ -0,0 +1,100 @@ +#include "rf_bridge.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace rf_bridge { + +static const char *TAG = "rf_bridge"; + +void RFBridgeComponent::ack_() { + ESP_LOGV(TAG, "Sending ACK"); + this->write(RF_CODE_START); + this->write(RF_CODE_ACK); + this->write(RF_CODE_STOP); + this->flush(); +} + +void RFBridgeComponent::decode_() { + uint8_t action = uartbuf_[0]; + RFBridgeData data{}; + + switch (action) { + case RF_CODE_ACK: + ESP_LOGD(TAG, "Action OK"); + break; + case RF_CODE_LEARN_KO: + this->ack_(); + ESP_LOGD(TAG, "Learn timeout"); + break; + case RF_CODE_LEARN_OK: + ESP_LOGD(TAG, "Learn started"); + case RF_CODE_RFIN: + this->ack_(); + + data.sync = (uartbuf_[1] << 8) | uartbuf_[2]; + data.low = (uartbuf_[3] << 8) | uartbuf_[4]; + data.high = (uartbuf_[5] << 8) | uartbuf_[6]; + data.code = (uartbuf_[7] << 16) | (uartbuf_[8] << 8) | uartbuf_[9]; + + ESP_LOGD(TAG, "Received RFBridge Code: sync=0x%04X low=0x%04X high=0x%04X code=0x%06X", data.sync, data.low, + data.high, data.code); + this->callback_.call(data); + break; + default: + ESP_LOGD(TAG, "Unknown action: 0x%02X", action); + break; + } + this->last_ = millis(); +} + +void RFBridgeComponent::loop() { + bool receiving = false; + if (this->last_ != 0 && millis() - this->last_ > RF_DEBOUNCE) { + this->last_ = 0; + } + + while (this->available()) { + uint8_t c = this->read(); + if (receiving) { + if (c == RF_CODE_STOP && (this->uartpos_ == 1 || this->uartpos_ == RF_MESSAGE_SIZE + 1)) { + this->decode_(); + receiving = false; + } else if (this->uartpos_ <= RF_MESSAGE_SIZE) { + this->uartbuf_[uartpos_++] = c; + } else { + receiving = false; + } + } else if (c == RF_CODE_START) { + this->uartpos_ = 0; + receiving = true; + } + } +} + +void RFBridgeComponent::send_code(RFBridgeData data) { + ESP_LOGD(TAG, "Sending code: sync=0x%04X low=0x%04X high=0x%04X code=0x%06X", data.sync, data.low, data.high, + data.code); + this->write(RF_CODE_START); + this->write(RF_CODE_RFOUT); + this->write(data.sync); + this->write(data.low); + this->write(data.high); + this->write(data.code); + this->write(RF_CODE_STOP); +} + +void RFBridgeComponent::learn() { + ESP_LOGD(TAG, "Learning mode"); + this->write(RF_CODE_START); + this->write(RF_CODE_LEARN); + this->write(RF_CODE_STOP); +} + +void RFBridgeComponent::dump_config() { + ESP_LOGCONFIG(TAG, "RF_Bridge:"); + this->check_uart_settings(19200); +} + +} // namespace rf_bridge +} // namespace esphome diff --git a/esphome/components/rf_bridge/rf_bridge.h b/esphome/components/rf_bridge/rf_bridge.h new file mode 100644 index 0000000000..86713b8a5c --- /dev/null +++ b/esphome/components/rf_bridge/rf_bridge.h @@ -0,0 +1,95 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/uart/uart.h" +#include "esphome/core/automation.h" + +namespace esphome { +namespace rf_bridge { + +static const uint8_t RF_MESSAGE_SIZE = 9; +static const uint8_t RF_CODE_START = 0xAA; +static const uint8_t RF_CODE_ACK = 0xA0; +static const uint8_t RF_CODE_LEARN = 0xA1; +static const uint8_t RF_CODE_LEARN_KO = 0xA2; +static const uint8_t RF_CODE_LEARN_OK = 0xA3; +static const uint8_t RF_CODE_RFIN = 0xA4; +static const uint8_t RF_CODE_RFOUT = 0xA5; +static const uint8_t RF_CODE_SNIFFING_ON = 0xA6; +static const uint8_t RF_CODE_SNIFFING_OFF = 0xA7; +static const uint8_t RF_CODE_RFOUT_NEW = 0xA8; +static const uint8_t RF_CODE_LEARN_NEW = 0xA9; +static const uint8_t RF_CODE_LEARN_KO_NEW = 0xAA; +static const uint8_t RF_CODE_LEARN_OK_NEW = 0xAB; +static const uint8_t RF_CODE_RFOUT_BUCKET = 0xB0; +static const uint8_t RF_CODE_STOP = 0x55; +static const uint8_t RF_DEBOUNCE = 200; + +struct RFBridgeData { + uint16_t sync; + uint16_t low; + uint16_t high; + uint32_t code; +}; + +class RFBridgeComponent : public uart::UARTDevice, public Component { + public: + void loop() override; + void dump_config() override; + void add_on_code_received_callback(std::function callback) { + this->callback_.add(std::move(callback)); + } + void send_code(RFBridgeData data); + void learn(); + + protected: + void ack_(); + void decode_(); + + unsigned long last_ = 0; + unsigned char uartbuf_[RF_MESSAGE_SIZE + 3] = {0}; + unsigned char uartpos_ = 0; + + CallbackManager callback_; +}; + +class RFBridgeReceivedCodeTrigger : public Trigger { + public: + explicit RFBridgeReceivedCodeTrigger(RFBridgeComponent *parent) { + parent->add_on_code_received_callback([this](RFBridgeData data) { this->trigger(data); }); + } +}; + +template class RFBridgeSendCodeAction : public Action { + public: + RFBridgeSendCodeAction(RFBridgeComponent *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(uint16_t, sync) + TEMPLATABLE_VALUE(uint16_t, low) + TEMPLATABLE_VALUE(uint16_t, high) + TEMPLATABLE_VALUE(uint32_t, code) + + void play(Ts... x) { + RFBridgeData data{}; + data.sync = this->sync_.value(x...); + data.low = this->low_.value(x...); + data.high = this->high_.value(x...); + data.code = this->code_.value(x...); + this->parent_->send_code(data); + } + + protected: + RFBridgeComponent *parent_; +}; + +template class RFBridgeLearnAction : public Action { + public: + RFBridgeLearnAction(RFBridgeComponent *parent) : parent_(parent) {} + + void play(Ts... x) { this->parent_->learn(); } + + protected: + RFBridgeComponent *parent_; +}; + +} // namespace rf_bridge +} // namespace esphome diff --git a/tests/test3.yaml b/tests/test3.yaml index 2703621752..77a0fa631c 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -623,7 +623,7 @@ output: mcp23017: id: mcp23017_hub - + mcp23008: id: mcp23008_hub @@ -666,8 +666,21 @@ dfplayer: dfplayer.is_playing then: logger.log: 'Playback finished event' - tm1651: id: tm1651_battery clk_pin: D6 dio_pin: D5 +rf_bridge: + on_code_received: + - lambda: |- + uint32_t test; + test = data.sync; + test = data.low; + test = data.high; + test = data.code; + - rf_bridge.send_code: + sync: 0x1234 + low: 0x1234 + high: 0x1234 + code: 0x123456 + - rf_bridge.learn