Last active
April 17, 2025 11:55
-
-
Save jpawlowski/e7c1369d3fbd5213a3a40f96b668c3b9 to your computer and use it in GitHub Desktop.
HASS Tibber Price Information & Rating
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
alias: tibber_api_timer | |
description: "" | |
triggers: | |
- trigger: time_pattern | |
hours: "0" | |
minutes: "0" | |
seconds: "5" | |
- trigger: time_pattern | |
hours: "0" | |
minutes: "1" | |
seconds: "1" | |
- trigger: time_pattern | |
hours: "13" | |
minutes: "10" | |
seconds: "5" | |
- trigger: time_pattern | |
hours: "13" | |
minutes: "45" | |
seconds: "1" | |
- trigger: time_pattern | |
hours: "15" | |
minutes: "2" | |
seconds: "5" | |
- trigger: time_pattern | |
hours: "15" | |
minutes: "15" | |
seconds: "1" | |
- trigger: homeassistant | |
event: start | |
conditions: [] | |
actions: | |
- action: homeassistant.update_entity | |
data: | |
entity_id: | |
- sensor.tibber_price_data_pricerating_monthly | |
enabled: true | |
- delay: | |
hours: 0 | |
minutes: 0 | |
seconds: 10 | |
milliseconds: 0 | |
- action: homeassistant.update_entity | |
data: | |
entity_id: | |
- sensor.tibber_price_data_priceinfo | |
enabled: true | |
- delay: | |
hours: 0 | |
minutes: 0 | |
seconds: 10 | |
milliseconds: 0 | |
- action: homeassistant.update_entity | |
data: | |
entity_id: | |
- sensor.tibber_price_data_pricerating_hourly | |
enabled: true | |
- delay: | |
hours: 0 | |
minutes: 0 | |
seconds: 15 | |
milliseconds: 0 | |
- action: homeassistant.update_entity | |
data: | |
entity_id: | |
- sensor.tibber_price_data_pricerating_daily | |
enabled: true | |
mode: single |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
platform: rest | |
unique_id: tibber_price_data_priceinfo | |
name: Tibber Price Data PriceInfo | |
icon: mdi:database | |
resource: https://api.tibber.com/v1-beta/gql | |
method: POST | |
payload: > | |
{ | |
"query": "{ viewer { homes { currentSubscription { priceInfo { range(resolution: HOURLY, last: 48) { edges { node { startsAt total energy tax level } } } today { startsAt total energy tax level } tomorrow { startsAt total energy tax level } } } } } }" | |
} | |
json_attributes_path: "$.data.viewer.homes[0].currentSubscription" | |
json_attributes: | |
- priceInfo | |
value_template: >- | |
{% set data = | |
value_json.data.viewer.homes[0].currentSubscription.priceInfo | |
if value_json is defined and | |
value_json.data is defined and | |
value_json.data.viewer is defined and | |
value_json.data.viewer.homes is defined and | |
value_json.data.viewer.homes | length > 0 and | |
value_json.data.viewer.homes[0] is defined and | |
value_json.data.viewer.homes[0].currentSubscription is defined and | |
value_json.data.viewer.homes[0].currentSubscription.priceInfo is defined | |
else none %} | |
{{ data.tomorrow[0].startsAt[:10] | |
if data is defined and data is not none and | |
data.tomorrow is defined and data.tomorrow | length > 0 | |
else data.today[0].startsAt[:10] | |
if data is defined and data.today is defined and data.today | length > 0 | |
else 'unavailable' }} | |
scan_interval: 99999 | |
headers: | |
Authorization: !secret tibber_token | |
Content-Type: application/json | |
Accept: application/json | |
User-Agent: HomeAssistant/unknown (RESTful Sensor; +https://home-assistant.io/integrations/sensor.rest/) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
platform: rest | |
unique_id: tibber_price_data_pricerating_daily | |
name: Tibber Price Data PriceRating Daily | |
icon: mdi:database | |
resource: https://api.tibber.com/v1-beta/gql | |
method: POST | |
payload: > | |
{ | |
"query": "{ viewer { homes { currentSubscription { priceRating { thresholdPercentages { low high } daily { entries { time total energy tax difference level } } } } } } }" | |
} | |
json_attributes_path: "$.data.viewer.homes[0].currentSubscription" | |
json_attributes: | |
- priceRating | |
value_template: >- | |
{% set data = | |
value_json.data.viewer.homes[0].currentSubscription.priceRating.daily.entries | |
if value_json is defined and | |
value_json.data is defined and | |
value_json.data.viewer is defined and | |
value_json.data.viewer.homes is defined and | |
value_json.data.viewer.homes | length > 0 and | |
value_json.data.viewer.homes[0] is defined and | |
value_json.data.viewer.homes[0].currentSubscription is defined and | |
value_json.data.viewer.homes[0].currentSubscription.priceRating is defined and | |
value_json.data.viewer.homes[0].currentSubscription.priceRating.daily is defined and | |
value_json.data.viewer.homes[0].currentSubscription.priceRating.daily.entries is defined | |
else none %} | |
{% set today = now().strftime('%Y-%m-%dT00:00:00') %} | |
{% set tomorrow = (now() + timedelta(days=1)).strftime('%Y-%m-%dT00:00:00') %} | |
{% set has_tomorrow = data | selectattr('time', 'search', tomorrow) | list | first | |
if data is defined and data is not none else none %} | |
{% set has_today = data | selectattr('time', 'search', today) | list | first | |
if data is defined and data is not none else none %} | |
{{ tomorrow[:10] | |
if has_tomorrow is defined and has_tomorrow is not none | |
else today[:10] | |
if has_today is defined and has_today is not none | |
else 'unavailable' }} | |
scan_interval: 99999 | |
headers: | |
Authorization: !secret tibber_token | |
Content-Type: application/json | |
Accept: application/json | |
User-Agent: HomeAssistant/unknown (RESTful Sensor; +https://home-assistant.io/integrations/sensor.rest/) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
platform: rest | |
unique_id: tibber_price_data_pricerating_hourly | |
name: Tibber Price Data PriceRating Hourly | |
icon: mdi:database | |
resource: https://api.tibber.com/v1-beta/gql | |
method: POST | |
payload: > | |
{ | |
"query": "{ viewer { homes { currentSubscription { priceRating { thresholdPercentages { low high } hourly { entries { time total energy tax difference level } } } } } } }" | |
} | |
json_attributes_path: "$.data.viewer.homes[0].currentSubscription" | |
json_attributes: | |
- priceRating | |
value_template: >- | |
{% set data = | |
value_json.data.viewer.homes[0].currentSubscription.priceRating.hourly.entries | |
if value_json is defined and | |
value_json.data is defined and | |
value_json.data.viewer is defined and | |
value_json.data.viewer.homes is defined and | |
value_json.data.viewer.homes | length > 0 and | |
value_json.data.viewer.homes[0] is defined and | |
value_json.data.viewer.homes[0].currentSubscription is defined and | |
value_json.data.viewer.homes[0].currentSubscription.priceRating is defined and | |
value_json.data.viewer.homes[0].currentSubscription.priceRating.hourly is defined and | |
value_json.data.viewer.homes[0].currentSubscription.priceRating.hourly.entries is defined | |
else none %} | |
{% set today = now().strftime('%Y-%m-%dT00:00:00') %} | |
{% set tomorrow = (now() + timedelta(days=1)).strftime('%Y-%m-%dT00:00:00') %} | |
{% set has_tomorrow = data | selectattr('time', 'search', tomorrow) | list | first | |
if data is defined and data is not none else none %} | |
{% set has_today = data | selectattr('time', 'search', today) | list | first | |
if data is defined and data is not none else none %} | |
{{ tomorrow[:10] | |
if has_tomorrow is defined and has_tomorrow is not none | |
else today[:10] | |
if has_today is defined and has_today is not none | |
else 'unavailable' }} | |
scan_interval: 99999 | |
headers: | |
Authorization: !secret tibber_token | |
Content-Type: application/json | |
Accept: application/json | |
User-Agent: HomeAssistant/unknown (RESTful Sensor; +https://home-assistant.io/integrations/sensor.rest/) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
platform: rest | |
unique_id: tibber_price_data_pricerating_monthly | |
name: Tibber Price Data PriceRating Monthly | |
icon: mdi:database | |
resource: https://api.tibber.com/v1-beta/gql | |
method: POST | |
payload: > | |
{ | |
"query": "{ viewer { homes { currentSubscription { priceRating { thresholdPercentages { low high } monthly { currency entries { time total energy tax difference level } } } } } } }" | |
} | |
json_attributes_path: "$.data.viewer.homes[0].currentSubscription" | |
json_attributes: | |
- priceRating | |
value_template: >- | |
{% set data = value_json.data.viewer.homes[0].currentSubscription.priceRating.monthly.entries | |
if value_json is defined and | |
value_json.data is defined and | |
value_json.data.viewer is defined and | |
value_json.data.viewer.homes is defined and | |
value_json.data.viewer.homes | length > 0 and | |
value_json.data.viewer.homes[0].currentSubscription is defined and | |
value_json.data.viewer.homes[0].currentSubscription.priceRating is defined and | |
value_json.data.viewer.homes[0].currentSubscription.priceRating.monthly is defined and | |
value_json.data.viewer.homes[0].currentSubscription.priceRating.monthly.entries is defined | |
else none %} | |
{% if data is not none and data | length > 0 %} | |
{{ data[-1].time[:7] }} | |
{% else %} | |
unavailable | |
{% endif %} | |
scan_interval: 99999 | |
headers: | |
Authorization: !secret tibber_token | |
Content-Type: application/json | |
Accept: application/json | |
User-Agent: HomeAssistant/unknown (RESTful Sensor; +https://home-assistant.io/integrations/sensor.rest/) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- trigger: | |
- trigger: time_pattern | |
minutes: 0 | |
- trigger: state | |
entity_id: | |
- sensor.tibber_price_data_pricerating_monthly | |
binary_sensor: | |
- name: "Tibber Price Month Available" | |
unique_id: tibber_price_month_available | |
icon: mdi:calendar-month | |
state: > | |
{% set p = state_attr('sensor.tibber_price_data_pricerating_monthly', 'priceRating') %} | |
{% set e = p.monthly.entries if p is not none and p.monthly is defined else none %} | |
{% set current_month = now().strftime('%Y-%m') %} | |
{{ e is not none and e | selectattr('time', 'search', current_month) | list | count > 0 }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- trigger: | |
- trigger: time_pattern | |
minutes: 0 | |
- trigger: state | |
entity_id: | |
- sensor.tibber_price_data_priceinfo | |
- sensor.tibber_price_data_pricerating_hourly | |
- sensor.tibber_price_data_pricerating_daily | |
sensor: | |
- name: "Tibber Price Today" | |
unique_id: tibber_price_today | |
device_class: monetary | |
unit_of_measurement: ct/kWh | |
state_class: total | |
state: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{{ ((data | map(attribute='total') | average | round(4)) * 100) | round(2) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: mdi:chart-bar | |
attributes: | |
friendly_name: "Strompreis heute (Ø)" | |
description: "Average electricity price for the entire day." | |
description_de: "Durchschnittlicher Strompreis für den gesamten Tag." | |
deviation_to_min: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set pmin = data | min(attribute='total') %} | |
{% set pavg = data | map(attribute='total') | average | round(4) %} | |
{{ ((pavg - pmin.total) * 100) | round(2) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
deviation_to_max: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set pmax = data | max(attribute='total') %} | |
{% set pavg = data | map(attribute='total') | average | round(4) %} | |
{{ ((pmax.total - pavg) * 100) | round(2) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
yesterday_avg: > | |
{% set last_48h = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('range', {}).get('edges', []) | map(attribute='node') | list %} | |
{% if last_48h and last_48h | length > 0 %} | |
{% set yesterday_date = (now() + timedelta(days=-1)).strftime('%Y-%m-%d') %} | |
{{ ((last_48h | selectattr('startsAt', 'search', yesterday_date) | map(attribute='total') | average | round(4)) * 100) | round(2) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
yesterday_deviation_to_min: > | |
{% set last_48h = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('range', {}).get('edges', []) | map(attribute='node') | list %} | |
{% if last_48h and last_48h | length > 0 %} | |
{% set yesterday_date = (now() + timedelta(days=-1)).strftime('%Y-%m-%d') %} | |
{% set pmin = last_48h | selectattr('startsAt', 'search', yesterday_date) | min(attribute='total') %} | |
{% set pavg = last_48h | selectattr('startsAt', 'search', yesterday_date) | map(attribute='total') | average | round(4) %} | |
{{ ((pavg - pmin.total) * 100) | round(2) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
yesterday_deviation_to_max: > | |
{% set last_48h = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('range', {}).get('edges', []) | map(attribute='node') | list %} | |
{% if last_48h and last_48h | length > 0 %} | |
{% set yesterday_date = (now() + timedelta(days=-1)).strftime('%Y-%m-%d') %} | |
{% set pmax = last_48h | selectattr('startsAt', 'search', yesterday_date) | max(attribute='total') %} | |
{% set pavg = last_48h | selectattr('startsAt', 'search', yesterday_date) | map(attribute='total') | average | round(4) %} | |
{{ ((pmax.total - pavg) * 100) | round(2) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
- name: "Tibber Price Minimum Today" | |
unique_id: tibber_price_minimum_today | |
device_class: monetary | |
unit_of_measurement: ct/kWh | |
state_class: total | |
state: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | min(attribute='total') %} | |
{{ entry.total * 100 }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: mdi:triangle-small-down | |
attributes: | |
friendly_name: "Günstigste Stunde heute" | |
description: "Lowest hourly price of today." | |
description_de: "Günstigste Stunde des heutigen Tages." | |
energy: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | min(attribute='total') %} | |
{{ entry.energy * 100 }} | |
{% else %} | |
unavailable | |
{% endif %} | |
tax: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | min(attribute='total') %} | |
{{ entry.tax * 100 }} | |
{% else %} | |
unavailable | |
{% endif %} | |
start_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | min(attribute='total') %} | |
{{ entry.startsAt }} | |
{% else %} | |
unavailable | |
{% endif %} | |
2nd_total: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{# 1. Günstigsten Eintrag finden #} | |
{% set primary = data | min(attribute='total') %} | |
{% set primary_level = primary.level %} | |
{% set ns1 = namespace(begin=primary.startsAt, end=primary.startsAt) %} | |
{# 2. Begin-Ende des ersten Slots ermitteln #} | |
{% for p in data | reverse %} | |
{% if p.startsAt < primary.startsAt and p.level == primary_level %} | |
{% set ns1.begin = p.startsAt %} | |
{% elif p.startsAt < primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% if p.startsAt > primary.startsAt and p.level == primary_level %} | |
{% set ns1.end = p.startsAt %} | |
{% elif p.startsAt > primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{# 3. Bereich ausschließen #} | |
{% set reduced = namespace(list=[]) %} | |
{% for p in data %} | |
{% if as_timestamp(p.startsAt) < as_timestamp(ns1.begin) or as_timestamp(p.startsAt) > as_timestamp(ns1.end) %} | |
{% set reduced.list = reduced.list + [p] %} | |
{% endif %} | |
{% endfor %} | |
{# 4. Zweitgünstigsten Eintrag suchen #} | |
{% if reduced.list | length > 0 %} | |
{% set secondary = reduced.list | min(attribute='total') %} | |
{{ secondary.total }} | |
{% else %} | |
unavailable2 | |
{% endif %} | |
{% else %} | |
unavailable1 | |
{% endif %} | |
2nd_energy: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{# 1. Günstigsten Eintrag finden #} | |
{% set primary = data | min(attribute='total') %} | |
{% set primary_level = primary.level %} | |
{% set ns1 = namespace(begin=primary.startsAt, end=primary.startsAt) %} | |
{# 2. Begin-Ende des ersten Slots ermitteln #} | |
{% for p in data | reverse %} | |
{% if p.startsAt < primary.startsAt and p.level == primary_level %} | |
{% set ns1.begin = p.startsAt %} | |
{% elif p.startsAt < primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% if p.startsAt > primary.startsAt and p.level == primary_level %} | |
{% set ns1.end = p.startsAt %} | |
{% elif p.startsAt > primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{# 3. Bereich ausschließen #} | |
{% set reduced = namespace(list=[]) %} | |
{% for p in data %} | |
{% if as_timestamp(p.startsAt) < as_timestamp(ns1.begin) or as_timestamp(p.startsAt) > as_timestamp(ns1.end) %} | |
{% set reduced.list = reduced.list + [p] %} | |
{% endif %} | |
{% endfor %} | |
{# 4. Zweitgünstigsten Eintrag suchen #} | |
{% if reduced.list | length > 0 %} | |
{% set secondary = reduced.list | min(attribute='total') %} | |
{{ secondary.energy }} | |
{% else %} | |
unavailable2 | |
{% endif %} | |
{% else %} | |
unavailable1 | |
{% endif %} | |
2nd_tax: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{# 1. Günstigsten Eintrag finden #} | |
{% set primary = data | min(attribute='total') %} | |
{% set primary_level = primary.level %} | |
{% set ns1 = namespace(begin=primary.startsAt, end=primary.startsAt) %} | |
{# 2. Begin-Ende des ersten Slots ermitteln #} | |
{% for p in data | reverse %} | |
{% if p.startsAt < primary.startsAt and p.level == primary_level %} | |
{% set ns1.begin = p.startsAt %} | |
{% elif p.startsAt < primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% if p.startsAt > primary.startsAt and p.level == primary_level %} | |
{% set ns1.end = p.startsAt %} | |
{% elif p.startsAt > primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{# 3. Bereich ausschließen #} | |
{% set reduced = namespace(list=[]) %} | |
{% for p in data %} | |
{% if as_timestamp(p.startsAt) < as_timestamp(ns1.begin) or as_timestamp(p.startsAt) > as_timestamp(ns1.end) %} | |
{% set reduced.list = reduced.list + [p] %} | |
{% endif %} | |
{% endfor %} | |
{# 4. Zweitgünstigsten Eintrag suchen #} | |
{% if reduced.list | length > 0 %} | |
{% set secondary = reduced.list | min(attribute='total') %} | |
{{ secondary.tax }} | |
{% else %} | |
unavailable2 | |
{% endif %} | |
{% else %} | |
unavailable1 | |
{% endif %} | |
2nd_start_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{# 1. Günstigsten Eintrag finden #} | |
{% set primary = data | min(attribute='total') %} | |
{% set primary_level = primary.level %} | |
{% set ns1 = namespace(begin=primary.startsAt, end=primary.startsAt) %} | |
{# 2. Begin-Ende des ersten Slots ermitteln #} | |
{% for p in data | reverse %} | |
{% if p.startsAt < primary.startsAt and p.level == primary_level %} | |
{% set ns1.begin = p.startsAt %} | |
{% elif p.startsAt < primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% if p.startsAt > primary.startsAt and p.level == primary_level %} | |
{% set ns1.end = p.startsAt %} | |
{% elif p.startsAt > primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{# 3. Bereich ausschließen #} | |
{% set reduced = namespace(list=[]) %} | |
{% for p in data %} | |
{% if as_timestamp(p.startsAt) < as_timestamp(ns1.begin) or as_timestamp(p.startsAt) > as_timestamp(ns1.end) %} | |
{% set reduced.list = reduced.list + [p] %} | |
{% endif %} | |
{% endfor %} | |
{# 4. Zweitgünstigsten Eintrag suchen #} | |
{% if reduced.list | length > 0 %} | |
{% set secondary = reduced.list | min(attribute='total') %} | |
{{ secondary.startsAt }} | |
{% else %} | |
unavailable2 | |
{% endif %} | |
{% else %} | |
unavailable1 | |
{% endif %} | |
- name: "Tibber Price Maximum Today" | |
unique_id: tibber_price_maximum_today | |
device_class: monetary | |
unit_of_measurement: ct/kWh | |
state_class: total | |
state: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | max(attribute='total') %} | |
{{ entry.total * 100 }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: mdi:triangle-small-up | |
attributes: | |
friendly_name: "Teuerste Stunde heute" | |
description: "Highest hourly price of today." | |
description_de: "Teuerste Stunde des heutigen Tages." | |
energy: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | max(attribute='total') %} | |
{{ entry.energy * 100 }} | |
{% else %} | |
unavailable | |
{% endif %} | |
tax: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | max(attribute='total') %} | |
{{ entry.tax * 100 }} | |
{% else %} | |
unavailable | |
{% endif %} | |
start_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | max(attribute='total') %} | |
{{ entry.startsAt }} | |
{% else %} | |
unavailable | |
{% endif %} | |
2nd_total: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{# 1. Teuersten Eintrag finden #} | |
{% set primary = data | max(attribute='total') %} | |
{% set primary_level = primary.level %} | |
{% set ns1 = namespace(begin=primary.startsAt, end=primary.startsAt) %} | |
{# 2. Begin-Ende des ersten Slots ermitteln #} | |
{% for p in data | reverse %} | |
{% if p.startsAt < primary.startsAt and p.level == primary_level %} | |
{% set ns1.begin = p.startsAt %} | |
{% elif p.startsAt < primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% if p.startsAt > primary.startsAt and p.level == primary_level %} | |
{% set ns1.end = p.startsAt %} | |
{% elif p.startsAt > primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{# 3. Bereich ausschließen #} | |
{% set reduced = namespace(list=[]) %} | |
{% for p in data %} | |
{% if as_timestamp(p.startsAt) < as_timestamp(ns1.begin) or as_timestamp(p.startsAt) > as_timestamp(ns1.end) %} | |
{% set reduced.list = reduced.list + [p] %} | |
{% endif %} | |
{% endfor %} | |
{# 4. Zweitteuersten Eintrag suchen #} | |
{% if reduced.list | length > 0 %} | |
{% set secondary = reduced.list | max(attribute='total') %} | |
{{ secondary.total }} | |
{% else %} | |
unavailable2 | |
{% endif %} | |
{% else %} | |
unavailable1 | |
{% endif %} | |
2nd_energy: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{# 1. Teuersten Eintrag finden #} | |
{% set primary = data | max(attribute='total') %} | |
{% set primary_level = primary.level %} | |
{% set ns1 = namespace(begin=primary.startsAt, end=primary.startsAt) %} | |
{# 2. Begin-Ende des ersten Slots ermitteln #} | |
{% for p in data | reverse %} | |
{% if p.startsAt < primary.startsAt and p.level == primary_level %} | |
{% set ns1.begin = p.startsAt %} | |
{% elif p.startsAt < primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% if p.startsAt > primary.startsAt and p.level == primary_level %} | |
{% set ns1.end = p.startsAt %} | |
{% elif p.startsAt > primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{# 3. Bereich ausschließen #} | |
{% set reduced = namespace(list=[]) %} | |
{% for p in data %} | |
{% if as_timestamp(p.startsAt) < as_timestamp(ns1.begin) or as_timestamp(p.startsAt) > as_timestamp(ns1.end) %} | |
{% set reduced.list = reduced.list + [p] %} | |
{% endif %} | |
{% endfor %} | |
{# 4. Zweitteuersten Eintrag suchen #} | |
{% if reduced.list | length > 0 %} | |
{% set secondary = reduced.list | max(attribute='total') %} | |
{{ secondary.energy }} | |
{% else %} | |
unavailable2 | |
{% endif %} | |
{% else %} | |
unavailable1 | |
{% endif %} | |
2nd_tax: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{# 1. Teuersten Eintrag finden #} | |
{% set primary = data | max(attribute='total') %} | |
{% set primary_level = primary.level %} | |
{% set ns1 = namespace(begin=primary.startsAt, end=primary.startsAt) %} | |
{# 2. Begin-Ende des ersten Slots ermitteln #} | |
{% for p in data | reverse %} | |
{% if p.startsAt < primary.startsAt and p.level == primary_level %} | |
{% set ns1.begin = p.startsAt %} | |
{% elif p.startsAt < primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% if p.startsAt > primary.startsAt and p.level == primary_level %} | |
{% set ns1.end = p.startsAt %} | |
{% elif p.startsAt > primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{# 3. Bereich ausschließen #} | |
{% set reduced = namespace(list=[]) %} | |
{% for p in data %} | |
{% if as_timestamp(p.startsAt) < as_timestamp(ns1.begin) or as_timestamp(p.startsAt) > as_timestamp(ns1.end) %} | |
{% set reduced.list = reduced.list + [p] %} | |
{% endif %} | |
{% endfor %} | |
{# 4. Zweitteuersten Eintrag suchen #} | |
{% if reduced.list | length > 0 %} | |
{% set secondary = reduced.list | max(attribute='total') %} | |
{{ secondary.tax }} | |
{% else %} | |
unavailable2 | |
{% endif %} | |
{% else %} | |
unavailable1 | |
{% endif %} | |
2nd_start_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{# 1. Teuersten Eintrag finden #} | |
{% set primary = data | max(attribute='total') %} | |
{% set primary_level = primary.level %} | |
{% set ns1 = namespace(begin=primary.startsAt, end=primary.startsAt) %} | |
{# 2. Begin-Ende des ersten Slots ermitteln #} | |
{% for p in data | reverse %} | |
{% if p.startsAt < primary.startsAt and p.level == primary_level %} | |
{% set ns1.begin = p.startsAt %} | |
{% elif p.startsAt < primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% if p.startsAt > primary.startsAt and p.level == primary_level %} | |
{% set ns1.end = p.startsAt %} | |
{% elif p.startsAt > primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{# 3. Bereich ausschließen #} | |
{% set reduced = namespace(list=[]) %} | |
{% for p in data %} | |
{% if as_timestamp(p.startsAt) < as_timestamp(ns1.begin) or as_timestamp(p.startsAt) > as_timestamp(ns1.end) %} | |
{% set reduced.list = reduced.list + [p] %} | |
{% endif %} | |
{% endfor %} | |
{# 4. Zweitteuersten Eintrag suchen #} | |
{% if reduced.list | length > 0 %} | |
{% set secondary = reduced.list | max(attribute='total') %} | |
{{ secondary.startsAt }} | |
{% else %} | |
unavailable2 | |
{% endif %} | |
{% else %} | |
unavailable1 | |
{% endif %} | |
- name: "Tibber Price Relative Spread Today" | |
unique_id: tibber_price_relative_spread_today | |
unit_of_measurement: "%" | |
state_class: measurement | |
state: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set pmax = data | max(attribute='total') %} | |
{% set pmin = data | min(attribute='total') %} | |
{% set pavg = data | map(attribute='total') | average %} | |
{{ (((pmax.total - pmin.total) / pavg) * 100) | round(2) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: mdi:percent | |
attributes: | |
friendly_name: "Relative Preisspanne heute" | |
description: "Percentage spread between max and min price relative to today's average." | |
description_de: "Prozentuale Spannweite zwischen Höchst- und Tiefstpreis im Verhältnis zum Tagesdurchschnitt." | |
yesterday: > | |
{% set last_48h = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('range', {}).get('edges', []) | map(attribute='node') | list %} | |
{% if last_48h and last_48h | length > 0 %} | |
{% set yesterday_date = (now() + timedelta(days=-1)).strftime('%Y-%m-%d') %} | |
{% set pmax = last_48h | selectattr('startsAt', 'search', yesterday_date) | max(attribute='total') %} | |
{% set pmin = last_48h | selectattr('startsAt', 'search', yesterday_date) | min(attribute='total') %} | |
{% set pavg = last_48h | selectattr('startsAt', 'search', yesterday_date) | map(attribute='total') | average %} | |
{{ (((pmax.total - pmin.total) / pavg) * 100) | round(2) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
- name: "Tibber Price Difference Today" | |
unique_id: tibber_price_difference_today | |
device_class: monetary | |
unit_of_measurement: ct/kWh | |
state_class: total | |
state: > | |
{% set last_48h = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('range', {}).get('edges', []) | map(attribute='node') | list %} | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data and data | length > 0 and last_48h and last_48h | length > 0 %} | |
{% set yesterday_date = (now() + timedelta(days=-1)).strftime('%Y-%m-%d') %} | |
{% set yesterday_avg = last_48h | selectattr('startsAt', 'search', yesterday_date) | map(attribute='total') | average %} | |
{% set today_avg = data | map(attribute='total') | average %} | |
{{ ((today_avg - yesterday_avg) * 100) | round(2) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: mdi:vector-difference | |
attributes: | |
friendly_name: "Preisdifferenz heute" | |
description: "Shows the difference between today's and yesterday's average electricity price." | |
description_de: "Zeigt die Differenz zwischen dem heutigen und dem gestrigen durchschnittlichen Strompreis." | |
- name: "Tibber Price Skew Today" | |
unique_id: tibber_price_skew_today | |
device_class: monetary | |
unit_of_measurement: ct/kWh | |
state_class: total | |
state: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set pmax = data | max(attribute='total') %} | |
{% set pmin = data | min(attribute='total') %} | |
{% set pavg = data | map(attribute='total') | average | round(4) %} | |
{{ (((pmax.total - pavg) - (pavg - pmin.total)) * 100) | round(2) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: mdi:chart-bar | |
attributes: | |
friendly_name: "Verteilungsschiefe heute" | |
description: "Deviation of the distribution from the mean value." | |
description_de: "Abweichung der Verteilung vom Mittelwert." | |
yesterday: > | |
{% set last_48h = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('range', {}).get('edges', []) | map(attribute='node') | list %} | |
{% if last_48h and last_48h | length > 0 %} | |
{% set yesterday_date = (now() + timedelta(days=-1)).strftime('%Y-%m-%d') %} | |
{% set pmax = last_48h | selectattr('startsAt', 'search', yesterday_date) | max(attribute='total') %} | |
{% set pmin = last_48h | selectattr('startsAt', 'search', yesterday_date) | min(attribute='total') %} | |
{% set pavg = last_48h | selectattr('startsAt', 'search', yesterday_date) | map(attribute='total') | average | round(4) %} | |
{{ (((pmax.total - pavg) - (pavg - pmin.total)) * 100) | round(2) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
- name: "Tibber Price Distribution Bias Today" | |
unique_id: tibber_price_distribution_bias_today | |
device_class: enum | |
state: > | |
{% set skew = states('sensor.tibber_price_skew_today') | float(0) %} | |
{% if skew > 0.5 %} | |
upward | |
{% elif skew < -0.5 %} | |
downward | |
{% else %} | |
neutral | |
{% endif %} | |
icon: mdi:chart-bell-curve | |
attributes: | |
friendly_name: "Verzerrungsrichtung heute" | |
description: "Describes the price distribution shape for today: neutral, upward or downward skew." | |
description_de: "Bewertung der Tagesverteilung: neutral, nach oben oder nach unten verzerrt." | |
friendly_state: > | |
{% set skew = states('sensor.tibber_price_skew_today') | float(0) %} | |
{% if skew > 0.5 %} | |
Obenlastig | |
{% elif skew < -0.5 %} | |
Untenlastig | |
{% else %} | |
Ausgeglichen | |
{% endif %} | |
- name: "Tibber Price Level Today" | |
unique_id: tibber_price_level_today | |
device_class: enum | |
state: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set score = namespace(total=0) %} | |
{% for p in data %} | |
{% if p.level == 'VERY_CHEAP' %} | |
{% set score.total = score.total - 2 %} | |
{% elif p.level == 'CHEAP' %} | |
{% set score.total = score.total - 1 %} | |
{% elif p.level == 'EXPENSIVE' %} | |
{% set score.total = score.total + 1 %} | |
{% elif p.level == 'VERY_EXPENSIVE' %} | |
{% set score.total = score.total + 2 %} | |
{% endif %} | |
{% endfor %} | |
{% set raw_level = | |
'VERY_CHEAP' if score.total <= -10 else | |
'CHEAP' if score.total <= -4 else | |
'NORMAL' if score.total < 4 else | |
'EXPENSIVE' if score.total < 10 else | |
'VERY_EXPENSIVE' %} | |
{% set present_levels = data | map(attribute='level') | list %} | |
{% set fallback_map = { | |
'VERY_CHEAP': ['VERY_CHEAP', 'CHEAP', 'NORMAL'], | |
'CHEAP': ['CHEAP', 'NORMAL'], | |
'NORMAL': ['NORMAL'], | |
'EXPENSIVE': ['EXPENSIVE', 'NORMAL'], | |
'VERY_EXPENSIVE': ['VERY_EXPENSIVE', 'EXPENSIVE', 'NORMAL'] | |
} %} | |
{% set fallback_levels = fallback_map[raw_level] %} | |
{% set actual = namespace(value='NORMAL') %} | |
{% for lvl in fallback_levels %} | |
{% if lvl in present_levels %} | |
{% set actual.value = lvl %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{{ actual.value }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set score = namespace(total=0) %} | |
{% for p in data %} | |
{% if p.level == 'VERY_CHEAP' %} | |
{% set score.total = score.total - 2 %} | |
{% elif p.level == 'CHEAP' %} | |
{% set score.total = score.total - 1 %} | |
{% elif p.level == 'EXPENSIVE' %} | |
{% set score.total = score.total + 1 %} | |
{% elif p.level == 'VERY_EXPENSIVE' %} | |
{% set score.total = score.total + 2 %} | |
{% endif %} | |
{% endfor %} | |
{% set raw_level = | |
'VERY_CHEAP' if score.total <= -10 else | |
'CHEAP' if score.total <= -4 else | |
'NORMAL' if score.total < 4 else | |
'EXPENSIVE' if score.total < 10 else | |
'VERY_EXPENSIVE' %} | |
{% set present_levels = data | map(attribute='level') | list %} | |
{% set fallback_map = { | |
'VERY_CHEAP': ['VERY_CHEAP', 'CHEAP', 'NORMAL'], | |
'CHEAP': ['CHEAP', 'NORMAL'], | |
'NORMAL': ['NORMAL'], | |
'EXPENSIVE': ['EXPENSIVE', 'NORMAL'], | |
'VERY_EXPENSIVE': ['VERY_EXPENSIVE', 'EXPENSIVE', 'NORMAL'] | |
} %} | |
{% set fallback_levels = fallback_map[raw_level] %} | |
{% set actual = namespace(value='NORMAL') %} | |
{% for lvl in fallback_levels %} | |
{% if lvl in present_levels %} | |
{% set actual.value = lvl %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% if actual.value == 'VERY_CHEAP' %} | |
mdi:arrow-bottom-right-thick | |
{% elif actual.value == 'CHEAP' %} | |
mdi:trending-down | |
{% elif actual.value == 'NORMAL' %} | |
mdi:plus-minus-variant | |
{% elif actual.value == 'EXPENSIVE' %} | |
mdi:trending-up | |
{% elif actual.value == 'VERY_EXPENSIVE' %} | |
mdi:arrow-top-right-thick | |
{% else %} | |
mdi:help-circle | |
{% endif %} | |
{% else %} | |
mdi:help-circle | |
{% endif %} | |
attributes: | |
friendly_name: "Dominantes Preisniveau heute" | |
description: "Long-term level of today's prices (compared to last 7 days)." | |
description_de: "Langfristige Einordnung des heutigen Preises (7-Tage-Vergleich)." | |
valid_states: '["VERY_CHEAP", "CHEAP", "NORMAL", "EXPENSIVE", "VERY_EXPENSIVE"]' | |
friendly_state: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set score = namespace(total=0) %} | |
{% for p in data %} | |
{% if p.level == 'VERY_CHEAP' %} | |
{% set score.total = score.total - 2 %} | |
{% elif p.level == 'CHEAP' %} | |
{% set score.total = score.total - 1 %} | |
{% elif p.level == 'EXPENSIVE' %} | |
{% set score.total = score.total + 1 %} | |
{% elif p.level == 'VERY_EXPENSIVE' %} | |
{% set score.total = score.total + 2 %} | |
{% endif %} | |
{% endfor %} | |
{% set raw_level = | |
'VERY_CHEAP' if score.total <= -10 else | |
'CHEAP' if score.total <= -4 else | |
'NORMAL' if score.total < 4 else | |
'EXPENSIVE' if score.total < 10 else | |
'VERY_EXPENSIVE' %} | |
{% set present_levels = data | map(attribute='level') | list %} | |
{% set fallback_map = { | |
'VERY_CHEAP': ['VERY_CHEAP', 'CHEAP', 'NORMAL'], | |
'CHEAP': ['CHEAP', 'NORMAL'], | |
'NORMAL': ['NORMAL'], | |
'EXPENSIVE': ['EXPENSIVE', 'NORMAL'], | |
'VERY_EXPENSIVE': ['VERY_EXPENSIVE', 'EXPENSIVE', 'NORMAL'] | |
} %} | |
{% set fallback_levels = fallback_map[raw_level] %} | |
{% set actual = namespace(value='NORMAL') %} | |
{% for lvl in fallback_levels %} | |
{% if lvl in present_levels %} | |
{% set actual.value = lvl %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% if actual.value == 'VERY_CHEAP' %} | |
Sehr günstig | |
{% elif actual.value == 'CHEAP' %} | |
Günstig | |
{% elif actual.value == 'NORMAL' %} | |
Normal | |
{% elif actual.value == 'EXPENSIVE' %} | |
Teuer | |
{% elif actual.value == 'VERY_EXPENSIVE' %} | |
Sehr teuer | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
state_absolute: >- | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set score = namespace(total=0) %} | |
{% for p in data %} | |
{% if p.level == 'VERY_CHEAP' %} | |
{% set score.total = score.total - 2 %} | |
{% elif p.level == 'CHEAP' %} | |
{% set score.total = score.total - 1 %} | |
{% elif p.level == 'EXPENSIVE' %} | |
{% set score.total = score.total + 1 %} | |
{% elif p.level == 'VERY_EXPENSIVE' %} | |
{% set score.total = score.total + 2 %} | |
{% endif %} | |
{% endfor %} | |
{{ score.total }} | |
{% else %} | |
unavailable | |
{% endif %} | |
counts: |- | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set counts = namespace( | |
VERY_CHEAP=0, | |
CHEAP=0, | |
NORMAL=0, | |
EXPENSIVE=0, | |
VERY_EXPENSIVE=0 | |
) %} | |
{% for p in data %} | |
{% if p.level == 'VERY_CHEAP' %} | |
{% set counts.VERY_CHEAP = counts.VERY_CHEAP + 1 %} | |
{% elif p.level == 'CHEAP' %} | |
{% set counts.CHEAP = counts.CHEAP + 1 %} | |
{% elif p.level == 'NORMAL' %} | |
{% set counts.NORMAL = counts.NORMAL + 1 %} | |
{% elif p.level == 'EXPENSIVE' %} | |
{% set counts.EXPENSIVE = counts.EXPENSIVE + 1 %} | |
{% elif p.level == 'VERY_EXPENSIVE' %} | |
{% set counts.VERY_EXPENSIVE = counts.VERY_EXPENSIVE + 1 %} | |
{% endif %} | |
{% endfor %} | |
{% set d = dict({ | |
'VERY_CHEAP': counts.VERY_CHEAP, | |
'CHEAP': counts.CHEAP, | |
'NORMAL': counts.NORMAL, | |
'EXPENSIVE': counts.EXPENSIVE, | |
'VERY_EXPENSIVE': counts.VERY_EXPENSIVE | |
}) %} | |
{{ d }} | |
{% else %} | |
unavailable | |
{% endif %} | |
min: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | min(attribute='total') %} | |
{{ entry.level }} | |
{% else %} | |
unavailable | |
{% endif %} | |
min_friendly_name: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | min(attribute='total') %} | |
{% if entry.level == 'VERY_CHEAP' %} | |
Sehr günstig | |
{% elif entry.level == 'CHEAP' %} | |
Günstig | |
{% elif entry.level == 'NORMAL' %} | |
Normal | |
{% elif entry.level == 'EXPENSIVE' %} | |
Teuer | |
{% elif entry.level == 'VERY_EXPENSIVE' %} | |
Sehr teuer | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
min_begin_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | min(attribute='total') %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.startsAt, found=false) %} | |
{% for p in data | reverse %} | |
{% if p.startsAt < entry.startsAt %} | |
{% if p.level != level %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.begin if ns.found else now().replace(hour=0, minute=0, second=0, microsecond=0).isoformat() }} | |
{% else %} | |
unavailable | |
{% endif %} | |
min_end_at: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% if data_today is not none and data_today | length > 0 %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% set entry = data_today | min(attribute='total') %} | |
{% set level = entry.level %} | |
{% set ns = namespace(end=entry.startsAt, found=false) %} | |
{% for p in combined %} | |
{% if p.startsAt > entry.startsAt %} | |
{% if p.level != level %} | |
{% set ns.end = p.startsAt %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.end if ns.found else ( | |
(now() + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
if data_tomorrow is not none and data_tomorrow | length > 0 | |
else now().replace(hour=23, minute=59, second=59, microsecond=0).isoformat() | |
) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
min_duration_h: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% if data_today is not none and data_today | length > 0 %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% set entry = data_today | min(attribute='total') %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.startsAt, end=entry.startsAt, end_found=false) %} | |
{% for p in combined | reverse %} | |
{% if p.startsAt < entry.startsAt %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{% for p in combined %} | |
{% if p.startsAt > entry.startsAt %} | |
{% if p.level != level %} | |
{% set ns.end = p.startsAt %} | |
{% set ns.end_found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp(ns.end if ns.end_found else ( | |
(now() + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
)) - as_timestamp(ns.begin)) / 3600) | int }} | |
{% else %} | |
unavailable | |
{% endif %} | |
2_min: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{# 1. Günstigsten Eintrag finden #} | |
{% set primary = data | min(attribute='total') %} | |
{% set primary_level = primary.level %} | |
{% set ns1 = namespace(begin=primary.startsAt, end=primary.startsAt) %} | |
{# 2. Begin-Ende des ersten Slots ermitteln #} | |
{% for p in data | reverse %} | |
{% if p.startsAt < primary.startsAt and p.level == primary_level %} | |
{% set ns1.begin = p.startsAt %} | |
{% elif p.startsAt < primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% if p.startsAt > primary.startsAt and p.level == primary_level %} | |
{% set ns1.end = p.startsAt %} | |
{% elif p.startsAt > primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{# 3. Bereich ausschließen #} | |
{% set reduced = namespace(list=[]) %} | |
{% for p in data %} | |
{% if as_timestamp(p.startsAt) < as_timestamp(ns1.begin) or as_timestamp(p.startsAt) > as_timestamp(ns1.end) %} | |
{% set reduced.list = reduced.list + [p] %} | |
{% endif %} | |
{% endfor %} | |
{# 4. Zweitgünstigsten Eintrag suchen #} | |
{% if reduced.list | length > 0 %} | |
{% set secondary = reduced.list | min(attribute='total') %} | |
{{ secondary.level }} | |
{% else %} | |
unavailable2 | |
{% endif %} | |
{% else %} | |
unavailable1 | |
{% endif %} | |
max: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | max(attribute='total') %} | |
{{ entry.level }} | |
{% else %} | |
unavailable | |
{% endif %} | |
max_friendly_name: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | max(attribute='total') %} | |
{% if entry.level == 'VERY_CHEAP' %} | |
Sehr günstig | |
{% elif entry.level == 'CHEAP' %} | |
Günstig | |
{% elif entry.level == 'NORMAL' %} | |
Normal | |
{% elif entry.level == 'EXPENSIVE' %} | |
Teuer | |
{% elif entry.level == 'VERY_EXPENSIVE' %} | |
Sehr teuer | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
max_begin_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set entry = data | max(attribute='total') %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.startsAt, found=false) %} | |
{% for p in data | reverse %} | |
{% if p.startsAt < entry.startsAt %} | |
{% if p.level != level %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.begin if ns.found else now().replace(hour=0, minute=0, second=0, microsecond=0).isoformat() }} | |
{% else %} | |
unavailable | |
{% endif %} | |
max_end_at: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% if data_today is not none and data_today | length > 0 %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% set entry = data_today | max(attribute='total') %} | |
{% set level = entry.level %} | |
{% set ns = namespace(end=entry.startsAt, found=false) %} | |
{% for p in combined %} | |
{% if p.startsAt > entry.startsAt %} | |
{% if p.level != level %} | |
{% set ns.end = p.startsAt %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.end if ns.found else ( | |
(now() + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
if data_tomorrow is not none and data_tomorrow | length > 0 | |
else now().replace(hour=23, minute=59, second=59, microsecond=0).isoformat() | |
) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
max_duration_h: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% if data_today is not none and data_today | length > 0 %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% set entry = data_today | max(attribute='total') %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.startsAt, end=entry.startsAt, end_found=false) %} | |
{% for p in combined | reverse %} | |
{% if p.startsAt < entry.startsAt %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{% for p in combined %} | |
{% if p.startsAt > entry.startsAt %} | |
{% if p.level != level %} | |
{% set ns.end = p.startsAt %} | |
{% set ns.end_found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp(ns.end if ns.end_found else ( | |
(now() + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
)) - as_timestamp(ns.begin)) / 3600) | int }} | |
{% else %} | |
unavailable | |
{% endif %} | |
2_max: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{# 1. Günstigsten Eintrag finden #} | |
{% set primary = data | max(attribute='total') %} | |
{% set primary_level = primary.level %} | |
{% set ns1 = namespace(begin=primary.startsAt, end=primary.startsAt) %} | |
{# 2. Begin-Ende des ersten Slots ermitteln #} | |
{% for p in data | reverse %} | |
{% if p.startsAt < primary.startsAt and p.level == primary_level %} | |
{% set ns1.begin = p.startsAt %} | |
{% elif p.startsAt < primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% if p.startsAt > primary.startsAt and p.level == primary_level %} | |
{% set ns1.end = p.startsAt %} | |
{% elif p.startsAt > primary.startsAt %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{# 3. Bereich ausschließen #} | |
{% set reduced = namespace(list=[]) %} | |
{% for p in data %} | |
{% if as_timestamp(p.startsAt) < as_timestamp(ns1.begin) or as_timestamp(p.startsAt) > as_timestamp(ns1.end) %} | |
{% set reduced.list = reduced.list + [p] %} | |
{% endif %} | |
{% endfor %} | |
{# 4. Zweitgünstigsten Eintrag suchen #} | |
{% if reduced.list | length > 0 %} | |
{% set secondary = reduced.list | max(attribute='total') %} | |
{{ secondary.level }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
- name: "Tibber Price Rating Today" | |
unique_id: tibber_price_rating_today | |
device_class: enum | |
state: > | |
{% set rating = (state_attr('sensor.tibber_price_data_pricerating_daily', 'priceRating') or {}).get('daily', {}).get('entries', []) %} | |
{% if rating | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT00:00:00') %} | |
{% set current = rating | selectattr('time', 'search', today) | list | first %} | |
{{ current.level if current is defined else 'unavailable' }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: > | |
{% set rating = (state_attr('sensor.tibber_price_data_pricerating_daily', 'priceRating') or {}).get('daily', {}).get('entries', []) %} | |
{% if rating | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT00:00:00') %} | |
{% set current = rating | selectattr('time', 'search', today) | list | first %} | |
{% if current is not defined %} | |
mdi:help-circle | |
{% elif current.level == 'LOW' %} | |
mdi:cash-check | |
{% elif current.level == 'NORMAL' %} | |
mdi:cash | |
{% elif current.level == 'HIGH' %} | |
mdi:cash-remove | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
attributes: | |
friendly_name: "Dominante Preisphase heute" | |
description: "Classifies the electricity price compared to other hours of the last 24 hours." | |
description_de: "Einordnung des Strompreises im Vergleich zu anderen Stunden der letzten 24 Stunden." | |
valid_states: '["LOW", "NORMAL", "HIGH"]' | |
friendly_state: > | |
{% set rating = (state_attr('sensor.tibber_price_data_pricerating_daily', 'priceRating') or {}).get('daily', {}).get('entries', []) %} | |
{% if rating | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT00:00:00') %} | |
{% set current = rating | selectattr('time', 'search', today) | list | first %} | |
{% if current is not defined %} | |
unavailable | |
{% elif current.level == 'LOW' %} | |
Niedrig | |
{% elif current.level == 'NORMAL' %} | |
Normal | |
{% elif current.level == 'HIGH' %} | |
Hoch | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
min: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | min(attribute='total') %} | |
{{ entry.level }} | |
{% else %} | |
unavailable | |
{% endif %} | |
min_friendly_name: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | min(attribute='total') %} | |
{% if entry.level == 'LOW' %} | |
Niedrig | |
{% elif entry.level == 'NORMAL' %} | |
Normal | |
{% elif entry.level == 'HIGH' %} | |
Hoch | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
min_difference: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | min(attribute='total') %} | |
{{ entry.difference }} | |
{% else %} | |
unavailable | |
{% endif %} | |
min_begin_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | min(attribute='total') %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(begin=entry_time.isoformat(), found=false) %} | |
{% for p in data | reverse %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time < entry_time %} | |
{% if p.level != level %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.begin if ns.found else now().replace(hour=0, minute=0, second=0, microsecond=0).isoformat() }} | |
{% else %} | |
unavailable | |
{% endif %} | |
min_end_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | min(attribute='total') %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(end=entry_time.isoformat(), found=false) %} | |
{% for p in data %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time > entry_time %} | |
{% if p.level != level %} | |
{% set ns.end = p_time.isoformat() %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.end if ns.found else ( | |
(now() + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
if data_tomorrow is not none and data_tomorrow | length > 0 | |
else now().replace(hour=23, minute=59, second=59, microsecond=0).isoformat() | |
) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
min_duration_h: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | min(attribute='total') %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(begin=entry_time.isoformat(), end=entry_time.isoformat(), end_found=false) %} | |
{% for p in data | reverse %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time < entry_time %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time > entry_time %} | |
{% if p.level != level %} | |
{% set ns.end = p_time.isoformat() %} | |
{% set ns.end_found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp(ns.end if ns.end_found else ( | |
(now() + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
)) - as_timestamp(ns.begin)) / 3600) | int }} | |
{% else %} | |
unavailable | |
{% endif %} | |
max: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | max(attribute='total') %} | |
{{ entry.level }} | |
{% else %} | |
unavailable | |
{% endif %} | |
max_friendly_name: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | max(attribute='total') %} | |
{% if entry.level == 'LOW' %} | |
Niedrig | |
{% elif entry.level == 'NORMAL' %} | |
Normal | |
{% elif entry.level == 'HIGH' %} | |
Hoch | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
max_difference: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | max(attribute='total') %} | |
{{ entry.difference }} | |
{% else %} | |
unavailable | |
{% endif %} | |
max_begin_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | max(attribute='total') %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(begin=entry_time.isoformat(), found=false) %} | |
{% for p in data | reverse %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time < entry_time %} | |
{% if p.level != level %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.begin if ns.found else now().replace(hour=0, minute=0, second=0, microsecond=0).isoformat() }} | |
{% else %} | |
unavailable | |
{% endif %} | |
max_end_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | max(attribute='total') %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(end=entry_time.isoformat(), found=false) %} | |
{% for p in data %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time > entry_time %} | |
{% if p.level != level %} | |
{% set ns.end = p_time.isoformat() %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.end if ns.found else ( | |
(now() + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
if data_tomorrow is not none and data_tomorrow | length > 0 | |
else now().replace(hour=23, minute=59, second=59, microsecond=0).isoformat() | |
) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
max_duration_h: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set today = now().strftime('%Y-%m-%dT') %} | |
{% set entry = data | selectattr('time', 'search', today) | max(attribute='total') %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(begin=entry_time.isoformat(), end=entry_time.isoformat(), end_found=false) %} | |
{% for p in data | reverse %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time < entry_time %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time > entry_time %} | |
{% if p.level != level %} | |
{% set ns.end = p_time.isoformat() %} | |
{% set ns.end_found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp(ns.end if ns.end_found else ( | |
(now() + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
)) - as_timestamp(ns.begin)) / 3600) | int }} | |
{% else %} | |
unavailable | |
{% endif %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- trigger: | |
- trigger: time_pattern | |
minutes: 0 | |
- trigger: state | |
entity_id: | |
- sensor.tibber_price_data_priceinfo | |
- sensor.tibber_price_data_pricerating_hourly | |
sensor: | |
- name: "Tibber Price Current Hour" | |
unique_id: tibber_price_current_hour | |
device_class: monetary | |
unit_of_measurement: ct/kWh | |
state_class: total | |
state: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('startsAt', 'search', now_hour) | list | first %} | |
{{ entry.total * 100 if entry is defined else 'unavailable' }} | |
icon: mdi:cash-clock | |
attributes: | |
friendly_name: "Strompreis aktuell" | |
description: "Price in the current hour." | |
description_de: "Strompreis in der aktuellen Stunde." | |
start_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('startsAt', 'search', now_hour) | list | first %} | |
{{ entry.startsAt if entry is defined else 'unavailable' }} | |
energy: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('startsAt', 'search', now_hour) | list | first %} | |
{{ entry.energy * 100 if entry is defined else 'unavailable' }} | |
tax: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('startsAt', 'search', now_hour) | list | first %} | |
{{ entry.tax * 100 if entry is defined else 'unavailable' }} | |
difference_to_before: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set this_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set last_hour = (now() + timedelta(hours=-1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set now_entry = data | selectattr('startsAt', 'search', this_hour) | list | first %} | |
{% set last_entry = data | selectattr('startsAt', 'search', last_hour) | list | first %} | |
{% if now_entry is defined and last_entry is defined %} | |
{{ (now_entry.total - last_entry.total) * 100 }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
is_cheaper: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set this_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set last_hour = (now() + timedelta(hours=-1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set now_entry = data | selectattr('startsAt', 'search', this_hour) | list | first %} | |
{% set last_entry = data | selectattr('startsAt', 'search', last_hour) | list | first %} | |
{% if now_entry is defined and last_entry is defined %} | |
{{ now_entry.total < last_entry.total }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
is_more_expensive: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set this_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set last_hour = (now() + timedelta(hours=-1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set now_entry = data | selectattr('startsAt', 'search', this_hour) | list | first %} | |
{% set last_entry = data | selectattr('startsAt', 'search', last_hour) | list | first %} | |
{% if now_entry is defined and last_entry is defined %} | |
{{ now_entry.total > last_entry.total }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
- name: "Tibber Price Next Hour" | |
unique_id: tibber_price_next_hour | |
device_class: monetary | |
unit_of_measurement: ct/kWh | |
state_class: total | |
state: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', next_hour) | list | first %} | |
{{ entry.total * 100 if entry is defined else 'unavailable' }} | |
icon: mdi:cash-clock | |
attributes: | |
friendly_name: "Strompreis nächste Stunde" | |
description: "Price in the next hour." | |
description_de: "Strompreis in der nächsten Stunde." | |
start_at: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', next_hour) | list | first %} | |
{{ entry.startsAt if entry is defined else 'unavailable' }} | |
energy: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', next_hour) | list | first %} | |
{{ entry.energy * 100 if entry is defined else 'unavailable' }} | |
tax: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', next_hour) | list | first %} | |
{{ entry.tax * 100 if entry is defined else 'unavailable' }} | |
difference_to_now: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set this_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set now_entry = combined | selectattr('startsAt', 'search', this_hour) | list | first %} | |
{% set next_entry = combined | selectattr('startsAt', 'search', next_hour) | list | first %} | |
{% if now_entry is defined and next_entry is defined %} | |
{{ (next_entry.total - now_entry.total) * 100 }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
is_cheaper: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set this_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set now_entry = combined | selectattr('startsAt', 'search', this_hour) | list | first %} | |
{% set next_entry = combined | selectattr('startsAt', 'search', next_hour) | list | first %} | |
{% if now_entry is defined and next_entry is defined %} | |
{{ next_entry.total < now_entry.total }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
- name: "Tibber Price Level Current Hour" | |
unique_id: tibber_price_level_current_hour | |
state: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr("startsAt", "search", now_hour) | list | first %} | |
{{ entry.level if entry is defined else none }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr("startsAt", "search", now_hour) | list | first %} | |
{% if entry.level == 'VERY_CHEAP' %} | |
mdi:arrow-bottom-right-thick | |
{% elif entry.level == 'CHEAP' %} | |
mdi:trending-down | |
{% elif entry.level == 'NORMAL' %} | |
mdi:plus-minus-variant | |
{% elif entry.level == 'EXPENSIVE' %} | |
mdi:trending-up | |
{% elif entry.level == 'VERY_EXPENSIVE' %} | |
mdi:arrow-top-right-thick | |
{% else %} | |
mdi:help-circle | |
{% endif %} | |
{% else %} | |
mdi:help-circle | |
{% endif %} | |
attributes: | |
friendly_name: "Preisniveau aktuell (Langzeit)" | |
description: "Long-term level of current hour's price (compared to last 7 days)." | |
description_de: "Langfristige Einordnung des aktuellen Preises (7-Tage-Vergleich)." | |
valid_states: '["VERY_CHEAP", "CHEAP", "NORMAL", "EXPENSIVE", "VERY_EXPENSIVE"]' | |
friendly_state: > | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr("startsAt", "search", now_hour) | list | first %} | |
{% if entry.level == 'VERY_CHEAP' %} | |
Sehr günstig | |
{% elif entry.level == 'CHEAP' %} | |
Günstig | |
{% elif entry.level == 'NORMAL' %} | |
Normal | |
{% elif entry.level == 'EXPENSIVE' %} | |
Teuer | |
{% elif entry.level == 'VERY_EXPENSIVE' %} | |
Sehr teuer | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
begin_at: >- | |
{% set data = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('startsAt', 'search', now_hour) | list | first %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.startsAt) %} | |
{% for p in data | reverse %} | |
{% if p.startsAt < now_hour %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.begin }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
end_at: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', now_hour) | list | first %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set ns = namespace(end=entry.startsAt) %} | |
{% for p in combined %} | |
{% if p.startsAt > now_hour %} | |
{% if p.level != level %} | |
{% set ns.end = p.startsAt %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.end }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
duration_h: >- | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', now_hour) | list | first %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.startsAt, end=entry.startsAt) %} | |
{% for p in combined | reverse %} | |
{% if p.startsAt < now_hour %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{% for p in combined %} | |
{% if p.startsAt > now_hour %} | |
{% if p.level != level %} | |
{% set ns.end = p.startsAt %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp(ns.end) - as_timestamp(ns.begin)) / 3600) | int }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
progress_h: >- | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', now_hour) | list | first %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.startsAt) %} | |
{% for p in combined | reverse %} | |
{% if p.startsAt < now_hour %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp(now()) - as_timestamp(ns.begin)) / 3600) | round(0, 'floor') }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
- name: "Tibber Price Level Next Hour" | |
unique_id: tibber_price_level_next_hour | |
state: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', next_hour) | list | first %} | |
{{ entry.level if entry is defined else none }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr("startsAt", "search", next_hour) | list | first %} | |
{% if entry.level == 'VERY_CHEAP' %} | |
mdi:arrow-bottom-right-thick | |
{% elif entry.level == 'CHEAP' %} | |
mdi:trending-down | |
{% elif entry.level == 'NORMAL' %} | |
mdi:plus-minus-variant | |
{% elif entry.level == 'EXPENSIVE' %} | |
mdi:trending-up | |
{% elif entry.level == 'VERY_EXPENSIVE' %} | |
mdi:arrow-top-right-thick | |
{% else %} | |
mdi:help-circle | |
{% endif %} | |
{% else %} | |
mdi:help-circle | |
{% endif %} | |
attributes: | |
friendly_name: "Preisniveau nächste Stunde (Langzeit)" | |
description: "Long-term level of next hour's price (compared to last 7 days)." | |
description_de: "Langfristige Einordnung des nächsten Preises (7-Tage-Vergleich)." | |
valid_states: '["VERY_CHEAP", "CHEAP", "NORMAL", "EXPENSIVE", "VERY_EXPENSIVE"]' | |
friendly_state: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr("startsAt", "search", next_hour) | list | first %} | |
{% if entry.level == 'VERY_CHEAP' %} | |
Sehr günstig | |
{% elif entry.level == 'CHEAP' %} | |
Günstig | |
{% elif entry.level == 'NORMAL' %} | |
Normal | |
{% elif entry.level == 'EXPENSIVE' %} | |
Teuer | |
{% elif entry.level == 'VERY_EXPENSIVE' %} | |
Sehr teuer | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
begin_at: >- | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', next_hour) | list | first %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.startsAt) %} | |
{% for p in combined | reverse %} | |
{% if p.startsAt < next_hour %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.begin }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
end_at: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', next_hour) | list | first %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set ns = namespace(end=entry.startsAt) %} | |
{% for p in combined %} | |
{% if p.startsAt > next_hour %} | |
{% if p.level != level %} | |
{% set ns.end = p.startsAt %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.end }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
duration_h: >- | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', next_hour) | list | first %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.startsAt, end=entry.startsAt) %} | |
{% for p in combined | reverse %} | |
{% if p.startsAt < next_hour %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{% for p in combined %} | |
{% if p.startsAt > next_hour %} | |
{% if p.level != level %} | |
{% set ns.end = p.startsAt %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp(ns.end) - as_timestamp(ns.begin)) / 3600) | int }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
progress_h: >- | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today %} | |
{% if data_tomorrow is not none and data_tomorrow | length > 0 %} | |
{% set combined = combined + data_tomorrow %} | |
{% endif %} | |
{% if combined is not none and combined | length > 0 %} | |
{% set next_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = combined | selectattr('startsAt', 'search', next_hour) | list | first %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.startsAt) %} | |
{% for p in combined | reverse %} | |
{% if p.startsAt < next_hour %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p.startsAt %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp((now() + timedelta(hours=1))) - as_timestamp(ns.begin)) / 3600) | round(0, 'floor') }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
is_cheaper: > | |
unknown | |
is_more_expensive: > | |
unknown | |
- name: "Tibber Price Level Next Change" | |
unique_id: tibber_price_level_next_change | |
device_class: timestamp | |
icon: > | |
{% set order = { | |
'VERY_CHEAP': 0, | |
'CHEAP': 1, | |
'NORMAL': 2, | |
'EXPENSIVE': 3, | |
'VERY_EXPENSIVE': 4 | |
} %} | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today', []) %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow', []) %} | |
{% set combined = data_today + data_tomorrow if data_tomorrow else data_today %} | |
{% set current_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current = combined | selectattr('startsAt', 'search', current_hour) | list | first %} | |
{% if current is defined %} | |
{% set current_level = order.get(current.level) %} | |
{% for entry in combined %} | |
{% if entry.startsAt > current_hour and order.get(entry.level) != current_level %} | |
{% set next_level = order.get(entry.level) %} | |
{% if next_level > current_level %} | |
mdi:arrow-up-bold | |
{% elif next_level < current_level %} | |
mdi:arrow-down-bold | |
{% else %} | |
mdi:minus | |
{% endif %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% else %} | |
mdi:help-circle | |
{% endif %} | |
state: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today', []) %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow', []) %} | |
{% set combined = data_today + data_tomorrow if data_tomorrow else data_today %} | |
{% set current_time = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current_entry = combined | selectattr('startsAt', 'search', current_time) | list | first %} | |
{% if current_entry is defined %} | |
{% set current_level = current_entry.level %} | |
{% for entry in combined %} | |
{% if entry.startsAt > current_time and entry.level != current_level %} | |
{{ entry.startsAt }} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% else %} | |
unavailable | |
{% endif %} | |
attributes: | |
friendly_name: "Wechsel des Preisniveaus" | |
description: "Time when the next price level change occurs." | |
description_de: "Zeitpunkt des nächsten Wechsels im Preisniveau." | |
from_level: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today + data_tomorrow if data_tomorrow else data_today %} | |
{% set current_time = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current_entry = combined | selectattr('startsAt', 'search', current_time) | list | first %} | |
{{ current_entry.level if current_entry is defined else 'unknown' }} | |
to_level: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today') %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow') %} | |
{% set combined = data_today + data_tomorrow if data_tomorrow else data_today %} | |
{% set current_time = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current_entry = combined | selectattr('startsAt', 'search', current_time) | list | first %} | |
{% if current_entry is defined %} | |
{% set current_level = current_entry.level %} | |
{% for entry in combined %} | |
{% if entry.startsAt > current_time and entry.level != current_level %} | |
{{ entry.level }} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% else %} | |
unknown | |
{% endif %} | |
in_h: > | |
{% set data_today = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('today', []) %} | |
{% set data_tomorrow = (state_attr('sensor.tibber_price_data_priceinfo', 'priceInfo') or {}).get('tomorrow', []) %} | |
{% set combined = data_today + data_tomorrow if data_tomorrow else data_today %} | |
{% set current_time = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current_entry = combined | selectattr('startsAt', 'search', current_time) | list | first %} | |
{% if current_entry is defined %} | |
{% set current_level = current_entry.level %} | |
{% for entry in combined %} | |
{% if entry.startsAt > current_time and entry.level != current_level %} | |
{{ ((as_timestamp(entry.startsAt) - as_timestamp(now())) / 3600) | round(0, 'ceil') }} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% else %} | |
unavailable | |
{% endif %} | |
- name: "Tibber Price Rating Current Hour" | |
unique_id: tibber_price_rating_current_hour | |
state: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current = data | selectattr('time', 'search', now_hour) | list | first %} | |
{{ current.level if current is defined else 'unavailable' }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% if current is not defined %} | |
mdi:help-circle | |
{% elif current.level == 'LOW' %} | |
mdi:cash-check | |
{% elif current.level == 'NORMAL' %} | |
mdi:cash | |
{% elif current.level == 'HIGH' %} | |
mdi:cash-remove | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
attributes: | |
friendly_name: "Preisphase aktuell" | |
description: "Classifies the electricity price compared to other hours of the last 24 hours." | |
description_de: "Einordnung des Strompreises im Vergleich zu anderen Stunden der letzten 24 Stunden." | |
valid_states: '["LOW", "NORMAL", "HIGH"]' | |
friendly_state: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% if current is not defined %} | |
unavailable | |
{% elif current.level == 'LOW' %} | |
Niedrig | |
{% elif current.level == 'NORMAL' %} | |
Normal | |
{% elif current.level == 'HIGH' %} | |
Hoch | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
begin_at: >- | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(begin=entry_time.isoformat(), found=false) %} | |
{% for p in data | reverse %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time < entry_time %} | |
{% if p.level != level %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.begin if ns.found else now().replace(hour=0, minute=0, second=0, microsecond=0).isoformat() }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
end_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(end=entry_time.isoformat(), found=false) %} | |
{% for p in data %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time > entry_time %} | |
{% if p.level != level %} | |
{% set ns.end = p_time.isoformat() %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.end if ns.found else ( | |
(now() + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
if data_tomorrow is not none and data_tomorrow | length > 0 | |
else now().replace(hour=23, minute=59, second=59, microsecond=0).isoformat() | |
) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
duration_h: >- | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(begin=entry_time.isoformat(), end=entry_time.isoformat(), end_found=false) %} | |
{% for p in data | reverse %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time < entry_time %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time > entry_time %} | |
{% if p.level != level %} | |
{% set ns.end = p_time.isoformat() %} | |
{% set ns.end_found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp(ns.end if ns.end_found else ( | |
(now() + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
)) - as_timestamp(ns.begin)) / 3600) | int }} | |
{% else %} | |
unavailable | |
{% endif %} | |
progress_h: >- | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.time) %} | |
{% for p in data | reverse %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time < entry_time %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp(now()) - as_timestamp(ns.begin)) / 3600) | round(0, 'floor') }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
- name: "Tibber Price Rating Next Hour" | |
unique_id: tibber_price_rating_next_hour | |
state: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data | length > 0 %} | |
{% set now_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set current = data | selectattr('time', 'search', now_hour) | list | first %} | |
{{ current.level if current is defined else 'unavailable' }} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data | length > 0 %} | |
{% set now_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set current = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% if current is not defined %} | |
mdi:help-circle | |
{% elif current.level == 'LOW' %} | |
mdi:cash-check | |
{% elif current.level == 'NORMAL' %} | |
mdi:cash | |
{% elif current.level == 'HIGH' %} | |
mdi:cash-remove | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
attributes: | |
friendly_name: "Preisphase nächste Stunde" | |
description: "Classifies the electricity price compared to other hours of the last 24 hours." | |
description_de: "Einordnung des Strompreises im Vergleich zu anderen Stunden der letzten 24 Stunden." | |
valid_states: '["LOW", "NORMAL", "HIGH"]' | |
friendly_state: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data | length > 0 %} | |
{% set now_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set current = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% if current is not defined %} | |
unavailable | |
{% elif current.level == 'LOW' %} | |
Niedrig | |
{% elif current.level == 'NORMAL' %} | |
Normal | |
{% elif current.level == 'HIGH' %} | |
Hoch | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
begin_at: >- | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(begin=entry_time.isoformat(), found=false) %} | |
{% for p in data | reverse %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time < entry_time %} | |
{% if p.level != level %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.begin if ns.found else (now() + timedelta(hours=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
end_at: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(end=entry_time.isoformat(), found=false) %} | |
{% for p in data %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time > entry_time %} | |
{% if p.level != level %} | |
{% set ns.end = p_time.isoformat() %} | |
{% set ns.found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ns.end if ns.found else ( | |
(now() + timedelta(days=2)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
if data_tomorrow is not none and data_tomorrow | length > 0 | |
else (now() + timedelta(hours=1)).replace(hour=23, minute=59, second=59, microsecond=0).isoformat() | |
) }} | |
{% else %} | |
unavailable | |
{% endif %} | |
duration_h: >- | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% set level = entry.level %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% set ns = namespace(begin=entry_time.isoformat(), end=entry_time.isoformat(), end_found=false) %} | |
{% for p in data | reverse %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time < entry_time %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{% for p in data %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time > entry_time %} | |
{% if p.level != level %} | |
{% set ns.end = p_time.isoformat() %} | |
{% set ns.end_found = true %} | |
{% break %} | |
{% else %} | |
{% set ns.end = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp(ns.end if ns.end_found else ( | |
((now() + timedelta(hours=1)) + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | |
)) - as_timestamp(ns.begin)) / 3600) | int }} | |
{% else %} | |
unavailable | |
{% endif %} | |
progress_h: >- | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data is not none and data | length > 0 %} | |
{% set now_hour = (now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:00') %} | |
{% set entry = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% set entry_time = as_datetime(entry.time) %} | |
{% if entry is defined %} | |
{% set level = entry.level %} | |
{% set ns = namespace(begin=entry.time) %} | |
{% for p in data | reverse %} | |
{% set p_time = as_datetime(p.time) %} | |
{% if p_time < entry_time %} | |
{% if p.level != level %} | |
{% break %} | |
{% else %} | |
{% set ns.begin = p_time.isoformat() %} | |
{% endif %} | |
{% endif %} | |
{% endfor %} | |
{{ ((as_timestamp((now() + timedelta(hours=1))) - as_timestamp(ns.begin)) / 3600) | round(0, 'floor') }} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
- name: "Tibber Price Rating Next Change" | |
unique_id: tibber_price_rating_next_change | |
device_class: timestamp | |
state: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% if current is defined %} | |
{% set current_level = current.level %} | |
{% for entry in data %} | |
{% if entry.time > now_hour and entry.level != current_level %} | |
{{ entry.time }} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} | |
icon: > | |
{% set order = {'LOW': 0, 'NORMAL': 1, 'HIGH': 2} %} | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% if current is defined %} | |
{% set current_level = order.get(current.level) %} | |
{% for entry in data %} | |
{% if entry.time > now_hour and order.get(entry.level) != current_level %} | |
{% set next_level = order.get(entry.level) %} | |
{% if next_level > current_level %} | |
mdi:arrow-up-bold | |
{% elif next_level < current_level %} | |
mdi:arrow-down-bold | |
{% else %} | |
mdi:minus | |
{% endif %} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% else %} | |
mdi:help-circle | |
{% endif %} | |
{% else %} | |
mdi:help-circle | |
{% endif %} | |
attributes: | |
friendly_name: "Nächste Preisphase" | |
description: "Time when the next price phase of the day begins." | |
description_de: "Zeitpunkt der nächsten Preisphase des Tages." | |
from_rating: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current = data | selectattr('time', 'search', now_hour) | list | first %} | |
{{ current.level if current is defined else 'unknown' }} | |
{% else %} | |
unknown | |
{% endif %} | |
to_rating: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% if current is defined %} | |
{% set current_level = current.level %} | |
{% for entry in data %} | |
{% if entry.time > now_hour and entry.level != current_level %} | |
{{ entry.level }} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% else %} | |
unknown | |
{% endif %} | |
{% else %} | |
unknown | |
{% endif %} | |
in_h: > | |
{% set data = (state_attr('sensor.tibber_price_data_pricerating_hourly', 'priceRating') or {}).get('hourly', {}).get('entries', []) %} | |
{% if data | length > 0 %} | |
{% set now_hour = now().strftime('%Y-%m-%dT%H:00') %} | |
{% set current = data | selectattr('time', 'search', now_hour) | list | first %} | |
{% if current is defined %} | |
{% set current_level = current.level %} | |
{% for entry in data %} | |
{% if entry.time > now_hour and entry.level != current_level %} | |
{{ ((as_timestamp(entry.time) - as_timestamp(now())) / 3600) | round(0, 'ceil') }} | |
{% break %} | |
{% endif %} | |
{% endfor %} | |
{% else %} | |
unavailable | |
{% endif %} | |
{% else %} | |
unavailable | |
{% endif %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
views: | |
- type: sections | |
max_columns: 3 | |
title: Dynamischer Stromtarif | |
path: strompreis | |
icon: mdi:transmission-tower | |
subview: true | |
sections: | |
- type: grid | |
cards: | |
- type: heading | |
icon: mdi:numeric-1-circle-outline | |
heading: Aktuelle Stunde | |
heading_style: title | |
badges: | |
- type: entity | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_price_current_hour | |
icon: '' | |
color: amber | |
tap_action: | |
action: more-info | |
- type: entity | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_price_rating_current_hour | |
state_content: friendly_state | |
tap_action: | |
action: more-info | |
- type: entity | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_price_level_current_hour | |
tap_action: | |
action: more-info | |
state_content: friendly_state | |
- graph: none | |
type: sensor | |
entity: sensor.tibber_geschatzte_kosten_aktuelle_stunde | |
detail: 1 | |
name: Kosten geschätzt | |
icon: mdi:currency-eur | |
hours_to_show: 1 | |
- graph: line | |
type: sensor | |
entity: sensor.tibber_geschatzter_verbrauch_aktuelle_stunde | |
detail: 2 | |
name: Verbrauch geschätzt | |
hours_to_show: 1 | |
- type: heading | |
icon: mdi:numeric-2-circle-outline | |
heading: Nächste Stunde | |
heading_style: subtitle | |
badges: | |
- type: entity | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_price_next_hour | |
color: amber | |
tap_action: | |
action: more-info | |
icon: '' | |
- type: entity | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_price_rating_next_hour | |
tap_action: | |
action: more-info | |
state_content: friendly_state | |
- type: entity | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_price_level_next_hour | |
tap_action: | |
action: more-info | |
state_content: friendly_state | |
- type: glance | |
entities: | |
- entity: sensor.tibber_price_rating_next_change | |
- entity: sensor.tibber_price_level_next_change | |
- chart_type: line | |
period: 5minute | |
type: statistics-graph | |
entities: | |
- sensor.tibber_leistung | |
- sensor.solarflow_output_home_power | |
stat_types: | |
- mean | |
- max | |
- min | |
days_to_show: 0.4 | |
hide_legend: false | |
logarithmic_scale: false | |
title: Leistungskurve | |
- type: grid | |
cards: | |
- type: heading | |
icon: mdi:hours-24 | |
heading: Tagesübersicht | |
heading_style: title | |
badges: | |
- type: entity | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_price_today | |
tap_action: | |
action: more-info | |
icon: '' | |
color: amber | |
- type: entity | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_price_rating_today | |
state_content: friendly_state | |
tap_action: | |
action: more-info | |
- type: entity | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_price_level_today | |
state_content: friendly_state | |
tap_action: | |
action: more-info | |
- graph: none | |
type: sensor | |
entity: sensor.tibber_kumulierte_kosten | |
detail: 1 | |
name: Kosten | |
icon: mdi:currency-eur | |
- graph: line | |
type: sensor | |
entity: sensor.tibber_kumulierter_verbrauch | |
detail: 2 | |
name: Verbrauch | |
hours_to_show: 24 | |
- type: tile | |
entity: sensor.tibber_price_minimum_today | |
features_position: bottom | |
vertical: false | |
name: Günstigste Stunde | |
state_content: start_at | |
color: green | |
- type: tile | |
entity: sensor.tibber_price_maximum_today | |
features_position: bottom | |
vertical: false | |
name: Teuerste Stunde | |
state_content: start_at | |
color: red | |
- type: custom:apexcharts-card | |
update_interval: 5m | |
span: | |
start: day | |
header: | |
show: true | |
title: Preisphasen Tagesverlauf | |
show_states: false | |
apex_config: | |
stroke: | |
curve: stepline | |
fill: | |
opacity: 0.4 | |
tooltip: | |
x: | |
format: HH:mm | |
legend: | |
show: true | |
yaxis: | |
- id: price | |
decimals: 0 | |
min: 10 | |
- id: pv | |
decimals: 0 | |
min: 0 | |
max: 800 | |
opposite: true | |
apex_config: | |
labels: | |
style: | |
colors: '#0077ff' | |
now: | |
show: true | |
color: '#8e24aa' | |
label: 🕒 LIVE | |
all_series_config: | |
stroke_width: 1 | |
show: | |
legend_value: false | |
series: | |
- entity: sensor.tibber_price_data_pricerating_hourly | |
name: Niedrig | |
type: area | |
color: '#2ecc71' | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: > | |
const entries = entity.attributes.priceRating?.hourly?.entries | |
|| []; | |
const today = new Date(); | |
today.setHours(0, 0, 0, 0); | |
const tomorrow = new Date(today.getTime() + 86400000); | |
const points = []; | |
for (let i = 0; i < entries.length - 1; i++) { | |
const p = entries[i]; | |
const next = entries[i + 1]; | |
const pDate = new Date(p.time); | |
pDate.setHours(0, 0, 0, 0); | |
const nextDate = new Date(next.time); | |
nextDate.setHours(0, 0, 0, 0); | |
const ts = new Date(p.time).getTime(); | |
const tsNext = new Date(next.time).getTime(); | |
const isTodayOrTomorrow = ( | |
pDate.getTime() === today.getTime() | |
); | |
const isNextValid = ( | |
nextDate.getTime() === today.getTime() | |
); | |
if (isTodayOrTomorrow && p.level === 'LOW') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'LOW' && isNextValid) { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
return points; | |
- entity: sensor.tibber_price_data_pricerating_hourly | |
name: Normal | |
type: area | |
color: '#f1c40f' | |
yaxis_id: price | |
show: | |
extremas: false | |
data_generator: > | |
const entries = entity.attributes.priceRating?.hourly?.entries | |
|| []; | |
const today = new Date(); | |
today.setHours(0, 0, 0, 0); | |
const tomorrow = new Date(today.getTime() + 86400000); | |
const points = []; | |
for (let i = 0; i < entries.length - 1; i++) { | |
const p = entries[i]; | |
const next = entries[i + 1]; | |
const pDate = new Date(p.time); | |
pDate.setHours(0, 0, 0, 0); | |
const nextDate = new Date(next.time); | |
nextDate.setHours(0, 0, 0, 0); | |
const ts = new Date(p.time).getTime(); | |
const tsNext = new Date(next.time).getTime(); | |
const isTodayOrTomorrow = ( | |
pDate.getTime() === today.getTime() || pDate.getTime() === tomorrow.getTime() | |
); | |
const isNextValid = ( | |
nextDate.getTime() === today.getTime() | |
); | |
if (isTodayOrTomorrow && p.level === 'NORMAL') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'NORMAL' && isNextValid) { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
return points; | |
- entity: sensor.tibber_price_data_pricerating_hourly | |
name: Hoch | |
type: area | |
color: '#e74c3c' | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: > | |
const entries = entity.attributes.priceRating?.hourly?.entries | |
|| []; | |
const today = new Date(); | |
today.setHours(0, 0, 0, 0); | |
const tomorrow = new Date(today.getTime() + 86400000); | |
const points = []; | |
for (let i = 0; i < entries.length - 1; i++) { | |
const p = entries[i]; | |
const next = entries[i + 1]; | |
const pDate = new Date(p.time); | |
pDate.setHours(0, 0, 0, 0); | |
const nextDate = new Date(next.time); | |
nextDate.setHours(0, 0, 0, 0); | |
const ts = new Date(p.time).getTime(); | |
const tsNext = new Date(next.time).getTime(); | |
const isTodayOrTomorrow = ( | |
pDate.getTime() === today.getTime() | |
); | |
const isNextValid = ( | |
nextDate.getTime() === today.getTime() || nextDate.getTime() === tomorrow.getTime() | |
); | |
if (isTodayOrTomorrow && p.level === 'HIGH') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'HIGH' && isNextValid) { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
return points; | |
- entity: sensor.durchschnitt_pv_leistung_10min | |
name: PV-Produktion | |
yaxis_id: pv | |
type: line | |
color: '#0077ff' | |
stroke_width: 2 | |
- entity: sensor.energy_production_today | |
name: PV-Prognose | |
type: line | |
stroke_width: 1 | |
stroke_dash: 5.5 | |
curve: smooth | |
yaxis_id: pv | |
color: '#0077ff' | |
data_generator: | | |
const result = []; | |
const now = new Date(); | |
const data = entity.attributes.watts; | |
for (const [timestamp, value] of Object.entries(data)) { | |
const ts = new Date(timestamp); | |
if (ts >= now) { | |
result.push([ts, value]); | |
} | |
} | |
return result; | |
- type: custom:apexcharts-card | |
update_interval: 5m | |
span: | |
start: day | |
header: | |
show: true | |
title: Preisniveau | |
show_states: false | |
apex_config: | |
stroke: | |
curve: stepline | |
fill: | |
opacity: 0.4 | |
tooltip: | |
x: | |
format: HH:mm | |
legend: | |
show: true | |
yaxis: | |
- id: price | |
decimals: 0 | |
min: 10 | |
now: | |
show: true | |
color: '#8e24aa' | |
label: 🕒 LIVE | |
all_series_config: | |
stroke_width: 1 | |
show: | |
legend_value: false | |
series: | |
- entity: sensor.tibber_price_data_priceinfo | |
name: Sehr günstig | |
type: area | |
color: '#27ae60' | |
unit: ct/kWh | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: > | |
const data = [...(entity.attributes.priceInfo?.today || []), | |
...(entity.attributes.priceInfo?.tomorrow || [])]; | |
const points = []; | |
for (let i = 0; i < data.length - 1; i++) { | |
const p = data[i], next = data[i + 1]; | |
const ts = new Date(p.startsAt).getTime(), tsNext = new Date(next.startsAt).getTime(); | |
if (p.level === 'VERY_CHEAP') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'VERY_CHEAP') { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
const last = data[data.length - 1]; | |
if (last?.level === 'VERY_CHEAP') { | |
const ts = new Date(last.startsAt).getTime(); | |
points.push([ts, last.total * 100]); | |
points.push([ts + 3600000, last.total * 100]); | |
} | |
return points; | |
- entity: sensor.tibber_price_data_priceinfo | |
name: Günstig | |
type: area | |
color: '#2ecc71' | |
unit: ct/kWh | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: > | |
const data = [...(entity.attributes.priceInfo?.today || []), | |
...(entity.attributes.priceInfo?.tomorrow || [])]; | |
const points = []; | |
for (let i = 0; i < data.length - 1; i++) { | |
const p = data[i], next = data[i + 1]; | |
const ts = new Date(p.startsAt).getTime(), tsNext = new Date(next.startsAt).getTime(); | |
if (p.level === 'CHEAP') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'CHEAP') { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
const last = data[data.length - 1]; | |
if (last?.level === 'CHEAP') { | |
const ts = new Date(last.startsAt).getTime(); | |
points.push([ts, last.total * 100]); | |
points.push([ts + 3600000, last.total * 100]); | |
} | |
return points; | |
- entity: sensor.tibber_price_data_priceinfo | |
name: Normal | |
type: area | |
color: '#f1c40f' | |
unit: ct/kWh | |
yaxis_id: price | |
show: | |
extremas: false | |
data_generator: > | |
const data = [...(entity.attributes.priceInfo?.today || []), | |
...(entity.attributes.priceInfo?.tomorrow || [])]; | |
const points = []; | |
for (let i = 0; i < data.length - 1; i++) { | |
const p = data[i], next = data[i + 1]; | |
const ts = new Date(p.startsAt).getTime(), tsNext = new Date(next.startsAt).getTime(); | |
if (p.level === 'NORMAL') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'NORMAL') { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
const last = data[data.length - 1]; | |
if (last?.level === 'NORMAL') { | |
const ts = new Date(last.startsAt).getTime(); | |
points.push([ts, last.total * 100]); | |
points.push([ts + 3600000, last.total * 100]); | |
} | |
return points; | |
- entity: sensor.tibber_price_data_priceinfo | |
name: Teuer | |
type: area | |
color: '#e67e22' | |
unit: ct/kWh | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: > | |
const data = [...(entity.attributes.priceInfo?.today || []), | |
...(entity.attributes.priceInfo?.tomorrow || [])]; | |
const points = []; | |
for (let i = 0; i < data.length - 1; i++) { | |
const p = data[i], next = data[i + 1]; | |
const ts = new Date(p.startsAt).getTime(), tsNext = new Date(next.startsAt).getTime(); | |
if (p.level === 'EXPENSIVE') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'EXPENSIVE') { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
const last = data[data.length - 1]; | |
if (last?.level === 'EXPENSIVE') { | |
const ts = new Date(last.startsAt).getTime(); | |
points.push([ts, last.total * 100]); | |
points.push([ts + 3600000, last.total * 100]); | |
} | |
return points; | |
- entity: sensor.tibber_price_data_priceinfo | |
name: Sehr teuer | |
type: area | |
color: '#e74c3c' | |
unit: ct/kWh | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: > | |
const data = [...(entity.attributes.priceInfo?.today || []), | |
...(entity.attributes.priceInfo?.tomorrow || [])]; | |
const points = []; | |
for (let i = 0; i < data.length - 1; i++) { | |
const p = data[i], next = data[i + 1]; | |
const ts = new Date(p.startsAt).getTime(), tsNext = new Date(next.startsAt).getTime(); | |
if (p.level === 'VERY_EXPENSIVE') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'VERY_EXPENSIVE') { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
const last = data[data.length - 1]; | |
if (last?.level === 'VERY_EXPENSIVE') { | |
const ts = new Date(last.startsAt).getTime(); | |
points.push([ts, last.total * 100]); | |
points.push([ts + 3600000, last.total * 100]); | |
} | |
return points; | |
- type: grid | |
cards: | |
- type: heading | |
icon: mdi:calendar-plus | |
heading: Vorschau auf morgen | |
heading_style: title | |
badges: [] | |
visibility: | |
- condition: state | |
entity: binary_sensor.tibber_price_tomorrow_available | |
state: 'on' | |
- type: tile | |
entity: sensor.tibber_price_tomorrow | |
features_position: bottom | |
vertical: true | |
grid_options: | |
rows: 1 | |
columns: full | |
hide_state: false | |
name: Preis Ø | |
color: amber | |
- type: tile | |
entity: sensor.tibber_price_minimum_tomorrow | |
features_position: bottom | |
vertical: false | |
name: Günstigste Stunde | |
state_content: start_at | |
color: green | |
- type: tile | |
entity: sensor.tibber_price_maximum_tomorrow | |
features_position: bottom | |
vertical: false | |
name: Teuerste Stunde | |
state_content: start_at | |
color: red | |
- type: custom:apexcharts-card | |
update_interval: 5m | |
graph_span: 24h | |
span: | |
start: day | |
offset: +1d | |
header: | |
show: true | |
title: Preisphasen Tagesverlauf | |
show_states: false | |
apex_config: | |
stroke: | |
curve: stepline | |
fill: | |
opacity: 0.4 | |
tooltip: | |
x: | |
format: HH:mm | |
legend: | |
show: true | |
yaxis: | |
- id: price | |
decimals: 0 | |
min: 10 | |
- id: pv | |
decimals: 0 | |
min: 0 | |
max: 800 | |
opposite: true | |
apex_config: | |
labels: | |
style: | |
colors: '#0077ff' | |
all_series_config: | |
stroke_width: 1 | |
show: | |
legend_value: false | |
series: | |
- entity: sensor.tibber_price_data_pricerating_hourly | |
name: Niedrig | |
type: area | |
color: '#2ecc71' | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: > | |
const entries = entity.attributes.priceRating?.hourly?.entries | |
|| []; | |
const today = new Date(); | |
today.setHours(0, 0, 0, 0); | |
const tomorrow = new Date(today.getTime() + 86400000); | |
const points = []; | |
for (let i = 0; i < entries.length - 1; i++) { | |
const p = entries[i]; | |
const next = entries[i + 1]; | |
const pDate = new Date(p.time); | |
pDate.setHours(0, 0, 0, 0); | |
const nextDate = new Date(next.time); | |
nextDate.setHours(0, 0, 0, 0); | |
const ts = new Date(p.time).getTime(); | |
const tsNext = new Date(next.time).getTime(); | |
const isTodayOrTomorrow = ( | |
pDate.getTime() === tomorrow.getTime() | |
); | |
const isNextValid = ( | |
nextDate.getTime() === tomorrow.getTime() | |
); | |
if (isTodayOrTomorrow && p.level === 'LOW') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'LOW' && isNextValid) { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
return points; | |
- entity: sensor.tibber_price_data_pricerating_hourly | |
name: Normal | |
type: area | |
color: '#f1c40f' | |
yaxis_id: price | |
show: | |
extremas: false | |
data_generator: > | |
const entries = entity.attributes.priceRating?.hourly?.entries | |
|| []; | |
const today = new Date(); | |
today.setHours(0, 0, 0, 0); | |
const tomorrow = new Date(today.getTime() + 86400000); | |
const points = []; | |
for (let i = 0; i < entries.length - 1; i++) { | |
const p = entries[i]; | |
const next = entries[i + 1]; | |
const pDate = new Date(p.time); | |
pDate.setHours(0, 0, 0, 0); | |
const nextDate = new Date(next.time); | |
nextDate.setHours(0, 0, 0, 0); | |
const ts = new Date(p.time).getTime(); | |
const tsNext = new Date(next.time).getTime(); | |
const isTodayOrTomorrow = ( | |
pDate.getTime() === tomorrow.getTime() | |
); | |
const isNextValid = ( | |
nextDate.getTime() === tomorrow.getTime() | |
); | |
if (isTodayOrTomorrow && p.level === 'NORMAL') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'NORMAL' && isNextValid) { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
return points; | |
- entity: sensor.tibber_price_data_pricerating_hourly | |
name: Hoch | |
type: area | |
color: '#e74c3c' | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: > | |
const entries = entity.attributes.priceRating?.hourly?.entries | |
|| []; | |
const today = new Date(); | |
today.setHours(0, 0, 0, 0); | |
const tomorrow = new Date(today.getTime() + 86400000); | |
const points = []; | |
for (let i = 0; i < entries.length - 1; i++) { | |
const p = entries[i]; | |
const next = entries[i + 1]; | |
const pDate = new Date(p.time); | |
pDate.setHours(0, 0, 0, 0); | |
const nextDate = new Date(next.time); | |
nextDate.setHours(0, 0, 0, 0); | |
const ts = new Date(p.time).getTime(); | |
const tsNext = new Date(next.time).getTime(); | |
const isTodayOrTomorrow = ( | |
pDate.getTime() === tomorrow.getTime() | |
); | |
const isNextValid = ( | |
nextDate.getTime() === today.getTime() || nextDate.getTime() === tomorrow.getTime() | |
); | |
if (isTodayOrTomorrow && p.level === 'HIGH') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'HIGH' && isNextValid) { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
return points; | |
- entity: sensor.energy_production_tomorrow | |
name: PV-Prognose | |
type: line | |
stroke_width: 2 | |
stroke_dash: 5.5 | |
curve: smooth | |
yaxis_id: pv | |
color: '#0077ff' | |
data_generator: > | |
return Object.entries(entity.attributes.watts).map(([ts, val]) | |
=> [ts, val]); | |
- type: custom:apexcharts-card | |
update_interval: 5m | |
graph_span: 24h | |
span: | |
start: day | |
offset: +1d | |
header: | |
show: true | |
title: Preisniveau | |
show_states: false | |
apex_config: | |
stroke: | |
curve: stepline | |
fill: | |
opacity: 0.4 | |
tooltip: | |
x: | |
format: HH:mm | |
legend: | |
show: true | |
yaxis: | |
- id: price | |
decimals: 0 | |
min: 10 | |
all_series_config: | |
stroke_width: 1 | |
show: | |
legend_value: false | |
series: | |
- entity: sensor.tibber_price_data_priceinfo | |
name: Sehr günstig | |
type: area | |
color: '#27ae60' | |
unit: ct/kWh | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: > | |
const data = entity.attributes.priceInfo?.tomorrow; const | |
points = []; | |
for (let i = 0; i < data.length - 1; i++) { | |
const p = data[i], next = data[i + 1]; | |
const ts = new Date(p.startsAt).getTime(), tsNext = new Date(next.startsAt).getTime(); | |
if (p.level === 'VERY_CHEAP') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'VERY_CHEAP') { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
const last = data[data.length - 1]; | |
if (last?.level === 'VERY_CHEAP') { | |
const ts = new Date(last.startsAt).getTime(); | |
points.push([ts, last.total * 100]); | |
points.push([ts + 3600000, last.total * 100]); | |
} | |
return points; | |
- entity: sensor.tibber_price_data_priceinfo | |
name: Günstig | |
type: area | |
color: '#2ecc71' | |
unit: ct/kWh | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: | | |
const data = entity.attributes.priceInfo?.tomorrow; | |
const points = []; | |
for (let i = 0; i < data.length - 1; i++) { | |
const p = data[i], next = data[i + 1]; | |
const ts = new Date(p.startsAt).getTime(), tsNext = new Date(next.startsAt).getTime(); | |
if (p.level === 'CHEAP') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'CHEAP') { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
const last = data[data.length - 1]; | |
if (last?.level === 'CHEAP') { | |
const ts = new Date(last.startsAt).getTime(); | |
points.push([ts, last.total * 100]); | |
points.push([ts + 3600000, last.total * 100]); | |
} | |
return points; | |
- entity: sensor.tibber_price_data_priceinfo | |
name: Normal | |
type: area | |
color: '#f1c40f' | |
unit: ct/kWh | |
yaxis_id: price | |
show: | |
extremas: false | |
data_generator: | | |
const data = entity.attributes.priceInfo?.tomorrow; | |
const points = []; | |
for (let i = 0; i < data.length - 1; i++) { | |
const p = data[i], next = data[i + 1]; | |
const ts = new Date(p.startsAt).getTime(), tsNext = new Date(next.startsAt).getTime(); | |
if (p.level === 'NORMAL') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'NORMAL') { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
const last = data[data.length - 1]; | |
if (last?.level === 'NORMAL') { | |
const ts = new Date(last.startsAt).getTime(); | |
points.push([ts, last.total * 100]); | |
points.push([ts + 3600000, last.total * 100]); | |
} | |
return points; | |
- entity: sensor.tibber_price_data_priceinfo | |
name: Teuer | |
type: area | |
color: '#e67e22' | |
unit: ct/kWh | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: > | |
const data = entity.attributes.priceInfo?.tomorrow; const | |
points = []; | |
for (let i = 0; i < data.length - 1; i++) { | |
const p = data[i], next = data[i + 1]; | |
const ts = new Date(p.startsAt).getTime(), tsNext = new Date(next.startsAt).getTime(); | |
if (p.level === 'EXPENSIVE') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'EXPENSIVE') { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
const last = data[data.length - 1]; | |
if (last?.level === 'EXPENSIVE') { | |
const ts = new Date(last.startsAt).getTime(); | |
points.push([ts, last.total * 100]); | |
points.push([ts + 3600000, last.total * 100]); | |
} | |
return points; | |
- entity: sensor.tibber_price_data_priceinfo | |
name: Sehr teuer | |
type: area | |
color: '#e74c3c' | |
unit: ct/kWh | |
yaxis_id: price | |
show: | |
extremas: true | |
data_generator: | | |
const data = entity.attributes.priceInfo?.tomorrow; | |
const points = []; | |
for (let i = 0; i < data.length - 1; i++) { | |
const p = data[i], next = data[i + 1]; | |
const ts = new Date(p.startsAt).getTime(), tsNext = new Date(next.startsAt).getTime(); | |
if (p.level === 'VERY_EXPENSIVE') { | |
points.push([ts, p.total * 100]); | |
if (next.level === 'VERY_EXPENSIVE') { | |
points.push([tsNext, next.total * 100]); | |
} else { | |
points.push([tsNext, p.total * 100]); | |
points.push([tsNext, null]); | |
} | |
} | |
} | |
const last = data[data.length - 1]; | |
if (last?.level === 'VERY_EXPENSIVE') { | |
const ts = new Date(last.startsAt).getTime(); | |
points.push([ts, last.total * 100]); | |
points.push([ts + 3600000, last.total * 100]); | |
} | |
return points; | |
visibility: | |
- condition: state | |
entity: binary_sensor.tibber_price_tomorrow_available | |
state: 'on' | |
- type: grid | |
cards: | |
- type: heading | |
icon: mdi:calendar-month | |
heading: Monatsübersicht | |
heading_style: title | |
- graph: none | |
type: sensor | |
entity: sensor.tibber_monatliche_kosten | |
detail: 1 | |
name: Kosten | |
icon: mdi:currency-eur | |
hours_to_show: 168 | |
- graph: line | |
type: sensor | |
entity: sensor.tibber_monatlicher_nettoverbrauch | |
detail: 1 | |
name: Verbrauch | |
hours_to_show: 168 | |
- chart_type: line | |
period: hour | |
type: statistics-graph | |
entities: | |
- sensor.tibber_leistung | |
- sensor.solarflow_output_home_power | |
stat_types: | |
- max | |
- mean | |
- min | |
days_to_show: 30 | |
hide_legend: false | |
logarithmic_scale: false | |
title: Leistungskurve | |
grid_options: | |
columns: full | |
rows: auto | |
column_span: 3 | |
header: | |
card: | |
type: markdown | |
text_only: true | |
content: >- | |
# Dynamischer Stromtarif | |
<u>Hinweis:</u> Kosten enthalten keine Grundgebühren, siehe [Tibber | |
FAQ](https://support.tibber.com/de/articles/4895076-strompreise-in-der-tibber-app). | |
badges: | |
- type: entity | |
show_name: true | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_leistung | |
name: Netzbezug | |
visibility: | |
- condition: or | |
conditions: | |
- condition: numeric_state | |
entity: sensor.tibber_leistung | |
above: 0 | |
- condition: numeric_state | |
entity: sensor.tibber_stromerzeugung | |
below: 1 | |
icon: mdi:transmission-tower-export | |
color: red | |
- type: entity | |
show_name: true | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_einspeiseleistung | |
name: Einspeisung | |
visibility: | |
- condition: numeric_state | |
entity: sensor.tibber_einspeiseleistung | |
above: 0 | |
icon: mdi:transmission-tower-import | |
color: green | |
- type: entity | |
show_name: true | |
show_state: true | |
show_icon: true | |
entity: sensor.tibber_zahlerstand_verbrauch | |
name: Zählerstand | |
icon: mdi:counter | |
color: deep-orange | |
cards: [] | |
top_margin: false |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment