mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-30 11:39:26 +00:00
Compare commits
1 Commits
use_summar
...
fix-fullsc
Author | SHA1 | Date | |
---|---|---|---|
![]() |
dbbcfd1707 |
@@ -7,20 +7,20 @@ import type {
|
||||
} from "@codemirror/autocomplete";
|
||||
import type { Extension, TransactionSpec } from "@codemirror/state";
|
||||
import type { EditorView, KeyBinding, ViewUpdate } from "@codemirror/view";
|
||||
import { mdiArrowExpand, mdiArrowCollapse } from "@mdi/js";
|
||||
import { mdiArrowCollapse, mdiArrowExpand } from "@mdi/js";
|
||||
import type { HassEntities } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, ReactiveElement, html, render } from "lit";
|
||||
import { css, html, ReactiveElement, render } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { getEntityContext } from "../common/entity/context/get_entity_context";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-code-editor-completion-items";
|
||||
import type { CompletionItem } from "./ha-code-editor-completion-items";
|
||||
import "./ha-icon";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-code-editor-completion-items";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
@@ -72,6 +72,8 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
|
||||
@state() private _isFullscreen = false;
|
||||
|
||||
private _fullscreenDialog?: HTMLDialogElement;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
private _loadedCodeMirror?: typeof import("../resources/codemirror");
|
||||
|
||||
@@ -120,7 +122,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
this.removeEventListener("keydown", stopPropagation);
|
||||
this.removeEventListener("keydown", this._handleKeyDown);
|
||||
if (this._isFullscreen) {
|
||||
this._toggleFullscreen();
|
||||
this._closeFullscreenDialog();
|
||||
}
|
||||
this.updateComplete.then(() => {
|
||||
this.codemirror!.destroy();
|
||||
@@ -180,9 +182,6 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
if (changedProps.has("error")) {
|
||||
this.classList.toggle("error-state", this.error);
|
||||
}
|
||||
if (changedProps.has("_isFullscreen")) {
|
||||
this.classList.toggle("fullscreen", this._isFullscreen);
|
||||
}
|
||||
if (changedProps.has("disableFullscreen")) {
|
||||
this._updateFullscreenButton();
|
||||
}
|
||||
@@ -312,10 +311,93 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
};
|
||||
|
||||
private _toggleFullscreen() {
|
||||
if (!this._isFullscreen) {
|
||||
this._openFullscreenDialog();
|
||||
} else {
|
||||
this._closeFullscreenDialog();
|
||||
}
|
||||
this._isFullscreen = !this._isFullscreen;
|
||||
this._updateFullscreenButton();
|
||||
}
|
||||
|
||||
private _openFullscreenDialog() {
|
||||
// Create dialog if it doesn't exist
|
||||
if (!this._fullscreenDialog) {
|
||||
this._fullscreenDialog = document.createElement("dialog");
|
||||
this._fullscreenDialog.style =
|
||||
"width: calc(100% - 16px); margin: 56px 8px 8px; height: calc(100% - 64px); border: none; border-radius: 8px;";
|
||||
|
||||
// Handle dialog close events
|
||||
this._fullscreenDialog.addEventListener("close", () => {
|
||||
if (this._isFullscreen) {
|
||||
this._isFullscreen = false;
|
||||
this._updateFullscreenButton();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle click on backdrop to close
|
||||
this._fullscreenDialog.addEventListener("click", (e) => {
|
||||
if (e.target === this._fullscreenDialog) {
|
||||
this._closeFullscreenDialog();
|
||||
}
|
||||
});
|
||||
|
||||
document.body.appendChild(this._fullscreenDialog);
|
||||
}
|
||||
|
||||
// Clone the editor and move it to dialog
|
||||
const editorClone = this.cloneNode(true) as HaCodeEditor;
|
||||
editorClone.classList.add("fullscreen-editor");
|
||||
|
||||
// Copy current value to cloned editor
|
||||
editorClone.value = this.value;
|
||||
|
||||
// Listen for changes in the cloned editor
|
||||
editorClone.addEventListener("value-changed", (e: any) => {
|
||||
this._value = e.detail.value;
|
||||
fireEvent(this, "value-changed", { value: this._value });
|
||||
});
|
||||
|
||||
// Add close button
|
||||
const closeButton = document.createElement("ha-icon-button");
|
||||
closeButton.path = mdiArrowCollapse;
|
||||
closeButton.setAttribute("label", "Exit fullscreen");
|
||||
closeButton.className = "fullscreen-close-button";
|
||||
closeButton.addEventListener("click", () => this._closeFullscreenDialog());
|
||||
|
||||
// Clear dialog content and add new content
|
||||
this._fullscreenDialog.innerHTML = "";
|
||||
this._fullscreenDialog.appendChild(editorClone);
|
||||
this._fullscreenDialog.appendChild(closeButton);
|
||||
|
||||
// Show modal dialog
|
||||
this._fullscreenDialog.showModal();
|
||||
}
|
||||
|
||||
private _closeFullscreenDialog() {
|
||||
if (this._fullscreenDialog) {
|
||||
this._fullscreenDialog.close();
|
||||
|
||||
// Update original editor with current value
|
||||
const editorInDialog = this._fullscreenDialog.querySelector(
|
||||
"ha-code-editor"
|
||||
) as HaCodeEditor;
|
||||
if (editorInDialog) {
|
||||
this._value = editorInDialog.value;
|
||||
if (this.codemirror) {
|
||||
this.codemirror.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: this.codemirror.state.doc.length,
|
||||
insert: this._value,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
this._isFullscreen = false;
|
||||
}
|
||||
|
||||
private _handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (this._isFullscreen && e.key === "Escape") {
|
||||
e.preventDefault();
|
||||
@@ -643,40 +725,6 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
}
|
||||
}
|
||||
|
||||
:host(.fullscreen) {
|
||||
position: fixed !important;
|
||||
top: calc(var(--header-height, 56px) + 8px) !important;
|
||||
left: 8px !important;
|
||||
right: 8px !important;
|
||||
bottom: 8px !important;
|
||||
z-index: 9999 !important;
|
||||
border-radius: 12px !important;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3) !important;
|
||||
overflow: hidden !important;
|
||||
background-color: var(
|
||||
--code-editor-background-color,
|
||||
var(--card-background-color)
|
||||
) !important;
|
||||
margin: 0 !important;
|
||||
padding-top: var(--safe-area-inset-top) !important;
|
||||
padding-left: var(--safe-area-inset-left) !important;
|
||||
padding-right: var(--safe-area-inset-right) !important;
|
||||
padding-bottom: var(--safe-area-inset-bottom) !important;
|
||||
box-sizing: border-box !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
:host(.fullscreen) .cm-editor {
|
||||
height: 100% !important;
|
||||
max-height: 100% !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
:host(.fullscreen) .fullscreen-button {
|
||||
top: calc(var(--safe-area-inset-top, 0px) + 8px);
|
||||
right: calc(var(--safe-area-inset-right, 0px) + 8px);
|
||||
}
|
||||
|
||||
.completion-info {
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
|
@@ -1,372 +0,0 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { generateEntityFilter } from "../../../common/entity/entity_filter";
|
||||
import { formatNumber } from "../../../common/number/format_number";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-ripple";
|
||||
import "../../../components/tile/ha-tile-icon";
|
||||
import "../../../components/tile/ha-tile-info";
|
||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||
import "../../../state-display/state-display";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
import { handleAction } from "../common/handle-action";
|
||||
import { hasAction } from "../common/has-action";
|
||||
import {
|
||||
findEntities,
|
||||
getSummaryLabel,
|
||||
HOME_SUMMARIES_FILTERS,
|
||||
HOME_SUMMARIES_ICONS,
|
||||
type HomeSummary,
|
||||
} from "../strategies/home/helpers/home-summaries";
|
||||
import type { LovelaceCard, LovelaceGridOptions } from "../types";
|
||||
import type { HomeSummaryCard } from "./types";
|
||||
|
||||
const COLORS: Record<HomeSummary, string> = {
|
||||
lights: "amber",
|
||||
climate: "deep-orange",
|
||||
security: "blue",
|
||||
media_players: "purple",
|
||||
};
|
||||
|
||||
@customElement("hui-home-summary-card")
|
||||
export class HuiHomeSummaryCard extends LitElement implements LovelaceCard {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: HomeSummaryCard;
|
||||
|
||||
public setConfig(config: HomeSummaryCard): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
public getCardSize(): number {
|
||||
return this._config?.vertical ? 2 : 1;
|
||||
}
|
||||
|
||||
public getGridOptions(): LovelaceGridOptions {
|
||||
const columns = 6;
|
||||
let min_columns = 6;
|
||||
let rows = 1;
|
||||
|
||||
if (this._config?.vertical) {
|
||||
rows++;
|
||||
min_columns = 3;
|
||||
}
|
||||
return {
|
||||
columns,
|
||||
rows,
|
||||
min_columns,
|
||||
min_rows: rows,
|
||||
};
|
||||
}
|
||||
|
||||
private _handleAction(ev: ActionHandlerEvent) {
|
||||
handleAction(this, this.hass!, this._config!, ev.detail.action!);
|
||||
}
|
||||
|
||||
private get _hasCardAction() {
|
||||
return (
|
||||
hasAction(this._config?.tap_action) ||
|
||||
hasAction(this._config?.hold_action) ||
|
||||
hasAction(this._config?.double_tap_action)
|
||||
);
|
||||
}
|
||||
|
||||
private _computeSummaryState(): string {
|
||||
if (!this._config || !this.hass) {
|
||||
return "";
|
||||
}
|
||||
const allEntities = Object.keys(this.hass!.states);
|
||||
|
||||
const areas = Object.values(this.hass.areas);
|
||||
const areasFilter = generateEntityFilter(this.hass, {
|
||||
area: areas.map((area) => area.area_id),
|
||||
});
|
||||
|
||||
const entitiesInsideArea = allEntities.filter(areasFilter);
|
||||
|
||||
switch (this._config.summary) {
|
||||
case "lights": {
|
||||
// Number of lights on
|
||||
const lightsFilters = HOME_SUMMARIES_FILTERS.lights.map((filter) =>
|
||||
generateEntityFilter(this.hass!, filter)
|
||||
);
|
||||
|
||||
const lightEntities = findEntities(entitiesInsideArea, lightsFilters);
|
||||
|
||||
const onLights = lightEntities.filter((entityId) => {
|
||||
const s = this.hass!.states[entityId]?.state;
|
||||
return s === "on";
|
||||
});
|
||||
|
||||
return onLights.length
|
||||
? this.hass.localize("ui.card.home-summary.count_lights_on", {
|
||||
count: onLights.length,
|
||||
})
|
||||
: this.hass.localize("ui.card.home-summary.all_lights_off");
|
||||
}
|
||||
case "climate": {
|
||||
// Min/Max temperature of the areas
|
||||
const areaSensors = areas
|
||||
.map((area) => area.temperature_entity_id)
|
||||
.filter(Boolean);
|
||||
|
||||
const sensorsValues = areaSensors
|
||||
.map(
|
||||
(entityId) => parseFloat(this.hass!.states[entityId!].state) || NaN
|
||||
)
|
||||
.filter((value) => !isNaN(value));
|
||||
|
||||
if (sensorsValues.length === 0) {
|
||||
return "";
|
||||
}
|
||||
const minTemp = Math.min(...sensorsValues);
|
||||
const maxTemp = Math.max(...sensorsValues);
|
||||
|
||||
if (isNaN(minTemp) || isNaN(maxTemp)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const formattedMinTemp = formatNumber(minTemp, this.hass?.locale, {
|
||||
minimumFractionDigits: 1,
|
||||
maximumFractionDigits: 1,
|
||||
});
|
||||
const formattedMaxTemp = formatNumber(maxTemp, this.hass?.locale, {
|
||||
minimumFractionDigits: 1,
|
||||
maximumFractionDigits: 1,
|
||||
});
|
||||
return formattedMinTemp === formattedMaxTemp
|
||||
? `${formattedMinTemp}°`
|
||||
: `${formattedMinTemp} - ${formattedMaxTemp}°`;
|
||||
}
|
||||
case "security": {
|
||||
// Alarm and lock status
|
||||
const securityFilters = HOME_SUMMARIES_FILTERS.security.map((filter) =>
|
||||
generateEntityFilter(this.hass!, filter)
|
||||
);
|
||||
|
||||
const securityEntities = findEntities(
|
||||
entitiesInsideArea,
|
||||
securityFilters
|
||||
);
|
||||
|
||||
const locks = securityEntities.filter((entityId) => {
|
||||
const domain = computeDomain(entityId);
|
||||
return domain === "lock";
|
||||
});
|
||||
|
||||
const alarms = securityEntities.filter((entityId) => {
|
||||
const domain = computeDomain(entityId);
|
||||
return domain === "alarm_control_panel";
|
||||
});
|
||||
|
||||
const disarmedAlarms = alarms.filter((entityId) => {
|
||||
const s = this.hass!.states[entityId]?.state;
|
||||
return s === "disarmed";
|
||||
});
|
||||
|
||||
if (!locks.length && !alarms.length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const unlockedLocks = locks.filter((entityId) => {
|
||||
const s = this.hass!.states[entityId]?.state;
|
||||
return s === "unlocked" || s === "jammed" || s === "open";
|
||||
});
|
||||
|
||||
if (unlockedLocks.length) {
|
||||
return this.hass.localize(
|
||||
"ui.card.home-summary.count_locks_unlocked",
|
||||
{
|
||||
count: unlockedLocks.length,
|
||||
}
|
||||
);
|
||||
}
|
||||
if (disarmedAlarms.length) {
|
||||
return this.hass.localize(
|
||||
"ui.card.home-summary.count_alarms_disarmed",
|
||||
{
|
||||
count: disarmedAlarms.length,
|
||||
}
|
||||
);
|
||||
}
|
||||
return "All secure";
|
||||
}
|
||||
case "media_players": {
|
||||
// Playing media
|
||||
const mediaPlayerFilters = HOME_SUMMARIES_FILTERS.media_players.map(
|
||||
(filter) => generateEntityFilter(this.hass!, filter)
|
||||
);
|
||||
|
||||
const mediaPlayerEntities = findEntities(
|
||||
entitiesInsideArea,
|
||||
mediaPlayerFilters
|
||||
);
|
||||
|
||||
const playingMedia = mediaPlayerEntities.filter((entityId) => {
|
||||
const s = this.hass!.states[entityId]?.state;
|
||||
return s === "playing";
|
||||
});
|
||||
|
||||
return playingMedia.length
|
||||
? this.hass.localize("ui.card.home-summary.count_media_playing", {
|
||||
count: playingMedia.length,
|
||||
})
|
||||
: this.hass.localize("ui.card.home-summary.no_media_playing");
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._config || !this.hass) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const contentClasses = { vertical: Boolean(this._config.vertical) };
|
||||
|
||||
const color = computeCssColor(COLORS[this._config.summary]);
|
||||
|
||||
const style = {
|
||||
"--tile-color": color,
|
||||
};
|
||||
|
||||
const secondary = this._computeSummaryState();
|
||||
|
||||
const label = getSummaryLabel(this.hass.localize, this._config.summary);
|
||||
const icon = HOME_SUMMARIES_ICONS[this._config.summary];
|
||||
|
||||
return html`
|
||||
<ha-card style=${styleMap(style)}>
|
||||
<div
|
||||
class="background"
|
||||
@action=${this._handleAction}
|
||||
.actionHandler=${actionHandler({
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
role=${ifDefined(this._hasCardAction ? "button" : undefined)}
|
||||
tabindex=${ifDefined(this._hasCardAction ? "0" : undefined)}
|
||||
aria-labelledby="info"
|
||||
>
|
||||
<ha-ripple .disabled=${!this._hasCardAction}></ha-ripple>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="content ${classMap(contentClasses)}">
|
||||
<ha-tile-icon>
|
||||
<ha-icon slot="icon" .icon=${icon}></ha-icon>
|
||||
</ha-tile-icon>
|
||||
<ha-tile-info
|
||||
id="info"
|
||||
.primary=${label}
|
||||
.secondary=${secondary}
|
||||
></ha-tile-info>
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
--tile-color: var(--state-inactive-color);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
ha-card:has(.background:focus-visible) {
|
||||
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
|
||||
--shadow-focus: 0 0 0 1px var(--tile-color);
|
||||
border-color: var(--tile-color);
|
||||
box-shadow: var(--shadow-default), var(--shadow-focus);
|
||||
}
|
||||
ha-card {
|
||||
--ha-ripple-color: var(--tile-color);
|
||||
--ha-ripple-hover-opacity: 0.04;
|
||||
--ha-ripple-pressed-opacity: 0.12;
|
||||
height: 100%;
|
||||
transition:
|
||||
box-shadow 180ms ease-in-out,
|
||||
border-color 180ms ease-in-out;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
ha-card.active {
|
||||
--tile-color: var(--state-icon-color);
|
||||
}
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
[role="button"]:focus {
|
||||
outline: none;
|
||||
}
|
||||
.background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-radius: var(--ha-card-border-radius, 12px);
|
||||
margin: calc(-1 * var(--ha-card-border-width, 1px));
|
||||
overflow: hidden;
|
||||
}
|
||||
.container {
|
||||
margin: calc(-1 * var(--ha-card-border-width, 1px));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
.container.horizontal {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
pointer-events: none;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.vertical ha-tile-info {
|
||||
width: 100%;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
ha-tile-icon {
|
||||
--tile-icon-color: var(--tile-color);
|
||||
position: relative;
|
||||
padding: 6px;
|
||||
margin: -6px;
|
||||
}
|
||||
|
||||
ha-tile-info {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
transition: background-color 180ms ease-in-out;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-home-summary-card": HuiHomeSummaryCard;
|
||||
}
|
||||
}
|
@@ -26,7 +26,6 @@ import type {
|
||||
import type { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
||||
import type { LovelaceHeadingBadgeConfig } from "../heading-badges/types";
|
||||
import type { TimeFormat } from "../../../data/translation";
|
||||
import type { HomeSummary } from "../strategies/home/helpers/home-summaries";
|
||||
|
||||
export type AlarmPanelCardConfigState =
|
||||
| "arm_away"
|
||||
@@ -589,11 +588,3 @@ export interface HeadingCardConfig extends LovelaceCardConfig {
|
||||
/** @deprecated Use `badges` instead */
|
||||
entities?: LovelaceHeadingBadgeConfig[];
|
||||
}
|
||||
|
||||
export interface HomeSummaryCard extends LovelaceCardConfig {
|
||||
summary: HomeSummary;
|
||||
vertical?: boolean;
|
||||
tap_action?: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
double_tap_action?: ActionConfig;
|
||||
}
|
||||
|
@@ -68,7 +68,6 @@ const LAZY_LOAD_TYPES = {
|
||||
"energy-sankey": () => import("../cards/energy/hui-energy-sankey-card"),
|
||||
"entity-filter": () => import("../cards/hui-entity-filter-card"),
|
||||
error: () => import("../cards/hui-error-card"),
|
||||
"home-summary": () => import("../cards/hui-home-summary-card"),
|
||||
gauge: () => import("../cards/hui-gauge-card"),
|
||||
"history-graph": () => import("../cards/hui-history-graph-card"),
|
||||
"horizontal-stack": () => import("../cards/hui-horizontal-stack-card"),
|
||||
|
@@ -9,12 +9,16 @@ import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type {
|
||||
AreaCardConfig,
|
||||
HomeSummaryCard,
|
||||
ButtonCardConfig,
|
||||
MarkdownCardConfig,
|
||||
TileCardConfig,
|
||||
WeatherForecastCardConfig,
|
||||
} from "../../cards/types";
|
||||
import { getAreas } from "../areas/helpers/areas-strategy-helper";
|
||||
import {
|
||||
getSummaryLabel,
|
||||
HOME_SUMMARIES_ICONS,
|
||||
} from "./helpers/home-summaries";
|
||||
|
||||
export interface HomeMainViewStrategyConfig {
|
||||
type: "home-main";
|
||||
@@ -110,57 +114,61 @@ export class HomeMainViewStrategy extends ReactiveElement {
|
||||
heading: hass.localize("ui.panel.lovelace.strategy.home.summaries"),
|
||||
},
|
||||
{
|
||||
type: "home-summary",
|
||||
summary: "lights",
|
||||
vertical: true,
|
||||
type: "button",
|
||||
icon: HOME_SUMMARIES_ICONS.lights,
|
||||
name: getSummaryLabel(hass.localize, "lights"),
|
||||
icon_height: "24px",
|
||||
grid_options: {
|
||||
rows: 2,
|
||||
columns: 4,
|
||||
},
|
||||
tap_action: {
|
||||
action: "navigate",
|
||||
navigation_path: "lights",
|
||||
},
|
||||
} satisfies ButtonCardConfig,
|
||||
{
|
||||
type: "button",
|
||||
icon: HOME_SUMMARIES_ICONS.climate,
|
||||
name: getSummaryLabel(hass.localize, "climate"),
|
||||
icon_height: "30px",
|
||||
grid_options: {
|
||||
rows: 2,
|
||||
columns: 4,
|
||||
},
|
||||
} satisfies HomeSummaryCard,
|
||||
{
|
||||
type: "home-summary",
|
||||
summary: "climate",
|
||||
vertical: true,
|
||||
tap_action: {
|
||||
action: "navigate",
|
||||
navigation_path: "climate",
|
||||
},
|
||||
} satisfies ButtonCardConfig,
|
||||
{
|
||||
type: "button",
|
||||
icon: HOME_SUMMARIES_ICONS.security,
|
||||
name: getSummaryLabel(hass.localize, "security"),
|
||||
icon_height: "30px",
|
||||
grid_options: {
|
||||
rows: 2,
|
||||
columns: 4,
|
||||
},
|
||||
} satisfies HomeSummaryCard,
|
||||
{
|
||||
type: "home-summary",
|
||||
summary: "security",
|
||||
vertical: true,
|
||||
tap_action: {
|
||||
action: "navigate",
|
||||
navigation_path: "security",
|
||||
},
|
||||
} satisfies ButtonCardConfig,
|
||||
{
|
||||
type: "button",
|
||||
icon: HOME_SUMMARIES_ICONS.media_players,
|
||||
name: getSummaryLabel(hass.localize, "media_players"),
|
||||
icon_height: "30px",
|
||||
grid_options: {
|
||||
rows: 2,
|
||||
columns: 4,
|
||||
},
|
||||
} satisfies HomeSummaryCard,
|
||||
{
|
||||
type: "home-summary",
|
||||
summary: "media_players",
|
||||
vertical: true,
|
||||
tap_action: {
|
||||
action: "navigate",
|
||||
navigation_path: "media-players",
|
||||
},
|
||||
grid_options: {
|
||||
rows: 2,
|
||||
columns: 4,
|
||||
},
|
||||
} satisfies HomeSummaryCard,
|
||||
} satisfies ButtonCardConfig,
|
||||
],
|
||||
};
|
||||
|
||||
|
@@ -201,15 +201,6 @@
|
||||
"open_door_confirm": "Really open?",
|
||||
"open_door_done": "Done"
|
||||
},
|
||||
"home-summary": {
|
||||
"all_lights_off": "All off",
|
||||
"count_lights_on": "{count} {count, plural,\n one {on}\n other {on}\n}",
|
||||
"count_locks_unlocked": "{count} {count, plural,\n one {unlocked}\n other {unlocked}\n}",
|
||||
"count_alarms_disarmed": "{count} {count, plural,\n one {disarmed}\n other {disarmed}\n}",
|
||||
"all_secure": "All secure",
|
||||
"no_media_playing": "No media playing",
|
||||
"count_media_playing": "{count} {count, plural,\n one {playing}\n other {playing}\n}"
|
||||
},
|
||||
"media_player": {
|
||||
"source": "Source",
|
||||
"sound_mode": "Sound mode",
|
||||
|
Reference in New Issue
Block a user