Use authenticated brands proxy for repository icons - #937
Conversation
The dashboard constructed icon URLs via the upstream `brandsUrl` helper
at a pinned submodule (3ffbd435, Jan 2025) that predates the
brands-proxy API introduced in HA 2026.3. After upstream rewrote
`brands-url.ts` to return `/api/brands/integration/{domain}/icon.png`
with an access token, custom integrations shipping inline brand assets
no longer render (the legacy CDN no longer accepts new custom
integration submissions).
Bump `homeassistant-frontend` to current master, drop the now-removed
`useFallback` parameter, and fetch/schedule the brands access token
from the dashboard's own bundle (module-level token state is
bundle-scoped, so HACS must populate its own copy). Clear the refresh
interval on disconnect.
Fixes hacs/integration#5171
Fixes hacs/integration#5223
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@ludeeus sorry for pinging you, but any chance to take a look at this PR and additionally hacs/integration#5228? We would love to have icons back for integrations. It's fixing hacs/integration#5171 and hacs/integration#5223 Hope to hear from you! |
|
I don't think that will work. If the integration is not installed, the local image will not be available, right? |
|
That is correct @piitaya. |
|
You are right, but that's why I have another PR: hacs/integration#5228 |
|
The solution I had in mind : a HACS endpoint ( @manuveli This PR is only about |
|
It would be great to have this supported soon, since |
Nachgeprüft statt vermutet, und das Ergebnis widerlegt den bisherigen Text: ein Eintrag im brands-Repo ist für Custom-Integrationen seit Home Assistant 2026.3 nicht mehr nötig - und nicht mehr möglich. Der Versuch (PR #10853) wurde binnen einer Minute vom Bot geschlossen: "we no longer accept brand icons for custom integrations in this repository". Dass die HACS-Übersicht trotzdem den Platzhalter zeigt, hat einen anderen Grund: HACS 2.0.5 baut seine Bild-URLs fest gegen brands.home-assistant.io, statt den lokalen Proxy zu nutzen, den HA für genau diesen Fall anbietet (im mitgelieferten hacs_frontend nachgesehen: kein einziger Treffer auf /api/brands). Dort ist das bekannt (hacs/frontend#937, hacs/integration#5179); von dieser Integration aus lässt sich daran nichts ändern. Home Assistant selbst zeigt das Icon längst - der Proxy liefert an der Produktivinstanz die installierte brand/icon.png md5-identisch aus. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Restores custom_components/noaa_it_all/brand/ byte-for-byte to its previous state. The wordmark logos and the dark_icon.png deletion are reverted, and tests/test_manifest.py goes back to the original pinned dimensions. Also corrects docs/BRANDING.md, which overstated the problem. The earlier claim that "no change to this repository can fix it" was wrong, and the observation that disproves it is simple: other custom integrations do show icons in the HACS store list. Verified: home-assistant/brands/custom_integrations/alarmo -> exists, icon shows home-assistant/brands/custom_integrations/frigate -> exists, icon shows home-assistant/brands/custom_integrations/noaa_it_all -> 404, placeholder So the store-list icon depends on exactly one thing: whether the domain is in the home-assistant/brands repo. Ours is not. Being in the HACS default store is separate and does not grant a CDN icon -- dawg-io/noaa_it_all is already listed in hacs/default and still shows the placeholder. That makes submitting custom_integrations/noaa_it_all/ to home-assistant/brands the actual fix, and worth one attempt. The repo has a workflow that auto-closes PRs adding new custom-integration folders, but its history shows new entries still landing through spring 2026, so the outcome is not certain until tried. Waiting on hacs/frontend#937 is the fallback, not the only option. The README /blob/ URL fix, its regression test, and the hacs.json tidy are unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011TA3si1SuRW4W8MD7N9x7a
Removes docs/BRANDING.md and reverts .github/copilot-instructions.md, per the decision to keep only the README fix. The copilot-instructions changes included three accurate corrections to a stale "manual trigger only" claim about hacs-validate.yml, but those are out of scope here and are reverted rather than carried along; they remain worth a separate change. test_root_icon_exists lost its pointer to the deleted doc and now states the reason inline: the root icon.png is the README header source and is not read by HACS or Home Assistant for store or UI display. What remains in this branch: - README.md: the header image used a github.com/.../blob/... URL, which serves an HTML page rather than an image, so it rendered broken on GitHub and on the HACS repository page (hacs.json sets render_readme). Now points at raw.githubusercontent.com. - tests/test_manifest.py: TestReadmeImages strips fenced code blocks, then fails on any github.com /blob/ image URL. Verified by reintroducing the old URL -- the test fails, and passes again once restored. - hacs.json: stray tab replaced with spaces. custom_components/noaa_it_all/brand/ is byte-for-byte identical to main. For the record, on the HACS store-list icon this branch does not address: it is served from brands.home-assistant.io, backed by home-assistant/brands, where noaa_it_all is absent and can no longer be added -- close-new-custom-integrations.yml (merged 2026-03-03) auto-closes any PR adding a new custom_integrations folder, confirmed on brands PRs #10952 and #10930 from this month. Integrations that do show icons were added before that cutoff; open_pico, for instance, landed 2025-10-18. It resolves only when HACS ships hacs/frontend#937. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011TA3si1SuRW4W8MD7N9x7a
Summary
The HACS dashboard shows "icon not available" for custom integrations that ship inline brand assets (a feature introduced in Home Assistant 2026.3). Root cause is a combination of three things:
homeassistant-frontendsubmodule is pinned to3ffbd435(2025-01-09) — pre-brands-proxy.brandsUrl({...})still returnshttps://brands.home-assistant.io/_/{domain}/icon.png. That CDN no longer accepts new custom-integration submissions (see the brands-proxy API blog post), so custom-integration icons 404.useFallback: true— a parameter that has since been removed from upstreamBrandsOptions, so a naive submodule bump breaks the TypeScript compile.Changes
homeassistant-frontendsubmodule to current upstreammaster— brings in the newbrands-url.tsthat returns/api/brands/integration/{domain}/icon.png?token=…, along withfetchAndScheduleBrandsAccessToken/clearBrandsTokenRefreshexports.useFallback: truefrom thebrandsUrl({...})call insrc/dashboards/hacs-dashboard.ts— the field no longer exists onBrandsOptions. Fallback is now the HA backend's job (stale-while-revalidate through the proxy)._brandsAccessTokeninbrands-url.tsis bundle-scoped, so HACS must populate its own copy; upstream'sfetchAndScheduleBrandsAccessTokensilently swallows failures on older HA backends. Clear the refresh interval indisconnectedCallbackto avoid leaking thesetIntervalwhen the panel unmounts.Fixes hacs/integration#5171
Fixes hacs/integration#5223
A companion PR in the integration repo (hacs/integration) updates the HACS Update entity's
entity_pictureto use the same authenticated proxy path.Test plan
script/bootstrap && script/buildcompiles clean (CI via.github/workflows/test.yml).brand/icon.pnginline: downloads table shows real icons; rendered<img>hassrc=/api/brands/integration/{domain}/icon.png?token=….darkOptimized(filename prefixed withdark_).brands/access_tokenWS command): dashboard renders without thrown errors —fetchAndScheduleBrandsAccessTokencatches silently.🤖 Generated with Claude Code