15static const char *
const TAG =
"zwave_proxy";
18static constexpr size_t ZWAVE_MAX_LOG_BYTES = 168;
20static constexpr uint8_t ZWAVE_COMMAND_GET_NETWORK_IDS = 0x20;
24static constexpr uint8_t ZWAVE_COMMAND_TYPE_RESPONSE = 0x01;
25static constexpr uint8_t ZWAVE_MIN_GET_NETWORK_IDS_LENGTH = 7;
26static constexpr uint8_t ZWAVE_MIN_FRAME_LENGTH = 3;
27static constexpr uint32_t ZWAVE_FRAME_TIMEOUT_MS = 1500;
28static constexpr uint32_t HOME_ID_TIMEOUT_MS = 100;
29static constexpr uint32_t RECONNECT_DELAY_MS = 500;
30static constexpr uint8_t MAX_QUERY_RETRIES = 5;
32static constexpr bool is_bootloader_menu_byte(uint8_t
byte) {
34 return byte == 0 ||
byte ==
'\r' ||
byte ==
'\n' || (
byte >= 0x20 &&
byte <= 0x7E);
37static uint8_t calculate_frame_checksum(
const uint8_t *data, uint8_t
length) {
42 for (uint8_t i = 1; i <
length - 1; i++) {
71 ESP_LOGV(TAG,
"Handled response during setup");
85 ESP_LOGW(TAG,
"Timeout reading Home ID during setup");
99 ESP_LOGV(TAG,
"Handled late response");
102 ESP_LOGW(TAG,
"Subscriber disconnected");
123 ESP_LOGW(TAG,
"Timeout waiting for frame data; resetting parser");
149 this->
buffer_[2] == ZWAVE_COMMAND_TYPE_RESPONSE && this->
buffer_[3] == ZWAVE_COMMAND_GET_NETWORK_IDS) {
159 ESP_LOGV(TAG,
"Sending to client: %s", YESNO(this->
api_connection_ !=
nullptr));
199 ESP_LOGV(TAG,
"API connection is already subscribed");
207 ESP_LOGE(TAG,
"Only one API subscription is allowed at a time");
210 ESP_LOGW(TAG,
"Previous subscriber disconnected; taking over subscription");
213 ESP_LOGV(TAG,
"API connection is now subscribed");
218 ESP_LOGV(TAG,
"API connection is not subscribed");
225 ESP_LOGW(TAG,
"Unknown request type: %" PRIu32,
static_cast<uint32_t>(
type));
233 ESP_LOGD(TAG,
"Modem reconnected");
242 ESP_LOGW(TAG,
"Modem disconnected");
259 ESP_LOGD(TAG,
"Querying Home ID (attempt %u)", this->
query_retries_);
262 ESP_LOGW(TAG,
"Failed to read Home ID after %u attempts", MAX_QUERY_RETRIES);
268 static constexpr uint8_t ZERO_HOME_ID[ZWAVE_HOME_ID_SIZE] = {};
270 ESP_LOGV(TAG,
"Home ID cleared");
281 if (std::memcmp(this->
home_id_.data(), new_home_id, this->home_id_.size()) == 0) {
282 ESP_LOGV(TAG,
"Home ID unchanged");
285 std::memcpy(this->
home_id_.data(), new_home_id, this->home_id_.size());
293 ESP_LOGW(TAG,
"Ignoring frame from unsubscribed client");
301 if (data ==
nullptr) {
302 ESP_LOGE(TAG,
"Null data pointer");
306 ESP_LOGE(TAG,
"Length 0");
312 ESP_LOGV(TAG,
"Response already sent: 0x%02X", data[0]);
316#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
329 if (conn !=
nullptr) {
343 uint8_t cmd[] = {0x01, 0x03, 0x00, command_id, 0x00};
344 cmd[4] = calculate_frame_checksum(cmd,
sizeof(cmd));
349 bool frame_completed =
false;
356 if (
byte < ZWAVE_MIN_FRAME_LENGTH) {
357 ESP_LOGW(TAG,
"Invalid LENGTH: %u",
byte);
364 ESP_LOGVV(TAG,
"Received LENGTH: %u",
byte);
372 ESP_LOGVV(TAG,
"Received TYPE: 0x%02X",
byte);
377 ESP_LOGVV(TAG,
"Received COMMAND ID: 0x%02X",
byte);
384 ESP_LOGVV(TAG,
"Received PAYLOAD: 0x%02X",
byte);
391 auto checksum = calculate_frame_checksum(this->
buffer_.data(), this->buffer_index_);
392 ESP_LOGVV(TAG,
"CHECKSUM Received: 0x%02X - Calculated: 0x%02X",
byte,
checksum);
394 ESP_LOGW(TAG,
"Bad checksum: expected 0x%02X, got 0x%02X",
checksum,
byte);
398#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
402 frame_completed =
true;
420 ESP_LOGD(TAG,
"Entered bootloader mode");
427 frame_completed =
true;
434 ESP_LOGW(TAG,
"Bad parsing state; resetting");
438 return frame_completed;
446 ESP_LOGV(TAG,
"Received START");
448 ESP_LOGD(TAG,
"Exited bootloader mode");
456 ESP_LOGV(TAG,
"Received BL_MENU");
465 ESP_LOGV(TAG,
"Received BL_BEGIN_UPLOAD");
468 ESP_LOGV(TAG,
"Received ACK");
471 ESP_LOGV(TAG,
"Received NAK");
474 ESP_LOGV(TAG,
"Received CAN");
477 ESP_LOGV(TAG,
"Unrecognized START: 0x%02X",
byte);
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void status_set_warning()
void status_clear_warning()
bool is_connection_setup()
bool send_message(const T &msg)
void on_zwave_proxy_request(const ZWaveProxyRequest &msg)
enums::ZWaveProxyRequestType type
virtual bool is_connected()
bool read_byte(uint8_t *data)
void write_byte(uint8_t data)
void write_array(const uint8_t *data, size_t len)
void zwave_proxy_request(api::APIConnection *api_connection, api::enums::ZWaveProxyRequestType type)
api::ZWaveProxyFrame outgoing_proto_msg_
bool parse_byte_(uint8_t byte)
void parse_start_(uint8_t byte)
void send_homeid_changed_msg_(api::APIConnection *conn=nullptr)
uint16_t end_frame_after_
bool set_home_id_(const uint8_t *new_home_id)
void send_simple_command_(uint8_t command_id)
ESPHOME_ALWAYS_INLINE void process_uart_()
void send_frame(api::APIConnection *api_connection, const uint8_t *data, size_t length)
void send_frame_(const uint8_t *data, size_t length)
bool response_handler_slow_()
void retry_home_id_query_()
void api_connection_authenticated(api::APIConnection *conn)
bool can_proceed() override
ZWaveParsingState parsing_state_
std::array< uint8_t, MAX_ZWAVE_FRAME_SIZE > buffer_
void dump_config() override
ESPHOME_ALWAYS_INLINE bool response_handler_()
uint32_t frame_start_time_
api::APIConnection * api_connection_
float get_setup_priority() const override
void on_connection_changed_(bool connected)
void process_uart_slow_()
std::array< uint8_t, ZWAVE_HOME_ID_SIZE > home_id_
@ ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE
@ ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE
@ ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE
APIServer * global_api_server
constexpr float BEFORE_CONNECTION
For components that should be initialized after WiFi and before API is connected.
@ ZWAVE_FRAME_TYPE_BL_MENU
@ ZWAVE_FRAME_TYPE_BL_BEGIN_UPLOAD
@ ZWAVE_PARSING_STATE_WAIT_COMMAND_ID
@ ZWAVE_PARSING_STATE_WAIT_LENGTH
@ ZWAVE_PARSING_STATE_WAIT_CHECKSUM
@ ZWAVE_PARSING_STATE_WAIT_PAYLOAD
@ ZWAVE_PARSING_STATE_SEND_ACK
@ ZWAVE_PARSING_STATE_SEND_NAK
@ ZWAVE_PARSING_STATE_READ_BL_MENU
@ ZWAVE_PARSING_STATE_WAIT_TYPE
@ ZWAVE_PARSING_STATE_SEND_CAN
@ ZWAVE_PARSING_STATE_WAIT_START
ZWaveProxy * global_zwave_proxy
ESPHOME_ALWAYS_INLINE bool api_is_connected()
Return whether the node has at least one client connected to the native API.
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
Application App
Global storage of Application pointer - only one Application can exist.