Fit this lamp: 4 alarms, full data model, knock snooze, server use
Decode DP127 as 48 bytes (4 alarms), plus DP128, DP107 and DP126, from Tuya's data model. Add day switches, alarm and sunset controls, a knock sensor on PA18, display schedule, bedtime fade, radio timer and grouped web UI. Pin LibreTiny v1.14.0 for the OTA restart fix. Ship the patched UART as an external component so a server needs only a short YAML. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3e3358af08
commit
a8f6bb3ca5
36 files changed
+5132
-214
No files matched your search
@@ -0,0 +1,297 @@
|
||||
#ifdef USE_HOST
|
||||
#include "uart_component_host.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#if !(defined(__linux__) || defined(__APPLE__))
|
||||
#error This HostUartComponent implementation is not supported on this host OS
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#ifdef USE_LOGGER
|
||||
#include "esphome/components/logger/logger.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
speed_t get_baud(int baud) {
|
||||
#ifdef __APPLE__
|
||||
return baud;
|
||||
#else
|
||||
switch (baud) {
|
||||
case 50:
|
||||
return B50;
|
||||
case 75:
|
||||
return B75;
|
||||
case 110:
|
||||
return B110;
|
||||
case 134:
|
||||
return B134;
|
||||
case 150:
|
||||
return B150;
|
||||
case 200:
|
||||
return B200;
|
||||
case 300:
|
||||
return B300;
|
||||
case 600:
|
||||
return B600;
|
||||
case 1200:
|
||||
return B1200;
|
||||
case 1800:
|
||||
return B1800;
|
||||
case 2400:
|
||||
return B2400;
|
||||
case 4800:
|
||||
return B4800;
|
||||
case 9600:
|
||||
return B9600;
|
||||
case 19200:
|
||||
return B19200;
|
||||
case 38400:
|
||||
return B38400;
|
||||
case 57600:
|
||||
return B57600;
|
||||
case 115200:
|
||||
return B115200;
|
||||
case 230400:
|
||||
return B230400;
|
||||
case 460800:
|
||||
return B460800;
|
||||
case 500000:
|
||||
return B500000;
|
||||
case 576000:
|
||||
return B576000;
|
||||
case 921600:
|
||||
return B921600;
|
||||
case 1000000:
|
||||
return B1000000;
|
||||
case 1152000:
|
||||
return B1152000;
|
||||
case 1500000:
|
||||
return B1500000;
|
||||
case 2000000:
|
||||
return B2000000;
|
||||
case 2500000:
|
||||
return B2500000;
|
||||
case 3000000:
|
||||
return B3000000;
|
||||
case 3500000:
|
||||
return B3500000;
|
||||
case 4000000:
|
||||
return B4000000;
|
||||
default:
|
||||
return B0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart";
|
||||
|
||||
HostUartComponent::~HostUartComponent() {
|
||||
if (this->file_descriptor_ != -1) {
|
||||
close(this->file_descriptor_);
|
||||
this->file_descriptor_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void HostUartComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Opening UART port");
|
||||
speed_t baud = get_baud(this->baud_rate_);
|
||||
if (baud == B0) {
|
||||
ESP_LOGE(TAG, "Unsupported baud rate: %d", this->baud_rate_);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->file_descriptor_ = ::open(this->port_name_.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
|
||||
if (this->file_descriptor_ == -1) {
|
||||
this->update_error_(strerror(errno));
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
fcntl(this->file_descriptor_, F_SETFL, 0);
|
||||
struct termios options;
|
||||
tcgetattr(this->file_descriptor_, &options);
|
||||
cfmakeraw(&options);
|
||||
options.c_cflag &= ~CRTSCTS;
|
||||
options.c_cflag |= CREAD | CLOCAL;
|
||||
options.c_iflag &= ~(IXOFF | IXANY);
|
||||
// Set data bits
|
||||
options.c_cflag &= ~CSIZE; // Mask the character size bits
|
||||
switch (this->data_bits_) {
|
||||
case 5:
|
||||
options.c_cflag |= CS5;
|
||||
break;
|
||||
case 6:
|
||||
options.c_cflag |= CS6;
|
||||
break;
|
||||
case 7:
|
||||
options.c_cflag |= CS7;
|
||||
break;
|
||||
case 8:
|
||||
default:
|
||||
options.c_cflag |= CS8;
|
||||
break;
|
||||
}
|
||||
// Set parity
|
||||
switch (this->parity_) {
|
||||
case UART_CONFIG_PARITY_NONE:
|
||||
options.c_cflag &= ~PARENB;
|
||||
break;
|
||||
case UART_CONFIG_PARITY_EVEN:
|
||||
options.c_cflag |= PARENB;
|
||||
options.c_cflag &= ~PARODD;
|
||||
break;
|
||||
case UART_CONFIG_PARITY_ODD:
|
||||
options.c_cflag |= PARENB;
|
||||
options.c_cflag |= PARODD;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
// Set stop bits
|
||||
if (this->stop_bits_ == 2) {
|
||||
options.c_cflag |= CSTOPB;
|
||||
} else {
|
||||
options.c_cflag &= ~CSTOPB;
|
||||
}
|
||||
cfsetispeed(&options, baud);
|
||||
cfsetospeed(&options, baud);
|
||||
tcsetattr(this->file_descriptor_, TCSANOW, &options);
|
||||
}
|
||||
|
||||
void HostUartComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "UART:");
|
||||
ESP_LOGCONFIG(TAG, " Port: %s", this->port_name_.c_str());
|
||||
if (this->file_descriptor_ == -1) {
|
||||
ESP_LOGCONFIG(TAG, " Port status: Not opened");
|
||||
if (!this->first_error_.empty()) {
|
||||
ESP_LOGCONFIG(TAG, " Error: %s", this->first_error_.c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Port status: opened\n"
|
||||
" Baud Rate: %d\n"
|
||||
" Data Bits: %d\n"
|
||||
" Parity: %s\n"
|
||||
" Stop Bits: %d",
|
||||
this->baud_rate_, this->data_bits_,
|
||||
this->parity_ == UART_CONFIG_PARITY_NONE ? "None"
|
||||
: this->parity_ == UART_CONFIG_PARITY_EVEN ? "Even"
|
||||
: "Odd",
|
||||
this->stop_bits_);
|
||||
this->check_logger_conflict();
|
||||
}
|
||||
|
||||
void HostUartComponent::write_array(const uint8_t *data, size_t len) {
|
||||
if (this->file_descriptor_ == -1) {
|
||||
return;
|
||||
}
|
||||
size_t written = ::write(this->file_descriptor_, data, len);
|
||||
if (written != len) {
|
||||
this->update_error_(strerror(errno));
|
||||
return;
|
||||
}
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
bool HostUartComponent::peek_byte(uint8_t *data) {
|
||||
if (this->file_descriptor_ == -1) {
|
||||
return false;
|
||||
}
|
||||
if (!this->has_peek_) {
|
||||
if (!this->check_read_timeout_()) {
|
||||
return false;
|
||||
}
|
||||
if (::read(this->file_descriptor_, &this->peek_byte_, 1) != 1) {
|
||||
this->update_error_(strerror(errno));
|
||||
return false;
|
||||
}
|
||||
this->has_peek_ = true;
|
||||
}
|
||||
*data = this->peek_byte_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HostUartComponent::read_array(uint8_t *data, size_t len) {
|
||||
if ((this->file_descriptor_ == -1) || (len == 0)) {
|
||||
return false;
|
||||
}
|
||||
if (!this->check_read_timeout_(len))
|
||||
return false;
|
||||
size_t length_to_read = len;
|
||||
if (this->has_peek_) {
|
||||
length_to_read--;
|
||||
*data = this->peek_byte_;
|
||||
this->has_peek_ = false;
|
||||
}
|
||||
if (length_to_read > 0) {
|
||||
int sz = ::read(this->file_descriptor_, data + (len - length_to_read), length_to_read);
|
||||
if (sz == -1) {
|
||||
this->update_error_(strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t HostUartComponent::available() {
|
||||
if (this->file_descriptor_ == -1) {
|
||||
return 0;
|
||||
}
|
||||
int available;
|
||||
int res = ioctl(this->file_descriptor_, FIONREAD, &available);
|
||||
if (res == -1) {
|
||||
this->update_error_(strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
size_t result = available;
|
||||
if (this->has_peek_)
|
||||
result++;
|
||||
return result;
|
||||
};
|
||||
|
||||
UARTFlushResult HostUartComponent::flush() {
|
||||
if (this->file_descriptor_ == -1) {
|
||||
return UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS;
|
||||
}
|
||||
ESP_LOGV(TAG, " Flushing");
|
||||
if (tcdrain(this->file_descriptor_) == -1) {
|
||||
this->update_error_(strerror(errno));
|
||||
return UARTFlushResult::UART_FLUSH_RESULT_FAILED;
|
||||
}
|
||||
return UARTFlushResult::UART_FLUSH_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void HostUartComponent::update_error_(const std::string &error) {
|
||||
if (this->first_error_.empty()) {
|
||||
this->first_error_ = error;
|
||||
}
|
||||
ESP_LOGE(TAG, "Port error: %s", error.c_str());
|
||||
}
|
||||
|
||||
} // namespace esphome::uart
|
||||
#endif // USE_HOST
|
||||
Reference in new issue
Block a user