Merge dialog with more info

This commit is contained in:
Paul Bottein 2025-07-10 14:43:46 +02:00
parent 3269fd3c5b
commit 9e4835107d
No known key found for this signature in database
7 changed files with 430 additions and 458 deletions

View File

@ -0,0 +1,225 @@
import { mdiLightbulb, mdiLightbulbOff } from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { computeDomain } from "../../../../common/entity/compute_domain";
import { computeStateDomain } from "../../../../common/entity/compute_state_domain";
import { computeGroupEntitiesState } from "../../../../common/entity/group_entities";
import "../../../../components/ha-control-button";
import "../../../../components/ha-control-button-group";
import "../../../../components/ha-domain-icon";
import { isFullyClosed, isFullyOpen } from "../../../../data/cover";
import { UNAVAILABLE } from "../../../../data/entity";
import { forwardHaptic } from "../../../../data/haptics";
import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
import type { TileCardConfig } from "../../../../panels/lovelace/cards/types";
import "../../../../panels/lovelace/sections/hui-section";
import type { HomeAssistant } from "../../../../types";
export interface GroupToggleDialogParams {
entityIds: string[];
}
@customElement("ha-more-info-view-toggle-group")
class HaMoreInfoViewToggleGroup extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public params?: GroupToggleDialogParams;
private _sectionConfig = memoizeOne(
(entities: string[]): LovelaceSectionConfig => ({
type: "grid",
cards: entities.map<TileCardConfig>((entity) => ({
type: "tile",
entity: entity,
icon_tap_action: {
action: "toggle",
},
tap_action: {
action: "more-info",
},
grid_options: {
columns: 12,
},
})),
})
);
protected render() {
if (!this.params) {
return nothing;
}
const sectionConfig = this._sectionConfig(this.params.entityIds);
const entities = this.params.entityIds
.map((entityId) => this.hass!.states[entityId] as HassEntity | undefined)
.filter((v): v is HassEntity => Boolean(v));
const mainStateObj = entities[0];
const groupState = computeGroupEntitiesState(entities);
const formattedGroupState = this.hass.formatEntityState(
mainStateObj,
groupState
);
const domain = computeStateDomain(mainStateObj);
const deviceClass = mainStateObj.attributes.device_class;
const isGroup = this.params.entityIds.length > 1;
const isAllOn = entities.every((entity) =>
entity.state === UNAVAILABLE ||
computeDomain(entity.entity_id) === "cover"
? isFullyOpen(entity)
: entity.state === "on"
);
const isAllOff = entities.every((entity) =>
entity.state === UNAVAILABLE ||
computeDomain(entity.entity_id) === "cover"
? isFullyClosed(entity)
: entity.state === "off"
);
return html`
<div class="content">
<ha-more-info-state-header
.hass=${this.hass}
.stateObj=${mainStateObj}
.stateOverride=${formattedGroupState}
></ha-more-info-state-header>
<ha-control-button-group vertical>
<ha-control-button
vertical
@click=${this._turnAllOn}
.disabled=${isAllOn}
>
${domain !== "light"
? html`<ha-domain-icon
.hass=${this.hass}
.domain=${domain}
.state=${domain === "cover" ? "open" : "on"}
.deviceClass=${deviceClass}
></ha-domain-icon>`
: html` <ha-svg-icon .path=${mdiLightbulb}></ha-svg-icon> `}
<p>
${domain === "cover"
? isGroup
? "Open all"
: "Open"
: isGroup
? "Turn all on"
: "Turn on"}
</p>
</ha-control-button>
<ha-control-button
vertical
@click=${this._turnAllOff}
.disabled=${isAllOff}
>
${domain !== "light"
? html`
<ha-domain-icon
.hass=${this.hass}
.domain=${domain}
.state=${domain === "cover" ? "closed" : "off"}
.deviceClass=${deviceClass}
></ha-domain-icon>
`
: html` <ha-svg-icon .path=${mdiLightbulbOff}></ha-svg-icon>`}
<p>
${domain === "cover"
? isGroup
? "Close all"
: "Close"
: isGroup
? "Turn all off"
: "Turn off"}
</p>
</ha-control-button>
</ha-control-button-group>
<hui-section .config=${sectionConfig} .hass=${this.hass}></hui-section>
</div>
`;
}
private _turnAllOff() {
if (!this.params) {
return;
}
forwardHaptic("light");
const domain = computeDomain(this.params.entityIds[0]);
if (domain === "cover") {
this.hass.callService("cover", "close_cover", {
entity_id: this.params.entityIds,
});
return;
}
this.hass.callService("homeassistant", "turn_off", {
entity_id: this.params.entityIds,
});
}
private _turnAllOn() {
if (!this.params) {
return;
}
forwardHaptic("light");
const domain = computeDomain(this.params.entityIds[0]);
if (domain === "cover") {
this.hass.callService("cover", "open_cover", {
entity_id: this.params.entityIds,
});
return;
}
this.hass.callService("homeassistant", "turn_on", {
entity_id: this.params.entityIds,
});
}
static styles = [
css`
.content {
padding: 24px;
padding-bottom: max(var(--safe-area-inset-bottom), 24px);
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
ha-control-button-group {
--control-button-group-spacing: 12px;
--control-button-group-thickness: 130px;
margin-bottom: 32px;
}
ha-control-button {
--control-button-border-radius: 16px;
--mdc-icon-size: 24px;
color: #006787;
--control-button-padding: 16px 8px;
--control-button-icon-color: #006787;
--control-button-background-color: #eff9fe;
--control-button-background-opacity: 1;
--control-button-focus-color: #006787;
--ha-ripple-color: #006787;
}
ha-control-button p {
margin: 0;
}
hui-section {
width: 100%;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-more-info-view-toggle-group": HaMoreInfoViewToggleGroup;
}
}

View File

@ -8,9 +8,9 @@ export const showVoiceAssistantsView = (
title: string
): void => {
fireEvent(element, "show-child-view", {
viewTag: "ha-more-info-view-voice-assistants",
viewImport: loadVoiceAssistantsView,
viewTitle: title,
viewParams: {},
tag: "ha-more-info-view-voice-assistants",
import: loadVoiceAssistantsView,
title: title,
params: {},
});
};

View File

@ -10,7 +10,7 @@ import {
mdiPencilOutline,
} from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
import type { PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { cache } from "lit/directives/cache";
@ -68,15 +68,24 @@ export interface MoreInfoDialogParams {
view?: View;
/** @deprecated Use `view` instead */
tab?: View;
parentView?: ParentView;
}
type View = "info" | "history" | "settings" | "related";
type View = "info" | "history" | "settings" | "related" | "parent";
interface ParentView {
tag: string;
title?: string;
subtitle?: string;
import?: () => Promise<unknown>;
params?: any;
}
interface ChildView {
viewTag: string;
viewTitle?: string;
viewImport?: () => Promise<unknown>;
viewParams?: any;
tag: string;
title?: string;
import?: () => Promise<unknown>;
params?: any;
}
declare global {
@ -88,7 +97,8 @@ declare global {
}
}
const DEFAULT_VIEW: View = "info";
const INFO_VIEW: View = "info";
const PARENT_VIEW: View = "parent";
@customElement("ha-more-info-dialog")
export class MoreInfoDialog extends LitElement {
@ -98,9 +108,11 @@ export class MoreInfoDialog extends LitElement {
@state() private _entityId?: string | null;
@state() private _currView: View = DEFAULT_VIEW;
@state() private _currView: View = INFO_VIEW;
@state() private _initialView: View = DEFAULT_VIEW;
@state() private _initialView: View = INFO_VIEW;
@state() private _parentView?: ParentView;
@state() private _childView?: ChildView;
@ -114,17 +126,29 @@ export class MoreInfoDialog extends LitElement {
public showDialog(params: MoreInfoDialogParams) {
this._entityId = params.entityId;
if (!this._entityId) {
if (!this._entityId && !params.parentView) {
this.closeDialog();
return;
}
this._currView = params.view || DEFAULT_VIEW;
this._initialView = params.view || DEFAULT_VIEW;
this._parentView = params.parentView;
if (this._parentView?.import) {
this._parentView.import();
this._currView = PARENT_VIEW;
} else {
this._currView = params.view || INFO_VIEW;
}
this._initialView = params.view || INFO_VIEW;
this._childView = undefined;
this.large = false;
this._loadEntityRegistryEntry();
}
public willUpdate(changedProps: PropertyValues): void {
if (changedProps.has("_entityId")) {
this._loadEntityRegistryEntry();
}
}
private async _loadEntityRegistryEntry() {
if (!this._entityId) {
return;
@ -143,19 +167,18 @@ export class MoreInfoDialog extends LitElement {
this._entityId = undefined;
this._entry = undefined;
this._childView = undefined;
this._parentView = undefined;
this._currView = INFO_VIEW;
this._infoEditMode = false;
this._initialView = DEFAULT_VIEW;
this._initialView = INFO_VIEW;
this._isEscapeEnabled = true;
window.removeEventListener("dialog-closed", this._enableEscapeKeyClose);
window.removeEventListener("show-dialog", this._disableEscapeKeyClose);
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
private _shouldShowEditIcon(
domain: string,
stateObj: HassEntity | undefined
): boolean {
if (__DEMO__ || !stateObj) {
private _shouldShowEditIcon(domain?: string, stateObj?: HassEntity): boolean {
if (__DEMO__ || !stateObj || !domain) {
return false;
}
if (EDITABLE_DOMAINS_WITH_ID.includes(domain) && stateObj.attributes.id) {
@ -171,8 +194,9 @@ export class MoreInfoDialog extends LitElement {
return false;
}
private _shouldShowHistory(domain: string): boolean {
private _shouldShowHistory(domain?: string): boolean {
return (
domain !== undefined &&
DOMAINS_WITH_MORE_INFO.includes(domain) &&
(computeShowHistoryComponent(this.hass, this._entityId!) ||
computeShowLogBookComponent(
@ -207,14 +231,30 @@ export class MoreInfoDialog extends LitElement {
private _goBack() {
if (this._childView) {
this._childView = undefined;
} else {
this._setView(this._initialView);
return;
}
const previousView = this._previousView();
if (previousView) {
this._setView(previousView);
}
}
private _previousView(): View | undefined {
if (this._currView === PARENT_VIEW) {
return undefined;
}
if (this._currView !== this._initialView) {
return this._initialView;
}
if (this._parentView) {
return PARENT_VIEW;
}
return undefined;
}
private _resetInitialView() {
this._initialView = DEFAULT_VIEW;
this._setView(DEFAULT_VIEW);
this._initialView = INFO_VIEW;
this._setView(INFO_VIEW);
}
private _goToHistory() {
@ -227,8 +267,8 @@ export class MoreInfoDialog extends LitElement {
private _showChildView(ev: CustomEvent): void {
const view = ev.detail as ChildView;
if (view.viewImport) {
view.viewImport();
if (view.import) {
view.import();
}
this._childView = view;
}
@ -286,45 +326,99 @@ export class MoreInfoDialog extends LitElement {
this._sensorNumericDeviceClasses = deviceClasses.numeric_device_classes;
}
private _handleMoreInfoEvent(ev: CustomEvent) {
// If the parent view has a `show-dialog` event to open more info, we handle it here to set the entity ID and view.
const detail = ev.detail as MoreInfoDialogParams;
if (detail.entityId) {
this._entityId = detail.entityId;
this._setView(detail.view || INFO_VIEW);
ev.stopPropagation();
ev.preventDefault();
}
}
private _renderHeader = (): TemplateResult | typeof nothing => {
if (this._parentView && this._currView === PARENT_VIEW) {
return html`
${this._parentView
? html`<p class="breadcrumb">${this._parentView.subtitle}</p>`
: nothing}
<p class="main">${this._parentView.title}</p>
`;
}
const entityId = this._entityId;
if (entityId) {
const stateObj = this.hass.states[entityId] as HassEntity | undefined;
const context = stateObj
? getEntityContext(stateObj, this.hass)
: this._entry
? getEntityEntryContext(this._entry, this.hass)
: undefined;
const entityName = stateObj
? computeEntityName(stateObj, this.hass)
: this._entry
? computeEntityEntryName(this._entry, this.hass)
: entityId;
const deviceName = context?.device
? computeDeviceName(context.device)
: undefined;
const areaName = context?.area
? computeAreaName(context.area)
: undefined;
const breadcrumb = [areaName, deviceName, entityName].filter(
(v): v is string => Boolean(v)
);
const title = this._childView?.title || breadcrumb.pop() || entityId;
const isAdmin = this.hass.user!.is_admin;
return html`
${breadcrumb.length > 0
? !__DEMO__ && isAdmin
? html`
<button
class="breadcrumb"
@click=${this._breadcrumbClick}
aria-label=${breadcrumb.join(" > ")}
>
${join(breadcrumb, html`<ha-icon-next></ha-icon-next>`)}
</button>
`
: html`
<p class="breadcrumb">
${join(breadcrumb, html`<ha-icon-next></ha-icon-next>`)}
</p>
`
: nothing}
<p class="main">${title}</p>
`;
}
return nothing;
};
protected render() {
if (!this._entityId) {
if (!this._entityId && !this._parentView) {
return nothing;
}
const entityId = this._entityId;
const stateObj = this.hass.states[entityId] as HassEntity | undefined;
const stateObj = entityId ? this.hass.states[entityId] : undefined;
const domain = computeDomain(entityId);
const domain = entityId ? computeDomain(entityId) : undefined;
const isAdmin = this.hass.user!.is_admin;
const deviceId = this._getDeviceId();
const isDefaultView = this._currView === DEFAULT_VIEW && !this._childView;
const previousView = this._previousView();
const isDefaultView = this._currView === INFO_VIEW && !this._childView;
const isSpecificInitialView =
this._initialView !== DEFAULT_VIEW && !this._childView;
const showCloseIcon = isDefaultView || isSpecificInitialView;
const context = stateObj
? getEntityContext(stateObj, this.hass)
: this._entry
? getEntityEntryContext(this._entry, this.hass)
: undefined;
const entityName = stateObj
? computeEntityName(stateObj, this.hass)
: this._entry
? computeEntityEntryName(this._entry, this.hass)
: entityId;
const deviceName = context?.device
? computeDeviceName(context.device)
: undefined;
const areaName = context?.area ? computeAreaName(context.area) : undefined;
const breadcrumb = [areaName, deviceName, entityName].filter(
(v): v is string => Boolean(v)
);
const title = this._childView?.viewTitle || breadcrumb.pop() || entityId;
this._initialView !== INFO_VIEW && !this._childView;
const showCloseIcon = !previousView && !this._childView;
return html`
<ha-dialog
@ -332,7 +426,7 @@ export class MoreInfoDialog extends LitElement {
@closed=${this.closeDialog}
@opened=${this._handleOpened}
.escapeKeyAction=${this._isEscapeEnabled ? undefined : ""}
.heading=${title}
.heading=${" "}
hideActions
flexContent
>
@ -356,24 +450,7 @@ export class MoreInfoDialog extends LitElement {
></ha-icon-button-prev>
`}
<span slot="title" @click=${this._enlarge} class="title">
${breadcrumb.length > 0
? !__DEMO__ && isAdmin
? html`
<button
class="breadcrumb"
@click=${this._breadcrumbClick}
aria-label=${breadcrumb.join(" > ")}
>
${join(breadcrumb, html`<ha-icon-next></ha-icon-next>`)}
</button>
`
: html`
<p class="breadcrumb">
${join(breadcrumb, html`<ha-icon-next></ha-icon-next>`)}
</p>
`
: nothing}
<p class="main">${title}</p>
${this._renderHeader()}
</span>
${isDefaultView
? html`
@ -521,54 +598,62 @@ export class MoreInfoDialog extends LitElement {
@show-child-view=${this._showChildView}
@entity-entry-updated=${this._entryUpdated}
@toggle-edit-mode=${this._handleToggleInfoEditModeEvent}
@hass-more-info=${this._handleMoreInfoEvent}
>
${cache(
this._childView
? html`
<div class="child-view">
${dynamicElement(this._childView.viewTag, {
${dynamicElement(this._childView.tag, {
hass: this.hass,
entry: this._entry,
params: this._childView.viewParams,
params: this._childView.params,
})}
</div>
`
: this._currView === "info"
? html`
<ha-more-info-info
dialogInitialFocus
.hass=${this.hass}
.entityId=${this._entityId}
.entry=${this._entry}
.editMode=${this._infoEditMode}
></ha-more-info-info>
`
: this._currView === "history"
: this._currView === "parent"
? dynamicElement(this._parentView!.tag, {
hass: this.hass,
entry: this._entry,
params: this._parentView!.params,
})
: this._currView === "info"
? html`
<ha-more-info-history-and-logbook
<ha-more-info-info
dialogInitialFocus
.hass=${this.hass}
.entityId=${this._entityId}
></ha-more-info-history-and-logbook>
.entry=${this._entry}
.editMode=${this._infoEditMode}
></ha-more-info-info>
`
: this._currView === "settings"
: this._currView === "history"
? html`
<ha-more-info-settings
<ha-more-info-history-and-logbook
.hass=${this.hass}
.entityId=${this._entityId}
.entry=${this._entry}
></ha-more-info-settings>
></ha-more-info-history-and-logbook>
`
: this._currView === "related"
: this._currView === "settings"
? html`
<ha-related-items
<ha-more-info-settings
.hass=${this.hass}
.itemId=${entityId}
.itemType=${SearchableDomains.has(domain)
? (domain as ItemType)
: "entity"}
></ha-related-items>
.entityId=${this._entityId}
.entry=${this._entry}
></ha-more-info-settings>
`
: nothing
: this._currView === "related"
? html`
<ha-related-items
.hass=${this.hass}
.itemId=${entityId}
.itemType=${domain &&
SearchableDomains.has(domain)
? (domain as ItemType)
: "entity"}
></ha-related-items>
`
: nothing
)}
</div>
</ha-dialog>

View File

@ -15,10 +15,11 @@ import "../../../components/ha-control-button-group";
import "../../../components/ha-domain-icon";
import "../../../components/ha-svg-icon";
import type { AreaRegistryEntry } from "../../../data/area_registry";
import type { GroupToggleDialogParams } from "../../../dialogs/more-info/components/voice/ha-more-info-view-toggle-group";
import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog";
import { computeCssVariable } from "../../../resources/css-variables";
import type { HomeAssistant } from "../../../types";
import type { AreaCardFeatureContext } from "../cards/hui-area-card";
import { showGroupControlDialog } from "../dialogs/show-group-toggle-dialog";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { cardFeatureStyles } from "./common/card-feature-styles";
import type {
@ -176,10 +177,20 @@ class HuiAreaControlsCardFeature
const domain = AREA_CONTROLS_BUTTONS[control].filter.domain;
showGroupControlDialog(this, {
title: computeAreaName(this._area!) || "",
subtitle: domain,
entityIds: entitiesIds,
showMoreInfoDialog(this, {
entityId: null,
parentView: {
title: computeAreaName(this._area!) || "",
subtitle: domain,
tag: "ha-more-info-view-toggle-group",
import: () =>
import(
"../../../dialogs/more-info/components/voice/ha-more-info-view-toggle-group"
),
params: {
entityIds: entitiesIds,
} as GroupToggleDialogParams,
},
});
}

View File

@ -1,332 +0,0 @@
import { mdiClose, mdiLightbulb, mdiLightbulbOff } from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
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 { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { computeGroupEntitiesState } from "../../../common/entity/group_entities";
import "../../../components/ha-control-button";
import "../../../components/ha-control-button-group";
import "../../../components/ha-dialog";
import "../../../components/ha-dialog-header";
import "../../../components/ha-domain-icon";
import "../../../components/ha-header-bar";
import { forwardHaptic } from "../../../data/haptics";
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
import "../../../dialogs/more-info/components/ha-more-info-state-header";
import { haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import type { TileCardConfig } from "../cards/types";
import "../sections/hui-section";
import type { GroupToggleDialogParams } from "./show-group-toggle-dialog";
import { isFullyClosed, isFullyOpen } from "../../../data/cover";
import { UNAVAILABLE } from "../../../data/entity";
@customElement("hui-dialog-group-toggle")
class HuiGroupToggleDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _params?: GroupToggleDialogParams;
public async showDialog(params: GroupToggleDialogParams): Promise<void> {
this._params = params;
}
public async closeDialog(): Promise<void> {
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
private _sectionConfig = memoizeOne(
(entities: string[]): LovelaceSectionConfig => ({
type: "grid",
cards: entities.map<TileCardConfig>((entity) => ({
type: "tile",
entity: entity,
icon_tap_action: {
action: "toggle",
},
tap_action: {
action: "more-info",
},
grid_options: {
columns: 12,
},
})),
})
);
protected render() {
if (!this._params) {
return nothing;
}
const sectionConfig = this._sectionConfig(this._params.entityIds);
const entities = this._params.entityIds
.map((entityId) => this.hass!.states[entityId] as HassEntity | undefined)
.filter((v): v is HassEntity => Boolean(v));
const mainStateObj = entities[0];
const groupState = computeGroupEntitiesState(entities);
const formattedGroupState = this.hass.formatEntityState(
mainStateObj,
groupState
);
const domain = computeStateDomain(mainStateObj);
const deviceClass = mainStateObj.attributes.device_class;
const isGroup = this._params.entityIds.length > 1;
const isAllOn = entities.every((entity) =>
entity.state === UNAVAILABLE ||
computeDomain(entity.entity_id) === "cover"
? isFullyOpen(entity)
: entity.state === "on"
);
const isAllOff = entities.every((entity) =>
entity.state === UNAVAILABLE ||
computeDomain(entity.entity_id) === "cover"
? isFullyClosed(entity)
: entity.state === "off"
);
return html`
<ha-dialog
open
@closed=${this.closeDialog}
.heading=${this._params.title}
hideActions
flexContent
>
<ha-dialog-header slot="heading">
<ha-icon-button
slot="navigationIcon"
dialogAction="cancel"
.label=${this.hass.localize("ui.common.close")}
.path=${mdiClose}
></ha-icon-button>
<span slot="title" class="title">
${this._params.subtitle
? html`<span class="subtitle">${this._params.subtitle}</span>`
: nothing}
<p class="main">${this._params.title}</p>
</span>
</ha-dialog-header>
<div class="content">
<ha-more-info-state-header
.hass=${this.hass}
.stateObj=${mainStateObj}
.stateOverride=${formattedGroupState}
></ha-more-info-state-header>
<ha-control-button-group vertical>
<ha-control-button
vertical
@click=${this._turnAllOn}
.disabled=${isAllOn}
>
${domain !== "light"
? html`<ha-domain-icon
.hass=${this.hass}
.domain=${domain}
.state=${domain === "cover" ? "open" : "on"}
.deviceClass=${deviceClass}
></ha-domain-icon>`
: html` <ha-svg-icon .path=${mdiLightbulb}></ha-svg-icon> `}
<p>
${domain === "cover"
? isGroup
? "Open all"
: "Open"
: isGroup
? "Turn all on"
: "Turn on"}
</p>
</ha-control-button>
<ha-control-button
vertical
@click=${this._turnAllOff}
.disabled=${isAllOff}
>
${domain !== "light"
? html`
<ha-domain-icon
.hass=${this.hass}
.domain=${domain}
.state=${domain === "cover" ? "closed" : "off"}
.deviceClass=${deviceClass}
></ha-domain-icon>
`
: html` <ha-svg-icon .path=${mdiLightbulbOff}></ha-svg-icon>`}
<p>
${domain === "cover"
? isGroup
? "Close all"
: "Close"
: isGroup
? "Turn all off"
: "Turn off"}
</p>
</ha-control-button>
</ha-control-button-group>
<hui-section
.config=${sectionConfig}
.hass=${this.hass}
></hui-section>
</div>
</ha-dialog>
`;
}
private _turnAllOff() {
if (!this._params) {
return;
}
forwardHaptic("light");
const domain = computeDomain(this._params.entityIds[0]);
if (domain === "cover") {
this.hass.callService("cover", "close_cover", {
entity_id: this._params.entityIds,
});
return;
}
this.hass.callService("homeassistant", "turn_off", {
entity_id: this._params.entityIds,
});
}
private _turnAllOn() {
if (!this._params) {
return;
}
forwardHaptic("light");
const domain = computeDomain(this._params.entityIds[0]);
if (domain === "cover") {
this.hass.callService("cover", "open_cover", {
entity_id: this._params.entityIds,
});
return;
}
this.hass.callService("homeassistant", "turn_on", {
entity_id: this._params.entityIds,
});
}
static styles = [
haStyleDialog,
css`
ha-dialog {
/* Set the top top of the dialog to a fixed position, so it doesnt jump when the content changes size */
--vertical-align-dialog: flex-start;
--dialog-surface-margin-top: 40px;
--dialog-content-padding: 0;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
/* When in fullscreen dialog should be attached to top */
ha-dialog {
--dialog-surface-margin-top: 0px;
}
}
@media all and (min-width: 600px) and (min-height: 501px) {
ha-dialog {
--mdc-dialog-min-width: 580px;
--mdc-dialog-max-width: 580px;
--mdc-dialog-max-height: calc(100% - 72px);
}
.main-title {
cursor: default;
}
:host([large]) ha-dialog {
--mdc-dialog-min-width: 90vw;
--mdc-dialog-max-width: 90vw;
}
}
.title {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.title p {
margin: 0;
min-width: 0;
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
}
.title .main {
color: var(--primary-text-color);
font-size: var(--ha-font-size-xl);
line-height: var(--ha-line-height-condensed);
}
.title .subtitle {
color: var(--secondary-text-color);
font-size: var(--ha-font-size-m);
line-height: 16px;
--mdc-icon-size: 16px;
padding: 4px;
margin: -4px;
margin-top: -10px;
background: none;
border: none;
outline: none;
display: inline;
border-radius: 6px;
transition: background-color 180ms ease-in-out;
min-width: 0;
max-width: 100%;
text-overflow: ellipsis;
overflow: hidden;
text-align: left;
}
.content {
padding: 0 16px 16px 16px;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
ha-control-button-group {
--control-button-group-spacing: 12px;
--control-button-group-thickness: 130px;
margin-bottom: 32px;
}
ha-control-button {
--control-button-border-radius: 16px;
--mdc-icon-size: 24px;
color: #006787;
--control-button-padding: 16px 8px;
--control-button-icon-color: #006787;
--control-button-background-color: #eff9fe;
--control-button-background-opacity: 1;
--control-button-focus-color: #006787;
--ha-ripple-color: #006787;
}
ha-control-button p {
margin: 0;
}
hui-section {
width: 100%;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"hui-dialog-group-toggle": HuiGroupToggleDialog;
}
}

View File

@ -1,18 +0,0 @@
import { fireEvent } from "../../../common/dom/fire_event";
export interface GroupToggleDialogParams {
title: string;
subtitle?: string;
entityIds: string[];
}
export const showGroupControlDialog = (
element: HTMLElement,
dialogParams: GroupToggleDialogParams
) => {
fireEvent(element, "show-dialog", {
dialogTag: "hui-dialog-group-toggle",
dialogImport: () => import("./hui-dialog-group-toggle"),
dialogParams: dialogParams,
});
};

View File

@ -30,6 +30,7 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
{
entityId: ev.detail.entityId,
view: ev.detail.view || ev.detail.tab,
parentView: ev.detail.parentView,
},
() => import("../dialogs/more-info/ha-more-info-dialog")
);