Ryan Malloy 4ad20c9350 clients: iter_programs() for both v1 and v2 wire dialects
v2 path adds an iterator over UploadProgram with request_reason=1
("next defined after slot"), mirroring the C# ReadConfig loop at
clsHAC.cs:4985 (seed call) and 5331 (per-reply re-issue). The mock
panel now honours reason=1: walks state.programs for the next
slot strictly greater than the requested one, returns EOD when none.

v1 path wraps OmniConnectionV1.iter_streaming(UploadPrograms) and
decodes each ProgramData reply into a Program. The panel already
streams in slot-ascending order from the previous commit, so the
client just decodes-and-yields.

Both methods return AsyncIterator[Program] for HA-side consumption.
Tests cover populated and empty states for both dialects, plus the
raw v2 reason=1 semantics on a single request.
2026-05-12 19:07:42 -06:00
..