diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx
index 3c2302a..2eafcf5 100644
--- a/src/content/docs/index.mdx
+++ b/src/content/docs/index.mdx
@@ -2,69 +2,159 @@
title: HAI Omni Pro II — omni-pca
description: Reverse-engineered Python library and Home Assistant integration for HAI/Leviton Omni Pro II home automation panels.
template: doc
+hero:
+ tagline: |
+ Async Python library and a drop-in Home Assistant integration for the
+ HAI/Leviton Omni-Link II protocol — clean-room reverse-engineered from
+ PC Access 3.17, complete with two non-public crypto quirks no other
+ public client implements.
+ actions:
+ - text: Add to Home Assistant
+ link: /how-to/install-in-home-assistant/
+ icon: right-arrow
+ variant: primary
+ - text: Decode your .pca file
+ link: /tutorials/decrypt-your-pca/
+ icon: external
+ variant: secondary
+ - text: Source on Gitea
+ link: https://git.supported.systems/warehack.ing/omni-pca
+ icon: external
+ variant: minimal
---
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
import { Image } from 'astro:assets';
+
import wordmark from '../../assets/manual/omnipro-ii-wordmark.png';
+import shotIntegrations from '../../assets/screenshots/02-integrations-list.png';
+import shotConfig from '../../assets/screenshots/03-omni-pca-config.png';
+import shotDevice from '../../assets/screenshots/04-panel-device.png';
+import shotStates from '../../assets/screenshots/06-developer-states.png';
-## What it is
+## In Home Assistant
-`omni-pca` is an async Python library and a matching Home Assistant custom
-component for HAI / Leviton **Omni Pro II**, **Omni IIe**, **Omni LTe**, and
-**Lumina** panels. It speaks Omni-Link II straight to the controller over TCP,
-opens an encrypted session, and surfaces the panel's typed object model — zones,
-units, areas, thermostats, buttons, programs, codes, messages — plus the
-unsolicited push-event stream the panel emits on state changes.
+One device per panel. Typed entities for every named object the controller
+knows about — alarm areas, lights and outputs, binary zones with bypass,
+thermostats with HVAC modes, programs and panel-button macros, plus a
+single `event` entity that relays the panel's typed push-event stream
+into HA automations. Push updates arrive within one TCP round-trip; a
+30-second poll backstops anything that didn't push.
-The protocol layer was built from a clean-room decompilation of HAI's PC Access
-3.17. Every opcode, byte layout, and crypto step is cited back to the source
-line in the decompiled C# (`clsOmniLinkConnection.cs`, `clsHAC.cs`, etc.).
+
+
+## In Python
+
+The library underneath is intentionally async-first and typed end-to-end:
+
+```python
+import asyncio
+from omni_pca import OmniClient
+
+async def main() -> None:
+ async with OmniClient(
+ host="192.168.1.9",
+ port=4369,
+ controller_key=bytes.fromhex("6ba7b4e9b4656de3cd7edd4c650cdb09"),
+ ) as panel:
+ info = await panel.get_system_information()
+ print(info.model_name, info.firmware_version)
+
+ async for event in panel.events():
+ print(event) # ZoneStateChanged, ArmingChanged, AlarmActivated, …
+
+asyncio.run(main())
+```
+
+What you get from the library:
+
+- **Full opcode coverage** — 104 v1 + 83 v2 message types, byte-exact to the
+ decompiled C# enums.
+- **21 typed status/properties dataclasses**, 26 typed `SystemEvent`
+ subclasses, no untyped bytes leaking past the framing layer.
+- **Stateful mock controller** for offline development. The same `MockPanel`
+ class powers the integration tests and the docker dev stack.
+- **Async-first** — `OmniClient` is an async context manager, `events()` is
+ an async iterator, no callback soup.
## Two non-public protocol quirks
The wire protocol — as actually implemented in PC Access 3.17 — has two
-non-public quirks that public Omni-Link clients miss. Without them the panel
-will accept your TCP connection, complete the unencrypted handshake, and then
+quirks that public Omni-Link clients miss. Without them the panel will
+accept your TCP connection, complete the unencrypted handshake, and then
silently drop you on the first encrypted message:
1. **Session key XOR mix.** The AES-128 session key is *not* the panel's
- `ControllerKey` directly. Bytes `[11..16)` of the ControllerKey are XORed
- with a 5-byte `SessionID` nonce that the controller sends in
- `ControllerAckNewSession`. Bytes `[0..11)` are the ControllerKey verbatim.
-2. **Per-block XOR pre-whitening before AES.** Before each 16-byte block is
- AES-encrypted, its first two bytes are XORed with the packet's 16-bit
- sequence number (high byte first). The same mask is applied to *every*
- block of the packet. Decrypt reverses it.
+ `ControllerKey` directly. Bytes `[11..16)` of the ControllerKey are
+ XORed with a 5-byte `SessionID` nonce that the controller sends in
+ `ControllerAckNewSession`. Bytes `[0..11)` are the ControllerKey
+ verbatim.
+2. **Per-block XOR pre-whitening before AES.** Before each 16-byte block
+ is AES-encrypted, its first two bytes are XORed with the packet's
+ 16-bit sequence number (high byte first). The same mask is applied
+ to *every* block of the packet. Decrypt reverses it.
Both are unambiguous in the decompiled C# (`clsOmniLinkConnection.cs:1886-1892`
and `:396-401`). Neither appears in `jomnilinkII`, `pyomnilink`, or any
-third-party Omni-Link writeup we found. See [the quirks
-explainer](/explanation/quirks/) for the full story.
+third-party Omni-Link writeup we found. See
+[the quirks explainer](/explanation/quirks/) for why they exist and the
+full visual breakdown.
-## Where to start
+## Read the in-depth content
+
+
+