From 70be747e9da5d9b69aa7381f2269c9641c1df80b Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Tue, 31 Mar 2026 13:27:22 +0200 Subject: [PATCH] Gauge improvements (#30368) * Gauge last improvements * Change needle * Fixup * Feedback comments * Update src/components/ha-gauge.ts --------- Co-authored-by: Petar Petrov --- gallery/src/pages/lovelace/gauge-card.ts | 15 ++ src/components/ha-gauge.ts | 186 ++++++++++++-------- src/panels/lovelace/cards/hui-gauge-card.ts | 4 +- 3 files changed, 125 insertions(+), 80 deletions(-) diff --git a/gallery/src/pages/lovelace/gauge-card.ts b/gallery/src/pages/lovelace/gauge-card.ts index c79a3def95..5472b7f02b 100644 --- a/gallery/src/pages/lovelace/gauge-card.ts +++ b/gallery/src/pages/lovelace/gauge-card.ts @@ -134,6 +134,21 @@ const CONFIGS = [ entity: sensor.not_working `, }, + { + heading: "Lower minimum", + config: ` +- type: gauge + entity: sensor.brightness_high + needle: true + severity: + green: 0 + yellow: 0.45 + red: 0.9 + min: -0.05 + name: " " + max: 1.9 + unit: GBP/h`, + }, ]; @customElement("demo-lovelace-gauge-card") diff --git a/src/components/ha-gauge.ts b/src/components/ha-gauge.ts index 78b0c84be1..61d08bc58a 100644 --- a/src/components/ha-gauge.ts +++ b/src/components/ha-gauge.ts @@ -1,4 +1,4 @@ -import type { PropertyValues, TemplateResult } from "lit"; +import type { PropertyValues } from "lit"; import { css, LitElement, svg } from "lit"; import { customElement, property, state } from "lit/decorators"; import { styleMap } from "lit/directives/style-map"; @@ -54,6 +54,7 @@ export class HaGauge extends LitElement { this._angle = getAngle(this.value, this.min, this.max); } this._segment_label = this._getSegmentLabel(); + this._rescaleSvg(); }); } @@ -70,6 +71,7 @@ export class HaGauge extends LitElement { } this._angle = getAngle(this.value, this.min, this.max); this._segment_label = this._getSegmentLabel(); + this._rescaleSvg(); } protected render() { @@ -88,87 +90,91 @@ export class HaGauge extends LitElement { /> - ${ - this.levels - ? [...this.levels] - .sort((a, b) => a.level - b.level) - .map((level, i, arr) => { - const startLevel = i === 0 ? this.min : arr[i].level; - const endLevel = i + 1 < arr.length ? arr[i + 1].level : this.max; + ${ + this.levels + ? (() => { + const sortedLevels = [...this.levels].sort( + (a, b) => a.level - b.level + ); - const startAngle = getAngle(startLevel, this.min, this.max); - const endAngle = getAngle(endLevel, this.min, this.max); - const largeArc = endAngle - startAngle > 180 ? 1 : 0; + if ( + sortedLevels.length > 0 && + sortedLevels[0].level !== this.min + ) { + sortedLevels.unshift({ + level: this.min, + stroke: "var(--info-color)", + }); + } - const x1 = -arcRadius * Math.cos((startAngle * Math.PI) / 180); - const y1 = -arcRadius * Math.sin((startAngle * Math.PI) / 180); - const x2 = -arcRadius * Math.cos((endAngle * Math.PI) / 180); - const y2 = -arcRadius * Math.sin((endAngle * Math.PI) / 180); + return sortedLevels.map((level, i, arr) => { + const startLevel = level.level; + const endLevel = + i + 1 < arr.length ? arr[i + 1].level : this.max; - const firstSegment = i === 0; - const lastSegment = i === arr.length - 1; + const startAngle = getAngle(startLevel, this.min, this.max); + const endAngle = getAngle(endLevel, this.min, this.max); + const largeArc = endAngle - startAngle > 180 ? 1 : 0; - const paths: TemplateResult[] = []; + const x1 = + -arcRadius * Math.cos((startAngle * Math.PI) / 180); + const y1 = + -arcRadius * Math.sin((startAngle * Math.PI) / 180); + const x2 = -arcRadius * Math.cos((endAngle * Math.PI) / 180); + const y2 = -arcRadius * Math.sin((endAngle * Math.PI) / 180); - if (firstSegment) { - paths.push(svg` - - `); - } else if (lastSegment) { - const offsetAngle = 0.5; - const midAngle = endAngle - offsetAngle; - const xm = -arcRadius * Math.cos((midAngle * Math.PI) / 180); - const ym = -arcRadius * Math.sin((midAngle * Math.PI) / 180); + const isFirst = i === 0; + const isLast = i === arr.length - 1; - paths.push(svg` - - `); + if (isFirst) { + return svg` + + `; + } - paths.push(svg` - - `); - } else { - paths.push(svg` - - `); - } + if (isLast) { + const offsetAngle = 0.5; + const midAngle = endAngle - offsetAngle; + const xm = + -arcRadius * Math.cos((midAngle * Math.PI) / 180); + const ym = + -arcRadius * Math.sin((midAngle * Math.PI) / 180); - return paths; - }) - : "" - } + return svg` + + + `; + } + + return svg` + + `; + }); + })() + : "" + } ${ this.needle ? svg` - + ` : svg` ` } - + + a.level - b.level); @@ -224,32 +243,43 @@ export class HaGauge extends LitElement { .levels-base { fill: none; stroke: var(--primary-background-color); - stroke-width: 8; - stroke-linecap: round; + stroke-width: 6; + stroke-linecap: butt; } .level { fill: none; - stroke-width: 8; + stroke-width: 6; stroke-linecap: butt; } .value { fill: none; - stroke-width: 8; + stroke-width: 6; stroke: var(--gauge-color); - stroke-linecap: round; + stroke-linecap: butt; transition: stroke-dashoffset 1s ease 0s; } .needle { - stroke: var(--primary-text-color); - stroke-width: 2; + fill: var(--primary-text-color); + stroke: var(--card-background-color); + color: var(--primary-text-color); + stroke-width: 1; stroke-linecap: round; transform-origin: 0 0; transition: all 1s ease 0s; } + .text { + position: absolute; + max-height: 40%; + max-width: 55%; + left: 50%; + bottom: 10%; + transform: translate(-50%, 0%); + } + .value-text { font-size: var(--ha-font-size-l); fill: var(--primary-text-color); diff --git a/src/panels/lovelace/cards/hui-gauge-card.ts b/src/panels/lovelace/cards/hui-gauge-card.ts index 7bc8ef8479..b157b0bfee 100644 --- a/src/panels/lovelace/cards/hui-gauge-card.ts +++ b/src/panels/lovelace/cards/hui-gauge-card.ts @@ -303,9 +303,8 @@ class HuiGaugeCard extends LitElement implements LovelaceCard { .title { width: 100%; - font-size: var(--ha-font-size-l); + font-size: var(--ha-font-size-m); line-height: var(--ha-line-height-expanded); - padding: 0px 0px var(--ha-space-2) 0px; margin: 0; text-align: center; box-sizing: border-box; @@ -318,6 +317,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard { ha-gauge { width: 100%; + max-width: 250px; } `; }