mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-14 04:46:34 +00:00
Replace round slider gauge with svg-gauge (#6384)
This commit is contained in:
parent
894f4379e6
commit
acb471fbe5
@ -108,6 +108,7 @@
|
|||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"roboto-fontface": "^0.10.0",
|
"roboto-fontface": "^0.10.0",
|
||||||
"superstruct": "^0.6.1",
|
"superstruct": "^0.6.1",
|
||||||
|
"svg-gauge": "^1.0.6",
|
||||||
"unfetch": "^4.1.0",
|
"unfetch": "^4.1.0",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue2-daterange-picker": "^0.5.1",
|
"vue2-daterange-picker": "^0.5.1",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket/dist/types";
|
import { HassEntity } from "home-assistant-js-websocket/dist/types";
|
||||||
|
import Gauge from "svg-gauge";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@ -8,9 +9,8 @@ import {
|
|||||||
property,
|
property,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
|
query,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { styleMap } from "lit-html/directives/style-map";
|
|
||||||
import "@thomasloven/round-slider";
|
|
||||||
|
|
||||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
@ -23,8 +23,6 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
|
|||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||||
import type { GaugeCardConfig } from "./types";
|
import type { GaugeCardConfig } from "./types";
|
||||||
import { debounce } from "../../../common/util/debounce";
|
|
||||||
import { installResizeObserver } from "../common/install-resize-observer";
|
|
||||||
|
|
||||||
export const severityMap = {
|
export const severityMap = {
|
||||||
red: "var(--label-badge-red)",
|
red: "var(--label-badge-red)",
|
||||||
@ -69,18 +67,9 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
@property() private _config?: GaugeCardConfig;
|
@property() private _config?: GaugeCardConfig;
|
||||||
|
|
||||||
private _resizeObserver?: ResizeObserver;
|
@property() private _gauge?: any;
|
||||||
|
|
||||||
public connectedCallback(): void {
|
@query("#gauge") private _gaugeElement!: HTMLDivElement;
|
||||||
super.connectedCallback();
|
|
||||||
this.updateComplete.then(() => this._attachObserver());
|
|
||||||
}
|
|
||||||
|
|
||||||
public disconnectedCallback(): void {
|
|
||||||
if (this._resizeObserver) {
|
|
||||||
this._resizeObserver.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
return 2;
|
return 2;
|
||||||
@ -94,6 +83,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
throw new Error("Invalid Entity");
|
throw new Error("Invalid Entity");
|
||||||
}
|
}
|
||||||
this._config = { min: 0, max: 100, ...config };
|
this._config = { min: 0, max: 100, ...config };
|
||||||
|
this._initGauge();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@ -125,43 +115,12 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sliderBarColor = this._computeSeverity(state);
|
|
||||||
|
|
||||||
let value: number | undefined;
|
|
||||||
|
|
||||||
if (this._config.max === null || isNaN(this._config.max!)) {
|
|
||||||
value = undefined;
|
|
||||||
} else {
|
|
||||||
value = Math.min(this._config.max!, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card @click=${this._handleClick} tabindex="0">
|
||||||
@click=${this._handleClick}
|
<div id="gauge"></div>
|
||||||
tabindex="0"
|
|
||||||
style=${styleMap({
|
|
||||||
"--round-slider-bar-color": sliderBarColor,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<round-slider
|
|
||||||
readonly
|
|
||||||
arcLength="180"
|
|
||||||
startAngle="180"
|
|
||||||
.value=${value}
|
|
||||||
.min=${this._config.min}
|
|
||||||
.max=${this._config.max}
|
|
||||||
></round-slider>
|
|
||||||
<div class="gauge-data">
|
|
||||||
<div class="percent">
|
|
||||||
${stateObj.state}
|
|
||||||
${this._config.unit ||
|
|
||||||
stateObj.attributes.unit_of_measurement ||
|
|
||||||
""}
|
|
||||||
</div>
|
|
||||||
<div class="name">
|
<div class="name">
|
||||||
${this._config.name || computeStateName(stateObj)}
|
${this._config.name || computeStateName(stateObj)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -170,9 +129,11 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
return hasConfigOrEntityChanged(this, changedProps);
|
return hasConfigOrEntityChanged(this, changedProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(): void {
|
protected firstUpdated(changedProps: PropertyValues): void {
|
||||||
this._measureCard();
|
super.firstUpdated(changedProps);
|
||||||
this._attachObserver();
|
if (!this._gauge) {
|
||||||
|
this._initGauge();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues): void {
|
protected updated(changedProps: PropertyValues): void {
|
||||||
@ -194,9 +155,35 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
) {
|
) {
|
||||||
applyThemesOnElement(this, this.hass.themes, this._config.theme);
|
applyThemesOnElement(this, this.hass.themes, this._config.theme);
|
||||||
}
|
}
|
||||||
|
const oldState = oldHass?.states[this._config.entity];
|
||||||
|
const stateObj = this.hass.states[this._config.entity];
|
||||||
|
if (oldState?.state !== stateObj.state) {
|
||||||
|
this._gauge.setValueAnimated(stateObj.state, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeSeverity(numberValue: number): string {
|
private _initGauge() {
|
||||||
|
if (!this._gaugeElement || !this._config || !this.hass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._gauge) {
|
||||||
|
this._gaugeElement.removeChild(this._gaugeElement.lastChild!);
|
||||||
|
this._gauge = undefined;
|
||||||
|
}
|
||||||
|
this._gauge = Gauge(this._gaugeElement, {
|
||||||
|
min: this._config.min,
|
||||||
|
max: this._config.max,
|
||||||
|
dialStartAngle: 180,
|
||||||
|
dialEndAngle: 0,
|
||||||
|
viewBox: "0 0 100 55",
|
||||||
|
label: (value) => `${Math.round(value)}
|
||||||
|
${
|
||||||
|
this._config!.unit ||
|
||||||
|
this.hass?.states[this._config!.entity].attributes
|
||||||
|
.unit_of_measurement ||
|
||||||
|
""
|
||||||
|
}`,
|
||||||
|
color: (value) => {
|
||||||
const sections = this._config!.severity;
|
const sections = this._config!.severity;
|
||||||
|
|
||||||
if (!sections) {
|
if (!sections) {
|
||||||
@ -216,54 +203,24 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
sortable.sort((a, b) => a[1] - b[1]);
|
sortable.sort((a, b) => a[1] - b[1]);
|
||||||
|
|
||||||
if (numberValue >= sortable[0][1] && numberValue < sortable[1][1]) {
|
if (value >= sortable[0][1] && value < sortable[1][1]) {
|
||||||
return severityMap[sortable[0][0]];
|
return severityMap[sortable[0][0]];
|
||||||
}
|
}
|
||||||
if (numberValue >= sortable[1][1] && numberValue < sortable[2][1]) {
|
if (value >= sortable[1][1] && value < sortable[2][1]) {
|
||||||
return severityMap[sortable[1][0]];
|
return severityMap[sortable[1][0]];
|
||||||
}
|
}
|
||||||
if (numberValue >= sortable[2][1]) {
|
if (value >= sortable[2][1]) {
|
||||||
return severityMap[sortable[2][0]];
|
return severityMap[sortable[2][0]];
|
||||||
}
|
}
|
||||||
return severityMap.normal;
|
return severityMap.normal;
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleClick(): void {
|
private _handleClick(): void {
|
||||||
fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
|
fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _attachObserver(): Promise<void> {
|
|
||||||
if (!this._resizeObserver) {
|
|
||||||
await installResizeObserver();
|
|
||||||
this._resizeObserver = new ResizeObserver(
|
|
||||||
debounce(() => this._measureCard(), 250, false)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const card = this.shadowRoot!.querySelector("ha-card");
|
|
||||||
// If we show an error or warning there is no ha-card
|
|
||||||
if (!card) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._resizeObserver.observe(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _measureCard() {
|
|
||||||
if (!this.isConnected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.offsetWidth < 200) {
|
|
||||||
this.setAttribute("narrow", "");
|
|
||||||
} else {
|
|
||||||
this.removeAttribute("narrow");
|
|
||||||
}
|
|
||||||
if (this.offsetWidth < 150) {
|
|
||||||
this.setAttribute("veryNarrow", "");
|
|
||||||
} else {
|
|
||||||
this.removeAttribute("veryNarrow");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
@ -286,73 +243,31 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
outline: none;
|
outline: none;
|
||||||
background: var(--divider-color);
|
background: var(--divider-color);
|
||||||
}
|
}
|
||||||
|
#gauge {
|
||||||
round-slider {
|
width: 100%;
|
||||||
max-width: 200px;
|
max-width: 300px;
|
||||||
--round-slider-path-width: 35px;
|
|
||||||
--round-slider-path-color: var(--primary-background-color);
|
|
||||||
--round-slider-linecap: "butt";
|
|
||||||
}
|
}
|
||||||
|
.dial {
|
||||||
.gauge-data {
|
stroke: #ccc;
|
||||||
|
stroke-width: 15;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
stroke-width: 15;
|
||||||
|
}
|
||||||
|
.value-text {
|
||||||
|
fill: #000;
|
||||||
|
font-size: var(--gauge-value-font-size, 1.1em);
|
||||||
|
transform: translate(0, -5px);
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
.name {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: initial;
|
line-height: initial;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
margin-top: -26px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
|
||||||
|
|
||||||
.gauge-data .percent {
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gauge-data .name {
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============= NARROW ============= */
|
|
||||||
|
|
||||||
:host([narrow]) ha-card {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([narrow]) round-slider {
|
|
||||||
--round-slider-path-width: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([narrow]) .gauge-data {
|
|
||||||
margin-top: -22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([narrow]) .gauge-data .percent {
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([narrow]) .gauge-data .name {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============= VERY NARROW ============= */
|
|
||||||
|
|
||||||
:host([narrow]) ha-card {
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([veryNarrow]) round-slider {
|
|
||||||
--round-slider-path-width: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([veryNarrow]) .gauge-data {
|
|
||||||
margin-top: -16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([veryNarrow]) .gauge-data .percent {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([veryNarrow]) .gauge-data .name {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -11305,6 +11305,11 @@ sver-compat@^1.5.0:
|
|||||||
es6-iterator "^2.0.1"
|
es6-iterator "^2.0.1"
|
||||||
es6-symbol "^3.1.1"
|
es6-symbol "^3.1.1"
|
||||||
|
|
||||||
|
svg-gauge@^1.0.6:
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/svg-gauge/-/svg-gauge-1.0.6.tgz#1e84a366b1cce5b95dab3e33f41fdde867692d28"
|
||||||
|
integrity sha512-gRkznVhtS18eOM/GMPDXAvrLZOpqzNVDg4bFAPAEjiDKd1tZHFIe8Bwt3G6TFg/H+pFboetPPI+zoV+bOL26QQ==
|
||||||
|
|
||||||
symbol-observable@^1.1.0:
|
symbol-observable@^1.1.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user