Skip to content

Connection Overlay System

Singleton: window.lcards.core.connectionOverlayServiceConsole API: window.lcards.connectionOverlay.*Source: src/core/services/ConnectionOverlayService.jsConfig UI: LCARdS Config Panel → Connectivity tab


Overview

The Connection Overlay Service monitors the Home Assistant WebSocket connection and displays a full-screen overlay whenever the frontend loses contact with the HA server. It is a BaseService singleton that is active on every page the LCARdS module is loaded — no card placement is required.

Key capabilities:

  • Automatic detection via two complementary signals (WS events + hass.connected flag)
  • Per-scope config via the ScopedSettings waterfall — device, user, or global overrides
  • Offline-first — reads a localStorage cache at construction, so the overlay appears immediately even after a hard browser refresh while HA is already offline
  • Two display modes — simple text message or a full custom HA card
  • Optional reconnection banner — brief confirmation overlay that auto-dismisses after reconnection
  • SEM integration — backdrop, canvas, and colour effect layers via ScreenEffectManager
  • Stackable with alert overlay — both overlays can be visible simultaneously; the connection overlay renders on top when both are active

Architecture

ConnectionOverlayService (BaseService singleton)

    ├─ Detection (two signals)
    │   ├─ hass.connection WS events  ('disconnected' / 'reconnected')  ← primary
    │   └─ hass.connected flag in updateHass()                          ← belt-and-suspenders

    ├─ Config waterfall (ScopedSettingsService)
    │   device scope → user scope → global scope → localStorage cache → built-in defaults

    └─ Portal (delegated to PortalOverlayManager, slot 'connection-overlay')
        └─ PortalOverlayManager → ScreenEffectManager portal (position:fixed, z:9100)

Key Files

FilePurpose
src/core/services/ConnectionOverlayService.jsCore singleton service
src/core/services/PortalOverlayManager.jsShared portal DOM lifecycle engine
src/core/services/ScopedSettingsConstants.jsFlat storage key constants (CONN_OVERLAY_*)
src/panels/components/lcards-connectivity-tab.jsConfig Panel UI
src/lcards.jswindow.lcards.connectionOverlay console API shim

Detection Strategy

Two complementary signals are monitored so that disconnection is caught reliably under all conditions:

  1. hass.connection WebSocket events (primary) — home-assistant-js-websocket fires 'disconnected' / 'reconnected' on the Connection object immediately when the underlying WebSocket closes or reopens. This fires regardless of dashboard activity, avoiding false positives from idle dashboards (no state changes → no updateHass calls is normal and is not a disconnect signal).

  2. hass.connected flag (belt-and-suspenders) — HA propagates the hass.connected boolean on connect/disconnect transitions. updateHass() monitors it to catch any edge-cases where the WS event fires slightly outside the updateHass call cycle.

The service subscribes to hass.connection events each time it sees a new connection object, and unsubscribes from the old one. Subscription is idempotent — re-subscribing to the same connection object is skipped.


Config Schema

Resolved config shape

js
{
  enabled:  true,          // Whether the overlay is active at all
  dismiss:  true,          // Whether clicking the backdrop dismisses it
  position: 'center',      // Content anchor: 'center' | 'top' | 'top-left' | 'bottom-right' | ...
  width:    'auto',        // CSS width for the content container ('auto' = size to content)
  height:   'auto',        // CSS height for the content container

  message: {
    mode:      'text',         // 'text' | 'card'
    text:      'Connection Lost',
    color:     '#93e1ff',
    font:      'Antonio',      // lcards font registry key
    size:      26,             // px
    weight:    '400',          // CSS font-weight
    transform: 'uppercase',    // CSS text-transform
  },

  reconnected: {
    enabled:              false,
    text:                 'Connection Restored',
    color:                '#4caf50',
    auto_dismiss_seconds: 3,
    font:      'Antonio',
    size:      26,
    weight:    '400',
    transform: 'uppercase',
    content:   null,           // Optional HA card config (card mode)
  },

  layers: {
    backdrop: null,            // null = disabled, or { preset, ...params }
    color:    { preset: 'color-tint', color: 'rgba(0,0,0,0.55)' },
    canvas:   { preset: 'static', intensity: 0.45 },
  },

  content: null,               // Optional HA card config when mode = 'card'
}

Position values

ValueAnchor
center (default)Centred horizontally and vertically
top / top-centerTop edge, horizontally centred
top-leftTop-left corner
top-rightTop-right corner
left / left-centerLeft edge, vertically centred
right / right-centerRight edge, vertically centred
bottom / bottom-centerBottom edge, horizontally centred
bottom-leftBottom-left corner
bottom-rightBottom-right corner

Config Resolution — Scoped Waterfall

Config is stored as 23 flat keys in ScopedSettingsService (one per independently overridable field). Resolution order, first non-null wins:

Device scope → User scope → Global scope → localStorage cache → Built-in defaults

Flat keys are independent — a device-scoped colour does not clobber a global-scoped text value. This allows kiosks or individual users to override only the fields they care about.

The localStorage cache (key: lcards_connection_overlay_config) is written every time the full waterfall is read and is loaded synchronously at service construction. This means the overlay uses the last-known config immediately on page load, even if HA is already offline.

Flat key constants (from ScopedSettingsConstants.js)

ConstantKey stringField
CONN_OVERLAY_ENABLEDconn_overlay_enabledenabled
CONN_OVERLAY_DISMISSconn_overlay_dismissdismiss
CONN_OVERLAY_POSITIONconn_overlay_positionposition
CONN_OVERLAY_WIDTHconn_overlay_widthwidth
CONN_OVERLAY_HEIGHTconn_overlay_heightheight
CONN_OVERLAY_CONTENTconn_overlay_contentcontent
CONN_OVERLAY_SEMconn_overlay_semlayers (entire object)
CONN_OVERLAY_MSG_MODEconn_overlay_msg_modemessage.mode
CONN_OVERLAY_MSG_TEXTconn_overlay_msg_textmessage.text
CONN_OVERLAY_MSG_COLORconn_overlay_msg_colormessage.color
CONN_OVERLAY_MSG_FONTconn_overlay_msg_fontmessage.font
CONN_OVERLAY_MSG_SIZEconn_overlay_msg_sizemessage.size
CONN_OVERLAY_MSG_WEIGHTconn_overlay_msg_weightmessage.weight
CONN_OVERLAY_MSG_TRANSFORMconn_overlay_msg_transformmessage.transform
CONN_OVERLAY_RECON_ENABLEDconn_overlay_recon_enabledreconnected.enabled
CONN_OVERLAY_RECON_TEXTconn_overlay_recon_textreconnected.text
CONN_OVERLAY_RECON_COLORconn_overlay_recon_colorreconnected.color
CONN_OVERLAY_RECON_DISMISS_SECSconn_overlay_recon_dismiss_secsreconnected.auto_dismiss_seconds
CONN_OVERLAY_RECON_FONTconn_overlay_recon_fontreconnected.font
CONN_OVERLAY_RECON_SIZEconn_overlay_recon_sizereconnected.size
CONN_OVERLAY_RECON_WEIGHTconn_overlay_recon_weightreconnected.weight
CONN_OVERLAY_RECON_TRANSFORMconn_overlay_recon_transformreconnected.transform
CONN_OVERLAY_RECON_CONTENTconn_overlay_recon_contentreconnected.content

Portal Structure

The overlay is managed entirely by PortalOverlayManager (POM) under the named slot 'connection-overlay'. ConnectionOverlayService calls pom.show('connection-overlay', options) and pom.hide('connection-overlay') — it does not manage any DOM directly.

POM injects the following structure into ScreenEffectManager's shared position:fixed portal (z-index:9100, appended to document.body):

ScreenEffectManager portal  (position:fixed, inset:0, z:9100)
  ├─ SEM slot elements          (backdrop, canvas, color)

  ├─ [slot: 'alert-overlay']    ← when alert overlay is also active
  │   ├─ dismissEl  (z:10)
  │   └─ wrapperEl  (z:11)

  └─ [slot: 'connection-overlay']
      ├─ dismissEl              (position:absolute, inset:0, z:10)  — click-to-dismiss
      └─ wrapperEl              (position:absolute, inset:0, z:11, display:flex)
          └─ contentContainer   (flex child — width/height from config)
              └─ content card element  OR  text <div>

Slots are independent DOM subtrees. When both overlays are active, the connection overlay renders on top because its elements are appended after the alert overlay's elements.


SEM Layer Ownership

When the overlay is visible with layers configured, PortalOverlayManager acquires the backdrop, canvas, and color SEM slots on behalf of the 'connection-overlay' slot. Only one POM slot can own the SEM effect slots at a time (last-in-wins). All three slots are explicitly cleared when the slot is hidden — even if only some were occupied — to avoid leaving stale effects.

POM calls sem.setOverlayOccupied(true) as soon as any slot is shown, and sem.setOverlayOccupied(false) when all slots have been hidden.


Alert Overlay Co-existence

Both the connection overlay and the alert overlay use PortalOverlayManager with independent named slots ('connection-overlay' and 'alert-overlay'). They stack freely — there is no suppression logic. If an alert is active and HA disconnects, the connection overlay appears on top of the alert overlay (last appendChild wins at equal z-index).

The SEM effect slots are shared (last-in-wins): the connection overlay's layers config will evict the alert overlay's SEM effects if both are active simultaneously. If the connection overlay is configured without layers (text or card mode only), the alert backdrop effects are unaffected.


Reconnection Banner

When config.reconnected.enabled is true, a brief confirmation overlay is shown after reconnection instead of immediately removing the portal:

  1. The disconnect content is unmounted
  2. Either a custom card (config.reconnected.content) or a plain text <div> is rendered
  3. A timer fires after auto_dismiss_seconds and calls _removePortal()

The banner uses PortalOverlayManager under the 'connection-overlay' slot — the existing slot is replaced (last-in-wins). No additional DOM elements are created.


Display Modes

Text mode (message.mode = 'text')

A plain <div> is rendered in the content container using inline styles only (no CSS custom properties). This ensures the overlay remains styled correctly while disconnected, when the HA theme system may not be fully operational.

Styling fields: text, color, font (resolved via font registry), size (px), weight, transform.

Card mode (message.mode = 'card')

An arbitrary HA card config (config.content) is mounted as a custom element via ha-card-factory. The card element receives hass updates via applyHassToCard() on every updateHass() call. width and height from config are applied to the content container so the card fills a predictable area.


Console API

js
// Force-show the overlay (useful for testing without disconnecting)
window.lcards.connectionOverlay.show()

// Show with a temporary preview config (not persisted; restored on hide)
window.lcards.connectionOverlay.showWith({ message: { text: 'Testing...' } })

// Hide the overlay
window.lcards.connectionOverlay.hide()

// Read the currently active resolved config
window.lcards.connectionOverlay.getConfig()

// Save config to global scope (default)
await window.lcards.connectionOverlay.saveConfig({
  message: { text: 'No Connection', color: '#ff9900' }
})

// Save to device scope only (overrides for this browser)
await window.lcards.connectionOverlay.saveConfig(
  { enabled: false },
  'device'
)

// Remove all overrides from device scope (falls back to user/global)
await window.lcards.connectionOverlay.clearConfig('device')

// Reload config from the scoped waterfall
await window.lcards.connectionOverlay.loadConfig()

Relationship to Other Systems

SystemRelationship
PortalOverlayManagerOwns all portal DOM for the connection overlay. ConnectionOverlayService calls pom.show/hide('connection-overlay') and has no direct DOM dependencies.
ScreenEffectManagerProvides the shared position:fixed portal and named effect slots. POM acquires/releases SEM slots on behalf of the connection overlay.
ScopedSettingsServiceStores the 23 flat config keys at device/user/global scope. Waterfall resolution is done in loadConfig() via parallel sss.read() calls.
Alert overlay (lcards-alert-overlay)Both overlays use POM and stack independently. The connection overlay renders on top when both are active. SEM effect slots are last-in-wins.
AssetManagerFont keys in config (e.g. 'Antonio') are resolved to CSS font-family strings via assetManager.getRegistry('font'). Falls back to <key>, sans-serif if the registry is unavailable.
Config Panel (lcards-connectivity-tab)UI tab that reads/writes config via the window.lcards.connectionOverlay API. Supports scope switching (device/user/global) and per-field override badges. Includes "Simulate Disconnect" and "Clear Test" buttons that call showWith() / hide().