mirror of
https://github.com/esphome/esphome.git
synced 2026-09-17 10:08:40 +00:00
[store_yaml] Report store_yaml support in DeviceCapabilitiesResponse instead of DeviceInfoResponse
This commit is contained in:
@@ -334,11 +334,6 @@ message DeviceInfoResponse {
|
||||
// all-zeros PSK, so the api encryption key can be provisioned without being
|
||||
// sent in plaintext (protects against passive sniffing, not active MITM)
|
||||
bool api_encryption_provisionable = 26 [(field_ifdef) = "USE_API_NOISE"];
|
||||
|
||||
// Whether this firmware embeds its YAML configuration for recovery via
|
||||
// `get_yaml`. Clients use this to skip the request entirely when the
|
||||
// device cannot answer it instead of waiting for a timeout.
|
||||
bool has_store_yaml = 27 [(field_ifdef) = "USE_STORE_YAML"];
|
||||
}
|
||||
|
||||
// ==================== DEVICE CAPABILITIES ====================
|
||||
@@ -387,6 +382,12 @@ message ZWaveProxyCapabilities {
|
||||
uint32 home_id = 2;
|
||||
}
|
||||
|
||||
message StoreYamlCapabilities {
|
||||
// Whether this firmware embeds its YAML configuration for recovery via
|
||||
// `get_yaml`, so clients can skip the request instead of waiting for a timeout
|
||||
bool supported = 1;
|
||||
}
|
||||
|
||||
message DeviceCapabilitiesResponse {
|
||||
option (id) = 150;
|
||||
option (source) = SOURCE_SERVER;
|
||||
@@ -396,6 +397,7 @@ message DeviceCapabilitiesResponse {
|
||||
ZWaveProxyCapabilities zwave_proxy = 3 [(field_ifdef) = "USE_ZWAVE_PROXY"];
|
||||
repeated SerialProxyInfo serial_proxies = 4
|
||||
[(field_ifdef) = "USE_SERIAL_PROXY", (fixed_array_size_define) = "SERIAL_PROXY_COUNT"];
|
||||
StoreYamlCapabilities store_yaml = 5 [(field_ifdef) = "USE_STORE_YAML"];
|
||||
}
|
||||
|
||||
message ListEntitiesRequest {
|
||||
|
||||
@@ -2002,12 +2002,6 @@ bool APIConnection::send_device_info_response_() {
|
||||
#ifdef USE_DEEP_SLEEP
|
||||
resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
// Compile-time knowledge: codegen always embeds a non-empty blob when the
|
||||
// component is compiled in. Deriving this from the runtime pointer could
|
||||
// report false to a client connecting before store_yaml's setup() ran.
|
||||
resp.has_store_yaml = true;
|
||||
#endif
|
||||
#ifdef ESPHOME_PROJECT_NAME
|
||||
#ifdef USE_ESP8266
|
||||
static const char PROJECT_NAME_PROGMEM[] PROGMEM = ESPHOME_PROJECT_NAME;
|
||||
@@ -2112,6 +2106,12 @@ bool APIConnection::send_device_capabilities_response_() {
|
||||
info.port_type = proxy->get_port_type();
|
||||
info.configured_line_states = proxy->get_configured_modem_pins();
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
// Compile-time knowledge: codegen always embeds a non-empty blob when the
|
||||
// component is compiled in. Deriving this from the runtime pointer could
|
||||
// report false to a client connecting before store_yaml's setup() ran.
|
||||
resp.store_yaml.supported = true;
|
||||
#endif
|
||||
return this->send_message(resp);
|
||||
}
|
||||
|
||||
@@ -175,9 +175,6 @@ uint8_t *DeviceInfoResponse::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 26, this->api_encryption_provisionable);
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 27, this->has_store_yaml);
|
||||
#endif
|
||||
return pos;
|
||||
}
|
||||
@@ -243,9 +240,6 @@ uint32_t DeviceInfoResponse::calculate_size() const {
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
size += ProtoSize::calc_bool(2, this->api_encryption_provisionable);
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
size += ProtoSize::calc_bool(2, this->has_store_yaml);
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
@@ -289,6 +283,18 @@ uint32_t ZWaveProxyCapabilities::calculate_size() const {
|
||||
return size;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
uint8_t *StoreYamlCapabilities::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
|
||||
uint8_t *__restrict__ pos = buffer.get_pos();
|
||||
ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 1, this->supported);
|
||||
return pos;
|
||||
}
|
||||
uint32_t StoreYamlCapabilities::calculate_size() const {
|
||||
uint32_t size = 0;
|
||||
size += ProtoSize::calc_bool(1, this->supported);
|
||||
return size;
|
||||
}
|
||||
#endif
|
||||
uint8_t *DeviceCapabilitiesResponse::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
|
||||
uint8_t *__restrict__ pos = buffer.get_pos();
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
@@ -304,6 +310,9 @@ uint8_t *DeviceCapabilitiesResponse::encode(ProtoWriteBuffer &buffer PROTO_ENCOD
|
||||
for (const auto &it : this->serial_proxies) {
|
||||
ProtoEncode::encode_sub_message(pos PROTO_ENCODE_DEBUG_ARG, buffer, 4, it);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
ProtoEncode::encode_optional_sub_message(pos PROTO_ENCODE_DEBUG_ARG, buffer, 5, this->store_yaml);
|
||||
#endif
|
||||
return pos;
|
||||
}
|
||||
@@ -322,6 +331,9 @@ uint32_t DeviceCapabilitiesResponse::calculate_size() const {
|
||||
for (const auto &it : this->serial_proxies) {
|
||||
size += ProtoSize::calc_message_force(1, it.calculate_size());
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
size += ProtoSize::calc_message(1, this->store_yaml.calculate_size());
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -554,7 +554,7 @@ class SerialProxyInfo final : public ProtoMessage {
|
||||
class DeviceInfoResponse final : public ProtoMessage {
|
||||
public:
|
||||
static constexpr uint16_t MESSAGE_TYPE = 10;
|
||||
static constexpr uint16_t ESTIMATED_SIZE = 315;
|
||||
static constexpr uint16_t ESTIMATED_SIZE = 312;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const LogString *message_name() const override { return LOG_STR("device_info_response"); }
|
||||
#endif
|
||||
@@ -612,9 +612,6 @@ class DeviceInfoResponse final : public ProtoMessage {
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
bool api_encryption_provisionable{false};
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
bool has_store_yaml{false};
|
||||
#endif
|
||||
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const;
|
||||
uint32_t calculate_size() const;
|
||||
@@ -665,10 +662,23 @@ class ZWaveProxyCapabilities final : public ProtoMessage {
|
||||
protected:
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
class StoreYamlCapabilities final : public ProtoMessage {
|
||||
public:
|
||||
bool supported{false};
|
||||
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const;
|
||||
uint32_t calculate_size() const;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
#endif
|
||||
class DeviceCapabilitiesResponse final : public ProtoMessage {
|
||||
public:
|
||||
static constexpr uint16_t MESSAGE_TYPE = 150;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 102;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 119;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const LogString *message_name() const override { return LOG_STR("device_capabilities_response"); }
|
||||
#endif
|
||||
@@ -683,6 +693,9 @@ class DeviceCapabilitiesResponse final : public ProtoMessage {
|
||||
#endif
|
||||
#ifdef USE_SERIAL_PROXY
|
||||
std::array<SerialProxyInfo, SERIAL_PROXY_COUNT> serial_proxies{};
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
StoreYamlCapabilities store_yaml{};
|
||||
#endif
|
||||
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const;
|
||||
uint32_t calculate_size() const;
|
||||
|
||||
@@ -1020,9 +1020,6 @@ const char *DeviceInfoResponse::dump_to(DumpBuffer &out) const {
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
dump_field(out, ESPHOME_PSTR("api_encryption_provisionable"), this->api_encryption_provisionable);
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
dump_field(out, ESPHOME_PSTR("has_store_yaml"), this->has_store_yaml);
|
||||
#endif
|
||||
return out.c_str();
|
||||
}
|
||||
@@ -1049,6 +1046,13 @@ const char *ZWaveProxyCapabilities::dump_to(DumpBuffer &out) const {
|
||||
return out.c_str();
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
const char *StoreYamlCapabilities::dump_to(DumpBuffer &out) const {
|
||||
MessageDumpHelper helper(out, ESPHOME_PSTR("StoreYamlCapabilities"));
|
||||
dump_field(out, ESPHOME_PSTR("supported"), this->supported);
|
||||
return out.c_str();
|
||||
}
|
||||
#endif
|
||||
const char *DeviceCapabilitiesResponse::dump_to(DumpBuffer &out) const {
|
||||
MessageDumpHelper helper(out, ESPHOME_PSTR("DeviceCapabilitiesResponse"));
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
@@ -1072,6 +1076,11 @@ const char *DeviceCapabilitiesResponse::dump_to(DumpBuffer &out) const {
|
||||
it.dump_to(out);
|
||||
out.append("\n");
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_STORE_YAML
|
||||
out.append(2, ' ').append_p(ESPHOME_PSTR("store_yaml")).append(": ");
|
||||
this->store_yaml.dump_to(out);
|
||||
out.append("\n");
|
||||
#endif
|
||||
return out.c_str();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ from .types import RunCompiledFunction
|
||||
# Message IDs from esphome/components/api/api.proto.
|
||||
HELLO_REQUEST = 1
|
||||
HELLO_RESPONSE = 2
|
||||
DEVICE_CAPABILITIES_REQUEST = 149
|
||||
DEVICE_CAPABILITIES_RESPONSE = 150
|
||||
GET_YAML_REQUEST = 154
|
||||
GET_YAML_RESPONSE = 155
|
||||
|
||||
@@ -90,6 +92,30 @@ def _parse_get_yaml_response(payload: bytes) -> tuple[bytes, bool, int, str]:
|
||||
return data, done, total_size, encoding
|
||||
|
||||
|
||||
def _parse_store_yaml_supported(payload: bytes) -> bool:
|
||||
"""Read `store_yaml.supported` (field 5, field 1) from `DeviceCapabilitiesResponse`."""
|
||||
pos = 0
|
||||
while pos < len(payload):
|
||||
tag, pos = _read_varint(payload, pos)
|
||||
wire_type = tag & 0x07
|
||||
if wire_type == 0:
|
||||
_, pos = _read_varint(payload, pos)
|
||||
continue
|
||||
assert wire_type == 2, f"unexpected wire type {wire_type}"
|
||||
length, pos = _read_varint(payload, pos)
|
||||
chunk = payload[pos : pos + length]
|
||||
pos += length
|
||||
if tag >> 3 != 5:
|
||||
continue
|
||||
sub_pos = 0
|
||||
while sub_pos < len(chunk):
|
||||
sub_tag, sub_pos = _read_varint(chunk, sub_pos)
|
||||
value, sub_pos = _read_varint(chunk, sub_pos)
|
||||
if sub_tag == 0x08:
|
||||
return bool(value)
|
||||
return False
|
||||
|
||||
|
||||
async def _read_varint_from(reader: asyncio.StreamReader) -> int:
|
||||
"""Read a protobuf varint byte-by-byte from a stream."""
|
||||
result = 0
|
||||
@@ -159,6 +185,16 @@ async def test_store_yaml_recovery(
|
||||
msg_type, _ = await asyncio.wait_for(client.recv(), timeout=5.0)
|
||||
assert msg_type == HELLO_RESPONSE, f"expected HelloResponse, got {msg_type}"
|
||||
|
||||
# Clients learn the device can answer get_yaml from its capabilities.
|
||||
await client.send(DEVICE_CAPABILITIES_REQUEST, b"")
|
||||
while True:
|
||||
msg_type, payload = await asyncio.wait_for(client.recv(), timeout=5.0)
|
||||
if msg_type == DEVICE_CAPABILITIES_RESPONSE:
|
||||
break
|
||||
assert _parse_store_yaml_supported(payload), (
|
||||
"expected DeviceCapabilitiesResponse to report store_yaml.supported"
|
||||
)
|
||||
|
||||
# The actual request under test.
|
||||
await client.send(GET_YAML_REQUEST, b"")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user