├── Stromligning.jinja ├── README.md └── calculator.yaml /Stromligning.jinja: -------------------------------------------------------------------------------- 1 | {# Configuration #} 2 | 3 | {# Name of Energi Price Sensor #} 4 | {% set sensor_name = "sensor.stromligning_current_price_vat" %} 5 | {% set sensor_name_tomorrow = "binary_sensor.stromligning_tomorrow_available_vat" %} 6 | {# Consumption in kW #} 7 | {% set consumption = 1500 / 1000 %} 8 | 9 | {# Duration of charging, washing, etc. #} 10 | {% set duration = timedelta(hours = 2, minutes = 30) %} 11 | 12 | {# First possible time to start. Default now() #} 13 | {% set start_time = now() %} 14 | 15 | {# Deadline for when the operation should be finished (For example departure time). 16 | Default tomorrow at 23:59:59 if tomorrows prices are available, otherwise today at 23:59:59 #} 17 | {% set end_time = today_at("00:00") + timedelta(days=2) %} 18 | 19 | {# Macros #} 20 | {%- macro date_to_index(date) -%}{{(date.hour + (date.date() - now().date()).days * 24)}}{%- endmacro -%}{# as datetime? #} 21 | {%- macro index_to_date(index) -%}{{today_at("00:00") + timedelta(hours = index)}}{%- endmacro -%}{# as int? #} 22 | 23 | {# All available prices from price sensor #} 24 | {% set prices_raw = state_attr(sensor_name, "prices") + state_attr(sensor_name_tomorrow, "prices") | selectattr('value', '!=', none) | list %} 25 | 26 | {# End time can not be later than the available price data #} 27 | {% set end_time = [end_time, (prices_raw | last).start + timedelta(hours=1)] | min %} 28 | 29 | {% set start_hour = date_to_index(start_time) | int %} 30 | {% set end_hour = 2 + date_to_index(end_time - duration) | int %} 31 | 32 | {# All prices within timerange #} 33 | {% set prices = prices_raw[start_hour:end_hour] %} 34 | 35 | 36 | {# namespace to hold values #} 37 | {% set ns = namespace(start_times = []) %} 38 | {% for price in prices %} 39 | {% set item = namespace(start = price.start, end = price.start + duration) %} 40 | {% if loop.first %} 41 | {% set item.start = start_time %} 42 | {% set item.end = start_time + duration %} 43 | {% elif loop.last %} 44 | {% set item.start = end_time - duration %} 45 | {% set item.end = end_time %} 46 | {% endif %} 47 | {% set item.value = 0%} 48 | {% for index in range(date_to_index(item.start) | int, date_to_index(item.end) | int + 1) %} 49 | {% set hour_duration = ([item.end, index_to_date(index + 1) | as_datetime] | min - [item.start, index_to_date(index) | as_datetime] | max).total_seconds() / 60 / 60 %} 50 | {% if hour_duration > 0 %} 51 | {% set item.value = item.value + (prices_raw[index].price * hour_duration) %} 52 | {% endif %} 53 | {% endfor %} 54 | {% set ns.start_times = ns.start_times + [{'start': item.start | string, 'end': item.end | string, 'value': (item.value * consumption) | round (2)}] %} 55 | {% endfor -%} 56 | {# Sort items by value or start #} 57 | {{ns.start_times | sort(attribute='start') | tojson}} 58 | 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Energy Price Calculator 2 | 3 | ## Configuration 4 | 5 | ### sensor_name 6 | 7 | The entity_id of your [EnergiDataService](https://github.com/MTrab/energidataservice) Sensor. 8 | Examples: 9 | 10 | ``` 11 | {% set sensor_name = "sensor.elpris" %} 12 | ``` 13 | 14 | ``` 15 | {% set sensor_name = "sensor.energi_data_service" %} 16 | ``` 17 | 18 | ### consumption 19 | 20 | The amount of kW used. 21 | Examples: 22 | 23 | Hybrid Electric car charging at 230 volt * 16 amp 24 | ``` 25 | {% set consumption = 230 * 16 / 1000 %} 26 | ``` 27 | 28 | Washing machine using 1500 watts 29 | ``` 30 | {% set consumption = 1500 / 1000 %} 31 | ``` 32 | 33 | 34 | ### duration 35 | 36 | How long will it run for. 37 | Examples: 38 | 39 | Hybrid Electric car charging 10 kWh battery: (Assuming sensor.car_battery_level is the battery level %) 40 | ``` 41 | {% set duration = timedelta(hours = (10 / consumption) * (1 - (states("sensor.car_battery_level") | float / 100))) %} 42 | ``` 43 | 44 | Electric car charging 75 kWh battery up to 80%: (Assuming sensor.car_battery_level is the battery level %) 45 | ``` 46 | {% set duration = timedelta(hours = 0.8 * (75 / consumption) * (1 - (states("sensor.car_battery_level") | float / 100))) %} 47 | ``` 48 | 49 | Washing machine running 2.5 hours 50 | ``` 51 | {% set duration = timedelta(hours = 2, minutes = 30) %} 52 | ``` 53 | 54 | ### start_time 55 | 56 | First possible time to start. Typically: 57 | ``` 58 | {% set start_time = now() %} 59 | ``` 60 | 61 | To show all values use: 62 | ``` 63 | {% set start_time = today_at("00:00") %} 64 | ``` 65 | 66 | ### end_time 67 | 68 | Deadline for when the operation should be finished. (For example departure time). 69 | 70 | Maximum 71 | ``` 72 | {% set end_time = today_at("00:00") + timedelta(days=2) %} 73 | ``` 74 | 75 | Tomorrow at 7:30 76 | ``` 77 | {% set end_time = today_at("7:30") + timedelta(days=1) %} 78 | ``` 79 | 80 | In 12 hours: 81 | ``` 82 | {% set end_time = now() + timedelta(hours=12) %} 83 | ``` 84 | 85 | 86 | ## Sample Card (in danish) 87 | ``` 88 | type: vertical-stack 89 | cards: 90 | - type: entity 91 | entity: sensor.price_calculator_cheapest_time 92 | attribute: countdown 93 | name: 'Start tid:' 94 | icon: mdi:clock-digital 95 | - type: entities 96 | entities: 97 | - entity: binary_sensor.price_calculator_is_cheapest 98 | name: Billigst nu 99 | icon: mdi:currency-usd 100 | - entity: sensor.price_calculator_price_now 101 | icon: mdi:cash-100 102 | name: Pris nu 103 | - entity: sensor.price_calculator_cheapest_price 104 | name: Billigste pris 105 | icon: mdi:cash-100 106 | - entity: sensor.price_calculator_cheapest_time 107 | name: Billigste tidspunkt 108 | icon: mdi:clock 109 | - type: entities 110 | entities: 111 | - entity: input_datetime.calculator_end_time 112 | - entity: input_datetime.calculator_duration 113 | - entity: input_number.calculator_kilowatt 114 | name: Forbrug kW 115 | icon: mdi:lightning-bolt 116 | title: Indstilling 117 | 118 | ``` 119 | -------------------------------------------------------------------------------- /calculator.yaml: -------------------------------------------------------------------------------- 1 | energy_calculator: 2 | input_datetime: 3 | calculator_end_time: 4 | name: Slut tid 5 | has_date: false 6 | has_time: true 7 | 8 | calculator_duration: 9 | name: Varighed 10 | has_date: false 11 | has_time: true 12 | 13 | input_number: 14 | calculator_kilowatt: 15 | name: Consumption kW 16 | icon: mdi:clock-start 17 | min: 0 18 | max: 30 19 | step: 0.01 20 | mode: box 21 | 22 | template: 23 | - sensor: 24 | - name: "Price Calculator" 25 | unique_id: 75a51b09 26 | attributes: 27 | start_times: > 28 | {# Configuration #} 29 | 30 | {# Name of Energi Price Sensor #} 31 | {% set sensor_name = "sensor.energi_data_service" %} 32 | 33 | {# Consumption in kW #} 34 | {% set consumption = states("input_number.calculator_kilowatt") | float %} 35 | 36 | {# Duration of charging, washing, etc. #} 37 | {% set input_duration = today_at(states("input_datetime.calculator_duration")) %} 38 | {% set duration = timedelta(hours = input_duration.hour, minutes = input_duration.minute) %} 39 | 40 | {# First possible time to start. Default now() #} 41 | {% set start_time = now() %} 42 | 43 | {# Deadline for when the operation should be finished (For example departure time). 44 | Default tomorrow at 23:59:59 if tomorrows prices are available, otherwise today at 23:59:59 #} 45 | {% set end_time = today_at(states("input_datetime.calculator_end_time")) + timedelta(days = 1 if today_at(states("input_datetime.calculator_end_time")) < now() else 0) %} 46 | 47 | {# Macros #} 48 | {%- macro date_to_index(date) -%}{{(date.hour + (date.date() - now().date()).days * 24)}}{%- endmacro -%}{# as datetime? #} 49 | {%- macro index_to_date(index) -%}{{today_at("00:00") + timedelta(hours = index)}}{%- endmacro -%}{# as int? #} 50 | 51 | {# All available prices from price sensor #} 52 | {% set prices_raw = state_attr(sensor_name, "raw_today") + state_attr(sensor_name, "raw_tomorrow") | selectattr('value', '!=', none) | list %} 53 | 54 | {# End time can not be later than the available price data #} 55 | {% set end_time = [end_time, (prices_raw | last).hour + timedelta(hours=1)] | min %} 56 | 57 | {% set start_hour = date_to_index(start_time) | int %} 58 | {% set end_hour = 2 + date_to_index(end_time - duration) | int %} 59 | 60 | {# All prices within timerange #} 61 | {% set prices = prices_raw[start_hour:end_hour] %} 62 | 63 | {# namespace to hold values #} 64 | {% set ns = namespace(start_times = []) %} 65 | {% for price in prices %} 66 | {% set item = namespace(start = price.hour, end = price.hour + duration) %} 67 | {% if loop.first %} 68 | {% set item.start = start_time %} 69 | {% set item.end = start_time + duration %} 70 | {% elif loop.last %} 71 | {% set item.start = end_time - duration %} 72 | {% set item.end = end_time %} 73 | {% endif %} 74 | {% set item.value = 0%} 75 | {% for index in range(date_to_index(item.start) | int, date_to_index(item.end) | int + 1) %} 76 | {% set hour_duration = ([item.end, index_to_date(index + 1) | as_datetime] | min - [item.start, index_to_date(index) | as_datetime] | max).total_seconds() / 60 / 60 %} 77 | {% if hour_duration > 0 %} 78 | {% set item.value = item.value + (prices_raw[index].price * hour_duration) %} 79 | {% endif %} 80 | {% endfor %} 81 | {% set ns.start_times = ns.start_times + [{'start': item.start | string, 'end': item.end | string, 'value': (item.value * consumption) | round (2)}] %} 82 | {% endfor -%} 83 | {# Sort items by value or start #} 84 | {{ns.start_times | sort(attribute='start') | tojson}} 85 | 86 | state: > 87 | Calculating...? 88 | 89 | - name: "Price Calculator Price now" 90 | unique_id: 3d357de0 91 | state: > 92 | {{ state_attr("sensor.price_calculator", "start_times")[0].value | round (2)}} 93 | 94 | - name: "Price Calculator Cheapest Time" 95 | unique_id: fa95a6f8 96 | state: > 97 | {{ (state_attr("sensor.price_calculator", "start_times") | sort(attribute='value'))[0].start }} 98 | attributes: 99 | countdown: > 100 | {% set value = states("sensor.price_calculator_cheapest_time") | as_datetime %} 101 | {% if value > now() -%} 102 | start om {{'%02d' % ((value - now()).total_seconds() / 60 / 60) | round(0, 'floor')}}:{{'%02d' % ((value - now()).total_seconds() / 60 % 60) | round(0, 'floor')}} 103 | {%- else -%}start nu{%- endif %} 104 | 105 | - name: "Price Calculator Cheapest Price" 106 | unique_id: e725fdd4 107 | state: > 108 | {{ (state_attr("sensor.price_calculator", "start_times") | sort(attribute='value'))[0].value | round (2)}} 109 | 110 | - binary_sensor: 111 | - name: "Price Calculator Is cheapest" 112 | unique_id: 26d33c17 113 | state: > 114 | {{ (state_attr("sensor.price_calculator", "start_times") | sort(attribute='value'))[0] == state_attr("sensor.price_calculator", "start_times")[0]}} 115 | --------------------------------------------------------------------------------