Merge branch 'dev' into allthebackupchanges

This commit is contained in:
Paul Bottein 2024-11-20 09:30:54 +01:00
commit 7b4536564e
No known key found for this signature in database
15 changed files with 416 additions and 48 deletions

View File

@ -1,6 +1,6 @@
export default { export default {
"*.?(c|m){js,ts}": [ "*.?(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", "prettier --cache --write",
"lit-analyzer --quiet", "lit-analyzer --quiet",
], ],

View File

@ -72,6 +72,12 @@ export class StatisticsChart extends LitElement {
@property() public chartType: ChartType = "line"; @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 hideLegend = false;
@property({ type: Boolean }) public logarithmicScale = false; @property({ type: Boolean }) public logarithmicScale = false;
@ -113,6 +119,9 @@ export class StatisticsChart extends LitElement {
changedProps.has("unit") || changedProps.has("unit") ||
changedProps.has("period") || changedProps.has("period") ||
changedProps.has("chartType") || changedProps.has("chartType") ||
changedProps.has("minYAxis") ||
changedProps.has("maxYAxis") ||
changedProps.has("fitYData") ||
changedProps.has("logarithmicScale") || changedProps.has("logarithmicScale") ||
changedProps.has("hideLegend") changedProps.has("hideLegend")
) { ) {
@ -232,6 +241,8 @@ export class StatisticsChart extends LitElement {
text: unit || this.unit, text: unit || this.unit,
}, },
type: this.logarithmicScale ? "logarithmic" : "linear", type: this.logarithmicScale ? "logarithmic" : "linear",
min: this.fitYData ? null : this.minYAxis,
max: this.fitYData ? null : this.maxYAxis,
}, },
}, },
plugins: { plugins: {

View File

@ -20,6 +20,8 @@ class HaHLSPlayer extends LitElement {
@property() public entityid?: string; @property() public entityid?: string;
@property() public url?: string;
@property({ attribute: "poster-url" }) public posterUrl?: string; @property({ attribute: "poster-url" }) public posterUrl?: string;
@property({ type: Boolean, attribute: "controls" }) @property({ type: Boolean, attribute: "controls" })
@ -94,14 +96,19 @@ class HaHLSPlayer extends LitElement {
super.updated(changedProps); super.updated(changedProps);
const entityChanged = changedProps.has("entityid"); const entityChanged = changedProps.has("entityid");
const urlChanged = changedProps.has("url");
if (!entityChanged) { if (entityChanged) {
return; 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._cleanUp();
this._resetError(); this._resetError();

View File

@ -23,6 +23,7 @@ export const STATE_ATTRIBUTES = [
"state_class", "state_class",
"supported_features", "supported_features",
"unit_of_measurement", "unit_of_measurement",
"available_tones",
]; ];
export const TEMPERATURE_ATTRIBUTES = new Set([ export const TEMPERATURE_ATTRIBUTES = new Set([

7
src/data/siren.ts Normal file
View File

@ -0,0 +1,7 @@
export const SirenEntityFeature = {
TURN_ON: 1,
TURN_OFF: 2,
TONES: 4,
VOLUME_SET: 8,
DURATION: 16,
};

View File

@ -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;
}
}

View File

@ -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,
},
});
};

View File

@ -5,9 +5,13 @@ import { LitElement, html, nothing } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import "../../../components/ha-attributes"; import "../../../components/ha-attributes";
import "../../../state-control/ha-state-control-toggle"; import "../../../state-control/ha-state-control-toggle";
import "../../../components/ha-button";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import "../components/ha-more-info-state-header"; import "../components/ha-more-info-state-header";
import { moreInfoControlStyle } from "../components/more-info-control-style"; 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") @customElement("more-info-siren")
class MoreInfoSiren extends LitElement { class MoreInfoSiren extends LitElement {
@ -20,6 +24,20 @@ class MoreInfoSiren extends LitElement {
return nothing; 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` return html`
<ha-more-info-state-header <ha-more-info-state-header
.hass=${this.hass} .hass=${this.hass}
@ -32,6 +50,11 @@ class MoreInfoSiren extends LitElement {
.iconPathOn=${mdiVolumeHigh} .iconPathOn=${mdiVolumeHigh}
.iconPathOff=${mdiVolumeOff} .iconPathOff=${mdiVolumeOff}
></ha-state-control-toggle> ></ha-state-control-toggle>
${allowAdvanced
? html`<ha-button @click=${this._showAdvancedControlsDialog}>
${this.hass.localize("ui.components.siren.advanced_controls")}
</ha-button>`
: nothing}
</div> </div>
<ha-attributes <ha-attributes
.hass=${this.hass} .hass=${this.hass}
@ -40,6 +63,10 @@ class MoreInfoSiren extends LitElement {
`; `;
} }
private _showAdvancedControlsDialog() {
showSirenAdvancedControlsView(this, this.stateObj!);
}
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return moreInfoControlStyle; return moreInfoControlStyle;
} }

View File

@ -1,5 +1,5 @@
import type { CSSResultGroup, PropertyValues } from "lit"; 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 { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import type { LocalizeKeys } from "../../../../common/translations/localize"; 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 { HomeAssistant } from "../../../../types";
import type { WakeWord } from "../../../../data/wake_word"; import type { WakeWord } from "../../../../data/wake_word";
import { fetchWakeWordInfo } from "../../../../data/wake_word"; import { fetchWakeWordInfo } from "../../../../data/wake_word";
import { documentationUrl } from "../../../../util/documentation-url";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
@customElement("assist-pipeline-detail-wakeword") @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() { protected render() {
const hasWakeWorkEntities = this._hasWakeWorkEntities(this.hass.states);
return html` return html`
<div class="section"> <div class="section">
<div class="content"> <div class="content">
@ -99,29 +93,17 @@ export class AssistPipelineDetailWakeWord extends LitElement {
`ui.panel.config.voice_assistants.assistants.pipeline.detail.steps.wakeword.description` `ui.panel.config.voice_assistants.assistants.pipeline.detail.steps.wakeword.description`
)} )}
</p> </p>
<ha-alert alert-type="info">
${this.hass.localize(
`ui.panel.config.voice_assistants.assistants.pipeline.detail.steps.wakeword.note`
)}
</ha-alert>
</div> </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 <ha-form
.schema=${this._schema(this._wakeWords)} .schema=${this._schema(this._wakeWords)}
.data=${this.data} .data=${this.data}
.hass=${this.hass} .hass=${this.hass}
.computeLabel=${this._computeLabel} .computeLabel=${this._computeLabel}
.disabled=${!hasWakeWorkEntities}
></ha-form> ></ha-form>
</div> </div>
</div> </div>

View File

@ -1,7 +1,8 @@
import { mdiClose } from "@mdi/js"; import { mdiClose, mdiDotsVertical } from "@mdi/js";
import type { CSSResultGroup } from "lit"; import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button"; import "../../../components/ha-button";
import "../../../components/ha-dialog-header"; import "../../../components/ha-dialog-header";
@ -21,6 +22,7 @@ import "./assist-pipeline-detail/assist-pipeline-detail-wakeword";
import "./debug/assist-render-pipeline-events"; import "./debug/assist-render-pipeline-events";
import type { VoiceAssistantPipelineDetailsDialogParams } from "./show-dialog-voice-assistant-pipeline-detail"; import type { VoiceAssistantPipelineDetailsDialogParams } from "./show-dialog-voice-assistant-pipeline-detail";
import { computeDomain } from "../../../common/entity/compute_domain"; import { computeDomain } from "../../../common/entity/compute_domain";
import { stopPropagation } from "../../../common/dom/stop_propagation";
@customElement("dialog-voice-assistant-pipeline-detail") @customElement("dialog-voice-assistant-pipeline-detail")
export class DialogVoiceAssistantPipelineDetail extends LitElement { export class DialogVoiceAssistantPipelineDetail extends LitElement {
@ -30,6 +32,8 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
@state() private _data?: Partial<AssistPipeline>; @state() private _data?: Partial<AssistPipeline>;
@state() private _hideWakeWord = false;
@state() private _cloudActive?: boolean; @state() private _cloudActive?: boolean;
@state() private _error?: Record<string, string>; @state() private _error?: Record<string, string>;
@ -42,11 +46,17 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
this._params = params; this._params = params;
this._error = undefined; this._error = undefined;
this._cloudActive = this._params.cloudActiveSubscription; this._cloudActive = this._params.cloudActiveSubscription;
if (this._params.pipeline) { if (this._params.pipeline) {
this._data = this._params.pipeline; this._data = this._params.pipeline;
this._hideWakeWord =
this._params.hideWakeWord || !this._data.wake_word_entity;
return; return;
} }
this._hideWakeWord = true;
let sstDefault: string | undefined; let sstDefault: string | undefined;
let ttsDefault: string | undefined; let ttsDefault: string | undefined;
if (this._cloudActive) { if (this._cloudActive) {
@ -79,6 +89,7 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
public closeDialog(): void { public closeDialog(): void {
this._params = undefined; this._params = undefined;
this._data = undefined; this._data = undefined;
this._hideWakeWord = false;
fireEvent(this, "dialog-closed", { dialog: this.localName }); fireEvent(this, "dialog-closed", { dialog: this.localName });
} }
@ -91,6 +102,10 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
this._supportedLanguages = languages; this._supportedLanguages = languages;
} }
private _hasWakeWorkEntities = memoizeOne((states: HomeAssistant["states"]) =>
Object.keys(states).some((entityId) => entityId.startsWith("wake_word."))
);
protected render() { protected render() {
if (!this._params || !this._data) { if (!this._params || !this._data) {
return nothing; return nothing;
@ -118,6 +133,27 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
.path=${mdiClose} .path=${mdiClose}
></ha-icon-button> ></ha-icon-button>
<span slot="title" .title=${title}>${title}</span> <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> </ha-dialog-header>
<div class="content"> <div class="content">
${this._error ${this._error
@ -171,7 +207,7 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
keys="tts_engine,tts_language,tts_voice" keys="tts_engine,tts_language,tts_voice"
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></assist-pipeline-detail-tts> ></assist-pipeline-detail-tts>
${this._params.hideWakeWord ${this._hideWakeWord
? nothing ? nothing
: html`<assist-pipeline-detail-wakeword : html`<assist-pipeline-detail-wakeword
.hass=${this.hass} .hass=${this.hass}
@ -198,6 +234,10 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
`; `;
} }
private _handleShowWakeWord() {
this._hideWakeWord = false;
}
private _valueChanged(ev: CustomEvent) { private _valueChanged(ev: CustomEvent) {
this._error = undefined; this._error = undefined;
const value = {}; const value = {};

View File

@ -315,7 +315,6 @@ export class HaPanelLogbook extends LitElement {
.filters { .filters {
display: flex; display: flex;
align-items: flex-end;
padding: 8px 16px 0; padding: 8px 16px 0;
} }

View File

@ -195,6 +195,9 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
.statTypes=${this._statTypes!} .statTypes=${this._statTypes!}
.names=${this._names} .names=${this._names}
.unit=${this._unit} .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} .hideLegend=${this._config.hide_legend || false}
.logarithmicScale=${this._config.logarithmic_scale || false} .logarithmicScale=${this._config.logarithmic_scale || false}
></statistics-chart> ></statistics-chart>

View File

@ -356,6 +356,9 @@ export interface StatisticsGraphCardConfig extends LovelaceCardConfig {
period?: "5minute" | "hour" | "day" | "month"; period?: "5minute" | "hour" | "day" | "month";
stat_types?: StatisticType | StatisticType[]; stat_types?: StatisticType | StatisticType[];
chart_type?: "line" | "bar"; chart_type?: "line" | "bar";
min_y_axis?: number;
max_y_axis?: number;
fit_y_data?: boolean;
hide_legend?: boolean; hide_legend?: boolean;
logarithmic_scale?: boolean; logarithmic_scale?: boolean;
} }

View File

@ -69,6 +69,9 @@ const cardConfigStruct = assign(
unit: optional(string()), unit: optional(string()),
hide_legend: optional(boolean()), hide_legend: optional(boolean()),
logarithmic_scale: 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, localize: LocalizeFunc,
statisticIds: string[] | undefined, statisticIds: string[] | undefined,
metaDatas: StatisticsMetaData[] | undefined metaDatas: StatisticsMetaData[] | undefined,
showFitOption: boolean
) => { ) => {
const units = new Set<string>(); const units = new Set<string>();
metaDatas?.forEach((metaData) => { 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", name: "hide_legend",
required: false, required: false,
@ -254,7 +285,9 @@ export class HuiStatisticsGraphCardEditor
const schema = this._schema( const schema = this._schema(
this.hass.localize, this.hass.localize,
this._configEntities, 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 const configured_stat_types = this._config!.stat_types
? ensureArray(this._config.stat_types) ? ensureArray(this._config.stat_types)
@ -359,6 +392,9 @@ export class HuiStatisticsGraphCardEditor
case "unit": case "unit":
case "hide_legend": case "hide_legend":
case "logarithmic_scale": case "logarithmic_scale":
case "min_y_axis":
case "max_y_axis":
case "fit_y_data":
return this.hass!.localize( return this.hass!.localize(
`ui.panel.lovelace.editor.card.statistics-graph.${schema.name}` `ui.panel.lovelace.editor.card.statistics-graph.${schema.name}`
); );

View File

@ -367,7 +367,8 @@
"copied": "Copied", "copied": "Copied",
"copied_clipboard": "Copied to clipboard", "copied_clipboard": "Copied to clipboard",
"name": "Name", "name": "Name",
"optional": "optional" "optional": "optional",
"default": "Default"
}, },
"components": { "components": {
"selectors": { "selectors": {
@ -881,6 +882,12 @@
"restore": "Restore defaults" "restore": "Restore defaults"
} }
}, },
"siren": {
"advanced_controls": "Advanced controls",
"tone": "Tone",
"duration": "Duration",
"volume": "Volume"
},
"media-browser": { "media-browser": {
"tts": { "tts": {
"message": "Message", "message": "Message",
@ -2179,7 +2186,7 @@
"create_backup": "[%key:supervisor::backup::create_backup%]", "create_backup": "[%key:supervisor::backup::create_backup%]",
"creating_backup": "Backup is currently being created", "creating_backup": "Backup is currently being created",
"download_backup": "[%key:supervisor::backup::download_backup%]", "download_backup": "[%key:supervisor::backup::download_backup%]",
"remove_backup": "[%key:supervisor::backup::delete_backup_title%]", "remove_backup": "Delete backup",
"name": "[%key:supervisor::backup::name%]", "name": "[%key:supervisor::backup::name%]",
"path": "Path", "path": "Path",
"size": "[%key:supervisor::backup::size%]", "size": "[%key:supervisor::backup::size%]",
@ -2224,7 +2231,7 @@
"name": "Name", "name": "Name",
"description": "Description", "description": "Description",
"tag_id": "Tag ID", "tag_id": "Tag ID",
"tag_id_placeholder": "Autogenerated when left empty", "tag_id_placeholder": "Autogenerated if left empty",
"delete": "Delete", "delete": "Delete",
"update": "Update", "update": "Update",
"create": "Create", "create": "Create",
@ -2712,6 +2719,7 @@
"try_tts": "Try voice", "try_tts": "Try voice",
"debug": "Debug", "debug": "Debug",
"set_as_preferred": "Set as preferred", "set_as_preferred": "Set as preferred",
"add_streaming_wake_word": "Add streaming wake word",
"form": { "form": {
"name": "[%key:ui::common::name%]", "name": "[%key:ui::common::name%]",
"conversation_engine": "Conversation agent", "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." "description": "When you are controlling your assistant with voice, the text-to-speech engine turns the conversation text responses into audio."
}, },
"wakeword": { "wakeword": {
"title": "Wake word", "title": "Streaming wake word engine",
"description": "If a device supports wake words, you can activate Assist by saying this word.", "description": " If a device supports streaming wake word engines, you can activate Assist by saying this word.",
"no_wake_words": "It looks like you don't have a wake word engine setup yet.", "note": "Most recent devices support on-device wake word engines and are configured on their device page."
"no_wake_words_link": "Find out more about wake words."
} }
}, },
"no_cloud_message": "You should have an active cloud subscription to use cloud speech services.", "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%]", "value_template": "[%key:ui::panel::config::automation::editor::triggers::type::numeric_state::value_template%]",
"description": { "description": {
"picker": "If the numeric value of an entity''s state (or attribute''s value) is above or below a given threshold.", "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}", "above": "If {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}", "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": "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-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": { "or": {
@ -3347,7 +3354,7 @@
"id": "Trigger", "id": "Trigger",
"description": { "description": {
"picker": "If the automation has been triggered by a specific trigger.", "picker": "If the automation has been triggered by a specific trigger.",
"full": "When triggered by {id}" "full": "If triggered by {id}"
} }
}, },
"zone": { "zone": {
@ -3597,7 +3604,7 @@
"set_conversation_response": { "set_conversation_response": {
"label": "Set conversation response", "label": "Set conversation response",
"description": { "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}" "full": "Set response of conversation to {response}"
} }
}, },
@ -6155,7 +6162,10 @@
"pick_statistic": "Add a statistic", "pick_statistic": "Add a statistic",
"picked_statistic": "Statistic", "picked_statistic": "Statistic",
"hide_legend": "Hide legend", "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": { "statistic": {
"name": "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.", "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.", "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_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_text": "Do you want to delete {number} {number, plural,\n one {backup}\n other {backups}\n}?",
"delete_backup_confirm": "delete", "delete_backup_confirm": "delete",
"selected": "{number} selected", "selected": "{number} selected",