Skip to content

Caching & Performance

What OpenCCU-Loom caches, why it matters for boot time and CCU radio load, and the few levers you have to tune it.

Who this page is for

Operators who want fast restarts and a low CCU DutyCycle. You do not need to know the internals — this page explains the behaviour you can observe and the handful of knobs you can turn. Contributors changing a boot path should also read the code in internal/central and internal/store.

Why caching matters here

A Homematic CCU talks to most devices over a shared radio. Every value read that actually goes to the radio counts against the CCU's DutyCycle budget — read too much, too fast, and the CCU throttles or warns. OpenCCU-Loom is built around one rule: never read through the radio just to fill a cache. Everything it caches comes from a path that costs no radio — push events the CCU sends on its own, ReGa script reads served from the CCU's database, file-cache hits, or the daemon's own SQLite store.

The only radio traffic the daemon causes is for explicit actions you take: turning a device on, writing a paramset, or asking for a forced refresh.

What gets cached

Layer Survives restart? What it holds
Embedded static data n/a (compiled in) device profiles, paramset templates, translations, Matter schema
In-memory caches no — rebuilt on start last-observed values, hub catalogues (programs, sysvars, rooms), device details, visibility decisions
SQLite store (<data_dir>/openccu-loom.db) yes last-known values snapshot, MASTER values, paramset/device descriptors, audit log, sessions, Matter fabrics
Filesystem yes CCU descriptor backups, optional translation override

The key persistent layer for performance is the values cache in SQLite: at shutdown the daemon writes the last-known state of every data point, and on the next boot it restores that snapshot so the model is populated before the first CCU push — with zero radio cost.

Cold start vs. warm start

A "warm" start is the normal case: the CCU has been running, and the daemon's database carries a recent values snapshot.

Situation What boot looks like
Warm CCU + values cache present (normal) Device list, descriptors, and MASTER paramsets load from caches; values restore from SQLite. Effectively zero radio reads to reach a ready state.
Warm CCU + no cache (fresh install or wiped data_dir) The daemon fills the cache from push events and a bounded bootstrap read pass. Most of those reads are served from the CCU's own value database; DutyCycle ticks up but stays modest on a healthy CCU. The next restart is a warm start.
Cold CCU just rebooted The CCU re-polls its devices over the radio to rebuild its own state — that DutyCycle activity is CCU work, not daemon-induced. Expect a spike for several minutes after a CCU reboot.
Cold CCU + no daemon cache (worst case) Both sides are cold, so the daemon's bootstrap reads hit devices that the CCU also has no value for. Avoid this — if you wipe data_dir, wait until the CCU has been up at least a minute before starting the daemon.

After a CCU reboot, don't immediately restart the daemon

A freshly rebooted CCU is busy re-learning device state over the radio. Restarting the daemon during that window can push the combined load into the DutyCycle warning band. Let the CCU settle first.

Steady-state radio load

Once the daemon is ready, the only radio traffic it generates is from explicit actions:

Trigger Radio cost
Turn a device on/off (SPA, MQTT, REST) one write per action
Edit a MASTER or LINK paramset one write per action
Force a fresh value read one read per request
Push events from the CCU none (the CCU emits them on its own)
Connectivity ping none (service-level handshake only)

Some data is never pre-loaded and is fetched only on demand — for example LINK paramsets and MASTER values for hidden parameters are read only when the configuration UI asks for them.

Operator levers

There are deliberately few knobs.

  • Values-cache flush intervalpersistence.values_cache.flush_interval (default 60s). Trades the crash-loss window against the SQLite write rate. A shorter interval means more frequent writes but less data lost on an ungraceful kill.
  • Values-cache disable listpersistence.values_cache.disabled_centrals. Turns the persistent values cache off for a named central. Use only to work around a corrupted snapshot.
  • Clear all in-memory caches at runtime — the WebSocket command ccu.cache_clear drops the central's in-memory caches so the next read fetches fresh data from the CCU. Useful after you change device configuration directly on the CCU and want the daemon to re-read.

There is intentionally no "preload everything on boot" option — that would defeat the radio-cost guarantees above.

Where this data lives

  • Persistent caches and all daemon state: <data_dir>/openccu-loom.db (default <data_dir> is ./var). Back this up — see Backup & restore.
  • Optional on-disk translation override and CCU descriptor backups live under the data directory as well.

See also