mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
20230110.0 (#15070)
This commit is contained in:
commit
24e6b8483e
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20230104.0"
|
version = "20230110.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"
|
||||||
|
@ -22,6 +22,7 @@ import {
|
|||||||
isNumericState,
|
isNumericState,
|
||||||
} from "../../common/number/format_number";
|
} from "../../common/number/format_number";
|
||||||
import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
||||||
|
import { EntityRegistryEntry } from "../../data/entity_registry";
|
||||||
import { timerTimeRemaining } from "../../data/timer";
|
import { timerTimeRemaining } from "../../data/timer";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "../ha-label-badge";
|
import "../ha-label-badge";
|
||||||
@ -103,8 +104,10 @@ export class HaStateLabelBadge extends LitElement {
|
|||||||
// 4. Icon determined via entity state
|
// 4. Icon determined via entity state
|
||||||
// 5. Value string as fallback
|
// 5. Value string as fallback
|
||||||
const domain = computeStateDomain(entityState);
|
const domain = computeStateDomain(entityState);
|
||||||
|
const entry = this.hass?.entities[entityState.entity_id];
|
||||||
|
|
||||||
const showIcon = this.icon || this._computeShowIcon(domain, entityState);
|
const showIcon =
|
||||||
|
this.icon || this._computeShowIcon(domain, entityState, entry);
|
||||||
const image = this.icon
|
const image = this.icon
|
||||||
? ""
|
? ""
|
||||||
: this.image
|
: this.image
|
||||||
@ -112,7 +115,9 @@ export class HaStateLabelBadge extends LitElement {
|
|||||||
: entityState.attributes.entity_picture_local ||
|
: entityState.attributes.entity_picture_local ||
|
||||||
entityState.attributes.entity_picture;
|
entityState.attributes.entity_picture;
|
||||||
const value =
|
const value =
|
||||||
!image && !showIcon ? this._computeValue(domain, entityState) : undefined;
|
!image && !showIcon
|
||||||
|
? this._computeValue(domain, entityState, entry)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@ -152,7 +157,11 @@ export class HaStateLabelBadge extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeValue(domain: string, entityState: HassEntity) {
|
private _computeValue(
|
||||||
|
domain: string,
|
||||||
|
entityState: HassEntity,
|
||||||
|
entry?: EntityRegistryEntry
|
||||||
|
) {
|
||||||
switch (domain) {
|
switch (domain) {
|
||||||
case "alarm_control_panel":
|
case "alarm_control_panel":
|
||||||
case "binary_sensor":
|
case "binary_sensor":
|
||||||
@ -165,7 +174,7 @@ export class HaStateLabelBadge extends LitElement {
|
|||||||
return null;
|
return null;
|
||||||
// @ts-expect-error we don't break and go to default
|
// @ts-expect-error we don't break and go to default
|
||||||
case "sensor":
|
case "sensor":
|
||||||
if (entityState.attributes.device_class === "moon__phase") {
|
if (entry?.platform === "moon") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line: disable=no-fallthrough
|
// eslint-disable-next-line: disable=no-fallthrough
|
||||||
@ -188,7 +197,11 @@ export class HaStateLabelBadge extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeShowIcon(domain: string, entityState: HassEntity): boolean {
|
private _computeShowIcon(
|
||||||
|
domain: string,
|
||||||
|
entityState: HassEntity,
|
||||||
|
entry?: EntityRegistryEntry
|
||||||
|
): boolean {
|
||||||
if (entityState.state === UNAVAILABLE) {
|
if (entityState.state === UNAVAILABLE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -204,7 +217,7 @@ export class HaStateLabelBadge extends LitElement {
|
|||||||
case "timer":
|
case "timer":
|
||||||
return true;
|
return true;
|
||||||
case "sensor":
|
case "sensor":
|
||||||
return entityState.attributes.device_class === "moon__phase";
|
return entry?.platform === "moon";
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
} from "lit";
|
} 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 { ensureArray } from "../../common/array/ensure-array";
|
||||||
import {
|
import {
|
||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
getDeviceIntegrationLookup,
|
getDeviceIntegrationLookup,
|
||||||
@ -78,7 +79,7 @@ export class HaTargetSelector extends LitElement {
|
|||||||
? [this.selector.target?.entity.device_class]
|
? [this.selector.target?.entity.device_class]
|
||||||
: undefined}
|
: undefined}
|
||||||
.includeDomains=${this.selector.target?.entity?.domain
|
.includeDomains=${this.selector.target?.entity?.domain
|
||||||
? [this.selector.target?.entity.domain]
|
? ensureArray(this.selector.target.entity.domain as string | string[])
|
||||||
: undefined}
|
: undefined}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
></ha-target-picker>`;
|
></ha-target-picker>`;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import type { SelectedDetail } from "@material/mwc-list";
|
import type { SelectedDetail } from "@material/mwc-list";
|
||||||
|
import { formatInTimeZone, toDate } from "date-fns-tz";
|
||||||
import { css, html, LitElement, PropertyValues } from "lit";
|
import { css, html, LitElement, PropertyValues } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
@ -64,7 +65,7 @@ export class RecurrenceRuleEditor extends LitElement {
|
|||||||
|
|
||||||
@state() private _count?: number;
|
@state() private _count?: number;
|
||||||
|
|
||||||
@state() private _until?: Date;
|
@state() private _untilDay?: Date;
|
||||||
|
|
||||||
@query("#monthly") private _monthlyRepeatSelect!: HaSelect;
|
@query("#monthly") private _monthlyRepeatSelect!: HaSelect;
|
||||||
|
|
||||||
@ -97,15 +98,17 @@ export class RecurrenceRuleEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
changedProps.has("timezone") ||
|
!changedProps.has("value") &&
|
||||||
changedProps.has("_freq") ||
|
(changedProps.has("dtstart") ||
|
||||||
changedProps.has("_interval") ||
|
changedProps.has("timezone") ||
|
||||||
changedProps.has("_weekday") ||
|
changedProps.has("_freq") ||
|
||||||
changedProps.has("_monthlyRepeatWeekday") ||
|
changedProps.has("_interval") ||
|
||||||
changedProps.has("_monthday") ||
|
changedProps.has("_weekday") ||
|
||||||
changedProps.has("_end") ||
|
changedProps.has("_monthlyRepeatWeekday") ||
|
||||||
changedProps.has("_count") ||
|
changedProps.has("_monthday") ||
|
||||||
changedProps.has("_until")
|
changedProps.has("_end") ||
|
||||||
|
changedProps.has("_count") ||
|
||||||
|
changedProps.has("_untilDay"))
|
||||||
) {
|
) {
|
||||||
this._updateRule();
|
this._updateRule();
|
||||||
return;
|
return;
|
||||||
@ -122,7 +125,7 @@ export class RecurrenceRuleEditor extends LitElement {
|
|||||||
this._monthlyRepeatWeekday = undefined;
|
this._monthlyRepeatWeekday = undefined;
|
||||||
this._end = "never";
|
this._end = "never";
|
||||||
this._count = undefined;
|
this._count = undefined;
|
||||||
this._until = undefined;
|
this._untilDay = undefined;
|
||||||
|
|
||||||
this._computedRRule = this.value;
|
this._computedRRule = this.value;
|
||||||
if (this.value === "") {
|
if (this.value === "") {
|
||||||
@ -162,7 +165,7 @@ export class RecurrenceRuleEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
if (rrule.until) {
|
if (rrule.until) {
|
||||||
this._end = "on";
|
this._end = "on";
|
||||||
this._until = rrule.until;
|
this._untilDay = toDate(rrule.until, { timeZone: this.timezone });
|
||||||
} else if (rrule.count) {
|
} else if (rrule.count) {
|
||||||
this._end = "after";
|
this._end = "after";
|
||||||
this._count = rrule.count;
|
this._count = rrule.count;
|
||||||
@ -327,7 +330,7 @@ export class RecurrenceRuleEditor extends LitElement {
|
|||||||
"ui.components.calendar.event.repeat.end_on.label"
|
"ui.components.calendar.event.repeat.end_on.label"
|
||||||
)}
|
)}
|
||||||
.locale=${this.locale}
|
.locale=${this.locale}
|
||||||
.value=${this._until!.toISOString()}
|
.value=${this._formatDate(this._untilDay!)}
|
||||||
@value-changed=${this._onUntilChange}
|
@value-changed=${this._onUntilChange}
|
||||||
></ha-date-input>
|
></ha-date-input>
|
||||||
`
|
`
|
||||||
@ -381,6 +384,7 @@ export class RecurrenceRuleEditor extends LitElement {
|
|||||||
} else {
|
} else {
|
||||||
this._weekday.delete(value);
|
this._weekday.delete(value);
|
||||||
}
|
}
|
||||||
|
this.requestUpdate("_weekday");
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onEndSelected(e: CustomEvent<SelectedDetail<number>>) {
|
private _onEndSelected(e: CustomEvent<SelectedDetail<number>>) {
|
||||||
@ -393,15 +397,15 @@ export class RecurrenceRuleEditor extends LitElement {
|
|||||||
switch (this._end) {
|
switch (this._end) {
|
||||||
case "after":
|
case "after":
|
||||||
this._count = DEFAULT_COUNT[this._freq!];
|
this._count = DEFAULT_COUNT[this._freq!];
|
||||||
this._until = undefined;
|
this._untilDay = undefined;
|
||||||
break;
|
break;
|
||||||
case "on":
|
case "on":
|
||||||
this._count = undefined;
|
this._count = undefined;
|
||||||
this._until = untilValue(this._freq!);
|
this._untilDay = untilValue(this._freq!);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this._count = undefined;
|
this._count = undefined;
|
||||||
this._until = undefined;
|
this._untilDay = undefined;
|
||||||
}
|
}
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
@ -412,7 +416,9 @@ export class RecurrenceRuleEditor extends LitElement {
|
|||||||
|
|
||||||
private _onUntilChange(e: CustomEvent) {
|
private _onUntilChange(e: CustomEvent) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this._until = new Date(e.detail.value);
|
this._untilDay = toDate(e.detail.value + "T00:00:00", {
|
||||||
|
timeZone: this.timezone,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the weekday selected when there is only a single value
|
// Reset the weekday selected when there is only a single value
|
||||||
@ -441,18 +447,27 @@ export class RecurrenceRuleEditor extends LitElement {
|
|||||||
freq: convertRepeatFrequency(this._freq!)!,
|
freq: convertRepeatFrequency(this._freq!)!,
|
||||||
interval: this._interval > 1 ? this._interval : undefined,
|
interval: this._interval > 1 ? this._interval : undefined,
|
||||||
count: this._count,
|
count: this._count,
|
||||||
until: this._until,
|
|
||||||
tzid: this.timezone,
|
|
||||||
byweekday: byweekday,
|
byweekday: byweekday,
|
||||||
bymonthday: bymonthday,
|
bymonthday: bymonthday,
|
||||||
};
|
};
|
||||||
let contentline = RRule.optionsToString(options);
|
let contentline = RRule.optionsToString(options);
|
||||||
if (this._until && this.allDay) {
|
if (this._untilDay) {
|
||||||
// rrule.js only computes UNTIL values as DATE-TIME however rfc5545 says
|
// The UNTIL value should be inclusive of the last event instance
|
||||||
// The value of the UNTIL rule part MUST have the same value type as the
|
const until = toDate(
|
||||||
// "DTSTART" property. If needed, strip off any time values as a workaround
|
this._formatDate(this._untilDay!) +
|
||||||
// This converts "UNTIL=20220512T060000" to "UNTIL=20220512"
|
"T" +
|
||||||
contentline = contentline.replace(/(UNTIL=\d{8})T\d{6}Z?/, "$1");
|
this._formatTime(this.dtstart!),
|
||||||
|
{ timeZone: this.timezone }
|
||||||
|
);
|
||||||
|
// rrule.js can't compute some UNTIL variations so we compute that ourself. Must be
|
||||||
|
// in the same format as dtstart.
|
||||||
|
const format = this.allDay ? "yyyyMMdd" : "yyyyMMdd'T'HHmmss";
|
||||||
|
const newUntilValue = formatInTimeZone(
|
||||||
|
until,
|
||||||
|
this.hass.config.time_zone,
|
||||||
|
format
|
||||||
|
);
|
||||||
|
contentline += `;UNTIL=${newUntilValue}`;
|
||||||
}
|
}
|
||||||
return contentline.slice(6); // Strip "RRULE:" prefix
|
return contentline.slice(6); // Strip "RRULE:" prefix
|
||||||
}
|
}
|
||||||
@ -472,6 +487,16 @@ export class RecurrenceRuleEditor extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Formats a date in browser display timezone
|
||||||
|
private _formatDate(date: Date): string {
|
||||||
|
return formatInTimeZone(date, this.timezone!, "yyyy-MM-dd");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formats a time in browser display timezone
|
||||||
|
private _formatTime(date: Date): string {
|
||||||
|
return formatInTimeZone(date, this.timezone!, "HH:mm:ss");
|
||||||
|
}
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
ha-textfield,
|
ha-textfield,
|
||||||
ha-select {
|
ha-select {
|
||||||
|
@ -184,7 +184,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const target = ev.target as any;
|
const target = ev.target as any;
|
||||||
const key = target.key;
|
const key = target.key;
|
||||||
const value = ev.detail?.value ?? target.value;
|
const value = ev.detail ? ev.detail.value : target.value;
|
||||||
if (
|
if (
|
||||||
(this.config.use_blueprint.input &&
|
(this.config.use_blueprint.input &&
|
||||||
this.config.use_blueprint.input[key] === value) ||
|
this.config.use_blueprint.input[key] === value) ||
|
||||||
|
@ -40,17 +40,12 @@ import {
|
|||||||
updateCloudAlexaEntityConfig,
|
updateCloudAlexaEntityConfig,
|
||||||
updateCloudPref,
|
updateCloudPref,
|
||||||
} from "../../../../data/cloud";
|
} from "../../../../data/cloud";
|
||||||
import {
|
import { EntityRegistryEntry } from "../../../../data/entity_registry";
|
||||||
EntityRegistryEntry,
|
|
||||||
getExtendedEntityRegistryEntry,
|
|
||||||
updateEntityRegistryEntry,
|
|
||||||
} from "../../../../data/entity_registry";
|
|
||||||
import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler";
|
import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler";
|
||||||
import "../../../../layouts/hass-loading-screen";
|
import "../../../../layouts/hass-loading-screen";
|
||||||
import "../../../../layouts/hass-subpage";
|
import "../../../../layouts/hass-subpage";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
import { haStyle } from "../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { showEntityAliasesDialog } from "../../entities/entity-aliases/show-dialog-entity-aliases";
|
|
||||||
|
|
||||||
const DEFAULT_CONFIG_EXPOSE = true;
|
const DEFAULT_CONFIG_EXPOSE = true;
|
||||||
|
|
||||||
@ -167,20 +162,8 @@ class CloudAlexa extends LitElement {
|
|||||||
<state-info
|
<state-info
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
secondary-line
|
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
>
|
>
|
||||||
${entity.entity_id in this.hass.entities
|
|
||||||
? html`<button
|
|
||||||
class="link"
|
|
||||||
.entityId=${entity.entity_id}
|
|
||||||
@click=${this._openAliasesSettings}
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.cloud.alexa.manage_aliases"
|
|
||||||
)}
|
|
||||||
</button>`
|
|
||||||
: ""}
|
|
||||||
</state-info>
|
</state-info>
|
||||||
${!emptyFilter
|
${!emptyFilter
|
||||||
? html`${iconButton}`
|
? html`${iconButton}`
|
||||||
@ -343,21 +326,6 @@ class CloudAlexa extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _openAliasesSettings(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
const entityId = ev.target.entityId;
|
|
||||||
const entry = await getExtendedEntityRegistryEntry(this.hass, entityId);
|
|
||||||
if (!entry) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showEntityAliasesDialog(this, {
|
|
||||||
entity: entry,
|
|
||||||
updateEntry: async (updates) => {
|
|
||||||
await updateEntityRegistryEntry(this.hass, entry.entity_id, updates);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _fetchData() {
|
private async _fetchData() {
|
||||||
const entities = await fetchCloudAlexaEntities(this.hass);
|
const entities = await fetchCloudAlexaEntities(this.hass);
|
||||||
entities.sort((a, b) => {
|
entities.sort((a, b) => {
|
||||||
@ -558,6 +526,7 @@ class CloudAlexa extends LitElement {
|
|||||||
}
|
}
|
||||||
state-info {
|
state-info {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
height: 40px;
|
||||||
}
|
}
|
||||||
ha-switch {
|
ha-switch {
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
|
@ -158,7 +158,7 @@ export class HaBlueprintScriptEditor extends LitElement {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const target = ev.target as any;
|
const target = ev.target as any;
|
||||||
const key = target.key;
|
const key = target.key;
|
||||||
const value = ev.detail?.value ?? target.value;
|
const value = ev.detail ? ev.detail.value : target.value;
|
||||||
if (
|
if (
|
||||||
(this.config.use_blueprint.input &&
|
(this.config.use_blueprint.input &&
|
||||||
this.config.use_blueprint.input[key] === value) ||
|
this.config.use_blueprint.input[key] === value) ||
|
||||||
|
Loading…
x
Reference in New Issue
Block a user