home-assistant/companion.home-assistant 10
:book: Home Assistant Companion docs
SeanPM5/alerts.home-assistant.io 0
Home Assistant Alerts
:iphone: Home Assistant Companion for Android
🎨 Brands for Home Assistant
Lovelace button-card for home assistant
:house_with_garden: Open source home automation that puts local control and privacy first
SeanPM5/developers.home-assistant 0
Developers website for Home Assistant.
The frontend of HACS
Manage (Install, track, upgrade) and discover custom elements for Home Assistant.
pull request commenthome-assistant/companion.home-assistant
Bump @docusaurus/preset-classic from 2.0.0-alpha.68 to 2.0.0-alpha.69
Hi @dependabot[bot],
It seems you haven't yet signed a CLA. Please do so here.
Once you do that we will be able to review and accept this pull request.
Thanks!
comment created time in 10 minutes
create barnchhome-assistant/companion.home-assistant
branch : dependabot/npm_and_yarn/master/docusaurus/preset-classic-2.0.0-alpha.69
created branch time in 10 minutes
PR opened home-assistant/companion.home-assistant
Bumps @docusaurus/preset-classic from 2.0.0-alpha.68 to 2.0.0-alpha.69. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/facebook/docusaurus/releases">@docusaurus/preset-classic's releases</a>.</em></p> <blockquote> <h2>2.0.0-alpha.69 (2020-11-24)</h2> <h4>:rocket: New Feature</h4> <ul> <li><code>docusaurus-types</code>, <code>docusaurus</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3802">#3802</a> feat(v2): add baseUrlIssueBanner configuration to disable banner (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> </li> </ul> <h4>:bug: Bug Fix</h4> <ul> <li><code>docusaurus-theme-classic</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3807">#3807</a> chore(v2): upgrade Infima to v0.2.0-alpha.18 (<a href="https://github.com/lex111">@lex111</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3806">#3806</a> fix(v2): remove aria-hidden attr from anchor link of heading (<a href="https://github.com/lex111">@lex111</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3784">#3784</a> fix(v2): fix missing logo in dark theme when darkSrc was not set (<a href="https://github.com/Simek">@Simek</a>)</li> </ul> </li> <li><code>docusaurus-theme-search-algolia</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3804">#3804</a> fix(v2): Algolia: allow contextualSearch + facetFilters (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> </li> <li><code>docusaurus</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3796">#3796</a> Adds createRequire for preset resolution (<a href="https://github.com/arcanis">@arcanis</a>)</li> </ul> </li> </ul> <h4>:memo: Documentation</h4> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3803">#3803</a> docs(v2): fix docusaurus init issue when not using <a href="https://github.com/latest">@latest</a> (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3785">#3785</a> docs(v2): recommend Docusaurus 2 usage (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3783">#3783</a> docs(v2): remove <a href="https://github.com/next">@next</a> tags needed to install v2 packages (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3781">#3781</a> docs(v2): rename zeit to vercel (<a href="https://github.com/camiluc">@camiluc</a>)</li> </ul> <h4>:house: Internal</h4> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3800">#3800</a> chore(v2): remove useless stylelint-copyright peerDependency (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3786">#3786</a> chore(v2): ensure publishConfig.access presence with tests (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> <h4>Committers: 5</h4> <ul> <li>Alexey Pyltsyn (<a href="https://github.com/lex111">@lex111</a>)</li> <li>Bartosz Kaszubowski (<a href="https://github.com/Simek">@Simek</a>)</li> <li>Maël Nison (<a href="https://github.com/arcanis">@arcanis</a>)</li> <li>Sébastien Lorber (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github.com/camiluc">@camiluc</a></li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/facebook/docusaurus/blob/master/CHANGELOG-2.x.md">@docusaurus/preset-classic's changelog</a>.</em></p> <blockquote> <h2>2.0.0-alpha.69 (2020-11-24)</h2> <h4>:rocket: New Feature</h4> <ul> <li><code>docusaurus-types</code>, <code>docusaurus</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3802">#3802</a> feat(v2): add baseUrlIssueBanner configuration to disable banner (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> </li> </ul> <h4>:bug: Bug Fix</h4> <ul> <li><code>docusaurus-theme-classic</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3807">#3807</a> chore(v2): upgrade Infima to v0.2.0-alpha.18 (<a href="https://github.com/lex111">@lex111</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3806">#3806</a> fix(v2): remove aria-hidden attr from anchor link of heading (<a href="https://github.com/lex111">@lex111</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3784">#3784</a> fix(v2): fix missing logo in dark theme when darkSrc was not set (<a href="https://github.com/Simek">@Simek</a>)</li> </ul> </li> <li><code>docusaurus-theme-search-algolia</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3804">#3804</a> fix(v2): Algolia: allow contextualSearch + facetFilters (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> </li> <li><code>docusaurus</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3796">#3796</a> Adds createRequire for preset resolution (<a href="https://github.com/arcanis">@arcanis</a>)</li> </ul> </li> </ul> <h4>:memo: Documentation</h4> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3803">#3803</a> docs(v2): fix docusaurus init issue when not using <a href="https://github.com/latest">@latest</a> (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3785">#3785</a> docs(v2): recommend Docusaurus 2 usage (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3783">#3783</a> docs(v2): remove <a href="https://github.com/next">@next</a> tags needed to install v2 packages (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3781">#3781</a> docs(v2): rename zeit to vercel (<a href="https://github.com/camiluc">@camiluc</a>)</li> </ul> <h4>:house: Internal</h4> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3800">#3800</a> chore(v2): remove useless stylelint-copyright peerDependency (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3786">#3786</a> chore(v2): ensure publishConfig.access presence with tests (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> <h4>Committers: 5</h4> <ul> <li>Alexey Pyltsyn (<a href="https://github.com/lex111">@lex111</a>)</li> <li>Bartosz Kaszubowski (<a href="https://github.com/Simek">@Simek</a>)</li> <li>Maël Nison (<a href="https://github.com/arcanis">@arcanis</a>)</li> <li>Sébastien Lorber (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github.com/camiluc">@camiluc</a></li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/facebook/docusaurus/commit/4410a9eb01b87c2df664986ba37e748ed34a3c78"><code>4410a9e</code></a> v2.0.0-alpha.69</li> <li><a href="https://github.com/facebook/docusaurus/commit/d1e51f7e3b864a7a25b62c02095159a62c89ac60"><code>d1e51f7</code></a> chore(v2): prepare v2.0.0.alpha-68 release (<a href="https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic/issues/3779">#3779</a>)</li> <li>See full diff in <a href="https://github.com/facebook/docusaurus/commits/v2.0.0-alpha.69/packages/docusaurus-preset-classic">compare view</a></li> </ul> </details> <br />
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.
<details> <summary>Dependabot commands and options</summary> <br />
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot mergewill merge this PR after your CI passes on it@dependabot squash and mergewill squash and merge this PR after your CI passes on it@dependabot cancel mergewill cancel a previously requested merge and block automerging@dependabot reopenwill reopen this PR if it is closed@dependabot closewill close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
pr created time in 10 minutes
pull request commenthome-assistant/companion.home-assistant
Bump @docusaurus/core from 2.0.0-alpha.68 to 2.0.0-alpha.69
Hi @dependabot[bot],
It seems you haven't yet signed a CLA. Please do so here.
Once you do that we will be able to review and accept this pull request.
Thanks!
comment created time in 10 minutes
create barnchhome-assistant/companion.home-assistant
branch : dependabot/npm_and_yarn/master/docusaurus/core-2.0.0-alpha.69
created branch time in 10 minutes
PR opened home-assistant/companion.home-assistant
Bumps @docusaurus/core from 2.0.0-alpha.68 to 2.0.0-alpha.69. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/facebook/docusaurus/releases">@docusaurus/core's releases</a>.</em></p> <blockquote> <h2>2.0.0-alpha.69 (2020-11-24)</h2> <h4>:rocket: New Feature</h4> <ul> <li><code>docusaurus-types</code>, <code>docusaurus</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3802">#3802</a> feat(v2): add baseUrlIssueBanner configuration to disable banner (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> </li> </ul> <h4>:bug: Bug Fix</h4> <ul> <li><code>docusaurus-theme-classic</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3807">#3807</a> chore(v2): upgrade Infima to v0.2.0-alpha.18 (<a href="https://github.com/lex111">@lex111</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3806">#3806</a> fix(v2): remove aria-hidden attr from anchor link of heading (<a href="https://github.com/lex111">@lex111</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3784">#3784</a> fix(v2): fix missing logo in dark theme when darkSrc was not set (<a href="https://github.com/Simek">@Simek</a>)</li> </ul> </li> <li><code>docusaurus-theme-search-algolia</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3804">#3804</a> fix(v2): Algolia: allow contextualSearch + facetFilters (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> </li> <li><code>docusaurus</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3796">#3796</a> Adds createRequire for preset resolution (<a href="https://github.com/arcanis">@arcanis</a>)</li> </ul> </li> </ul> <h4>:memo: Documentation</h4> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3803">#3803</a> docs(v2): fix docusaurus init issue when not using <a href="https://github.com/latest">@latest</a> (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3785">#3785</a> docs(v2): recommend Docusaurus 2 usage (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3783">#3783</a> docs(v2): remove <a href="https://github.com/next">@next</a> tags needed to install v2 packages (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3781">#3781</a> docs(v2): rename zeit to vercel (<a href="https://github.com/camiluc">@camiluc</a>)</li> </ul> <h4>:house: Internal</h4> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3800">#3800</a> chore(v2): remove useless stylelint-copyright peerDependency (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3786">#3786</a> chore(v2): ensure publishConfig.access presence with tests (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> <h4>Committers: 5</h4> <ul> <li>Alexey Pyltsyn (<a href="https://github.com/lex111">@lex111</a>)</li> <li>Bartosz Kaszubowski (<a href="https://github.com/Simek">@Simek</a>)</li> <li>Maël Nison (<a href="https://github.com/arcanis">@arcanis</a>)</li> <li>Sébastien Lorber (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github.com/camiluc">@camiluc</a></li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/facebook/docusaurus/blob/master/CHANGELOG-2.x.md">@docusaurus/core's changelog</a>.</em></p> <blockquote> <h2>2.0.0-alpha.69 (2020-11-24)</h2> <h4>:rocket: New Feature</h4> <ul> <li><code>docusaurus-types</code>, <code>docusaurus</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3802">#3802</a> feat(v2): add baseUrlIssueBanner configuration to disable banner (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> </li> </ul> <h4>:bug: Bug Fix</h4> <ul> <li><code>docusaurus-theme-classic</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3807">#3807</a> chore(v2): upgrade Infima to v0.2.0-alpha.18 (<a href="https://github.com/lex111">@lex111</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3806">#3806</a> fix(v2): remove aria-hidden attr from anchor link of heading (<a href="https://github.com/lex111">@lex111</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3784">#3784</a> fix(v2): fix missing logo in dark theme when darkSrc was not set (<a href="https://github.com/Simek">@Simek</a>)</li> </ul> </li> <li><code>docusaurus-theme-search-algolia</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3804">#3804</a> fix(v2): Algolia: allow contextualSearch + facetFilters (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> </li> <li><code>docusaurus</code> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3796">#3796</a> Adds createRequire for preset resolution (<a href="https://github.com/arcanis">@arcanis</a>)</li> </ul> </li> </ul> <h4>:memo: Documentation</h4> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3803">#3803</a> docs(v2): fix docusaurus init issue when not using <a href="https://github.com/latest">@latest</a> (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3785">#3785</a> docs(v2): recommend Docusaurus 2 usage (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3783">#3783</a> docs(v2): remove <a href="https://github.com/next">@next</a> tags needed to install v2 packages (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3781">#3781</a> docs(v2): rename zeit to vercel (<a href="https://github.com/camiluc">@camiluc</a>)</li> </ul> <h4>:house: Internal</h4> <ul> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3800">#3800</a> chore(v2): remove useless stylelint-copyright peerDependency (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github-redirect.dependabot.com/facebook/docusaurus/pull/3786">#3786</a> chore(v2): ensure publishConfig.access presence with tests (<a href="https://github.com/slorber">@slorber</a>)</li> </ul> <h4>Committers: 5</h4> <ul> <li>Alexey Pyltsyn (<a href="https://github.com/lex111">@lex111</a>)</li> <li>Bartosz Kaszubowski (<a href="https://github.com/Simek">@Simek</a>)</li> <li>Maël Nison (<a href="https://github.com/arcanis">@arcanis</a>)</li> <li>Sébastien Lorber (<a href="https://github.com/slorber">@slorber</a>)</li> <li><a href="https://github.com/camiluc">@camiluc</a></li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/facebook/docusaurus/commit/4410a9eb01b87c2df664986ba37e748ed34a3c78"><code>4410a9e</code></a> v2.0.0-alpha.69</li> <li><a href="https://github.com/facebook/docusaurus/commit/b6cd30335441480300bdba0dc5a99c51e9f5cf31"><code>b6cd303</code></a> fix(v2): add createRequire for preset resolution (<a href="https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus/issues/3796">#3796</a>)</li> <li><a href="https://github.com/facebook/docusaurus/commit/746a19f25befd1034cf0c5b91a1200cac2d555c6"><code>746a19f</code></a> feat(v2): add baseUrlIssueBanner configuration to disable banner (<a href="https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus/issues/3802">#3802</a>)</li> <li><a href="https://github.com/facebook/docusaurus/commit/d79d5ba8988ab8111c22939e6ff3a416cc9bfc38"><code>d79d5ba</code></a> fix(v2): ensure that the react-router and *-dom version matches (<a href="https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus/issues/3794">#3794</a>)</li> <li><a href="https://github.com/facebook/docusaurus/commit/d9ed20ebcfc8f2433e9c60c24a50f525a386ecb6"><code>d9ed20e</code></a> chore: match fs-events version across the workspace (<a href="https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus/issues/3795">#3795</a>)</li> <li><a href="https://github.com/facebook/docusaurus/commit/d1e51f7e3b864a7a25b62c02095159a62c89ac60"><code>d1e51f7</code></a> chore(v2): prepare v2.0.0.alpha-68 release (<a href="https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus/issues/3779">#3779</a>)</li> <li>See full diff in <a href="https://github.com/facebook/docusaurus/commits/v2.0.0-alpha.69/packages/docusaurus">compare view</a></li> </ul> </details> <br />
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.
<details> <summary>Dependabot commands and options</summary> <br />
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot mergewill merge this PR after your CI passes on it@dependabot squash and mergewill squash and merge this PR after your CI passes on it@dependabot cancel mergewill cancel a previously requested merge and block automerging@dependabot reopenwill reopen this PR if it is closed@dependabot closewill close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
pr created time in 10 minutes
issue openedhome-assistant/frontend
Insanely bad performance/memory leak with device / entity drop downs.
<!-- READ THIS FIRST:
- If you need additional help with this template please refer to https://www.home-assistant.io/help/reporting_issues/
- Make sure you are running the latest version of Home Assistant before reporting an issue: https://github.com/home-assistant/home-assistant/releases
- Do not report issues for custom Lovelace cards.
- Provide as many details as possible. Paste logs, configuration samples and code into the backticks. DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed without comment. -->
Checklist
- [x] I have updated to the latest available Home Assistant version.
- [x] I have cleared the cache of my browser.
- [x] I have tried a different browser to see if it is related to my browser.
The problem
All Entity and Device dropdown are insanely slow to load and freeze the browser to the point I can't right click the page or select an entity for a considerable amount of time.

<!-- Describe the issue you are experiencing here to communicate to the maintainers. Tell us about the current behavior. If possible provide a screenshot with a description. -->
Expected behavior
The user interface and drop downs shouldn't freeze. <!-- Describe what you expected to happen or it should look/behave. If possible provide a screenshot with a description. -->
Steps to reproduce
<!-- Provide steps for us, that helps reproducing your issue. For example: 1. Add a climate integration 2. Navigate to Lovelace 3. Click more info of the climate entity 4. Set the HVAC action to heat 5. Set the temperature higher than the current temperature 6. Set the HVAC action to cool -->
- Use the log book (or state viewer, love lace entity picker or automation editor).
- Type to select an entity (notice slight lag).
- Start deleting the entity id with backspace/delete, Mix it up and delete a few characters in from the end and then select all text and delete. I seem to get into this more and more with deletes, and everything becomes slower even selection.
- Start typing a new entity id (it starts getting worse every iteration)
- Keep repeating #2-4.
Environment
I notice this mostly on Safari (14.0.1) but have been able to reproduce on chrome (it takes longer to reproduce and isn't as noticeable). I've seen this both on PC running windows 10 (Ryzen 9 5900x) and Mac OS (2018 6core i7 MBP).
<!-- Provide details about the versions you are using, which helps us reproducing and finding the issue quicker. Version information is found in the Home Assistant frontend: Configuration -> Info.
Browser version and operating system is important! Please try to replicate your issue in a different browser and be sure to include your findings. -->
- Home Assistant release with the issue: All-0.118.3
- Last working Home Assistant release (if known):
- Browser and browser version: Safari - Version 14.0.1 (16610.2.11.51.8)
- Operating system: Mac OS - Big Sur / Windows 10
State of relevant entities
There are way to many entities to list here, but they are in all different kind of states from unavailable (a few). The ones I'm selecting are all available and mostly motion sensors.
Additional information

Here is a view of longest time taken while the cpu is going crazy:


created time in 3 hours
pull request commenthome-assistant/companion.home-assistant
Hi @NickPeoples,
It seems you haven't yet signed a CLA. Please do so here.
Once you do that we will be able to review and accept this pull request.
Thanks!
comment created time in 4 hours
PR opened home-assistant/companion.home-assistant
Fixed typo 'teh' on line 222.
pr created time in 4 hours
PR opened home-assistant/frontend
Co-Authored-By: Neil Kalman [email protected]
<!-- You are amazing! Thanks for contributing to our project! Please, DO NOT DELETE ANY TEXT from this template! (unless instructed). -->
Proposed change
<!-- Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue or discussion in the additional information section. --> This updates the colors to look a bit more modern in graphs. Note to self: Test it, make it themeable.
Type of change
<!-- What type of change does your PR introduce to the Home Assistant frontend? NOTE: Please, check only 1! box! If your PR requires multiple boxes to be checked, you'll most likely need to split it into multiple PRs. This makes things easier and faster to code review. -->
- [ ] Dependency upgrade
- [ ] Bugfix (non-breaking change which fixes an issue)
- [x] New feature (thank you!)
- [ ] Breaking change (fix/feature causing existing functionality to break)
- [ ] Code quality improvements to existing code or addition of tests
Example configuration
<!-- Supplying a configuration snippet, makes it easier for a maintainer to test your PR. -->
Additional information
<!-- Details are important, and help maintainers processing your PR. Please be sure to fill out additional details, if applicable. -->
- This PR fixes or closes issue: fixes #
- This PR is related to issue or discussion: https://github.com/Kibibit/kb-better-graph-colors/issues/2
- Link to documentation pull request:
Checklist
<!--
Put an x in the boxes that apply. You can also fill these out after
creating the PR. If you're unsure about any of them, don't hesitate to ask.
We're here to help! This is simply a reminder of what we are going to look
for before merging your code.
-->
- [ ] The code change is tested and works locally.
- [x] There is no commented out code in this PR.
- [ ] Tests have been added to verify that the new code works.
If user exposed functionality or configuration variables are added/changed:
- [ ] Documentation added/updated for www.home-assistant.io
<!-- Thank you for contributing <3 -->
pr created time in 4 hours
pull request commenthome-assistant/frontend
Ah ok lovelace config:
type: fan
entity: fan.bedroom_fan
name: Name
icon: 'hass:fan'


comment created time in 5 hours
pull request commenthome-assistant/frontend
Can you add an image of the card, an exhaustive YAML config (all of the variables), and a gif of your using it?
Sure, what images do you want? What do you need to see in the gif? Is the YAML config I provided not enough? What else do I need to add to it?
Images of the card. Gif of the card being used. And the YAML for the Card...
The YAML provided is for the configuration of a fan entity not a card
comment created time in 5 hours
Pull request review commenthome-assistant/frontend
+import { mdiDotsVertical } from "@mdi/js";+import "@thomasloven/round-slider";+import {+ css,+ CSSResult,+ customElement,+ html,+ internalProperty,+ LitElement,+ property,+ PropertyValues,+ TemplateResult,+} from "lit-element";+import { classMap } from "lit-html/directives/class-map";+import { styleMap } from "lit-html/directives/style-map";+import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";+import { fireEvent } from "../../../common/dom/fire_event";+import { computeStateDisplay } from "../../../common/entity/compute_state_display";+import { computeStateName } from "../../../common/entity/compute_state_name";+import { stateIcon } from "../../../common/entity/state_icon";+import "../../../components/ha-card";+import "../../../components/ha-icon-button";+import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";+import { ActionHandlerEvent } from "../../../data/lovelace";+import { HomeAssistant, FanEntity } from "../../../types";+import { actionHandler } from "../common/directives/action-handler-directive";+import { findEntities } from "../common/find-entites";+import { handleAction } from "../common/handle-action";+import { hasAction } from "../common/has-action";+import { hasConfigOrEntityChanged } from "../common/has-changed";+import { createEntityNotFoundWarning } from "../components/hui-warning";+import { LovelaceCard, LovelaceCardEditor } from "../types";+import { FanCardConfig } from "./types";++@customElement("hui-fan-card")+export class HuiFanCard extends LitElement implements LovelaceCard {+ public static async getConfigElement(): Promise<LovelaceCardEditor> {+ await import(+ /* webpackChunkName: "hui-fan-card-editor" */ "../editor/config-elements/hui-fan-card-editor"+ );+ return document.createElement("hui-fan-card-editor");+ }++ public static getStubConfig(+ hass: HomeAssistant,+ entities: string[],+ entitiesFallback: string[]+ ): FanCardConfig {+ const includeDomains = ["fan"];+ const maxEntities = 1;+ const foundEntities = findEntities(+ hass,+ maxEntities,+ entities,+ entitiesFallback,+ includeDomains+ );++ return { type: "fan", entity: foundEntities[0] || "" };+ }++ @property({ attribute: false }) public hass?: HomeAssistant;++ @internalProperty() private _config?: FanCardConfig;++ private _speedTimeout?: number;++ public getCardSize(): number {+ return 5;+ }++ public setConfig(config: FanCardConfig): void {+ if (!config.entity || config.entity.split(".")[0] !== "fan") {+ throw new Error("Specify an entity from within the fan domain.");+ }++ this._config = {+ tap_action: { action: "toggle" },+ hold_action: { action: "more-info" },+ ...config,+ };+ }++ protected render(): TemplateResult {+ if (!this.hass || !this._config) {+ return html``;+ }++ const stateObj = this.hass.states[this._config!.entity] as FanEntity;++ if (!stateObj) {+ return html`+ <hui-warning>+ ${createEntityNotFoundWarning(this.hass, this._config.entity)}+ </hui-warning>+ `;+ }++ const max_speed = stateObj.attributes.speed_list.length;++ const speed = stateObj.attributes.speed_list.indexOf(+ stateObj.attributes.speed+ );++ return html`+ <ha-card+ .header=${this._config!.title}+ class=${classMap({ "no-header": !this._config!.title })}+ >+ <mwc-icon-button+ class="more-info"+ label="Open more info"+ @click=${this._handleMoreInfo}+ tabindex="0"+ >+ <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>+ </mwc-icon-button>++ <div class="content">+ <div id="controls">+ <div id="slider">+ <round-slider+ min="0"+ max=${max_speed}+ .value=${speed}+ .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}+ @value-changing=${this._dragEvent}+ @value-changed=${this._setSpeed}+ style=${styleMap({+ visibility: "visible",+ })}
just forgot to remove it, it came from the light card that I used as a reference
comment created time in 5 hours
pull request commenthome-assistant/frontend
Can you add an image of the card, an exhaustive YAML config (all of the variables), and a gif of your using it?
Sure, what images do you want? What do you need to see in the gif? Is the YAML config I provided not enough? What else do I need to add to it?
comment created time in 5 hours
push eventhome-assistant/frontend
commit sha f6cb1ffe2061bf86490e4c2aa3390c6d087d52ed
[ci skip] Translation update
push time in 6 hours
issue commenthome-assistant/frontend
Automation Editor expands !secrets references to actual values
This is still occurring. My alarm code appears in the UI editor rather than the !secret alarm_code value in the yaml file, and saving the UI form replaces the value in the yaml file.
comment created time in 7 hours
pull request commenthome-assistant/frontend
@balloob let's put a pin in this till the new year as I'm sure you are plenty busy getting ready for the conference. We can then get together and brainstorm. 🧠
comment created time in 7 hours
Pull request review commenthome-assistant/frontend
+import { mdiDotsVertical } from "@mdi/js";+import "@thomasloven/round-slider";+import {+ css,+ CSSResult,+ customElement,+ html,+ internalProperty,+ LitElement,+ property,+ PropertyValues,+ TemplateResult,+} from "lit-element";+import { classMap } from "lit-html/directives/class-map";+import { styleMap } from "lit-html/directives/style-map";+import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";+import { fireEvent } from "../../../common/dom/fire_event";+import { computeStateDisplay } from "../../../common/entity/compute_state_display";+import { computeStateName } from "../../../common/entity/compute_state_name";+import { stateIcon } from "../../../common/entity/state_icon";+import "../../../components/ha-card";+import "../../../components/ha-icon-button";+import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";+import { ActionHandlerEvent } from "../../../data/lovelace";+import { HomeAssistant, FanEntity } from "../../../types";+import { actionHandler } from "../common/directives/action-handler-directive";+import { findEntities } from "../common/find-entites";+import { handleAction } from "../common/handle-action";+import { hasAction } from "../common/has-action";+import { hasConfigOrEntityChanged } from "../common/has-changed";+import { createEntityNotFoundWarning } from "../components/hui-warning";+import { LovelaceCard, LovelaceCardEditor } from "../types";+import { FanCardConfig } from "./types";++@customElement("hui-fan-card")+export class HuiFanCard extends LitElement implements LovelaceCard {+ public static async getConfigElement(): Promise<LovelaceCardEditor> {+ await import(+ /* webpackChunkName: "hui-fan-card-editor" */ "../editor/config-elements/hui-fan-card-editor"+ );+ return document.createElement("hui-fan-card-editor");+ }++ public static getStubConfig(+ hass: HomeAssistant,+ entities: string[],+ entitiesFallback: string[]+ ): FanCardConfig {+ const includeDomains = ["fan"];+ const maxEntities = 1;+ const foundEntities = findEntities(+ hass,+ maxEntities,+ entities,+ entitiesFallback,+ includeDomains+ );++ return { type: "fan", entity: foundEntities[0] || "" };+ }++ @property({ attribute: false }) public hass?: HomeAssistant;++ @internalProperty() private _config?: FanCardConfig;++ private _speedTimeout?: number;++ public getCardSize(): number {+ return 5;+ }++ public setConfig(config: FanCardConfig): void {+ if (!config.entity || config.entity.split(".")[0] !== "fan") {+ throw new Error("Specify an entity from within the fan domain.");+ }++ this._config = {+ tap_action: { action: "toggle" },+ hold_action: { action: "more-info" },+ ...config,+ };+ }++ protected render(): TemplateResult {+ if (!this.hass || !this._config) {+ return html``;+ }++ const stateObj = this.hass.states[this._config!.entity] as FanEntity;++ if (!stateObj) {+ return html`+ <hui-warning>+ ${createEntityNotFoundWarning(this.hass, this._config.entity)}+ </hui-warning>+ `;+ }++ const max_speed = stateObj.attributes.speed_list.length;++ const speed = stateObj.attributes.speed_list.indexOf(+ stateObj.attributes.speed+ );++ return html`+ <ha-card+ .header=${this._config!.title}+ class=${classMap({ "no-header": !this._config!.title })}+ >+ <mwc-icon-button+ class="more-info"+ label="Open more info"+ @click=${this._handleMoreInfo}+ tabindex="0"+ >+ <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>+ </mwc-icon-button>++ <div class="content">+ <div id="controls">+ <div id="slider">+ <round-slider+ min="0"+ max=${max_speed}+ .value=${speed}+ .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}+ @value-changing=${this._dragEvent}+ @value-changed=${this._setSpeed}+ style=${styleMap({+ visibility: "visible",+ })}+ ></round-slider>+ <ha-icon-button+ class="fan-button ${classMap({+ "slider-center": true,+ "state-on": stateObj.state === "on",+ "state-unavailable": stateObj.state === UNAVAILABLE,+ })}"+ .icon=${this._config.icon || stateIcon(stateObj)}+ .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}+ style=${styleMap({+ filter: this._computeSpeed(stateObj),+ })}+ @action=${this._handleAction}+ .actionHandler=${actionHandler({+ hasHold: hasAction(this._config!.hold_action),+ hasDoubleClick: hasAction(this._config!.double_tap_action),+ })}+ tabindex="0"+ ></ha-icon-button>+ </div>+ </div>++ <div id="info">+ ${UNAVAILABLE_STATES.includes(stateObj.state)+ ? html`+ <div>+ ${computeStateDisplay(+ this.hass.localize,+ stateObj,+ this.hass.language+ )}+ </div>+ `+ : html` <div class="speed"></div> `}+ ${this._config.name || computeStateName(stateObj)}+ </div>+ </div>+ </ha-card>+ `;+ }++ protected shouldUpdate(changedProps: PropertyValues): boolean {+ return hasConfigOrEntityChanged(this, changedProps);+ }++ protected updated(changedProps: PropertyValues): void {+ super.updated(changedProps);+ if (!this._config || !this.hass) {+ return;+ }++ const stateObj = this.hass!.states[this._config!.entity];++ if (!stateObj) {+ return;+ }++ const oldHass = changedProps.get("hass") as HomeAssistant | undefined;+ const oldConfig = changedProps.get("_config") as FanCardConfig | undefined;++ if (+ !oldHass ||+ !oldConfig ||+ oldHass.themes !== this.hass.themes ||+ oldConfig.theme !== this._config.theme+ ) {+ applyThemesOnElement(this, this.hass.themes, this._config.theme);+ }+ }++ private _dragEvent(e: any): void {+ const stateObj = this.hass!.states[this._config!.entity] as FanEntity;+ if (e.detail.value < 1) {+ this.shadowRoot!.querySelector(".speed")!.innerHTML = `Off`;+ } else {+ this.shadowRoot!.querySelector(".speed")!.innerHTML = `${+ stateObj.attributes.speed_list[e.detail.value - 1]+ }`;+ }+ this._showspeed();+ this._hidespeed();+ }++ private _showspeed(): void {+ clearTimeout(this._speedTimeout);+ this.shadowRoot!.querySelector(".speed")!.classList.add("show_speed");+ }++ private _hidespeed(): void {+ this._speedTimeout = window.setTimeout(() => {+ this.shadowRoot!.querySelector(".speed")!.classList.remove("show_speed");+ }, 500);+ }++ private _setSpeed(e: any): void {+ const stateObj = this.hass!.states[this._config!.entity] as FanEntity;+ if (e.detail.value < 1) {+ this.hass!.callService("fan", "turn_off", {+ entity_id: this._config!.entity,+ });+ } else {+ this.hass!.callService("fan", "turn_on", {+ entity_id: this._config!.entity,+ speed: stateObj.attributes.speed_list[e.detail.value - 1],+ });+ }+ }++ private _computeSpeed(stateObj: FanEntity): string {
This can probably be done inline in the HTML template with a ternary
stateObj.state === "off" || !stateObj.attributes.speed_list ? "off" : `${stateObj.attributes.speed}`
comment created time in 7 hours
Pull request review commenthome-assistant/frontend
+import { mdiDotsVertical } from "@mdi/js";+import "@thomasloven/round-slider";+import {+ css,+ CSSResult,+ customElement,+ html,+ internalProperty,+ LitElement,+ property,+ PropertyValues,+ TemplateResult,+} from "lit-element";+import { classMap } from "lit-html/directives/class-map";+import { styleMap } from "lit-html/directives/style-map";+import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";+import { fireEvent } from "../../../common/dom/fire_event";+import { computeStateDisplay } from "../../../common/entity/compute_state_display";+import { computeStateName } from "../../../common/entity/compute_state_name";+import { stateIcon } from "../../../common/entity/state_icon";+import "../../../components/ha-card";+import "../../../components/ha-icon-button";+import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";+import { ActionHandlerEvent } from "../../../data/lovelace";+import { HomeAssistant, FanEntity } from "../../../types";+import { actionHandler } from "../common/directives/action-handler-directive";+import { findEntities } from "../common/find-entites";+import { handleAction } from "../common/handle-action";+import { hasAction } from "../common/has-action";+import { hasConfigOrEntityChanged } from "../common/has-changed";+import { createEntityNotFoundWarning } from "../components/hui-warning";+import { LovelaceCard, LovelaceCardEditor } from "../types";+import { FanCardConfig } from "./types";++@customElement("hui-fan-card")+export class HuiFanCard extends LitElement implements LovelaceCard {+ public static async getConfigElement(): Promise<LovelaceCardEditor> {+ await import(+ /* webpackChunkName: "hui-fan-card-editor" */ "../editor/config-elements/hui-fan-card-editor"+ );+ return document.createElement("hui-fan-card-editor");+ }++ public static getStubConfig(+ hass: HomeAssistant,+ entities: string[],+ entitiesFallback: string[]+ ): FanCardConfig {+ const includeDomains = ["fan"];+ const maxEntities = 1;+ const foundEntities = findEntities(+ hass,+ maxEntities,+ entities,+ entitiesFallback,+ includeDomains+ );++ return { type: "fan", entity: foundEntities[0] || "" };+ }++ @property({ attribute: false }) public hass?: HomeAssistant;++ @internalProperty() private _config?: FanCardConfig;++ private _speedTimeout?: number;++ public getCardSize(): number {+ return 5;+ }++ public setConfig(config: FanCardConfig): void {+ if (!config.entity || config.entity.split(".")[0] !== "fan") {+ throw new Error("Specify an entity from within the fan domain.");+ }++ this._config = {+ tap_action: { action: "toggle" },+ hold_action: { action: "more-info" },+ ...config,+ };+ }++ protected render(): TemplateResult {+ if (!this.hass || !this._config) {+ return html``;+ }++ const stateObj = this.hass.states[this._config!.entity] as FanEntity;++ if (!stateObj) {+ return html`+ <hui-warning>+ ${createEntityNotFoundWarning(this.hass, this._config.entity)}+ </hui-warning>+ `;+ }++ const max_speed = stateObj.attributes.speed_list.length;++ const speed = stateObj.attributes.speed_list.indexOf(+ stateObj.attributes.speed+ );++ return html`+ <ha-card+ .header=${this._config!.title}+ class=${classMap({ "no-header": !this._config!.title })}+ >+ <mwc-icon-button+ class="more-info"+ label="Open more info"+ @click=${this._handleMoreInfo}+ tabindex="0"+ >+ <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>+ </mwc-icon-button>++ <div class="content">+ <div id="controls">+ <div id="slider">+ <round-slider+ min="0"+ max=${max_speed}+ .value=${speed}+ .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}+ @value-changing=${this._dragEvent}+ @value-changed=${this._setSpeed}+ style=${styleMap({+ visibility: "visible",+ })}+ ></round-slider>+ <ha-icon-button+ class="fan-button ${classMap({+ "slider-center": true,+ "state-on": stateObj.state === "on",+ "state-unavailable": stateObj.state === UNAVAILABLE,+ })}"+ .icon=${this._config.icon || stateIcon(stateObj)}+ .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}+ style=${styleMap({+ filter: this._computeSpeed(stateObj),+ })}+ @action=${this._handleAction}+ .actionHandler=${actionHandler({+ hasHold: hasAction(this._config!.hold_action),+ hasDoubleClick: hasAction(this._config!.double_tap_action),+ })}+ tabindex="0"+ ></ha-icon-button>+ </div>+ </div>++ <div id="info">+ ${UNAVAILABLE_STATES.includes(stateObj.state)+ ? html`+ <div>+ ${computeStateDisplay(+ this.hass.localize,+ stateObj,+ this.hass.language+ )}+ </div>+ `+ : html` <div class="speed"></div> `}+ ${this._config.name || computeStateName(stateObj)}+ </div>+ </div>+ </ha-card>+ `;+ }++ protected shouldUpdate(changedProps: PropertyValues): boolean {+ return hasConfigOrEntityChanged(this, changedProps);+ }++ protected updated(changedProps: PropertyValues): void {+ super.updated(changedProps);+ if (!this._config || !this.hass) {+ return;+ }++ const stateObj = this.hass!.states[this._config!.entity];++ if (!stateObj) {+ return;+ }++ const oldHass = changedProps.get("hass") as HomeAssistant | undefined;+ const oldConfig = changedProps.get("_config") as FanCardConfig | undefined;++ if (+ !oldHass ||+ !oldConfig ||+ oldHass.themes !== this.hass.themes ||+ oldConfig.theme !== this._config.theme+ ) {+ applyThemesOnElement(this, this.hass.themes, this._config.theme);+ }+ }++ private _dragEvent(e: any): void {+ const stateObj = this.hass!.states[this._config!.entity] as FanEntity;+ if (e.detail.value < 1) {+ this.shadowRoot!.querySelector(".speed")!.innerHTML = `Off`;
And then use this everywhere
this._speedElement.innerHTML = `Off`;
comment created time in 7 hours
Pull request review commenthome-assistant/frontend
+import { mdiDotsVertical } from "@mdi/js";+import "@thomasloven/round-slider";+import {+ css,+ CSSResult,+ customElement,+ html,+ internalProperty,+ LitElement,+ property,+ PropertyValues,+ TemplateResult,+} from "lit-element";+import { classMap } from "lit-html/directives/class-map";+import { styleMap } from "lit-html/directives/style-map";+import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";+import { fireEvent } from "../../../common/dom/fire_event";+import { computeStateDisplay } from "../../../common/entity/compute_state_display";+import { computeStateName } from "../../../common/entity/compute_state_name";+import { stateIcon } from "../../../common/entity/state_icon";+import "../../../components/ha-card";+import "../../../components/ha-icon-button";+import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";+import { ActionHandlerEvent } from "../../../data/lovelace";+import { HomeAssistant, FanEntity } from "../../../types";+import { actionHandler } from "../common/directives/action-handler-directive";+import { findEntities } from "../common/find-entites";+import { handleAction } from "../common/handle-action";+import { hasAction } from "../common/has-action";+import { hasConfigOrEntityChanged } from "../common/has-changed";+import { createEntityNotFoundWarning } from "../components/hui-warning";+import { LovelaceCard, LovelaceCardEditor } from "../types";+import { FanCardConfig } from "./types";++@customElement("hui-fan-card")+export class HuiFanCard extends LitElement implements LovelaceCard {+ public static async getConfigElement(): Promise<LovelaceCardEditor> {+ await import(+ /* webpackChunkName: "hui-fan-card-editor" */ "../editor/config-elements/hui-fan-card-editor"+ );+ return document.createElement("hui-fan-card-editor");+ }++ public static getStubConfig(+ hass: HomeAssistant,+ entities: string[],+ entitiesFallback: string[]+ ): FanCardConfig {+ const includeDomains = ["fan"];+ const maxEntities = 1;+ const foundEntities = findEntities(+ hass,+ maxEntities,+ entities,+ entitiesFallback,+ includeDomains+ );++ return { type: "fan", entity: foundEntities[0] || "" };+ }++ @property({ attribute: false }) public hass?: HomeAssistant;++ @internalProperty() private _config?: FanCardConfig;++ private _speedTimeout?: number;++ public getCardSize(): number {+ return 5;+ }++ public setConfig(config: FanCardConfig): void {+ if (!config.entity || config.entity.split(".")[0] !== "fan") {+ throw new Error("Specify an entity from within the fan domain.");+ }++ this._config = {+ tap_action: { action: "toggle" },+ hold_action: { action: "more-info" },+ ...config,+ };+ }++ protected render(): TemplateResult {+ if (!this.hass || !this._config) {+ return html``;+ }++ const stateObj = this.hass.states[this._config!.entity] as FanEntity;++ if (!stateObj) {+ return html`+ <hui-warning>+ ${createEntityNotFoundWarning(this.hass, this._config.entity)}+ </hui-warning>+ `;+ }++ const max_speed = stateObj.attributes.speed_list.length;++ const speed = stateObj.attributes.speed_list.indexOf(+ stateObj.attributes.speed+ );++ return html`+ <ha-card+ .header=${this._config!.title}+ class=${classMap({ "no-header": !this._config!.title })}+ >+ <mwc-icon-button+ class="more-info"+ label="Open more info"+ @click=${this._handleMoreInfo}+ tabindex="0"+ >+ <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>+ </mwc-icon-button>++ <div class="content">+ <div id="controls">+ <div id="slider">+ <round-slider+ min="0"+ max=${max_speed}+ .value=${speed}+ .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}+ @value-changing=${this._dragEvent}+ @value-changed=${this._setSpeed}+ style=${styleMap({+ visibility: "visible",+ })}+ ></round-slider>+ <ha-icon-button+ class="fan-button ${classMap({+ "slider-center": true,+ "state-on": stateObj.state === "on",+ "state-unavailable": stateObj.state === UNAVAILABLE,+ })}"+ .icon=${this._config.icon || stateIcon(stateObj)}+ .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}+ style=${styleMap({+ filter: this._computeSpeed(stateObj),+ })}+ @action=${this._handleAction}+ .actionHandler=${actionHandler({+ hasHold: hasAction(this._config!.hold_action),+ hasDoubleClick: hasAction(this._config!.double_tap_action),+ })}+ tabindex="0"+ ></ha-icon-button>+ </div>+ </div>++ <div id="info">+ ${UNAVAILABLE_STATES.includes(stateObj.state)+ ? html`+ <div>+ ${computeStateDisplay(+ this.hass.localize,+ stateObj,+ this.hass.language+ )}+ </div>+ `+ : html` <div class="speed"></div> `}+ ${this._config.name || computeStateName(stateObj)}+ </div>+ </div>+ </ha-card>+ `;+ }++ protected shouldUpdate(changedProps: PropertyValues): boolean {+ return hasConfigOrEntityChanged(this, changedProps);+ }++ protected updated(changedProps: PropertyValues): void {+ super.updated(changedProps);+ if (!this._config || !this.hass) {+ return;+ }++ const stateObj = this.hass!.states[this._config!.entity];++ if (!stateObj) {+ return;+ }++ const oldHass = changedProps.get("hass") as HomeAssistant | undefined;+ const oldConfig = changedProps.get("_config") as FanCardConfig | undefined;++ if (+ !oldHass ||+ !oldConfig ||+ oldHass.themes !== this.hass.themes ||+ oldConfig.theme !== this._config.theme+ ) {+ applyThemesOnElement(this, this.hass.themes, this._config.theme);+ }+ }++ private _dragEvent(e: any): void {+ const stateObj = this.hass!.states[this._config!.entity] as FanEntity;+ if (e.detail.value < 1) {+ this.shadowRoot!.querySelector(".speed")!.innerHTML = `Off`;
Please use the Query decorator and make this a private variable on the class.
@query(".speed") private _speedElement?: HTMLElement;
comment created time in 7 hours
Pull request review commenthome-assistant/frontend
+import { mdiDotsVertical } from "@mdi/js";+import "@thomasloven/round-slider";+import {+ css,+ CSSResult,+ customElement,+ html,+ internalProperty,+ LitElement,+ property,+ PropertyValues,+ TemplateResult,+} from "lit-element";+import { classMap } from "lit-html/directives/class-map";+import { styleMap } from "lit-html/directives/style-map";+import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";+import { fireEvent } from "../../../common/dom/fire_event";+import { computeStateDisplay } from "../../../common/entity/compute_state_display";+import { computeStateName } from "../../../common/entity/compute_state_name";+import { stateIcon } from "../../../common/entity/state_icon";+import "../../../components/ha-card";+import "../../../components/ha-icon-button";+import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";+import { ActionHandlerEvent } from "../../../data/lovelace";+import { HomeAssistant, FanEntity } from "../../../types";+import { actionHandler } from "../common/directives/action-handler-directive";+import { findEntities } from "../common/find-entites";+import { handleAction } from "../common/handle-action";+import { hasAction } from "../common/has-action";+import { hasConfigOrEntityChanged } from "../common/has-changed";+import { createEntityNotFoundWarning } from "../components/hui-warning";+import { LovelaceCard, LovelaceCardEditor } from "../types";+import { FanCardConfig } from "./types";++@customElement("hui-fan-card")+export class HuiFanCard extends LitElement implements LovelaceCard {+ public static async getConfigElement(): Promise<LovelaceCardEditor> {+ await import(+ /* webpackChunkName: "hui-fan-card-editor" */ "../editor/config-elements/hui-fan-card-editor"+ );+ return document.createElement("hui-fan-card-editor");+ }++ public static getStubConfig(+ hass: HomeAssistant,+ entities: string[],+ entitiesFallback: string[]+ ): FanCardConfig {+ const includeDomains = ["fan"];+ const maxEntities = 1;+ const foundEntities = findEntities(+ hass,+ maxEntities,+ entities,+ entitiesFallback,+ includeDomains+ );++ return { type: "fan", entity: foundEntities[0] || "" };+ }++ @property({ attribute: false }) public hass?: HomeAssistant;++ @internalProperty() private _config?: FanCardConfig;++ private _speedTimeout?: number;++ public getCardSize(): number {+ return 5;+ }++ public setConfig(config: FanCardConfig): void {+ if (!config.entity || config.entity.split(".")[0] !== "fan") {+ throw new Error("Specify an entity from within the fan domain.");+ }++ this._config = {+ tap_action: { action: "toggle" },+ hold_action: { action: "more-info" },+ ...config,+ };+ }++ protected render(): TemplateResult {+ if (!this.hass || !this._config) {+ return html``;+ }++ const stateObj = this.hass.states[this._config!.entity] as FanEntity;++ if (!stateObj) {+ return html`+ <hui-warning>+ ${createEntityNotFoundWarning(this.hass, this._config.entity)}+ </hui-warning>+ `;+ }++ const max_speed = stateObj.attributes.speed_list.length;++ const speed = stateObj.attributes.speed_list.indexOf(+ stateObj.attributes.speed+ );++ return html`+ <ha-card+ .header=${this._config!.title}+ class=${classMap({ "no-header": !this._config!.title })}+ >+ <mwc-icon-button+ class="more-info"+ label="Open more info"+ @click=${this._handleMoreInfo}+ tabindex="0"+ >+ <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>+ </mwc-icon-button>++ <div class="content">+ <div id="controls">+ <div id="slider">+ <round-slider+ min="0"+ max=${max_speed}+ .value=${speed}+ .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}+ @value-changing=${this._dragEvent}+ @value-changed=${this._setSpeed}+ style=${styleMap({+ visibility: "visible",+ })}+ ></round-slider>+ <ha-icon-button+ class="fan-button ${classMap({+ "slider-center": true,+ "state-on": stateObj.state === "on",+ "state-unavailable": stateObj.state === UNAVAILABLE,+ })}"+ .icon=${this._config.icon || stateIcon(stateObj)}+ .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}+ style=${styleMap({+ filter: this._computeSpeed(stateObj),+ })}+ @action=${this._handleAction}+ .actionHandler=${actionHandler({+ hasHold: hasAction(this._config!.hold_action),+ hasDoubleClick: hasAction(this._config!.double_tap_action),+ })}+ tabindex="0"+ ></ha-icon-button>+ </div>+ </div>++ <div id="info">+ ${UNAVAILABLE_STATES.includes(stateObj.state)+ ? html`+ <div>+ ${computeStateDisplay(+ this.hass.localize,+ stateObj,+ this.hass.language+ )}+ </div>+ `+ : html` <div class="speed"></div> `}
: html`<div class="speed"></div>`}
comment created time in 7 hours
Pull request review commenthome-assistant/frontend
+import { mdiDotsVertical } from "@mdi/js";+import "@thomasloven/round-slider";+import {+ css,+ CSSResult,+ customElement,+ html,+ internalProperty,+ LitElement,+ property,+ PropertyValues,+ TemplateResult,+} from "lit-element";+import { classMap } from "lit-html/directives/class-map";+import { styleMap } from "lit-html/directives/style-map";+import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";+import { fireEvent } from "../../../common/dom/fire_event";+import { computeStateDisplay } from "../../../common/entity/compute_state_display";+import { computeStateName } from "../../../common/entity/compute_state_name";+import { stateIcon } from "../../../common/entity/state_icon";+import "../../../components/ha-card";+import "../../../components/ha-icon-button";+import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";+import { ActionHandlerEvent } from "../../../data/lovelace";+import { HomeAssistant, FanEntity } from "../../../types";+import { actionHandler } from "../common/directives/action-handler-directive";+import { findEntities } from "../common/find-entites";+import { handleAction } from "../common/handle-action";+import { hasAction } from "../common/has-action";+import { hasConfigOrEntityChanged } from "../common/has-changed";+import { createEntityNotFoundWarning } from "../components/hui-warning";+import { LovelaceCard, LovelaceCardEditor } from "../types";+import { FanCardConfig } from "./types";++@customElement("hui-fan-card")+export class HuiFanCard extends LitElement implements LovelaceCard {+ public static async getConfigElement(): Promise<LovelaceCardEditor> {+ await import(+ /* webpackChunkName: "hui-fan-card-editor" */ "../editor/config-elements/hui-fan-card-editor"+ );+ return document.createElement("hui-fan-card-editor");+ }++ public static getStubConfig(+ hass: HomeAssistant,+ entities: string[],+ entitiesFallback: string[]+ ): FanCardConfig {+ const includeDomains = ["fan"];+ const maxEntities = 1;+ const foundEntities = findEntities(+ hass,+ maxEntities,+ entities,+ entitiesFallback,+ includeDomains+ );++ return { type: "fan", entity: foundEntities[0] || "" };+ }++ @property({ attribute: false }) public hass?: HomeAssistant;++ @internalProperty() private _config?: FanCardConfig;++ private _speedTimeout?: number;++ public getCardSize(): number {+ return 5;+ }++ public setConfig(config: FanCardConfig): void {+ if (!config.entity || config.entity.split(".")[0] !== "fan") {+ throw new Error("Specify an entity from within the fan domain.");+ }++ this._config = {+ tap_action: { action: "toggle" },+ hold_action: { action: "more-info" },+ ...config,+ };+ }++ protected render(): TemplateResult {+ if (!this.hass || !this._config) {+ return html``;+ }++ const stateObj = this.hass.states[this._config!.entity] as FanEntity;++ if (!stateObj) {+ return html`+ <hui-warning>+ ${createEntityNotFoundWarning(this.hass, this._config.entity)}+ </hui-warning>+ `;+ }++ const max_speed = stateObj.attributes.speed_list.length;++ const speed = stateObj.attributes.speed_list.indexOf(+ stateObj.attributes.speed+ );++ return html`+ <ha-card+ .header=${this._config!.title}+ class=${classMap({ "no-header": !this._config!.title })}+ >+ <mwc-icon-button+ class="more-info"+ label="Open more info"+ @click=${this._handleMoreInfo}+ tabindex="0"+ >+ <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>+ </mwc-icon-button>++ <div class="content">+ <div id="controls">+ <div id="slider">+ <round-slider+ min="0"+ max=${max_speed}+ .value=${speed}+ .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}+ @value-changing=${this._dragEvent}+ @value-changed=${this._setSpeed}+ style=${styleMap({+ visibility: "visible",+ })}+ ></round-slider>+ <ha-icon-button+ class="fan-button ${classMap({+ "slider-center": true,
no need for a classmap for a class that is always true. Just add it like you have fan-button
comment created time in 7 hours
Pull request review commenthome-assistant/frontend
+import { mdiDotsVertical } from "@mdi/js";+import "@thomasloven/round-slider";+import {+ css,+ CSSResult,+ customElement,+ html,+ internalProperty,+ LitElement,+ property,+ PropertyValues,+ TemplateResult,+} from "lit-element";+import { classMap } from "lit-html/directives/class-map";+import { styleMap } from "lit-html/directives/style-map";+import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";+import { fireEvent } from "../../../common/dom/fire_event";+import { computeStateDisplay } from "../../../common/entity/compute_state_display";+import { computeStateName } from "../../../common/entity/compute_state_name";+import { stateIcon } from "../../../common/entity/state_icon";+import "../../../components/ha-card";+import "../../../components/ha-icon-button";+import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";+import { ActionHandlerEvent } from "../../../data/lovelace";+import { HomeAssistant, FanEntity } from "../../../types";+import { actionHandler } from "../common/directives/action-handler-directive";+import { findEntities } from "../common/find-entites";+import { handleAction } from "../common/handle-action";+import { hasAction } from "../common/has-action";+import { hasConfigOrEntityChanged } from "../common/has-changed";+import { createEntityNotFoundWarning } from "../components/hui-warning";+import { LovelaceCard, LovelaceCardEditor } from "../types";+import { FanCardConfig } from "./types";++@customElement("hui-fan-card")+export class HuiFanCard extends LitElement implements LovelaceCard {+ public static async getConfigElement(): Promise<LovelaceCardEditor> {+ await import(+ /* webpackChunkName: "hui-fan-card-editor" */ "../editor/config-elements/hui-fan-card-editor"+ );+ return document.createElement("hui-fan-card-editor");+ }++ public static getStubConfig(+ hass: HomeAssistant,+ entities: string[],+ entitiesFallback: string[]+ ): FanCardConfig {+ const includeDomains = ["fan"];+ const maxEntities = 1;+ const foundEntities = findEntities(+ hass,+ maxEntities,+ entities,+ entitiesFallback,+ includeDomains+ );++ return { type: "fan", entity: foundEntities[0] || "" };+ }++ @property({ attribute: false }) public hass?: HomeAssistant;++ @internalProperty() private _config?: FanCardConfig;++ private _speedTimeout?: number;++ public getCardSize(): number {+ return 5;+ }++ public setConfig(config: FanCardConfig): void {+ if (!config.entity || config.entity.split(".")[0] !== "fan") {+ throw new Error("Specify an entity from within the fan domain.");+ }++ this._config = {+ tap_action: { action: "toggle" },+ hold_action: { action: "more-info" },+ ...config,+ };+ }++ protected render(): TemplateResult {+ if (!this.hass || !this._config) {+ return html``;+ }++ const stateObj = this.hass.states[this._config!.entity] as FanEntity;++ if (!stateObj) {+ return html`+ <hui-warning>+ ${createEntityNotFoundWarning(this.hass, this._config.entity)}+ </hui-warning>+ `;+ }++ const max_speed = stateObj.attributes.speed_list.length;++ const speed = stateObj.attributes.speed_list.indexOf(+ stateObj.attributes.speed+ );++ return html`+ <ha-card+ .header=${this._config!.title}+ class=${classMap({ "no-header": !this._config!.title })}+ >+ <mwc-icon-button+ class="more-info"+ label="Open more info"+ @click=${this._handleMoreInfo}+ tabindex="0"+ >+ <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>+ </mwc-icon-button>++ <div class="content">+ <div id="controls">+ <div id="slider">+ <round-slider+ min="0"+ max=${max_speed}+ .value=${speed}+ .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}+ @value-changing=${this._dragEvent}+ @value-changed=${this._setSpeed}+ style=${styleMap({+ visibility: "visible",+ })}
why are you using a style map for this?
comment created time in 7 hours
Pull request review commenthome-assistant/frontend
+import { mdiDotsVertical } from "@mdi/js";+import "@thomasloven/round-slider";+import {+ css,+ CSSResult,+ customElement,+ html,+ internalProperty,+ LitElement,+ property,+ PropertyValues,+ TemplateResult,+} from "lit-element";+import { classMap } from "lit-html/directives/class-map";+import { styleMap } from "lit-html/directives/style-map";+import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";+import { fireEvent } from "../../../common/dom/fire_event";+import { computeStateDisplay } from "../../../common/entity/compute_state_display";+import { computeStateName } from "../../../common/entity/compute_state_name";+import { stateIcon } from "../../../common/entity/state_icon";+import "../../../components/ha-card";+import "../../../components/ha-icon-button";+import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";+import { ActionHandlerEvent } from "../../../data/lovelace";+import { HomeAssistant, FanEntity } from "../../../types";+import { actionHandler } from "../common/directives/action-handler-directive";+import { findEntities } from "../common/find-entites";+import { handleAction } from "../common/handle-action";+import { hasAction } from "../common/has-action";+import { hasConfigOrEntityChanged } from "../common/has-changed";+import { createEntityNotFoundWarning } from "../components/hui-warning";+import { LovelaceCard, LovelaceCardEditor } from "../types";+import { FanCardConfig } from "./types";++@customElement("hui-fan-card")+export class HuiFanCard extends LitElement implements LovelaceCard {+ public static async getConfigElement(): Promise<LovelaceCardEditor> {+ await import(+ /* webpackChunkName: "hui-fan-card-editor" */ "../editor/config-elements/hui-fan-card-editor"+ );+ return document.createElement("hui-fan-card-editor");+ }++ public static getStubConfig(+ hass: HomeAssistant,+ entities: string[],+ entitiesFallback: string[]+ ): FanCardConfig {+ const includeDomains = ["fan"];+ const maxEntities = 1;+ const foundEntities = findEntities(+ hass,+ maxEntities,+ entities,+ entitiesFallback,+ includeDomains+ );++ return { type: "fan", entity: foundEntities[0] || "" };+ }++ @property({ attribute: false }) public hass?: HomeAssistant;++ @internalProperty() private _config?: FanCardConfig;++ private _speedTimeout?: number;++ public getCardSize(): number {+ return 5;+ }++ public setConfig(config: FanCardConfig): void {+ if (!config.entity || config.entity.split(".")[0] !== "fan") {+ throw new Error("Specify an entity from within the fan domain.");+ }++ this._config = {+ tap_action: { action: "toggle" },+ hold_action: { action: "more-info" },+ ...config,+ };+ }++ protected render(): TemplateResult {+ if (!this.hass || !this._config) {+ return html``;+ }++ const stateObj = this.hass.states[this._config!.entity] as FanEntity;++ if (!stateObj) {+ return html`+ <hui-warning>+ ${createEntityNotFoundWarning(this.hass, this._config.entity)}+ </hui-warning>+ `;+ }++ const max_speed = stateObj.attributes.speed_list.length;++ const speed = stateObj.attributes.speed_list.indexOf(+ stateObj.attributes.speed+ );++ return html`+ <ha-card+ .header=${this._config!.title}+ class=${classMap({ "no-header": !this._config!.title })}+ >+ <mwc-icon-button+ class="more-info"+ label="Open more info"+ @click=${this._handleMoreInfo}+ tabindex="0"+ >+ <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>+ </mwc-icon-button>++ <div class="content">+ <div id="controls">+ <div id="slider">+ <round-slider+ min="0"+ max=${max_speed}
.max=${max_speed}
comment created time in 7 hours
pull request commenthome-assistant/frontend
Can you add an image of the card, an exhaustive YAML config (all of the variables), and a gif of your using it?
comment created time in 7 hours
pull request commenthome-assistant/frontend
Hi @wbagdon,
It seems you haven't yet signed a CLA. Please do so here.
Once you do that we will be able to review and accept this pull request.
Thanks!
comment created time in 8 hours
PR opened home-assistant/frontend
<!-- You are amazing! Thanks for contributing to our project! Please, DO NOT DELETE ANY TEXT from this template! (unless instructed). -->
Proposed change
Add fan card to control fan speed without having to click into more-info
Type of change
- [ ] Dependency upgrade
- [ ] Bugfix (non-breaking change which fixes an issue)
- [X ] New feature (thank you!)
- [ ] Breaking change (fix/feature causing existing functionality to break)
- [ ] Code quality improvements to existing code or addition of tests
Example configuration
<!-- Supplying a configuration snippet, makes it easier for a maintainer to test your PR. -->
fan:
- platform: template
fans:
bedroom_fan:
friendly_name: "Bedroom fan"
value_template: "{{ states('input_boolean.state') }}"
speed_template: "{{ states('input_select.speed') }}"
oscillating_template: "{{ states('input_select.osc') }}"
direction_template: "{{ states('input_select.direction') }}"
turn_on:
service: script.fan_on
turn_off:
service: script.fan_off
set_speed:
service: script.fan_speed
data:
speed: "{{ speed }}"
set_oscillating:
service: script.fan_oscillating
data:
oscillating: "{{ oscillating }}"
set_direction:
service: script.fan_direction
data:
direction: "{{ direction }}"
speeds:
- '1'
- '2'
- '3'
zwave_fan:
friendly_name: "Zwave fan"
value_template: "{{ states('input_boolean.state') }}"
speed_template: "{{ states('input_select.speed') }}"
oscillating_template: "{{ states('input_select.osc') }}"
direction_template: "{{ states('input_select.direction') }}"
turn_on:
service: script.fan_on
turn_off:
service: script.fan_off
set_speed:
service: script.fan_speed
data:
speed: "{{ speed }}"
set_oscillating:
service: script.fan_oscillating
data:
oscillating: "{{ oscillating }}"
set_direction:
service: script.fan_direction
data:
direction: "{{ direction }}"
speeds:
- 'Low'
- 'Med'
- 'High'
zwave_fan2:
friendly_name: "Zwave fan"
value_template: "{{ states('input_boolean.state') }}"
speed_template: "{{ states('input_select.speed') }}"
oscillating_template: "{{ states('input_select.osc') }}"
direction_template: "{{ states('input_select.direction') }}"
turn_on:
service: script.fan_on
turn_off:
service: script.fan_off
set_speed:
service: script.fan_speed
data:
speed: "{{ speed }}"
set_oscillating:
service: script.fan_oscillating
data:
oscillating: "{{ oscillating }}"
set_direction:
service: script.fan_direction
data:
direction: "{{ direction }}"
speeds:
- 'High'
- 'Med'
- 'Low'
Additional information
<!-- Details are important, and help maintainers processing your PR. Please be sure to fill out additional details, if applicable. -->
Checklist
<!--
Put an x in the boxes that apply. You can also fill these out after
creating the PR. If you're unsure about any of them, don't hesitate to ask.
We're here to help! This is simply a reminder of what we are going to look
for before merging your code.
-->
- [X ] The code change is tested and works locally.
- [X ] There is no commented out code in this PR.
- [ ] Tests have been added to verify that the new code works.
If user exposed functionality or configuration variables are added/changed:
- [ ] Documentation added/updated for www.home-assistant.io
<!-- Thank you for contributing <3 -->
pr created time in 8 hours
Pull request review commenthome-assistant/frontend
Add network visualization to the ZHA config panel
export const zhaTabs: PageNavigation[] = [ path: `/config/zha/groups`, iconPath: mdiFolderMultipleOutline, },+ {+ translationKey: "ui.panel.config.zha.visualization.caption",+ path: `/config/zha/visualization`,+ iconPath: mdiFolderMultipleOutline,
done
comment created time in 9 hours
Pull request review commenthome-assistant/frontend
Add network visualization to the ZHA config panel
+import {+ css,+ CSSResult,+ customElement,+ html,+ internalProperty,+ LitElement,+ property,+ PropertyValues,+ query,+} from "lit-element";++import { navigate } from "../../../../../common/navigate";+import { fetchDevices, ZHADevice } from "../../../../../data/zha";+import "../../../../../layouts/hass-error-screen";+import "../../../../../layouts/hass-subpage";+import type { HomeAssistant } from "../../../../../types";+import { Network, Edge, Node, EdgeOptions } from "vis-network";+import "vis-network/styles/vis-network.css";+import "../../../ha-config-section";++@customElement("zha-network-visualization-page")+export class ZHANetworkVisualizationPage extends LitElement {+ @property({ type: Object }) public hass!: HomeAssistant;++ @property({ type: Boolean }) public narrow!: boolean;++ @query("#visualization", true)+ private _visualization!: HTMLElement;++ @internalProperty()+ private _firstUpdatedCalled = false;++ @internalProperty()+ private _devices: ZHADevice[] = [];++ @internalProperty()+ private _network!: Network;++ public connectedCallback(): void {+ super.connectedCallback();+ if (this.hass && this._firstUpdatedCalled) {+ this._fetchData();
removed
comment created time in 9 hours
Pull request review commenthome-assistant/frontend
Add network visualization to the ZHA config panel
+import {+ css,+ CSSResult,+ customElement,+ html,+ internalProperty,+ LitElement,+ property,+ PropertyValues,+ query,+} from "lit-element";++import { navigate } from "../../../../../common/navigate";+import { fetchDevices, ZHADevice } from "../../../../../data/zha";+import "../../../../../layouts/hass-error-screen";+import "../../../../../layouts/hass-subpage";+import type { HomeAssistant } from "../../../../../types";+import { Network, Edge, Node, EdgeOptions } from "vis-network";+import "vis-network/styles/vis-network.css";+import "../../../ha-config-section";++@customElement("zha-network-visualization-page")+export class ZHANetworkVisualizationPage extends LitElement {+ @property({ type: Object }) public hass!: HomeAssistant;++ @property({ type: Boolean }) public narrow!: boolean;++ @query("#visualization", true)+ private _visualization!: HTMLElement;++ @internalProperty()+ private _firstUpdatedCalled = false;++ @internalProperty()+ private _devices: ZHADevice[] = [];++ @internalProperty()+ private _network!: Network;++ public connectedCallback(): void {+ super.connectedCallback();+ if (this.hass && this._firstUpdatedCalled) {+ this._fetchData();+ }+ }++ protected firstUpdated(changedProperties: PropertyValues): void {+ super.firstUpdated(changedProperties);+ if (this.hass) {+ this._fetchData();+ }+ this._network = new Network(+ this._visualization,+ {},+ {+ autoResize: true,+ height: window.innerHeight + "px",+ width: window.innerWidth + "px",+ layout: {+ improvedLayout: true,+ },+ physics: {+ barnesHut: {+ springConstant: 0,+ avoidOverlap: 10,+ damping: 0.09,+ },+ },+ nodes: {+ font: {+ multi: "html",+ },+ },+ edges: {+ smooth: {+ enabled: true,+ type: "continuous",+ forceDirection: "none",+ roundness: 0.6,+ },+ },+ }+ );+ this._network.on("doubleClick", (properties) => {+ const ieee = properties.nodes[0];+ if (ieee) {+ const devices = this._devices.filter((regDev) => {+ return regDev.ieee === ieee;+ });+ if (devices[0]) {+ navigate(+ this,+ "/config/devices/device/" + devices[0].device_reg_id,+ false+ );+ }+ }+ });+ this._firstUpdatedCalled = true;+ }++ protected render() {+ return html`+ <hass-subpage+ .hass=${this.hass}+ .header=${this.hass.localize(+ "ui.panel.config.zha.visualization.header"+ )}+ >+ <ha-config-section .isWide=${!this.narrow}>+ <div id="visualization"></div>+ </ha-config-section>+ </hass-subpage>+ `;+ }++ private async _fetchData() {+ this._devices = await fetchDevices(this.hass!);+ this._updateDevices(this._devices);+ }++ private _updateDevices(devices: ZHADevice[]) {+ const nodes: Node[] = [];+ const edges: Edge[] = [];++ devices.forEach((device) => {+ nodes.push({+ id: device.ieee,+ label: this._buildLabel(device),+ shape: this._getShape(device),+ mass: this._getMass(device),+ });+ if (device.neighbors && device.neighbors.length > 0) {+ device.neighbors.forEach((neighbor) => {+ const idx = edges.findIndex(function (e) {+ return device.ieee === e.to && neighbor.ieee === e.from;+ });+ if (idx === -1) {+ edges.push({+ from: device.ieee,+ to: neighbor.ieee,+ label: neighbor.lqi + "",+ color: this._getLQI(neighbor.lqi),+ });+ } else {+ edges[idx].color = this._getLQI(+ (parseInt(edges[idx].label!) + neighbor.lqi) / 2+ );+ edges[idx].label += "/" + neighbor.lqi;+ }+ });+ }+ });++ this._network.setData({ nodes: nodes, edges: edges });+ }++ private _getLQI(lqi: number): EdgeOptions["color"] {+ if (lqi > 192) {+ return { color: "#17ab00", highlight: "#17ab00" };+ }+ if (lqi > 128) {+ return { color: "#e6b402", highlight: "#e6b402" };+ }+ if (lqi > 80) {+ return { color: "#fc4c4c", highlight: "#fc4c4c" };+ }+ return { color: "#bfbfbf", highlight: "#bfbfbf" };+ }++ private _getMass(device: ZHADevice) {+ if (device.device_type === "Coordinator") {+ return 2;+ }+ if (device.device_type === "Router") {+ return 4;+ }+ return 5;+ }++ private _getShape(device: ZHADevice): string {+ if (device.device_type === "Coordinator") {+ return "box";+ }+ if (device.device_type === "Router") {+ return "ellipse";+ }+ return "circle";+ }++ private _buildLabel(device: ZHADevice): string {+ const regDevices = this._devices.filter((regDev) => {
done
comment created time in 9 hours