mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 15:26:36 +00:00
Add tone,volume & duration selector to more-info dialog for sirens (#22786)
* Add tone selector to more-info for sirens * add selected tone to service call * rework the tone into an advanced controls dialog * tweaks from PR comments * fix % conversion * assume duration is in seconds
This commit is contained in:
parent
f4f2cce57e
commit
c9cad254d2
@ -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
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 { 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;
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user