kotope / aio_energy_management

Home Assistant All-In-One Energy Management integration
MIT License
10 stars 0 forks source link

Feature request / change: ensure 'list' attribute always exists (but empty if no slots exist) #19

Open emarvegt opened 2 days ago

emarvegt commented 2 days ago

When working with the 'list' attribute, the number of entries not only varies from 0 to 2, but sometimes the attribute itself is completely gone. Something like the Apexcharts card easily breaks when there is missing data. So first I have to verify that the list attribute exists, and then if there are any elements, and if so, iterate through them and use the start and end keys. Would it be an idea to ensure that the list attribute always exists, even if it contains 0 entries? That way, there is one less safeguard to implement. Or, that the entries are always there too but contain undefined or 0?

emarvegt commented 2 days ago

Example card using config-template-card which allows a bit of inline Javascript for date processing and the Apexcharts card - this still breaks when the list attribute is missing, as it has no check for its existence yet:

entities:
  - sensor.dyn_energieprijzen_average_electricity_price_today
  - binary_sensor.my_cheapest_hours
  - binary_sensor.expensive_hours
card:
  type: custom:apexcharts-card
  graph_span: 32h
  all_series_config:
    stroke_width: 2
  span:
    start: hour
    offset: "-8h"
  now:
    show: false
  update_interval: 5min
  header:
    show: true
    title: Stroomprijsprognose (€/kWh)
    show_states: true
    colorize_states: true
  series:
    - entity: sensor.dyn_energieprijzen_average_electricity_price_today
      yaxis_id: left
      type: line
      float_precision: 4
      name: Prijsprognose
      curve: stepline
      unit: €/kWh
      show:
        in_header: false
        extremas: true
        legend_value: false
      data_generator: |
        return entity.attributes.prices.map((record, index) => {
          return [record.time, record.price];
        });
    - entity: sensor.stroomprijs_gemiddelde
      name: Gemiddelde
      extend_to: now
      color: orange
      opacity: 0.5
      yaxis_id: left
      curve: stepline
      show:
        in_header: false
        in_legend: false
        legend_value: false
  yaxis:
    - id: left
      decimals: 2
      min: 0
    - id: right
      opposite: true
      decimals: 2
      min: 0
      max: 10
      show: false
  apex_config:
    xaxis:
      stepSize: 5
      labels:
        format: HH:mm
    annotations:
      xaxis:
        - x: >-
            ${if(typeof
            states['binary_sensor.my_cheapest_hours'].attributes.list[0] !==
            'undefined') { new
            Date(states['binary_sensor.my_cheapest_hours'].attributes.list[0].start).getTime()
            } else { 0 } }
          x2: >-
            ${if(typeof
            states['binary_sensor.my_cheapest_hours'].attributes.list[0] !==
            'undefined') { new
            Date(states['binary_sensor.my_cheapest_hours'].attributes.list[0].end).getTime()
            } else { 0 } }
          fillColor: green
          borderColor: "#00000000"
          opacity: 0.2
          label:
            text: Laagst
            borderWidth: 0
            style:
              background: "#006600cc"
        - x: >-
            ${if(typeof
            states['binary_sensor.my_cheapest_hours'].attributes.list[1] !==
            'undefined') { new
            Date(states['binary_sensor.my_cheapest_hours'].attributes.list[1].start).getTime()
            } else { 0 } }
          x2: >-
            ${if(typeof
            states['binary_sensor.my_cheapest_hours'].attributes.list[1] !==
            'undefined') { new
            Date(states['binary_sensor.my_cheapest_hours'].attributes.list[1].end).getTime()
            } else { 0 } }
          fillColor: green
          borderColor: "#00000000"
          opacity: 0.2
          label:
            text: Laagst
            borderWidth: 0
            style:
              background: "#006600cc"
        - x: >-
            ${if(typeof
            states['binary_sensor.expensive_hours'].attributes.list[0] !==
            'undefined') { new Date(
            states['binary_sensor.expensive_hours'].attributes.list[0].start
            ).getTime() } else { 0 } }
          x2: >-
            ${if(typeof
            states['binary_sensor.expensive_hours'].attributes.list[0] !==
            'undefined') { new
            Date(states['binary_sensor.expensive_hours'].attributes.list[0].end
            ).getTime() } else { 0 } }
          fillColor: red
          borderColor: "#00000000"
          opacity: 0.2
          label:
            text: Hoogst
            borderWidth: 0
            style:
              background: "#660000cc"
        - x: >-
            ${if(typeof
            states['binary_sensor.expensive_hours'].attributes.list[1] !==
            'undefined') { new Date(
            states['binary_sensor.expensive_hours'].attributes.list[1].start
            ).getTime() } else { 0 } }
          x2: >-
            ${if(typeof
            states['binary_sensor.expensive_hours'].attributes.list[1] !==
            'undefined') { new Date(
            states['binary_sensor.expensive_hours'].attributes.list[1].end
            ).getTime() } else { 0 } }
          fillColor: red
          borderColor: "#00000000"
          opacity: 0.2
          label:
            text: Hoogst
            borderWidth: 0
            style:
              background: "#660000cc"
        - x: ${Date.now()}
          borderColor: var(--primary-color)
          strokeDashArray: 0
          label:
            text: Nu
            borderWidth: 0
            style:
              background: var(--primary-color)
kotope commented 2 days ago

Excellent idea and should be easily implemented. I'll implement this for the next release.

kotope commented 1 day ago

I will push this to 0.3.1 as it affects failsafe functionality quite heavily.

emarvegt commented 1 day ago

I wonder if it would make sense to put the failsafe data in list[0] and set some Boolean like failSafe=true if the slot retrieval failed, to indicate the list data has failsafe times instead of actually found slots?

kotope commented 1 day ago

Failsafe can be used by checking expiration and current time, no problem with that. However, the current version checks for list existence as well and therefore changes to that needs to be tested throughly.

Fix related to data loss has to be released soon, that's the reason I'm pushing this one release longer :-) No worries though, I will implement this very soon, most probably this week still.