Skip to content

Frontend Architecture

Willem edited this page Mar 24, 2026 · 6 revisions

Frontend Architecture

JavaScript card system

Ramses Extras uses an on-demand loading architecture for Lovelace UI components.

Bootstrap loading (main.js)

Instead of registering every custom card as a separate Lovelace resource, the integration registers a single bootstrap resource (main.js).

  • main.js uses a MutationObserver to watch for Ramses Extras custom element tags appearing in the DOM.
  • When a tag is detected, it uses dynamic import() to load the corresponding card module from its deployment path.

Result: dashboards only download the cards they actually use.

Base card pattern (RamsesBaseCard)

All Ramses Extras cards extend:

  • Source: custom_components/ramses_extras/framework/www/ramses-base-card.js
  • Deployed helper path: /local/ramses_extras/helpers/ramses-base-card.js?v={version}

The base card centralizes:

  • lifecycle (connectedCallback() / disconnectedCallback())
  • gating/validation and shared UX states
  • a consistent "subclass implements _renderContent()" pattern

Feature enablement and startup latches

Cards are gated by two backend-driven readiness mechanisms:

  • enabled_features
    • loaded via WebSocket (ramses_extras/default/get_enabled_features)
    • exposed at runtime as window.ramsesExtras.features
    • used by RamsesBaseCard.isFeatureEnabled()
  • cards_enabled
    • loaded via WebSocket (ramses_extras/default/get_cards_enabled)
    • prevents cards from rendering before backend startup completes

Option updates (live frontend refresh)

When the config entry options change, Ramses Extras fires a HA event ramses_extras_options_updated. The base card subscribes to this event and updates runtime globals such as:

  • window.ramsesExtras.features
  • window.ramsesExtras.debug
  • window.ramsesExtras.frontendLogLevel
  • window.ramsesExtras.logLevel
  • window.ramsesExtras.cardsEnabled

All card instances re-render after an update so feature-disabled placeholders appear/disappear immediately.

Deployment & versioning

To avoid stale browser caches, card assets are still deployed under /config/www/ramses_extras/v{version}/..., but the Lovelace bootstrap resource now uses a stable path with a cache-busting query string.

Only one Lovelace resource is registered:

  • URL: /local/ramses_extras/helpers/main.js?v=<version>
  • Type: module

Theme-adaptive styling

Cards and editors should use Home Assistant theme variables (e.g. --primary-text-color, --ha-card-background) so they render correctly across themes.

Visual editor registration requirements

To support HA’s visual editor, each card must provide:

  • static getConfigElement()
  • static getStubConfig()

And ensure the editor web component is registered exactly once.

Shared device helper pattern for editors

Editors should leverage the framework helpers introduced in card-services.js:

import {
  getAvailableDevices,
  normalizeDeviceDescriptor,
  filterDevicesBySlugs,
} from '../../helpers/card-services.js';
  • getAvailableDevices() calls the ramses_extras/get_available_devices WebSocket (which now returns device_type, slugs, and slug_label for each device).
  • normalizeDeviceDescriptor() produces consistent { id, label, slugs, slugLabel } objects so dropdowns show 12:345678 (FAN, HUM) instead of numeric placeholders.
  • filterDevicesBySlugs() lets cards restrict the list (e.g., HVAC editors pass ['FAN']).

All editors (Hello World, HVAC, etc.) should follow this pattern to ensure consistent UX and avoid duplicating slug-formatting logic.

Real-time message system

JavaScript message listener integration

The system provides real-time HVAC state updates through ramses_cc 31DA message handling:

// Auto-registration in base-card connectedCallback(), with optional feature hook
const messageHelper = getRamsesMessageBroker();
messageHelper.addListener(this, "32:153289", ["31DA", "10D0"]);

// Called automatically by helper when messages received
handle_31DA(messageData) {
    const hvacData = HvacFanCardHandlers.handle_31DA(this, messageData);
    this.updateFrom31DA(hvacData);
}

Non-hvac systems may require different listeners.

Message processing flow

31DA Message → RamsesMessageBroker → HvacFanCardHandlers.handle_31DA() → Card Update
     ↓                  ↓                          ↓                         ↓
Real-time     Route to correct           Extract/format             Immediate UI
HVAC Data     card                       data                       re-render

Translation system

Translations are feature-centric:

  • Integration translations: custom_components/ramses_extras/translations/
  • Feature translations: custom_components/ramses_extras/features/{feature}/www/{feature}/translations/

Template systems

JavaScript template system (frontend cards)

  • Location: features/{feature}/www/{feature}/templates/ directories
  • Purpose: Generate HTML dynamically for Home Assistant Lovelace cards. For example, generate all the commands and entities needed from only the device_id chosen when editing the card in the dashboard.
  • Structure: Modular template organization with possibility to separate files for header, controls, etc. for larger projects.
  • Const.py source of truth: Feature const.py defines entity mappings, template metadata, and WebSocket IDs; both frontend templates and backend mapping commands read from these constants so changes only need to be defined once.

Entity resolution in cards

Cards treat Home Assistant state (hass.states) as the source of truth. Entity IDs are derived using a feature-centric mapping layer.

getRequiredEntities() via WebSocket entity mappings

The base card provides an async getRequiredEntities() that loads entity IDs via:

  • WebSocket: ramses_extras/get_entity_mappings
  • Inputs:
    • device_id
    • feature_id (from getFeatureName())

On the backend, entity mappings are derived from each feature's const.py using:

  • Entity config dicts (e.g. *_SENSOR_CONFIGS, *_SWITCH_CONFIGS, etc. containing entity_template)
  • Or (when a feature is UI-focused) *_CONST["entity_mappings"]

The result is cached on the card instance (_cachedEntities) to avoid repeated WebSocket calls.

For the HVAC Fan Card, those mappings now include shared default-feature diagnostics such as fan_control_mode_entity, so the UI can display the current control source and manual-override state without hardcoding entity IDs.

This keeps frontend cards and editors from hardcoding entity IDs, while still allowing advanced cards to override getRequiredEntities() when needed.

On-demand card loading

The architecture ensures that .js files for specific cards are only loaded if they are actually needed on the current dashboard:

  1. Bootstrap Loader: main.js is the only file registered as a Lovelace resource.
  2. Tag Detection: It monitors the DOM for Ramses Extras custom card tags.
  3. Dynamic Import: When a tag is detected (e.g., a user navigates to a dashboard containing the HVAC card), the corresponding module is fetched via import().
  4. Resource Efficiency: This prevents the browser from fetching megabytes of unused card code on dashboards where they aren't displayed.

Back to: Home Prev: Home Assistant Integration Next: Development Guide

Clone this wiki locally