Skip to content

Commit

Permalink
Merge pull request #35 from igloo32/main
Browse files Browse the repository at this point in the history
Work with v9 multiple "events".
  • Loading branch information
lozzd committed Nov 24, 2023
2 parents 1232e45 + af97173 commit 7bb7699
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 44 deletions.
39 changes: 23 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ The only **required** key is the name of the entity sensor that contains the rat

The easiest way to find that entity name is by opening the Search within Home Assistant: search for `current_rate` -> click the chosen result -> choose the Settings tab -> copy `Entity ID`

(The format is `sensor.octopus_energy_electricity_{{METER_SERIAL_NUMBER}}_{{MPAN_NUMBER}}_current_rate`)
(The format is `event.octopus_energy_electricity_{METER_SERIAL_NUMBER}}_{{MPAN_NUMBER}}_current_day_rates`)

Here's an example yaml configuration:

```
entity: sensor.octopus_energy_electricity_<your_id_here>_current_rate
currentEntity: event.octopus_energy_electricity_<your_id_here>_current_day_rates
pastEntity: event.octopus_energy_electricity_<your_id_here>_previous_day_rates
futureEntity: event.octopus_energy_electricity_<your_id_here>_next_day_rates
type: custom:octopus-energy-rates-card
cols: 2
showday: true
Expand All @@ -53,20 +55,25 @@ showpast: false

Here's a breakdown of all the available configuration items:

| Name | Optional | Default | Description |
|-------------|----------|---------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------|
| entity | N | N/A | Name of the sensor that contains the rates you want to render, generated from the `HomeAssistant-OctopusEnergy` integration |
| cols | Y | 1 | How many columns to break the rates in to, pick the one that fits best with how wide your card is |
| showpast | Y | false | Show the rates that have already happened today. Provides a simpler card when there are two days of dates to show |
| showday | Y | false | Shows the (short) day of the week next to the time for each rate. Helpful if it's not clear which day is which if you have a lot of rates to display |
| title | Y | "Agile Rates" | The title of the card in the dashboard |
| mediumlimit | Y | 20 (pence) | If the price is above `mediumlimit`, the row is marked yellow |
| highlimit | Y | 30 (pence) | If the price is above `highlimit`, the row is marked red. |
| roundUnits | Y | 2 | Controls how many decimal places to round the rates to |
| showunits | Y | N/A | No longer supported. Never worked. Please set a blank string using `unitstr` (see below) |
| unitstr | Y | "p/kWh" | The unit to show after the rate in the table. Set to an empty string for none. |
| exportrates | Y | false | Reverses the colours for use when showing export rates instead of import |
| hour12 | Y | true | Show the times in 12 hour format if `true`, and 24 hour format if `false` |
| Name | Optional | Default | Description |
|---------------|----------|---------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------|
| currentEntity | N | N/A | Name of the sensor that contains the current rates you want to render, generated from the `HomeAssistant-OctopusEnergy` integration |
| pastEntity | Y | N/A | Name of the sensor that contains the past rates you want to render, generated from the `HomeAssistant-OctopusEnergy` integration |
| futureEntity | Y | N/A | Name of the sensor that contains the future rates you want to render, generated from the `HomeAssistant-OctopusEnergy` integration |
| cols | Y | 1 | How many columns to break the rates in to, pick the one that fits best with how wide your card is |
| showpast | Y | false | Show the rates that have already happened today. Provides a simpler card when there are two days of dates to show |
| showday | Y | false | Shows the (short) day of the week next to the time for each rate. Helpful if it's not clear which day is which if you have a lot of rates to display |
| title | Y | "Agile Rates" | The title of the card in the dashboard |
| mediumlimit | Y | 20 (pence) | If the price is above `mediumlimit`, the row is marked yellow |
| highlimit | Y | 30 (pence) | If the price is above `highlimit`, the row is marked red. |
| roundUnits | Y | 2 | Controls how many decimal places to round the rates to |
| showunits | Y | N/A | No longer supported. Never worked. Please set a blank string using `unitstr` (see below) |
| unitstr | Y | "p/kWh" | The unit to show after the rate in the table. Set to an empty string for none. |
| exportrates | Y | false | Reverses the colours for use when showing export rates instead of import |
| hour12 | Y | true | Show the times in 12 hour format if `true`, and 24 hour format if `false` |
| cheapest | Y | false | If true show the cheapest rate in light green / light blue |
| combinerate | Y | false | If true combine rows where the rate is the same price, useful if you have a daily tracker tarrif for instance |
| multiplier | Y | 100 | multiple rate values for pence (100) or pounds (1) |



Expand Down
149 changes: 121 additions & 28 deletions octopus-energy-rates-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ class OctopusEnergyRatesCard extends HTMLElement {
td.time_blue{
border-bottom: 1px solid #391CD9;
}
td.time_cheapest{
border-bottom: 1px solid LightGreen;
}
td.time_cheapestblue{
border-bottom: 1px solid LightBlue;
}
td.rate {
color:white;
text-align:center;
Expand All @@ -75,75 +81,153 @@ class OctopusEnergyRatesCard extends HTMLElement {
border: 2px solid #391CD9;
background-color: #391CD9;
}
td.cheapest {
color: black;
border: 2px solid LightGreen;
background-color: LightGreen;
}
td.cheapestblue {
color: black;
border: 2px solid LightBlue;
background-color: LightBlue;
}
`;
card.appendChild(style);
card.appendChild(this.content);
this.appendChild(card);
}

const colours_import = [ 'green', 'red', 'orange', 'blue' ];
const colours_export = [ 'red', 'green', 'orange' ];
const colours_import = ['green', 'red', 'orange', 'blue', 'cheapest', 'cheapestblue'];
const colours_export = ['red', 'green', 'orange'];

const currentEntityId = config.currentEntity;
const currentstate = hass.states[currentEntityId];
const currentattributes = this.reverseObject(currentstate.attributes);

const futureEntityId = config.futureEntity;
const futurestate = hass.states[futureEntityId];
const futureattributes = this.reverseObject(futurestate.attributes);

const pastEntityId = config.pastEntity;
const paststate = hass.states[pastEntityId];
const pastattributes = this.reverseObject(paststate.attributes);

const entityId = config.entity;
const state = hass.states[entityId];
const attributes = this.reverseObject(state.attributes);
const stateStr = state ? state.state : 'unavailable';
const mediumlimit = config.mediumlimit;
const highlimit = config.highlimit;
const unitstr = config.unitstr;
const roundUnits = config.roundUnits;
const showpast = config.showpast;
const showday = config.showday;
const hour12 = config.hour12;

const cheapest = config.cheapest;
const combinerate = config.combinerate;
const multiplier = config.multiplier
var colours = (config.exportrates ? colours_export : colours_import);

// Grab the rates which are stored as an attribute of the sensor
var rates = attributes.all_rates
var ratesCurrent = currentattributes.rates;
var ratesFuture = futureattributes.rates;
var ratesPast = pastattributes.rates;
// Check to see if the 'rates' attribute exists on the chosen entity. If not, either the wrong entity
// was chosen or there's something wrong with the integration.
// The rates attribute also appears to be missing after a restart for a while - please see:
// https://github.com/BottlecapDave/HomeAssistant-OctopusEnergy/issues/135
if (!rates) {
if (!ratesCurrent) {
throw new Error("There are no rates assigned to that entity! Please check integration or chosen entity");
}

// This is critical to breaking down the columns properly. For now, there's now
// two loops doing the same thing which is not ideal.
// TODO: there should be one clear data process loop and one rendering loop? Or a function?
var rates_list_length = 0;
rates.forEach(function (key) {
const date_milli = Date.parse(key.valid_from);
var cheapest_rate = 5000;
var previous_rate = 0;
var rates_totalnumber = 0;
var rates_currentNumber = 0;
var previous_rates_day = "";
var rates_processingRow = 0;
var combinedRates = [];
var filteredRates = [];

// Combine the data sources
ratesPast.forEach(function (key) {
combinedRates.push(key);
rates_totalnumber++;
});
ratesCurrent.forEach(function (key) {
combinedRates.push(key);
rates_totalnumber++;
});
ratesFuture.forEach(function (key) {
combinedRates.push(key);
rates_totalnumber++;
});

// filter out rates to display
combinedRates.forEach(function (key) {
const date_milli = Date.parse(key.start);
var date = new Date(date_milli);
if(showpast || (date - Date.parse(new Date())>-1800000)) {
rates_list_length++;
const lang = navigator.language || navigator.languages[0];
var current_rates_day = date.toLocaleDateString(lang, { weekday: 'short' });
rates_processingRow++;
var ratesToEvaluate = key.value_inc_vat * multiplier;

if (showpast || (date - Date.parse(new Date()) > -1800000)) {
rates_currentNumber++;

// Find the cheapest rate that hasn't past yet
if ((ratesToEvaluate < cheapest_rate) && (date - Date.parse(new Date()) > -1800000)) cheapest_rate = ratesToEvaluate;

// If we don't want to combine same values rates then just push them to new display array
if (!combinerate) {
filteredRates.push(key);
rates_list_length++;
}

if (combinerate &&
(
(rates_currentNumber == 1)
|| (current_rates_day != previous_rates_day)
|| (previous_rate != ratesToEvaluate)
)
) {
filteredRates.push(key);
rates_list_length++;
}
previous_rate = ratesToEvaluate;
previous_rates_day = current_rates_day;
}
});

const rows_per_col = Math.ceil(rates_list_length / config.cols);

var tables = "";
tables = tables.concat("<td><table class='sub_table'><tbody>");
var table = ""
var x = 1;

rates.forEach(function (key) {
const date_milli = Date.parse(key.valid_from);
filteredRates.forEach(function (key) {
const date_milli = Date.parse(key.start);
var date = new Date(date_milli);
const lang = navigator.language || navigator.languages[0];
var options = {hourCycle: 'h23', hour12: hour12, hour: '2-digit', minute:'2-digit'};
var options = { hourCycle: 'h23', hour12: hour12, hour: '2-digit', minute: '2-digit' };
// The time formatted in the user's Locale
var time_locale = date.toLocaleTimeString(lang, options);
// If the showday config option is set, include the shortened weekday name in the user's Locale
var date_locale = (showday ? date.toLocaleDateString(lang, { weekday: 'short' }) + ' ' : '');

var colour = colours[0];
if(key.value_inc_vat > config.highlimit) colour = colours[1];
else if(key.value_inc_vat > config.mediumlimit) colour = colours[2];
else if(key.value_inc_vat <= 0 ) colour = colours[3];
var valueToDisplay = key.value_inc_vat * multiplier;
if (cheapest && (valueToDisplay == cheapest_rate && cheapest_rate > 0)) colour = colours[4];
else if (cheapest && (valueToDisplay == cheapest_rate && cheapest_rate <= 0)) colour = colours[5];
else if (valueToDisplay > highlimit) colour = colours[1];
else if (valueToDisplay > mediumlimit) colour = colours[2];
else if (valueToDisplay <= 0) colour = colours[3];

if (showpast || (date - Date.parse(new Date()) > -1800000)) {
table = table.concat("<tr class='rate_row'><td class='time time_" + colour + "'>" + date_locale + time_locale +
"</td><td class='rate " + colour + "'>" + valueToDisplay.toFixed(roundUnits) + unitstr + "</td></tr>");

if(showpast || (date - Date.parse(new Date())>-1800000)) {
table = table.concat("<tr class='rate_row'><td class='time time_"+colour+"'>" + date_locale + time_locale +
"</td><td class='rate "+colour+"'>" + key.value_inc_vat.toFixed(roundUnits) + unitstr + "</td></tr>");
if (x % rows_per_col == 0) {
tables = tables.concat(table);
table = "";
Expand All @@ -154,7 +238,6 @@ class OctopusEnergyRatesCard extends HTMLElement {
};
x++;
}

});
tables = tables.concat(table);
tables = tables.concat("</tbody></table></td>");
Expand Down Expand Up @@ -185,11 +268,15 @@ class OctopusEnergyRatesCard extends HTMLElement {
}

setConfig(config) {
if (!config.entity) {
if (!config.currentEntity) {
throw new Error('You need to define an entity');
}

const defaultConfig = {
// Entities to get data from
currentEntity: null,
pastEntity: null,
futureEntity: null,
// Controls how many columns the rates split in to
cols: 1,
// Show rates that already happened in the card
Expand All @@ -213,6 +300,12 @@ class OctopusEnergyRatesCard extends HTMLElement {
unitstr: 'p/kWh',
// Make the colouring happen in reverse, for export rates
exportrates: false,
// Higlight the cheapest rate
cheapest: false,
// Combine equal rates
combinerate: false,
// multiple rate values for pence (100) or pounds (1)
multiplier: 100
};

const cardConfig = {
Expand All @@ -235,8 +328,8 @@ customElements.define('octopus-energy-rates-card', OctopusEnergyRatesCard);
// Configure the preview in the Lovelace card picker
window.customCards = window.customCards || [];
window.customCards.push({
type: 'octopus-energy-rates-card',
name: 'Octopus Energy Rates Card',
preview: false,
description: 'This card displays the energy rates for Octopus Energy',
type: 'octopus-energy-rates-card',
name: 'Octopus Energy Rates Card',
preview: false,
description: 'This card displays the energy rates for Octopus Energy',
});

0 comments on commit 7bb7699

Please sign in to comment.