mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-19 06:09:27 +00:00
Compare commits
43 Commits
graph-feat
...
20250811.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
64e00e559f | ||
![]() |
b407bd4c4f | ||
![]() |
d466abf9c4 | ||
![]() |
4d98230145 | ||
![]() |
8a5bca0eb0 | ||
![]() |
1638da858c | ||
![]() |
bfb11102cc | ||
![]() |
a3d3539e82 | ||
![]() |
1fc6cff857 | ||
![]() |
c5d7eb5384 | ||
![]() |
759e6eba35 | ||
![]() |
153129e066 | ||
![]() |
7bf3c7273e | ||
![]() |
08765e6ce2 | ||
![]() |
550e4cd4aa | ||
![]() |
f6041c5cbb | ||
![]() |
42f65c2ca1 | ||
![]() |
588f171f7f | ||
![]() |
bd1445840d | ||
![]() |
97a0903cec | ||
![]() |
e2525a3d07 | ||
![]() |
8bc0f5a42c | ||
![]() |
bf7e8ffd24 | ||
![]() |
255e598c65 | ||
![]() |
5e22178225 | ||
![]() |
56b7a6abec | ||
![]() |
f34c4a11af | ||
![]() |
102689b711 | ||
![]() |
b16d769192 | ||
![]() |
29bcacc64e | ||
![]() |
c7f3331373 | ||
![]() |
c32444b70c | ||
![]() |
11c6b90eb0 | ||
![]() |
f7a17598f0 | ||
![]() |
56967bc0c1 | ||
![]() |
b01ab9234b | ||
![]() |
ad39228dea | ||
![]() |
8cc48cdecb | ||
![]() |
524e89acf0 | ||
![]() |
48f6b34882 | ||
![]() |
44d9185574 | ||
![]() |
51ff6c6564 | ||
![]() |
b49b8e3db8 |
6
.github/copilot-instructions.md
vendored
6
.github/copilot-instructions.md
vendored
@@ -310,7 +310,11 @@ export class DialogMyFeature
|
||||
.heading=${createCloseHeading(this.hass, this._params.title)}
|
||||
>
|
||||
<!-- Dialog content -->
|
||||
<ha-button @click=${this.closeDialog} slot="secondaryAction">
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this.closeDialog}
|
||||
slot="secondaryAction"
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button @click=${this._submit} slot="primaryAction">
|
||||
|
@@ -89,11 +89,14 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
)}
|
||||
</div>
|
||||
<div class="actions small-hidden">
|
||||
<a href="https://www.home-assistant.io" target="_blank">
|
||||
<ha-button>
|
||||
${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")}
|
||||
</ha-button>
|
||||
</a>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
href="https://www.home-assistant.io"
|
||||
target="_blank"
|
||||
>
|
||||
${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
|
@@ -147,13 +147,13 @@ The `title ` option should not be used without a description.
|
||||
|
||||
<ha-alert alert-type="success">
|
||||
This is a success alert — check it out!
|
||||
<ha-button slot="action" label="Undo"></ha-button>
|
||||
<ha-button slot="action">Undo</ha-button>
|
||||
</ha-alert>
|
||||
|
||||
```html
|
||||
<ha-alert alert-type="success">
|
||||
This is a success alert — check it out!
|
||||
<ha-button slot="action" label="Undo"></ha-button>
|
||||
<ha-button slot="action">Undo</ha-button>
|
||||
</ha-alert>
|
||||
```
|
||||
|
||||
|
@@ -78,21 +78,13 @@ const alerts: {
|
||||
title: "Error with action",
|
||||
description: "This is a test error alert with action",
|
||||
type: "error",
|
||||
actionSlot: html`<ha-button
|
||||
size="small"
|
||||
slot="action"
|
||||
label="restart"
|
||||
></ha-button>`,
|
||||
actionSlot: html`<ha-button size="small" slot="action">restart</ha-button>`,
|
||||
},
|
||||
{
|
||||
title: "Unsaved data",
|
||||
description: "You have unsaved data",
|
||||
type: "warning",
|
||||
actionSlot: html`<ha-button
|
||||
size="small"
|
||||
slot="action"
|
||||
label="save"
|
||||
></ha-button>`,
|
||||
actionSlot: html`<ha-button size="small" slot="action">save</ha-button>`,
|
||||
},
|
||||
{
|
||||
title: "Slotted icon",
|
||||
@@ -116,7 +108,7 @@ const alerts: {
|
||||
title: "Slotted action",
|
||||
description: "Alert with slotted action",
|
||||
type: "info",
|
||||
actionSlot: html`<ha-button slot="action" label="action"></ha-button>`,
|
||||
actionSlot: html`<ha-button slot="action">action</ha-button>`,
|
||||
},
|
||||
{
|
||||
description: "Dismissable information (RTL)",
|
||||
@@ -128,7 +120,7 @@ const alerts: {
|
||||
title: "Error with action",
|
||||
description: "This is a test error alert with action (RTL)",
|
||||
type: "error",
|
||||
actionSlot: html`<ha-button slot="action" label="restart"></ha-button>`,
|
||||
actionSlot: html`<ha-button slot="action">restart</ha-button>`,
|
||||
rtl: true,
|
||||
},
|
||||
{
|
||||
|
@@ -53,14 +53,15 @@ Check the [webawesome documentation](https://webawesome.com/docs/components/butt
|
||||
|
||||
**Properties/Attributes**
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| ----------- | ---------------------------------------------- | -------- | -------------------------------------------------- |
|
||||
| appearance | "accent"/"filled"/"plain" | "accent" | Sets the button appearance. |
|
||||
| variants | "brand"/"danger"/"neutral"/"warning"/"success" | "brand" | Sets the button color variant. "brand" is default. |
|
||||
| size | "small"/"medium" | "medium" | Sets the button size. |
|
||||
| hideContent | Boolean | false | Hides the button content (for overlays) |
|
||||
| Name | Type | Default | Description |
|
||||
| ---------- | ---------------------------------------------- | -------- | --------------------------------------------------------------------------------- |
|
||||
| appearance | "accent"/"filled"/"plain" | "accent" | Sets the button appearance. |
|
||||
| variants | "brand"/"danger"/"neutral"/"warning"/"success" | "brand" | Sets the button color variant. "brand" is default. |
|
||||
| size | "small"/"medium" | "medium" | Sets the button size. |
|
||||
| loading | Boolean | false | Shows a loading indicator instead of the buttons label and disable buttons click. |
|
||||
| disabled | Boolean | false | Disables the button and prevents user interaction. |
|
||||
|
||||
**CSS Custom Properties**
|
||||
|
||||
- `--ha-button-height` - Height of the button.
|
||||
- `--ha-button-radius` - Border radius of the button. Defaults to `var(--wa-border-radius-pill)`.
|
||||
- `--ha-button-border-radius` - Border radius of the button. Defaults to `var(--ha-border-radius-pill)`.
|
||||
|
@@ -21,8 +21,8 @@ import type { HomeAssistant } from "../../../../src/types";
|
||||
import type { HassioDatatiskDialogParams } from "./show-dialog-hassio-datadisk";
|
||||
|
||||
const calculateMoveTime = memoizeOne((supervisor: Supervisor): number => {
|
||||
const speed = supervisor.host.disk_life_time !== "" ? 30 : 10;
|
||||
const moveTime = (supervisor.host.disk_used * 1000) / 60 / speed;
|
||||
// Assume a speed of 30 MB/s.
|
||||
const moveTime = (supervisor.host.disk_used * 1000) / 60 / 30;
|
||||
const rebootTime = (supervisor.host.startup_time * 4) / 60;
|
||||
return Math.ceil((moveTime + rebootTime) / 10) * 10;
|
||||
});
|
||||
|
@@ -604,8 +604,8 @@ export class DialogHassioNetwork
|
||||
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px;
|
||||
padding-bottom: max(var(--safe-area-inset-bottom), 8px);
|
||||
padding: 16px;
|
||||
padding-bottom: max(var(--safe-area-inset-bottom), 16px);
|
||||
background-color: var(--mdc-theme-surface, #fff);
|
||||
}
|
||||
.warning {
|
||||
|
@@ -2,13 +2,13 @@ import { mdiDelete, mdiPlus } from "@mdi/js";
|
||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../src/components/ha-button";
|
||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-form/ha-form";
|
||||
import type { SchemaUnion } from "../../../../src/components/ha-form/types";
|
||||
import "../../../../src/components/ha-icon-button";
|
||||
import "../../../../src/components/ha-button";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||
import {
|
||||
addHassioDockerRegistry,
|
||||
|
@@ -7,10 +7,14 @@ import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { caseInsensitiveStringCompare } from "../../../../src/common/string/compare";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-button";
|
||||
import "../../../../src/components/ha-tooltip";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-icon-button";
|
||||
import "../../../../src/components/ha-md-list";
|
||||
import "../../../../src/components/ha-md-list-item";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import "../../../../src/components/ha-textfield";
|
||||
import type { HaTextField } from "../../../../src/components/ha-textfield";
|
||||
import "../../../../src/components/ha-tooltip";
|
||||
import type {
|
||||
HassioAddonInfo,
|
||||
HassioAddonRepository,
|
||||
@@ -24,10 +28,6 @@ import {
|
||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import type { HassioRepositoryDialogParams } from "./show-dialog-repositories";
|
||||
import type { HaTextField } from "../../../../src/components/ha-textfield";
|
||||
import "../../../../src/components/ha-textfield";
|
||||
import "../../../../src/components/ha-md-list";
|
||||
import "../../../../src/components/ha-md-list-item";
|
||||
|
||||
@customElement("dialog-hassio-repositories")
|
||||
class HassioRepositoriesDialog extends LitElement {
|
||||
|
@@ -143,16 +143,12 @@ class HassioHostInfo extends LitElement {
|
||||
: ""}
|
||||
</div>
|
||||
<div>
|
||||
${this.supervisor.host.disk_life_time !== "" &&
|
||||
this.supervisor.host.disk_life_time >= 10
|
||||
${this.supervisor.host.disk_life_time !== null
|
||||
? html` <ha-settings-row>
|
||||
<span slot="heading">
|
||||
${this.supervisor.localize(
|
||||
"system.host.emmc_lifetime_used"
|
||||
)}
|
||||
${this.supervisor.localize("system.host.lifetime_used")}
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this.supervisor.host.disk_life_time - 10} % -
|
||||
${this.supervisor.host.disk_life_time} %
|
||||
</span>
|
||||
</ha-settings-row>`
|
||||
|
@@ -208,14 +208,16 @@ class UpdateAvailableCard extends LitElement {
|
||||
<div class="card-actions">
|
||||
${changelog
|
||||
? html`
|
||||
<a href=${changelog} target="_blank" rel="noreferrer">
|
||||
<ha-button
|
||||
.label=${this.supervisor.localize(
|
||||
"update_available.open_release_notes"
|
||||
)}
|
||||
>
|
||||
</ha-button>
|
||||
</a>
|
||||
<ha-button
|
||||
href=${changelog}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
appearance="plain"
|
||||
>
|
||||
${this.supervisor.localize(
|
||||
"update_available.open_release_notes"
|
||||
)}
|
||||
</ha-button>
|
||||
`
|
||||
: nothing}
|
||||
<span></span>
|
||||
|
@@ -3,26 +3,26 @@ import { mdiArrowCollapseDown, mdiDownload } from "@mdi/js";
|
||||
// eslint-disable-next-line import/extensions
|
||||
import { IntersectionController } from "@lit-labs/observers/intersection-controller.js";
|
||||
import { LitElement, type PropertyValues, css, html, nothing } from "lit";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import type {
|
||||
LandingPageKeys,
|
||||
LocalizeFunc,
|
||||
} from "../../../src/common/translations/localize";
|
||||
import { waitForSeconds } from "../../../src/common/util/wait";
|
||||
import "../../../src/components/ha-alert";
|
||||
import "../../../src/components/ha-ansi-to-html";
|
||||
import type { HaAnsiToHtml } from "../../../src/components/ha-ansi-to-html";
|
||||
import "../../../src/components/ha-button";
|
||||
import "../../../src/components/ha-icon-button";
|
||||
import "../../../src/components/ha-svg-icon";
|
||||
import "../../../src/components/ha-ansi-to-html";
|
||||
import "../../../src/components/ha-alert";
|
||||
import type { HaAnsiToHtml } from "../../../src/components/ha-ansi-to-html";
|
||||
import { fileDownload } from "../../../src/util/file_download";
|
||||
import {
|
||||
getObserverLogs,
|
||||
downloadUrl as observerLogsDownloadUrl,
|
||||
} from "../data/observer";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import { fileDownload } from "../../../src/util/file_download";
|
||||
import { getSupervisorLogs, getSupervisorLogsFollow } from "../data/supervisor";
|
||||
import { waitForSeconds } from "../../../src/common/util/wait";
|
||||
import { ASSUME_CORE_START_SECONDS } from "../ha-landing-page";
|
||||
|
||||
const ERROR_CHECK = /^[\d\s-:]+(ERROR|CRITICAL)(.*)/gm;
|
||||
@@ -108,14 +108,13 @@ class LandingPageLogs extends LitElement {
|
||||
!this._scrolledToBottomController.value) ||
|
||||
false,
|
||||
})}"
|
||||
size="small"
|
||||
appearance="filled"
|
||||
@click=${this._scrollToBottom}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiArrowCollapseDown} slot="icon"></ha-svg-icon>
|
||||
<ha-svg-icon .path=${mdiArrowCollapseDown} slot="start"></ha-svg-icon>
|
||||
${this.localize("logs.scroll_down_button")}
|
||||
<ha-svg-icon
|
||||
.path=${mdiArrowCollapseDown}
|
||||
slot="trailingIcon"
|
||||
></ha-svg-icon>
|
||||
<ha-svg-icon .path=${mdiArrowCollapseDown} slot="end"></ha-svg-icon>
|
||||
</ha-button>
|
||||
`;
|
||||
}
|
||||
@@ -312,21 +311,14 @@ class LandingPageLogs extends LitElement {
|
||||
}
|
||||
|
||||
.new-logs-indicator {
|
||||
--mdc-theme-primary: var(--text-primary-color);
|
||||
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 4px;
|
||||
left: 4px;
|
||||
height: 0;
|
||||
background-color: var(--primary-color);
|
||||
border-radius: 8px;
|
||||
|
||||
transition: height 0.4s ease-out;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.new-logs-indicator.visible {
|
||||
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20250730.0"
|
||||
version = "20250811.0"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*"]
|
||||
description = "The Home Assistant frontend"
|
||||
|
@@ -22,8 +22,8 @@ export type LocalizeKeys =
|
||||
| `ui.dialogs.more_info_control.lawn_mower.${string}`
|
||||
| `ui.dialogs.more_info_control.vacuum.${string}`
|
||||
| `ui.dialogs.quick-bar.commands.${string}`
|
||||
| `ui.dialogs.unhealthy.reason.${string}`
|
||||
| `ui.dialogs.unsupported.reason.${string}`
|
||||
| `ui.dialogs.unhealthy.reasons.${string}`
|
||||
| `ui.dialogs.unsupported.reasons.${string}`
|
||||
| `ui.panel.config.${string}.${"caption" | "description"}`
|
||||
| `ui.panel.config.dashboard.${string}`
|
||||
| `ui.panel.config.zha.${string}`
|
||||
|
@@ -117,7 +117,7 @@ export class HaProgressButton extends LitElement {
|
||||
}
|
||||
|
||||
ha-svg-icon {
|
||||
color: var(--white);
|
||||
color: var(--white-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@@ -29,7 +29,6 @@ import { formatTimeLabel } from "./axis-label";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import "../chips/ha-assist-chip";
|
||||
import { downSampleLineData } from "./down-sample";
|
||||
import { colorVariables } from "../../resources/theme/color/color.globals";
|
||||
|
||||
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
|
||||
const LEGEND_OVERFLOW_LIMIT = 10;
|
||||
@@ -168,14 +167,16 @@ export class HaChartBase extends LitElement {
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
this._setupChart();
|
||||
if (this.isConnected) {
|
||||
this._setupChart();
|
||||
}
|
||||
}
|
||||
|
||||
public willUpdate(changedProps: PropertyValues): void {
|
||||
if (!this.chart) {
|
||||
return;
|
||||
}
|
||||
if (changedProps.has("_themes")) {
|
||||
if (changedProps.has("_themes") && this.hasUpdated) {
|
||||
this._setupChart();
|
||||
return;
|
||||
}
|
||||
@@ -342,7 +343,8 @@ export class HaChartBase extends LitElement {
|
||||
echarts.use(this.extraComponents);
|
||||
}
|
||||
|
||||
echarts.registerTheme("custom", this._createTheme());
|
||||
const style = getComputedStyle(this);
|
||||
echarts.registerTheme("custom", this._createTheme(style));
|
||||
|
||||
this.chart = echarts.init(container, "custom");
|
||||
this.chart.on("datazoom", (e: any) => {
|
||||
@@ -385,24 +387,25 @@ export class HaChartBase extends LitElement {
|
||||
lastTipX = e.x;
|
||||
lastTipY = e.y;
|
||||
this.chart?.setOption({
|
||||
xAxis: ensureArray(this.chart?.getOption().xAxis as any).map(
|
||||
(axis: XAXisOption) =>
|
||||
axis.show
|
||||
? {
|
||||
...axis,
|
||||
axisPointer: {
|
||||
...axis.axisPointer,
|
||||
status: "show",
|
||||
handle: {
|
||||
color: colorVariables["primary-color"],
|
||||
margin: 0,
|
||||
size: 20,
|
||||
...axis.axisPointer?.handle,
|
||||
show: true,
|
||||
},
|
||||
xAxis: ensureArray(
|
||||
(this.chart?.getOption().xAxis as any) ?? []
|
||||
).map((axis: XAXisOption) =>
|
||||
axis.show
|
||||
? {
|
||||
...axis,
|
||||
axisPointer: {
|
||||
...axis.axisPointer,
|
||||
status: "show",
|
||||
handle: {
|
||||
color: style.getPropertyValue("primary-color"),
|
||||
margin: 0,
|
||||
size: 20,
|
||||
...axis.axisPointer?.handle,
|
||||
show: true,
|
||||
},
|
||||
}
|
||||
: axis
|
||||
},
|
||||
}
|
||||
: axis
|
||||
),
|
||||
});
|
||||
});
|
||||
@@ -415,21 +418,22 @@ export class HaChartBase extends LitElement {
|
||||
return;
|
||||
}
|
||||
this.chart?.setOption({
|
||||
xAxis: ensureArray(this.chart?.getOption().xAxis as any).map(
|
||||
(axis: XAXisOption) =>
|
||||
axis.show
|
||||
? {
|
||||
...axis,
|
||||
axisPointer: {
|
||||
...axis.axisPointer,
|
||||
handle: {
|
||||
...axis.axisPointer?.handle,
|
||||
show: false,
|
||||
},
|
||||
status: "hide",
|
||||
xAxis: ensureArray(
|
||||
(this.chart?.getOption().xAxis as any) ?? []
|
||||
).map((axis: XAXisOption) =>
|
||||
axis.show
|
||||
? {
|
||||
...axis,
|
||||
axisPointer: {
|
||||
...axis.axisPointer,
|
||||
handle: {
|
||||
...axis.axisPointer?.handle,
|
||||
show: false,
|
||||
},
|
||||
}
|
||||
: axis
|
||||
status: "hide",
|
||||
},
|
||||
}
|
||||
: axis
|
||||
),
|
||||
});
|
||||
this.chart?.dispatchAction({
|
||||
@@ -568,8 +572,7 @@ export class HaChartBase extends LitElement {
|
||||
return options;
|
||||
}
|
||||
|
||||
private _createTheme() {
|
||||
const style = getComputedStyle(this);
|
||||
private _createTheme(style: CSSStyleDeclaration) {
|
||||
return {
|
||||
color: getAllGraphColors(style),
|
||||
backgroundColor: "transparent",
|
||||
@@ -597,6 +600,13 @@ export class HaChartBase extends LitElement {
|
||||
textBorderWidth: 2,
|
||||
},
|
||||
},
|
||||
sankey: {
|
||||
label: {
|
||||
color: style.getPropertyValue("--primary-text-color"),
|
||||
textBorderColor: style.getPropertyValue("--primary-background-color"),
|
||||
textBorderWidth: 2,
|
||||
},
|
||||
},
|
||||
categoryAxis: {
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
@@ -802,6 +812,7 @@ export class HaChartBase extends LitElement {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const replaceMerge = options.series ? ["series"] : [];
|
||||
this.chart.setOption(options, { replaceMerge });
|
||||
}
|
||||
|
@@ -105,12 +105,14 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!GraphChart) {
|
||||
if (!GraphChart || !this.data.nodes?.length) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const isMobile = window.matchMedia(
|
||||
"all and (max-width: 450px), all and (max-height: 500px)"
|
||||
).matches;
|
||||
|
||||
return html`<ha-chart-base
|
||||
.hass=${this.hass}
|
||||
.data=${this._getSeries(
|
||||
@@ -243,6 +245,7 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
) {
|
||||
const containerWidth = this.clientWidth;
|
||||
const containerHeight = this.clientHeight;
|
||||
|
||||
const positionedNodes: NetworkNode[] = nodes.map((node) => ({ ...node }));
|
||||
positionedNodes.forEach((node) => {
|
||||
if (nodePositions[node.id]) {
|
||||
|
@@ -186,23 +186,22 @@ export class HaSankeyChart extends LitElement {
|
||||
""
|
||||
);
|
||||
const wordWidth = measureTextWidth(longestWord, FONT_SIZE);
|
||||
const availableWidth = params.rect.width + 6;
|
||||
const fontSize = Math.min(
|
||||
FONT_SIZE,
|
||||
(params.rect.width / wordWidth) * FONT_SIZE
|
||||
(availableWidth / wordWidth) * FONT_SIZE
|
||||
);
|
||||
return {
|
||||
fontSize: fontSize > 1 ? fontSize : 0,
|
||||
width: params.rect.width,
|
||||
width: availableWidth,
|
||||
align: "center",
|
||||
dy: -2, // shift up or the lowest row labels may be cut off
|
||||
};
|
||||
}
|
||||
|
||||
// estimate the number of lines after the label is wrapped
|
||||
// this is a very rough estimate, but it works for now
|
||||
const lineCount = Math.ceil(params.labelRect.width / labelSpace);
|
||||
// `overflow: "break"` allows the label to overflow outside its height, so we need to account for that
|
||||
const availableHeight = params.rect.height + 8; // account for the margin
|
||||
const fontSize = Math.min(
|
||||
(params.rect.height / lineCount) * FONT_SIZE,
|
||||
(availableHeight / params.labelRect.height) * FONT_SIZE,
|
||||
FONT_SIZE
|
||||
);
|
||||
return {
|
||||
|
@@ -27,12 +27,13 @@ export type Appearance = "accent" | "filled" | "outlined" | "plain";
|
||||
* @csspart spinner - The spinner that shows when the button is in the loading state.
|
||||
*
|
||||
* @cssprop --ha-button-height - The height of the button.
|
||||
* @cssprop --ha-button-radius - The border radius of the button. defaults to `var(--wa-border-radius-pill)`.
|
||||
* @cssprop --ha-button-border-radius - The border radius of the button. defaults to `var(--ha-border-radius-pill)`.
|
||||
*
|
||||
* @attr {("small"|"medium")} size - Sets the button size.
|
||||
* @attr {("brand"|"neutral"|"danger"|"warning"|"success")} variant - Sets the button color variant. "primary" is default.
|
||||
* @attr {("accent"|"filled"|"plain")} appearance - Sets the button appearance.
|
||||
* @attr {boolean} hideContent - Hides the button content (for overlays).
|
||||
* @attr {boolean} loading - shows a loading indicator instead of the buttons label and disable buttons click.
|
||||
* @attr {boolean} disabled - Disables the button and prevents user interaction.
|
||||
*/
|
||||
@customElement("ha-button")
|
||||
export class HaButton extends Button {
|
||||
@@ -54,10 +55,9 @@ export class HaButton extends Button {
|
||||
/* set theme vars */
|
||||
--wa-form-control-padding-inline: 16px;
|
||||
--wa-font-weight-action: var(--ha-font-weight-medium);
|
||||
--wa-border-radius-pill: 9999px;
|
||||
--wa-form-control-border-radius: var(
|
||||
--ha-button-radius,
|
||||
var(--wa-border-radius-pill)
|
||||
--ha-button-border-radius,
|
||||
var(--ha-border-radius-pill)
|
||||
);
|
||||
|
||||
--wa-form-control-height: var(
|
||||
@@ -66,6 +66,7 @@ export class HaButton extends Button {
|
||||
);
|
||||
|
||||
font-size: var(--ha-font-size-m);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
:host([size="small"]) .button {
|
||||
@@ -77,61 +78,102 @@ export class HaButton extends Button {
|
||||
}
|
||||
|
||||
:host([variant="brand"]) {
|
||||
--color-fill-normal-active: var(--color-fill-primary-normal-active);
|
||||
--color-fill-normal-hover: var(--color-fill-primary-normal-hover);
|
||||
--color-fill-loud-active: var(--color-fill-primary-loud-active);
|
||||
--color-fill-loud-hover: var(--color-fill-primary-loud-hover);
|
||||
--button-color-fill-normal-active: var(
|
||||
--ha-color-fill-primary-normal-active
|
||||
);
|
||||
--button-color-fill-normal-hover: var(
|
||||
--ha-color-fill-primary-normal-hover
|
||||
);
|
||||
--button-color-fill-loud-active: var(
|
||||
--ha-color-fill-primary-loud-active
|
||||
);
|
||||
--button-color-fill-loud-hover: var(
|
||||
--ha-color-fill-primary-loud-hover
|
||||
);
|
||||
}
|
||||
|
||||
:host([variant="neutral"]) {
|
||||
--color-fill-normal-active: var(--color-fill-neutral-normal-active);
|
||||
--color-fill-normal-hover: var(--color-fill-neutral-normal-hover);
|
||||
--color-fill-loud-active: var(--color-fill-neutral-loud-active);
|
||||
--color-fill-loud-hover: var(--color-fill-neutral-loud-hover);
|
||||
--button-color-fill-normal-active: var(
|
||||
--ha-color-fill-neutral-normal-active
|
||||
);
|
||||
--button-color-fill-normal-hover: var(
|
||||
--ha-color-fill-neutral-normal-hover
|
||||
);
|
||||
--button-color-fill-loud-active: var(
|
||||
--ha-color-fill-neutral-loud-active
|
||||
);
|
||||
--button-color-fill-loud-hover: var(
|
||||
--ha-color-fill-neutral-loud-hover
|
||||
);
|
||||
}
|
||||
|
||||
:host([variant="success"]) {
|
||||
--color-fill-normal-active: var(--color-fill-success-normal-active);
|
||||
--color-fill-normal-hover: var(--color-fill-success-normal-hover);
|
||||
--color-fill-loud-active: var(--color-fill-success-loud-active);
|
||||
--color-fill-loud-hover: var(--color-fill-success-loud-hover);
|
||||
--button-color-fill-normal-active: var(
|
||||
--ha-color-fill-success-normal-active
|
||||
);
|
||||
--button-color-fill-normal-hover: var(
|
||||
--ha-color-fill-success-normal-hover
|
||||
);
|
||||
--button-color-fill-loud-active: var(
|
||||
--ha-color-fill-success-loud-active
|
||||
);
|
||||
--button-color-fill-loud-hover: var(
|
||||
--ha-color-fill-success-loud-hover
|
||||
);
|
||||
}
|
||||
|
||||
:host([variant="warning"]) {
|
||||
--color-fill-normal-active: var(--color-fill-warning-normal-active);
|
||||
--color-fill-normal-hover: var(--color-fill-warning-normal-hover);
|
||||
--color-fill-loud-active: var(--color-fill-warning-loud-active);
|
||||
--color-fill-loud-hover: var(--color-fill-warning-loud-hover);
|
||||
--button-color-fill-normal-active: var(
|
||||
--ha-color-fill-warning-normal-active
|
||||
);
|
||||
--button-color-fill-normal-hover: var(
|
||||
--ha-color-fill-warning-normal-hover
|
||||
);
|
||||
--button-color-fill-loud-active: var(
|
||||
--ha-color-fill-warning-loud-active
|
||||
);
|
||||
--button-color-fill-loud-hover: var(
|
||||
--ha-color-fill-warning-loud-hover
|
||||
);
|
||||
}
|
||||
|
||||
:host([variant="danger"]) {
|
||||
--color-fill-normal-active: var(--color-fill-danger-normal-active);
|
||||
--color-fill-normal-hover: var(--color-fill-danger-normal-hover);
|
||||
--color-fill-loud-active: var(--color-fill-danger-loud-active);
|
||||
--color-fill-loud-hover: var(--color-fill-danger-loud-hover);
|
||||
--button-color-fill-normal-active: var(
|
||||
--ha-color-fill-danger-normal-active
|
||||
);
|
||||
--button-color-fill-normal-hover: var(
|
||||
--ha-color-fill-danger-normal-hover
|
||||
);
|
||||
--button-color-fill-loud-active: var(
|
||||
--ha-color-fill-danger-loud-active
|
||||
);
|
||||
--button-color-fill-loud-hover: var(
|
||||
--ha-color-fill-danger-loud-hover
|
||||
);
|
||||
}
|
||||
|
||||
:host([appearance~="plain"]) .button {
|
||||
color: var(--wa-color-on-normal);
|
||||
background-color: transparent;
|
||||
}
|
||||
:host([appearance~="plain"]) .button.disabled {
|
||||
background-color: var(--transparent-none);
|
||||
color: var(--color-on-disabled-quiet);
|
||||
background-color: transparent;
|
||||
color: var(--ha-color-on-disabled-quiet);
|
||||
}
|
||||
|
||||
:host([appearance~="outlined"]) .button.disabled {
|
||||
background-color: var(--transparent-none);
|
||||
color: var(--color-on-disabled-quiet);
|
||||
background-color: transparent;
|
||||
color: var(--ha-color-on-disabled-quiet);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
:host([appearance~="filled"])
|
||||
.button:not(.disabled):not(.loading):hover {
|
||||
background-color: var(--color-fill-normal-hover);
|
||||
background-color: var(--button-color-fill-normal-hover);
|
||||
}
|
||||
:host([appearance~="accent"])
|
||||
.button:not(.disabled):not(.loading):hover {
|
||||
background-color: var(--color-fill-loud-hover);
|
||||
background-color: var(--button-color-fill-loud-hover);
|
||||
}
|
||||
:host([appearance~="plain"])
|
||||
.button:not(.disabled):not(.loading):hover {
|
||||
@@ -140,11 +182,11 @@ export class HaButton extends Button {
|
||||
}
|
||||
:host([appearance~="filled"])
|
||||
.button:not(.disabled):not(.loading):active {
|
||||
background-color: var(--color-fill-normal-active);
|
||||
background-color: var(--button-color-fill-normal-active);
|
||||
}
|
||||
:host([appearance~="filled"]) .button.disabled {
|
||||
background-color: var(--color-fill-disabled-normal-resting);
|
||||
color: var(--color-on-disabled-normal);
|
||||
background-color: var(--ha-color-fill-disabled-normal-resting);
|
||||
color: var(--ha-color-on-disabled-normal);
|
||||
}
|
||||
|
||||
:host([appearance~="accent"]) .button {
|
||||
@@ -155,11 +197,11 @@ export class HaButton extends Button {
|
||||
}
|
||||
:host([appearance~="accent"])
|
||||
.button:not(.disabled):not(.loading):active {
|
||||
background-color: var(--color-fill-loud-active);
|
||||
background-color: var(--button-color-fill-loud-active);
|
||||
}
|
||||
:host([appearance~="accent"]) .button.disabled {
|
||||
background-color: var(--color-fill-disabled-loud-resting);
|
||||
color: var(--color-on-disabled-loud);
|
||||
background-color: var(--ha-color-fill-disabled-loud-resting);
|
||||
color: var(--ha-color-on-disabled-loud);
|
||||
}
|
||||
|
||||
:host([loading]) {
|
||||
@@ -169,6 +211,13 @@ export class HaButton extends Button {
|
||||
.button.disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
slot[name="start"]::slotted(*) {
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
slot[name="end"]::slotted(*) {
|
||||
margin-inline-start: 4px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -506,7 +506,7 @@ export class HaControlSlider extends LitElement {
|
||||
width: 100%;
|
||||
}
|
||||
.slider .slider-track-bar {
|
||||
--border-radius: var(--control-slider-border-radius);
|
||||
--ha-border-radius: var(--control-slider-border-radius);
|
||||
--slider-size: 100%;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
|
@@ -5,8 +5,8 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import type { ConfigEntry } from "../data/config_entries";
|
||||
import { getConfigEntry } from "../data/config_entries";
|
||||
import type { ConfigEntry, SubEntry } from "../data/config_entries";
|
||||
import { getConfigEntry, getSubEntries } from "../data/config_entries";
|
||||
import type { Agent } from "../data/conversation";
|
||||
import { listAgents } from "../data/conversation";
|
||||
import { fetchIntegrationManifest } from "../data/integration";
|
||||
@@ -16,6 +16,7 @@ import "./ha-list-item";
|
||||
import "./ha-select";
|
||||
import type { HaSelect } from "./ha-select";
|
||||
import { getExtendedEntityRegistryEntry } from "../data/entity_registry";
|
||||
import { showSubConfigFlowDialog } from "../dialogs/config-flow/show-dialog-sub-config-flow";
|
||||
|
||||
const NONE = "__NONE_OPTION__";
|
||||
|
||||
@@ -37,6 +38,8 @@ export class HaConversationAgentPicker extends LitElement {
|
||||
|
||||
@state() private _configEntry?: ConfigEntry;
|
||||
|
||||
@state() private _subConfigEntry?: SubEntry;
|
||||
|
||||
protected render() {
|
||||
if (!this._agents) {
|
||||
return nothing;
|
||||
@@ -101,7 +104,11 @@ export class HaConversationAgentPicker extends LitElement {
|
||||
${agent.name}
|
||||
</ha-list-item>`
|
||||
)}</ha-select
|
||||
>${this._configEntry?.supports_options
|
||||
>${(this._subConfigEntry &&
|
||||
this._configEntry?.supported_subentry_types[
|
||||
this._subConfigEntry.subentry_type
|
||||
]?.supports_reconfigure) ||
|
||||
this._configEntry?.supports_options
|
||||
? html`<ha-icon-button
|
||||
.path=${mdiCog}
|
||||
@click=${this._openOptionsFlow}
|
||||
@@ -142,8 +149,17 @@ export class HaConversationAgentPicker extends LitElement {
|
||||
this._configEntry = (
|
||||
await getConfigEntry(this.hass, regEntry.config_entry_id)
|
||||
).config_entry;
|
||||
|
||||
if (!regEntry.config_subentry_id) {
|
||||
this._subConfigEntry = undefined;
|
||||
} else {
|
||||
this._subConfigEntry = (
|
||||
await getSubEntries(this.hass, regEntry.config_entry_id)
|
||||
).find((entry) => entry.subentry_id === regEntry.config_subentry_id);
|
||||
}
|
||||
} catch (_err) {
|
||||
this._configEntry = undefined;
|
||||
this._subConfigEntry = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,6 +198,25 @@ export class HaConversationAgentPicker extends LitElement {
|
||||
if (!this._configEntry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
this._subConfigEntry &&
|
||||
this._configEntry.supported_subentry_types[
|
||||
this._subConfigEntry.subentry_type
|
||||
]?.supports_reconfigure
|
||||
) {
|
||||
showSubConfigFlowDialog(
|
||||
this,
|
||||
this._configEntry,
|
||||
this._subConfigEntry.subentry_type,
|
||||
{
|
||||
startFlowHandler: this._configEntry.entry_id,
|
||||
subEntryId: this._subConfigEntry.subentry_id,
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
showOptionsFlowDialog(this, this._configEntry, {
|
||||
manifest: await fetchIntegrationManifest(
|
||||
this.hass,
|
||||
|
@@ -20,6 +20,18 @@ export class HaFab extends FabBase {
|
||||
--mdc-typography-button-font-family: var(--ha-font-family-body);
|
||||
--mdc-typography-button-font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
:host .mdc-fab--extended {
|
||||
border-radius: var(
|
||||
--ha-button-border-radius,
|
||||
var(--ha-border-radius-pill)
|
||||
);
|
||||
}
|
||||
:host .mdc-fab.mdc-fab--extended .ripple {
|
||||
border-radius: var(
|
||||
--ha-button-border-radius,
|
||||
var(--ha-border-radius-pill)
|
||||
);
|
||||
}
|
||||
:host .mdc-fab--extended .mdc-fab__icon {
|
||||
margin-inline-start: -8px;
|
||||
margin-inline-end: 12px;
|
||||
|
@@ -7,8 +7,8 @@ import { haStyle } from "../resources/styles";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-button";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-textfield";
|
||||
import "./ha-input-helper-text";
|
||||
import "./ha-textfield";
|
||||
import type { HaTextField } from "./ha-textfield";
|
||||
|
||||
@customElement("ha-multi-textfield")
|
||||
@@ -79,6 +79,7 @@ class HaMultiTextField extends LitElement {
|
||||
@click=${this._addItem}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
||||
${this.addLabel ??
|
||||
(this.label
|
||||
? this.hass?.localize("ui.components.multi-textfield.add_item", {
|
||||
@@ -86,7 +87,6 @@ class HaMultiTextField extends LitElement {
|
||||
})
|
||||
: this.hass?.localize("ui.common.add")) ??
|
||||
"Add"}
|
||||
<ha-svg-icon slot="end" .path=${mdiPlus}></ha-svg-icon>
|
||||
</ha-button>
|
||||
</div>
|
||||
${this.helper
|
||||
|
@@ -92,11 +92,12 @@ export class HaPictureUpload extends LitElement {
|
||||
/>
|
||||
<div>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
variant="danger"
|
||||
@click=${this._handleChangeClick}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.picture-upload.clear_picture"
|
||||
)}
|
||||
>
|
||||
${this.hass.localize("ui.components.picture-upload.clear_picture")}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -103,7 +103,7 @@ class HaQrScanner extends LitElement {
|
||||
>
|
||||
${this._error || this._warning}
|
||||
${this._error
|
||||
? html` <ha-button @click=${this._retry} slot="action">
|
||||
? html`<ha-button @click=${this._retry} slot="action">
|
||||
${this.hass.localize("ui.components.qr-scanner.retry")}
|
||||
</ha-button>`
|
||||
: nothing}
|
||||
|
@@ -130,7 +130,7 @@ export class HaYamlEditor extends LitElement {
|
||||
<div class="card-actions">
|
||||
${this.copyClipboard
|
||||
? html`
|
||||
<ha-button @click=${this._copyYaml}>
|
||||
<ha-button appearance="plain" @click=${this._copyYaml}>
|
||||
${this.hass.localize(
|
||||
"ui.components.yaml-editor.copy_to_clipboard"
|
||||
)}
|
||||
|
@@ -116,18 +116,18 @@ class DialogMediaManage extends LitElement {
|
||||
`
|
||||
: html`
|
||||
<ha-button
|
||||
class="danger"
|
||||
variant="danger"
|
||||
slot="navigationIcon"
|
||||
.disabled=${this._deleting}
|
||||
.label=${this.hass.localize(
|
||||
@click=${this._handleDelete}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDelete} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
`ui.components.media-browser.file_management.${
|
||||
this._deleting ? "deleting" : "delete"
|
||||
}`,
|
||||
{ count: this._selected.size }
|
||||
)}
|
||||
@click=${this._handleDelete}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDelete} slot="start"></ha-svg-icon>
|
||||
</ha-button>
|
||||
|
||||
${this._deleting
|
||||
@@ -135,15 +135,15 @@ class DialogMediaManage extends LitElement {
|
||||
: html`
|
||||
<ha-button
|
||||
slot="actionItems"
|
||||
.label=${this.hass.localize(
|
||||
`ui.components.media-browser.file_management.deselect_all`
|
||||
)}
|
||||
@click=${this._handleDeselectAll}
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${mdiClose}
|
||||
slot="icon"
|
||||
slot="start"
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
`ui.components.media-browser.file_management.deselect_all`
|
||||
)}
|
||||
</ha-button>
|
||||
`}
|
||||
`}
|
||||
@@ -327,24 +327,10 @@ class DialogMediaManage extends LitElement {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.danger {
|
||||
--mdc-theme-primary: var(--error-color);
|
||||
}
|
||||
|
||||
ha-svg-icon[slot="icon"] {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
ha-tip {
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
ha-svg-icon[slot="icon"] {
|
||||
margin-inline-start: 0px !important;
|
||||
margin-inline-end: 8px !important;
|
||||
direction: var(--direction);
|
||||
}
|
||||
|
||||
.refresh {
|
||||
display: flex;
|
||||
height: 200px;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { mdiFolderEdit } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { MediaPlayerItem } from "../../data/media-player";
|
||||
@@ -53,18 +53,6 @@ class MediaManageButton extends LitElement {
|
||||
onClose: () => fireEvent(this, "media-refresh"),
|
||||
});
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-svg-icon[slot="icon"] {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
ha-svg-icon[slot="icon"] {
|
||||
margin-inline-start: 0px;
|
||||
margin-inline-end: 8px;
|
||||
direction: var(--direction);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -18,9 +18,9 @@ import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
import { isUnavailableState } from "../../data/entity";
|
||||
import type {
|
||||
MediaPlayerItem,
|
||||
MediaPickedEvent,
|
||||
MediaPlayerBrowseAction,
|
||||
MediaPlayerItem,
|
||||
MediaPlayerLayoutType,
|
||||
} from "../../data/media-player";
|
||||
import {
|
||||
@@ -32,6 +32,7 @@ import { browseLocalMediaPlayer } from "../../data/media_source";
|
||||
import { isTTSMediaSource } from "../../data/tts";
|
||||
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import { loadVirtualizer } from "../../resources/virtualizer";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import {
|
||||
brandsUrl,
|
||||
@@ -44,16 +45,15 @@ import "../ha-alert";
|
||||
import "../ha-button";
|
||||
import "../ha-button-menu";
|
||||
import "../ha-card";
|
||||
import "../ha-spinner";
|
||||
import "../ha-fab";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-svg-icon";
|
||||
import "../ha-tooltip";
|
||||
import "../ha-list";
|
||||
import "../ha-list-item";
|
||||
import "../ha-spinner";
|
||||
import "../ha-svg-icon";
|
||||
import "../ha-tooltip";
|
||||
import "./ha-browse-media-tts";
|
||||
import type { TtsMediaPickedEvent } from "./ha-browse-media-tts";
|
||||
import { loadVirtualizer } from "../../resources/virtualizer";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
|
@@ -169,6 +169,7 @@ export interface TagTrigger extends BaseTrigger {
|
||||
export interface TimeTrigger extends BaseTrigger {
|
||||
trigger: "time";
|
||||
at: string | { entity_id: string; offset?: string };
|
||||
weekday?: string | string[];
|
||||
}
|
||||
|
||||
export interface TemplateTrigger extends BaseTrigger {
|
||||
|
@@ -400,8 +400,23 @@ const tryDescribeTrigger = (
|
||||
return `${entityStr}${offsetStr}`;
|
||||
});
|
||||
|
||||
// Handle weekday information if present
|
||||
let weekdays: string[] = [];
|
||||
if (trigger.weekday) {
|
||||
const weekdayArray = ensureArray(trigger.weekday);
|
||||
if (weekdayArray.length > 0) {
|
||||
weekdays = weekdayArray.map((day) =>
|
||||
hass.localize(
|
||||
`ui.panel.config.automation.editor.triggers.type.time.weekdays.${day}` as any
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return hass.localize(`${triggerTranslationBaseKey}.time.description.full`, {
|
||||
time: formatListWithOrs(hass.locale, result),
|
||||
hasWeekdays: weekdays.length > 0 ? "true" : "false",
|
||||
weekdays: formatListWithOrs(hass.locale, weekdays),
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,7 @@ export interface HassioHostInfo {
|
||||
chassis: string;
|
||||
cpe: string;
|
||||
deployment: string;
|
||||
disk_life_time: number | "";
|
||||
disk_life_time: number | null;
|
||||
disk_free: number;
|
||||
disk_total: number;
|
||||
disk_used: number;
|
||||
|
@@ -104,22 +104,12 @@ class StepFlowForm extends LitElement {
|
||||
</div>`
|
||||
: nothing}
|
||||
<div class="buttons">
|
||||
${this._loading
|
||||
? html`
|
||||
<div class="submit-spinner">
|
||||
<ha-spinner size="small"></ha-spinner>
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<div>
|
||||
<ha-button @click=${this._submitStep}>
|
||||
${this.flowConfig.renderShowFormStepSubmitButton(
|
||||
this.hass,
|
||||
this.step
|
||||
)}
|
||||
</ha-button>
|
||||
</div>
|
||||
`}
|
||||
<ha-button @click=${this._submitStep} .loading=${this._loading}>
|
||||
${this.flowConfig.renderShowFormStepSubmitButton(
|
||||
this.hass,
|
||||
this.step
|
||||
)}
|
||||
</ha-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -304,15 +294,6 @@ class StepFlowForm extends LitElement {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.submit-spinner {
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 16px;
|
||||
margin-inline-end: 16px;
|
||||
margin-inline-start: initial;
|
||||
}
|
||||
|
||||
ha-alert,
|
||||
ha-form {
|
||||
margin-top: 24px;
|
||||
@@ -320,7 +301,7 @@ class StepFlowForm extends LitElement {
|
||||
}
|
||||
|
||||
.buttons {
|
||||
padding: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@@ -34,7 +34,7 @@ export const configFlowContentStyles = css`
|
||||
|
||||
.buttons {
|
||||
position: relative;
|
||||
padding: 8px 16px 8px 24px;
|
||||
padding: 16px;
|
||||
margin: 8px 0 0;
|
||||
color: var(--primary-color);
|
||||
display: flex;
|
||||
|
@@ -228,7 +228,7 @@ class DialogLightColorFavorite extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<ha-button @click=${this._cancelDialog}>
|
||||
<ha-button appearance="plain" @click=${this._cancelDialog}>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button @click=${this._save} .disabled=${!this._color}
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import "../../../components/buttons/ha-progress-button";
|
||||
import "../../../components/ha-camera-stream";
|
||||
import type { CameraEntity } from "../../../data/camera";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../../../components/buttons/ha-progress-button";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { fileDownload } from "../../../util/file_download";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
|
||||
class MoreInfoCamera extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -46,6 +46,7 @@ class MoreInfoCamera extends LitElement {
|
||||
@click=${this._downloadSnapshot}
|
||||
.progress=${this._waiting}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
appearance="filled"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.camera.download_snapshot"
|
||||
@@ -104,7 +105,7 @@ class MoreInfoCamera extends LitElement {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
padding: 16px;
|
||||
z-index: 1;
|
||||
gap: 8px;
|
||||
}
|
||||
|
@@ -51,7 +51,11 @@ class MoreInfoSiren extends LitElement {
|
||||
.iconPathOff=${mdiVolumeOff}
|
||||
></ha-state-control-toggle>
|
||||
${allowAdvanced
|
||||
? html`<ha-button @click=${this._showAdvancedControlsDialog}>
|
||||
? html`<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
@click=${this._showAdvancedControlsDialog}
|
||||
>
|
||||
${this.hass.localize("ui.components.siren.advanced_controls")}
|
||||
</ha-button>`
|
||||
: nothing}
|
||||
|
@@ -508,7 +508,7 @@ class MoreInfoUpdate extends LitElement {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
padding: 16px;
|
||||
z-index: 1;
|
||||
gap: 8px;
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ export class HuiNotificationItemTemplate extends LitElement {
|
||||
|
||||
.actions {
|
||||
border-top: 1px solid var(--divider-color, #e8e8e8);
|
||||
padding: 5px 16px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ class DialogBox extends LitElement {
|
||||
)}
|
||||
>
|
||||
<p>${this.hass.localize("ui.dialogs.update_backup.text")}</p>
|
||||
<ha-button @click=${this._no} slot="secondaryAction">
|
||||
<ha-button appearance="plain" @click=${this._no} slot="secondaryAction">
|
||||
${this.hass!.localize("ui.common.no")}
|
||||
</ha-button>
|
||||
<ha-button @click=${this._yes} slot="primaryAction">
|
||||
|
@@ -76,7 +76,6 @@ export class CloudStepSignin extends LitElement {
|
||||
</div>
|
||||
<div class="footer">
|
||||
<ha-button
|
||||
unelevated
|
||||
@click=${this._handleLogin}
|
||||
.disabled=${this._requestInProgress}
|
||||
>${this.hass.localize(
|
||||
|
@@ -90,11 +90,11 @@ export class CloudStepSignup extends LitElement {
|
||||
? html`<ha-button
|
||||
@click=${this._handleResendVerifyEmail}
|
||||
.disabled=${this._requestInProgress}
|
||||
appearance="plain"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.cloud.register.resend_confirm_email"
|
||||
)}</ha-button
|
||||
><ha-button
|
||||
unelevated
|
||||
@click=${this._login}
|
||||
.disabled=${this._requestInProgress}
|
||||
>${this.hass.localize(
|
||||
@@ -104,12 +104,12 @@ export class CloudStepSignup extends LitElement {
|
||||
: html`<ha-button
|
||||
@click=${this._signIn}
|
||||
.disabled=${this._requestInProgress}
|
||||
appearance="plain"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.cloud.login.sign_in"
|
||||
)}</ha-button
|
||||
>
|
||||
<ha-button
|
||||
unelevated
|
||||
@click=${this._handleRegister}
|
||||
.disabled=${this._requestInProgress}
|
||||
>${this.hass.localize("ui.common.next")}</ha-button
|
||||
|
@@ -70,6 +70,7 @@ export class HaVoiceAssistantSetupStepArea extends LitElement {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 24px;
|
||||
text-align: initial;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@@ -51,16 +51,16 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement {
|
||||
)}
|
||||
</p>
|
||||
<div class="footer">
|
||||
<a
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
href=${documentationUrl(
|
||||
this.hass,
|
||||
"/voice_control/troubleshooting/#i-dont-get-a-voice-response"
|
||||
)}
|
||||
><ha-button
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.voice_assistants.satellite_wizard.check.help"
|
||||
)}</ha-button
|
||||
></a
|
||||
>
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.voice_assistants.satellite_wizard.check.help"
|
||||
)}</ha-button
|
||||
>
|
||||
<ha-button @click=${this._testConnection}
|
||||
>${this.hass.localize(
|
||||
|
@@ -80,7 +80,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
|
||||
${this._detailState || "Installation can take several minutes"}
|
||||
</p>`
|
||||
: this._state === "ERROR"
|
||||
? html` <img
|
||||
? html`<img
|
||||
src="/static/images/voice-assistant/error.png"
|
||||
alt="Casita Home Assistant error logo"
|
||||
/>
|
||||
@@ -95,24 +95,27 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
|
||||
"ui.panel.config.voice_assistants.satellite_wizard.local.failed_secondary"
|
||||
)}
|
||||
</p>
|
||||
<ha-button @click=${this._prevStep}
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
@click=${this._prevStep}
|
||||
>${this.hass.localize("ui.common.back")}</ha-button
|
||||
>
|
||||
<a
|
||||
<ha-button
|
||||
href=${documentationUrl(
|
||||
this.hass,
|
||||
"/voice_control/voice_remote_local_assistant/"
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
size="small"
|
||||
appearance="plain"
|
||||
>
|
||||
<ha-button>
|
||||
<ha-svg-icon .path=${mdiOpenInNew} slot="icon"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.common.learn_more"
|
||||
)}</ha-button
|
||||
>
|
||||
</a>`
|
||||
<ha-svg-icon .path=${mdiOpenInNew} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.common.learn_more"
|
||||
)}</ha-button
|
||||
>`
|
||||
: this._state === "NOT_SUPPORTED"
|
||||
? html`<img
|
||||
src="/static/images/voice-assistant/error.png"
|
||||
@@ -128,27 +131,27 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
|
||||
"ui.panel.config.voice_assistants.satellite_wizard.local.not_supported_secondary"
|
||||
)}
|
||||
</p>
|
||||
<ha-button @click=${this._prevStep}
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
@click=${this._prevStep}
|
||||
>${this.hass.localize("ui.common.back")}</ha-button
|
||||
>
|
||||
<a
|
||||
<ha-button
|
||||
href=${documentationUrl(
|
||||
this.hass,
|
||||
"/voice_control/voice_remote_local_assistant/"
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
appearance="plain"
|
||||
size="small"
|
||||
>
|
||||
<ha-button>
|
||||
<ha-svg-icon
|
||||
.path=${mdiOpenInNew}
|
||||
slot="icon"
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.common.learn_more"
|
||||
)}</ha-button
|
||||
>
|
||||
</a>`
|
||||
<ha-svg-icon .path=${mdiOpenInNew} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.common.learn_more"
|
||||
)}</ha-button
|
||||
>`
|
||||
: nothing}
|
||||
</div>`;
|
||||
}
|
||||
|
@@ -235,10 +235,7 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
|
||||
: nothing}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<ha-button
|
||||
@click=${this._createPipeline}
|
||||
unelevated
|
||||
.disabled=${!this._value}
|
||||
<ha-button @click=${this._createPipeline} .disabled=${!this._value}
|
||||
>${this.hass.localize("ui.common.next")}</ha-button
|
||||
>
|
||||
</div>`;
|
||||
|
@@ -126,8 +126,15 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
|
||||
</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
<ha-button @click=${this._testWakeWord}>
|
||||
<ha-svg-icon slot="icon" .path=${mdiMicrophone}></ha-svg-icon>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
@click=${this._testWakeWord}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiMicrophone}
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.voice_assistants.satellite_wizard.success.test_wakeword"
|
||||
)}
|
||||
@@ -151,8 +158,12 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
|
||||
</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
<ha-button @click=${this._openPipeline}>
|
||||
<ha-svg-icon slot="icon" .path=${mdiCog}></ha-svg-icon>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
@click=${this._openPipeline}
|
||||
>
|
||||
<ha-svg-icon slot="start" .path=${mdiCog}></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.voice_assistants.satellite_wizard.success.edit_pipeline"
|
||||
)}
|
||||
@@ -169,8 +180,12 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
|
||||
@value-changed=${this._voicePicked}
|
||||
@closed=${stopPropagation}
|
||||
></ha-tts-voice-picker>
|
||||
<ha-button @click=${this._testTts}>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlay}></ha-svg-icon>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
@click=${this._testTts}
|
||||
>
|
||||
<ha-svg-icon slot="start" .path=${mdiPlay}></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.voice_assistants.satellite_wizard.success.try_tts"
|
||||
)}
|
||||
|
@@ -148,7 +148,10 @@ export class HaVoiceAssistantSetupStepWakeWord extends LitElement {
|
||||
${this.assistConfiguration &&
|
||||
this.assistConfiguration.available_wake_words.length > 1
|
||||
? html`<div class="footer centered">
|
||||
<ha-button @click=${this._changeWakeWord}
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
@click=${this._changeWakeWord}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.voice_assistants.satellite_wizard.wake_word.change_wake_word"
|
||||
)}</ha-button
|
||||
|
@@ -271,7 +271,6 @@ export class HaVoiceCommandDialog extends LitElement {
|
||||
margin-inline-start: -8px;
|
||||
}
|
||||
ha-button-menu ha-button {
|
||||
--ha-font-size-l: var(--ha-font-size-m);
|
||||
--ha-button-height: 20px;
|
||||
}
|
||||
ha-button-menu ha-button::part(base) {
|
||||
|
@@ -22,7 +22,9 @@ class HaInitPage extends LitElement {
|
||||
<p class="retry-text">
|
||||
Retrying in ${this._retryInSeconds} seconds...
|
||||
</p>
|
||||
<ha-button @click=${this._retry}>Retry now</ha-button>
|
||||
<ha-button size="small" appearance="plain" @click=${this._retry}
|
||||
>Retry now</ha-button
|
||||
>
|
||||
${location.host.includes("ui.nabu.casa")
|
||||
? html`
|
||||
<p>
|
||||
|
@@ -69,10 +69,13 @@ class NotificationManager extends LitElement {
|
||||
${this._parameters?.action
|
||||
? html`
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
slot="action"
|
||||
.label=${this._parameters?.action.text}
|
||||
@click=${this._buttonClicked}
|
||||
></ha-button>
|
||||
>
|
||||
${this._parameters?.action.text}
|
||||
</ha-button>
|
||||
`
|
||||
: nothing}
|
||||
${this._parameters?.dismissable
|
||||
|
34
src/mixins/mobile-aware-mixin.ts
Normal file
34
src/mixins/mobile-aware-mixin.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { LitElement } from "lit";
|
||||
import { state } from "lit/decorators";
|
||||
import type { Constructor } from "../types";
|
||||
import { isMobileClient } from "../util/is_mobile";
|
||||
import { listenMediaQuery } from "../common/dom/media_query";
|
||||
|
||||
export const MobileAwareMixin = <T extends Constructor<LitElement>>(
|
||||
superClass: T
|
||||
) => {
|
||||
class MobileAwareClass extends superClass {
|
||||
@state() protected _isMobileSize = false;
|
||||
|
||||
protected _isMobileClient = isMobileClient;
|
||||
|
||||
private _unsubMql?: () => void;
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._unsubMql = listenMediaQuery(
|
||||
"all and (max-width: 450px), all and (max-height: 500px)",
|
||||
(matches) => {
|
||||
this._isMobileSize = matches;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._unsubMql?.();
|
||||
this._unsubMql = undefined;
|
||||
}
|
||||
}
|
||||
return MobileAwareClass;
|
||||
};
|
@@ -32,17 +32,16 @@ class OnboardingRestoreBackupNoCloudBackup extends LitElement {
|
||||
<ha-button @click=${this._signOut}>
|
||||
${this.localize("ui.panel.page-onboarding.restore.ha-cloud.sign_out")}
|
||||
</ha-button>
|
||||
<a
|
||||
<ha-button
|
||||
href="https://www.nabucasa.com/config/backups/"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
appearance="plain"
|
||||
>
|
||||
<ha-button>
|
||||
${this.localize(
|
||||
"ui.panel.page-onboarding.restore.ha-cloud.learn_more"
|
||||
)}
|
||||
</ha-button>
|
||||
</a>
|
||||
${this.localize(
|
||||
"ui.panel.page-onboarding.restore.ha-cloud.learn_more"
|
||||
)}
|
||||
</ha-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
@@ -131,18 +131,17 @@ class OnboardingRestoreBackupRestore extends LitElement {
|
||||
${this.localize(
|
||||
"ui.panel.page-onboarding.restore.details.addons_unsupported"
|
||||
)}
|
||||
<a
|
||||
<ha-button
|
||||
slot="action"
|
||||
href="https://www.home-assistant.io/installation/#advanced-installation-methods"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
size="small"
|
||||
>
|
||||
${this.localize(
|
||||
"ui.panel.page-onboarding.restore.ha-cloud.learn_more"
|
||||
)}</ha-button
|
||||
>
|
||||
<ha-button
|
||||
>${this.localize(
|
||||
"ui.panel.page-onboarding.restore.ha-cloud.learn_more"
|
||||
)}</ha-button
|
||||
>
|
||||
</a>
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
${!onlyHomeAssistantBackup
|
||||
@@ -191,14 +190,13 @@ class OnboardingRestoreBackupRestore extends LitElement {
|
||||
|
||||
<div class="actions${this.mode === "cloud" ? " cloud" : ""}">
|
||||
${this.mode === "cloud"
|
||||
? html`<ha-button @click=${this._signOut}>
|
||||
? html`<ha-button appearance="plain" @click=${this._signOut}>
|
||||
${this.localize(
|
||||
"ui.panel.page-onboarding.restore.ha-cloud.sign_out"
|
||||
)}
|
||||
</ha-button>`
|
||||
: nothing}
|
||||
<ha-progress-button
|
||||
unelevated
|
||||
.progress=${this._loading}
|
||||
.disabled=${this._loading ||
|
||||
(backupProtected && this._encryptionKey === "") ||
|
||||
|
@@ -40,7 +40,11 @@ class ConfirmEventDialogBox extends LitElement {
|
||||
<div>
|
||||
<p>${this._params.text}</p>
|
||||
</div>
|
||||
<ha-button @click=${this._dismiss} slot="secondaryAction">
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._dismiss}
|
||||
slot="secondaryAction"
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import { formatInTimeZone, toDate } from "date-fns-tz";
|
||||
import {
|
||||
addDays,
|
||||
addHours,
|
||||
@@ -6,6 +5,7 @@ import {
|
||||
differenceInMilliseconds,
|
||||
startOfHour,
|
||||
} from "date-fns";
|
||||
import { formatInTimeZone, toDate } from "date-fns-tz";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
@@ -18,11 +18,11 @@ import { supportsFeature } from "../../common/entity/supports-feature";
|
||||
import { isDate } from "../../common/string/is_date";
|
||||
import "../../components/entity/ha-entity-picker";
|
||||
import "../../components/ha-alert";
|
||||
import "../../components/ha-button";
|
||||
import "../../components/ha-date-input";
|
||||
import { createCloseHeading } from "../../components/ha-dialog";
|
||||
import "../../components/ha-formfield";
|
||||
import "../../components/ha-switch";
|
||||
import "../../components/ha-button";
|
||||
import "../../components/ha-textarea";
|
||||
import "../../components/ha-textfield";
|
||||
import "../../components/ha-time-input";
|
||||
@@ -282,6 +282,7 @@ class DialogCalendarEventEditor extends LitElement {
|
||||
? html`
|
||||
<ha-button
|
||||
slot="secondaryAction"
|
||||
appearance="plain"
|
||||
variant="danger"
|
||||
@click=${this._deleteEvent}
|
||||
.disabled=${this._submitting}
|
||||
|
@@ -135,10 +135,7 @@ class PanelCalendar extends LitElement {
|
||||
>
|
||||
<ha-button slot="trigger">
|
||||
${this.hass.localize("ui.components.calendar.my_calendars")}
|
||||
<ha-svg-icon
|
||||
slot="trailingIcon"
|
||||
.path=${mdiChevronDown}
|
||||
></ha-svg-icon>
|
||||
<ha-svg-icon slot="end" .path=${mdiChevronDown}></ha-svg-icon>
|
||||
</ha-button>
|
||||
${calendarItems}
|
||||
${this.hass.user?.is_admin
|
||||
@@ -303,25 +300,7 @@ class PanelCalendar extends LitElement {
|
||||
--calendar-border-width: 1px 0;
|
||||
}
|
||||
ha-button-menu ha-button {
|
||||
--mdc-theme-primary: currentColor;
|
||||
--mdc-typography-button-text-transform: none;
|
||||
--mdc-typography-button-font-size: var(
|
||||
--mdc-typography-headline6-font-size,
|
||||
var(--ha-font-size-l)
|
||||
);
|
||||
--mdc-typography-button-font-weight: var(
|
||||
--mdc-typography-headline6-font-weight,
|
||||
var(--ha-font-weight-medium)
|
||||
);
|
||||
--mdc-typography-button-letter-spacing: var(
|
||||
--mdc-typography-headline6-letter-spacing,
|
||||
0.0125em
|
||||
);
|
||||
--mdc-typography-button-line-height: var(
|
||||
--mdc-typography-headline6-line-height,
|
||||
var(--ha-line-height-expanded)
|
||||
);
|
||||
--button-height: 40px;
|
||||
--ha-font-size-m: var(--ha-font-size-l);
|
||||
}
|
||||
:host([mobile]) .lists {
|
||||
--mdc-menu-min-width: 100vw;
|
||||
|
@@ -220,32 +220,25 @@ export class DialogAddApplicationCredential extends LitElement {
|
||||
helperPersistent
|
||||
></ha-password-field>
|
||||
</div>
|
||||
${this._loading
|
||||
? html`
|
||||
<div slot="primaryAction" class="submit-spinner">
|
||||
<ha-spinner></ha-spinner>
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="secondaryAction"
|
||||
@click=${this._abortDialog}
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
.disabled=${!this._domain ||
|
||||
!this._clientId ||
|
||||
!this._clientSecret}
|
||||
@click=${this._addApplicationCredential}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.application_credentials.editor.add"
|
||||
)}
|
||||
</ha-button>
|
||||
`}
|
||||
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="secondaryAction"
|
||||
@click=${this._abortDialog}
|
||||
.disabled=${this._loading}
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
.disabled=${!this._domain || !this._clientId || !this._clientSecret}
|
||||
@click=${this._addApplicationCredential}
|
||||
.loading=${this._loading}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.application_credentials.editor.add"
|
||||
)}
|
||||
</ha-button>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
@@ -30,6 +30,8 @@ export default class HaAutomationAction extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean }) public root = false;
|
||||
|
||||
@property({ attribute: false }) public actions!: Action[];
|
||||
|
||||
@property({ attribute: false }) public highlightedActions?: Action[];
|
||||
@@ -110,6 +112,8 @@ export default class HaAutomationAction extends LitElement {
|
||||
<ha-button
|
||||
.disabled=${this.disabled}
|
||||
@click=${this._addActionDialog}
|
||||
.appearance=${this.root ? "accent" : "filled"}
|
||||
.size=${this.root ? "medium" : "small"}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
@@ -117,9 +121,10 @@ export default class HaAutomationAction extends LitElement {
|
||||
)}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
.disabled=${this.disabled}
|
||||
@click=${this._addActionBuildingBlockDialog}
|
||||
appearance="plain"
|
||||
.size=${this.root ? "medium" : "small"}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
@@ -312,9 +317,6 @@ export default class HaAutomationAction extends LitElement {
|
||||
display: block;
|
||||
scroll-margin-top: 48px;
|
||||
}
|
||||
ha-svg-icon {
|
||||
height: 20px;
|
||||
}
|
||||
.handle {
|
||||
padding: 12px;
|
||||
cursor: move; /* fallback if grab cursor is unsupported */
|
||||
|
@@ -260,12 +260,14 @@ class DialogAutomationSave extends LitElement implements HassDialog {
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
<span slot="title">${this._params.title || title}</span>
|
||||
<ha-suggest-with-ai-button
|
||||
slot="actionItems"
|
||||
.hass=${this.hass}
|
||||
.generateTask=${this._generateTask}
|
||||
@suggestion=${this._handleSuggestion}
|
||||
></ha-suggest-with-ai-button>
|
||||
${this._params.hideInputs
|
||||
? nothing
|
||||
: html` <ha-suggest-with-ai-button
|
||||
slot="actionItems"
|
||||
.hass=${this.hass}
|
||||
.generateTask=${this._generateTask}
|
||||
@suggestion=${this._handleSuggestion}
|
||||
></ha-suggest-with-ai-button>`}
|
||||
</ha-dialog-header>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error"
|
||||
@@ -381,7 +383,7 @@ class DialogAutomationSave extends LitElement implements HassDialog {
|
||||
return {
|
||||
type: "data",
|
||||
task: {
|
||||
task_name: `frontend:${term}:save`,
|
||||
task_name: `frontend__${term}__save`,
|
||||
instructions: `Suggest in language "${this.hass.language}" a name, description, category and labels for the following Home Assistant ${term}.
|
||||
|
||||
The name should be relevant to the ${term}'s purpose.
|
||||
|
@@ -34,6 +34,8 @@ export default class HaAutomationCondition extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean }) public root = false;
|
||||
|
||||
@state() private _showReorder = false;
|
||||
|
||||
@state()
|
||||
@@ -159,6 +161,8 @@ export default class HaAutomationCondition extends LitElement {
|
||||
<ha-button
|
||||
.disabled=${this.disabled}
|
||||
@click=${this._addConditionDialog}
|
||||
.appearance=${this.root ? "accent" : "filled"}
|
||||
.size=${this.root ? "medium" : "small"}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
@@ -168,6 +172,7 @@ export default class HaAutomationCondition extends LitElement {
|
||||
<ha-button
|
||||
.disabled=${this.disabled}
|
||||
appearance="plain"
|
||||
.size=${this.root ? "medium" : "small"}
|
||||
@click=${this._addConditionBuildingBlockDialog}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
|
||||
@@ -340,9 +345,6 @@ export default class HaAutomationCondition extends LitElement {
|
||||
.buttons {
|
||||
order: 1;
|
||||
}
|
||||
ha-svg-icon {
|
||||
height: 20px;
|
||||
}
|
||||
.handle {
|
||||
padding: 12px;
|
||||
cursor: move; /* fallback if grab cursor is unsupported */
|
||||
|
@@ -487,6 +487,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
.disabled=${Boolean(this._readOnly)}
|
||||
.dirty=${this._dirty}
|
||||
@value-changed=${this._valueChanged}
|
||||
@editor-save=${this._handleSaveAutomation}
|
||||
></manual-automation-editor>
|
||||
`}
|
||||
</div>
|
||||
@@ -517,6 +518,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
.defaultValue=${this._preprocessYaml()}
|
||||
.readOnly=${this._readOnly}
|
||||
@value-changed=${this._yamlChanged}
|
||||
@editor-save=${this._handleSaveAutomation}
|
||||
.showErrors=${false}
|
||||
disable-fullscreen
|
||||
></ha-yaml-editor>`
|
||||
|
@@ -228,6 +228,7 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
@value-changed=${this._conditionChanged}
|
||||
.hass=${this.hass}
|
||||
.disabled=${this.disabled}
|
||||
root
|
||||
></ha-automation-condition>
|
||||
|
||||
<div class="header">
|
||||
@@ -269,6 +270,7 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.disabled=${this.disabled}
|
||||
root
|
||||
></ha-automation-action>
|
||||
`;
|
||||
}
|
||||
|
@@ -100,14 +100,14 @@ export default class HaAutomationOption extends LitElement {
|
||||
)}
|
||||
<div class="buttons">
|
||||
<ha-button
|
||||
outlined
|
||||
appearance="filled"
|
||||
.disabled=${this.disabled}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.choose.add_option"
|
||||
)}
|
||||
@click=${this._addOption}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.choose.add_option"
|
||||
)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -255,9 +255,6 @@ export default class HaAutomationOption extends LitElement {
|
||||
display: block;
|
||||
scroll-margin-top: 48px;
|
||||
}
|
||||
ha-svg-icon {
|
||||
height: 20px;
|
||||
}
|
||||
.handle {
|
||||
padding: 12px;
|
||||
cursor: move; /* fallback if grab cursor is unsupported */
|
||||
|
@@ -58,7 +58,7 @@ class DialogPasteReplace extends LitElement implements HassDialog {
|
||||
></ha-yaml-editor>
|
||||
|
||||
<div slot="primaryAction">
|
||||
<ha-button @click=${this._handleAppend}>
|
||||
<ha-button appearance="plain" @click=${this._handleAppend}>
|
||||
${this.hass.localize("ui.common.append")}
|
||||
</ha-button>
|
||||
<ha-button @click=${this._handleReplace}>
|
||||
@@ -89,6 +89,10 @@ class DialogPasteReplace extends LitElement implements HassDialog {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
div[slot="primaryAction"] {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -296,9 +296,6 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
display: block;
|
||||
scroll-margin-top: 48px;
|
||||
}
|
||||
ha-svg-icon {
|
||||
height: 20px;
|
||||
}
|
||||
.handle {
|
||||
padding: 12px;
|
||||
cursor: move; /* fallback if grab cursor is unsupported */
|
||||
|
@@ -2,11 +2,13 @@ import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { firstWeekdayIndex } from "../../../../../common/datetime/first_weekday";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../../../../../common/translations/localize";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
import type { TimeTrigger } from "../../../../../data/automation";
|
||||
import type { FrontendLocaleData } from "../../../../../data/translation";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import type { TriggerElement } from "../ha-automation-trigger-row";
|
||||
import { computeDomain } from "../../../../../common/entity/compute_domain";
|
||||
@@ -14,6 +16,7 @@ import { computeDomain } from "../../../../../common/entity/compute_domain";
|
||||
const MODE_TIME = "time";
|
||||
const MODE_ENTITY = "entity";
|
||||
const VALID_DOMAINS = ["sensor", "input_datetime"];
|
||||
const DAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"] as const;
|
||||
|
||||
@customElement("ha-automation-trigger-time")
|
||||
export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
@@ -35,9 +38,14 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
private _schema = memoizeOne(
|
||||
(
|
||||
localize: LocalizeFunc,
|
||||
locale: FrontendLocaleData,
|
||||
inputMode: typeof MODE_TIME | typeof MODE_ENTITY
|
||||
) =>
|
||||
[
|
||||
) => {
|
||||
const dayIndex = firstWeekdayIndex(locale);
|
||||
const sortedDays = DAYS.slice(dayIndex, DAYS.length).concat(
|
||||
DAYS.slice(0, dayIndex)
|
||||
);
|
||||
return [
|
||||
{
|
||||
name: "mode",
|
||||
type: "select",
|
||||
@@ -73,7 +81,21 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
},
|
||||
{ name: "offset", selector: { text: {} } },
|
||||
] as const)),
|
||||
] as const
|
||||
{
|
||||
type: "multi_select",
|
||||
name: "weekday",
|
||||
options: sortedDays.map(
|
||||
(day) =>
|
||||
[
|
||||
day,
|
||||
localize(
|
||||
`ui.panel.config.automation.editor.triggers.type.time.weekdays.${day}`
|
||||
),
|
||||
] as const
|
||||
),
|
||||
},
|
||||
] as const;
|
||||
}
|
||||
);
|
||||
|
||||
public willUpdate(changedProperties: PropertyValues) {
|
||||
@@ -95,12 +117,14 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
inputMode: undefined | typeof MODE_ENTITY | typeof MODE_TIME,
|
||||
at:
|
||||
| string
|
||||
| { entity_id: string | undefined; offset?: string | undefined }
|
||||
| { entity_id: string | undefined; offset?: string | undefined },
|
||||
weekday: string | string[] | undefined
|
||||
): {
|
||||
mode: typeof MODE_TIME | typeof MODE_ENTITY;
|
||||
entity: string | undefined;
|
||||
time: string | undefined;
|
||||
offset: string | undefined;
|
||||
weekday: string | string[] | undefined;
|
||||
} => {
|
||||
const entity =
|
||||
typeof at === "object"
|
||||
@@ -116,6 +140,7 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
entity,
|
||||
time,
|
||||
offset,
|
||||
weekday,
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -127,8 +152,12 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const data = this._data(this._inputMode, at);
|
||||
const schema = this._schema(this.hass.localize, data.mode);
|
||||
const data = this._data(this._inputMode, at, this.trigger.weekday);
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
this.hass.locale,
|
||||
data.mode
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
@@ -146,22 +175,36 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
ev.stopPropagation();
|
||||
const newValue = { ...ev.detail.value };
|
||||
this._inputMode = newValue.mode;
|
||||
|
||||
const weekday = newValue.weekday;
|
||||
delete newValue.weekday;
|
||||
|
||||
if (newValue.mode === MODE_TIME) {
|
||||
delete newValue.entity;
|
||||
delete newValue.offset;
|
||||
} else {
|
||||
delete newValue.time;
|
||||
}
|
||||
|
||||
const triggerUpdate: TimeTrigger = {
|
||||
...this.trigger,
|
||||
at: newValue.offset
|
||||
? {
|
||||
entity_id: newValue.entity,
|
||||
offset: newValue.offset,
|
||||
}
|
||||
: newValue.entity || newValue.time,
|
||||
};
|
||||
|
||||
// Only include weekday if it has a value
|
||||
if (weekday && weekday.length > 0) {
|
||||
triggerUpdate.weekday = weekday;
|
||||
} else {
|
||||
delete triggerUpdate.weekday;
|
||||
}
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.trigger,
|
||||
at: newValue.offset
|
||||
? {
|
||||
entity_id: newValue.entity,
|
||||
offset: newValue.offset,
|
||||
}
|
||||
: newValue.entity || newValue.time,
|
||||
},
|
||||
value: triggerUpdate,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -173,6 +216,10 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
return this.hass.localize(
|
||||
`ui.panel.config.automation.editor.triggers.type.time.at`
|
||||
);
|
||||
case "weekday":
|
||||
return this.hass.localize(
|
||||
`ui.panel.config.automation.editor.triggers.type.time.weekday`
|
||||
);
|
||||
}
|
||||
return this.hass.localize(
|
||||
`ui.panel.config.automation.editor.triggers.type.time.${schema.name}`
|
||||
|
@@ -99,13 +99,11 @@ class HaBackupOverviewBackups extends LitElement {
|
||||
</ha-md-list>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a href="/config/backup/backups?type=all">
|
||||
<ha-button appearance="filled">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.overview.backups.show_all"
|
||||
)}
|
||||
</ha-button>
|
||||
</a>
|
||||
<ha-button appearance="filled" href="/config/backup/backups?type=all">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.overview.backups.show_all"
|
||||
)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
|
@@ -375,8 +375,13 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
||||
"ui.panel.config.backup.encryption_key.download_emergency_kit_description"
|
||||
)}
|
||||
</span>
|
||||
<ha-button slot="end" @click=${this._downloadKey}>
|
||||
<ha-svg-icon .path=${mdiDownload} slot="icon"></ha-svg-icon>
|
||||
<ha-button
|
||||
size="small"
|
||||
appearance="plain"
|
||||
slot="end"
|
||||
@click=${this._downloadKey}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.encryption_key.download_emergency_kit_action"
|
||||
)}
|
||||
|
@@ -128,7 +128,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
|
||||
<ha-button
|
||||
@click=${this._submit}
|
||||
.disabled=${!this._newEncryptionKey}
|
||||
class="danger"
|
||||
variant="danger"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.dialogs.change_encryption_key.actions.change"
|
||||
@@ -176,7 +176,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
|
||||
)}
|
||||
</span>
|
||||
<ha-button slot="end" @click=${this._downloadOld}>
|
||||
<ha-svg-icon .path=${mdiDownload} slot="icon"></ha-svg-icon>
|
||||
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.encryption_key.download_old_emergency_kit_action"
|
||||
)}
|
||||
@@ -211,7 +211,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
|
||||
)}
|
||||
</span>
|
||||
<ha-button slot="end" @click=${this._downloadNew}>
|
||||
<ha-svg-icon .path=${mdiDownload} slot="icon"></ha-svg-icon>
|
||||
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.encryption_key.download_emergency_kit_action"
|
||||
)}
|
||||
@@ -297,9 +297,6 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
|
||||
--md-list-item-leading-space: 0;
|
||||
--md-list-item-trailing-space: 0;
|
||||
}
|
||||
ha-button.danger {
|
||||
--mdc-theme-primary: var(--error-color);
|
||||
}
|
||||
.encryption-key {
|
||||
border: 1px solid var(--divider-color);
|
||||
background-color: var(--primary-background-color);
|
||||
|
@@ -112,7 +112,7 @@ class DialogDownloadDecryptedBackup extends LitElement implements HassDialog {
|
||||
: nothing}
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<ha-button @click=${this._cancel}>
|
||||
<ha-button appearance="plain" @click=${this._cancel}>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
|
||||
|
@@ -2,10 +2,10 @@ import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-button";
|
||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import "../../../../components/ha-alert";
|
||||
import type {
|
||||
HaFormSchema,
|
||||
SchemaUnion,
|
||||
@@ -91,6 +91,7 @@ class LocalBackupLocationDialog extends LitElement {
|
||||
</ha-alert>
|
||||
<ha-button
|
||||
slot="secondaryAction"
|
||||
appearance="plain"
|
||||
@click=${this.closeDialog}
|
||||
dialogInitialFocus
|
||||
>
|
||||
|
@@ -227,7 +227,7 @@ class DialogRestoreBackup extends LitElement implements HassDialog {
|
||||
|
||||
private _renderConfirmActions() {
|
||||
return html`
|
||||
<ha-button @click=${this.closeDialog}>
|
||||
<ha-button appearance="plain" @click=${this.closeDialog}>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button @click=${this._restoreBackup} variant="danger">
|
||||
|
@@ -151,8 +151,13 @@ class DialogSetBackupEncryptionKey extends LitElement implements HassDialog {
|
||||
"ui.panel.config.backup.encryption_key.download_emergency_kit_description"
|
||||
)}
|
||||
</span>
|
||||
<ha-button slot="end" @click=${this._download}>
|
||||
<ha-svg-icon .path=${mdiDownload} slot="icon"></ha-svg-icon>
|
||||
<ha-button
|
||||
size="small"
|
||||
appearance="plain"
|
||||
slot="end"
|
||||
@click=${this._download}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.encryption_key.download_emergency_kit_action"
|
||||
)}
|
||||
|
@@ -87,8 +87,13 @@ class DialogShowBackupEncryptionKey extends LitElement implements HassDialog {
|
||||
"ui.panel.config.backup.encryption_key.download_emergency_kit_description"
|
||||
)}
|
||||
</span>
|
||||
<ha-button slot="end" @click=${this._download}>
|
||||
<ha-svg-icon .path=${mdiDownload} slot="icon"></ha-svg-icon>
|
||||
<ha-button
|
||||
size="small"
|
||||
appearance="plain"
|
||||
slot="end"
|
||||
@click=${this._download}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.encryption_key.download_emergency_kit_action"
|
||||
)}
|
||||
|
@@ -113,7 +113,10 @@ export class DialogUploadBackup
|
||||
></ha-file-upload>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<ha-button @click=${this.closeDialog} .disabled=${this._uploading}
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this.closeDialog}
|
||||
.disabled=${this._uploading}
|
||||
>${this.hass.localize("ui.common.cancel")}</ha-button
|
||||
>
|
||||
<ha-button
|
||||
|
@@ -417,7 +417,11 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) {
|
||||
<div slot="selection-bar">
|
||||
${!this.narrow
|
||||
? html`
|
||||
<ha-button @click=${this._deleteSelected} class="warning">
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@click=${this._deleteSelected}
|
||||
variant="danger"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.backups.delete_selected"
|
||||
)}
|
||||
|
@@ -158,18 +158,18 @@ class HaConfigBackupDetails extends LitElement {
|
||||
"ui.panel.config.backup.location.encryption.location_encrypted_cloud_description"
|
||||
)}
|
||||
</span>
|
||||
<a
|
||||
<ha-button
|
||||
href="https://www.nabucasa.com/config/backups/"
|
||||
target="_blank"
|
||||
slot="end"
|
||||
rel="noreferrer noopener"
|
||||
appearance="plain"
|
||||
size="small"
|
||||
>
|
||||
<ha-button>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.location.encryption.location_encrypted_cloud_learn_more"
|
||||
)}
|
||||
</ha-button>
|
||||
</a>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.location.encryption.location_encrypted_cloud_learn_more"
|
||||
)}
|
||||
</ha-button>
|
||||
</ha-md-list-item>
|
||||
`
|
||||
: encrypted
|
||||
|
@@ -187,7 +187,11 @@ export class CloudRemotePref extends LitElement {
|
||||
)
|
||||
: nothing}</span
|
||||
>
|
||||
<ha-button @click=${this._openCertInfo}>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
@click=${this._openCertInfo}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.remote.more_info"
|
||||
)}
|
||||
|
@@ -126,7 +126,7 @@ export class CloudTTSPref extends LitElement {
|
||||
`}
|
||||
</div>
|
||||
<div class="flex"></div>
|
||||
<ha-button @click=${this._openTryDialog}>
|
||||
<ha-button appearance="plain" @click=${this._openTryDialog}>
|
||||
${this.hass.localize("ui.panel.config.cloud.account.tts.try")}
|
||||
</ha-button>
|
||||
</div>
|
||||
|
@@ -77,7 +77,9 @@ export class DialogSupportPackage extends LitElement {
|
||||
</ha-alert>
|
||||
<hr />
|
||||
<div class="actions">
|
||||
<ha-button @click=${this.closeDialog}>Close</ha-button>
|
||||
<ha-button appearance="plain" @click=${this.closeDialog}
|
||||
>Close</ha-button
|
||||
>
|
||||
<ha-button @click=${this._download}>Download</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,19 +2,20 @@ import "@material/mwc-button";
|
||||
import { mdiHelpCircle, mdiStarFourPoints } from "@mdi/js";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-settings-row";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import type { HaProgressButton } from "../../../components/buttons/ha-progress-button";
|
||||
import "../../../components/entity/ha-entity-picker";
|
||||
import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { brandsUrl } from "../../../util/brands-url";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-settings-row";
|
||||
import {
|
||||
fetchAITaskPreferences,
|
||||
saveAITaskPreferences,
|
||||
type AITaskPreferences,
|
||||
} from "../../../data/ai_task";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { brandsUrl } from "../../../util/brands-url";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
|
||||
@customElement("ai-task-pref")
|
||||
export class AITaskPref extends LitElement {
|
||||
@@ -24,6 +25,8 @@ export class AITaskPref extends LitElement {
|
||||
|
||||
@state() private _prefs?: AITaskPreferences;
|
||||
|
||||
private _gen_data_entity_id?: string | null;
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
if (!this.hass || !isComponentLoaded(this.hass, "ai_task")) {
|
||||
@@ -86,30 +89,51 @@ export class AITaskPref extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.disabled=${this._prefs === undefined &&
|
||||
isComponentLoaded(this.hass, "ai_task")}
|
||||
.value=${this._prefs?.gen_data_entity_id}
|
||||
.value=${this._gen_data_entity_id ||
|
||||
this._prefs?.gen_data_entity_id}
|
||||
.includeDomains=${["ai_task"]}
|
||||
@value-changed=${this._handlePrefChange}
|
||||
></ha-entity-picker>
|
||||
</ha-settings-row>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-progress-button @click=${this._update}>
|
||||
${this.hass!.localize("ui.common.save")}
|
||||
</ha-progress-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _handlePrefChange(
|
||||
ev: CustomEvent<{ value: string | undefined }>
|
||||
) {
|
||||
private _handlePrefChange(ev: CustomEvent<{ value: string | undefined }>) {
|
||||
const input = ev.target as HaEntityPicker;
|
||||
const key = input.getAttribute("data-name") as keyof AITaskPreferences;
|
||||
const entityId = ev.detail.value || null;
|
||||
const key = input.dataset.name as keyof AITaskPreferences;
|
||||
const value = ev.detail.value || null;
|
||||
this[`_${key}`] = value;
|
||||
}
|
||||
|
||||
private async _update(ev) {
|
||||
const button = ev.target as HaProgressButton;
|
||||
if (button.progress) {
|
||||
return;
|
||||
}
|
||||
button.progress = true;
|
||||
|
||||
const oldPrefs = this._prefs;
|
||||
this._prefs = { ...this._prefs!, [key]: entityId };
|
||||
const update: Partial<AITaskPreferences> = {
|
||||
gen_data_entity_id: this._gen_data_entity_id,
|
||||
};
|
||||
this._prefs = { ...this._prefs!, ...update };
|
||||
try {
|
||||
this._prefs = await saveAITaskPreferences(this.hass, {
|
||||
[key]: entityId,
|
||||
...update,
|
||||
});
|
||||
button.actionSuccess();
|
||||
} catch (_err: any) {
|
||||
button.actionError();
|
||||
this._prefs = oldPrefs;
|
||||
} finally {
|
||||
button.progress = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +169,9 @@ export class AITaskPref extends LitElement {
|
||||
direction: var(--direction);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.card-actions {
|
||||
text-align: right;
|
||||
}
|
||||
ha-entity-picker {
|
||||
flex: 1;
|
||||
margin-left: 16px;
|
||||
|
@@ -1442,11 +1442,11 @@ export class HaConfigDevicePage extends LitElement {
|
||||
}
|
||||
|
||||
private async _signUrl(ev) {
|
||||
const anchor = ev.currentTarget.closest("a");
|
||||
const signedUrl = await getSignedPath(
|
||||
this.hass,
|
||||
anchor.getAttribute("href")
|
||||
);
|
||||
const a = ev.currentTarget.getAttribute("href")
|
||||
? ev.currentTarget
|
||||
: ev.currentTarget.closest("a");
|
||||
|
||||
const signedUrl = await getSignedPath(this.hass, a.getAttribute("href"));
|
||||
fileDownload(signedUrl.path);
|
||||
}
|
||||
|
||||
|
@@ -202,7 +202,7 @@ export class EntitySettingsHelperTab extends LitElement {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 24px 24px 24px;
|
||||
padding: 16px;
|
||||
background-color: var(--mdc-theme-surface, #fff);
|
||||
}
|
||||
.error {
|
||||
|
@@ -249,9 +249,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
||||
.buttons {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
padding: 8px;
|
||||
padding: 16px;
|
||||
justify-content: space-between;
|
||||
padding-bottom: max(var(--safe-area-inset-bottom), 8px);
|
||||
padding-bottom: max(var(--safe-area-inset-bottom), 16px);
|
||||
background-color: var(--mdc-theme-surface, #fff);
|
||||
border-top: 1px solid var(--divider-color);
|
||||
position: sticky;
|
||||
|
@@ -92,8 +92,9 @@ class DialogScheduleBlockInfo extends LitElement {
|
||||
</div>
|
||||
<ha-button
|
||||
slot="secondaryAction"
|
||||
class="warning"
|
||||
@click=${this._deleteBlock}
|
||||
appearance="plain"
|
||||
variant="danger"
|
||||
>
|
||||
${this.hass!.localize("ui.common.delete")}
|
||||
</ha-button>
|
||||
|
@@ -145,7 +145,7 @@ class HaInputSelectForm extends LitElement {
|
||||
)}
|
||||
@keydown=${this._handleKeyAdd}
|
||||
></ha-textfield>
|
||||
<ha-button @click=${this._addOption}
|
||||
<ha-button size="small" appearance="plain" @click=${this._addOption}
|
||||
>${this.hass!.localize(
|
||||
"ui.dialogs.helper_settings.input_select.add"
|
||||
)}</ha-button
|
||||
|
@@ -207,7 +207,7 @@ class HaConfigEntryRow extends LitElement {
|
||||
: nothing}
|
||||
</div>
|
||||
${item.disabled_by === "user"
|
||||
? html`<ha-button unelevated slot="end" @click=${this._handleEnable}>
|
||||
? html`<ha-button slot="end" @click=${this._handleEnable}>
|
||||
${this.hass.localize("ui.common.enable")}
|
||||
</ha-button>`
|
||||
: configPanel &&
|
||||
|
@@ -17,6 +17,10 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import {
|
||||
PROTOCOL_INTEGRATIONS,
|
||||
protocolIntegrationPicked,
|
||||
} from "../../../common/integrations/protocolIntegrationPicked";
|
||||
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
||||
import { nextRender } from "../../../common/util/render-status";
|
||||
import "../../../components/ha-button";
|
||||
@@ -64,10 +68,6 @@ import "./ha-config-entry-row";
|
||||
import type { DataEntryFlowProgressExtended } from "./ha-config-integrations";
|
||||
import { showAddIntegrationDialog } from "./show-add-integration-dialog";
|
||||
import { showPickConfigEntryDialog } from "./show-pick-config-entry-dialog";
|
||||
import {
|
||||
PROTOCOL_INTEGRATIONS,
|
||||
protocolIntegrationPicked,
|
||||
} from "../../../common/integrations/protocolIntegrationPicked";
|
||||
|
||||
export const renderConfigEntryError = (
|
||||
hass: HomeAssistant,
|
||||
|
@@ -45,10 +45,9 @@ export class HaDisabledConfigEntryCard extends LitElement {
|
||||
>
|
||||
<ha-icon-button .path=${mdiCog}></ha-icon-button>
|
||||
</a>
|
||||
<ha-button
|
||||
@click=${this._handleEnable}
|
||||
.label=${this.hass.localize("ui.common.enable")}
|
||||
></ha-button>
|
||||
<ha-button @click=${this._handleEnable} appearance="filled">
|
||||
${this.hass.localize("ui.common.enable")}
|
||||
</ha-button>
|
||||
</ha-integration-action-card>
|
||||
`;
|
||||
}
|
||||
|
@@ -34,12 +34,11 @@ export class HaIgnoredConfigEntryCard extends LitElement {
|
||||
this.entry.localized_domain_name
|
||||
: this.entry.title}
|
||||
>
|
||||
<ha-button
|
||||
@click=${this._removeIgnoredIntegration}
|
||||
.label=${this.hass.localize(
|
||||
<ha-button appearance="plain" @click=${this._removeIgnoredIntegration}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.ignore.stop_ignore"
|
||||
)}
|
||||
></ha-button>
|
||||
</ha-button>
|
||||
</ha-integration-action-card>
|
||||
`;
|
||||
}
|
||||
|
@@ -112,7 +112,8 @@ export class HaIntegrationCard extends LitElement {
|
||||
return html`
|
||||
<div class="card-actions">
|
||||
${devices.length > 0
|
||||
? html`<a
|
||||
? html`<ha-button
|
||||
appearance="plain"
|
||||
href=${devices.length === 1 &&
|
||||
// Always link to device page for protocol integrations to show Add Device button
|
||||
// @ts-expect-error
|
||||
@@ -120,40 +121,36 @@ export class HaIntegrationCard extends LitElement {
|
||||
? `/config/devices/device/${devices[0].id}`
|
||||
: `/config/devices/dashboard?historyBack=1&domain=${this.domain}`}
|
||||
>
|
||||
<ha-button appearance="plain">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.integrations.config_entry.${
|
||||
services ? "services" : "devices"
|
||||
}`,
|
||||
{ count: devices.length }
|
||||
)}
|
||||
</ha-button>
|
||||
</a>`
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.integrations.config_entry.${
|
||||
services ? "services" : "devices"
|
||||
}`,
|
||||
{ count: devices.length }
|
||||
)}
|
||||
</ha-button>`
|
||||
: entitiesCount > 0
|
||||
? html`<a
|
||||
? html`<ha-button
|
||||
appearance="plain"
|
||||
href=${`/config/entities?historyBack=1&domain=${this.domain}`}
|
||||
>
|
||||
<ha-button appearance="plain">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.integrations.config_entry.entities`,
|
||||
{ count: entitiesCount }
|
||||
)}
|
||||
</ha-button>
|
||||
</a>`
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.integrations.config_entry.entities`,
|
||||
{ count: entitiesCount }
|
||||
)}
|
||||
</ha-button>`
|
||||
: this.items.find((itm) => itm.source !== "yaml")
|
||||
? html`<a
|
||||
? html`<ha-button
|
||||
appearance="plain"
|
||||
href=${`/config/integrations/integration/${this.domain}`}
|
||||
>
|
||||
<ha-button appearance="plain">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.integrations.config_entry.entries`,
|
||||
{
|
||||
count: this.items.filter((itm) => itm.source !== "yaml")
|
||||
.length,
|
||||
}
|
||||
)}
|
||||
</ha-button>
|
||||
</a>`
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.integrations.config_entry.entries`,
|
||||
{
|
||||
count: this.items.filter((itm) => itm.source !== "yaml")
|
||||
.length,
|
||||
}
|
||||
)}
|
||||
</ha-button>`
|
||||
: html`<div class="spacer"></div>`}
|
||||
<div class="icons">
|
||||
${this.manifest && !this.manifest.is_built_in
|
||||
|
@@ -26,7 +26,6 @@ import {
|
||||
} from "../../../../../data/bluetooth";
|
||||
import type { DeviceRegistryEntry } from "../../../../../data/device_registry";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import { colorVariables } from "../../../../../resources/theme/color/color.globals";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import { bluetoothAdvertisementMonitorTabs } from "./bluetooth-advertisement-monitor";
|
||||
|
||||
@@ -131,33 +130,34 @@ export class BluetoothNetworkVisualization extends LitElement {
|
||||
data: BluetoothDeviceData[],
|
||||
scanners: BluetoothScannersDetails
|
||||
): NetworkData => {
|
||||
const style = getComputedStyle(this);
|
||||
const categories = [
|
||||
{
|
||||
name: CORE_SOURCE_LABEL,
|
||||
symbol: "roundRect",
|
||||
itemStyle: {
|
||||
color: colorVariables["primary-color"],
|
||||
color: style.getPropertyValue("--primary-color"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: this.hass.localize("ui.panel.config.bluetooth.scanners"),
|
||||
symbol: "circle",
|
||||
itemStyle: {
|
||||
color: colorVariables["cyan-color"],
|
||||
color: style.getPropertyValue("--cyan-color"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: this.hass.localize("ui.panel.config.bluetooth.known_devices"),
|
||||
symbol: "circle",
|
||||
itemStyle: {
|
||||
color: colorVariables["teal-color"],
|
||||
color: style.getPropertyValue("--teal-color"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: this.hass.localize("ui.panel.config.bluetooth.unknown_devices"),
|
||||
symbol: "circle",
|
||||
itemStyle: {
|
||||
color: colorVariables["disabled-color"],
|
||||
color: style.getPropertyValue("--disabled-color"),
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -192,7 +192,7 @@ export class BluetoothNetworkVisualization extends LitElement {
|
||||
symbol: "none",
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
color: colorVariables["primary-color"],
|
||||
color: style.getPropertyValue("--primary-color"),
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -206,7 +206,7 @@ export class BluetoothNetworkVisualization extends LitElement {
|
||||
symbol: "none",
|
||||
lineStyle: {
|
||||
width: this._getLineWidth(node.rssi),
|
||||
color: colorVariables["primary-color"],
|
||||
color: style.getPropertyValue("--primary-color"),
|
||||
},
|
||||
});
|
||||
return;
|
||||
@@ -227,8 +227,8 @@ export class BluetoothNetworkVisualization extends LitElement {
|
||||
lineStyle: {
|
||||
width: this._getLineWidth(node.rssi),
|
||||
color: device
|
||||
? colorVariables["primary-color"]
|
||||
: colorVariables["disabled-color"],
|
||||
? style.getPropertyValue("--primary-color")
|
||||
: style.getPropertyValue("--disabled-color"),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@@ -14,6 +14,9 @@ import "../../../../../layouts/hass-subpage";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "./mqtt-subscribe-card";
|
||||
import type { Action } from "../../../../../data/script";
|
||||
import { callExecuteScript } from "../../../../../data/service";
|
||||
import { showToast } from "../../../../../util/toast";
|
||||
|
||||
const qosLevel = ["0", "1", "2"];
|
||||
|
||||
@@ -55,14 +58,6 @@ export class MQTTConfigPanel extends LitElement {
|
||||
})
|
||||
private _retain = false;
|
||||
|
||||
@state()
|
||||
@storage({
|
||||
key: "panel-dev-mqtt-allow-template-ls",
|
||||
state: true,
|
||||
subscribe: false,
|
||||
})
|
||||
private _allowTemplate = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<hass-subpage .narrow=${this.narrow} .hass=${this.hass}>
|
||||
@@ -108,25 +103,7 @@ export class MQTTConfigPanel extends LitElement {
|
||||
></ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
<p>
|
||||
<ha-formfield
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.mqtt.allow_template"
|
||||
)}
|
||||
>
|
||||
<ha-switch
|
||||
@change=${this._handleAllowTemplate}
|
||||
.checked=${this._allowTemplate}
|
||||
></ha-switch>
|
||||
</ha-formfield>
|
||||
</p>
|
||||
<p>
|
||||
${this._allowTemplate
|
||||
? this.hass.localize("ui.panel.config.mqtt.payload")
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.mqtt.payload_no_template"
|
||||
)}
|
||||
</p>
|
||||
<p>${this.hass.localize("ui.panel.config.mqtt.payload")}</p>
|
||||
<ha-code-editor
|
||||
mode="jinja2"
|
||||
autocomplete-entities
|
||||
@@ -171,21 +148,28 @@ export class MQTTConfigPanel extends LitElement {
|
||||
this._retain = (ev.target! as any).checked;
|
||||
}
|
||||
|
||||
private _handleAllowTemplate(ev: CustomEvent) {
|
||||
this._allowTemplate = (ev.target! as any).checked;
|
||||
}
|
||||
|
||||
private _publish(): void {
|
||||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
this.hass.callService("mqtt", "publish", {
|
||||
topic: this._topic,
|
||||
payload: !this._allowTemplate ? this._payload : undefined,
|
||||
payload_template: this._allowTemplate ? this._payload : undefined,
|
||||
qos: parseInt(this._qos),
|
||||
retain: this._retain,
|
||||
});
|
||||
|
||||
const script: Action[] = [
|
||||
{
|
||||
action: "mqtt.publish",
|
||||
data: {
|
||||
topic: this._topic,
|
||||
payload: this._payload,
|
||||
qos: parseInt(this._qos),
|
||||
retain: this._retain,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
callExecuteScript(this.hass, script).catch((err) =>
|
||||
showToast(this, {
|
||||
message: err.message,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private async _openOptionFlow() {
|
||||
|
@@ -105,7 +105,6 @@ class MqttSubscribeCard extends LitElement {
|
||||
size="small"
|
||||
.disabled=${this._topic === ""}
|
||||
@click=${this._handleSubmit}
|
||||
type="submit"
|
||||
>
|
||||
${this._subscribed
|
||||
? this.hass.localize("ui.panel.config.mqtt.stop_listening")
|
||||
|
@@ -114,7 +114,11 @@ class DialogSSDPDiscoveryInfo extends LitElement implements HassDialog {
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ha-button slot="secondaryAction" @click=${this._copyToClipboard}>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="secondaryAction"
|
||||
@click=${this._copyToClipboard}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.ssdp.copy_to_clipboard")}
|
||||
</ha-button>
|
||||
</ha-dialog>
|
||||
|
@@ -3,16 +3,16 @@ import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import type { HassDialog } from "../../../../../dialogs/make-dialog-manager";
|
||||
import { changeZHANetworkChannel } from "../../../../../data/zha";
|
||||
import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import "../../../../../components/ha-alert";
|
||||
import "../../../../../components/ha-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import "../../../../../components/ha-select";
|
||||
import { changeZHANetworkChannel } from "../../../../../data/zha";
|
||||
import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||
import type { HassDialog } from "../../../../../dialogs/make-dialog-manager";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import type { ZHAChangeChannelDialogParams } from "./show-dialog-zha-change-channel";
|
||||
|
||||
const VALID_CHANNELS = [
|
||||
@@ -128,6 +128,7 @@ class DialogZHAChangeChannel extends LitElement implements HassDialog {
|
||||
|
||||
<ha-button
|
||||
slot="secondaryAction"
|
||||
appearance="plain"
|
||||
@click=${this.closeDialog}
|
||||
.disabled=${this._migrationInProgress}
|
||||
>${this.hass.localize("ui.common.cancel")}</ha-button
|
||||
|
@@ -160,7 +160,11 @@ class DialogZHAReconfigureDevice extends LitElement {
|
||||
<ha-button slot="primaryAction" @click=${this.closeDialog}>
|
||||
${this.hass.localize("ui.common.close")}
|
||||
</ha-button>
|
||||
<ha-button slot="secondaryAction" @click=${this._toggleDetails}>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="secondaryAction"
|
||||
@click=${this._toggleDetails}
|
||||
>
|
||||
${this._showDetails
|
||||
? this.hass.localize(
|
||||
`ui.dialogs.zha_reconfigure_device.button_hide`
|
||||
@@ -189,7 +193,11 @@ class DialogZHAReconfigureDevice extends LitElement {
|
||||
<ha-button slot="primaryAction" @click=${this.closeDialog}>
|
||||
${this.hass.localize("ui.common.close")}
|
||||
</ha-button>
|
||||
<ha-button slot="secondaryAction" @click=${this._toggleDetails}>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="secondaryAction"
|
||||
@click=${this._toggleDetails}
|
||||
>
|
||||
${this._showDetails
|
||||
? this.hass.localize(
|
||||
`ui.dialogs.zha_reconfigure_device.button_hide`
|
||||
|
@@ -176,7 +176,7 @@ export class ZHAAddGroupPage extends LitElement {
|
||||
}
|
||||
.buttons {
|
||||
align-items: flex-end;
|
||||
padding: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
.buttons .warning {
|
||||
--mdc-theme-primary: var(--error-color);
|
||||
|
@@ -305,7 +305,7 @@ export class ZHAGroupPage extends LitElement {
|
||||
}
|
||||
.buttons {
|
||||
align-items: flex-end;
|
||||
padding: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
.buttons .warning {
|
||||
--mdc-theme-primary: var(--error-color);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user