docs: Edit-programs-in-HA how-to + side-panel reference + MDX fixes

New how-to walks through the Omni Programs side panel end-to-end:
list view, filters, the detail panel, compact-form editing, the
clausal chain editor, structured-AND with Arg2-as-object, known
limits, and the websocket commands the panel speaks. Five
embedded screenshots from a real .pca fixture.

reference/ha-entities.md picks up a short callout near the top so
readers browsing entity docs don't miss the side panel.

program-format.mdx had three pre-existing unescaped '<' characters
('firmware <3.0' and '(family << 8)' inside a table) that were
breaking the MDX build. Replaced with 'before 3.0' and escaped
operators.
This commit is contained in:
Ryan Malloy 2026-05-17 13:06:30 -06:00
parent 7bbe1b4372
commit de02f10be7
8 changed files with 216 additions and 3 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

View File

@ -0,0 +1,206 @@
---
title: Edit panel programs from Home Assistant
description: Browse, search, clone, and edit the panel's built-in automation programs from a Home Assistant side panel — no PC Access required.
sidebar:
order: 5
---
import { Image } from 'astro:assets';
import shotList from '../../../assets/screenshots/programs-list.png';
import shotDetail from '../../../assets/screenshots/programs-detail.png';
import shotCompact from '../../../assets/screenshots/programs-editor-compact.png';
import shotChain from '../../../assets/screenshots/programs-editor-chain.png';
import shotStructured from '../../../assets/screenshots/programs-editor-structured.png';
The integration registers a side-panel item called **Omni Programs**
that lets you view and edit the panel's built-in automation programs
without opening PC Access. These are the **panel-side** programs that
run on the Omni controller itself (so they survive HA outages), as
opposed to HA automations which run inside HA.
Open Home Assistant in your browser. **Omni Programs** appears in the
left sidebar alongside Overview, Map, Energy, etc.
<Image src={shotList} alt="Omni Programs side panel showing a list of panel programs with WHEN/THEN summaries, trigger-type badges, and a search box" style="border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 6px; margin: 1rem 0;" />
## Browsing & filtering
The header shows the total program count (`330 programs` on the panel
this screenshot was taken against). Each row is one program — for
multi-record clausal chains the row shows the **head** slot and
collapses the chain into a single summary line.
- **Search** — typing into the search box filters by any visible text
in the summary (program names, object names, command labels).
- **Trigger-type chips** — `TIMED` / `EVENT` / `YEARLY` / `WHEN` / `AT`
/ `EVERY` / `REMARK`. Click to toggle each filter; combinations are
OR'd. The right-edge badge on each row tells you the trigger type
and condition count.
- **References filter** — programs referencing a specific object
(zone, unit, area, button, thermostat) can be filtered from the HA
entity page: open any zone/unit/etc. entity, scroll to *Related*,
and click the program count to jump into the side panel pre-filtered
to that object.
The list updates in real time as the panel pushes program changes —
no manual refresh needed.
## The detail panel
Click any row to open the detail panel on the right.
<Image src={shotDetail} alt="Detail panel for slot 1 showing structured-English breakdown 'WHEN OPEN BIG GAR is pressed AND IF Time clock 4 is disabled THEN Turn ON Unit 33025' and four action buttons: Fire now, Edit, Clone, Clear" style="border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 6px; margin: 1rem 0;" />
The structured-English breakdown is the same renderer that produces
the row summary, but expanded — one clause per line, conditions in
context.
Four actions:
- **Fire now** — executes the program's THEN action immediately,
bypassing any conditions or schedule. Equivalent to PC Access's
"Test" button.
- **Edit** — opens the inline editor (see below).
- **Clone…** — duplicates the program into a different slot. Prompts
for the target slot number; refuses to overwrite a non-empty slot.
- **Clear** — wipes the slot to all zeros (`FREE`). Confirmation
required; this is sent to the panel as `DownloadProgram` with a
zero body, matching PC Access's "Delete" behaviour.
## Editing a compact-form program
Compact-form programs (`TIMED`, `EVENT`, `YEARLY`, `REMARK`) fit
entirely in one 14-byte record — the editor lays them out as a single
form with trigger fields up top, the action in the middle, and up to
two inline AND-IF conditions at the bottom.
<Image src={shotCompact} alt="Compact-form program editor for an EVENT-triggered program showing Category (Button press), Button (#1 OPEN BIG GAR), Action (Turn ON unit #33025), and an inline AND-IF on Time clock 4 disabled" style="border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 6px; margin: 1rem 0;" />
The form adapts to the trigger type:
- **TIMED** — hour / minute (or sunrise/sunset offset) + days-of-week
bitmask.
- **EVENT** — a category dropdown (Button press / Zone state change /
Unit state change / Fixed event), then category-specific sub-fields.
For example, *Button press* shows a button picker; *Zone state
change* shows zone + becomes-state.
- **YEARLY** — month + day-of-month picker.
- **REMARK** — read-only in this release (see [known
limits](#known-limits) below).
The action section is a command picker plus a parameter input whose
shape follows the command. *Turn ON unit* shows a unit picker; *Arm
area Night* shows an area picker; *Execute button* shows a button
picker.
The two inline AND-IF slots each get a Family dropdown (Zone / Unit /
Time clock / Misc / Area in security mode) and per-family sub-fields.
Hit **Save** to write the program back to the panel via
`DownloadProgram`. The integration validates the form server-side
before sending: bad ranges produce a structured error, not a corrupt
program.
## Editing a multi-record chain
Programs with `prog_type` ≥ 5 (`WHEN` / `AT` / `EVERY`) are
**clausal chains** — one record per visual line, occupying sequential
slots in the program table. The editor presents the whole chain as
one logical unit even though it's stored across multiple slots.
<Image src={shotChain} alt="Chain editor for a WHEN program showing the trigger header (Zone state change / FRONT_DOOR / not ready), a Conditions section with one AND IF row (Unit state / LIVING_LAMP / ON), and an Actions section with a THEN row (Turn ON unit / #2 KITCHEN_OVERHEAD)" style="border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 6px; margin: 1rem 0;" />
The editor has three sections:
- **Trigger header** (top) — the `WHEN` / `AT` / `EVERY` record.
- **Conditions** — zero or more `AND IF` / `OR IF` rows. Use the
`+ AND IF` button to add a row to the current group, or `+ OR IF` to
start a new alternative group. Each row has an `×` button to remove
it.
- **Actions** — one or more `THEN` rows. Use `+ THEN` to add another
action; chains can fire multiple actions in sequence.
**Anti-trample** — when you Save, the editor writes only the slots
needed for the current chain layout. If you remove a condition or
action, the slots it used to occupy are explicitly cleared so no
half-stale records linger on the panel.
## Structured-AND conditions
Most AND IF rows use the **Traditional** form (family + instance +
operand — "Zone 5 is SECURE", "Unit 12 is ON", etc). But the panel
also supports **Structured** AND records that compare a typed field
against either a constant or another typed field:
> AND IF Thermostat 1 Current temperature **>** Thermostat 2 Current temperature
The editor renders these as a dedicated form with operator picker
and per-arg type pickers:
<Image src={shotStructured} alt="Structured-AND editor showing Arg1 (Thermostat #1 LIVING_ROOM Current temperature), Operator (>), and Arg2 (Thermostat #2 MASTER_BEDROOM Current temperature) — both sides are typed references rather than constants" style="border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 6px; margin: 1rem 0;" />
**Arg1** can be any of: Zone, Unit, Thermostat, Area, Time/Date.
Each type has its own field selector — for Thermostat that's
*Current temperature / Heat setpoint / Cool setpoint / System mode /
Humidity / …*; for Zone it's *Loop reading / Current state / …*.
**Operator** is one of: `==`, `!=`, `<`, `>`, `is odd`, `is even`,
`is multiple of`, `in (bitmask)`, `not in (bitmask)`. The unary
operators (`is odd` / `is even`) hide the Arg2 controls entirely.
**Arg2** can be:
- **Constant** (default for new structured rows) — a 0..65535 value.
Used for "TEMP > 70", "Zone.CurrentState == 1", etc.
- **A typed reference** — Zone / Unit / Thermostat / Area / TimeDate
with its own field selector. This is how you author cross-object
comparisons like "Thermostat 1 temp > Thermostat 2 temp" or
"Zone 1 reading > Zone 2 reading".
Switching Arg2 from Constant to a reference type re-populates the
form with the appropriate picker; switching back preserves the
numeric value.
For the wire format behind all this, see the
[Program record format reference](/reference/program-format/#structured-op-and-records-op--0).
## Known limits
The editor covers the common cases but a few program shapes are
deliberately presented read-only — they're preserved verbatim on
Save, you just can't reshape them in the form:
- **REMARK programs** — read-only. The `remark_id` → text lookup
table layout is documented but the editor doesn't expose it yet.
- **Exotic Arg1/Arg2 types** on structured-AND — `UserSetting`,
`Auxiliary`, `Audio`, `AccessControl`, `Message`, `System`. These
show as a read-only banner with an `×` remove button.
- **Non-zero `CompConst`** on structured-AND — rarely used; preserved
but not exposed as a form control.
- **Multi-panel installs** — the side panel currently shows the
first configured panel. If you have multiple Omni panels, the
other panels' programs aren't accessible from the side panel yet.
## Where the data lives
The side panel is a Lit web component (`<omni-panel-programs>`)
registered via Home Assistant's `panel_custom` integration. It talks
to the integration's websocket API directly — there's no separate
state machine, no caching, no debouncing. Each user action becomes
one websocket round-trip:
| Action | Websocket command |
|---|---|
| List view + filters | `omni_pca/programs/list` |
| Open detail panel | `omni_pca/programs/get` |
| Fire now | `omni_pca/programs/fire` |
| Save (compact) | `omni_pca/programs/write` |
| Save (chain) | `omni_pca/programs/write_chain` |
| Clone… | `omni_pca/programs/clone` |
| Clear | `omni_pca/programs/clear` |
All seven commands validate input server-side and produce structured
errors (no opaque tracebacks) on bad input. The websocket layer's
own tests cover the contract — see `tests/ha_integration/
test_program_websocket.py` in the source tree if you want to extend
the API surface.

View File

@ -10,6 +10,13 @@ refresh; live state propagates over the panel's unsolicited push channel
within one TCP round-trip, with a 30-second poll backstopping anything that within one TCP round-trip, with a 30-second poll backstopping anything that
didn't push. didn't push.
In addition to the entity catalogue below, the integration registers an
**Omni Programs** sidebar item — a dedicated UI for browsing, editing,
cloning, and firing the panel's built-in automation programs (the
panel-side rules that run on the controller itself, distinct from HA
automations). See the [edit-programs-in-ha
how-to](/how-to/edit-programs-in-ha/) for the walkthrough.
| Platform | Entity | Per | | Platform | Entity | Per |
|---|---|---| |---|---|---|
| `alarm_control_panel` | Area arm/disarm with code | discovered area | | `alarm_control_panel` | Area arm/disarm with code | discovered area |

View File

@ -347,7 +347,7 @@ The block fits in **one** 14-byte record. `prog_type` is `TIMED`,
- For `EVENT`, the event identifier replaces the calendar - For `EVENT`, the event identifier replaces the calendar
month/day at bytes 9-10 (see [the EVENT `Evt` u16 section](#also-for-event-bytes-910-are-an-event-identifier-not-a-date)). month/day at bytes 9-10 (see [the EVENT `Evt` u16 section](#also-for-event-bytes-910-are-an-event-identifier-not-a-date)).
This is how 100% of records in any panel running firmware <3.0 are This is how 100% of records in any panel running firmware before 3.0 are
encoded. It is also how PC Access encodes any block that fits the encoded. It is also how PC Access encodes any block that fits the
constraints, even on firmware ≥3.0 — see the simplification rules constraints, even on firmware ≥3.0 — see the simplification rules
in `frmAutomationEditBlock.cs:589 SimplifyLines`. in `frmAutomationEditBlock.cs:589 SimplifyLines`.
@ -398,7 +398,7 @@ The firmware gate is `Features.Add(MultiLinePrograms, 196608u)` in
`clsCapOMNI_PRO_II.cs:290` (the 24-bit value packs as `clsCapOMNI_PRO_II.cs:290` (the 24-bit value packs as
`major*65536 + minor*256 + revision` → 3.0.0). `major*65536 + minor*256 + revision` → 3.0.0).
When MultiLinePrograms is OFF (firmware <3.0): When MultiLinePrograms is OFF (firmware before 3.0):
- PC Access's `Or` toolbar button and "Add Comment Block" menu item - PC Access's `Or` toolbar button and "Add Comment Block" menu item
are disabled. are disabled.
@ -420,7 +420,7 @@ load on a real 2.16A panel.
| Byte(s) | Field | Notes | | Byte(s) | Field | Notes |
| --- | --- | --- | | --- | --- | --- |
| 0 | `prog_type` | = 5 | | 0 | `prog_type` | = 5 |
| 9-10 (BE u16) | event-id | `(family << 8) | instance` — same encoding as compact-form `EVENT`'s bytes 9-10 in wire form. **No** Mon/Day file-form swap. | | 9-10 (BE u16) | event-id | `(family \<\< 8) \| instance` — same encoding as compact-form `EVENT`'s bytes 9-10 in wire form. **No** Mon/Day file-form swap. |
| 1-8, 11-13 | zeros | (action lives in a separate `THEN` record) | | 1-8, 11-13 | zeros | (action lives in a separate `THEN` record) |
#### `AT` (ProgType=6) — single-occurrence time trigger #### `AT` (ProgType=6) — single-occurrence time trigger