T3m3z / spotprices2ha

Simple copy-paste approach to fetch data from api.spot-hinta.fi (see https://spot-hinta.fi) to Home Assistant. Includes simple sensors and UI elements to ease automation work.
MIT License
111 stars 14 forks source link

Average price Selected period #16

Open apexperi opened 1 year ago

apexperi commented 1 year ago

Hi,

Would it be possible to implement, in addition to the already existing "Average price" functionality, an "Average price Selected period" calculation, where you could define the starting time and the ending time or the length of the period yourself.

It would make it much easier to play with solar electricity and water heater controls, when you could compare the next day's average selling price (from the solar production period) with the current day's night electricity price and make decisions based on that.

jumakki commented 1 year ago

I have extended functionality for my similar needs by using automations and helpers in Home Assistant. I have a water heater that heats during night but because of "reasons" I can't control when night time electricity "pulse" from electricity company comes and so when is the earliest the heater can heat. I calculate cheapest continuous hours for heat start time based on number of hours to heat from helper input number input_number.water_heater_hours and a first_hour variable which depends on week day. Night time electricity ends at 7:00, so I have start of last_hour set to 6.

Automation sets helper input datetime input_datetime.water_heater_start_time and also calculated average price to helper input number input_number.water_heater_average_price.

In your case first_hour could be set based on some helper input_number instead of based on weekday. For example

first_hour: |-
    {{ states("input_number.water_heater_first_hour") | int }}

Similar for last_hour.

Here is my automation.

alias: Water heater start time setter
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.shf_electricity_price
    for:
      hours: 0
      minutes: 1
      seconds: 0
  - platform: state
    entity_id:
      - input_number.water_heater_hours
condition:
  - condition: not
    conditions:
      - condition: state
        state: unknown
        entity_id: input_datetime.shf_cheapest_period_start
action:
  - variables:
      first_hour: |-
        {%- set firstHour = 24 -%}
        {%- if now().weekday() in (0,3) -%}
          {%- set firstHour = 22 -%}
        {%- elif now().weekday() in (1,5,6) -%}
          {%- set firstHour = 23 -%}
        {%- endif -%}
        {{ firstHour}}
      last_hour: 6
      start_index: >-
        {% set hours = states("input_number.water_heater_hours") | int %} 
        {% set output = namespace(value=[])%}
        {% set data = state_attr("sensor.shf_electricity_price_now","all_prices")[first_hour:24+last_hour+1] %} 

        {% for inval in data[:min(-hours+1, -1)] %}
          {% set temp = namespace(value=[]) %}
          {% set j = loop.index -1 %}
          {% for i in range(hours) %}
            {% set temp.value = temp.value + [data[j+i]] %}
          {% endfor %}
          {% set output.value = output.value + [temp.value | average] %}
        {% endfor %}

        {{ output.value.index(output.value|min) + first_hour}}
      average_price: >-
        {% set output = namespace(value=[])%} {% set hours = states("input_number.water_heater_hours") | int %}          
        {% set data = state_attr("sensor.shf_electricity_price_now","all_prices")[start_index:start_index + hours + 1] %}
        {% for i in range(hours) %}
          {% set output.value = output.value + [data[i]] %}
        {% endfor %}
        {{ output.value | average }}
  - service: input_datetime.set_datetime
    data:
      datetime: >-
        {%- set data2 = state_attr("sensor.shf_electricity_price_now", "data")[first_hour:24+last_hour+1] -%}
        {%- set time = data2[start_index - first_hour]["DateTime"] -%} 
        {{ time[:19] | replace("T", " ") }}
    target:
      entity_id: input_datetime.water_heater_start_time
  - service: input_number.set_value
    data:
      value: "{{ average_price }}"
    target:
      entity_id: input_number.water_heater_average_price
mode: single

Edit 2023-05-29: Changed trigger from input_datetime.shf_cheapest_period_start to sensor.shf_electricity_price (with a delay) as original was buggy

T3m3z commented 1 year ago

Hello and thank you for this enhancement proposal!

I have rewritten a lot of the code and the new version is in "beta testing phase". I will merge it to master branch if I don't hear any problems with it. You can find it here: https://github.com/T3m3z/spotprices2ha/tree/v0.2.0-draft

With this new version you should be able to create a new Template sensor which could use a code like below to get average price between some specific times. Attribute "TotalPrice" contains also fixed fees (if given by the user) such as transmission fees and extra fees collected by the electricity seller. So depending on the use case using "PriceWithTax" or "PriceNoTax" might be better (raw stock price of the electricity without any extra fees). You could use triggered Template sensors to make this calculation happen at certain time (if necessary).

Please note that running the code below at 17:00:01 would result the calculation starting from the next available hour which is 18 (and end would be +12h).

{% set start = now() %}
{% set end = start + timedelta(hours=12) %}

{{ state_attr('sensor.shf_data', 'data') |
selectattr("Timestamp", "ge", start | as_timestamp) |
selectattr("Timestamp", "lt", end | as_timestamp) | map(attribute="TotalPrice") |
average }}

For debugging purposes you can use "Developer Tools -> Template" and possibly the code below which returns the data as a list (to debug start and end times maybe?):

{% set start = now() %}
{% set end = start + timedelta(hours=12) %}

{{ state_attr('sensor.shf_data', 'data') |
selectattr("Timestamp", "ge", start | as_timestamp) |
selectattr("Timestamp", "lt", end | as_timestamp) | list }}

If this solution is ok for you, then please consider closing this issue. I'm not sure whether it makes sense to add this to the actual code base. Maybe I could add a folder called "Examples" where this code snippets could be added.

whcrg commented 10 months ago

Other feature request, rank of next hour (and if possible rank for n+1hours from now) would be handy (or can this be had already somehow?).

Use case: I for example have dish washer that washes bit over 1h, and does not take any major power during first 10minutes. It would thus be handy to trigger script on next hour rank being 1 and wait 50min before starting...

T3m3z commented 10 months ago

This Template code should give you Rank of the next hour: {{ (state_attr("sensor.shf_data", "data") | selectattr("Timestamp", "eq", now().replace(minute=0, second=0, microsecond=0)| as_timestamp + 3600) | first)["Rank"]}}

Just multiple 3600 with some integer to get Ranks of n hours from now. Just note that you cannot always expect to have Ranks more than 9 hours from now as there is not enough data when new prices haven't yet arrived.

whcrg commented 7 months ago

Kinda on topic, kinda not... sorry for abusing this ticket

Trying to get month average so far calculated. With this:

  - platform: average
    name: Electricity current month average price
    start: "{{ now().replace(day=1).replace(hour=0).replace(minute=0).replace(second=0) }}"
    end: "{{ now().replace(hour=0).replace(minute=0).replace(second=0)+timedelta(days=2) }}"
    entities:
      - sensor.shf_electricity_price_now

(https://github.com/Limych/ha-average)

based on this https://community.home-assistant.io/t/monthly-average-to-date-how/463887/4

In am close but as this accesses history of sensor.shf_electricity_price_now it does not see the rest of today (and tomorrow when available) despite those are included in the times... Should somehow incorporate the data available in sensor.shf_data like couple of messages higher T3m3z used to calculate average for future time interval...

Ideas, tips? I am sure I am not the only one who would like to show month average price (for kulutusvaikutus "fixed price" use on graph.