esphome 2025.7.0b1__py3-none-any.whl → 2025.7.0b2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. esphome/components/api/__init__.py +19 -5
  2. esphome/components/api/api_connection.cpp +53 -175
  3. esphome/components/api/api_connection.h +28 -25
  4. esphome/components/api/api_frame_helper.cpp +2 -3
  5. esphome/components/api/api_frame_helper.h +7 -9
  6. esphome/components/api/api_pb2.cpp +1862 -5133
  7. esphome/components/api/api_pb2.h +291 -533
  8. esphome/components/api/api_pb2_dump.cpp +99 -0
  9. esphome/components/api/api_pb2_service.cpp +4 -0
  10. esphome/components/api/api_pb2_service.h +6 -0
  11. esphome/components/api/api_server.cpp +2 -9
  12. esphome/components/api/api_server.h +7 -33
  13. esphome/components/api/custom_api_device.h +8 -0
  14. esphome/components/api/list_entities.cpp +2 -0
  15. esphome/components/api/list_entities.h +3 -1
  16. esphome/components/api/proto.h +506 -23
  17. esphome/components/api/user_services.h +2 -0
  18. esphome/components/debug/debug_esp32.cpp +2 -0
  19. esphome/components/esp32/__init__.py +1 -0
  20. esphome/components/esp32_camera/__init__.py +1 -1
  21. esphome/components/esp32_touch/esp32_touch_v1.cpp +12 -10
  22. esphome/components/esp8266/__init__.py +1 -0
  23. esphome/components/host/__init__.py +1 -0
  24. esphome/components/ld2410/ld2410.cpp +12 -28
  25. esphome/components/libretiny/__init__.py +1 -0
  26. esphome/components/mqtt/mqtt_backend_esp32.cpp +6 -2
  27. esphome/components/packet_transport/packet_transport.cpp +3 -0
  28. esphome/components/rp2040/__init__.py +1 -0
  29. esphome/components/sx126x/__init__.py +3 -3
  30. esphome/components/sx127x/__init__.py +2 -2
  31. esphome/components/usb_host/usb_host_client.cpp +10 -10
  32. esphome/components/usb_uart/cp210x.cpp +1 -1
  33. esphome/components/usb_uart/usb_uart.cpp +41 -44
  34. esphome/components/usb_uart/usb_uart.h +4 -3
  35. esphome/const.py +1 -1
  36. esphome/core/component_iterator.cpp +4 -2
  37. esphome/core/component_iterator.h +3 -3
  38. esphome/core/defines.h +1 -1
  39. esphome/core/entity_helpers.py +6 -0
  40. esphome/core/scheduler.cpp +9 -12
  41. esphome/core/scheduler.h +0 -3
  42. esphome/wizard.py +1 -1
  43. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/METADATA +2 -2
  44. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/RECORD +48 -49
  45. esphome/components/api/api_pb2_size.h +0 -359
  46. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/WHEEL +0 -0
  47. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/entry_points.txt +0 -0
  48. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/licenses/LICENSE +0 -0
  49. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@
4
4
  #include "esphome/core/helpers.h"
5
5
  #include "esphome/core/log.h"
6
6
 
7
+ #include <cassert>
7
8
  #include <vector>
8
9
 
9
10
  #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
@@ -59,7 +60,6 @@ class ProtoVarInt {
59
60
  uint32_t as_uint32() const { return this->value_; }
60
61
  uint64_t as_uint64() const { return this->value_; }
61
62
  bool as_bool() const { return this->value_; }
62
- template<typename T> T as_enum() const { return static_cast<T>(this->as_uint32()); }
63
63
  int32_t as_int32() const {
64
64
  // Not ZigZag encoded
65
65
  return static_cast<int32_t>(this->as_int64());
@@ -133,15 +133,24 @@ class ProtoVarInt {
133
133
  uint64_t value_;
134
134
  };
135
135
 
136
+ // Forward declaration for decode_to_message and encode_to_writer
137
+ class ProtoMessage;
138
+
136
139
  class ProtoLengthDelimited {
137
140
  public:
138
141
  explicit ProtoLengthDelimited(const uint8_t *value, size_t length) : value_(value), length_(length) {}
139
142
  std::string as_string() const { return std::string(reinterpret_cast<const char *>(this->value_), this->length_); }
140
- template<class C> C as_message() const {
141
- auto msg = C();
142
- msg.decode(this->value_, this->length_);
143
- return msg;
144
- }
143
+
144
+ /**
145
+ * Decode the length-delimited data into an existing ProtoMessage instance.
146
+ *
147
+ * This method allows decoding without templates, enabling use in contexts
148
+ * where the message type is not known at compile time. The ProtoMessage's
149
+ * decode() method will be called with the raw data and length.
150
+ *
151
+ * @param msg The ProtoMessage instance to decode into
152
+ */
153
+ void decode_to_message(ProtoMessage &msg) const;
145
154
 
146
155
  protected:
147
156
  const uint8_t *const value_;
@@ -263,9 +272,6 @@ class ProtoWriteBuffer {
263
272
  this->write((value >> 48) & 0xFF);
264
273
  this->write((value >> 56) & 0xFF);
265
274
  }
266
- template<typename T> void encode_enum(uint32_t field_id, T value, bool force = false) {
267
- this->encode_uint32(field_id, static_cast<uint32_t>(value), force);
268
- }
269
275
  void encode_float(uint32_t field_id, float value, bool force = false) {
270
276
  if (value == 0.0f && !force)
271
277
  return;
@@ -306,18 +312,7 @@ class ProtoWriteBuffer {
306
312
  }
307
313
  this->encode_uint64(field_id, uvalue, force);
308
314
  }
309
- template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) {
310
- this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
311
- size_t begin = this->buffer_->size();
312
-
313
- value.encode(*this);
314
-
315
- const uint32_t nested_length = this->buffer_->size() - begin;
316
- // add size varint
317
- std::vector<uint8_t> var;
318
- ProtoVarInt(nested_length).encode(var);
319
- this->buffer_->insert(this->buffer_->begin() + begin, var.begin(), var.end());
320
- }
315
+ void encode_message(uint32_t field_id, const ProtoMessage &value, bool force = false);
321
316
  std::vector<uint8_t> *get_buffer() const { return buffer_; }
322
317
 
323
318
  protected:
@@ -345,6 +340,494 @@ class ProtoMessage {
345
340
  virtual bool decode_64bit(uint32_t field_id, Proto64Bit value) { return false; }
346
341
  };
347
342
 
343
+ class ProtoSize {
344
+ public:
345
+ /**
346
+ * @brief ProtoSize class for Protocol Buffer serialization size calculation
347
+ *
348
+ * This class provides static methods to calculate the exact byte counts needed
349
+ * for encoding various Protocol Buffer field types. All methods are designed to be
350
+ * efficient for the common case where many fields have default values.
351
+ *
352
+ * Implements Protocol Buffer encoding size calculation according to:
353
+ * https://protobuf.dev/programming-guides/encoding/
354
+ *
355
+ * Key features:
356
+ * - Early-return optimization for zero/default values
357
+ * - Direct total_size updates to avoid unnecessary additions
358
+ * - Specialized handling for different field types according to protobuf spec
359
+ * - Templated helpers for repeated fields and messages
360
+ */
361
+
362
+ /**
363
+ * @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
364
+ *
365
+ * @param value The uint32_t value to calculate size for
366
+ * @return The number of bytes needed to encode the value
367
+ */
368
+ static inline uint32_t varint(uint32_t value) {
369
+ // Optimized varint size calculation using leading zeros
370
+ // Each 7 bits requires one byte in the varint encoding
371
+ if (value < 128)
372
+ return 1; // 7 bits, common case for small values
373
+
374
+ // For larger values, count bytes needed based on the position of the highest bit set
375
+ if (value < 16384) {
376
+ return 2; // 14 bits
377
+ } else if (value < 2097152) {
378
+ return 3; // 21 bits
379
+ } else if (value < 268435456) {
380
+ return 4; // 28 bits
381
+ } else {
382
+ return 5; // 32 bits (maximum for uint32_t)
383
+ }
384
+ }
385
+
386
+ /**
387
+ * @brief Calculates the size in bytes needed to encode a uint64_t value as a varint
388
+ *
389
+ * @param value The uint64_t value to calculate size for
390
+ * @return The number of bytes needed to encode the value
391
+ */
392
+ static inline uint32_t varint(uint64_t value) {
393
+ // Handle common case of values fitting in uint32_t (vast majority of use cases)
394
+ if (value <= UINT32_MAX) {
395
+ return varint(static_cast<uint32_t>(value));
396
+ }
397
+
398
+ // For larger values, determine size based on highest bit position
399
+ if (value < (1ULL << 35)) {
400
+ return 5; // 35 bits
401
+ } else if (value < (1ULL << 42)) {
402
+ return 6; // 42 bits
403
+ } else if (value < (1ULL << 49)) {
404
+ return 7; // 49 bits
405
+ } else if (value < (1ULL << 56)) {
406
+ return 8; // 56 bits
407
+ } else if (value < (1ULL << 63)) {
408
+ return 9; // 63 bits
409
+ } else {
410
+ return 10; // 64 bits (maximum for uint64_t)
411
+ }
412
+ }
413
+
414
+ /**
415
+ * @brief Calculates the size in bytes needed to encode an int32_t value as a varint
416
+ *
417
+ * Special handling is needed for negative values, which are sign-extended to 64 bits
418
+ * in Protocol Buffers, resulting in a 10-byte varint.
419
+ *
420
+ * @param value The int32_t value to calculate size for
421
+ * @return The number of bytes needed to encode the value
422
+ */
423
+ static inline uint32_t varint(int32_t value) {
424
+ // Negative values are sign-extended to 64 bits in protocol buffers,
425
+ // which always results in a 10-byte varint for negative int32
426
+ if (value < 0) {
427
+ return 10; // Negative int32 is always 10 bytes long
428
+ }
429
+ // For non-negative values, use the uint32_t implementation
430
+ return varint(static_cast<uint32_t>(value));
431
+ }
432
+
433
+ /**
434
+ * @brief Calculates the size in bytes needed to encode an int64_t value as a varint
435
+ *
436
+ * @param value The int64_t value to calculate size for
437
+ * @return The number of bytes needed to encode the value
438
+ */
439
+ static inline uint32_t varint(int64_t value) {
440
+ // For int64_t, we convert to uint64_t and calculate the size
441
+ // This works because the bit pattern determines the encoding size,
442
+ // and we've handled negative int32 values as a special case above
443
+ return varint(static_cast<uint64_t>(value));
444
+ }
445
+
446
+ /**
447
+ * @brief Calculates the size in bytes needed to encode a field ID and wire type
448
+ *
449
+ * @param field_id The field identifier
450
+ * @param type The wire type value (from the WireType enum in the protobuf spec)
451
+ * @return The number of bytes needed to encode the field ID and wire type
452
+ */
453
+ static inline uint32_t field(uint32_t field_id, uint32_t type) {
454
+ uint32_t tag = (field_id << 3) | (type & 0b111);
455
+ return varint(tag);
456
+ }
457
+
458
+ /**
459
+ * @brief Common parameters for all add_*_field methods
460
+ *
461
+ * All add_*_field methods follow these common patterns:
462
+ *
463
+ * @param total_size Reference to the total message size to update
464
+ * @param field_id_size Pre-calculated size of the field ID in bytes
465
+ * @param value The value to calculate size for (type varies)
466
+ * @param force Whether to calculate size even if the value is default/zero/empty
467
+ *
468
+ * Each method follows this implementation pattern:
469
+ * 1. Skip calculation if value is default (0, false, empty) and not forced
470
+ * 2. Calculate the size based on the field's encoding rules
471
+ * 3. Add the field_id_size + calculated value size to total_size
472
+ */
473
+
474
+ /**
475
+ * @brief Calculates and adds the size of an int32 field to the total message size
476
+ */
477
+ static inline void add_int32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
478
+ // Skip calculation if value is zero
479
+ if (value == 0) {
480
+ return; // No need to update total_size
481
+ }
482
+
483
+ // Calculate and directly add to total_size
484
+ if (value < 0) {
485
+ // Negative values are encoded as 10-byte varints in protobuf
486
+ total_size += field_id_size + 10;
487
+ } else {
488
+ // For non-negative values, use the standard varint size
489
+ total_size += field_id_size + varint(static_cast<uint32_t>(value));
490
+ }
491
+ }
492
+
493
+ /**
494
+ * @brief Calculates and adds the size of an int32 field to the total message size (repeated field version)
495
+ */
496
+ static inline void add_int32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
497
+ // Always calculate size for repeated fields
498
+ if (value < 0) {
499
+ // Negative values are encoded as 10-byte varints in protobuf
500
+ total_size += field_id_size + 10;
501
+ } else {
502
+ // For non-negative values, use the standard varint size
503
+ total_size += field_id_size + varint(static_cast<uint32_t>(value));
504
+ }
505
+ }
506
+
507
+ /**
508
+ * @brief Calculates and adds the size of a uint32 field to the total message size
509
+ */
510
+ static inline void add_uint32_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
511
+ // Skip calculation if value is zero
512
+ if (value == 0) {
513
+ return; // No need to update total_size
514
+ }
515
+
516
+ // Calculate and directly add to total_size
517
+ total_size += field_id_size + varint(value);
518
+ }
519
+
520
+ /**
521
+ * @brief Calculates and adds the size of a uint32 field to the total message size (repeated field version)
522
+ */
523
+ static inline void add_uint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
524
+ // Always calculate size for repeated fields
525
+ total_size += field_id_size + varint(value);
526
+ }
527
+
528
+ /**
529
+ * @brief Calculates and adds the size of a boolean field to the total message size
530
+ */
531
+ static inline void add_bool_field(uint32_t &total_size, uint32_t field_id_size, bool value) {
532
+ // Skip calculation if value is false
533
+ if (!value) {
534
+ return; // No need to update total_size
535
+ }
536
+
537
+ // Boolean fields always use 1 byte when true
538
+ total_size += field_id_size + 1;
539
+ }
540
+
541
+ /**
542
+ * @brief Calculates and adds the size of a boolean field to the total message size (repeated field version)
543
+ */
544
+ static inline void add_bool_field_repeated(uint32_t &total_size, uint32_t field_id_size, bool value) {
545
+ // Always calculate size for repeated fields
546
+ // Boolean fields always use 1 byte
547
+ total_size += field_id_size + 1;
548
+ }
549
+
550
+ /**
551
+ * @brief Calculates and adds the size of a fixed field to the total message size
552
+ *
553
+ * Fixed fields always take exactly N bytes (4 for fixed32/float, 8 for fixed64/double).
554
+ *
555
+ * @tparam NumBytes The number of bytes for this fixed field (4 or 8)
556
+ * @param is_nonzero Whether the value is non-zero
557
+ */
558
+ template<uint32_t NumBytes>
559
+ static inline void add_fixed_field(uint32_t &total_size, uint32_t field_id_size, bool is_nonzero) {
560
+ // Skip calculation if value is zero
561
+ if (!is_nonzero) {
562
+ return; // No need to update total_size
563
+ }
564
+
565
+ // Fixed fields always take exactly NumBytes
566
+ total_size += field_id_size + NumBytes;
567
+ }
568
+
569
+ /**
570
+ * @brief Calculates and adds the size of an enum field to the total message size
571
+ *
572
+ * Enum fields are encoded as uint32 varints.
573
+ */
574
+ static inline void add_enum_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
575
+ // Skip calculation if value is zero
576
+ if (value == 0) {
577
+ return; // No need to update total_size
578
+ }
579
+
580
+ // Enums are encoded as uint32
581
+ total_size += field_id_size + varint(value);
582
+ }
583
+
584
+ /**
585
+ * @brief Calculates and adds the size of an enum field to the total message size (repeated field version)
586
+ *
587
+ * Enum fields are encoded as uint32 varints.
588
+ */
589
+ static inline void add_enum_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
590
+ // Always calculate size for repeated fields
591
+ // Enums are encoded as uint32
592
+ total_size += field_id_size + varint(value);
593
+ }
594
+
595
+ /**
596
+ * @brief Calculates and adds the size of a sint32 field to the total message size
597
+ *
598
+ * Sint32 fields use ZigZag encoding, which is more efficient for negative values.
599
+ */
600
+ static inline void add_sint32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
601
+ // Skip calculation if value is zero
602
+ if (value == 0) {
603
+ return; // No need to update total_size
604
+ }
605
+
606
+ // ZigZag encoding for sint32: (n << 1) ^ (n >> 31)
607
+ uint32_t zigzag = (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
608
+ total_size += field_id_size + varint(zigzag);
609
+ }
610
+
611
+ /**
612
+ * @brief Calculates and adds the size of a sint32 field to the total message size (repeated field version)
613
+ *
614
+ * Sint32 fields use ZigZag encoding, which is more efficient for negative values.
615
+ */
616
+ static inline void add_sint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
617
+ // Always calculate size for repeated fields
618
+ // ZigZag encoding for sint32: (n << 1) ^ (n >> 31)
619
+ uint32_t zigzag = (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
620
+ total_size += field_id_size + varint(zigzag);
621
+ }
622
+
623
+ /**
624
+ * @brief Calculates and adds the size of an int64 field to the total message size
625
+ */
626
+ static inline void add_int64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value) {
627
+ // Skip calculation if value is zero
628
+ if (value == 0) {
629
+ return; // No need to update total_size
630
+ }
631
+
632
+ // Calculate and directly add to total_size
633
+ total_size += field_id_size + varint(value);
634
+ }
635
+
636
+ /**
637
+ * @brief Calculates and adds the size of an int64 field to the total message size (repeated field version)
638
+ */
639
+ static inline void add_int64_field_repeated(uint32_t &total_size, uint32_t field_id_size, int64_t value) {
640
+ // Always calculate size for repeated fields
641
+ total_size += field_id_size + varint(value);
642
+ }
643
+
644
+ /**
645
+ * @brief Calculates and adds the size of a uint64 field to the total message size
646
+ */
647
+ static inline void add_uint64_field(uint32_t &total_size, uint32_t field_id_size, uint64_t value) {
648
+ // Skip calculation if value is zero
649
+ if (value == 0) {
650
+ return; // No need to update total_size
651
+ }
652
+
653
+ // Calculate and directly add to total_size
654
+ total_size += field_id_size + varint(value);
655
+ }
656
+
657
+ /**
658
+ * @brief Calculates and adds the size of a uint64 field to the total message size (repeated field version)
659
+ */
660
+ static inline void add_uint64_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint64_t value) {
661
+ // Always calculate size for repeated fields
662
+ total_size += field_id_size + varint(value);
663
+ }
664
+
665
+ /**
666
+ * @brief Calculates and adds the size of a sint64 field to the total message size
667
+ *
668
+ * Sint64 fields use ZigZag encoding, which is more efficient for negative values.
669
+ */
670
+ static inline void add_sint64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value) {
671
+ // Skip calculation if value is zero
672
+ if (value == 0) {
673
+ return; // No need to update total_size
674
+ }
675
+
676
+ // ZigZag encoding for sint64: (n << 1) ^ (n >> 63)
677
+ uint64_t zigzag = (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
678
+ total_size += field_id_size + varint(zigzag);
679
+ }
680
+
681
+ /**
682
+ * @brief Calculates and adds the size of a sint64 field to the total message size (repeated field version)
683
+ *
684
+ * Sint64 fields use ZigZag encoding, which is more efficient for negative values.
685
+ */
686
+ static inline void add_sint64_field_repeated(uint32_t &total_size, uint32_t field_id_size, int64_t value) {
687
+ // Always calculate size for repeated fields
688
+ // ZigZag encoding for sint64: (n << 1) ^ (n >> 63)
689
+ uint64_t zigzag = (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
690
+ total_size += field_id_size + varint(zigzag);
691
+ }
692
+
693
+ /**
694
+ * @brief Calculates and adds the size of a string/bytes field to the total message size
695
+ */
696
+ static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, const std::string &str) {
697
+ // Skip calculation if string is empty
698
+ if (str.empty()) {
699
+ return; // No need to update total_size
700
+ }
701
+
702
+ // Calculate and directly add to total_size
703
+ const uint32_t str_size = static_cast<uint32_t>(str.size());
704
+ total_size += field_id_size + varint(str_size) + str_size;
705
+ }
706
+
707
+ /**
708
+ * @brief Calculates and adds the size of a string/bytes field to the total message size (repeated field version)
709
+ */
710
+ static inline void add_string_field_repeated(uint32_t &total_size, uint32_t field_id_size, const std::string &str) {
711
+ // Always calculate size for repeated fields
712
+ const uint32_t str_size = static_cast<uint32_t>(str.size());
713
+ total_size += field_id_size + varint(str_size) + str_size;
714
+ }
715
+
716
+ /**
717
+ * @brief Calculates and adds the size of a nested message field to the total message size
718
+ *
719
+ * This helper function directly updates the total_size reference if the nested size
720
+ * is greater than zero.
721
+ *
722
+ * @param nested_size The pre-calculated size of the nested message
723
+ */
724
+ static inline void add_message_field(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) {
725
+ // Skip calculation if nested message is empty
726
+ if (nested_size == 0) {
727
+ return; // No need to update total_size
728
+ }
729
+
730
+ // Calculate and directly add to total_size
731
+ // Field ID + length varint + nested message content
732
+ total_size += field_id_size + varint(nested_size) + nested_size;
733
+ }
734
+
735
+ /**
736
+ * @brief Calculates and adds the size of a nested message field to the total message size (repeated field version)
737
+ *
738
+ * @param nested_size The pre-calculated size of the nested message
739
+ */
740
+ static inline void add_message_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) {
741
+ // Always calculate size for repeated fields
742
+ // Field ID + length varint + nested message content
743
+ total_size += field_id_size + varint(nested_size) + nested_size;
744
+ }
745
+
746
+ /**
747
+ * @brief Calculates and adds the size of a nested message field to the total message size
748
+ *
749
+ * This version takes a ProtoMessage object, calculates its size internally,
750
+ * and updates the total_size reference. This eliminates the need for a temporary variable
751
+ * at the call site.
752
+ *
753
+ * @param message The nested message object
754
+ */
755
+ static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const ProtoMessage &message) {
756
+ uint32_t nested_size = 0;
757
+ message.calculate_size(nested_size);
758
+
759
+ // Use the base implementation with the calculated nested_size
760
+ add_message_field(total_size, field_id_size, nested_size);
761
+ }
762
+
763
+ /**
764
+ * @brief Calculates and adds the size of a nested message field to the total message size (repeated field version)
765
+ *
766
+ * @param message The nested message object
767
+ */
768
+ static inline void add_message_object_repeated(uint32_t &total_size, uint32_t field_id_size,
769
+ const ProtoMessage &message) {
770
+ uint32_t nested_size = 0;
771
+ message.calculate_size(nested_size);
772
+
773
+ // Use the base implementation with the calculated nested_size
774
+ add_message_field_repeated(total_size, field_id_size, nested_size);
775
+ }
776
+
777
+ /**
778
+ * @brief Calculates and adds the sizes of all messages in a repeated field to the total message size
779
+ *
780
+ * This helper processes a vector of message objects, calculating the size for each message
781
+ * and adding it to the total size.
782
+ *
783
+ * @tparam MessageType The type of the nested messages in the vector
784
+ * @param messages Vector of message objects
785
+ */
786
+ template<typename MessageType>
787
+ static inline void add_repeated_message(uint32_t &total_size, uint32_t field_id_size,
788
+ const std::vector<MessageType> &messages) {
789
+ // Skip if the vector is empty
790
+ if (messages.empty()) {
791
+ return;
792
+ }
793
+
794
+ // Use the repeated field version for all messages
795
+ for (const auto &message : messages) {
796
+ add_message_object_repeated(total_size, field_id_size, message);
797
+ }
798
+ }
799
+ };
800
+
801
+ // Implementation of encode_message - must be after ProtoMessage is defined
802
+ inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value, bool force) {
803
+ this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
804
+
805
+ // Calculate the message size first
806
+ uint32_t msg_length_bytes = 0;
807
+ value.calculate_size(msg_length_bytes);
808
+
809
+ // Calculate how many bytes the length varint needs
810
+ uint32_t varint_length_bytes = ProtoSize::varint(msg_length_bytes);
811
+
812
+ // Reserve exact space for the length varint
813
+ size_t begin = this->buffer_->size();
814
+ this->buffer_->resize(this->buffer_->size() + varint_length_bytes);
815
+
816
+ // Write the length varint directly
817
+ ProtoVarInt(msg_length_bytes).encode_to_buffer_unchecked(this->buffer_->data() + begin, varint_length_bytes);
818
+
819
+ // Now encode the message content - it will append to the buffer
820
+ value.encode(*this);
821
+
822
+ // Verify that the encoded size matches what we calculated
823
+ assert(this->buffer_->size() == begin + varint_length_bytes + msg_length_bytes);
824
+ }
825
+
826
+ // Implementation of decode_to_message - must be after ProtoMessage is defined
827
+ inline void ProtoLengthDelimited::decode_to_message(ProtoMessage &msg) const {
828
+ msg.decode(this->value_, this->length_);
829
+ }
830
+
348
831
  template<typename T> const char *proto_enum_to_string(T value);
349
832
 
350
833
  class ProtoService {
@@ -363,11 +846,11 @@ class ProtoService {
363
846
  * @return A ProtoWriteBuffer object with the reserved size.
364
847
  */
365
848
  virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0;
366
- virtual bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) = 0;
849
+ virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) = 0;
367
850
  virtual void read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
368
851
 
369
852
  // Optimized method that pre-allocates buffer based on message size
370
- bool send_message_(const ProtoMessage &msg, uint16_t message_type) {
853
+ bool send_message_(const ProtoMessage &msg, uint8_t message_type) {
371
854
  uint32_t msg_size = 0;
372
855
  msg.calculate_size(msg_size);
373
856
 
@@ -7,6 +7,7 @@
7
7
  #include "esphome/core/automation.h"
8
8
  #include "api_pb2.h"
9
9
 
10
+ #ifdef USE_API_SERVICES
10
11
  namespace esphome {
11
12
  namespace api {
12
13
 
@@ -73,3 +74,4 @@ template<typename... Ts> class UserServiceTrigger : public UserServiceBase<Ts...
73
74
 
74
75
  } // namespace api
75
76
  } // namespace esphome
77
+ #endif // USE_API_SERVICES
@@ -53,6 +53,7 @@ void DebugComponent::on_shutdown() {
53
53
  auto pref = global_preferences->make_preference(REBOOT_MAX_LEN, fnv1_hash(REBOOT_KEY + App.get_name()));
54
54
  if (component != nullptr) {
55
55
  strncpy(buffer, component->get_component_source(), REBOOT_MAX_LEN - 1);
56
+ buffer[REBOOT_MAX_LEN - 1] = '\0';
56
57
  }
57
58
  ESP_LOGD(TAG, "Storing reboot source: %s", buffer);
58
59
  pref.save(&buffer);
@@ -68,6 +69,7 @@ std::string DebugComponent::get_reset_reason_() {
68
69
  auto pref = global_preferences->make_preference(REBOOT_MAX_LEN, fnv1_hash(REBOOT_KEY + App.get_name()));
69
70
  char buffer[REBOOT_MAX_LEN]{};
70
71
  if (pref.load(&buffer)) {
72
+ buffer[REBOOT_MAX_LEN - 1] = '\0';
71
73
  reset_reason = "Reboot request from " + std::string(buffer);
72
74
  }
73
75
  }
@@ -707,6 +707,7 @@ async def to_code(config):
707
707
  cg.add_define("ESPHOME_VARIANT", VARIANT_FRIENDLY[config[CONF_VARIANT]])
708
708
 
709
709
  cg.add_platformio_option("lib_ldf_mode", "off")
710
+ cg.add_platformio_option("lib_compat_mode", "strict")
710
711
 
711
712
  framework_ver: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
712
713
 
@@ -308,7 +308,7 @@ async def to_code(config):
308
308
  cg.add(var.set_frame_buffer_count(config[CONF_FRAME_BUFFER_COUNT]))
309
309
  cg.add(var.set_frame_size(config[CONF_RESOLUTION]))
310
310
 
311
- cg.add_define("USE_ESP32_CAMERA")
311
+ cg.add_define("USE_CAMERA")
312
312
 
313
313
  if CORE.using_esp_idf:
314
314
  add_idf_component(name="espressif/esp32-camera", ref="2.0.15")
@@ -109,6 +109,7 @@ void ESP32TouchComponent::loop() {
109
109
 
110
110
  // Only publish if state changed - this filters out repeated events
111
111
  if (new_state != child->last_state_) {
112
+ child->initial_state_published_ = true;
112
113
  child->last_state_ = new_state;
113
114
  child->publish_state(new_state);
114
115
  // Original ESP32: ISR only fires when touched, release is detected by timeout
@@ -175,6 +176,9 @@ void ESP32TouchComponent::on_shutdown() {
175
176
  void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) {
176
177
  ESP32TouchComponent *component = static_cast<ESP32TouchComponent *>(arg);
177
178
 
179
+ uint32_t mask = 0;
180
+ touch_ll_read_trigger_status_mask(&mask);
181
+ touch_ll_clear_trigger_status_mask();
178
182
  touch_pad_clear_status();
179
183
 
180
184
  // INTERRUPT BEHAVIOR: On ESP32 v1 hardware, the interrupt fires when ANY configured
@@ -184,6 +188,11 @@ void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) {
184
188
  // as any pad remains touched. This allows us to detect both new touches and
185
189
  // continued touches, but releases must be detected by timeout in the main loop.
186
190
 
191
+ // IMPORTANT: ESP32 v1 touch detection logic - INVERTED compared to v2!
192
+ // ESP32 v1: Touch is detected when capacitance INCREASES, causing the measured value to DECREASE
193
+ // Therefore: touched = (value < threshold)
194
+ // This is opposite to ESP32-S2/S3 v2 where touched = (value > threshold)
195
+
187
196
  // Process all configured pads to check their current state
188
197
  // Note: ESP32 v1 doesn't tell us which specific pad triggered the interrupt,
189
198
  // so we must scan all configured pads to find which ones were touched
@@ -201,19 +210,12 @@ void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) {
201
210
  value = touch_ll_read_raw_data(pad);
202
211
  }
203
212
 
204
- // Skip pads with 0 value - they haven't been measured in this cycle
205
- // This is important: not all pads are measured every interrupt cycle,
206
- // only those that the hardware has updated
207
- if (value == 0) {
213
+ // Skip pads that aren’t in the trigger mask
214
+ bool is_touched = (mask >> pad) & 1;
215
+ if (!is_touched) {
208
216
  continue;
209
217
  }
210
218
 
211
- // IMPORTANT: ESP32 v1 touch detection logic - INVERTED compared to v2!
212
- // ESP32 v1: Touch is detected when capacitance INCREASES, causing the measured value to DECREASE
213
- // Therefore: touched = (value < threshold)
214
- // This is opposite to ESP32-S2/S3 v2 where touched = (value > threshold)
215
- bool is_touched = value < child->get_threshold();
216
-
217
219
  // Always send the current state - the main loop will filter for changes
218
220
  // We send both touched and untouched states because the ISR doesn't
219
221
  // track previous state (to keep ISR fast and simple)
@@ -180,6 +180,7 @@ async def to_code(config):
180
180
  cg.add(esp8266_ns.setup_preferences())
181
181
 
182
182
  cg.add_platformio_option("lib_ldf_mode", "off")
183
+ cg.add_platformio_option("lib_compat_mode", "strict")
183
184
 
184
185
  cg.add_platformio_option("board", config[CONF_BOARD])
185
186
  cg.add_build_flag("-DUSE_ESP8266")