mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Merge pull request #11899 from home-assistant/dev
This commit is contained in:
commit
7f086c0900
@ -279,7 +279,7 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
|
|||||||
can_play: true,
|
can_play: true,
|
||||||
can_expand: false,
|
can_expand: false,
|
||||||
children_media_class: null,
|
children_media_class: null,
|
||||||
thumbnail: null,
|
thumbnail: "https://brands.home-assistant.io/_/image/logo.png",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "movie.mp4",
|
title: "movie.mp4",
|
||||||
|
@ -110,8 +110,6 @@ class HassioAddonStore extends LitElement {
|
|||||||
<div class="search">
|
<div class="search">
|
||||||
<search-input
|
<search-input
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
no-label-float
|
|
||||||
no-underline
|
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
@value-changed=${this._filterChanged}
|
@value-changed=${this._filterChanged}
|
||||||
></search-input>
|
></search-input>
|
||||||
|
@ -80,8 +80,6 @@ class HassioHardwareDialog extends LitElement {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<search-input
|
<search-input
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
dialogInitialFocus
|
|
||||||
no-label-float
|
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
@value-changed=${this._handleSearchChange}
|
@value-changed=${this._handleSearchChange}
|
||||||
.label=${this._dialogParams.supervisor.localize(
|
.label=${this._dialogParams.supervisor.localize(
|
||||||
|
@ -106,6 +106,9 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
</paper-item-body>
|
</paper-item-body>
|
||||||
<div class="delete">
|
<div class="delete">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
|
.label=${this._dialogParams!.supervisor.localize(
|
||||||
|
"dialog.repositories.remove"
|
||||||
|
)}
|
||||||
.disabled=${usedRepositories.includes(repo.slug)}
|
.disabled=${usedRepositories.includes(repo.slug)}
|
||||||
.slug=${repo.slug}
|
.slug=${repo.slug}
|
||||||
.path=${usedRepositories.includes(repo.slug)
|
.path=${usedRepositories.includes(repo.slug)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = home-assistant-frontend
|
name = home-assistant-frontend
|
||||||
version = 20220226.0
|
version = 20220301.0
|
||||||
author = The Home Assistant Authors
|
author = The Home Assistant Authors
|
||||||
author_email = hello@home-assistant.io
|
author_email = hello@home-assistant.io
|
||||||
license = Apache-2.0
|
license = Apache-2.0
|
||||||
|
@ -115,6 +115,9 @@ class DateRangePickerElement extends WrappedElement {
|
|||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
min-width: initial !important;
|
min-width: initial !important;
|
||||||
}
|
}
|
||||||
|
.daterangepicker:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.daterangepicker:after {
|
.daterangepicker:after {
|
||||||
border-bottom: 6px solid var(--card-background-color);
|
border-bottom: 6px solid var(--card-background-color);
|
||||||
}
|
}
|
||||||
|
@ -51,10 +51,11 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
|
|||||||
private _filterEntities = (entity: HassEntity): boolean => {
|
private _filterEntities = (entity: HassEntity): boolean => {
|
||||||
if (this.selector.entity?.domain) {
|
if (this.selector.entity?.domain) {
|
||||||
const filterDomain = this.selector.entity.domain;
|
const filterDomain = this.selector.entity.domain;
|
||||||
|
const filterDomainIsArray = Array.isArray(filterDomain);
|
||||||
const entityDomain = computeStateDomain(entity);
|
const entityDomain = computeStateDomain(entity);
|
||||||
if (
|
if (
|
||||||
(Array.isArray(filterDomain) && !filterDomain.includes(entityDomain)) ||
|
(filterDomainIsArray && !filterDomain.includes(entityDomain)) ||
|
||||||
entityDomain !== filterDomain
|
(!filterDomainIsArray && entityDomain !== filterDomain)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
} from "../../data/media-player";
|
} from "../../data/media-player";
|
||||||
import type { MediaSelector, MediaSelectorValue } from "../../data/selector";
|
import type { MediaSelector, MediaSelectorValue } from "../../data/selector";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
|
import { brandsUrl, extractDomainFromBrandUrl } from "../../util/brands-url";
|
||||||
import "../ha-alert";
|
import "../ha-alert";
|
||||||
import "../ha-form/ha-form";
|
import "../ha-form/ha-form";
|
||||||
import type { HaFormSchema } from "../ha-form/types";
|
import type { HaFormSchema } from "../ha-form/types";
|
||||||
@ -50,6 +51,18 @@ export class HaMediaSelector extends LitElement {
|
|||||||
getSignedPath(this.hass, thumbnail).then((signedPath) => {
|
getSignedPath(this.hass, thumbnail).then((signedPath) => {
|
||||||
this._thumbnailUrl = signedPath.path;
|
this._thumbnailUrl = signedPath.path;
|
||||||
});
|
});
|
||||||
|
} else if (
|
||||||
|
thumbnail &&
|
||||||
|
thumbnail.startsWith("https://brands.home-assistant.io")
|
||||||
|
) {
|
||||||
|
// The backend is not aware of the theme used by the users,
|
||||||
|
// so we rewrite the URL to show a proper icon
|
||||||
|
this._thumbnailUrl = brandsUrl({
|
||||||
|
domain: extractDomainFromBrandUrl(thumbnail),
|
||||||
|
type: "icon",
|
||||||
|
useFallback: true,
|
||||||
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this._thumbnailUrl = thumbnail;
|
this._thumbnailUrl = thumbnail;
|
||||||
}
|
}
|
||||||
|
@ -34,23 +34,24 @@ import {
|
|||||||
MediaPickedEvent,
|
MediaPickedEvent,
|
||||||
MediaPlayerBrowseAction,
|
MediaPlayerBrowseAction,
|
||||||
} from "../../data/media-player";
|
} from "../../data/media-player";
|
||||||
|
import { browseLocalMediaPlayer } from "../../data/media_source";
|
||||||
|
import { isTTSMediaSource } from "../../data/tts";
|
||||||
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
||||||
import { installResizeObserver } from "../../panels/lovelace/common/install-resize-observer";
|
import { installResizeObserver } from "../../panels/lovelace/common/install-resize-observer";
|
||||||
import { haStyle } from "../../resources/styles";
|
import { haStyle } from "../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
|
import { brandsUrl, extractDomainFromBrandUrl } from "../../util/brands-url";
|
||||||
import { documentationUrl } from "../../util/documentation-url";
|
import { documentationUrl } from "../../util/documentation-url";
|
||||||
import "../entity/ha-entity-picker";
|
import "../entity/ha-entity-picker";
|
||||||
import "../ha-button-menu";
|
import "../ha-button-menu";
|
||||||
import "../ha-card";
|
import "../ha-card";
|
||||||
import type { HaCard } from "../ha-card";
|
import type { HaCard } from "../ha-card";
|
||||||
import "../ha-circular-progress";
|
import "../ha-circular-progress";
|
||||||
|
import "../ha-fab";
|
||||||
import "../ha-icon-button";
|
import "../ha-icon-button";
|
||||||
import "../ha-svg-icon";
|
import "../ha-svg-icon";
|
||||||
import "../ha-fab";
|
|
||||||
import { browseLocalMediaPlayer } from "../../data/media_source";
|
|
||||||
import { isTTSMediaSource } from "../../data/tts";
|
|
||||||
import type { TtsMediaPickedEvent } from "./ha-browse-media-tts";
|
|
||||||
import "./ha-browse-media-tts";
|
import "./ha-browse-media-tts";
|
||||||
|
import type { TtsMediaPickedEvent } from "./ha-browse-media-tts";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
@ -681,6 +682,17 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
// Thumbnails served by local API require authentication
|
// Thumbnails served by local API require authentication
|
||||||
const signedPath = await getSignedPath(this.hass, thumbnailUrl);
|
const signedPath = await getSignedPath(this.hass, thumbnailUrl);
|
||||||
thumbnailUrl = signedPath.path;
|
thumbnailUrl = signedPath.path;
|
||||||
|
} else if (
|
||||||
|
thumbnailUrl.startsWith("https://brands.home-assistant.io")
|
||||||
|
) {
|
||||||
|
// The backend is not aware of the theme used by the users,
|
||||||
|
// so we rewrite the URL to show a proper icon
|
||||||
|
thumbnailUrl = brandsUrl({
|
||||||
|
domain: extractDomainFromBrandUrl(thumbnailUrl),
|
||||||
|
type: "icon",
|
||||||
|
useFallback: true,
|
||||||
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
thumbnailCard.style.backgroundImage = `url(${thumbnailUrl})`;
|
thumbnailCard.style.backgroundImage = `url(${thumbnailUrl})`;
|
||||||
observer.unobserve(thumbnailCard); // loaded, so no need to observe anymore
|
observer.unobserve(thumbnailCard); // loaded, so no need to observe anymore
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
export interface InputDateTime {
|
export interface InputDateTime {
|
||||||
@ -17,6 +18,19 @@ export interface InputDateTimeMutableParams {
|
|||||||
has_date: boolean;
|
has_date: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const stateToIsoDateString = (entityState: HassEntity) =>
|
||||||
|
`${entityState.attributes.year || "1970"}-${String(
|
||||||
|
entityState.attributes.month || "01"
|
||||||
|
).padStart(2, "0")}-${String(entityState.attributes.day || "01").padStart(
|
||||||
|
2,
|
||||||
|
"0"
|
||||||
|
)}T${String(entityState.attributes.hour || "00").padStart(2, "0")}:${String(
|
||||||
|
entityState.attributes.minute || "00"
|
||||||
|
).padStart(2, "0")}:${String(entityState.attributes.second || "00").padStart(
|
||||||
|
2,
|
||||||
|
"0"
|
||||||
|
)}`;
|
||||||
|
|
||||||
export const setInputDateTimeValue = (
|
export const setInputDateTimeValue = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entityId: string,
|
entityId: string,
|
||||||
|
@ -4,7 +4,10 @@ import { customElement, property } from "lit/decorators";
|
|||||||
import "../../../components/ha-date-input";
|
import "../../../components/ha-date-input";
|
||||||
import "../../../components/ha-time-input";
|
import "../../../components/ha-time-input";
|
||||||
import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
|
import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
|
||||||
import { setInputDateTimeValue } from "../../../data/input_datetime";
|
import {
|
||||||
|
setInputDateTimeValue,
|
||||||
|
stateToIsoDateString,
|
||||||
|
} from "../../../data/input_datetime";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
@customElement("more-info-input_datetime")
|
@customElement("more-info-input_datetime")
|
||||||
@ -24,7 +27,7 @@ class MoreInfoInputDatetime extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
<ha-date-input
|
<ha-date-input
|
||||||
.locale=${this.hass.locale}
|
.locale=${this.hass.locale}
|
||||||
.value=${`${this.stateObj.attributes.year}-${this.stateObj.attributes.month}-${this.stateObj.attributes.day}`}
|
.value=${stateToIsoDateString(this.stateObj)}
|
||||||
.disabled=${UNAVAILABLE_STATES.includes(this.stateObj.state)}
|
.disabled=${UNAVAILABLE_STATES.includes(this.stateObj.state)}
|
||||||
@value-changed=${this._dateChanged}
|
@value-changed=${this._dateChanged}
|
||||||
>
|
>
|
||||||
|
@ -13,6 +13,9 @@ export const SubscribeMixin = <T extends Constructor<ReactiveElement>>(
|
|||||||
class SubscribeClass extends superClass {
|
class SubscribeClass extends superClass {
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
// we wait with subscribing till these properties are set on the host element
|
||||||
|
protected hassSubscribeRequiredHostProps?: string[];
|
||||||
|
|
||||||
private __unsubs?: Array<UnsubscribeFunc | Promise<UnsubscribeFunc>>;
|
private __unsubs?: Array<UnsubscribeFunc | Promise<UnsubscribeFunc>>;
|
||||||
|
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
@ -39,6 +42,16 @@ export const SubscribeMixin = <T extends Constructor<ReactiveElement>>(
|
|||||||
super.updated(changedProps);
|
super.updated(changedProps);
|
||||||
if (changedProps.has("hass")) {
|
if (changedProps.has("hass")) {
|
||||||
this.__checkSubscribed();
|
this.__checkSubscribed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.hassSubscribeRequiredHostProps) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const key of changedProps.keys()) {
|
||||||
|
if (this.hassSubscribeRequiredHostProps.includes(key as string)) {
|
||||||
|
this.__checkSubscribed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +65,10 @@ export const SubscribeMixin = <T extends Constructor<ReactiveElement>>(
|
|||||||
if (
|
if (
|
||||||
this.__unsubs !== undefined ||
|
this.__unsubs !== undefined ||
|
||||||
!(this as unknown as Element).isConnected ||
|
!(this as unknown as Element).isConnected ||
|
||||||
this.hass === undefined
|
this.hass === undefined ||
|
||||||
|
this.hassSubscribeRequiredHostProps?.some(
|
||||||
|
(prop) => this[prop] === undefined
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,7 @@ class HaConfigDashboard extends LitElement {
|
|||||||
></ha-menu-button>
|
></ha-menu-button>
|
||||||
<div main-title>${this.hass.localize("panel.config")}</div>
|
<div main-title>${this.hass.localize("panel.config")}</div>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
|
.label=${this.hass.localize("ui.dialogs.quick-bar.title")}
|
||||||
.path=${mdiMagnify}
|
.path=${mdiMagnify}
|
||||||
@click=${this._showQuickBar}
|
@click=${this._showQuickBar}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
@ -86,6 +86,7 @@ export class EnergyDeviceSettings extends LitElement {
|
|||||||
: device.stat_consumption}</span
|
: device.stat_consumption}</span
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
|
.label=${this.hass.localize("ui.common.delete")}
|
||||||
@click=${this._deleteDevice}
|
@click=${this._deleteDevice}
|
||||||
.device=${device}
|
.device=${device}
|
||||||
.path=${mdiDelete}
|
.path=${mdiDelete}
|
||||||
|
@ -144,8 +144,6 @@ export class ZHANetworkVisualizationPage extends LitElement {
|
|||||||
<div slot="header">
|
<div slot="header">
|
||||||
<search-input
|
<search-input
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
no-label-float
|
|
||||||
no-underline
|
|
||||||
class="header"
|
class="header"
|
||||||
@value-changed=${this._handleSearchChange}
|
@value-changed=${this._handleSearchChange}
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
@ -161,8 +159,6 @@ export class ZHANetworkVisualizationPage extends LitElement {
|
|||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html`<search-input
|
? html`<search-input
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
no-label-float
|
|
||||||
no-underline
|
|
||||||
@value-changed=${this._handleSearchChange}
|
@value-changed=${this._handleSearchChange}
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
|
@ -43,8 +43,6 @@ export class HaConfigLogs extends LitElement {
|
|||||||
<div slot="header">
|
<div slot="header">
|
||||||
<search-input
|
<search-input
|
||||||
class="header"
|
class="header"
|
||||||
no-label-float
|
|
||||||
no-underline
|
|
||||||
@value-changed=${this._filterChanged}
|
@value-changed=${this._filterChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
@ -55,9 +53,6 @@ export class HaConfigLogs extends LitElement {
|
|||||||
: html`
|
: html`
|
||||||
<div class="search">
|
<div class="search">
|
||||||
<search-input
|
<search-input
|
||||||
autofocus
|
|
||||||
no-label-float
|
|
||||||
no-underline
|
|
||||||
@value-changed=${this._filterChanged}
|
@value-changed=${this._filterChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
|
@ -32,6 +32,8 @@ class HuiEnergyCarbonGaugeCard
|
|||||||
|
|
||||||
@state() private _data?: EnergyData;
|
@state() private _data?: EnergyData;
|
||||||
|
|
||||||
|
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,8 @@ export class HuiEnergyDevicesGraphCard
|
|||||||
|
|
||||||
@query("ha-chart-base") private _chart?: HaChartBase;
|
@query("ha-chart-base") private _chart?: HaChartBase;
|
||||||
|
|
||||||
|
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
getEnergyDataCollection(this.hass, {
|
getEnergyDataCollection(this.hass, {
|
||||||
|
@ -43,6 +43,8 @@ class HuiEnergyDistrubutionCard
|
|||||||
|
|
||||||
@state() private _data?: EnergyData;
|
@state() private _data?: EnergyData;
|
||||||
|
|
||||||
|
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||||
|
|
||||||
public setConfig(config: EnergyDistributionCardConfig): void {
|
public setConfig(config: EnergyDistributionCardConfig): void {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,8 @@ export class HuiEnergyGasGraphCard
|
|||||||
|
|
||||||
@state() private _unit?: string;
|
@state() private _unit?: string;
|
||||||
|
|
||||||
|
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
getEnergyDataCollection(this.hass, {
|
getEnergyDataCollection(this.hass, {
|
||||||
|
@ -35,6 +35,8 @@ class HuiEnergyGridGaugeCard
|
|||||||
|
|
||||||
@state() private _data?: EnergyData;
|
@state() private _data?: EnergyData;
|
||||||
|
|
||||||
|
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
getEnergyDataCollection(this.hass!, {
|
getEnergyDataCollection(this.hass!, {
|
||||||
|
@ -30,6 +30,8 @@ class HuiEnergySolarGaugeCard
|
|||||||
|
|
||||||
@state() private _data?: EnergyData;
|
@state() private _data?: EnergyData;
|
||||||
|
|
||||||
|
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
getEnergyDataCollection(this.hass!, {
|
getEnergyDataCollection(this.hass!, {
|
||||||
|
@ -61,6 +61,8 @@ export class HuiEnergySolarGraphCard
|
|||||||
|
|
||||||
@state() private _end = endOfToday();
|
@state() private _end = endOfToday();
|
||||||
|
|
||||||
|
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
getEnergyDataCollection(this.hass, {
|
getEnergyDataCollection(this.hass, {
|
||||||
|
@ -45,6 +45,8 @@ export class HuiEnergySourcesTableCard
|
|||||||
|
|
||||||
@state() private _data?: EnergyData;
|
@state() private _data?: EnergyData;
|
||||||
|
|
||||||
|
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
getEnergyDataCollection(this.hass, {
|
getEnergyDataCollection(this.hass, {
|
||||||
|
@ -50,6 +50,8 @@ export class HuiEnergyUsageGraphCard
|
|||||||
|
|
||||||
@state() private _end = endOfToday();
|
@state() private _end = endOfToday();
|
||||||
|
|
||||||
|
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
getEnergyDataCollection(this.hass, {
|
getEnergyDataCollection(this.hass, {
|
||||||
|
@ -9,7 +9,10 @@ import {
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../../components/ha-date-input";
|
import "../../../components/ha-date-input";
|
||||||
import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
|
import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
|
||||||
import { setInputDateTimeValue } from "../../../data/input_datetime";
|
import {
|
||||||
|
setInputDateTimeValue,
|
||||||
|
stateToIsoDateString,
|
||||||
|
} from "../../../data/input_datetime";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
import "../components/hui-generic-entity-row";
|
import "../components/hui-generic-entity-row";
|
||||||
@ -65,7 +68,7 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
|
|||||||
.label=${stateObj.attributes.has_time ? name : undefined}
|
.label=${stateObj.attributes.has_time ? name : undefined}
|
||||||
.locale=${this.hass.locale}
|
.locale=${this.hass.locale}
|
||||||
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}
|
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}
|
||||||
.value=${`${stateObj.attributes.year}-${stateObj.attributes.month}-${stateObj.attributes.day}`}
|
.value=${stateToIsoDateString(stateObj)}
|
||||||
@value-changed=${this._dateChanged}
|
@value-changed=${this._dateChanged}
|
||||||
>
|
>
|
||||||
</ha-date-input>
|
</ha-date-input>
|
||||||
|
@ -9,6 +9,8 @@ import {
|
|||||||
import { ResolvedMediaSource } from "../../data/media_source";
|
import { ResolvedMediaSource } from "../../data/media_source";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
|
export const ERR_UNSUPPORTED_MEDIA = "Unsupported Media";
|
||||||
|
|
||||||
export class BrowserMediaPlayer {
|
export class BrowserMediaPlayer {
|
||||||
private player: HTMLAudioElement;
|
private player: HTMLAudioElement;
|
||||||
|
|
||||||
@ -25,6 +27,9 @@ export class BrowserMediaPlayer {
|
|||||||
private onChange: () => void
|
private onChange: () => void
|
||||||
) {
|
) {
|
||||||
const player = new Audio(this.resolved.url);
|
const player = new Audio(this.resolved.url);
|
||||||
|
if (player.canPlayType(resolved.mime_type) === "") {
|
||||||
|
throw new Error(ERR_UNSUPPORTED_MEDIA);
|
||||||
|
}
|
||||||
player.autoplay = true;
|
player.autoplay = true;
|
||||||
player.volume = volume;
|
player.volume = volume;
|
||||||
player.addEventListener("play", this._handleChange);
|
player.addEventListener("play", this._handleChange);
|
||||||
|
@ -49,9 +49,13 @@ import {
|
|||||||
SUPPORT_VOLUME_SET,
|
SUPPORT_VOLUME_SET,
|
||||||
} from "../../data/media-player";
|
} from "../../data/media-player";
|
||||||
import { ResolvedMediaSource } from "../../data/media_source";
|
import { ResolvedMediaSource } from "../../data/media_source";
|
||||||
|
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "../lovelace/components/hui-marquee";
|
import "../lovelace/components/hui-marquee";
|
||||||
import { BrowserMediaPlayer } from "./browser-media-player";
|
import {
|
||||||
|
BrowserMediaPlayer,
|
||||||
|
ERR_UNSUPPORTED_MEDIA,
|
||||||
|
} from "./browser-media-player";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
@ -125,13 +129,25 @@ export class BarMediaPlayer extends LitElement {
|
|||||||
throw Error("Only browser supported");
|
throw Error("Only browser supported");
|
||||||
}
|
}
|
||||||
this._tearDownBrowserPlayer();
|
this._tearDownBrowserPlayer();
|
||||||
this._browserPlayer = new BrowserMediaPlayer(
|
try {
|
||||||
this.hass,
|
this._browserPlayer = new BrowserMediaPlayer(
|
||||||
item,
|
this.hass,
|
||||||
resolved,
|
item,
|
||||||
this._browserPlayerVolume,
|
resolved,
|
||||||
() => this.requestUpdate("_browserPlayer")
|
this._browserPlayerVolume,
|
||||||
);
|
() => this.requestUpdate("_browserPlayer")
|
||||||
|
);
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err.message === ERR_UNSUPPORTED_MEDIA) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
text: this.hass.localize(
|
||||||
|
"ui.components.media-browser.media_not_supported"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
this._newMediaExpected = false;
|
this._newMediaExpected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,10 @@ import {
|
|||||||
MediaPickedEvent,
|
MediaPickedEvent,
|
||||||
MediaPlayerItem,
|
MediaPlayerItem,
|
||||||
} from "../../data/media-player";
|
} from "../../data/media-player";
|
||||||
import { resolveMediaSource } from "../../data/media_source";
|
import {
|
||||||
|
ResolvedMediaSource,
|
||||||
|
resolveMediaSource,
|
||||||
|
} from "../../data/media_source";
|
||||||
import "../../layouts/ha-app-layout";
|
import "../../layouts/ha-app-layout";
|
||||||
import { haStyle } from "../../resources/styles";
|
import { haStyle } from "../../resources/styles";
|
||||||
import type { HomeAssistant, Route } from "../../types";
|
import type { HomeAssistant, Route } from "../../types";
|
||||||
@ -224,11 +227,19 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._player.showResolvingNewMediaPicked();
|
this._player.showResolvingNewMediaPicked();
|
||||||
|
let resolvedUrl: ResolvedMediaSource;
|
||||||
const resolvedUrl = await resolveMediaSource(
|
try {
|
||||||
this.hass,
|
resolvedUrl = await resolveMediaSource(this.hass, item.media_content_id);
|
||||||
item.media_content_id
|
} catch (err: any) {
|
||||||
);
|
showAlertDialog(this, {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.components.media-browser.media_browsing_error"
|
||||||
|
),
|
||||||
|
text: err.message,
|
||||||
|
});
|
||||||
|
this._player.hideResolvingNewMediaPicked();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (resolvedUrl.mime_type.startsWith("audio/")) {
|
if (resolvedUrl.mime_type.startsWith("audio/")) {
|
||||||
this._player.playItem(item, resolvedUrl);
|
this._player.playItem(item, resolvedUrl);
|
||||||
|
@ -9,3 +9,5 @@ export const brandsUrl = (options: BrandsOptions): string =>
|
|||||||
`https://brands.home-assistant.io/${options.useFallback ? "_/" : ""}${
|
`https://brands.home-assistant.io/${options.useFallback ? "_/" : ""}${
|
||||||
options.domain
|
options.domain
|
||||||
}/${options.darkOptimized ? "dark_" : ""}${options.type}.png`;
|
}/${options.darkOptimized ? "dark_" : ""}${options.type}.png`;
|
||||||
|
|
||||||
|
export const extractDomainFromBrandUrl = (url: string) => url.split("/")[4];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user