1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-02 19:32:19 +01:00

Initial commit

This commit is contained in:
Keith Burzinski
2025-08-14 01:39:13 -05:00
parent 5fa84439c2
commit cfa1f5795e
3 changed files with 217 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
import esphome.codegen as cg
from esphome.components import uart
import esphome.config_validation as cv
from esphome.const import CONF_ID
DEPENDENCIES = ["uart"]
zwave_proxy_ns = cg.esphome_ns.namespace("zwave_proxy")
ZWaveProxy = zwave_proxy_ns.class_("ZWaveProxy", cg.Component, uart.UARTDevice)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ZWaveProxy),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(uart.UART_DEVICE_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)

View File

@@ -0,0 +1,143 @@
#include "zwave_proxy.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace zwave_proxy {
static const char *TAG = "zwave_proxy";
void ZWaveProxy::setup() {
// Get capabilities command sent once here just to test communication for component development
uint8_t get_capabilities_cmd[] = {0x01, 0x03, 0x00, 0x07, 0xfb};
ESP_LOGD(TAG, "Sending: %s", format_hex_pretty(get_capabilities_cmd, sizeof(get_capabilities_cmd)).c_str());
this->write_array(get_capabilities_cmd, sizeof(get_capabilities_cmd));
}
void ZWaveProxy::loop() {
if (this->response_handler_()) {
return; // If a response was handled, exit early to avoid a CAN
}
while (this->available()) {
uint8_t byte;
if (!this->read_byte(&byte)) {
this->status_set_warning("Failed reading from UART");
return;
}
this->parse_byte_(byte);
}
this->status_clear_warning();
}
void ZWaveProxy::dump_config() { ESP_LOGCONFIG(TAG, "Z-Wave Proxy"); }
void ZWaveProxy::send_frame(std::vector<uint8_t> &data) {
ESP_LOGD(TAG, "Sending: %s", format_hex_pretty(data).c_str());
this->write_array(data);
}
void ZWaveProxy::parse_byte_(uint8_t byte) {
// Basic parsing logic for received frames
switch (this->parsing_state_) {
case ZWAVE_PARSING_STATE_WAIT_START:
this->parse_start_(byte);
break;
case ZWAVE_PARSING_STATE_WAIT_LENGTH:
ESP_LOGD(TAG, "Received LENGTH: %u", byte);
this->end_frame_after_ = this->buffer_index_ + byte;
ESP_LOGVV(TAG, "Calculated EOF: %u", this->end_frame_after_);
this->buffer_[this->buffer_index_++] = byte;
this->checksum_ ^= byte;
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_TYPE;
break;
case ZWAVE_PARSING_STATE_WAIT_TYPE:
this->buffer_[this->buffer_index_++] = byte;
ESP_LOGD(TAG, "Received TYPE: 0x%02X", byte);
this->checksum_ ^= byte;
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_COMMAND_ID;
break;
case ZWAVE_PARSING_STATE_WAIT_COMMAND_ID:
this->buffer_[this->buffer_index_++] = byte;
ESP_LOGD(TAG, "Received COMMAND ID: 0x%02X", byte);
this->checksum_ ^= byte;
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_PAYLOAD;
break;
case ZWAVE_PARSING_STATE_WAIT_PAYLOAD:
this->buffer_[this->buffer_index_++] = byte;
this->checksum_ ^= byte;
ESP_LOGVV(TAG, "Received PAYLOAD: 0x%02X", byte);
if (this->buffer_index_ >= this->end_frame_after_) {
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_CHECKSUM;
}
break;
case ZWAVE_PARSING_STATE_WAIT_CHECKSUM:
this->buffer_[this->buffer_index_++] = byte;
ESP_LOGD(TAG, "Received CHECKSUM: 0x%02X", byte);
ESP_LOGV(TAG, "Calculated CHECKSUM: 0x%02X", this->checksum_);
if (this->checksum_ != byte) {
ESP_LOGW(TAG, "Bad checksum: expected 0x%02X, got 0x%02X", this->checksum_, byte);
this->parsing_state_ = ZWAVE_PARSING_STATE_SEND_NAK;
} else {
this->parsing_state_ = ZWAVE_PARSING_STATE_SEND_ACK;
ESP_LOGD(TAG, "Received frame: %s", format_hex_pretty(this->buffer_, this->buffer_index_).c_str());
}
break;
case ZWAVE_PARSING_STATE_SEND_ACK:
case ZWAVE_PARSING_STATE_SEND_NAK:
break; // Should not happen, handled in loop()
default:
ESP_LOGD(TAG, "Received unknown byte: 0x%02X", byte);
break;
}
}
void ZWaveProxy::parse_start_(uint8_t byte) {
this->buffer_index_ = 0;
this->checksum_ = 0xFF;
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_START;
switch (byte) {
case ZWAVE_FRAME_TYPE_START:
ESP_LOGD(TAG, "Received START");
this->buffer_[this->buffer_index_++] = byte;
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_LENGTH;
break;
case ZWAVE_FRAME_TYPE_ACK:
ESP_LOGD(TAG, "Received ACK");
break;
case ZWAVE_FRAME_TYPE_NAK:
ESP_LOGW(TAG, "Received NAK");
break;
case ZWAVE_FRAME_TYPE_CAN:
ESP_LOGW(TAG, "Received CAN");
break;
default:
ESP_LOGW(TAG, "Unexpected type: 0x%02X", byte);
break;
}
}
bool ZWaveProxy::response_handler_() {
uint8_t response_byte = 0;
switch (this->parsing_state_) {
case ZWAVE_PARSING_STATE_SEND_ACK:
response_byte = ZWAVE_FRAME_TYPE_ACK;
break;
case ZWAVE_PARSING_STATE_SEND_CAN:
response_byte = ZWAVE_FRAME_TYPE_CAN;
break;
case ZWAVE_PARSING_STATE_SEND_NAK:
response_byte = ZWAVE_FRAME_TYPE_NAK;
break;
default:
return false; // No response handled
}
ESP_LOGD(TAG, "Sending %s (0x%02X)", response_byte == ZWAVE_FRAME_TYPE_ACK ? "ACK" : "NAK/CAN", response_byte);
this->write_byte(response_byte);
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_START;
return true;
}
} // namespace zwave_proxy
} // namespace esphome

View File

@@ -0,0 +1,49 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace zwave_proxy {
enum ZWaveResponseTypes : uint8_t {
ZWAVE_FRAME_TYPE_ACK = 0x06,
ZWAVE_FRAME_TYPE_CAN = 0x18,
ZWAVE_FRAME_TYPE_NAK = 0x15,
ZWAVE_FRAME_TYPE_START = 0x01,
};
enum ZWaveParsingState : uint8_t {
ZWAVE_PARSING_STATE_WAIT_START,
ZWAVE_PARSING_STATE_WAIT_LENGTH,
ZWAVE_PARSING_STATE_WAIT_TYPE,
ZWAVE_PARSING_STATE_WAIT_COMMAND_ID,
ZWAVE_PARSING_STATE_WAIT_PAYLOAD,
ZWAVE_PARSING_STATE_WAIT_CHECKSUM,
ZWAVE_PARSING_STATE_SEND_ACK,
ZWAVE_PARSING_STATE_SEND_CAN,
ZWAVE_PARSING_STATE_SEND_NAK,
};
class ZWaveProxy : public uart::UARTDevice, public Component {
public:
void setup() override;
void loop() override;
void dump_config() override;
void send_frame(std::vector<uint8_t> &data);
protected:
void parse_byte_(uint8_t byte);
void parse_start_(uint8_t byte);
bool response_handler_();
uint8_t buffer_[257]; // Fixed buffer for incoming data: max length = 255 + 2 (start of frame and checksum)
uint8_t buffer_index_{0}; // Index for populating the data buffer
uint8_t checksum_{0}; // Checksum of the frame being parsed
uint8_t end_frame_after_{0}; // Payload reception ends after this index
ZWaveParsingState parsing_state_{ZWAVE_PARSING_STATE_WAIT_START};
};
} // namespace zwave_proxy
} // namespace esphome