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

subscribe

This commit is contained in:
Keith Burzinski
2025-08-22 00:31:56 -05:00
parent 637e8ebd5d
commit 47a9724e0c
11 changed files with 177 additions and 18 deletions

View File

@@ -70,6 +70,8 @@ service APIConnection {
rpc alarm_control_panel_command (AlarmControlPanelCommandRequest) returns (void) {} rpc alarm_control_panel_command (AlarmControlPanelCommandRequest) returns (void) {}
rpc zwave_proxy_subscribe(ZWaveProxySubscribeRequest) returns (void) {}
rpc zwave_proxy_unsubscribe(ZWaveProxyUnsubscribeRequest) returns (void) {}
rpc zwave_proxy_from_device(ZWaveProxyFromDeviceRequest) returns (void) {} rpc zwave_proxy_from_device(ZWaveProxyFromDeviceRequest) returns (void) {}
rpc zwave_proxy_to_device(ZWaveProxyToDeviceRequest) returns (void) {} rpc zwave_proxy_to_device(ZWaveProxyToDeviceRequest) returns (void) {}
} }
@@ -2289,6 +2291,8 @@ message ZWaveProxyFromDeviceRequest {
option (source) = SOURCE_CLIENT; option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_ZWAVE_PROXY"; option (ifdef) = "USE_ZWAVE_PROXY";
option (no_delay) = true; option (no_delay) = true;
string data = 1;
} }
message ZWaveProxyFromDeviceResponse { message ZWaveProxyFromDeviceResponse {
@@ -2296,8 +2300,6 @@ message ZWaveProxyFromDeviceResponse {
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
option (ifdef) = "USE_ZWAVE_PROXY"; option (ifdef) = "USE_ZWAVE_PROXY";
option (no_delay) = true; option (no_delay) = true;
string data = 1;
} }
message ZWaveProxyToDeviceRequest { message ZWaveProxyToDeviceRequest {
@@ -2315,3 +2317,17 @@ message ZWaveProxyToDeviceResponse {
option (ifdef) = "USE_ZWAVE_PROXY"; option (ifdef) = "USE_ZWAVE_PROXY";
option (no_delay) = true; option (no_delay) = true;
} }
message ZWaveProxySubscribeRequest {
option (id) = 132;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_ZWAVE_PROXY";
uint32 flags = 1;
}
message ZWaveProxyUnsubscribeRequest {
option (id) = 133;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_ZWAVE_PROXY";
}

View File

@@ -1207,7 +1207,18 @@ void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetCon
#endif #endif
#ifdef USE_ZWAVE_PROXY #ifdef USE_ZWAVE_PROXY
void APIConnection::zwave_proxy_from_device(const ZWaveProxyFromDeviceRequest &msg) {} void APIConnection::zwave_proxy_subscribe(const ZWaveProxySubscribeRequest &msg) {
zwave_proxy::global_zwave_proxy->subscribe_api_connection(this, msg.flags);
}
void APIConnection::zwave_proxy_unsubscribe(const ZWaveProxyUnsubscribeRequest &msg) {
zwave_proxy::global_zwave_proxy->unsubscribe_api_connection(this);
}
void APIConnection::zwave_proxy_from_device(const ZWaveProxyFromDeviceRequest &msg) {
this->send_message(msg, ZWaveProxyFromDeviceRequest::MESSAGE_TYPE);
}
void APIConnection::zwave_proxy_to_device(const ZWaveProxyToDeviceRequest &msg) { void APIConnection::zwave_proxy_to_device(const ZWaveProxyToDeviceRequest &msg) {
zwave_proxy::global_zwave_proxy->send_frame(msg.data); zwave_proxy::global_zwave_proxy->send_frame(msg.data);
} }

View File

@@ -172,6 +172,8 @@ class APIConnection final : public APIServerConnection {
#endif #endif
#ifdef USE_ZWAVE_PROXY #ifdef USE_ZWAVE_PROXY
void zwave_proxy_subscribe(const ZWaveProxySubscribeRequest &msg) override;
void zwave_proxy_unsubscribe(const ZWaveProxyUnsubscribeRequest &msg) override;
void zwave_proxy_from_device(const ZWaveProxyFromDeviceRequest &msg) override; void zwave_proxy_from_device(const ZWaveProxyFromDeviceRequest &msg) override;
void zwave_proxy_to_device(const ZWaveProxyToDeviceRequest &msg) override; void zwave_proxy_to_device(const ZWaveProxyToDeviceRequest &msg) override;
#endif #endif

View File

@@ -3008,8 +3008,16 @@ bool UpdateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
} }
#endif #endif
#ifdef USE_ZWAVE_PROXY #ifdef USE_ZWAVE_PROXY
void ZWaveProxyFromDeviceResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->data_ref_); } bool ZWaveProxyFromDeviceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
void ZWaveProxyFromDeviceResponse::calculate_size(ProtoSize &size) const { size.add_length(1, this->data_ref_.size()); } switch (field_id) {
case 1:
this->data = value.as_string();
break;
default:
return false;
}
return true;
}
bool ZWaveProxyToDeviceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { bool ZWaveProxyToDeviceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) { switch (field_id) {
case 1: case 1:
@@ -3020,6 +3028,16 @@ bool ZWaveProxyToDeviceRequest::decode_length(uint32_t field_id, ProtoLengthDeli
} }
return true; return true;
} }
bool ZWaveProxySubscribeRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->flags = value.as_uint32();
break;
default:
return false;
}
return true;
}
#endif #endif
} // namespace esphome::api } // namespace esphome::api

View File

@@ -2914,30 +2914,28 @@ class UpdateCommandRequest final : public CommandProtoMessage {
}; };
#endif #endif
#ifdef USE_ZWAVE_PROXY #ifdef USE_ZWAVE_PROXY
class ZWaveProxyFromDeviceRequest final : public ProtoMessage { class ZWaveProxyFromDeviceRequest final : public ProtoDecodableMessage {
public: public:
static constexpr uint8_t MESSAGE_TYPE = 128; static constexpr uint8_t MESSAGE_TYPE = 128;
static constexpr uint8_t ESTIMATED_SIZE = 0; static constexpr uint8_t ESTIMATED_SIZE = 9;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "z_wave_proxy_from_device_request"; } const char *message_name() const override { return "z_wave_proxy_from_device_request"; }
#endif #endif
std::string data{};
#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;
}; };
class ZWaveProxyFromDeviceResponse final : public ProtoMessage { class ZWaveProxyFromDeviceResponse final : public ProtoMessage {
public: public:
static constexpr uint8_t MESSAGE_TYPE = 129; static constexpr uint8_t MESSAGE_TYPE = 129;
static constexpr uint8_t ESTIMATED_SIZE = 9; static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "z_wave_proxy_from_device_response"; } const char *message_name() const override { return "z_wave_proxy_from_device_response"; }
#endif #endif
StringRef data_ref_{};
void set_data(const StringRef &ref) { this->data_ref_ = ref; }
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
@@ -2972,6 +2970,34 @@ class ZWaveProxyToDeviceResponse final : public ProtoMessage {
protected: protected:
}; };
class ZWaveProxySubscribeRequest final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 132;
static constexpr uint8_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "z_wave_proxy_subscribe_request"; }
#endif
uint32_t flags{0};
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ZWaveProxyUnsubscribeRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 133;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "z_wave_proxy_unsubscribe_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
#endif #endif
} // namespace esphome::api } // namespace esphome::api

View File

@@ -2101,10 +2101,12 @@ void UpdateCommandRequest::dump_to(std::string &out) const {
} }
#endif #endif
#ifdef USE_ZWAVE_PROXY #ifdef USE_ZWAVE_PROXY
void ZWaveProxyFromDeviceRequest::dump_to(std::string &out) const { out.append("ZWaveProxyFromDeviceRequest {}"); } void ZWaveProxyFromDeviceRequest::dump_to(std::string &out) const { dump_field(out, "data", this->data); }
void ZWaveProxyFromDeviceResponse::dump_to(std::string &out) const { dump_field(out, "data", this->data_ref_); } void ZWaveProxyFromDeviceResponse::dump_to(std::string &out) const { out.append("ZWaveProxyFromDeviceResponse {}"); }
void ZWaveProxyToDeviceRequest::dump_to(std::string &out) const { dump_field(out, "data", this->data); } void ZWaveProxyToDeviceRequest::dump_to(std::string &out) const { dump_field(out, "data", this->data); }
void ZWaveProxyToDeviceResponse::dump_to(std::string &out) const { out.append("ZWaveProxyToDeviceResponse {}"); } void ZWaveProxyToDeviceResponse::dump_to(std::string &out) const { out.append("ZWaveProxyToDeviceResponse {}"); }
void ZWaveProxySubscribeRequest::dump_to(std::string &out) const { dump_field(out, "flags", this->flags); }
void ZWaveProxyUnsubscribeRequest::dump_to(std::string &out) const { out.append("ZWaveProxyUnsubscribeRequest {}"); }
#endif #endif
} // namespace esphome::api } // namespace esphome::api

View File

@@ -599,7 +599,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_ZWAVE_PROXY #ifdef USE_ZWAVE_PROXY
case ZWaveProxyFromDeviceRequest::MESSAGE_TYPE: { case ZWaveProxyFromDeviceRequest::MESSAGE_TYPE: {
ZWaveProxyFromDeviceRequest msg; ZWaveProxyFromDeviceRequest msg;
// Empty message: no decode needed msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_z_wave_proxy_from_device_request: %s", msg.dump().c_str()); ESP_LOGVV(TAG, "on_z_wave_proxy_from_device_request: %s", msg.dump().c_str());
#endif #endif
@@ -617,6 +617,28 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
this->on_z_wave_proxy_to_device_request(msg); this->on_z_wave_proxy_to_device_request(msg);
break; break;
} }
#endif
#ifdef USE_ZWAVE_PROXY
case ZWaveProxySubscribeRequest::MESSAGE_TYPE: {
ZWaveProxySubscribeRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_z_wave_proxy_subscribe_request: %s", msg.dump().c_str());
#endif
this->on_z_wave_proxy_subscribe_request(msg);
break;
}
#endif
#ifdef USE_ZWAVE_PROXY
case ZWaveProxyUnsubscribeRequest::MESSAGE_TYPE: {
ZWaveProxyUnsubscribeRequest msg;
// Empty message: no decode needed
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_z_wave_proxy_unsubscribe_request: %s", msg.dump().c_str());
#endif
this->on_z_wave_proxy_unsubscribe_request(msg);
break;
}
#endif #endif
default: default:
break; break;
@@ -932,6 +954,20 @@ void APIServerConnection::on_alarm_control_panel_command_request(const AlarmCont
} }
#endif #endif
#ifdef USE_ZWAVE_PROXY #ifdef USE_ZWAVE_PROXY
void APIServerConnection::on_z_wave_proxy_subscribe_request(const ZWaveProxySubscribeRequest &msg) {
if (this->check_authenticated_()) {
this->zwave_proxy_subscribe(msg);
}
}
#endif
#ifdef USE_ZWAVE_PROXY
void APIServerConnection::on_z_wave_proxy_unsubscribe_request(const ZWaveProxyUnsubscribeRequest &msg) {
if (this->check_authenticated_()) {
this->zwave_proxy_unsubscribe(msg);
}
}
#endif
#ifdef USE_ZWAVE_PROXY
void APIServerConnection::on_z_wave_proxy_from_device_request(const ZWaveProxyFromDeviceRequest &msg) { void APIServerConnection::on_z_wave_proxy_from_device_request(const ZWaveProxyFromDeviceRequest &msg) {
if (this->check_authenticated_()) { if (this->check_authenticated_()) {
this->zwave_proxy_from_device(msg); this->zwave_proxy_from_device(msg);

View File

@@ -214,6 +214,12 @@ class APIServerConnectionBase : public ProtoService {
virtual void on_z_wave_proxy_to_device_request(const ZWaveProxyToDeviceRequest &value){}; virtual void on_z_wave_proxy_to_device_request(const ZWaveProxyToDeviceRequest &value){};
#endif #endif
#ifdef USE_ZWAVE_PROXY
virtual void on_z_wave_proxy_subscribe_request(const ZWaveProxySubscribeRequest &value){};
#endif
#ifdef USE_ZWAVE_PROXY
virtual void on_z_wave_proxy_unsubscribe_request(const ZWaveProxyUnsubscribeRequest &value){};
#endif
protected: protected:
void read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; void read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
}; };
@@ -341,6 +347,12 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_ALARM_CONTROL_PANEL #ifdef USE_ALARM_CONTROL_PANEL
virtual void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) = 0; virtual void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) = 0;
#endif #endif
#ifdef USE_ZWAVE_PROXY
virtual void zwave_proxy_subscribe(const ZWaveProxySubscribeRequest &msg) = 0;
#endif
#ifdef USE_ZWAVE_PROXY
virtual void zwave_proxy_unsubscribe(const ZWaveProxyUnsubscribeRequest &msg) = 0;
#endif
#ifdef USE_ZWAVE_PROXY #ifdef USE_ZWAVE_PROXY
virtual void zwave_proxy_from_device(const ZWaveProxyFromDeviceRequest &msg) = 0; virtual void zwave_proxy_from_device(const ZWaveProxyFromDeviceRequest &msg) = 0;
#endif #endif
@@ -469,6 +481,12 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_ALARM_CONTROL_PANEL #ifdef USE_ALARM_CONTROL_PANEL
void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override; void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override;
#endif #endif
#ifdef USE_ZWAVE_PROXY
void on_z_wave_proxy_subscribe_request(const ZWaveProxySubscribeRequest &msg) override;
#endif
#ifdef USE_ZWAVE_PROXY
void on_z_wave_proxy_unsubscribe_request(const ZWaveProxyUnsubscribeRequest &msg) override;
#endif
#ifdef USE_ZWAVE_PROXY #ifdef USE_ZWAVE_PROXY
void on_z_wave_proxy_from_device_request(const ZWaveProxyFromDeviceRequest &msg) override; void on_z_wave_proxy_from_device_request(const ZWaveProxyFromDeviceRequest &msg) override;
#endif #endif

View File

@@ -3,7 +3,7 @@ from esphome.components import uart
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ["uart"] DEPENDENCIES = ["api", "uart"]
zwave_proxy_ns = cg.esphome_ns.namespace("zwave_proxy") zwave_proxy_ns = cg.esphome_ns.namespace("zwave_proxy")
ZWaveProxy = zwave_proxy_ns.class_("ZWaveProxy", cg.Component, uart.UARTDevice) ZWaveProxy = zwave_proxy_ns.class_("ZWaveProxy", cg.Component, uart.UARTDevice)

View File

@@ -28,8 +28,11 @@ void ZWaveProxy::loop() {
return; return;
} }
if (this->parse_byte_(byte)) { if (this->parse_byte_(byte)) {
// TODO: send frame to client(s)... this->outgoing_request_.data = std::string((const char *) this->buffer_, this->buffer_index_);
ESP_LOGD(TAG, "Frame complete"); ESP_LOGD(TAG, "Sending frame...%s", YESNO(this->api_connection_ != nullptr));
if (this->api_connection_ != nullptr) {
this->api_connection_->send_message(this->outgoing_request_, api::ZWaveProxyFromDeviceRequest::MESSAGE_TYPE);
}
} }
} }
this->status_clear_warning(); this->status_clear_warning();
@@ -37,6 +40,22 @@ void ZWaveProxy::loop() {
void ZWaveProxy::dump_config() { ESP_LOGCONFIG(TAG, "Z-Wave Proxy"); } void ZWaveProxy::dump_config() { ESP_LOGCONFIG(TAG, "Z-Wave Proxy"); }
void ZWaveProxy::subscribe_api_connection(api::APIConnection *api_connection, uint32_t flags) {
if (this->api_connection_ != nullptr) {
ESP_LOGE(TAG, "Only one API subscription is allowed at a time");
return;
}
this->api_connection_ = api_connection;
}
void ZWaveProxy::unsubscribe_api_connection(api::APIConnection *api_connection) {
if (this->api_connection_ != api_connection) {
ESP_LOGV(TAG, "API connection is not subscribed");
return;
}
this->api_connection_ = nullptr;
}
void ZWaveProxy::send_frame(const std::string &data) { void ZWaveProxy::send_frame(const std::string &data) {
ESP_LOGD(TAG, "Sending: %s", format_hex_pretty(data).c_str()); ESP_LOGD(TAG, "Sending: %s", format_hex_pretty(data).c_str());
this->write_array((uint8_t *) data.data(), data.size()); this->write_array((uint8_t *) data.data(), data.size());

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include "esphome/components/api/api_connection.h"
#include "esphome/components/api/api_pb2.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/components/uart/uart.h" #include "esphome/components/uart/uart.h"
@@ -37,6 +39,10 @@ class ZWaveProxy : public uart::UARTDevice, public Component {
void loop() override; void loop() override;
void dump_config() override; void dump_config() override;
void subscribe_api_connection(api::APIConnection *api_connection, uint32_t flags);
void unsubscribe_api_connection(api::APIConnection *api_connection);
api::APIConnection *get_api_connection() { return this->api_connection_; }
uint32_t get_feature_flags() const { return ZWaveProxyFeature::FEATURE_ZWAVE_PROXY_ENABLED; } uint32_t get_feature_flags() const { return ZWaveProxyFeature::FEATURE_ZWAVE_PROXY_ENABLED; }
void send_frame(const std::string &data); void send_frame(const std::string &data);
@@ -47,11 +53,16 @@ class ZWaveProxy : public uart::UARTDevice, public Component {
void parse_start_(uint8_t byte); void parse_start_(uint8_t byte);
bool response_handler_(); bool response_handler_();
api::APIConnection *api_connection_{nullptr}; // Current subscribed client
uint8_t buffer_[257]; // Fixed buffer for incoming data: max length = 255 + 2 (start of frame and checksum) 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 buffer_index_{0}; // Index for populating the data buffer
uint8_t checksum_{0}; // Checksum of the frame being parsed uint8_t checksum_{0}; // Checksum of the frame being parsed
uint8_t end_frame_after_{0}; // Payload reception ends after this index uint8_t end_frame_after_{0}; // Payload reception ends after this index
ZWaveParsingState parsing_state_{ZWAVE_PARSING_STATE_WAIT_START}; ZWaveParsingState parsing_state_{ZWAVE_PARSING_STATE_WAIT_START};
// Pre-allocated response message - always ready to send
api::ZWaveProxyFromDeviceRequest outgoing_request_;
}; };
extern ZWaveProxy *global_zwave_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) extern ZWaveProxy *global_zwave_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)