diff --git a/gallery/src/pages/components/ha-selector.ts b/gallery/src/pages/components/ha-selector.ts
index afa9d15e4c..ea4a892015 100644
--- a/gallery/src/pages/components/ha-selector.ts
+++ b/gallery/src/pages/components/ha-selector.ts
@@ -279,7 +279,7 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
can_play: true,
can_expand: false,
children_media_class: null,
- thumbnail: null,
+ thumbnail: "https://brands.home-assistant.io/_/image/logo.png",
},
{
title: "movie.mp4",
diff --git a/hassio/src/addon-store/hassio-addon-store.ts b/hassio/src/addon-store/hassio-addon-store.ts
index bff3331e6b..4d1f5ba1bf 100644
--- a/hassio/src/addon-store/hassio-addon-store.ts
+++ b/hassio/src/addon-store/hassio-addon-store.ts
@@ -110,8 +110,6 @@ class HassioAddonStore extends LitElement {
diff --git a/hassio/src/dialogs/hardware/dialog-hassio-hardware.ts b/hassio/src/dialogs/hardware/dialog-hassio-hardware.ts
index 7bc8775558..12a6519101 100755
--- a/hassio/src/dialogs/hardware/dialog-hassio-hardware.ts
+++ b/hassio/src/dialogs/hardware/dialog-hassio-hardware.ts
@@ -80,8 +80,6 @@ class HassioHardwareDialog extends LitElement {
>
{
if (this.selector.entity?.domain) {
const filterDomain = this.selector.entity.domain;
+ const filterDomainIsArray = Array.isArray(filterDomain);
const entityDomain = computeStateDomain(entity);
if (
- (Array.isArray(filterDomain) && !filterDomain.includes(entityDomain)) ||
- entityDomain !== filterDomain
+ (filterDomainIsArray && !filterDomain.includes(entityDomain)) ||
+ (!filterDomainIsArray && entityDomain !== filterDomain)
) {
return false;
}
diff --git a/src/components/ha-selector/ha-selector-media.ts b/src/components/ha-selector/ha-selector-media.ts
index bbfb203050..212f20c706 100644
--- a/src/components/ha-selector/ha-selector-media.ts
+++ b/src/components/ha-selector/ha-selector-media.ts
@@ -12,6 +12,7 @@ import {
} from "../../data/media-player";
import type { MediaSelector, MediaSelectorValue } from "../../data/selector";
import type { HomeAssistant } from "../../types";
+import { brandsUrl, extractDomainFromBrandUrl } from "../../util/brands-url";
import "../ha-alert";
import "../ha-form/ha-form";
import type { HaFormSchema } from "../ha-form/types";
@@ -50,6 +51,18 @@ export class HaMediaSelector extends LitElement {
getSignedPath(this.hass, thumbnail).then((signedPath) => {
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 {
this._thumbnailUrl = thumbnail;
}
diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts
index 882673175e..3de84c431d 100644
--- a/src/components/media-player/ha-media-player-browse.ts
+++ b/src/components/media-player/ha-media-player-browse.ts
@@ -34,23 +34,24 @@ import {
MediaPickedEvent,
MediaPlayerBrowseAction,
} from "../../data/media-player";
+import { browseLocalMediaPlayer } from "../../data/media_source";
+import { isTTSMediaSource } from "../../data/tts";
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
import { installResizeObserver } from "../../panels/lovelace/common/install-resize-observer";
import { haStyle } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
+import { brandsUrl, extractDomainFromBrandUrl } from "../../util/brands-url";
import { documentationUrl } from "../../util/documentation-url";
import "../entity/ha-entity-picker";
import "../ha-button-menu";
import "../ha-card";
import type { HaCard } from "../ha-card";
import "../ha-circular-progress";
+import "../ha-fab";
import "../ha-icon-button";
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 type { TtsMediaPickedEvent } from "./ha-browse-media-tts";
declare global {
interface HASSDomEvents {
@@ -681,6 +682,17 @@ export class HaMediaPlayerBrowse extends LitElement {
// Thumbnails served by local API require authentication
const signedPath = await getSignedPath(this.hass, thumbnailUrl);
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})`;
observer.unobserve(thumbnailCard); // loaded, so no need to observe anymore
diff --git a/src/data/input_datetime.ts b/src/data/input_datetime.ts
index 16a2f1b09d..08af492afd 100644
--- a/src/data/input_datetime.ts
+++ b/src/data/input_datetime.ts
@@ -1,3 +1,4 @@
+import { HassEntity } from "home-assistant-js-websocket";
import { HomeAssistant } from "../types";
export interface InputDateTime {
@@ -17,6 +18,19 @@ export interface InputDateTimeMutableParams {
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 = (
hass: HomeAssistant,
entityId: string,
diff --git a/src/dialogs/more-info/controls/more-info-input_datetime.ts b/src/dialogs/more-info/controls/more-info-input_datetime.ts
index a0f11eb726..2bcb5f534e 100644
--- a/src/dialogs/more-info/controls/more-info-input_datetime.ts
+++ b/src/dialogs/more-info/controls/more-info-input_datetime.ts
@@ -4,7 +4,10 @@ import { customElement, property } from "lit/decorators";
import "../../../components/ha-date-input";
import "../../../components/ha-time-input";
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";
@customElement("more-info-input_datetime")
@@ -24,7 +27,7 @@ class MoreInfoInputDatetime extends LitElement {
? html`
diff --git a/src/mixins/subscribe-mixin.ts b/src/mixins/subscribe-mixin.ts
index 9895637df3..66dbd99519 100644
--- a/src/mixins/subscribe-mixin.ts
+++ b/src/mixins/subscribe-mixin.ts
@@ -13,6 +13,9 @@ export const SubscribeMixin = >(
class SubscribeClass extends superClass {
@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>;
public connectedCallback() {
@@ -39,6 +42,16 @@ export const SubscribeMixin = >(
super.updated(changedProps);
if (changedProps.has("hass")) {
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 = >(
if (
this.__unsubs !== undefined ||
!(this as unknown as Element).isConnected ||
- this.hass === undefined
+ this.hass === undefined ||
+ this.hassSubscribeRequiredHostProps?.some(
+ (prop) => this[prop] === undefined
+ )
) {
return;
}
diff --git a/src/panels/config/dashboard/ha-config-dashboard.ts b/src/panels/config/dashboard/ha-config-dashboard.ts
index 2f67454a9c..731a5c7079 100644
--- a/src/panels/config/dashboard/ha-config-dashboard.ts
+++ b/src/panels/config/dashboard/ha-config-dashboard.ts
@@ -133,6 +133,7 @@ class HaConfigDashboard extends LitElement {
>
${this.hass.localize("panel.config")}
diff --git a/src/panels/config/energy/components/ha-energy-device-settings.ts b/src/panels/config/energy/components/ha-energy-device-settings.ts
index fe75d061f6..b135201f13 100644
--- a/src/panels/config/energy/components/ha-energy-device-settings.ts
+++ b/src/panels/config/energy/components/ha-energy-device-settings.ts
@@ -86,6 +86,7 @@ export class EnergyDeviceSettings extends LitElement {
: device.stat_consumption}
diff --git a/src/panels/media-browser/browser-media-player.ts b/src/panels/media-browser/browser-media-player.ts
index bc88414a98..b4096f9a7f 100644
--- a/src/panels/media-browser/browser-media-player.ts
+++ b/src/panels/media-browser/browser-media-player.ts
@@ -9,6 +9,8 @@ import {
import { ResolvedMediaSource } from "../../data/media_source";
import { HomeAssistant } from "../../types";
+export const ERR_UNSUPPORTED_MEDIA = "Unsupported Media";
+
export class BrowserMediaPlayer {
private player: HTMLAudioElement;
@@ -25,6 +27,9 @@ export class BrowserMediaPlayer {
private onChange: () => void
) {
const player = new Audio(this.resolved.url);
+ if (player.canPlayType(resolved.mime_type) === "") {
+ throw new Error(ERR_UNSUPPORTED_MEDIA);
+ }
player.autoplay = true;
player.volume = volume;
player.addEventListener("play", this._handleChange);
diff --git a/src/panels/media-browser/ha-bar-media-player.ts b/src/panels/media-browser/ha-bar-media-player.ts
index 14b6b6cdfd..3e913c6e43 100644
--- a/src/panels/media-browser/ha-bar-media-player.ts
+++ b/src/panels/media-browser/ha-bar-media-player.ts
@@ -49,9 +49,13 @@ import {
SUPPORT_VOLUME_SET,
} from "../../data/media-player";
import { ResolvedMediaSource } from "../../data/media_source";
+import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../../types";
import "../lovelace/components/hui-marquee";
-import { BrowserMediaPlayer } from "./browser-media-player";
+import {
+ BrowserMediaPlayer,
+ ERR_UNSUPPORTED_MEDIA,
+} from "./browser-media-player";
declare global {
interface HASSDomEvents {
@@ -125,13 +129,25 @@ export class BarMediaPlayer extends LitElement {
throw Error("Only browser supported");
}
this._tearDownBrowserPlayer();
- this._browserPlayer = new BrowserMediaPlayer(
- this.hass,
- item,
- resolved,
- this._browserPlayerVolume,
- () => this.requestUpdate("_browserPlayer")
- );
+ try {
+ this._browserPlayer = new BrowserMediaPlayer(
+ this.hass,
+ item,
+ resolved,
+ 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;
}
diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts
index af3339077e..30cc866b17 100644
--- a/src/panels/media-browser/ha-panel-media-browser.ts
+++ b/src/panels/media-browser/ha-panel-media-browser.ts
@@ -27,7 +27,10 @@ import {
MediaPickedEvent,
MediaPlayerItem,
} from "../../data/media-player";
-import { resolveMediaSource } from "../../data/media_source";
+import {
+ ResolvedMediaSource,
+ resolveMediaSource,
+} from "../../data/media_source";
import "../../layouts/ha-app-layout";
import { haStyle } from "../../resources/styles";
import type { HomeAssistant, Route } from "../../types";
@@ -224,11 +227,19 @@ class PanelMediaBrowser extends LitElement {
}
this._player.showResolvingNewMediaPicked();
-
- const resolvedUrl = await resolveMediaSource(
- this.hass,
- item.media_content_id
- );
+ let resolvedUrl: ResolvedMediaSource;
+ try {
+ resolvedUrl = await resolveMediaSource(this.hass, 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/")) {
this._player.playItem(item, resolvedUrl);
diff --git a/src/util/brands-url.ts b/src/util/brands-url.ts
index 0d39127baf..479cd5eb50 100644
--- a/src/util/brands-url.ts
+++ b/src/util/brands-url.ts
@@ -9,3 +9,5 @@ export const brandsUrl = (options: BrandsOptions): string =>
`https://brands.home-assistant.io/${options.useFallback ? "_/" : ""}${
options.domain
}/${options.darkOptimized ? "dark_" : ""}${options.type}.png`;
+
+export const extractDomainFromBrandUrl = (url: string) => url.split("/")[4];