mirror of
https://github.com/esphome/esphome.git
synced 2025-09-02 11:22:24 +01:00
Initial commit
This commit is contained in:
25
esphome/components/zwave_proxy/__init__.py
Normal file
25
esphome/components/zwave_proxy/__init__.py
Normal 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)
|
143
esphome/components/zwave_proxy/zwave_proxy.cpp
Normal file
143
esphome/components/zwave_proxy/zwave_proxy.cpp
Normal 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
|
49
esphome/components/zwave_proxy/zwave_proxy.h
Normal file
49
esphome/components/zwave_proxy/zwave_proxy.h
Normal 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
|
Reference in New Issue
Block a user