ESPHome 2026.3.0
Loading...
Searching...
No Matches
helpers.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <cmath>
6#include <cstdarg>
7#include <cstdint>
8#include <cstdio>
9#include <cstring>
10#include <functional>
11#include <iterator>
12#include <limits>
13#include <memory>
14#include <span>
15#include <string>
16#include <type_traits>
17#include <vector>
18#include <concepts>
19#include <strings.h>
20
22
23#ifdef USE_ESP8266
24#include <Esp.h>
25#include <pgmspace.h>
26#endif
27
28#ifdef USE_RP2040
29#include <Arduino.h>
30#endif
31
32#ifdef USE_ESP32
33#include <esp_heap_caps.h>
34#endif
35
36#if defined(USE_ESP32)
37#include <freertos/FreeRTOS.h>
38#include <freertos/semphr.h>
39#elif defined(USE_LIBRETINY)
40#include <FreeRTOS.h>
41#include <semphr.h>
42#endif
43
44#ifdef USE_HOST
45#include <mutex>
46#endif
47
48#define HOT __attribute__((hot))
49#define ESPDEPRECATED(msg, when) __attribute__((deprecated(msg)))
50#define ESPHOME_ALWAYS_INLINE __attribute__((always_inline))
51#define PACKED __attribute__((packed))
52
53namespace esphome {
54
55// Forward declaration to avoid circular dependency with string_ref.h
56class StringRef;
57
60
61// Keep "using" even after the removal of our backports, to avoid breaking existing code.
62using std::to_string;
63using std::is_trivially_copyable;
64using std::make_unique;
65using std::enable_if_t;
66using std::clamp;
67using std::is_invocable;
68#if __cpp_lib_bit_cast >= 201806
69using std::bit_cast;
70#else
72template<
73 typename To, typename From,
74 enable_if_t<sizeof(To) == sizeof(From) && is_trivially_copyable<From>::value && is_trivially_copyable<To>::value,
75 int> = 0>
76To bit_cast(const From &src) {
77 To dst;
78 memcpy(&dst, &src, sizeof(To));
79 return dst;
80}
81#endif
82
83// clang-format off
84inline float lerp(float completion, float start, float end) = delete; // Please use std::lerp. Notice that it has different order on arguments!
85// clang-format on
86
87// std::byteswap from C++23
88template<typename T> constexpr T byteswap(T n) {
89 T m;
90 for (size_t i = 0; i < sizeof(T); i++)
91 reinterpret_cast<uint8_t *>(&m)[i] = reinterpret_cast<uint8_t *>(&n)[sizeof(T) - 1 - i];
92 return m;
93}
94template<> constexpr uint8_t byteswap(uint8_t n) { return n; }
95#ifdef USE_LIBRETINY
96// LibreTiny's Beken framework redefines __builtin_bswap functions as non-constexpr
97template<> inline uint16_t byteswap(uint16_t n) { return __builtin_bswap16(n); }
98template<> inline uint32_t byteswap(uint32_t n) { return __builtin_bswap32(n); }
99template<> inline uint64_t byteswap(uint64_t n) { return __builtin_bswap64(n); }
100template<> inline int8_t byteswap(int8_t n) { return n; }
101template<> inline int16_t byteswap(int16_t n) { return __builtin_bswap16(n); }
102template<> inline int32_t byteswap(int32_t n) { return __builtin_bswap32(n); }
103template<> inline int64_t byteswap(int64_t n) { return __builtin_bswap64(n); }
104#else
105template<> constexpr uint16_t byteswap(uint16_t n) { return __builtin_bswap16(n); }
106template<> constexpr uint32_t byteswap(uint32_t n) { return __builtin_bswap32(n); }
107template<> constexpr uint64_t byteswap(uint64_t n) { return __builtin_bswap64(n); }
108template<> constexpr int8_t byteswap(int8_t n) { return n; }
109template<> constexpr int16_t byteswap(int16_t n) { return __builtin_bswap16(n); }
110template<> constexpr int32_t byteswap(int32_t n) { return __builtin_bswap32(n); }
111template<> constexpr int64_t byteswap(int64_t n) { return __builtin_bswap64(n); }
112#endif
113
115
118
122
123template<typename T> class ConstVector {
124 public:
125 constexpr ConstVector(const T *data, size_t size) : data_(data), size_(size) {}
126
127 const constexpr T &operator[](size_t i) const { return data_[i]; }
128 constexpr size_t size() const { return size_; }
129 constexpr bool empty() const { return size_ == 0; }
130
131 protected:
132 const T *data_;
133 size_t size_;
134};
135
139template<size_t InlineSize = 8> class SmallInlineBuffer {
140 public:
141 SmallInlineBuffer() = default;
143 if (!this->is_inline_())
144 delete[] this->heap_;
145 }
146
147 // Move constructor
148 SmallInlineBuffer(SmallInlineBuffer &&other) noexcept : len_(other.len_) {
149 if (other.is_inline_()) {
150 memcpy(this->inline_, other.inline_, this->len_);
151 } else {
152 this->heap_ = other.heap_;
153 other.heap_ = nullptr;
154 }
155 other.len_ = 0;
156 }
157
158 // Move assignment
160 if (this != &other) {
161 if (!this->is_inline_())
162 delete[] this->heap_;
163 this->len_ = other.len_;
164 if (other.is_inline_()) {
165 memcpy(this->inline_, other.inline_, this->len_);
166 } else {
167 this->heap_ = other.heap_;
168 other.heap_ = nullptr;
169 }
170 other.len_ = 0;
171 }
172 return *this;
173 }
174
175 // Disable copy (would need deep copy of heap data)
178
180 void set(const uint8_t *src, size_t size) {
181 // Free existing heap allocation if switching from heap to inline or different heap size
183 delete[] this->heap_;
184 this->heap_ = nullptr; // Defensive: prevent use-after-free if logic changes
185 }
186 // Allocate new heap buffer if needed
187 if (size > InlineSize && (this->is_inline_() || size != this->len_)) {
188 this->heap_ = new uint8_t[size]; // NOLINT(cppcoreguidelines-owning-memory)
189 }
190 this->len_ = size;
191 memcpy(this->data(), src, size);
192 }
193
194 uint8_t *data() { return this->is_inline_() ? this->inline_ : this->heap_; }
195 const uint8_t *data() const { return this->is_inline_() ? this->inline_ : this->heap_; }
196 size_t size() const { return this->len_; }
197
198 protected:
199 bool is_inline_() const { return this->len_ <= InlineSize; }
200
201 size_t len_{0};
202 union {
203 uint8_t inline_[InlineSize]{}; // Zero-init ensures clean initial state
204 uint8_t *heap_;
205 };
206};
207
209template<typename T, size_t N> class StaticVector {
210 public:
211 using value_type = T;
212 using iterator = typename std::array<T, N>::iterator;
213 using const_iterator = typename std::array<T, N>::const_iterator;
214 using reverse_iterator = std::reverse_iterator<iterator>;
215 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
216
217 private:
218 std::array<T, N> data_; // intentionally not value-initialized to avoid memset
219 size_t count_{0};
220
221 public:
222 // Default constructor
223 StaticVector() = default;
224
225 // Iterator range constructor
226 template<typename InputIt> StaticVector(InputIt first, InputIt last) {
227 while (first != last && count_ < N) {
228 data_[count_++] = *first++;
229 }
230 }
231
232 // Initializer list constructor
233 StaticVector(std::initializer_list<T> init) {
234 for (const auto &val : init) {
235 if (count_ >= N)
236 break;
237 data_[count_++] = val;
238 }
239 }
240
241 // Minimal vector-compatible interface - only what we actually use
242 void push_back(const T &value) {
243 if (count_ < N) {
244 data_[count_++] = value;
245 }
246 }
247
248 // Clear all elements
249 void clear() { count_ = 0; }
250
251 // Assign from iterator range
252 template<typename InputIt> void assign(InputIt first, InputIt last) {
253 count_ = 0;
254 while (first != last && count_ < N) {
255 data_[count_++] = *first++;
256 }
257 }
258
259 // Return reference to next element and increment count (with bounds checking)
261 if (count_ >= N) {
262 // Should never happen with proper size calculation
263 // Return reference to last element to avoid crash
264 return data_[N - 1];
265 }
266 return data_[count_++];
267 }
268
269 size_t size() const { return count_; }
270 bool empty() const { return count_ == 0; }
271
272 // Direct access to underlying data
273 T *data() { return data_.data(); }
274 const T *data() const { return data_.data(); }
275
276 T &operator[](size_t i) { return data_[i]; }
277 const T &operator[](size_t i) const { return data_[i]; }
278
279 // For range-based for loops
280 iterator begin() { return data_.begin(); }
281 iterator end() { return data_.begin() + count_; }
282 const_iterator begin() const { return data_.begin(); }
283 const_iterator end() const { return data_.begin() + count_; }
284
285 // Reverse iterators
290
291 // Conversion to std::span for compatibility with span-based APIs
292 operator std::span<T>() { return std::span<T>(data_.data(), count_); }
293 operator std::span<const T>() const { return std::span<const T>(data_.data(), count_); }
294};
295
303template<typename T, size_t N> class StaticRingBuffer {
304 using index_type = std::conditional_t<(N <= 255), uint8_t, uint16_t>;
305
306 public:
307 class Iterator {
308 public:
309 Iterator(StaticRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
310 T &operator*() { return buf_->data_[(buf_->head_ + pos_) % N]; }
312 ++pos_;
313 return *this;
314 }
315 bool operator!=(const Iterator &other) const { return pos_ != other.pos_; }
316
317 private:
318 StaticRingBuffer *buf_;
319 index_type pos_;
320 };
321
323 public:
324 ConstIterator(const StaticRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
325 const T &operator*() const { return buf_->data_[(buf_->head_ + pos_) % N]; }
327 ++pos_;
328 return *this;
329 }
330 bool operator!=(const ConstIterator &other) const { return pos_ != other.pos_; }
331
332 private:
333 const StaticRingBuffer *buf_;
334 index_type pos_;
335 };
336
337 bool push(const T &value) {
338 if (this->count_ >= N) {
339 return false;
340 }
341 this->data_[this->tail_] = value;
342 this->tail_ = (this->tail_ + 1) % N;
343 ++this->count_;
344 return true;
345 }
346
347 void pop() {
348 if (this->count_ > 0) {
349 this->head_ = (this->head_ + 1) % N;
350 --this->count_;
351 }
352 }
353
354 T &front() { return this->data_[this->head_]; }
355 const T &front() const { return this->data_[this->head_]; }
356 index_type size() const { return this->count_; }
357 bool empty() const { return this->count_ == 0; }
358
359 Iterator begin() { return Iterator(this, 0); }
360 Iterator end() { return Iterator(this, this->count_); }
361 ConstIterator begin() const { return ConstIterator(this, 0); }
362 ConstIterator end() const { return ConstIterator(this, this->count_); }
363
364 protected:
365 T data_[N];
366 index_type head_{0};
367 index_type tail_{0};
368 index_type count_{0};
369};
370
374template<typename T> class FixedVector {
375 private:
376 T *data_{nullptr};
377 size_t size_{0};
378 size_t capacity_{0};
379
380 // Helper to destroy all elements without freeing memory
381 void destroy_elements_() {
382 // Only call destructors for non-trivially destructible types
383 if constexpr (!std::is_trivially_destructible<T>::value) {
384 for (size_t i = 0; i < size_; i++) {
385 data_[i].~T();
386 }
387 }
388 }
389
390 // Helper to destroy elements and free memory
391 void cleanup_() {
392 if (data_ != nullptr) {
393 destroy_elements_();
394 // Free raw memory
395 ::operator delete(data_);
396 }
397 }
398
399 // Helper to reset pointers after cleanup
400 void reset_() {
401 data_ = nullptr;
402 capacity_ = 0;
403 size_ = 0;
404 }
405
406 // Helper to assign from initializer list (shared by constructor and assignment operator)
407 void assign_from_initializer_list_(std::initializer_list<T> init_list) {
408 init(init_list.size());
409 size_t idx = 0;
410 for (const auto &item : init_list) {
411 new (data_ + idx) T(item);
412 ++idx;
413 }
414 size_ = init_list.size();
415 }
416
417 public:
418 FixedVector() = default;
419
422 FixedVector(std::initializer_list<T> init_list) { assign_from_initializer_list_(init_list); }
423
424 ~FixedVector() { cleanup_(); }
425
426 // Disable copy operations (avoid accidental expensive copies)
427 FixedVector(const FixedVector &) = delete;
429
430 // Enable move semantics (allows use in move-only containers like std::vector)
431 FixedVector(FixedVector &&other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
432 other.reset_();
433 }
434
435 // Allow conversion to std::vector
436 operator std::vector<T>() const { return {data_, data_ + size_}; }
437
438 FixedVector &operator=(FixedVector &&other) noexcept {
439 if (this != &other) {
440 // Delete our current data
441 cleanup_();
442 // Take ownership of other's data
443 data_ = other.data_;
444 size_ = other.size_;
445 capacity_ = other.capacity_;
446 // Leave other in valid empty state
447 other.reset_();
448 }
449 return *this;
450 }
451
454 FixedVector &operator=(std::initializer_list<T> init_list) {
455 cleanup_();
456 reset_();
457 assign_from_initializer_list_(init_list);
458 return *this;
459 }
460
461 // Allocate capacity - can be called multiple times to reinit
462 // IMPORTANT: After calling init(), you MUST use push_back() to add elements.
463 // Direct assignment via operator[] does NOT update the size counter.
464 void init(size_t n) {
465 cleanup_();
466 reset_();
467 if (n > 0) {
468 // Allocate raw memory without calling constructors
469 // sizeof(T) is correct here for any type T (value types, pointers, etc.)
470 // NOLINTNEXTLINE(bugprone-sizeof-expression)
471 data_ = static_cast<T *>(::operator new(n * sizeof(T)));
472 capacity_ = n;
473 }
474 }
475
476 // Clear the vector (destroy all elements, reset size to 0, keep capacity)
477 void clear() {
478 destroy_elements_();
479 size_ = 0;
480 }
481
482 // Release all memory (destroys elements and frees memory)
483 void release() {
484 cleanup_();
485 reset_();
486 }
487
491 void push_back(const T &value) {
492 if (size_ < capacity_) {
493 // Use placement new to construct the object in pre-allocated memory
494 new (&data_[size_]) T(value);
495 size_++;
496 }
497 }
498
502 void push_back(T &&value) {
503 if (size_ < capacity_) {
504 // Use placement new to move-construct the object in pre-allocated memory
505 new (&data_[size_]) T(std::move(value));
506 size_++;
507 }
508 }
509
514 template<typename... Args> T &emplace_back(Args &&...args) {
515 // Use placement new to construct the object in pre-allocated memory
516 new (&data_[size_]) T(std::forward<Args>(args)...);
517 size_++;
518 return data_[size_ - 1];
519 }
520
523 T &front() { return data_[0]; }
524 const T &front() const { return data_[0]; }
525
528 T &back() { return data_[size_ - 1]; }
529 const T &back() const { return data_[size_ - 1]; }
530
531 size_t size() const { return size_; }
532 bool empty() const { return size_ == 0; }
533 size_t capacity() const { return capacity_; }
534 bool full() const { return size_ == capacity_; }
535
538 T &operator[](size_t i) { return data_[i]; }
539 const T &operator[](size_t i) const { return data_[i]; }
540
543 T &at(size_t i) { return data_[i]; }
544 const T &at(size_t i) const { return data_[i]; }
545
546 // Iterator support for range-based for loops
547 T *begin() { return data_; }
548 T *end() { return data_ + size_; }
549 const T *begin() const { return data_; }
550 const T *end() const { return data_ + size_; }
551};
552
558template<size_t STACK_SIZE, typename T = uint8_t> class SmallBufferWithHeapFallback {
559 public:
561 if (size <= STACK_SIZE) {
562 this->buffer_ = this->stack_buffer_;
563 } else {
564 this->heap_buffer_ = new T[size];
565 this->buffer_ = this->heap_buffer_;
566 }
567 }
568 ~SmallBufferWithHeapFallback() { delete[] this->heap_buffer_; }
569
570 // Delete copy and move operations to prevent double-delete
575
576 T *get() { return this->buffer_; }
577
578 private:
579 T stack_buffer_[STACK_SIZE];
580 T *heap_buffer_{nullptr};
581 T *buffer_;
582};
583
585
588
592inline float pow10_int(int8_t exp) {
593 float result = 1.0f;
594 if (exp >= 0) {
595 for (int8_t i = 0; i < exp; i++)
596 result *= 10.0f;
597 } else {
598 for (int8_t i = exp; i < 0; i++)
599 result /= 10.0f;
600 }
601 return result;
602}
603
605template<typename T, typename U> T remap(U value, U min, U max, T min_out, T max_out) {
606 return (value - min) * (max_out - min_out) / (max - min) + min_out;
607}
608
610uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc = 0x00, uint8_t poly = 0x8C, bool msb_first = false);
611
613uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc = 0xffff, uint16_t reverse_poly = 0xa001,
614 bool refin = false, bool refout = false);
615uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc = 0, uint16_t poly = 0x1021, bool refin = false,
616 bool refout = false);
617
620uint32_t fnv1_hash(const char *str);
621inline uint32_t fnv1_hash(const std::string &str) { return fnv1_hash(str.c_str()); }
622
624constexpr uint32_t FNV1_OFFSET_BASIS = 2166136261UL;
626constexpr uint32_t FNV1_PRIME = 16777619UL;
627
629template<std::integral T> constexpr uint32_t fnv1_hash_extend(uint32_t hash, T value) {
630 using UnsignedT = std::make_unsigned_t<T>;
631 UnsignedT uvalue = static_cast<UnsignedT>(value);
632 for (size_t i = 0; i < sizeof(T); i++) {
633 hash *= FNV1_PRIME;
634 hash ^= (uvalue >> (i * 8)) & 0xFF;
635 }
636 return hash;
637}
639constexpr uint32_t fnv1_hash_extend(uint32_t hash, const char *str) {
640 if (str) {
641 while (*str) {
642 hash *= FNV1_PRIME;
643 hash ^= *str++;
644 }
645 }
646 return hash;
647}
648inline uint32_t fnv1_hash_extend(uint32_t hash, const std::string &str) { return fnv1_hash_extend(hash, str.c_str()); }
649
651constexpr uint32_t fnv1a_hash_extend(uint32_t hash, const char *str) {
652 if (str) {
653 while (*str) {
654 hash ^= *str++;
655 hash *= FNV1_PRIME;
656 }
657 }
658 return hash;
659}
660inline uint32_t fnv1a_hash_extend(uint32_t hash, const std::string &str) {
661 return fnv1a_hash_extend(hash, str.c_str());
662}
664template<std::integral T> constexpr uint32_t fnv1a_hash_extend(uint32_t hash, T value) {
665 using UnsignedT = std::make_unsigned_t<T>;
666 UnsignedT uvalue = static_cast<UnsignedT>(value);
667 for (size_t i = 0; i < sizeof(T); i++) {
668 hash ^= (uvalue >> (i * 8)) & 0xFF;
669 hash *= FNV1_PRIME;
670 }
671 return hash;
672}
674constexpr uint32_t fnv1a_hash(const char *str) { return fnv1a_hash_extend(FNV1_OFFSET_BASIS, str); }
675inline uint32_t fnv1a_hash(const std::string &str) { return fnv1a_hash(str.c_str()); }
676
700template<typename ReturnT = uint32_t> inline constexpr ESPHOME_ALWAYS_INLINE ReturnT micros_to_millis(uint64_t us) {
701 constexpr uint32_t d = 125U;
702 constexpr uint32_t q = static_cast<uint32_t>((1ULL << 32) / d); // 34359738
703 constexpr uint32_t r = static_cast<uint32_t>((1ULL << 32) % d); // 46
704 // 1000 = 8 * 125; divide-by-8 is a free shift
705 uint64_t x = us >> 3;
706 uint32_t lo = static_cast<uint32_t>(x);
707 uint32_t hi = static_cast<uint32_t>(x >> 32);
708 // Combine remainder term: hi * (2^32 % 125) + lo
709 uint32_t adj = hi * r + lo;
710 // If adj overflowed, the true value is 2^32 + adj; apply the identity again
711 // static_cast<ReturnT>(hi) widens to 64-bit when ReturnT=uint64_t, preserving upper bits of hi*q
712 return static_cast<ReturnT>(hi) * q + (adj < lo ? (adj + r) / d + q : adj / d);
713}
714
718float random_float();
720bool random_bytes(uint8_t *data, size_t len);
721
723
726
728constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb) {
729 return (static_cast<uint16_t>(msb) << 8) | (static_cast<uint16_t>(lsb));
730}
732constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3) {
733 return (static_cast<uint32_t>(byte1) << 16) | (static_cast<uint32_t>(byte2) << 8) | (static_cast<uint32_t>(byte3));
734}
736constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4) {
737 return (static_cast<uint32_t>(byte1) << 24) | (static_cast<uint32_t>(byte2) << 16) |
738 (static_cast<uint32_t>(byte3) << 8) | (static_cast<uint32_t>(byte4));
739}
740
742template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> constexpr T encode_value(const uint8_t *bytes) {
743 T val = 0;
744 for (size_t i = 0; i < sizeof(T); i++) {
745 val <<= 8;
746 val |= bytes[i];
747 }
748 return val;
749}
751template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
752constexpr T encode_value(const std::array<uint8_t, sizeof(T)> bytes) {
753 return encode_value<T>(bytes.data());
754}
756template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
757constexpr std::array<uint8_t, sizeof(T)> decode_value(T val) {
758 std::array<uint8_t, sizeof(T)> ret{};
759 for (size_t i = sizeof(T); i > 0; i--) {
760 ret[i - 1] = val & 0xFF;
761 val >>= 8;
762 }
763 return ret;
764}
765
767inline uint8_t reverse_bits(uint8_t x) {
768 x = ((x & 0xAA) >> 1) | ((x & 0x55) << 1);
769 x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2);
770 x = ((x & 0xF0) >> 4) | ((x & 0x0F) << 4);
771 return x;
772}
774inline uint16_t reverse_bits(uint16_t x) {
775 return (reverse_bits(static_cast<uint8_t>(x & 0xFF)) << 8) | reverse_bits(static_cast<uint8_t>((x >> 8) & 0xFF));
776}
779 return (reverse_bits(static_cast<uint16_t>(x & 0xFFFF)) << 16) |
780 reverse_bits(static_cast<uint16_t>((x >> 16) & 0xFFFF));
781}
782
784template<typename T> constexpr T convert_big_endian(T val) {
785#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
786 return byteswap(val);
787#else
788 return val;
789#endif
790}
791
793template<typename T> constexpr T convert_little_endian(T val) {
794#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
795 return val;
796#else
797 return byteswap(val);
798#endif
799}
800
802
805
807bool str_equals_case_insensitive(const std::string &a, const std::string &b);
809bool str_equals_case_insensitive(StringRef a, StringRef b);
811inline bool str_equals_case_insensitive(const char *a, const char *b) { return strcasecmp(a, b) == 0; }
812inline bool str_equals_case_insensitive(const std::string &a, const char *b) { return strcasecmp(a.c_str(), b) == 0; }
813inline bool str_equals_case_insensitive(const char *a, const std::string &b) { return strcasecmp(a, b.c_str()) == 0; }
814
816bool str_startswith(const std::string &str, const std::string &start);
818bool str_endswith(const std::string &str, const std::string &end);
819
821bool str_endswith_ignore_case(const char *str, size_t str_len, const char *suffix, size_t suffix_len);
822inline bool str_endswith_ignore_case(const char *str, const char *suffix) {
823 return str_endswith_ignore_case(str, strlen(str), suffix, strlen(suffix));
824}
825inline bool str_endswith_ignore_case(const std::string &str, const char *suffix) {
826 return str_endswith_ignore_case(str.c_str(), str.size(), suffix, strlen(suffix));
827}
828
831std::string str_truncate(const std::string &str, size_t length);
832
835std::string str_until(const char *str, char ch);
837std::string str_until(const std::string &str, char ch);
838
840std::string str_lower_case(const std::string &str);
843std::string str_upper_case(const std::string &str);
844
846constexpr char to_snake_case_char(char c) { return (c == ' ') ? '_' : (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; }
849std::string str_snake_case(const std::string &str);
850
852constexpr char to_sanitized_char(char c) {
853 return (c == '-' || c == '_' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) ? c : '_';
854}
855
865char *str_sanitize_to(char *buffer, size_t buffer_size, const char *str);
866
868template<size_t N> inline char *str_sanitize_to(char (&buffer)[N], const char *str) {
869 return str_sanitize_to(buffer, N, str);
870}
871
874std::string str_sanitize(const std::string &str);
875
880inline uint32_t fnv1_hash_object_id(const char *str, size_t len) {
882 for (size_t i = 0; i < len; i++) {
883 hash *= FNV1_PRIME;
884 // Apply snake_case (space->underscore, uppercase->lowercase) then sanitize
885 hash ^= static_cast<uint8_t>(to_sanitized_char(to_snake_case_char(str[i])));
886 }
887 return hash;
888}
889
892std::string __attribute__((format(printf, 1, 3))) str_snprintf(const char *fmt, size_t len, ...);
893
896std::string __attribute__((format(printf, 1, 2))) str_sprintf(const char *fmt, ...);
897
898#ifdef USE_ESP8266
899// ESP8266: Use vsnprintf_P to keep format strings in flash (PROGMEM)
900// Format strings must be wrapped with PSTR() macro
907inline size_t buf_append_printf_p(char *buf, size_t size, size_t pos, PGM_P fmt, ...) {
908 if (pos >= size) {
909 return size;
910 }
911 va_list args;
912 va_start(args, fmt);
913 int written = vsnprintf_P(buf + pos, size - pos, fmt, args);
914 va_end(args);
915 if (written < 0) {
916 return pos; // encoding error
917 }
918 return std::min(pos + static_cast<size_t>(written), size);
919}
920#define buf_append_printf(buf, size, pos, fmt, ...) buf_append_printf_p(buf, size, pos, PSTR(fmt), ##__VA_ARGS__)
921#else
929__attribute__((format(printf, 4, 5))) inline size_t buf_append_printf(char *buf, size_t size, size_t pos,
930 const char *fmt, ...) {
931 if (pos >= size) {
932 return size;
933 }
934 va_list args;
936 int written = vsnprintf(buf + pos, size - pos, fmt, args);
938 if (written < 0) {
939 return pos; // encoding error
940 }
941 return std::min(pos + static_cast<size_t>(written), size);
942}
943#endif
944
952inline size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str) {
953 if (pos >= size) {
954 return size;
955 }
956 size_t remaining = size - pos - 1; // reserve space for null terminator
957 size_t len = strlen(str);
958 if (len > remaining) {
959 len = remaining;
960 }
961 memcpy(buf + pos, str, len);
962 pos += len;
963 buf[pos] = '\0';
964 return pos;
965}
966
975std::string make_name_with_suffix(const std::string &name, char sep, const char *suffix_ptr, size_t suffix_len);
976
985std::string make_name_with_suffix(const char *name, size_t name_len, char sep, const char *suffix_ptr,
986 size_t suffix_len);
987
997size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, char sep,
998 const char *suffix_ptr, size_t suffix_len);
999
1001
1004
1006template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
1007optional<T> parse_number(const char *str) {
1008 char *end = nullptr;
1009 unsigned long value = ::strtoul(str, &end, 10); // NOLINT(google-runtime-int)
1010 if (end == str || *end != '\0' || value > std::numeric_limits<T>::max())
1011 return {};
1012 return value;
1013}
1015template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
1016optional<T> parse_number(const std::string &str) {
1017 return parse_number<T>(str.c_str());
1018}
1020template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
1021optional<T> parse_number(const char *str) {
1022 char *end = nullptr;
1023 signed long value = ::strtol(str, &end, 10); // NOLINT(google-runtime-int)
1024 if (end == str || *end != '\0' || value < std::numeric_limits<T>::min() || value > std::numeric_limits<T>::max())
1025 return {};
1026 return value;
1027}
1029template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
1030optional<T> parse_number(const std::string &str) {
1031 return parse_number<T>(str.c_str());
1032}
1034template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0> optional<T> parse_number(const char *str) {
1035 char *end = nullptr;
1036 float value = ::strtof(str, &end);
1037 if (end == str || *end != '\0' || value == HUGE_VALF)
1038 return {};
1039 return value;
1040}
1042template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
1043optional<T> parse_number(const std::string &str) {
1044 return parse_number<T>(str.c_str());
1045}
1046
1058size_t parse_hex(const char *str, size_t len, uint8_t *data, size_t count);
1060inline bool parse_hex(const char *str, uint8_t *data, size_t count) {
1061 return parse_hex(str, strlen(str), data, count) == 2 * count;
1062}
1064inline bool parse_hex(const std::string &str, uint8_t *data, size_t count) {
1065 return parse_hex(str.c_str(), str.length(), data, count) == 2 * count;
1066}
1068inline bool parse_hex(const char *str, std::vector<uint8_t> &data, size_t count) {
1069 data.resize(count);
1070 return parse_hex(str, strlen(str), data.data(), count) == 2 * count;
1071}
1073inline bool parse_hex(const std::string &str, std::vector<uint8_t> &data, size_t count) {
1074 data.resize(count);
1075 return parse_hex(str.c_str(), str.length(), data.data(), count) == 2 * count;
1076}
1082template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1083optional<T> parse_hex(const char *str, size_t len) {
1084 T val = 0;
1085 if (len > 2 * sizeof(T) || parse_hex(str, len, reinterpret_cast<uint8_t *>(&val), sizeof(T)) == 0)
1086 return {};
1087 return convert_big_endian(val);
1088}
1090template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const char *str) {
1091 return parse_hex<T>(str, strlen(str));
1092}
1094template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const std::string &str) {
1095 return parse_hex<T>(str.c_str(), str.length());
1096}
1097
1100static constexpr uint8_t INVALID_HEX_CHAR = 255;
1101
1102constexpr uint8_t parse_hex_char(char c) {
1103 if (c >= '0' && c <= '9')
1104 return c - '0';
1105 if (c >= 'A' && c <= 'F')
1106 return c - 'A' + 10;
1107 if (c >= 'a' && c <= 'f')
1108 return c - 'a' + 10;
1109 return INVALID_HEX_CHAR;
1110}
1111
1113inline char format_hex_char(uint8_t v, char base) { return v >= 10 ? base + (v - 10) : '0' + v; }
1114
1116inline char format_hex_char(uint8_t v) { return format_hex_char(v, 'a'); }
1117
1119inline char format_hex_pretty_char(uint8_t v) { return format_hex_char(v, 'A'); }
1120
1123inline char *int8_to_str(char *buf, int8_t val) {
1124 int32_t v = val;
1125 if (v < 0) {
1126 *buf++ = '-';
1127 v = -v;
1128 }
1129 if (v >= 100) {
1130 *buf++ = '1'; // int8 max is 128, so hundreds digit is always 1
1131 v -= 100;
1132 // Must write tens digit (even if 0) after hundreds
1133 int32_t tens = v / 10;
1134 *buf++ = '0' + tens;
1135 v -= tens * 10;
1136 } else if (v >= 10) {
1137 int32_t tens = v / 10;
1138 *buf++ = '0' + tens;
1139 v -= tens * 10;
1140 }
1141 *buf++ = '0' + v;
1142 return buf;
1143}
1144
1146char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length);
1147
1150template<size_t N> inline char *format_hex_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1151 static_assert(N >= 3, "Buffer must hold at least one hex byte (3 chars)");
1152 return format_hex_to(buffer, N, data, length);
1153}
1154
1156template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1157inline char *format_hex_to(char (&buffer)[N], T val) {
1158 static_assert(N >= sizeof(T) * 2 + 1, "Buffer too small for type");
1160 return format_hex_to(buffer, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1161}
1162
1164template<size_t N> inline char *format_hex_to(char (&buffer)[N], const std::vector<uint8_t> &data) {
1165 return format_hex_to(buffer, data.data(), data.size());
1166}
1167
1169template<size_t N, size_t M> inline char *format_hex_to(char (&buffer)[N], const std::array<uint8_t, M> &data) {
1170 return format_hex_to(buffer, data.data(), data.size());
1171}
1172
1174constexpr size_t format_hex_size(size_t byte_count) { return byte_count * 2 + 1; }
1175
1177constexpr size_t format_hex_prefixed_size(size_t byte_count) { return byte_count * 2 + 3; }
1178
1180template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1181inline char *format_hex_prefixed_to(char (&buffer)[N], T val) {
1182 static_assert(N >= sizeof(T) * 2 + 3, "Buffer too small for prefixed hex");
1183 buffer[0] = '0';
1184 buffer[1] = 'x';
1186 format_hex_to(buffer + 2, N - 2, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1187 return buffer;
1188}
1189
1191template<size_t N> inline char *format_hex_prefixed_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1192 static_assert(N >= 5, "Buffer must hold at least '0x' + one hex byte + null");
1193 buffer[0] = '0';
1194 buffer[1] = 'x';
1195 format_hex_to(buffer + 2, N - 2, data, length);
1196 return buffer;
1197}
1198
1200constexpr size_t format_hex_pretty_size(size_t byte_count) { return byte_count * 3; }
1201
1213char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator = ':');
1214
1216template<size_t N>
1217inline char *format_hex_pretty_to(char (&buffer)[N], const uint8_t *data, size_t length, char separator = ':') {
1218 static_assert(N >= 3, "Buffer must hold at least one hex byte");
1219 return format_hex_pretty_to(buffer, N, data, length, separator);
1220}
1221
1223template<size_t N>
1224inline char *format_hex_pretty_to(char (&buffer)[N], const std::vector<uint8_t> &data, char separator = ':') {
1225 return format_hex_pretty_to(buffer, data.data(), data.size(), separator);
1226}
1227
1229template<size_t N, size_t M>
1230inline char *format_hex_pretty_to(char (&buffer)[N], const std::array<uint8_t, M> &data, char separator = ':') {
1231 return format_hex_pretty_to(buffer, data.data(), data.size(), separator);
1232}
1233
1235constexpr size_t format_hex_pretty_uint16_size(size_t count) { return count * 5; }
1236
1250char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint16_t *data, size_t length, char separator = ':');
1251
1253template<size_t N>
1254inline char *format_hex_pretty_to(char (&buffer)[N], const uint16_t *data, size_t length, char separator = ':') {
1255 static_assert(N >= 5, "Buffer must hold at least one hex uint16_t");
1256 return format_hex_pretty_to(buffer, N, data, length, separator);
1257}
1258
1260static constexpr size_t MAC_ADDRESS_SIZE = 6;
1262static constexpr size_t MAC_ADDRESS_PRETTY_BUFFER_SIZE = format_hex_pretty_size(MAC_ADDRESS_SIZE);
1264static constexpr size_t MAC_ADDRESS_BUFFER_SIZE = MAC_ADDRESS_SIZE * 2 + 1;
1265
1267inline char *format_mac_addr_upper(const uint8_t *mac, char *output) {
1268 return format_hex_pretty_to(output, MAC_ADDRESS_PRETTY_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE, ':');
1269}
1270
1272inline void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output) {
1273 format_hex_to(output, MAC_ADDRESS_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE);
1274}
1275
1279std::string format_mac_address_pretty(const uint8_t mac[6]);
1283std::string format_hex(const uint8_t *data, size_t length);
1287std::string format_hex(const std::vector<uint8_t> &data);
1291template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_hex(T val) {
1293 return format_hex(reinterpret_cast<uint8_t *>(&val), sizeof(T));
1294}
1298template<std::size_t N> std::string format_hex(const std::array<uint8_t, N> &data) {
1299 return format_hex(data.data(), data.size());
1300}
1301
1330std::string format_hex_pretty(const uint8_t *data, size_t length, char separator = '.', bool show_length = true);
1331
1355std::string format_hex_pretty(const uint16_t *data, size_t length, char separator = '.', bool show_length = true);
1356
1381std::string format_hex_pretty(const std::vector<uint8_t> &data, char separator = '.', bool show_length = true);
1382
1406std::string format_hex_pretty(const std::vector<uint16_t> &data, char separator = '.', bool show_length = true);
1407
1431std::string format_hex_pretty(const std::string &data, char separator = '.', bool show_length = true);
1432
1459template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1460std::string format_hex_pretty(T val, char separator = '.', bool show_length = true) {
1462 return format_hex_pretty(reinterpret_cast<uint8_t *>(&val), sizeof(T), separator, show_length);
1463}
1464
1466constexpr size_t format_bin_size(size_t byte_count) { return byte_count * 8 + 1; }
1467
1487char *format_bin_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length);
1488
1490template<size_t N> inline char *format_bin_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1491 static_assert(N >= 9, "Buffer must hold at least one binary byte (9 chars)");
1492 return format_bin_to(buffer, N, data, length);
1493}
1494
1511template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1512inline char *format_bin_to(char (&buffer)[N], T val) {
1513 static_assert(N >= sizeof(T) * 8 + 1, "Buffer too small for type");
1515 return format_bin_to(buffer, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1516}
1517
1521std::string format_bin(const uint8_t *data, size_t length);
1525template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_bin(T val) {
1527 return format_bin(reinterpret_cast<uint8_t *>(&val), sizeof(T));
1528}
1529
1538ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const char *off = nullptr);
1539
1541ESPDEPRECATED("Allocates heap memory. Use value_accuracy_to_buf() instead. Removed in 2026.7.0.", "2026.1.0")
1542std::string value_accuracy_to_string(float value, int8_t accuracy_decimals);
1543
1545static constexpr size_t VALUE_ACCURACY_MAX_LEN = 64;
1546
1548size_t value_accuracy_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value, int8_t accuracy_decimals);
1550size_t value_accuracy_with_uom_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value,
1551 int8_t accuracy_decimals, StringRef unit_of_measurement);
1552
1554int8_t step_to_accuracy_decimals(float step);
1555
1556std::string base64_encode(const uint8_t *buf, size_t buf_len);
1557std::string base64_encode(const std::vector<uint8_t> &buf);
1558
1559std::vector<uint8_t> base64_decode(const std::string &encoded_string);
1560size_t base64_decode(std::string const &encoded_string, uint8_t *buf, size_t buf_len);
1561size_t base64_decode(const uint8_t *encoded_data, size_t encoded_len, uint8_t *buf, size_t buf_len);
1562
1567bool base64_decode_int32_vector(const std::string &base64, std::vector<int32_t> &out);
1568
1570
1573
1575// Remove before 2026.9.0
1576ESPDEPRECATED("Use LightState::gamma_correct_lut() instead. Removed in 2026.9.0.", "2026.3.0")
1577float gamma_correct(float value, float gamma);
1579// Remove before 2026.9.0
1580ESPDEPRECATED("Use LightState::gamma_uncorrect_lut() instead. Removed in 2026.9.0.", "2026.3.0")
1581float gamma_uncorrect(float value, float gamma);
1582
1584void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value);
1586void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue);
1587
1589
1592
1594constexpr float celsius_to_fahrenheit(float value) { return value * 1.8f + 32.0f; }
1596constexpr float fahrenheit_to_celsius(float value) { return (value - 32.0f) / 1.8f; }
1597
1599
1602
1603template<typename... X> class CallbackManager;
1604
1609template<typename... Ts> class CallbackManager<void(Ts...)> {
1610 public:
1612 void add(std::function<void(Ts...)> &&callback) { this->callbacks_.push_back(std::move(callback)); }
1613
1615 void call(Ts... args) {
1616 for (auto &cb : this->callbacks_)
1617 cb(args...);
1618 }
1619 size_t size() const { return this->callbacks_.size(); }
1620
1622 void operator()(Ts... args) { call(args...); }
1623
1624 protected:
1625 std::vector<std::function<void(Ts...)>> callbacks_;
1626};
1627
1628template<typename... X> class LazyCallbackManager;
1629
1645template<typename... Ts> class LazyCallbackManager<void(Ts...)> {
1646 public:
1650 ~LazyCallbackManager() { delete this->callbacks_; }
1651
1652 // Non-copyable and non-movable (entities are never copied or moved)
1657
1659 void add(std::function<void(Ts...)> &&callback) {
1660 if (!this->callbacks_) {
1661 this->callbacks_ = new CallbackManager<void(Ts...)>();
1662 }
1663 this->callbacks_->add(std::move(callback));
1664 }
1665
1667 void call(Ts... args) {
1668 if (this->callbacks_) {
1669 this->callbacks_->call(args...);
1670 }
1671 }
1672
1674 size_t size() const { return this->callbacks_ ? this->callbacks_->size() : 0; }
1675
1677 bool empty() const { return !this->callbacks_ || this->callbacks_->size() == 0; }
1678
1680 void operator()(Ts... args) { this->call(args...); }
1681
1682 protected:
1683 CallbackManager<void(Ts...)> *callbacks_{nullptr};
1684};
1685
1687template<typename T> class Deduplicator {
1688 public:
1690 bool next(T value) {
1691 if (this->has_value_ && !this->value_unknown_ && this->last_value_ == value) {
1692 return false;
1693 }
1694 this->has_value_ = true;
1695 this->value_unknown_ = false;
1696 this->last_value_ = value;
1697 return true;
1698 }
1701 bool ret = !this->value_unknown_;
1702 this->value_unknown_ = true;
1703 return ret;
1704 }
1706 bool has_value() const { return this->has_value_; }
1707
1708 protected:
1709 bool has_value_{false};
1710 bool value_unknown_{false};
1712};
1713
1715template<typename T> class Parented {
1716 public:
1718 Parented(T *parent) : parent_(parent) {}
1719
1721 T *get_parent() const { return parent_; }
1723 void set_parent(T *parent) { parent_ = parent; }
1724
1725 protected:
1726 T *parent_{nullptr};
1727};
1728
1730
1733
1738class Mutex {
1739 public:
1740 Mutex();
1741 Mutex(const Mutex &) = delete;
1742 ~Mutex();
1743 void lock();
1744 bool try_lock();
1745 void unlock();
1746
1747 Mutex &operator=(const Mutex &) = delete;
1748
1749 private:
1750#if defined(USE_ESP32) || defined(USE_LIBRETINY)
1751 SemaphoreHandle_t handle_;
1752#else
1753 // d-pointer to store private data on new platforms
1754 void *handle_; // NOLINT(clang-diagnostic-unused-private-field)
1755#endif
1756};
1757
1763 public:
1764 LockGuard(Mutex &mutex) : mutex_(mutex) { mutex_.lock(); }
1765 ~LockGuard() { mutex_.unlock(); }
1766
1767 private:
1768 Mutex &mutex_;
1769};
1770
1792 public:
1793 InterruptLock();
1795
1796 protected:
1797#if defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_ZEPHYR)
1799#endif
1800};
1801
1811 public:
1812 LwIPLock(const LwIPLock &) = delete;
1813 LwIPLock &operator=(const LwIPLock &) = delete;
1814
1815#if defined(USE_ESP32) || defined(USE_RP2040)
1816 // Platforms with potential lwIP core locking — out-of-line implementations in helpers.cpp
1817 LwIPLock();
1818 ~LwIPLock();
1819#else
1820 // No lwIP core locking — inline no-ops (empty bodies instead of = default
1821 // to prevent clang-tidy unused-variable warnings at call sites)
1824#endif
1825};
1826
1833 public:
1835 void start();
1837 void stop();
1838
1840 static bool is_high_frequency() { return num_requests > 0; }
1841
1842 protected:
1843 bool started_{false};
1844 static uint8_t num_requests; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
1845};
1846
1848void get_mac_address_raw(uint8_t *mac); // NOLINT(readability-non-const-parameter)
1849
1853std::string get_mac_address();
1854
1858std::string get_mac_address_pretty();
1859
1863void get_mac_address_into_buffer(std::span<char, MAC_ADDRESS_BUFFER_SIZE> buf);
1864
1868const char *get_mac_address_pretty_into_buffer(std::span<char, MAC_ADDRESS_PRETTY_BUFFER_SIZE> buf);
1869
1870#ifdef USE_ESP32
1872void set_mac_address(uint8_t *mac);
1873#endif
1874
1878
1881bool mac_address_is_valid(const uint8_t *mac);
1882
1885
1887
1890
1899template<class T> class RAMAllocator {
1900 public:
1901 using value_type = T;
1902
1903 enum Flags {
1904 NONE = 0, // Perform external allocation and fall back to internal memory
1905 ALLOC_EXTERNAL = 1 << 0, // Perform external allocation only.
1906 ALLOC_INTERNAL = 1 << 1, // Perform internal allocation only.
1907 ALLOW_FAILURE = 1 << 2, // Does nothing. Kept for compatibility.
1908 };
1909
1910 constexpr RAMAllocator() = default;
1911 constexpr RAMAllocator(uint8_t flags)
1914 template<class U> constexpr RAMAllocator(const RAMAllocator<U> &other) : flags_{other.flags_} {}
1915
1916 T *allocate(size_t n) { return this->allocate(n, sizeof(T)); }
1917
1918 T *allocate(size_t n, size_t manual_size) {
1919 size_t size = n * manual_size;
1920 T *ptr = nullptr;
1921#ifdef USE_ESP32
1922 if (this->flags_ & Flags::ALLOC_EXTERNAL) {
1923 ptr = static_cast<T *>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
1924 }
1925 if (ptr == nullptr && this->flags_ & Flags::ALLOC_INTERNAL) {
1926 ptr = static_cast<T *>(heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
1927 }
1928#else
1929 // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported
1930 ptr = static_cast<T *>(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
1931#endif
1932 return ptr;
1933 }
1934
1935 T *reallocate(T *p, size_t n) { return this->reallocate(p, n, sizeof(T)); }
1936
1937 T *reallocate(T *p, size_t n, size_t manual_size) {
1938 size_t size = n * manual_size;
1939 T *ptr = nullptr;
1940#ifdef USE_ESP32
1941 if (this->flags_ & Flags::ALLOC_EXTERNAL) {
1942 ptr = static_cast<T *>(heap_caps_realloc(p, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
1943 }
1944 if (ptr == nullptr && this->flags_ & Flags::ALLOC_INTERNAL) {
1945 ptr = static_cast<T *>(heap_caps_realloc(p, size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
1946 }
1947#else
1948 // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported
1949 ptr = static_cast<T *>(realloc(p, size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
1950#endif
1951 return ptr;
1952 }
1953
1954 void deallocate(T *p, size_t n) {
1955 free(p); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
1956 }
1957
1961 size_t get_free_heap_size() const {
1962#ifdef USE_ESP8266
1963 return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
1964#elif defined(USE_ESP32)
1965 auto max_internal =
1966 this->flags_ & ALLOC_INTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0;
1967 auto max_external =
1968 this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0;
1969 return max_internal + max_external;
1970#elif defined(USE_RP2040)
1971 return ::rp2040.getFreeHeap();
1972#elif defined(USE_LIBRETINY)
1973 return lt_heap_get_free();
1974#else
1975 return 100000;
1976#endif
1977 }
1978
1983#ifdef USE_ESP8266
1984 return ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance)
1985#elif defined(USE_ESP32)
1986 auto max_internal =
1987 this->flags_ & ALLOC_INTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0;
1988 auto max_external =
1989 this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0;
1990 return std::max(max_internal, max_external);
1991#else
1992 return this->get_free_heap_size();
1993#endif
1994 }
1995
1996 private:
1997 uint8_t flags_{ALLOC_INTERNAL | ALLOC_EXTERNAL};
1998};
1999
2000template<class T> using ExternalRAMAllocator = RAMAllocator<T>;
2001
2006template<typename T, typename U>
2007concept comparable_with = requires(T a, U b) {
2008 { a > b } -> std::convertible_to<bool>;
2009 { a < b } -> std::convertible_to<bool>;
2010};
2011
2012template<std::totally_ordered T, comparable_with<T> U> T clamp_at_least(T value, U min) {
2013 if (value < min)
2014 return min;
2015 return value;
2016}
2017template<std::totally_ordered T, comparable_with<T> U> T clamp_at_most(T value, U max) {
2018 if (value > max)
2019 return max;
2020 return value;
2021}
2022
2025
2030template<typename T, enable_if_t<!std::is_pointer<T>::value, int> = 0> T id(T value) { return value; }
2035template<typename T, enable_if_t<std::is_pointer<T *>::value, int> = 0> T &id(T *value) { return *value; }
2036
2038
2039} // namespace esphome
uint8_t m
Definition bl0906.h:1
void operator()(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1622
std::vector< std::function< void(Ts...)> > callbacks_
Definition helpers.h:1625
void call(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1615
void add(std::function< void(Ts...)> &&callback)
Add a callback to the list.
Definition helpers.h:1612
Lightweight read-only view over a const array stored in RODATA (will typically be in flash memory) Av...
Definition helpers.h:123
const constexpr T & operator[](size_t i) const
Definition helpers.h:127
constexpr bool empty() const
Definition helpers.h:129
constexpr ConstVector(const T *data, size_t size)
Definition helpers.h:125
constexpr size_t size() const
Definition helpers.h:128
Helper class to deduplicate items in a series of values.
Definition helpers.h:1687
bool next(T value)
Feeds the next item in the series to the deduplicator and returns false if this is a duplicate.
Definition helpers.h:1690
bool has_value() const
Returns true if this deduplicator has processed any items.
Definition helpers.h:1706
bool next_unknown()
Returns true if the deduplicator's value was previously known.
Definition helpers.h:1700
Fixed-capacity vector - allocates once at runtime, never reallocates This avoids std::vector template...
Definition helpers.h:374
const T & at(size_t i) const
Definition helpers.h:544
FixedVector(FixedVector &&other) noexcept
Definition helpers.h:431
FixedVector(std::initializer_list< T > init_list)
Constructor from initializer list - allocates exact size needed This enables brace initialization: Fi...
Definition helpers.h:422
const T * begin() const
Definition helpers.h:549
bool full() const
Definition helpers.h:534
FixedVector & operator=(std::initializer_list< T > init_list)
Assignment from initializer list - avoids temporary and move overhead This enables: FixedVector<int> ...
Definition helpers.h:454
T & front()
Access first element (no bounds checking - matches std::vector behavior) Caller must ensure vector is...
Definition helpers.h:523
const T & operator[](size_t i) const
Definition helpers.h:539
T & operator[](size_t i)
Access element without bounds checking (matches std::vector behavior) Caller must ensure index is val...
Definition helpers.h:538
size_t capacity() const
Definition helpers.h:533
T & back()
Access last element (no bounds checking - matches std::vector behavior) Caller must ensure vector is ...
Definition helpers.h:528
bool empty() const
Definition helpers.h:532
FixedVector & operator=(const FixedVector &)=delete
FixedVector(const FixedVector &)=delete
void push_back(T &&value)
Add element by move without bounds checking Caller must ensure sufficient capacity was allocated via ...
Definition helpers.h:502
T & emplace_back(Args &&...args)
Emplace element without bounds checking - constructs in-place with arguments Caller must ensure suffi...
Definition helpers.h:514
size_t size() const
Definition helpers.h:531
const T & front() const
Definition helpers.h:524
const T & back() const
Definition helpers.h:529
const T * end() const
Definition helpers.h:550
FixedVector & operator=(FixedVector &&other) noexcept
Definition helpers.h:438
T & at(size_t i)
Access element with bounds checking (matches std::vector behavior) Note: No exception thrown on out o...
Definition helpers.h:543
void push_back(const T &value)
Add element without bounds checking Caller must ensure sufficient capacity was allocated via init() S...
Definition helpers.h:491
void init(size_t n)
Definition helpers.h:464
Helper class to request loop() to be called as fast as possible.
Definition helpers.h:1832
static bool is_high_frequency()
Check whether the loop is running continuously.
Definition helpers.h:1840
void stop()
Stop running the loop continuously.
Definition helpers.cpp:791
void start()
Start running the loop continuously.
Definition helpers.cpp:785
Helper class to disable interrupts.
Definition helpers.h:1791
LazyCallbackManager & operator=(const LazyCallbackManager &)=delete
LazyCallbackManager(const LazyCallbackManager &)=delete
size_t size() const
Return the number of registered callbacks.
Definition helpers.h:1674
void operator()(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1680
LazyCallbackManager & operator=(LazyCallbackManager &&)=delete
void add(std::function< void(Ts...)> &&callback)
Add a callback to the list. Allocates the underlying CallbackManager on first use.
Definition helpers.h:1659
~LazyCallbackManager()
Destructor - clean up allocated CallbackManager if any.
Definition helpers.h:1650
void call(Ts... args)
Call all callbacks in this manager. No-op if no callbacks registered.
Definition helpers.h:1667
bool empty() const
Check if any callbacks are registered.
Definition helpers.h:1677
LazyCallbackManager(LazyCallbackManager &&)=delete
Helper class that wraps a mutex with a RAII-style API.
Definition helpers.h:1762
LockGuard(Mutex &mutex)
Definition helpers.h:1764
Helper class to lock the lwIP TCPIP core when making lwIP API calls from non-TCPIP threads.
Definition helpers.h:1810
LwIPLock(const LwIPLock &)=delete
LwIPLock & operator=(const LwIPLock &)=delete
Mutex implementation, with API based on the unavailable std::mutex.
Definition helpers.h:1738
void unlock()
Definition helpers.cpp:27
bool try_lock()
Definition helpers.cpp:26
Mutex(const Mutex &)=delete
Mutex & operator=(const Mutex &)=delete
Helper class to easily give an object a parent of type T.
Definition helpers.h:1715
T * get_parent() const
Get the parent of this object.
Definition helpers.h:1721
Parented(T *parent)
Definition helpers.h:1718
void set_parent(T *parent)
Set the parent of this object.
Definition helpers.h:1723
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:1899
constexpr RAMAllocator(uint8_t flags)
Definition helpers.h:1911
T * reallocate(T *p, size_t n, size_t manual_size)
Definition helpers.h:1937
size_t get_free_heap_size() const
Return the total heap space available via this allocator.
Definition helpers.h:1961
T * reallocate(T *p, size_t n)
Definition helpers.h:1935
void deallocate(T *p, size_t n)
Definition helpers.h:1954
size_t get_max_free_block_size() const
Return the maximum size block this allocator could allocate.
Definition helpers.h:1982
T * allocate(size_t n)
Definition helpers.h:1916
constexpr RAMAllocator(const RAMAllocator< U > &other)
Definition helpers.h:1914
T * allocate(size_t n, size_t manual_size)
Definition helpers.h:1918
constexpr RAMAllocator()=default
Helper class for efficient buffer allocation - uses stack for small sizes, heap for large This is use...
Definition helpers.h:558
SmallBufferWithHeapFallback(const SmallBufferWithHeapFallback &)=delete
SmallBufferWithHeapFallback & operator=(SmallBufferWithHeapFallback &&)=delete
SmallBufferWithHeapFallback & operator=(const SmallBufferWithHeapFallback &)=delete
SmallBufferWithHeapFallback(SmallBufferWithHeapFallback &&)=delete
Small buffer optimization - stores data inline when small, heap-allocates for large data This avoids ...
Definition helpers.h:139
SmallInlineBuffer(const SmallInlineBuffer &)=delete
bool is_inline_() const
Definition helpers.h:199
void set(const uint8_t *src, size_t size)
Set buffer contents, allocating heap if needed.
Definition helpers.h:180
SmallInlineBuffer & operator=(const SmallInlineBuffer &)=delete
size_t size() const
Definition helpers.h:196
uint8_t inline_[InlineSize]
Definition helpers.h:203
SmallInlineBuffer & operator=(SmallInlineBuffer &&other) noexcept
Definition helpers.h:159
const uint8_t * data() const
Definition helpers.h:195
SmallInlineBuffer(SmallInlineBuffer &&other) noexcept
Definition helpers.h:148
ConstIterator(const StaticRingBuffer *buf, index_type pos)
Definition helpers.h:324
bool operator!=(const ConstIterator &other) const
Definition helpers.h:330
bool operator!=(const Iterator &other) const
Definition helpers.h:315
Iterator(StaticRingBuffer *buf, index_type pos)
Definition helpers.h:309
Fixed-size circular buffer with FIFO semantics and iteration support.
Definition helpers.h:303
bool push(const T &value)
Definition helpers.h:337
ConstIterator begin() const
Definition helpers.h:361
ConstIterator end() const
Definition helpers.h:362
index_type size() const
Definition helpers.h:356
const T & front() const
Definition helpers.h:355
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:209
const_reverse_iterator rend() const
Definition helpers.h:289
size_t size() const
Definition helpers.h:269
reverse_iterator rbegin()
Definition helpers.h:286
const T & operator[](size_t i) const
Definition helpers.h:277
reverse_iterator rend()
Definition helpers.h:287
void push_back(const T &value)
Definition helpers.h:242
bool empty() const
Definition helpers.h:270
void assign(InputIt first, InputIt last)
Definition helpers.h:252
const_reverse_iterator rbegin() const
Definition helpers.h:288
T & operator[](size_t i)
Definition helpers.h:276
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition helpers.h:215
typename std::array< T, N >::iterator iterator
Definition helpers.h:212
typename std::array< T, N >::const_iterator const_iterator
Definition helpers.h:213
std::reverse_iterator< iterator > reverse_iterator
Definition helpers.h:214
const T * data() const
Definition helpers.h:274
const_iterator end() const
Definition helpers.h:283
StaticVector(InputIt first, InputIt last)
Definition helpers.h:226
StaticVector(std::initializer_list< T > init)
Definition helpers.h:233
const_iterator begin() const
Definition helpers.h:282
struct @65::@66 __attribute__
Functions to constrain the range of arithmetic values.
Definition helpers.h:2007
uint16_t flags
uint16_t id
mopeka_std_values val[3]
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
T clamp_at_most(T value, U max)
Definition helpers.h:2017
bool random_bytes(uint8_t *data, size_t len)
Generate len number of random bytes.
Definition helpers.cpp:18
size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str)
Safely append a string to buffer without format parsing, returning new position (capped at size).
Definition helpers.h:952
constexpr uint32_t fnv1a_hash_extend(uint32_t hash, const char *str)
Extend a FNV-1a hash with additional string data.
Definition helpers.h:651
float random_float()
Return a random float between 0 and 1.
Definition helpers.cpp:159
const char int const __FlashStringHelper * format
Definition log.h:74
float gamma_uncorrect(float value, float gamma)
Definition helpers.cpp:711
uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t reverse_poly, bool refin, bool refout)
Calculate a CRC-16 checksum of data with size len.
Definition helpers.cpp:73
size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, char sep, const char *suffix_ptr, size_t suffix_len)
Zero-allocation version: format name + separator + suffix directly into buffer.
Definition helpers.cpp:261
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals)
Definition helpers.cpp:484
constexpr T convert_big_endian(T val)
Convert a value between host byte order and big endian (most significant byte first) order.
Definition helpers.h:784
char format_hex_pretty_char(uint8_t v)
Convert a nibble (0-15) to uppercase hex char (used for pretty printing)
Definition helpers.h:1119
float gamma_correct(float value, float gamma)
Definition helpers.cpp:703
constexpr char to_sanitized_char(char c)
Sanitize a single char: keep alphanumerics, dashes, underscores; replace others with underscore.
Definition helpers.h:852
bool mac_address_is_valid(const uint8_t *mac)
Check if the MAC address is not all zeros or all ones.
Definition helpers.cpp:828
void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output)
Format MAC address as xxxxxxxxxxxxxx (lowercase, no separators)
Definition helpers.h:1272
constexpr uint32_t FNV1_OFFSET_BASIS
FNV-1 32-bit offset basis.
Definition helpers.h:624
void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value)
Convert red, green and blue (all 0-1) values to hue (0-360), saturation (0-1) and value (0-1).
Definition helpers.cpp:720
std::string format_hex(const uint8_t *data, size_t length)
Format the byte array data of length len in lowercased hex.
Definition helpers.cpp:345
char format_hex_char(uint8_t v, char base)
Convert a nibble (0-15) to hex char with specified base ('a' for lowercase, 'A' for uppercase)
Definition helpers.h:1113
size_t value_accuracy_to_buf(std::span< char, VALUE_ACCURACY_MAX_LEN > buf, float value, int8_t accuracy_decimals)
Format value with accuracy to buffer, returns chars written (excluding null)
Definition helpers.cpp:490
std::string str_lower_case(const std::string &str)
Convert the string to lower case.
Definition helpers.cpp:201
ParseOnOffState parse_on_off(const char *str, const char *on, const char *off)
Parse a string that contains either on, off or toggle.
Definition helpers.cpp:454
const char int const __FlashStringHelper va_list args
Definition log.h:74
std::string format_bin(const uint8_t *data, size_t length)
Format the byte array data of length len in binary.
Definition helpers.cpp:447
constexpr T convert_little_endian(T val)
Convert a value between host byte order and little endian (least significant byte first) order.
Definition helpers.h:793
std::string str_sanitize(const std::string &str)
Sanitizes the input string by removing all characters but alphanumerics, dashes and underscores.
Definition helpers.cpp:222
constexpr uint32_t fnv1_hash_extend(uint32_t hash, T value)
Extend a FNV-1 hash with an integer (hashes each byte).
Definition helpers.h:629
bool base64_decode_int32_vector(const std::string &base64, std::vector< int32_t > &out)
Decode base64/base64url string directly into vector of little-endian int32 values.
Definition helpers.cpp:665
va_end(args)
std::string size_t len
Definition helpers.h:892
constexpr size_t format_hex_prefixed_size(size_t byte_count)
Calculate buffer size needed for format_hex_prefixed_to: "0xXXXXXXXX...\0" = bytes * 2 + 3.
Definition helpers.h:1177
constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3)
Encode a 24-bit value given three bytes in most to least significant byte order.
Definition helpers.h:732
char * format_hex_prefixed_to(char(&buffer)[N], T val)
Format an unsigned integer as "0x" prefixed lowercase hex to buffer.
Definition helpers.h:1181
bool has_custom_mac_address()
Check if a custom MAC address is set (ESP32 & variants)
Definition helpers.cpp:93
size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count)
Parse bytes from a hex-encoded string into a byte array.
Definition helpers.cpp:294
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
Definition helpers.cpp:353
size_t size
Definition helpers.h:929
uint32_t fnv1_hash_object_id(const char *str, size_t len)
Calculate FNV-1 hash of a string while applying snake_case + sanitize transformations.
Definition helpers.h:880
uint32_t fnv1_hash(const char *str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:148
T clamp_at_least(T value, U min)
Definition helpers.h:2012
optional< T > parse_number(const char *str)
Parse an unsigned decimal number from a null-terminated string.
Definition helpers.h:1007
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
Definition helpers.cpp:806
std::string str_snprintf(const char *fmt, size_t len,...)
Definition helpers.cpp:228
void set_mac_address(uint8_t *mac)
Set the MAC address to use from the provided byte array (6 bytes).
Definition helpers.cpp:91
int8_t step_to_accuracy_decimals(float step)
Derive accuracy in decimals from an increment step.
Definition helpers.cpp:514
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition helpers.cpp:17
size_t size_t pos
Definition helpers.h:929
const char * get_mac_address_pretty_into_buffer(std::span< char, MAC_ADDRESS_PRETTY_BUFFER_SIZE > buf)
Get the device MAC address into the given buffer, in colon-separated uppercase hex notation.
Definition helpers.cpp:817
void IRAM_ATTR HOT delay_microseconds_safe(uint32_t us)
Delay for the given amount of microseconds, possibly yielding to other processes during the wait.
Definition helpers.cpp:843
std::string str_upper_case(const std::string &str)
Convert the string to upper case.
Definition helpers.cpp:202
std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length)
Format a byte array in pretty-printed, human-readable hex format.
Definition helpers.cpp:401
constexpr size_t format_hex_size(size_t byte_count)
Calculate buffer size needed for format_hex_to: "XXXXXXXX...\0" = bytes * 2 + 1.
Definition helpers.h:1174
bool str_equals_case_insensitive(const std::string &a, const std::string &b)
Compare strings for equality in case-insensitive manner.
Definition helpers.cpp:163
std::string str_until(const char *str, char ch)
Extract the part of the string until either the first occurrence of the specified character,...
Definition helpers.cpp:188
bool str_endswith_ignore_case(const char *str, size_t str_len, const char *suffix, size_t suffix_len)
Case-insensitive check if string ends with suffix (no heap allocation).
Definition helpers.cpp:179
constexpr ESPHOME_ALWAYS_INLINE ReturnT micros_to_millis(uint64_t us)
Convert a 64-bit microsecond count to milliseconds without calling __udivdi3 (software 64-bit divide,...
Definition helpers.h:700
char * str_sanitize_to(char *buffer, size_t buffer_size, const char *str)
Sanitize a string to buffer, keeping only alphanumerics, dashes, and underscores.
Definition helpers.cpp:210
char * int8_to_str(char *buf, int8_t val)
Write int8 value to buffer without modulo operations.
Definition helpers.h:1123
std::string format_mac_address_pretty(const uint8_t *mac)
Definition helpers.cpp:305
size_t value_accuracy_with_uom_to_buf(std::span< char, VALUE_ACCURACY_MAX_LEN > buf, float value, int8_t accuracy_decimals, StringRef unit_of_measurement)
Format value with accuracy and UOM to buffer, returns chars written (excluding null)
Definition helpers.cpp:500
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
Definition helpers.h:1200
std::string base64_encode(const std::vector< uint8_t > &buf)
Definition helpers.cpp:553
constexpr uint32_t FNV1_PRIME
FNV-1 32-bit prime.
Definition helpers.h:626
constexpr T encode_value(const uint8_t *bytes)
Encode a value from its constituent bytes (from most to least significant) in an array with length si...
Definition helpers.h:742
void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue)
Convert hue (0-360), saturation (0-1) and value (0-1) to red, green and blue (all 0-1).
Definition helpers.cpp:743
void get_mac_address_into_buffer(std::span< char, MAC_ADDRESS_BUFFER_SIZE > buf)
Get the device MAC address into the given buffer, in lowercase hex notation.
Definition helpers.cpp:811
uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t poly, bool refin, bool refout)
Definition helpers.cpp:113
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition helpers.h:736
uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc, uint8_t poly, bool msb_first)
Calculate a CRC-8 checksum of data with size len.
Definition helpers.cpp:46
constexpr float celsius_to_fahrenheit(float value)
Convert degrees Celsius to degrees Fahrenheit.
Definition helpers.h:1594
std::string str_sprintf(const char *fmt,...)
Definition helpers.cpp:242
size_t size_t const char va_start(args, fmt)
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:728
void get_mac_address_raw(uint8_t *mac)
Get the device MAC address as raw bytes, written into the provided byte array (6 bytes).
Definition helpers.cpp:73
size_t size_t const char * fmt
Definition helpers.h:930
constexpr uint8_t parse_hex_char(char c)
Definition helpers.h:1102
bool str_startswith(const std::string &str, const std::string &start)
Check whether a string starts with a value.
Definition helpers.cpp:170
constexpr std::array< uint8_t, sizeof(T)> decode_value(T val)
Decode a value into its constituent bytes (from most to least significant).
Definition helpers.h:757
std::string get_mac_address()
Get the device MAC address as a string, in lowercase hex notation.
Definition helpers.cpp:798
constexpr size_t format_bin_size(size_t byte_count)
Calculate buffer size needed for format_bin_to: "01234567...\0" = bytes * 8 + 1.
Definition helpers.h:1466
int written
Definition helpers.h:936
struct ESPDEPRECATED("Use std::index_sequence instead. Removed in 2026.6.0", "2025.12.0") seq
Definition automation.h:26
To bit_cast(const From &src)
Convert data between types, without aliasing issues or undefined behaviour.
Definition helpers.h:76
constexpr char to_snake_case_char(char c)
Convert a single char to snake_case: lowercase and space to underscore.
Definition helpers.h:846
constexpr float fahrenheit_to_celsius(float value)
Convert degrees Fahrenheit to degrees Celsius.
Definition helpers.h:1596
uint8_t reverse_bits(uint8_t x)
Reverse the order of 8 bits.
Definition helpers.h:767
char * format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length)
Format byte array as lowercase hex to buffer (base implementation).
Definition helpers.cpp:341
std::string str_snake_case(const std::string &str)
Convert the string to snake case (lowercase with underscores).
Definition helpers.cpp:203
float lerp(float completion, float start, float end)=delete
constexpr size_t format_hex_pretty_uint16_size(size_t count)
Calculate buffer size needed for format_hex_pretty_to with uint16_t data: "XXXX:XXXX:....
Definition helpers.h:1235
T remap(U value, U min, U max, T min_out, T max_out)
Remap value from the range (min, max) to (min_out, max_out).
Definition helpers.h:605
bool str_endswith(const std::string &str, const std::string &end)
Check whether a string ends with a value.
Definition helpers.cpp:171
constexpr uint32_t fnv1a_hash(const char *str)
Calculate a FNV-1a hash of str.
Definition helpers.h:674
std::string size_t std::string size_t buf_append_printf_p(char *buf, size_t size, size_t pos, PGM_P fmt,...)
Safely append formatted string to buffer, returning new position (capped at size).
Definition helpers.h:907
float gamma
Definition helpers.h:1577
size_t base64_decode(const std::string &encoded_string, uint8_t *buf, size_t buf_len)
Definition helpers.cpp:593
ParseOnOffState
Return values for parse_on_off().
Definition helpers.h:1531
@ PARSE_ON
Definition helpers.h:1533
@ PARSE_TOGGLE
Definition helpers.h:1535
@ PARSE_OFF
Definition helpers.h:1534
@ PARSE_NONE
Definition helpers.h:1532
float pow10_int(int8_t exp)
Compute 10^exp using iterative multiplication/division.
Definition helpers.h:592
std::string make_name_with_suffix(const char *name, size_t name_len, char sep, const char *suffix_ptr, size_t suffix_len)
Optimized string concatenation: name + separator + suffix (const char* overload) Uses a fixed stack b...
Definition helpers.cpp:281
char * format_bin_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length)
Format byte array as binary string to buffer.
Definition helpers.cpp:426
char * format_mac_addr_upper(const uint8_t *mac, char *output)
Format MAC address as XX:XX:XX:XX:XX:XX (uppercase, colon separators)
Definition helpers.h:1267
std::string str_truncate(const std::string &str, size_t length)
Truncate a string to a specific length.
Definition helpers.cpp:185
static void uint32_t
uint8_t end[39]
Definition sun_gtil2.cpp:17
void byteswap()
uint16_t length
Definition tt21100.cpp:0
uint16_t x
Definition tt21100.cpp:5