Fluid Level (Animated) Background Card [original]

Please post in English.

Sorry, I’m not used to using this forum, and there’s a lot of information I need to include, so the post might look cluttered.

Anyone use this to display how much salt is left in a tank ?

Great job.

First of all, thank you for this amazing card. The liquid animation looks fantastic.

I am trying to integrate lovelace-fluid-level-background-card into a circular energy consumption button that I already built using custom:button-card.

My goal is simple:

  • I have a circular energy button (grid consumption / solar production).
  • I want the liquid animation to appear strictly inside the circle.
  • The liquid must not render outside the circular boundary.
  • The circular ring, glow, and value text stay on top.

My original implementation (without fluid-level-background-card) works perfectly. The liquid is clipped inside an SVG circle using clipPath.

However, when I try to use fluid-level-background-card, the liquid renders outside the circular shape, even when applying border-radius and overflow clipping via card_mod or mod-card.

Below is my original working button-card implementation (custom SVG liquid inside circle).

type: custom:button-card
entity: sensor.smappee_5010015633_local_load_grid
show_icon: false
show_name: false
show_state: false
tap_action:
  action: more-info

variables:
  MAX: 9000
  UNIT: W
  MIN_PCT_VISUAL: 0.1
  TILT_DEG: -7
  TILT_SKEW: -6
  WARN_W: 3500
  DANGER_W: 7000

styles:
  card:
    - height: 240px
    - padding: 0px
    - border-radius: 22px
    - background: rgba(0,0,0,0)
    - overflow: hidden
    - display: grid
    - place-items: center
  grid:
    - grid-template-areas: '"gauge"'
    - grid-template-columns: 1fr
    - grid-template-rows: 1fr
  custom_fields:
    gauge:
      - grid-area: gauge
      - justify-self: center
      - align-self: center

custom_fields:
  gauge: |
    [[[
      const num = (x) => {
        const n = Number(x);
        return Number.isFinite(n) ? n : 0;
      };

      const clamp = (x, a, b) => Math.max(a, Math.min(x, b));

      // Grid value (never negative)
      const raw0 = entity && entity.state != null ? num(entity.state) : 0;
      const raw  = Math.abs(raw0);

      const max = variables.MAX !== undefined ? num(variables.MAX) : 9000;
      const v   = clamp(raw, 0, max);

      const warn   = variables.WARN_W   !== undefined ? num(variables.WARN_W)   : 3500;
      const danger = variables.DANGER_W !== undefined ? num(variables.DANGER_W) : 7000;

      const level  = (v >= danger)
        ? "danger"
        : (v >= warn)
        ? "warn"
        : "ok";

      // Color palette (green / yellow / red)
      const pal = {
        ok: {
          ringA: "rgba(90,255,190,0.92)",
          ringB: "rgba(0,200,120,0.92)",
          liqTop: "rgba(140,255,210,0.90)",
          liqBot: "rgba(0,220,120,0.94)",
          wave1: "rgba(90,255,190,0.55)",
          wave2: "rgba(0,200,120,0.36)",
          glow:  "rgba(0,230,118,0.55)"
        },
        warn: {
          ringA: "rgba(255,209,102,0.95)",
          ringB: "rgba(255,176,0,0.95)",
          liqTop: "rgba(255,233,160,0.88)",
          liqBot: "rgba(255,176,0,0.94)",
          wave1: "rgba(255,200,90,0.55)",
          wave2: "rgba(255,176,0,0.36)",
          glow:  "rgba(255,193,7,0.60)"
        },
        danger: {
          ringA: "rgba(255,90,110,0.95)",
          ringB: "rgba(255,23,68,0.95)",
          liqTop: "rgba(255,170,185,0.88)",
          liqBot: "rgba(255,23,68,0.94)",
          wave1: "rgba(255,120,150,0.55)",
          wave2: "rgba(255,23,68,0.36)",
          glow:  "rgba(255,23,68,0.62)"
        }
      }[level];

      const displayVal = Math.round(v);

      return `
        <div style="display:flex; align-items:center; justify-content:center; height:100%;">
          <div style="
            font-size: 56px;
            font-weight: 600;
            color: rgba(255,255,255,0.92);
            text-shadow: 0 10px 25px rgba(0,0,0,0.65);
          ">
            ${displayVal} <span style="font-size:18px; opacity:0.8;">W</span>
          </div>
        </div>
      `;
    ]]]

My intention is to use fluid-level-background-card as the liquid engine, but keep it inside a circular energy meter button.

Example that I liked:

https://aarcoraci.github.io/fluid-meter/

Any guidance would be greatly appreciated.

Hi hotsauce, thanks for the kind words and for the detailed write-up!

Currently the card is designed as a rectangular wrapper — the fluid animation renders on a full-size canvas and gets
clipped by the card’s border-radius (overflow: hidden). Making it work cleanly inside a circular boundary would
require changes at the canvas/rendering level, so card_mod or CSS-only approaches won’t reliably get you there.

That said, this is a really cool use case! I’d love to explore adding support for circular shapes. Could you open a
feature request on GitHub so we can track it properly?

Issues · swingerman/lovelace-fluid-level-background-card · GitHub

It’d be great if you could include the reference you linked (the fluid-meter demo) and describe the desired result —
that helps a lot when scoping the work.

Anyone adapt this for a Water Softener Salt tank?