Coder Social home page Coder Social logo

knudsvik / energyscore Goto Github PK

View Code? Open in Web Editor NEW
35.0 4.0 4.0 468 KB

Custom Integration for Home Assistant to score how energy is utilized based on price point

License: MIT License

Python 100.00%
energy hacs home-assistant homeassistant integration

energyscore's Introduction

hacs_badge analytics_badge GitHub Activity codecov_badge License BuyMeCoffee

An EnergyScore integration provides three sensors; EnergyScore, Cost and Potential Savings.

EnergyScore is a metric that scores how well you are utilizing changing energy prices throughout the last 24 hours. The EnergyScore will be 0% if you use all of your energy in the most expensive hour, 100% in the cheapest hour, but most likely somewhere in between depending on how well you are able to match your energy use with cheap prices. This integration will not try to optimize your energy use, but is complementary to those like PowerSaver or PriceAnalyzer.

The cost sensor provides the current day cost while the potential savings sensor compares actual current day cost with what the cost would be if all energy was consumed in the cheapes hour of the day. This is thus the potential savings that can be achieved if energy usage is optimised.

Visualisation alternatives available in the visualisation.md file.

You can set up several EnergyScore integrations,e.g. one on your total energy usage, another for EV charging or maybe one for your boiler or dishwasher. EnergyScore and Potential Savings sensors both have a quality attribute with a score from 0 to 1 depending on the available data. If a sensor has price and energy data for 18 hours of the last 24, the quality will be 0.75. The higher the quality is, the more you can trust the sensors.

Smart Home Junkie has made a nice YouTube video on his channel about this integration. For questions and discussion, please see this thread on the Home Assistant Community Forum.

Get started

Installation

Install EnergyScore via HACS by using the My Button below or alternatively search for it in HACS integrations. Remember to restart Home Assistant afterwards.

Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.

Configuration

EnergyScore sensors can be added directly from the user interface by using the My Button below or alternatively by browsing to your integrations page and adding it manually. It may take up to two hours to get enough data to calculate the EnergyScore.

Open your Home Assistant instance and start setting up a new integration.

Attribute Description
Name Name of the integration instance. This will provide name for the sensors to use in the frontend, but can be changed later. "Boiler" will give sensors: sensor.boiler_energyscore, sensor.boiler_cost and sensor.boiler_potential_savings (which can all be changed later).
Energy entity A total (cumulative) energy entity, e.g. from Tibber or PowerCalc integrations or a state from a device. It can be both an entity that resets at given intervals or one that keeps increasing indefinetely. If several is available, it is recommended to choose one with high update frequency.
Price entity A price entity which provides the current hourly energy price as the state, e.g. from Nordpool or Tibber integrations.

Advanced configuration

Some more options are available for advanced use and can be set up after initial setup by clicking the configure button in the integration.

Attribute Description Default
Energy Treshold Energy less than the treshold (during one hour) will not contribute to the EnergyScore 0
Rolling Hours The period of time an EnergyScore should be scored on 24

YAML Configuration

Alternatively, this integration can be configured and set up manually via YAML instead. To enable the Integration sensor in your installation, add the following to your configuration.yaml file:

sensor:
  - platform: energyscore
    name: Heater
    energy_entity: sensor.heater_energy
    price_entity: sensor.nordpool_electricity_price
    unique_id: 23115006-9C33-4DBD-BF01-498058F61BEC

Configuration variables

Attribute Data type Type Description
name string Required Name of the integration instance. This will provide name for the sensors to use in the frontend, but can be changed later if a unique_id is provided. "Boiler" will give sensors: sensor.boiler_energyscore, sensor.boiler_cost and sensor.boiler_potential_savings.
energy_entity string Required A total (cumulative) energy entity, e.g. from Tibber or PowerCalc integrations or a state from a device. It can be both an entity that resets at given intervals or one that keeps increasing indefinetely. If several is available, it is recommended to choose one with high update frequency.
price_entity string Required TA price entity which provides the current hourly energy price as the state, e.g. from Nordpool or Tibber integrations.
unique_id string Required Unique id to be able to configure the entity in the UI.
energy_treshold float Optional Energy less than the treshold (during one hour) will not contribute to the EnergyScore (default = 0).
rolling_hours int Optional The number of hours the EnergyScore should be calculated from (default=24, min=2, max=168).

Debugging

The integration can be debugged in several ways.

From user interface

Go to your integrations dashboard (My Button below), choose an EnergyScore sensor to be debugged, click the three dots and then Enable debug logging.

Open your Home Assistant instance and show your integrations.

Always-on by use of YAML

The following code in your configuration.yaml will provide continuous information on EnergyScore sensor updates in the Home Assistant log.

logger:
  logs:
    custom_components.energyscore: debug

Service call

You can start debugging with a service call:

service: logger.set_level
data:
  custom_components.energyscore: debug

Contributions are welcome!

If you want to contribute to this please read the Contribution guidelines


energyscore's People

Contributors

kamaradclimber avatar knudsvik avatar misa1515 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

energyscore's Issues

Refactoring dicts

Refactor dicts:

  • Should only need one dict to hold energy and price?
  • Do not really need a dict for energy_total?
  • Prices should be saved to dict even when there is no energy data in dict. Needs to handle uneven lengths of price and energy
  • The dicts could be stored in an attribute, that way it can be restored.

Data from nordpool sensor's attribute?

Could get data from NordPool sensors attribute for the whole day, but does it matter if the integration can remember states through restarts? It could help with being more correct throughout the day since it knows the price for the whole day in advance,.

Integration name without unique_id

Integration name in HA without unique_id is now sensor, should be EnergyScore

Something to do with unique_id it seems, when defining it the it will change to energyscore for the sensors that have a valid input. For those without it still is sensor. Several other components use entry.get(CONF_UNIQUE_ID) instead of just config[CONF_UNIQUE_ID], e.g. modbus, bayesian or generic thermostat.

Minimum time

A dishwasher that runs for 90 minutes cannot score 100% even when run in the chespest hour. Could be 100% if run during the two cheapest hours.?

  • Add in YAML data schema
  • Add functionality
  • Add configFlow
  • Add tests
  • Update read-me for explaining configuration

Name could be optional

Define a sensor name based on the entity_id if the name is not provided. PowerCalc does this.

Time to analyze over

An EV charger should maybe look for best timing over a week, not just the intra-day

  • Add in YAML data schema
  • Add functionality
  • Add configFlow
  • Limit to 2-168 hours? (one hour does not make sense)
  • Test default is 24 hours in yaml without configuring
  • Test functionality for rolling hours of 1, 2, 3, 24 (not needed since default), 72, 168?
  • Test outside limits
  • Update read-me for explaining configuration
  • Update readme with limits

Service to purge data

Create a service to purge attribute data like price and energy, to clean poor data.

Support energy sensors that resets at midnight

Since version 0.4.0 the score adds up constantly. It shows a value higher than 100%. This did not happen before this version.

Screenshot_1

The attributes show this:

state_class: measurement
energy_entity: sensor.zonneplan_p1_electricity_consumption_today
price_entity: sensor.zonneplan_current_electricity_tariff
quality: 1
total_energy:
2022-12-28T12:00:00+0100: 8.65
2022-12-28T13:00:00+0100: 9.1
2022-12-28T14:00:00+0100: 9.68
2022-12-28T15:00:00+0100: 10.2
2022-12-28T16:00:00+0100: 10.81
2022-12-28T17:00:00+0100: 11.52
2022-12-28T18:00:00+0100: 12.34
2022-12-28T19:00:00+0100: 13.08
2022-12-28T20:00:00+0100: 13.68
2022-12-28T21:00:00+0100: 14.12
2022-12-28T22:00:00+0100: 14.54
2022-12-28T23:00:00+0100: 14.95
2022-12-29T00:00:00+0100: 0.38
2022-12-29T01:00:00+0100: 0.8
2022-12-29T02:00:00+0100: 0.96
2022-12-29T03:00:00+0100: 1.63
2022-12-29T04:00:00+0100: 2.03
2022-12-29T05:00:00+0100: 2.45
2022-12-29T06:00:00+0100: 3.04
2022-12-29T07:00:00+0100: 3.47
2022-12-29T08:00:00+0100: 3.89
2022-12-29T09:00:00+0100: 4.54
2022-12-29T10:00:00+0100: 5.02
2022-12-29T11:00:00+0100: 5.34
price:
2022-12-28T12:00:00+0100: 0.21
2022-12-28T13:00:00+0100: 0.19
2022-12-28T14:00:00+0100: 0.16
2022-12-28T15:00:00+0100: 0.19
2022-12-28T16:00:00+0100: 0.2
2022-12-28T17:00:00+0100: 0.21
2022-12-28T18:00:00+0100: 0.21
2022-12-28T19:00:00+0100: 0.18
2022-12-28T20:00:00+0100: 0.18
2022-12-28T21:00:00+0100: 0.15
2022-12-28T22:00:00+0100: 0.12
2022-12-28T23:00:00+0100: 0.1
2022-12-29T00:00:00+0100: 0.08
2022-12-29T01:00:00+0100: 0.08
2022-12-29T02:00:00+0100: 0.07
2022-12-29T03:00:00+0100: 0.07
2022-12-29T04:00:00+0100: 0.07
2022-12-29T05:00:00+0100: 0.08
2022-12-29T06:00:00+0100: 0.08
2022-12-29T07:00:00+0100: 0.08
2022-12-29T08:00:00+0100: 0.12
2022-12-29T09:00:00+0100: 0.18
2022-12-29T10:00:00+0100: 0.2
2022-12-29T11:00:00+0100: 0.19
last_updated: '2022-12-29T11:59:46.345340+01:00'
unit_of_measurement: '%'
icon: mdi:speedometer
friendly_name: Total Energy Score

ConfigFlow

  • Add new sensor from UI
  • UUID generated automatically
  • Remove sensor from UI - info here.

Tests

Config

  • Basic config is working
  • Config with and without unique_id
  • config with something else than entities
  • Config without name
  • energy_entity not being total_increasing
  • #49

Process new data

  • give dummy energy and price data in float and string, assert it returns the correct int
  • give dummy data with different date, assert blank sheet
  • assert max yesterday when date is yesterday
  • if not enough data yet, assert logger debug message
  • assert arrays and normalized arrays
  • assert self score

Restore data

  • Strings can be used to create number values (also in dicts)

Update

  • state is unavailable throws right exception
  • state is unknown throws right exception

Some info on testing here: https://developers.home-assistant.io/docs/development_testing/
Custom component tutorial has some intro on testing.

Sensor name

Sensor name is now name, should be named after yaml config.

Sensor unavailable

Sensor unavailability should be showed with which of them is unavailable. Currently it does not report to the logger which of them.

AttributeError: 'NoneType' object has no attribute 'platform'

Version of the custom_component

0.3.1

Configuration

 - platform: energyscore
    name: House Energy Score
    unique_id: a6e5f61a-0761-478a-8520-87612e0c1ea5
    price_entity: sensor.electricity_cost
    energy_entity: sensor.home_energy_meter_gen5_electric_consumed_kwh

Describe the bug

Sensor initialization is throwing an error at https://github.com/knudsvik/EnergyScore/blob/master/custom_components/energyscore/sensor.py#L114-L116. I assume it happens because the price_entity is not setup yet when the sensor is built.

I've confirmed this hypothesis by simply removing the line (the integration is then working as expected).

I would suggest to delay this piece of code to later (async_added_to_hass or later in process_new_data since there is no way to know the order of entity bootstrap as far as I know).

Debug log

2022-12-22 18:29:13.546 ERROR (MainThread) [homeassistant.components.sensor] Error while setting up energyscore platform for sensor
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 281, in _async_setup_platform
    await asyncio.shield(task)
  File "/config/custom_components/energyscore/sensor.py", line 63, in async_setup_platform
    async_add_entities([EnergyScore(hass, config)], update_before_add=False)
  File "/config/custom_components/energyscore/sensor.py", line 116, in __init__
    if entity_reg.async_get(self._price_entity).platform == "nordpool":
AttributeError: 'NoneType' object has no attribute 'platform'

Algo improvement

When energy usage is 0, the dot-function is reducing the score (if energy same day in not cheapest hour). Usage = 0 should not be included in calc..

Icon

Having a default and optional icon defined in the config. The default is already in place in .const, but has not been implemented.

Data update frequency

Updating should be close to the whole hours, e.g. every minute between 13:58 and 14:02. Alternatively push updates when a new value of energy or price is available, see some info here.

Coverage

Get coverage report and badge

Treshold energy

Optional input: Treshold for energy use, e.g. for a dishwasher that uses a tiny bit of standby power.

  • Add in YAML data schema
  • Add functionality
  • Add configFlow
  • Add tests
  • Update read-me for explaining configuration

Improve config flow handling of unique id

There are two async methods to set and check unique id in a config flow step, but if implemented as-is it will be over written by the class instantiation. See here for docs.

Necessary if an options flow is to be created, e.g. changing the entities (will change the unique ID per now) or future options.

  • Add functionality
  • Add tests to check abort string

MIT license

Is there a need to update the MIT license?

Score is reset every day at midnight

Version of the custom_component

0.3.2

Configuration

 - platform: energyscore
    name: House Energy Score
    unique_id: a6e5f61a-0761-478a-8520-87612e0c1ea5
    price_entity: sensor.electricity_cost
    energy_entity: sensor.home_energy_meter_gen5_electric_consumed_kw

Describe the bug

Score is reset to 100% everyday at midnight.
It means the only moment where score is precise is 1min before midnight.

image

I think the correct behavior would be to have a rolling score on the last 24h.

UoM

Sensor UoM should be %, now it does not have anything.

Test: restoring

Probably have to mock an entity with attributes first to have something to restore.

  • version with dicts and datetimes that has to be processed
  • version with UNKNOWN/UNAVAILABLE state
  • version without mocking something first?

ZeroDivisionError: float division by zero

Version of the custom_component

0.3.1

Configuration

 - platform: energyscore
    name: House Energy Score
    unique_id: a6e5f61a-0761-478a-8520-87612e0c1ea5
    price_entity: sensor.electricity_cost
    energy_entity: sensor.home_energy_meter_gen5_electric_consumed_kw

Describe the bug

Update of the entity fails with:

Traceback (most recent call last):
  File "/config/custom_components/energyscore/sensor.py", line 268, in async_update
    self._state = self.process_new_data()
  File "/config/custom_components/energyscore/sensor.py", line 233, in process_new_data
    _norm_prices = normalise_price(self.attr[PRICES])
  File "/config/custom_components/energyscore/sensor.py", line 73, in normalise_price
    return {
  File "/config/custom_components/energyscore/sensor.py", line 74, in <dictcomp>
    key: (max_value - value) / (max_value - min_value)
ZeroDivisionError: float division by zero

(line numbers are a bit shifted because I added some log lines to understand the issue).

The price dictionary contains 2 values with the same value (thus the division by zero).

It happens on a setup without nordpool sensor: it's a simple price sensor to follow peak/offpeak price.

Part of the day to analyze on

Users may want to only run dishwasher between 07:00 and 22:00. Would make sense to be able to configure this and only normalise price for these hours.

  • Add in YAML data schema
  • Add functionality
  • Add configFlow
  • Add tests
  • Update read-me for explaining configuration

First hour energy

Energy in the first hour of the day does not seem to be recorded in the attributes. Leading to wrong score and wrong quality

Visualisation

Create a custom visualisation, eg. based on apexchart. Could have energyscore, energy, quality, sensitivity. Code and picture to be included in repo.

Example from user in Home Assistant Norge facebook thread:
image

`type: custom:apexcharts-card
apex_config:
legend: false
chart:
height: 240px
show:
loading: false
experimental:
color_threshold: true
header:
show: true
show_states: true
colorize_states: false
chart_type: radialBar
series:

  • entity: sensor.electricity_price_adresse
    name: Timespris, strøm
    max: 1
    show:
    in_chart: true
    header_color_threshold: true
    color_threshold:
    • value: 0
      color: green
    • value: 50
      color: orange
    • value: 70
      color: orangered
    • value: 90
      color: red
  • entity: sensor.ps_cap_hour_estimate
    name: Timesestimat
    show:
    in_chart: true
    header_color_threshold: true
    max: 10
    color_threshold:
    • value: 0
      color: green
    • value: 50
      color: orange
    • value: 75
      color: orangered
    • value: 90
      color: red
  • entity: sensor.total_energy_score
    name: Energyscore
    show:
    in_chart: true
    header_color_threshold: true
    color_threshold:
    • value: 0
      color: red
    • value: 45
      color: orange
    • value: 55
      color: green`

QC input of correct device_class

Check that the input entities are of device_class energy and currency? See https://www.home-assistant.io/integrations/sensor/

UI:

  • Can only choose energy entities that is proper energy entities (total) and of correct type (energy)
  • Can only choose price entities

YAML:

  • Choosing wrong device class raises an a warning or similar in the log and cancels setup
  • Test

** EnergyScore sensors **

  • Cost and potential are MONETARY, see link

Potential savings

Potential savings in local currency if the energy use was moved to the cheapest hour.. or cheapest hours rather.

potential also needs actual price, so maybe that is a third sensor

  • Setup automatically adds new saving sensor from config entry to same device
  • Setup automatically adds new cost sensor from config entry to same device
  • Setup yaml automatically adds new saving sensor
  • Setup yaml automatically adds new cost sensor
  • Calculate cost for current day
  • Calculate potential savings for the current day or the last x hours
  • Saving and cost sensors have quality indicators
  • Restore
  • Readme

Tests:

  • Cost sensor is added from yaml
  • Potential sensor is added from yaml
  • Both sensors are added from config entry
  • All three sensors belong to same device
  • Quality for both new sensors
  • Currency of both new sensors are the same as price sensor, see this link
  • Handle energy with different UoM on price than energy
  • Restoring state of cost
  • Cost cycles resetting at midnight
  • Cost sensor update correctly
  • Cost sensor functions with resetting totals
  • Cost sensor exceptions
  • Sensors restore differently another day or same day
  • Restoring state of potential cost
  • Potential sensor update correctly
  • Potential sensor resetting at midnight
  • Potential sensor functions with resetting totals
  • Potential sensor exceptions
  • Restoring UoM on cost and potential
  • Restoring UoM if changed entity with different uom? (yaml)
  • Cost should not be restored if not for same day

Create log file

As the developer I want users to send me a log file with all relevant entries when they encounter issues.

Better handling of time

Dicts should maybe have clock hours like "17-18" instead of "17". See how nordpool does this with full date time objects. This may need to be broken down into an even smaller item (just replace with datetime). Using datetime objects should also fix elif self.attr[ENERGY_YESTERDAY] is not None: to check if it actually is from yesterday.. no need to use the value if it's a week old. Be aware that the times are in UTC in database, need to verify local.

Sensitivity

The sensitivity for energy-price current day could be an attribute. Needs to look at historic price-variations, either by saving them or looking at the long term statistics of the entity.

sensor updates

Sensor updates from real entities provided in config. See line 126 here for an example.

verify last hour on energy state

Introduced new potential bug with Nordpool: when processing new data, energy last hour is just added from the hour before. How to verify that it is still in sync with the state?

Quality indicator

Attribute that is some kind of a quality indicator:

  • length of dictionary == hours passed today (means that every hour has data. For both price and energy.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.