Compare commits

..

No commits in common. "dev" and "20250730.0" have entirely different histories.

155 changed files with 1523 additions and 1820 deletions

View File

@ -310,11 +310,7 @@ export class DialogMyFeature
.heading=${createCloseHeading(this.hass, this._params.title)}
>
<!-- Dialog content -->
<ha-button
appearance="plain"
@click=${this.closeDialog}
slot="secondaryAction"
>
<ha-button @click=${this.closeDialog} slot="secondaryAction">
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button @click=${this._submit} slot="primaryAction">

View File

@ -74,7 +74,7 @@ jobs:
echo "home-assistant-frontend==$version" > ./requirements.txt
- name: Build wheels
uses: home-assistant/wheels@2025.07.0
uses: home-assistant/wheels@2025.03.0
with:
abi: cp313
tag: musllinux_1_2

View File

@ -1,8 +0,0 @@
# People marked here will be automatically requested for a review
# when the code that they own is touched.
# https://github.com/blog/2392-introducing-code-owners
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
# Part of the frontend that mobile developper should review
src/external_app/ @bgoncal @TimoPtr
test/external_app/ @bgoncal @TimoPtr

View File

@ -89,14 +89,11 @@ export class HADemoCard extends LitElement implements LovelaceCard {
)}
</div>
<div class="actions small-hidden">
<ha-button
appearance="plain"
size="small"
href="https://www.home-assistant.io"
target="_blank"
>
<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>
</div>
</ha-card>
`;

View File

@ -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">Undo</ha-button>
<ha-button slot="action" label="Undo"></ha-button>
</ha-alert>
```html
<ha-alert alert-type="success">
This is a success alert — check it out!
<ha-button slot="action">Undo</ha-button>
<ha-button slot="action" label="Undo"></ha-button>
</ha-alert>
```

View File

@ -78,13 +78,21 @@ 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">restart</ha-button>`,
actionSlot: html`<ha-button
size="small"
slot="action"
label="restart"
></ha-button>`,
},
{
title: "Unsaved data",
description: "You have unsaved data",
type: "warning",
actionSlot: html`<ha-button size="small" slot="action">save</ha-button>`,
actionSlot: html`<ha-button
size="small"
slot="action"
label="save"
></ha-button>`,
},
{
title: "Slotted icon",
@ -108,7 +116,7 @@ const alerts: {
title: "Slotted action",
description: "Alert with slotted action",
type: "info",
actionSlot: html`<ha-button slot="action">action</ha-button>`,
actionSlot: html`<ha-button slot="action" label="action"></ha-button>`,
},
{
description: "Dismissable information (RTL)",
@ -120,7 +128,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">restart</ha-button>`,
actionSlot: html`<ha-button slot="action" label="restart"></ha-button>`,
rtl: true,
},
{

View File

@ -54,14 +54,13 @@ 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. |
| 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. |
| hideContent | Boolean | false | Hides the button content (for overlays) |
**CSS Custom Properties**
- `--ha-button-height` - Height of the button.
- `--ha-button-border-radius` - Border radius of the button. Defaults to `var(--ha-border-radius-pill)`.
- `--ha-button-radius` - Border radius of the button. Defaults to `var(--wa-border-radius-pill)`.

View File

@ -21,8 +21,8 @@ import type { HomeAssistant } from "../../../../src/types";
import type { HassioDatatiskDialogParams } from "./show-dialog-hassio-datadisk";
const calculateMoveTime = memoizeOne((supervisor: Supervisor): number => {
// Assume a speed of 30 MB/s.
const moveTime = (supervisor.host.disk_used * 1000) / 60 / 30;
const speed = supervisor.host.disk_life_time !== "" ? 30 : 10;
const moveTime = (supervisor.host.disk_used * 1000) / 60 / speed;
const rebootTime = (supervisor.host.startup_time * 4) / 60;
return Math.ceil((moveTime + rebootTime) / 10) * 10;
});

View File

@ -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: 16px;
padding-bottom: max(var(--safe-area-inset-bottom), 16px);
padding: 8px;
padding-bottom: max(var(--safe-area-inset-bottom), 8px);
background-color: var(--mdc-theme-surface, #fff);
}
.warning {

View File

@ -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-settings-row";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-svg-icon";
import "../../../../src/components/ha-settings-row";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import {
addHassioDockerRegistry,

View File

@ -7,14 +7,10 @@ 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,
@ -28,6 +24,10 @@ 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 {

View File

@ -143,12 +143,16 @@ class HassioHostInfo extends LitElement {
: ""}
</div>
<div>
${this.supervisor.host.disk_life_time !== null
${this.supervisor.host.disk_life_time !== "" &&
this.supervisor.host.disk_life_time >= 10
? html` <ha-settings-row>
<span slot="heading">
${this.supervisor.localize("system.host.lifetime_used")}
${this.supervisor.localize(
"system.host.emmc_lifetime_used"
)}
</span>
<span slot="description">
${this.supervisor.host.disk_life_time - 10} % -
${this.supervisor.host.disk_life_time} %
</span>
</ha-settings-row>`

View File

@ -208,16 +208,14 @@ class UpdateAvailableCard extends LitElement {
<div class="card-actions">
${changelog
? html`
<a href=${changelog} target="_blank" rel="noreferrer">
<ha-button
href=${changelog}
target="_blank"
rel="noreferrer"
appearance="plain"
>
${this.supervisor.localize(
.label=${this.supervisor.localize(
"update_available.open_release_notes"
)}
>
</ha-button>
</a>
`
: nothing}
<span></span>

View File

@ -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 { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../src/common/dom/fire_event";
import { customElement, property, query, state } from "lit/decorators";
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 { fileDownload } from "../../../src/util/file_download";
import "../../../src/components/ha-ansi-to-html";
import "../../../src/components/ha-alert";
import type { HaAnsiToHtml } from "../../../src/components/ha-ansi-to-html";
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,13 +108,14 @@ class LandingPageLogs extends LitElement {
!this._scrolledToBottomController.value) ||
false,
})}"
size="small"
appearance="filled"
@click=${this._scrollToBottom}
>
<ha-svg-icon .path=${mdiArrowCollapseDown} slot="start"></ha-svg-icon>
<ha-svg-icon .path=${mdiArrowCollapseDown} slot="icon"></ha-svg-icon>
${this.localize("logs.scroll_down_button")}
<ha-svg-icon .path=${mdiArrowCollapseDown} slot="end"></ha-svg-icon>
<ha-svg-icon
.path=${mdiArrowCollapseDown}
slot="trailingIcon"
></ha-svg-icon>
</ha-button>
`;
}
@ -311,14 +312,21 @@ class LandingPageLogs extends LitElement {
}
.new-logs-indicator {
--mdc-theme-primary: var(--text-primary-color);
overflow: hidden;
position: absolute;
bottom: 4px;
left: 4px;
bottom: 0;
left: 0;
right: 0;
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 {

View File

@ -108,7 +108,7 @@
"deep-clone-simple": "1.1.1",
"deep-freeze": "0.0.1",
"dialog-polyfill": "0.5.6",
"echarts": "6.0.0",
"echarts": "5.6.0",
"element-internals-polyfill": "3.0.2",
"fuse.js": "7.1.0",
"google-timezones-json": "1.2.0",
@ -124,7 +124,7 @@
"lit": "3.3.1",
"lit-html": "3.3.1",
"luxon": "3.7.1",
"marked": "16.1.2",
"marked": "16.1.1",
"memoize-one": "6.0.0",
"node-vibrant": "4.0.3",
"object-hash": "3.0.0",
@ -160,8 +160,8 @@
"@octokit/plugin-retry": "8.0.1",
"@octokit/rest": "22.0.0",
"@rsdoctor/rspack-plugin": "1.1.10",
"@rspack/cli": "1.4.11",
"@rspack/core": "1.4.11",
"@rspack/cli": "1.4.10",
"@rspack/core": "1.4.10",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.22",
"@types/chromecast-caf-sender": "1.0.11",
@ -173,7 +173,7 @@
"@types/leaflet-draw": "1.0.12",
"@types/leaflet.markercluster": "1.5.5",
"@types/lodash.merge": "4.6.9",
"@types/luxon": "3.7.1",
"@types/luxon": "3.6.2",
"@types/mocha": "10.0.10",
"@types/qrcode": "1.5.5",
"@types/sortablejs": "1.15.8",
@ -205,7 +205,7 @@
"husky": "9.1.7",
"jsdom": "26.1.0",
"jszip": "3.10.1",
"lint-staged": "16.1.4",
"lint-staged": "16.1.2",
"lit-analyzer": "2.0.3",
"lodash.merge": "4.6.2",
"lodash.template": "4.5.0",

View File

@ -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.reasons.${string}`
| `ui.dialogs.unsupported.reasons.${string}`
| `ui.dialogs.unhealthy.reason.${string}`
| `ui.dialogs.unsupported.reason.${string}`
| `ui.panel.config.${string}.${"caption" | "description"}`
| `ui.panel.config.dashboard.${string}`
| `ui.panel.config.zha.${string}`

View File

@ -117,7 +117,7 @@ export class HaProgressButton extends LitElement {
}
ha-svg-icon {
color: var(--white-color);
color: var(--white);
}
`;
}

View File

@ -29,6 +29,7 @@ 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;
@ -167,16 +168,14 @@ export class HaChartBase extends LitElement {
}
protected firstUpdated() {
if (this.isConnected) {
this._setupChart();
}
}
public willUpdate(changedProps: PropertyValues): void {
if (!this.chart) {
return;
}
if (changedProps.has("_themes") && this.hasUpdated) {
if (changedProps.has("_themes")) {
this._setupChart();
return;
}
@ -343,8 +342,7 @@ export class HaChartBase extends LitElement {
echarts.use(this.extraComponents);
}
const style = getComputedStyle(this);
echarts.registerTheme("custom", this._createTheme(style));
echarts.registerTheme("custom", this._createTheme());
this.chart = echarts.init(container, "custom");
this.chart.on("datazoom", (e: any) => {
@ -396,7 +394,7 @@ export class HaChartBase extends LitElement {
...axis.axisPointer,
status: "show",
handle: {
color: style.getPropertyValue("primary-color"),
color: colorVariables["primary-color"],
margin: 0,
size: 20,
...axis.axisPointer?.handle,
@ -570,7 +568,8 @@ export class HaChartBase extends LitElement {
return options;
}
private _createTheme(style: CSSStyleDeclaration) {
private _createTheme() {
const style = getComputedStyle(this);
return {
color: getAllGraphColors(style),
backgroundColor: "transparent",
@ -803,7 +802,6 @@ export class HaChartBase extends LitElement {
};
}
}
const replaceMerge = options.series ? ["series"] : [];
this.chart.setOption(options, { replaceMerge });
}

View File

@ -105,14 +105,12 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
}
protected render() {
if (!GraphChart || !this.data.nodes?.length) {
if (!GraphChart) {
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(
@ -245,7 +243,6 @@ 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]) {

View File

@ -27,13 +27,12 @@ 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-border-radius - The border radius of the button. defaults to `var(--ha-border-radius-pill)`.
* @cssprop --ha-button-radius - The border radius of the button. defaults to `var(--wa-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} loading - shows a loading indicator instead of the buttons label and disable buttons click.
* @attr {boolean} disabled - Disables the button and prevents user interaction.
* @attr {boolean} hideContent - Hides the button content (for overlays).
*/
@customElement("ha-button")
export class HaButton extends Button {
@ -55,9 +54,10 @@ 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-border-radius,
var(--ha-border-radius-pill)
--ha-button-radius,
var(--wa-border-radius-pill)
);
--wa-form-control-height: var(
@ -66,7 +66,6 @@ export class HaButton extends Button {
);
font-size: var(--ha-font-size-m);
line-height: 1;
}
:host([size="small"]) .button {
@ -78,102 +77,61 @@ export class HaButton extends Button {
}
:host([variant="brand"]) {
--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
);
--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);
}
:host([variant="neutral"]) {
--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
);
--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);
}
:host([variant="success"]) {
--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
);
--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);
}
:host([variant="warning"]) {
--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
);
--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);
}
:host([variant="danger"]) {
--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
);
--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);
}
:host([appearance~="plain"]) .button {
color: var(--wa-color-on-normal);
background-color: transparent;
}
:host([appearance~="plain"]) .button.disabled {
background-color: transparent;
color: var(--ha-color-on-disabled-quiet);
background-color: var(--transparent-none);
color: var(--color-on-disabled-quiet);
}
:host([appearance~="outlined"]) .button.disabled {
background-color: transparent;
color: var(--ha-color-on-disabled-quiet);
background-color: var(--transparent-none);
color: var(--color-on-disabled-quiet);
}
@media (hover: hover) {
:host([appearance~="filled"])
.button:not(.disabled):not(.loading):hover {
background-color: var(--button-color-fill-normal-hover);
background-color: var(--color-fill-normal-hover);
}
:host([appearance~="accent"])
.button:not(.disabled):not(.loading):hover {
background-color: var(--button-color-fill-loud-hover);
background-color: var(--color-fill-loud-hover);
}
:host([appearance~="plain"])
.button:not(.disabled):not(.loading):hover {
@ -182,11 +140,11 @@ export class HaButton extends Button {
}
:host([appearance~="filled"])
.button:not(.disabled):not(.loading):active {
background-color: var(--button-color-fill-normal-active);
background-color: var(--color-fill-normal-active);
}
:host([appearance~="filled"]) .button.disabled {
background-color: var(--ha-color-fill-disabled-normal-resting);
color: var(--ha-color-on-disabled-normal);
background-color: var(--color-fill-disabled-normal-resting);
color: var(--color-on-disabled-normal);
}
:host([appearance~="accent"]) .button {
@ -197,11 +155,11 @@ export class HaButton extends Button {
}
:host([appearance~="accent"])
.button:not(.disabled):not(.loading):active {
background-color: var(--button-color-fill-loud-active);
background-color: var(--color-fill-loud-active);
}
:host([appearance~="accent"]) .button.disabled {
background-color: var(--ha-color-fill-disabled-loud-resting);
color: var(--ha-color-on-disabled-loud);
background-color: var(--color-fill-disabled-loud-resting);
color: var(--color-on-disabled-loud);
}
:host([loading]) {
@ -211,13 +169,6 @@ 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;
}
`,
];
}

View File

@ -506,7 +506,7 @@ export class HaControlSlider extends LitElement {
width: 100%;
}
.slider .slider-track-bar {
--ha-border-radius: var(--control-slider-border-radius);
--border-radius: var(--control-slider-border-radius);
--slider-size: 100%;
position: absolute;
height: 100%;

View File

@ -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, SubEntry } from "../data/config_entries";
import { getConfigEntry, getSubEntries } from "../data/config_entries";
import type { ConfigEntry } from "../data/config_entries";
import { getConfigEntry } from "../data/config_entries";
import type { Agent } from "../data/conversation";
import { listAgents } from "../data/conversation";
import { fetchIntegrationManifest } from "../data/integration";
@ -16,7 +16,6 @@ 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__";
@ -38,8 +37,6 @@ export class HaConversationAgentPicker extends LitElement {
@state() private _configEntry?: ConfigEntry;
@state() private _subConfigEntry?: SubEntry;
protected render() {
if (!this._agents) {
return nothing;
@ -104,11 +101,7 @@ export class HaConversationAgentPicker extends LitElement {
${agent.name}
</ha-list-item>`
)}</ha-select
>${(this._subConfigEntry &&
this._configEntry?.supported_subentry_types[
this._subConfigEntry.subentry_type
]?.supports_reconfigure) ||
this._configEntry?.supports_options
>${this._configEntry?.supports_options
? html`<ha-icon-button
.path=${mdiCog}
@click=${this._openOptionsFlow}
@ -149,17 +142,8 @@ 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;
}
}
@ -198,25 +182,6 @@ 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,

View File

@ -20,18 +20,6 @@ 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;

View File

@ -7,8 +7,8 @@ import { haStyle } from "../resources/styles";
import type { HomeAssistant } from "../types";
import "./ha-button";
import "./ha-icon-button";
import "./ha-input-helper-text";
import "./ha-textfield";
import "./ha-input-helper-text";
import type { HaTextField } from "./ha-textfield";
@customElement("ha-multi-textfield")
@ -79,7 +79,6 @@ 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", {
@ -87,6 +86,7 @@ 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

View File

@ -92,12 +92,11 @@ 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>

View File

@ -130,7 +130,7 @@ export class HaYamlEditor extends LitElement {
<div class="card-actions">
${this.copyClipboard
? html`
<ha-button appearance="plain" @click=${this._copyYaml}>
<ha-button @click=${this._copyYaml}>
${this.hass.localize(
"ui.components.yaml-editor.copy_to_clipboard"
)}

View File

@ -116,18 +116,18 @@ class DialogMediaManage extends LitElement {
`
: html`
<ha-button
variant="danger"
class="danger"
slot="navigationIcon"
.disabled=${this._deleting}
@click=${this._handleDelete}
>
<ha-svg-icon .path=${mdiDelete} slot="start"></ha-svg-icon>
${this.hass.localize(
.label=${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="start"
slot="icon"
></ha-svg-icon>
${this.hass.localize(
`ui.components.media-browser.file_management.deselect_all`
)}
</ha-button>
`}
`}
@ -327,10 +327,24 @@ 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;

View File

@ -1,5 +1,5 @@
import { mdiFolderEdit } from "@mdi/js";
import { html, LitElement, nothing } from "lit";
import { css, 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,6 +53,18 @@ 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 {

View File

@ -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,7 +32,6 @@ 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,
@ -45,15 +44,16 @@ 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-list";
import "../ha-list-item";
import "../ha-spinner";
import "../ha-svg-icon";
import "../ha-tooltip";
import "../ha-list";
import "../ha-list-item";
import "./ha-browse-media-tts";
import type { TtsMediaPickedEvent } from "./ha-browse-media-tts";
import { loadVirtualizer } from "../../resources/virtualizer";
declare global {
interface HASSDomEvents {

View File

@ -169,7 +169,6 @@ 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 {

View File

@ -400,23 +400,8 @@ 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),
});
}

View File

@ -8,7 +8,7 @@ export interface HassioHostInfo {
chassis: string;
cpe: string;
deployment: string;
disk_life_time: number | null;
disk_life_time: number | "";
disk_free: number;
disk_total: number;
disk_used: number;

View File

@ -4,7 +4,7 @@ import type {
HassEntityBase,
HassEvent,
} from "home-assistant-js-websocket";
import { BINARY_STATE_ON, BINARY_STATE_OFF } from "../common/const";
import { BINARY_STATE_ON } from "../common/const";
import { computeDomain } from "../common/entity/compute_domain";
import { computeStateDomain } from "../common/entity/compute_state_domain";
import { supportsFeature } from "../common/entity/supports-feature";
@ -52,15 +52,6 @@ export const updateCanInstall = (
(showSkipped && Boolean(entity.attributes.skipped_version))) &&
supportsFeature(entity, UpdateEntityFeature.INSTALL);
export const latestVersionIsSkipped = (entity: UpdateEntity): boolean =>
!!(
entity.attributes.latest_version &&
entity.attributes.skipped_version === entity.attributes.latest_version
);
export const updateButtonIsDisabled = (entity: UpdateEntity): boolean =>
entity.state === BINARY_STATE_OFF && !latestVersionIsSkipped(entity);
export const updateIsInstalling = (entity: UpdateEntity): boolean =>
!!entity.attributes.in_progress;

View File

@ -104,13 +104,23 @@ class StepFlowForm extends LitElement {
</div>`
: nothing}
<div class="buttons">
<ha-button @click=${this._submitStep} .loading=${this._loading}>
${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>
`}
</div>
`;
}
@ -294,6 +304,15 @@ 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;
@ -301,7 +320,7 @@ class StepFlowForm extends LitElement {
}
.buttons {
padding: 16px;
padding: 8px;
}
`,
];

View File

@ -34,7 +34,7 @@ export const configFlowContentStyles = css`
.buttons {
position: relative;
padding: 16px;
padding: 8px 16px 8px 24px;
margin: 8px 0 0;
color: var(--primary-color);
display: flex;

View File

@ -228,7 +228,7 @@ class DialogLightColorFavorite extends LitElement {
</div>
</div>
<div slot="actions">
<ha-button appearance="plain" @click=${this._cancelDialog}>
<ha-button @click=${this._cancelDialog}>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button @click=${this._save} .disabled=${!this._color}

View File

@ -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 { UNAVAILABLE } from "../../../data/entity";
import type { HomeAssistant } from "../../../types";
import "../../../components/buttons/ha-progress-button";
import { UNAVAILABLE } from "../../../data/entity";
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,7 +46,6 @@ 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"
@ -105,7 +104,7 @@ class MoreInfoCamera extends LitElement {
flex-wrap: wrap;
justify-content: flex-end;
box-sizing: border-box;
padding: 16px;
padding: 12px;
z-index: 1;
gap: 8px;
}

View File

@ -51,11 +51,7 @@ class MoreInfoSiren extends LitElement {
.iconPathOff=${mdiVolumeOff}
></ha-state-control-toggle>
${allowAdvanced
? html`<ha-button
appearance="plain"
size="small"
@click=${this._showAdvancedControlsDialog}
>
? html`<ha-button @click=${this._showAdvancedControlsDialog}>
${this.hass.localize("ui.components.siren.advanced_controls")}
</ha-button>`
: nothing}

View File

@ -7,7 +7,6 @@ import { relativeTime } from "../../../common/datetime/relative_time";
import { supportsFeature } from "../../../common/entity/supports-feature";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/buttons/ha-progress-button";
import "../../../components/ha-checkbox";
import "../../../components/ha-faded";
import "../../../components/ha-markdown";
@ -27,8 +26,6 @@ import {
UpdateEntityFeature,
updateIsInstalling,
updateReleaseNotes,
latestVersionIsSkipped,
updateButtonIsDisabled,
} from "../../../data/update";
import type { HomeAssistant } from "../../../types";
import { showAlertDialog } from "../../generic/show-dialog-box";
@ -183,6 +180,11 @@ class MoreInfoUpdate extends LitElement {
return nothing;
}
const skippedVersion =
this.stateObj.attributes.latest_version &&
this.stateObj.attributes.skipped_version ===
this.stateObj.attributes.latest_version;
const createBackupTexts = this._computeCreateBackupTexts();
return html`
@ -310,7 +312,7 @@ class MoreInfoUpdate extends LitElement {
<ha-button
appearance="plain"
@click=${this._handleSkip}
.disabled=${latestVersionIsSkipped(this.stateObj) ||
.disabled=${skippedVersion ||
this.stateObj.state === BINARY_STATE_OFF ||
updateIsInstalling(this.stateObj)}
>
@ -323,8 +325,9 @@ class MoreInfoUpdate extends LitElement {
? html`
<ha-button
@click=${this._handleInstall}
.loading=${updateIsInstalling(this.stateObj)}
.disabled=${updateButtonIsDisabled(this.stateObj)}
.disabled=${(this.stateObj.state === BINARY_STATE_OFF &&
!skippedVersion) ||
updateIsInstalling(this.stateObj)}
>
${this.hass.localize(
"ui.dialogs.more_info_control.update.update"
@ -505,7 +508,7 @@ class MoreInfoUpdate extends LitElement {
flex-wrap: wrap;
justify-content: flex-end;
box-sizing: border-box;
padding: 16px;
padding: 12px;
z-index: 1;
gap: 8px;
}

View File

@ -38,7 +38,7 @@ export class HuiNotificationItemTemplate extends LitElement {
.actions {
border-top: 1px solid var(--divider-color, #e8e8e8);
padding: 8px;
padding: 5px 16px;
display: flex;
justify-content: flex-end;
}

View File

@ -32,7 +32,7 @@ class DialogBox extends LitElement {
)}
>
<p>${this.hass.localize("ui.dialogs.update_backup.text")}</p>
<ha-button appearance="plain" @click=${this._no} slot="secondaryAction">
<ha-button @click=${this._no} slot="secondaryAction">
${this.hass!.localize("ui.common.no")}
</ha-button>
<ha-button @click=${this._yes} slot="primaryAction">

View File

@ -76,6 +76,7 @@ export class CloudStepSignin extends LitElement {
</div>
<div class="footer">
<ha-button
unelevated
@click=${this._handleLogin}
.disabled=${this._requestInProgress}
>${this.hass.localize(

View File

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

View File

@ -70,7 +70,6 @@ export class HaVoiceAssistantSetupStepArea extends LitElement {
display: block;
width: 100%;
margin-bottom: 24px;
text-align: initial;
}
`,
];

View File

@ -51,16 +51,16 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement {
)}
</p>
<div class="footer">
<ha-button
appearance="plain"
<a
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
>
<ha-button @click=${this._testConnection}
>${this.hass.localize(

View File

@ -95,27 +95,24 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
"ui.panel.config.voice_assistants.satellite_wizard.local.failed_secondary"
)}
</p>
<ha-button
appearance="plain"
size="small"
@click=${this._prevStep}
<ha-button @click=${this._prevStep}
>${this.hass.localize("ui.common.back")}</ha-button
>
<ha-button
<a
href=${documentationUrl(
this.hass,
"/voice_control/voice_remote_local_assistant/"
)}
target="_blank"
rel="noreferrer noopener"
size="small"
appearance="plain"
>
<ha-svg-icon .path=${mdiOpenInNew} slot="start"></ha-svg-icon>
<ha-button>
<ha-svg-icon .path=${mdiOpenInNew} slot="icon"></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.common.learn_more"
)}</ha-button
>`
>
</a>`
: this._state === "NOT_SUPPORTED"
? html`<img
src="/static/images/voice-assistant/error.png"
@ -131,27 +128,27 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
"ui.panel.config.voice_assistants.satellite_wizard.local.not_supported_secondary"
)}
</p>
<ha-button
appearance="plain"
size="small"
@click=${this._prevStep}
<ha-button @click=${this._prevStep}
>${this.hass.localize("ui.common.back")}</ha-button
>
<ha-button
<a
href=${documentationUrl(
this.hass,
"/voice_control/voice_remote_local_assistant/"
)}
target="_blank"
rel="noreferrer noopener"
appearance="plain"
size="small"
>
<ha-svg-icon .path=${mdiOpenInNew} slot="start"></ha-svg-icon>
<ha-button>
<ha-svg-icon
.path=${mdiOpenInNew}
slot="icon"
></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.common.learn_more"
)}</ha-button
>`
>
</a>`
: nothing}
</div>`;
}

View File

@ -235,7 +235,10 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
: nothing}
</div>
<div class="footer">
<ha-button @click=${this._createPipeline} .disabled=${!this._value}
<ha-button
@click=${this._createPipeline}
unelevated
.disabled=${!this._value}
>${this.hass.localize("ui.common.next")}</ha-button
>
</div>`;

View File

@ -126,15 +126,8 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
</ha-list-item>`
)}
</ha-select>
<ha-button
appearance="plain"
size="small"
@click=${this._testWakeWord}
>
<ha-svg-icon
slot="start"
.path=${mdiMicrophone}
></ha-svg-icon>
<ha-button @click=${this._testWakeWord}>
<ha-svg-icon slot="icon" .path=${mdiMicrophone}></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.voice_assistants.satellite_wizard.success.test_wakeword"
)}
@ -158,12 +151,8 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
</ha-list-item>`
)}
</ha-select>
<ha-button
appearance="plain"
size="small"
@click=${this._openPipeline}
>
<ha-svg-icon slot="start" .path=${mdiCog}></ha-svg-icon>
<ha-button @click=${this._openPipeline}>
<ha-svg-icon slot="icon" .path=${mdiCog}></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.voice_assistants.satellite_wizard.success.edit_pipeline"
)}
@ -180,12 +169,8 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
@value-changed=${this._voicePicked}
@closed=${stopPropagation}
></ha-tts-voice-picker>
<ha-button
appearance="plain"
size="small"
@click=${this._testTts}
>
<ha-svg-icon slot="start" .path=${mdiPlay}></ha-svg-icon>
<ha-button @click=${this._testTts}>
<ha-svg-icon slot="icon" .path=${mdiPlay}></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.voice_assistants.satellite_wizard.success.try_tts"
)}

View File

@ -148,10 +148,7 @@ export class HaVoiceAssistantSetupStepWakeWord extends LitElement {
${this.assistConfiguration &&
this.assistConfiguration.available_wake_words.length > 1
? html`<div class="footer centered">
<ha-button
appearance="plain"
size="small"
@click=${this._changeWakeWord}
<ha-button @click=${this._changeWakeWord}
>${this.hass.localize(
"ui.panel.config.voice_assistants.satellite_wizard.wake_word.change_wake_word"
)}</ha-button

View File

@ -271,6 +271,7 @@ 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) {

View File

@ -22,9 +22,7 @@ class HaInitPage extends LitElement {
<p class="retry-text">
Retrying in ${this._retryInSeconds} seconds...
</p>
<ha-button size="small" appearance="plain" @click=${this._retry}
>Retry now</ha-button
>
<ha-button @click=${this._retry}>Retry now</ha-button>
${location.host.includes("ui.nabu.casa")
? html`
<p>

View File

@ -69,13 +69,10 @@ class NotificationManager extends LitElement {
${this._parameters?.action
? html`
<ha-button
appearance="plain"
size="small"
slot="action"
.label=${this._parameters?.action.text}
@click=${this._buttonClicked}
>
${this._parameters?.action.text}
</ha-button>
></ha-button>
`
: nothing}
${this._parameters?.dismissable

View File

@ -32,16 +32,17 @@ class OnboardingRestoreBackupNoCloudBackup extends LitElement {
<ha-button @click=${this._signOut}>
${this.localize("ui.panel.page-onboarding.restore.ha-cloud.sign_out")}
</ha-button>
<ha-button
<a
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>
</div>
`;
}

View File

@ -131,17 +131,18 @@ class OnboardingRestoreBackupRestore extends LitElement {
${this.localize(
"ui.panel.page-onboarding.restore.details.addons_unsupported"
)}
<ha-button
<a
slot="action"
href="https://www.home-assistant.io/installation/#advanced-installation-methods"
target="_blank"
rel="noreferrer noopener"
size="small"
>
${this.localize(
<ha-button
>${this.localize(
"ui.panel.page-onboarding.restore.ha-cloud.learn_more"
)}</ha-button
>
</a>
</ha-alert>`
: nothing}
${!onlyHomeAssistantBackup
@ -190,13 +191,14 @@ class OnboardingRestoreBackupRestore extends LitElement {
<div class="actions${this.mode === "cloud" ? " cloud" : ""}">
${this.mode === "cloud"
? html`<ha-button appearance="plain" @click=${this._signOut}>
? html`<ha-button @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 === "") ||

View File

@ -40,11 +40,7 @@ class ConfirmEventDialogBox extends LitElement {
<div>
<p>${this._params.text}</p>
</div>
<ha-button
appearance="plain"
@click=${this._dismiss}
slot="secondaryAction"
>
<ha-button @click=${this._dismiss} slot="secondaryAction">
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button

View File

@ -1,3 +1,4 @@
import { formatInTimeZone, toDate } from "date-fns-tz";
import {
addDays,
addHours,
@ -5,7 +6,6 @@ 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,7 +282,6 @@ class DialogCalendarEventEditor extends LitElement {
? html`
<ha-button
slot="secondaryAction"
appearance="plain"
variant="danger"
@click=${this._deleteEvent}
.disabled=${this._submitting}

View File

@ -135,7 +135,10 @@ class PanelCalendar extends LitElement {
>
<ha-button slot="trigger">
${this.hass.localize("ui.components.calendar.my_calendars")}
<ha-svg-icon slot="end" .path=${mdiChevronDown}></ha-svg-icon>
<ha-svg-icon
slot="trailingIcon"
.path=${mdiChevronDown}
></ha-svg-icon>
</ha-button>
${calendarItems}
${this.hass.user?.is_admin
@ -300,7 +303,25 @@ class PanelCalendar extends LitElement {
--calendar-border-width: 1px 0;
}
ha-button-menu ha-button {
--ha-font-size-m: var(--ha-font-size-l);
--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;
}
:host([mobile]) .lists {
--mdc-menu-min-width: 100vw;

View File

@ -220,25 +220,32 @@ 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}
.disabled=${this._loading}
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button
slot="primaryAction"
.disabled=${!this._domain || !this._clientId || !this._clientSecret}
.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>
`;
}

View File

@ -30,8 +30,6 @@ 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[];
@ -112,8 +110,6 @@ 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(
@ -121,10 +117,9 @@ 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(
@ -317,6 +312,9 @@ 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 */

View File

@ -15,8 +15,6 @@ export class HaParallelAction extends LitElement implements ActionElement {
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public narrow = false;
@property({ attribute: false }) public action!: ParallelAction;
public static get defaultConfig(): ParallelAction {
@ -31,7 +29,6 @@ export class HaParallelAction extends LitElement implements ActionElement {
return html`
<ha-automation-action
.actions=${action.parallel}
.narrow=${this.narrow}
.disabled=${this.disabled}
@value-changed=${this._actionsChanged}
.hass=${this.hass}

View File

@ -15,8 +15,6 @@ export class HaSequenceAction extends LitElement implements ActionElement {
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public narrow = false;
@property({ attribute: false }) public action!: SequenceAction;
public static get defaultConfig(): SequenceAction {
@ -31,7 +29,6 @@ export class HaSequenceAction extends LitElement implements ActionElement {
return html`
<ha-automation-action
.actions=${action.sequence}
.narrow=${this.narrow}
.disabled=${this.disabled}
@value-changed=${this._actionsChanged}
.hass=${this.hass}

View File

@ -260,14 +260,12 @@ class DialogAutomationSave extends LitElement implements HassDialog {
.path=${mdiClose}
></ha-icon-button>
<span slot="title">${this._params.title || title}</span>
${this._params.hideInputs
? nothing
: html` <ha-suggest-with-ai-button
<ha-suggest-with-ai-button
slot="actionItems"
.hass=${this.hass}
.generateTask=${this._generateTask}
@suggestion=${this._handleSuggestion}
></ha-suggest-with-ai-button>`}
></ha-suggest-with-ai-button>
</ha-dialog-header>
${this._error
? html`<ha-alert alert-type="error"
@ -383,7 +381,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.

View File

@ -34,8 +34,6 @@ export default class HaAutomationCondition extends LitElement {
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public root = false;
@state() private _showReorder = false;
@state()
@ -161,8 +159,6 @@ 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(
@ -172,7 +168,6 @@ 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>
@ -345,6 +340,9 @@ 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 */

View File

@ -487,7 +487,6 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
.disabled=${Boolean(this._readOnly)}
.dirty=${this._dirty}
@value-changed=${this._valueChanged}
@editor-save=${this._handleSaveAutomation}
></manual-automation-editor>
`}
</div>
@ -518,7 +517,6 @@ 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>`

View File

@ -228,7 +228,6 @@ export class HaManualAutomationEditor extends LitElement {
@value-changed=${this._conditionChanged}
.hass=${this.hass}
.disabled=${this.disabled}
root
></ha-automation-condition>
<div class="header">
@ -270,7 +269,6 @@ export class HaManualAutomationEditor extends LitElement {
.hass=${this.hass}
.narrow=${this.narrow}
.disabled=${this.disabled}
root
></ha-automation-action>
`;
}

View File

@ -100,14 +100,14 @@ export default class HaAutomationOption extends LitElement {
)}
<div class="buttons">
<ha-button
appearance="filled"
outlined
.disabled=${this.disabled}
@click=${this._addOption}
>
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
${this.hass.localize(
.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-button>
</div>
</div>
@ -255,6 +255,9 @@ 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 */

View File

@ -58,7 +58,7 @@ class DialogPasteReplace extends LitElement implements HassDialog {
></ha-yaml-editor>
<div slot="primaryAction">
<ha-button appearance="plain" @click=${this._handleAppend}>
<ha-button @click=${this._handleAppend}>
${this.hass.localize("ui.common.append")}
</ha-button>
<ha-button @click=${this._handleReplace}>
@ -89,10 +89,6 @@ class DialogPasteReplace extends LitElement implements HassDialog {
font-size: inherit;
font-weight: inherit;
}
div[slot="primaryAction"] {
display: flex;
gap: 8px;
}
`,
];
}

View File

@ -296,6 +296,9 @@ 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 */

View File

@ -2,13 +2,11 @@ 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";
@ -16,7 +14,6 @@ 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 {
@ -38,14 +35,9 @@ 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",
@ -81,21 +73,7 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
},
{ name: "offset", selector: { text: {} } },
] 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) {
@ -117,14 +95,12 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
inputMode: undefined | typeof MODE_ENTITY | typeof MODE_TIME,
at:
| string
| { entity_id: string | undefined; offset?: string | undefined },
weekday: string | string[] | undefined
| { entity_id: string | undefined; offset?: 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"
@ -140,7 +116,6 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
entity,
time,
offset,
weekday,
};
}
);
@ -152,12 +127,8 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
return nothing;
}
const data = this._data(this._inputMode, at, this.trigger.weekday);
const schema = this._schema(
this.hass.localize,
this.hass.locale,
data.mode
);
const data = this._data(this._inputMode, at);
const schema = this._schema(this.hass.localize, data.mode);
return html`
<ha-form
@ -175,18 +146,14 @@ 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 = {
fireEvent(this, "value-changed", {
value: {
...this.trigger,
at: newValue.offset
? {
@ -194,17 +161,7 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
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: triggerUpdate,
},
});
}
@ -216,10 +173,6 @@ 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}`

View File

@ -99,11 +99,13 @@ class HaBackupOverviewBackups extends LitElement {
</ha-md-list>
</div>
<div class="card-actions">
<ha-button appearance="filled" href="/config/backup/backups?type=all">
<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>
</div>
</ha-card>
`;

View File

@ -375,13 +375,8 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
"ui.panel.config.backup.encryption_key.download_emergency_kit_description"
)}
</span>
<ha-button
size="small"
appearance="plain"
slot="end"
@click=${this._downloadKey}
>
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
<ha-button slot="end" @click=${this._downloadKey}>
<ha-svg-icon .path=${mdiDownload} slot="icon"></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_emergency_kit_action"
)}

View File

@ -128,7 +128,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
<ha-button
@click=${this._submit}
.disabled=${!this._newEncryptionKey}
variant="danger"
class="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="start"></ha-svg-icon>
<ha-svg-icon .path=${mdiDownload} slot="icon"></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="start"></ha-svg-icon>
<ha-svg-icon .path=${mdiDownload} slot="icon"></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_emergency_kit_action"
)}
@ -297,6 +297,9 @@ 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);

View File

@ -112,7 +112,7 @@ class DialogDownloadDecryptedBackup extends LitElement implements HassDialog {
: nothing}
</div>
<div slot="actions">
<ha-button appearance="plain" @click=${this._cancel}>
<ha-button @click=${this._cancel}>
${this.hass.localize("ui.common.cancel")}
</ha-button>

View File

@ -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,7 +91,6 @@ class LocalBackupLocationDialog extends LitElement {
</ha-alert>
<ha-button
slot="secondaryAction"
appearance="plain"
@click=${this.closeDialog}
dialogInitialFocus
>

View File

@ -227,7 +227,7 @@ class DialogRestoreBackup extends LitElement implements HassDialog {
private _renderConfirmActions() {
return html`
<ha-button appearance="plain" @click=${this.closeDialog}>
<ha-button @click=${this.closeDialog}>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button @click=${this._restoreBackup} variant="danger">

View File

@ -151,13 +151,8 @@ class DialogSetBackupEncryptionKey extends LitElement implements HassDialog {
"ui.panel.config.backup.encryption_key.download_emergency_kit_description"
)}
</span>
<ha-button
size="small"
appearance="plain"
slot="end"
@click=${this._download}
>
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
<ha-button slot="end" @click=${this._download}>
<ha-svg-icon .path=${mdiDownload} slot="icon"></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_emergency_kit_action"
)}

View File

@ -87,13 +87,8 @@ class DialogShowBackupEncryptionKey extends LitElement implements HassDialog {
"ui.panel.config.backup.encryption_key.download_emergency_kit_description"
)}
</span>
<ha-button
size="small"
appearance="plain"
slot="end"
@click=${this._download}
>
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
<ha-button slot="end" @click=${this._download}>
<ha-svg-icon .path=${mdiDownload} slot="icon"></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_emergency_kit_action"
)}

View File

@ -113,10 +113,7 @@ export class DialogUploadBackup
></ha-file-upload>
</div>
<div slot="actions">
<ha-button
appearance="plain"
@click=${this.closeDialog}
.disabled=${this._uploading}
<ha-button @click=${this.closeDialog} .disabled=${this._uploading}
>${this.hass.localize("ui.common.cancel")}</ha-button
>
<ha-button

View File

@ -417,11 +417,7 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) {
<div slot="selection-bar">
${!this.narrow
? html`
<ha-button
appearance="plain"
@click=${this._deleteSelected}
variant="danger"
>
<ha-button @click=${this._deleteSelected} class="warning">
${this.hass.localize(
"ui.panel.config.backup.backups.delete_selected"
)}

View File

@ -158,18 +158,18 @@ class HaConfigBackupDetails extends LitElement {
"ui.panel.config.backup.location.encryption.location_encrypted_cloud_description"
)}
</span>
<ha-button
<a
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>
</ha-md-list-item>
`
: encrypted

View File

@ -187,11 +187,7 @@ export class CloudRemotePref extends LitElement {
)
: nothing}</span
>
<ha-button
appearance="plain"
size="small"
@click=${this._openCertInfo}
>
<ha-button @click=${this._openCertInfo}>
${this.hass.localize(
"ui.panel.config.cloud.account.remote.more_info"
)}

View File

@ -126,7 +126,7 @@ export class CloudTTSPref extends LitElement {
`}
</div>
<div class="flex"></div>
<ha-button appearance="plain" @click=${this._openTryDialog}>
<ha-button @click=${this._openTryDialog}>
${this.hass.localize("ui.panel.config.cloud.account.tts.try")}
</ha-button>
</div>

View File

@ -77,9 +77,7 @@ export class DialogSupportPackage extends LitElement {
</ha-alert>
<hr />
<div class="actions">
<ha-button appearance="plain" @click=${this.closeDialog}
>Close</ha-button
>
<ha-button @click=${this.closeDialog}>Close</ha-button>
<ha-button @click=${this._download}>Download</ha-button>
</div>
</div>

View File

@ -2,20 +2,19 @@ import "@material/mwc-button";
import { mdiHelpCircle, mdiStarFourPoints } from "@mdi/js";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
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 "../../../components/ha-card";
import "../../../components/ha-settings-row";
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 {
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 {
@ -25,8 +24,6 @@ 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")) {
@ -89,51 +86,30 @@ export class AITaskPref extends LitElement {
.hass=${this.hass}
.disabled=${this._prefs === undefined &&
isComponentLoaded(this.hass, "ai_task")}
.value=${this._gen_data_entity_id ||
this._prefs?.gen_data_entity_id}
.value=${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 _handlePrefChange(ev: CustomEvent<{ value: string | undefined }>) {
private async _handlePrefChange(
ev: CustomEvent<{ value: string | undefined }>
) {
const input = ev.target as HaEntityPicker;
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 key = input.getAttribute("data-name") as keyof AITaskPreferences;
const entityId = ev.detail.value || null;
const oldPrefs = this._prefs;
const update: Partial<AITaskPreferences> = {
gen_data_entity_id: this._gen_data_entity_id,
};
this._prefs = { ...this._prefs!, ...update };
this._prefs = { ...this._prefs!, [key]: entityId };
try {
this._prefs = await saveAITaskPreferences(this.hass, {
...update,
[key]: entityId,
});
button.actionSuccess();
} catch (_err: any) {
button.actionError();
this._prefs = oldPrefs;
} finally {
button.progress = false;
}
}
@ -169,9 +145,6 @@ 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;

View File

@ -1442,9 +1442,10 @@ export class HaConfigDevicePage extends LitElement {
}
private async _signUrl(ev) {
const anchor = ev.currentTarget.closest("a");
const signedUrl = await getSignedPath(
this.hass,
ev.currentTarget.getAttribute("href")
anchor.getAttribute("href")
);
fileDownload(signedUrl.path);
}

View File

@ -202,7 +202,7 @@ export class EntitySettingsHelperTab extends LitElement {
box-sizing: border-box;
display: flex;
justify-content: space-between;
padding: 16px;
padding: 0 24px 24px 24px;
background-color: var(--mdc-theme-surface, #fff);
}
.error {

View File

@ -249,9 +249,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
.buttons {
box-sizing: border-box;
display: flex;
padding: 16px;
padding: 8px;
justify-content: space-between;
padding-bottom: max(var(--safe-area-inset-bottom), 16px);
padding-bottom: max(var(--safe-area-inset-bottom), 8px);
background-color: var(--mdc-theme-surface, #fff);
border-top: 1px solid var(--divider-color);
position: sticky;

View File

@ -92,9 +92,8 @@ 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>

View File

@ -145,7 +145,7 @@ class HaInputSelectForm extends LitElement {
)}
@keydown=${this._handleKeyAdd}
></ha-textfield>
<ha-button size="small" appearance="plain" @click=${this._addOption}
<ha-button @click=${this._addOption}
>${this.hass!.localize(
"ui.dialogs.helper_settings.input_select.add"
)}</ha-button

View File

@ -207,7 +207,7 @@ class HaConfigEntryRow extends LitElement {
: nothing}
</div>
${item.disabled_by === "user"
? html`<ha-button slot="end" @click=${this._handleEnable}>
? html`<ha-button unelevated slot="end" @click=${this._handleEnable}>
${this.hass.localize("ui.common.enable")}
</ha-button>`
: configPanel &&

View File

@ -17,10 +17,6 @@ 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";
@ -68,6 +64,10 @@ 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,

View File

@ -45,9 +45,10 @@ export class HaDisabledConfigEntryCard extends LitElement {
>
<ha-icon-button .path=${mdiCog}></ha-icon-button>
</a>
<ha-button @click=${this._handleEnable} appearance="filled">
${this.hass.localize("ui.common.enable")}
</ha-button>
<ha-button
@click=${this._handleEnable}
.label=${this.hass.localize("ui.common.enable")}
></ha-button>
</ha-integration-action-card>
`;
}

View File

@ -34,11 +34,12 @@ export class HaIgnoredConfigEntryCard extends LitElement {
this.entry.localized_domain_name
: this.entry.title}
>
<ha-button appearance="plain" @click=${this._removeIgnoredIntegration}>
${this.hass.localize(
<ha-button
@click=${this._removeIgnoredIntegration}
.label=${this.hass.localize(
"ui.panel.config.integrations.ignore.stop_ignore"
)}
</ha-button>
></ha-button>
</ha-integration-action-card>
`;
}

View File

@ -112,8 +112,7 @@ export class HaIntegrationCard extends LitElement {
return html`
<div class="card-actions">
${devices.length > 0
? html`<ha-button
appearance="plain"
? html`<a
href=${devices.length === 1 &&
// Always link to device page for protocol integrations to show Add Device button
// @ts-expect-error
@ -121,28 +120,31 @@ 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>`
</ha-button>
</a>`
: entitiesCount > 0
? html`<ha-button
appearance="plain"
? html`<a
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>`
</ha-button>
</a>`
: this.items.find((itm) => itm.source !== "yaml")
? html`<ha-button
appearance="plain"
? html`<a
href=${`/config/integrations/integration/${this.domain}`}
>
<ha-button appearance="plain">
${this.hass.localize(
`ui.panel.config.integrations.config_entry.entries`,
{
@ -150,7 +152,8 @@ export class HaIntegrationCard extends LitElement {
.length,
}
)}
</ha-button>`
</ha-button>
</a>`
: html`<div class="spacer"></div>`}
<div class="icons">
${this.manifest && !this.manifest.is_built_in

View File

@ -26,6 +26,7 @@ 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";
@ -130,34 +131,33 @@ export class BluetoothNetworkVisualization extends LitElement {
data: BluetoothDeviceData[],
scanners: BluetoothScannersDetails
): NetworkData => {
const style = getComputedStyle(this);
const categories = [
{
name: CORE_SOURCE_LABEL,
symbol: "roundRect",
itemStyle: {
color: style.getPropertyValue("--primary-color"),
color: colorVariables["primary-color"],
},
},
{
name: this.hass.localize("ui.panel.config.bluetooth.scanners"),
symbol: "circle",
itemStyle: {
color: style.getPropertyValue("--cyan-color"),
color: colorVariables["cyan-color"],
},
},
{
name: this.hass.localize("ui.panel.config.bluetooth.known_devices"),
symbol: "circle",
itemStyle: {
color: style.getPropertyValue("--teal-color"),
color: colorVariables["teal-color"],
},
},
{
name: this.hass.localize("ui.panel.config.bluetooth.unknown_devices"),
symbol: "circle",
itemStyle: {
color: style.getPropertyValue("--disabled-color"),
color: colorVariables["disabled-color"],
},
},
];
@ -192,7 +192,7 @@ export class BluetoothNetworkVisualization extends LitElement {
symbol: "none",
lineStyle: {
width: 3,
color: style.getPropertyValue("--primary-color"),
color: colorVariables["primary-color"],
},
});
});
@ -206,7 +206,7 @@ export class BluetoothNetworkVisualization extends LitElement {
symbol: "none",
lineStyle: {
width: this._getLineWidth(node.rssi),
color: style.getPropertyValue("--primary-color"),
color: colorVariables["primary-color"],
},
});
return;
@ -227,8 +227,8 @@ export class BluetoothNetworkVisualization extends LitElement {
lineStyle: {
width: this._getLineWidth(node.rssi),
color: device
? style.getPropertyValue("--primary-color")
: style.getPropertyValue("--disabled-color"),
? colorVariables["primary-color"]
: colorVariables["disabled-color"],
},
});
});

View File

@ -14,9 +14,6 @@ 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"];
@ -58,6 +55,14 @@ 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}>
@ -103,7 +108,25 @@ export class MQTTConfigPanel extends LitElement {
></ha-switch>
</ha-formfield>
</div>
<p>${this.hass.localize("ui.panel.config.mqtt.payload")}</p>
<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>
<ha-code-editor
mode="jinja2"
autocomplete-entities
@ -148,28 +171,21 @@ 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;
}
const script: Action[] = [
{
action: "mqtt.publish",
data: {
this.hass.callService("mqtt", "publish", {
topic: this._topic,
payload: this._payload,
payload: !this._allowTemplate ? this._payload : undefined,
payload_template: this._allowTemplate ? this._payload : undefined,
qos: parseInt(this._qos),
retain: this._retain,
},
},
];
callExecuteScript(this.hass, script).catch((err) =>
showToast(this, {
message: err.message,
})
);
});
}
private async _openOptionFlow() {

View File

@ -105,6 +105,7 @@ 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")

View File

@ -114,11 +114,7 @@ class DialogSSDPDiscoveryInfo extends LitElement implements HassDialog {
</tbody>
</table>
<ha-button
appearance="plain"
slot="secondaryAction"
@click=${this._copyToClipboard}
>
<ha-button slot="secondaryAction" @click=${this._copyToClipboard}>
${this.hass.localize("ui.panel.config.ssdp.copy_to_clipboard")}
</ha-button>
</ha-dialog>

View File

@ -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 { 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 "../../../../../components/ha-list-item";
import type { ZHAChangeChannelDialogParams } from "./show-dialog-zha-change-channel";
const VALID_CHANNELS = [
@ -128,7 +128,6 @@ 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

View File

@ -160,11 +160,7 @@ class DialogZHAReconfigureDevice extends LitElement {
<ha-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.common.close")}
</ha-button>
<ha-button
appearance="plain"
slot="secondaryAction"
@click=${this._toggleDetails}
>
<ha-button slot="secondaryAction" @click=${this._toggleDetails}>
${this._showDetails
? this.hass.localize(
`ui.dialogs.zha_reconfigure_device.button_hide`
@ -193,11 +189,7 @@ class DialogZHAReconfigureDevice extends LitElement {
<ha-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.common.close")}
</ha-button>
<ha-button
appearance="plain"
slot="secondaryAction"
@click=${this._toggleDetails}
>
<ha-button slot="secondaryAction" @click=${this._toggleDetails}>
${this._showDetails
? this.hass.localize(
`ui.dialogs.zha_reconfigure_device.button_hide`

Some files were not shown because too many files have changed in this diff Show More