npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

home-assistant-query-selector

v4.3.0

Published

Easily query home-assistant DOM elements in an async way

Downloads

317

Readme

home-assistant-query-selector (HAQuerySelector)

Easily query Home Assistant DOM elements in an asynchronous way.

Deployment Status Tests Coverage Status npm version

Home Assistant Beta Nightly Tests

Introduction

When one wants to build a Home Assistant front-end plugin, like many of the ones published in HACS, most of the time first thing to do is dealing with query-selection of DOM elements. This is a tedius task, because Home Assistant elements are custom WebComponents, so when the DOM loads, most of those elements don‘t exist, they will be created in an asynchronous way and not all of them at the same time. On top of that, as they are WebComponents, many of them have their own DOM subtree under a ShadowDOM, something that makes the task of query-selecting elements double tedious.

As I develop and maintain several Home Assistant plugins that change the style of the DOM elements, I find myself repeating the same piece of logic over and over to select the elements, and when the Home Asistant front-end code changes (something that occurs more than I would like), I need to go and fix the same in all of them.

This is from where the idea of home-assistant-query-selector comes from. Imagine instantiating a class, and without querying for any element, through promises, just wait for the main Home Assistant DOM elements to be created and rendered to be able to access them. That sounds great! doesn‘t it?. In that way, the Home Assistant plugins that I maintain could be agnostic to the DOM tree, and if someting changes with a new version and all the plugins break at the same time, the changes to fix them could be done in a single place, to fix the plugins only a simple update of this library is needed once the patch is released.

How to detect if something will break with a new Home Assistant version?

There are exhaustive end-to-end tests in place in this library, when a new version of Home Assistant is released, running the end-to-end tests in this repository will ensure that the library still works with the new version, so no need to manually check this.

End-to-end tests

end-to-end tests

Code coverage

code-coverage

Code example

More details of the API can be consulted in the API section:

import { HAQuerySelector } from 'home-assistant-query-selector';

const instance = new HAQuerySelector();

// This even will be triggered when the listen method is called
// You can also use the enum value HAQuerySelectorEvent.ON_LISTEN
instance.addEventListener('onListen', ({ detail }) => {

    const { HOME_ASSISTANT, HOME_ASSISTANT_MAIN, HA_SIDEBAR } = detail;

    // Querying the ha-sidebar element from the home-assistant element
    HOME_ASSISTANT.selector.$.query('home-assistant-main').$.query('ha-sidebar').element
        .then((sidebar) => {
            // sidebar === ha-sidebar element
        });

    // Deep-querying the sidebar element
    HOME_ASSISTANT.selector.deepQuery('ha-sidebar').element
        .then((sidebar) => {
            // sidebar === ha-sidebar element
        });

    // When the sidebar is available in the DOM
    HA_SIDEBAR.element
        then((sidebar) => {
            // Do whatever we want with the sidebar
        });
});

// This event will be triggered when any panel loads
// You can also use the enum value HAQuerySelectorEvent.ON_PANEL_LOAD
instance.addEventListener('onPanelLoad', ({ detail }) => {

    const { PARTIAL_PANEL_RESOLVER } = detail;

    // When the partial panel resolver is available in the DOM
    PARTIAL_PANEL_RESOLVER.element
        .then((partialPanelResolver) => {
            // Do whatever we want with the partial panel resolver
        });

});

// This event will be triggered every time a lovelace dashboard loads
// You can also use the enum value HAQuerySelectorEvent.ON_LOVELACE_PANEL_LOAD
instance.addEventListener('onLovelacePanelLoad', ({ detail }) => {

    const { HEADER, HA_PANEL_LOVELACE } = detail;

    // When the header is available in the DOM
    HEADER.element
        .then((header) => {
            // Do whatever we want with the header
        });    

    // Querying all the ha-icon-button elements inside the .action-items in the header
    HEADER.selector.query('.action-items ha-icon-button').all
        .then((buttons) => {
            // buttons === Search, Assist, and Open dashboard menu elements (top-right header buttons)
        });

    // Querying the hui-root shadowRoot
    HA_PANEL_LOVELACE.selector.$.query('hui-root').$.element
        .then((shadowRoot) => {
            // shadowRoot === hui-root‘s shadowRoot
        });

});

// This event will be triggered every time a more-info dialog is open
// You can also use the enum value HAQuerySelectorEvent.ON_MORE_INFO_DIALOG_OPEN
instance.addEventListener('onMoreInfoDialogOpen', ({ detail }) => {

    // When the ha-more-info-info element is available in the DOM
    detail.HA_MORE_INFO_DIALOG_INFO.element.then((dialogInfo) => {
        // Do whatever we want with the dialogInfo element
    });

});

// Start to listen
instance.listen();

Installation of the library

npm

npm install home-assistant-query-selector

yarn

yarn add home-assistant-query-selector

PNPM

pnpm add home-assistant-query-selector

API

Class instantiation

The HAQuerySelector class can be instantiated sending an optional parameter, this parameter will decide how much time is required to query for each element before giving up.

new HAQuerySelector([config])

config optional parameter

{
    retries?: number;
    delay?: number;
    eventThreshold?: number;
}

| Parameter | Optional | Description | | -------------- | ------------- | --------------------------------------------------- | | retries | yes | How many retries trying to find an element in the DOM tree before giving up (defaults to 100) | | delay | yes | Delay between each retry trying to find an element in the DOM tree (defaults to 50) | | eventThreshold | yes | Timestamp threshold to fire an event. If two consecutive events of the same type have a timestamp difference lower than this value, the second event will be ignored (defaults to 450) |

Public methods

HAQuerySelector instances count with a public method. When it is called, this method will trigger the onListen and onPanelLoad (and onLovelacePanelLoad if the panel in which it is invoked is a lovelace panel) events inmediatly and start to watch for changes in the DOM to trigger the proper events.

instance.listen();

Events

The HAQuerySelector class extends from EventTarget, so it is possible to add events listeners to it. It will dispatch events that will allow us to access the proper elements in the DOM.

onListen

This event is triggered when the listen method is called. It is useful if you only want to access the main Home Assistant elements only once.

instance.addEventListener('onListen', function({detail}) {
    /* detail:
    {
        HOME_ASSISTANT: {...},
        HOME_ASSISTANT_MAIN: {...},
        HA_DRAWER: {...},
        ...
    }
    */
});

The dispatched event is a CustomEvent and its detail property is an object containing the main Home Assistant DOM elements. All the properties and methods included in each element are Promises, so they are async and will be resolved when the element is ready to work with it.

onListen event elements

This is the list of the elements available inside the detail property of the onListen event:

dom tree

| Detail element | DOM element | | ------------------------ | ------------------------ | | HOME_ASSISTANT | home-assistant | | HOME_ASSISTANT_MAIN | home-assistant-main | | HA_DRAWER | ha-drawer | | HA_SIDEBAR | ha-sidebar | | PARTIAL_PANEL_RESOLVER | partial-panel-resolver |

All the available elements contain an element property and the selector property:

| Property or method | Description | | ------------------------- | --------------------------------------------------------------- | | element | Promise that resolves in the respective DOM element | | selector | Object that allows one to query for elements using dot notation |

onPanelLoad

This event is triggered when the listen method is called or when any panel is loaded.

instance.addEventListener('onPanelLoad', function({detail}) {
    /* detail:
    {
        HOME_ASSISTANT: {...},
        HOME_ASSISTANT_MAIN: {...},
        HA_DRAWER: {...},
        ...
    }
    */
});

The dispatched event is a CustomEvent and its detail property is an object containing the main Home Assistant DOM elements. All the properties and methods included in each element are Promises, so they are async and will be resolved when the element is ready to work with it.

onPanelLoad event elements

This is the list of the elements available inside the detail property of the onPanelLoad event:

dom tree

| Detail element | DOM element | | ------------------------ | ------------------------ | | HOME_ASSISTANT | home-assistant | | HOME_ASSISTANT_MAIN | home-assistant-main | | HA_DRAWER | ha-drawer | | HA_SIDEBAR | ha-sidebar | | PARTIAL_PANEL_RESOLVER | partial-panel-resolver |

All the available elements contain an element property and the selector property:

| Property or method | Description | | ------------------------- | --------------------------------------------------------------- | | element | Promise that resolves in the respective DOM element | | selector | Object that allows one to query for elements using dot notation |

onLovelacePanelLoad

This event is triggered when the listen method is called on a lovelace dashboard or when a lovelace dashboard is loaded.

instance.addEventListener('onLovelacePanelLoad', function({ detail }) {
    /* detail:
    {
        HOME_ASSISTANT: {...},
        HOME_ASSISTANT_MAIN: {...},
        HA_DRAWER: {...},
        ...
    }
    */
});

The dispatched event is a CustomEvent and its detail property is an object containing the main Home Assistant DOM elements. All the properties and methods included in each element are Promises, so they are async and will be resolved when the element is ready to work with it.

onLovelacePanelLoad event elements

This is the list of the elements available inside the detail property of the onLovelacePanelLoad event:

dom tree

| Detail element | DOM element | | ------------------------ | ------------------------ | | HOME_ASSISTANT | home-assistant | | HOME_ASSISTANT_MAIN | home-assistant-main | | HA_DRAWER | ha-drawer | | HA_SIDEBAR | ha-sidebar | | PARTIAL_PANEL_RESOLVER | partial-panel-resolver | | HA_PANEL_LOVELACE | ha-panel-lovelace | | HUI_ROOT | hui-root | | HEADER | .header | | HUI_VIEW | hui-view |

All the available elements contain an element property and the selector property:

| Property or method | Description | | ------------------------- | --------------------------------------------------------------- | | element | Promise that resolves in the respective DOM element | | selector | Object that allows one to query for elements using dot notation |

onMoreInfoDialogOpen

This event is triggered when a more-info dialog is open or when one returns to the main view of the more-info dialog from the History or Settings view inside the dialog.

instance.addEventListener('onMoreInfoDialogOpen', function({detail}) {
    /* detail:
    {
        HA_MORE_INFO_DIALOG: {...},
        HA_DIALOG: {...},
        HA_DIALOG_CONTENT: {...},
        ...
    }
    */
});

The dispatched event is a CustomEvent and its detail property is an object containing the main Home Assistant DOM elements inside a more-info dialog. All the properties and methods included in each element are Promises, so they are async and will be resolved when the element is ready to work with it.

onMoreInfoDialogOpen event elements

This is the list of the elements available inside the detail property of the onMoreInfoDialogOpen event:

more-info dialog dom tree

| Detail element | DOM element | | -------------------------- | ------------------------ | | HA_MORE_INFO_DIALOG | ha-more-info-dialog | | HA_DIALOG | ha-dialog | | HA_DIALOG_CONTENT | .content | | HA_MORE_INFO_DIALOG_INFO | ha-more-info-info |

All the available elements contain an element property and three methods:

| Property or method | Description | | ------------------------- | --------------------------------------------------------------- | | element | Promise that resolves in the respective DOM element | | selector | Object that allows one to query for elements using dot notation |

onHistoryAndLogBookDialogOpen

This event is triggered when the History view is opened from the header actions of a more-info dialog.

instance.addEventListener('onHistoryAndLogBookDialogOpen', function({detail}) {
    /* detail:
    {
        HA_MORE_INFO_DIALOG: {...},
        HA_DIALOG: {...},
        HA_DIALOG_CONTENT: {...},
        ...
    }
    */
});

The dispatched event is a CustomEvent and its detail property is an object containing the main Home Assistant DOM elements inside a more-info dialog History view. All the properties and methods included in each element are Promises, so they are async and will be resolved when the element is ready to work with it.

onHistoryAndLogBookDialogOpen event elements

This is the list of the elements available inside the detail property of the onHistoryAndLogBookDialogOpen event:

more-info dialog dom tree

| Detail element | DOM element | | ----------------------------------------- | ---------------------------------- | | HA_MORE_INFO_DIALOG | ha-more-info-dialog | | HA_DIALOG | ha-dialog | | HA_DIALOG_CONTENT | .content | | HA_DIALOG_MORE_INFO_HISTORY_AND_LOGBOOK | ha-more-info-history-and-logbook |

All the available elements contain an element property and three methods:

| Property or method | Description | | ------------------------- | --------------------------------------------------------------- | | element | Promise that resolves in the respective DOM element | | selector | Object that allows one to query for elements using dot notation |

onSettingsDialogOpen

This event is triggered when the Settings view is opened from the header actions of a more-info dialog.

instance.addEventListener('onSettingsDialogOpen', function({detail}) {
    /* detail:
    {
        HA_MORE_INFO_DIALOG: {...},
        HA_DIALOG: {...},
        HA_DIALOG_CONTENT: {...},
        ...
    }
    */
});

The dispatched event is a CustomEvent and its detail property is an object containing the main Home Assistant DOM elements inside a more-info dialog Settings view. All the properties and methods included in each element are Promises, so they are async and will be resolved when the element is ready to work with it.

onSettingsDialogOpen event elements

This is the list of the elements available inside the detail property of the onSettingsDialogOpen event:

more-info dialog dom tree

| Detail element | DOM element | | ------------------------------ | ----------------------- | | HA_MORE_INFO_DIALOG | ha-more-info-dialog | | HA_DIALOG | ha-dialog | | HA_DIALOG_CONTENT | .content | | HA_DIALOG_MORE_INFO_SETTINGS | ha-more-info-settings |

All the available elements contain an element property and three methods:

| Property or method | Description | | ------------------------- | --------------------------------------------------------------- | | element | Promise that resolves in the respective DOM element | | selector | Object that allows one to query for elements using dot notation |

Note

The selector property used in the library uses behind the secenes the AsyncSelector class from shadow-dom-selector, which is highly inspired in the query philosophy of lovelace-card-mod.