Compare commits

...

8 Commits

Author SHA1 Message Date
Bram Kragten
b01ab9234b Bumped version to 20250731.0 2025-07-31 16:54:24 +02:00
Wendelin
ad39228dea Fix line-height, fix script editor buttons (#26337)
* Fix line-height

* Fix script root buttons
2025-07-31 16:54:03 +02:00
Wendelin
8cc48cdecb Use tilecard button feature editor (#26335)
Use button feature editor
2025-07-31 16:54:02 +02:00
Wendelin
524e89acf0 Revert "Use query params instead of path for media browser navigate ids" (#26333) 2025-07-31 16:54:01 +02:00
Wendelin
48f6b34882 Fix ha-button with missing label and links (#26332) 2025-07-31 16:54:00 +02:00
Bram Kragten
44d9185574 Fix area picker text alignment in voice wizard (#26330) 2025-07-31 16:53:59 +02:00
Joost Lekkerkerker
51ff6c6564 Use underscores in AI task name (#26327) 2025-07-31 16:53:58 +02:00
Franck Nijhof
b49b8e3db8 Add weekdays to time trigger (#25908)
* Add weekdays to time trigger

* Update src/translations/en.json

Co-authored-by: Norbert Rittel <norbert@rittel.de>

* Localization changes

---------

Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2025-07-31 16:53:57 +02:00
99 changed files with 543 additions and 510 deletions

View File

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

View File

@ -89,11 +89,14 @@ export class HADemoCard extends LitElement implements LovelaceCard {
)} )}
</div> </div>
<div class="actions small-hidden"> <div class="actions small-hidden">
<a href="https://www.home-assistant.io" target="_blank"> <ha-button
<ha-button> appearance="plain"
${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")} size="small"
</ha-button> href="https://www.home-assistant.io"
</a> target="_blank"
>
${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")}
</ha-button>
</div> </div>
</ha-card> </ha-card>
`; `;

View File

@ -147,13 +147,13 @@ The `title ` option should not be used without a description.
<ha-alert alert-type="success"> <ha-alert alert-type="success">
This is a success alert — check it out! 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> </ha-alert>
```html ```html
<ha-alert alert-type="success"> <ha-alert alert-type="success">
This is a success alert — check it out! 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> </ha-alert>
``` ```

View File

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

View File

@ -53,12 +53,13 @@ Check the [webawesome documentation](https://webawesome.com/docs/components/butt
**Properties/Attributes** **Properties/Attributes**
| Name | Type | Default | Description | | Name | Type | Default | Description |
| ----------- | ---------------------------------------------- | -------- | -------------------------------------------------- | | ---------- | ---------------------------------------------- | -------- | --------------------------------------------------------------------------------- |
| appearance | "accent"/"filled"/"plain" | "accent" | Sets the button appearance. | | appearance | "accent"/"filled"/"plain" | "accent" | Sets the button appearance. |
| variants | "brand"/"danger"/"neutral"/"warning"/"success" | "brand" | Sets the button color variant. "brand" is default. | | variants | "brand"/"danger"/"neutral"/"warning"/"success" | "brand" | Sets the button color variant. "brand" is default. |
| size | "small"/"medium" | "medium" | Sets the button size. | | size | "small"/"medium" | "medium" | Sets the button size. |
| hideContent | Boolean | false | Hides the button content (for overlays) | | 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** **CSS Custom Properties**

View File

@ -604,8 +604,8 @@ export class DialogHassioNetwork
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding: 8px; padding: 16px;
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); background-color: var(--mdc-theme-surface, #fff);
} }
.warning { .warning {

View File

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

View File

@ -110,12 +110,9 @@ class LandingPageLogs extends LitElement {
})}" })}"
@click=${this._scrollToBottom} @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")} ${this.localize("logs.scroll_down_button")}
<ha-svg-icon <ha-svg-icon .path=${mdiArrowCollapseDown} slot="end"></ha-svg-icon>
.path=${mdiArrowCollapseDown}
slot="trailingIcon"
></ha-svg-icon>
</ha-button> </ha-button>
`; `;
} }

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "home-assistant-frontend" name = "home-assistant-frontend"
version = "20250730.0" version = "20250731.0"
license = "Apache-2.0" license = "Apache-2.0"
license-files = ["LICENSE*"] license-files = ["LICENSE*"]
description = "The Home Assistant frontend" description = "The Home Assistant frontend"

View File

@ -32,7 +32,8 @@ export type Appearance = "accent" | "filled" | "outlined" | "plain";
* @attr {("small"|"medium")} size - Sets the button size. * @attr {("small"|"medium")} size - Sets the button size.
* @attr {("brand"|"neutral"|"danger"|"warning"|"success")} variant - Sets the button color variant. "primary" is default. * @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 {("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") @customElement("ha-button")
export class HaButton extends Button { export class HaButton extends Button {
@ -66,6 +67,7 @@ export class HaButton extends Button {
); );
font-size: var(--ha-font-size-m); font-size: var(--ha-font-size-m);
line-height: 1;
} }
:host([size="small"]) .button { :host([size="small"]) .button {

View File

@ -92,11 +92,12 @@ export class HaPictureUpload extends LitElement {
/> />
<div> <div>
<ha-button <ha-button
appearance="plain"
size="small"
variant="danger"
@click=${this._handleChangeClick} @click=${this._handleChangeClick}
.label=${this.hass.localize(
"ui.components.picture-upload.clear_picture"
)}
> >
${this.hass.localize("ui.components.picture-upload.clear_picture")}
</ha-button> </ha-button>
</div> </div>
</div> </div>

View File

@ -103,7 +103,7 @@ class HaQrScanner extends LitElement {
> >
${this._error || this._warning} ${this._error || this._warning}
${this._error ${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")} ${this.hass.localize("ui.components.qr-scanner.retry")}
</ha-button>` </ha-button>`
: nothing} : nothing}

View File

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

View File

@ -119,15 +119,15 @@ class DialogMediaManage extends LitElement {
class="danger" class="danger"
slot="navigationIcon" slot="navigationIcon"
.disabled=${this._deleting} .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.${ `ui.components.media-browser.file_management.${
this._deleting ? "deleting" : "delete" this._deleting ? "deleting" : "delete"
}`, }`,
{ count: this._selected.size } { count: this._selected.size }
)} )}
@click=${this._handleDelete}
>
<ha-svg-icon .path=${mdiDelete} slot="start"></ha-svg-icon>
</ha-button> </ha-button>
${this._deleting ${this._deleting
@ -135,15 +135,15 @@ class DialogMediaManage extends LitElement {
: html` : html`
<ha-button <ha-button
slot="actionItems" slot="actionItems"
.label=${this.hass.localize(
`ui.components.media-browser.file_management.deselect_all`
)}
@click=${this._handleDeselectAll} @click=${this._handleDeselectAll}
> >
<ha-svg-icon <ha-svg-icon
.path=${mdiClose} .path=${mdiClose}
slot="icon" slot="start"
></ha-svg-icon> ></ha-svg-icon>
${this.hass.localize(
`ui.components.media-browser.file_management.deselect_all`
)}
</ha-button> </ha-button>
`} `}
`} `}
@ -331,20 +331,10 @@ class DialogMediaManage extends LitElement {
--mdc-theme-primary: var(--error-color); --mdc-theme-primary: var(--error-color);
} }
ha-svg-icon[slot="icon"] {
vertical-align: middle;
}
ha-tip { ha-tip {
margin: 16px; margin: 16px;
} }
ha-svg-icon[slot="icon"] {
margin-inline-start: 0px !important;
margin-inline-end: 8px !important;
direction: var(--direction);
}
.refresh { .refresh {
display: flex; display: flex;
height: 200px; height: 200px;

View File

@ -1,5 +1,5 @@
import { mdiFolderEdit } from "@mdi/js"; 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 { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import type { MediaPlayerItem } from "../../data/media-player"; import type { MediaPlayerItem } from "../../data/media-player";
@ -53,18 +53,6 @@ class MediaManageButton extends LitElement {
onClose: () => fireEvent(this, "media-refresh"), 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 { declare global {

View File

@ -169,6 +169,7 @@ export interface TagTrigger extends BaseTrigger {
export interface TimeTrigger extends BaseTrigger { export interface TimeTrigger extends BaseTrigger {
trigger: "time"; trigger: "time";
at: string | { entity_id: string; offset?: string }; at: string | { entity_id: string; offset?: string };
weekday?: string | string[];
} }
export interface TemplateTrigger extends BaseTrigger { export interface TemplateTrigger extends BaseTrigger {

View File

@ -400,8 +400,23 @@ const tryDescribeTrigger = (
return `${entityStr}${offsetStr}`; 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`, { return hass.localize(`${triggerTranslationBaseKey}.time.description.full`, {
time: formatListWithOrs(hass.locale, result), time: formatListWithOrs(hass.locale, result),
hasWeekdays: weekdays.length > 0 ? "true" : "false",
weekdays: formatListWithOrs(hass.locale, weekdays),
}); });
} }

View File

@ -104,22 +104,12 @@ class StepFlowForm extends LitElement {
</div>` </div>`
: nothing} : nothing}
<div class="buttons"> <div class="buttons">
${this._loading <ha-button @click=${this._submitStep} .loading=${this._loading}>
? html` ${this.flowConfig.renderShowFormStepSubmitButton(
<div class="submit-spinner"> this.hass,
<ha-spinner size="small"></ha-spinner> this.step
</div> )}
` </ha-button>
: html`
<div>
<ha-button @click=${this._submitStep}>
${this.flowConfig.renderShowFormStepSubmitButton(
this.hass,
this.step
)}
</ha-button>
</div>
`}
</div> </div>
`; `;
} }
@ -304,15 +294,6 @@ class StepFlowForm extends LitElement {
color: red; 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-alert,
ha-form { ha-form {
margin-top: 24px; margin-top: 24px;
@ -320,7 +301,7 @@ class StepFlowForm extends LitElement {
} }
.buttons { .buttons {
padding: 8px; padding: 16px;
} }
`, `,
]; ];

View File

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

View File

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

View File

@ -1,13 +1,13 @@
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { property, state } from "lit/decorators"; import { property, state } from "lit/decorators";
import { slugify } from "../../../common/string/slugify";
import "../../../components/buttons/ha-progress-button";
import "../../../components/ha-camera-stream"; import "../../../components/ha-camera-stream";
import type { CameraEntity } from "../../../data/camera"; import type { CameraEntity } from "../../../data/camera";
import type { HomeAssistant } from "../../../types";
import "../../../components/buttons/ha-progress-button";
import { UNAVAILABLE } from "../../../data/entity"; import { UNAVAILABLE } from "../../../data/entity";
import type { HomeAssistant } from "../../../types";
import { fileDownload } from "../../../util/file_download"; import { fileDownload } from "../../../util/file_download";
import { showToast } from "../../../util/toast"; import { showToast } from "../../../util/toast";
import { slugify } from "../../../common/string/slugify";
class MoreInfoCamera extends LitElement { class MoreInfoCamera extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@ -46,6 +46,7 @@ class MoreInfoCamera extends LitElement {
@click=${this._downloadSnapshot} @click=${this._downloadSnapshot}
.progress=${this._waiting} .progress=${this._waiting}
.disabled=${this.stateObj.state === UNAVAILABLE} .disabled=${this.stateObj.state === UNAVAILABLE}
appearance="filled"
> >
${this.hass.localize( ${this.hass.localize(
"ui.dialogs.more_info_control.camera.download_snapshot" "ui.dialogs.more_info_control.camera.download_snapshot"
@ -104,7 +105,7 @@ class MoreInfoCamera extends LitElement {
flex-wrap: wrap; flex-wrap: wrap;
justify-content: flex-end; justify-content: flex-end;
box-sizing: border-box; box-sizing: border-box;
padding: 12px; padding: 16px;
z-index: 1; z-index: 1;
gap: 8px; gap: 8px;
} }

View File

@ -51,7 +51,11 @@ class MoreInfoSiren extends LitElement {
.iconPathOff=${mdiVolumeOff} .iconPathOff=${mdiVolumeOff}
></ha-state-control-toggle> ></ha-state-control-toggle>
${allowAdvanced ${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")} ${this.hass.localize("ui.components.siren.advanced_controls")}
</ha-button>` </ha-button>`
: nothing} : nothing}

View File

@ -508,7 +508,7 @@ class MoreInfoUpdate extends LitElement {
flex-wrap: wrap; flex-wrap: wrap;
justify-content: flex-end; justify-content: flex-end;
box-sizing: border-box; box-sizing: border-box;
padding: 12px; padding: 16px;
z-index: 1; z-index: 1;
gap: 8px; gap: 8px;
} }

View File

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

View File

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

View File

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

View File

@ -90,11 +90,11 @@ export class CloudStepSignup extends LitElement {
? html`<ha-button ? html`<ha-button
@click=${this._handleResendVerifyEmail} @click=${this._handleResendVerifyEmail}
.disabled=${this._requestInProgress} .disabled=${this._requestInProgress}
appearance="plain"
>${this.hass.localize( >${this.hass.localize(
"ui.panel.config.cloud.register.resend_confirm_email" "ui.panel.config.cloud.register.resend_confirm_email"
)}</ha-button )}</ha-button
><ha-button ><ha-button
unelevated
@click=${this._login} @click=${this._login}
.disabled=${this._requestInProgress} .disabled=${this._requestInProgress}
>${this.hass.localize( >${this.hass.localize(
@ -104,12 +104,12 @@ export class CloudStepSignup extends LitElement {
: html`<ha-button : html`<ha-button
@click=${this._signIn} @click=${this._signIn}
.disabled=${this._requestInProgress} .disabled=${this._requestInProgress}
appearance="plain"
>${this.hass.localize( >${this.hass.localize(
"ui.panel.config.cloud.login.sign_in" "ui.panel.config.cloud.login.sign_in"
)}</ha-button )}</ha-button
> >
<ha-button <ha-button
unelevated
@click=${this._handleRegister} @click=${this._handleRegister}
.disabled=${this._requestInProgress} .disabled=${this._requestInProgress}
>${this.hass.localize("ui.common.next")}</ha-button >${this.hass.localize("ui.common.next")}</ha-button

View File

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

View File

@ -51,16 +51,16 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement {
)} )}
</p> </p>
<div class="footer"> <div class="footer">
<a <ha-button
appearance="plain"
href=${documentationUrl( href=${documentationUrl(
this.hass, this.hass,
"/voice_control/troubleshooting/#i-dont-get-a-voice-response" "/voice_control/troubleshooting/#i-dont-get-a-voice-response"
)} )}
><ha-button >
>${this.hass.localize( >${this.hass.localize(
"ui.panel.config.voice_assistants.satellite_wizard.check.help" "ui.panel.config.voice_assistants.satellite_wizard.check.help"
)}</ha-button )}</ha-button
></a
> >
<ha-button @click=${this._testConnection} <ha-button @click=${this._testConnection}
>${this.hass.localize( >${this.hass.localize(

View File

@ -80,7 +80,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
${this._detailState || "Installation can take several minutes"} ${this._detailState || "Installation can take several minutes"}
</p>` </p>`
: this._state === "ERROR" : this._state === "ERROR"
? html` <img ? html`<img
src="/static/images/voice-assistant/error.png" src="/static/images/voice-assistant/error.png"
alt="Casita Home Assistant error logo" 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" "ui.panel.config.voice_assistants.satellite_wizard.local.failed_secondary"
)} )}
</p> </p>
<ha-button @click=${this._prevStep} <ha-button
appearance="plain"
size="small"
@click=${this._prevStep}
>${this.hass.localize("ui.common.back")}</ha-button >${this.hass.localize("ui.common.back")}</ha-button
> >
<a <ha-button
href=${documentationUrl( href=${documentationUrl(
this.hass, this.hass,
"/voice_control/voice_remote_local_assistant/" "/voice_control/voice_remote_local_assistant/"
)} )}
target="_blank" target="_blank"
rel="noreferrer noopener" rel="noreferrer noopener"
size="small"
appearance="plain"
> >
<ha-button> <ha-svg-icon .path=${mdiOpenInNew} slot="start"></ha-svg-icon>
<ha-svg-icon .path=${mdiOpenInNew} slot="icon"></ha-svg-icon> ${this.hass.localize(
${this.hass.localize( "ui.panel.config.common.learn_more"
"ui.panel.config.common.learn_more" )}</ha-button
)}</ha-button >`
>
</a>`
: this._state === "NOT_SUPPORTED" : this._state === "NOT_SUPPORTED"
? html`<img ? html`<img
src="/static/images/voice-assistant/error.png" 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" "ui.panel.config.voice_assistants.satellite_wizard.local.not_supported_secondary"
)} )}
</p> </p>
<ha-button @click=${this._prevStep} <ha-button
appearance="plain"
size="small"
@click=${this._prevStep}
>${this.hass.localize("ui.common.back")}</ha-button >${this.hass.localize("ui.common.back")}</ha-button
> >
<a <ha-button
href=${documentationUrl( href=${documentationUrl(
this.hass, this.hass,
"/voice_control/voice_remote_local_assistant/" "/voice_control/voice_remote_local_assistant/"
)} )}
target="_blank" target="_blank"
rel="noreferrer noopener" rel="noreferrer noopener"
appearance="plain"
size="small"
> >
<ha-button> <ha-svg-icon .path=${mdiOpenInNew} slot="start"></ha-svg-icon>
<ha-svg-icon ${this.hass.localize(
.path=${mdiOpenInNew} "ui.panel.config.common.learn_more"
slot="icon" )}</ha-button
></ha-svg-icon> >`
${this.hass.localize(
"ui.panel.config.common.learn_more"
)}</ha-button
>
</a>`
: nothing} : nothing}
</div>`; </div>`;
} }

View File

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

View File

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

View File

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

View File

@ -271,7 +271,6 @@ export class HaVoiceCommandDialog extends LitElement {
margin-inline-start: -8px; margin-inline-start: -8px;
} }
ha-button-menu ha-button { ha-button-menu ha-button {
--ha-font-size-l: var(--ha-font-size-m);
--ha-button-height: 20px; --ha-button-height: 20px;
} }
ha-button-menu ha-button::part(base) { ha-button-menu ha-button::part(base) {

View File

@ -22,7 +22,9 @@ class HaInitPage extends LitElement {
<p class="retry-text"> <p class="retry-text">
Retrying in ${this._retryInSeconds} seconds... Retrying in ${this._retryInSeconds} seconds...
</p> </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") ${location.host.includes("ui.nabu.casa")
? html` ? html`
<p> <p>

View File

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

View File

@ -32,17 +32,16 @@ class OnboardingRestoreBackupNoCloudBackup extends LitElement {
<ha-button @click=${this._signOut}> <ha-button @click=${this._signOut}>
${this.localize("ui.panel.page-onboarding.restore.ha-cloud.sign_out")} ${this.localize("ui.panel.page-onboarding.restore.ha-cloud.sign_out")}
</ha-button> </ha-button>
<a <ha-button
href="https://www.nabucasa.com/config/backups/" href="https://www.nabucasa.com/config/backups/"
target="_blank" target="_blank"
rel="noreferrer noopener" rel="noreferrer noopener"
appearance="plain"
> >
<ha-button> ${this.localize(
${this.localize( "ui.panel.page-onboarding.restore.ha-cloud.learn_more"
"ui.panel.page-onboarding.restore.ha-cloud.learn_more" )}
)} </ha-button>
</ha-button>
</a>
</div> </div>
`; `;
} }

View File

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

View File

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

View File

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

View File

@ -220,32 +220,25 @@ export class DialogAddApplicationCredential extends LitElement {
helperPersistent helperPersistent
></ha-password-field> ></ha-password-field>
</div> </div>
${this._loading
? html` <ha-button
<div slot="primaryAction" class="submit-spinner"> appearance="plain"
<ha-spinner></ha-spinner> slot="secondaryAction"
</div> @click=${this._abortDialog}
` .disabled=${this._loading}
: html` >
<ha-button ${this.hass.localize("ui.common.cancel")}
appearance="plain" </ha-button>
slot="secondaryAction" <ha-button
@click=${this._abortDialog} slot="primaryAction"
> .disabled=${!this._domain || !this._clientId || !this._clientSecret}
${this.hass.localize("ui.common.cancel")} @click=${this._addApplicationCredential}
</ha-button> .loading=${this._loading}
<ha-button >
slot="primaryAction" ${this.hass.localize(
.disabled=${!this._domain || "ui.panel.config.application_credentials.editor.add"
!this._clientId || )}
!this._clientSecret} </ha-button>
@click=${this._addApplicationCredential}
>
${this.hass.localize(
"ui.panel.config.application_credentials.editor.add"
)}
</ha-button>
`}
</ha-dialog> </ha-dialog>
`; `;
} }

View File

@ -30,6 +30,8 @@ export default class HaAutomationAction extends LitElement {
@property({ type: Boolean }) public disabled = false; @property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public root = false;
@property({ attribute: false }) public actions!: Action[]; @property({ attribute: false }) public actions!: Action[];
@property({ attribute: false }) public highlightedActions?: Action[]; @property({ attribute: false }) public highlightedActions?: Action[];
@ -110,6 +112,8 @@ export default class HaAutomationAction extends LitElement {
<ha-button <ha-button
.disabled=${this.disabled} .disabled=${this.disabled}
@click=${this._addActionDialog} @click=${this._addActionDialog}
.appearance=${this.root ? "accent" : "filled"}
.size=${this.root ? "medium" : "small"}
> >
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon> <ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
${this.hass.localize( ${this.hass.localize(
@ -117,9 +121,10 @@ export default class HaAutomationAction extends LitElement {
)} )}
</ha-button> </ha-button>
<ha-button <ha-button
appearance="plain"
.disabled=${this.disabled} .disabled=${this.disabled}
@click=${this._addActionBuildingBlockDialog} @click=${this._addActionBuildingBlockDialog}
appearance="plain"
.size=${this.root ? "medium" : "small"}
> >
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon> <ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
${this.hass.localize( ${this.hass.localize(

View File

@ -381,7 +381,7 @@ class DialogAutomationSave extends LitElement implements HassDialog {
return { return {
type: "data", type: "data",
task: { 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}. 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. The name should be relevant to the ${term}'s purpose.

View File

@ -34,6 +34,8 @@ export default class HaAutomationCondition extends LitElement {
@property({ type: Boolean }) public disabled = false; @property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public root = false;
@state() private _showReorder = false; @state() private _showReorder = false;
@state() @state()
@ -159,6 +161,8 @@ export default class HaAutomationCondition extends LitElement {
<ha-button <ha-button
.disabled=${this.disabled} .disabled=${this.disabled}
@click=${this._addConditionDialog} @click=${this._addConditionDialog}
.appearance=${this.root ? "accent" : "filled"}
.size=${this.root ? "medium" : "small"}
> >
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon> <ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
${this.hass.localize( ${this.hass.localize(
@ -168,6 +172,7 @@ export default class HaAutomationCondition extends LitElement {
<ha-button <ha-button
.disabled=${this.disabled} .disabled=${this.disabled}
appearance="plain" appearance="plain"
.size=${this.root ? "medium" : "small"}
@click=${this._addConditionBuildingBlockDialog} @click=${this._addConditionBuildingBlockDialog}
> >
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon> <ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>

View File

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

View File

@ -100,14 +100,14 @@ export default class HaAutomationOption extends LitElement {
)} )}
<div class="buttons"> <div class="buttons">
<ha-button <ha-button
outlined appearance="filled"
.disabled=${this.disabled} .disabled=${this.disabled}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.add_option"
)}
@click=${this._addOption} @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> </ha-button>
</div> </div>
</div> </div>

View File

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

View File

@ -2,11 +2,13 @@ import type { PropertyValues } from "lit";
import { html, LitElement, nothing } from "lit"; import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { firstWeekdayIndex } from "../../../../../common/datetime/first_weekday";
import { fireEvent } from "../../../../../common/dom/fire_event"; import { fireEvent } from "../../../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../../../common/translations/localize"; import type { LocalizeFunc } from "../../../../../common/translations/localize";
import "../../../../../components/ha-form/ha-form"; import "../../../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../../components/ha-form/types"; import type { SchemaUnion } from "../../../../../components/ha-form/types";
import type { TimeTrigger } from "../../../../../data/automation"; import type { TimeTrigger } from "../../../../../data/automation";
import type { FrontendLocaleData } from "../../../../../data/translation";
import type { HomeAssistant } from "../../../../../types"; import type { HomeAssistant } from "../../../../../types";
import type { TriggerElement } from "../ha-automation-trigger-row"; import type { TriggerElement } from "../ha-automation-trigger-row";
import { computeDomain } from "../../../../../common/entity/compute_domain"; import { computeDomain } from "../../../../../common/entity/compute_domain";
@ -14,6 +16,7 @@ import { computeDomain } from "../../../../../common/entity/compute_domain";
const MODE_TIME = "time"; const MODE_TIME = "time";
const MODE_ENTITY = "entity"; const MODE_ENTITY = "entity";
const VALID_DOMAINS = ["sensor", "input_datetime"]; const VALID_DOMAINS = ["sensor", "input_datetime"];
const DAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"] as const;
@customElement("ha-automation-trigger-time") @customElement("ha-automation-trigger-time")
export class HaTimeTrigger extends LitElement implements TriggerElement { export class HaTimeTrigger extends LitElement implements TriggerElement {
@ -35,9 +38,14 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
private _schema = memoizeOne( private _schema = memoizeOne(
( (
localize: LocalizeFunc, localize: LocalizeFunc,
locale: FrontendLocaleData,
inputMode: typeof MODE_TIME | typeof MODE_ENTITY 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", name: "mode",
type: "select", type: "select",
@ -73,7 +81,21 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
}, },
{ name: "offset", selector: { text: {} } }, { name: "offset", selector: { text: {} } },
] as const)), ] 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) { public willUpdate(changedProperties: PropertyValues) {
@ -95,12 +117,14 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
inputMode: undefined | typeof MODE_ENTITY | typeof MODE_TIME, inputMode: undefined | typeof MODE_ENTITY | typeof MODE_TIME,
at: at:
| string | 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; mode: typeof MODE_TIME | typeof MODE_ENTITY;
entity: string | undefined; entity: string | undefined;
time: string | undefined; time: string | undefined;
offset: string | undefined; offset: string | undefined;
weekday: string | string[] | undefined;
} => { } => {
const entity = const entity =
typeof at === "object" typeof at === "object"
@ -116,6 +140,7 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
entity, entity,
time, time,
offset, offset,
weekday,
}; };
} }
); );
@ -127,8 +152,12 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
return nothing; return nothing;
} }
const data = this._data(this._inputMode, at); const data = this._data(this._inputMode, at, this.trigger.weekday);
const schema = this._schema(this.hass.localize, data.mode); const schema = this._schema(
this.hass.localize,
this.hass.locale,
data.mode
);
return html` return html`
<ha-form <ha-form
@ -146,22 +175,36 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
ev.stopPropagation(); ev.stopPropagation();
const newValue = { ...ev.detail.value }; const newValue = { ...ev.detail.value };
this._inputMode = newValue.mode; this._inputMode = newValue.mode;
const weekday = newValue.weekday;
delete newValue.weekday;
if (newValue.mode === MODE_TIME) { if (newValue.mode === MODE_TIME) {
delete newValue.entity; delete newValue.entity;
delete newValue.offset; delete newValue.offset;
} else { } else {
delete newValue.time; 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", { fireEvent(this, "value-changed", {
value: { value: triggerUpdate,
...this.trigger,
at: newValue.offset
? {
entity_id: newValue.entity,
offset: newValue.offset,
}
: newValue.entity || newValue.time,
},
}); });
} }
@ -173,6 +216,10 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
return this.hass.localize( return this.hass.localize(
`ui.panel.config.automation.editor.triggers.type.time.at` `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( return this.hass.localize(
`ui.panel.config.automation.editor.triggers.type.time.${schema.name}` `ui.panel.config.automation.editor.triggers.type.time.${schema.name}`

View File

@ -99,13 +99,11 @@ class HaBackupOverviewBackups extends LitElement {
</ha-md-list> </ha-md-list>
</div> </div>
<div class="card-actions"> <div class="card-actions">
<a href="/config/backup/backups?type=all"> <ha-button appearance="filled" href="/config/backup/backups?type=all">
<ha-button appearance="filled"> ${this.hass.localize(
${this.hass.localize( "ui.panel.config.backup.overview.backups.show_all"
"ui.panel.config.backup.overview.backups.show_all" )}
)} </ha-button>
</ha-button>
</a>
</div> </div>
</ha-card> </ha-card>
`; `;

View File

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

View File

@ -128,7 +128,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
<ha-button <ha-button
@click=${this._submit} @click=${this._submit}
.disabled=${!this._newEncryptionKey} .disabled=${!this._newEncryptionKey}
class="danger" variant="danger"
> >
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.backup.dialogs.change_encryption_key.actions.change" "ui.panel.config.backup.dialogs.change_encryption_key.actions.change"
@ -176,7 +176,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
)} )}
</span> </span>
<ha-button slot="end" @click=${this._downloadOld}> <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( ${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_old_emergency_kit_action" "ui.panel.config.backup.encryption_key.download_old_emergency_kit_action"
)} )}
@ -211,7 +211,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
)} )}
</span> </span>
<ha-button slot="end" @click=${this._downloadNew}> <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( ${this.hass.localize(
"ui.panel.config.backup.encryption_key.download_emergency_kit_action" "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-leading-space: 0;
--md-list-item-trailing-space: 0; --md-list-item-trailing-space: 0;
} }
ha-button.danger {
--mdc-theme-primary: var(--error-color);
}
.encryption-key { .encryption-key {
border: 1px solid var(--divider-color); border: 1px solid var(--divider-color);
background-color: var(--primary-background-color); background-color: var(--primary-background-color);

View File

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

View File

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

View File

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

View File

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

View File

@ -113,7 +113,10 @@ export class DialogUploadBackup
></ha-file-upload> ></ha-file-upload>
</div> </div>
<div slot="actions"> <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 >${this.hass.localize("ui.common.cancel")}</ha-button
> >
<ha-button <ha-button

View File

@ -417,7 +417,11 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) {
<div slot="selection-bar"> <div slot="selection-bar">
${!this.narrow ${!this.narrow
? html` ? html`
<ha-button @click=${this._deleteSelected} class="warning"> <ha-button
appearance="plain"
@click=${this._deleteSelected}
variant="danger"
>
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.backup.backups.delete_selected" "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" "ui.panel.config.backup.location.encryption.location_encrypted_cloud_description"
)} )}
</span> </span>
<a <ha-button
href="https://www.nabucasa.com/config/backups/" href="https://www.nabucasa.com/config/backups/"
target="_blank" target="_blank"
slot="end" slot="end"
rel="noreferrer noopener" rel="noreferrer noopener"
appearance="plain"
size="small"
> >
<ha-button> ${this.hass.localize(
${this.hass.localize( "ui.panel.config.backup.location.encryption.location_encrypted_cloud_learn_more"
"ui.panel.config.backup.location.encryption.location_encrypted_cloud_learn_more" )}
)} </ha-button>
</ha-button>
</a>
</ha-md-list-item> </ha-md-list-item>
` `
: encrypted : encrypted

View File

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

View File

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

View File

@ -77,7 +77,9 @@ export class DialogSupportPackage extends LitElement {
</ha-alert> </ha-alert>
<hr /> <hr />
<div class="actions"> <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> <ha-button @click=${this._download}>Download</ha-button>
</div> </div>
</div> </div>

View File

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

View File

@ -249,9 +249,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
.buttons { .buttons {
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
padding: 8px; padding: 16px;
justify-content: space-between; 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); background-color: var(--mdc-theme-surface, #fff);
border-top: 1px solid var(--divider-color); border-top: 1px solid var(--divider-color);
position: sticky; position: sticky;

View File

@ -92,8 +92,9 @@ class DialogScheduleBlockInfo extends LitElement {
</div> </div>
<ha-button <ha-button
slot="secondaryAction" slot="secondaryAction"
class="warning"
@click=${this._deleteBlock} @click=${this._deleteBlock}
appearance="plain"
variant="danger"
> >
${this.hass!.localize("ui.common.delete")} ${this.hass!.localize("ui.common.delete")}
</ha-button> </ha-button>

View File

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

View File

@ -207,7 +207,7 @@ class HaConfigEntryRow extends LitElement {
: nothing} : nothing}
</div> </div>
${item.disabled_by === "user" ${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")} ${this.hass.localize("ui.common.enable")}
</ha-button>` </ha-button>`
: configPanel && : configPanel &&

View File

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

View File

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

View File

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

View File

@ -114,7 +114,11 @@ class DialogSSDPDiscoveryInfo extends LitElement implements HassDialog {
</tbody> </tbody>
</table> </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")} ${this.hass.localize("ui.panel.config.ssdp.copy_to_clipboard")}
</ha-button> </ha-button>
</ha-dialog> </ha-dialog>

View File

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

View File

@ -176,7 +176,7 @@ export class ZHAAddGroupPage extends LitElement {
} }
.buttons { .buttons {
align-items: flex-end; align-items: flex-end;
padding: 8px; padding: 16px;
} }
.buttons .warning { .buttons .warning {
--mdc-theme-primary: var(--error-color); --mdc-theme-primary: var(--error-color);

View File

@ -305,7 +305,7 @@ export class ZHAGroupPage extends LitElement {
} }
.buttons { .buttons {
align-items: flex-end; align-items: flex-end;
padding: 8px; padding: 16px;
} }
.buttons .warning { .buttons .warning {
--mdc-theme-primary: var(--error-color); --mdc-theme-primary: var(--error-color);

View File

@ -33,13 +33,11 @@ export class ZWaveJsAddNodeFailed extends LitElement {
</div>` </div>`
: nothing} : nothing}
${this.device?.id ${this.device?.id
? html`<a href=${`/config/devices/device/${this.device.id}`}> ? html`<ha-button href=${`/config/devices/device/${this.device.id}`}>
<ha-button> ${this.hass.localize(
${this.hass.localize( "ui.panel.config.zwave_js.add_node.view_device"
"ui.panel.config.zwave_js.add_node.view_device" )}
)} </ha-button>`
</ha-button>
</a>`
: nothing} : nothing}
`; `;
} }

View File

@ -219,7 +219,11 @@ class DialogZWaveJSRemoveNode extends LitElement {
if (this._step === "start_removal") { if (this._step === "start_removal") {
return html` return html`
<ha-button slot="secondaryAction" @click=${this.closeDialog}> <ha-button
appearance="plain"
slot="secondaryAction"
@click=${this.closeDialog}
>
${this.hass.localize("ui.common.cancel")} ${this.hass.localize("ui.common.cancel")}
</ha-button> </ha-button>
<ha-button <ha-button
@ -234,7 +238,11 @@ class DialogZWaveJSRemoveNode extends LitElement {
if (this._step === "start_exclusion") { if (this._step === "start_exclusion") {
return html` return html`
<ha-button slot="secondaryAction" @click=${this.closeDialog}> <ha-button
appearance="plain"
slot="secondaryAction"
@click=${this.closeDialog}
>
${this.hass.localize("ui.common.cancel")} ${this.hass.localize("ui.common.cancel")}
</ha-button> </ha-button>
<ha-button <ha-button

View File

@ -92,12 +92,12 @@ class ZWaveJSCustomParam extends LitElement {
</div> </div>
<div class="custom-config-buttons"> <div class="custom-config-buttons">
${this._isLoading ? html`<ha-spinner></ha-spinner>` : nothing} ${this._isLoading ? html`<ha-spinner></ha-spinner>` : nothing}
<ha-button @click=${this._getCustomConfigValue}> <ha-button appearance="plain" @click=${this._getCustomConfigValue}>
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.zwave_js.node_config.get_value" "ui.panel.config.zwave_js.node_config.get_value"
)} )}
</ha-button> </ha-button>
<ha-button @click=${this._setCustomConfigValue}> <ha-button appearance="plain" @click=${this._setCustomConfigValue}>
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.zwave_js.node_config.set_value" "ui.panel.config.zwave_js.node_config.set_value"
)} )}

View File

@ -94,7 +94,7 @@ class DownloadLogsDialog extends LitElement {
</ha-md-select> </ha-md-select>
</div> </div>
<div slot="actions"> <div slot="actions">
<ha-button @click=${this.closeDialog}> <ha-button appearance="plain" @click=${this.closeDialog}>
${this.hass.localize("ui.common.cancel")} ${this.hass.localize("ui.common.cancel")}
</ha-button> </ha-button>
<ha-button @click=${this._downloadLogs}> <ha-button @click=${this._downloadLogs}>

View File

@ -312,17 +312,16 @@ class ErrorLogCard extends LitElement {
!this._scrolledToBottomController.value) || !this._scrolledToBottomController.value) ||
false, false,
})}" })}"
size="small"
appearance="filled"
@click=${this._scrollToBottom} @click=${this._scrollToBottom}
> >
<ha-svg-icon <ha-svg-icon
.path=${mdiArrowCollapseDown} .path=${mdiArrowCollapseDown}
slot="icon" slot="start"
></ha-svg-icon> ></ha-svg-icon>
${localize("ui.panel.config.logs.scroll_down_button")} ${localize("ui.panel.config.logs.scroll_down_button")}
<ha-svg-icon <ha-svg-icon .path=${mdiArrowCollapseDown} slot="end"></ha-svg-icon>
.path=${mdiArrowCollapseDown}
slot="trailingIcon"
></ha-svg-icon>
</ha-button> </ha-button>
${streaming && this._boot === 0 && !this._error ${streaming && this._boot === 0 && !this._error
? html`<div class="live-indicator"> ? html`<div class="live-indicator">
@ -823,25 +822,15 @@ class ErrorLogCard extends LitElement {
} }
.new-logs-indicator { .new-logs-indicator {
--mdc-theme-primary: var(--text-primary-color);
overflow: hidden; overflow: hidden;
position: absolute; position: absolute;
bottom: 0; bottom: 4px;
left: 0;
right: 0;
height: 0; height: 0;
background-color: var(--primary-color);
border-radius: 8px;
transition: height 0.4s ease-out; transition: height 0.4s ease-out;
display: flex;
justify-content: space-between;
align-items: center;
} }
.new-logs-indicator.visible { .new-logs-indicator.visible {
height: 24px; height: 32px;
} }
.error { .error {

View File

@ -284,7 +284,7 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
.url=${internalUrl} .url=${internalUrl}
@click=${this._copyURL} @click=${this._copyURL}
> >
<ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon> <ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
${this.hass.localize("ui.panel.config.common.copy_link")} ${this.hass.localize("ui.panel.config.common.copy_link")}
</ha-button> </ha-button>
</div> </div>

View File

@ -145,13 +145,12 @@ export class HassioNetwork extends LitElement {
class="scan" class="scan"
@click=${this._scanForAP} @click=${this._scanForAP}
.disabled=${this._scanning} .disabled=${this._scanning}
.loading=${this._scanning}
> >
${this._scanning ${this.hass.localize(
? html`<ha-spinner size="small"> </ha-spinner>` "ui.panel.config.network.supervisor.scan_ap"
: this.hass.localize( )}
"ui.panel.config.network.supervisor.scan_ap" <ha-svg-icon slot="start" .path=${mdiWifi}></ha-svg-icon>
)}
<ha-svg-icon slot="icon" .path=${mdiWifi}></ha-svg-icon>
</ha-button> </ha-button>
${this._accessPoints.length ${this._accessPoints.length
? html` ? html`
@ -261,6 +260,9 @@ export class HassioNetwork extends LitElement {
: nothing} : nothing}
</div> </div>
<div class="card-actions"> <div class="card-actions">
<ha-button appearance="plain" @click=${this._clear}>
${this.hass.localize("ui.panel.config.network.supervisor.reset")}
</ha-button>
<ha-button <ha-button
.loading=${this._processing} .loading=${this._processing}
@click=${this._updateNetwork} @click=${this._updateNetwork}
@ -268,9 +270,6 @@ export class HassioNetwork extends LitElement {
> >
${this.hass.localize("ui.common.save")} ${this.hass.localize("ui.common.save")}
</ha-button> </ha-button>
<ha-button appearance="plain" @click=${this._clear}>
${this.hass.localize("ui.panel.config.network.supervisor.reset")}
</ha-button>
</div>`; </div>`;
} }

View File

@ -196,6 +196,7 @@ export class HaManualScriptEditor extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.narrow=${this.narrow} .narrow=${this.narrow}
.disabled=${this.disabled} .disabled=${this.disabled}
root
></ha-automation-action> ></ha-automation-action>
`; `;
} }

View File

@ -91,12 +91,10 @@ export class AssistPipelineDetailTTS extends LitElement {
${ ${
this.data?.tts_engine this.data?.tts_engine
? html`<div class="footer"> ? html`<div class="footer">
<ha-button <ha-button @click=${this._preview}>
.label=${this.hass.localize( ${this.hass.localize(
"ui.panel.config.voice_assistants.assistants.pipeline.detail.try_tts" "ui.panel.config.voice_assistants.assistants.pipeline.detail.try_tts"
)} )}
@click=${this._preview}
>
</ha-button> </ha-button>
</div>` </div>`
: nothing : nothing

View File

@ -61,10 +61,12 @@ export class AssistPipelineRunDebug extends LitElement {
slot="toolbar-icon" slot="toolbar-icon"
@click=${this._clearConversation} @click=${this._clearConversation}
.disabled=${!this._finished} .disabled=${!this._finished}
appearance="plain"
> >
${this.hass.localize("ui.common.clear")} ${this.hass.localize("ui.common.clear")}
</ha-button> </ha-button>
<ha-button <ha-button
appearance="plain"
slot="toolbar-icon" slot="toolbar-icon"
@click=${this._downloadConversation} @click=${this._downloadConversation}
> >
@ -83,13 +85,16 @@ export class AssistPipelineRunDebug extends LitElement {
@value-changed=${this._pipelinePicked} @value-changed=${this._pipelinePicked}
></ha-assist-pipeline-picker> ></ha-assist-pipeline-picker>
<div class="start-buttons"> <div class="start-buttons">
<ha-button raised @click=${this._runTextPipeline}> <ha-button
appearance="filled"
@click=${this._runTextPipeline}
>
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.voice_assistants.debug.pipeline.run_text_pipeline" "ui.panel.config.voice_assistants.debug.pipeline.run_text_pipeline"
)} )}
</ha-button> </ha-button>
<ha-button <ha-button
raised appearance="filled"
@click=${this._runAudioPipeline} @click=${this._runAudioPipeline}
.disabled=${!window.isSecureContext || .disabled=${!window.isSecureContext ||
// @ts-ignore-next-line // @ts-ignore-next-line
@ -100,7 +105,7 @@ export class AssistPipelineRunDebug extends LitElement {
)} )}
</ha-button> </ha-button>
<ha-button <ha-button
raised appearance="filled"
@click=${this._runAudioWakeWordPipeline} @click=${this._runAudioWakeWordPipeline}
.disabled=${!window.isSecureContext || .disabled=${!window.isSecureContext ||
// @ts-ignore-next-line // @ts-ignore-next-line
@ -135,13 +140,19 @@ export class AssistPipelineRunDebug extends LitElement {
? this._pipelineRuns[0].init_options!.start_stage === ? this._pipelineRuns[0].init_options!.start_stage ===
"wake_word" "wake_word"
? html` ? html`
<ha-button @click=${this._runAudioWakeWordPipeline}> <ha-button
appearance="filled"
@click=${this._runAudioWakeWordPipeline}
>
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.voice_assistants.debug.pipeline.continue_listening" "ui.panel.config.voice_assistants.debug.pipeline.continue_listening"
)} )}
</ha-button> </ha-button>
` `
: html`<ha-button @click=${this._runAudioPipeline}> : html`<ha-button
appearance="filled"
@click=${this._runAudioPipeline}
>
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.voice_assistants.debug.pipeline.continue_talking" "ui.panel.config.voice_assistants.debug.pipeline.continue_talking"
)} )}

View File

@ -182,13 +182,11 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.voice_assistants.assistants.pipeline.detail.no_cloud_message" "ui.panel.config.voice_assistants.assistants.pipeline.detail.no_cloud_message"
)} )}
<a href="/config/cloud" slot="action"> <ha-button size="small" href="/config/cloud" slot="action">
<ha-button> ${this.hass.localize(
${this.hass.localize( "ui.panel.config.voice_assistants.assistants.pipeline.detail.no_cloud_action"
"ui.panel.config.voice_assistants.assistants.pipeline.detail.no_cloud_action" )}
)} </ha-button>
</ha-button>
</a>
</ha-alert> </ha-alert>
` `
: nothing} : nothing}

View File

@ -164,12 +164,16 @@ class HaPanelDevAssist extends SubscribeMixin(LitElement) {
${this._results.length ${this._results.length
? html` ? html`
<div class="result-toolbar"> <div class="result-toolbar">
<ha-button outlined @click=${this._clear} variant="danger"> <ha-button
<ha-svg-icon slot="icon" .path=${mdiTrashCan}></ha-svg-icon> appearance="filled"
@click=${this._clear}
variant="danger"
>
<ha-svg-icon slot="start" .path=${mdiTrashCan}></ha-svg-icon>
${this.hass.localize("ui.common.clear")} ${this.hass.localize("ui.common.clear")}
</ha-button> </ha-button>
<ha-button outlined @click=${this._download}> <ha-button appearance="filled" @click=${this._download}>
<ha-svg-icon slot="icon" .path=${mdiDownload}></ha-svg-icon> <ha-svg-icon slot="start" .path=${mdiDownload}></ha-svg-icon>
${this.hass.localize( ${this.hass.localize(
"ui.panel.developer-tools.tabs.assist.download_results" "ui.panel.developer-tools.tabs.assist.download_results"
)} )}

View File

@ -287,6 +287,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
slot="secondaryAction" slot="secondaryAction"
.disabled=${this._busy} .disabled=${this._busy}
@click=${this._clearChosenStatistic} @click=${this._clearChosenStatistic}
appearance="plain"
> >
${this.hass.localize("ui.common.back")}</ha-button ${this.hass.localize("ui.common.back")}</ha-button
> >

View File

@ -1,15 +1,15 @@
import { html, LitElement, nothing } from "lit";
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain"; import { computeDomain } from "../../../common/entity/compute_domain";
import "../../../components/ha-control-button"; import "../../../components/ha-control-button";
import "../../../components/ha-control-button-group"; import "../../../components/ha-control-button-group";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature } from "../types"; import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { cardFeatureStyles } from "./common/card-feature-styles"; import { cardFeatureStyles } from "./common/card-feature-styles";
import type { import type {
LovelaceCardFeatureContext,
ButtonCardFeatureConfig, ButtonCardFeatureConfig,
LovelaceCardFeatureContext,
} from "./types"; } from "./types";
export const supportsButtonCardFeature = ( export const supportsButtonCardFeature = (
@ -89,6 +89,11 @@ class HuiButtonCardFeature extends LitElement implements LovelaceCardFeature {
} }
static styles = cardFeatureStyles; static styles = cardFeatureStyles;
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
await import("../editor/config-elements/hui-button-card-feature-editor");
return document.createElement("hui-button-card-feature-editor");
}
} }
declare global { declare global {

View File

@ -154,14 +154,11 @@ export class HuiDialogSuggestCard extends LitElement {
slot="primaryAction" slot="primaryAction"
.disabled=${this._saving} .disabled=${this._saving}
@click=${this._save} @click=${this._save}
.loading=${this._saving}
> >
${this._saving ${this.hass!.localize(
? html` "ui.panel.lovelace.editor.suggest_card.add"
<ha-spinner aria-label="Saving" size="small"></ha-spinner> )}
`
: this.hass!.localize(
"ui.panel.lovelace.editor.suggest_card.add"
)}
</ha-button> </ha-button>
` `
: nothing} : nothing}

View File

@ -1,10 +1,11 @@
import { html, LitElement, nothing } from "lit"; import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-form/ha-form";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import type { ButtonCardFeatureConfig } from "../../card-features/types"; import type { ButtonCardFeatureConfig } from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types"; import type { LovelaceCardFeatureEditor } from "../../types";
import "../../../../components/ha-form/ha-form";
import type { HaFormSchema } from "../../../../components/ha-form/types";
@customElement("hui-button-card-feature-editor") @customElement("hui-button-card-feature-editor")
export class HuiButtonCardFeatureEditor export class HuiButtonCardFeatureEditor
@ -19,14 +20,15 @@ export class HuiButtonCardFeatureEditor
this._config = config; this._config = config;
} }
private _schema: HaFormSchema[] = [ private _schema = memoizeOne((localize: LocalizeFunc) => [
{ {
name: "action_name", name: "action_name",
default: localize("ui.card.button.press"),
selector: { selector: {
text: {}, text: {},
}, },
}, },
]; ]);
protected render() { protected render() {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
@ -37,12 +39,15 @@ export class HuiButtonCardFeatureEditor
<ha-form <ha-form
.hass=${this.hass} .hass=${this.hass}
.data=${this._config} .data=${this._config}
.schema=${this._schema} .schema=${this._schema(this.hass.localize)}
.computeLabel=${this._computeLabel}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></ha-form> ></ha-form>
`; `;
} }
private _computeLabel = () => this.hass.localize("ui.common.name");
private _valueChanged(ev: CustomEvent) { private _valueChanged(ev: CustomEvent) {
ev.stopPropagation(); ev.stopPropagation();
this.dispatchEvent( this.dispatchEvent(

View File

@ -372,14 +372,9 @@ export class HuiCardFeaturesEditor extends LitElement {
@action=${this._addFeature} @action=${this._addFeature}
@closed=${stopPropagation} @closed=${stopPropagation}
> >
<ha-button <ha-button slot="trigger" appearance="filled" size="small">
slot="trigger" <ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
outlined ${this.hass!.localize(`ui.panel.lovelace.editor.features.add`)}
.label=${this.hass!.localize(
`ui.panel.lovelace.editor.features.add`
)}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button> </ha-button>
${types.map( ${types.map(
(type) => html` (type) => html`

View File

@ -120,11 +120,11 @@ export class HuiHeadingBadgesEditor extends LitElement {
<div class="add-container"> <div class="add-container">
<ha-button <ha-button
data-add-entity data-add-entity
outlined appearance="filled"
.label=${this.hass!.localize(`ui.panel.lovelace.editor.entities.add`)}
@click=${this._addEntity} @click=${this._addEntity}
> >
<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.lovelace.editor.entities.add`)}
</ha-button> </ha-button>
${this._renderPicker()} ${this._renderPicker()}
</div> </div>

View File

@ -110,7 +110,11 @@ export class HuiDialogSelectDashboard extends LitElement {
</div>`} </div>`}
</div> </div>
<div slot="actions"> <div slot="actions">
<ha-button @click=${this.closeDialog} .disabled=${this._saving}> <ha-button
appearance="plain"
@click=${this.closeDialog}
.disabled=${this._saving}
>
${this.hass!.localize("ui.common.cancel")} ${this.hass!.localize("ui.common.cancel")}
</ha-button> </ha-button>
<ha-button <ha-button

View File

@ -253,12 +253,13 @@ export class HuiDialogEditView extends LitElement {
"ui.panel.lovelace.editor.edit_view.card_to_section_convert" "ui.panel.lovelace.editor.edit_view.card_to_section_convert"
)} )}
<ha-button <ha-button
size="small"
slot="action" slot="action"
.label=${this.hass!.localize(
"ui.panel.lovelace.editor.edit_view.convert_view"
)}
@click=${this._convertToSection} @click=${this._convertToSection}
> >
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_view.convert_view"
)}
</ha-button> </ha-button>
</ha-alert> </ha-alert>
` `

View File

@ -139,10 +139,8 @@ export class HuiDialogEditViewHeader extends LitElement {
slot="primaryAction" slot="primaryAction"
.disabled=${!this._config || this._saving || !this._dirty} .disabled=${!this._config || this._saving || !this._dirty}
@click=${this._save} @click=${this._save}
.loading=${this._saving}
> >
${this._saving
? html`<ha-spinner size="small" aria-label="Saving"></ha-spinner>`
: nothing}
${this.hass!.localize("ui.common.save")}</ha-button ${this.hass!.localize("ui.common.save")}</ha-button
> >
</ha-dialog> </ha-dialog>

View File

@ -703,17 +703,6 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
ha-list-item[selected] { ha-list-item[selected] {
font-weight: var(--ha-font-weight-bold); font-weight: var(--ha-font-weight-bold);
} }
span[slot="icon"] {
display: flex;
align-items: center;
}
ha-svg-icon[slot="trailingIcon"] {
margin-inline-start: 8px !important;
margin-inline-end: 0px !important;
direction: var(--direction);
}
`; `;
} }

View File

@ -44,38 +44,15 @@ import type { BarMediaPlayer } from "./ha-bar-media-player";
import { showWebBrowserPlayMediaDialog } from "./show-media-player-dialog"; import { showWebBrowserPlayMediaDialog } from "./show-media-player-dialog";
const createMediaPanelUrl = (entityId: string, items: MediaPlayerItemId[]) => { const createMediaPanelUrl = (entityId: string, items: MediaPlayerItemId[]) => {
const path = `/media-browser/${entityId}`; let path = `/media-browser/${entityId}`;
if (items.length <= 1) { for (const item of items.slice(1)) {
return path; path +=
"/" +
encodeURIComponent(`${item.media_content_type},${item.media_content_id}`);
} }
const navigateIds = items return path;
.slice(1)
.map((item) =>
encodeURIComponent(`${item.media_content_type},${item.media_content_id}`)
);
const urlparams = new URLSearchParams();
urlparams.set("ids", navigateIds.join(","));
return path + "?" + urlparams.toString();
}; };
const decodeNavigateIds = (
navigateIdsEncoded: string[]
): MediaPlayerItemId[] => [
{
media_content_id: undefined,
media_content_type: undefined,
},
...navigateIdsEncoded.map((navigateId) => {
const decoded = decodeURIComponent(navigateId);
// Don't use split because media_content_id could contain commas
const delimiter = decoded.indexOf(",");
return {
media_content_type: decoded.substring(0, delimiter),
media_content_id: decoded.substring(delimiter + 1),
};
}),
];
@customElement("ha-panel-media-browser") @customElement("ha-panel-media-browser")
class PanelMediaBrowser extends LitElement { class PanelMediaBrowser extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@ -233,19 +210,9 @@ class PanelMediaBrowser extends LitElement {
return; return;
} }
const [routePlayer, ...paths] = this.route.path.substring(1).split("/"); const [routePlayer, ...navigateIdsEncoded] = this.route.path
.substring(1)
const navigateIdsEncoded = .split("/");
new URLSearchParams(location.search).get("ids")?.split(",") || [];
// Backwards compatibility with old URLs
if (navigateIdsEncoded.length === 0 && paths.length > 0) {
const navigateIds = decodeNavigateIds(paths);
navigate(createMediaPanelUrl(this._entityId, navigateIds), {
replace: true,
});
return;
}
if (routePlayer !== this._entityId) { if (routePlayer !== this._entityId) {
// Detect if picked player doesn't exist (anymore) // Detect if picked player doesn't exist (anymore)
@ -268,7 +235,21 @@ class PanelMediaBrowser extends LitElement {
this._entityId = routePlayer; this._entityId = routePlayer;
} }
this._navigateIds = decodeNavigateIds(navigateIdsEncoded); this._navigateIds = [
{
media_content_type: undefined,
media_content_id: undefined,
},
...navigateIdsEncoded.map((navigateId) => {
const decoded = decodeURIComponent(navigateId);
// Don't use split because media_content_id could contain commas
const delimiter = decoded.indexOf(",");
return {
media_content_type: decoded.substring(0, delimiter),
media_content_id: decoded.substring(delimiter + 1),
};
}),
];
this._currentItem = undefined; this._currentItem = undefined;
} }
@ -276,7 +257,6 @@ class PanelMediaBrowser extends LitElement {
navigate( navigate(
createMediaPanelUrl(this._entityId, this._navigateIds.slice(0, -1)) createMediaPanelUrl(this._entityId, this._navigateIds.slice(0, -1))
); );
this.requestUpdate("route");
} }
private _mediaBrowsed(ev: { detail: HASSDomEvents["media-browsed"] }) { private _mediaBrowsed(ev: { detail: HASSDomEvents["media-browsed"] }) {
@ -288,7 +268,6 @@ class PanelMediaBrowser extends LitElement {
navigate(createMediaPanelUrl(this._entityId, ev.detail.ids), { navigate(createMediaPanelUrl(this._entityId, ev.detail.ids), {
replace: ev.detail.replace, replace: ev.detail.replace,
}); });
this.requestUpdate("route");
} }
private async _mediaPicked( private async _mediaPicked(

View File

@ -187,11 +187,6 @@ class HaMfaModuleSetupFlow extends LitElement {
padding: 10px 100px 34px; padding: 10px 100px 34px;
text-align: center; text-align: center;
} }
.submit-spinner {
margin-right: 16px;
margin-inline-end: 16px;
margin-inline-start: initial;
}
`, `,
]; ];
} }

View File

@ -191,10 +191,7 @@ class PanelTodo extends LitElement {
: this._entityId : this._entityId
: ""} : ""}
</div> </div>
<ha-svg-icon <ha-svg-icon slot="end" .path=${mdiChevronDown}></ha-svg-icon>
slot="trailingIcon"
.path=${mdiChevronDown}
></ha-svg-icon>
</ha-button> </ha-button>
${listItems} ${listItems}
${this.hass.user?.is_admin ${this.hass.user?.is_admin
@ -395,27 +392,7 @@ class PanelTodo extends LitElement {
max-width: 100%; max-width: 100%;
} }
ha-button-menu ha-button { ha-button-menu ha-button {
--button-slot-container-overflow: hidden; --ha-font-size-m: var(--ha-font-size-l);
max-width: 100%;
--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,
500
);
--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-button-menu ha-button div { ha-button-menu ha-button div {
text-overflow: ellipsis; text-overflow: ellipsis;

View File

@ -4017,9 +4017,19 @@
"entity": "Entity with timestamp", "entity": "Entity with timestamp",
"offset_by": "offset by {offset}", "offset_by": "offset by {offset}",
"mode": "Mode", "mode": "Mode",
"weekday": "Weekdays",
"weekdays": {
"mon": "[%key:ui::weekdays::monday%]",
"tue": "[%key:ui::weekdays::tuesday%]",
"wed": "[%key:ui::weekdays::wednesday%]",
"thu": "[%key:ui::weekdays::thursday%]",
"fri": "[%key:ui::weekdays::friday%]",
"sat": "[%key:ui::weekdays::saturday%]",
"sun": "[%key:ui::weekdays::sunday%]"
},
"description": { "description": {
"picker": "At a specific time, or on a specific date.", "picker": "At a specific time, or on a specific date.",
"full": "When the time is equal to {time}" "full": "When the time is equal to {time}{hasWeekdays, select, \n true { on {weekdays}} \n other {}\n}"
} }
}, },
"time_pattern": { "time_pattern": {