Releases: francois-le-ko4la/lovelace-entity-progress-card
Release list
1.6.2-rc1
✨ New
--epb-icon-shape-opacity: new CSS hook,0removes the circular shape behind the icon entirely. See CSS hooks.hide: [shape]: same result as the hook above, withoutcard_mod— Card and Template, Badge/BadgeTemplate have no shape by default. ➡️ [Feature]: Configuration possibility for the icon background visibility #136 (@RkcCorian)alert_when.jinja: new Advanced mode (Simple/Advanced chip in the editor) — a Jinja condition replacesabove/belowas the trigger, returningtrue/falseor an object overriding any ofcolor/animation/highlight/labelfor that push. Seealert_when.alert_whenon Template/Badge Template: new, Jinja-only — neither variant hadalert_whenin any form before. Noabove/below/color/highlight/animation/label, so the object return is the only form with a visible effect.value_compact: Card and Badge, opt-in — abbreviates large values using locale-aware suffixes (1250000→1.3M) instead of the full number.decimalstill caps the fraction digits, trailing zeros are trimmed. Seevalue_compact.unit_position: Card and Badge, opt-in — moves the unit before the value instead of after ($100rather than100$) for currency-style display. Seeunit_position.value_sign: Card and Badge, opt-in — forces an explicit+/-on the displayed value (zero stays unsigned), clarifying the direction of acenter_zero/bar_stack: { mode: net }delta at a glance. Seevalue_sign.- Editor: 2-mode chip selectors now render as a single fused segmented pill instead of two separate chips or a plain toggle -
theme_mode(Preset/Custom),alert_when_mode/bar_effect_mode/hide_mode(Simple/Advanced, replacing their old boolean toggle), the newicon_animation_mode(Auto/Template, same replacement), andforce_circular_background_mode(Auto/Forced, same replacement). - Editor:
hold_action/double_tap_action/icon_hold_action/icon_double_tap_actionnow stay hidden behind a "+ Add interaction" button, revealed one at a time — replaces the old "show all" toggle.
🐛 Fixes
status_label's own Jinja pill stayed permanently hidden oncealert_when.highlight: 'label'was configured, even while the alert itself was inactive.- The editor's "Alert" toggle wiped the whole
alert_whenconfig (including a typedalert_when.jinjatemplate) every time it was switched off, unlikestatus_label's own toggle — now preserves a draft, same pattern. - Same bug on the "Markers" toggle: switching it off permanently discarded the whole
watermarksetup (type, thresholds, colors, …) instead of just hiding it — now preserves a draft too.bar_max_width's toggle had the milder version (lost a custom value, fell back to the 300px default) — fixed the same way. - The editor's "Compact" toggle (
density: compact) stayed visible and clickable withlayout: verticalselected, even though compact density only has a meaningful shape in horizontal — toggling it on silently forced layout back to horizontal instead. Now hidden while vertical. - Editor translations are now complete across all 39 supported languages — several labels had silently stayed in English since the day they shipped, invisible to the usual checks.
- Translations are organized more consistently under the hood, with a welcome side effect: a noticeably lighter download.
- A few editor labels were simplified or clarified where two different fields ended up showing the exact same text.
📚 Documentation
- The
--epb-*CSS styling API is now fully documented. Most of it was built during 1.6.1's browser-compat CSS rework, but stayed undocumented — there wasn't yet a test bench to confirm every hook actually did what it claimed. 13 previously-undocumented variables added todocs/theme.md(iconbattery-chargingbolt geometry,bar_segments/progress-bar-min-width, therainbow_fullmarker set,status_labelpill colors), backed by a new CSS hooks view indocs/demo-dashboard.yaml— one card per--epb-*variable, each isolating a single override. graphic-effects-compatibility.htmllinks (release notes + CHANGELOG) now go throughhtmlpreview.github.ioso the page renders instead of showing as raw source.configuration.mdupdated:hide'sshapeoption,alert_when's Advanced mode (with two new examples) and its reach onto Template/Badge Template,value_compact,unit_position, andvalue_sign.editor.option.hide.shapeadded to all 39 languages + template, using a descriptive phrase ("Icon's circular background") instead of a bare "Shape".alert_when.jinja's editor labels/field-helper added to all 39 languages + template; two demo cards indocs/demo-dashboard.yaml(plain boolean, and an object with a partial override).- One more demo card for Template's own
alert_when.jinja— reuses the same translation keys, no new ones needed. value_compact's editor label added to all 39 languages + template; three demo cards indocs/demo-dashboard.yaml(default, compact, compact withdecimal: 2) on a newinput_number.epb_demo_total_energyhelper.unit_position's editor label +after/beforeoptions, andvalue_sign's editor label, added to all 39 languages + template; a currency-prefix demo card and acenter_zero+value_signdemo card indocs/demo-dashboard.yaml.docs/troubleshooting.md: a version check that still shows the old one right after updating is usually HACS lagging behind the actual release — use ⋮ → Redownload to force a fresh install instead of assuming the update failed. ➡️ #137
🧹 Under the hood
translations/template.json(the structural reference file, never shipped) no longer picks up stray real text — it should only ever hold placeholders.- Badge's schema silently accepted
hide: [shape]even though Badge has no shape to hide in the first place (Badge Template already excluded it) — now consistently blocked for both. - Bundle down ~195 KB (-27%): the generated translations file used to repeat every key's full name in each of the 39 languages. Restated once, with each language's values kept separately — same translated text, same behavior, meaningfully less to download. Verified against the previous version value-by-value before shipping (9672 values × 39 languages, 0 mismatches). See Internationalization.
- A further ~32 KB shaved off on top of that: a long tail of internally-duplicated text — the same word or sentence stored separately for half a dozen near-identical fields or error messages — now shares one entry instead of a copy each (mode-toggle option words like "Simple"/"Advanced", the Jinja-helper hints, several developer-console-only validation messages). Purely internal; nothing translated changed.
- Net result: the full prod bundle (
dist/entity-progress-card.js) ships ~109 KB smaller than 1.6.1 (-16%), even after every feature and fix above — verified by comparing the built file between the two tags. - A handful of editor fields that happened to carry the exact same label as a neighboring one (
bar_position/unit_positionboth said "Position",alert_when_mode/icon_animation_modeboth said "Trigger mode") now share one, shorter label instead of two redundant copies.
1.6.1
What's new
A bigger release than it looks: combine several bars into one card, themes and per-icon animations reach Template, status_label/badge_icon can drive their own color from a single Jinja condition, a proper keyboard/screen reader pass, and a handful of layout options that couldn't quite do what people needed yet. Fully backward compatible — nothing to change in your existing dashboards.
⭐ Highlights
📊 Combine several bars into one card or Tile feature
entity-progress-multi-card / entity-progress-multi-feature stack several full-featured progress bars (each with its own colors, state and tap actions) into a single card or a single Tile feature row, instead of one card per entity — handy for printer ink cartridges, a set of batteries, or any group of related sensors. A new, thinner bar_size: xsmall helps fit more of them in a tight space.
A small bar is enough on its own for something like a printer cartridge, but not when the number itself matters. Set show_value: true to show each entity's own value right next to its bar.
See [multi] and [show_value].
➡️ [Feature]: Multi-bar card/feature #126 (@EDelsman)
📐 A more compact layout: bar_position: compact_below
name and the value now share one row (value right-justified), freeing the progress bar to use the card's full width on its own row underneath — instead of the bar and the value fighting for the same narrow space, or the value sitting in empty, unused room. See [bar_position].
➡️ [Enhancement]: Display value above bar #123 (@davidcoulson)
🎬 Trigger icon animations from a template
icon_animation now accepts a Jinja condition ({ effect, jinja }) instead of relying only on automatic entity-state detection. See [icon_animation].
➡️ [Enhancement]: template/condition to trigger the icon animation directly #125 (@FoxP)
🏷️ Status label pill
status_label draws a small rounded status pill in the card's corner ({ jinja, position, color_source }) — colored automatically from whatever color the progress bar currently shows by default (color_source: icon to follow the icon instead), or set your own color directly by returning {label, color} from the template instead of plain text. alert_when.highlight: 'label' reuses the same pill to flash a fixed alert label instead of tinting the border or background. See [status_label] and [alert_when].
🎫 One template, both badge icon and color
badge_icon can now return a {icon, color} object instead of just an icon name — one condition drives both at once.
See [badge_icon].
🌈 A hue-picker style bar: bar_color_mode: rainbow_full
The track always shows the theme's complete gradient end to end — not just the filled portion, like rainbow already does — with a small marker sliding along it to show where the current value sits, closer to a hue-picker strip than a traditional progress bar. Works with center_zero too, each arm showing its own half of the gradient in full.
See [bar_color_mode].
⏱️ Smarter countdown refresh (and a fix for stalled Template timers)
A Template card's now()-driven countdown (e.g. a secondary: line ticking down a timer) could freeze after its first render — Home Assistant only pushes a fresh now()/utcnow() value once a minute on its own. Add fast_refresh: true to opt that Template card into a forced per-second refresh instead. Not automatic (it re-subscribes every second), and standard cards/badges/features don't need it — they already self-correct on their own. See [fast_refresh].
🎨 A friendlier theme picker
The built-in theme list is reordered for readability: battery_adaptive (the one most people reach for) comes first, and each Critical when X/Optimal when X pair now sits together instead of being split into two separate groups.
🎯 A center_zero-aware "Critical when extreme" theme
theme: critical_when_extreme_center brings critical_when_extreme's "danger at both ends, safe in the middle" shape to center_zero — green at zero, red at both extremes, one continuous -100%/100% scale. Handy for a net power flow or a deviation from a setpoint that can swing either way. See theme.md.
🖌️ Theme support for Template
entity-progress-card-template / -badge-template can now pick a built-in [theme] instead of writing your own color Jinja by hand — the icon and bar color follow your percent: value automatically. [bar_color_mode] (segment/rainbow/rainbow_full) is available too, for a gradient look instead of a flat color.
📏 A hardened height
Card sizing got a real pass end to end: the min-height-as-a-floor protection (never clip text when the OS/browser text-size accessibility setting is scaled up) now applies consistently everywhere a card can live — Sections, Masonry, or embedded in another card. An explicit height: now always wins outright as a real, exact size everywhere too, instead of just another floor tall content could still outgrow. See height.
➡️ [Bug]: Some parts of the card are not visible on Android #131 (@zkurzyns)
🧹 Consistent hide: behavior
Hiding a field (hide: [icon, name, secondary_info, progress_bar]) now always gives its reserved space back, the same way, in every layout — previously each field followed its own inconsistent rule. An empty value/unit row now collapses too, whatever made it empty. See hide.
📶 Sturdier bar_segments
bar_segments: N's divider lines are real elements now, not a CSS gradient/mask trick — all N segments are the same width now (the two end ones used to read wider), and divider thickness can scale with the bar's own length instead of a fixed size, tunable via --epb-bar-segment-gap hook. See bar_segments.
📱 A compact density
density: compact shrinks the card to the smallest a complete card (icon, name, value, bar) can be in a Sections grid, in one toggle — instead of hand-tuning layout/bar_position/multiline yourself. Flipping it on through the visual editor also pins the card's size outright, restored automatically if you switch it back off. See density.
🃏 Show up when you pick an entity, not just when you search for a card
Home Assistant 2026.6 added an entity-first card picker: pick an entity, and compatible cards show live previews right there instead of you having to search for this card by name first. The Card and Badge now opt in — pick a compatible entity (a numeric sensor, a cover, a light...) and they show up under "Community" with a working config already filled in. Template/Badge Template don't suggest themselves — they need a hand-written Jinja percent: to render anything meaningful.
✍️ More inline styles for Jinja-rendered text
name/secondary/custom_info templates now also accept font-size, font-weight, text-align, width, display, position and z-index (on top of the existing color/background-color) — enough to build simple multi-column layouts directly in a template.
➡️ [Enhancement]: JINJA Should accept more STYLE tags #129 (@emartoni)
♿ Better keyboard and screen reader support
The card and its icon (when either has its own tap action) are now properly reachable and understandable for keyboard and screen-reader users, not just mouse/touch:
- Tab reaches the card, and separately the icon if it has its own distinct action (
icon_tap_action) — each now announces something meaningful (the entity's name and current value for the card, "Icon action" for the icon) instead of a bare, unlabeled "button". A badge, where only the card itself is ever clickable, no longer exposes its icon as a confusing extra stop. - Enter/Space activates each one, same as a mouse click.
- Tabbing to the card or the icon now shows a clear, colored outline (matching the entity's own color, following the card's rounded corners properly) instead of the browser's plain default rectangle.
- Fixed a structural issue where the card and its icon were both marked as "buttons" nested one inside the other — some screen readers and touch navigation handle that inconsistently. They're independent controls now, side by side rather than one inside the other.
🔧 Improvements
- The visual editor now remembers a field's value across a mode toggle (standard/entity/Jinja) instead of resetting it to blank — switch
min_value/max_value/watermark.low/.high/alert_when.above/.below,badge_icon/badge_color,bar_effect,hide,icon_animation, orstatus_labelaway from Jinja and back, and your template is still there. - Jinja-capable fields in the editor now show a short hint under the field for what the template is expected to return (a number, a boolean, a string, or an object with specific keys) instead of leaving you to guess from the docs.
- Several fields in the editor's theme panel (
icon/color,bar_orientation,bar_size,bar_segments,bar_scale,bar_color_mode,interpolate) could end up alone in their row instead of sharing it with their usual neighbor, depending on which other options were active — all paired up correctly now.
🐛 Notable fixes
- The card could fail to load entirely on some older or embedded browsers (ki...
1.6.1-rc8
What's new
🐛 Fixes
- A Jinja-driven
icon_animation: { effect, jinja }could briefly fall back to the automatic entity-based detection instead of staying off — configuring jinja mode only ever overrode the automatic detection once a push had resolved to a real boolean; right after connect, or right after a config change reset the cache, it silently ran the automatic detection instead of just staying off until the real result arrived. - Jinja-driven
badge_icon/badge_color/icon_animationfields could get visually stuck on a value that never matched the final, settled state — a multi-step automation/script (e.g. turning aninput_booleanon, then picking aninput_selectoption right after) makes Home Assistant re-evaluate a template once per intermediate state, not just once for the final one, since the entities referenced by anandcondition don't change atomically. Applied immediately, that transient push could render (and get stuck showing) a value nothing in the final state actually supports. Every Jinja field now settles for ~80ms before rendering, so only the last, settled push of a rapid burst is ever shown. ➡️ [Bug]: Cached / invalid Jinja result? #135 (@FoxP)
🧰 Internal
- Continuing 1.6.0's TypeScript hardening pass (
Hass→HomeAssistant, removing hand-maintainedanyblobs):Configis now derived directly from the YAML validation schema instead of a separately maintained type — one less place for the two to silently drift apart. No behavior change.
1.6.1-rc7
What's new
✨ New
- Card/Badge suggest themselves in Home Assistant's entity-first card picker
(HA 2026.6+, "Pick a card, any card") — pick an entity there and, for a
compatible one, the card/badge shows up under "Community" with a live preview
already wired to it: entities whose own state is a plain number (sensor,
number,input_number,counter),timer(native support), and entities
whose value lives in an attribute (cover/valve'scurrent_position,
fan'spercentage,light'sbrightness,humidifier's
current_humidity). Deliberately conservative — anything more
context-dependent (climate targets, a text sensor...) is left out rather than
guessed, so the suggestion never shows up somewhere it'd render empty or
nonsensical. Template/Badge Template don't suggest themselves: they need a
hand-written Jinjapercent:to render anything meaningful.
🔧 Improvements
- Template/Badge Template gained
theme(percentage-based themes only —
battery_adaptive,critical_when_low/_high,optimal_when_low/_high,
critical_when_extreme/_center,light,humidity) andbar_color_mode
(segment/rainbow/rainbow_full) — drives icon/bar color straight off the
percent:Jinja field, no manual color Jinja needed. - New theme:
critical_when_extreme_center—critical_when_extreme's own
shape, built forcenter_zerospecifically (green at zero, red at both ends,
one continuous -100%/100% scale instead of the same zones mirrored onto each
arm). status_label.jinjacan return{label, color}, andbadge_iconcan
return{icon, color}— one Jinja condition drives text/icon and color
together, instead of two separate fields kept in sync by hand.- New
status_label.color_source(bardefault, oricon) — picks which
color the pill falls back to when its ownjinjadoesn't return an explicit
{label, color}. badge_icon/badge_colormoved to the editor's Markers panel (were in
Theme/appearance) — same "reacts to a condition" grouping as
watermark/status_label/alert_when. New toggle shows/hides both together.
🐛 Fixes
- A conditional
badge_icon/badge_color(only showing while some state holds)
could flicker back on right after correctly hiding — any unrelated dashboard
update was enough to force it back, ignoring the template's own current
result. bar_color_mode: rainbow_full's marker got clipped near a value close to
0%/100% (or either end withcenter_zero) - it's centered on its own
position, so at the extremes part of it pushed past the bar row's own boundary
and got cut off by the card'soverflow: hidden. Its position now clamps to
stay fully inside instead. A second, unrelated clip also showed at the bottom
regardless of value,bar_position: defaultincluded - the marker's soft
shadow wasn't accounted for in the vertical room reserved for it; now sized
off the same variable that room already comes from.- A Template card with
percent: ''(empty, e.g. still waiting on the entity it
depends on) never painted its icon/bar/theme at all - an emptypercentwas
never subscribed to in the first place, so nothing ever called the code that
renders them. Now falls back to a plain 0% render instead of staying blank. badge_icon's own{icon, color}object (see the highlight above): if a
later push kept the icon but droppedcolor, the badge's background stayed on
whatever color it last showed instead of clearing.center_zerowith athemeandbar_color_modeset (segment/rainbow/
rainbow_full) could leave a gap in the fill instead of a clean edge, for any
value pastmax_value(or belowmin_value) — the two arms' own gradient
size wasn't capped at 100% the way the fill itself already is.- Template's
watermark.low/.highin{ jinja: ... }mode never resolved —
no subscription was ever wired for it, so the mark just never appeared.
entity/plain-number modes were unaffected. bar_color_mode: rainbow_full's marker didn't mirror with
bar_orientation: rtl— the fill visually flipped, the marker stayed put and
ended up on the wrong side of it.
1.6.1-rc6
What's new
🔧 Improvements
watermarkeditor labels reworded to "marker(s)" (issue #133, @Ascathon)
—Watermarktoggle →Markers,Low/High watermark source→
Low/High marker source. Editor wording only, thewatermark:YAML option
itself is unchanged.round/trianglewatermark markers now center precisely on their value,
same pixel-rounding fixbar_segments' own dividers already got.- Browser-compat fallbacks (
round()/color-mix()) cleaned up and extended
across the file — same graceful-degradation behavior, less duplicated CSS to
maintain. Turned up several real gaps along the way, see Fixes. - New
--epb-alert-background-opacityhook tunes the
alert_when.highlight: backgroundtint on browsers using the fallback above
(defaults to matching the same look modern browsers already get).
🐛 Fixes
- The icon's circular background could disappear entirely on an older browser
(Chrome/Edge < 111, Firefox < 113, Safari < 16.2 — see issue #128) —
simplified to a technique that renders the same way on every supported
browser, so there's no gap left to fall back from. bar_effect: gradient/gradient_reversecould silently do nothing on the
same class of browser — now falls back to a softly lightened bar instead of a
flat, un-lightened one.- The alert
blinkanimation's background color could glitch mid-cycle on the
same class of browser — now falls back consistently. icon_animation: pingandalert_when'sping/blinkanimations could fail
to start at all on the same class of browser — not just lose the color
blending like the others above, the animation itself never ran. Same fallback
approach applied there too.- Clicking the icon also lit up the whole card — a real double-ripple,
confirmed and fixed by rebuilding the click-feedback layering after comparing
directly against Home Assistant's own native Tile card. The icon now gives its
own visual feedback (a quick highlight/scale) instead of triggering a second
ripple.
♿ Accessibility
A full pass on keyboard and screen-reader support for the card and its icon:
- The card (and its icon, when it has its own separate
icon_tap_action) are
now correctly announced as buttons with a real, specific name — the entity's
own name and current value for the card, "Icon action" for the icon — instead
of a generic, unlabeled one. A badge (only ever the card itself is clickable
there) no longer exposes its icon as a confusing extra keyboard stop. - The icon's own action, when it has one, is now reachable and activatable from
the keyboard (Tab, then Enter/Space) — previously mouse/touch only. - Tabbing to the card or the icon now shows a clear, colored focus outline (the
entity's own color, following the card's rounded corners correctly) instead of
the browser's plain default rectangle. - Fixed the card and its icon being marked as two "buttons" nested one inside
the other — inconsistent handling on some screen readers/touch navigation.
They're independent, side-by-side controls now.
1.6.1-rc5
What's new
🔧 Improvements
- Hardened
heighthandling. Left unset, the card's height has always been a floor rather than a fixed size on purpose (issue #131 - it lets the card grow instead of clipping text when the OS/browser text-size accessibility setting is scaled up). The gap: an explicitheight:used to become that same floor too, so it could be silently outgrown by the card's own content (a bigger font-scale setting, a taller theme,secondary_infowrapping...) with no way for a deliberate value to actually pin an exact size — a "sometimes my height isn't respected" report that traced back to exactly this.- Left unset — unchanged: still a floor, can still grow past it, everywhere (Sections, Masonry, embedded in any other card).
- Set explicitly — now wins outright as a real, exact height, everywhere, no exceptions. The trade-off (clipping if the value is too small for the actual content) is the user's to make once they've set it on purpose, not something the card second-guesses for them.
- First attempt at this only applied inside a handful of containers the card could auto-detect via a class a card_mod convention injects (
entities,picture-elements- not native HA, needs card_mod installed) -type: gridlooked like a good next candidate (confirmed live:hui-grid-carddoes mark itself the same way) until a live DOM dump showed that marker never actually reaches this card's ownha-card. Rather than chase down containers one at a time,height:set explicitly is now unconditional - it doesn't need to detect anything about its surroundings anymore, so it works the same everywhere,type: grid/custom:combined-card(which never got auto-detected at all) included. The one exception:type: gridwithsquare: true(the default) forces a 1:1 cell regardless of any height, explicit or not - see theheightnote and the "Inside another Lovelace card" table. entities's hardcoded ~44.8px row-height guess for the unconfigured default (based on a Tile row withsecondary_infoshown - not a real HA constant, and wrong the momentsecondary_infois hidden) is gone too, replaced by ordinary content-driven sizing there by default. ➡️ Discord @RKT62- That entities default surfaced two more edge cases live-tested inside a real
entitiesrow:bar_size: xlargeandbar_position: backgroundwere both getting squeezed down to the tiny default bar thickness instead of their own dedicated size, since they shared the same override slot the new default was feeding - both excluded now. bar_orientation: upcombined withbar_position: overlaylost the vertical room it needs to actually show a vertical bar. Root cause: the overlay bar isposition: absolute, so it never contributed to the card's own auto-height in the first place - harmless under the old unconditionalheight: 100%fallback, exposed once unset height became genuinely content-driven (auto) above. That specific combination now gets its own100%fallback instead ofauto, everything else unaffected.
labelrenamed tostatus_label. It shared its name withalert_when.label(a different field - a fixed string, not a Jinja template) despite both driving the same status pill, an ambiguity made more visible now that the two live in the same editor panel (see below). Safe to rename now:status_labelonly ever shipped in a 1.6.1 release candidate,alert_when.labelhas been stable since 1.6.0 and keeps its name. Seestatus_label.- The visual editor's "Look & Feel" panel had regrown to ~28 fields, the same size that justified splitting
watermark/alert_wheninto their own "Markers & Alerts" panel earlier.status_labelmoves into that same panel too - it's the same "react to a condition" status-pill markeralert_when.highlight: labelreuses, not core entity/display content. hide:reclaims space consistently everywhere, not just in the cases each field happened to already handle:hide: iconwithlayout: horizontal-.content's width formula (calc(100% - 56px)) deducted a fixed 56px column for the icon unconditionally, on purpose, matching how Mushroom/Tile-style cards keep name/value aligned to the same column with or without an icon shown. Traded that alignment for consistency instead - the 56px is given back once the icon is hidden, same as every other field below.hide: name/hide: secondary_infowithlayout: vertical- onlylayout: horizontalzeroed the hidden row's height term before (issue #129's fix never got a vertical equivalent).layout: vertical + bar_position: defaultstacks name/secondary-info/bar as 3 independent rows (unlike horizontal's default, which shares one row between secondary-info and the bar) - safe to always zero either one's height term with no conditional needed, unlike horizontal's own exception just above it.hide: progress_bar- only ever hid the fill (.bar), never.bar-containeritself, which kept its ownheight/min-widthregardless - an empty box whereverbar_positiongave the bar a dedicated row (below/top/bottom), or extra reserved width sharingsecondary_info's row by default..bar-container's height now reads from--current-specific-progress-container-height(the same dedicated top-priority override slot.bar-containeralready checked first for everything else) wheneverhide: progress_baris set, so this one rule collapses it the same way regardless ofbar_positionor layout,min-width/flex-growreset alongside it.layout: vertical + bar_position: default's ownpadding-top(proportional to--progress-size, pushes the icon+content group down so it stays visually centered against just the text once the bar renders below it) now drops to 0 once the bar itself is hidden - nothing left below to stay centered against. Left untouched byhide: icon: that padding was never about the icon, so it stays regardless.hide: value/hide: unitclosed the same way, but differently: neither has a CSS class (both are a pure text-level omission, seeViewBase#secondaryInfoMain), so reclaiming an empty row needs the parent to react to that emptiness - not something plain CSS can do at this card's documented browser floor without:has()(Firefox 121+ vs. the 94+ floor - see the Prerequisites table in README.md, and note thatstyles.ts's existing.info-multilinecollapse rule already relies on it anyway, an inconsistency worth revisiting separately). Solved with a JS-computed flag instead, following the pattern the rest ofhide:already uses:_processStandardFields(the one placesecondaryInfoMain's final string is both computed and written) pushes--detail-height: 0pxinline onha-cardwhenever that string comes out empty,removeStyleotherwise - content-driven, not config-driven, so it reclaims the row regardless of why it's empty.
hide: value/hide: unitas a Jinja template never actually worked - found while building the reclaim logic above. Root cause: no single source of truth for "what's hidden right now"._handleHiddenComponentstoggled CSS classes straight from a Jinja push and threw the resolved list away afterward;hasComponentHiddenFlag(whichsecondaryInfoMain's value/unit omission andminGridRows's icon check both call) independently re-readconfig.hidedirectly, which only ever works for the static array shape - a Jinja string silently failsis.array(), so the check always came back "nothing hidden", regardless of what the template actually resolved to.- New
ViewCore#resolvedHide(aSet<string> | null,nulluntil a real Jinja push writes to it via the newsetResolvedHide(), reset on everyset config) is now the one state everything reads.hasComponentHiddenFlagchecks it first, falling back toconfig.hidedirectly only when it's stillnull- which is also exactly what a plain statichide: [...]array leaves it at forever, so one code path now covers both shapes with no branching. _baseJinjaHandlers.hidenow also calls_processStandardFields()after_handleHiddenComponents(content)-icon/name/secondary_info/progress_barare purely visual (CSS class, no text to recompute), butvalue/unitneed the assembled string itself redone the moment the template's result changes, not just on the next unrelated entity refresh.minGridRows(Sections grid row reservation) reads the same resolved state now too, but stays effectively static-only regardless: it's computed once at config negotiation, and HA's owngetGridOptions()isn't reactive - a Jinjahide: iconwas never going to resize the grid reservation live, before or after this fix. Not a regression, a pre-existing ceiling.
- New
- Two
:has()-based CSS rules replaced with a JS-computed check - found while reviewing this card's own browser-compat floor (Firefox 94+ per README.md) against features actually in use. Both collapsed.secondary-info-wrapper(display: none) once every line inside it (custom_info/Template'ssecondary, single- or multi-line) had gone empty, using.secondary-info-extra-1:empty/.secondary-info-extra-2:empty/.secondary-info-main:emptychained through:has()/:not(:has())-:has()itself needs Firefox 121+, well past the documented floor; below that version the rule was just silently invalid, leaving a small empty gap instea...
1.6.1-rc4
What's new
✨ New
- New
bar_position: compact_below—nameand the secondary info
(state/value) now share one row, with the value right-justified, and the
progress bar takes the full width of its own row underneath instead of
squeezing next to the text. Reclaims the dead space that used to sit empty to
the right ofnameon a horizontal card, and lets the bar grow to the card's
full width. Horizontal-only — withlayout: vertical, where there's no such
row to reuse, it silently falls back todefault. See
bar_position.
➡️ [Enhancement]: Display value above bar #123 (@davidcoulson) - New
label— a small rounded status pill in the card's corner
({ jinja, position }), colored automatically from whatever color the icon
currently shows (theme,custom_theme, or a plaincoloroverride).
alert_when.highlight: 'label'reuses the same pill to flash a fixed alert
label instead of tinting the border or background. See
label
and
alert_when. - New
bar_color_mode: rainbow_full— the track always shows the theme's
complete gradient end to end, not just the filled portion, like a hue-picker
strip — with a small marker sliding along it to show where the current value
sits. Works with
center_zero
(each arm shows its own half of the theme in full, the marker crosses the
visual center at zero). Falls back to plainrainbowwith
bar_stack
— several entities, no single position for one marker. See
bar_color_mode.
🐛 Fixes
icon_animation's{ effect, jinja }docs didn't warn thatjinjamust be a
bare{{ ... }}expression — a{% if %}block or a literal string like
"true"renders back as text instead of a native boolean, and silently falls
back to automatic detection instead of erroring.layout: vertical's min-height formula for the name/value/bar column never
accounted for the small gaps between those three rows, so the card's real
rendered height consistently ran a couple pixels taller than the formula
claimed — harmless on its own (nothing clipped), but it meant
grid_options: rows: autocould never land on a clean Sections grid row
multiple.
🧪 Try it: demo dashboard
docs/demo-dashboard.yaml's
"Layout & bar_position" view got a compact_below card in both the horizontal
and vertical "every bar_position" galleries (the vertical one shows the
fallback to default), plus a new "Série 2 — compact_below, hide progressif
(#123)" section walking through hide combinations on top of it. The
"bar_color_mode, custom_theme, alert_when, trend" view gets new label demo
cards (custom-theme-based, built-in-theme-based, position: left, and
alert_when.highlight: label), a "Full rainbow" section showing rainbow_full
at every bar_size, and a showcase section covering different
temperatures/layouts/center_zero combinations with labels enabled. The
"Sunset" real-world example (#60) gets a second, alternate version contributed
by @WarC0zes (HACF).
1.6.1-rc3
What's new
✨ New
-
entity-progress-multi-card/entity-progress-multi-featurecan now show each entity's own value next to its bar (show_value: true, shared or per item) — a bare bar is enough on its own for something like a printer cartridge, but not for values where the number itself matters (energy in Watts, a tank in liters…). The bar gives up part of its width for the value instead of an overlay - a bare feature bar is already thin enough that overlaid text would fight it for contrast.decimal/unit/disable_unit/unit_spacingall work the same as on the standard card.value_position: left/right(defaultleft) picks which side it sits on, and every value takes up the same fixed width instead of sizing to its own text, so bars meant to read as comparable actually line up regardless of digit count. Seeshow_value. -
The built-in
themepicker (dropdown and docs) is reorganized for readability:battery_adaptivenow sits first (it's the one most people reach for), and eachcritical_when_*/optimal_when_*pair sits together (Critical when lownext toOptimal when low,Critical when highnext toOptimal when high) instead of all the criticals then all the optimals.Optimal when low/Optimal when high's labels also drop their parenthetical examples ((CPU, RAM...)/(Battery...)) — plain text now, matching every other theme's label. No config keys changed, purely a picker/docs reordering. Seetheme.
🐛 Fixes
-
icon_animation: { effect, jinja }never actually triggered the animation on a Template card (entity-progress-card-templateand its badge), even for a template that always resolvestrue. Every other Jinja-driven option re-applies whatever it just changed itself right after resolving (e.g.min_value/max_valuecall_updateCSS(),alert_whencalls_applyAlertClasses()) -icon_animation's own handler was missing this and only ever set the resolved value, relying on some other, unrelated refresh to happen to re-check it afterward. A standard card gets one soon enough from its regular entity-driven updates, masking the bug there, but a Template card has no such incidental refresh to piggyback on, so the animation never started. It now re-applies its own CSS classes right after resolving, the same way every other option already does.➡️ [Enhancement]: template/condition to trigger the icon animation directly #125 (@FoxP)
-
center_zero's negative arm withbar_color_mode: segment/rainbowand a theme (orcustom_theme) could render as a single flat color instead of a gradient — e.g.theme: temperaturewithcenter_zero: { value: 20 }showed solid blue (or another single theme color) across the whole negative arm instead of fading through the zones its actual range covers. Two independent bugs combined here, both pre-existing and never exercised until now (not related to themin_value/max_value/theme-scope changes above): the negative arm's projection window is reversed (it grows away from center, toward the scale's own low end), which flips the zones' position order relative to their value-ascending declaration order (e.g. temperature's coldest zone ends up at the highest local position) - CSSlinear-gradientclamps any stop whose position is lower than the previous one up to that same position, so once one out-of-order stop hit, every stop after it collapsed onto it; zones are now sorted by their projected local position before building gradient stops (a no-op for the normal, non-reversed direction), keeping stops properly ordered either way. Separately, the negative arm's CSS grows in the opposite direction from every other bar (it slides toward the low end instead of away from it), which puts its "current value" and "zeroValue" ends on the opposite box edges from what the gradient math assumes - the real color transitions ended up computed entirely outside the visible portion, leaving only a single held-flat color showing; the gradient is now painted in the mirrored CSS direction for this arm specifically, which corrects this without changing any of the position math itself. -
On a compressed horizontal card, the progress bar could shrink to a near-invisible sliver (or get pushed out past the card's edge entirely) while the name/secondary-info text next to it never gave up any of its own space. The bar had no minimum width at all, while the text already had one (45px) - every bit of horizontal squeeze landed on the bar alone instead of being shared. The two now hold coupled, proportional floors: the bar keeps a
30pxminimum (--epb-progress-bar-min-width, overridable viacard_modfor anyone who wants a different balance) and the text keeps45px, but each caps at a fraction of the row (33% for the bar, 25% for the text - the bar gets the bigger guaranteed share, since a squeezed bar loses its whole purpose as a progress indicator while squeezed text still has ellipsis to fall back on) once the row can't fit both floors at once, so they can never sum past the row's width and force one or the other to overflow. The gap between them (10px by default) shrinks the same way under pressure, freeing a few more pixels right where the text and bar floors are already fighting for room. -
The card could clip
name/secondary_infotext when the OS or browser's own font-size accessibility setting was scaled up (e.g. Android's system "Font size" option) — text rendered larger than the fixed-height row it sat in, and got cut off mid-glyph instead of the row growing to fit.name/secondary_inforows now hold amin-height(not a fixedheight) with aline-heightthat only ever grows past the default (max(default, 1.2em)) - identical look at the default font scale, but room to grow instead of clip once the actual font is genuinely larger. Not applied tolayout: vertical+bar_position: overlay+bar_orientation: up: there, the text row is a flex item competing with the icon section (flex-shrink: 0) for space, and letting it grow broke that specific layout (confirmed via DevTools computed styles) - it keeps its original fixed-height behavior for now (seedocs/ideas.mdfor a possible follow-up).➡️ [Bug]: Some parts of the card are not visible on Android #131 (@zkurzyns)
-
A percent-based sensor (
unit_of_measurement: "%") withcustom_themezones could stop filling the bar all the way at 100%, if any zone'smaxwent past 100 (e.g. a top zone of80-120used purely as a color buffer for values that might drift slightly above 100). 1.6.1-rc2 hadmax_valueauto-default from the active theme's/custom_theme's own top zone bound whenever left unset, so it picked up120here and used it as the bar's own fill scale - a100.0reading only filled to100/120 ≈ 83%.custom_themeis now dropped from that auto-default entirely - its zones are user-defined and may extend past the entity's real range on purpose, with no reliable way to tell that apart from a genuinemax_value. A built-in theme with real-world value zones (temperature,voc,pm25) keeps the auto-default formax_valueonly - picking one of them is already a deliberate, specific choice, so the theme's own top bound is safe to assume as the entity's real range;min_valuestays at its own default (0, or the theme's own lowest bound withcenter_zero- seemax_valuefor the full explanation).bar_color_mode: segment/rainbowalways projects a theme's zones onto[min_value, max_value]- the visible "scope": a mismatch between an entity's real range and its theme's/custom_theme's own zone scale now only ever clips or shifts which zones are reachable, never the fill percentage itself.➡️ [Bug]: JINJA Should accept more STYLE tags #129 (@emartoni)
🧪 Try it: demo dashboard
docs/demo-dashboard.yaml got several new sections worth a look:
- "Error / edge-case states" — what the card looks like when things go wrong: an entity that's
unavailable, one that'sunknown, one that doesn't exist at all, and a request for an attribute the entity doesn't have. Useful to check before reporting something as a bug — it might just be one of these. - "Compare with tile" — the same entity shown with this card side-by-side with Home Assistant's own built-in Tile card, across several kinds of entities (fan, battery, cover, light, timer, counter), so you can see how the two compare at a glance.
- "custom_theme — use cases" — color-only zones, color + a per-zone icon override, and a full negative/positive range (shown standalone, then with
center_zeroat two different zero points), right next to the built-in theme examples.
The "Energy (consumption vs production)" example also got a fix: its "Net" bar now shows the actual balance (production minus consumption) as a single value, instead of showing both raw numbers stacked on top of each other, which never actually matched what "Net" was supposed to mean.
Almost every section across the whole dashboard now starts with a small YAML snippet showing the config it's demonstrating, plus a short note on what changes from one card to the next — meant to make the dashboard readable on its own, not just clickable.
1.6.1-rc2
What's new
✨ New
icon_animationcan now be triggered by a Jinja condition instead of entity state —{ effect, jinja }, wherejinjaresolves totrue/falseand fully replaces the automatic entity-based detection. Covers cases like a plain numericsensorwith no active/inactive concept at all, which nothing in the automatic detection could ever match. Seeicon_animation.
➡️ [Enhancement]: template/condition to trigger the icon animation directly #125 (@FoxP)- The refresh rate of an active timer's countdown is now driven by what's actually displayed, and self-corrects against real time instead of drifting. Standard cards/badges/features refresh every second when
unitshows seconds (s/timer/flextimer), once a minute otherwise (min/h/d/natural format) — replacing the old fixed duration-based formula. Template cards get this once-a-minute refresh for free from Home Assistant's own push (no extra subscriptions); a newfast_refresh: trueopts into a forced once-a-second refresh for a real tickingMM:SScountdown, at a real cost (seefast_refresh) — not tied toentitybeing atimer, anynow()-driven countdown benefits the same way (e.g. a sunrise/sunset countdown againstsun.sun). Every tick, standard or template, now lands on a round second/minute boundary and re-aligns itself on every cycle, instead of drifting from whatever moment the card happened to load. Each tick also does only the minimal work its own display actually needs (the bar + value for cards/badges, the bar alone for Tile features, a forced Jinja resubscribe for templates) instead of the full icon/badge/shape/trend/Jinja-processing pipeline - lighter, and less prone to per-tick timing jitter from that pipeline's own variable cost. The bar-only part is shared by every card type (defined once, not duplicated per type) since it's the one thing a ticking timer always needs to repaint, regardless of what else a given card type shows on top. The countdown text itself now writes to the DOM immediately instead of through the usual RAF batching (newDOMHelper.setTextNow, mirroring the existingsetStyleNow) - a RAF callback runs at the next display frame, whose cadence isn't aligned to our wall-clock-second scheduling, so batching it reintroduced up to a frame's worth of per-tick unevenness on the one thing a ticking countdown makes visible (a discrete text jump, unlike the bar's own width change, which already glides on its CSS transition regardless of paint timing). - Timer/duration seconds were rounded instead of truncated (
NumberFormatter.formatTiming), unlike the hours/minutes next to them, which already floored. A timer at 332.847s truly elapsed floored to minute 5 but rounded its seconds to "33", showing05:33up to ~500ms before the 33rd second had actually passed - a real-value discrepancy against any reference that floors (e.g. HA's own Tile), not a timing/jitter issue like the fixes above. Seconds now floor the same way hours/minutes do.
🐛 Fixes
bar_size: xlarge(orbar_position: below) with the icon hidden got squeezed into a single grid row instead of the extra row that size/position needs, cutting the bar off. A 1.6.0 regression - hiding the icon and needing an extra row for a large bar were wrongly treated as mutually exclusive instead of composing. Fixed for Home Assistant's own Sections grid, and also for everywhere else the card can be placed (Masonry, embedded in another card) - that fallback sizing had a second, independent copy of the same row-count logic (a hardcoded "always 1 row (horizontal) / always 2 rows (vertical)"), so the identical squeeze could still happen there even after the Sections-only fix, just never reported. Both now read the same row count from one place.
➡️ [Bug]: Card not rendering correctly with xlarge bar and icon hidden #133 (@Ascathon)bar_size: xsmallwasn't treated the same assmallin the card's height/grid-row calculation, even though it's the smaller of the two - a vertical card withbar_size: xsmallstill reserved the extra row meant for medium/large/xlarge bars, unlikesmall, which never needed it.alert_whenwithanimation: pingandhighlight: backgroundsilently degraded tostaticinstead of ringing.pinganimates abox-shadowaround the whole card, independent ofhighlight's border-color/background-color - the two combine fine, the fallback was based on a wrong assumption that no matching CSS rule existed.bar_color_mode: segment/rainbowcollapsed to a single flat color with a theme whose zones are real-world values instead of%(temperature,voc,pm25…), or withcustom_theme, unlessmax_valuehappened to already match the theme's own scale. Each zone boundary is projected onto themin_value/max_valuewindow to paint the bar - left at the flat100default while the theme's zones went into the thousands (voc), every zone past the first got clamped to zero width and filtered out, leaving one color for the whole bar.max_valuenow defaults to the active theme's (orcustom_theme's) own highest zone bound instead of100whenever it's left unset, so the fill and the color zones share one coherent scale out of the box - an explicitmax_valuealways overrides this and is used as-is. Seemax_value.- Secondary info (unit/state text) stayed capped at its narrow bar-sharing width budget (45px–60%) with
bar_position: below,overlayor
background, even though the bar renders elsewhere for those positions and isn't actually competing for room in that row.top/bottomwere already exempted from that cap;below/overlay/backgroundshare the exact same "bar renders elsewhere" condition (seeStructureElements.createSecondaryInfo) but were missing from the CSS rule that lifts it. - A Jinja-templated countdown (e.g.
secondary: {{ now() - ... }}) froze after a few seconds instead of ticking every second. Home Assistant only pushes a fresh render for anow()/utcnow()template once a minute on its own, absent a state change on a tracked entity - the 1.6.0 rewrite removed an incidental resubscribe-on-every-hass-update that used to paper over this on Template cards/badges. Fix: a new opt-in,fast_refresh: true, forces a per-second resubscribe instead - it isn't automatic (Template's display is arbitrary Jinja text, not a fixed field the card can key a local tick off of the way standard cards do for an active timer), so it needs to be added explicitly to any Template card with anow()-driven countdown.
➡️ Reported alongside [Bug]: "Run" status not catched #127 (@annaoskarson) - Hiding
nameorsecondary_infoon a horizontal card left an empty gap where the hidden row used to be, instead of shrinking the card. The content area's height was a fixed name+detail sum regardless of which rows were actually visible. Fixed for horizontal layout - vertical's ring-shaped bar still needs that reserved space, so it's unchanged for now. For a card embedded outside Home Assistant's own grid sizing (e.g. as acustom:button-cardfield), combine this withheight: auto(an existing, previously-undocumented valid value) andframeless: trueto get a card that shrinks fully to its content instead of stretching to fill the surrounding element. With the defaultbar_position, the bar itself shares that row with the secondary-info text rather than living in its own container - the first pass at this fix zeroed the row's height wheneversecondary_infowas hidden regardless, which starved the still-visible bar and shrank the name above it too (flex-shrink pulling from the wrong row). Now that height is only reclaimed when the row actually goes empty (bar elsewhere, or also hidden viahide: [progress_bar]).
➡️ Follow-up to [Enhancement]: JINJA Should accept more STYLE tags #129 (@emartoni)
🔧 Improvements
- The
heightfield in the visual editor now has a toggle to switch between the slider and a free-text value (e.g.auto,calc(...)). Previously that mode only activated if the config already held a non-number+unit value (set through YAML) - there was no way to reach it from the visual editor alone. theme: humidity's ranges rebalanced. The comfort zone (40–60%) is now one solidgreenband instead of agreen/tealsplit that read as a washed-out green rather than a distinct color, and the remaining ranges now mirror symmetrically around it (10/10/10/20 on each side) instead of the old lopsided split - a 95% reading (mold/condensation risk) used to land ondeep-purple, which doesn't read as "alert" the way a 15% reading'sreddoes, understating the actual risk on the humid end. Seetheme: humidity.bar_position: overlay'sname/secondary_infotext now keeps 10px of breathing room on the right edge, matching the existing 7px it already had on the left, instead of running flush against the card's edge.
🧪 Try it: demo dashboard
docs/demo-dashboard.yaml got a full rebuild — a comprehensive showroom covering essentially every option in this changelog (themes, watermarks, bar_effect, icon_animation, center_zero, bar_stack, aggregation, card_mod/UIX styling…) plus a regression-test view with one card per statically-reproducible closed issue. Import it and move the helper sliders to see everything react live: docs/demo-dashboard-helpers.yaml (all the demo helper entities as a drop-in homeassistant: packages: file, instead of creating them one by one).
...
1.6.1-rc1
What's new
✨ New
entity-progress-multi-card/entity-progress-multi-feature: aggregate several progress bars — each a real, fully-featured bar (its own colors, state, more-info tap) — into a single card or a single Tile feature, instead of one bar per entity. See Configuration Reference.bar_size: xsmall: a new, thinner bar size (6px) — mainly useful to fit more bars in a tight space, e.g. insideentity-progress-multi-feature. Seebar_size.- Jinja-rendered
name/secondary/custom_infonow accept more inline styles:font-size,font-weight,text-align,width,display,positionandz-index, on top of the existingcolor/background-color— enough to build simple multi-column layouts directly in a template.
➡️ [Enhancement]: JINJA Should accept more STYLE tags #129 (@emartoni)
🐛 Fixes
- The card could fail to load entirely on older/embedded browsers (e.g. Chromium-based kiosk panels), even though it worked fine on 1.5.x. The 1.6.0 build pipeline could emit ES2022-only syntax that these engines can't even parse - the whole module failed silently, showing an error card instead of the progress bar. The build now targets ES2021 (matching what 1.5.x actually required), and a new release-time check (
npm run check:es-target) catches this class of regression before it ever ships again.
➡️ [Browser Support]: chromium 92.0.4515.98 / After upgrade to 1.6.0 it stopped working on chromium #128 (@Slomo5) name: { type: device }ignored a renamed device. It only read the device's original/manufacturer name, never the name you set yourself in Settings → Devices → rename (name_by_user) — now it follows the same precedence Home Assistant's own UI uses.
➡️ [Bug]: Friendly name is not used #130 (@zkurzyns)