mirror of
https://github.com/esphome/esphome.git
synced 2026-10-09 04:16:01 +00:00
Merge branch 'beta' into dev
This commit is contained in:
@@ -107,6 +107,89 @@ std::optional<size_t> parse_record_slot(const std::string &key) {
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
|
||||
// The reason passed to on_pairing_failed automations, in the protocol's own spelling.
|
||||
StringRef pair_abort_reason_to_string(sendspin::SendspinPairAbortReason reason) {
|
||||
using sendspin::SendspinPairAbortReason;
|
||||
switch (reason) {
|
||||
case SendspinPairAbortReason::ATTEMPT_TIMEOUT:
|
||||
return StringRef::from_lit("attempt_timeout");
|
||||
case SendspinPairAbortReason::CONCURRENT_ATTEMPT:
|
||||
return StringRef::from_lit("concurrent_attempt");
|
||||
case SendspinPairAbortReason::METHOD_NOT_SUPPORTED:
|
||||
return StringRef::from_lit("method_not_supported");
|
||||
case SendspinPairAbortReason::PAIRING_CODE_MISMATCH:
|
||||
return StringRef::from_lit("pairing_code_mismatch");
|
||||
case SendspinPairAbortReason::USER_CANCELLED:
|
||||
return StringRef::from_lit("user_cancelled");
|
||||
case SendspinPairAbortReason::UNKNOWN:
|
||||
break;
|
||||
}
|
||||
return StringRef::from_lit("unknown");
|
||||
}
|
||||
|
||||
// Zeroes through a volatile pointer so dead-store elimination cannot drop it.
|
||||
void secure_wipe(void *data, size_t len) {
|
||||
volatile auto *p = static_cast<volatile uint8_t *>(data);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
p[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Call once per key, from setup().
|
||||
ESPPreferenceObject make_blob_pref(const std::string &name, size_t size) {
|
||||
return global_preferences->make_preference(size, fnv1a_hash("sendspin_" + name));
|
||||
}
|
||||
|
||||
// Loads straight into the returned buffer, which the library wipes after use. A failed load can
|
||||
// leave part of a stored private key or PSK behind, so that path wipes it here. This only covers
|
||||
// this buffer: the preference backend keeps its own copy of a pending write until it syncs.
|
||||
std::optional<std::vector<uint8_t>> read_blob(ESPPreferenceObject &pref, size_t size) {
|
||||
std::vector<uint8_t> out(size);
|
||||
if (!pref.load(out.data(), out.size())) {
|
||||
secure_wipe(out.data(), out.size());
|
||||
return std::nullopt;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// The library always writes a key's fixed size; any other length is misuse and is not stored.
|
||||
bool write_blob(ESPPreferenceObject &pref, const char *key, size_t size, const uint8_t *data, size_t len) {
|
||||
if (len != size) {
|
||||
ESP_LOGW(TAG, "\"%s\" blob of %zu bytes does not match its size of %zu; rejecting write", key, len, size);
|
||||
return false;
|
||||
}
|
||||
if (!pref.save(data, len)) {
|
||||
ESP_LOGW(TAG, "Failed to persist \"%s\" blob (%zu bytes)", key, len);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// "rec_<n>" to n; nullopt for any other key or a slot with no storage here.
|
||||
std::optional<size_t> parse_record_slot(const std::string &key) {
|
||||
const char *const prefix = sendspin::persistence_keys::RECORD_SLOT_PREFIX;
|
||||
const size_t prefix_len = std::strlen(prefix);
|
||||
if (key.size() <= prefix_len || key.compare(0, prefix_len, prefix) != 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
size_t slot = 0;
|
||||
for (size_t i = prefix_len; i < key.size(); i++) {
|
||||
const char c = key[i];
|
||||
if (c < '0' || c > '9') {
|
||||
return std::nullopt;
|
||||
}
|
||||
slot = slot * 10 + static_cast<size_t>(c - '0');
|
||||
if (slot >= SENDSPIN_RECORD_SLOTS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef USE_SENDSPIN_ARTWORK
|
||||
// Indexed by the library enums, which start at zero and are contiguous.
|
||||
static const char *const IMAGE_SOURCE_NAMES[] = {"ALBUM", "ARTIST", "NONE"};
|
||||
|
||||
Reference in New Issue
Block a user