Extend interrupt_pin support to MCP23008, MCP23017, MCP23S08, MCP23S17,
and PI4IOE5V6408 GPIO expander components. Same approach as PCF8574/PCA9554:
when configured, the component disables its loop and only wakes on interrupt,
and the cache stays valid between interrupts so binary sensors return from
cache instead of doing I2C/SPI reads every loop iteration.
For MCP23xxx, the interrupt_pin config and C++ logic lives in the shared
MCP23XXXBase template class, so all four variants (I2C 8/16-pin, SPI
8/16-pin) inherit it automatically.
Inspired by jesserockz's work in #11959 which proposed a more comprehensive
interrupt-driven approach with per-pin interrupt status register reading.
This implementation takes a simpler path by leveraging the existing
CachedGpioExpander cache invalidation mechanism.
The base class doesn't need to know about interrupts — it just controls
whether digital_read() self-invalidates cache entries. Rename the flag
and invert the polarity for clarity.
When interrupt_driven_ is set, digital_read() no longer self-invalidates
each pin's cache entry. The cache stays valid until reset_pin_cache_()
is called from loop() when an interrupt fires. This eliminates I2C reads
on every binary sensor loop iteration — only reading when pins actually
change.
Add interrupt_pin config option to PCF8574 and PCA9554 GPIO expander
components. When configured, the component disables its loop and only
wakes on interrupt, eliminating continuous I2C polling when no pins
have changed state.
Move cache invalidation into CachedGpioExpander::digital_read() itself,
triggered by detecting a new loop iteration via App.get_loop_component_start_time().
This eliminates the need for a dedicated loop() override in every GPIO expander
component that only existed to call reset_pin_cache_().
Removes loop() from: pcf8574, pca9554, tca9555, pi4ioe5v6408, pca6416a,
mcp23016, mcp23xxx_base. For sx1509, only the reset_pin_cache_() call is
removed (loop() is kept for keypad scanning).