edwardtfn / HomeAssistant-Config

Edward Firmo's Home Assistant config files
MIT License
22 stars 7 forks source link

Colored graph for tibber #1

Open 6thGuest opened 1 year ago

6thGuest commented 1 year ago

Hey Edward, since I am totally unexperienced in programming I had to use "copy-paste" and "try-and-error" for integrating the tibber price graphs into HA. I only used your rest-sensor:

sensor:
    # https://community.home-assistant.io/t/tibber-sensor-for-future-price-tomorrow/253818/23
    - platform: rest
      name: Tibber Electricity Price
      resource: https://api.tibber.com/v1-beta/gql
      method: POST
      scan_interval: 900
      payload: '{ "query": "{ viewer { homes { currentSubscription { priceInfo { current { total currency level } today { total startsAt level } tomorrow { total startsAt level }}}}}}" }'
      json_attributes_path: "$.data.viewer.homes[0].currentSubscription.priceInfo"
      json_attributes:
        - current
        - today
        - tomorrow
      value_template: '{{ value_json["data"]["viewer"]["homes"][0]["currentSubscription"]["priceInfo"]["current"]["total"] }}'
      headers:
        Authorization: !secret tibber_api_token
        Content-Type: application/json
        User-Agent: REST

It also states "Cheap, normal and expensive".

tibber_prices

Now I wonder if it is possible to create a line or bar chart with the different colors for it. E.g. cheap=green, normal=yellow and expensive=red and/or a sensor which states the times with the cheapest prices maybe like this: "Cheapest today: 01:00:00 - 02:00:00 15:00:00 - 16:00:00" "Cheapest tomorrow: 07:00:00 - 08:00:00"

Do you think it is possible to create something like this?

THANKS so far!

edwardtfn commented 1 year ago

Now I wonder if it is possible to create a line or bar chart with the different colors for it. E.g. cheap=green, normal=yellow and expensive=red

I don't have control on the colors on that screen where you are, but we probably can have a card on your dashboard with the right colors.

or a sensor which states the times with the cheapest prices maybe like this: "Cheapest today: 01:00:00 - 02:00:00 15:00:00 - 16:00:00" "Cheapest tomorrow: 07:00:00 - 08:00:00" Do you think it is possible to create something like this?

This is pretty easy. I already have a "min" attribute a other sensor giving the lowest value available (which includes today and tomorrow) and another one called "future_min", which is kind of the same, but excluding the past hours of today. I will work on this later and share this sensor with you. How are you planning to display this info?

6thGuest commented 1 year ago

I don't have control on the colors on that screen where you are, but we probably can have a card on your dashboard with the right colors.

Well I was thinking about a seperate card (maybe Apexcharts) with only the colored bars for the prices. Something like this - but green, yellow, red for cheap, normal, expensive.

bars

How are you planning to display this info?

Maybe on a "text-card" like "Cheapest periods today: 09:00-10:00 ; 15:00-17:00" "Cheapest periods tomorrow: xxxxxxxxxxxx"

edwardtfn commented 1 year ago

Sorry, @6thGuest, I've been quite busy those days and couldn't work at this chart yet, but I was playing a bit with a table card that might help you in the meantime: image

This is the card I've used for this: https://github.com/edwardtfn/HomeAssistant-Config/blob/main/lovelace/library/cards/card_electricity_price_table.yaml

6thGuest commented 1 year ago

Hey Edward, that looks great - THANKS!!! I adapted that to German language > works well :-) Can you tell me, how to start the table at the current hour (or maybe 1 hour back)?

edwardtfn commented 1 year ago

This card is using the attribute all_prices from that template sensor. The same sensor have another attribute called future which is the same, but without past data. So, if you are using that same sensor, you can replace in the card all instances of all_prices by future and it should work.

6thGuest commented 1 year ago

Perfect. future_prices works perfectly.

6thGuest commented 1 year ago

Hey Edward, I managed to get some more information from tibber (energy-price and taxes) - not only the sum of it. In the flex-table-card you have defined the variables to change the background-color corresponding to the price-level.

Now I tried to define a variable which changes the text-color (of only the energy-price cell) corresponding to the energy-price. Means: If energy-price is <=0 then color... and if it is >0 then color...

But now I fail to define the comparison to "0".

Can you help once more?

Tibber-Flex-Table

edwardtfn commented 1 year ago

Could you please share the yaml of your card as it is now?

edwardtfn commented 1 year ago

If energy-price is <=0 then color... and if it is >0 then color.

Try this:

( energy-price <= 0 ? 'red' : 'green')

6thGuest commented 1 year ago

This is like it is now:

type: custom:flex-table-card
title: Strompreis-Level
entities:
  include: sensor.electricity_price
sort:
  - future_prices+
columns:
  - name: Sorting columns
    data: future_prices
    modify: x.startsAt
    hidden: true
  - name: beginnt
    data: future_prices
    modify: |
      {
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        var startsAt = new Date(x.startsAt)
        var d = new Date()
        var sday = ""
        if (startsAt.getDay() == d.getDay())
          sday = "heute"
        else
          sday = "morgen";
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + sday + " ab " + startsAt.getHours().toLocaleString('en-US', {minimumIntegerDigits: 1}) + " Uhr" +
        '</div>'
      }
    align: center
  - name: Level
    data: future_prices
    modify: |
      { 
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + x.level.replace('CHEAP', 'günstig').replace('NORMAL', 'normal').replace('EXPENSIVE', 'teuer').replace('VERY_', 'sehr ') + 
          '</div>'
      }
    align: center
  - name: Strompreis
    data: future_prices
    modify: |
      { 
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + (x.energy * 100).toFixed(2) + 
        '&nbsp;Ct/kWh</div>'
      }
    align: center
  - name: Steuer/Gebühr
    data: future_prices
    modify: |
      { 
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + (x.tax * 100).toFixed(2) + 
        '&nbsp;Ct/kWh</div>'
      }
    align: center
  - name: Gesamtpreis
    data: future_prices
    modify: |
      { 
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + (x.total * 100).toFixed(2) + 
        '&nbsp;Ct/kWh</div>'
      }
    align: center

I only want to change the textcolor in the "Strompreis" tab (energy).

edwardtfn commented 1 year ago

Try this:

type: custom:flex-table-card
title: Strompreis-Level
entities:
  include: sensor.electricity_price
sort:
  - future_prices+
columns:
  - name: Sorting columns
    data: future_prices
    modify: x.startsAt
    hidden: true
  - name: beginnt
    data: future_prices
    modify: |
      {
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        var startsAt = new Date(x.startsAt)
        var d = new Date()
        var sday = ""
        if (startsAt.getDay() == d.getDay())
          sday = "heute"
        else
          sday = "morgen";
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + sday + " ab " + startsAt.getHours().toLocaleString('en-US', {minimumIntegerDigits: 1}) + " Uhr" +
        '</div>'
      }
    align: center
  - name: Level
    data: future_prices
    modify: |
      { 
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + x.level.replace('CHEAP', 'günstig').replace('NORMAL', 'normal').replace('EXPENSIVE', 'teuer').replace('VERY_', 'sehr ') + 
          '</div>'
      }
    align: center
  - name: Strompreis
    data: future_prices
    modify: |
      {
        '<div style="color: black; background-color: ' + ( x.energy <= 0 ? 'green' : 'red') + '">' 
          + (x.energy * 100).toFixed(2) + 
        '&nbsp;Ct/kWh</div>'
      }
    align: center
  - name: Steuer/Gebühr
    data: future_prices
    modify: |
      { 
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + (x.tax * 100).toFixed(2) + 
        '&nbsp;Ct/kWh</div>'
      }
    align: center
  - name: Gesamtpreis
    data: future_prices
    modify: |
      { 
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + (x.total * 100).toFixed(2) + 
        '&nbsp;Ct/kWh</div>'
      }
    align: center
6thGuest commented 1 year ago

Yeah - thanks again I changed it a little bit to only change the textcolor...

        '<div style="color: ' + ( x.energy <= 0 ? 'white' : 'black') + '; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + (x.energy * 100).toFixed(2) + 
        '&nbsp;Ct/kWh</div>'
edwardtfn commented 1 year ago

Great. By the way, I saw you using replace to translate the strings on the column "Level". It will work fine as you did, but if in the future you have a bigger list or more complex strings, you could try adding the translations to the table. Something like this:

  - name: Level
    data: future_prices
    modify: |
      { 
        var table = [
            { level: "VERY_EXPENSIVE",  color: "red",          translation: "Sehr teuer" },
            { level: "EXPENSIVE",       color: "goldenrod",    translation: "Teuer" },
            { level: "NORMAL",          color: "green",        translation: "Normal" },
            { level: "CHEAP",           color: "limegreen",    translation: "Günstig" },
            { level: "VERY_CHEAP",      color: "lightgreen" }, translation: "Sehr günstig" ]
        '<div style="color: black; background-color: ' + table.find(c => c.level === x.level).color + '">' 
        + table.find(c => c.level === x.level).translation + 
        '</div>'
      }
    align: center
6thGuest commented 1 year ago

Okay - I'll try this. But unfortunately I don't get along with the syntax of all this. Just if you leave out a "space" something might not work as expected. I also tried to figure out c.level x.level and so on - no chance. Now I tried to sort the "today-prices" (total) in ascending order and thought that this couldn't be a big problem - only change the "sort_by" and that's it. But I failed again. The table is only sorted by the "startsAt" column but not by the price. My intention was to have a table with maybe 3 to 5 rows showing the cheapest prices and the "startsAt" column.

This is my try:

type: custom:flex-table-card
sort_by:
  - total+
title: Tibber - Test
entities:
  include: sensor.electricity_price
columns:
  - name: beginnt
    data: today
    modify: |
      {
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        var startsAt = new Date(x.startsAt)
        var d = new Date()
        var sday = ""
        if (startsAt.getDay() == d.getDay())
          sday = "heute"
        else
          sday = "morgen";
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + sday + " ab " + startsAt.getHours().toLocaleString('en-US', {minimumIntegerDigits: 1}) + " Uhr" +
        '</div>'
      }
    align: center
  - name: Gesamtpreis
    data: today
    modify: |
      { 
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + (x.total * 100).toFixed(2) + 
        '&nbsp;Ct/kWh</div>'
      }
    align: center

This drives me maaaaaaaad :-(

edwardtfn commented 1 year ago

Now I tried to sort the "today-prices" (total) in ascending order and thought that this couldn't be a big problem - only change the "sort_by" and that's it. But I failed again. The table is only sorted by the "startsAt" column but not by the price. My intention was to have a table with maybe 3 to 5 rows showing the cheapest prices and the "startsAt" column.

The problem is on the data format. This card is not prepared to handle fields inside an attribute, so all your columns are showing the same attribute (today) and, when sorting by that it will always sort by the first column with that attribute. So, a work around is to have a hidden column first with the data you want to sort.

Something like this:

type: custom:flex-table-card
sort_by:
  - today+
title: Tibber - Test
entities:
  include: sensor.electricity_price
columns:
  - name: Sorting column
    data: today
    modify: x.total
    hidden: true
  - name: beginnt
    data: today
    modify: |
      {
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        var startsAt = new Date(x.startsAt)
        var d = new Date()
        var sday = ""
        if (startsAt.getDay() == d.getDay())
          sday = "heute"
        else
          sday = "morgen";
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + sday + " ab " + startsAt.getHours().toLocaleString('en-US', {minimumIntegerDigits: 1}) + " Uhr" +
        '</div>'
      }
    align: center
  - name: Gesamtpreis
    data: today
    modify: |
      { 
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + (x.total * 100).toFixed(2) + 
        '&nbsp;Ct/kWh</div>'
      }
    align: center
edwardtfn commented 1 year ago

But unfortunately I don't get along with the syntax of all this.

It is a YAML using Javascript to create a HTML with CSS... This makes me crazy also... 😜

6thGuest commented 1 year ago

Slowly but surely I seem to get in...

I added an additional attribute to the sensor (for the future prices of only today)...

          future_prices_today: >-
            {% if (this.attributes.today | default('unknown')) in ['unknown','unavailable','none'] %}
              unknown
            {% else %}
              {{ (this.attributes.today | default([])) | selectattr('startsAt', 'gt', (now() - timedelta(hours=1)) | string | replace(' ','T')) | list }}
            {% endif %}

And now I can sort the prices of only today in ascending order:

type: custom:flex-table-card
sort_by:
  - future_prices_today+
max_rows: 10
title: günstigste Strompreise heut
entities:
  include: sensor.electricity_price
columns:
  - name: Sorting column
    data: future_prices_today
    modify: x.total
    hidden: true
  - name: beginnt
    data: future_prices_today
    modify: |
      {
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        var startsAt = new Date(x.startsAt)
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + " ab " + startsAt.getHours().toLocaleString('en-US', {minimumIntegerDigits: 1}) + " Uhr" +
        '</div>'
      }
    align: center
  - name: Gesamtpreis
    data: future_prices_today
    modify: |
      { 
        var colors = [
            { level: "VERY_EXPENSIVE",  color: "red" },
            { level: "EXPENSIVE",       color: "goldenrod" },
            { level: "NORMAL",          color: "green" },
            { level: "CHEAP",           color: "limegreen" },
            { level: "VERY_CHEAP",      color: "lightgreen" } ]
        '<div style="color: black; background-color: ' + colors.find(c => c.level === x.level).color + '">' 
          + (x.total * 100).toFixed(2) + 
        '&nbsp;Ct/kWh</div>'
      }
    align: center
6thGuest commented 1 year ago

Short feedback... The total-price-sorted table works well. But the WAF (woman acceptance factor) is very low... - "too confusing" :-( "Can you remove this and make a table with the 3 or 4 cheapest hours for today and tomorrow?!?!?!" - That was the only reaction to this. And my answer: "NOOOO I CAN'T!" But this helped me a bit to get a little bit more into the syntax of this YAML-thing. Cheers