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,90 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import event, uart
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_EVENT_TYPES, CONF_ID
|
||||
from esphome.core import ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import uart_ns
|
||||
|
||||
CODEOWNERS = ["@eoasmxd"]
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
|
||||
UARTEvent = uart_ns.class_("UARTEvent", event.Event, uart.UARTDevice, cg.Component)
|
||||
|
||||
|
||||
def validate_event_types(value) -> list[tuple[str, str | list[int]]]:
|
||||
if not isinstance(value, list):
|
||||
raise cv.Invalid("Event type must be a list of key-value mappings.")
|
||||
|
||||
processed: list[tuple[str, str | list[int]]] = []
|
||||
for item in value:
|
||||
if not isinstance(item, dict):
|
||||
raise cv.Invalid(f"Event type item must be a mapping (dictionary): {item}")
|
||||
if len(item) != 1:
|
||||
raise cv.Invalid(
|
||||
f"Event type item must be a single key-value mapping: {item}"
|
||||
)
|
||||
|
||||
# Get the single key-value pair
|
||||
event_name, match_data = next(iter(item.items()))
|
||||
|
||||
if not isinstance(event_name, str):
|
||||
raise cv.Invalid(f"Event name (key) must be a string: {event_name}")
|
||||
|
||||
try:
|
||||
# Try to validate as list of hex bytes
|
||||
match_data_bin = cv.ensure_list(cv.hex_uint8_t)(match_data)
|
||||
processed.append((event_name, match_data_bin))
|
||||
continue
|
||||
except cv.Invalid:
|
||||
pass # Not binary, try string
|
||||
|
||||
try:
|
||||
# Try to validate as string
|
||||
match_data_str = cv.string_strict(match_data)
|
||||
processed.append((event_name, match_data_str))
|
||||
continue
|
||||
except cv.Invalid:
|
||||
pass # Not string either
|
||||
|
||||
# If neither validation passed
|
||||
raise cv.Invalid(
|
||||
f"Event match data for '{event_name}' must be a string or a list of hex bytes. Invalid data: {match_data}"
|
||||
)
|
||||
|
||||
if not processed:
|
||||
raise cv.Invalid("event_types must contain at least one event mapping.")
|
||||
|
||||
return processed
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
event.event_schema(UARTEvent)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_EVENT_TYPES): validate_event_types,
|
||||
}
|
||||
)
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
event_names = [item[0] for item in config[CONF_EVENT_TYPES]]
|
||||
var = await event.new_event(config, event_types=event_names)
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
for i, (event_name, match_data) in enumerate(config[CONF_EVENT_TYPES]):
|
||||
if isinstance(match_data, str):
|
||||
match_data = [ord(c) for c in match_data]
|
||||
|
||||
match_data_var_id = ID(
|
||||
f"match_data_{config[CONF_ID]}_{i}", is_declaration=True, type=cg.uint8
|
||||
)
|
||||
match_data_var = cg.static_const_array(
|
||||
match_data_var_id, cg.ArrayInitializer(*match_data)
|
||||
)
|
||||
cg.add(var.add_event_matcher(event_name, match_data_var, len(match_data)))
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "uart_event.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart.event";
|
||||
|
||||
void UARTEvent::setup() {}
|
||||
|
||||
void UARTEvent::dump_config() { LOG_EVENT("", "UART Event", this); }
|
||||
|
||||
void UARTEvent::loop() { this->read_data_(); }
|
||||
|
||||
void UARTEvent::add_event_matcher(const char *event_name, const uint8_t *match_data, size_t match_data_len) {
|
||||
this->matchers_.push_back({event_name, match_data, match_data_len});
|
||||
if (match_data_len > this->max_matcher_len_) {
|
||||
this->max_matcher_len_ = match_data_len;
|
||||
}
|
||||
}
|
||||
|
||||
void UARTEvent::read_data_() {
|
||||
while (this->available()) {
|
||||
uint8_t data;
|
||||
this->read_byte(&data);
|
||||
this->buffer_.push_back(data);
|
||||
|
||||
bool match_found = false;
|
||||
for (const auto &matcher : this->matchers_) {
|
||||
if (this->buffer_.size() < matcher.data_len) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::equal(matcher.data, matcher.data + matcher.data_len, this->buffer_.end() - matcher.data_len)) {
|
||||
this->trigger(matcher.event_name);
|
||||
this->buffer_.clear();
|
||||
match_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match_found && this->max_matcher_len_ > 0 && this->buffer_.size() > this->max_matcher_len_) {
|
||||
this->buffer_.erase(this->buffer_.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::uart
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/event/event.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include <vector>
|
||||
|
||||
namespace esphome::uart {
|
||||
|
||||
class UARTEvent final : public event::Event, public UARTDevice, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
|
||||
void add_event_matcher(const char *event_name, const uint8_t *match_data, size_t match_data_len);
|
||||
|
||||
protected:
|
||||
struct EventMatcher {
|
||||
const char *event_name;
|
||||
const uint8_t *data;
|
||||
size_t data_len;
|
||||
};
|
||||
|
||||
void read_data_();
|
||||
std::vector<EventMatcher> matchers_;
|
||||
std::vector<uint8_t> buffer_;
|
||||
size_t max_matcher_len_ = 0;
|
||||
};
|
||||
|
||||
} // namespace esphome::uart
|
||||
Reference in new issue
Block a user