The 16-bit cond/cond2 fields of a program record pack a 5-family
discriminator + a per-family selector + a per-family operand. The high
byte's bits 2-7 (i.e. (cond >> 8) & 0xFC) pick the family; the rest is
family-specific:
OTHER → bits 0-3 = MiscConditional value (DARK, AC_POWER_OFF, …)
ZONE → bits 0-7 = zone #; bit 9 = NOT_READY (1) / SECURE (0)
CTRL → bits 0-8 = unit #; bit 9 = ON (1) / OFF (0)
TIME → bits 0-7 = clock#; bit 9 = ENABLED (1) / DISABLED(0)
SEC → bits 8-11 = area #; bits 12-14 = SecurityMode;
bit 15 = arming-transition flag
(only when mode != 0)
The Sec family is the catch-all default (per clsText.cs:2226-2273 the
switch falls through to it from anything not Other/Zone/Ctrl/Time).
omni_pca.programs:
* New ConditionFamily IntEnum and MiscConditional IntEnum.
* New Condition frozen dataclass with decode classmethod, is_empty,
describe (renders with index-based labels for offline use).
* New Program.condition() and Program.condition2() helpers.
omni_pca top-level: re-exports Condition, ConditionFamily, MiscConditional.
Verified against the live fixture (330 defined programs):
cond family distribution: SEC=156, TIME=8, ZONE=4, CTRL=3, OTHER=3
cond2 family distribution: SEC=21, TIME=10
tests/test_programs.py (+24 cases):
* Parametrised per-family decode with worked examples from the docs.
* Arming-transition flag asserts (mode=Off + bit 15 is NOT arming).
* Program.condition()/condition2() integration.
* OTHER ignores high bits (PC Access sometimes leaves them set).
* u16 range validation.
* MiscConditional enum values match enuMiscConditional.cs.
Full suite: 463 passed, 1 skipped (was 439 / 1).
Source: clsText.GetConditionalText (clsText.cs:2224-2273) for the
decode logic; frmAutomationEditCondition.cs:615-2550 for the encoder.