mirror of
https://github.com/esphome/esphome.git
synced 2026-02-12 20:47:34 -07:00
Compare commits
5 Commits
api-string
...
20260210-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0ea2cbe93 | ||
|
|
f7d03ab381 | ||
|
|
41f344c0e2 | ||
|
|
686f59eb48 | ||
|
|
587ea23864 |
@@ -429,6 +429,7 @@ esphome/components/sen21231/* @shreyaskarnik
|
|||||||
esphome/components/sen5x/* @martgras
|
esphome/components/sen5x/* @martgras
|
||||||
esphome/components/sensirion_common/* @martgras
|
esphome/components/sensirion_common/* @martgras
|
||||||
esphome/components/sensor/* @esphome/core
|
esphome/components/sensor/* @esphome/core
|
||||||
|
esphome/components/serial_proxy/* @kbx81
|
||||||
esphome/components/sfa30/* @ghsensdev
|
esphome/components/sfa30/* @ghsensdev
|
||||||
esphome/components/sgp40/* @SenexCrenshaw
|
esphome/components/sgp40/* @SenexCrenshaw
|
||||||
esphome/components/sgp4x/* @martgras @SenexCrenshaw
|
esphome/components/sgp4x/* @martgras @SenexCrenshaw
|
||||||
|
|||||||
@@ -57,14 +57,8 @@ def maybe_conf(conf, *validators):
|
|||||||
return validate
|
return validate
|
||||||
|
|
||||||
|
|
||||||
def register_action(
|
def register_action(name: str, action_type: MockObjClass, schema: cv.Schema):
|
||||||
name: str,
|
return ACTION_REGISTRY.register(name, action_type, schema)
|
||||||
action_type: MockObjClass,
|
|
||||||
schema: cv.Schema,
|
|
||||||
*,
|
|
||||||
deferred: bool = False,
|
|
||||||
):
|
|
||||||
return ACTION_REGISTRY.register(name, action_type, schema, deferred=deferred)
|
|
||||||
|
|
||||||
|
|
||||||
def register_condition(name: str, condition_type: MockObjClass, schema: cv.Schema):
|
def register_condition(name: str, condition_type: MockObjClass, schema: cv.Schema):
|
||||||
@@ -341,10 +335,7 @@ async def component_is_idle_condition_to_code(
|
|||||||
|
|
||||||
|
|
||||||
@register_action(
|
@register_action(
|
||||||
"delay",
|
"delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds)
|
||||||
DelayAction,
|
|
||||||
cv.templatable(cv.positive_time_period_milliseconds),
|
|
||||||
deferred=True,
|
|
||||||
)
|
)
|
||||||
async def delay_action_to_code(
|
async def delay_action_to_code(
|
||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
@@ -454,7 +445,7 @@ _validate_wait_until = cv.maybe_simple_value(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@register_action("wait_until", WaitUntilAction, _validate_wait_until, deferred=True)
|
@register_action("wait_until", WaitUntilAction, _validate_wait_until)
|
||||||
async def wait_until_action_to_code(
|
async def wait_until_action_to_code(
|
||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
action_id: ID,
|
action_id: ID,
|
||||||
@@ -587,26 +578,6 @@ async def build_condition_list(
|
|||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
|
|
||||||
def has_deferred_actions(actions: ConfigType) -> bool:
|
|
||||||
"""Check if a validated action list contains any deferred actions.
|
|
||||||
|
|
||||||
Deferred actions (delay, wait_until, script.wait) store trigger args
|
|
||||||
for later execution, making non-owning types like StringRef unsafe.
|
|
||||||
"""
|
|
||||||
if isinstance(actions, list):
|
|
||||||
return any(has_deferred_actions(item) for item in actions)
|
|
||||||
if isinstance(actions, dict):
|
|
||||||
for key in actions:
|
|
||||||
if key in ACTION_REGISTRY and ACTION_REGISTRY[key].deferred:
|
|
||||||
return True
|
|
||||||
return any(
|
|
||||||
has_deferred_actions(v)
|
|
||||||
for v in actions.values()
|
|
||||||
if isinstance(v, (list, dict))
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
async def build_automation(
|
async def build_automation(
|
||||||
trigger: MockObj, args: TemplateArgsType, config: ConfigType
|
trigger: MockObj, args: TemplateArgsType, config: ConfigType
|
||||||
) -> MockObj:
|
) -> MockObj:
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ SERVICE_ARG_NATIVE_TYPES: dict[str, MockObj] = {
|
|||||||
"bool": cg.bool_,
|
"bool": cg.bool_,
|
||||||
"int": cg.int32,
|
"int": cg.int32,
|
||||||
"float": cg.float_,
|
"float": cg.float_,
|
||||||
"string": cg.StringRef,
|
"string": cg.std_string,
|
||||||
"bool[]": cg.FixedVector.template(cg.bool_).operator("const").operator("ref"),
|
"bool[]": cg.FixedVector.template(cg.bool_).operator("const").operator("ref"),
|
||||||
"int[]": cg.FixedVector.template(cg.int32).operator("const").operator("ref"),
|
"int[]": cg.FixedVector.template(cg.int32).operator("const").operator("ref"),
|
||||||
"float[]": cg.FixedVector.template(cg.float_).operator("const").operator("ref"),
|
"float[]": cg.FixedVector.template(cg.float_).operator("const").operator("ref"),
|
||||||
@@ -380,16 +380,9 @@ async def to_code(config: ConfigType) -> None:
|
|||||||
if is_optional:
|
if is_optional:
|
||||||
func_args.append((cg.bool_, "return_response"))
|
func_args.append((cg.bool_, "return_response"))
|
||||||
|
|
||||||
# Check if action chain has deferred actions that would make
|
|
||||||
# non-owning StringRef dangle (rx_buf_ reused after delay)
|
|
||||||
has_deferred = automation.has_deferred_actions(conf.get(CONF_THEN, []))
|
|
||||||
|
|
||||||
service_arg_names: list[str] = []
|
service_arg_names: list[str] = []
|
||||||
for name, var_ in conf[CONF_VARIABLES].items():
|
for name, var_ in conf[CONF_VARIABLES].items():
|
||||||
native = SERVICE_ARG_NATIVE_TYPES[var_]
|
native = SERVICE_ARG_NATIVE_TYPES[var_]
|
||||||
# Fall back to std::string for string args if deferred actions exist
|
|
||||||
if has_deferred and native is cg.StringRef:
|
|
||||||
native = cg.std_string
|
|
||||||
service_template_args.append(native)
|
service_template_args.append(native)
|
||||||
func_args.append((native, name))
|
func_args.append((native, name))
|
||||||
service_arg_names.append(name)
|
service_arg_names.append(name)
|
||||||
|
|||||||
@@ -69,6 +69,12 @@ service APIConnection {
|
|||||||
rpc zwave_proxy_request(ZWaveProxyRequest) returns (void) {}
|
rpc zwave_proxy_request(ZWaveProxyRequest) returns (void) {}
|
||||||
|
|
||||||
rpc infrared_rf_transmit_raw_timings(InfraredRFTransmitRawTimingsRequest) returns (void) {}
|
rpc infrared_rf_transmit_raw_timings(InfraredRFTransmitRawTimingsRequest) returns (void) {}
|
||||||
|
|
||||||
|
rpc serial_proxy_configure(SerialProxyConfigureRequest) returns (void) {}
|
||||||
|
rpc serial_proxy_write(SerialProxyWriteRequest) returns (void) {}
|
||||||
|
rpc serial_proxy_set_modem_pins(SerialProxySetModemPinsRequest) returns (void) {}
|
||||||
|
rpc serial_proxy_get_modem_pins(SerialProxyGetModemPinsRequest) returns (void) {}
|
||||||
|
rpc serial_proxy_request(SerialProxyRequest) returns (void) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -198,6 +204,17 @@ message DeviceInfo {
|
|||||||
uint32 area_id = 3;
|
uint32 area_id = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum SerialProxyPortType {
|
||||||
|
SERIAL_PROXY_PORT_TYPE_TTL = 0;
|
||||||
|
SERIAL_PROXY_PORT_TYPE_RS232 = 1;
|
||||||
|
SERIAL_PROXY_PORT_TYPE_RS485 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SerialProxyInfo {
|
||||||
|
string name = 1; // Human-readable port name
|
||||||
|
SerialProxyPortType port_type = 2; // Port type (RS232, RS485)
|
||||||
|
}
|
||||||
|
|
||||||
message DeviceInfoResponse {
|
message DeviceInfoResponse {
|
||||||
option (id) = 10;
|
option (id) = 10;
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
@@ -260,6 +277,9 @@ message DeviceInfoResponse {
|
|||||||
// Indicates if Z-Wave proxy support is available and features supported
|
// Indicates if Z-Wave proxy support is available and features supported
|
||||||
uint32 zwave_proxy_feature_flags = 23 [(field_ifdef) = "USE_ZWAVE_PROXY"];
|
uint32 zwave_proxy_feature_flags = 23 [(field_ifdef) = "USE_ZWAVE_PROXY"];
|
||||||
uint32 zwave_home_id = 24 [(field_ifdef) = "USE_ZWAVE_PROXY"];
|
uint32 zwave_home_id = 24 [(field_ifdef) = "USE_ZWAVE_PROXY"];
|
||||||
|
|
||||||
|
// Serial proxy instance metadata
|
||||||
|
repeated SerialProxyInfo serial_proxies = 25 [(field_ifdef) = "USE_SERIAL_PROXY", (fixed_array_size_define) = "SERIAL_PROXY_COUNT"];
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListEntitiesRequest {
|
message ListEntitiesRequest {
|
||||||
@@ -824,7 +844,7 @@ message HomeAssistantStateResponse {
|
|||||||
option (ifdef) = "USE_API_HOMEASSISTANT_STATES";
|
option (ifdef) = "USE_API_HOMEASSISTANT_STATES";
|
||||||
|
|
||||||
string entity_id = 1;
|
string entity_id = 1;
|
||||||
string state = 2 [(null_terminate) = true];
|
string state = 2;
|
||||||
string attribute = 3;
|
string attribute = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -882,7 +902,7 @@ message ExecuteServiceArgument {
|
|||||||
bool bool_ = 1;
|
bool bool_ = 1;
|
||||||
int32 legacy_int = 2;
|
int32 legacy_int = 2;
|
||||||
float float_ = 3;
|
float float_ = 3;
|
||||||
string string_ = 4 [(null_terminate) = true];
|
string string_ = 4;
|
||||||
// ESPHome 1.14 (api v1.3) make int a signed value
|
// ESPHome 1.14 (api v1.3) make int a signed value
|
||||||
sint32 int_ = 5;
|
sint32 int_ = 5;
|
||||||
repeated bool bool_array = 6 [packed=false, (fixed_vector) = true];
|
repeated bool bool_array = 6 [packed=false, (fixed_vector) = true];
|
||||||
@@ -2488,3 +2508,92 @@ message InfraredRFReceiveEvent {
|
|||||||
fixed32 key = 2; // Key identifying the receiver instance
|
fixed32 key = 2; // Key identifying the receiver instance
|
||||||
repeated sint32 timings = 3 [packed = true, (container_pointer_no_template) = "std::vector<int32_t>"]; // Raw timings in microseconds (zigzag-encoded): alternating mark/space periods
|
repeated sint32 timings = 3 [packed = true, (container_pointer_no_template) = "std::vector<int32_t>"]; // Raw timings in microseconds (zigzag-encoded): alternating mark/space periods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== SERIAL PROXY ====================
|
||||||
|
|
||||||
|
enum SerialProxyParity {
|
||||||
|
SERIAL_PROXY_PARITY_NONE = 0;
|
||||||
|
SERIAL_PROXY_PARITY_EVEN = 1;
|
||||||
|
SERIAL_PROXY_PARITY_ODD = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure UART parameters for a serial proxy instance
|
||||||
|
message SerialProxyConfigureRequest {
|
||||||
|
option (id) = 138;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_SERIAL_PROXY";
|
||||||
|
|
||||||
|
uint32 instance = 1; // Instance index (0-based)
|
||||||
|
uint32 baudrate = 2; // Baud rate in bits per second
|
||||||
|
bool flow_control = 3; // Enable hardware flow control
|
||||||
|
SerialProxyParity parity = 4; // Parity setting
|
||||||
|
uint32 stop_bits = 5; // Number of stop bits (1 or 2)
|
||||||
|
uint32 data_size = 6; // Number of data bits (5-8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data received from a serial device, forwarded to clients
|
||||||
|
message SerialProxyDataReceived {
|
||||||
|
option (id) = 139;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_SERIAL_PROXY";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
uint32 instance = 1; // Instance index (0-based)
|
||||||
|
bytes data = 2; // Raw data received from the serial device
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write data to a serial device
|
||||||
|
message SerialProxyWriteRequest {
|
||||||
|
option (id) = 140;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_SERIAL_PROXY";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
uint32 instance = 1; // Instance index (0-based)
|
||||||
|
bytes data = 2; // Raw data to write to the serial device
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set modem control pin states (RTS and DTR)
|
||||||
|
message SerialProxySetModemPinsRequest {
|
||||||
|
option (id) = 141;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_SERIAL_PROXY";
|
||||||
|
|
||||||
|
uint32 instance = 1; // Instance index (0-based)
|
||||||
|
bool rts = 2; // Desired RTS pin state
|
||||||
|
bool dtr = 3; // Desired DTR pin state
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request current modem control pin states
|
||||||
|
message SerialProxyGetModemPinsRequest {
|
||||||
|
option (id) = 142;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_SERIAL_PROXY";
|
||||||
|
|
||||||
|
uint32 instance = 1; // Instance index (0-based)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response with current modem control pin states
|
||||||
|
message SerialProxyGetModemPinsResponse {
|
||||||
|
option (id) = 143;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_SERIAL_PROXY";
|
||||||
|
|
||||||
|
uint32 instance = 1; // Instance index (0-based)
|
||||||
|
bool rts = 2; // Current RTS pin state
|
||||||
|
bool dtr = 3; // Current DTR pin state
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SerialProxyRequestType {
|
||||||
|
SERIAL_PROXY_REQUEST_TYPE_FLUSH = 0; // Flush the serial port (block until all TX data is sent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic request message for simple serial proxy operations
|
||||||
|
message SerialProxyRequest {
|
||||||
|
option (id) = 144;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_SERIAL_PROXY";
|
||||||
|
|
||||||
|
uint32 instance = 1; // Instance index (0-based)
|
||||||
|
SerialProxyRequestType type = 2; // Request type
|
||||||
|
}
|
||||||
|
|||||||
@@ -1413,6 +1413,73 @@ void APIConnection::send_infrared_rf_receive_event(const InfraredRFReceiveEvent
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
void APIConnection::on_serial_proxy_configure_request(const SerialProxyConfigureRequest &msg) {
|
||||||
|
auto &proxies = App.get_serial_proxies();
|
||||||
|
if (msg.instance >= proxies.size()) {
|
||||||
|
ESP_LOGW(TAG, "Serial proxy instance %u out of range (max %u)", msg.instance,
|
||||||
|
static_cast<uint32_t>(proxies.size()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
proxies[msg.instance]->configure(msg.baudrate, msg.flow_control, static_cast<uint8_t>(msg.parity), msg.stop_bits,
|
||||||
|
msg.data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APIConnection::on_serial_proxy_write_request(const SerialProxyWriteRequest &msg) {
|
||||||
|
auto &proxies = App.get_serial_proxies();
|
||||||
|
if (msg.instance >= proxies.size()) {
|
||||||
|
ESP_LOGW(TAG, "Serial proxy instance %u out of range", msg.instance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
proxies[msg.instance]->write(msg.data, msg.data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APIConnection::on_serial_proxy_set_modem_pins_request(const SerialProxySetModemPinsRequest &msg) {
|
||||||
|
auto &proxies = App.get_serial_proxies();
|
||||||
|
if (msg.instance >= proxies.size()) {
|
||||||
|
ESP_LOGW(TAG, "Serial proxy instance %u out of range", msg.instance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
proxies[msg.instance]->set_modem_pins(msg.rts, msg.dtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APIConnection::on_serial_proxy_get_modem_pins_request(const SerialProxyGetModemPinsRequest &msg) {
|
||||||
|
auto &proxies = App.get_serial_proxies();
|
||||||
|
if (msg.instance >= proxies.size()) {
|
||||||
|
ESP_LOGW(TAG, "Serial proxy instance %u out of range", msg.instance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool rts, dtr;
|
||||||
|
proxies[msg.instance]->get_modem_pins(rts, dtr);
|
||||||
|
|
||||||
|
SerialProxyGetModemPinsResponse resp{};
|
||||||
|
resp.instance = msg.instance;
|
||||||
|
resp.rts = rts;
|
||||||
|
resp.dtr = dtr;
|
||||||
|
this->send_message(resp, SerialProxyGetModemPinsResponse::MESSAGE_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APIConnection::on_serial_proxy_request(const SerialProxyRequest &msg) {
|
||||||
|
auto &proxies = App.get_serial_proxies();
|
||||||
|
if (msg.instance >= proxies.size()) {
|
||||||
|
ESP_LOGW(TAG, "Serial proxy instance %u out of range", msg.instance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (msg.type) {
|
||||||
|
case enums::SERIAL_PROXY_REQUEST_TYPE_FLUSH:
|
||||||
|
proxies[msg.instance]->flush_port();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGW(TAG, "Unknown serial proxy request type: %u", static_cast<uint32_t>(msg.type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APIConnection::send_serial_proxy_data(const SerialProxyDataReceived &msg) {
|
||||||
|
this->send_message(msg, SerialProxyDataReceived::MESSAGE_TYPE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_INFRARED
|
#ifdef USE_INFRARED
|
||||||
uint16_t APIConnection::try_send_infrared_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
uint16_t APIConnection::try_send_infrared_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
||||||
auto *infrared = static_cast<infrared::Infrared *>(entity);
|
auto *infrared = static_cast<infrared::Infrared *>(entity);
|
||||||
@@ -1627,6 +1694,16 @@ bool APIConnection::send_device_info_response_() {
|
|||||||
resp.zwave_proxy_feature_flags = zwave_proxy::global_zwave_proxy->get_feature_flags();
|
resp.zwave_proxy_feature_flags = zwave_proxy::global_zwave_proxy->get_feature_flags();
|
||||||
resp.zwave_home_id = zwave_proxy::global_zwave_proxy->get_home_id();
|
resp.zwave_home_id = zwave_proxy::global_zwave_proxy->get_home_id();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
size_t serial_proxy_index = 0;
|
||||||
|
for (auto const &proxy : App.get_serial_proxies()) {
|
||||||
|
if (serial_proxy_index >= SERIAL_PROXY_COUNT)
|
||||||
|
break;
|
||||||
|
auto &info = resp.serial_proxies[serial_proxy_index++];
|
||||||
|
info.name = StringRef(proxy->get_name());
|
||||||
|
info.port_type = proxy->get_port_type();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
resp.api_encryption_supported = true;
|
resp.api_encryption_supported = true;
|
||||||
#endif
|
#endif
|
||||||
@@ -1683,18 +1760,31 @@ void APIConnection::on_home_assistant_state_response(const HomeAssistantStateRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto &it : this->parent_->get_state_subs()) {
|
for (auto &it : this->parent_->get_state_subs()) {
|
||||||
if (msg.entity_id != it.entity_id) {
|
// Compare entity_id: check length matches and content matches
|
||||||
|
size_t entity_id_len = strlen(it.entity_id);
|
||||||
|
if (entity_id_len != msg.entity_id.size() ||
|
||||||
|
memcmp(it.entity_id, msg.entity_id.c_str(), msg.entity_id.size()) != 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare attribute: either both have matching attribute, or both have none
|
// Compare attribute: either both have matching attribute, or both have none
|
||||||
// it.attribute can be nullptr (meaning no attribute filter)
|
size_t sub_attr_len = it.attribute != nullptr ? strlen(it.attribute) : 0;
|
||||||
if (it.attribute != nullptr ? msg.attribute != it.attribute : !msg.attribute.empty()) {
|
if (sub_attr_len != msg.attribute.size() ||
|
||||||
|
(sub_attr_len > 0 && memcmp(it.attribute, msg.attribute.c_str(), sub_attr_len) != 0)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// msg.state is already null-terminated in-place after protobuf decode
|
// Create null-terminated state for callback (parse_number needs null-termination)
|
||||||
it.callback(msg.state);
|
// HA state max length is 255 characters, but attributes can be much longer
|
||||||
|
// Use stack buffer for common case (states), heap fallback for large attributes
|
||||||
|
size_t state_len = msg.state.size();
|
||||||
|
SmallBufferWithHeapFallback<MAX_STATE_LEN + 1> state_buf_alloc(state_len + 1);
|
||||||
|
char *state_buf = reinterpret_cast<char *>(state_buf_alloc.get());
|
||||||
|
if (state_len > 0) {
|
||||||
|
memcpy(state_buf, msg.state.c_str(), state_len);
|
||||||
|
}
|
||||||
|
state_buf[state_len] = '\0';
|
||||||
|
it.callback(StringRef(state_buf, state_len));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -182,6 +182,15 @@ class APIConnection final : public APIServerConnectionBase {
|
|||||||
void send_infrared_rf_receive_event(const InfraredRFReceiveEvent &msg);
|
void send_infrared_rf_receive_event(const InfraredRFReceiveEvent &msg);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
void on_serial_proxy_configure_request(const SerialProxyConfigureRequest &msg) override;
|
||||||
|
void on_serial_proxy_write_request(const SerialProxyWriteRequest &msg) override;
|
||||||
|
void on_serial_proxy_set_modem_pins_request(const SerialProxySetModemPinsRequest &msg) override;
|
||||||
|
void on_serial_proxy_get_modem_pins_request(const SerialProxyGetModemPinsRequest &msg) override;
|
||||||
|
void on_serial_proxy_request(const SerialProxyRequest &msg) override;
|
||||||
|
void send_serial_proxy_data(const SerialProxyDataReceived &msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
void send_event(event::Event *event);
|
void send_event(event::Event *event);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -201,10 +201,9 @@ APIError APINoiseFrameHelper::try_read_frame_() {
|
|||||||
return (state_ == State::DATA) ? APIError::BAD_DATA_PACKET : APIError::BAD_HANDSHAKE_PACKET_LEN;
|
return (state_ == State::DATA) ? APIError::BAD_DATA_PACKET : APIError::BAD_HANDSHAKE_PACKET_LEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserve space for body (+1 for null terminator so protobuf StringRef fields
|
// Reserve space for body
|
||||||
// can be safely null-terminated in-place after decode)
|
if (this->rx_buf_.size() != msg_size) {
|
||||||
if (this->rx_buf_.size() != msg_size + 1) {
|
this->rx_buf_.resize(msg_size);
|
||||||
this->rx_buf_.resize(msg_size + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rx_buf_len_ < msg_size) {
|
if (rx_buf_len_ < msg_size) {
|
||||||
|
|||||||
@@ -163,10 +163,9 @@ APIError APIPlaintextFrameHelper::try_read_frame_() {
|
|||||||
}
|
}
|
||||||
// header reading done
|
// header reading done
|
||||||
|
|
||||||
// Reserve space for body (+1 for null terminator so protobuf StringRef fields
|
// Reserve space for body
|
||||||
// can be safely null-terminated in-place after decode)
|
if (this->rx_buf_.size() != this->rx_header_parsed_len_) {
|
||||||
if (this->rx_buf_.size() != this->rx_header_parsed_len_ + 1) {
|
this->rx_buf_.resize(this->rx_header_parsed_len_);
|
||||||
this->rx_buf_.resize(this->rx_header_parsed_len_ + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rx_buf_len_ < rx_header_parsed_len_) {
|
if (rx_buf_len_ < rx_header_parsed_len_) {
|
||||||
|
|||||||
@@ -90,13 +90,4 @@ extend google.protobuf.FieldOptions {
|
|||||||
// - uint16_t <field>_length_{0};
|
// - uint16_t <field>_length_{0};
|
||||||
// - uint16_t <field>_count_{0};
|
// - uint16_t <field>_count_{0};
|
||||||
optional bool packed_buffer = 50015 [default=false];
|
optional bool packed_buffer = 50015 [default=false];
|
||||||
|
|
||||||
// null_terminate: Write a null byte after string data in the decode buffer.
|
|
||||||
// When set on a string field in a SOURCE_CLIENT (decodable) message, the
|
|
||||||
// generated decode() override writes '\0' at data[length] after decoding.
|
|
||||||
// This makes the StringRef safe for c_str() usage without copying.
|
|
||||||
// Safe because: (1) frame helpers reserve +1 byte in rx_buf_, and
|
|
||||||
// (2) the overwritten byte was already consumed during decode.
|
|
||||||
// Only mark fields that actually need null-terminated access.
|
|
||||||
optional bool null_terminate = 50016 [default=false];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,16 @@ void DeviceInfo::calculate_size(ProtoSize &size) const {
|
|||||||
size.add_uint32(1, this->area_id);
|
size.add_uint32(1, this->area_id);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
void SerialProxyInfo::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_string(1, this->name);
|
||||||
|
buffer.encode_uint32(2, static_cast<uint32_t>(this->port_type));
|
||||||
|
}
|
||||||
|
void SerialProxyInfo::calculate_size(ProtoSize &size) const {
|
||||||
|
size.add_length(1, this->name.size());
|
||||||
|
size.add_uint32(1, static_cast<uint32_t>(this->port_type));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_string(2, this->name);
|
buffer.encode_string(2, this->name);
|
||||||
buffer.encode_string(3, this->mac_address);
|
buffer.encode_string(3, this->mac_address);
|
||||||
@@ -119,6 +129,11 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
#ifdef USE_ZWAVE_PROXY
|
#ifdef USE_ZWAVE_PROXY
|
||||||
buffer.encode_uint32(24, this->zwave_home_id);
|
buffer.encode_uint32(24, this->zwave_home_id);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
for (const auto &it : this->serial_proxies) {
|
||||||
|
buffer.encode_message(25, it);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
void DeviceInfoResponse::calculate_size(ProtoSize &size) const {
|
void DeviceInfoResponse::calculate_size(ProtoSize &size) const {
|
||||||
size.add_length(1, this->name.size());
|
size.add_length(1, this->name.size());
|
||||||
@@ -174,6 +189,11 @@ void DeviceInfoResponse::calculate_size(ProtoSize &size) const {
|
|||||||
#ifdef USE_ZWAVE_PROXY
|
#ifdef USE_ZWAVE_PROXY
|
||||||
size.add_uint32(2, this->zwave_home_id);
|
size.add_uint32(2, this->zwave_home_id);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
for (const auto &it : this->serial_proxies) {
|
||||||
|
size.add_message_object_force(2, it);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
|
void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
@@ -953,12 +973,6 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void HomeAssistantStateResponse::decode(const uint8_t *buffer, size_t length) {
|
|
||||||
ProtoDecodableMessage::decode(buffer, length);
|
|
||||||
if (!this->state.empty()) {
|
|
||||||
const_cast<char *>(this->state.c_str())[this->state.size()] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
bool GetTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
bool GetTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
@@ -1063,9 +1077,6 @@ void ExecuteServiceArgument::decode(const uint8_t *buffer, size_t length) {
|
|||||||
uint32_t count_string_array = ProtoDecodableMessage::count_repeated_field(buffer, length, 9);
|
uint32_t count_string_array = ProtoDecodableMessage::count_repeated_field(buffer, length, 9);
|
||||||
this->string_array.init(count_string_array);
|
this->string_array.init(count_string_array);
|
||||||
ProtoDecodableMessage::decode(buffer, length);
|
ProtoDecodableMessage::decode(buffer, length);
|
||||||
if (!this->string_.empty()) {
|
|
||||||
const_cast<char *>(this->string_.c_str())[this->string_.size()] = '\0';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bool ExecuteServiceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool ExecuteServiceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
@@ -3449,5 +3460,111 @@ void InfraredRFReceiveEvent::calculate_size(ProtoSize &size) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
bool SerialProxyConfigureRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1:
|
||||||
|
this->instance = value.as_uint32();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this->baudrate = value.as_uint32();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
this->flow_control = value.as_bool();
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
this->parity = static_cast<enums::SerialProxyParity>(value.as_uint32());
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
this->stop_bits = value.as_uint32();
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
this->data_size = value.as_uint32();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void SerialProxyDataReceived::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_uint32(1, this->instance);
|
||||||
|
buffer.encode_bytes(2, this->data_ptr_, this->data_len_);
|
||||||
|
}
|
||||||
|
void SerialProxyDataReceived::calculate_size(ProtoSize &size) const {
|
||||||
|
size.add_uint32(1, this->instance);
|
||||||
|
size.add_length(1, this->data_len_);
|
||||||
|
}
|
||||||
|
bool SerialProxyWriteRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1:
|
||||||
|
this->instance = value.as_uint32();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool SerialProxyWriteRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->data = value.data();
|
||||||
|
this->data_len = value.size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool SerialProxySetModemPinsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1:
|
||||||
|
this->instance = value.as_uint32();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this->rts = value.as_bool();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
this->dtr = value.as_bool();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool SerialProxyGetModemPinsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1:
|
||||||
|
this->instance = value.as_uint32();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void SerialProxyGetModemPinsResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_uint32(1, this->instance);
|
||||||
|
buffer.encode_bool(2, this->rts);
|
||||||
|
buffer.encode_bool(3, this->dtr);
|
||||||
|
}
|
||||||
|
void SerialProxyGetModemPinsResponse::calculate_size(ProtoSize &size) const {
|
||||||
|
size.add_uint32(1, this->instance);
|
||||||
|
size.add_bool(1, this->rts);
|
||||||
|
size.add_bool(1, this->dtr);
|
||||||
|
}
|
||||||
|
bool SerialProxyRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1:
|
||||||
|
this->instance = value.as_uint32();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this->type = static_cast<enums::SerialProxyRequestType>(value.as_uint32());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace esphome::api
|
} // namespace esphome::api
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ namespace esphome::api {
|
|||||||
|
|
||||||
namespace enums {
|
namespace enums {
|
||||||
|
|
||||||
|
enum SerialProxyPortType : uint32_t {
|
||||||
|
SERIAL_PROXY_PORT_TYPE_TTL = 0,
|
||||||
|
SERIAL_PROXY_PORT_TYPE_RS232 = 1,
|
||||||
|
SERIAL_PROXY_PORT_TYPE_RS485 = 2,
|
||||||
|
};
|
||||||
enum EntityCategory : uint32_t {
|
enum EntityCategory : uint32_t {
|
||||||
ENTITY_CATEGORY_NONE = 0,
|
ENTITY_CATEGORY_NONE = 0,
|
||||||
ENTITY_CATEGORY_CONFIG = 1,
|
ENTITY_CATEGORY_CONFIG = 1,
|
||||||
@@ -311,6 +316,16 @@ enum ZWaveProxyRequestType : uint32_t {
|
|||||||
ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE = 2,
|
ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE = 2,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
enum SerialProxyParity : uint32_t {
|
||||||
|
SERIAL_PROXY_PARITY_NONE = 0,
|
||||||
|
SERIAL_PROXY_PARITY_EVEN = 1,
|
||||||
|
SERIAL_PROXY_PARITY_ODD = 2,
|
||||||
|
};
|
||||||
|
enum SerialProxyRequestType : uint32_t {
|
||||||
|
SERIAL_PROXY_REQUEST_TYPE_FLUSH = 0,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace enums
|
} // namespace enums
|
||||||
|
|
||||||
@@ -471,10 +486,24 @@ class DeviceInfo final : public ProtoMessage {
|
|||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
class SerialProxyInfo final : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
StringRef name{};
|
||||||
|
enums::SerialProxyPortType port_type{};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
void calculate_size(ProtoSize &size) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *dump_to(DumpBuffer &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
#endif
|
||||||
class DeviceInfoResponse final : public ProtoMessage {
|
class DeviceInfoResponse final : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 10;
|
static constexpr uint8_t MESSAGE_TYPE = 10;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 255;
|
static constexpr uint16_t ESTIMATED_SIZE = 309;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "device_info_response"; }
|
const char *message_name() const override { return "device_info_response"; }
|
||||||
#endif
|
#endif
|
||||||
@@ -526,6 +555,9 @@ class DeviceInfoResponse final : public ProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_ZWAVE_PROXY
|
#ifdef USE_ZWAVE_PROXY
|
||||||
uint32_t zwave_home_id{0};
|
uint32_t zwave_home_id{0};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
std::array<SerialProxyInfo, SERIAL_PROXY_COUNT> serial_proxies{};
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(ProtoSize &size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
@@ -1095,7 +1127,6 @@ class HomeAssistantStateResponse final : public ProtoDecodableMessage {
|
|||||||
StringRef entity_id{};
|
StringRef entity_id{};
|
||||||
StringRef state{};
|
StringRef state{};
|
||||||
StringRef attribute{};
|
StringRef attribute{};
|
||||||
void decode(const uint8_t *buffer, size_t length) override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *dump_to(DumpBuffer &out) const override;
|
const char *dump_to(DumpBuffer &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -3026,5 +3057,133 @@ class InfraredRFReceiveEvent final : public ProtoMessage {
|
|||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
class SerialProxyConfigureRequest final : public ProtoDecodableMessage {
|
||||||
|
public:
|
||||||
|
static constexpr uint8_t MESSAGE_TYPE = 138;
|
||||||
|
static constexpr uint8_t ESTIMATED_SIZE = 20;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *message_name() const override { return "serial_proxy_configure_request"; }
|
||||||
|
#endif
|
||||||
|
uint32_t instance{0};
|
||||||
|
uint32_t baudrate{0};
|
||||||
|
bool flow_control{false};
|
||||||
|
enums::SerialProxyParity parity{};
|
||||||
|
uint32_t stop_bits{0};
|
||||||
|
uint32_t data_size{0};
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *dump_to(DumpBuffer &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class SerialProxyDataReceived final : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
static constexpr uint8_t MESSAGE_TYPE = 139;
|
||||||
|
static constexpr uint8_t ESTIMATED_SIZE = 23;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *message_name() const override { return "serial_proxy_data_received"; }
|
||||||
|
#endif
|
||||||
|
uint32_t instance{0};
|
||||||
|
const uint8_t *data_ptr_{nullptr};
|
||||||
|
size_t data_len_{0};
|
||||||
|
void set_data(const uint8_t *data, size_t len) {
|
||||||
|
this->data_ptr_ = data;
|
||||||
|
this->data_len_ = len;
|
||||||
|
}
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
void calculate_size(ProtoSize &size) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *dump_to(DumpBuffer &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
class SerialProxyWriteRequest final : public ProtoDecodableMessage {
|
||||||
|
public:
|
||||||
|
static constexpr uint8_t MESSAGE_TYPE = 140;
|
||||||
|
static constexpr uint8_t ESTIMATED_SIZE = 23;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *message_name() const override { return "serial_proxy_write_request"; }
|
||||||
|
#endif
|
||||||
|
uint32_t instance{0};
|
||||||
|
const uint8_t *data{nullptr};
|
||||||
|
uint16_t data_len{0};
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *dump_to(DumpBuffer &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;
|
||||||
|
};
|
||||||
|
class SerialProxySetModemPinsRequest final : public ProtoDecodableMessage {
|
||||||
|
public:
|
||||||
|
static constexpr uint8_t MESSAGE_TYPE = 141;
|
||||||
|
static constexpr uint8_t ESTIMATED_SIZE = 8;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *message_name() const override { return "serial_proxy_set_modem_pins_request"; }
|
||||||
|
#endif
|
||||||
|
uint32_t instance{0};
|
||||||
|
bool rts{false};
|
||||||
|
bool dtr{false};
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *dump_to(DumpBuffer &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class SerialProxyGetModemPinsRequest final : public ProtoDecodableMessage {
|
||||||
|
public:
|
||||||
|
static constexpr uint8_t MESSAGE_TYPE = 142;
|
||||||
|
static constexpr uint8_t ESTIMATED_SIZE = 4;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *message_name() const override { return "serial_proxy_get_modem_pins_request"; }
|
||||||
|
#endif
|
||||||
|
uint32_t instance{0};
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *dump_to(DumpBuffer &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class SerialProxyGetModemPinsResponse final : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
static constexpr uint8_t MESSAGE_TYPE = 143;
|
||||||
|
static constexpr uint8_t ESTIMATED_SIZE = 8;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *message_name() const override { return "serial_proxy_get_modem_pins_response"; }
|
||||||
|
#endif
|
||||||
|
uint32_t instance{0};
|
||||||
|
bool rts{false};
|
||||||
|
bool dtr{false};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
void calculate_size(ProtoSize &size) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *dump_to(DumpBuffer &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
class SerialProxyRequest final : public ProtoDecodableMessage {
|
||||||
|
public:
|
||||||
|
static constexpr uint8_t MESSAGE_TYPE = 144;
|
||||||
|
static constexpr uint8_t ESTIMATED_SIZE = 6;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *message_name() const override { return "serial_proxy_request"; }
|
||||||
|
#endif
|
||||||
|
uint32_t instance{0};
|
||||||
|
enums::SerialProxyRequestType type{};
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
const char *dump_to(DumpBuffer &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace esphome::api
|
} // namespace esphome::api
|
||||||
|
|||||||
@@ -100,6 +100,18 @@ static void dump_bytes_field(DumpBuffer &out, const char *field_name, const uint
|
|||||||
out.append(hex_buf).append("\n");
|
out.append(hex_buf).append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<> const char *proto_enum_to_string<enums::SerialProxyPortType>(enums::SerialProxyPortType value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::SERIAL_PROXY_PORT_TYPE_TTL:
|
||||||
|
return "SERIAL_PROXY_PORT_TYPE_TTL";
|
||||||
|
case enums::SERIAL_PROXY_PORT_TYPE_RS232:
|
||||||
|
return "SERIAL_PROXY_PORT_TYPE_RS232";
|
||||||
|
case enums::SERIAL_PROXY_PORT_TYPE_RS485:
|
||||||
|
return "SERIAL_PROXY_PORT_TYPE_RS485";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::EntityCategory value) {
|
template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::EntityCategory value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case enums::ENTITY_CATEGORY_NONE:
|
case enums::ENTITY_CATEGORY_NONE:
|
||||||
@@ -736,6 +748,28 @@ template<> const char *proto_enum_to_string<enums::ZWaveProxyRequestType>(enums:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
template<> const char *proto_enum_to_string<enums::SerialProxyParity>(enums::SerialProxyParity value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::SERIAL_PROXY_PARITY_NONE:
|
||||||
|
return "SERIAL_PROXY_PARITY_NONE";
|
||||||
|
case enums::SERIAL_PROXY_PARITY_EVEN:
|
||||||
|
return "SERIAL_PROXY_PARITY_EVEN";
|
||||||
|
case enums::SERIAL_PROXY_PARITY_ODD:
|
||||||
|
return "SERIAL_PROXY_PARITY_ODD";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<> const char *proto_enum_to_string<enums::SerialProxyRequestType>(enums::SerialProxyRequestType value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::SERIAL_PROXY_REQUEST_TYPE_FLUSH:
|
||||||
|
return "SERIAL_PROXY_REQUEST_TYPE_FLUSH";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const char *HelloRequest::dump_to(DumpBuffer &out) const {
|
const char *HelloRequest::dump_to(DumpBuffer &out) const {
|
||||||
MessageDumpHelper helper(out, "HelloRequest");
|
MessageDumpHelper helper(out, "HelloRequest");
|
||||||
@@ -785,6 +819,14 @@ const char *DeviceInfo::dump_to(DumpBuffer &out) const {
|
|||||||
return out.c_str();
|
return out.c_str();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
const char *SerialProxyInfo::dump_to(DumpBuffer &out) const {
|
||||||
|
MessageDumpHelper helper(out, "SerialProxyInfo");
|
||||||
|
dump_field(out, "name", this->name);
|
||||||
|
dump_field(out, "port_type", static_cast<enums::SerialProxyPortType>(this->port_type));
|
||||||
|
return out.c_str();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
const char *DeviceInfoResponse::dump_to(DumpBuffer &out) const {
|
const char *DeviceInfoResponse::dump_to(DumpBuffer &out) const {
|
||||||
MessageDumpHelper helper(out, "DeviceInfoResponse");
|
MessageDumpHelper helper(out, "DeviceInfoResponse");
|
||||||
dump_field(out, "name", this->name);
|
dump_field(out, "name", this->name);
|
||||||
@@ -845,6 +887,13 @@ const char *DeviceInfoResponse::dump_to(DumpBuffer &out) const {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_ZWAVE_PROXY
|
#ifdef USE_ZWAVE_PROXY
|
||||||
dump_field(out, "zwave_home_id", this->zwave_home_id);
|
dump_field(out, "zwave_home_id", this->zwave_home_id);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
for (const auto &it : this->serial_proxies) {
|
||||||
|
out.append(" serial_proxies: ");
|
||||||
|
it.dump_to(out);
|
||||||
|
out.append("\n");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return out.c_str();
|
return out.c_str();
|
||||||
}
|
}
|
||||||
@@ -2469,6 +2518,55 @@ const char *InfraredRFReceiveEvent::dump_to(DumpBuffer &out) const {
|
|||||||
return out.c_str();
|
return out.c_str();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
const char *SerialProxyConfigureRequest::dump_to(DumpBuffer &out) const {
|
||||||
|
MessageDumpHelper helper(out, "SerialProxyConfigureRequest");
|
||||||
|
dump_field(out, "instance", this->instance);
|
||||||
|
dump_field(out, "baudrate", this->baudrate);
|
||||||
|
dump_field(out, "flow_control", this->flow_control);
|
||||||
|
dump_field(out, "parity", static_cast<enums::SerialProxyParity>(this->parity));
|
||||||
|
dump_field(out, "stop_bits", this->stop_bits);
|
||||||
|
dump_field(out, "data_size", this->data_size);
|
||||||
|
return out.c_str();
|
||||||
|
}
|
||||||
|
const char *SerialProxyDataReceived::dump_to(DumpBuffer &out) const {
|
||||||
|
MessageDumpHelper helper(out, "SerialProxyDataReceived");
|
||||||
|
dump_field(out, "instance", this->instance);
|
||||||
|
dump_bytes_field(out, "data", this->data_ptr_, this->data_len_);
|
||||||
|
return out.c_str();
|
||||||
|
}
|
||||||
|
const char *SerialProxyWriteRequest::dump_to(DumpBuffer &out) const {
|
||||||
|
MessageDumpHelper helper(out, "SerialProxyWriteRequest");
|
||||||
|
dump_field(out, "instance", this->instance);
|
||||||
|
dump_bytes_field(out, "data", this->data, this->data_len);
|
||||||
|
return out.c_str();
|
||||||
|
}
|
||||||
|
const char *SerialProxySetModemPinsRequest::dump_to(DumpBuffer &out) const {
|
||||||
|
MessageDumpHelper helper(out, "SerialProxySetModemPinsRequest");
|
||||||
|
dump_field(out, "instance", this->instance);
|
||||||
|
dump_field(out, "rts", this->rts);
|
||||||
|
dump_field(out, "dtr", this->dtr);
|
||||||
|
return out.c_str();
|
||||||
|
}
|
||||||
|
const char *SerialProxyGetModemPinsRequest::dump_to(DumpBuffer &out) const {
|
||||||
|
MessageDumpHelper helper(out, "SerialProxyGetModemPinsRequest");
|
||||||
|
dump_field(out, "instance", this->instance);
|
||||||
|
return out.c_str();
|
||||||
|
}
|
||||||
|
const char *SerialProxyGetModemPinsResponse::dump_to(DumpBuffer &out) const {
|
||||||
|
MessageDumpHelper helper(out, "SerialProxyGetModemPinsResponse");
|
||||||
|
dump_field(out, "instance", this->instance);
|
||||||
|
dump_field(out, "rts", this->rts);
|
||||||
|
dump_field(out, "dtr", this->dtr);
|
||||||
|
return out.c_str();
|
||||||
|
}
|
||||||
|
const char *SerialProxyRequest::dump_to(DumpBuffer &out) const {
|
||||||
|
MessageDumpHelper helper(out, "SerialProxyRequest");
|
||||||
|
dump_field(out, "instance", this->instance);
|
||||||
|
dump_field(out, "type", static_cast<enums::SerialProxyRequestType>(this->type));
|
||||||
|
return out.c_str();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace esphome::api
|
} // namespace esphome::api
|
||||||
|
|
||||||
|
|||||||
@@ -634,6 +634,61 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
this->on_infrared_rf_transmit_raw_timings_request(msg);
|
this->on_infrared_rf_transmit_raw_timings_request(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
case SerialProxyConfigureRequest::MESSAGE_TYPE: {
|
||||||
|
SerialProxyConfigureRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
this->log_receive_message_(LOG_STR("on_serial_proxy_configure_request"), msg);
|
||||||
|
#endif
|
||||||
|
this->on_serial_proxy_configure_request(msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
case SerialProxyWriteRequest::MESSAGE_TYPE: {
|
||||||
|
SerialProxyWriteRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
this->log_receive_message_(LOG_STR("on_serial_proxy_write_request"), msg);
|
||||||
|
#endif
|
||||||
|
this->on_serial_proxy_write_request(msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
case SerialProxySetModemPinsRequest::MESSAGE_TYPE: {
|
||||||
|
SerialProxySetModemPinsRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
this->log_receive_message_(LOG_STR("on_serial_proxy_set_modem_pins_request"), msg);
|
||||||
|
#endif
|
||||||
|
this->on_serial_proxy_set_modem_pins_request(msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
case SerialProxyGetModemPinsRequest::MESSAGE_TYPE: {
|
||||||
|
SerialProxyGetModemPinsRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
this->log_receive_message_(LOG_STR("on_serial_proxy_get_modem_pins_request"), msg);
|
||||||
|
#endif
|
||||||
|
this->on_serial_proxy_get_modem_pins_request(msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
case SerialProxyRequest::MESSAGE_TYPE: {
|
||||||
|
SerialProxyRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
this->log_receive_message_(LOG_STR("on_serial_proxy_request"), msg);
|
||||||
|
#endif
|
||||||
|
this->on_serial_proxy_request(msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -224,6 +224,23 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
virtual void on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &value){};
|
virtual void on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
virtual void on_serial_proxy_configure_request(const SerialProxyConfigureRequest &value){};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
virtual void on_serial_proxy_write_request(const SerialProxyWriteRequest &value){};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
virtual void on_serial_proxy_set_modem_pins_request(const SerialProxySetModemPinsRequest &value){};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
virtual void on_serial_proxy_get_modem_pins_request(const SerialProxyGetModemPinsRequest &value){};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
virtual void on_serial_proxy_request(const SerialProxyRequest &value){};
|
||||||
|
#endif
|
||||||
protected:
|
protected:
|
||||||
void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) override;
|
void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -382,6 +382,17 @@ void APIServer::send_infrared_rf_receive_event([[maybe_unused]] uint32_t device_
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
void APIServer::send_serial_proxy_data(uint32_t instance, const uint8_t *data, size_t len) {
|
||||||
|
SerialProxyDataReceived msg{};
|
||||||
|
msg.instance = instance;
|
||||||
|
msg.set_data(data, len);
|
||||||
|
|
||||||
|
for (auto &c : this->clients_)
|
||||||
|
c->send_serial_proxy_data(msg);
|
||||||
|
}
|
||||||
|
#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
|
||||||
|
|||||||
@@ -189,6 +189,10 @@ class APIServer : public Component,
|
|||||||
void send_infrared_rf_receive_event(uint32_t device_id, uint32_t key, const std::vector<int32_t> *timings);
|
void send_infrared_rf_receive_event(uint32_t device_id, uint32_t key, const std::vector<int32_t> *timings);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
void send_serial_proxy_data(uint32_t instance, const uint8_t *data, size_t len);
|
||||||
|
#endif
|
||||||
|
|
||||||
bool is_connected(bool state_subscription_only = false) const;
|
bool is_connected(bool state_subscription_only = false) const;
|
||||||
|
|
||||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "user_services.h"
|
#include "user_services.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/string_ref.h"
|
|
||||||
|
|
||||||
namespace esphome::api {
|
namespace esphome::api {
|
||||||
|
|
||||||
@@ -12,8 +11,6 @@ template<> int32_t get_execute_arg_value<int32_t>(const ExecuteServiceArgument &
|
|||||||
}
|
}
|
||||||
template<> float get_execute_arg_value<float>(const ExecuteServiceArgument &arg) { return arg.float_; }
|
template<> float get_execute_arg_value<float>(const ExecuteServiceArgument &arg) { return arg.float_; }
|
||||||
template<> std::string get_execute_arg_value<std::string>(const ExecuteServiceArgument &arg) { return arg.string_; }
|
template<> std::string get_execute_arg_value<std::string>(const ExecuteServiceArgument &arg) { return arg.string_; }
|
||||||
// Zero-copy StringRef version for YAML-generated services (string_ is null-terminated after decode)
|
|
||||||
template<> StringRef get_execute_arg_value<StringRef>(const ExecuteServiceArgument &arg) { return arg.string_; }
|
|
||||||
|
|
||||||
// Legacy std::vector versions for external components using custom_api_device.h - optimized with reserve
|
// Legacy std::vector versions for external components using custom_api_device.h - optimized with reserve
|
||||||
template<> std::vector<bool> get_execute_arg_value<std::vector<bool>>(const ExecuteServiceArgument &arg) {
|
template<> std::vector<bool> get_execute_arg_value<std::vector<bool>>(const ExecuteServiceArgument &arg) {
|
||||||
@@ -64,8 +61,6 @@ template<> enums::ServiceArgType to_service_arg_type<bool>() { return enums::SER
|
|||||||
template<> enums::ServiceArgType to_service_arg_type<int32_t>() { return enums::SERVICE_ARG_TYPE_INT; }
|
template<> enums::ServiceArgType to_service_arg_type<int32_t>() { return enums::SERVICE_ARG_TYPE_INT; }
|
||||||
template<> enums::ServiceArgType to_service_arg_type<float>() { return enums::SERVICE_ARG_TYPE_FLOAT; }
|
template<> enums::ServiceArgType to_service_arg_type<float>() { return enums::SERVICE_ARG_TYPE_FLOAT; }
|
||||||
template<> enums::ServiceArgType to_service_arg_type<std::string>() { return enums::SERVICE_ARG_TYPE_STRING; }
|
template<> enums::ServiceArgType to_service_arg_type<std::string>() { return enums::SERVICE_ARG_TYPE_STRING; }
|
||||||
// Zero-copy StringRef version for YAML-generated services
|
|
||||||
template<> enums::ServiceArgType to_service_arg_type<StringRef>() { return enums::SERVICE_ARG_TYPE_STRING; }
|
|
||||||
|
|
||||||
// Legacy std::vector versions for external components using custom_api_device.h
|
// Legacy std::vector versions for external components using custom_api_device.h
|
||||||
template<> enums::ServiceArgType to_service_arg_type<std::vector<bool>>() { return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; }
|
template<> enums::ServiceArgType to_service_arg_type<std::vector<bool>>() { return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; }
|
||||||
|
|||||||
@@ -219,7 +219,6 @@ async def script_stop_action_to_code(config, action_id, template_arg, args):
|
|||||||
"script.wait",
|
"script.wait",
|
||||||
ScriptWaitAction,
|
ScriptWaitAction,
|
||||||
maybe_simple_id({cv.Required(CONF_ID): cv.use_id(Script)}),
|
maybe_simple_id({cv.Required(CONF_ID): cv.use_id(Script)}),
|
||||||
deferred=True,
|
|
||||||
)
|
)
|
||||||
async def script_wait_action_to_code(config, action_id, template_arg, args):
|
async def script_wait_action_to_code(config, action_id, template_arg, args):
|
||||||
full_id, paren = await cg.get_variable_with_full_id(config[CONF_ID])
|
full_id, paren = await cg.get_variable_with_full_id(config[CONF_ID])
|
||||||
|
|||||||
80
esphome/components/serial_proxy/__init__.py
Normal file
80
esphome/components/serial_proxy/__init__.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
"""
|
||||||
|
Serial Proxy component for ESPHome.
|
||||||
|
|
||||||
|
WARNING: This component is EXPERIMENTAL. The API (both Python configuration
|
||||||
|
and C++ interfaces) may change at any time without following the normal
|
||||||
|
breaking changes policy. Use at your own risk.
|
||||||
|
|
||||||
|
Once the API is considered stable, this warning will be removed.
|
||||||
|
|
||||||
|
Provides a proxy to/from a serial interface on the ESPHome device, allowing
|
||||||
|
Home Assistant to connect to the serial port and send/receive data to/from
|
||||||
|
an arbitrary serial device.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from esphome import pins
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import uart
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import CONF_ID, CONF_NAME
|
||||||
|
|
||||||
|
CODEOWNERS = ["@kbx81"]
|
||||||
|
DEPENDENCIES = ["api", "uart"]
|
||||||
|
|
||||||
|
MULTI_CONF = True
|
||||||
|
|
||||||
|
serial_proxy_ns = cg.esphome_ns.namespace("serial_proxy")
|
||||||
|
SerialProxy = serial_proxy_ns.class_("SerialProxy", cg.Component, uart.UARTDevice)
|
||||||
|
|
||||||
|
api_enums_ns = cg.esphome_ns.namespace("api").namespace("enums")
|
||||||
|
SerialProxyPortType = api_enums_ns.enum("SerialProxyPortType")
|
||||||
|
SERIAL_PROXY_PORT_TYPES = {
|
||||||
|
"TTL": SerialProxyPortType.SERIAL_PROXY_PORT_TYPE_TTL,
|
||||||
|
"RS232": SerialProxyPortType.SERIAL_PROXY_PORT_TYPE_RS232,
|
||||||
|
"RS485": SerialProxyPortType.SERIAL_PROXY_PORT_TYPE_RS485,
|
||||||
|
}
|
||||||
|
|
||||||
|
CONF_DTR_PIN = "dtr_pin"
|
||||||
|
CONF_PORT_TYPE = "port_type"
|
||||||
|
CONF_RTS_PIN = "rts_pin"
|
||||||
|
|
||||||
|
_serial_proxy_count = []
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(SerialProxy),
|
||||||
|
cv.Required(CONF_NAME): cv.string_strict,
|
||||||
|
cv.Required(CONF_PORT_TYPE): cv.enum(SERIAL_PROXY_PORT_TYPES, upper=True),
|
||||||
|
cv.Optional(CONF_RTS_PIN): pins.gpio_output_pin_schema,
|
||||||
|
cv.Optional(CONF_DTR_PIN): pins.gpio_output_pin_schema,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.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)
|
||||||
|
cg.add(cg.App.register_serial_proxy(var))
|
||||||
|
cg.add(var.set_name(config[CONF_NAME]))
|
||||||
|
cg.add(var.set_port_type(config[CONF_PORT_TYPE]))
|
||||||
|
cg.add_define("USE_SERIAL_PROXY")
|
||||||
|
# Track instance count — last define wins, so the final value is the total count
|
||||||
|
_serial_proxy_count.append(var)
|
||||||
|
cg.add_define("SERIAL_PROXY_COUNT", len(_serial_proxy_count))
|
||||||
|
|
||||||
|
if CONF_RTS_PIN in config:
|
||||||
|
rts_pin = await cg.gpio_pin_expression(config[CONF_RTS_PIN])
|
||||||
|
cg.add(var.set_rts_pin(rts_pin))
|
||||||
|
|
||||||
|
if CONF_DTR_PIN in config:
|
||||||
|
dtr_pin = await cg.gpio_pin_expression(config[CONF_DTR_PIN])
|
||||||
|
cg.add(var.set_dtr_pin(dtr_pin))
|
||||||
|
|
||||||
|
# Request UART to wake the main loop when data arrives for low-latency processing
|
||||||
|
uart.request_wake_loop_on_rx()
|
||||||
137
esphome/components/serial_proxy/serial_proxy.cpp
Normal file
137
esphome/components/serial_proxy/serial_proxy.cpp
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
#include "serial_proxy.h"
|
||||||
|
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef USE_API
|
||||||
|
#include "esphome/components/api/api_server.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace esphome::serial_proxy {
|
||||||
|
|
||||||
|
static const char *const TAG = "serial_proxy";
|
||||||
|
|
||||||
|
void SerialProxy::setup() {
|
||||||
|
// Set up modem control pins if configured
|
||||||
|
if (this->rts_pin_ != nullptr) {
|
||||||
|
this->rts_pin_->setup();
|
||||||
|
this->rts_pin_->digital_write(this->rts_state_);
|
||||||
|
}
|
||||||
|
if (this->dtr_pin_ != nullptr) {
|
||||||
|
this->dtr_pin_->setup();
|
||||||
|
this->dtr_pin_->digital_write(this->dtr_state_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialProxy::loop() {
|
||||||
|
// Read available data from UART and forward to API clients
|
||||||
|
size_t available = this->available();
|
||||||
|
if (available == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Read in chunks up to SERIAL_PROXY_MAX_READ_SIZE
|
||||||
|
uint8_t buffer[SERIAL_PROXY_MAX_READ_SIZE];
|
||||||
|
size_t to_read = std::min(available, sizeof(buffer));
|
||||||
|
|
||||||
|
if (!this->read_array(buffer, to_read))
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef USE_API
|
||||||
|
if (api::global_api_server != nullptr) {
|
||||||
|
api::global_api_server->send_serial_proxy_data(this->instance_index_, buffer, to_read);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialProxy::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG,
|
||||||
|
"Serial Proxy [%u]:\n"
|
||||||
|
" Name: %s\n"
|
||||||
|
" Port Type: %s\n"
|
||||||
|
" RTS Pin: %s\n"
|
||||||
|
" DTR Pin: %s",
|
||||||
|
this->instance_index_, this->name_.c_str(),
|
||||||
|
this->port_type_ == api::enums::SERIAL_PROXY_PORT_TYPE_RS485 ? "RS485"
|
||||||
|
: this->port_type_ == api::enums::SERIAL_PROXY_PORT_TYPE_RS232 ? "RS232"
|
||||||
|
: "TTL",
|
||||||
|
this->rts_pin_ != nullptr ? "configured" : "not configured",
|
||||||
|
this->dtr_pin_ != nullptr ? "configured" : "not configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialProxy::configure(uint32_t baudrate, bool flow_control, uint8_t parity, uint8_t stop_bits,
|
||||||
|
uint8_t data_size) {
|
||||||
|
ESP_LOGD(TAG, "Configuring serial proxy [%u]: baud=%u, flow_ctrl=%s, parity=%u, stop=%u, data=%u",
|
||||||
|
this->instance_index_, baudrate, YESNO(flow_control), parity, stop_bits, data_size);
|
||||||
|
|
||||||
|
auto *uart_comp = this->parent_;
|
||||||
|
if (uart_comp == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "UART component not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply UART parameters
|
||||||
|
uart_comp->set_baud_rate(baudrate);
|
||||||
|
uart_comp->set_stop_bits(stop_bits);
|
||||||
|
uart_comp->set_data_bits(data_size);
|
||||||
|
|
||||||
|
// Map parity enum to UARTParityOptions
|
||||||
|
switch (parity) {
|
||||||
|
case 0:
|
||||||
|
uart_comp->set_parity(uart::UART_CONFIG_PARITY_NONE);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
uart_comp->set_parity(uart::UART_CONFIG_PARITY_EVEN);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
uart_comp->set_parity(uart::UART_CONFIG_PARITY_ODD);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGW(TAG, "Unknown parity value: %u, using NONE", parity);
|
||||||
|
uart_comp->set_parity(uart::UART_CONFIG_PARITY_NONE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the new settings
|
||||||
|
// load_settings() is available on ESP8266 and ESP32 platforms
|
||||||
|
#if defined(USE_ESP8266) || defined(USE_ESP32)
|
||||||
|
uart_comp->load_settings(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Note: Hardware flow control configuration is stored but not yet applied
|
||||||
|
// to the UART hardware - this requires additional platform support
|
||||||
|
(void) flow_control;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialProxy::write(const uint8_t *data, size_t len) {
|
||||||
|
if (data == nullptr || len == 0)
|
||||||
|
return;
|
||||||
|
this->write_array(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialProxy::set_modem_pins(bool rts, bool dtr) {
|
||||||
|
ESP_LOGV(TAG, "Setting modem pins [%u]: RTS=%s, DTR=%s", this->instance_index_, ONOFF(rts), ONOFF(dtr));
|
||||||
|
|
||||||
|
if (this->rts_pin_ != nullptr) {
|
||||||
|
this->rts_state_ = rts;
|
||||||
|
this->rts_pin_->digital_write(rts);
|
||||||
|
}
|
||||||
|
if (this->dtr_pin_ != nullptr) {
|
||||||
|
this->dtr_state_ = dtr;
|
||||||
|
this->dtr_pin_->digital_write(dtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialProxy::get_modem_pins(bool &rts, bool &dtr) const {
|
||||||
|
rts = this->rts_state_;
|
||||||
|
dtr = this->dtr_state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialProxy::flush_port() {
|
||||||
|
ESP_LOGV(TAG, "Flushing serial proxy [%u]", this->instance_index_);
|
||||||
|
this->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace esphome::serial_proxy
|
||||||
|
|
||||||
|
#endif // USE_SERIAL_PROXY
|
||||||
101
esphome/components/serial_proxy/serial_proxy.h
Normal file
101
esphome/components/serial_proxy/serial_proxy.h
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// WARNING: This component is EXPERIMENTAL. The API may change at any time
|
||||||
|
// without following the normal breaking changes policy. Use at your own risk.
|
||||||
|
// Once the API is considered stable, this warning will be removed.
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
|
||||||
|
#include "esphome/components/api/api_pb2.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/components/uart/uart.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace esphome::serial_proxy {
|
||||||
|
|
||||||
|
/// Maximum bytes to read from UART in a single loop iteration
|
||||||
|
static constexpr size_t SERIAL_PROXY_MAX_READ_SIZE = 256;
|
||||||
|
|
||||||
|
class SerialProxy : public uart::UARTDevice, public Component {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void loop() override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_CONNECTION; }
|
||||||
|
|
||||||
|
/// Get the instance index (position in Application's serial_proxies_ vector)
|
||||||
|
uint32_t get_instance_index() const { return this->instance_index_; }
|
||||||
|
|
||||||
|
/// Set the instance index (called by Application::register_serial_proxy)
|
||||||
|
void set_instance_index(uint32_t index) { this->instance_index_ = index; }
|
||||||
|
|
||||||
|
/// Set the human-readable port name (from YAML configuration)
|
||||||
|
void set_name(const std::string &name) { this->name_ = name; }
|
||||||
|
|
||||||
|
/// Get the human-readable port name
|
||||||
|
const std::string &get_name() const { return this->name_; }
|
||||||
|
|
||||||
|
/// Set the port type (from YAML configuration)
|
||||||
|
void set_port_type(api::enums::SerialProxyPortType port_type) { this->port_type_ = port_type; }
|
||||||
|
|
||||||
|
/// Get the port type
|
||||||
|
api::enums::SerialProxyPortType get_port_type() const { return this->port_type_; }
|
||||||
|
|
||||||
|
/// Configure UART parameters and apply them
|
||||||
|
/// @param baudrate Baud rate in bits per second
|
||||||
|
/// @param flow_control True to enable hardware flow control
|
||||||
|
/// @param parity Parity setting (0=none, 1=even, 2=odd)
|
||||||
|
/// @param stop_bits Number of stop bits (1 or 2)
|
||||||
|
/// @param data_size Number of data bits (5-8)
|
||||||
|
void configure(uint32_t baudrate, bool flow_control, uint8_t parity, uint8_t stop_bits, uint8_t data_size);
|
||||||
|
|
||||||
|
/// Write data to the serial device
|
||||||
|
/// @param data Pointer to data buffer
|
||||||
|
/// @param len Number of bytes to write
|
||||||
|
void write(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
/// Set modem pin states (RTS and DTR)
|
||||||
|
/// @param rts Desired RTS pin state
|
||||||
|
/// @param dtr Desired DTR pin state
|
||||||
|
void set_modem_pins(bool rts, bool dtr);
|
||||||
|
|
||||||
|
/// Get current modem pin states
|
||||||
|
/// @param[out] rts Current RTS pin state
|
||||||
|
/// @param[out] dtr Current DTR pin state
|
||||||
|
void get_modem_pins(bool &rts, bool &dtr) const;
|
||||||
|
|
||||||
|
/// Flush the serial port (block until all TX data is sent)
|
||||||
|
void flush_port();
|
||||||
|
|
||||||
|
/// Set the RTS GPIO pin (from YAML configuration)
|
||||||
|
void set_rts_pin(GPIOPin *pin) { this->rts_pin_ = pin; }
|
||||||
|
|
||||||
|
/// Set the DTR GPIO pin (from YAML configuration)
|
||||||
|
void set_dtr_pin(GPIOPin *pin) { this->dtr_pin_ = pin; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Instance index for identifying this proxy in API messages
|
||||||
|
uint32_t instance_index_{0};
|
||||||
|
|
||||||
|
/// Human-readable port name
|
||||||
|
std::string name_;
|
||||||
|
|
||||||
|
/// Port type
|
||||||
|
api::enums::SerialProxyPortType port_type_{api::enums::SERIAL_PROXY_PORT_TYPE_TTL};
|
||||||
|
|
||||||
|
/// Optional GPIO pins for modem control
|
||||||
|
GPIOPin *rts_pin_{nullptr};
|
||||||
|
GPIOPin *dtr_pin_{nullptr};
|
||||||
|
|
||||||
|
/// Current modem pin states
|
||||||
|
bool rts_state_{false};
|
||||||
|
bool dtr_state_{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esphome::serial_proxy
|
||||||
|
|
||||||
|
#endif // USE_SERIAL_PROXY
|
||||||
@@ -94,6 +94,9 @@
|
|||||||
#ifdef USE_INFRARED
|
#ifdef USE_INFRARED
|
||||||
#include "esphome/components/infrared/infrared.h"
|
#include "esphome/components/infrared/infrared.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
#include "esphome/components/serial_proxy/serial_proxy.h"
|
||||||
|
#endif
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
#include "esphome/components/event/event.h"
|
#include "esphome/components/event/event.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -234,6 +237,13 @@ class Application {
|
|||||||
void register_infrared(infrared::Infrared *infrared) { this->infrareds_.push_back(infrared); }
|
void register_infrared(infrared::Infrared *infrared) { this->infrareds_.push_back(infrared); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
void register_serial_proxy(serial_proxy::SerialProxy *proxy) {
|
||||||
|
proxy->set_instance_index(this->serial_proxies_.size());
|
||||||
|
this->serial_proxies_.push_back(proxy);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
void register_event(event::Event *event) { this->events_.push_back(event); }
|
void register_event(event::Event *event) { this->events_.push_back(event); }
|
||||||
#endif
|
#endif
|
||||||
@@ -473,6 +483,10 @@ class Application {
|
|||||||
GET_ENTITY_METHOD(infrared::Infrared, infrared, infrareds)
|
GET_ENTITY_METHOD(infrared::Infrared, infrared, infrareds)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
auto &get_serial_proxies() const { return this->serial_proxies_; }
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
auto &get_events() const { return this->events_; }
|
auto &get_events() const { return this->events_; }
|
||||||
GET_ENTITY_METHOD(event::Event, event, events)
|
GET_ENTITY_METHOD(event::Event, event, events)
|
||||||
@@ -690,6 +704,9 @@ class Application {
|
|||||||
#ifdef USE_INFRARED
|
#ifdef USE_INFRARED
|
||||||
StaticVector<infrared::Infrared *, ESPHOME_ENTITY_INFRARED_COUNT> infrareds_{};
|
StaticVector<infrared::Infrared *, ESPHOME_ENTITY_INFRARED_COUNT> infrareds_{};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SERIAL_PROXY
|
||||||
|
std::vector<serial_proxy::SerialProxy *> serial_proxies_{};
|
||||||
|
#endif
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
StaticVector<update::UpdateEntity *, ESPHOME_ENTITY_UPDATE_COUNT> updates_{};
|
StaticVector<update::UpdateEntity *, ESPHOME_ENTITY_UPDATE_COUNT> updates_{};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -99,6 +99,7 @@
|
|||||||
#define MDNS_SERVICE_COUNT 3
|
#define MDNS_SERVICE_COUNT 3
|
||||||
#define USE_MDNS_DYNAMIC_TXT
|
#define USE_MDNS_DYNAMIC_TXT
|
||||||
#define MDNS_DYNAMIC_TXT_COUNT 2
|
#define MDNS_DYNAMIC_TXT_COUNT 2
|
||||||
|
#define SERIAL_PROXY_COUNT 2
|
||||||
#define SNTP_SERVER_COUNT 3
|
#define SNTP_SERVER_COUNT 3
|
||||||
#define USE_MEDIA_PLAYER
|
#define USE_MEDIA_PLAYER
|
||||||
#define USE_NEXTION_TFT_UPLOAD
|
#define USE_NEXTION_TFT_UPLOAD
|
||||||
@@ -109,6 +110,7 @@
|
|||||||
#define USE_SAFE_MODE_CALLBACK
|
#define USE_SAFE_MODE_CALLBACK
|
||||||
#define USE_SELECT
|
#define USE_SELECT
|
||||||
#define USE_SENSOR
|
#define USE_SENSOR
|
||||||
|
#define USE_SERIAL_PROXY
|
||||||
#define USE_STATUS_LED
|
#define USE_STATUS_LED
|
||||||
#define USE_STATUS_SENSOR
|
#define USE_STATUS_SENSOR
|
||||||
#define USE_SWITCH
|
#define USE_SWITCH
|
||||||
|
|||||||
@@ -81,19 +81,6 @@ class StringRef {
|
|||||||
|
|
||||||
operator std::string() const { return str(); }
|
operator std::string() const { return str(); }
|
||||||
|
|
||||||
/// Compare with a null-terminated C string (compatible with std::string::compare)
|
|
||||||
int compare(const char *s) const {
|
|
||||||
size_t s_len = std::strlen(s);
|
|
||||||
int result = std::memcmp(base_, s, std::min(len_, s_len));
|
|
||||||
if (result != 0)
|
|
||||||
return result;
|
|
||||||
if (len_ < s_len)
|
|
||||||
return -1;
|
|
||||||
if (len_ > s_len)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find first occurrence of substring, returns std::string::npos if not found.
|
/// Find first occurrence of substring, returns std::string::npos if not found.
|
||||||
/// Note: Requires the underlying string to be null-terminated.
|
/// Note: Requires the underlying string to be null-terminated.
|
||||||
size_type find(const char *s, size_type pos = 0) const {
|
size_type find(const char *s, size_type pos = 0) const {
|
||||||
|
|||||||
@@ -24,14 +24,11 @@ class RegistryEntry:
|
|||||||
fun: Callable[..., Any],
|
fun: Callable[..., Any],
|
||||||
type_id: "MockObjClass",
|
type_id: "MockObjClass",
|
||||||
schema: "Schema",
|
schema: "Schema",
|
||||||
*,
|
|
||||||
deferred: bool = False,
|
|
||||||
):
|
):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.fun = fun
|
self.fun = fun
|
||||||
self.type_id = type_id
|
self.type_id = type_id
|
||||||
self.raw_schema = schema
|
self.raw_schema = schema
|
||||||
self.deferred = deferred
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def coroutine_fun(self):
|
def coroutine_fun(self):
|
||||||
@@ -52,16 +49,9 @@ class Registry(dict[str, RegistryEntry]):
|
|||||||
self.base_schema = base_schema or {}
|
self.base_schema = base_schema or {}
|
||||||
self.type_id_key = type_id_key
|
self.type_id_key = type_id_key
|
||||||
|
|
||||||
def register(
|
def register(self, name: str, type_id: "MockObjClass", schema: "Schema"):
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
type_id: "MockObjClass",
|
|
||||||
schema: "Schema",
|
|
||||||
*,
|
|
||||||
deferred: bool = False,
|
|
||||||
):
|
|
||||||
def decorator(fun: Callable[..., Any]):
|
def decorator(fun: Callable[..., Any]):
|
||||||
self[name] = RegistryEntry(name, fun, type_id, schema, deferred=deferred)
|
self[name] = RegistryEntry(name, fun, type_id, schema)
|
||||||
return fun
|
return fun
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|||||||
@@ -2020,8 +2020,6 @@ def build_message_type(
|
|||||||
|
|
||||||
# Collect fixed_vector fields for custom decode generation
|
# Collect fixed_vector fields for custom decode generation
|
||||||
fixed_vector_fields = []
|
fixed_vector_fields = []
|
||||||
# Collect fields with (null_terminate) = true option
|
|
||||||
null_terminate_fields = []
|
|
||||||
|
|
||||||
for field in desc.field:
|
for field in desc.field:
|
||||||
# Skip deprecated fields completely
|
# Skip deprecated fields completely
|
||||||
@@ -2064,10 +2062,6 @@ def build_message_type(
|
|||||||
|
|
||||||
ti = create_field_type_info(field, needs_decode, needs_encode)
|
ti = create_field_type_info(field, needs_decode, needs_encode)
|
||||||
|
|
||||||
# Collect fields with (null_terminate) = true for post-decode null-termination
|
|
||||||
if needs_decode and get_field_opt(field, pb.null_terminate, False):
|
|
||||||
null_terminate_fields.append(ti.field_name)
|
|
||||||
|
|
||||||
# Skip field declarations for fields that are in the base class
|
# Skip field declarations for fields that are in the base class
|
||||||
# but include their encode/decode logic
|
# but include their encode/decode logic
|
||||||
if field.name not in common_field_names:
|
if field.name not in common_field_names:
|
||||||
@@ -2174,8 +2168,8 @@ def build_message_type(
|
|||||||
prot = "bool decode_64bit(uint32_t field_id, Proto64Bit value) override;"
|
prot = "bool decode_64bit(uint32_t field_id, Proto64Bit value) override;"
|
||||||
protected_content.insert(0, prot)
|
protected_content.insert(0, prot)
|
||||||
|
|
||||||
# Generate custom decode() override for messages with FixedVector or null_terminate fields
|
# Generate custom decode() override for messages with FixedVector fields
|
||||||
if fixed_vector_fields or null_terminate_fields:
|
if fixed_vector_fields:
|
||||||
# Generate the decode() implementation in cpp
|
# Generate the decode() implementation in cpp
|
||||||
o = f"void {desc.name}::decode(const uint8_t *buffer, size_t length) {{\n"
|
o = f"void {desc.name}::decode(const uint8_t *buffer, size_t length) {{\n"
|
||||||
# Count and init each FixedVector field
|
# Count and init each FixedVector field
|
||||||
@@ -2184,13 +2178,6 @@ def build_message_type(
|
|||||||
o += f" this->{field_name}.init(count_{field_name});\n"
|
o += f" this->{field_name}.init(count_{field_name});\n"
|
||||||
# Call parent decode to populate the fields
|
# Call parent decode to populate the fields
|
||||||
o += " ProtoDecodableMessage::decode(buffer, length);\n"
|
o += " ProtoDecodableMessage::decode(buffer, length);\n"
|
||||||
# Null-terminate fields marked with (null_terminate) = true in-place.
|
|
||||||
# Safe: decode is complete, byte after string was already parsed (next field tag)
|
|
||||||
# or is the +1 reserved byte at end of rx_buf_.
|
|
||||||
for field_name in null_terminate_fields:
|
|
||||||
o += f" if (!this->{field_name}.empty()) {{\n"
|
|
||||||
o += f" const_cast<char *>(this->{field_name}.c_str())[this->{field_name}.size()] = '\\0';\n"
|
|
||||||
o += " }\n"
|
|
||||||
o += "}\n"
|
o += "}\n"
|
||||||
cpp += o
|
cpp += o
|
||||||
# Generate the decode() declaration in header (public method)
|
# Generate the decode() declaration in header (public method)
|
||||||
|
|||||||
10
tests/components/serial_proxy/common.yaml
Normal file
10
tests/components/serial_proxy/common.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
wifi:
|
||||||
|
ssid: MySSID
|
||||||
|
password: password1
|
||||||
|
|
||||||
|
api:
|
||||||
|
|
||||||
|
serial_proxy:
|
||||||
|
- id: serial_proxy_1
|
||||||
|
name: Test Serial Port
|
||||||
|
port_type: RS232
|
||||||
8
tests/components/serial_proxy/test.esp32-idf.yaml
Normal file
8
tests/components/serial_proxy/test.esp32-idf.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
substitutions:
|
||||||
|
tx_pin: GPIO4
|
||||||
|
rx_pin: GPIO5
|
||||||
|
|
||||||
|
packages:
|
||||||
|
uart: !include ../../test_build_components/common/uart/esp32-idf.yaml
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
||||||
8
tests/components/serial_proxy/test.esp8266-ard.yaml
Normal file
8
tests/components/serial_proxy/test.esp8266-ard.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
substitutions:
|
||||||
|
tx_pin: GPIO0
|
||||||
|
rx_pin: GPIO2
|
||||||
|
|
||||||
|
packages:
|
||||||
|
uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
||||||
8
tests/components/serial_proxy/test.rp2040-ard.yaml
Normal file
8
tests/components/serial_proxy/test.rp2040-ard.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
substitutions:
|
||||||
|
tx_pin: GPIO4
|
||||||
|
rx_pin: GPIO5
|
||||||
|
|
||||||
|
packages:
|
||||||
|
uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
||||||
Reference in New Issue
Block a user