Skip to content

Instantly share code, notes, and snippets.

@balloob
Created April 28, 2021 22:22
Show Gist options
  • Save balloob/4a70c83287ddba4e9085cb578ffb161f to your computer and use it in GitHub Desktop.
Save balloob/4a70c83287ddba4e9085cb578ffb161f to your computer and use it in GitHub Desktop.

Revisions

  1. balloob created this gist Apr 28, 2021.
    144 changes: 144 additions & 0 deletions balloob-battery-strategy.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,144 @@
    /*
    Battery strategy that shows your battery entities grouped by area.
    To use:
    - store this file in `<config>/www/balloob-battery-strategy.js`
    - Add lovelace resource: `/local/balloob-battery-strategy.js`, type JavaScript Module
    - Create a new Lovelace dashboard and set as content:
    views:
    - title: Batteries
    strategy:
    type: 'custom:balloob-battery'
    // Optional options. Will be merged into the grid/sensor configs.
    options:
    gridOptions:
    columns: 3
    sensorOptions:
    graph: none
    */

    const stateObj2Card = (stateObj, strategyOptions, groupName) => {
    const card = {
    type: "sensor",
    entity: stateObj.entity_id,
    graph: "line",
    ...strategyOptions.sensorOptions,
    };

    // If we have a group name, remove it from the entity name
    // Example: In group Bedroom, changes Bedroom Lights -> Lights
    if (groupName) {
    const entityName = stateObj.attributes.friendly_name;

    if (entityName && entityName.startsWith(`${groupName} `)) {
    card.name = entityName.substr(groupName.length + 1);
    }
    }

    return card;
    };

    const createGridCard = (title, cards, strategyOptions) => ({
    type: "grid",
    columns: 2,
    title,
    cards,
    ...strategyOptions.gridOptions,
    });

    class BatteryStrategy {
    /*
    info: {
    view: LovelaceViewConfig;
    config: LovelaceConfig;
    hass: HomeAssistant;
    narrow: boolean | undefined;
    }
    */
    static async generateView(info) {
    const strategyOptions = info.view.strategy.options || {};
    const [areas, devices, entities] = await Promise.all([
    info.hass.callWS({ type: "config/area_registry/list" }),
    info.hass.callWS({ type: "config/device_registry/list" }),
    info.hass.callWS({ type: "config/entity_registry/list" }),
    ]);

    const entityLookup = Object.fromEntries(
    entities.map((ent) => [ent.entity_id, ent])
    );
    const deviceLookup = Object.fromEntries(
    devices.map((dev) => [dev.id, dev])
    );

    // Find all battery sensors
    let states = Object.values(info.hass.states).filter(
    (stateObj) =>
    stateObj.entity_id.startsWith("sensor.") &&
    stateObj.attributes.device_class == "battery"
    );

    const gridCards = [];

    for (const area of areas) {
    const processed = new Set();
    const areaEntities = [];

    for (const stateObj of states) {
    const entry = entityLookup[stateObj.entity_id];

    if (!entry) {
    continue;
    }

    if (entry.area_id) {
    if (entry.area_id !== area.area_id) {
    continue;
    }
    } else if (entry.device_id) {
    const device = deviceLookup[entry.device_id];
    if (!device || device.area_id !== area.area_id) {
    continue;
    }
    } else {
    continue;
    }

    areaEntities.push(stateObj);
    processed.add(stateObj);
    }

    if (areaEntities.length > 0) {
    gridCards.push(
    createGridCard(
    area.name,
    areaEntities.map((stateObj) =>
    stateObj2Card(stateObj, strategyOptions, area.name)
    ),
    strategyOptions
    )
    );
    }

    states = states.filter((stateObj) => !processed.has(stateObj));
    if (states.length === 0) break;
    }

    // Entities without state
    if (states.length > 0) {
    gridCards.push(
    createGridCard(
    "No Area",
    states.map((stateObj) => stateObj2Card(stateObj, strategyOptions)),
    strategyOptions
    )
    );
    }

    return {
    cards: gridCards,
    };
    }
    }

    customElements.define("ll-strategy-balloob-battery", BatteryStrategy);