profile
viewpoint
Sean Mooney SeanPM5 USA Home automation enthusiast

home-assistant/companion.home-assistant 10

:book: Home Assistant Companion docs

SeanPM5/alerts.home-assistant.io 0

Home Assistant Alerts

SeanPM5/android 0

:iphone: Home Assistant Companion for Android

SeanPM5/brands 0

🎨 Brands for Home Assistant

SeanPM5/button-card 0

Lovelace button-card for home assistant

SeanPM5/core 0

:house_with_garden: Open source home automation that puts local control and privacy first

SeanPM5/developers.home-assistant 0

Developers website for Home Assistant.

SeanPM5/frontend 0

The frontend of HACS

SeanPM5/hacs 0

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!

dependabot[bot]

comment created time in 10 minutes

PR opened home-assistant/companion.home-assistant

Bump @docusaurus/preset-classic from 2.0.0-alpha.68 to 2.0.0-alpha.69

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 compatibility score

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 rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot ignore this major version will 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 version will 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 dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

</details>

+167 -154

0 comment

2 changed files

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!

dependabot[bot]

comment created time in 10 minutes

PR opened home-assistant/companion.home-assistant

Bump @docusaurus/core from 2.0.0-alpha.68 to 2.0.0-alpha.69

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 compatibility score

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 rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot ignore this major version will 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 version will 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 dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

</details>

+161 -3

0 comment

2 changed files

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.

image

<!-- 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 -->

  1. Use the log book (or state viewer, love lace entity picker or automation editor).
  2. Type to select an entity (notice slight lag).
  3. 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.
  4. Start typing a new entity id (it starts getting worse every iteration)
  5. 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

safari-timeline.zip

image

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

image

image

created time in 3 hours

pull request commenthome-assistant/companion.home-assistant

Update sensors.md

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!

NickPeoples

comment created time in 4 hours

PR opened home-assistant/companion.home-assistant

Update sensors.md

Fixed typo 'teh' on line 222.

+1 -1

0 comment

1 changed file

pr created time in 4 hours

PR opened home-assistant/frontend

Update colors

Co-Authored-By: Neil Kalman Neilkalman@gmail.com

<!-- 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:

<!-- Thank you for contributing <3 -->

+6 -5

0 comment

1 changed file

pr created time in 4 hours

pull request commenthome-assistant/frontend

Add fan card

Ah ok lovelace config:

type: fan
entity: fan.bedroom_fan
name: Name
icon: 'hass:fan'

Screenshot_2020-11-24 Home Assistant

usage

wbagdon

comment created time in 5 hours

pull request commenthome-assistant/frontend

Add fan card

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

wbagdon

comment created time in 5 hours

Pull request review commenthome-assistant/frontend

Add fan card

+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

wbagdon

comment created time in 5 hours

pull request commenthome-assistant/frontend

Add fan card

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?

wbagdon

comment created time in 5 hours

push eventhome-assistant/frontend

HomeAssistant Azure

commit sha f6cb1ffe2061bf86490e4c2aa3390c6d087d52ed

[ci skip] Translation update

view details

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.

alvaromi

comment created time in 7 hours

pull request commenthome-assistant/frontend

add more-info actions

@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. 🧠

iantrich

comment created time in 7 hours

Pull request review commenthome-assistant/frontend

Add fan card

+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}`
wbagdon

comment created time in 7 hours

Pull request review commenthome-assistant/frontend

Add fan card

+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`;
wbagdon

comment created time in 7 hours

Pull request review commenthome-assistant/frontend

Add fan card

+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;
wbagdon

comment created time in 7 hours

Pull request review commenthome-assistant/frontend

Add fan card

+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>`}
wbagdon

comment created time in 7 hours

Pull request review commenthome-assistant/frontend

Add fan card

+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

wbagdon

comment created time in 7 hours

Pull request review commenthome-assistant/frontend

Add fan card

+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?

wbagdon

comment created time in 7 hours

Pull request review commenthome-assistant/frontend

Add fan card

+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}
wbagdon

comment created time in 7 hours

pull request commenthome-assistant/frontend

Add fan card

Can you add an image of the card, an exhaustive YAML config (all of the variables), and a gif of your using it?

wbagdon

comment created time in 7 hours

pull request commenthome-assistant/frontend

Add fan card

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!

wbagdon

comment created time in 8 hours

PR opened home-assistant/frontend

Add fan card

<!-- 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:

<!-- Thank you for contributing <3 -->

+652 -0

0 comment

7 changed files

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

dmulcahey

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

dmulcahey

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

dmulcahey

comment created time in 9 hours

more