Skip to content

MSD Card

custom:lcards-msd-card

Master Systems Display — a zoomable SVG canvas on which you position any Home Assistant card as an overlay. Lines (routes) connect anchors across the canvas, and shapes (polylines, rectangles, circles) add freeform decorative or structural geometry — rooms, zones, conduits. Supports rules-based automation of overlay styles and base SVG filters.

New to MSD?

This page is the config reference. For a UI-only walkthrough of building your first MSD (no YAML) using MSD Studio, see Quick Start.


Minimal Example

yaml
type: custom:lcards-msd-card
msd:
  base_svg:
    source: builtin:ncc-1701-a-blue
  anchors:
    bridge: [520, 380]
    engineering: [620, 520]
  overlays:
    - id: bridge-status
      type: control
      anchor: bridge
      size: [180, 80]
      card:
        type: custom:lcards-button
        entity: light.bridge
        preset: lozenge
    - id: engineering-line
      type: line
      route: auto
      anchor: bridge
      attach_to: engineering

Top-Level Options

OptionTypeDescription
typestringcustom:lcards-msd-card (required)
msdobjectFull MSD configuration (required)
idstringCard ID for rule targeting
tagslistTags for rule targeting
ruleslistRules for dynamic overlay styling — see Rules Engine
data_sourcesobjectdata source definitions — see Data Sources

msd Object

OptionTypeDescription
base_svgobjectSVG source and filters (required)
view_boxstring / array"auto" or [minX, minY, width, height]
anchorsobjectNamed [x, y] anchor points for overlay placement
overlayslistControl, line, and shape overlays — see below
routingobjectGlobal line routing settings — see below
channelsobjectNamed routing corridors for guiding/bundling lines — see below
animationslistBulk-target a group of overlays with one animation, by CSS selector — see below

base_svg Object

FieldTypeDescription
sourcestringSVG source — builtin:<name>, /local/path.svg, a media-source://… content ID picked via the HA media library, or none
filterslistCSS/SVG filters (opacity, blur, brightness, tint, etc.) — see Base SVG Filters
render_visualbooleanDefault true. Set false to hide the SVG as the visible background (e.g. to use background_animation instead) while still parsing it for anchors.
harvest_landmarksbooleanDefault true. Computes geometric landmark anchors (hull_center, extremity_bow/extremity_stern/extremity_top/extremity_bottom, lateral_a/lateral_b) from the SVG's own silhouette — see Automatic Anchors below.
harvest_svg_elementsbooleanDefault true. Harvests anchors from any named <circle>/<text>/<g> element already present in the SVG markup — see Automatic Anchors below.
animationslistAnimations targeting elements inside the base SVG, by id/class — same target/targets selector syntax as Card Animations. Not to be confused with msd.animations (below), which targets overlays, not base-SVG internals.

In MSD Studio's Base SVG tab, the "Browse HA Media" source mode lets you pick an SVG uploaded to Home Assistant's media library, filtered to SVG's actual MIME type (image/svg+xml) so only SVG files show up — alongside the existing Asset Library (built-in ships) and Custom Path (typed /local/… or URL) modes.

Filters

Filters shift the visual weight of the base SVG so overlays stand out, or apply a color tint. See Base SVG Filters for the full type reference and a set of copy-paste recipes (dimmed, subtle, backdrop, faded, red wash, monochrome).

yaml
base_svg:
  source: builtin:ncc-1701-a-blue
  filters:
    opacity: 0.5
    brightness: 0.8

Rules can change filters dynamically — see the rules example below.


anchors Object

Named points used for overlay placement and line routing. Values are [x, y] in SVG user units (matching the SVG viewBox). Percentages are also accepted.

yaml
anchors:
  bridge: [520, 380]
  engineering: [620, 520]
  sickbay: ["40%", "55%"]   # Percentage of viewBox dimensions

Automatic Anchors

Anchors listed under anchors: aren't the only ones available — every base_svg also contributes two automatically-derived sources, both on by default:

  • Computed landmarks (base_svg.harvest_landmarks) — a fixed set of geometric anchors derived from the SVG's own silhouette: hull_center, extremity_bow, extremity_stern, extremity_top, extremity_bottom, and (on twin-hull shapes, e.g. nacelles) lateral_a/lateral_b. Useful for attaching overlays to a blueprint that has no hand-placed anchors at all.
  • Harvested SVG elements (base_svg.harvest_svg_elements) — any named <circle id="...">, <ellipse id="...">, <text id="...">, <rect id="...">, or <g id="..."> element already present in the SVG markup is exposed as an anchor under that element's own id (rect uses its bounding-box center; the others use their native position). Since these ids often come straight from the design tool that exported the SVG, they can be arbitrary/auto-numbered (e.g. g293) rather than meaningful names.

All three sources merge into one anchor set with this precedence — later wins on a name collision:

computed landmarks  <  harvested SVG elements  <  anchors: (this section)

So defining bridge: [520, 380] under anchors: always overrides a same-named computed or harvested anchor. Set base_svg.harvest_landmarks: false and/or base_svg.harvest_svg_elements: false to disable either automatic source entirely (e.g. to silence a noisy harvested-element list).

In MSD Studio's Anchors tab, the two automatic sources are shown in their own read-only "Base SVG: Computed" and "Base SVG: Harvested" sections (search/filter both by name), alongside the toggle switches above. Each entry has a Promote to User Anchor action that opens the Add Anchor form pre-filled with its current position — useful for renaming a harvested g293 into something meaningful before it becomes a real, editable entry under anchors:. A Promote All action does the same in bulk for everything currently matching the active filter.


Overlay Types

Control Overlay

Embeds any HA card at a position on the canvas.

FieldTypeDescription
idstringOverlay ID — required; used for rule targeting
typestringcontrol
anchorstringAnchor name to centre on
positionarrayExplicit [x, y] position (overrides anchor)
sizearray[width, height] in viewBox units
cardobjectAny HA card config
z_indexnumberStacking order (higher = in front)
lockedbooleanEditor-only: disables drag/resize in MSD Studio
tagslistTags for rule targeting
yaml
- id: engine-status
  type: control
  anchor: engineering
  size: [200, 90]
  z_index: 10
  card:
    type: custom:lcards-button
    entity: sensor.warp_core_status
    preset: lozenge

Full reference: Control Overlay — attachment points, card: examples, rules integration.

Line Overlay

Routes a line between two anchors on the canvas.

FieldTypeDescription
idstringLine ID (required)
typestringline
anchorstringSource anchor name
attach_tostringTarget anchor name
routestringRouting algorithm — see table below
waypointslistIntermediate [x, y] points or anchor names
route_hintstringInitial segment direction: xy (horizontal first) or yx
corner_stylestringmiter, round, or bevel
corner_radiusnumberRadius for round corners in viewBox units
route_channelslistChannel IDs this line routes through
clearancenumberMin clearance around obstacles in viewBox units

Routing Algorithms

route valueDescription
autoRecommended default — always full pathfinding: obstacle avoidance, trunk bundling, crossing avoidance
directStraight line
manhattanL-shaped (single bend) — advanced opt-out, no bundling/crossing avoidance
smartWhat auto resolves to — A* pathfinding plus a refinement pass
gridSame as smart without the refinement pass — advanced opt-out
manualExplicit waypoints list
yaml
- id: power-line
  type: line
  anchor: engineering
  attach_to: bridge
  route: manhattan
  route_hint: yx
  corner_style: round
  corner_radius: 6

Full reference: Line Overlay — attachment sides, gaps, dynamic/state-based color, gradients, patterns, markers, animations.

Shape Overlay

Freeform geometry — polylines, rectangles, circles — for rooms, zones, conduits, and other decorative or structural shapes. Shares its complete styling system with the line overlay (dashed strokes, state-based color/fill, animations), and lines can attach to a shape's corners or vertices the same way they attach to controls.

FieldTypeDescription
idstringShape ID (required)
typestringshape
kindstringpolyline, rect, or circle
pointslistpolyline only: ordered [x, y]/anchor-name vertices
position / sizearrayrect/circle only: same convention as control overlays
lockedbooleanEditor-only: disables drag/resize/vertex-editing in MSD Studio
yaml
- id: engineering-bay
  type: shape
  kind: rect
  position: [560, 460]
  size: [200, 140]
  corner_style: round
  corner_radius: 12
  style:
    color: var(--lcars-orange)
    width: 2
    fill: alpha(var(--lcars-orange), 0.12)

Full reference: Shape Overlay — all three kinds, attachment points, drawing/editing in MSD Studio.


routing Object

Global routing defaults that apply to all lines unless overridden per-line. The most commonly tuned:

FieldTypeDefaultDescription
default_modestringautoCard-wide routing mode override for lines that don't set route
clearancenumber0Global obstacle clearance in viewBox units
trunk_bundling_enabledbooleantrueNearby parallel lines bundle into shared trunks
trunk_line_spacingnumber8Lane gap between bundled lines (viewBox units)
trunk_proximitynumber32How close lines must run to bundle (viewBox units)
crossing_avoid_enabledbooleantrueLines avoid crossing each other when a small detour suffices
crossing_avoid_biasnumber4Crossing penalty — higher accepts longer detours

New to routing? Routing Concepts covers the mental model first. Full reference — including channels, bundling behavior, and every advanced knob: Line Routing & Channels.


channels Object

Named routing corridors that guide/force lines through authored regions, with automatic lane separation. Defined directly under msd: (not under routing:):

yaml
type: custom:lcards-msd-card
msd:
  view_box: [0, 0, 600, 300]
  channels:
    main_bus:
      bounds: [250, 90, 300, 20]
      mode: force                # prefer | avoid | force
      direction: horizontal
      line_spacing: 10
  overlays: []

Lines opt in with route_channels: [main_bus]. See Line Routing & Channels.


animations (Overlay Group) Object

Bulk-target a group of overlays with one animation declaration, by CSS selector against data-overlay-id (or any other attribute/class) — instead of repeating the same animations: entry on every overlay in the group. Defined directly under msd:, sibling to overlays/base_svg:

yaml
msd:
  animations:
    - trigger: on_load
      preset: draw
      target: '[data-overlay-id^="shield_"] [data-animatable="true"]'
      duration: 800

This matches shape, line, and control overlays uniformly — the selector runs against the shared overlay container, so it doesn't care what kind of overlay it lands on. For control overlays embedding a non-LCARdS HA card, this animates the control's own positioned wrapper (opacity/transform/glow-style effects), not the embedded card's internals — LCARdS has no access to an arbitrary HA card's own DOM.

Each entry uses the exact same field set as base_svg.animations and per-overlay animations: — see Card Animations for the full trigger/preset/duration/ease/loop/target/targets/etc. reference.

Target the animatable element, not the overlay's <g> wrapper. Every shape/line overlay renders as <g data-overlay-id="..."> wrapping its actual visible geometry (a <path>, <rect>, or <ellipse> marked data-animatable="true"). A selector matching only the wrapper ([data-overlay-id^="shield_"] alone) still animates somethingopacity/transform-based presets look correct, since those composite across the whole subtree regardless of which element they're set on — but color-based presets (stroke/fill, e.g. stagger-flash's trail_color) silently do nothing visible: stroke/fill are inherited SVG properties, and the inner geometry element already carries its own explicit stroke/fill (from the overlay's own style.color/style.fill), which always wins over an inherited value from an animated ancestor. Append [data-animatable="true"] to reach the real element (as in the example above) to avoid this — control overlays are the exception: the <foreignObject> itself is the correct target, with no descendant equivalent.

The MSD Studio editor's target/targets picker builds this correctly for you: pick "All Shapes overlays", "All Lines overlays", or "All Controls overlays" to bulk-target by overlay type, or "All "<prefix>_*" overlays" for any group of overlays sharing an id prefix before their first underscore — e.g. Shield Bubble's shield_fore/shield_starboard/shield_aft/shield_port (or shield_section_N) convention automatically offers an "All "shield_*" overlays" option once 2+ segments exist. These options are pre-aimed at the correct element (the animatable descendant for shape/line, the wrapper itself for controls), so picking one sidesteps the wrapper-vs-animatable-element pitfall above entirely — prefer them over hand-writing the selector.

This does not play in MSD Studio's live preview while editing, matching base_svg.animations and per-overlay animations — check the effect in the actual Lovelace dashboard.


Rules Engine Integration

The MSD card integrates with the global Rules Engine to dynamically restyle overlays and change the base SVG filter:

yaml
rules:
  - id: warp-alert
    when:
      entity: sensor.warp_core_temp
      above: 95
    apply:
      base_svg:
        filters:
          - { mode: svg, type: tint, value: { color: 'rgba(180,0,0,0.35)' } }
        transition: 500          # Crossfade in ms
      overlays:
        engine-status:
          style:
            color: "var(--lcards-alert-red)"
      animations:
        - overlay: engine-status
          preset: pulse
          loop: true

See Rules Engine for the full condition and apply reference.

apply.animations can also bulk-target overlays, via tag:/type:/pattern: matching against the rule's own overlay registry. Prefer top-level msd.animations for a simple whole-group animation with no conditional logic (e.g. "these all draw on load"); reach for apply.animations when the animation needs to start/stop with a rule's match/unmatch lifecycle (e.g. "pulse red while this sensor is above threshold").


Annotated Example

An MSD card with three anchors, a control overlay, a line, and a rule that changes the base SVG filter on alert:

yaml
type: custom:lcards-msd-card
msd:
  base_svg:
    source: builtin:ncc-1701-a-blue
    filters:
      opacity: 0.5
      brightness: 0.8

  anchors:
    bridge: [520, 380]
    engineering: [620, 520]
    sickbay: [410, 460]

  overlays:
    - id: bridge-card
      type: control
      anchor: bridge
      size: [180, 80]
      tags: [status-displays]
      card:
        type: custom:lcards-button
        entity: sensor.bridge_status
        preset: lozenge
        text:
          name:
            content: Bridge
        tap_action:
          action: more-info

    - id: power-line
      type: line
      anchor: engineering
      attach_to: bridge
      route: auto
      route_hint: yx
      corner_style: round
      corner_radius: 6

  routing:
    clearance: 10

rules:
  - id: reactor-alert
    when:
      entity: sensor.reactor_temp
      above: 90
    apply:
      base_svg:
        filters:
          - { mode: svg, type: tint, value: { color: 'rgba(180,0,0,0.35)' } }
        transition: 500
      overlays:
        bridge-card:
          style:
            color: "var(--lcards-alert-red)"
      animations:
        - tag: status-displays
          preset: pulse
          loop: true