mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-31 13:07:49 +00:00
Merge branch 'dev' into allthebackupchanges
This commit is contained in:
commit
7b4536564e
@ -1,6 +1,6 @@
|
||||
export default {
|
||||
"*.?(c|m){js,ts}": [
|
||||
"eslint --cache --cache-strategy=content --cache-location=node_modules/.cache/eslint/.eslintcache --fix",
|
||||
"eslint --flag unstable_config_lookup_from_file --cache --cache-strategy=content --cache-location=node_modules/.cache/eslint/.eslintcache --fix",
|
||||
"prettier --cache --write",
|
||||
"lit-analyzer --quiet",
|
||||
],
|
||||
|
@ -72,6 +72,12 @@ export class StatisticsChart extends LitElement {
|
||||
|
||||
@property() public chartType: ChartType = "line";
|
||||
|
||||
@property({ type: Number }) public minYAxis?: number;
|
||||
|
||||
@property({ type: Number }) public maxYAxis?: number;
|
||||
|
||||
@property({ type: Boolean }) public fitYData = false;
|
||||
|
||||
@property({ type: Boolean }) public hideLegend = false;
|
||||
|
||||
@property({ type: Boolean }) public logarithmicScale = false;
|
||||
@ -113,6 +119,9 @@ export class StatisticsChart extends LitElement {
|
||||
changedProps.has("unit") ||
|
||||
changedProps.has("period") ||
|
||||
changedProps.has("chartType") ||
|
||||
changedProps.has("minYAxis") ||
|
||||
changedProps.has("maxYAxis") ||
|
||||
changedProps.has("fitYData") ||
|
||||
changedProps.has("logarithmicScale") ||
|
||||
changedProps.has("hideLegend")
|
||||
) {
|
||||
@ -232,6 +241,8 @@ export class StatisticsChart extends LitElement {
|
||||
text: unit || this.unit,
|
||||
},
|
||||
type: this.logarithmicScale ? "logarithmic" : "linear",
|
||||
min: this.fitYData ? null : this.minYAxis,
|
||||
max: this.fitYData ? null : this.maxYAxis,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
|
@ -20,6 +20,8 @@ class HaHLSPlayer extends LitElement {
|
||||
|
||||
@property() public entityid?: string;
|
||||
|
||||
@property() public url?: string;
|
||||
|
||||
@property({ attribute: "poster-url" }) public posterUrl?: string;
|
||||
|
||||
@property({ type: Boolean, attribute: "controls" })
|
||||
@ -94,14 +96,19 @@ class HaHLSPlayer extends LitElement {
|
||||
super.updated(changedProps);
|
||||
|
||||
const entityChanged = changedProps.has("entityid");
|
||||
const urlChanged = changedProps.has("url");
|
||||
|
||||
if (!entityChanged) {
|
||||
return;
|
||||
if (entityChanged) {
|
||||
this._getStreamUrlFromEntityId();
|
||||
} else if (urlChanged && this.url) {
|
||||
this._cleanUp();
|
||||
this._resetError();
|
||||
this._url = this.url;
|
||||
this._startHls();
|
||||
}
|
||||
this._getStreamUrl();
|
||||
}
|
||||
|
||||
private async _getStreamUrl(): Promise<void> {
|
||||
private async _getStreamUrlFromEntityId(): Promise<void> {
|
||||
this._cleanUp();
|
||||
this._resetError();
|
||||
|
||||
|
@ -23,6 +23,7 @@ export const STATE_ATTRIBUTES = [
|
||||
"state_class",
|
||||
"supported_features",
|
||||
"unit_of_measurement",
|
||||
"available_tones",
|
||||
];
|
||||
|
||||
export const TEMPERATURE_ATTRIBUTES = new Set([
|
||||
|
7
src/data/siren.ts
Normal file
7
src/data/siren.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export const SirenEntityFeature = {
|
||||
TURN_ON: 1,
|
||||
TURN_OFF: 2,
|
||||
TONES: 4,
|
||||
VOLUME_SET: 8,
|
||||
DURATION: 16,
|
||||
};
|
@ -0,0 +1,224 @@
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { mdiClose, mdiPlay, mdiStop } from "@mdi/js";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import {
|
||||
getMobileCloseToBottomAnimation,
|
||||
getMobileOpenFromBottomAnimation,
|
||||
} from "../../../../components/ha-md-dialog";
|
||||
import "../../../../components/ha-dialog-header";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-textfield";
|
||||
import "../../../../components/ha-control-button";
|
||||
import "../../../../components/ha-select";
|
||||
import "../../../../components/ha-list-item";
|
||||
import type { HaMdDialog } from "../../../../components/ha-md-dialog";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
||||
import { SirenEntityFeature } from "../../../../data/siren";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
|
||||
@customElement("ha-more-info-siren-advanced-controls")
|
||||
class MoreInfoSirenAdvancedControls extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() _stateObj?: HassEntity;
|
||||
|
||||
@state() _tone?: string;
|
||||
|
||||
@state() _volume?: number;
|
||||
|
||||
@state() _duration?: number;
|
||||
|
||||
@query("ha-md-dialog") private _dialog?: HaMdDialog;
|
||||
|
||||
public showDialog({ stateObj }: { stateObj: HassEntity }) {
|
||||
this._stateObj = stateObj;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._dialog?.close();
|
||||
}
|
||||
|
||||
private _dialogClosed(): void {
|
||||
this._stateObj = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this._stateObj) {
|
||||
return nothing;
|
||||
}
|
||||
const supportsTones =
|
||||
supportsFeature(this._stateObj, SirenEntityFeature.TONES) &&
|
||||
this._stateObj.attributes.available_tones;
|
||||
const supportsVolume = supportsFeature(
|
||||
this._stateObj,
|
||||
SirenEntityFeature.VOLUME_SET
|
||||
);
|
||||
const supportsDuration = supportsFeature(
|
||||
this._stateObj,
|
||||
SirenEntityFeature.DURATION
|
||||
);
|
||||
return html`
|
||||
<ha-md-dialog
|
||||
open
|
||||
@closed=${this._dialogClosed}
|
||||
aria-labelledby="dialog-light-color-favorite-title"
|
||||
.getOpenAnimation=${getMobileOpenFromBottomAnimation}
|
||||
.getCloseAnimation=${getMobileCloseToBottomAnimation}
|
||||
>
|
||||
<ha-dialog-header slot="headline">
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
@click=${this.closeDialog}
|
||||
.label=${this.hass.localize("ui.common.close")}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
<span slot="title" id="dialog-light-color-favorite-title"
|
||||
>${this.hass.localize(
|
||||
"ui.components.siren.advanced_controls"
|
||||
)}</span
|
||||
>
|
||||
</ha-dialog-header>
|
||||
<div slot="content">
|
||||
<div class="options">
|
||||
${supportsTones
|
||||
? html`
|
||||
<ha-select
|
||||
.label=${this.hass.localize("ui.components.siren.tone")}
|
||||
@closed=${stopPropagation}
|
||||
@change=${this._handleToneChange}
|
||||
.value=${this._tone}
|
||||
>
|
||||
${Object.entries(
|
||||
this._stateObj.attributes.available_tones
|
||||
).map(
|
||||
([toneId, toneName]) => html`
|
||||
<ha-list-item .value=${toneId}
|
||||
>${toneName}</ha-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`
|
||||
: nothing}
|
||||
${supportsVolume
|
||||
? html`
|
||||
<ha-textfield
|
||||
type="number"
|
||||
.label=${this.hass.localize("ui.components.siren.volume")}
|
||||
.suffix=${"%"}
|
||||
.value=${this._volume ? this._volume * 100 : undefined}
|
||||
@change=${this._handleVolumeChange}
|
||||
.min=${0}
|
||||
.max=${100}
|
||||
.step=${1}
|
||||
></ha-textfield>
|
||||
`
|
||||
: nothing}
|
||||
${supportsDuration
|
||||
? html`
|
||||
<ha-textfield
|
||||
type="number"
|
||||
.label=${this.hass.localize("ui.components.siren.duration")}
|
||||
.value=${this._duration}
|
||||
suffix="s"
|
||||
@change=${this._handleDurationChange}
|
||||
></ha-textfield>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
<div class="controls">
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.common.turn_on")}
|
||||
@click=${this._turnOn}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlay}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
<ha-control-button
|
||||
.label=${this.hass.localize("ui.card.common.turn_off")}
|
||||
@click=${this._turnOff}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiStop}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<ha-button @click=${this.closeDialog}>
|
||||
${this.hass.localize("ui.common.close")}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-md-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleToneChange(ev) {
|
||||
this._tone = ev.target.value;
|
||||
}
|
||||
|
||||
private _handleVolumeChange(ev) {
|
||||
this._volume = parseFloat(ev.target.value) / 100;
|
||||
if (isNaN(this._volume)) {
|
||||
this._volume = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _handleDurationChange(ev) {
|
||||
this._duration = parseInt(ev.target.value);
|
||||
if (isNaN(this._duration)) {
|
||||
this._duration = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async _turnOn() {
|
||||
await this.hass.callService("siren", "turn_on", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
tone: this._tone,
|
||||
volume: this._volume,
|
||||
duration: this._duration,
|
||||
});
|
||||
}
|
||||
|
||||
private async _turnOff() {
|
||||
await this.hass.callService("siren", "turn_off", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
ha-control-button {
|
||||
--control-button-border-radius: 16px;
|
||||
--mdc-icon-size: 24px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-more-info-siren-advanced-controls": MoreInfoSirenAdvancedControls;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
|
||||
export const loadSirenAdvancedControlsView = () =>
|
||||
import("./ha-more-info-siren-advanced-controls");
|
||||
|
||||
export const showSirenAdvancedControlsView = (
|
||||
element: HTMLElement,
|
||||
stateObj: HassEntity
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "ha-more-info-siren-advanced-controls",
|
||||
dialogImport: loadSirenAdvancedControlsView,
|
||||
dialogParams: {
|
||||
stateObj,
|
||||
},
|
||||
});
|
||||
};
|
@ -5,9 +5,13 @@ import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../components/ha-attributes";
|
||||
import "../../../state-control/ha-state-control-toggle";
|
||||
import "../../../components/ha-button";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../components/ha-more-info-state-header";
|
||||
import { moreInfoControlStyle } from "../components/more-info-control-style";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import { SirenEntityFeature } from "../../../data/siren";
|
||||
import { showSirenAdvancedControlsView } from "../components/siren/show-dialog-siren-advanced-controls";
|
||||
|
||||
@customElement("more-info-siren")
|
||||
class MoreInfoSiren extends LitElement {
|
||||
@ -20,6 +24,20 @@ class MoreInfoSiren extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const supportsTones =
|
||||
supportsFeature(this.stateObj, SirenEntityFeature.TONES) &&
|
||||
this.stateObj.attributes.available_tones;
|
||||
const supportsVolume = supportsFeature(
|
||||
this.stateObj,
|
||||
SirenEntityFeature.VOLUME_SET
|
||||
);
|
||||
const supportsDuration = supportsFeature(
|
||||
this.stateObj,
|
||||
SirenEntityFeature.DURATION
|
||||
);
|
||||
// show advanced controls dialog if extra features are supported
|
||||
const allowAdvanced = supportsTones || supportsVolume || supportsDuration;
|
||||
|
||||
return html`
|
||||
<ha-more-info-state-header
|
||||
.hass=${this.hass}
|
||||
@ -32,6 +50,11 @@ class MoreInfoSiren extends LitElement {
|
||||
.iconPathOn=${mdiVolumeHigh}
|
||||
.iconPathOff=${mdiVolumeOff}
|
||||
></ha-state-control-toggle>
|
||||
${allowAdvanced
|
||||
? html`<ha-button @click=${this._showAdvancedControlsDialog}>
|
||||
${this.hass.localize("ui.components.siren.advanced_controls")}
|
||||
</ha-button>`
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-attributes
|
||||
.hass=${this.hass}
|
||||
@ -40,6 +63,10 @@ class MoreInfoSiren extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _showAdvancedControlsDialog() {
|
||||
showSirenAdvancedControlsView(this, this.stateObj!);
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return moreInfoControlStyle;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
||||
@ -8,7 +8,6 @@ import type { AssistPipeline } from "../../../../data/assist_pipeline";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { WakeWord } from "../../../../data/wake_word";
|
||||
import { fetchWakeWordInfo } from "../../../../data/wake_word";
|
||||
import { documentationUrl } from "../../../../util/documentation-url";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
|
||||
@customElement("assist-pipeline-detail-wakeword")
|
||||
@ -79,12 +78,7 @@ export class AssistPipelineDetailWakeWord extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _hasWakeWorkEntities = memoizeOne((states: HomeAssistant["states"]) =>
|
||||
Object.keys(states).some((entityId) => entityId.startsWith("wake_word."))
|
||||
);
|
||||
|
||||
protected render() {
|
||||
const hasWakeWorkEntities = this._hasWakeWorkEntities(this.hass.states);
|
||||
return html`
|
||||
<div class="section">
|
||||
<div class="content">
|
||||
@ -99,29 +93,17 @@ export class AssistPipelineDetailWakeWord extends LitElement {
|
||||
`ui.panel.config.voice_assistants.assistants.pipeline.detail.steps.wakeword.description`
|
||||
)}
|
||||
</p>
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.voice_assistants.assistants.pipeline.detail.steps.wakeword.note`
|
||||
)}
|
||||
</ha-alert>
|
||||
</div>
|
||||
${!hasWakeWorkEntities
|
||||
? html`${this.hass.localize(
|
||||
`ui.panel.config.voice_assistants.assistants.pipeline.detail.steps.wakeword.no_wake_words`
|
||||
)}
|
||||
<a
|
||||
href=${documentationUrl(
|
||||
this.hass,
|
||||
"/voice_control/install_wake_word_add_on/"
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>${this.hass.localize(
|
||||
`ui.panel.config.voice_assistants.assistants.pipeline.detail.steps.wakeword.no_wake_words_link`
|
||||
)}</a
|
||||
>`
|
||||
: nothing}
|
||||
<ha-form
|
||||
.schema=${this._schema(this._wakeWords)}
|
||||
.data=${this.data}
|
||||
.hass=${this.hass}
|
||||
.computeLabel=${this._computeLabel}
|
||||
.disabled=${!hasWakeWorkEntities}
|
||||
></ha-form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { mdiClose, mdiDotsVertical } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-dialog-header";
|
||||
@ -21,6 +22,7 @@ import "./assist-pipeline-detail/assist-pipeline-detail-wakeword";
|
||||
import "./debug/assist-render-pipeline-events";
|
||||
import type { VoiceAssistantPipelineDetailsDialogParams } from "./show-dialog-voice-assistant-pipeline-detail";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
|
||||
@customElement("dialog-voice-assistant-pipeline-detail")
|
||||
export class DialogVoiceAssistantPipelineDetail extends LitElement {
|
||||
@ -30,6 +32,8 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
|
||||
|
||||
@state() private _data?: Partial<AssistPipeline>;
|
||||
|
||||
@state() private _hideWakeWord = false;
|
||||
|
||||
@state() private _cloudActive?: boolean;
|
||||
|
||||
@state() private _error?: Record<string, string>;
|
||||
@ -42,11 +46,17 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
|
||||
this._params = params;
|
||||
this._error = undefined;
|
||||
this._cloudActive = this._params.cloudActiveSubscription;
|
||||
|
||||
if (this._params.pipeline) {
|
||||
this._data = this._params.pipeline;
|
||||
|
||||
this._hideWakeWord =
|
||||
this._params.hideWakeWord || !this._data.wake_word_entity;
|
||||
return;
|
||||
}
|
||||
|
||||
this._hideWakeWord = true;
|
||||
|
||||
let sstDefault: string | undefined;
|
||||
let ttsDefault: string | undefined;
|
||||
if (this._cloudActive) {
|
||||
@ -79,6 +89,7 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
|
||||
public closeDialog(): void {
|
||||
this._params = undefined;
|
||||
this._data = undefined;
|
||||
this._hideWakeWord = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
@ -91,6 +102,10 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
|
||||
this._supportedLanguages = languages;
|
||||
}
|
||||
|
||||
private _hasWakeWorkEntities = memoizeOne((states: HomeAssistant["states"]) =>
|
||||
Object.keys(states).some((entityId) => entityId.startsWith("wake_word."))
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this._params || !this._data) {
|
||||
return nothing;
|
||||
@ -118,6 +133,27 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
<span slot="title" .title=${title}>${title}</span>
|
||||
${!this._hideWakeWord ||
|
||||
this._params.hideWakeWord ||
|
||||
!this._hasWakeWorkEntities(this.hass.states)
|
||||
? nothing
|
||||
: html`<ha-button-menu
|
||||
slot="actionItems"
|
||||
@action=${this._handleShowWakeWord}
|
||||
@closed=${stopPropagation}
|
||||
menuCorner="END"
|
||||
corner="BOTTOM_END"
|
||||
>
|
||||
<ha-icon-button
|
||||
.path=${mdiDotsVertical}
|
||||
slot="trigger"
|
||||
></ha-icon-button>
|
||||
<mwc-list-item>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.voice_assistants.assistants.pipeline.detail.add_streaming_wake_word"
|
||||
)}
|
||||
</mwc-list-item></ha-button-menu
|
||||
>`}
|
||||
</ha-dialog-header>
|
||||
<div class="content">
|
||||
${this._error
|
||||
@ -171,7 +207,7 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
|
||||
keys="tts_engine,tts_language,tts_voice"
|
||||
@value-changed=${this._valueChanged}
|
||||
></assist-pipeline-detail-tts>
|
||||
${this._params.hideWakeWord
|
||||
${this._hideWakeWord
|
||||
? nothing
|
||||
: html`<assist-pipeline-detail-wakeword
|
||||
.hass=${this.hass}
|
||||
@ -198,6 +234,10 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleShowWakeWord() {
|
||||
this._hideWakeWord = false;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
this._error = undefined;
|
||||
const value = {};
|
||||
|
@ -315,7 +315,6 @@ export class HaPanelLogbook extends LitElement {
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
padding: 8px 16px 0;
|
||||
}
|
||||
|
||||
|
@ -195,6 +195,9 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
|
||||
.statTypes=${this._statTypes!}
|
||||
.names=${this._names}
|
||||
.unit=${this._unit}
|
||||
.minYAxis=${this._config.min_y_axis}
|
||||
.maxYAxis=${this._config.max_y_axis}
|
||||
.fitYData=${this._config.fit_y_data || false}
|
||||
.hideLegend=${this._config.hide_legend || false}
|
||||
.logarithmicScale=${this._config.logarithmic_scale || false}
|
||||
></statistics-chart>
|
||||
|
@ -356,6 +356,9 @@ export interface StatisticsGraphCardConfig extends LovelaceCardConfig {
|
||||
period?: "5minute" | "hour" | "day" | "month";
|
||||
stat_types?: StatisticType | StatisticType[];
|
||||
chart_type?: "line" | "bar";
|
||||
min_y_axis?: number;
|
||||
max_y_axis?: number;
|
||||
fit_y_data?: boolean;
|
||||
hide_legend?: boolean;
|
||||
logarithmic_scale?: boolean;
|
||||
}
|
||||
|
@ -69,6 +69,9 @@ const cardConfigStruct = assign(
|
||||
unit: optional(string()),
|
||||
hide_legend: optional(boolean()),
|
||||
logarithmic_scale: optional(boolean()),
|
||||
min_y_axis: optional(number()),
|
||||
max_y_axis: optional(number()),
|
||||
fit_y_data: optional(boolean()),
|
||||
})
|
||||
);
|
||||
|
||||
@ -126,7 +129,8 @@ export class HuiStatisticsGraphCardEditor
|
||||
(
|
||||
localize: LocalizeFunc,
|
||||
statisticIds: string[] | undefined,
|
||||
metaDatas: StatisticsMetaData[] | undefined
|
||||
metaDatas: StatisticsMetaData[] | undefined,
|
||||
showFitOption: boolean
|
||||
) => {
|
||||
const units = new Set<string>();
|
||||
metaDatas?.forEach((metaData) => {
|
||||
@ -213,6 +217,33 @@ export class HuiStatisticsGraphCardEditor
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "min_y_axis",
|
||||
required: false,
|
||||
selector: { number: { mode: "box", step: "any" } },
|
||||
},
|
||||
{
|
||||
name: "max_y_axis",
|
||||
required: false,
|
||||
selector: { number: { mode: "box", step: "any" } },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
...(showFitOption
|
||||
? [
|
||||
{
|
||||
name: "fit_y_data",
|
||||
required: false,
|
||||
selector: { boolean: {} },
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
{
|
||||
name: "hide_legend",
|
||||
required: false,
|
||||
@ -254,7 +285,9 @@ export class HuiStatisticsGraphCardEditor
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
this._configEntities,
|
||||
this._metaDatas
|
||||
this._metaDatas,
|
||||
this._config!.min_y_axis !== undefined ||
|
||||
this._config!.max_y_axis !== undefined
|
||||
);
|
||||
const configured_stat_types = this._config!.stat_types
|
||||
? ensureArray(this._config.stat_types)
|
||||
@ -359,6 +392,9 @@ export class HuiStatisticsGraphCardEditor
|
||||
case "unit":
|
||||
case "hide_legend":
|
||||
case "logarithmic_scale":
|
||||
case "min_y_axis":
|
||||
case "max_y_axis":
|
||||
case "fit_y_data":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.statistics-graph.${schema.name}`
|
||||
);
|
||||
|
@ -367,7 +367,8 @@
|
||||
"copied": "Copied",
|
||||
"copied_clipboard": "Copied to clipboard",
|
||||
"name": "Name",
|
||||
"optional": "optional"
|
||||
"optional": "optional",
|
||||
"default": "Default"
|
||||
},
|
||||
"components": {
|
||||
"selectors": {
|
||||
@ -881,6 +882,12 @@
|
||||
"restore": "Restore defaults"
|
||||
}
|
||||
},
|
||||
"siren": {
|
||||
"advanced_controls": "Advanced controls",
|
||||
"tone": "Tone",
|
||||
"duration": "Duration",
|
||||
"volume": "Volume"
|
||||
},
|
||||
"media-browser": {
|
||||
"tts": {
|
||||
"message": "Message",
|
||||
@ -2179,7 +2186,7 @@
|
||||
"create_backup": "[%key:supervisor::backup::create_backup%]",
|
||||
"creating_backup": "Backup is currently being created",
|
||||
"download_backup": "[%key:supervisor::backup::download_backup%]",
|
||||
"remove_backup": "[%key:supervisor::backup::delete_backup_title%]",
|
||||
"remove_backup": "Delete backup",
|
||||
"name": "[%key:supervisor::backup::name%]",
|
||||
"path": "Path",
|
||||
"size": "[%key:supervisor::backup::size%]",
|
||||
@ -2224,7 +2231,7 @@
|
||||
"name": "Name",
|
||||
"description": "Description",
|
||||
"tag_id": "Tag ID",
|
||||
"tag_id_placeholder": "Autogenerated when left empty",
|
||||
"tag_id_placeholder": "Autogenerated if left empty",
|
||||
"delete": "Delete",
|
||||
"update": "Update",
|
||||
"create": "Create",
|
||||
@ -2712,6 +2719,7 @@
|
||||
"try_tts": "Try voice",
|
||||
"debug": "Debug",
|
||||
"set_as_preferred": "Set as preferred",
|
||||
"add_streaming_wake_word": "Add streaming wake word",
|
||||
"form": {
|
||||
"name": "[%key:ui::common::name%]",
|
||||
"conversation_engine": "Conversation agent",
|
||||
@ -2743,10 +2751,9 @@
|
||||
"description": "When you are controlling your assistant with voice, the text-to-speech engine turns the conversation text responses into audio."
|
||||
},
|
||||
"wakeword": {
|
||||
"title": "Wake word",
|
||||
"description": "If a device supports wake words, you can activate Assist by saying this word.",
|
||||
"no_wake_words": "It looks like you don't have a wake word engine setup yet.",
|
||||
"no_wake_words_link": "Find out more about wake words."
|
||||
"title": "Streaming wake word engine",
|
||||
"description": " If a device supports streaming wake word engines, you can activate Assist by saying this word.",
|
||||
"note": "Most recent devices support on-device wake word engines and are configured on their device page."
|
||||
}
|
||||
},
|
||||
"no_cloud_message": "You should have an active cloud subscription to use cloud speech services.",
|
||||
@ -3275,9 +3282,9 @@
|
||||
"value_template": "[%key:ui::panel::config::automation::editor::triggers::type::numeric_state::value_template%]",
|
||||
"description": {
|
||||
"picker": "If the numeric value of an entity''s state (or attribute''s value) is above or below a given threshold.",
|
||||
"above": "When {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} {numberOfEntities, plural,\n one {is}\n other {are}\n} above {above}",
|
||||
"below": "When {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} {numberOfEntities, plural,\n one {is}\n other {are}\n} below {below}",
|
||||
"above-below": "When {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} {numberOfEntities, plural,\n one {is}\n other {are}\n} above {above} and below {below}"
|
||||
"above": "If {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} {numberOfEntities, plural,\n one {is}\n other {are}\n} above {above}",
|
||||
"below": "If {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} {numberOfEntities, plural,\n one {is}\n other {are}\n} below {below}",
|
||||
"above-below": "If {attribute, select, \n undefined {} \n other {{attribute} from }\n }{entity} {numberOfEntities, plural,\n one {is}\n other {are}\n} above {above} and below {below}"
|
||||
}
|
||||
},
|
||||
"or": {
|
||||
@ -3347,7 +3354,7 @@
|
||||
"id": "Trigger",
|
||||
"description": {
|
||||
"picker": "If the automation has been triggered by a specific trigger.",
|
||||
"full": "When triggered by {id}"
|
||||
"full": "If triggered by {id}"
|
||||
}
|
||||
},
|
||||
"zone": {
|
||||
@ -3597,7 +3604,7 @@
|
||||
"set_conversation_response": {
|
||||
"label": "Set conversation response",
|
||||
"description": {
|
||||
"picker": "Set response of conversation when automation was triggered by conversation trigger.",
|
||||
"picker": "Set response of conversation if automation was triggered by conversation trigger.",
|
||||
"full": "Set response of conversation to {response}"
|
||||
}
|
||||
},
|
||||
@ -6155,7 +6162,10 @@
|
||||
"pick_statistic": "Add a statistic",
|
||||
"picked_statistic": "Statistic",
|
||||
"hide_legend": "Hide legend",
|
||||
"logarithmic_scale": "Logarithmic scale"
|
||||
"logarithmic_scale": "Logarithmic scale",
|
||||
"min_y_axis": "Y axis minimum",
|
||||
"max_y_axis": "Y axis maximum",
|
||||
"fit_y_data": "Extend Y axis limits to fit data"
|
||||
},
|
||||
"statistic": {
|
||||
"name": "Statistic",
|
||||
@ -7857,7 +7867,7 @@
|
||||
"create_blocked_not_running": "Creating a backup is not possible right now because the system is in \"{state}\" state.",
|
||||
"restore_blocked_not_running": "Restoring a backup is not possible right now because the system is in \"{state}\" state.",
|
||||
"delete_selected": "Delete selected backups",
|
||||
"delete_backup_title": "Delete backup",
|
||||
"delete_backup_title": "Delete backups?",
|
||||
"delete_backup_text": "Do you want to delete {number} {number, plural,\n one {backup}\n other {backups}\n}?",
|
||||
"delete_backup_confirm": "delete",
|
||||
"selected": "{number} selected",
|
||||
|
Loading…
x
Reference in New Issue
Block a user