1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-03 18:42:23 +01:00

[zwave_proxy, api] Add notification message when Z-Wave HomeID changes (#10860)

This commit is contained in:
Keith Burzinski
2025-09-27 17:50:18 -05:00
committed by GitHub
parent 2bf79a607f
commit 9dd6be4061
8 changed files with 79 additions and 8 deletions

View File

@@ -2310,11 +2310,13 @@ message ZWaveProxyFrame {
enum ZWaveProxyRequestType { enum ZWaveProxyRequestType {
ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE = 0; ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE = 0;
ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE = 1; ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE = 1;
ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE = 2;
} }
message ZWaveProxyRequest { message ZWaveProxyRequest {
option (id) = 129; option (id) = 129;
option (source) = SOURCE_CLIENT; option (source) = SOURCE_BOTH;
option (ifdef) = "USE_ZWAVE_PROXY"; option (ifdef) = "USE_ZWAVE_PROXY";
ZWaveProxyRequestType type = 1; ZWaveProxyRequestType type = 1;
bytes data = 2 [(pointer_to_buffer) = true];
} }

View File

@@ -3112,6 +3112,27 @@ bool ZWaveProxyRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
} }
return true; return true;
} }
bool ZWaveProxyRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2: {
// Use raw data directly to avoid allocation
this->data = value.data();
this->data_len = value.size();
break;
}
default:
return false;
}
return true;
}
void ZWaveProxyRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, static_cast<uint32_t>(this->type));
buffer.encode_bytes(2, this->data, this->data_len);
}
void ZWaveProxyRequest::calculate_size(ProtoSize &size) const {
size.add_uint32(1, static_cast<uint32_t>(this->type));
size.add_length(2, this->data_len);
}
#endif #endif
} // namespace esphome::api } // namespace esphome::api

View File

@@ -280,6 +280,7 @@ enum UpdateCommand : uint32_t {
enum ZWaveProxyRequestType : uint32_t { enum ZWaveProxyRequestType : uint32_t {
ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE = 0, ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE = 0,
ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE = 1, ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE = 1,
ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE = 2,
}; };
#endif #endif
@@ -2971,16 +2972,21 @@ class ZWaveProxyFrame final : public ProtoDecodableMessage {
class ZWaveProxyRequest final : public ProtoDecodableMessage { class ZWaveProxyRequest final : public ProtoDecodableMessage {
public: public:
static constexpr uint8_t MESSAGE_TYPE = 129; static constexpr uint8_t MESSAGE_TYPE = 129;
static constexpr uint8_t ESTIMATED_SIZE = 2; static constexpr uint8_t ESTIMATED_SIZE = 21;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "z_wave_proxy_request"; } const char *message_name() const override { return "z_wave_proxy_request"; }
#endif #endif
enums::ZWaveProxyRequestType type{}; enums::ZWaveProxyRequestType type{};
const uint8_t *data{nullptr};
uint16_t data_len{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
#endif #endif
protected: protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };
#endif #endif

View File

@@ -662,6 +662,8 @@ template<> const char *proto_enum_to_string<enums::ZWaveProxyRequestType>(enums:
return "ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE"; return "ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE";
case enums::ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE: case enums::ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE:
return "ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE"; return "ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE";
case enums::ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE:
return "ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
@@ -2161,6 +2163,9 @@ void ZWaveProxyFrame::dump_to(std::string &out) const {
void ZWaveProxyRequest::dump_to(std::string &out) const { void ZWaveProxyRequest::dump_to(std::string &out) const {
MessageDumpHelper helper(out, "ZWaveProxyRequest"); MessageDumpHelper helper(out, "ZWaveProxyRequest");
dump_field(out, "type", static_cast<enums::ZWaveProxyRequestType>(this->type)); dump_field(out, "type", static_cast<enums::ZWaveProxyRequestType>(this->type));
out.append(" data: ");
out.append(format_hex_pretty(this->data, this->data_len));
out.append("\n");
} }
#endif #endif

View File

@@ -356,6 +356,15 @@ void APIServer::on_update(update::UpdateEntity *obj) {
} }
#endif #endif
#ifdef USE_ZWAVE_PROXY
void APIServer::on_zwave_proxy_request(const esphome::api::ProtoMessage &msg) {
// We could add code to manage a second subscription type, but, since this message type is
// very infrequent and small, we simply send it to all clients
for (auto &c : this->clients_)
c->send_message(msg, api::ZWaveProxyRequest::MESSAGE_TYPE);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL #ifdef USE_ALARM_CONTROL_PANEL
API_DISPATCH_UPDATE(alarm_control_panel::AlarmControlPanel, alarm_control_panel) API_DISPATCH_UPDATE(alarm_control_panel::AlarmControlPanel, alarm_control_panel)
#endif #endif

View File

@@ -126,6 +126,9 @@ class APIServer : public Component, public Controller {
#ifdef USE_UPDATE #ifdef USE_UPDATE
void on_update(update::UpdateEntity *obj) override; void on_update(update::UpdateEntity *obj) override;
#endif #endif
#ifdef USE_ZWAVE_PROXY
void on_zwave_proxy_request(const esphome::api::ProtoMessage &msg);
#endif
bool is_connected() const; bool is_connected() const;

View File

@@ -1,4 +1,5 @@
#include "zwave_proxy.h" #include "zwave_proxy.h"
#include "esphome/components/api/api_server.h"
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
@@ -97,12 +98,19 @@ void ZWaveProxy::process_uart_() {
// - buffer_[3]: Command ID (0x20 for GET_NETWORK_IDS) // - buffer_[3]: Command ID (0x20 for GET_NETWORK_IDS)
if (this->buffer_[3] == ZWAVE_COMMAND_GET_NETWORK_IDS && this->buffer_[2] == ZWAVE_COMMAND_TYPE_RESPONSE && if (this->buffer_[3] == ZWAVE_COMMAND_GET_NETWORK_IDS && this->buffer_[2] == ZWAVE_COMMAND_TYPE_RESPONSE &&
this->buffer_[1] >= ZWAVE_MIN_GET_NETWORK_IDS_LENGTH && this->buffer_[0] == ZWAVE_FRAME_TYPE_START) { this->buffer_[1] >= ZWAVE_MIN_GET_NETWORK_IDS_LENGTH && this->buffer_[0] == ZWAVE_FRAME_TYPE_START) {
// Extract the 4-byte Home ID starting at offset 4 // Store the 4-byte Home ID, which starts at offset 4, and notify connected clients if it changed
// The frame parser has already validated the checksum and ensured all bytes are present // The frame parser has already validated the checksum and ensured all bytes are present
std::memcpy(this->home_id_.data(), this->buffer_.data() + 4, this->home_id_.size()); if (this->set_home_id(&this->buffer_[4])) {
this->home_id_ready_ = true; api::ZWaveProxyRequest msg;
ESP_LOGI(TAG, "Home ID: %s", msg.type = api::enums::ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE;
format_hex_pretty(this->home_id_.data(), this->home_id_.size(), ':', false).c_str()); msg.data = this->home_id_.data();
msg.data_len = this->home_id_.size();
if (api::global_api_server != nullptr) {
// We could add code to manage a second subscription type, but, since this message is
// very infrequent and small, we simply send it to all clients
api::global_api_server->on_zwave_proxy_request(msg);
}
}
} }
ESP_LOGV(TAG, "Sending to client: %s", YESNO(this->api_connection_ != nullptr)); ESP_LOGV(TAG, "Sending to client: %s", YESNO(this->api_connection_ != nullptr));
if (this->api_connection_ != nullptr) { if (this->api_connection_ != nullptr) {
@@ -120,7 +128,12 @@ void ZWaveProxy::process_uart_() {
} }
} }
void ZWaveProxy::dump_config() { ESP_LOGCONFIG(TAG, "Z-Wave Proxy"); } void ZWaveProxy::dump_config() {
ESP_LOGCONFIG(TAG,
"Z-Wave Proxy:\n"
" Home ID: %s",
format_hex_pretty(this->home_id_.data(), this->home_id_.size(), ':', false).c_str());
}
void ZWaveProxy::zwave_proxy_request(api::APIConnection *api_connection, api::enums::ZWaveProxyRequestType type) { void ZWaveProxy::zwave_proxy_request(api::APIConnection *api_connection, api::enums::ZWaveProxyRequestType type) {
switch (type) { switch (type) {
@@ -145,6 +158,17 @@ void ZWaveProxy::zwave_proxy_request(api::APIConnection *api_connection, api::en
} }
} }
bool ZWaveProxy::set_home_id(const uint8_t *new_home_id) {
if (std::memcmp(this->home_id_.data(), new_home_id, this->home_id_.size()) == 0) {
ESP_LOGV(TAG, "Home ID unchanged");
return false; // No change
}
std::memcpy(this->home_id_.data(), new_home_id, this->home_id_.size());
ESP_LOGI(TAG, "Home ID: %s", format_hex_pretty(this->home_id_.data(), this->home_id_.size(), ':', false).c_str());
this->home_id_ready_ = true;
return true; // Home ID was changed
}
void ZWaveProxy::send_frame(const uint8_t *data, size_t length) { void ZWaveProxy::send_frame(const uint8_t *data, size_t length) {
if (length == 1 && data[0] == this->last_response_) { if (length == 1 && data[0] == this->last_response_) {
ESP_LOGV(TAG, "Skipping sending duplicate response: 0x%02X", data[0]); ESP_LOGV(TAG, "Skipping sending duplicate response: 0x%02X", data[0]);

View File

@@ -56,6 +56,7 @@ class ZWaveProxy : public uart::UARTDevice, public Component {
uint32_t get_home_id() { uint32_t get_home_id() {
return encode_uint32(this->home_id_[0], this->home_id_[1], this->home_id_[2], this->home_id_[3]); return encode_uint32(this->home_id_[0], this->home_id_[1], this->home_id_[2], this->home_id_[3]);
} }
bool set_home_id(const uint8_t *new_home_id); // Store a new home ID. Returns true if it changed.
void send_frame(const uint8_t *data, size_t length); void send_frame(const uint8_t *data, size_t length);