1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-03 10:32:21 +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 {
ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE = 0;
ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE = 1;
ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE = 2;
}
message ZWaveProxyRequest {
option (id) = 129;
option (source) = SOURCE_CLIENT;
option (source) = SOURCE_BOTH;
option (ifdef) = "USE_ZWAVE_PROXY";
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;
}
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
} // namespace esphome::api

View File

@@ -280,6 +280,7 @@ enum UpdateCommand : uint32_t {
enum ZWaveProxyRequestType : uint32_t {
ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE = 0,
ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE = 1,
ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE = 2,
};
#endif
@@ -2971,16 +2972,21 @@ class ZWaveProxyFrame final : public ProtoDecodableMessage {
class ZWaveProxyRequest final : public ProtoDecodableMessage {
public:
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
const char *message_name() const override { return "z_wave_proxy_request"; }
#endif
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
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
#endif

View File

@@ -662,6 +662,8 @@ template<> const char *proto_enum_to_string<enums::ZWaveProxyRequestType>(enums:
return "ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE";
case enums::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:
return "UNKNOWN";
}
@@ -2161,6 +2163,9 @@ void ZWaveProxyFrame::dump_to(std::string &out) const {
void ZWaveProxyRequest::dump_to(std::string &out) const {
MessageDumpHelper helper(out, "ZWaveProxyRequest");
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

View File

@@ -356,6 +356,15 @@ void APIServer::on_update(update::UpdateEntity *obj) {
}
#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
API_DISPATCH_UPDATE(alarm_control_panel::AlarmControlPanel, alarm_control_panel)
#endif

View File

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

View File

@@ -1,4 +1,5 @@
#include "zwave_proxy.h"
#include "esphome/components/api/api_server.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
@@ -97,12 +98,19 @@ void ZWaveProxy::process_uart_() {
// - 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 &&
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
std::memcpy(this->home_id_.data(), this->buffer_.data() + 4, this->home_id_.size());
this->home_id_ready_ = true;
ESP_LOGI(TAG, "Home ID: %s",
format_hex_pretty(this->home_id_.data(), this->home_id_.size(), ':', false).c_str());
if (this->set_home_id(&this->buffer_[4])) {
api::ZWaveProxyRequest msg;
msg.type = api::enums::ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE;
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));
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) {
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) {
if (length == 1 && data[0] == this->last_response_) {
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() {
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);