20221230.0 (#14925)

This commit is contained in:
Bram Kragten 2022-12-30 13:39:58 +01:00 committed by GitHub
commit 7cde3b66dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 95 additions and 13 deletions

View File

@ -98,7 +98,9 @@ const alerts: {
description: "Alert with slotted image", description: "Alert with slotted image",
type: "warning", type: "warning",
iconSlot: html`<span slot="icon" class="image" iconSlot: html`<span slot="icon" class="image"
><img src="https://www.home-assistant.io/images/home-assistant-logo.svg" ><img
alt="Home Assistant logo"
src="https://www.home-assistant.io/images/home-assistant-logo.svg"
/></span>`, /></span>`,
}, },
{ {

View File

@ -404,6 +404,7 @@ class HassioAddonInfo extends LitElement {
? html` ? html`
<img <img
class="logo" class="logo"
alt=""
src="/api/hassio/addons/${this.addon.slug}/logo" src="/api/hassio/addons/${this.addon.slug}/logo"
/> />
` `

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "home-assistant-frontend" name = "home-assistant-frontend"
version = "20221228.0" version = "20221230.0"
license = {text = "Apache-2.0"} license = {text = "Apache-2.0"}
description = "The Home Assistant frontend" description = "The Home Assistant frontend"
readme = "README.md" readme = "README.md"

View File

@ -16,7 +16,11 @@ const rowRenderer: ComboBoxLitRenderer<HassioAddonInfo> = (
<span>${item.name}</span> <span>${item.name}</span>
<span slot="secondary">${item.slug}</span> <span slot="secondary">${item.slug}</span>
${item.icon ${item.icon
? html`<img slot="graphic" .src="/api/hassio/addons/${item.slug}/icon" />` ? html`<img
alt=""
slot="graphic"
.src="/api/hassio/addons/${item.slug}/icon"
/>`
: ""} : ""}
</mwc-list-item>`; </mwc-list-item>`;

View File

@ -266,6 +266,9 @@ export class HaBaseTimeInput extends LitElement {
seconds: this.seconds, seconds: this.seconds,
milliseconds: this.milliseconds, milliseconds: this.milliseconds,
}; };
if (this.enableDay) {
value.days = this.days;
}
if (this.format === 12) { if (this.format === 12) {
value.amPm = this.amPm; value.amPm = this.amPm;
} }

View File

@ -59,6 +59,7 @@ class HaConfigEntryPicker extends LitElement {
> >
<span slot="secondary">${item.localized_domain_name}</span> <span slot="secondary">${item.localized_domain_name}</span>
<img <img
alt=""
slot="graphic" slot="graphic"
src=${brandsUrl({ src=${brandsUrl({
domain: item.domain, domain: item.domain,

View File

@ -1,14 +1,19 @@
import { CSSResultGroup, html, css, LitElement, TemplateResult } from "lit"; import { CSSResultGroup, html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
@customElement("ha-tile-image") @customElement("ha-tile-image")
export class HaTileImage extends LitElement { export class HaTileImage extends LitElement {
@property() public imageUrl?: string; @property() public imageUrl?: string;
@property() public imageAlt?: string;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<div class="image"> <div class="image">
${this.imageUrl ? html`<img src=${this.imageUrl} />` : null} ${this.imageUrl
? html`<img alt=${ifDefined(this.imageAlt)} src=${this.imageUrl} />`
: null}
</div> </div>
`; `;
} }

View File

@ -74,7 +74,7 @@ export class HaImagecropperDialog extends LitElement {
round: Boolean(this._params?.options.round), round: Boolean(this._params?.options.round),
})}" })}"
> >
<img /> <img alt=${this.hass.localize("ui.dialogs.image_cropper.crop_image")} />
</div> </div>
<mwc-button slot="secondaryAction" @click=${this.closeDialog}> <mwc-button slot="secondaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.common.cancel")} ${this.hass.localize("ui.common.cancel")}

View File

@ -19,6 +19,7 @@ class IntegrationBadge extends LitElement {
return html` return html`
<div class="icon"> <div class="icon">
<img <img
alt=""
src=${brandsUrl({ src=${brandsUrl({
domain: this.domain, domain: this.domain,
type: "icon", type: "icon",

View File

@ -250,6 +250,7 @@ class DialogCalendarEventEditor extends LitElement {
<ha-recurrence-rule-editor <ha-recurrence-rule-editor
.hass=${this.hass} .hass=${this.hass}
.dtstart=${this._dtstart} .dtstart=${this._dtstart}
.allDay=${this._allDay}
.locale=${this.hass.locale} .locale=${this.hass.locale}
.timezone=${this.hass.config.time_zone} .timezone=${this.hass.config.time_zone}
.value=${this._rrule || ""} .value=${this._rrule || ""}

View File

@ -41,6 +41,8 @@ export class RecurrenceRuleEditor extends LitElement {
@property() public dtstart?: Date; @property() public dtstart?: Date;
@property() public allDay?: boolean;
@property({ attribute: false }) public locale!: HomeAssistant["locale"]; @property({ attribute: false }) public locale!: HomeAssistant["locale"];
@property() public timezone?: string; @property() public timezone?: string;
@ -402,7 +404,14 @@ export class RecurrenceRuleEditor extends LitElement {
byweekday: byweekday, byweekday: byweekday,
bymonthday: bymonthday, bymonthday: bymonthday,
}; };
const contentline = RRule.optionsToString(options); let contentline = RRule.optionsToString(options);
if (this._until && this.allDay) {
// rrule.js only computes UNTIL values as DATE-TIME however rfc5545 says
// The value of the UNTIL rule part MUST have the same value type as the
// "DTSTART" property. If needed, strip off any time values as a workaround
// This converts "UNTIL=20220512T060000" to "UNTIL=20220512"
contentline = contentline.replace(/(UNTIL=\d{8})T\d{6}Z?/, "$1");
}
return contentline.slice(6); // Strip "RRULE:" prefix return contentline.slice(6); // Strip "RRULE:" prefix
} }

View File

@ -258,7 +258,8 @@ class HaConfigAreaPage extends SubscribeMixin(LitElement) {
<div class="column"> <div class="column">
${area.picture ${area.picture
? html`<div class="img-container"> ? html`<div class="img-container">
<img src=${area.picture} /><ha-icon-button <img alt=${area.name} src=${area.picture} />
<ha-icon-button
.path=${mdiPencil} .path=${mdiPencil}
.entry=${area} .entry=${area}
@click=${this._showSettings} @click=${this._showSettings}

View File

@ -659,6 +659,10 @@ export class HaConfigDevicePage extends LitElement {
integrations.length integrations.length
? html` ? html`
<img <img
alt=${domainToName(
this.hass.localize,
integrations[0].domain
)}
src=${brandsUrl({ src=${brandsUrl({
domain: integrations[0].domain, domain: integrations[0].domain,
type: "logo", type: "logo",

View File

@ -220,6 +220,7 @@ export class EnergyGridSettings extends LitElement {
${this._co2ConfigEntry ${this._co2ConfigEntry
? html`<div class="row" .entry=${this._co2ConfigEntry}> ? html`<div class="row" .entry=${this._co2ConfigEntry}>
<img <img
alt=""
referrerpolicy="no-referrer" referrerpolicy="no-referrer"
src=${brandsUrl({ src=${brandsUrl({
domain: "co2signal", domain: "co2signal",
@ -244,6 +245,7 @@ export class EnergyGridSettings extends LitElement {
: html` : html`
<div class="row border-bottom"> <div class="row border-bottom">
<img <img
alt=""
referrerpolicy="no-referrer" referrerpolicy="no-referrer"
src=${brandsUrl({ src=${brandsUrl({
domain: "co2signal", domain: "co2signal",

View File

@ -130,6 +130,7 @@ export class DialogEnergySolarSettings
style="display: flex; align-items: center;" style="display: flex; align-items: center;"
> >
<img <img
alt=""
referrerpolicy="no-referrer" referrerpolicy="no-referrer"
style="height: 24px; margin-right: 16px;" style="height: 24px; margin-right: 16px;"
src=${brandsUrl({ src=${brandsUrl({

View File

@ -118,6 +118,7 @@ const OVERRIDE_NUMBER_UNITS = {
}; };
const OVERRIDE_SENSOR_UNITS = { const OVERRIDE_SENSOR_UNITS = {
current: ["A", "mA"],
distance: ["cm", "ft", "in", "km", "m", "mi", "mm", "yd"], distance: ["cm", "ft", "in", "km", "m", "mi", "mm", "yd"],
gas: ["CCF", "ft³", "m³"], gas: ["CCF", "ft³", "m³"],
precipitation: ["cm", "in", "mm"], precipitation: ["cm", "in", "mm"],
@ -125,6 +126,7 @@ const OVERRIDE_SENSOR_UNITS = {
pressure: ["hPa", "Pa", "kPa", "bar", "cbar", "mbar", "mmHg", "inHg", "psi"], pressure: ["hPa", "Pa", "kPa", "bar", "cbar", "mbar", "mmHg", "inHg", "psi"],
speed: ["ft/s", "in/d", "in/h", "km/h", "kn", "m/s", "mm/d", "mm/h", "mph"], speed: ["ft/s", "in/d", "in/h", "km/h", "kn", "m/s", "mm/d", "mm/h", "mph"],
temperature: ["°C", "°F", "K"], temperature: ["°C", "°F", "K"],
voltage: ["V", "mV"],
volume: ["CCF", "fl. oz.", "ft³", "gal", "L", "mL", "m³"], volume: ["CCF", "fl. oz.", "ft³", "gal", "L", "mL", "m³"],
water: ["CCF", "ft³", "gal", "L", "m³"], water: ["CCF", "ft³", "gal", "L", "m³"],
weight: ["g", "kg", "lb", "mg", "oz", "st", "µg"], weight: ["g", "kg", "lb", "mg", "oz", "st", "µg"],

View File

@ -305,7 +305,7 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
.twoline=${Boolean(boardId)} .twoline=${Boolean(boardId)}
> >
${imageURL ${imageURL
? html`<img slot="graphic" src=${imageURL} />` ? html`<img alt="" slot="graphic" src=${imageURL} />`
: ""} : ""}
<span class="primary-text"> <span class="primary-text">
${boardName || ${boardName ||

View File

@ -48,6 +48,7 @@ class HaDomainIntegrations extends LitElement {
hasMeta hasMeta
> >
<img <img
alt=""
slot="graphic" slot="graphic"
loading="lazy" loading="lazy"
src=${brandsUrl({ src=${brandsUrl({

View File

@ -92,6 +92,7 @@ export class HaIntegrationHeader extends LitElement {
<slot name="above-header"></slot> <slot name="above-header"></slot>
<div class="header"> <div class="header">
<img <img
alt=""
src=${brandsUrl({ src=${brandsUrl({
domain: this.domain, domain: this.domain,
type: "icon", type: "icon",

View File

@ -47,6 +47,7 @@ export class HaIntegrationListItem extends ListItemBase {
)}" )}"
> >
<img <img
alt=""
loading="lazy" loading="lazy"
src=${brandsUrl({ src=${brandsUrl({
domain: this.integration.domain, domain: this.integration.domain,

View File

@ -52,6 +52,7 @@ class HaConfigRepairs extends LitElement {
@click=${this._openShowMoreDialog} @click=${this._openShowMoreDialog}
> >
<img <img
alt=${domainToName(this.hass.localize, issue.domain)}
loading="lazy" loading="lazy"
src=${brandsUrl({ src=${brandsUrl({
domain: issue.issue_domain || issue.domain, domain: issue.issue_domain || issue.domain,

View File

@ -65,6 +65,7 @@ class IntegrationsStartupTime extends LitElement {
href=${docLink} href=${docLink}
> >
<img <img
alt=""
loading="lazy" loading="lazy"
src=${brandsUrl({ src=${brandsUrl({
domain: setup.domain, domain: setup.domain,

View File

@ -244,7 +244,14 @@ class DialogTagDetail
canvas.height / 3 canvas.height / 3
); );
this._qrCode = html`<img src=${canvas.toDataURL()}></img>`; this._qrCode = html`<img
alt=${this.hass.localize(
"ui.panel.config.tag.qr_code_image",
"name",
this._name
)}
src=${canvas.toDataURL()}
></img>`;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {

View File

@ -101,7 +101,10 @@ export class HuiPictureCard extends LitElement implements LovelaceCard {
), ),
})} })}
> >
<img src=${this.hass.hassUrl(this._config.image)} /> <img
alt=${this._config.alt_text}
src=${this.hass.hassUrl(this._config.image)}
/>
</ha-card> </ha-card>
`; `;
} }

View File

@ -332,6 +332,7 @@ export interface PictureCardConfig extends LovelaceCardConfig {
hold_action?: ActionConfig; hold_action?: ActionConfig;
double_tap_action?: ActionConfig; double_tap_action?: ActionConfig;
theme?: string; theme?: string;
alt_text?: string;
} }
export interface PictureElementsCardConfig extends LovelaceCardConfig { export interface PictureElementsCardConfig extends LovelaceCardConfig {

View File

@ -20,6 +20,7 @@ const cardConfigStruct = assign(
tap_action: optional(actionConfigStruct), tap_action: optional(actionConfigStruct),
hold_action: optional(actionConfigStruct), hold_action: optional(actionConfigStruct),
theme: optional(string()), theme: optional(string()),
alt_text: optional(string()),
}) })
); );
@ -53,6 +54,10 @@ export class HuiPictureCardEditor
return this._config!.theme || ""; return this._config!.theme || "";
} }
get _alt_text(): string {
return this._config!.alt_text || "";
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
return html``; return html``;
@ -72,6 +77,16 @@ export class HuiPictureCardEditor
.configValue=${"image"} .configValue=${"image"}
@input=${this._valueChanged} @input=${this._valueChanged}
></ha-textfield> ></ha-textfield>
<ha-textfield
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.alt_text"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._alt_text}
.configValue=${"alt_text"}
@input=${this._valueChanged}
></ha-textfield>
<ha-theme-picker <ha-theme-picker
.hass=${this.hass} .hass=${this.hass}
.value=${this._theme} .value=${this._theme}

View File

@ -68,6 +68,7 @@ export class HuiPictureHeaderFooter
return html` return html`
<img <img
alt=${this._config!.alt_text}
@action=${this._handleAction} @action=${this._handleAction}
.actionHandler=${actionHandler({ .actionHandler=${actionHandler({
hasHold: hasAction(this._config!.hold_action), hasHold: hasAction(this._config!.hold_action),

View File

@ -17,6 +17,7 @@ export const pictureHeaderFooterConfigStruct = object({
tap_action: optional(actionConfigStruct), tap_action: optional(actionConfigStruct),
hold_action: optional(actionConfigStruct), hold_action: optional(actionConfigStruct),
double_tap_action: optional(actionConfigStruct), double_tap_action: optional(actionConfigStruct),
alt_text: optional(string()),
}); });
export const buttonsHeaderFooterConfigStruct = object({ export const buttonsHeaderFooterConfigStruct = object({

View File

@ -27,4 +27,5 @@ export interface PictureHeaderFooterConfig extends LovelaceHeaderFooterConfig {
tap_action?: ActionConfig; tap_action?: ActionConfig;
hold_action?: ActionConfig; hold_action?: ActionConfig;
double_tap_action?: ActionConfig; double_tap_action?: ActionConfig;
alt_text?: string;
} }

View File

@ -85,7 +85,14 @@ export class HaLongLivedAccessTokenDialog extends LitElement {
canvas.height / 3 canvas.height / 3
); );
this._qrCode = html`<img src=${canvas.toDataURL()}></img>`; this._qrCode = html`<img
alt=${this.hass.localize(
"ui.panel.profile.long_lived_access_tokens.qr_code_image",
"name",
this._params!.name
)}
src=${canvas.toDataURL()}
></img>`;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {

View File

@ -791,7 +791,8 @@
"close": "Close" "close": "Close"
}, },
"image_cropper": { "image_cropper": {
"crop": "Crop" "crop": "Crop",
"crop_image": "Picture to crop"
}, },
"date-picker": { "date-picker": {
"today": "Today" "today": "Today"
@ -1435,6 +1436,7 @@
"confirm_remove_title": "Remove tag?", "confirm_remove_title": "Remove tag?",
"confirm_remove": "Are you sure you want to remove tag {tag}?", "confirm_remove": "Are you sure you want to remove tag {tag}?",
"automation_title": "Tag {name} is scanned", "automation_title": "Tag {name} is scanned",
"qr_code_image": "QR code for tag {name}",
"headers": { "headers": {
"icon": "Icon", "icon": "Icon",
"name": "Name", "name": "Name",
@ -4195,6 +4197,7 @@
"description": "The Light card allows you to change the brightness of the light." "description": "The Light card allows you to change the brightness of the light."
}, },
"generic": { "generic": {
"alt_text": "Alternative Text",
"aspect_ratio": "Aspect Ratio", "aspect_ratio": "Aspect Ratio",
"attribute": "Attribute", "attribute": "Attribute",
"camera_image": "Camera Entity", "camera_image": "Camera Entity",
@ -4598,7 +4601,8 @@
"name": "Name", "name": "Name",
"prompt_name": "Give the token a name", "prompt_name": "Give the token a name",
"prompt_copy_token": "Copy your access token. It will not be shown again.", "prompt_copy_token": "Copy your access token. It will not be shown again.",
"empty_state": "You have no long-lived access tokens yet." "empty_state": "You have no long-lived access tokens yet.",
"qr_code_image": "QR code for token {name}"
} }
}, },
"shopping_list": { "shopping_list": {