Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refresh enhancement and history cards. #22

Merged
merged 1 commit into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 45 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,44 @@ A simple power distribution card of an inverter and battery system, for [Home As

<img src="https://raw.githubusercontent.com/DanteWinters/lux-power-distribution-card/main/docs/images/full-card-allocated-power.png" width="450" />

## Installation
# Installation

### HACS
## HACS

There is a PR to add this card to the HACS defaults, but for now this card can be added by adding the URL as a custom repository source in HACS:
```
DanteWinters/lux-power-distribution-card
```

### Manual install
## Manual install

1. Download `lux-power-distribution-card.js` from the [latest release](https://github.com/DanteWinters/lux-power-distribution-card/releases/latest) and copy it into your `config/www` directory.

2. Add the resource reference as described below.

### Add resource reference
## Add resource reference

Visit the Resources page in your Home Assistant install and add `lux-power-distribution-card.js` as a JavaScript Module.
[![Open your Home Assistant instance and show your dashboard resources.](https://my.home-assistant.io/badges/lovelace_resources.svg)](https://my.home-assistant.io/redirect/lovelace_resources/)

## Adding the card to the dashboard
# Adding the card to the dashboard

### Configuration
## Configuration
The following is a list of configs for the card:

**NOTE:** All entities must be added as an entity underneath the config. Refer to the example code below on how this looks.

**Required configurations:**
### Required configurations

| Name | Type | Description |
|---|:---:|---|
| battery_soc | entity | Battery state of charge |
| battery_flow | entity | Power flowing from and to the battery. Negative flow is discharge, and positive flow is charge. |
| home_consumption | entity | Output power of the inverter to your home. |
| grid_flow | entity | Power flowing to and from grid. Negative flow is import from grid, and positive flow is export to grid. |

**Optional configurations:**
### Optional configurations

| Name | Type | Description |
|---|:---:|---|
| battery_voltage | entity | Battery's voltage. |
Expand All @@ -54,14 +56,22 @@ The following is a list of configs for the card:
| grid_voltage | entity | Grid's voltage. |
| energy_allocations | list of entities | This is not a single entity, but a list of entities. Explanation below. |
| update_time | entity | An entity for the last time the values were updated. |
| update_time_timestamp_attribute | boolean | if the update time entity has atimestamp attribute, it can be used to show how long since the last update. |
| update_time_timestamp_attribute | boolean | If the update time entity has atimestamp attribute, it can be used to show how long since the last update. |
| grid_indicator_hue | boolean | If this is set to true and the grid voltage drops to 0, the grid image will become dimmer. (Requires a grid voltage entity.) |
| grid_indicator_dot | boolean | If this is set to true and the grid voltage drops to 0, a red indicator will be added next to the grid voltage text. (Requires a grid voltage entity.)|
| use_lux_status_codes | entity | This is used with the *lux_fail_status_codes* list. If the status code is in the given list, a warning will show on the top right card to indicate something is wrong. |
| lux_fail_status_codes | integer list | List of failure codes that will show a warning at the top right of the card. |
| lux_dongle | string | This is the LuxPower inverter's dongle number. It will later on be used to call the refresh service. (This requires the LuxPowerTek integration that supports this.)
| refresh_button_location | string | the location of the refresh button. see below for more information. **NOTE:** the refresh button will only show if the *lux_dongle* is added. |

# LuxpowerTek integration

The LuxpowerTek integration is hosted in a private repository by [Guy Wells](https://github.com/guybw)

## Configuration

If you have the Luxpower integration, you can use the following code directly (except for the energy_allocations, and change the dongle number):

```yaml
type: custom:lux-power-distribution-card
battery_soc:
Expand Down Expand Up @@ -90,7 +100,8 @@ lux_status_code:
lux_fail_status_codes:
- 64
- 16
lux_dongle: BA00000000
lux_dongle: BA________
refresh_button_location: right
energy_allocations:
entities:
- sensor.power_plug_1
Expand All @@ -99,11 +110,7 @@ energy_allocations:
- sensor.power_plug_4
```

## LuxpowerTek integration

The LuxpowerTek integration is hosted in a private repository by [Guy Wells](https://github.com/guybw)

### Status codes
## Status codes

The status codes are up to the user for what they want to see the warning for.

Expand All @@ -113,26 +120,44 @@ Currently there are 2 options that will be displayed:

At some point in time, the warning message may be updated and be based on the status represented by the code.

### Refresh and the Dongle serial number
## Refresh and the Dongle serial number

This refresh only works for the LuxPowerTek integration referenced above. The service name and function call format are hard-coded.

**NOTE:** *Although the refresh button is displayed, it does not refresh the registers.*
The location of the refresh button can be set with the *refresh_button_location* config value. There are 4 accepted values for this config:

## Energy Allocations Entities
- left (Displayed on the left hand side, below the battery text.)
- right (Displayed on the right hand side, above the grid image.)
- both (Displayed on both sides, as described on the above two points.)
- none (Removes the refresh button.)

# Energy Allocations Entities

The *energy_allocations* entities can be any entity that measures power. It will sum the values together and display on the card. The idea is to use this to track how much of the home's power usage is known.

## Grid indicators
# Grid indicators

Below are 2 pictures of the grid image. The first is the grid in a normal state, and the second is the grid image with both indicators active.

| Normal Grid | No Grid Input |
|---|---|
| <img src="https://raw.githubusercontent.com/DanteWinters/lux-power-distribution-card/main/docs/images/grid-normal.png" /> | <img src="https://raw.githubusercontent.com/DanteWinters/lux-power-distribution-card/main/docs/images/grid-no-ac.png" /> |

## Gallery
# Gallery

| The card with only required entities | The card with all required and optional entities | The card using all the LuxPower integration options and entities |
|---|---|---|
| <img src="https://raw.githubusercontent.com/DanteWinters/lux-power-distribution-card/main/docs/images/base-card.png" /> | <img src="https://raw.githubusercontent.com/DanteWinters/lux-power-distribution-card/main/docs/images/base-card-with-extras.png" /> | <img src="https://raw.githubusercontent.com/DanteWinters/lux-power-distribution-card/main/docs/images/full-card.png" /> |

# Interactive Card

The four entity images on the card can be clicked to display the history of the connected entity.

- Solar Image: Solar power entity's history.
- Battery Image: Battery SOC entity's history.
- Home Image: Home consumption entity's history.
- Grid Image: Grid flow entity's history.

# Developer's note

Although the card is functional and even has a few nice features, the development of it was done with a lot of inexperience. From my side, I do not have JavaScript or HTML experience other than this card. For this reason, there may be many ways I implemented things that aren't optimal or safe. If you are knowledgeable in and willing to look through the code, and advice and help will be much appreciated.
70 changes: 60 additions & 10 deletions lux-power-distribution-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class LuxPowerDistributionCard extends HTMLElement {

if (!this.card) {
this.createCard();
this.bindRefresh(this.card, this._hass, this.config);
this.bindHistoryGraph(this.card, this._hass, this.config);
}

this.updateCard();
Expand Down Expand Up @@ -37,6 +39,9 @@ class LuxPowerDistributionCard extends HTMLElement {
if (!this.config.use_lux_status_codes) {
this.config.use_lux_status_codes = false;
}
if (!this.config.refresh_button_location) {
this.config.refresh_button_location = "none";
}
}

createCard() {
Expand Down Expand Up @@ -532,6 +537,14 @@ class LuxPowerDistributionCard extends HTMLElement {

generateSolarCells() {
var cells = ``;
var refresh_button = ``;
if (this.config.lux_dongle && ["right", "both"].includes(String(this.config.refresh_button_location))) {
refresh_button = `
<button id="refresh-button-right" class="icon-button">
<ha-icon icon="mdi:cloud-refresh"></ha-icon>
</button>
`;
}
if (this.config.pv_power && this.config.pv_power.entity) {
// Row 0
cells += `<div class="cell"></div>`;
Expand All @@ -546,33 +559,33 @@ class LuxPowerDistributionCard extends HTMLElement {
cells += `<div id="solar-arrows" class="cell arrow-cell"></div>`; // Solar arrows
cells += `<div class="cell"></div>`;
cells += `<div class="cell"></div>`;
cells += `<div class="cell"></div>`;
cells += `<div class="cell">${refresh_button}</div>`;
} else {
// Row 1
cells += `<div id="battery-charge-info" class="cell text-cell-left"></div>`; // Battery charge/discharge info
cells += `<div class="cell"></div>`;
cells += `<div class="cell"></div>`;
cells += `<div class="cell"></div>`;
cells += `<div class="cell"></div>`;
cells += `<div class="cell"></div>`;
cells += `<div class="cell">${refresh_button}</div>`;
}
return cells;
}

generateHomeCells() {
var cells = ``;
var refresh_button = ``;
if (this.config.lux_dongle) {
if (this.config.lux_dongle && ["left", "both"].includes(String(this.config.refresh_button_location))) {
refresh_button = `
<button id="serviceButton" class="icon-button" @click="${this.callRefreshService}">
<button id="refresh-button-left" class="icon-button">
<ha-icon icon="mdi:cloud-refresh"></ha-icon>
</button>
`;
}

if (this.config.energy_allocations && this.config.energy_allocations.entities) {
// Power Allocations
cells += `<div id="refresh-button-cell" class="cell">${refresh_button}</div>`;
cells += `<div class="cell">${refresh_button}</div>`;
cells += `<div id="home-info" class="cell text-cell-right"></div>`; // Home info
cells += `<div id="home-image" class="cell image-cell"><img src="${this.getBase64Data("home-normal")}"></div>`; // Home image
cells += `<div id="power-allocation-arrows" class="cell arrow-cell"></div>`; // Power allocation arrows
Expand All @@ -581,7 +594,7 @@ class LuxPowerDistributionCard extends HTMLElement {
)}"></div>`; // Power allocation image
cells += `<div id="power-allocation-info" class="cell text-cell-left"></div>`; // Power allocation info
} else {
cells += `<div id="refresh-button-cell" class="cell">${refresh_button}</div>`;
cells += `<div class="cell">${refresh_button}</div>`;
cells += `<div id="home-info" class="cell text-cell-right"></div>`; // Home info
cells += `<div id="home-image" class="cell image-cell"><img src="${this.getBase64Data("home-normal")}"></div>`; // Home image
cells += `<div class="cell"></div>`;
Expand Down Expand Up @@ -751,10 +764,47 @@ class LuxPowerDistributionCard extends HTMLElement {
}
}

callRefreshService() {
if (this.config.lux_dongle) {
var lux_dongle = this.config.lux_dongle;
this._hass.callService("luxpower", "luxpower.luxpower_refresh_registers", { dongle: lux_dongle });
bindRefresh(card, hass, config) {
let refresh_button_left = card.querySelector("#refresh-button-left");
if (refresh_button_left) {
refresh_button_left.addEventListener("click", function (source) {
hass.callService("luxpower", "luxpower_refresh_registers", { dongle: config.lux_dongle });
});
}
let refresh_button_right = card.querySelector("#refresh-button-right");
if (refresh_button_right) {
refresh_button_right.addEventListener("click", function (source) {
hass.callService("luxpower", "luxpower_refresh_registers", { dongle: config.lux_dongle });
});
}
}

bindHistoryGraph(card, hass, config) {
const history_map = {
"#solar-image": "pv_power",
"#battery-image": "battery_soc",
"#grid-image": "grid_flow",
"#home-image": "home_consumption",
};

for (const [key, value] of Object.entries(history_map)) {
if (history_map.hasOwnProperty(key)) {
let button_element = card.querySelector(key);
if (button_element) {
button_element.addEventListener("click", function (source) {
const event = new Event("hass-more-info", {
bubbles: true,
cancelable: false,
composed: true,
});
event.detail = {
entityId: config[value].entity,
};
card.dispatchEvent(event);
return event;
});
}
}
}
}
}
Expand Down