mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 05:24:53 +01:00
Add RFBridge component (#896)
* Add RFBridge component * Fix format issues * Rename methods * More formatting * Fix line length * Apply suggestions from code review Co-Authored-By: Otto Winter <otto@otto-winter.com> * Check uart settings on dump * Make receiving local to the loop * FIx code order and schema * Add rf_bridge to test file * Apply suggestions from code review Co-Authored-By: Otto Winter <otto@otto-winter.com>
This commit is contained in:
parent
ae784dc74c
commit
f5b7cc81d8
4 changed files with 285 additions and 2 deletions
75
esphome/components/rf_bridge/__init__.py
Normal file
75
esphome/components/rf_bridge/__init__.py
Normal file
|
@ -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
|
100
esphome/components/rf_bridge/rf_bridge.cpp
Normal file
100
esphome/components/rf_bridge/rf_bridge.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
#include "rf_bridge.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cstring>
|
||||
|
||||
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
|
95
esphome/components/rf_bridge/rf_bridge.h
Normal file
95
esphome/components/rf_bridge/rf_bridge.h
Normal file
|
@ -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<void(RFBridgeData)> 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<void(RFBridgeData)> callback_;
|
||||
};
|
||||
|
||||
class RFBridgeReceivedCodeTrigger : public Trigger<RFBridgeData> {
|
||||
public:
|
||||
explicit RFBridgeReceivedCodeTrigger(RFBridgeComponent *parent) {
|
||||
parent->add_on_code_received_callback([this](RFBridgeData data) { this->trigger(data); });
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class RFBridgeSendCodeAction : public Action<Ts...> {
|
||||
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<typename... Ts> class RFBridgeLearnAction : public Action<Ts...> {
|
||||
public:
|
||||
RFBridgeLearnAction(RFBridgeComponent *parent) : parent_(parent) {}
|
||||
|
||||
void play(Ts... x) { this->parent_->learn(); }
|
||||
|
||||
protected:
|
||||
RFBridgeComponent *parent_;
|
||||
};
|
||||
|
||||
} // namespace rf_bridge
|
||||
} // namespace esphome
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue