mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-02 00:27:49 +01:00
Add hass url to brand images (#29961)
This commit is contained in:
@@ -173,11 +173,14 @@ export class HaDevicePicker extends LitElement {
|
||||
alt=""
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
src=${brandsUrl({
|
||||
domain: configEntry.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: configEntry.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
/>`
|
||||
: nothing}
|
||||
<span slot="headline">${primary}</span>
|
||||
@@ -195,11 +198,14 @@ export class HaDevicePicker extends LitElement {
|
||||
alt=""
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
src=${brandsUrl({
|
||||
domain: item.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: item.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
/>
|
||||
`
|
||||
: nothing}
|
||||
|
||||
@@ -138,10 +138,10 @@ export class StateBadge extends LitElement {
|
||||
let imageUrl =
|
||||
stateObj.attributes.entity_picture_local ||
|
||||
stateObj.attributes.entity_picture;
|
||||
imageUrl = addBrandsAuth(imageUrl);
|
||||
if (this.hass) {
|
||||
imageUrl = this.hass.hassUrl(imageUrl);
|
||||
}
|
||||
imageUrl = addBrandsAuth(imageUrl, this.hass?.auth.data.hassUrl);
|
||||
if (domain === "camera") {
|
||||
imageUrl = cameraUrlWithWidthHeight(imageUrl, 80, 80);
|
||||
}
|
||||
|
||||
@@ -61,11 +61,14 @@ export class HaDomainIcon extends LitElement {
|
||||
`;
|
||||
}
|
||||
if (this.brandFallback) {
|
||||
const image = brandsUrl({
|
||||
domain: this.domain!,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
});
|
||||
const image = brandsUrl(
|
||||
{
|
||||
domain: this.domain!,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
);
|
||||
return html`
|
||||
<img
|
||||
alt=""
|
||||
|
||||
@@ -220,11 +220,14 @@ export class HaRelatedItems extends LitElement {
|
||||
>
|
||||
<ha-list-item hasMeta graphic="icon">
|
||||
<img
|
||||
.src=${brandsUrl({
|
||||
domain: entry.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
.src=${brandsUrl(
|
||||
{
|
||||
domain: entry.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
alt=${entry.domain}
|
||||
@@ -245,11 +248,14 @@ export class HaRelatedItems extends LitElement {
|
||||
>
|
||||
<ha-list-item hasMeta graphic="icon">
|
||||
<img
|
||||
.src=${brandsUrl({
|
||||
domain: integration,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
.src=${brandsUrl(
|
||||
{
|
||||
domain: integration,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
alt=${integration}
|
||||
|
||||
@@ -79,11 +79,14 @@ export class HaMediaSelector extends LitElement {
|
||||
if (thumbnail && isBrandUrl(thumbnail)) {
|
||||
// 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",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
});
|
||||
this._thumbnailUrl = brandsUrl(
|
||||
{
|
||||
domain: extractDomainFromBrandUrl(thumbnail),
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
);
|
||||
} else if (thumbnail && thumbnail.startsWith("/")) {
|
||||
this._thumbnailUrl = undefined;
|
||||
// Thumbnails served by local API require authentication
|
||||
|
||||
@@ -1022,11 +1022,14 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
alt=""
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
src=${brandsUrl({
|
||||
domain: (item as DevicePickerItem).domain!,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: (item as DevicePickerItem).domain!,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
/>
|
||||
`
|
||||
: type === "floor"
|
||||
|
||||
@@ -768,11 +768,14 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
if (isBrandUrl(thumbnailUrl)) {
|
||||
// The backend is not aware of the theme used by the users,
|
||||
// so we rewrite the URL to show a proper icon
|
||||
return brandsUrl({
|
||||
domain: extractDomainFromBrandUrl(thumbnailUrl),
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
});
|
||||
return brandsUrl(
|
||||
{
|
||||
domain: extractDomainFromBrandUrl(thumbnailUrl),
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
);
|
||||
}
|
||||
|
||||
if (thumbnailUrl.startsWith("/")) {
|
||||
|
||||
@@ -577,11 +577,14 @@ export class HaTargetPickerItemRow extends LitElement {
|
||||
try {
|
||||
const data = await getConfigEntry(this.hass, configEntryId);
|
||||
const domain = data.config_entry.domain;
|
||||
this._iconImg = brandsUrl({
|
||||
domain: domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
});
|
||||
this._iconImg = brandsUrl(
|
||||
{
|
||||
domain: domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
);
|
||||
|
||||
this._setDomainName(domain);
|
||||
} catch {
|
||||
|
||||
@@ -203,11 +203,14 @@ export class HaTargetPickerValueChip extends LitElement {
|
||||
try {
|
||||
const data = await getConfigEntry(this.hass, configEntryId);
|
||||
const domain = data.config_entry.domain;
|
||||
this._iconImg = brandsUrl({
|
||||
domain: domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
});
|
||||
this._iconImg = brandsUrl(
|
||||
{
|
||||
domain: domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
);
|
||||
|
||||
this._setDomainName(domain);
|
||||
} catch {
|
||||
|
||||
@@ -17,11 +17,14 @@ export class VoiceAssistantBrandicon extends LitElement {
|
||||
<img
|
||||
class="logo"
|
||||
alt=${voiceAssistants[this.voiceAssistantId].name}
|
||||
src=${brandsUrl({
|
||||
domain: voiceAssistants[this.voiceAssistantId].domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: voiceAssistants[this.voiceAssistantId].domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
|
||||
@@ -140,11 +140,15 @@ class StepFlowCreateEntry extends LitElement {
|
||||
this.hass.localize,
|
||||
domains[device.primary_config_entry]
|
||||
)}
|
||||
src=${brandsUrl({
|
||||
domain: domains[device.primary_config_entry],
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain:
|
||||
domains[device.primary_config_entry],
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>`
|
||||
|
||||
@@ -21,11 +21,14 @@ class IntegrationBadge extends LitElement {
|
||||
<div class="icon">
|
||||
<img
|
||||
alt=""
|
||||
src=${brandsUrl({
|
||||
domain: this.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.darkOptimizedIcon,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: this.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.darkOptimizedIcon,
|
||||
},
|
||||
location.origin
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
|
||||
@@ -769,11 +769,14 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
alt=""
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
/>
|
||||
`;
|
||||
|
||||
|
||||
@@ -149,11 +149,14 @@ class HaBackupConfigAgents extends LitElement {
|
||||
|
||||
return html`
|
||||
<img
|
||||
.src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
.src=${brandsUrl(
|
||||
{
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
alt=""
|
||||
|
||||
@@ -63,11 +63,14 @@ class HaBackupAgentsPicker extends LitElement {
|
||||
? html` <ha-svg-icon .path=${mdiNas} slot="start"></ha-svg-icon> `
|
||||
: html`
|
||||
<img
|
||||
.src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
.src=${brandsUrl(
|
||||
{
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
alt=""
|
||||
|
||||
@@ -227,11 +227,14 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) {
|
||||
return html`
|
||||
<img
|
||||
title=${name}
|
||||
.src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
.src=${brandsUrl(
|
||||
{
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
height="24"
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
|
||||
@@ -204,12 +204,15 @@ class HaConfigBackupDetails extends LitElement {
|
||||
`
|
||||
: html`
|
||||
<img
|
||||
.src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized:
|
||||
this.hass.themes?.darkMode,
|
||||
})}
|
||||
.src=${brandsUrl(
|
||||
{
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized:
|
||||
this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
alt=${`${domain} logo`}
|
||||
|
||||
@@ -246,11 +246,14 @@ class HaConfigBackupSettings extends LitElement {
|
||||
? html`<ha-card class="cloud-info">
|
||||
<div class="cloud-header">
|
||||
<img
|
||||
.src=${brandsUrl({
|
||||
domain: "cloud",
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
.src=${brandsUrl(
|
||||
{
|
||||
domain: "cloud",
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
alt="Nabu Casa logo"
|
||||
|
||||
@@ -55,11 +55,14 @@ export class AITaskPref extends LitElement {
|
||||
<h1 class="card-header">
|
||||
<img
|
||||
alt=""
|
||||
src=${brandsUrl({
|
||||
domain: "ai_task",
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: "ai_task",
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>${this.hass.localize("ui.panel.config.ai_task.header")}
|
||||
|
||||
@@ -364,11 +364,14 @@ export class HaConfigDevicePage extends LitElement {
|
||||
<img
|
||||
slot="graphic"
|
||||
alt=${domainToName(this.hass.localize, integration.domain)}
|
||||
src=${brandsUrl({
|
||||
domain: integration.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: integration.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
@error=${this._onImageError}
|
||||
@@ -763,11 +766,14 @@ export class HaConfigDevicePage extends LitElement {
|
||||
this.hass.localize,
|
||||
integrations[0].domain
|
||||
)}
|
||||
src=${brandsUrl({
|
||||
domain: integrations[0].domain,
|
||||
type: "logo",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: integrations[0].domain,
|
||||
type: "logo",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
@load=${this._onImageLoad}
|
||||
|
||||
@@ -535,11 +535,14 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
||||
alt=""
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
src=${brandsUrl({
|
||||
domain: device.domains[0],
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: device.domains[0],
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
/>`
|
||||
: "",
|
||||
},
|
||||
|
||||
@@ -186,11 +186,14 @@ export class EnergyGridSettings extends LitElement {
|
||||
alt=""
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
src=${brandsUrl({
|
||||
domain: "co2signal",
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: "co2signal",
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
/>
|
||||
<span class="content">${this._co2ConfigEntry.title}</span>
|
||||
<ha-icon-button
|
||||
|
||||
@@ -193,11 +193,14 @@ export class DialogEnergySolarSettings
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
style="height: 24px; margin-right: 16px; margin-inline-end: 16px; margin-inline-start: initial;"
|
||||
src=${brandsUrl({
|
||||
domain: entry.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: entry.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
/>${entry.title}
|
||||
</div>`}
|
||||
>
|
||||
|
||||
@@ -229,12 +229,15 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
|
||||
boardId = boardData.board!.hassio_board_id;
|
||||
boardName = boardData.name;
|
||||
documentationURL = boardData.url;
|
||||
imageURL = hardwareBrandsUrl({
|
||||
category: "boards",
|
||||
manufacturer: boardData.board!.manufacturer,
|
||||
model: boardData.board!.model,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
});
|
||||
imageURL = hardwareBrandsUrl(
|
||||
{
|
||||
category: "boards",
|
||||
manufacturer: boardData.board!.manufacturer,
|
||||
model: boardData.board!.model,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
);
|
||||
} else if (this._OSData?.board) {
|
||||
boardId = this._OSData.board;
|
||||
boardName = BOARD_NAMES[this._OSData.board];
|
||||
|
||||
@@ -252,11 +252,14 @@ export class DialogHelperDetail extends LitElement {
|
||||
slot="graphic"
|
||||
loading="lazy"
|
||||
alt=""
|
||||
src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
|
||||
@@ -376,11 +376,14 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
||||
<div class="logo-container">
|
||||
<img
|
||||
alt=${domainToName(this.hass.localize, this.domain)}
|
||||
src=${brandsUrl({
|
||||
domain: this.domain,
|
||||
type: "icon@2x",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: this.domain,
|
||||
type: "icon@2x",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
@load=${this._onImageLoad}
|
||||
|
||||
@@ -64,11 +64,14 @@ class HaDomainIntegrations extends LitElement {
|
||||
alt=""
|
||||
slot="graphic"
|
||||
loading="lazy"
|
||||
src=${brandsUrl({
|
||||
domain: flow.handler,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: flow.handler,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
@@ -112,11 +115,14 @@ class HaDomainIntegrations extends LitElement {
|
||||
slot="graphic"
|
||||
loading="lazy"
|
||||
alt=""
|
||||
src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
@@ -175,11 +181,14 @@ class HaDomainIntegrations extends LitElement {
|
||||
slot="graphic"
|
||||
loading="lazy"
|
||||
alt=""
|
||||
src=${brandsUrl({
|
||||
domain: this.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: this.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
|
||||
@@ -31,11 +31,14 @@ export class HaIntegrationActionCard extends LitElement {
|
||||
<div class="card-content">
|
||||
<img
|
||||
alt=""
|
||||
src=${brandsUrl({
|
||||
domain: this.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: this.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
@error=${this._onImageError}
|
||||
|
||||
@@ -32,11 +32,14 @@ export class HaIntegrationHeader extends LitElement {
|
||||
<div class="header">
|
||||
<img
|
||||
alt=""
|
||||
src=${brandsUrl({
|
||||
domain: this.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: this.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
@error=${this._onImageError}
|
||||
|
||||
@@ -62,11 +62,14 @@ export class HaIntegrationListItem extends ListItemBase {
|
||||
: html`<img
|
||||
alt=""
|
||||
loading="lazy"
|
||||
src=${brandsUrl({
|
||||
domain: this.integration.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: this.integration.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>`}
|
||||
|
||||
@@ -67,11 +67,14 @@ class DialogMatterOpenCommissioningWindow extends LitElement {
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
alt=${domainToName(this.hass.localize, "matter")}
|
||||
src=${brandsUrl({
|
||||
domain: "matter",
|
||||
type: "logo",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: "matter",
|
||||
type: "logo",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
/>
|
||||
<ha-qr-code
|
||||
.data=${this._commissionParams.setup_qr_code}
|
||||
|
||||
@@ -222,11 +222,14 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
>
|
||||
<img
|
||||
slot="graphic"
|
||||
.src=${brandsUrl({
|
||||
domain: router.brand,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
.src=${brandsUrl(
|
||||
{
|
||||
domain: router.brand,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
alt=${router.brand}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
|
||||
@@ -208,11 +208,14 @@ class HaConfigLabs extends SubscribeMixin(LitElement) {
|
||||
<div class="card-header">
|
||||
<img
|
||||
alt=""
|
||||
src=${brandsUrl({
|
||||
domain: preview_feature.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: preview_feature.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
|
||||
@@ -74,11 +74,14 @@ class HaConfigRepairs extends LitElement {
|
||||
slot="start"
|
||||
alt=${domainName}
|
||||
loading="lazy"
|
||||
src=${brandsUrl({
|
||||
domain: issue.issue_domain || issue.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: issue.issue_domain || issue.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
.title=${domainName}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
|
||||
@@ -55,11 +55,14 @@ class IntegrationsStartupTime extends LitElement {
|
||||
<img
|
||||
alt=""
|
||||
loading="lazy"
|
||||
src=${brandsUrl({
|
||||
domain: setup.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
src=${brandsUrl(
|
||||
{
|
||||
domain: setup.domain,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)}
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
slot="start"
|
||||
|
||||
@@ -157,11 +157,14 @@ class HaLogbookRenderer extends LitElement {
|
||||
!item.state &&
|
||||
domain &&
|
||||
isComponentLoaded(this.hass, domain)
|
||||
? brandsUrl({
|
||||
domain: domain!,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})
|
||||
? brandsUrl(
|
||||
{
|
||||
domain: domain!,
|
||||
type: "icon",
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
},
|
||||
this.hass.auth.data.hassUrl
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const traceContext =
|
||||
|
||||
@@ -144,7 +144,10 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
|
||||
|
||||
if (!entityPicture) return undefined;
|
||||
|
||||
let imageUrl = this.hass!.hassUrl(addBrandsAuth(entityPicture));
|
||||
let imageUrl = addBrandsAuth(
|
||||
this.hass!.hassUrl(entityPicture),
|
||||
this.hass?.auth.data.hassUrl
|
||||
);
|
||||
if (computeStateDomain(stateObj) === "camera") {
|
||||
imageUrl = cameraUrlWithWidthHeight(imageUrl, 32, 32);
|
||||
}
|
||||
|
||||
@@ -159,7 +159,10 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
|
||||
if (!entityPicture) return undefined;
|
||||
|
||||
let imageUrl = this.hass!.hassUrl(addBrandsAuth(entityPicture));
|
||||
let imageUrl = addBrandsAuth(
|
||||
this.hass!.hassUrl(entityPicture),
|
||||
this.hass?.auth.data.hassUrl
|
||||
);
|
||||
if (computeDomain(entity.entity_id) === "camera") {
|
||||
imageUrl = cameraUrlWithWidthHeight(imageUrl, 80, 80);
|
||||
}
|
||||
|
||||
@@ -55,44 +55,62 @@ export const clearBrandsTokenRefresh = (): void => {
|
||||
}
|
||||
};
|
||||
|
||||
export const brandsUrl = (options: BrandsOptions): string => {
|
||||
export const brandsUrl = (options: BrandsOptions, hassUrl?: string): string => {
|
||||
hassUrl = hassUrl ?? location.origin;
|
||||
const base = `/api/brands/integration/${options.domain}/${
|
||||
options.darkOptimized ? "dark_" : ""
|
||||
}${options.type}.png`;
|
||||
|
||||
const url = new URL(base, hassUrl);
|
||||
if (_brandsAccessToken) {
|
||||
return `${base}?token=${_brandsAccessToken}`;
|
||||
url.searchParams.set("token", _brandsAccessToken);
|
||||
}
|
||||
return base;
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
export const hardwareBrandsUrl = (options: HardwareBrandsOptions): string => {
|
||||
export const hardwareBrandsUrl = (
|
||||
options: HardwareBrandsOptions,
|
||||
hassUrl?: string
|
||||
): string => {
|
||||
hassUrl = hassUrl ?? location.origin;
|
||||
const base = `/api/brands/hardware/${options.category}/${
|
||||
options.darkOptimized ? "dark_" : ""
|
||||
}${options.manufacturer}${options.model ? `_${options.model}` : ""}.png`;
|
||||
|
||||
const url = new URL(base, hassUrl);
|
||||
if (_brandsAccessToken) {
|
||||
return `${base}?token=${_brandsAccessToken}`;
|
||||
url.searchParams.set("token", _brandsAccessToken);
|
||||
}
|
||||
return base;
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
export const addBrandsAuth = (url: string): string => {
|
||||
if (!_brandsAccessToken || !url.startsWith("/api/brands/")) {
|
||||
export const addBrandsAuth = (url: string, hassUrl?: string): string => {
|
||||
hassUrl = hassUrl ?? location.origin;
|
||||
if (!_brandsAccessToken) {
|
||||
return url;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedUrl = new URL(url, hassUrl);
|
||||
if (!parsedUrl.pathname.startsWith("/api/brands/")) {
|
||||
return url;
|
||||
}
|
||||
parsedUrl.searchParams.set("token", _brandsAccessToken);
|
||||
return parsedUrl.toString();
|
||||
} catch {
|
||||
return url;
|
||||
}
|
||||
const fullUrl = new URL(url, location.origin);
|
||||
fullUrl.searchParams.set("token", _brandsAccessToken);
|
||||
return `${fullUrl.pathname}${fullUrl.search}`;
|
||||
};
|
||||
|
||||
export const extractDomainFromBrandUrl = (url: string): string => {
|
||||
// Handle both new local API paths (/api/brands/integration/{domain}/...)
|
||||
// and legacy CDN URLs (https://brands.home-assistant.io/_/{domain}/...)
|
||||
if (url.startsWith("/api/brands/")) {
|
||||
const parsed = new URL(url, location.origin);
|
||||
if (parsed.pathname.startsWith("/api/brands/")) {
|
||||
// /api/brands/integration/{domain}/... -> ["" ,"api", "brands", "integration", "{domain}", ...]
|
||||
return url.split("/")[4];
|
||||
return parsed.pathname.split("/")[4];
|
||||
}
|
||||
// https://brands.home-assistant.io/_/{domain}/... -> ["", "_", "{domain}", ...]
|
||||
const parsed = new URL(url);
|
||||
const segments = parsed.pathname.split("/").filter((s) => s.length > 0);
|
||||
const underscoreIdx = segments.indexOf("_");
|
||||
if (underscoreIdx !== -1 && underscoreIdx + 1 < segments.length) {
|
||||
@@ -101,6 +119,14 @@ export const extractDomainFromBrandUrl = (url: string): string => {
|
||||
return segments[1] ?? "";
|
||||
};
|
||||
|
||||
export const isBrandUrl = (thumbnail: string | ""): boolean =>
|
||||
thumbnail.startsWith("/api/brands/") ||
|
||||
thumbnail.startsWith("https://brands.home-assistant.io/");
|
||||
export const isBrandUrl = (thumbnail: string | ""): boolean => {
|
||||
try {
|
||||
const url = new URL(thumbnail, location.origin);
|
||||
return (
|
||||
url.pathname.startsWith("/api/brands/") ||
|
||||
thumbnail.startsWith("https://brands.home-assistant.io/")
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,21 +11,30 @@ import {
|
||||
describe("Generate brands Url", () => {
|
||||
it("Generate logo brands url for cloud component", () => {
|
||||
assert.strictEqual(
|
||||
brandsUrl({ domain: "cloud", type: "logo" }),
|
||||
"/api/brands/integration/cloud/logo.png"
|
||||
brandsUrl(
|
||||
{ domain: "cloud", type: "logo" },
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"http://homeassistant.local:8123/api/brands/integration/cloud/logo.png"
|
||||
);
|
||||
});
|
||||
it("Generate icon brands url for cloud component", () => {
|
||||
assert.strictEqual(
|
||||
brandsUrl({ domain: "cloud", type: "icon" }),
|
||||
"/api/brands/integration/cloud/icon.png"
|
||||
brandsUrl(
|
||||
{ domain: "cloud", type: "icon" },
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"http://homeassistant.local:8123/api/brands/integration/cloud/icon.png"
|
||||
);
|
||||
});
|
||||
|
||||
it("Generate dark theme optimized logo brands url for cloud component", () => {
|
||||
assert.strictEqual(
|
||||
brandsUrl({ domain: "cloud", type: "logo", darkOptimized: true }),
|
||||
"/api/brands/integration/cloud/dark_logo.png"
|
||||
brandsUrl(
|
||||
{ domain: "cloud", type: "logo", darkOptimized: true },
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"http://homeassistant.local:8123/api/brands/integration/cloud/dark_logo.png"
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -33,14 +42,20 @@ describe("Generate brands Url", () => {
|
||||
describe("addBrandsAuth", () => {
|
||||
it("Returns non-brands URLs unchanged", () => {
|
||||
assert.strictEqual(
|
||||
addBrandsAuth("/api/camera_proxy/camera.foo?token=abc"),
|
||||
addBrandsAuth(
|
||||
"/api/camera_proxy/camera.foo?token=abc",
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"/api/camera_proxy/camera.foo?token=abc"
|
||||
);
|
||||
});
|
||||
|
||||
it("Returns brands URL unchanged when no token is available", () => {
|
||||
assert.strictEqual(
|
||||
addBrandsAuth("/api/brands/integration/demo/icon.png"),
|
||||
addBrandsAuth(
|
||||
"/api/brands/integration/demo/icon.png",
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"/api/brands/integration/demo/icon.png"
|
||||
);
|
||||
});
|
||||
@@ -52,8 +67,11 @@ describe("addBrandsAuth", () => {
|
||||
await fetchBrandsAccessToken(mockHass);
|
||||
|
||||
assert.strictEqual(
|
||||
addBrandsAuth("/api/brands/integration/demo/icon.png"),
|
||||
"/api/brands/integration/demo/icon.png?token=test-token-123"
|
||||
addBrandsAuth(
|
||||
"/api/brands/integration/demo/icon.png",
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"http://homeassistant.local:8123/api/brands/integration/demo/icon.png?token=test-token-123"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -64,8 +82,11 @@ describe("addBrandsAuth", () => {
|
||||
await fetchBrandsAccessToken(mockHass);
|
||||
|
||||
assert.strictEqual(
|
||||
addBrandsAuth("/api/brands/integration/demo/icon.png?token=old-token"),
|
||||
"/api/brands/integration/demo/icon.png?token=new-token"
|
||||
addBrandsAuth(
|
||||
"/api/brands/integration/demo/icon.png?token=old-token",
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"http://homeassistant.local:8123/api/brands/integration/demo/icon.png?token=new-token"
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -90,8 +111,11 @@ describe("scheduleBrandsTokenRefresh", () => {
|
||||
await fetchBrandsAccessToken(mockHass);
|
||||
assert.strictEqual(callCount, 1);
|
||||
assert.strictEqual(
|
||||
brandsUrl({ domain: "test", type: "icon" }),
|
||||
"/api/brands/integration/test/icon.png?token=token-1"
|
||||
brandsUrl(
|
||||
{ domain: "test", type: "icon" },
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"http://homeassistant.local:8123/api/brands/integration/test/icon.png?token=token-1"
|
||||
);
|
||||
|
||||
scheduleBrandsTokenRefresh(mockHass);
|
||||
@@ -100,8 +124,11 @@ describe("scheduleBrandsTokenRefresh", () => {
|
||||
await vi.advanceTimersByTimeAsync(30 * 60 * 1000);
|
||||
assert.strictEqual(callCount, 2);
|
||||
assert.strictEqual(
|
||||
brandsUrl({ domain: "test", type: "icon" }),
|
||||
"/api/brands/integration/test/icon.png?token=token-2"
|
||||
brandsUrl(
|
||||
{ domain: "test", type: "icon" },
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"http://homeassistant.local:8123/api/brands/integration/test/icon.png?token=token-2"
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user