Remove last_state_ from GPIOBinarySensorStore — it was always set to
the same value as state_ in the ISR and never written elsewhere, making
the comparison `new_state != last_state_` equivalent to
`new_state != state_`.
Move use_interrupt_ and interrupt_type_ into the store to fill the
padding freed by removing last_state_, eliminating all internal padding.
Store layout (12B, zero padding):
isr_pin_.arg_* 4B
component_* 4B
state_ 1B (volatile)
changed_ 1B (volatile)
use_interrupt_ 1B
interrupt_type_ 1B
Saves 4 bytes per GPIOBinarySensor instance (64B → 60B).
Move restore_mode_ (uint8_t) next to next_write_ and
is_transformer_active_ (both bool) so all three pack into
a single 4-byte slot. Previously restore_mode_ sat alone
at the end of the struct with 3 bytes of trailing padding.
Saves 4 bytes per LightState instance.
Save 16 bytes per GPIOSwitch instance (FixedVector<Switch*> 12B +
uint32_t 4B) by guarding interlock fields and logic behind
USE_GPIO_SWITCH_INTERLOCK, which is only defined when the interlock
config option is present.
- Test empty name returns 0
- Test deduplication returns same index
- Test overflow warns and returns 0
- Remove duplicate index==0 check in get_component_log_str() since
component_source_lookup() already handles it
- Drop 9-bit index scheme in favor of plain uint8_t (max 255 unique
source names, overflow warns in Python and returns 0)
- Add friend declarations for setup()/original_setup() so generated
code can call protected set_component_source_()
- Move get_component_log_str() out of line
- Fix test mocks to provide CORE.data dict
Replace 4-byte component_source_ pointer with a 9-bit index into a
PROGMEM lookup table generated by Python codegen. Source names are
deduplicated at codegen time (same pattern as EntityStringPool).
Shrink warn_if_blocking_over_ from uint16_t (ms) to uint8_t
(centiseconds), saturating at 255 (2550ms).
Saves 4 bytes per component instance (typical configs: 120-200 bytes).
Scope the deprecation warning suppression to just the constructor
and publish_state() instead of the entire file, so unrelated
deprecation warnings aren't accidentally hidden.
Avoids requiring T to be default-constructible, which would break
future instantiations with non-default-constructible state types.
Capture old_state before set_state_value() since the pointer aliases
subclass storage that gets overwritten.
Check state == new_state directly before calling virtual set_new_state.
Avoids the virtual dispatch chain (BinarySensor::set_new_state →
StatefulEntityBase::set_new_state → virtual get_state()) on the
common no-change path. Fixes -20% CodSpeed regression on
BinarySensorPublish_NoChange benchmark.